diff --git a/.cargo/config b/.cargo/config index 9c4fa6729a..a818c0bf40 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,11 +1,27 @@ +# NOTE: if you make changes here, remember to also update: +# scripts/test-linux.sh +# scripts/build-linux.sh +# scripts/build-windows.sh + +# Using 'cfg` is broken, see https://github.com/rust-lang/cargo/issues/6858 +#[target.'cfg(target_arch = "x86_64")'] +#rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"] + +# …so instead we list all target triples (Tier 1 64-bit platforms) +[target.x86_64-unknown-linux-gnu] +# Enables the aes-ni instructions for RustCrypto dependency. +rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"] + +[target.x86_64-pc-windows-gnu] +# Enables the aes-ni instructions for RustCrypto dependency. +rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"] + [target.x86_64-pc-windows-msvc] +# Enables the aes-ni instructions for RustCrypto dependency. # Link the C runtime statically ; https://github.com/paritytech/parity-ethereum/issues/6643 -rustflags = ["-Ctarget-feature=+crt-static"] +rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3", "-Ctarget-feature=+crt-static"] -[net] -git-fetch-with-cli = false +[target.x86_64-apple-darwin] +# Enables the aes-ni instructions for RustCrypto dependency. +rustflags = ["-Ctarget-feature=+aes,+sse2,+ssse3"] -[registries] -default = "crates-io" -chronicled-platform-v2 = { index = "https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git" } -crates-io = { index = "https://github.com/rust-lang/crates.io-index" } diff --git a/.circleci/config.yml b/.circleci/config.yml index 9aa477369f..f7a4f4b57a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -94,9 +94,7 @@ workflows: blockchain-interface-workflow: jobs: - test - - image: - requires: - - test + - image - system_tests: requires: - image diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index fb059d428d..10a58c1ce0 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,10 +1,14 @@ _Before filing a new issue, please **provide the following information**._ +_If you think that your issue is an exploitable security vulnerability, please mail your bugreport to security@parity.io instead; your submission might be eligible for our Bug Bounty._ +_You can find mode info on the reporting process in [SECURITY.md](https://github.com/paritytech/parity-ethereum/blob/master/SECURITY.md)_ + + - **Parity Ethereum version**: 0.0.0 - **Operating system**: Windows / MacOS / Linux - **Installation**: homebrew / one-line installer / built from source - **Fully synchronized**: no / yes -- **Network**: ethereum / ropsten / kovan / ... +- **Network**: ethereum / ropsten / goerli / ... - **Restarted**: no / yes _Your issue description goes here below. Try to include **actual** vs. **expected behavior** and **steps to reproduce** the issue._ diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..f8ff2a969e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,21 @@ +Thank you for your Pull Request! + +Before you submitting, please check that: + +- [ ] You added a brief description of the PR, e.g.: + - What does it do? + - What important points reviewers should know? + - Is there something left for follow-up PRs? +- [ ] You labeled the PR with appropriate labels if you have permissions to do so. +- [ ] You mentioned a related issue if this PR related to it, e.g. `Fixes #228` or `Related #1337`. +- [ ] You asked any particular reviewers to review. If you aren't sure, start with GH suggestions. +- [ ] Your PR adheres [the style guide](https://wiki.parity.io/Coding-guide) + - In particular, mind the maximal line length. + - There is no commented code checked in unless necessary. + - Any panickers have a proof or removed. +- [ ] You updated any rustdocs which may have changed + +After you've read this notice feel free to remove it. +Thank you! + +✄ ----------------------------------------------------------------------------- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e5bcd95960..67eb80cf4d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,20 +4,21 @@ stages: - publish - optional -image: parity/parity-ci-linux:latest +image: ${REGISTRY}/parity-ci-linux:latest + variables: GIT_STRATEGY: fetch GIT_SUBMODULE_STRATEGY: recursive + GIT_DEPTH: 3 CI_SERVER_NAME: "GitLab CI" CARGO_HOME: "/ci-cache/${CI_PROJECT_NAME}/cargo/${CI_JOB_NAME}" - SCCACHE_DIR: "/ci-cache/${CI_PROJECT_NAME}/sccache" CARGO_TARGET: x86_64-unknown-linux-gnu - + CARGO_INCREMENTAL: 0 + REGISTRY: registry.parity.io/parity/infrastructure/scripts .releaseable_branches: # list of git refs for building GitLab artifacts (think "pre-release binaries") only: &releaseable_branches - stable - - beta - tags - schedules @@ -31,28 +32,17 @@ variables: - tools/ .docker-cache-status: &docker-cache-status - variables: - CARGO_HOME: "/ci-cache/parity-ethereum/cargo/${CI_JOB_NAME}" dependencies: [] + interruptible: true before_script: - rustup show - cargo --version - - SCCACHE_ERROR_LOG=/builds/parity/parity-ethereum/sccache_debug.log - RUST_LOG=sccache=debug - sccache --start-server - - sccache -s - after_script: - # sccache debug info - - if test -e sccache_debug.log; - then - echo "_____All crate-types:_____"; - grep 'parse_arguments.*--crate-type' sccache_debug.log | sed -re 's/.*"--crate-type", "([^"]+)".*/\1/' | sort | uniq -c; - echo "_____Non-cacheable reasons:_____"; - grep CannotCache sccache_debug.log | sed -re 's/.*CannotCache\((.+)\).*/\1/' | sort | uniq -c; - else - echo "_____No logs from sccache_____"; - exit 0; - fi + retry: + max: 2 + when: + - runner_system_failure + - unknown_failure + - api_failure tags: - linux-docker @@ -62,7 +52,6 @@ variables: <<: *collect_artifacts script: - scripts/gitlab/build-linux.sh - - sccache -s after_script: - mkdir -p tools - cp -r scripts/docker/hub/* ./tools @@ -80,21 +69,35 @@ cargo-check 0 3: <<: *docker-cache-status script: - time cargo check --target $CARGO_TARGET --locked --no-default-features --verbose --color=always - - sccache -s + - sccache --show-stats cargo-check 1 3: stage: test <<: *docker-cache-status script: - time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --no-default-features --verbose --color=always - - sccache -s + - sccache --show-stats cargo-check 2 3: stage: test <<: *docker-cache-status script: - time cargo check --target $CARGO_TARGET --locked --manifest-path util/io/Cargo.toml --features "mio" --verbose --color=always - - sccache -s + - sccache --show-stats + +cargo-check-evmbin: + stage: test + <<: *docker-cache-status + script: + - time cargo check -p evmbin --target $CARGO_TARGET --locked --verbose --color=always + - sccache --show-stats + +cargo-check-benches: + stage: test + <<: *docker-cache-status + script: + - time cargo check --all --benches --target $CARGO_TARGET --locked --verbose --color=always + - sccache --show-stats cargo-audit: stage: test @@ -108,21 +111,12 @@ validate-chainspecs: <<: *docker-cache-status script: - ./scripts/gitlab/validate-chainspecs.sh - - sccache -s - -test-cpp: - stage: build - <<: *docker-cache-status - script: - - ./scripts/gitlab/test-cpp.sh - - sccache -s test-linux: stage: build <<: *docker-cache-status script: - ./scripts/gitlab/test-linux.sh stable - - sccache -s test-linux-beta: stage: build @@ -130,7 +124,6 @@ test-linux-beta: <<: *docker-cache-status script: - ./scripts/gitlab/test-linux.sh beta - - sccache -s test-linux-nightly: stage: build @@ -138,37 +131,30 @@ test-linux-nightly: <<: *docker-cache-status script: - ./scripts/gitlab/test-linux.sh nightly - - sccache -s allow_failure: true -build-android: - <<: *build-on-linux - image: parity/parity-ci-android:stretch - variables: - CARGO_TARGET: armv7-linux-androideabi - build-linux: <<: *build-on-linux - # only: *releaseable_branches + only: *releaseable_branches build-linux-i386: <<: *build-on-linux only: *releaseable_branches - image: parity/parity-ci-i386:latest + image: ${REGISTRY}/parity-ci-i386:latest variables: CARGO_TARGET: i686-unknown-linux-gnu build-linux-arm64: <<: *build-on-linux only: *releaseable_branches - image: parity/parity-ci-arm64:latest + image: ${REGISTRY}/parity-ci-arm64:latest variables: CARGO_TARGET: aarch64-unknown-linux-gnu build-linux-armhf: <<: *build-on-linux only: *releaseable_branches - image: parity/parity-ci-armhf:latest + image: ${REGISTRY}/parity-ci-armhf:latest variables: CARGO_TARGET: armv7-unknown-linux-gnueabihf @@ -179,8 +165,6 @@ build-darwin: variables: CARGO_TARGET: x86_64-apple-darwin CARGO_HOME: "${CI_PROJECT_DIR}/.cargo" - CC: gcc - CXX: g++ script: - scripts/gitlab/build-linux.sh tags: @@ -219,7 +203,7 @@ publish-docker: DOCKER_DRIVER: overlay2 GIT_STRATEGY: none # DOCKERFILE: tools/Dockerfile - # CONTAINER_IMAGE: parity/parity + # CONTAINER_IMAGE: parity/parity script: - ./tools/publish-docker.sh tags: @@ -307,7 +291,7 @@ publish-onchain-manually: when: manual publish-release-awss3-nightly: &publish-release-awss3 - image: parity/awscli:latest + image: ${REGISTRY}/awscli:latest stage: publish only: - nightly @@ -321,7 +305,7 @@ publish-release-awss3-nightly: &publish-release-awss3 script: - echo "__________Push binaries to AWS S3____________" - case "${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" in - (beta|stable|nightly) + (stable|nightly) export BUCKET=releases.parity.io/ethereum; ;; (*) @@ -335,13 +319,13 @@ publish-release-awss3-nightly: &publish-release-awss3 - linux-docker publish-release-awss3-manually: - <<: *publish-release-awss3 + <<: *publish-release-awss3 only: *releaseable_branches when: manual publish-docs: stage: publish - image: parity/parity-ci-docs:latest + image: ${REGISTRY}/parity-ci-docs:latest only: - tags except: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ba5eb20b1..2a7dbcfdcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,139 +1,397 @@ -## Parity-Ethereum [v2.5.9](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.8) - -Parity Ethereum v2.5.9-stable is a patch release that adds the block numbers for activating the Istanbul hardfork on test networks: Ropsten, Görli, Rinkeby and Kovan. +## Parity-Ethereum [v2.7.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.7.2) +Parity Ethereum v2.7.2-stable is a patch version release of parity-ethereum. +Starting in the 2.7.x series of releases, parity-ethereum is switching to a single `stable` release +track. As a result, any clients that currently receive updates from the `beta` +track should switch to the `stable` track. +Due to database format changes, upgrading from 2.5.x or 2.6.x is one-way only. The full list of included changes: +* backwards compatible call_type creation_method (#11450 + #11455) +* chore: remove unused dependencies (#11432) +* Cargo.lock: new lockfile format (#11448) +* rlp_derive: cleanup (#11446) +* Avoid long state queries when serving GetNodeData requests (#11444) +* update kvdb-rocksdb to 0.4 (#11442) +* Remove dead bootnodes, add new geth bootnodes (#11441) +* goerli: replace foundation bootnode (#11433) +* fix: export hardcoded sync format (#11416) +* verification: fix race same block + misc (#11400) +* update classic testnet bootnodes (#11398) +* gcc to clang (#11453) + +## Parity-Ethereum [v2.7.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.7.1) +Parity Ethereum v2.7.1-stable is a patch version release of parity-ethereum. +Starting in the 2.7.x series of releases, parity-ethereum is switching to a single `stable` release +track. As a result, any clients that currently receive updates from the `beta` +track should switch to the `stable` track. +Due to database format changes, upgrading from 2.5.x or 2.6.x is one-way only. + +The full list of included changes from `v2.7.0` to `v2.7.1`: + +* Revert "Distinguish between `create` and `create2` (#11311)" (#11427) + +## Parity-Ethereum [v2.7.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.7.0) + +Parity Ethereum v2.7.0-stable is a minor version release of parity-ethereum. As +of this release, parity-ethereum is switching to a single `stable` release +track. As a result, any clients that currently receive updates from the `beta` +track should switch to the `stable` track. + +The full list of included changes from `v2.5-stable` to `v2.7-stable` (the +`v2.6-beta` branch will already include some of these changes): + +* Update POA bootnodes (#11411) +* Update ProgPoW to 0.9.3 (#11407) +* Add EtherCore support (#11402) +* json-tests: Fix compile error (#11384) +* ethcore/res: fix ethereum classic chainspec blake2_f activation block num (#11391) +* Switching to stable-track (#11377) +* Update copyright notice 2020 (#11386) +* miner: fix deprecation warning Error::description (#11380) +* Fix Aztlan hard fork issues (#11347) +* authority_round: Fix next_step_time_duration. (#11379) +* Set the block gas limit to the value returned by a contract call (#10928) +* [Trace] Distinguish between `create` and `create2` (#11311) +* fix cargo audit (#11378) +* Fix esoteric test config variable (#11292) +* Rip out the C and Java bindings (#11346) +* Encapsulate access to the client for secret store (#11232) +* Forward-port #11356 (#11359) +* Fix error message typo (#11363) +* [util/migration]: remove needless `static` bounds (#11348) +* Replace stale boot nodes with latest list (#11351) +* Update to latest `kvdb-*`: no default column, DBValue is Vec (#11312) +* we do not profit from incremental now (#11302) +* update autoupdate fork blocks for nightly (#11308) +* Add Nat PMP method to P2P module (#11210) +* Add randomness contract support to AuthorityRound. (#10946) +* ethcore/res: activate ecip-1061 on kotti and mordor (#11338) +* tx-q: enable basic verification of local transactions (#11332) +* remove null signatures (#11335) +* ethcore/res: activate agharta on classic 9573000 (#11331) +* [secretstore] migrate to version 4 (#11322) +* Enable EIP-2384 for ice age hard fork (#11281) +* Fix atomicity violation in network-devp2p (#11277) +* Istanbul activation on xDai (#11299) +* Istanbul activation on POA Core (#11298) +* Adds support for ipc socket permissions (#11273) +* Add check for deserialising hex values over U256 limit (#11309) +* validate-chainspecs: check istanbul eips are in the foundation spec (#11305) +* [chainspec]: add `eip1344_transition` for istanbul (#11301) +* only add transactions to signing-queue if it is enabled (#11272) +* Use upstream rocksdb (#11248) +* Treat only blocks in queue as synced (#11264) +* add support for evan.network chains (#11289) +* Add benchmarks and tests for RlpNodeCodec decoding (#11287) +* upgrade vergen to 3.0 (#11293) +* interruptible test and build jobs (#11294) +* Istanbul HF on POA Sokol (#11282) +* [ethcore]: apply filter when `PendingSet::AlwaysQueue` in `ready_transactions_filtered` (#11227) +* Update lib.rs (#11286) +* Don't prune ancient state when instantiating a Client (#11270) +* fixed verify_uncles error type (#11276) +* ethcore: fix rlp deprecation warnings (#11280) +* Upgrade trie-db to 0.16.0. (#11274) +* Clarify what first_block `None` means (#11269) +* removed redundant VMType enum with one variant (#11266) +* Ensure jsonrpc threading settings are sane (#11267) +* Return Ok(None) when the registrar contract returns empty slice (#11257) +* Add a benchmark for snapshot::account::to_fat_rlps() (#11185) +* Fix misc compile warnings (#11258) +* simplify verification (#11249) +* update ropsten forkCanonHash, forkBlock (#11247) +* Make InstantSeal Instant again (#11186) +* ropsten #6631425 foundation #8798209 (#11201) +* Update list of bootnodes for xDai chain (#11236) +* ethcore/res: add mordor testnet configuration (#11200) +* [chain specs]: activate `Istanbul` on mainnet (#11228) +* [builtin]: support `multiple prices and activations` in chain spec (#11039) +* Insert explicit warning into the panic hook (#11225) +* Snapshot restoration overhaul (#11219) +* Fix docker centos build (#11226) +* retry on gitlab system failures (#11222) +* Update bootnodes. (#11203) +* Use provided usd-per-eth value if an endpoint is specified (#11209) +* Use a lock instead of atomics for snapshot Progress (#11197) +* [informant]: `MillisecondDuration` -> `as_millis()` (#11211) +* Step duration map configuration parameter ported from the POA Network fork (#10902) +* Upgrade jsonrpc to latest (#11206) +* [export hardcoded sync]: use debug for `H256` (#11204) +* Pause pruning while snapshotting (#11178) +* Type annotation for next_key() matching of json filter options (#11192) +* Crypto primitives removed from ethkey (#11174) +* Made ecrecover implementation trait public (#11188) +* Remove unused macro_use. (#11191) +* [dependencies]: jsonrpc `14.0.1` (#11183) +* [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179) +* [dependencies] bump rand 0.7 (#11022) +* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180) +* TxPermissions ver 3: gas price & data (#11170) +* [ethash] chainspec validate `ecip1017EraRounds` non-zero (#11123) +* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175) +* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172) +* Aura: Report malice on sibling blocks from the same validator (#11160) +* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127) +* Cleanup stratum a bit (#11161) +* [keccak-hasher]: rust2018 (#11163) +* Upgrade to jsonrpc v14 (#11151) +* Secret store: fix Instant::now() related race in net_keep_alive (#11155) +* RPC method for clearing the engine signer (#10920) +* Use TryFrom instead of From+panic for Builtin (#11140) +* Fix sccache statistics (#11145) +* Update ethereum types to 0.8.0 version (#11139) +* [json]: add docs to `hardfork specification` (#11138) +* ServiceTransactionChecker::refresh_cache: allow registrar unavailable (#11126) +* Fix some random typos, formatting/whitespace (#11128) +* Refactor parity_listStorageKeys with count parameter optional (#11124) +* Make EIP712Domain Fields Optional (#11103) +* EIP-712: bump version in prep for publishing (#11106) +* move StateResult to `common-types` (#11121) +* Deduplicate registrar contract & calling logic (#11110) +* Refactor return type of `BlockChainClient::code` #7098 (#11102) +* Switching sccache from local to Redis (#10971) +* SIMD Implementation for EIP-152 (#11056) +* Fix deprecated trait objects without an explicit `dyn` (#11112) +* [spec] fix rinkeby spec (#11108) +* Update to latest jsonrpc (#11111) +* use images from our registry (#11105) +* Correct EIP-712 encoding (#11092) +* [CI] check evmbin build (#11096) +* Update `kvdb`, `kvdb-rocksdb` and `h2` (#11091) +* [client]: Fix for incorrectly dropped consensus messages (#11082) (#11086) +* Update JSON tests to d4f86ecf4aa7c (#11054) +* fix(network): typo (#11088) +* [ethash] remove manual unrolling (#11069) * ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068) -* [json-spec] make blake2 pricing spec more readable (#11034) - -## Parity-Ethereum [v2.5.8](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.8) - -Parity Ethereum v2.5.8-stable is a patch release that improves security, stability and performance. - -* The most noteworthy improvement in this release is incorporating all the EIPs required for the Istanbul hard fork. -* This release also fixes certain security and performance issues, one of which was suspected to be consensus-threatening but turned out to be benign. Thanks to Martin Holst Swende and Felix Lange from the Ethereum Foundation for bringing the suspicious issue to our attention. - -The full list of included changes: - +* [sync]: rust 2018 (#11067) +* [ethcore]: move client test types to test-helpers (#11062) +* [sync]: remove unused dependencies or make dev (#11061) +* [ethcore]: reduce re-exports (#11059) +* [evmbin] fix time formatting (#11060) +* Update hardcoded headers (foundation, classic, kovan, xdai, ewc, ...) (#11053) +* cargo update -p eth-secp256k1 (#11052) +* ethcore: remove `test-helper feat` from build (#11047) +* Include test-helpers from ethjson (#11045) +* [ethcore]: cleanup dependencies (#11043) * add more tx tests (#11038) * Fix parallel transactions race-condition (#10995) +* [ethcore]: make it compile without `test-helpers` feature (#11036) +* Benchmarks for block verification (#11035) +* Move snapshot related traits to their proper place (#11012) +* cleanup json crate (#11027) +* [spec] add istanbul test spec (#11033) +* [json-spec] make blake2 pricing spec more readable (#11034) * Add blake2_f precompile (#11017) +* Add new line after writing block to hex file. (#10984) +* fix: remove unused error-chain (#11028) +* fix: remove needless use of itertools (#11029) +* Convert `std::test` benchmarks to use Criterion (#10999) +* Fix block detail updating (#11015) * [trace] introduce trace failed to Ext (#11019) +* cli: update usage and version headers (#10924) +* [private-tx] remove unused rand (#11024) +* Extract snapshot to own crate (#11010) * Edit publish-onchain.sh to use https (#11016) -* Fix deadlock in network-devp2p (#11013) * EIP 1108: Reduce alt_bn128 precompile gas costs (#11008) +* Fix deadlock in `network-devp2p` (#11013) +* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191) +* EIP 1884 Re-pricing of trie-size dependent operations (#10992) * xDai chain support and nodes list update (#10989) -* EIP 2028: transaction gas lowered from 68 to 16 (#10987) +* [trace] check mem diff within range (#11002) * EIP-1344 Add CHAINID op-code (#10983) +* Make ClientIoMessage generic over the Client (#10981) +* bump spin to 0.5.2 (#10996) +* fix compile warnings (#10993) +* Fix compilation on recent nightlies (#10991) +* [ipfs] Convert to edition 2018 (#10979) +* Extract spec to own crate (#10978) +* EIP 2028: transaction gas lowered from 68 to 16 (#10987) +* Extract engines to own crates (#10966) +* Configuration map of block reward contract addresses (#10875) +* Add a 2/3 quorum option to Authority Round. (#10909) +* Fix rlp decode for inline trie nodes. (#10980) +* Private contract migration and offchain state sync (#10748) * manual publish jobs for releases, no changes for nightlies (#10977) -* [blooms-db] Fix benchmarks (#10974) -* Verify transaction against its block during import (#10954) +* Extract the Engine trait (#10958) * Better error message for rpc gas price errors (#10931) +* [.gitlab.yml] cargo check ethcore benches (#10965) +* Verify transaction against its block during import (#10954) +* [evmbin] fix compilation (#10976) +* Update to latest trie version. (#10972) +* [blooms-db] Fix benchmarks (#10974) +* Fix ethcore/benches build. (#10964) * tx-pool: accept local tx with higher gas price when pool full (#10901) -* Fix fork choice (#10837) -* Cleanup unused vm dependencies (#10787) -* Fix compilation on recent nightlies (#10991) -* Don't build rpc with ethcore test-helpers (#11048) -* EIP 1884 Re-pricing of trie-size dependent operations (#10992) -* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191) - -## Parity-Ethereum [v2.5.7](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.7) - -Parity Ethereum v2.5.7-stable is a bugfix release that fixes a potential DoS attack in the trace_call RPC method. This is a critical upgrade for anyone running Parity nodes with RPC exposed to the public internet (and highly recommended for anyone else). For details see this blog post. - -## Parity-Ethereum [v2.5.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.6) - -Parity-Ethereum v2.5.6-stable is a bugfix release that improves stability. - -* Allow specifying hostnames for node URLs -* Fix a bug where archive nodes were losing peers - -The full list of included changes: - +* Disable unsyncable expanse chain (#10926) +* Extract Machine from ethcore (#10949) +* removed redundant state_root function from spec, improve spec error types (#10955) +* Add support for Energy Web Foundation's new chains (#10957) +* [evmbin] add more tests to main.rs (#10956) +* Fix compiler warnings in util/io and upgrade to edition 2018 Upgrade mio to latest (#10953) +* unify loading spec && further spec cleanups (#10948) +* refactor: Refactor evmbin CLI (#10742) +* journaldb changes (#10929) +* Allow default block parameter to be blockHash (#10932) +* Enable sealing when engine is ready (#10938) +* Fix some warnings and typos. (#10941) +* Updated security@parity.io key (#10939) +* Change the return type of step_inner function. (#10940) +* get rid of hidden mutability of Spec (#10904) +* simplify BlockReward::reward implementation (#10906) * Kaspersky AV whitelisting (#10919) -* Avast whitelist script (#10900) -* Docker images renaming (#10863) -* Remove excessive warning (#10831) -* Allow --nat extip:your.host.here.org (#10830) +* additional arithmetic EVM opcode benchmarks (#10916) +* [Cargo.lock] cargo update -p crossbeam-epoch (#10921) +* Fixes incorrect comment. (#10913) +* Add file path to disk map write/read warnings (#10911) +* remove verify_transaction_unordered from engine (#10891) +* Avast whitelist script (#10900) +* cleanup ethcore ethereum module (#10899) +* Move more types out of ethcore (#10880) +* return block nonce when engine is clique (#10892) +* TransactionQueue::import accepts iterator (#10889) +* rename is_pruned to is_prunable (#10888) +* simplify create_address_scheme (#10890) +* Move DatabaseExtras back to trace (#10868) +* Update README.md and Changelogs (#10866) +* whisper is no longer a part of parity-ethereum repo (#10855) +* [ethash] remove mem::uninitialized (#10861) +* Docker images renaming (#10863) +* Move the substate module into ethcore/executive (#10867) +* Run cargo fix on a few of the worst offenders (#10854) +* removed redundant fork choice abstraction (#10849) +* Extract state-db from ethcore (#10858) +* Fix fork choice (#10837) +* Move more code into state-account (#10840) +* Remove compiler warning (#10865) +* [ethash] use static_assertions crate (#10860) +* EIP-1702: Generalized Account Versioning Scheme (#10771) +* ethcore-builtin (#10850) +* removed QueueError type (#10852) +* removed unused macros (#10851) +* bump crossbeam (#10848) +* removed unused trait PrivateNotify and unused Error types (#10847) +* make fn submit_seal more idiomatic (#10843) +* update parking-lot to 0.8 (#10845) +* Update version to 2.7.0 (#10846) +* update jsonrpc to 12.0 (#10841) +* Improve logging and cleanup in miner around block sealing (#10745) +* Extract AccountDB to account-db (#10839) +* test: Update Whisper test for invalid pool size (#10811) +* Extricate PodAccount and state Account to own crates (#10838) +* logs (#10817) +* refactor: whisper: Add type aliases and update rustdocs in message.rs (#10812) +* Break circular dependency between Client and Engine (part 1) (#10833) +* tests: Relates to #10655: Test instructions for Readme (#10835) +* refactor: Related #9459 - evmbin: replace untyped json! macro with fully typed serde serialization using Rust structs (#10657) +* idiomatic changes to PodState (#10834) +* Allow --nat extip:your.host.here.org (#10830) * When updating the client or when called from RPC, sleep should mean sleep (#10814) -* added new ropsten-bootnode and removed old one (#10794) -* ethkey no longer uses byteorder (#10786) +* Remove excessive warning (#10831) +* Fix typo in README.md (#10828) +* ethcore does not use byteorder (#10829) +* Better logging when backfilling ancient blocks fail (#10796) +* depends: Update wordlist to v1.3 (#10823) +* cargo update -p smallvec (#10822) +* replace memzero with zeroize crate (#10816) +* Don't repeat the logic from Default impl (#10813) +* removed additional_params method (#10818) +* Add Constantinople eips to the dev (instant_seal) config (#10809) +* removed redundant fmt::Display implementations (#10806) +* revert changes to .gitlab-ci.yml (#10807) +* Add filtering capability to `parity_pendingTransactions` (issue 8269) (#10506) +* removed EthEngine alias (#10805) +* wait a bit longer in should_check_status_of_request_when_its_resolved (#10808) * Do not drop the peer with None difficulty (#10772) +* ethcore-bloom-journal updated to 2018 (#10804) +* ethcore-light uses bincode 1.1 (#10798) +* Fix a few typos and unused warnings. (#10803) +* updated project to ansi_term 0.11 (#10799) +* added new ropsten-bootnode and removed old one (#10794) +* updated price-info to edition 2018 (#10801) +* ethcore-network-devp2p uses igd 0.9 (#10797) +* updated parity-local-store to edition 2018 and removed redundant Error type (#10800) +* Cleanup unused vm dependencies (#10787) +* Removed redundant ethcore-service error type (#10788) +* Removed machine abstraction from ethcore (#10791) +* Updated blooms-db to rust 2018 and removed redundant deps (#10785) +* ethkey no longer uses byteorder (#10786) +* Log validator set changes in EpochManager (#10734) +* Treat empty account the same as non-exist accounts in EIP-1052 (#10775) * docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652) - -## Parity-Ethereum [v2.5.5](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.5) - -Parity-Ethereum v2.5.5-stable is a minor release that improves performance and stability. -This release stabilises the 2.5 branch. - -As of today, Parity-Ethereum 2.4 reaches end of life and everyone is -encouraged to upgrade. - -## Parity-Ethereum [v2.5.4](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.4) - -Parity Ethereum v2.5.4-beta is a security update that addresses servo/rust-smallvec#148 - -The full list of included changes: - -* cargo update -p smallvec ([#10822](https://github.com/paritytech/parity-ethereum/pull/10822)) - -## Parity-Ethereum [v2.5.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.3) - -Parity-Ethereum 2.5.3-beta is a bugfix release that improves performance and stability. - -* EthereumClassic: activate the Atlantis Hardfork -* Clique: fix time overflow -* State tests: treat empty accounts the same as non-existant accounts (EIP 1052) -* Networking: support discovery-only peers (geth bootnodes) -* Snapshotting: fix unclean shutdown while snappshotting is under way - -The full list of included changes: - -* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/paritytech/parity-ethereum/pull/10766)) -* fix docker tags for publishing ([#10741](https://github.com/paritytech/parity-ethereum/pull/10741)) -* fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/paritytech/parity-ethereum/pull/10720)) -* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/paritytech/parity-ethereum/pull/10775)) -* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet ([#10705](https://github.com/paritytech/parity-ethereum/pull/10705)) -* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/paritytech/parity-ethereum/pull/10744)) - -## Parity-Ethereum [v2.5.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.2) - -Parity-Ethereum 2.5.2-beta is a bugfix release that improves performance and stability. - -Among others, it enables the _Atlantis_ hardfork on **Morden** and **Kotti** Classic networks. - -The full list of included changes: - -* [CI] allow cargo audit to fail ([#10676](https://github.com/paritytech/parity-ethereum/pull/10676)) -* Reset blockchain properly ([#10669](https://github.com/paritytech/parity-ethereum/pull/10669)) -* new image ([#10673](https://github.com/paritytech/parity-ethereum/pull/10673)) -* Update publishing ([#10644](https://github.com/paritytech/parity-ethereum/pull/10644)) -* enable lto for release builds ([#10717](https://github.com/paritytech/parity-ethereum/pull/10717)) -* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/paritytech/parity-ethereum/pull/10719)) -* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/paritytech/parity-ethereum/pull/10731)) - -## Parity-Ethereum [v2.5.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.1) - -Parity-Ethereum 2.5.1-beta is a bugfix release that improves performance and stability. - -Among others, it enables the Petersburg hardfork on **Rinkeby** and **POA-Core** Network, as well as the **Kovan** Network community hardfork. - -The full list of included changes: - -* ci: publish docs debug ([#10638](https://github.com/paritytech/parity-ethereum/pull/10638)) - -## Parity-Ethereum [v2.5.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.0) - -Parity-Ethereum 2.5.0-beta is a minor release that improves performance and stabilizes the 2.5 branch by marking it as beta release. - -- This release adds support for the Clique consensus engine ([#9981](https://github.com/paritytech/parity-ethereum/pull/9981)) - - This enables Parity-Ethereum users to use the Görli, the Kotti Classic, and the legacy Rinkeby testnet. To get started try `parity --chain goerli`; note that light client support is currently not yet fully functional. -- This release removes the dead chain configs for Easthub and Ethereum Social ([#10531](https://github.com/paritytech/parity-ethereum/pull/10531)) - -As of today, Parity-Ethereum 2.3 reaches end of life and everyone is encouraged to upgrade. - -The full list of included changes: - -* fix(light cull): poll light cull instead of timer ([#10559](https://github.com/paritytech/parity-ethereum/pull/10559)) - +* Move Engine::register_client to be before other I/O handler registration (#10767) +* Print warnings when using dangerous settings for ValidatorSet (#10733) +* ethcore/res: activate atlantis classic hf on block 8772000 (#10766) +* refactor: Fix indentation (#10740) +* Updated Bn128PairingImpl to use optimized batch pairing (#10765) +* fix: aura don't add `SystemTime::now()` (#10720) +* Initialize private tx logger only if private tx functionality is enabled (#10758) +* Remove unused code (#10762) +* Remove calls to heapsize (#10432) +* [devp2p] Update to 2018 edition (#10716) +* Add a way to signal shutdown to snapshotting threads (#10744) +* Enable aesni (#10756) +* remove support of old SS db formats (#10757) +* [devp2p] Don't use `rust-crypto` (#10714) +* updater: fix static id hashes initialization (#10755) +* Use fewer threads for snapshotting (#10752) +* Die error_chain, die (#10747) +* Fix deprectation warnings on nightly (#10746) +* fix docker tags for publishing (#10741) +* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet (#10705) +* ethcore: enable ECIP-1054 for classic (#10731) +* Stop breaking out of loop if a non-canonical hash is found (#10729) +* Refactor Clique stepping (#10691) +* Use RUSTFLAGS to set the optimization level (#10719) +* SecretStore: non-blocking wait of session completion (#10303) +* removed secret_store folder (#10722) +* SecretStore: expose restore_key_public in HTTP API (#10241) +* Revert "enable lto for release builds (#10717)" (#10721) +* enable lto for release builds (#10717) +* Merge `Notifier` and `TransactionsPoolNotifier` (#10591) +* [devp2p] Fix warnings and re-org imports (#10710) +* Upgrade ethereum types (#10670) +* introduce MissingParent Error, fixes #10699 (#10700) +* Update publishing (#10644) +* Upgrade to parity-crypto 0.4 (#10650) +* new image (#10673) +* Add SealingState; don't prepare block when not ready. (#10529) +* Fix compiler warning (that will become an error) (#10683) +* add_sync_notifier in EthPubSubClient holds on to a Client for too long (#10689) +* Don't panic if extra_data is longer than VANITY_LENGTH (#10682) +* docs: evmbin - Update Rust docs (#10658) +* Remove annoying compiler warnings (#10679) +* Reset blockchain properly (#10669) +* Remove support for hardware wallets (#10678) +* [CI] allow cargo audit to fail (#10676) +* docs: Add ProgPoW Rust docs to ethash module (#10653) +* fix: Move PR template into .github/ folder (#10663) +* docs: Add PR template (#10654) +* Trivial journal for private transactions (#10056) +* fix(compilation warnings) (#10649) +* [whisper] Move needed aes_gcm crypto in-crate (#10647) +* Adds parity_getRawBlockByNumber, parity_submitRawBlock (#10609) +* Fix rinkeby petersburg fork (#10632) +* ci: publish docs debug (#10638) +* Fix publish docs (#10635) +* Update kovan.json to switch validator set to POA Consensus Contracts (#10628) +* [ethcore] remove error_chain (#10616) +* Remove unused import (#10615) +* evm: add some mulmod benches (#10600) +* Clique: zero-fill extradata when the supplied value is less than 32 bytes in length (#10605) +* Constantinople HF on POA Core (#10606) +* adds rpc error message for --no-ancient-blocks (#10608) +* Allow CORS requests in Secret Store API (#10584) +* update bootnodes (#10595) +* sccache logs to stdout (#10596) +* fix(whisper expiry): current time + work + ttl (#10587) +* CI improvements (#10579) +* Watch transactions pool (#10558) +* fix(evmbin): make benches compile again (#10586) +* fix issue with compilation when 'slow-blocks' feature enabled (#10585) +* Reject crazy timestamps instead of truncating. (#10574) +* Node table limiting and cache for node filter (#10288) +* fix(light cull): poll light cull instead of timer (#10559) +* Update Issue Template to direct security issue to email (#10562) +* RPC: Implements eth_subscribe("syncing") (#10311) +* Explicitly enable or disable Stratum in config file (Issue 9785) (#10521) +* version: bump master to 2.6 (#10560) diff --git a/Cargo.lock b/Cargo.lock index 45005b93b1..ec444f4dc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,687 +1,908 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "account-db" +version = "0.1.0" +dependencies = [ + "ethereum-types", + "hash-db", + "keccak-hash", + "keccak-hasher 0.1.1", + "kvdb", + "rlp", +] + +[[package]] +name = "account-state" +version = "0.1.0" +dependencies = [ + "account-db", + "common-types", + "derive_more 0.15.0", + "ethereum-types", + "hash-db", + "journaldb", + "keccak-hash", + "keccak-hasher 0.1.1", + "kvdb", + "log", + "lru-cache", + "memory-db", + "parity-bytes", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "patricia-trie-ethereum", + "pod", + "rlp", + "rlp_compress", + "serde", + "trie-db", + "trie-vm-factories", +] + [[package]] name = "aes" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-soft", + "aesni", + "block-cipher-trait", ] [[package]] name = "aes-ctr" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" dependencies = [ - "aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aes-soft", + "aesni", + "ctr", + "stream-cipher", ] [[package]] name = "aes-soft" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "byteorder", + "opaque-debug", ] [[package]] name = "aesni" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "opaque-debug", + "stream-cipher", ] [[package]] -name = "aho-corasick" -version = "0.6.10" +name = "ahash" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f33b5018f120946c1dcf279194f238a9f146725593ead1c08fa47ff22b0b5d3" dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random", ] [[package]] name = "aho-corasick" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "amq-protocol" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "amq-protocol-codegen 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "amq-protocol-tcp 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "amq-protocol-types 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "amq-protocol-uri 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cookie-factory 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "amq-protocol-codegen" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "amq-protocol-types 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "handlebars 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "amq-protocol-tcp" -version = "3.1.0" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a" dependencies = [ - "amq-protocol-uri 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tcp-stream 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] -name = "amq-protocol-types" -version = "3.1.0" +name = "aho-corasick" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" dependencies = [ - "cookie-factory 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] -name = "amq-protocol-uri" -version = "3.1.0" +name = "ansi_term" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[package]] -name = "ansi_term" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ansi_term" -version = "0.11.0" +name = "anyhow" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" [[package]] name = "app_dirs" 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.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.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ole32-sys", + "shell32-sys", + "winapi 0.2.8", + "xdg", ] [[package]] name = "arrayref" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" [[package]] name = "arrayvec" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop", ] [[package]] -name = "ascii" -version = "0.9.3" +name = "arrayvec" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "assert_matches" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" + +[[package]] +name = "attohttpc" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf0ec4b0e00f61ee75556ca027485b7b354f4a714d88cc03f4468abd9378c86" +dependencies = [ + "http", + "log", + "url 1.7.1", +] [[package]] name = "atty" -version = "0.2.13" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +dependencies = [ + "libc", + "termion", + "winapi 0.3.8", +] + +[[package]] +name = "authority-round" +version = "0.1.0" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "block-gas-limit", + "block-reward", + "client-traits", + "common-types", + "derive_more 0.15.0", + "engine", + "env_logger 0.6.2", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethcore-accounts", + "ethcore-io", + "ethereum-types", + "ethjson", + "itertools 0.5.10", + "keccak-hash", + "lazy_static", + "log", + "lru-cache", + "machine", + "macros", + "parity-bytes", + "parity-crypto", + "parking_lot 0.9.0", + "rand 0.7.2", + "rlp", + "serde_json", + "spec", + "state-db", + "time-utils", + "unexpected", + "validator-set", ] [[package]] name = "autocfg" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" [[package]] name = "backtrace" -version = "0.3.38" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" dependencies = [ - "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys", + "cfg-if", + "libc", + "rustc-demangle", + "winapi 0.3.8", ] [[package]] name = "backtrace-sys" -version = "0.1.31" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", ] [[package]] name = "base-x" -version = "0.2.5" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cda5d0f5584d129112ad8bf4775b9fd2b9f1e30738c7b1a25314ba2244d6a51" [[package]] name = "base64" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "safemem", ] [[package]] name = "base64" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "basic-authority" +version = "0.1.0" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "client-traits", + "common-types", + "engine", + "ethcore", + "ethcore-accounts", + "ethereum-types", + "ethjson", + "keccak-hash", + "log", + "machine", + "parity-crypto", + "parking_lot 0.9.0", + "rlp", + "spec", + "tempdir", + "validator-set", ] [[package]] name = "bincode" -version = "0.8.0" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7" +dependencies = [ + "autocfg", + "byteorder", + "serde", +] + +[[package]] +name = "bindgen" +version = "0.49.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "846a1fba6535362a01487ef6b10f0275faa12e5c5d835c5c1c627aabc46ccbd6" dependencies = [ - "byteorder 1.3.2 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "cexpr", + "cfg-if", + "clang-sys", + "clap", + "env_logger 0.6.2", + "fxhash", + "lazy_static", + "log", + "peeking_take_while", + "proc-macro2 0.4.20", + "quote 0.6.8", + "regex", + "shlex", + "which", ] [[package]] name = "bit-set" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c" dependencies = [ - "bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-vec", ] [[package]] name = "bit-vec" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" [[package]] name = "bitflags" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "0.9.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" [[package]] -name = "bitflags" -version = "1.2.0" +name = "bitvec" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" [[package]] name = "block-buffer" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref", + "byte-tools 0.2.0", ] [[package]] name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-padding", + "byte-tools 0.3.1", + "byteorder", + "generic-array 0.12.0", ] [[package]] name = "block-cipher-trait" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" +dependencies = [ + "generic-array 0.12.0", +] + +[[package]] +name = "block-gas-limit" +version = "0.1.0" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "client-traits", + "common-types", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethereum-types", + "log", + "spec", ] [[package]] name = "block-modes" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31aa8410095e39fdb732909fb5730a48d5bd7c2e3cd76bd1b07b3dbea130c529" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "block-padding", ] [[package]] name = "block-padding" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" +dependencies = [ + "byte-tools 0.3.1", +] + +[[package]] +name = "block-reward" +version = "0.1.0" dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "engine", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethereum-types", + "keccak-hash", + "machine", + "spec", + "trace", ] [[package]] name = "blooms-db" version = "0.1.0" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethbloom 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion", + "ethbloom", + "parking_lot 0.9.0", + "tempdir", ] [[package]] name = "bn" version = "0.4.4" -source = "git+https://github.com/paritytech/bn#6079255e65793038b9a6e5292203eab482737cc2" +source = "git+https://github.com/paritytech/bn#6beba2ed6c9351622f9e948ccee4063846b2b39a" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crunchy 0.2.2", + "lazy_static", + "rand 0.5.5", + "rustc-hex 2.0.1", ] -[[package]] -name = "boolinator" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "bstr" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "memchr", + "regex-automata", + "serde", ] +[[package]] +name = "bumpalo" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fb8038c1ddc0a5f73787b130f4cc75151e96ed33e417fde765eb5a81e3532f4" + +[[package]] +name = "byte-slice-cast" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6209f3b2c1edea170002e016d5ead6903d3bb0a846477f53bbeb614967a52a9" + [[package]] name = "byte-tools" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" [[package]] name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" [[package]] name = "bytes" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "iovec", ] [[package]] name = "c2-chacha" -version = "0.2.3" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" dependencies = [ - "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "ppv-lite86", ] [[package]] name = "cast" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" [[package]] name = "cc" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c" +dependencies = [ + "jobserver", + "num_cpus", +] [[package]] -name = "cesu8" -version = "1.1.0" +name = "cexpr" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d" +dependencies = [ + "nom", +] [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chainspec" version = "0.1.0" dependencies = [ - "ethjson 0.1.0", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "ethjson", + "serde_json", ] [[package]] name = "chrono" -version = "0.4.9" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer", + "num-traits 0.2.6", + "time", ] [[package]] name = "cid" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0e37fba0087d9f3f4e269827a55dc511abf3e440cc097a0c154ff4e6584f988" dependencies = [ - "integer-encoding 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "integer-encoding", + "multibase", + "multihash", +] + +[[package]] +name = "clang-sys" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" +dependencies = [ + "glob", + "libc", + "libloading", ] [[package]] name = "clap" version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "atty", + "bitflags", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", ] [[package]] name = "cli-signer" version = "1.4.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (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)", + "ethereum-types", + "futures", + "parity-rpc", + "parity-rpc-client", + "rpassword", ] [[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "client-traits" +version = "0.1.0" dependencies = [ - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "account-state", + "common-types", + "ethcore-blockchain", + "ethcore-call-contract", + "ethcore-db", + "ethcore-miner", + "ethereum-types", + "kvdb", + "parity-bytes", + "registrar", + "stats", + "trace", + "vm", ] [[package]] -name = "cmake" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "clique" +version = "0.1.0" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", + "client-traits", + "common-types", + "engine", + "ethcore", + "ethereum-types", + "ethjson", + "keccak-hash", + "lazy_static", + "log", + "lru-cache", + "machine", + "macros", + "parity-crypto", + "parking_lot 0.9.0", + "rand 0.7.2", + "rlp", + "spec", + "state-db", + "time-utils", + "unexpected", ] [[package]] -name = "combine" -version = "3.8.1" +name = "cloudabi" +version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "ascii 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", ] [[package]] -name = "common-types" -version = "0.1.0" +name = "cmake" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ec65ee4f9c9d16f335091d23693457ed4928657ba4982289d7fafee03bc614a" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "ethkey 0.3.0", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_derive 0.1.0", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unexpected 0.1.0", + "cc", ] [[package]] -name = "config" -version = "0.9.3" +name = "common-types" +version = "0.1.0" +dependencies = [ + "derive_more 0.15.0", + "ethbloom", + "ethcore-io", + "ethereum-types", + "ethjson", + "keccak-hash", + "parity-bytes", + "parity-crypto", + "parity-snappy", + "parity-util-mem 0.3.0", + "patricia-trie-ethereum", + "rlp", + "rlp_derive", + "rustc-hex 2.0.1", + "unexpected", + "vm", +] + +[[package]] +name = "const-random" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b641a8c9867e341f3295564203b1c250eb8ce6cb6126e007941f78c4d2ed7fe" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde-hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random-macro", + "proc-macro-hack", ] [[package]] -name = "constant_time_eq" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cookie-factory" -version = "0.3.0" +name = "const-random-macro" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c750ec12b83377637110d5a57f5ae08e895b06c4b16e2bdbf1a94ef717428c59" +dependencies = [ + "proc-macro-hack", + "rand 0.7.2", +] [[package]] name = "core-foundation" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" dependencies = [ - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys", + "libc", ] [[package]] name = "core-foundation-sys" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" [[package]] name = "criterion" -version = "0.2.11" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938703e165481c8d612ea3479ac8342e5615185db37765162e762ec3523e2fc6" dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools 0.8.0", + "lazy_static", + "num-traits 0.2.6", + "rand_core 0.5.1", + "rand_os", + "rand_xoshiro", + "rayon", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", ] [[package]] name = "criterion-plot" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-channel" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eccdc6ce8bbe352ca89025bee672aa6d24f4eb8c53e3a8b5d1bc58011da072a2" dependencies = [ - "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cast", + "itertools 0.8.0", ] [[package]] name = "crossbeam-deque" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" dependencies = [ - "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch", + "crossbeam-utils 0.6.6", ] [[package]] name = "crossbeam-deque" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" dependencies = [ - "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch", + "crossbeam-utils 0.6.6", ] [[package]] name = "crossbeam-epoch" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.11", + "cfg-if", + "crossbeam-utils 0.6.6", + "lazy_static", + "memoffset", + "scopeguard 1.0.0", ] [[package]] name = "crossbeam-queue" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6", ] [[package]] name = "crossbeam-utils" -version = "0.6.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" [[package]] name = "crossbeam-utils" -version = "0.7.0" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "lazy_static", ] [[package]] name = "crunchy" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-mac" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" dependencies = [ - "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.0", + "subtle 1.0.0", ] [[package]] name = "csv" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" dependencies = [ - "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", ] [[package]] name = "csv-core" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] name = "ct-logs" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" dependencies = [ - "sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sct", ] [[package]] name = "ctr" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" dependencies = [ - "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-cipher-trait", + "stream-cipher", ] [[package]] @@ -689,1870 +910,1920 @@ name = "ctrlc" version = "1.1.1" source = "git+https://github.com/paritytech/rust-ctrlc.git#b523017108bb2d571a7a69bd97bc406e63bc7a9d" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "libc", + "winapi 0.2.8", +] + +[[package]] +name = "derive_more" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbe9f11be34f800b3ecaaed0ec9ec2e015d1d0ba0c8644c1310f73d6e8994615" +dependencies = [ + "proc-macro2 0.4.20", + "quote 0.6.8", + "rustc_version", + "syn 0.15.26", ] [[package]] name = "derive_more" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "proc-macro2 0.4.20", + "quote 0.6.8", + "regex", + "rustc_version", + "syn 0.15.26", ] [[package]] name = "difference" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" [[package]] name = "digest" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.9.0", ] [[package]] name = "digest" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.0", ] [[package]] name = "dir" version = "0.1.2" dependencies = [ - "app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "home 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", + "app_dirs", + "ethereum-types", + "home", + "journaldb", ] [[package]] name = "docopt" -version = "1.1.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2906c2579b5b7207fc1e328796a9a8835dc44e22dbe8e460b1d636f9a7b225" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "regex", + "serde", + "serde_derive", + "strsim 0.7.0", ] [[package]] name = "edit-distance" -version = "2.1.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd26878c3d921f89797a4e1a1711919f999a9f6946bb6f5a4ffda126d297b7e" [[package]] name = "eip-152" version = "0.1.0" dependencies = [ - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref", + "criterion", + "rustc-hex 2.0.1", ] [[package]] name = "eip-712" -version = "0.1.0" +version = "0.1.1" dependencies = [ - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lunarity-lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "validator_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi", + "ethereum-types", + "failure", + "indexmap", + "itertools 0.7.8", + "keccak-hash", + "lazy_static", + "lunarity-lexer", + "regex", + "rustc-hex 2.0.1", + "serde", + "serde_derive", + "serde_json", + "validator", + "validator_derive", ] [[package]] name = "either" -version = "1.5.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" [[package]] name = "elastic-array" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "073be79b6538296faf81c631872676600616073817dd9a440c477ad09b408983" dependencies = [ - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", + "heapsize", ] [[package]] -name = "enclose" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "enclose" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "engine" +version = "0.1.0" +dependencies = [ + "client-traits", + "common-types", + "ethcore-accounts", + "ethcore-blockchain", + "ethcore-builtin", + "ethereum-types", + "ethkey", + "log", + "machine", + "parity-bytes", + "parity-crypto", + "vm", +] [[package]] name = "enum_primitive" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" dependencies = [ - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.43", ] [[package]] name = "env_logger" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "humantime", + "log", + "regex", + "termcolor", ] [[package]] name = "env_logger" -version = "0.7.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "humantime", + "log", + "regex", + "termcolor", ] [[package]] name = "error-chain" -version = "0.12.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "eth-secp256k1" -version = "0.5.7" -source = "git+https://github.com/paritytech/rust-secp256k1#ccc06e7480148b723eb44ac56cf4d20eec380b6f" -dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" [[package]] name = "ethabi" -version = "6.1.0" +version = "9.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965126c64662832991f5a748893577630b558e47fa94e7f35aefcd20d737cef7" dependencies = [ - "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain", + "ethereum-types", + "rustc-hex 2.0.1", + "serde", + "serde_derive", + "serde_json", + "tiny-keccak", ] [[package]] name = "ethabi-contract" -version = "6.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf407dce0290374bfbb1528493bc14320e663f75856b73a5b76262d8e2cec3c9" [[package]] name = "ethabi-derive" -version = "6.0.2" +version = "9.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0753d4f9e1dba99450da5f2400b20527702ae8ce0309a5f7c239d305539884" dependencies = [ - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "ethabi", + "heck", + "proc-macro2 0.4.20", + "quote 0.6.8", + "syn 0.15.26", ] [[package]] name = "ethash" version = "1.12.0" dependencies = [ - "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (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.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "criterion", + "either", + "ethereum-types", + "keccak-hash", + "log", + "memmap", + "parking_lot 0.9.0", + "primal", + "rustc-hex 1.0.0", + "serde_json", + "static_assertions 0.3.3", + "tempdir", +] + +[[package]] +name = "ethash-engine" +version = "0.1.0" +dependencies = [ + "block-reward", + "common-types", + "engine", + "ethash", + "ethcore", + "ethereum-types", + "ethjson", + "keccak-hash", + "log", + "machine", + "macros", + "rlp", + "spec", + "tempdir", + "unexpected", ] [[package]] name = "ethbloom" -version = "0.5.3" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cfe1c169414b709cf28aa30c74060bdb830a03a8ba473314d079ac79d80a5f" dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types-serialize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.2.2", + "fixed-hash", + "impl-rlp", + "impl-serde", + "tiny-keccak", ] [[package]] name = "ethcore" version = "1.12.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "blooms-db 0.1.0", - "bn 0.4.4 (git+https://github.com/paritytech/bn)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "eip-152 0.1.0", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.12.0", - "ethcore-accounts 0.1.0", - "ethcore-blockchain 0.1.0", - "ethcore-bloom-journal 0.1.0", - "ethcore-call-contract 0.1.0", - "ethcore-db 0.1.0", - "ethcore-io 1.12.0", - "ethcore-miner 1.12.0", - "ethcore-stratum 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "ethkey 0.3.0", - "evm 0.1.0", - "fetch 0.1.0", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.1.1", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "len-caching-lock 0.1.1", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "macros 0.1.0", - "memory-cache 0.1.0", - "memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie-ethereum 0.1.0", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_compress 0.1.0", - "rlp_derive 0.1.0", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "stats 0.1.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "time-utils 0.1.0", - "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-standardmap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "triehash-ethereum 0.2.0", - "unexpected 0.1.0", - "using_queue 0.1.0", - "vm 0.1.0", - "wasm 0.1.0", + "account-db", + "account-state", + "ansi_term", + "basic-authority", + "blooms-db", + "client-traits", + "common-types", + "criterion", + "engine", + "env_logger 0.5.13", + "ethash", + "ethcore-accounts", + "ethcore-blockchain", + "ethcore-builtin", + "ethcore-call-contract", + "ethcore-db", + "ethcore-io", + "ethcore-miner", + "ethcore-stratum", + "ethereum-types", + "ethjson", + "evm", + "executive-state", + "fetch", + "futures", + "hash-db", + "itertools 0.5.10", + "journaldb", + "keccak-hash", + "kvdb", + "kvdb-memorydb", + "kvdb-rocksdb", + "lazy_static", + "log", + "machine", + "macros", + "memory-cache", + "parity-bytes", + "parity-crypto", + "parity-runtime", + "parking_lot 0.9.0", + "patricia-trie-ethereum", + "pod", + "rand 0.7.2", + "rand_xorshift 0.2.0", + "rayon", + "registrar", + "rlp", + "rustc-hex 2.0.1", + "scopeguard 1.0.0", + "serde", + "serde_derive", + "serde_json", + "snapshot", + "spec", + "state-db", + "stats", + "tempdir", + "trace", + "trace-time", + "trie-db", + "trie-standardmap", + "trie-vm-factories", + "triehash-ethereum", + "unexpected", + "using_queue", + "verification", + "vm", ] [[package]] name = "ethcore-accounts" version = "0.1.0" dependencies = [ - "common-types 0.1.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "ethstore 0.2.1", - "fake-hardware-wallet 0.0.1", - "hardware-wallet 1.12.0", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types", + "ethkey", + "ethstore", + "log", + "parity-crypto", + "parking_lot 0.9.0", + "serde", + "serde_derive", + "serde_json", + "tempdir", ] [[package]] name = "ethcore-blockchain" version = "0.1.0" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blooms-db 0.1.0", - "common-types 0.1.0", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-db 0.1.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_compress 0.1.0", - "rlp_derive 0.1.0", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "triehash-ethereum 0.2.0", + "ansi_term", + "blooms-db", + "common-types", + "env_logger 0.5.13", + "ethcore-db", + "ethereum-types", + "itertools 0.5.10", + "keccak-hash", + "kvdb", + "kvdb-memorydb", + "log", + "parity-bytes", + "parity-crypto", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "rand 0.7.2", + "rayon", + "rlp", + "rlp_compress", + "rlp_derive", + "rustc-hex 1.0.0", + "tempdir", + "triehash-ethereum", ] [[package]] name = "ethcore-bloom-journal" version = "0.1.0" dependencies = [ - "siphasher 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "siphasher 0.3.0", +] + +[[package]] +name = "ethcore-builtin" +version = "0.1.0" +dependencies = [ + "bn", + "byteorder", + "common-types", + "eip-152", + "ethereum-types", + "ethjson", + "hex-literal", + "keccak-hash", + "log", + "macros", + "num", + "parity-bytes", + "parity-crypto", ] [[package]] name = "ethcore-call-contract" version = "0.1.0" dependencies = [ - "common-types 0.1.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "ethereum-types", + "parity-bytes", ] [[package]] name = "ethcore-db" version = "0.1.0" dependencies = [ - "common-types 0.1.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_derive 0.1.0", + "common-types", + "ethereum-types", + "kvdb", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "rlp", + "rlp_derive", ] [[package]] name = "ethcore-io" version = "1.12.0" dependencies = [ - "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "timer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3", + "fnv", + "futures", + "log", + "mio", + "num_cpus", + "parking_lot 0.9.0", + "slab 0.4.1", + "time", + "timer", + "tokio", ] [[package]] name = "ethcore-light" version = "1.12.0" dependencies = [ - "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-blockchain 0.1.0", - "ethcore-db 0.1.0", - "ethcore-io 1.12.0", - "ethcore-network 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failsafe 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fastmap 0.1.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode", + "client-traits", + "common-types", + "derive_more 0.14.0", + "engine", + "ethcore", + "ethcore-blockchain", + "ethcore-db", + "ethcore-io", + "ethcore-miner", + "ethcore-network", + "ethereum-types", + "executive-state", + "failsafe", + "fastmap", + "futures", + "hash-db", + "journaldb", + "keccak-hash", "keccak-hasher 0.1.1", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-cache 0.1.0", - "memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie-ethereum 0.1.0", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_derive 0.1.0", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "stats 0.1.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "triehash-ethereum 0.2.0", - "vm 0.1.0", + "kvdb", + "kvdb-memorydb", + "log", + "machine", + "memory-cache", + "memory-db", + "parity-bytes", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "patricia-trie-ethereum", + "rand 0.7.2", + "rlp", + "rlp_derive", + "serde", + "serde_derive", + "smallvec 0.6.10", + "spec", + "stats", + "tempdir", + "trie-db", + "triehash-ethereum", + "verification", + "vm", ] [[package]] name = "ethcore-logger" version = "1.12.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "arrayvec 0.4.11", + "atty", + "env_logger 0.5.13", + "lazy_static", + "log", + "parking_lot 0.9.0", + "regex", + "time", ] [[package]] name = "ethcore-miner" version = "1.12.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethash 1.12.0", - "ethcore-call-contract 0.1.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "fetch 0.1.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "price-info 1.12.0", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "transaction-pool 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "common-types", + "env_logger 0.5.13", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethash", + "ethcore-call-contract", + "ethereum-types", + "fetch", + "futures", + "hyper", + "keccak-hash", + "linked-hash-map", + "log", + "parity-crypto", + "parity-runtime", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "price-info", + "registrar", + "rlp", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "trace-time", + "transaction-pool", + "url 2.1.0", ] [[package]] name = "ethcore-network" version = "1.12.0" dependencies = [ - "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-io 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_matches", + "derive_more 0.14.0", + "ethcore-io", + "ethereum-types", + "ipnetwork", + "lazy_static", + "libc", + "parity-crypto", + "parity-snappy", + "rlp", + "semver", + "serde", + "serde_derive", ] [[package]] name = "ethcore-network-devp2p" version = "1.12.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-io 1.12.0", - "ethcore-network 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "igd 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-path 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "assert_matches", + "bytes", + "env_logger 0.5.13", + "ethcore-io", + "ethcore-network", + "ethereum-types", + "igd", + "ipnetwork", + "keccak-hash", + "libc", + "log", + "lru-cache", + "mio", + "natpmp", + "parity-bytes", + "parity-crypto", + "parity-path", + "parity-snappy", + "parking_lot 0.9.0", + "rand 0.7.2", + "rlp", + "rustc-hex 1.0.0", + "serde", + "serde_json", + "slab 0.2.0", + "tempdir", + "tiny-keccak", ] [[package]] name = "ethcore-private-tx" version = "1.0.0" dependencies = [ - "common-types 0.1.0", - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-call-contract 0.1.0", - "ethcore-io 1.12.0", - "ethcore-miner 1.12.0", - "ethereum-types 0.4.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.29 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie-ethereum 0.1.0", - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp_derive 0.1.0", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "transaction-pool 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "account-state", + "client-traits", + "common-types", + "derive_more 0.14.0", + "env_logger 0.5.13", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethcore-call-contract", + "ethcore-db", + "ethcore-io", + "ethcore-miner", + "ethereum-types", + "ethjson", + "fetch", + "futures", + "hash-db", + "journaldb", + "keccak-hash", + "keccak-hasher 0.1.1", + "kvdb", + "log", + "machine", + "parity-bytes", + "parity-crypto", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "patricia-trie-ethereum", + "registrar", + "rlp", + "rlp_derive", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "spec", + "state-db", + "time-utils", + "tiny-keccak", + "trace", + "transaction-pool", + "trie-db", + "url 2.1.0", + "vm", ] [[package]] name = "ethcore-secretstore" version = "1.0.0" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-accounts 0.1.0", - "ethcore-call-contract 0.1.0", - "ethcore-sync 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parking_lot 0.7.1 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (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.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "env_logger 0.5.13", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethereum-types", + "ethkey", + "futures", + "hyper", + "jsonrpc-server-utils", + "keccak-hash", + "kvdb", + "kvdb-rocksdb", + "lazy_static", + "log", + "parity-bytes", + "parity-crypto", + "parity-runtime", + "parking_lot 0.9.0", + "percent-encoding 2.1.0", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "tempdir", + "tiny-keccak", + "tokio", + "tokio-io", + "tokio-service", + "url 2.1.0", ] [[package]] name = "ethcore-service" version = "0.1.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-blockchain 0.1.0", - "ethcore-db 0.1.0", - "ethcore-io 1.12.0", - "ethcore-private-tx 1.0.0", - "ethcore-sync 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "client-traits", + "common-types", + "ethcore", + "ethcore-blockchain", + "ethcore-db", + "ethcore-io", + "ethcore-private-tx", + "ethcore-sync", + "ethereum-types", + "kvdb", + "kvdb-rocksdb", + "log", + "snapshot", + "spec", + "tempdir", + "trace-time", ] [[package]] name = "ethcore-stratum" version = "1.12.0" dependencies = [ - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-tcp-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13", + "ethereum-types", + "jsonrpc-core", + "jsonrpc-tcp-server", + "keccak-hash", + "log", + "parking_lot 0.9.0", + "tokio", + "tokio-io", ] [[package]] name = "ethcore-sync" version = "1.12.0" dependencies = [ - "common-types 0.1.0", - "enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.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", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "ethstore 0.2.1", - "fastmap 0.1.0", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hasher 0.1.1", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "macros 0.1.0", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "triehash-ethereum 0.2.0", + "client-traits", + "common-types", + "engine", + "enum_primitive", + "env_logger 0.5.13", + "ethcore", + "ethcore-io", + "ethcore-light", + "ethcore-network", + "ethcore-network-devp2p", + "ethcore-private-tx", + "ethereum-types", + "fastmap", + "futures", + "indexmap", + "keccak-hash", + "kvdb-memorydb", + "log", + "machine", + "macros", + "parity-bytes", + "parity-crypto", + "parity-runtime", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "rand 0.7.2", + "rand_xorshift 0.2.0", + "rlp", + "rustc-hex 1.0.0", + "snapshot", + "spec", + "trace-time", + "triehash-ethereum", ] [[package]] name = "ethereum-types" -version = "0.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)", - "ethbloom 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types-serialize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "ethereum-types-serialize" -version = "0.2.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba744248e3553a393143d5ebb68939fc3a4ec0c22a269682535f5ffe7fed728c" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "ethbloom", + "fixed-hash", + "impl-rlp", + "impl-serde", + "primitive-types", + "uint", ] [[package]] name = "ethjson" version = "0.1.0" dependencies = [ - "ethereum-types 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)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types", + "macros", + "rustc-hex 1.0.0", + "serde", + "serde_json", ] [[package]] name = "ethkey" -version = "0.3.0" +version = "0.4.0" dependencies = [ - "edit-distance 2.1.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.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memzero 0.1.0", - "parity-crypto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wordlist 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "edit-distance", + "log", + "parity-crypto", + "parity-wordlist", + "serde", + "serde_derive", ] [[package]] name = "ethkey-cli" version = "0.1.0" dependencies = [ - "docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "panic_hook 0.1.0", - "parity-wordlist 1.3.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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "docopt", + "env_logger 0.5.13", + "ethkey", + "panic_hook", + "parity-crypto", + "parity-wordlist", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "threadpool", ] [[package]] name = "ethstore" version = "0.2.1" dependencies = [ - "dir 0.1.2", - "ethereum-types 0.4.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)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wordlist 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dir", + "ethereum-types", + "ethkey", + "itertools 0.5.10", + "libc", + "log", + "matches", + "parity-crypto", + "parity-wordlist", + "parking_lot 0.9.0", + "rand 0.7.2", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "smallvec 0.6.10", + "tempdir", + "time", + "tiny-keccak", ] [[package]] name = "ethstore-cli" version = "0.1.1" dependencies = [ - "dir 0.1.2", - "docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethstore 0.2.1", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "panic_hook 0.1.0", - "parking_lot 0.7.1 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dir", + "docopt", + "env_logger 0.5.13", + "ethkey", + "ethstore", + "num_cpus", + "panic_hook", + "parity-crypto", + "parking_lot 0.9.0", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "tempdir", ] [[package]] name = "evm" version = "0.1.0" dependencies = [ - "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-cache 0.1.0", - "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", + "bit-set", + "criterion", + "ethereum-types", + "hex-literal", + "keccak-hash", + "lazy_static", + "log", + "memory-cache", + "parity-bytes", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "rustc-hex 1.0.0", + "vm", ] [[package]] name = "evmbin" version = "0.1.0" dependencies = [ - "common-types 0.1.0", - "docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "evm 0.1.0", - "panic_hook 0.1.0", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", + "account-state", + "common-types", + "criterion", + "docopt", + "env_logger 0.5.13", + "ethcore", + "ethereum-types", + "ethjson", + "evm", + "panic_hook", + "parity-bytes", + "pod", + "rustc-hex 1.0.0", + "serde", + "serde_json", + "spec", + "tempdir", + "trace", + "vm", +] + +[[package]] +name = "executive-state" +version = "0.1.0" +dependencies = [ + "account-db", + "account-state", + "common-types", + "env_logger 0.5.13", + "ethcore", + "ethereum-types", + "evm", + "hash-db", + "keccak-hash", + "keccak-hasher 0.1.1", + "kvdb", + "log", + "machine", + "parity-bytes", + "parity-crypto", + "patricia-trie-ethereum", + "pod", + "rustc-hex 1.0.0", + "spec", + "trace", + "trie-db", + "trie-vm-factories", + "vm", ] [[package]] name = "failsafe" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad3bf1642583ea2f1fa38a1e8546613a7488816941b33e5f0fccceac61879118" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "parking_lot 0.6.4", + "rand 0.5.5", ] [[package]] name = "failure" -version = "0.1.6" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7" dependencies = [ - "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "failure_derive", ] [[package]] name = "failure_derive" -version = "0.1.6" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.20", + "quote 0.6.8", + "syn 0.15.26", + "synstructure 0.10.1", ] [[package]] name = "fake-fetch" version = "0.0.1" dependencies = [ - "fetch 0.1.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fake-hardware-wallet" -version = "0.0.1" -dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", + "fetch", + "futures", + "hyper", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fastmap" version = "0.1.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types", + "plain_hasher", ] [[package]] name = "fdlimit" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "fetch" version = "0.1.0" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper-rustls 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "http", + "hyper", + "hyper-rustls", + "log", + "tokio", + "url 2.1.0", ] [[package]] name = "fixed-hash" -version = "0.2.5" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" dependencies = [ - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "libc", + "rand 0.7.2", + "rustc-hex 2.0.1", + "static_assertions 1.1.0", ] [[package]] name = "fixedbitset" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" [[package]] name = "fnv" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" [[package]] name = "fs-swap" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921d332c89b3b61a826de38c61ee5b6e02c56806cade1b0e5d81bd71f57a71bb" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "libc", + "libloading", + "winapi 0.3.8", ] [[package]] name = "fs_extra" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "fuchsia-zircon-sys", ] [[package]] name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" [[package]] -name = "futures-channel" -version = "0.2.1" +name = "futures-cpupool" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" dependencies = [ - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "num_cpus", ] [[package]] -name = "futures-core" +name = "fxhash" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] -name = "futures-cpupool" -version = "0.1.8" +name = "generic-array" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] -name = "futures-executor" -version = "0.2.1" +name = "generic-array" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f28c2f5bfb5960175af447a2da7c18900693738343dc896ffbcabd9839592" dependencies = [ - "futures-channel 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum", ] [[package]] -name = "futures-io" -version = "0.2.1" +name = "getrandom" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc344b02d3868feb131e8b5fe2b9b0a1cc42942679af493061fc13b853243872" dependencies = [ - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "wasi", ] [[package]] -name = "futures-retry" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-sink" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-util" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "generic-array" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "generic-array" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "getrandom" -version = "0.1.12" +name = "glob" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "globset" -version = "0.4.4" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" dependencies = [ - "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.6.8", + "fnv", + "log", + "memchr", + "regex", ] [[package]] name = "h2" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "bytes", + "fnv", + "futures", + "http", + "indexmap", + "log", + "slab 0.4.1", + "string", + "tokio-io", ] [[package]] name = "hamming" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1" [[package]] -name = "handlebars" -version = "2.0.2" +name = "hash-db" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" [[package]] -name = "hardware-wallet" -version = "1.12.0" +name = "hash256-std-hasher" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16293646125e09e5bc216d9f73fa81ab31c4f97007d56c036bbf15a58e970540" dependencies = [ - "ethereum-types 0.4.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.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 1.7.5 (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)", + "crunchy 0.2.2", ] -[[package]] -name = "hash-db" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "hashbrown" -version = "0.5.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash", + "autocfg", ] [[package]] name = "heapsize" version = "0.4.2" -source = "git+https://github.com/cheme/heapsize.git?branch=ec-macfix#c07ffe843acb9da570682e290a48540741afdce1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" dependencies = [ - "jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[package]] name = "heck" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" dependencies = [ - "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation", ] -[[package]] -name = "hex" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "hex-literal" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" dependencies = [ - "hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "hex-literal-impl", + "proc-macro-hack", ] [[package]] name = "hex-literal-impl" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06095d08c7c05760f11a071b3e1d4c5b723761c01bd8d7201c30a9536668a612" dependencies = [ - "proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hidapi" -version = "0.3.1" -source = "git+https://github.com/paritytech/hidapi-rs#d4d323767d6f27cf5a3d73fbae0b0f2134d579bf" -dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", ] [[package]] name = "hmac" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto-mac 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "home" -version = "0.3.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f127a908633569f208325f86f71255d3363c79721d7f9fe31cd5569908819771" dependencies = [ - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-mac", + "digest 0.8.0", ] [[package]] name = "home" -version = "0.5.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80dff82fb58cfbbc617fb9a9184b010be0529201553cda50ad04372bc2333aff" dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3", + "winapi 0.3.8", ] [[package]] name = "http" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "http-body" -version = "0.1.0" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "fnv", + "itoa", ] [[package]] name = "httparse" -version = "1.3.4" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" [[package]] name = "humantime" -version = "1.3.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" dependencies = [ - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error", ] [[package]] name = "hyper" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (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.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (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.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (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.42 (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.12 (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.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hyper" -version = "0.12.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +version = "0.12.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ebec079129e43af5e234ef36ee3d7e6085687d145b7ea653b262d16c6b65f1" +dependencies = [ + "bytes", + "futures", + "futures-cpupool", + "h2", + "http", + "httparse", + "iovec", + "itoa", + "log", + "net2", + "time", + "tokio", + "tokio-executor", + "tokio-io", + "tokio-reactor", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer 0.2.11", + "want", ] [[package]] name = "hyper-rustls" -version = "0.16.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66a4973381d01141ed0a4f20070d47a232c764642dd6217d3d93f7a1f952ea5" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "ct-logs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-rustls 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "ct-logs", + "futures", + "hyper", + "rustls", + "rustls-native-certs", + "tokio-io", + "tokio-rustls", + "webpki", ] [[package]] name = "idna" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] name = "idna" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", + "unicode-bidi", + "unicode-normalization", ] [[package]] name = "if_chain" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bac95d9aa0624e7b78187d6fb8ab012b41d9f6f54b1bcb61e61c4845f8357ec" [[package]] name = "igd" -version = "0.7.1" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96f0f346ff76d5143011b2de50fbe72c3e521304868dfbd0d781b4f262a75dd5" +dependencies = [ + "attohttpc", + "bytes", + "http", + "log", + "rand 0.4.6", + "url 1.7.1", + "xmltree", +] + +[[package]] +name = "impl-codec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f7a72f11830b52333f36e3b09a288333888bf54380fd0ac0790a3c31ab0f3c5" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.11 (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)", + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef5550a42e3740a0e71f909d4c861056a284060af885ae7aa6242820f920d9d" +dependencies = [ + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn 1.0.14", ] [[package]] name = "indexmap" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "instant-seal" +version = "0.1.0" +dependencies = [ + "client-traits", + "common-types", + "engine", + "ethcore", + "ethereum-types", + "ethjson", + "keccak-hash", + "machine", + "rlp", + "spec", + "trace", +] [[package]] name = "integer-encoding" -version = "1.0.7" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26746cbc2e680af687e88d717f20ff90079bd10fc984ad57d277cd0e37309fa5" [[package]] name = "interleaved-ordered" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141340095b15ed7491bd3d4ced9d20cebfb826174b6bb03386381f62b01e3d77" [[package]] name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.2.8", ] [[package]] name = "ipnetwork" version = "0.12.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70783119ac90828aaba91eae39db32c6c1b8838deea3637e5238efa0130801ab" [[package]] name = "itertools" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc" dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itertools" -version = "0.7.11" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450" dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itertools" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" dependencies = [ - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "either", ] [[package]] name = "itoa" -version = "0.4.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" [[package]] name = "jemalloc-sys" -version = "0.1.8" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "fs_extra", + "libc", ] [[package]] name = "jemallocator" -version = "0.1.9" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69" dependencies = [ - "jemalloc-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "jemalloc-sys", + "libc", ] [[package]] -name = "jni" -version = "0.11.0" +name = "jobserver" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f74e73053eaf95399bf926e48fc7a2a3ce50bd0eaaa2357d391e95b2dcdd4f10" dependencies = [ - "cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "combine 3.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "log", + "rand 0.7.2", ] -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "journaldb" version = "0.2.0" dependencies = [ - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fastmap 0.1.0", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13", + "ethereum-types", + "fastmap", + "hash-db", + "keccak-hash", "keccak-hasher 0.1.1", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "kvdb", + "kvdb-memorydb", + "log", + "memory-db", + "parity-bytes", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "rlp", +] + +[[package]] +name = "js-sys" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7889c7c36282151f6bf465be4700359318aef36baa951462382eae49e9577cf9" +dependencies = [ + "wasm-bindgen", ] [[package]] name = "jsonrpc-core" -version = "10.1.0" +version = "14.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe3b688648f1ef5d5072229e2d672ecb92cbff7d1c79bcf3fd5898f3f3df0970" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "log", + "serde", + "serde_derive", + "serde_json", ] [[package]] name = "jsonrpc-derive" -version = "10.1.0" +version = "14.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8609af8f63b626e8e211f52441fcdb6ec54f1a446606b10d5c89ae9bf8a20058" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn 1.0.14", ] [[package]] name = "jsonrpc-http-server" -version = "10.1.0" +version = "14.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d83d348120edee487c560b7cdd2565055d61cda053aa0d0ef0f8b6a18429048" dependencies = [ - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper", + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "net2", + "parking_lot 0.9.0", + "unicase 2.2.0", ] [[package]] name = "jsonrpc-ipc-server" -version = "10.1.0" +version = "14.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2f793f6eddff0c96a96f3e144efc74930fd1343c1cc0f6302796b2d33bc35f" dependencies = [ - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-tokio-ipc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "parity-tokio-ipc", + "parking_lot 0.9.0", + "tokio-service", ] [[package]] name = "jsonrpc-pubsub" -version = "10.1.0" +version = "14.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3453625f0f0f5cd6d6776d389d73b7d70fcc98620b7cbb1cbbb1f6a36e95f39a" dependencies = [ - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core", + "log", + "parking_lot 0.9.0", + "serde", ] [[package]] name = "jsonrpc-server-utils" -version = "10.1.0" +version = "14.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87bc3c0a9a282211b2ec14abb3e977de33016bbec495332e9f7be858de7c5117" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "globset", + "jsonrpc-core", + "lazy_static", + "log", + "tokio", + "tokio-codec", + "unicase 2.2.0", ] [[package]] name = "jsonrpc-tcp-server" -version = "10.1.0" +version = "14.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7807563cd721401285b59b54358f5b2325b4de6ff6f1de5494a5879e890fc1" dependencies = [ - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "parking_lot 0.9.0", + "tokio-service", ] [[package]] name = "jsonrpc-ws-server" -version = "10.1.0" +version = "14.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b34faa167c3ac9705aeecb986c0da6056529f348425dbe0441db60a2c4cc41d1" dependencies = [ - "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-server-utils 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-ws 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "parking_lot 0.9.0", + "slab 0.4.1", + "ws", ] [[package]] name = "keccak-hash" -version = "0.1.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e563fa6fe52b2686094846118bf2cb2e6f75e6b8cec6c3aba09be8e835c7f998" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types", + "tiny-keccak", ] [[package]] name = "keccak-hasher" version = "0.1.1" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types", + "hash-db", + "plain_hasher", + "tiny-keccak", ] [[package]] -name = "kernel32-sys" -version = "0.2.2" +name = "keccak-hasher" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf18164fd7ce989041f8fc4a1ae72a8bd1bec3575f2aeaf1d4968fc053aabef" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "hash256-std-hasher", + "tiny-keccak", ] [[package]] -name = "kvdb" -version = "0.1.0" +name = "kernel32-sys" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ - "elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] -name = "kvdb-memorydb" -version = "0.1.2" +name = "kvdb" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8396be0e5561ccd1bf7ff0b2007487cdd7a87a056873fe6ea906b35d4dbf7ed8" dependencies = [ - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes", + "parity-util-mem 0.4.2", + "smallvec 1.0.0", ] [[package]] -name = "kvdb-rocksdb" -version = "0.1.5" +name = "kvdb-memorydb" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25ef14155e418515c4839e9144c839de3506e68946f255a32b7f166095493d" dependencies = [ - "elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fs-swap 0.2.4 (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 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rocksdb 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kvdb", + "parity-util-mem 0.4.2", + "parking_lot 0.9.0", ] [[package]] -name = "language-tags" -version = "0.2.2" +name = "kvdb-rocksdb" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lapin" -version = "0.28.3" -source = "registry+https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git" +checksum = "5a1053e90a54421a842b6bf5d0e4a5cb5364c0bf570f713c58e44a9906f501d9" dependencies = [ - "amq-protocol 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "amq-protocol-codegen 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "fs-swap", + "interleaved-ordered", + "kvdb", + "log", + "num_cpus", + "owning_ref 0.4.0", + "parity-util-mem 0.4.2", + "parking_lot 0.9.0", + "regex", + "rocksdb", + "smallvec 1.0.0", ] -[[package]] -name = "lapin-futures" -version = "0.28.3" -source = "registry+https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lapin 0.28.3 (registry+https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddba4c30a78328befecec92fc94970e53b3ae385827d28620f0f5bb2493081e0" [[package]] name = "len-caching-lock" version = "0.1.1" dependencies = [ - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lexical-core" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0", ] [[package]] name = "libc" -version = "0.2.62" +version = "0.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" [[package]] name = "libloading" -version = "0.5.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "winapi 0.3.8", ] [[package]] -name = "libusb" -version = "0.3.0" -source = "git+https://github.com/paritytech/libusb-rs#442708954a720bc89a9cf41e7be021a778bdbc27" -dependencies = [ - "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "libusb-sys 0.2.5 (git+https://github.com/paritytech/libusb-sys)", -] - -[[package]] -name = "libusb-sys" -version = "0.2.5" -source = "git+https://github.com/paritytech/libusb-sys#79e570b00379e1550fb728f2fbd2a083dc99bda6" +name = "librocksdb-sys" +version = "6.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0785e816e1e11e7599388a492c61ef80ddc2afc91e313e61662cce537809be" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "bindgen", + "cc", + "glob", + "libc", ] [[package]] name = "linked-hash-map" -version = "0.3.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" [[package]] -name = "linked-hash-map" -version = "0.5.2" +name = "lock_api" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a" +dependencies = [ + "owning_ref 0.3.3", + "scopeguard 0.3.3", +] [[package]] -name = "local-encoding" -version = "0.2.0" +name = "lock_api" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "skeptic 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0", ] [[package]] -name = "lock_api" -version = "0.1.5" +name = "log" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", ] [[package]] -name = "lock_api" -version = "0.3.1" +name = "logos" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60ca690691528b32832c7e8aaae8ae1edcdee4e9ffde55b2d31a4795bc7a12d0" dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "logos-derive", + "toolshed", ] [[package]] -name = "log" -version = "0.4.8" +name = "logos-derive" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917dccdd529d5681f3d28b26bcfdafd2ed67fe4f26d15b5ac679f67b55279f3d" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.20", + "quote 0.6.8", + "regex-syntax", + "syn 0.15.26", + "utf8-ranges", ] [[package]] name = "lru-cache" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" dependencies = [ - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map", ] [[package]] name = "lunarity-lexer" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28a5446c03ed5bd4ae2cca322c4c84d9bd9741b6788f75c404719474cb63d3b7" +dependencies = [ + "logos", +] + +[[package]] +name = "machine" +version = "0.1.0" dependencies = [ - "toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "account-state", + "client-traits", + "common-types", + "criterion", + "crossbeam-utils 0.6.6", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethcore-builtin", + "ethcore-call-contract", + "ethcore-io", + "ethereum-types", + "ethjson", + "evm", + "keccak-hash", + "log", + "lru-cache", + "macros", + "parity-bytes", + "parity-crypto", + "parking_lot 0.9.0", + "rlp", + "rustc-hex 1.0.0", + "spec", + "state-db", + "tempdir", + "trace", + "trie-vm-factories", + "vm", ] [[package]] @@ -2560,2133 +2831,2268 @@ name = "macros" version = "0.1.0" [[package]] -name = "maplit" -version = "1.0.2" +name = "malloc_size_of_derive" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37c5d4cd9473c5f4c9c111f033f15d4df9bd378fdf615944e360a4f55a05f0b" +dependencies = [ + "proc-macro2 1.0.8", + "syn 1.0.14", + "synstructure 0.12.3", +] [[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "memmap" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.8", ] [[package]] name = "memoffset" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version", ] [[package]] name = "memory-cache" version = "0.1.0" dependencies = [ - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lru-cache", + "parity-util-mem 0.3.0", ] [[package]] name = "memory-db" -version = "0.11.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "828bdf600636e90c56652689f7c3823ae2072104e4b0b5e83ea984f592f12ab9" dependencies = [ - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", + "ahash", + "hash-db", + "hashbrown", + "parity-util-mem 0.3.0", ] [[package]] name = "memory_units" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memzero" -version = "0.1.0" +checksum = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" [[package]] name = "migration-rocksdb" version = "0.1.0" dependencies = [ - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "macros 0.1.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "kvdb", + "kvdb-rocksdb", + "log", + "macros", + "tempdir", ] [[package]] name = "mime" -version = "0.3.14" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a907b83e7b9e987032439a387e187119cddafc92d5c2aaeb1d92580a793f630" +dependencies = [ + "unicase 2.2.0", +] [[package]] name = "mime_guess" -version = "2.0.1" +version = "2.0.0-alpha.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" dependencies = [ - "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mime", + "phf", + "phf_codegen", + "unicase 1.4.2", ] [[package]] name = "mio" version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (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.62 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.1", + "net2", + "slab 0.4.1", + "winapi 0.2.8", ] [[package]] name = "mio-extras" version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" dependencies = [ - "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell", + "log", + "mio", + "slab 0.4.1", ] [[package]] name = "mio-named-pipes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "mio", + "miow 0.3.3", + "winapi 0.3.8", ] [[package]] name = "mio-uds" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" dependencies = [ - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec", + "libc", + "mio", ] [[package]] name = "miow" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (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)", + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", ] [[package]] name = "miow" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" dependencies = [ - "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2", + "winapi 0.3.8", ] [[package]] name = "multibase" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6" dependencies = [ - "base-x 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "base-x", ] [[package]] name = "multihash" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62469025f45dee2464ef9fc845f4683c543993792c1993e7d903c17a4546b74" dependencies = [ - "sha1 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha1", + "sha2 0.7.1", + "tiny-keccak", ] [[package]] name = "nan-preserving-float" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f" [[package]] -name = "native-tls" -version = "0.2.3" +name = "natpmp" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d85b74917d95eab8b26ab6fe28e21d3fede3a614411ca4d3b01265c05bf86a12" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.52 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", ] [[package]] name = "net2" version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "libc", + "winapi 0.3.8", ] [[package]] name = "node-filter" version = "1.12.0" dependencies = [ - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.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.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "client-traits", + "common-types", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethcore-io", + "ethcore-network", + "ethcore-network-devp2p", + "ethereum-types", + "kvdb-memorydb", + "log", + "lru-cache", + "parking_lot 0.9.0", + "spec", + "tempdir", ] [[package]] name = "nodrop" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "nom" -version = "2.2.1" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" [[package]] name = "nom" version = "4.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" dependencies = [ - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", + "version_check", ] [[package]] -name = "nom" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "null-engine" +version = "0.1.0" dependencies = [ - "lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "block-reward", + "common-types", + "engine", + "ethereum-types", + "ethjson", + "machine", ] [[package]] name = "num" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" dependencies = [ - "num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint", + "num-integer", + "num-iter", + "num-traits 0.2.6", ] [[package]] name = "num-bigint" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" dependencies = [ - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-bigint" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer", + "num-traits 0.2.6", + "rand 0.4.6", + "rustc-serialize", ] [[package]] name = "num-integer" -version = "0.1.41" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6", ] [[package]] name = "num-iter" -version = "0.1.39" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer", + "num-traits 0.2.6", ] [[package]] name = "num-traits" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" dependencies = [ - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6", ] [[package]] name = "num-traits" -version = "0.2.8" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" [[package]] name = "num_cpus" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "number_prefix" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee" dependencies = [ - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6", ] [[package]] name = "ole32-sys" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "openssl" -version = "0.10.25" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.52 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" [[package]] name = "openssl-probe" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "openssl-sys" -version = "0.9.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "order-stat" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ordered-float" -version = "0.5.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb" [[package]] name = "ordermap" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" + +[[package]] +name = "owning_ref" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" +dependencies = [ + "stable_deref_trait", +] [[package]] name = "owning_ref" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "stable_deref_trait", ] [[package]] name = "panic_hook" version = "0.1.0" dependencies = [ - "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", ] [[package]] name = "parity-bytes" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "parity-clib" -version = "1.12.0" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "panic_hook 0.1.0", - "parity-ethereum 2.5.9", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "fa5168b4cf41f3835e4bc6ffb32f51bc9365dc50cb351904595b3931d917fd0c" [[package]] name = "parity-crypto" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27a9c2b525c93d717a234eb220c26474f8d97b08ac50d79faeac4cb6c74bf0b9" dependencies = [ - "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "block-modes 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scrypt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "aes", + "aes-ctr", + "block-modes", + "digest 0.8.0", + "ethereum-types", + "hmac", + "lazy_static", + "parity-secp256k1", + "pbkdf2", + "rand 0.7.2", + "ripemd160", + "rustc-hex 2.0.1", + "scrypt", + "sha2 0.8.0", + "subtle 2.1.0", + "tiny-keccak", + "zeroize", ] [[package]] name = "parity-daemonize" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b1910b2793ff52713fca0a4ee92544ebec59ccd218ea74560be6f947b4ca77" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "failure", + "libc", + "log", + "mio", ] [[package]] name = "parity-ethereum" -version = "2.5.9" -dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "blooms-db 0.1.0", - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cli-signer 1.4.0", - "common-types 0.1.0", - "ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)", - "dir 0.1.2", - "docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-accounts 0.1.0", - "ethcore-blockchain 0.1.0", - "ethcore-call-contract 0.1.0", - "ethcore-db 0.1.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-sync 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethkey 0.3.0", - "ethstore 0.2.1", - "fake-fetch 0.0.1", - "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "ipnetwork 0.12.8 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "migration-rocksdb 0.1.0", - "node-filter 1.12.0", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "panic_hook 0.1.0", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-hash-fetch 1.12.0", - "parity-ipfs-api 1.12.0", - "parity-local-store 0.1.0", - "parity-path 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rabbitmq 0.1.0", - "parity-rpc 1.12.0", - "parity-runtime 0.1.0", - "parity-updater 1.12.0", - "parity-version 2.5.9", - "parity-whisper 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "registrar 0.0.1", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rpassword 1.0.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.3 (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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (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.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +version = "2.7.2" +dependencies = [ + "ansi_term", + "atty", + "blooms-db", + "clap", + "cli-signer", + "client-traits", + "common-types", + "ctrlc", + "dir", + "docopt", + "engine", + "ethabi", + "ethcore", + "ethcore-accounts", + "ethcore-blockchain", + "ethcore-call-contract", + "ethcore-db", + "ethcore-io", + "ethcore-light", + "ethcore-logger", + "ethcore-miner", + "ethcore-network", + "ethcore-private-tx", + "ethcore-secretstore", + "ethcore-service", + "ethcore-sync", + "ethereum-types", + "ethkey", + "ethstore", + "fake-fetch", + "fdlimit", + "futures", + "ipnetwork", + "journaldb", + "jsonrpc-core", + "keccak-hash", + "kvdb", + "kvdb-rocksdb", + "log", + "migration-rocksdb", + "node-filter", + "num_cpus", + "number_prefix", + "panic_hook", + "parity-bytes", + "parity-crypto", + "parity-daemonize", + "parity-hash-fetch", + "parity-ipfs-api", + "parity-local-store", + "parity-path", + "parity-rpc", + "parity-runtime", + "parity-updater", + "parity-util-mem 0.3.0", + "parity-version", + "parking_lot 0.9.0", + "pretty_assertions", + "regex", + "registrar", + "rlp", + "rpassword", + "rustc-hex 1.0.0", + "rustc_version", + "semver", + "serde", + "serde_derive", + "serde_json", + "snapshot", + "spec", + "tempdir", + "term_size", + "textwrap 0.9.0", + "toml 0.4.10", + "verification", + "winapi 0.3.8", ] [[package]] name = "parity-hash-fetch" version = "1.12.0" dependencies = [ - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-fetch 0.0.1", - "fetch 0.1.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (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)", + "common-types", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore-call-contract", + "ethereum-types", + "fake-fetch", + "fetch", + "futures", + "keccak-hash", + "log", + "mime", + "mime_guess", + "parity-bytes", + "parity-runtime", + "parking_lot 0.9.0", + "rand 0.7.2", + "registrar", + "rustc-hex 1.0.0", ] [[package]] name = "parity-ipfs-api" version = "1.12.0" dependencies = [ - "cid 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cid", + "client-traits", + "common-types", + "ethcore", + "ethereum-types", + "jsonrpc-core", + "jsonrpc-http-server", + "multihash", + "parity-bytes", + "rlp", + "unicase 2.2.0", ] [[package]] name = "parity-local-store" version = "0.1.0" dependencies = [ - "common-types 0.1.0", - "ethcore-io 1.12.0", - "ethkey 0.3.0", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "ethcore-io", + "ethkey", + "kvdb", + "kvdb-memorydb", + "log", + "parity-crypto", + "rlp", + "serde", + "serde_derive", + "serde_json", ] [[package]] name = "parity-path" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "home 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "5962540f99d3895d9addf535f37ab1397886bc2c68e59efd040ef458e5f8c3f7" [[package]] -name = "parity-rabbitmq" -version = "0.1.0" -dependencies = [ - "boolinator 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "enclose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-miner 1.12.0", - "ethcore-network 1.12.0", - "ethcore-sync 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-memorydb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kvdb-rocksdb 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lapin-futures 0.28.3 (registry+https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "macros 0.1.0", - "parity-runtime 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "prometheus 0.8.0 (git+https://github.com/pingcap/rust-prometheus.git)", - "rabbitmq_adaptor 0.4.5 (registry+https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git)", - "rlp 0.3.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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", -] - -[[package]] -name = "parity-rocksdb" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "parity-rpc" +version = "1.12.0" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rocksdb-sys 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "account-state", + "ansi_term", + "cid", + "client-traits", + "common-types", + "eip-712", + "engine", + "ethash", + "ethcore", + "ethcore-accounts", + "ethcore-io", + "ethcore-light", + "ethcore-logger", + "ethcore-miner", + "ethcore-network", + "ethcore-private-tx", + "ethcore-sync", + "ethereum-types", + "ethjson", + "ethkey", + "ethstore", + "fake-fetch", + "fastmap", + "fetch", + "futures", + "itertools 0.5.10", + "jsonrpc-core", + "jsonrpc-derive", + "jsonrpc-http-server", + "jsonrpc-ipc-server", + "jsonrpc-pubsub", + "jsonrpc-ws-server", + "keccak-hash", + "log", + "machine", + "macros", + "multihash", + "order-stat", + "parity-bytes", + "parity-crypto", + "parity-runtime", + "parity-updater", + "parity-version", + "parking_lot 0.9.0", + "pretty_assertions", + "rand 0.7.2", + "rand_xorshift 0.2.0", + "rlp", + "rustc-hex 1.0.0", + "semver", + "serde", + "serde_derive", + "serde_json", + "snapshot", + "spec", + "stats", + "tempdir", + "tiny-keccak", + "tokio-timer 0.1.2", + "trace", + "transaction-pool", + "transient-hashmap", + "verification", + "vm", ] [[package]] -name = "parity-rocksdb-sys" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "parity-rpc-client" +version = "1.4.0" dependencies = [ - "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-snappy-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types", + "futures", + "jsonrpc-core", + "jsonrpc-ws-server", + "keccak-hash", + "log", + "matches", + "parity-rpc", + "parking_lot 0.9.0", + "serde", + "serde_json", + "url 2.1.0", ] [[package]] -name = "parity-rpc" -version = "1.12.0" +name = "parity-runtime" +version = "0.1.0" dependencies = [ - "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cid 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "common-types 0.1.0", - "eip-712 0.1.0", - "ethash 1.12.0", - "ethcore 1.12.0", - "ethcore-accounts 0.1.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", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "ethkey 0.3.0", - "ethstore 0.2.1", - "fake-fetch 0.0.1", - "fastmap 0.1.0", - "fetch 0.1.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-derive 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-ipc-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-ws-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "macros 0.1.0", - "multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parity-updater 1.12.0", - "parity-version 2.5.9", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (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)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "stats 0.1.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tiny-keccak 1.5.0 (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 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", + "futures", + "tokio", ] [[package]] -name = "parity-rpc-client" -version = "1.4.0" +name = "parity-scale-codec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f9d99dae413590a5f37e43cd99b94d4e62a244160562899126913ea7108673" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-ws-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-rpc 1.12.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1", + "bitvec", + "byte-slice-cast", + "serde", ] [[package]] -name = "parity-runtime" -version = "0.1.0" +name = "parity-secp256k1" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fca4f82fccae37e8bbdaeb949a4a218a1bbc485d11598f193d2a908042e5fc1" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.5.1", + "cc", + "cfg-if", + "rand 0.7.2", ] [[package]] name = "parity-snappy" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2c5f9d149b13134b8b354d93a92830efcbee6fe5b73a2e6e540fe70d4dd8a63" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-snappy-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "parity-snappy-sys", ] [[package]] name = "parity-snappy-sys" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a413d51e5e1927320c9de992998e4a279dffb8c8a7363570198bd8383e66f1b" dependencies = [ - "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "cmake", + "libc", ] [[package]] name = "parity-tokio-ipc" -version = "0.1.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e57fea504fea33f9fbb5f49f378359030e7e026a6ab849bb9e8f0787376f1bf" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-named-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "libc", + "log", + "mio-named-pipes", + "miow 0.3.3", + "rand 0.7.2", + "tokio", + "tokio-named-pipes", + "tokio-uds", + "winapi 0.3.8", ] [[package]] name = "parity-updater" version = "1.12.0" dependencies = [ - "common-types 0.1.0", - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore 1.12.0", - "ethcore-sync 1.12.0", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-hash-fetch 1.12.0", - "parity-path 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-version 2.5.9", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-version" -version = "2.5.9" + "client-traits", + "common-types", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethcore-sync", + "ethereum-types", + "keccak-hash", + "lazy_static", + "log", + "matches", + "parity-bytes", + "parity-hash-fetch", + "parity-path", + "parity-version", + "parking_lot 0.9.0", + "rand 0.7.2", + "semver", + "target_info", + "tempdir", +] + +[[package]] +name = "parity-util-mem" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8174d85e62c4d615fddd1ef67966bdc5757528891d0742f15b131ad04667b3f9" dependencies = [ - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (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.10 (registry+https://github.com/rust-lang/crates.io-index)", - "vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "ethereum-types", + "jemallocator", + "malloc_size_of_derive", + "parking_lot 0.9.0", + "smallvec 1.0.0", + "winapi 0.3.8", ] [[package]] -name = "parity-wasm" -version = "0.31.3" +name = "parity-util-mem" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b04e4d2588668d5aa93144b3bd719be963542e60042d66c7586ca763838a5b" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "parking_lot 0.9.0", + "smallvec 1.0.0", + "winapi 0.3.8", ] [[package]] -name = "parity-whisper" +name = "parity-util-mem-derive" version = "0.1.0" -dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-network 1.12.0", - "ethereum-types 0.4.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 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-derive 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memzero 0.1.0", - "ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-crypto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "time-utils 0.1.0", - "tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parity-wordlist" -version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "syn 1.0.14", + "synstructure 0.12.3", ] [[package]] -name = "parity-ws" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "parity-version" +version = "2.7.2" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-bytes", + "rlp", + "rustc_version", + "target_info", + "toml 0.4.10", + "vergen", ] [[package]] -name = "parking_lot" -version = "0.6.4" +name = "parity-wasm" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] -name = "parking_lot" -version = "0.7.1" +name = "parity-wordlist" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573d08f0d3bc8a6ffcdac1de2725b5daeed8db26345a9c12d91648e2d6457f3e" dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "rand 0.6.1", ] [[package]] name = "parking_lot" -version = "0.9.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" dependencies = [ - "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.1.4", + "parking_lot_core 0.3.1", ] [[package]] name = "parking_lot" -version = "0.10.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.1", + "parking_lot_core 0.6.2", + "rustc_version", ] [[package]] name = "parking_lot_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (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.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "rand 0.5.5", + "rustc_version", + "smallvec 0.6.10", + "winapi 0.3.8", ] [[package]] name = "parking_lot_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "cfg-if", + "cloudabi", + "libc", + "petgraph", + "redox_syscall", + "rustc_version", + "smallvec 0.6.10", + "thread-id", + "winapi 0.3.8", ] [[package]] name = "patricia-trie-ethereum" version = "0.1.0" dependencies = [ - "elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "journaldb 0.2.0", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion", + "elastic-array", + "ethereum-types", + "hash-db", + "journaldb", + "keccak-hash", "keccak-hasher 0.1.1", - "memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memory-db", + "parity-bytes", + "rlp", + "trie-db", ] [[package]] name = "pbkdf2" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006c038a43a45995a9670da19e67600114740e8511d4333bf97a56e66a7542d9" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-mac 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.9.3", + "byteorder", + "crypto-mac", + "hmac", + "rand 0.5.5", + "sha2 0.8.0", + "subtle 1.0.0", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] -name = "pest" -version = "2.1.2" +name = "petgraph" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" dependencies = [ - "ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fixedbitset", + "ordermap", ] [[package]] -name = "pest_derive" -version = "2.1.0" +name = "phf" +version = "0.7.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec29da322b242f4c3098852c77a0ca261c9c01b806cae85a5572a1eb94db9a6" dependencies = [ - "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_generator 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared", ] [[package]] -name = "pest_generator" -version = "2.1.1" +name = "phf_codegen" +version = "0.7.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d187f00cd98d5afbcd8898f6cf181743a449162aeb329dcd2f3849009e605ad" dependencies = [ - "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_meta 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_generator", + "phf_shared", ] [[package]] -name = "pest_meta" -version = "2.1.2" +name = "phf_generator" +version = "0.7.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03dc191feb9b08b0dc1330d6549b795b9d81aec19efe6b4a45aec8d4caee0c4b" dependencies = [ - "maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared", + "rand 0.5.5", ] [[package]] -name = "petgraph" -version = "0.4.13" +name = "phf_shared" +version = "0.7.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b539898d22d4273ded07f64a05737649dc69095d92cb87c7097ec68e3f150b93" 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)", + "siphasher 0.2.3", + "unicase 1.4.2", ] -[[package]] -name = "pkg-config" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "plain_hasher" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fa6386b1d34aaf0adb9b7dd2885dbe7c34190e6263785e5a7ec2b19044a90f" +dependencies = [ + "crunchy 0.1.6", +] + +[[package]] +name = "pod" +version = "0.1.0" dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "ethereum-types", + "ethjson", + "hash-db", + "itertools 0.8.0", + "keccak-hash", + "keccak-hasher 0.1.1", + "kvdb", + "log", + "macros", + "parity-bytes", + "patricia-trie-ethereum", + "rlp", + "rustc-hex 1.0.0", + "serde", + "trie-db", + "triehash-ethereum", ] [[package]] name = "ppv-lite86" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" [[package]] name = "pretty_assertions" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2412f3332a07c7a2a50168988dcc184f32180a9758ad470390e5f55e089f6b6e" dependencies = [ - "difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "difference", ] [[package]] name = "price-info" version = "1.12.0" dependencies = [ - "fake-fetch 0.0.1", - "fetch 0.1.0", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-runtime 0.1.0", - "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-fetch", + "fetch", + "futures", + "log", + "parity-runtime", + "serde_json", ] [[package]] name = "primal" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e31b86efadeaeb1235452171a66689682783149a6249ff334a2c5d8218d00a4" dependencies = [ - "primal-check 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "primal-estimate 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "primal-sieve 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "primal-check", + "primal-estimate", + "primal-sieve", ] [[package]] name = "primal-bit" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686a64e2f50194c64942992af5799e6b6e8775b8f88c607d72ed0a2fd58b9b21" dependencies = [ - "hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hamming", ] [[package]] name = "primal-check" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e65f96c0a171f887198c274392c99a116ef65aa7f53f3b6d4902f493965c2d1" dependencies = [ - "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer", ] [[package]] name = "primal-estimate" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ea4531dde757b56906493c8604641da14607bf9cdaa80fb9c9cabd2429f8d5" [[package]] name = "primal-sieve" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2d6ed369bb4b0273aeeb43f07c105c0117717cbae827b20719438eb2eb798c" dependencies = [ - "hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "primal-bit 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "primal-estimate 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "hamming", + "primal-bit", + "primal-estimate", + "smallvec 0.6.10", ] [[package]] -name = "proc-macro-hack" -version = "0.5.10" +name = "primitive-types" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0253db64c26d8b4e7896dd2063b516d2a1b9e0a5da26b5b78335f236d1e9522" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "uint", ] [[package]] -name = "proc-macro2" -version = "0.4.30" +name = "proc-macro-crate" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.1", ] [[package]] -name = "proc-macro2" -version = "1.0.4" +name = "proc-macro-hack" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e688f31d92ffd7c1ddc57a1b4e6d773c0f2a14ee437a4b0a4f5a69c80eb221c8" dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn 1.0.14", ] [[package]] -name = "procinfo" -version = "0.4.2" +name = "proc-macro2" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "nom 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "prometheus" -version = "0.8.0" -source = "git+https://github.com/pingcap/rust-prometheus.git#b668f3911d6569de2e1e8b2672fccec1cc8298be" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "procinfo 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0", ] [[package]] -name = "protobuf" -version = "1.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "protobuf" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "pulldown-cmark" -version = "0.0.3" +name = "proc-macro2" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" dependencies = [ - "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0", ] [[package]] name = "pwasm-run-test" version = "0.1.0" dependencies = [ - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.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.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", - "wasm 0.1.0", + "clap", + "env_logger 0.5.13", + "ethereum-types", + "ethjson", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "vm", + "wasm", ] [[package]] name = "pwasm-utils" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9135bed7b452e20dbb395a2d519abaf0c46d60e7ecc02daeeab447d29bada1" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "log", + "parity-wasm", ] [[package]] name = "quick-error" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" [[package]] name = "quote" -version = "0.6.13" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.20", ] [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rabbitmq_adaptor" -version = "0.4.5" -source = "registry+https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git" -dependencies = [ - "amq-protocol 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "config 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "enclose 1.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-retry 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lapin-futures 0.28.3 (registry+https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", ] [[package]] name = "rand" -version = "0.3.23" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi 0.3.8", ] [[package]] name = "rand" -version = "0.4.6" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" dependencies = [ - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi", + "fuchsia-zircon", + "libc", + "rand_core 0.2.2", + "winapi 0.3.8", ] [[package]] name = "rand" -version = "0.5.6" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi", + "fuchsia-zircon", + "libc", + "rand_chacha 0.1.0", + "rand_core 0.3.1", + "rand_hc 0.1.0", + "rand_isaac", + "rand_pcg", + "rand_xorshift 0.1.1", + "rustc_version", + "winapi 0.3.8", ] [[package]] name = "rand" -version = "0.6.5" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "libc", + "rand_chacha 0.2.1", + "rand_core 0.5.1", + "rand_hc 0.2.0", ] [[package]] -name = "rand" -version = "0.7.2" +name = "rand_chacha" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a" dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", + "rustc_version", ] [[package]] name = "rand_chacha" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "c2-chacha", + "rand_core 0.5.1", ] [[package]] -name = "rand_chacha" -version = "0.2.1" +name = "rand_core" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" dependencies = [ - "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", ] [[package]] name = "rand_hc" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] name = "rand_isaac" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] -name = "rand_jitter" -version = "0.1.4" +name = "rand_os" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "rand_core 0.5.1", ] [[package]] -name = "rand_os" -version = "0.1.3" +name = "rand_pcg" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", + "rustc_version", ] [[package]] -name = "rand_pcg" -version = "0.1.2" +name = "rand_xorshift" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" dependencies = [ - "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "rand_xorshift" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] name = "rand_xoshiro" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1", ] [[package]] name = "rayon" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4" dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3", + "either", + "rayon-core", ] [[package]] name = "rayon-core" -version = "1.6.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.6.3", + "crossbeam-queue", + "crossbeam-utils 0.6.6", + "lazy_static", + "num_cpus", ] [[package]] name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1", ] [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" [[package]] -name = "regex" -version = "0.2.11" +name = "redox_termios" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" dependencies = [ - "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall", ] [[package]] name = "regex" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" dependencies = [ - "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.7.6", + "memchr", + "regex-syntax", + "thread_local", ] [[package]] name = "regex-automata" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex-syntax" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", ] [[package]] name = "regex-syntax" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" [[package]] name = "registrar" version = "0.0.1" dependencies = [ - "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "relay" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "common-types", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore-call-contract", + "keccak-hash", ] [[package]] name = "remove_dir_all" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[package]] name = "ring" -version = "0.14.6" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6747f8da1f2b1fabbee1aaa4eb8a11abf9adef0bf58a41cee45db5d59cecdfac" dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "lazy_static", + "libc", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.8", ] [[package]] name = "ripemd160" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rlp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.3", + "digest 0.8.0", + "opaque-debug", ] [[package]] name = "rlp" -version = "0.3.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a44d5ae8afcb238af8b75640907edc6c931efcfab2c854e81ed35fa080f84cd" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.1", ] [[package]] name = "rlp_compress" version = "0.1.0" dependencies = [ - "elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "elastic-array", + "lazy_static", + "rlp", ] [[package]] name = "rlp_derive" version = "0.1.0" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "rlp", + "syn 1.0.14", ] [[package]] -name = "rpassword" -version = "1.0.2" +name = "rocksdb" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12069b106981c6103d3eab7dd1c86751482d0779a520b7c14954c8b586c1e643" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rprompt 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "librocksdb-sys", ] [[package]] -name = "rprompt" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rust-crypto" -version = "0.2.36" +name = "rpassword" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b273c91bd242ca03ad6d71c143b6f17a48790e61f21a6c78568fa2b6774a24a4" dependencies = [ - "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "libc", + "rprompt", + "winapi 0.2.8", ] [[package]] -name = "rust-ini" -version = "0.13.0" +name = "rprompt" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1601f32bc5858aae3cbfa1c645c96c4d820cc5c16be0194f089560c00b6eb625" [[package]] name = "rustc-demangle" -version = "0.1.16" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" [[package]] name = "rustc-hex" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" [[package]] name = "rustc-hex" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" [[package]] name = "rustc-serialize" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver", ] [[package]] name = "rustls" -version = "0.15.2" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e" +dependencies = [ + "base64 0.10.1", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ffebdbb48c14f84eba0b715197d673aff1dd22cc1007ca647e28483bbcc307" dependencies = [ - "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe", + "rustls", + "schannel", + "security-framework", ] [[package]] name = "ryu" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" [[package]] name = "safemem" -version = "0.3.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" [[package]] name = "same-file" -version = "1.0.5" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" dependencies = [ - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] name = "schannel" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "winapi 0.3.8", ] -[[package]] -name = "scoped-tls" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" [[package]] name = "scopeguard" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" [[package]] name = "scrypt" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "656c79d0e90d0ab28ac86bf3c3d10bfbbac91450d3f190113b4e76d9fec3cfdd" dependencies = [ - "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pbkdf2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1", + "byteorder", + "hmac", + "pbkdf2", + "sha2 0.8.0", ] [[package]] name = "sct" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" dependencies = [ - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] name = "security-framework" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ef2429d7cefe5fd28bd1d2ed41c944547d4ff84776f5935b456da44593a16df" dependencies = [ - "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" dependencies = [ - "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys", ] [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "semver-parser", + "serde", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "serde" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde-hjson" -version = "0.8.2" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702" dependencies = [ - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn 1.0.14", ] [[package]] name = "serde_json" -version = "1.0.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_test" -version = "0.8.23" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" dependencies = [ - "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] name = "sha-1" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.3", + "digest 0.8.0", + "fake-simd", + "opaque-debug", ] [[package]] name = "sha1" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171698ce4ec7cbb93babeb3190021b4d72e96ccb98e33d277ae4ea959d6f2d9e" [[package]] name = "sha2" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" dependencies = [ - "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.3.3", + "byte-tools 0.2.0", + "digest 0.7.6", + "fake-simd", ] [[package]] name = "sha2" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" dependencies = [ - "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "block-buffer 0.7.3", + "digest 0.8.0", + "fake-simd", + "opaque-debug", ] [[package]] name = "shell32-sys" 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)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] name = "siphasher" -version = "0.1.3" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" [[package]] -name = "skeptic" -version = "0.4.0" +name = "siphasher" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "9913c75df657d84a03fa689c016b0bb2863ff0b497b26a8d6e9703f8d5df03a8" [[package]] name = "slab" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4" [[package]] name = "slab" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" [[package]] name = "slab" -version = "0.4.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" [[package]] name = "smallvec" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" [[package]] name = "smallvec" -version = "1.2.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ecf3b85f68e8abaa7555aa5abdb1153079387e60b718283d732f03897fcfc86" + +[[package]] +name = "snapshot" +version = "0.1.0" +dependencies = [ + "account-db", + "account-state", + "client-traits", + "common-types", + "criterion", + "crossbeam-utils 0.6.6", + "engine", + "env_logger 0.5.13", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethcore-accounts", + "ethcore-blockchain", + "ethcore-bloom-journal", + "ethcore-db", + "ethcore-io", + "ethereum-types", + "ethkey", + "hash-db", + "itertools 0.5.10", + "journaldb", + "keccak-hash", + "keccak-hasher 0.1.1", + "kvdb", + "kvdb-rocksdb", + "lazy_static", + "log", + "num_cpus", + "parity-bytes", + "parity-snappy", + "parking_lot 0.9.0", + "patricia-trie-ethereum", + "rand 0.7.2", + "rand_xorshift 0.2.0", + "rlp", + "rlp_derive", + "scopeguard 1.0.0", + "snapshot-tests", + "spec", + "state-db", + "tempdir", + "trie-db", + "trie-standardmap", + "triehash-ethereum", +] + +[[package]] +name = "snapshot-tests" +version = "0.1.0" +dependencies = [ + "account-db", + "account-state", + "client-traits", + "common-types", + "engine", + "env_logger 0.5.13", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethcore-accounts", + "ethcore-blockchain", + "ethcore-db", + "ethcore-io", + "ethereum-types", + "hash-db", + "journaldb", + "keccak-hash", + "keccak-hasher 0.1.1", + "kvdb", + "kvdb-rocksdb", + "lazy_static", + "log", + "parity-bytes", + "parity-crypto", + "parity-snappy", + "parking_lot 0.9.0", + "patricia-trie-ethereum", + "rand 0.7.2", + "rand_xorshift 0.2.0", + "rlp", + "snapshot", + "spec", + "tempdir", + "trie-db", + "trie-standardmap", + "triehash-ethereum", +] [[package]] name = "socket2" -version = "0.3.11" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.8", +] + +[[package]] +name = "sourcefile" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" + +[[package]] +name = "spec" +version = "0.1.0" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "account-state", + "authority-round", + "basic-authority", + "clique", + "common-types", + "engine", + "env_logger 0.5.13", + "ethash", + "ethash-engine", + "ethcore", + "ethcore-builtin", + "ethereum-types", + "ethjson", + "evm", + "executive-state", + "hash-db", + "instant-seal", + "journaldb", + "keccak-hash", + "kvdb-memorydb", + "log", + "machine", + "null-engine", + "parity-bytes", + "pod", + "rlp", + "tempdir", + "trace", + "trie-vm-factories", + "vm", ] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" + +[[package]] +name = "state-db" +version = "0.1.0" +dependencies = [ + "account-state", + "common-types", + "env_logger 0.5.13", + "ethcore", + "ethcore-bloom-journal", + "ethcore-db", + "ethereum-types", + "hash-db", + "journaldb", + "keccak-hash", + "keccak-hasher 0.1.1", + "kvdb", + "log", + "lru-cache", + "memory-cache", + "parking_lot 0.9.0", +] [[package]] name = "static_assertions" -version = "0.3.4" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241" + +[[package]] +name = "static_assertions" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stats" version = "0.1.0" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "log", ] [[package]] name = "stream-cipher" -version = "0.3.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8861bc80f649f5b4c9bd38b696ae9af74499d479dbfb327f0607de6b326a36bc" dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.0", ] [[package]] name = "string" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", ] [[package]] name = "strsim" -version = "0.8.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" [[package]] name = "strsim" -version = "0.9.2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + +[[package]] +name = "subtle" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829" [[package]] name = "syn" -version = "0.15.44" +version = "0.15.26" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.20", + "quote 0.6.8", + "unicode-xid 0.1.0", ] [[package]] name = "syn" -version = "1.0.5" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.8", + "quote 1.0.2", + "unicode-xid 0.2.0", ] [[package]] name = "synstructure" -version = "0.12.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" dependencies = [ - "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.20", + "quote 0.6.8", + "syn 0.15.26", + "unicode-xid 0.1.0", ] [[package]] -name = "target_info" -version = "0.1.0" +name = "synstructure" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +dependencies = [ + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn 1.0.14", + "unicode-xid 0.2.0", +] [[package]] -name = "tcp-stream" -version = "0.8.0" +name = "target_info" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" [[package]] name = "tempdir" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6", + "remove_dir_all", ] [[package]] -name = "tempfile" -version = "3.1.0" +name = "term_size" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "libc", + "winapi 0.2.8", ] [[package]] -name = "term_size" -version = "0.3.1" +name = "termcolor" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "wincolor", ] [[package]] -name = "termcolor" -version = "1.0.5" +name = "termion" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" dependencies = [ - "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "redox_syscall", + "redox_termios", ] [[package]] name = "textwrap" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" dependencies = [ - "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] name = "thread-id" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "redox_syscall", + "winapi 0.3.8", ] [[package]] name = "thread_local" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", ] [[package]] name = "threadpool" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" dependencies = [ - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus", ] [[package]] name = "time" -version = "0.1.42" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" dependencies = [ - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "redox_syscall", + "winapi 0.3.8", ] [[package]] @@ -4697,396 +5103,406 @@ version = "0.1.0" name = "timer" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b" dependencies = [ - "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono", ] [[package]] name = "tiny-keccak" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" dependencies = [ - "crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crunchy 0.2.2", ] [[package]] name = "tinytemplate" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4574b75faccaacddb9b284faecdf0b544b80b6b294f3d062d325c5726a209c20" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", + "serde_json", ] [[package]] name = "tokio" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-buf" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "mio", + "num_cpus", + "tokio-codec", + "tokio-current-thread", + "tokio-executor", + "tokio-fs", + "tokio-io", + "tokio-reactor", + "tokio-sync", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer 0.2.11", + "tokio-udp", + "tokio-uds", ] [[package]] name = "tokio-codec" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-core" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (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.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "tokio-io", ] [[package]] name = "tokio-current-thread" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "tokio-executor", ] [[package]] name = "tokio-executor" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac" dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6", + "futures", ] [[package]] name = "tokio-fs" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "tokio-io", + "tokio-threadpool", ] [[package]] name = "tokio-io" -version = "0.1.12" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8a85fffbec3c5ab1ab62324570230dcd37ee5996a7859da5caf7b9d45e3e8c" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "log", ] [[package]] name = "tokio-named-pipes" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d282d483052288b2308ba5ee795f5673b159c9bdf63c385a05609da782a5eae" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "mio", + "mio-named-pipes", + "tokio", ] [[package]] name = "tokio-reactor" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tokio-retry" -version = "0.1.1" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b26fd37f1125738b2170c80b551f69ff6fecb277e6e5ca885e53eec2b005018" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.23 (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)", + "crossbeam-utils 0.5.0", + "futures", + "lazy_static", + "log", + "mio", + "num_cpus", + "parking_lot 0.6.4", + "slab 0.4.1", + "tokio-executor", + "tokio-io", ] [[package]] name = "tokio-rustls" -version = "0.9.4" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d7cf08f990090abd6c6a73cab46fed62f85e8aef8b99e4b918a9f4a637f0676" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "rustls 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "iovec", + "rustls", + "tokio-io", + "webpki", ] [[package]] name = "tokio-service" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", ] [[package]] name = "tokio-sync" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2162248ff317e2bc713b261f242b69dbb838b85248ed20bb21df56d60ea4cae7" dependencies = [ - "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv", + "futures", ] [[package]] name = "tokio-tcp" -version = "0.1.3" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ad235e9dadd126b2d47f6736f65aa1fdcd6420e66ca63f44177bc78df89f912" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (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.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "iovec", + "mio", + "tokio-io", + "tokio-reactor", ] [[package]] name = "tokio-threadpool" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2c6a3885302581f4401c82af70d792bb9df1700e7437b0aeb4ada94d5388c" dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.7.1", + "crossbeam-queue", + "crossbeam-utils 0.6.6", + "futures", + "lazy_static", + "log", + "num_cpus", + "slab 0.4.1", + "tokio-executor", ] [[package]] name = "tokio-timer" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures", + "slab 0.3.0", ] [[package]] name = "tokio-timer" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6", + "futures", + "slab 0.4.1", + "tokio-executor", ] [[package]] name = "tokio-udp" -version = "0.1.5" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da941144b816d0dcda4db3a1ba87596e4df5e860a72b70783fe435891f80601c" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "log", + "mio", + "tokio-codec", + "tokio-io", + "tokio-reactor", ] [[package]] name = "tokio-uds" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" dependencies = [ - "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.29 (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.62 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes", + "futures", + "iovec", + "libc", + "log", + "mio", + "mio-uds", + "tokio-codec", + "tokio-io", + "tokio-reactor", ] [[package]] name = "toml" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" dependencies = [ - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "toolshed" -version = "0.4.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a272adbf14cfbb486774d09ee3e00c38d488cd390084a528f70e10e3a184a8" +dependencies = [ + "fxhash", +] + +[[package]] +name = "trace" +version = "0.1.0" dependencies = [ - "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore", + "ethcore-blockchain", + "ethcore-db", + "ethereum-types", + "evm", + "kvdb", + "log", + "parity-bytes", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "rlp", + "rlp_derive", + "vm", ] [[package]] name = "trace-time" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe82f2f0bf1991e163e757baf044282823155dd326e70f44ce2186c3c320cc9" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "log", ] [[package]] name = "transaction-pool" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "454adc482e32785c3beab9415dd0f3c689f29cc2d16717eb62f6a784d53544b4" dependencies = [ - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "smallvec 0.6.10", + "trace-time", ] [[package]] name = "transient-hashmap" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e" [[package]] -name = "trezor-sys" -version = "1.0.0" -source = "git+https://github.com/paritytech/trezor-sys#8a401705e58c83db6c29c199d9577b78fde40709" +name = "trie-db" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5756812179defbff624e0ca766bedf6298cc7164037cc945584dc37833a4b3f9" dependencies = [ - "protobuf 1.7.5 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "hashbrown", + "log", + "rand 0.6.1", + "smallvec 1.0.0", ] [[package]] -name = "trie-db" -version = "0.11.0" +name = "trie-standardmap" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fda153c00484d640bc91334624be22ead0e5baca917d9fd53ff29bdebcf9b2" dependencies = [ - "elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "keccak-hasher 0.15.0", ] [[package]] -name = "trie-standardmap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "trie-vm-factories" +version = "0.1.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "account-db", + "evm", + "keccak-hasher 0.1.1", + "patricia-trie-ethereum", + "trie-db", + "vm", + "wasm", ] [[package]] name = "triehash" -version = "0.4.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a518c10ed2591fd67bbafd7d5daf725767d07b129d8c99b3b3831eeabd639ed9" dependencies = [ - "hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hash-db", + "rlp", ] [[package]] name = "triehash-ethereum" version = "0.2.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types", "keccak-hasher 0.1.1", - "triehash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "triehash", ] -[[package]] -name = "try-lock" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "try-lock" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" [[package]] name = "typenum" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ucd-trie" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ucd-util" -version = "0.1.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" [[package]] name = "uint" -version = "0.4.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "crunchy 0.2.2", + "rustc-hex 2.0.1", + "static_assertions 1.1.0", ] [[package]] @@ -5095,79 +5511,87 @@ version = "0.1.0" [[package]] name = "unicase" -version = "2.5.1" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicase" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90" dependencies = [ - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check", ] [[package]] name = "unicode-bidi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "matches", ] [[package]] name = "unicode-normalization" -version = "0.1.8" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" [[package]] name = "unicode-segmentation" -version = "1.3.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" [[package]] name = "unicode-width" -version = "0.1.6" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "untrusted" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" [[package]] name = "url" -version = "1.7.2" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5", + "matches", + "percent-encoding 1.0.1", ] [[package]] name = "url" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" dependencies = [ - "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0", + "matches", + "percent-encoding 2.1.0", ] [[package]] @@ -5178,731 +5602,429 @@ version = "0.1.0" name = "utf8-ranges" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" [[package]] -name = "uuid" -version = "0.8.1" +name = "validator" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236a5eda3df2c877872e98dbc55d497d943792e6405d8fc65bd4f8a5e3b53c99" dependencies = [ - "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.1.5", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url 1.7.1", ] [[package]] -name = "validator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +name = "validator-set" +version = "0.1.0" dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "client-traits", + "common-types", + "engine", + "env_logger 0.6.2", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethcore", + "ethcore-accounts", + "ethcore-call-contract", + "ethereum-types", + "ethjson", + "executive-state", + "keccak-hash", + "kvdb", + "lazy_static", + "log", + "machine", + "memory-cache", + "parity-bytes", + "parity-crypto", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "rlp", + "rustc-hex 1.0.0", + "spec", + "triehash-ethereum", + "unexpected", + "vm", ] [[package]] name = "validator_derive" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d360d6f5754972c0c1da14fb3d5580daa31aee566e1e45e2f8d3bf5950ecd3e9" dependencies = [ - "if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", - "validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "if_chain", + "lazy_static", + "proc-macro2 0.4.20", + "quote 0.6.8", + "regex", + "syn 0.15.26", + "validator", ] -[[package]] -name = "vcpkg" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" [[package]] name = "vergen" -version = "0.1.1" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aba5e34f93dc7051dfad05b98a18e9156f27e7b431fe1d2398cb6061c0a1dba" +dependencies = [ + "bitflags", + "chrono", + "failure", +] + +[[package]] +name = "verification" +version = "0.1.0" dependencies = [ - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "client-traits", + "common-types", + "criterion", + "engine", + "ethash-engine", + "ethcore", + "ethcore-blockchain", + "ethcore-call-contract", + "ethcore-io", + "ethereum-types", + "keccak-hash", + "len-caching-lock", + "log", + "machine", + "null-engine", + "num_cpus", + "parity-bytes", + "parity-crypto", + "parity-util-mem 0.3.0", + "parking_lot 0.9.0", + "rlp", + "spec", + "tempdir", + "time-utils", + "triehash-ethereum", + "unexpected", ] [[package]] name = "version_check" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" [[package]] name = "vm" version = "0.1.0" dependencies = [ - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ethjson 0.1.0", - "keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "patricia-trie-ethereum 0.1.0", - "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ethereum-types", + "ethjson", + "keccak-hash", + "parity-bytes", + "patricia-trie-ethereum", + "rlp", ] [[package]] -name = "void" -version = "1.0.2" +name = "walkdir" +version = "2.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" +dependencies = [ + "same-file", + "winapi 0.3.8", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" +dependencies = [ + "futures", + "log", + "try-lock", +] [[package]] -name = "walkdir" -version = "2.2.9" +name = "wasi" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd5442abcac6525a045cc8c795aedb60da7a2e5e89c7bf18a0d5357849bb23c7" + +[[package]] +name = "wasm" +version = "0.1.0" dependencies = [ - "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "env_logger 0.5.13", + "ethereum-types", + "libc", + "log", + "parity-wasm", + "pwasm-utils", + "vm", + "wasmi", ] [[package]] -name = "want" -version = "0.0.4" +name = "wasm-bindgen" +version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5205e9afdf42282b192e2310a5b463a6d1c1d774e30dc3c791ac37ab42d2616c" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", + "wasm-bindgen-macro", ] [[package]] -name = "want" -version = "0.2.0" +name = "wasm-bindgen-backend" +version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11cdb95816290b525b32587d76419facd99662a07e59d3cdb560488a819d9a45" dependencies = [ - "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo", + "lazy_static", + "log", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn 1.0.14", + "wasm-bindgen-shared", ] [[package]] -name = "wasi" -version = "0.7.0" +name = "wasm-bindgen-macro" +version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "574094772ce6921576fb6f2e3f7497b8a76273b6db092be18fc48a082de09dc3" +dependencies = [ + "quote 1.0.2", + "wasm-bindgen-macro-support", +] [[package]] -name = "wasm" -version = "0.1.0" +name = "wasm-bindgen-macro-support" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e85031354f25eaebe78bb7db1c3d86140312a911a106b2e29f9cc440ce3e7668" +dependencies = [ + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn 1.0.14", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e7e61fc929f4c0dddb748b102ebf9f632e2b8d739f2016542b4de2965a9601" + +[[package]] +name = "wasm-bindgen-webidl" +version = "0.2.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef012a0d93fc0432df126a8eaf547b2dce25a8ce9212e1d3cbeef5c11157975d" dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pwasm-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "vm 0.1.0", - "wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "heck", + "log", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn 1.0.14", + "wasm-bindgen-backend", + "weedle", ] [[package]] name = "wasmi" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4a6d379e9332b1b1f52c5a87f2481c85c7c931d8ec411963dfb8f26b1ec1e3" +dependencies = [ + "byteorder", + "memory_units", + "nan-preserving-float", + "parity-wasm", +] + +[[package]] +name = "web-sys" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf97caf6aa8c2b1dac90faf0db529d9d63c93846cca4911856f78a83cebf53b" dependencies = [ - "byteorder 1.3.2 (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.31.3 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow", + "js-sys", + "sourcefile", + "wasm-bindgen", + "wasm-bindgen-webidl", ] [[package]] name = "webpki" -version = "0.19.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7e664e770ac0110e2384769bcc59ed19e329d81f555916a6e072714957b81b4" dependencies = [ - "ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)", - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ring", + "untrusted", ] [[package]] -name = "webpki-roots" -version = "0.16.0" +name = "weedle" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" dependencies = [ - "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nom", ] [[package]] -name = "whisper-cli" -version = "0.1.0" +name = "which" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" dependencies = [ - "docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-network 1.12.0", - "ethcore-network-devp2p 1.12.0", - "ethkey 0.3.0", - "jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-http-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-pubsub 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "panic_hook 0.1.0", - "parity-whisper 0.1.0", - "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "failure", + "libc", ] [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "wincolor" -version = "1.0.2" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" +dependencies = [ + "winapi 0.3.8", + "winapi-util", +] + +[[package]] +name = "ws" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a6f5bb86663ff4d1639408410f50bf6050367a8525d644d49a6894cd618a631" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "bytes", + "httparse", + "log", + "mio", + "mio-extras", + "rand 0.6.1", + "sha-1", + "slab 0.4.1", + "url 2.1.0", ] [[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "xdg" -version = "2.2.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66b7c2281ebde13cf4391d70d4c7e5946c3c25e72a7b859ca8f677dcd0b0c61" [[package]] name = "xml-rs" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2" dependencies = [ - "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", ] [[package]] name = "xmltree" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff8eaee9d17062850f1e6163b509947969242990ee59a35801af437abe041e70" dependencies = [ - "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs", ] [[package]] -name = "yaml-rust" -version = "0.4.3" +name = "zeroize" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080616bd0e31f36095288bb0acdf1f78ef02c2fa15527d7e993f2a6c7591643e" dependencies = [ - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" -"checksum aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" -"checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" -"checksum aesni 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" -"checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" -"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -"checksum amq-protocol 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0261eeb244071203cd9727151a3161b9f6b443001c265dd52e782db6a36aa4d0" -"checksum amq-protocol-codegen 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "804fc12efd85663a6ced824386dde4f9b255fc00dd3db96459e35165164828e5" -"checksum amq-protocol-tcp 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7623ad8723830dd9c48372e1d5fde9973ce7c28636267dfcf506bed382e84150" -"checksum amq-protocol-types 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a13489ba9891aac7f217c80ef10ead9e0219422d9af826bceb13a84346f75d0f" -"checksum amq-protocol-uri 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "52e68b30bc7968ba7a013083e52d43429b1f253261a2960de6eb0905169e10f4" -"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum app_dirs 1.2.1 (git+https://github.com/paritytech/app-dirs-rs)" = "" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" -"checksum ascii 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" -"checksum assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5" -"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" -"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" -"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" -"checksum base-x 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "76f4eae81729e69bb1819a26c6caac956cc429238388091f98cb6cd858f16443" -"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -"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" -"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2" -"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" -"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -"checksum block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" -"checksum block-modes 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "31aa8410095e39fdb732909fb5730a48d5bd7c2e3cd76bd1b07b3dbea130c529" -"checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" -"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "" -"checksum boolinator 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" -"checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245" -"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" -"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" -"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" -"checksum cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" -"checksum cid 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6908948032561f2550467a477f659cdc358320a805237b9b5035c0350c441def" -"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" -"checksum combine 3.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" -"checksum config 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9107d78ed62b3fa5a86e7d18e647abed48cfd8f8fab6c72f4cdb982d196f7e6" -"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" -"checksum cookie-factory 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2b81f8a9b8f5ffa2ed91a82c53392837eab151baf682acf8322c909b6d4fe9" -"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" -"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" -"checksum criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0363053954f3e679645fc443321ca128b7b950a6fe288cf5f9335cc22ee58394" -"checksum criterion-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76f9212ddf2f4a9eb2d401635190600656a1f88a932ef53d06e7fa4c7e02fb8e" -"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c" -"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" -"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" -"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" -"checksum crunchy 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -"checksum crypto-mac 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7afa06d05a046c7a47c3a849907ec303504608c927f4e85f7bfff22b7180d971" -"checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" -"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" -"checksum ct-logs 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b4660f8b07a560a88c02d76286edb9f0d5d64e495d2b0f233186155aa51be1f" -"checksum ctr 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" -"checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "" -"checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" -"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" -"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" -"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum docopt 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f525a586d310c87df72ebcd98009e57f1cc030c8c268305287a476beb653969" -"checksum edit-distance 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bbbaaaf38131deb9ca518a274a45bfdb8771f139517b073b16c2d3d32ae5037b" -"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" -"checksum elastic-array 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "073be79b6538296faf81c631872676600616073817dd9a440c477ad09b408983" -"checksum enclose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5e432442c28955a3bc9d0ad23ffe730f29274edf1d1db1b3eb9c461eb01d38fd" -"checksum enclose 1.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1056f553da426e9c025a662efa48b52e62e0a3a7648aa2d15aeaaf7f0d329357" -"checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" -"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" -"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -"checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" -"checksum eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)" = "" -"checksum ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eb362fde43ed0b50b258bb0c72b72b3dccfd29f8de9506295eaf9251c49ca31" -"checksum ethabi-contract 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "795e25fd868e12a59ca235dbe1f6cc8f1eba8f67d6a39438b29535e0126e0c27" -"checksum ethabi-derive 6.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "66a587250c8190be9d6ae28d67b8957ed97cb9eee2e272173a20593ab054a075" -"checksum ethbloom 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a6294da962646baa738414e8e718d1a1f0360a51d92de89ccbf91870418f5360" -"checksum ethereum-types 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e742184dc63a01c8ea0637369f8faa27c40f537949908a237f95c05e68d2c96" -"checksum ethereum-types-serialize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1873d77b32bc1891a79dad925f2acbc318ee942b38b9110f9dbc5fbeffcea350" -"checksum failsafe 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf04c6ffd217a68c73fdf40eb3331c484fd7a9fa4fd1028fcf74456ef889ca12" -"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" -"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" -"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" -"checksum fixed-hash 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7afe6ce860afb14422711595a7b26ada9ed7de2f43c0b2ab79d09ee196287273" -"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" -"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -"checksum fs-swap 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "921d332c89b3b61a826de38c61ee5b6e02c56806cade1b0e5d81bd71f57a71bb" -"checksum fs_extra 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"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.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" -"checksum futures-channel 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbb37ec6418c577b25f5b129c0f4456ad7ce8714ec43c59712aa7e4cd2cb6b85" -"checksum futures-core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7455c91eb2eae38f33b013f77ebe766c75761af333efd9d550e154045c63e225" -"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum futures-executor 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5db1dd3979745f5e50b28fd604602f2715f9d5a28ab835a5f9686a9d84cd1315" -"checksum futures-io 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6a0470fdba9dc87c27a3564ad6d5cc04e080f3afa26c93549728cce46ab21a2" -"checksum futures-retry 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d79b1e875b3ab07ef294b5bd43ae509d0ed1be990389003ea5fcdecf2e62ec96" -"checksum futures-sink 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a8a93a7c480876b8e02cdd70022e7eb9c8423575ea6a25a0b749b18834c16412" -"checksum futures-util 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cf12a3fc1ccaf1bc2901ec6e0ed6ed407a4f16eaa20dd838f40cabf5f7b31f1" -"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" -"checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" -"checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2" -"checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" -"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1" -"checksum handlebars 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "91ef1ac30f2eaaa2b835fce73c57091cb6b9fc62b7eef285efbf980b0f20001b" -"checksum hash-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b03501f6e1a2a97f1618879aba3156f14ca2847faa530c4e28859638bd11483" -"checksum hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353" -"checksum heapsize 0.4.2 (git+https://github.com/cheme/heapsize.git?branch=ec-macfix)" = "" -"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" -"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" -"checksum hex-literal-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d4c5c844e2fee0bf673d54c2c177f1713b3d2af2ff6e666b49cb7572e6cf42d" -"checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "" -"checksum hmac 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "733e1b3ac906631ca01ebb577e9bb0f5e37a454032b9036b5eaea4013ed6f99a" -"checksum home 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "29302b90cfa76231a757a887d1e3153331a63c7f80b6c75f86366334cbe70708" -"checksum home 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c07c315e106bd6f83f026a20ddaeef2706782e490db1dcdd37caad38a0e895b3" -"checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" -"checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" -"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" -"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -"checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" -"checksum hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)" = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" -"checksum hyper-rustls 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "15b66d1bd4864ef036adf2363409caa3acd63ebb4725957b66e621c8a36631a3" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4bac95d9aa0624e7b78187d6fb8ab012b41d9f6f54b1bcb61e61c4845f8357ec" -"checksum igd 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8aef7814a769f156ef3a86169a8b04c066e3aebc324f522c159978466e32a1c" -"checksum indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3" -"checksum integer-encoding 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1aec89c15e2cfa0f0eae8ca60e03cb10b30d25ea2c0ad7d6be60a95e32729994" -"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.8 (registry+https://github.com/rust-lang/crates.io-index)" = "70783119ac90828aaba91eae39db32c6c1b8838deea3637e5238efa0130801ab" -"checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc" -"checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" -"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" -"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum jemalloc-sys 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "bfc62c8e50e381768ce8ee0428ee53741929f7ebd73e4d83f669bcf7693e00ae" -"checksum jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9f0cd42ac65f758063fea55126b0148b1ce0a6354ff78e07a4d6806bc65c4ab3" -"checksum jni 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "294eca097d1dc0bf59de5ab9f7eafa5f77129e9f6464c957ed3ddeb705fb4292" -"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -"checksum jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dc15eef5f8b6bef5ac5f7440a957ff95d036e2f98706947741bfc93d1976db4c" -"checksum jsonrpc-derive 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c2dae61ca8a3b047fb11309b00661bc56837085bd07e46f907b9c562c0b03e68" -"checksum jsonrpc-http-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11d2a00824306155b8ef57fe957f31b8cd8ad24262f15cf911d84dcf9a3f206d" -"checksum jsonrpc-ipc-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3da135d38d35137f9af49fde7bac99e3fe028352bfb96fe0140ae23a189ea027" -"checksum jsonrpc-pubsub 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37fce55133ee264d0ab42bd862efcd45ae1d062cda599f4cc12ccc4be3195f2a" -"checksum jsonrpc-server-utils 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c9527f01ef25f251d64082cbefc0c6d6f367349afe6848ef908a674e06b2bdd3" -"checksum jsonrpc-tcp-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec7a04458f5fcba7bb0f109a4d42f47fab4c068cdeb40bdf9b984327e9aaea24" -"checksum jsonrpc-ws-server 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3889012aa638a2f18eb1a879f46fc8b34e7e1423cbff3247cd1531de0d51084b" -"checksum keccak-hash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "253bbe643c32c816bf58fa5a88248fafedeebb139705ad17a62add3517854a86" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum kvdb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "72ae89206cea31c32014b39d5a454b96135894221610dbfd19cf4d2d044fa546" -"checksum kvdb-memorydb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "296c12309ed36cb74d59206406adbf1971c3baa56d5410efdb508d8f1c60a351" -"checksum kvdb-rocksdb 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96eb0e0112bb66fe5401294ca0f43c9cb771456af9270443545026e55fd00912" -"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" -"checksum lapin 0.28.3 (registry+https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git)" = "8be17594e6c381a168dee69d88919a0c4a3326a8a021a8b98364cc90834a669b" -"checksum lapin-futures 0.28.3 (registry+https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git)" = "fcdd837603b01f834c2122b3ace73545f350d3190536286c884d83a4904d4b2f" -"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14" -"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" -"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" -"checksum libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)" = "" -"checksum libusb-sys 0.2.5 (git+https://github.com/paritytech/libusb-sys)" = "" -"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" -"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" -"checksum local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -"checksum lunarity-lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a1670671f305792567116d4660e6e5bd785d6fa973e817c3445c0a7a54cecb6" -"checksum maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" -"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" -"checksum memory-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94da53143d45f6bad3753f532e56ad57a6a26c0ca6881794583310c7cb4c885f" -"checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" -"checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf" -"checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599" -"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" -"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" -"checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" -"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" -"checksum multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6" -"checksum multihash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c62469025f45dee2464ef9fc845f4683c543993792c1993e7d903c17a4546b74" -"checksum nan-preserving-float 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34d4f00fcc2f4c9efa8cc971db0da9e28290e28e97af47585e48691ef10ff31f" -"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" -"checksum nom 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff" -"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -"checksum nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c618b63422da4401283884e6668d39f819a106ef51f5f59b81add00075da35ca" -"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" -"checksum num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" -"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" -"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" -"checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" -"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" -"checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee" -"checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" -"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" -"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.52 (registry+https://github.com/rust-lang/crates.io-index)" = "c977d08e1312e2f7e4b86f9ebaa0ed3b19d1daff75fae88bbb88108afbd801fc" -"checksum order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb" -"checksum ordered-float 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7eb5259643245d3f292c7a146b2df53bba24d7eab159410e648eb73dc164669d" -"checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" -"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" -"checksum parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5168b4cf41f3835e4bc6ffb32f51bc9365dc50cb351904595b3931d917fd0c" -"checksum parity-crypto 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1b9c063d87e1507cb3807493c8d21859ef23b5414b39f81c53f0ba267d64c1" -"checksum parity-daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "69b1910b2793ff52713fca0a4ee92544ebec59ccd218ea74560be6f947b4ca77" -"checksum parity-path 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b027aab22527061b7005cecf7805e8f42eed94ce89e76bac3a6035394b56627" -"checksum parity-rocksdb 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d17caf6640e24b70242f3f48615e3f0764f98871e8c7aea25584e29833eb5a8" -"checksum parity-rocksdb-sys 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9581e6b8c63f3808500638372ee56faaaffb57c4d349974bff591606b94d5f57" -"checksum parity-snappy 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2c5f9d149b13134b8b354d93a92830efcbee6fe5b73a2e6e540fe70d4dd8a63" -"checksum parity-snappy-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1a413d51e5e1927320c9de992998e4a279dffb8c8a7363570198bd8383e66f1b" -"checksum parity-tokio-ipc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb002c2d3539ccd3b82bd915ec060028d4ab350ad203dbffa20028c1e483af5b" -"checksum parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "511379a8194230c2395d2f5fa627a5a7e108a9f976656ce723ae68fca4097bfc" -"checksum parity-wordlist 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "573d08f0d3bc8a6ffcdac1de2725b5daeed8db26345a9c12d91648e2d6457f3e" -"checksum parity-ws 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2fec5048fba72a2e01baeb0d08089db79aead4b57e2443df172fb1840075a233" -"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" -"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" -"checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" -"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" -"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" -"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" -"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" -"checksum pbkdf2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0c09cddfbfc98de7f76931acf44460972edb4023eb14d0c6d4018800e552d8e0" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a" -"checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" -"checksum pest_generator 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0" -"checksum pest_meta 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "df43fd99896fd72c485fe47542c7b500e4ac1e8700bf995544d1317a60ded547" -"checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" -"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" -"checksum plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95fa6386b1d34aaf0adb9b7dd2885dbe7c34190e6263785e5a7ec2b19044a90f" -"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" -"checksum pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2412f3332a07c7a2a50168988dcc184f32180a9758ad470390e5f55e089f6b6e" -"checksum primal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0e31b86efadeaeb1235452171a66689682783149a6249ff334a2c5d8218d00a4" -"checksum primal-bit 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "686a64e2f50194c64942992af5799e6b6e8775b8f88c607d72ed0a2fd58b9b21" -"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.9 (registry+https://github.com/rust-lang/crates.io-index)" = "da2d6ed369bb4b0273aeeb43f07c105c0117717cbae827b20719438eb2eb798c" -"checksum proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "114cdf1f426eb7f550f01af5f53a33c0946156f6814aec939b3bd77e844f9a9d" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc" -"checksum procinfo 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6ab1427f3d2635891f842892dda177883dca0639e05fe66796a62c9d2f23b49c" -"checksum prometheus 0.8.0 (git+https://github.com/pingcap/rust-prometheus.git)" = "" -"checksum protobuf 1.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e14ccd6b79ec748412d4f2dfde1a80fa363a67def4062969f8aed3d790a30f28" -"checksum protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40361836defdd5871ff7e84096c6f6444af7fc157f8ef1789f54f147687caa20" -"checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" -"checksum pwasm-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "efb0dcbddbb600f47a7098d33762a00552c671992171637f5bb310b37fe1f0e4" -"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rabbitmq_adaptor 0.4.5 (registry+https://dl.cloudsmith.io/ItqwH3F8rYFNB5vv/chronicled/platform-v2/cargo/index.git)" = "f9658323174e4660136cf716d2f75435563d9883a6ce9a74ecc4b0c3d4bbd571" -"checksum rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" -"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -"checksum rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929" -"checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" -"checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" -"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" -"checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" -"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" -"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" -"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" -"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum ring 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "426bc186e3e95cac1e4a4be125a4aca7e84c2d616ffc02244eef36e2a60a093c" -"checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a" -"checksum rlp 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "524c5ad554859785dfc8469df3ed5e0b5784d4d335877ed47c8d90fc0eb238fe" -"checksum rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16d1effe9845d54f90e7be8420ee49e5c94623140b97ee4bc6fb5bfddb745720" -"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 rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" -"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" -"checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8" -"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustls 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f271e3552cd835fa28c541c34a7e8fdd8cdff09d77fe4eb8f6c42e87a11b096e" -"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" -"checksum safemem 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b08423011dae9a5ca23f07cf57dac3857f5c885d352b76f6d95f4aea9434d0" -"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" -"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" -"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" -"checksum scrypt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8570c5e2fa69cb29d492fd4e9974b6b5facb5a888e1c6da630d4a3cd7ebfef4a" -"checksum sct 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f5adf8fbd58e1b1b52699dc8bed2630faecb6d8c7bee77d009d6bbe4af569b9" -"checksum security-framework 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "301c862a6d0ee78f124c5e1710205965fc5c553100dcda6d98f13ef87a763f04" -"checksum security-framework-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e31493fc37615debb8c5090a7aeb4a9730bc61e77ab10b9af59f1a202284f895" -"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 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" -"checksum serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4b39bd9b0b087684013a792c59e3e07a46a01d2322518d8a1104641a0b1be0" -"checksum serde-hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0b833c5ad67d52ced5f5938b2980f32a9c1c5ef047f0b4fb3127e7a423c76153" -"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" -"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" -"checksum serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5" -"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" -"checksum sha1 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "171698ce4ec7cbb93babeb3190021b4d72e96ccb98e33d277ae4ea959d6f2d9e" -"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" -"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" -"checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" -"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 skeptic 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24ebf8a06f5f8bae61ae5bbc7af7aac4ef6907ae975130faba1199e5fe82256a" -"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4" -"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" -"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" -"checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" -"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" -"checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" -"checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" -"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "032c03039aae92b350aad2e3779c352e104d919cb192ba2fabbd7b831ce4f0f6" -"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" -"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" -"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe" -"checksum tcp-stream 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd9341a6cbb07bc9bface9028e2e8902f12f3971755688816fd405bc8cd4d8d7" -"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327" -"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" -"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"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.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"checksum threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f0c90a5f3459330ac8bc0d2f879c693bb7a2f59689c1083fc4ef83834da865" -"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum timer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b" -"checksum tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" -"checksum tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4574b75faccaacddb9b284faecdf0b544b80b6b294f3d062d325c5726a209c20" -"checksum tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" -"checksum tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" -"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" -"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" -"checksum tokio-current-thread 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" -"checksum tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f27ee0e6db01c5f0b2973824547ce7e637b2ed79b891a9677b0de9bd532b6ac" -"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" -"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" -"checksum tokio-named-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d282d483052288b2308ba5ee795f5673b159c9bdf63c385a05609da782a5eae" -"checksum tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c56391be9805bc80163151c0b9e5164ee64f4b0200962c346fea12773158f22d" -"checksum tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f05746ae87dca83a2016b4f5dba5b237b897dd12fd324f60afe282112f16969a" -"checksum tokio-rustls 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "95a199832a67452c60bed18ed951d28d5755ff57b02b3d2d535d9f13a81ea6c9" -"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" -"checksum tokio-sync 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2162248ff317e2bc713b261f242b69dbb838b85248ed20bb21df56d60ea4cae7" -"checksum tokio-tcp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" -"checksum tokio-threadpool 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2bd2c6a3885302581f4401c82af70d792bb9df1700e7437b0aeb4ada94d5388c" -"checksum tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc" -"checksum tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f2106812d500ed25a4f38235b9cae8f78a09edf43203e16e59c3b769a342a60e" -"checksum tokio-udp 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f02298505547f73e60f568359ef0d016d5acd6e830ab9bc7c4a5b3403440121b" -"checksum tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "037ffc3ba0e12a0ab4aca92e5234e0dedeb48fddf6ccd260f1f150a36a9f2445" -"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" -"checksum toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "450441e131c7663af72e63a33c02a6a1fbaaa8601dc652ed6757813bb55aeec7" -"checksum trace-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe82f2f0bf1991e163e757baf044282823155dd326e70f44ce2186c3c320cc9" -"checksum transaction-pool 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "454adc482e32785c3beab9415dd0f3c689f29cc2d16717eb62f6a784d53544b4" -"checksum transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e" -"checksum trezor-sys 1.0.0 (git+https://github.com/paritytech/trezor-sys)" = "" -"checksum trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7319e28ca295f27359d944a682f7f65b419158bf1590c92cadc0000258d788" -"checksum trie-standardmap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e26f52976a57a0859616d6fcec87092ac35d08eabbd78dc3dabee93b480ea5f" -"checksum triehash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d26efb4ddf87870fc08dc9a6580dc3061be350d7b9d0eb30aef1c8b4227aa46" -"checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2" -"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" -"checksum ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2" -"checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874" -"checksum uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646" -"checksum unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" -"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" -"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" -"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" -"checksum utf8-ranges 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" -"checksum uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" -"checksum validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "236a5eda3df2c877872e98dbc55d497d943792e6405d8fc65bd4f8a5e3b53c99" -"checksum validator_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d360d6f5754972c0c1da14fb3d5580daa31aee566e1e45e2f8d3bf5950ecd3e9" -"checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" -"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c3365f36c57e5df714a34be40902b27a992eeddb9996eca52d0584611cf885d" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" -"checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" -"checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" -"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" -"checksum wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4a6d379e9332b1b1f52c5a87f2481c85c7c931d8ec411963dfb8f26b1ec1e3" -"checksum webpki 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f7e1cd7900a3a6b65a3e8780c51a3e6b59c0e2c55c6dc69578c288d69f7d082" -"checksum webpki-roots 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c10fa4212003ba19a564f25cd8ab572c6791f99a03cc219c13ed35ccab00de0e" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" -"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 yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" + "proc-macro2 0.4.20", + "quote 0.6.8", + "syn 0.15.26", + "synstructure 0.10.1", +] diff --git a/Cargo.toml b/Cargo.toml index 8a9cbb2d0e..ea9adb20a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,40 +2,27 @@ description = "Parity Ethereum client" name = "parity-ethereum" # NOTE Make sure to update util/version/Cargo.toml as well -version = "2.5.9" +version = "2.7.2" license = "GPL-3.0" authors = ["Parity Technologies "] [dependencies] +ansi_term = "0.11" +atty = "0.2.8" blooms-db = { path = "util/blooms-db" } -log = "0.4" -rustc-hex = "1.0" -docopt = "1.0" clap = "2" -term_size = "0.3" -textwrap = "0.9" -num_cpus = "1.2" -number_prefix = "0.2" -rpassword = "1.0" -semver = "0.9" -ansi_term = "0.10" -parking_lot = "0.7" -regex = "1.0" -atty = "0.2.8" -toml = "0.4" -serde = "1.0" -serde_json = "1.0" -serde_derive = "1.0" -futures = "0.1" -fdlimit = "0.1" -ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } -jsonrpc-core = "10.0.1" -parity-bytes = "0.1" +cli-signer= { path = "cli-signer" } +client-traits = { path = "ethcore/client-traits" } common-types = { path = "ethcore/types" } +ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } +dir = { path = "util/dir" } +docopt = "1.0" +engine = { path = "ethcore/engine" } +ethabi = { version = "9.0.1", optional = true } ethcore = { path = "ethcore", features = ["parity"] } ethcore-accounts = { path = "accounts", optional = true } ethcore-blockchain = { path = "ethcore/blockchain" } -ethcore-call-contract = { path = "ethcore/call-contract"} +ethcore-call-contract = { path = "ethcore/call-contract", optional = true } ethcore-db = { path = "ethcore/db" } ethcore-io = { path = "util/io" } ethcore-light = { path = "ethcore/light" } @@ -43,36 +30,53 @@ ethcore-logger = { path = "parity/logger" } ethcore-miner = { path = "miner" } ethcore-network = { path = "util/network" } ethcore-private-tx = { path = "ethcore/private-tx" } +ethcore-secretstore = { path = "secret-store", optional = true } ethcore-service = { path = "ethcore/service" } ethcore-sync = { path = "ethcore/sync" } -ethereum-types = "0.4" +ethereum-types = "0.8.0" ethkey = { path = "accounts/ethkey" } ethstore = { path = "accounts/ethstore" } +fdlimit = "0.1" +futures = "0.1" +journaldb = { path = "util/journaldb" } +jsonrpc-core = "14.0.3" +keccak-hash = "0.4.0" +kvdb = "0.3.1" +kvdb-rocksdb = "0.4.1" +log = "0.4" +migration-rocksdb = { path = "util/migration-rocksdb" } node-filter = { path = "ethcore/node-filter" } -rlp = { version = "0.3.0", features = ["ethereum"] } -cli-signer= { path = "cli-signer" } +num_cpus = "1.2" +number_prefix = "0.2" +panic_hook = { path = "util/panic-hook" } +parity-bytes = "0.1" +parity-crypto = { version = "0.4.2", features = ["publickey"] } parity-daemonize = "0.3" parity-hash-fetch = { path = "updater/hash-fetch" } parity-ipfs-api = { path = "ipfs" } parity-local-store = { path = "miner/local-store" } -parity-rabbitmq = { path = "rabbitmq" } -parity-runtime = { path = "util/runtime" } +parity-path = "0.1" parity-rpc = { path = "rpc" } +parity-runtime = { path = "util/runtime" } parity-updater = { path = "updater" } +parity-util-mem = { version = "0.3.0", features = ["jemalloc-global"] } parity-version = { path = "util/version" } -parity-whisper = { path = "whisper" } -parity-path = "0.1" -dir = { path = "util/dir" } -panic_hook = { path = "util/panic-hook" } -keccak-hash = "0.1" -migration-rocksdb = { path = "util/migration-rocksdb" } -kvdb = "0.1" -kvdb-rocksdb = "0.1.3" -journaldb = { path = "util/journaldb" } - -ethcore-secretstore = { path = "secret-store", optional = true } - +parking_lot = "0.9" +regex = "1.0" registrar = { path = "util/registrar" } +rlp = "0.4.0" +rpassword = "1.0" +rustc-hex = "1.0" +semver = "0.9" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" +snapshot = { path = "ethcore/snapshot" } +spec = { path = "ethcore/spec" } +term_size = "0.3" +textwrap = "0.9" +toml = "0.4" +verification = { path = "ethcore/verification" } [build-dependencies] rustc_version = "0.2" @@ -82,7 +86,6 @@ pretty_assertions = "0.1" ipnetwork = "0.12.6" tempdir = "0.3" fake-fetch = { path = "util/fake-fetch" } -lazy_static = "1.2.0" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.4", features = ["winsock2", "winuser", "shellapi"] } @@ -92,12 +95,11 @@ default = ["accounts"] accounts = ["ethcore-accounts", "parity-rpc/accounts"] miner-debug = ["ethcore/miner-debug"] json-tests = ["ethcore/json-tests"] -ci-skip-tests = ["ethcore/ci-skip-tests"] test-heavy = ["ethcore/test-heavy"] evm-debug = ["ethcore/evm-debug"] evm-debug-tests = ["ethcore/evm-debug-tests"] slow-blocks = ["ethcore/slow-blocks"] -secretstore = ["ethcore-secretstore", "ethcore-secretstore/accounts"] +secretstore = ["ethcore-secretstore", "accounts", "ethabi", "ethcore-call-contract"] final = ["parity-version/final"] deadlock_detection = ["parking_lot/deadlock_detection"] # to create a memory profile (requires nightly rust), use e.g. @@ -119,10 +121,6 @@ path = "parity/lib.rs" path = "parity/main.rs" name = "parity" -[profile.test] -lto = false -opt-level = 3 # makes tests slower to compile, but faster to run - [profile.release] debug = false lto = true @@ -137,14 +135,4 @@ members = [ "chainspec", "ethcore/wasm/run", "evmbin", - "parity-clib", - "whisper/cli", - "util/triehash-ethereum", - "util/keccak-hasher", - "util/patricia-trie-ethereum", - "util/fastmap", - "util/time-utils" ] - -[patch.crates-io] -heapsize = { git = "https://github.com/cheme/heapsize.git", branch = "ec-macfix" } diff --git a/README.md b/README.md index b244256725..be31c49243 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,12 @@ 3.2 [Building from Source Code](#chapter-0032)
3.3 [Simple One-Line Installer for Mac and Linux](#chapter-0033)
3.4 [Starting Parity Ethereum](#chapter-0034) -4. [Documentation](#chapter-004) -5. [Toolchain](#chapter-005) -6. [Community](#chapter-006) -7. [Contributing](#chapter-007) -8. [License](#chapter-008) +4. [Testing](#chapter-004) +5. [Documentation](#chapter-005) +6. [Toolchain](#chapter-006) +7. [Community](#chapter-007) +8. [Contributing](#chapter-008) +9. [License](#chapter-009) ## 1. Description @@ -41,7 +42,7 @@ By default, Parity Ethereum runs a JSON-RPC HTTP server on port `:8545` and a We If you run into problems while using Parity Ethereum, check out the [wiki for documentation](https://wiki.parity.io/), feel free to [file an issue in this repository](https://github.com/paritytech/parity-ethereum/issues/new), or hop on our [Gitter](https://gitter.im/paritytech/parity) or [Riot](https://riot.im/app/#/group/+parity:matrix.parity.io) chat room to ask a question. We are glad to help! **For security-critical issues**, please refer to the security policy outlined in [SECURITY.md](SECURITY.md). -Parity Ethereum's current beta-release is 2.1. You can download it at [the releases page](https://github.com/paritytech/parity-ethereum/releases) or follow the instructions below to build from source. Please, mind the [CHANGELOG.md](CHANGELOG.md) for a list of all changes between different versions. +Parity Ethereum's current beta-release is 2.6. You can download it at [the releases page](https://github.com/paritytech/parity-ethereum/releases) or follow the instructions below to build from source. Please, mind the [CHANGELOG.md](CHANGELOG.md) for a list of all changes between different versions. ## 3. Building @@ -56,7 +57,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do $ curl https://sh.rustup.rs -sSf | sh ``` - Parity Ethereum also requires `gcc`, `g++`, `libudev-dev`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed. + Parity Ethereum also requires `gcc`, `g++`, `pkg-config`, `file`, `make`, and `cmake` packages to be installed. - OSX: ```bash @@ -65,7 +66,7 @@ We recommend installing Rust through [rustup](https://www.rustup.rs/). If you do `clang` is required. It comes with Xcode command line tools or can be installed with homebrew. -- Windows +- Windows: Make sure you have Visual Studio 2015 with C++ support installed. Next, download and run the `rustup` installer from https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe, start "VS2015 x64 Native Tools Command Prompt", and use the following command to install and set up the `msvc` toolchain: ```bash @@ -148,7 +149,25 @@ To start Parity Ethereum as a regular user using `systemd` init: 2. Copy release to bin folder, write `sudo install ./target/release/parity /usr/bin/parity` 3. To configure Parity Ethereum, write a `/etc/parity/config.toml` config file, see [Configuring Parity Ethereum](https://paritytech.github.io/wiki/Configuring-Parity) for details. -## 4. Documentation +## 4. Testing + +Download the required test files: `git submodule update --init --recursive`. You can run tests with the following commands: + +* **All** packages + ``` + cargo test --all + ``` + +* Specific package + ``` + cargo test --package + ``` + +Replace `` with one of the packages from the [package list](#package-list) (e.g. `cargo test --package evmbin`). + +You can show your logs in the test output by passing `--nocapture` (i.e. `cargo test --package evmbin -- --nocapture`) + +## 5. Documentation Official website: https://parity.io @@ -160,16 +179,20 @@ You can generate documentation for Parity Ethereum Rust packages that automatica * **All** packages ``` - cargo doc --open + cargo doc --document-private-items --open ``` * Specific package ``` - cargo doc --package --open + cargo doc --package -- --document-private-items --open ``` +Use`--document-private-items` to also view private documentation and `--no-deps` to exclude building documentation for dependencies. + Replacing `` with one of the following from the details section below (i.e. `cargo doc --package parity-ethereum --open`): + +**Package List**

* Parity Ethereum (EthCore) Client Application @@ -297,10 +320,6 @@ Caching, Importing Blocks, and Block Information patricia-trie-ethereum registrar rlp_compress rlp_derive parity-runtime stats time-utils triehash-ethereum unexpected parity-version ``` -* Parity Whisper Protocol Implementation - ```bash - parity-whisper whisper-cli - ```

@@ -330,19 +349,19 @@ Example (generic documentation comment): /// ``` -## 5. Toolchain +## 6. Toolchain In addition to the Parity Ethereum client, there are additional tools in this repository available: - [evmbin](./evmbin) - Parity Ethereum EVM Implementation. - [ethstore](./accounts/ethstore) - Parity Ethereum Key Management. - [ethkey](./accounts/ethkey) - Parity Ethereum Keys Generator. -- [whisper](./whisper) - Parity Ethereum Whisper-v2 PoC Implementation. The following tool is available in a separate repository: - [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum Encoding of Function Calls. [Docs here](https://crates.io/crates/ethabi) +- [whisper](https://github.com/paritytech/whisper) - Parity Ethereum Whisper-v2 PoC Implementation. -## 6. Community +## 7. Community ### Join the chat! @@ -355,7 +374,7 @@ Questions? Get in touch with us on Gitter: Alternatively, join our community on Matrix: [![Riot: +Parity](https://img.shields.io/badge/riot-%2Bparity%3Amatrix.parity.io-orange.svg)](https://riot.im/app/#/group/+parity:matrix.parity.io) -## 7. Contributing +## 8. Contributing An introduction has been provided in the ["So You Want to be a Core Developer" presentation slides by Hernando Castano](http://tiny.cc/contrib-to-parity-eth). Additional guidelines are provided in [CONTRIBUTING](./.github/CONTRIBUTING.md). @@ -363,6 +382,6 @@ An introduction has been provided in the ["So You Want to be a Core Developer" p [CODE_OF_CONDUCT](./.github/CODE_OF_CONDUCT.md) -## 8. License +## 9. License [LICENSE](./LICENSE) diff --git a/SECURITY.md b/SECURITY.md index 3f977bd1b0..c5a3f35fdd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -48,33 +48,54 @@ Our Bug Bounty Program allows us to recognise and reward members of the Parity c ``` -----BEGIN PGP PUBLIC KEY BLOCK----- -mQENBFlyIAwBCACe0keNPjgYzZ1Oy/8t3zj/Qw9bHHqrzx7FWy8NbXnYBM19NqOZ -DIP7Oe0DvCaf/uruBskCS0iVstHlEFQ2AYe0Ei0REt9lQdy61GylU/DEB3879IG+ -6FO0SnFeYeerv1/hFI2K6uv8v7PyyVDiiJSW0I1KIs2OBwJicTKmWxLAeQsRgx9G -yRGalrVk4KP+6pWTA7k3DxmDZKZyfYV/Ej10NtuzmsemwDbv98HKeomp/kgFOfSy -3AZjeCpctlsNqpjUuXa0/HudmH2WLxZ0fz8XeoRh8XM9UudNIecjrDqmAFrt/btQ -/3guvlzhFCdhYPVGsUusKMECk/JG+Xx1/1ZjABEBAAG0LFBhcml0eSBTZWN1cml0 -eSBDb250YWN0IDxzZWN1cml0eUBwYXJpdHkuaW8+iQFUBBMBCAA+FiEE2uUVYCjP -N6B8aTiDXQ8DAY0H3nMFAllyIAwCGwMFCQPCZwAFCwkIBwIGFQgJCgsCBBYCAwEC -HgECF4AACgkQXQ8DAY0H3nM60wgAkS3A36Zc+upiaxU7tumcGv+an17j7gin0sif -+0ELSjVfrXInM6ovai+NhUdcLkJ7tCrKS90fvlaELK5Sg9CXBWCTFccKN4A/B7ey -rOg2NPXUecnyBB/XqQgKYH7ujYlOlqBDXMfz6z8Hj6WToxg9PPMGGomyMGh8AWxM -3yRPFs5RKt0VKgN++5N00oly5Y8ri5pgCidDvCLYMGTVDHFKwkuc9w6BlWlu1R1e -/hXFWUFAP1ffTAul3QwyKhjPn2iotCdxXjvt48KaU8DN4iL7aMBN/ZBKqGS7yRdF -D/JbJyaaJ0ZRvFSTSXy/sWY3z1B5mtCPBxco8hqqNfRkCwuZ6LkBDQRZciAMAQgA -8BP8xrwe12TOUTqL/Vrbxv/FLdhKh53J6TrPKvC2TEEKOrTNo5ahRq+XOS5E7G2N -x3b+fq8gR9BzFcldAx0XWUtGs/Wv++ulaSNqTBxj13J3G3WGsUfMKxRgj//piCUD -bCFLQfGZdKk0M1o9QkPVARwwmvCNiNB/l++xGqPtfc44H5jWj3GoGvL2MkShPzrN -yN/bJ+m+R5gtFGdInqa5KXBuxxuW25eDKJ+LzjbgUgeC76wNcfOiQHTdMkcupjdO -bbGFwo10hcbRAOcZEv6//Zrlmk/6nPxEd2hN20St2bSN0+FqfZ267mWEu3ejsgF8 -ArdCpv5h4fBvJyNwiTZwIQARAQABiQE8BBgBCAAmFiEE2uUVYCjPN6B8aTiDXQ8D -AY0H3nMFAllyIAwCGwwFCQPCZwAACgkQXQ8DAY0H3nNisggAl4fqhRlA34wIb190 -sqXHVxiCuzPaqS6krE9xAa1+gncX485OtcJNqnjugHm2rFE48lv7oasviuPXuInE -/OgVFnXYv9d/Xx2JUeDs+bFTLouCDRY2Unh7KJZasfqnMcCHWcxHx5FvRNZRssaB -WTZVo6sizPurGUtbpYe4/OLFhadBqAE0EUmVRFEUMc1YTnu4eLaRBzoWN4d2UWwi -LN25RSrVSke7LTSFbgn9ntQrQ2smXSR+cdNkkfRCjFcpUaecvFl9HwIqoyVbT4Ym -0hbpbbX/cJdc91tKa+psa29uMeGL/cgL9fAu19yNFRyOTMxjZnvql1X/WE1pLmoP -ETBD1Q== -=K9Qw +mQINBF0vHwQBEADKui4qAo4bzdzRhMm+uhUpYGf8jjjmET3zJ8kKQIpp6JTsV+HJ +6m1We0QYeMRXoOYH1xVHBf2zNCuHS0nSQdUCQA7SHWsPB05STa2hvlR7fSdQnCCp +gnLOJWXvvedlRDIAhvqI6cwLdUlXgVSKEwrwmrpiBhh4NxI3qX+LyIa+Ovkchu2S +d/YCnE4GqojSGRfJYiGwe2N+sF7OfaoKhQuTrtdDExHrMU4cWnTXW2wyxTr4xkj9 +jS2WeLVZWflvkDHT8JD9N6jNxBVEF/Qvjk83zI0kCOzkhek8x+YUgfLq3/rHOYbX +3pW21ccHYPacHjHWvKE+xRebjeEhJ4KxKHfCVjQcxybwDBqDka1AniZt4CQ7UORf +MU/ue2oSZ9nNg0uMdb/0AbQPZ04OlMcYPAPWzFL08nVPox9wT9uqlL6JtcOeC90h +oOeDmfgwmjMmdwWTRgt9qQjcbgXzVvuAzIGbzj1X3MdLspWdHs/d2+US4nji1TkN +oYIW7vE+xkd3aB+NZunIlm9Rwd/0mSgDg+DaNa5KceOLhq0/qKgcXC/RRU29I8II +tusRoR/oesGJGYTjh4k6PJkG+nvDPsoQrwYT44bhnniS1xYkxWYXF99JFI7LgMdD +e1SgKeIDVpvm873k82E6arp5655Wod1XOjaXBggCwFp84eKcEZEN+1qEWwARAQAB +tClQYXJpdHkgU2VjdXJpdHkgVGVhbSA8c2VjdXJpdHlAcGFyaXR5LmlvPokCVAQT +AQoAPhYhBJ1LK264+XFW0ZZpqf8IEtSRuWeYBQJdLx8EAhsDBQkDwmcABQsJCAcC +BhUKCQgLAgQWAgMBAh4BAheAAAoJEP8IEtSRuWeYL84QAI6NwnwS561DWYYRAd4y +ocGPr3CnwFSt1GjkSkRy3B+tMhzexBg1y7EbLRUefIrO4LwOlywtRk8tTRGgEI4i +5xRLHbOkeolfgCFSpOj5d8cMKCt5HEIv18hsv6dkrzlSYA5NLX/GRBEh3F/0sGny +vCXapfxa1cx72sU7631JBK7t2Tf+MfwxdfyFZ9TI9WdtP5AfVjgTkIVkEDFcZPTc +n3CYXqTYFIBCNUD8LP4iTi3xUt7pTGJQQoFT8l15nJCgzRYQ+tXpoTRlf+/LtXmw +6iidPV87E06jHdK9666rBouIabAtx7i0/4kwo+bSZ8DiSKRUaehiHGd212HSEmdF +jxquWE4pEzoUowYznhSIfR+WWIqRBHxEYarP4m98Hi+VXZ7Fw1ytzO8+BAKnLXnj +2W2+T9qJks5gqVEoaWNnqpvya6JA11QZvZ0w7Om2carDc2ILNm2Xx9J0mRUye8P0 +KxcgqJuKNGFtugebQAsXagkxOKsdKna1PlDlxEfTf6AgI3ST8qSiMAwaaIMB/REF +VKUapGoslQX4tOCjibI2pzEgE//D8NAaSVu2A9+BUcFERdZRxsI7fydIXNeZ2R46 +N2qfW+DP3YR/14QgdRxDItEavUoE1vByRXwIufKAkVemOZzIoFXKFsDeXwqTVW5i +6CXu6OddZ3QHDiT9TEbRny4QuQINBF0vKCwBEACnP5J7LEGbpxNBrPvGdxZUo0YA +U8RgeKDRPxJTvMo27V1IPZGaKRCRq8LBfg/eHhqZhQ7SLJBjBljd8kuT5dHDBTRe +jE1UIOhmnlSlrEJjAmpVO08irlGpq1o+8mGcvkBsR0poCVjeNeSnwYfRnR+c3GK5 +Er6/JRqfN4mJvnEC9/Pbm6C7ql6YLKxC3yqzF97JL5brbbuozrW7nixY/yAI8619 +VlBIMP7PAUbGcnSQyuV5b/Wr2Sgr6NJclnNSLjh2U9/Du6w/0tDGlMBts8HjRnWJ +BXbkTdQKCTaqgK68kTKSiN1/x+lynxHC2AavMpH/08Kopg2ZCzJowMKIgcB+4Z/I +DJKZWHWKumhaZMGXcWgzgcByog9IpamuROEZFJNEUAFf7YIncEckPSif4looiOdS +VurKZGvYXXaGSsZbGgHxI5CWu7ZxMdLBLvtOcCYmRQrG+g/h+PGU5BT0bNAfNTkm +V3/n1B/TWbpWRmB3AwT2emQivXHkaubGI0VivhaO43AuI9JWoqiMqFtxbuTeoxwD +xlu2Dzcp0v+AR4T5cIG9D5/+yiPc25aIY7cIKxuNFHIDL4td5fwSGC7vU6998PIG +2Y48TGBnw7zpEfDfMayqAeBjX0YU6PTNsvS5O6bP3j4ojTOUYD7Z8QdCvgISDID3 +WMGAdmSwmCRvsQ/OJwARAQABiQI8BBgBCgAmFiEEnUsrbrj5cVbRlmmp/wgS1JG5 +Z5gFAl0vKCwCGwwFCQB2pwAACgkQ/wgS1JG5Z5hdbw//ZqR+JcWm59NUIHjauETJ +sYDYhcAfa3txTacRn5uPz/TQiTd7wZ82+G8Et0ZnpEHy6eWyBqHpG0hiPhFBzxjY +nhjHl8jJeyo2mQIVJhzkL58BHBZk8WM2TlaU7VxZ6TYOmP2y3qf6FD6mCcrQ4Fml +E9f0lyVUoI/5Zs9oF0izRk8vkwaY3UvLM7XEY6nM8GnFG8kaiZMYmx26Zo7Uz31G +7EGGZFsrVDXfNhSJyz79Gyn+Lx9jOTdoR0sH/THYIIosE83awMGE6jKeuDYTbVWu ++ZtHQef+pRteki3wvNLJK+kC1y3BtHqDJS9Lqx0s8SCiVozlC+fZfC9hCtU7bXJK +0UJZ4qjSvj6whzfaNgOZAqJpmwgOnd8W/3YJk1DwUeX98FcU38MR23SOkx2EDdDE +77Kdu62vTs/tLmOTuyKBvYPaHaYulYjQTxurG+o8vhHtaL87ARvuq+83dj+nO5z3 +5O9vkcVJYWjOEnJe7ZvCTxeLJehpCmHIbyUuDx5P24MWVbyXOxIlxNxTqlub5GlW +rQF6Qsa/0k9TRk7Htbct6fAA0/VahJS0g096MrTH8AxBXDNE8lIoNeGikVlaxK9Z +S+aannlWYIJymZ4FygIPPaRlzhAoXBuJd8OaR5giC7dS1xquxKOiQEXTGsLeGFaI +BZYiIhW7GG4ozvKDqyNm4eg= +=yKcB -----END PGP PUBLIC KEY BLOCK----- ``` diff --git a/accounts/Cargo.toml b/accounts/Cargo.toml index 12100df6ed..7e63f9f584 100644 --- a/accounts/Cargo.toml +++ b/accounts/Cargo.toml @@ -8,21 +8,15 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -common-types = { path = "../ethcore/types" } ethkey = { path = "ethkey" } ethstore = { path = "ethstore" } log = "0.4" -parking_lot = "0.7" +parity-crypto = { version = "0.4.2", features = ["publickey"] } +parking_lot = "0.9" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" -[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dependencies] -hardware-wallet = { path = "hw" } - -[target.'cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))'.dependencies] -fake-hardware-wallet = { path = "fake-hardware-wallet" } - [dev-dependencies] -ethereum-types = "0.4" +ethereum-types = "0.8.0" tempdir = "0.3" diff --git a/accounts/ethkey/Cargo.toml b/accounts/ethkey/Cargo.toml index c10949622c..8118ad1637 100644 --- a/accounts/ethkey/Cargo.toml +++ b/accounts/ethkey/Cargo.toml @@ -1,21 +1,13 @@ [package] description = "Parity Ethereum Keys Generator" name = "ethkey" -version = "0.3.0" +version = "0.4.0" authors = ["Parity Technologies "] [dependencies] edit-distance = "2.0" -parity-crypto = "0.3.0" -eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" } -ethereum-types = "0.4" -lazy_static = "1.0" log = "0.4" -memzero = { path = "../../util/memzero" } -parity-wordlist = "1.3" -quick-error = "1.2.2" -rand = "0.4" -rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" -tiny-keccak = "1.4" +parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-wordlist = "1.3" diff --git a/accounts/ethkey/README.md b/accounts/ethkey/README.md index 23c57fa4ca..a21992db0c 100644 --- a/accounts/ethkey/README.md +++ b/accounts/ethkey/README.md @@ -6,7 +6,7 @@ Parity Ethereum keys generator. ``` Parity Ethereum Keys Generator. - Copyright 2015-2019 Parity Technologies (UK) Ltd. + Copyright 2015-2020 Parity Technologies (UK) Ltd. Usage: ethkey info [options] @@ -218,4 +218,4 @@ _This project is a part of the Parity Ethereum toolchain._ - [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding. - [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management. - [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator. -- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC. +- [whisper](https://github.com/paritytech/whisper) - Implementation of Whisper-v2 PoC. diff --git a/accounts/ethkey/cli/Cargo.toml b/accounts/ethkey/cli/Cargo.toml index c1d44897cc..f9e616ce9a 100644 --- a/accounts/ethkey/cli/Cargo.toml +++ b/accounts/ethkey/cli/Cargo.toml @@ -9,7 +9,8 @@ docopt = "1.0" env_logger = "0.5" ethkey = { path = "../" } panic_hook = { path = "../../../util/panic-hook" } -parity-wordlist="1.3" +parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-wordlist="1.2" rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" diff --git a/accounts/ethkey/cli/src/main.rs b/accounts/ethkey/cli/src/main.rs index 759f5f484c..a2f26f34a0 100644 --- a/accounts/ethkey/cli/src/main.rs +++ b/accounts/ethkey/cli/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,6 +19,7 @@ extern crate env_logger; extern crate ethkey; extern crate panic_hook; extern crate parity_wordlist; +extern crate parity_crypto; extern crate rustc_hex; extern crate serde; extern crate threadpool; @@ -30,12 +31,13 @@ use std::num::ParseIntError; use std::{env, fmt, process, io, sync}; use docopt::Docopt; -use ethkey::{KeyPair, Random, Brain, BrainPrefix, Prefix, Error as EthkeyError, Generator, sign, verify_public, verify_address, brain_recover}; +use ethkey::{Brain, BrainPrefix, Prefix, brain_recover}; +use parity_crypto::publickey::{KeyPair, Random, Error as EthkeyError, Generator, sign, verify_public, verify_address}; use rustc_hex::{FromHex, FromHexError}; const USAGE: &'static str = r#" Parity Ethereum keys generator. - Copyright 2015-2019 Parity Technologies (UK) Ltd. + Copyright 2015-2020 Parity Technologies (UK) Ltd. Usage: ethkey info [options] @@ -200,7 +202,7 @@ fn execute(command: I) -> Result where I: IntoIterator(command: I) -> Result where I: IntoIterator(command: I) -> Result where I: IntoIterator. -use keccak::Keccak256; -use super::{KeyPair, Generator, Secret}; +use std::convert::Infallible; +use parity_crypto::publickey::{KeyPair, Generator, Secret}; +use parity_crypto::Keccak256; use parity_wordlist; /// Simple brainwallet. @@ -32,7 +33,7 @@ impl Brain { } impl Generator for Brain { - type Error = ::Void; + type Error = Infallible; fn generate(&mut self) -> Result { let seed = self.0.clone(); @@ -45,7 +46,7 @@ impl Generator for Brain { match i > 16384 { false => i += 1, true => { - if let Ok(pair) = Secret::from_unsafe_slice(&secret) + if let Ok(pair) = Secret::import_key(&secret) .and_then(KeyPair::from_secret) { if pair.address()[0] == 0 { @@ -61,7 +62,8 @@ impl Generator for Brain { #[cfg(test)] mod tests { - use {Brain, Generator}; + use Brain; + use parity_crypto::publickey::Generator; #[test] fn test_brain() { diff --git a/accounts/ethkey/src/brain_prefix.rs b/accounts/ethkey/src/brain_prefix.rs index ba0d812961..1fdd753fb5 100644 --- a/accounts/ethkey/src/brain_prefix.rs +++ b/accounts/ethkey/src/brain_prefix.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,7 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use super::{Generator, KeyPair, Error, Brain}; +use super::Brain; +use parity_crypto::publickey::{Generator, KeyPair, Error}; use parity_wordlist as wordlist; /// Tries to find brain-seed keypair with address starting with given prefix. @@ -47,7 +48,7 @@ impl Generator for BrainPrefix { for _ in 0..self.iterations { let phrase = wordlist::random_phrase(self.no_of_words); let keypair = Brain::new(phrase.clone()).generate().unwrap(); - if keypair.address().starts_with(&self.prefix) { + if keypair.address().as_ref().starts_with(&self.prefix) { self.last_phrase = phrase; return Ok(keypair) } @@ -59,12 +60,13 @@ impl Generator for BrainPrefix { #[cfg(test)] mod tests { - use {Generator, BrainPrefix}; + use BrainPrefix; + use parity_crypto::publickey::Generator; #[test] fn prefix_generator() { let prefix = vec![0x00u8]; let keypair = BrainPrefix::new(prefix.clone(), usize::max_value(), 12).generate().unwrap(); - assert!(keypair.address().starts_with(&prefix)); + assert!(keypair.address().as_bytes().starts_with(&prefix)); } } diff --git a/accounts/ethkey/src/brain_recover.rs b/accounts/ethkey/src/brain_recover.rs index f9922fae97..e9673c843e 100644 --- a/accounts/ethkey/src/brain_recover.rs +++ b/accounts/ethkey/src/brain_recover.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,8 @@ use std::collections::HashSet; use edit_distance::edit_distance; use parity_wordlist; -use super::{Address, Brain, Generator}; +use super::Brain; +use parity_crypto::publickey::{Address, Generator}; /// Tries to find a phrase for address, given the number /// of expected words and a partial phrase. diff --git a/accounts/ethkey/src/crypto.rs b/accounts/ethkey/src/crypto.rs deleted file mode 100644 index ec883dcb6d..0000000000 --- a/accounts/ethkey/src/crypto.rs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use secp256k1; -use std::io; -use parity_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 parity_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/accounts/ethkey/src/error.rs b/accounts/ethkey/src/error.rs deleted file mode 100644 index ee19115745..0000000000 --- a/accounts/ethkey/src/error.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::{fmt, error}; - -#[derive(Debug)] -/// Crypto error -pub enum Error { - /// Invalid secret key - InvalidSecret, - /// Invalid public key - InvalidPublic, - /// Invalid address - InvalidAddress, - /// Invalid EC signature - InvalidSignature, - /// Invalid AES message - InvalidMessage, - /// IO Error - Io(::std::io::Error), - /// Custom - Custom(String), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let msg = match *self { - Error::InvalidSecret => "Invalid secret".into(), - Error::InvalidPublic => "Invalid public".into(), - Error::InvalidAddress => "Invalid address".into(), - Error::InvalidSignature => "Invalid EC signature".into(), - Error::InvalidMessage => "Invalid AES message".into(), - Error::Io(ref err) => format!("I/O error: {}", err), - Error::Custom(ref s) => s.clone(), - }; - - f.write_fmt(format_args!("Crypto error ({})", msg)) - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - "Crypto error" - } -} - -impl Into for Error { - fn into(self) -> String { - format!("{}", self) - } -} - -impl From<::secp256k1::Error> for Error { - fn from(e: ::secp256k1::Error) -> Error { - match e { - ::secp256k1::Error::InvalidMessage => Error::InvalidMessage, - ::secp256k1::Error::InvalidPublicKey => Error::InvalidPublic, - ::secp256k1::Error::InvalidSecretKey => Error::InvalidSecret, - _ => Error::InvalidSignature, - } - } -} - -impl From<::std::io::Error> for Error { - fn from(err: ::std::io::Error) -> Error { - Error::Io(err) - } -} diff --git a/accounts/ethkey/src/extended.rs b/accounts/ethkey/src/extended.rs deleted file mode 100644 index 401d98f2f6..0000000000 --- a/accounts/ethkey/src/extended.rs +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Extended keys - -use secret::Secret; -use Public; -use ethereum_types::H256; -pub use self::derivation::Error as DerivationError; - -/// Represents label that can be stored as a part of key derivation -pub trait Label { - /// Length of the data that label occupies - fn len() -> usize; - - /// Store label data to the key derivation sequence - /// Must not use more than `len()` bytes from slice - fn store(&self, target: &mut [u8]); -} - -impl Label for u32 { - fn len() -> usize { 4 } - - fn store(&self, target: &mut [u8]) { - let bytes = self.to_be_bytes(); - target[0..4].copy_from_slice(&bytes); - } -} - -/// Key derivation over generic label `T` -pub enum Derivation { - /// Soft key derivation (allow proof of parent) - Soft(T), - /// Hard key derivation (does not allow proof of parent) - Hard(T), -} - -impl From for Derivation { - fn from(index: u32) -> Self { - if index < (2 << 30) { - Derivation::Soft(index) - } - else { - Derivation::Hard(index) - } - } -} - -impl Label for H256 { - fn len() -> usize { 32 } - - fn store(&self, target: &mut [u8]) { - self.copy_to(&mut target[0..32]); - } -} - -/// Extended secret key, allows deterministic derivation of subsequent keys. -pub struct ExtendedSecret { - secret: Secret, - chain_code: H256, -} - -impl ExtendedSecret { - /// New extended key from given secret and chain code. - pub fn with_code(secret: Secret, chain_code: H256) -> ExtendedSecret { - ExtendedSecret { - secret: secret, - chain_code: chain_code, - } - } - - /// New extended key from given secret with the random chain code. - pub fn new_random(secret: Secret) -> ExtendedSecret { - ExtendedSecret::with_code(secret, H256::random()) - } - - /// New extended key from given secret. - /// Chain code will be derived from the secret itself (in a deterministic way). - pub fn new(secret: Secret) -> ExtendedSecret { - let chain_code = derivation::chain_code(*secret); - ExtendedSecret::with_code(secret, chain_code) - } - - /// Derive new private key - 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(derived_key.0); - - ExtendedSecret::with_code(derived_secret, next_chain_code) - } - - /// Private key component of the extended key. - pub fn as_raw(&self) -> &Secret { - &self.secret - } -} - -/// Extended public key, allows deterministic derivation of subsequent keys. -pub struct ExtendedPublic { - public: Public, - chain_code: H256, -} - -impl ExtendedPublic { - /// New extended public key from known parent and chain code - pub fn new(public: Public, chain_code: H256) -> Self { - ExtendedPublic { public: public, chain_code: chain_code } - } - - /// Create new extended public key from known secret - pub fn from_secret(secret: &ExtendedSecret) -> Result { - Ok( - ExtendedPublic::new( - derivation::point(**secret.as_raw())?, - secret.chain_code.clone(), - ) - ) - } - - /// Derive new public key - /// Operation is defined only for index belongs [0..2^31) - pub fn derive(&self, index: Derivation) -> Result where T: Label { - let (derived_key, next_chain_code) = derivation::public(self.public, self.chain_code, index)?; - Ok(ExtendedPublic::new(derived_key, next_chain_code)) - } - - pub fn public(&self) -> &Public { - &self.public - } -} - -pub struct ExtendedKeyPair { - secret: ExtendedSecret, - public: ExtendedPublic, -} - -impl ExtendedKeyPair { - pub fn new(secret: Secret) -> Self { - let extended_secret = ExtendedSecret::new(secret); - let extended_public = ExtendedPublic::from_secret(&extended_secret) - .expect("Valid `Secret` always produces valid public; qed"); - ExtendedKeyPair { - secret: extended_secret, - public: extended_public, - } - } - - pub fn with_code(secret: Secret, public: Public, chain_code: H256) -> Self { - ExtendedKeyPair { - secret: ExtendedSecret::with_code(secret, chain_code.clone()), - public: ExtendedPublic::new(public, chain_code), - } - } - - pub fn with_secret(secret: Secret, chain_code: H256) -> Self { - let extended_secret = ExtendedSecret::with_code(secret, chain_code); - let extended_public = ExtendedPublic::from_secret(&extended_secret) - .expect("Valid `Secret` always produces valid public; qed"); - ExtendedKeyPair { - secret: extended_secret, - public: extended_public, - } - } - - pub fn with_seed(seed: &[u8]) -> Result { - let (master_key, chain_code) = derivation::seed_pair(seed); - Ok(ExtendedKeyPair::with_secret( - Secret::from_unsafe_slice(&*master_key).map_err(|_| DerivationError::InvalidSeed)?, - chain_code, - )) - } - - pub fn secret(&self) -> &ExtendedSecret { - &self.secret - } - - pub fn public(&self) -> &ExtendedPublic { - &self.public - } - - pub fn derive(&self, index: Derivation) -> Result where T: Label { - let derived = self.secret.derive(index); - - Ok(ExtendedKeyPair { - public: ExtendedPublic::from_secret(&derived)?, - secret: derived, - }) - } -} - -// Derivation functions for private and public keys -// Work is based on BIP0032 -// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki -mod derivation { - use parity_crypto::hmac; - use ethereum_types::{U256, U512, H512, H256}; - use secp256k1::key::{SecretKey, PublicKey}; - use SECP256K1; - use keccak; - use math::curve_order; - use super::{Label, Derivation}; - - #[derive(Debug)] - pub enum Error { - InvalidHardenedUse, - InvalidPoint, - MissingIndex, - InvalidSeed, - } - - // Deterministic derivation of the key using secp256k1 elliptic curve. - // Derivation can be either hardened or not. - // For hardened derivation, pass u32 index at least 2^31 or custom Derivation::Hard(T) enum - // - // Can panic if passed `private_key` is not a valid secp256k1 private key - // (outside of (0..curve_order()]) field - pub fn private(private_key: H256, chain_code: H256, index: Derivation) -> (H256, H256) where T: Label { - match index { - Derivation::Soft(index) => private_soft(private_key, chain_code, index), - Derivation::Hard(index) => private_hard(private_key, chain_code, index), - } - } - - fn hmac_pair(data: &[u8], private_key: H256, chain_code: H256) -> (H256, H256) { - let private: U256 = private_key.into(); - - // produces 512-bit derived hmac (I) - 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(); - // right most 256 bits are new chain code for later derivations - let next_chain_code = H256::from(&i_512[32..64]); - - let child_key = private_add(hmac_key, private).into(); - (child_key, next_chain_code) - } - - // Can panic if passed `private_key` is not a valid secp256k1 private key - // (outside of (0..curve_order()]) field - fn private_soft(private_key: H256, chain_code: H256, index: T) -> (H256, H256) where T: Label { - let mut data = vec![0u8; 33 + T::len()]; - - let sec_private = SecretKey::from_slice(&SECP256K1, &*private_key) - .expect("Caller should provide valid private key"); - let sec_public = PublicKey::from_secret_key(&SECP256K1, &sec_private) - .expect("Caller should provide valid private key"); - let public_serialized = sec_public.serialize_vec(&SECP256K1, true); - - // curve point (compressed public key) -- index - // 0.33 -- 33..end - data[0..33].copy_from_slice(&public_serialized); - index.store(&mut data[33..]); - - hmac_pair(&data, private_key, chain_code) - } - - // Deterministic derivation of the key using secp256k1 elliptic curve - // This is hardened derivation and does not allow to associate - // corresponding public keys of the original and derived private keys - fn private_hard(private_key: H256, chain_code: H256, index: T) -> (H256, H256) where T: Label { - let mut data: Vec = vec![0u8; 33 + T::len()]; - let private: U256 = private_key.into(); - - // 0x00 (padding) -- private_key -- index - // 0 -- 1..33 -- 33..end - private.to_big_endian(&mut data[1..33]); - index.store(&mut data[33..(33 + T::len())]); - - hmac_pair(&data, private_key, chain_code) - } - - fn private_add(k1: U256, k2: U256) -> U256 { - let sum = U512::from(k1) + U512::from(k2); - modulo(sum, curve_order()) - } - - // todo: surely can be optimized - fn modulo(u1: U512, u2: U256) -> U256 { - let dv = u1 / U512::from(u2); - let md = u1 - (dv * U512::from(u2)); - md.into() - } - - pub fn public(public_key: H512, chain_code: H256, derivation: Derivation) -> Result<(H512, H256), Error> where T: Label { - let index = match derivation { - Derivation::Soft(index) => index, - Derivation::Hard(_) => { return Err(Error::InvalidHardenedUse); } - }; - - let mut public_sec_raw = [0u8; 65]; - public_sec_raw[0] = 4; - public_sec_raw[1..65].copy_from_slice(&*public_key); - let public_sec = PublicKey::from_slice(&SECP256K1, &public_sec_raw).map_err(|_| Error::InvalidPoint)?; - let public_serialized = public_sec.serialize_vec(&SECP256K1, true); - - let mut data = vec![0u8; 33 + T::len()]; - // curve point (compressed public key) -- index - // 0.33 -- 33..end - data[0..33].copy_from_slice(&public_serialized); - index.store(&mut data[33..(33 + T::len())]); - - // HMAC512SHA produces [derived private(256); new chain code(256)] - 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]); - - // Generated private key can (extremely rarely) be out of secp256k1 key field - if curve_order() <= new_private.clone().into() { return Err(Error::MissingIndex); } - let new_private_sec = SecretKey::from_slice(&SECP256K1, &*new_private) - .expect("Private key belongs to the field [0..CURVE_ORDER) (checked above); So initializing can never fail; qed"); - let mut new_public = PublicKey::from_secret_key(&SECP256K1, &new_private_sec) - .expect("Valid private key produces valid public key"); - - // Adding two points on the elliptic curves (combining two public keys) - new_public.add_assign(&SECP256K1, &public_sec) - .expect("Addition of two valid points produce valid point"); - - let serialized = new_public.serialize_vec(&SECP256K1, false); - - Ok(( - H512::from(&serialized[1..65]), - new_chain_code, - )) - } - - fn sha3(slc: &[u8]) -> H256 { - keccak::Keccak256::keccak256(slc).into() - } - - pub fn chain_code(secret: H256) -> H256 { - // 10,000 rounds of sha3 - let mut running_sha3 = sha3(&*secret); - for _ in 0..99999 { running_sha3 = sha3(&*running_sha3); } - running_sha3 - } - - pub fn point(secret: H256) -> Result { - let sec = SecretKey::from_slice(&SECP256K1, &*secret) - .map_err(|_| Error::InvalidPoint)?; - let public_sec = PublicKey::from_secret_key(&SECP256K1, &sec) - .map_err(|_| Error::InvalidPoint)?; - let serialized = public_sec.serialize_vec(&SECP256K1, false); - Ok(H512::from(&serialized[1..65])) - } - - pub fn seed_pair(seed: &[u8]) -> (H256, H256) { - 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]); - - (master_key, chain_code) - } -} - -#[cfg(test)] -mod tests { - use super::{ExtendedSecret, ExtendedPublic, ExtendedKeyPair}; - use secret::Secret; - use std::str::FromStr; - use ethereum_types::{H128, H256}; - use super::{derivation, Derivation}; - - fn master_chain_basic() -> (H256, H256) { - let seed = H128::from_str("000102030405060708090a0b0c0d0e0f") - .expect("Seed should be valid H128") - .to_vec(); - - derivation::seed_pair(&*seed) - } - - 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(private_seed.0), chain_code); - let derived = f(extended_secret); - assert_eq!(**derived.as_raw(), test_private); - } - - #[test] - fn smoky() { - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into()); - - // hardened - assert_eq!(&**extended_secret.as_raw(), &*secret); - assert_eq!(&**extended_secret.derive(2147483648.into()).as_raw(), &"0927453daed47839608e414a3738dfad10aed17c459bbd9ab53f89b026c834b6".into()); - assert_eq!(&**extended_secret.derive(2147483649.into()).as_raw(), &"44238b6a29c6dcbe9b401364141ba11e2198c289a5fed243a1c11af35c19dc0f".into()); - - // normal - assert_eq!(&**extended_secret.derive(0.into()).as_raw(), &"bf6a74e3f7b36fc4c96a1e12f31abc817f9f5904f5a8fc27713163d1f0b713f6".into()); - assert_eq!(&**extended_secret.derive(1.into()).as_raw(), &"bd4fca9eb1f9c201e9448c1eecd66e302d68d4d313ce895b8c134f512205c1bc".into()); - assert_eq!(&**extended_secret.derive(2.into()).as_raw(), &"86932b542d6cab4d9c65490c7ef502d89ecc0e2a5f4852157649e3251e2a3268".into()); - - let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); - let derived_public = extended_public.derive(0.into()).expect("First derivation of public should succeed"); - assert_eq!(&*derived_public.public(), &"f7b3244c96688f92372bfd4def26dc4151529747bab9f188a4ad34e141d47bd66522ff048bc6f19a0a4429b04318b1a8796c000265b4fa200dae5f6dda92dd94".into()); - - let keypair = ExtendedKeyPair::with_secret( - Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(), - 064.into(), - ); - assert_eq!(&**keypair.derive(2147483648u32.into()).expect("Derivation of keypair should succeed").secret().as_raw(), &"edef54414c03196557cf73774bc97a645c9a1df2164ed34f0c2a78d1375a930c".into()); - } - - #[test] - fn h256_soft_match() { - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let derivation_secret = H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015").unwrap(); - - let extended_secret = ExtendedSecret::with_code(secret.clone(), 0u64.into()); - let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); - - let derived_secret0 = extended_secret.derive(Derivation::Soft(derivation_secret)); - let derived_public0 = extended_public.derive(Derivation::Soft(derivation_secret)).expect("First derivation of public should succeed"); - - let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0).expect("Extended public should be created"); - - assert_eq!(public_from_secret0.public(), derived_public0.public()); - } - - #[test] - fn h256_hard() { - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let derivation_secret = H256::from_str("51eaf04f9dbbc1417dc97e789edd0c37ecda88bac490434e367ea81b71b7b015").unwrap(); - let extended_secret = ExtendedSecret::with_code(secret.clone(), 1u64.into()); - - assert_eq!(&**extended_secret.derive(Derivation::Hard(derivation_secret)).as_raw(), &"2bc2d696fb744d77ff813b4a1ef0ad64e1e5188b622c54ba917acc5ebc7c5486".into()); - } - - #[test] - fn match_() { - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let extended_secret = ExtendedSecret::with_code(secret.clone(), 1.into()); - let extended_public = ExtendedPublic::from_secret(&extended_secret).expect("Extended public should be created"); - - let derived_secret0 = extended_secret.derive(0.into()); - let derived_public0 = extended_public.derive(0.into()).expect("First derivation of public should succeed"); - - let public_from_secret0 = ExtendedPublic::from_secret(&derived_secret0).expect("Extended public should be created"); - - assert_eq!(public_from_secret0.public(), derived_public0.public()); - } - - #[test] - fn test_seeds() { - let seed = H128::from_str("000102030405060708090a0b0c0d0e0f") - .expect("Seed should be valid H128") - .to_vec(); - - // private key from bitcoin test vector - // xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs - let test_private = H256::from_str("e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35") - .expect("Private should be decoded ok"); - - let (private_seed, _) = derivation::seed_pair(&*seed); - - assert_eq!(private_seed, test_private); - } - - #[test] - fn test_vector_1() { - // xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7 - // H(0) - test_extended( - |secret| secret.derive(2147483648.into()), - H256::from_str("edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea") - .expect("Private should be decoded ok") - ); - } - - #[test] - fn test_vector_2() { - // xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs - // H(0)/1 - test_extended( - |secret| secret.derive(2147483648.into()).derive(1.into()), - H256::from_str("3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368") - .expect("Private should be decoded ok") - ); - } -} diff --git a/accounts/ethkey/src/keypair.rs b/accounts/ethkey/src/keypair.rs deleted file mode 100644 index 2919f0cfb7..0000000000 --- a/accounts/ethkey/src/keypair.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::fmt; -use secp256k1::key; -use rustc_hex::ToHex; -use keccak::Keccak256; -use super::{Secret, Public, Address, SECP256K1, Error}; - -pub fn public_to_address(public: &Public) -> Address { - let hash = public.keccak256(); - let mut result = Address::default(); - result.copy_from_slice(&hash[12..]); - result -} - -#[derive(Debug, Clone, PartialEq)] -/// secp256k1 key pair -pub struct KeyPair { - secret: Secret, - public: Public, -} - -impl fmt::Display for KeyPair { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - writeln!(f, "secret: {}", self.secret.to_hex())?; - writeln!(f, "public: {}", self.public.to_hex())?; - write!(f, "address: {}", self.address().to_hex()) - } -} - -impl KeyPair { - /// Create a pair from secret key - pub fn from_secret(secret: Secret) -> Result { - let context = &SECP256K1; - let s: key::SecretKey = key::SecretKey::from_slice(context, &secret[..])?; - let pub_key = key::PublicKey::from_secret_key(context, &s)?; - let serialized = pub_key.serialize_vec(context, false); - - let mut public = Public::default(); - public.copy_from_slice(&serialized[1..65]); - - let keypair = KeyPair { - secret: secret, - public: public, - }; - - Ok(keypair) - } - - pub fn from_secret_slice(slice: &[u8]) -> Result { - Self::from_secret(Secret::from_unsafe_slice(slice)?) - } - - pub fn from_keypair(sec: key::SecretKey, publ: key::PublicKey) -> Self { - let context = &SECP256K1; - let serialized = publ.serialize_vec(context, false); - let secret = Secret::from(sec); - let mut public = Public::default(); - public.copy_from_slice(&serialized[1..65]); - - KeyPair { - secret: secret, - public: public, - } - } - - pub fn secret(&self) -> &Secret { - &self.secret - } - - pub fn public(&self) -> &Public { - &self.public - } - - pub fn address(&self) -> Address { - public_to_address(&self.public) - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - use {KeyPair, Secret}; - - #[test] - fn from_secret() { - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let _ = KeyPair::from_secret(secret).unwrap(); - } - - #[test] - fn keypair_display() { - let expected = -"secret: a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65 -public: 8ce0db0b0359ffc5866ba61903cc2518c3675ef2cf380a7e54bde7ea20e6fa1ab45b7617346cd11b7610001ee6ae5b0155c41cad9527cbcdff44ec67848943a4 -address: 5b073e9233944b5e729e46d618f0d8edf3d9c34a".to_owned(); - let secret = Secret::from_str("a100df7a048e50ed308ea696dc600215098141cb391e9527329df289f9383f65").unwrap(); - let kp = KeyPair::from_secret(secret).unwrap(); - assert_eq!(format!("{}", kp), expected); - } -} diff --git a/accounts/ethkey/src/lib.rs b/accounts/ethkey/src/lib.rs index 2a1968bbea..a6e8f9a36b 100644 --- a/accounts/ethkey/src/lib.rs +++ b/accounts/ethkey/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,19 +18,9 @@ extern crate edit_distance; extern crate parity_crypto; -extern crate ethereum_types; -extern crate memzero; extern crate parity_wordlist; -#[macro_use] -extern crate quick_error; -extern crate rand; -extern crate rustc_hex; -extern crate secp256k1; extern crate serde; -extern crate tiny_keccak; -#[macro_use] -extern crate lazy_static; #[macro_use] extern crate log; #[macro_use] @@ -38,50 +28,13 @@ extern crate serde_derive; mod brain; mod brain_prefix; -mod error; -mod keypair; -mod keccak; mod password; mod prefix; -mod random; -mod signature; -mod secret; -mod extended; pub mod brain_recover; -pub mod crypto; -pub mod math; pub use self::parity_wordlist::Error as WordlistError; pub use self::brain::Brain; pub use self::brain_prefix::BrainPrefix; -pub use self::error::Error; -pub use self::keypair::{KeyPair, public_to_address}; -pub use self::math::public_is_valid; pub use self::password::Password; -pub use self::prefix::Prefix; -pub use self::random::Random; -pub use self::signature::{sign, verify_public, verify_address, recover, Signature}; -pub use self::secret::Secret; -pub use self::extended::{ExtendedPublic, ExtendedSecret, ExtendedKeyPair, DerivationError, Derivation}; - -use ethereum_types::H256; - -pub use ethereum_types::{Address, Public}; -pub type Message = H256; - -lazy_static! { - pub static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new(); -} - -/// Uninstantiatable error type for infallible generators. -#[derive(Debug)] -pub enum Void {} - -/// Generates new keypair. -pub trait Generator { - type Error; - - /// Should be called to generate new keypair. - fn generate(&mut self) -> Result; -} +pub use self::prefix::Prefix; \ No newline at end of file diff --git a/accounts/ethkey/src/math.rs b/accounts/ethkey/src/math.rs deleted file mode 100644 index 8c3fe650df..0000000000 --- a/accounts/ethkey/src/math.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use super::{SECP256K1, Public, Secret, Error}; -use secp256k1::key; -use secp256k1::constants::{GENERATOR_X, GENERATOR_Y, CURVE_ORDER}; -use ethereum_types::{U256, H256}; - -/// Whether the public key is valid. -pub fn public_is_valid(public: &Public) -> bool { - to_secp256k1_public(public).ok() - .map_or(false, |p| p.is_valid()) -} - -/// Inplace multiply public key by secret key (EC point * scalar) -pub fn public_mul_secret(public: &mut Public, secret: &Secret) -> Result<(), Error> { - let key_secret = secret.to_secp256k1_secret()?; - let mut key_public = to_secp256k1_public(public)?; - key_public.mul_assign(&SECP256K1, &key_secret)?; - set_public(public, &key_public); - Ok(()) -} - -/// Inplace add one public key to another (EC point + EC point) -pub fn public_add(public: &mut Public, other: &Public) -> Result<(), Error> { - let mut key_public = to_secp256k1_public(public)?; - let other_public = to_secp256k1_public(other)?; - key_public.add_assign(&SECP256K1, &other_public)?; - set_public(public, &key_public); - Ok(()) -} - -/// Inplace sub one public key from another (EC point - EC point) -pub fn public_sub(public: &mut Public, other: &Public) -> Result<(), Error> { - let mut key_neg_other = to_secp256k1_public(other)?; - key_neg_other.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; - - let mut key_public = to_secp256k1_public(public)?; - key_public.add_assign(&SECP256K1, &key_neg_other)?; - set_public(public, &key_public); - Ok(()) -} - -/// Replace public key with its negation (EC point = - EC point) -pub fn public_negate(public: &mut Public) -> Result<(), Error> { - let mut key_public = to_secp256k1_public(public)?; - key_public.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; - set_public(public, &key_public); - Ok(()) -} - -/// Return base point of secp256k1 -pub fn generation_point() -> Public { - let mut public_sec_raw = [0u8; 65]; - public_sec_raw[0] = 4; - public_sec_raw[1..33].copy_from_slice(&GENERATOR_X); - public_sec_raw[33..65].copy_from_slice(&GENERATOR_Y); - - let public_key = key::PublicKey::from_slice(&SECP256K1, &public_sec_raw) - .expect("constructing using predefined constants; qed"); - let mut public = Public::default(); - set_public(&mut public, &public_key); - public -} - -/// Return secp256k1 elliptic curve order -pub fn curve_order() -> U256 { - H256::from_slice(&CURVE_ORDER).into() -} - -fn to_secp256k1_public(public: &Public) -> Result { - let public_data = { - let mut temp = [4u8; 65]; - (&mut temp[1..65]).copy_from_slice(&public[0..64]); - temp - }; - - Ok(key::PublicKey::from_slice(&SECP256K1, &public_data)?) -} - -fn set_public(public: &mut Public, key_public: &key::PublicKey) { - let key_public_serialized = key_public.serialize_vec(&SECP256K1, false); - public.copy_from_slice(&key_public_serialized[1..65]); -} - -#[cfg(test)] -mod tests { - use super::super::{Random, Generator}; - use super::{public_add, public_sub}; - - #[test] - fn public_addition_is_commutative() { - let public1 = Random.generate().unwrap().public().clone(); - let public2 = Random.generate().unwrap().public().clone(); - - let mut left = public1.clone(); - public_add(&mut left, &public2).unwrap(); - - let mut right = public2.clone(); - public_add(&mut right, &public1).unwrap(); - - assert_eq!(left, right); - } - - #[test] - fn public_addition_is_reversible_with_subtraction() { - let public1 = Random.generate().unwrap().public().clone(); - let public2 = Random.generate().unwrap().public().clone(); - - let mut sum = public1.clone(); - public_add(&mut sum, &public2).unwrap(); - public_sub(&mut sum, &public2).unwrap(); - - assert_eq!(sum, public1); - } -} diff --git a/accounts/ethkey/src/password.rs b/accounts/ethkey/src/password.rs index 6ad665e396..87fe61581a 100644 --- a/accounts/ethkey/src/password.rs +++ b/accounts/ethkey/src/password.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethkey/src/prefix.rs b/accounts/ethkey/src/prefix.rs index 6695e93c50..1e4d42c0f9 100644 --- a/accounts/ethkey/src/prefix.rs +++ b/accounts/ethkey/src/prefix.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use super::{Random, Generator, KeyPair, Error}; +use parity_crypto::publickey::{Random, Generator, KeyPair, Error}; /// Tries to find keypair with address starting with given prefix. pub struct Prefix { @@ -37,7 +37,7 @@ impl Generator for Prefix { fn generate(&mut self) -> Result { for _ in 0..self.iterations { let keypair = Random.generate()?; - if keypair.address().starts_with(&self.prefix) { + if keypair.address().as_ref().starts_with(&self.prefix) { return Ok(keypair) } } @@ -48,12 +48,13 @@ impl Generator for Prefix { #[cfg(test)] mod tests { - use {Generator, Prefix}; + use Prefix; + use parity_crypto::publickey::Generator; #[test] fn prefix_generator() { let prefix = vec![0xffu8]; let keypair = Prefix::new(prefix.clone(), usize::max_value()).generate().unwrap(); - assert!(keypair.address().starts_with(&prefix)); + assert!(keypair.address().as_bytes().starts_with(&prefix)); } } diff --git a/accounts/ethkey/src/random.rs b/accounts/ethkey/src/random.rs deleted file mode 100644 index 1966cb361b..0000000000 --- a/accounts/ethkey/src/random.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use rand::os::OsRng; -use super::{Generator, KeyPair, SECP256K1}; - -/// Randomly generates new keypair, instantiating the RNG each time. -pub struct Random; - -impl Generator for Random { - type Error = ::std::io::Error; - - fn generate(&mut self) -> Result { - let mut rng = OsRng::new()?; - match rng.generate() { - Ok(pair) => Ok(pair), - Err(void) => match void {}, // LLVM unreachable - } - } -} - -impl Generator for OsRng { - type Error = ::Void; - - fn generate(&mut self) -> Result { - let (sec, publ) = SECP256K1.generate_keypair(self) - .expect("context always created with full capabilities; qed"); - - Ok(KeyPair::from_keypair(sec, publ)) - } -} diff --git a/accounts/ethkey/src/secret.rs b/accounts/ethkey/src/secret.rs deleted file mode 100644 index 84e849cabc..0000000000 --- a/accounts/ethkey/src/secret.rs +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -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 memzero::Memzero; -use {Error, SECP256K1}; - -#[derive(Clone, PartialEq, Eq)] -pub struct Secret { - inner: Memzero, -} - -impl ToHex for Secret { - fn to_hex(&self) -> String { - 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 { - self.inner.fmt(fmt) - } -} - -impl fmt::Display for Secret { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "Secret: 0x{:x}{:x}..{:x}{:x}", self.inner[0], self.inner[1], self.inner[30], self.inner[31]) - } -} - -impl Secret { - /// 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]); - 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: Memzero::from(H256::default()) } - } - - /// Imports and validates the key. - pub fn from_unsafe_slice(key: &[u8]) -> Result { - let secret = key::SecretKey::from_slice(&super::SECP256K1, key)?; - Ok(secret.into()) - } - - /// Checks validity of this key. - pub fn check_validity(&self) -> Result<(), Error> { - self.to_secp256k1_secret().map(|_| ()) - } - - /// Inplace add one secret key to another (scalar + scalar) - pub fn add(&mut self, other: &Secret) -> Result<(), Error> { - match (self.is_zero(), other.is_zero()) { - (true, true) | (false, true) => Ok(()), - (true, false) => { - *self = other.clone(); - Ok(()) - }, - (false, false) => { - let mut key_secret = self.to_secp256k1_secret()?; - let other_secret = other.to_secp256k1_secret()?; - key_secret.add_assign(&SECP256K1, &other_secret)?; - - *self = key_secret.into(); - Ok(()) - }, - } - } - - /// Inplace subtract one secret key from another (scalar - scalar) - pub fn sub(&mut self, other: &Secret) -> Result<(), Error> { - match (self.is_zero(), other.is_zero()) { - (true, true) | (false, true) => Ok(()), - (true, false) => { - *self = other.clone(); - self.neg() - }, - (false, false) => { - let mut key_secret = self.to_secp256k1_secret()?; - let mut other_secret = other.to_secp256k1_secret()?; - other_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; - key_secret.add_assign(&SECP256K1, &other_secret)?; - - *self = key_secret.into(); - Ok(()) - }, - } - } - - /// Inplace decrease secret key (scalar - 1) - pub fn dec(&mut self) -> Result<(), Error> { - match self.is_zero() { - true => { - *self = key::MINUS_ONE_KEY.into(); - Ok(()) - }, - false => { - let mut key_secret = self.to_secp256k1_secret()?; - key_secret.add_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; - - *self = key_secret.into(); - Ok(()) - }, - } - } - - /// Inplace multiply one secret key to another (scalar * scalar) - pub fn mul(&mut self, other: &Secret) -> Result<(), Error> { - match (self.is_zero(), other.is_zero()) { - (true, true) | (true, false) => Ok(()), - (false, true) => { - *self = Self::zero(); - Ok(()) - }, - (false, false) => { - let mut key_secret = self.to_secp256k1_secret()?; - let other_secret = other.to_secp256k1_secret()?; - key_secret.mul_assign(&SECP256K1, &other_secret)?; - - *self = key_secret.into(); - Ok(()) - }, - } - } - - /// Inplace negate secret key (-scalar) - pub fn neg(&mut self) -> Result<(), Error> { - match self.is_zero() { - true => Ok(()), - false => { - let mut key_secret = self.to_secp256k1_secret()?; - key_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; - - *self = key_secret.into(); - Ok(()) - }, - } - } - - /// Inplace inverse secret key (1 / scalar) - pub fn inv(&mut self) -> Result<(), Error> { - let mut key_secret = self.to_secp256k1_secret()?; - key_secret.inv_assign(&SECP256K1)?; - - *self = key_secret.into(); - Ok(()) - } - - /// Compute power of secret key inplace (secret ^ pow). - /// This function is not intended to be used with large powers. - pub fn pow(&mut self, pow: usize) -> Result<(), Error> { - if self.is_zero() { - return Ok(()); - } - - match pow { - 0 => *self = key::ONE_KEY.into(), - 1 => (), - _ => { - let c = self.clone(); - for _ in 1..pow { - self.mul(&c)?; - } - }, - } - - Ok(()) - } - - /// Create `secp256k1::key::SecretKey` based on this secret - pub fn to_secp256k1_secret(&self) -> Result { - Ok(key::SecretKey::from_slice(&SECP256K1, &self[..])?) - } -} - -impl FromStr for Secret { - type Err = Error; - fn from_str(s: &str) -> Result { - Ok(H256::from_str(s).map_err(|e| Error::Custom(format!("{:?}", e)))?.into()) - } -} - -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 { - s.0.into() - } -} - -impl From<&'static str> for Secret { - fn from(s: &'static str) -> Self { - s.parse().expect(&format!("invalid string literal for {}: '{}'", stringify!(Self), s)) - } -} - -impl From for Secret { - fn from(key: key::SecretKey) -> Self { - let mut a = [0; SECP256K1_SECRET_KEY_SIZE]; - a.copy_from_slice(&key[0 .. SECP256K1_SECRET_KEY_SIZE]); - a.into() - } -} - -impl Deref for Secret { - type Target = H256; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - use super::super::{Random, Generator}; - use super::Secret; - - #[test] - fn multiplicating_secret_inversion_with_secret_gives_one() { - let secret = Random.generate().unwrap().secret().clone(); - let mut inversion = secret.clone(); - inversion.inv().unwrap(); - inversion.mul(&secret).unwrap(); - assert_eq!(inversion, Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()); - } - - #[test] - fn secret_inversion_is_reversible_with_inversion() { - let secret = Random.generate().unwrap().secret().clone(); - let mut inversion = secret.clone(); - inversion.inv().unwrap(); - inversion.inv().unwrap(); - assert_eq!(inversion, secret); - } - - #[test] - fn secret_pow() { - let secret = Random.generate().unwrap().secret().clone(); - - let mut pow0 = secret.clone(); - pow0.pow(0).unwrap(); - assert_eq!(pow0, Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()); - - let mut pow1 = secret.clone(); - pow1.pow(1).unwrap(); - assert_eq!(pow1, secret); - - let mut pow2 = secret.clone(); - pow2.pow(2).unwrap(); - let mut pow2_expected = secret.clone(); - pow2_expected.mul(&secret).unwrap(); - assert_eq!(pow2, pow2_expected); - - let mut pow3 = secret.clone(); - pow3.pow(3).unwrap(); - let mut pow3_expected = secret.clone(); - pow3_expected.mul(&secret).unwrap(); - pow3_expected.mul(&secret).unwrap(); - assert_eq!(pow3, pow3_expected); - } -} diff --git a/accounts/ethkey/src/signature.rs b/accounts/ethkey/src/signature.rs deleted file mode 100644 index cc712df690..0000000000 --- a/accounts/ethkey/src/signature.rs +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::ops::{Deref, DerefMut}; -use std::cmp::PartialEq; -use std::fmt; -use std::str::FromStr; -use std::hash::{Hash, Hasher}; -use secp256k1::{Message as SecpMessage, RecoverableSignature, RecoveryId, Error as SecpError}; -use secp256k1::key::{SecretKey, PublicKey}; -use rustc_hex::{ToHex, FromHex}; -use ethereum_types::{H520, H256}; -use {Secret, Public, SECP256K1, Error, Message, public_to_address, Address}; - -/// Signature encoded as RSV components -#[repr(C)] -pub struct Signature([u8; 65]); - -impl Signature { - /// Get a slice into the 'r' portion of the data. - pub fn r(&self) -> &[u8] { - &self.0[0..32] - } - - /// Get a slice into the 's' portion of the data. - pub fn s(&self) -> &[u8] { - &self.0[32..64] - } - - /// Get the recovery byte. - pub fn v(&self) -> u8 { - self.0[64] - } - - /// Encode the signature into RSV array (V altered to be in "Electrum" notation). - pub fn into_electrum(mut self) -> [u8; 65] { - self.0[64] += 27; - self.0 - } - - /// Parse bytes as a signature encoded as RSV (V in "Electrum" notation). - /// May return empty (invalid) signature if given data has invalid length. - pub fn from_electrum(data: &[u8]) -> Self { - if data.len() != 65 || data[64] < 27 { - // fallback to empty (invalid) signature - return Signature::default(); - } - - let mut sig = [0u8; 65]; - sig.copy_from_slice(data); - sig[64] -= 27; - Signature(sig) - } - - /// Create a signature object from the sig. - pub fn from_rsv(r: &H256, s: &H256, v: u8) -> Self { - let mut sig = [0u8; 65]; - sig[0..32].copy_from_slice(&r); - sig[32..64].copy_from_slice(&s); - sig[64] = v; - Signature(sig) - } - - /// Check if this is a "low" signature. - pub fn is_low_s(&self) -> bool { - H256::from_slice(self.s()) <= "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0".into() - } - - /// Check if each component of the signature is in range. - pub fn is_valid(&self) -> bool { - self.v() <= 1 && - H256::from_slice(self.r()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() && - H256::from_slice(self.r()) >= 1.into() && - H256::from_slice(self.s()) < "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141".into() && - H256::from_slice(self.s()) >= 1.into() - } -} - -// manual implementation large arrays don't have trait impls by default. -// remove when integer generics exist -impl PartialEq for Signature { - fn eq(&self, other: &Self) -> bool { - &self.0[..] == &other.0[..] - } -} - -// manual implementation required in Rust 1.13+, see `std::cmp::AssertParamIsEq`. -impl Eq for Signature { } - -// also manual for the same reason, but the pretty printing might be useful. -impl fmt::Debug for Signature { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - f.debug_struct("Signature") - .field("r", &self.0[0..32].to_hex()) - .field("s", &self.0[32..64].to_hex()) - .field("v", &self.0[64..65].to_hex()) - .finish() - } -} - -impl fmt::Display for Signature { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", self.to_hex()) - } -} - -impl FromStr for Signature { - type Err = Error; - - fn from_str(s: &str) -> Result { - match s.from_hex() { - Ok(ref hex) if hex.len() == 65 => { - let mut data = [0; 65]; - data.copy_from_slice(&hex[0..65]); - Ok(Signature(data)) - }, - _ => Err(Error::InvalidSignature) - } - } -} - -impl Default for Signature { - fn default() -> Self { - Signature([0; 65]) - } -} - -impl Hash for Signature { - fn hash(&self, state: &mut H) { - H520::from(self.0).hash(state); - } -} - -impl Clone for Signature { - fn clone(&self) -> Self { - Signature(self.0) - } -} - -impl From<[u8; 65]> for Signature { - fn from(s: [u8; 65]) -> Self { - Signature(s) - } -} - -impl Into<[u8; 65]> for Signature { - fn into(self) -> [u8; 65] { - self.0 - } -} - -impl From for H520 { - fn from(s: Signature) -> Self { - H520::from(s.0) - } -} - -impl From for Signature { - fn from(bytes: H520) -> Self { - Signature(bytes.into()) - } -} - -impl Deref for Signature { - type Target = [u8; 65]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Signature { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -pub fn sign(secret: &Secret, message: &Message) -> Result { - let context = &SECP256K1; - let sec = SecretKey::from_slice(context, &secret)?; - let s = context.sign_recoverable(&SecpMessage::from_slice(&message[..])?, &sec)?; - let (rec_id, data) = s.serialize_compact(context); - let mut data_arr = [0; 65]; - - // no need to check if s is low, it always is - data_arr[0..64].copy_from_slice(&data[0..64]); - data_arr[64] = rec_id.to_i32() as u8; - Ok(Signature(data_arr)) -} - -pub fn verify_public(public: &Public, signature: &Signature, message: &Message) -> Result { - let context = &SECP256K1; - let rsig = RecoverableSignature::from_compact(context, &signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?; - let sig = rsig.to_standard(context); - - let pdata: [u8; 65] = { - let mut temp = [4u8; 65]; - temp[1..65].copy_from_slice(&**public); - temp - }; - - let publ = PublicKey::from_slice(context, &pdata)?; - match context.verify(&SecpMessage::from_slice(&message[..])?, &sig, &publ) { - Ok(_) => Ok(true), - Err(SecpError::IncorrectSignature) => Ok(false), - Err(x) => Err(Error::from(x)) - } -} - -pub fn verify_address(address: &Address, signature: &Signature, message: &Message) -> Result { - let public = recover(signature, message)?; - let recovered_address = public_to_address(&public); - Ok(address == &recovered_address) -} - -pub fn recover(signature: &Signature, message: &Message) -> Result { - let context = &SECP256K1; - let rsig = RecoverableSignature::from_compact(context, &signature[0..64], RecoveryId::from_i32(signature[64] as i32)?)?; - let pubkey = context.recover(&SecpMessage::from_slice(&message[..])?, &rsig)?; - let serialized = pubkey.serialize_vec(context, false); - - let mut public = Public::default(); - public.copy_from_slice(&serialized[1..65]); - Ok(public) -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - use {Generator, Random, Message}; - use super::{sign, verify_public, verify_address, recover, Signature}; - - #[test] - fn vrs_conversion() { - // given - let keypair = Random.generate().unwrap(); - let message = Message::default(); - let signature = sign(keypair.secret(), &message).unwrap(); - - // when - let vrs = signature.clone().into_electrum(); - let from_vrs = Signature::from_electrum(&vrs); - - // then - assert_eq!(signature, from_vrs); - } - - #[test] - fn signature_to_and_from_str() { - let keypair = Random.generate().unwrap(); - let message = Message::default(); - let signature = sign(keypair.secret(), &message).unwrap(); - let string = format!("{}", signature); - let deserialized = Signature::from_str(&string).unwrap(); - assert_eq!(signature, deserialized); - } - - #[test] - fn sign_and_recover_public() { - let keypair = Random.generate().unwrap(); - let message = Message::default(); - let signature = sign(keypair.secret(), &message).unwrap(); - assert_eq!(keypair.public(), &recover(&signature, &message).unwrap()); - } - - #[test] - fn sign_and_verify_public() { - let keypair = Random.generate().unwrap(); - let message = Message::default(); - let signature = sign(keypair.secret(), &message).unwrap(); - assert!(verify_public(keypair.public(), &signature, &message).unwrap()); - } - - #[test] - fn sign_and_verify_address() { - let keypair = Random.generate().unwrap(); - let message = Message::default(); - let signature = sign(keypair.secret(), &message).unwrap(); - assert!(verify_address(&keypair.address(), &signature, &message).unwrap()); - } -} diff --git a/accounts/ethstore/Cargo.toml b/accounts/ethstore/Cargo.toml index af497bcff3..ac343b6efc 100644 --- a/accounts/ethstore/Cargo.toml +++ b/accounts/ethstore/Cargo.toml @@ -7,7 +7,7 @@ authors = ["Parity Technologies "] [dependencies] log = "0.4" libc = "0.2" -rand = "0.4" +rand = "0.7" ethkey = { path = "../ethkey" } serde = "1.0" serde_json = "1.0" @@ -16,14 +16,13 @@ rustc-hex = "1.0" tiny-keccak = "1.4" time = "0.1.34" itertools = "0.5" -parking_lot = "0.7" -parity-crypto = "0.3.0" -ethereum-types = "0.4" +parking_lot = "0.9" +parity-crypto = { version = "0.4.2", features = ["publickey"] } +ethereum-types = "0.8.0" dir = { path = "../../util/dir" } smallvec = "0.6" -parity-wordlist = "1.3" +parity-wordlist = "1.0" tempdir = "0.3" -lazy_static = "1.2.0" [dev-dependencies] matches = "0.1" diff --git a/accounts/ethstore/README.md b/accounts/ethstore/README.md index 77c37bd246..9c834568e4 100644 --- a/accounts/ethstore/README.md +++ b/accounts/ethstore/README.md @@ -6,7 +6,7 @@ Parity Ethereum key management. ``` Parity Ethereum key management tool. - Copyright 2015-2019 Parity Technologies (UK) Ltd. + Copyright 2015-2020 Parity Technologies (UK) Ltd. Usage: ethstore insert [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] @@ -337,4 +337,4 @@ _This project is a part of the Parity Ethereum toolchain._ - [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding. - [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management. - [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator. -- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC. +- [whisper](https://github.com/paritytech/whisper) - Implementation of Whisper-v2 PoC. diff --git a/accounts/ethstore/cli/Cargo.toml b/accounts/ethstore/cli/Cargo.toml index 9578a75377..f6d12e67fe 100644 --- a/accounts/ethstore/cli/Cargo.toml +++ b/accounts/ethstore/cli/Cargo.toml @@ -11,8 +11,10 @@ num_cpus = "1.6" rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" -parking_lot = "0.7" +parking_lot = "0.9" ethstore = { path = "../" } +ethkey = { path = "../../ethkey" } +parity-crypto = { version = "0.4.2", features = ["publickey"] } dir = { path = '../../../util/dir' } panic_hook = { path = "../../../util/panic-hook" } @@ -22,4 +24,4 @@ path = "src/main.rs" doc = false [dev-dependencies] -tempdir = "0.3.5" +tempdir = "0.3" diff --git a/accounts/ethstore/cli/src/crack.rs b/accounts/ethstore/cli/src/crack.rs index abe171c356..f656e23f55 100644 --- a/accounts/ethstore/cli/src/crack.rs +++ b/accounts/ethstore/cli/src/crack.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,8 @@ use std::sync::Arc; use std::collections::VecDeque; use parking_lot::Mutex; -use ethstore::{ethkey::Password, PresaleWallet, Error}; +use ethstore::{PresaleWallet, Error}; +use ethkey::Password; use num_cpus; pub fn run(passwords: VecDeque, wallet_path: &str) -> Result<(), Error> { diff --git a/accounts/ethstore/cli/src/main.rs b/accounts/ethstore/cli/src/main.rs index 0f56440639..a71830d75e 100644 --- a/accounts/ethstore/cli/src/main.rs +++ b/accounts/ethstore/cli/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,9 +17,11 @@ extern crate dir; extern crate docopt; extern crate ethstore; +extern crate ethkey; extern crate num_cpus; extern crate panic_hook; extern crate parking_lot; +extern crate parity_crypto; extern crate rustc_hex; extern crate serde; @@ -34,14 +36,15 @@ use std::{env, process, fs, fmt}; use docopt::Docopt; use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory}; -use ethstore::ethkey::{Address, Password}; +use ethkey::Password; +use parity_crypto::publickey::Address; use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, PresaleWallet, SecretVaultRef, StoreAccountRef}; mod crack; pub const USAGE: &'static str = r#" Parity Ethereum key management tool. - Copyright 2015-2019 Parity Technologies (UK) Ltd. + Copyright 2015-2020 Parity Technologies (UK) Ltd. Usage: ethstore insert [--dir DIR] [--vault VAULT] [--vault-pwd VAULTPWD] @@ -163,7 +166,7 @@ fn main() { } } -fn key_dir(location: &str, password: Option) -> Result, Error> { +fn key_dir(location: &str, password: Option) -> Result, Error> { let dir: RootDiskDirectory = match location { "geth" => RootDiskDirectory::create(dir::geth(false))?, "geth-test" => RootDiskDirectory::create(dir::geth(true))?, diff --git a/accounts/ethstore/cli/tests/cli.rs b/accounts/ethstore/cli/tests/cli.rs index 39e40864f2..6ced66e5dd 100644 --- a/accounts/ethstore/cli/tests/cli.rs +++ b/accounts/ethstore/cli/tests/cli.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/account/cipher.rs b/accounts/ethstore/src/account/cipher.rs index 1d97b69e8f..b908cccfa8 100644 --- a/accounts/ethstore/src/account/cipher.rs +++ b/accounts/ethstore/src/account/cipher.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/account/crypto.rs b/accounts/ethstore/src/account/crypto.rs index a3f6f9e9a3..faa04e645d 100644 --- a/accounts/ethstore/src/account/crypto.rs +++ b/accounts/ethstore/src/account/crypto.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,8 +15,8 @@ // along with Parity Ethereum. If not, see . use std::str; -use std::num::NonZeroU32; -use ethkey::{Password, Secret}; +use crypto::publickey::Secret; +use ethkey::Password; use {json, Error, crypto}; use crypto::Keccak256; use random::Random; @@ -74,12 +74,12 @@ impl From for String { impl Crypto { /// Encrypt account secret - pub fn with_secret(secret: &Secret, password: &Password, iterations: NonZeroU32) -> Result { - Crypto::with_plain(&*secret, password, iterations) + pub fn with_secret(secret: &Secret, password: &Password, iterations: u32) -> Result { + Crypto::with_plain(secret.as_ref(), password, iterations) } /// Encrypt custom plain data - pub fn with_plain(plain: &[u8], password: &Password, iterations: NonZeroU32) -> Result { + pub fn with_plain(plain: &[u8], password: &Password, iterations: u32) -> Result { let salt: [u8; 32] = Random::random(); let iv: [u8; 16] = Random::random(); @@ -121,7 +121,7 @@ impl Crypto { } let secret = self.do_decrypt(password, 32)?; - Ok(Secret::from_unsafe_slice(&secret)?) + Ok(Secret::import_key(&secret)?) } /// Try to decrypt and return result as is @@ -159,18 +159,14 @@ impl Crypto { #[cfg(test)] mod tests { - use ethkey::{Generator, Random}; - use super::{Crypto, Error, NonZeroU32}; - - lazy_static! { - static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); - } + use crypto::publickey::{Generator, Random}; + use super::{Crypto, Error}; #[test] fn crypto_with_secret_create() { let keypair = Random.generate().unwrap(); let passwd = "this is sparta".into(); - let crypto = Crypto::with_secret(keypair.secret(), &passwd, *ITERATIONS).unwrap(); + let crypto = Crypto::with_secret(keypair.secret(), &passwd, 10240).unwrap(); let secret = crypto.secret(&passwd).unwrap(); assert_eq!(keypair.secret(), &secret); } @@ -178,7 +174,7 @@ mod tests { #[test] fn crypto_with_secret_invalid_password() { let keypair = Random.generate().unwrap(); - let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), *ITERATIONS).unwrap(); + let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), 10240).unwrap(); assert_matches!(crypto.secret(&"this is sparta!".into()), Err(Error::InvalidPassword)) } @@ -186,7 +182,7 @@ mod tests { fn crypto_with_null_plain_data() { let original_data = b""; let passwd = "this is sparta".into(); - let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap(); + let crypto = Crypto::with_plain(&original_data[..], &passwd, 10240).unwrap(); let decrypted_data = crypto.decrypt(&passwd).unwrap(); assert_eq!(original_data[..], *decrypted_data); } @@ -195,7 +191,7 @@ mod tests { fn crypto_with_tiny_plain_data() { let original_data = b"{}"; let passwd = "this is sparta".into(); - let crypto = Crypto::with_plain(&original_data[..], &passwd, *ITERATIONS).unwrap(); + let crypto = Crypto::with_plain(&original_data[..], &passwd, 10240).unwrap(); let decrypted_data = crypto.decrypt(&passwd).unwrap(); assert_eq!(original_data[..], *decrypted_data); } @@ -204,7 +200,7 @@ mod tests { fn crypto_with_huge_plain_data() { let original_data: Vec<_> = (1..65536).map(|i| (i % 256) as u8).collect(); let passwd = "this is sparta".into(); - let crypto = Crypto::with_plain(&original_data, &passwd, *ITERATIONS).unwrap(); + let crypto = Crypto::with_plain(&original_data, &passwd, 10240).unwrap(); let decrypted_data = crypto.decrypt(&passwd).unwrap(); assert_eq!(&original_data, &decrypted_data); } diff --git a/accounts/ethstore/src/account/kdf.rs b/accounts/ethstore/src/account/kdf.rs index 06b361cdca..fd797829a9 100644 --- a/accounts/ethstore/src/account/kdf.rs +++ b/accounts/ethstore/src/account/kdf.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,7 +15,6 @@ // along with Parity Ethereum. If not, see . use json; -use std::num::NonZeroU32; #[derive(Debug, PartialEq, Clone)] pub enum Prf { @@ -24,7 +23,7 @@ pub enum Prf { #[derive(Debug, PartialEq, Clone)] pub struct Pbkdf2 { - pub c: NonZeroU32, + pub c: u32, pub dklen: u32, pub prf: Prf, pub salt: Vec, diff --git a/accounts/ethstore/src/account/mod.rs b/accounts/ethstore/src/account/mod.rs index b979d34a5a..34397c2930 100644 --- a/accounts/ethstore/src/account/mod.rs +++ b/accounts/ethstore/src/account/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/account/safe_account.rs b/accounts/ethstore/src/account/safe_account.rs index 63971ef6a2..691b41b960 100644 --- a/accounts/ethstore/src/account/safe_account.rs +++ b/accounts/ethstore/src/account/safe_account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use ethkey::{self, KeyPair, sign, Address, Password, Signature, Message, Public, Secret}; -use ethkey::crypto::ecdh::agree; +use crypto::publickey::{KeyPair, sign, Address, Signature, Message, Public, Secret}; +use ethkey::Password; +use crypto::publickey::ecdh::agree; use {json, Error}; use account::Version; use crypto; use super::crypto::Crypto; -use std::num::NonZeroU32; /// Account representation. #[derive(Debug, PartialEq, Clone)] @@ -60,7 +60,7 @@ impl SafeAccount { keypair: &KeyPair, id: [u8; 16], password: &Password, - iterations: NonZeroU32, + iterations: u32, name: String, meta: String ) -> Result { @@ -136,7 +136,7 @@ impl SafeAccount { } /// Create a new `VaultKeyFile` from the given `self` - pub fn into_vault_file(self, iterations: NonZeroU32, password: &Password) -> Result { + pub fn into_vault_file(self, iterations: u32, password: &Password) -> Result { let meta_plain = json::VaultKeyMeta { address: self.address.into(), name: Some(self.name), @@ -162,7 +162,7 @@ impl SafeAccount { /// Decrypt a message. pub fn decrypt(&self, password: &Password, shared_mac: &[u8], message: &[u8]) -> Result, Error> { let secret = self.crypto.secret(password)?; - ethkey::crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from) + crypto::publickey::ecies::decrypt(&secret, shared_mac, message).map_err(From::from) } /// Agree on shared key. @@ -178,7 +178,7 @@ impl SafeAccount { } /// Change account's password. - pub fn change_password(&self, old_password: &Password, new_password: &Password, iterations: NonZeroU32) -> Result { + pub fn change_password(&self, old_password: &Password, new_password: &Password, iterations: u32) -> Result { let secret = self.crypto.secret(old_password)?; let result = SafeAccount { id: self.id.clone(), @@ -200,20 +200,15 @@ impl SafeAccount { #[cfg(test)] mod tests { - use ethkey::{Generator, Random, verify_public, Message}; - use super::{SafeAccount, NonZeroU32}; - - lazy_static! { - static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); - } - + use crypto::publickey::{Generator, Random, verify_public, Message}; + use super::SafeAccount; #[test] fn sign_and_verify_public() { let keypair = Random.generate().unwrap(); let password = "hello world".into(); let message = Message::default(); - let account = SafeAccount::create(&keypair, [0u8; 16], &password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()); + let account = SafeAccount::create(&keypair, [0u8; 16], &password, 10240, "Test".to_owned(), "{}".to_owned()); let signature = account.unwrap().sign(&password, &message).unwrap(); assert!(verify_public(keypair.public(), &signature, &message).unwrap()); } @@ -223,9 +218,10 @@ mod tests { let keypair = Random.generate().unwrap(); let first_password = "hello world".into(); let sec_password = "this is sparta".into(); + let i = 10240; let message = Message::default(); - let account = SafeAccount::create(&keypair, [0u8; 16], &first_password, *ITERATIONS, "Test".to_owned(), "{}".to_owned()).unwrap(); - let new_account = account.change_password(&first_password, &sec_password, *ITERATIONS).unwrap(); + let account = SafeAccount::create(&keypair, [0u8; 16], &first_password, i, "Test".to_owned(), "{}".to_owned()).unwrap(); + let new_account = account.change_password(&first_password, &sec_password, i).unwrap(); assert!(account.sign(&first_password, &message).is_ok()); assert!(account.sign(&sec_password, &message).is_err()); assert!(new_account.sign(&first_password, &message).is_err()); diff --git a/accounts/ethstore/src/account/version.rs b/accounts/ethstore/src/account/version.rs index 3048b95b0a..28afcfced6 100644 --- a/accounts/ethstore/src/account/version.rs +++ b/accounts/ethstore/src/account/version.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/accounts_dir/disk.rs b/accounts/ethstore/src/accounts_dir/disk.rs index 00c59b254d..76bb5629b1 100644 --- a/accounts/ethstore/src/accounts_dir/disk.rs +++ b/accounts/ethstore/src/accounts_dir/disk.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -61,7 +61,6 @@ pub fn find_unique_filename_using_random_suffix(parent_path: &Path, original_fil /// Create a new file and restrict permissions to owner only. It errors if the file already exists. #[cfg(unix)] pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result { - use libc; use std::os::unix::fs::OpenOptionsExt; fs::OpenOptions::new() @@ -83,7 +82,6 @@ pub fn create_new_file_with_permissions_to_owner(file_path: &Path) -> io::Result /// Create a new file and restrict permissions to owner only. It replaces the existing file if it already exists. #[cfg(unix)] pub fn replace_file_with_permissions_to_owner(file_path: &Path) -> io::Result { - use libc; use std::os::unix::fs::PermissionsExt; let file = fs::File::create(file_path)?; @@ -286,7 +284,7 @@ impl KeyDirectory for DiskDirectory where T: KeyFileManager { fn path(&self) -> Option<&PathBuf> { Some(&self.path) } - fn as_vault_provider(&self) -> Option<&VaultKeyDirectoryProvider> { + fn as_vault_provider(&self) -> Option<&dyn VaultKeyDirectoryProvider> { Some(self) } @@ -296,12 +294,12 @@ impl KeyDirectory for DiskDirectory where T: KeyFileManager { } impl VaultKeyDirectoryProvider for DiskDirectory where T: KeyFileManager { - fn create(&self, name: &str, key: VaultKey) -> Result, Error> { + fn create(&self, name: &str, key: VaultKey) -> Result, Error> { let vault_dir = VaultDiskDirectory::create(&self.path, name, key)?; Ok(Box::new(vault_dir)) } - fn open(&self, name: &str, key: VaultKey) -> Result, Error> { + fn open(&self, name: &str, key: VaultKey) -> Result, Error> { let vault_dir = VaultDiskDirectory::at(&self.path, name, key)?; Ok(Box::new(vault_dir)) } @@ -359,7 +357,7 @@ mod test { use std::num::NonZeroU32; use super::{KeyDirectory, RootDiskDirectory, VaultKey}; use account::SafeAccount; - use ethkey::{Random, Generator}; + use crypto::publickey::{Random, Generator}; use self::tempdir::TempDir; lazy_static! { diff --git a/accounts/ethstore/src/accounts_dir/memory.rs b/accounts/ethstore/src/accounts_dir/memory.rs index 617e7bcb02..9242bd318b 100644 --- a/accounts/ethstore/src/accounts_dir/memory.rs +++ b/accounts/ethstore/src/accounts_dir/memory.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ use std::collections::HashMap; use parking_lot::RwLock; use itertools; -use ethkey::Address; +use crypto::publickey::Address; use {SafeAccount, Error}; use super::KeyDirectory; @@ -68,7 +68,7 @@ impl KeyDirectory for MemoryDirectory { fn unique_repr(&self) -> Result { let mut val = 0u64; let accounts = self.accounts.read(); - for acc in accounts.keys() { val = val ^ acc.low_u64() } + for acc in accounts.keys() { val = val ^ acc.to_low_u64_be() } Ok(val) } } diff --git a/accounts/ethstore/src/accounts_dir/mod.rs b/accounts/ethstore/src/accounts_dir/mod.rs index 9b1328e115..8a4b182f23 100644 --- a/accounts/ethstore/src/accounts_dir/mod.rs +++ b/accounts/ethstore/src/accounts_dir/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,6 @@ //! Accounts Directory use ethkey::Password; -use std::num::NonZeroU32; use std::path::{PathBuf}; use {SafeAccount, Error}; @@ -42,7 +41,7 @@ pub struct VaultKey { /// Vault password pub password: Password, /// Number of iterations to produce a derived key from password - pub iterations: NonZeroU32, + pub iterations: u32, } /// Keys directory @@ -58,7 +57,7 @@ pub trait KeyDirectory: Send + Sync { /// Get directory filesystem path, if available fn path(&self) -> Option<&PathBuf> { None } /// Return vault provider, if available - fn as_vault_provider(&self) -> Option<&VaultKeyDirectoryProvider> { None } + fn as_vault_provider(&self) -> Option<&dyn VaultKeyDirectoryProvider> { None } /// Unique representation of directory account collection fn unique_repr(&self) -> Result; } @@ -66,9 +65,9 @@ pub trait KeyDirectory: Send + Sync { /// Vaults provider pub trait VaultKeyDirectoryProvider { /// Create new vault with given key - fn create(&self, name: &str, key: VaultKey) -> Result, Error>; + fn create(&self, name: &str, key: VaultKey) -> Result, Error>; /// Open existing vault with given key - fn open(&self, name: &str, key: VaultKey) -> Result, Error>; + fn open(&self, name: &str, key: VaultKey) -> Result, Error>; /// List all vaults fn list_vaults(&self) -> Result, Error>; /// Get vault meta @@ -78,7 +77,7 @@ pub trait VaultKeyDirectoryProvider { /// Vault directory pub trait VaultKeyDirectory: KeyDirectory { /// Cast to `KeyDirectory` - fn as_key_directory(&self) -> &KeyDirectory; + fn as_key_directory(&self) -> &dyn KeyDirectory; /// Vault name fn name(&self) -> &str; /// Get vault key @@ -97,7 +96,7 @@ pub use self::vault::VaultDiskDirectory; impl VaultKey { /// Create new vault key - pub fn new(password: &Password, iterations: NonZeroU32) -> Self { + pub fn new(password: &Password, iterations: u32) -> Self { VaultKey { password: password.clone(), iterations: iterations, diff --git a/accounts/ethstore/src/accounts_dir/vault.rs b/accounts/ethstore/src/accounts_dir/vault.rs index c54de7c12c..7d505940ae 100644 --- a/accounts/ethstore/src/accounts_dir/vault.rs +++ b/accounts/ethstore/src/accounts_dir/vault.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -119,7 +119,7 @@ impl VaultDiskDirectory { } impl VaultKeyDirectory for VaultDiskDirectory { - fn as_key_directory(&self) -> &KeyDirectory { + fn as_key_directory(&self) -> &dyn KeyDirectory { self } diff --git a/accounts/ethstore/src/error.rs b/accounts/ethstore/src/error.rs index fceaf16768..168cef9527 100644 --- a/accounts/ethstore/src/error.rs +++ b/accounts/ethstore/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,9 +16,8 @@ use std::fmt; use std::io::Error as IoError; -use ethkey::{self, Error as EthKeyError}; use crypto::{self, Error as EthCryptoError}; -use ethkey::DerivationError; +use crypto::publickey::{Error as EthPublicKeyCryptoError, DerivationError}; /// Account-related errors. #[derive(Debug)] @@ -47,12 +46,10 @@ pub enum Error { VaultNotFound, /// Account creation failed. CreationFailed, - /// `EthKey` error - EthKey(EthKeyError), - /// `ethkey::crypto::Error` - EthKeyCrypto(ethkey::crypto::Error), /// `EthCrypto` error EthCrypto(EthCryptoError), + /// `EthPublicKeyCryptoError` error + EthPublicKeyCrypto(EthPublicKeyCryptoError), /// Derivation error Derivation(DerivationError), /// Custom error @@ -74,9 +71,8 @@ impl fmt::Display for Error { Error::InvalidVaultName => "Invalid vault name".into(), 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::EthPublicKeyCrypto(ref err) => err.to_string(), Error::Derivation(ref err) => format!("Derivation error: {:?}", err), Error::Custom(ref s) => s.clone(), }; @@ -91,15 +87,9 @@ impl From for Error { } } -impl From for Error { - fn from(err: EthKeyError) -> Self { - Error::EthKey(err) - } -} - -impl From for Error { - fn from(err: ethkey::crypto::Error) -> Self { - Error::EthKeyCrypto(err) +impl From for Error { + fn from(err: EthPublicKeyCryptoError) -> Self { + Error::EthPublicKeyCrypto(err) } } diff --git a/accounts/ethstore/src/ethkey.rs b/accounts/ethstore/src/ethkey.rs deleted file mode 100644 index 8cd2c533ad..0000000000 --- a/accounts/ethstore/src/ethkey.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! ethkey reexport to make documentation look pretty. -pub use _ethkey::*; -use json; - -impl Into for Address { - fn into(self) -> json::H160 { - let a: [u8; 20] = self.into(); - From::from(a) - } -} - -impl From for Address { - fn from(json: json::H160) -> Self { - let a: [u8; 20] = json.into(); - From::from(a) - } -} - -impl<'a> From<&'a json::H160> for Address { - fn from(json: &'a json::H160) -> Self { - let mut a = [0u8; 20]; - a.copy_from_slice(json); - From::from(a) - } -} diff --git a/accounts/ethstore/src/ethstore.rs b/accounts/ethstore/src/ethstore.rs index 92eb949673..be4fe6795b 100644 --- a/accounts/ethstore/src/ethstore.rs +++ b/accounts/ethstore/src/ethstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,26 +15,21 @@ // along with Parity Ethereum. If not, see . use std::collections::{BTreeMap, HashMap}; -use std::num::NonZeroU32; use std::mem; use std::path::PathBuf; use parking_lot::{Mutex, RwLock}; use std::time::{Instant, Duration}; +use crypto::KEY_ITERATIONS; use random::Random; -use ethkey::{self, Signature, Password, Address, Message, Secret, Public, KeyPair, ExtendedKeyPair}; +use crypto::publickey::{Signature, Address, Message, Secret, Public, KeyPair, ExtendedKeyPair}; +use ethkey::Password; use accounts_dir::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError}; use account::SafeAccount; use presale::PresaleWallet; use json::{self, Uuid, OpaqueKeyFile}; use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation, OpaqueSecret}; - -lazy_static! { - static ref KEY_ITERATIONS: NonZeroU32 = - NonZeroU32::new(crypto::KEY_ITERATIONS as u32).expect("KEY_ITERATIONS > 0; qed"); -} - /// Accounts store. pub struct EthStore { store: EthMultiStore, @@ -42,12 +37,12 @@ pub struct EthStore { impl EthStore { /// Open a new accounts store with given key directory backend. - pub fn open(directory: Box) -> Result { - Self::open_with_iterations(directory, *KEY_ITERATIONS) + pub fn open(directory: Box) -> Result { + Self::open_with_iterations(directory, KEY_ITERATIONS as u32) } /// Open a new account store with given key directory backend and custom number of iterations. - pub fn open_with_iterations(directory: Box, iterations: NonZeroU32) -> Result { + pub fn open_with_iterations(directory: Box, iterations: u32) -> Result { Ok(EthStore { store: EthMultiStore::open_with_iterations(directory, iterations)?, }) @@ -190,7 +185,7 @@ impl SecretStore for EthStore { Ok(account.check_password(password)) } - fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error> { + fn copy_account(&self, new_store: &dyn SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error> { let account = self.get(account)?; let secret = account.crypto.secret(password)?; new_store.insert_account(new_vault, secret, new_password)?; @@ -262,11 +257,11 @@ impl SecretStore for EthStore { /// Similar to `EthStore` but may store many accounts (with different passwords) for the same `Address` pub struct EthMultiStore { - dir: Box, - iterations: NonZeroU32, + dir: Box, + iterations: u32, // order lock: cache, then vaults cache: RwLock>>, - vaults: Mutex>>, + vaults: Mutex>>, timestamp: Mutex, } @@ -278,12 +273,12 @@ struct Timestamp { impl EthMultiStore { /// Open new multi-accounts store with given key directory backend. - pub fn open(directory: Box) -> Result { - Self::open_with_iterations(directory, *KEY_ITERATIONS) + pub fn open(directory: Box) -> Result { + Self::open_with_iterations(directory, KEY_ITERATIONS as u32) } /// Open new multi-accounts store with given key directory backend and custom number of iterations for new keys. - pub fn open_with_iterations(directory: Box, iterations: NonZeroU32) -> Result { + pub fn open_with_iterations(directory: Box, iterations: u32) -> Result { let store = EthMultiStore { dir: directory, vaults: Mutex::new(HashMap::new()), @@ -448,13 +443,13 @@ impl EthMultiStore { Derivation::Hierarchical(path) => { for path_item in path { extended = extended.derive( - if path_item.soft { ethkey::Derivation::Soft(path_item.index) } - else { ethkey::Derivation::Hard(path_item.index) } + if path_item.soft { crypto::publickey::Derivation::Soft(path_item.index) } + else { crypto::publickey::Derivation::Hard(path_item.index) } )?; } }, - Derivation::SoftHash(h256) => { extended = extended.derive(ethkey::Derivation::Soft(h256))?; } - Derivation::HardHash(h256) => { extended = extended.derive(ethkey::Derivation::Hard(h256))?; } + Derivation::SoftHash(h256) => { extended = extended.derive(crypto::publickey::Derivation::Soft(h256))?; } + Derivation::HardHash(h256) => { extended = extended.derive(crypto::publickey::Derivation::Hard(h256))?; } } Ok(extended) } @@ -485,7 +480,7 @@ impl SimpleSecretStore for EthMultiStore { let accounts = self.get_matching(&account_ref, password)?; for account in accounts { let extended = self.generate(account.crypto.secret(password)?, derivation)?; - return Ok(ethkey::public_to_address(extended.public().public())); + return Ok(crypto::publickey::public_to_address(extended.public().public())); } Err(Error::InvalidPassword) } @@ -497,7 +492,7 @@ impl SimpleSecretStore for EthMultiStore { for account in accounts { let extended = self.generate(account.crypto.secret(password)?, derivation)?; let secret = extended.secret().as_raw(); - return Ok(ethkey::sign(&secret, message)?) + return Ok(crypto::publickey::sign(&secret, message)?) } Err(Error::InvalidPassword) } @@ -696,7 +691,7 @@ mod tests { extern crate tempdir; use accounts_dir::{KeyDirectory, MemoryDirectory, RootDiskDirectory}; - use ethkey::{Random, Generator, KeyPair}; + use crypto::publickey::{Random, Generator, KeyPair}; use secret_store::{SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation}; use super::{EthStore, EthMultiStore}; use self::tempdir::TempDir; @@ -715,7 +710,7 @@ mod tests { } struct RootDiskDirectoryGuard { - pub key_dir: Option>, + pub key_dir: Option>, _path: TempDir, } @@ -1090,7 +1085,7 @@ mod tests { SecretVaultRef::Root, &address, &"test".into(), - Derivation::HardHash(H256::from(0)), + Derivation::HardHash(H256::zero()), ).unwrap(); // there should be 2 accounts in the store diff --git a/accounts/ethstore/src/import.rs b/accounts/ethstore/src/import.rs index 87e9783eae..2426de1c0a 100644 --- a/accounts/ethstore/src/import.rs +++ b/accounts/ethstore/src/import.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,13 +18,13 @@ use std::collections::HashSet; use std::path::Path; use std::fs; -use ethkey::Address; +use crypto::publickey::Address; use accounts_dir::{KeyDirectory, RootDiskDirectory, DiskKeyFileManager, KeyFileManager}; use dir; use Error; /// Import an account from a file. -pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result { +pub fn import_account(path: &Path, dst: &dyn KeyDirectory) -> Result { let key_manager = DiskKeyFileManager::default(); let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::>(); let filename = path.file_name().and_then(|n| n.to_str()).map(|f| f.to_owned()); @@ -40,7 +40,7 @@ pub fn import_account(path: &Path, dst: &KeyDirectory) -> Result } /// Import all accounts from one directory to the other. -pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result, Error> { +pub fn import_accounts(src: &dyn KeyDirectory, dst: &dyn KeyDirectory) -> Result, Error> { let accounts = src.load()?; let existing_accounts = dst.load()?.into_iter() .map(|a| a.address) @@ -64,7 +64,7 @@ pub fn read_geth_accounts(testnet: bool) -> Vec
{ } /// Import specific `desired` accounts from the Geth keystore into `dst`. -pub fn import_geth_accounts(dst: &KeyDirectory, desired: HashSet
, testnet: bool) -> Result, Error> { +pub fn import_geth_accounts(dst: &dyn KeyDirectory, desired: HashSet
, testnet: bool) -> Result, Error> { let src = RootDiskDirectory::at(dir::geth(testnet)); let accounts = src.load()?; let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::>(); diff --git a/accounts/ethstore/src/json/bytes.rs b/accounts/ethstore/src/json/bytes.rs index 71391d8d18..0cb00c8d3b 100644 --- a/accounts/ethstore/src/json/bytes.rs +++ b/accounts/ethstore/src/json/bytes.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/json/cipher.rs b/accounts/ethstore/src/json/cipher.rs index 38d897b64b..d5acc75f8a 100644 --- a/accounts/ethstore/src/json/cipher.rs +++ b/accounts/ethstore/src/json/cipher.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/json/crypto.rs b/accounts/ethstore/src/json/crypto.rs index 34664f98b0..b1736df02c 100644 --- a/accounts/ethstore/src/json/crypto.rs +++ b/accounts/ethstore/src/json/crypto.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -41,7 +41,7 @@ impl str::FromStr for Crypto { impl From for String { fn from(c: Crypto) -> Self { - serde_json::to_string(&c).expect("serialization cannot fail, cause all crypto keys are strings") + serde_json::to_string(&c).expect("Serialization cannot fail, because all crypto keys are strings") } } diff --git a/accounts/ethstore/src/json/error.rs b/accounts/ethstore/src/json/error.rs index e02ecb9633..7dba257e63 100644 --- a/accounts/ethstore/src/json/error.rs +++ b/accounts/ethstore/src/json/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/json/hash.rs b/accounts/ethstore/src/json/hash.rs index 6678abb73a..1a699367a2 100644 --- a/accounts/ethstore/src/json/hash.rs +++ b/accounts/ethstore/src/json/hash.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/json/id.rs b/accounts/ethstore/src/json/id.rs index 27550428fa..0da0a3b83a 100644 --- a/accounts/ethstore/src/json/id.rs +++ b/accounts/ethstore/src/json/id.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -38,7 +38,7 @@ impl<'a> Into for &'a Uuid { let d3 = &self.0[6..8]; let d4 = &self.0[8..10]; let d5 = &self.0[10..16]; - [d1, d2, d3, d4, d5].into_iter().map(|d| d.to_hex()).collect::>().join("-") + [d1, d2, d3, d4, d5].iter().map(|d| d.to_hex()).collect::>().join("-") } } diff --git a/accounts/ethstore/src/json/kdf.rs b/accounts/ethstore/src/json/kdf.rs index a8bb8b261c..eabb49cd71 100644 --- a/accounts/ethstore/src/json/kdf.rs +++ b/accounts/ethstore/src/json/kdf.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,7 +15,6 @@ // along with Parity Ethereum. If not, see . use std::fmt; -use std::num::NonZeroU32; use serde::{Serialize, Serializer, Deserialize, Deserializer}; use serde::de::{Visitor, Error as SerdeError}; use super::{Error, Bytes}; @@ -109,7 +108,7 @@ impl<'a> Visitor<'a> for PrfVisitor { #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct Pbkdf2 { - pub c: NonZeroU32, + pub c: u32, pub dklen: u32, pub prf: Prf, pub salt: Bytes, diff --git a/accounts/ethstore/src/json/key_file.rs b/accounts/ethstore/src/json/key_file.rs index 60c3ae859f..2e7a45bfa4 100644 --- a/accounts/ethstore/src/json/key_file.rs +++ b/accounts/ethstore/src/json/key_file.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/json/mod.rs b/accounts/ethstore/src/json/mod.rs index 2b6348aae2..e0ab9abd3b 100644 --- a/accounts/ethstore/src/json/mod.rs +++ b/accounts/ethstore/src/json/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/json/presale.rs b/accounts/ethstore/src/json/presale.rs index 70568d510f..7aa4e897e3 100644 --- a/accounts/ethstore/src/json/presale.rs +++ b/accounts/ethstore/src/json/presale.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/json/vault_file.rs b/accounts/ethstore/src/json/vault_file.rs index 0da870931d..cbb9f8588c 100644 --- a/accounts/ethstore/src/json/vault_file.rs +++ b/accounts/ethstore/src/json/vault_file.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/json/vault_key_file.rs b/accounts/ethstore/src/json/vault_key_file.rs index dd4ba49798..b97908baf3 100644 --- a/accounts/ethstore/src/json/vault_key_file.rs +++ b/accounts/ethstore/src/json/vault_key_file.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/json/version.rs b/accounts/ethstore/src/json/version.rs index cd8439c59f..583f05e883 100644 --- a/accounts/ethstore/src/json/version.rs +++ b/accounts/ethstore/src/json/version.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/src/lib.rs b/accounts/ethstore/src/lib.rs index c0955caeb0..0016bd41fe 100644 --- a/accounts/ethstore/src/lib.rs +++ b/accounts/ethstore/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -33,11 +33,9 @@ extern crate tempdir; extern crate parity_crypto as crypto; extern crate ethereum_types; -extern crate ethkey as _ethkey; +extern crate ethkey as ethkey; extern crate parity_wordlist; -#[macro_use] -extern crate lazy_static; #[macro_use] extern crate log; #[macro_use] @@ -48,7 +46,6 @@ extern crate serde_derive; extern crate matches; pub mod accounts_dir; -pub mod ethkey; mod account; mod json; @@ -74,4 +71,30 @@ pub use self::random::random_string; pub use self::parity_wordlist::random_phrase; /// An opaque wrapper for secret. -pub struct OpaqueSecret(::ethkey::Secret); +pub struct OpaqueSecret(crypto::publickey::Secret); + +// Additional converters for Address +use crypto::publickey::Address; + +impl Into for Address { + fn into(self) -> json::H160 { + let a: [u8; 20] = self.into(); + From::from(a) + } +} + +impl From for Address { + fn from(json: json::H160) -> Self { + let a: [u8; 20] = json.into(); + From::from(a) + } +} + +impl<'a> From<&'a json::H160> for Address { + fn from(json: &'a json::H160) -> Self { + let mut a = [0u8; 20]; + a.copy_from_slice(json); + From::from(a) + } +} + diff --git a/accounts/ethstore/src/presale.rs b/accounts/ethstore/src/presale.rs index 8ca5d0b98b..b317e8ca73 100644 --- a/accounts/ethstore/src/presale.rs +++ b/accounts/ethstore/src/presale.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,10 +15,10 @@ // along with Parity Ethereum. If not, see . use std::fs; -use std::num::NonZeroU32; use std::path::Path; use json; -use ethkey::{Address, Secret, KeyPair, Password}; +use crypto::publickey::{Address, Secret, KeyPair}; +use ethkey::Password; use crypto::{Keccak256, pbkdf2}; use {crypto, Error}; @@ -59,15 +59,14 @@ impl PresaleWallet { let mut derived_key = [0u8; 32]; let salt = pbkdf2::Salt(password.as_bytes()); let sec = pbkdf2::Secret(password.as_bytes()); - let iter = NonZeroU32::new(2000).expect("2000 > 0; qed"); - pbkdf2::sha256(iter, salt, sec, &mut derived_key); + pbkdf2::sha256(2000, salt, sec, &mut derived_key); let mut key = vec![0; self.ciphertext.len()]; let len = crypto::aes::decrypt_128_cbc(&derived_key[0..16], &self.iv, &self.ciphertext, &mut key) .map_err(|_| Error::InvalidPassword)?; let unpadded = &key[..len]; - let secret = Secret::from_unsafe_slice(&unpadded.keccak256())?; + let secret = Secret::import_key(&unpadded.keccak256())?; if let Ok(kp) = KeyPair::from_secret(secret) { if kp.address() == self.address { return Ok(kp) diff --git a/accounts/ethstore/src/random.rs b/accounts/ethstore/src/random.rs index 969c8a366d..1faa518246 100644 --- a/accounts/ethstore/src/random.rs +++ b/accounts/ethstore/src/random.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use rand::{Rng, OsRng}; +use rand::{Rng, RngCore, rngs::OsRng, distributions::Alphanumeric}; pub trait Random { fn random() -> Self where Self: Sized; @@ -23,7 +23,7 @@ pub trait Random { impl Random for [u8; 16] { fn random() -> Self { let mut result = [0u8; 16]; - let mut rng = OsRng::new().unwrap(); + let mut rng = OsRng; rng.fill_bytes(&mut result); result } @@ -32,7 +32,7 @@ impl Random for [u8; 16] { impl Random for [u8; 32] { fn random() -> Self { let mut result = [0u8; 32]; - let mut rng = OsRng::new().unwrap(); + let mut rng = OsRng; rng.fill_bytes(&mut result); result } @@ -40,6 +40,6 @@ impl Random for [u8; 32] { /// Generate a random string of given length. pub fn random_string(length: usize) -> String { - let mut rng = OsRng::new().expect("Not able to operate without random source."); - rng.gen_ascii_chars().take(length).collect() + let rng = OsRng; + rng.sample_iter(&Alphanumeric).take(length).collect() } diff --git a/accounts/ethstore/src/secret_store.rs b/accounts/ethstore/src/secret_store.rs index 5571f83c0c..9632c345e9 100644 --- a/accounts/ethstore/src/secret_store.rs +++ b/accounts/ethstore/src/secret_store.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,8 @@ use std::hash::{Hash, Hasher}; use std::path::PathBuf; use std::cmp::Ordering; -use ethkey::{Address, Message, Signature, Secret, Password, Public}; +use crypto::publickey::{Address, Message, Signature, Secret, Public}; +use ethkey::Password; use Error; use json::{Uuid, OpaqueKeyFile}; use ethereum_types::H256; @@ -110,7 +111,7 @@ pub trait SecretStore: SimpleSecretStore { /// Signs a message with raw secret. fn sign_with_secret(&self, secret: &OpaqueSecret, message: &Message) -> Result { - Ok(::ethkey::sign(&secret.0, message)?) + Ok(crypto::publickey::sign(&secret.0, message)?) } /// Imports presale wallet @@ -118,7 +119,7 @@ pub trait SecretStore: SimpleSecretStore { /// Imports existing JSON wallet fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &Password, gen_id: bool) -> Result; /// Copies account between stores and vaults. - fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error>; + fn copy_account(&self, new_store: &dyn SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &Password, new_password: &Password) -> Result<(), Error>; /// Checks if password matches given account. fn test_password(&self, account: &StoreAccountRef, password: &Password) -> Result; diff --git a/accounts/ethstore/tests/api.rs b/accounts/ethstore/tests/api.rs index c274737522..a5d39f346e 100644 --- a/accounts/ethstore/tests/api.rs +++ b/accounts/ethstore/tests/api.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,13 +16,17 @@ extern crate rand; extern crate ethstore; +extern crate ethereum_types; +extern crate parity_crypto; mod util; use ethstore::{EthStore, SimpleSecretStore, SecretVaultRef, StoreAccountRef}; -use ethstore::ethkey::{Random, Generator, Secret, KeyPair, verify_address}; +use parity_crypto::publickey::{Random, Generator, Secret, KeyPair, verify_address}; use ethstore::accounts_dir::RootDiskDirectory; use util::TransientDir; +use ethereum_types::Address; +use std::str::FromStr; #[test] fn secret_store_create() { @@ -114,9 +118,9 @@ fn secret_store_laod_geth_files() { let dir = RootDiskDirectory::at(test_path()); let store = EthStore::open(Box::new(dir)).unwrap(); assert_eq!(store.accounts().unwrap(), vec![ - StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()), - StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()), - StoreAccountRef::root("63121b431a52f8043c16fcf0d1df9cb7b5f66649".into()), + StoreAccountRef::root(Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()), + StoreAccountRef::root(Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()), + StoreAccountRef::root(Address::from_str("63121b431a52f8043c16fcf0d1df9cb7b5f66649").unwrap()), ]); } @@ -125,8 +129,8 @@ fn secret_store_load_pat_files() { let dir = RootDiskDirectory::at(pat_path()); let store = EthStore::open(Box::new(dir)).unwrap(); assert_eq!(store.accounts().unwrap(), vec![ - StoreAccountRef::root("3f49624084b67849c7b4e805c5988c21a430f9d9".into()), - StoreAccountRef::root("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf".into()), + StoreAccountRef::root(Address::from_str("3f49624084b67849c7b4e805c5988c21a430f9d9").unwrap()), + StoreAccountRef::root(Address::from_str("5ba4dcf897e97c2bdf8315b9ef26c13c085988cf").unwrap()), ]); } @@ -140,8 +144,8 @@ fn test_decrypting_files_with_short_ciphertext() { let store = EthStore::open(Box::new(dir)).unwrap(); let accounts = store.accounts().unwrap(); assert_eq!(accounts, vec![ - StoreAccountRef::root("31e9d1e6d844bd3a536800ef8d8be6a9975db509".into()), - StoreAccountRef::root("d1e64e5480bfaf733ba7d48712decb8227797a4e".into()), + StoreAccountRef::root(Address::from_str("31e9d1e6d844bd3a536800ef8d8be6a9975db509").unwrap()), + StoreAccountRef::root(Address::from_str("d1e64e5480bfaf733ba7d48712decb8227797a4e").unwrap()), ]); let message = Default::default(); diff --git a/accounts/ethstore/tests/util/mod.rs b/accounts/ethstore/tests/util/mod.rs index 7650079627..fbe24465c4 100644 --- a/accounts/ethstore/tests/util/mod.rs +++ b/accounts/ethstore/tests/util/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/accounts/ethstore/tests/util/transient_dir.rs b/accounts/ethstore/tests/util/transient_dir.rs index 67511a9b99..8bee9ae6de 100644 --- a/accounts/ethstore/tests/util/transient_dir.rs +++ b/accounts/ethstore/tests/util/transient_dir.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,12 +16,12 @@ use std::path::PathBuf; use std::{env, fs}; -use rand::{Rng, OsRng}; +use rand::{RngCore, rngs::OsRng}; use ethstore::accounts_dir::{KeyDirectory, RootDiskDirectory}; use ethstore::{Error, SafeAccount}; pub fn random_dir() -> PathBuf { - let mut rng = OsRng::new().unwrap(); + let mut rng = OsRng; let mut dir = env::temp_dir(); dir.push(format!("{:x}-{:x}", rng.next_u64(), rng.next_u64())); dir diff --git a/accounts/fake-hardware-wallet/Cargo.toml b/accounts/fake-hardware-wallet/Cargo.toml deleted file mode 100644 index 5e20d8a6d3..0000000000 --- a/accounts/fake-hardware-wallet/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -description = "Fake hardware-wallet, for OS' that don't support libusb" -name = "fake-hardware-wallet" -version = "0.0.1" -license = "GPL-3.0" -authors = ["Parity Technologies "] - -[dependencies] -ethereum-types = "0.4" -ethkey = { path = "../../accounts/ethkey" } diff --git a/accounts/fake-hardware-wallet/src/lib.rs b/accounts/fake-hardware-wallet/src/lib.rs deleted file mode 100644 index d04590865c..0000000000 --- a/accounts/fake-hardware-wallet/src/lib.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Dummy module for platforms that does not provide support for hardware wallets (libusb) - -extern crate ethereum_types; -extern crate ethkey; - -use std::fmt; -use ethereum_types::U256; -use ethkey::{Address, Signature}; - -pub struct WalletInfo { - pub address: Address, - pub name: String, - pub manufacturer: String, -} - -#[derive(Debug)] -/// `ErrorType` for devices with no `hardware wallet` -pub enum Error { - NoWallet, - KeyNotFound, -} - -pub struct TransactionInfo { - /// Nonce - pub nonce: U256, - /// Gas price - pub gas_price: U256, - /// Gas limit - pub gas_limit: U256, - /// Receiver - pub to: Option
, - /// Value - pub value: U256, - /// Data - pub data: Vec, - /// Chain ID - pub chain_id: Option, -} - -pub enum KeyPath { - /// Ethereum. - Ethereum, - /// Ethereum classic. - EthereumClassic, -} - -/// `HardwareWalletManager` for devices with no `hardware wallet` -pub struct HardwareWalletManager; - -impl HardwareWalletManager { - pub fn new() -> Result { - Err(Error::NoWallet) - } - - pub fn set_key_path(&self, _key_path: KeyPath) {} - - pub fn wallet_info(&self, _: &Address) -> Option { - None - } - - pub fn list_wallets(&self) -> Vec { - Vec::with_capacity(0) - } - - pub fn list_locked_wallets(&self) -> Result, Error> { - Err(Error::NoWallet) - } - - pub fn pin_matrix_ack(&self, _: &str, _: &str) -> Result { - Err(Error::NoWallet) - } - - pub fn sign_transaction(&self, _address: &Address, _transaction: &TransactionInfo, _rlp_transaction: &[u8]) -> Result { - Err(Error::NoWallet) } - - pub fn sign_message(&self, _address: &Address, _msg: &[u8]) -> Result { - Err(Error::NoWallet) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "No hardware wallet!!") - } -} diff --git a/accounts/hw/Cargo.toml b/accounts/hw/Cargo.toml deleted file mode 100644 index ae7f0a655c..0000000000 --- a/accounts/hw/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -description = "Hardware wallet support." -homepage = "http://parity.io" -license = "GPL-3.0" -name = "hardware-wallet" -version = "1.12.0" -authors = ["Parity Technologies "] - -[dependencies] -log = "0.4" -parking_lot = "0.7" -protobuf = "1.4" -hidapi = { git = "https://github.com/paritytech/hidapi-rs" } -libusb = { git = "https://github.com/paritytech/libusb-rs" } -trezor-sys = { git = "https://github.com/paritytech/trezor-sys" } -ethkey = { path = "../ethkey" } -ethereum-types = "0.4" -semver = "0.9" - -[dev-dependencies] -rustc-hex = "1.0" diff --git a/accounts/hw/src/ledger.rs b/accounts/hw/src/ledger.rs deleted file mode 100644 index 9bad48ec54..0000000000 --- a/accounts/hw/src/ledger.rs +++ /dev/null @@ -1,534 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Ledger hardware wallet module. Supports Ledger Blue and Nano S. -//! See for protocol details. - -use std::cmp::min; -use std::str::FromStr; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use std::fmt; - -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, is_valid_hid_device}; - -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 - -/// 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; - -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. -#[derive(Debug)] -pub enum Error { - /// Ethereum wallet protocol error. - Protocol(&'static str), - /// Hidapi error. - Usb(hidapi::HidError), - /// Libusb error - LibUsb(libusb::Error), - /// Device with request key is not available. - KeyNotFound, - /// Signing has been cancelled by user. - UserCancel, - /// Impossible error - Impossible, - /// No device arrived - NoDeviceArrived, - /// No device left - NoDeviceLeft, - /// Invalid PID or VID - InvalidDevice, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::Protocol(ref s) => write!(f, "Ledger protocol error: {}", s), - Error::Usb(ref e) => write!(f, "USB communication error: {}", e), - Error::LibUsb(ref e) => write!(f, "LibUSB communication error: {}", e), - Error::KeyNotFound => write!(f, "Key not found"), - Error::UserCancel => write!(f, "Operation has been cancelled"), - Error::Impossible => write!(f, "Placeholder error"), - Error::NoDeviceArrived => write!(f, "No device arrived"), - Error::NoDeviceLeft=> write!(f, "No device left"), - Error::InvalidDevice => write!(f, "Device with non-supported product ID or vendor ID was detected"), - } - } -} - -impl From for Error { - fn from(err: hidapi::HidError) -> Self { - Error::Usb(err) - } -} - -impl From for Error { - fn from(err: libusb::Error) -> Self { - Error::LibUsb(err) - } -} - -/// Ledger device manager. -pub struct Manager { - usb: Arc>, - devices: RwLock>, - key_path: RwLock, -} - -impl Manager { - /// Create a new instance. - pub fn new(usb: Arc>) -> Arc { - Arc::new(Self { - usb, - devices: RwLock::new(Vec::new()), - key_path: RwLock::new(KeyPath::Ethereum), - }) - } - - // 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 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..]; - chunk[0..5].copy_from_slice(&[0x01, 0x01, APDU_TAG, (sequence_number >> 8) as u8, (sequence_number & 0xff) as u8 ]); - - if sequence_number == 0 { - let data_len = data.len() + 5; - 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[header..header + size].copy_from_slice(&data[offset..offset + size]); - } - trace!(target: "hw", "Ledger write {:?}", &hid_chunk[..]); - let n = handle.write(&hid_chunk[..])?; - if n < size + header { - return Err(Error::Protocol("Write data size mismatch")); - } - offset += size; - sequence_number += 1; - if sequence_number >= 0xffff { - return Err(Error::Protocol("Maximum sequence number reached")); - } - } - 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(); - - // 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!(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); - if seq != chunk_index { - return Err(Error::Protocol("Unexpected chunk header")); - } - - let mut offset = 5; - if seq == 0 { - // Read message size and status word. - if chunk_size < 7 { - return Err(Error::Protocol("Unexpected chunk header")); - } - message_size = (chunk[5] as usize) << 8 | (chunk[6] as usize); - offset += 2; - } - message.extend_from_slice(&chunk[offset..chunk_size]); - message.truncate(message_size); - if message.len() == message_size { - break; - } - } - 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!(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)")), - 0x6a80 => Err(Error::Protocol("Invalid data")), - 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 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")), - - }?; - let new_len = message.len() - 2; - message.truncate(new_len); - 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 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) - } -} - -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(); - - // Sometimes when a ledger is connected at run-time with no other devices connected it will case a `disconnected` event. - // To work around this, ignore such spurious events and poll a couple of extra times in order to get the correct state. - if DeviceDirection::Left == device_direction && num_prev_devices == 0 { - return Err(Error::NoDeviceArrived); - } - - let detected_devices = devices.iter() - .filter(|&d| is_valid_ledger(d.vendor_id, d.product_id) && - is_valid_hid_device(d.usage_page, d.interface_number) - ) - .fold(Vec::new(), |mut v, d| { - match self.read_device(&usb, &d) { - Ok(info) => { - trace!(target: "hw", "Found device: {:?}", info); - v.push(info); - } - Err(e) => trace!(target: "hw", "Error reading device info: {}", e), - }; - v - }); - - let num_curr_devices = detected_devices.len(); - *self.devices.write() = detected_devices; - - match device_direction { - DeviceDirection::Arrived => { - if num_curr_devices > num_prev_devices { - Ok(num_curr_devices - num_prev_devices) - } else { - Err(Error::NoDeviceArrived) - } - } - DeviceDirection::Left => { - if num_prev_devices > num_curr_devices { - Ok(num_prev_devices - num_curr_devices) - } else { - Err(Error::NoDeviceLeft) - } - } - } - } - - fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result { - 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) - } -} - -/// Check if the detected device is a valid `Ledger device` by checking both the product ID and the vendor ID -pub fn is_valid_ledger(vendor_id: u16, product_id: u16) -> bool { - vendor_id == LEDGER_VID && LEDGER_PIDS.contains(&product_id) -} - -/// Poll the device in maximum `max_polling_duration` if it doesn't succeed -pub fn try_connect_polling(ledger: &Manager, max_polling_duration: &Duration, device_direction: DeviceDirection) -> bool { - let start_time = Instant::now(); - while start_time.elapsed() <= *max_polling_duration { - if let Ok(num_devices) = ledger.update_devices(device_direction) { - trace!(target: "hw", "{} number of Ledger(s) {}", num_devices, device_direction); - return true; - } - } - false -} - -#[cfg(test)] -mod tests { - use rustc_hex::FromHex; - use super::*; - use ::HardwareWalletManager; - - /// 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 = HardwareWalletManager::new().unwrap(); - - let ledger = &manager.ledger; - - // Update device list - ledger.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 = ledger.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 = ledger.sign_transaction(&address, &tx); - assert!(signature.is_ok()); - } - - /// 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 = HardwareWalletManager::new().unwrap(); - let ledger = &manager.ledger; - - // Update device list - ledger.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 = ledger.list_devices() - .iter() - .filter(|d| d.manufacturer == "Ledger".to_string()) - .nth(0) - .map(|d| d.address) - .expect("No ledger device detected"); - - // 44 bytes transaction - let tx = FromHex::from_hex("eb018504a817c80082520894a6ca2e6707f2cc189794a9dd459d5b05ed1bcd1c8703f26fcfb7a22480018080").unwrap(); - let signature = ledger.sign_transaction(&address, &tx); - println!("Got {:?}", signature); - assert!(signature.is_ok()); - - // 218 bytes transaction - let large_tx = FromHex::from_hex("f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c19701040f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c19701040").unwrap(); - let signature = ledger.sign_transaction(&address, &large_tx); - println!("Got {:?}", signature); - assert!(signature.is_ok()); - - // 36206 bytes transaction (You need to confirm many transaction on your `Ledger` for this) - let huge_tx = FromHex::from_hex("f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c19701040f86b028511cfc15d00825208940975ca9f986eee35f5cbba2d672ad9bc8d2a08448766c92c5cf830008026a0d2b0d401b543872d2a6a50de92455decbb868440321bf63a13b310c069e2ba5ba03c6d51bcb2e1653be86546b87f8a12ddb45b6d4e568420299b96f64c1970104000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7cd58ab9190c2792714ab06df5b67e66d9e3873eed251d7beb4fa252d6fed6a0ab1e5fabd284f40878d38f6e63d72eec55c6e1aa8d79c06adf714e3523a1f83da763f4bcc9d34424aba82981534066379c1cba244352042de13168556be761f8b1000807b6a6cd340b97a93cd850ee54335b1043bac153c1b0736a88919bb1a21d6befba34d9af51a9b3eb39164c64fe88efe62f136d0bc83cad1f963aec6344b9e406f7381ad2462dcf1434c90c426ee907e6a05abe39c2b36d1dfb966bcf5a4de5af9f07819256357489365c96b21d92a103a776b656fc10ad1083cf679d240bf09bf2eb7635d7bfa969ce7fbb4e0cd5835f79ca9f5583e3a9eca219fab2f773d9c7e838a7a9ef8755dc22e4880367c2b5e40795fe526fc5d1461e50d5cb053e001206460fc6617a38499db525112a7edde38b9547853ad6e5ab359233611148f196501deafae414acde9df81efd7c4144b8fd27f63ac252ecede9609b3f9e634ae95c13058ad2b4529bbb07b5d7ac567c2da994084c3c73ef7c453fc139fcdb3939461da5bf0fa3f2a83517463d02b903af5d845929cf12c9a1479f6801f20085887a94d72814671dac994e14b2faa3251d465ce16d855f33259d94fcc9553b25b488d5c45fe74de60c303bc75bcdde9374ca268767f5767638d1aec5f6f95cab8e9e27b9a80ddf3dbbe24f790debd9e3baa30145d499dd1afb5662a11788b1bb3dedc1ebc5eff9641fa6918d958e4738bae3854e4cd43f9173cd4c9c821190ec287c18035a530c2dc63077d292b3a35b3756ba9e08295a02e37d332552f9f4fdbb945df004aa5b072f9f0e9fc2e4ed6fe455d95b003e5e593dcbfad0b3b47aa855b34008e0e9a2e1cc23b975a3e6808be59dcaa8a87145c1d5183c799d06100d500227e6a758757b4f7d042b3485aa0ce5e91b2b2e67d3cfdf1c226b7ab90e40f0a0d30cbbf425f495bd5a80202909ad419745a59210e2c42a1846e656f67a764ee307abbd76fbb0c99a702253b7a753c3b93e974881f3c97987856b57449e92ffa759da041a2acac59ea2d53836098196355ae0aa2a185dbb002a67c1a278a6032f156bc1e6d7f4ff6c674126af272fdfd1dcd6a810f42878164f1c7ae346b0dd91b678b363d0e33f4b81f2d7cc14da555dcbe4b9f80ac0fed6265a6ecce278888c9794373dcb0d20aa811a9fe9864fab25eaf12764bb2f1a68cd8756cd0b3583f6e5ec74ca5c327b3f6599fa9ec32ccd1831ae323689ef4a1b1a587cbbd2120e0bb8e59f9fc87d93e0365eb36557be6c45c30c1baeba33cdaa877a87e51fd70f2b5521078607d012d65f1fcca8051a01004a6d10f662dfa6445b2ac015cb3ce8fde56bbff93f5d620171e638c6e05504c2aeeeb74c7667aee1709846cb84d345a011c21c1b4e3fd09774ab4dcc63bda04bb0f4fc49d6145d202d807cc2d8eab29b3babe15e53a3656daf0b022ac37513f77660d43d60bdd3e882eef239bfe13dba2e12707733d56e49f638005e06019a7335d8184f1039ab18084de896a946c23045e5c164dc9d32f2f227c89f717a87d1243516b922e5f270c751f1bdb2b1d3a38a15b18a7b8b7e0818573f31320d496e14a348f979b7606c5124e007493f2f40c931f68e3483a46ab2b853a90bd38ae85e6252fece6fd36f7dad0d07b6763d8001a0d6abee62452904f979cc52fa15001b06eef08f17d6e16d493d227ce9277392337a1c71713603e03803d38d1c24184b52049bc029f4f00b22d2acdef91c776a74aa184cc84b0e764f463ed05c2e16a7a0dcb6c27dd4aeca8aeac1545b48896775ba3fe9de4ea36e946d8f4ec16ca7ae58165e8ddc9189d5cc569888a59733529add4b213ea5c00ad3ed3709c0175b542513c90e18f2d4fa2301389102839d969e9f0d614943fe489750f27382f7ab273f51fcb995f449fa5fba108ad0955ed0819a0a62308021ac4ab0c97f04de9fb8533489b2685447ad71c7f9a9bc89975f9cdde87a3af89ae5bff37d1f192a31b7c5aad50486931bc07820d7dae398960965baba6cfc05c56df18b8ef0f5db488eb87be803fc94e3ad3bd6e4f358fe7ce15ca21c9a4752ddfa98337177a7c096d829886e8d71340a01644c64090c84e88235b11bd1fefe506d59733cdd82286fb466ee215914b06a138356e82c0ae6d5fd8e5fb310eb375540308d95b5d53832a5dae9652f91c1e8c14402991e38836813604dcaf272fc552e7682a6eaa7aacfd4ed1c7107b0232cdee00aef865c5577f2391937b76e34810f9d49fe31e54425b6f5e1d0e436e1366e9762d8295877e27ae495ace18fccfaafd850544c9be949d15d421cf6f4bb180225f7f86ca64480975c486df0eeb4fa80a4632cff28d36585cb5dc534553454ea810260983d02060caf6b1eb2b9443b1552ff73d243fecc9779635ed137a3bc8c04ef13f0329a7a5a54b2af0738218cc91be0ee63512f009435d8623ff4e8cdaf743818510b22e42b586a7e5e75525bb61dd2deb96adc95e07998a265d58fe4df4b9ead5b5f15b9daee510558fbdfae7a56931a6f4c729c18e0d29c467fed504810b7d9dfa0613d1657d9bfa5887e3f327cf46d7059a8a0fd654c60cb9c683c55439cd5186d1615f45f7108f261aff77791cf24c975120acf2b357dfbd2defafac0016525cff9400e0feeddff27910fbf2fa84c35fcaaec90863b605db5adbad0593601447605d68b943249861f8cd33c6419c7611403376a6bb438ee857ced2e6842f99ed1b4a9dc79f835813a4f8d07c14f1ef98773286e79cec1c9ce8c26e00418f1b27c7ef104fc96ea2b2ddefb46e2fec4feef2771a1d7e2643586b6fb97094a8d298de12a6f8f78d88e5d67442ed3310fb40aa6439b89c834e43ecd4a80c0a1d74ce6a90a67bcc996a7e93b6f397fe7ab2fa43711a72b84f8c94bd1e4ac62657b98a4b814d8ef2bb469165464a90d5353aa95d09b6ef4ffef081cab5e9dc12d743364f06d4118a585f7d455fd6e3b01434a728a768987c181409eb939e9396666560d394fb151fc67cb9cddea0a94d3e33382bd0617c95304da97994f110eafaaaff6eecb54421e01dc850dc73d77df18bbf68ecc8b37ee2fff7b6f88c139f7d88d763248deb8b4e16a8fab216c0ce88faea030f3a5c994c6e4ef6a9a68cbc9310787232198b020a7c014a1fa32c1736885603dd4921cd360bfb7dca7aafcbe81d7621dbeb4e5c094c2584c339ce70176d7fd2a6cfc4bbea6b433377eff7320d412947ac774688010369b197ec4d0471b9cc73cf9a3e71bd10901beefb10ca1c53428b89ea63427aae9ede5ba104d3fb54d0447458dd9780cd4e925f1edad33f6f0884cc47da562a3c6e2f5a958a8d8723919c4b88d067343a246c6722b6f9f82018d5213648792f38fa8ea1e635b3983dc1f941630fb3762ef1814ee3f41691b24583ddca585289568b4e64f82448b54797d382916e562b3f4795e2d726facea988249e2c3f72d44ec7197b6f783c6c7a133004d5e131b7b4d6a9557c56942ca4bd1f070a2b46c3a6b81bb9a4d570ac6afea75de65ecd331dff1e0252e0f9095f974f47b2d340d67704343b2e8832232210d2f79665bebccab528745c1dc3b28a78aafa3785c29ce2eb6a8403e4d8eded1cc2554ece0a542aa2febd711164f7d7e3a492a87b01d6b4206e593b3aa6d431e908282fcfee0d14dae4b99176a16fa32f730c2d336dcfe7eff84a7aaab1fc32ac8c2e9ab6ebb72c0306bc6998ec22d6cf20c2b6660cfbbeb064b3047c1cf650df12bd153cd7eec5dc181e46575f07c8e292cc191117cd28302d1f9c72d79b1f4062dd683ca95c3a744ac310764e56b2f02a0c2850a2f24c1b298e712374e9adfe68e5414386d7671bd52f6f472eebfdf51677ce379afe7b8085459fb1e6966f5cef45b256489b7ec8a8939cd931009c8a26642f1ff78cab06a5d25522a922cd5e4541dcdbde4848177a42476b141ce9ea035d28742cee0e5e85eb78ceb2b720e112aeb76cd0eb3fc34574c7476110b3b9dff5c19fceae816715b31fc289c0e7149e8488a59e075ac6683f237886a63a25ad23bf903480b9acf3f724d5ace0ca3a842939d4828910cc735e6513dfc4055624d68a048a626fab6b910eaf558c1b43daf1cf26338bca68b5e308b734b61624c97bf70a82430d586a6c3cf59e1bab2532fd9fa1f6fe4f757c7ede0cabea52f2cbf00cc88ca7db4ccc0ff92c0836e7405ebef2ad2e4b7d3b455d8e4d9ae575d884347bdadb67f5e24058a44ae1335280b671ec3bb9d8247e28fecedf5c151fe892bb0f6e67351752e4b1bf75dcd5af3e62ab4aedc5aa32a1606b4a0de3156b356b0fe74e898065d1e720b81663453fc97f935da3b5755a0629f38d6ae5f8e5e77eb64bbef5fc70d4081ebee7a9f7169df4f0e11796f7a79e9128ec996b6fbd8f6fa56e11f17db4925c27f4cd3ddbdee8a50e0b0d4d8f6e527302cbc4dbeef4b0338e6ac7515c1e796b39c8e83f457b50925c39d405f4cd3c1aaf3188c5ac62bf1dd362bc8c9d4e49d3d2b7c2dd2291fa4bb22d7cbe7963b654d92643b789366d1dce842f47919a1cf5073da8916701f907c4d2f8a710c58e85b59f590123d3f8e57cdc14df41a1481a893b9f9505dc0637ba9b27657b0ceab87b0e4bc742924e6d8bf895b407c54df8622018417f9e543fe49f5b10a7a5fc66e5589304af33a20ea108ddf63facebcb20d22eac2fdf4a97285ae6d3f87865fae1331d00e631dfe5366345e0d78bb39a8077484a941176bc63f469f001cfd230347580b6226d6adff5ab112dcd53e7118925296b1a05978a703e383e6ffa5158fc36781f74501564992ab244d3475e1ee8e7146033da2dc116489b84c378e4a750947eb9ccb982a197f13976bb105c81624618c697f32a5b9e03f3675b2315fe773e4922c2e3da7f68ac225107405ece58dc6bbe2bd8947f3e4269ce245589497cd892c750f9ace0440f48057090c8a6cbd5046d3d982d634b4ad6ba41c7a38b7b8b0f91cb6898e769479fc3c7e7d2010b7fb38ef13c17db705a36455a34969803323806009a4e141a5c42da0f7a5e4760d07250d7e483ca6274e57cc2885e5728c24c8b5102845e8bb74b1c394fa7a206ec052c953967380d64c148ca480ab0edbc5da1a7a1e649c2ebfd19fefc52d81aeed7cd83f3c1d2128bd66feb99d5d8fbced01383d2abbf9be47f3390dd336c22b533a731d1c59c3bc5361d781ca15430d84f3c67d6981ab99100f53b6b5623df9d8eecc99d24e02d9301d636c2d5988e98a54339d5b516379a67d50dd9994a28fae5b806c56b353a84cb31729487a6d9851960b83ebc5178be689720a80c5c412e67f8ed55724534c92ab15c3bbc5bf13dfbff02d41ce4c9bc112746b62dea2b21d034e9a31e276eacfeeafc672b95e701ec0fc7ebd4b020a73fc37361b3f136246a0e3a8378442eb5e60abd7da2032dca9b5556aa22e5007c901f438c5e1baeb5d3ec6128a84d310363c6ec17d4ffece27f502b5c63d20cb1d11d0cfc316074faa820a03e6c577389e5e82ebe5f0976b6f5266618f5eb56986714d5cc75fe87176e92dcf01c58029d2b838022c0812c933db17dc4566d233720075065fda26f44b0ed3a46b6143fe180b7a1e6c1558f87b875aedf8c2fa968e2c925f0c08c7e0f23a9cf1b46f7955d9f1db300dab801f5672e2a7231bb2b622b0dc0dd9f2ec64a5f10c239e613247f8685369ed60b2d262c038fcc43924c5aca318385c12412b10d89753f9dfca43eff5f2be7d7d7b2788b877efa8b46ec5c9e99f922839bef71c613cd44cba597cf68de366eaa8874032c14d8012b41e72fd66422f7031d26be0dc4fef8f36a3c124e4ae767a665a94233812984c4466f5bd698b5fc22153c9c2f4110d9defb23c00e722692983b32ee0e84514169910bb21b14066d048960b29b3ff4c090dd5723ca4dcdebd207d4f88da831f0ee7de4aa302a06589a4aba3ca696e7d3c3e9a93af79db91f7a06b0ad825a8652f74bdb72f580e9afb31aae58807e24067f08dd719abb4e6e458bc8aa272d7a5bbd00710c43a1fea220b9022a26b574997517d04573786a4c3e09d30f3ec32f328462e26d4f7ff015121758ce1a2fd51e7f419eb6d8ac04497ab812aa6ba2e981a312ca16c38ed887b2342b0a91348198797919671a23e2b0634b523f931e48ce0d8eb840c54045d9193afec069803901e5ec1108782503cabd0f43373a85acacfa8af44ef2b1d09e4589d2dd4fdcefbf435cb61254f189ad433fa6a4e190627732ae4ef2b0c85cfcbbbaa0137033034e70a3906112dc76ec101f3198e25fb38aad46261d6019690dbf059d66c44e7ada244589c55edfc2e7d18c0ddfcd2d3841bd54d8502763cd0f4696d44686ae3be29ba3063ff6e7aee14de126dc43302f7c0b57d59eb4fdfc4903ccbd3f7309225dd90b5f25c5ade49c14334c0e00fd18b1dc611b10fbbb98c560ad4908842e765c661b9bce005aeede6461254338b8dad3203ee1b58bac1062c7e02e2aa6d420283ed81525839f2c8ff54ac71cc105042c594fb7fd7b55c14cd1247347a197ea8f93c1bbeada1dbf3e59b798c9b15765ab23f856fcf4eeaa5892c3857646bcfd8ad2bf0a15607e0d6696a8548da32955f1f8476f8a20fe4f59b3e9bf4468730b8d46c824a370d37695d1bdcac521032804c5cc66505637701e653ccbddb052f4ecf185b3605d0ba3a4fd99161973e36a35bf79571841ef7506db822dd2a5c959f36418a8dd8acb5b3ecbf3e7918a73695501ef8f440aba43c6e4575880ba3bb83e0a839254fd8d8c6b979d79337a68d218565a5dcb1518c6c82aa73ce7f54a9434ceb5f5fd503137164d74a230e46ce298b98576fea88806bc51e393acdb2abac1da23219b4dbcfba366d834d40dd8e616d214c3478136050555539eba776bf506870c3d20c4a4645b9a7c4ffa976534068009840aadae71f578ef1a325717f64dff840b9dda81b123086a47a172e6793e68af6140b1492058fecd68c4c23db1cc13d2b57f52d0cba89cd4c26d1bd580dd2a054a1d934a80b9eda8ffb503b7e3e62d00a3d075235410149e976529d8029595e4daaae1aa685f3cbdac9b26916320e75b0846d2de8673600212bb648b26e3f1709df425136f33f46129afc90839d24de1e9fee51c685db8a280a5dd4c3ac1539664cc36ffd4537af480d4082146e7395cd6de1f8b652bca8853ec742366702afd6ed79a5920e4ad1317545266f6dbb796ace0fdc731997cd94e1bd8e6689c856adcf153909cfe882b9b02650f4f9eb8620983f0c6b95b3558682d8134a9ec8fa97e174173041115b2eae21fa0b72d0a3c7c2bf9b022fa141a8b495de8321c152b0a9a942c5baf290a234ade4e8b579238a627196fa5621b196ecbe31583517ec4ed82e8d3fb21a892dfd65ccfccd2d36c5d32afa4d4bf201d684c4b1c8c1207db455dede5b908ac63d5fc0bd2b36e11df53bd52e5ce27a9af9444a8cc4391ccc82914b79ba2971ef4ea5d5c30372e7cdbe9bedfcea9ccc8140f8c3ad1bcda29d11fe51affc74f17c9832798e10222701e0d6e93fd109cc9a12df4ee5d38c531574d39a9f4357a60f8150ee509c68e469b4eb0e9be2e6ef9099f1bb949f738fa801d223316fbb1e179b74445228c8b3c40440306e4821077860c37d6b8c17230fcf7ea48d0bb0d98fd3f1f00655e11a8b2e0a7d5da8427784a8fc6d1a2d4d1d3adcc02030b50a700788ce4078c199fc733e2ad469dd9c775d7a8025b4db9b960619f0263b7f09d038cdf85045ac2a1cc5a18364048bf242af713ac4db889489d781ff16b1dcdf66acd89bd6c7651f25a17ce751b67697739dc4d1a125fdd5a8ecbb0cfaf31cd4179249e91171ef3e628dda697afed9d09b53260ae475d59ccb45a6ffd85a2c4241fd134462cf2ec21b51422439aac77954d1b2396761f16e1c6e3242b538f23f584b95cd4b811e35a526748050a7eaa02cebdf8887d94287c99500bf9c2afb7f36ff47e17906534097b02f10620958e889d2392d30660e513c22f580a505314eea4a865d97adb9136c495403e321f425348b56ce8f8e8e91ccd702ade0bbd1efdebef8344bb9defd471ef4b214976556f59f679e0fa39a2007bb9902f5a60ba044c4316c27f6b634241acdc3ce437c4fad599aabba291bfd71c05eca6d9df49abc33ae7709f6622e516c22418e7ab86144f6baf3697bfeeee65294175e5dc9ce5ec82da64537f5f5b83f5a938e41fa8f6f97f9102fda8bcbfb6a5c58f79648b97e948a074e459b9b75a1793cf7d9ca5d7ab27cf7035ece0612d348a23c0fed509c5e18d19b1e659af237c3b9aba4fa8477de805c5f8ccd0cbf3846b6ee1bc9ef76a190952115bd08a5108c8bba76d8d762184c122d081c6dc8b4c49a7f0e16ad4cbed86c6818d4f22c03a100c9afe3675a2f354bf1c2cde1f5e5a63b95761e10d27c9482539387e3aeeaadaeab59faaa20cf595d4d8c57509c751446282581ed28cc55736211e6fabb63d0f299e39ac1cd2af1431bfb03f86e5e59691dffad4e275d4611cb2d7d3be3defcb77907c94db86d989a2ca7e19729e3454eef23b0d58bff8203b08f41b40913f2d2dd2e8c98af09e5aaee76030d8201640d78e7bcfc6c1171e04cb39a6bd060ca41ebbfd090883d8b3569c39fc19cb5d87c15062c9f09138d4e3d3f3421227fb2ac48b224438b12702cb67e2db161a3c771d866c3cc55d15a094f72fe314092e846256e44a1dc513b02bbdd976321f470f81f36e719b9acf22179855d36ad0c50dab79da662e9ea7f9685ec0b44817271ffe2b7254ab7f3ddc389847e17edbd33fbf789bcd604ccca0c01c60deca286858b16dfa17c5875916e0159dfd4f0495c08bf6de51365e2175e47325d5ee71c96ea8ce24c4541886e0854bf7dd8a980aea1aba9add0316f3d052a2eea95c02c241523f3274ee62c883c4ac440d7626cdb4f0aba7a4ea686b2778cd7d7be220357de63cce55a3928aab4c200a2cd65b04d831ba0b54dc91cd6ea410359512130d2a0122f3c9752ba6210ea3b115caf891f0a0a7ef210d1988324a9af926cea8487640a473aefb2e3b4b9259ca4da66089d7f7800f87cb2bd068b8c268dfac897b9a2dd1ff4ac2b19a48b7e95a39ebc6afa2dceca7928ed8e43630d673e5c7ba1fb4afbbd40243ed411b6519420e738c24ab183f900872f10248190358636c789b842f156987d0593fa7cb813f5c688652f871aada7cb5a9c2e15ddedac147151b4d5a7bc4b33cecac961a3487984918868515ca73ebc647945fd9044f3c085b184b3f9d333a7b74927fbbe4a0d846744e0fd6bc36f9381f76422633946fe79e64c3fd63e30096ef400df8cd8c884bad1955b82c013c1a190db92699d39217e46d3db284f35b18b782e791d722d12b85c8a26ac98e9dea8356f9d3ca58833aef4ffd883953f24c96f5351438dccf33693230db5d72389905b49d7308cc30b805fa968532a976009a527bfce9ea921ff4ea9723be5b5972ace8553441a4dac7f0b2114edd3a25666d70c4f94131a63f4521dbd004309157bb32f9fc649058ffbe747bc3addc523f805f1b34787b0f446c9ed1d1966550c7d0c10e342316c6b34899064d0d2dcbb09087ac20572103ee01193a3eab06c06e3206cd60bdbe367af81dee5ab3e5dde9836c558e54c9bb6aa306a609225cf25a65b575fa97d9c962b72b798e9a7fd8192ba879964cedf623d544c8929af5c8dea56721d25578434e2b234289895c697c9c1bc4556e4f6df479a837d1e9132c011e47f9e23fd27b70e7601fdd24f28937efb9e46673b9f56914638c793f5c3b625664f2b221afb3fce5aee92a84d45bab5cda58c49777f82b2b1c8293d727fec90dd73581b087367add474dc7b4cad75cea1e43619ef3fa1b35175f5f0889c031c2083e764b0f4389fffeb307831b73763e73d2c3112adff579d4dcfa1c09d3f2c5927568a70027242e6bec83c5e2cf7e125d8b5e4ad2ec339fb79bb15b8b9a6db0ea9408fc6fb8ca6efe9ae0c8c25900d859b17fc44c4a262c7a5e06ae9e2083fc6dc36bd08d648e9a1a3d8fcbedf12777d690ff15dc7096e7c8b33e71b19005c9e1b20d2c2b6f5c7c1204edc691b389b6ad04f896ed297922bb92b9e6d10a2df2a83dc71c15d2010b595c72d5677017d6d7938ca3538d671e13b8496583b4f9fa59fd481f1f438f92b01a6c5f7169d44b93c0b6863c1a183e871e7f50e26e6d41243a1c509d423309dc886dbb9ac245263ae9d6024456e72b57e17cb08ef00f4fa4dd9fd27de0685c4c6c680ad654e3d81dbb450f0a5e7821412d442c2034093e3fb10234e6a51b98fd388eafd0eec66b42c275a3547f72c7f3d16ed81395e9a2664faacdf99bb22327280e518e4ff047451e6f7420b562c68877c96e129d0cbe18896aff48d49da028dc97aa0108da9b29c540c5238d676dccafdf463694aea34ad4f513b6c7a58d071c335ff1313d41b7cdd902904b8c9fbea2ed34878b407ebb8144f603683ce4ce61eb0690a00d492978aac3a0f3010b7479667811c3332c06553c14809c723316c84d084530e93a63bf0b7658f7bf367d29577236e23ab658a685f2612f0216a932a24aa4f70b8d0609aa9ca14e4d91b8ed9fb62864ded646012ef675ea359117c07f528d7dfb742aab9ac892851e97c94f72d5c34d4feebc7f67e09fdc6f633f050833192f15a7acf4f8c8beb3adf3860fb26fee39a416ec362e4b6d9ced09fa57b3d5b7fb7de018e4fd93eb65634c08f6d4f1e2f490c2a8b1be2794a27de0dbecc9949fd1d5eefa0fc6f0033a2bdecfcaa267280b445e92385d2edd4c2b31bdc5d54ddd6cb30b3c370a893c217945d346d1c5b8b98ac754a01afeba6f5526939ccfe9f2432461a99c7b9b44a3983eb65fb064c32f8c72e18b8f6e42e72a1bac21b3cf94526f81089b235794412d1aed20f48324d742d4079e9546f495248cf7f42839852d604598ca2079fe44b125ae9970973b57c156e83fabe6d64c9aaab5c243d1dc71520d45317b913205979fe5bc075b0068d8a5ceb7c8ff9149c763c22b08d35a09feb8156bf7d8eda212a102906e251efcef1ebed894556f18444a0938b4c050f2b873505bdce97cd4fe539a944b94e281292f38850dec9e9f108d3b2d5a83837d114bcb3d6e6511629f310d194328eb05a7b88e7a053e97dd92881c89a1169e7d23a4fa1ebf532eed2579fc4482b9c93da2b5e9619f289f346160996cc61a3f380ea71b25e777af37dce79039cf90a2bf16ddd46733fe9c1cddbe7a42fc5faa7869c96ec463e9817495bc24a23cd9968213927522ddb0d6ba5db92f5736a5723135305a6c083a9bb54da7e43da3ebb07066ad94e597706062118fef17e9e65363f71d8859d30527a495f06bb025c1d26c6fc80e9b140c7108c57ee5583063bd8d2a7efe6a3026a79f2294e09ce980be8ce1a017132ccf48a63eb32454b12506a6099d4e310f07612e77da46aa0caed8fb0446fd6091140db2cb1432bb93cbf681cefae9d849fee6b0d87898d52d31a209ca6f168b6305011e2c9a55fc5ad2237d7c2d06b98e0703ff2a89fc7af8471aecd2a6cc0a4745082db863bc8d46209d51135333a03b328345b86d6cfc23d6d7384fae5d8546f05725ab139e2c25b0dd9b2113b2774391aa058cf90915bc97a94e74ca0ff6785243122f12decdc48aaa8ff27200007f35e928e62269f7f07407802c9a10648a91180d559c5c37cf3f425c9949b9e38ce4c99b71810babe45344d929906776a66fab175e20bc5930f1dc4b5b888301028b6e0f92293e468d0c6b191f0840ed822c036e6257bbd4f0db8e931463826c0be855add67bff5fdc6d4de7347fa07e63d68f4b6876774a39dff1ae927614f8a879f128713e24b263850f1ab3176ed0e9ca9369af947bb8e862e927cf803ea7b53b68eb8c5f87f1cde2399122b7892ccd4071610f0873981ece2ed719bebb0d508037e46b95610d14e9a826549cfedecea1d32074aa439592929873b49d9434f35646adeabc8b52e323ec2dd6d0d6e27b530361fd8bf9e4e3a0a58e3079dc63156a684bd5cde53ba8c9c51da274bd61cdab187a3fc0a84d5005319f05fc7ddbda575f73f3178336413f8ba0b99cbfdd5c350a3a925260284d75fe06371716f951d76078df7cbe6f25beab46b8f4222c74f68822d6747314b688839540d3bb9bd0f45a028e780fe2b5c78e28dbce66680f1e57b68d6088101146aa9f976bad10933e4f5481444a46d40413ae5d00044a29dd3760c712c04771976280f793ac5bf8cc1187976096e4620d646358f207a9166b9d27030721fc00688a0df926e6f4944ba6e78dc862a8e55e3d1a20d2993d8c8410548e9bf1b6efa181daf8bc060bd1af3dbd8853d6d3f54bdd1f6270b20fcf7f90310109b98f6b366a4ebc6f717962e408bf865d0128fc9ed607f848d376ab1c50e66152f74916a28539a762c75387d144bdaf4a0b8b0e7baec532e8d531501674a8727547916fbcb2e45f9c7d41063bcfec3de1b0adee000e555397ab16fb0977a8c3ac1385dfc89eb7db5cceb9109077d36ca9ff5fcf9feed6b985693746a95ba34f7d2875f61ee8606302b6470f8ad17b781daab036e288e5ee083a3a36eb116a34f5ad97e1675181818289f514efe868feeec3b48b1a574b9405668aa536e572f0e2b46fdfccaea5b2f65285f6a9a05c020bf440f5db912c8ac289c67b9d724225eff88366992f08711f35112e66b765872d39b54cdb5c4c0719b2c17dfade7e2f19281e6ae7885708ee8a8f6f90ce79387e6e47b33f15f212c5b386a5aa5f93cb597698dae4b5999ccb4d652a08c41ed27c45d2ecbd112a679374ddd6606ca76ceca9ab08f7f648d248622ddd633dfc121f9470930ae058cfa9455ddbd25a38aaf48f242ab6e0dc895c5b2af0d9ab0c996df526f144cce6297af5f3ac5fa1d159f52e072b827dbd273afcc6e3b8fa1151acaaca5965a4b6cf5b0ea6275da3208159c6bd6d716eb61309eb4ddfe1bbc4ef8d013d477668cb3506ebb4724ccc72affdab79dcdfaaee55a5946b4a3f768dae9fedddedc6c5712296f26c025ed2ee299cd15b1e692c616094f500fc53fcd9838401c0ea6b6ccb883c149a52d875501ec2e647b1d6720a8227e33cbc1f429ef60103f3334e3de2e40ed4a59d811b8cc51a695de25ebc66eca519222dafa22dbca634220097b1d3f9aeddc91d11019d7215629122b4dc6e3211ad842288b581c31e44fa79e1f7855d8fa77e7a224cf571aa3c16b5f4fe5feb16d7d1bdecc543b0e8ff01c677ec6801e87241ddaa02a5c83bbfd1d84c62e269f6ce8a708e693b86d8e5439f129431a4c1c0bc6ad47784c38e1cacf6c523da23f65a76c264b96aabb50aa9e299be6abd1c9d078ac3b2c5f2c3986b5707f143513b4ea91a2052731ef5b48780dd0cc6626a0f0c358454f6eb36df7caee6f8dfb3ea19a0ae79c0d1587140147be3efb2a0da1305d5fe056010c518e3471572d889304c4ce00acc78fed04a4b888d5e7e57d6cb5cf4e5cf1f8782e1b25ad948eb3e443db75af9233aaaf6659adbe0ef33d4b3ba5214b85e656719df2eba42235b2e268f80e3c5971d28957f8e93f5b04a3d5eaa607fd4bb838ae48661bd093342762cfd1ed60b21f04f5b95c3e5426ca6127b04810e2ee25bb56ae81d7840328d8d4f7d1bd341ed58b102d9860806f4a4d117c044f472c85ba422eab084faf8994cfe0a880bc46dc9c1a8c11995610756e2ac50c5fea8ebbcb53dcc76b1944ce364f8878f42310fe0f8cc211c62f627d12b20527dfd84b78c98b1122050cbcbdb70e08010f68294a6a805d3fab97e76cd695f918e73763ac2c3dfe4a8d75db87dc37e2399fd854f3284d29c7bae3d3e31c4375ad9e047f03a5204c2ba93b6025c112ea2c9fcd731e380a8aaa42860c859c2e2cfd333f0bee741e21f78776defea86e862711f0d0bbf64003ee848a8d1a12dd00c024cbee343d1093e653555c033c198401caeb951860392b5b1eed6200828aa310ed466e41d855dc4231464adc2b6b6fd66e03fd42736fb791387efec28b37d0686272a6bb181a621aae7be06866bdc1c4be69e94642c8d3782f5ab7cc8c890699008b52a11b149a517771b93bc2ae597dedaf0237ea8d9674e26fc75c3b468e04e2fc317d03484a75fb274f7ba1617bbb72ec16da1fd4109952d052e9de7c00761736dd17e70db0976692626ccf8bc9e88ad6c25ed88a2f7c2750add4ceb95744f690ee5f2fa423a2b62ae57c1105958bd8e81025c9412fa71f5d1e81bd6cffa01f489fab7e90ab8a3c8aaffc8e3d594beb254c460347196473117ec2a416dea464eaff95da6cec26b5535954901298f11932ebeca52aded139f2d5aa2c24174e2f6c701ce1f4564c60861ce3b9cdac1cfecf071295c5ec581f0f075096fa457373c124b6c8cae3aaf915e4701ad94ec9c01e5ca0552019bd7f107a7d5afab9e4a5e7cc7b4c5416656ad064f4a0f89afbf7c5b884b69a12fbce8aa73a49b2e5c5728c67a7396bb8341afdf52213b2f7f8e84962cccbeaea63a3c7b24881ecdde39cc57b4f211cd57c6f982217758042f61b648496e62b612b7b8bbe1b9f15d237aeac42b54d15166b5c71eb27ccca1fc9e050adc62a267eb82ca2144ba323a73aa11e2fdaa87695c70316754faf7aec44a49b668362b0b35e884019227e7b9a35e8841e64e0009c713d7f3e4a74cc3feaecf4c99b8d0ecd85c8ff89771b63a38e3af990641f28fa7e4ea560577d600f43ccd467d6a347fef04d392d42f8e97659348c68b41299f94db4b713d61868adbd20a4db74f61bd0d1e7846bfc8b8f8bb50bf50c2fbfdaa87328933741aa2b1ca50cb759c1276f1a7930952ed656921f5ce5569ed16b31b2a1b6009c784199ae60ce2e35d573808a195974536f220cd14dd634bd06800435cf1219047f6246c2d9bdea5e489ab4862f0cb0f01439ad2ad1e2042b3f63b8611a87efbe842613c21761de4c79291a8491092c20134252b8e900e5d3cc70e75d32cc41452c5c33b66087213c34f67ae73fd56a183be858f1c3bcd73d814bb9e3f78cd18992b0ea401d8f25c3b60c055df8e6430b62899bc86167d0b5e2bbf16d75bf3f2b94c26542202bbfa0abe99be1a07c78140f42c12f51576007bb5439966a47cadf5c4ea624a75e7a4f01d8733aee57e3497c013de4a33cf54a94acad9b1aad837865a6881db9a725310eed49581d2223f2b0984757bf3fc5122c5dd572ecc781b48fc508122775779d2b2849e11684a585ce844d21352f8d35ea53f0f34d772bd9ca76cc4dc33aa3f2e72418c097614fa5260eaf3c2d724d3599dfa0991a9c0eec9c4d550886c85e1ab2541e9868a36afbe0d9c07c93e44c4c73c66f88e770e5d4e4ac331fafc6870c928fca85756c444c6e8f6cf75865859abf0cfecc8e89b8c806a2e6af7cb752215bec6201eeb41759b27d599931dc2ae75d605b3e387bf263ebfd09ce2154b81479675555ec74ad85150f8eb8c1b3c4f31f6409648f9c1b4678c82e8e2afa9c887f3210afffed160d1634ab0259e1bf5565d8598605a435bd289afbbc12034f67199b67bb0fddb4b9180908c483ae5a8eed16221687e1f524d010ce5db78d1b999069f225479fd6bf0681c7ee95d4665925bc96399989b85284087e67d5a070f2713feb78bcb91bc019f3f19bf3abb7cf36ebb98f09fd64b61e2bddc9ae6335da48ba85b62562726e142bb9d9e5c8f278dbaa0657dfe3e410f03211a072555624d98790aefe8e7b0281ff6af3de79dd5a414632f9d4913a480e9cd6990f94350304f853ba5679a4cb3a647b98bf1eee6cf70f77581a1ff82a9ffd7296e8fd172d37b1b0d1621692cbfeff8de18658f04af5d5be08bce66e5dfec5989b674219f9ceb6a1037c80a8febdfac63d482debd34c3057a677420f0bdd66e2c2b25a9c1d34b76b4a998ad3ee21d1e49f812422c83016c12c201ac2b0f07ddc00638846f215bfa6c575cbfd577178eb0282ade2c459a13386f5dee8a7502321292a7de077f4fd12967b8c8055596e7a43287639843b6ebee58d463fa044562ec2da7f9c2a7f28cce685178eddd3b9fe7b10202997b6b170555a71555cfebd06cba6bb019f8cfac2ec5db3b1d1ca88acef9accf76b6a74600e590a0eba1c839d6a577d3877e7d6d010b04fc58e160ec9733bf200a9e0b24fe8ef32613cf2c7b1515008b8833e34d3967ccbc8bbe30fd1810f23bb153b814392eb37d8917e96260b3cb16895ef13b96d72c81a14b908224571680dd56d04a59a6583a232ec58e8cff16f6428b5e3dd19f362992608aba912b642aac9950777627ffa4eadfe9f31b73c3fbca11d2abb623b732f3d7c296806151257c9f2306dee1c84eb05d586e7a82a8750905716b3e51600250a1e3b4bf274130a1bfa47117cc8b6db3741ba04d977015b8ee250c3ffaf859fdf0372b88fec188830b5870f251889584333547f3436a548801fd3236da2ccb2b504f85ef1d259bc3e00f0ced934a4b297ecce0d668fb3ecb524d3ff4380a7856c7060006de31931d0b26ec1d084e0dce3b9a123741cdc326b441131d777799623c6340410c331c7e8a4a8175d7d250274cc4ffebd5d46d855bf90842888893c348f0a447998e3aaffc81c9b65e3a772eca5c2f0907ee13ab6a2babe99f388755fa3ac9dc79a2ba4ad7a869a876448ed1d4dd6a8c678065cfc90df8470b29c83719bfcbec7c5e3244a665a28593ad42ab84663bccf570a8e8b783565f909b5e6e8cd69ed6f79fc945ce5d845c998f25b9dc118c96dd2c0f592a73497dbd9e050632c8d82656a71460d0ae7f5f38636692a78083b2fffaa517dc2dfe18ae020e6a5562be54ed9046c7129b3a57dcbd1917efb0579fa9a3978690fded8e52e4860db75b2a93c77316a6e84df4965291a7531e2abc0fcc0d0016acc29680baa575cb7be1a03206236310eb5120ab4069e0f8f0cc3f6bd188ca91963eafc2bc66b1a42f8c49359cf3171a72eef94eddd8aab03f770cb2f489aece4e09a85fe6b9790ced5feced19e4cfe6bcafd1a5d99fe56b78f7a14fdea11fd5e331e23191a3f74b32d8ff2740409f346aedf469eb8aca16b43dcc44c400ae3e6d1c4717ae1f18a2f70830aa0c4d5734922374dad8c006ab97e02a4263999ecad0b1e9f24ed0b599467c962932ec610e63c0b3ac845f5d4d10979c92bd884669908696172609e0da039728baa1f0dca8885d5439ca420e87f5c449908b2a5f69b65b60adbf5d74b21eb1f4e0d79558c59b4499c245a9952de8d3a51021f2e77c44e06a489df3b72d28e5d03ddd358ced4f5a1fe057e58b86f9e717cb9001cec6d6665cc0f5b9cf89873e6e7d10355746e99494766c937683684312b630337d1c411f3f2eddc52a8267e19d38ee12c810cc4e33193e26790b13d1847c56282ac86697996daa386b06ec2ceaa97fac9c018baf644622c74546177267b053a82292c1a1cf194909beba3f2670acf1d095b0caed4b8da2fe48c9da3dc61969d938707a62ce9cf55b89ceaa04a9069d38f4e89db794a335933c5b45fe215976e76dc71b7719c2ef29d06d2dbcfce0470007331a221dbce6baa3f418f989d7dd927d343152ee310d084799300e8d3801f9d464d9bbd5687e3203cfb8e589fbab39ad4851b07bd13b29d7f4b767858d13c5937a482207470f673593aa9abe339b3d63b7ea4ad60e51e7f9080381eb07213ad1996ba7bd28f8b44b7ea037e0bf9716f56820f908fd4027249df11aea06df25b3860cb18b68a7df5ed0d14730035291346049e1e5cbdefb30719548fde4f986bd9871a71b5bc7f6e03ea4fcf1c6ddfecb06413832ac27b08d203070acdaf432bafdb288908dfd673caddbfe41af8255ff7106d39db8d003ec1abcc3000bd7fe1daec2624bbe8417f81150f20a8a48324100ef1570a6de7c0a21e16f6991b23016671bc96ee55e99a97a5a0120af8ecb816137d5f40b9e71d56cbecf61569dcd2f850ede77437be06fd85b54d7220b9bcd13e682a8227c7a05a4efc8d258b0331b0f47cf45ec370b491d6b2e4e601e50483480d9437fdf570b6be69b28b964972fac047f8aaecbe567c8ee3d583a46d5b58fa3c361dd3ad73c91727e4d0594f428acfa977206c20995612834497928d507eb62aca1752a8f3048c932b9f0f80f7c627a87f2b50d581961b8739bddfe2afabb1c757f366acd1e639de808409f598755dad254c60b5aefbbdcbad52f72c756e5e4b286a6866af769593f66256fadc939d3d23d1db9096038b40ed224ace023f2e3ea84fb4092c974cb44ffbe489f0ddbdd79e66281ef9c44e81781b849b0d3101c17e54ebf8bd69393b9220c75c7d3c564862ef35d7dfedc855e2ea15a6159c6c2bd01d2c4f3c316ddc43f937cc295fe35365a69ffe68a2a3bfa7eff90c2fe8563f6438117c31ab48cbd5a3ef1c7a03a03a048be4a9fe0de1d6a86feb144731f4e84f1b509db65d35b1b8ec3d0f462392da10694b207ef1d9fa2581b572f9c45012151f039ebed848b3fc211b2b4d6d48266e8bf800e68cb1165cfb17cb14af4fff107e57bc90b9e32006dd090ae12ff39b000c474f77da32549f51d07bb23d233485be9143c55849b5fa241337c050d48d88e4723f7f1032120cb609c584cb10cd777404556df84cd095c4a9668d392cb9a6197ce04e4234d48b47f8deaad83ee95292c9a9e9d42838c12e34046483ebd821284ac349fddb3d89c0e9a85716ca5f2c60569686d3580c6c7bce0a0ec4183fea724ad02763f66f85992fedf49c67a54c8ecc5b47d6e00cfeaf23b2425b795be93d65d92fe0ac761cca8b2feb4fd7a4bd21bc98a7328f178a61aabc2edf843e23ee94c757a457d448f3588b4e39cb14d855c35372c2060966df0e3382afe2d18988ee7676511e43afae09d6e16b50bfd290c1202c5c82520bfadb7b9eff22c2e9d202e7606f23182c08f0d405cfda6e8bf4b222a14a96015602cd77b2e0af5027938348075115b146166990bdccdaefa94626e140f8ea6fe6b51fb38fbf7ec39b89e68174db08d243a5da08a573545993db451bcd7462ba2c308849e6f54fd68eac003dff1971d19a00ae1d326d9db706197ce15397066ca114645ee39bb1a950c068908be503b2cf3ee74048dd92808e07172ba1362b3ad4103953c990e19b4581c54b5a240d90ec56150fdd5d9d1e497090941b541a9fa202d09f2790bd29f53fcf2adeddd4b4ecbff252921feca36cbe51e5185234641c8df314dec556280e408ad6605cb82f9fa5cbec32b2d478e876b4c3bc5019c344ee2f0bc33d26ae3b69e349771a8069f38f879d82e1c68f84d44516db921ca606b6e310e9ef0729b9fc76eaff94d3e44f865a6943eecc5ea1dc097e69e91344f7b287223fdf25ed3512e1fa34b0879ade1a2786571435e71d3fab19a6ba93b5d83e20f05afba10ab48ddee2c6feee813635318ac35bece3a339fb5c2278df5b9a6b7859343ff5530a2dbeda669a47a5eb0efc46c148ab00165563023536cf71f189c6b855ca6aaa056233ba82edf29e82d96c6118a0e6bf37d2ab2945ed1904f1dfd19ede3dfcf257aea6d560e3776159ffc384b3540deb1cc38d1022e530c2d46557a21eeb744ed5c00843f7b6d5953f1ff4770d26dde34c4cfbd308074e0df53264afc5a3a7ab8a57dae296c39bd72b88ad988319ba9e13ea529783d5c926d2f48599720695fd174f8873d0f660f002d8d0ee134271450c12e9dddb641b240795c2c09b958778e16081bc9180442c45fa916de16c83f16c50092eef58a56191bbcd906eb475b97d37b7f5cb00a79a9ad66a636e1052f9dd1e75d02a5af4840dfda7eac68c749bb857675e67b450a484d3e7b13a77fdabff0e97dfb705e5f4f6cf1e95a5f6cc38e099634a020087f868580ce2ec0837525b8c58f08444d7fd4333a589c0356de22568b4fad8766ee3325cbd65843f2c713ecdb44c96411ea871c039915b546ed6fbafbd51805ac48d06c6924d3f7036e1814250f50f27342c8c4ded3e68b6b3f161d46379c1088a7a123f48f0e7cb5a348f472eb155956fe232fd301e64f341041683ce3b25bba7f290a10282a8dba3a2a3da24461a5be148c2241d627889adca5acad981583fac81d0ee4ef77038c1f80db9dfe740720904512691a9c8545a9d173c08c2e8599010c972c2c34287d91ac7803a5700a0d6e29b7774f8f487b70cf8d0ec9474443e2c0c051116b16aef491c3945a65e6ddcd7931a7259e56902a2866b95d3c0bb7a3ea61b1f3b54ae56e6a7366ea895056ea0d1c251cd74f7b82b0d47464826f4aca77434df3d909271a825b57890cd830011981d95229cc0427cdc97758ddbc76d6cc77ba06c92d19daac8bbecbf55535e98bd4754ec06a6e632225c43bc46068baa688636eaba53926ca093a7addcd6a696a902ac35631aa43d9d66f77270cc7bf66140dac239034ba304e1aa0a265131e9fb2b7f079861b0e4cb9c911ce82ef0b685002476baf26401dc8cc444543129f82ac6b103881c596b19d9eba8ed6b230c17914d5c34a0040c18dc54d8c4b637ee683637fc5a82ac1cf12691bb28fc0bbb307fc032ec3d2b06eaec56ed769b5e892816c7350dce89551e87918f67a117c39f256a368586c78c2e9614e9658161511a8dad53afe8cb9eebe67c6596a90eeea1d3d2466a4d77a1129c0a4409b98d8ac0b925c4b2b3500665a3cf4ceb82cb0b6732eea8a796f9b79d2ea49be97066bc1f606d9f1f59f41d2acbb878a0783093fc4ab0ef866ff60a6a1a58d3cee90307f09247b5212f8709856251ff5d8fb77657110bbb3f3aeff07898f049c821a82c11e27b0c176a9feb12de5d08498018f7607156c5065cb56bf9d6867a4495f26a07e0f01312c2ee897b82d8eba0cbc473da402814dba727521cfec6afac2cc59cdd6a75e1f8f40585e5cda51a7434a81ccf4b7de33c663dc174ba973cebc5a56831005d231c719ea34ce42999c471fccfdbdbaf1acd2f9c16f258e32c70511c475ab264173246ebf31459a05ecb4df443066b61a243903e80ff907af17a96d7afd9763df8f8c4fc49775bc805e2dc165bd6f1c4e06688521557ed9ddb6860fbed1e32957bea1174b3a9aa809d7fa6301fbbb6b3774cd856095f14c6378cfe98f05d4f06fae91769165dd0adfc51bf8f57d701ef14a99d608db0a104ea78fe5b13794cb8529afa5352d1dbc8235d96148c8f9c2e29d6e2359a8dbeba56c9376b26f8384c66548979f4d982fe0652cd86bb60e6f2463ec63dcdd5f93d4bfaefe48f8012c63b32ad3c02ec9088896f6a0c8b1097c1ad911ada7a2d6f0d201a28b70752182885464dd688535bdfc045e8dafbe34b20eca00848e757b4a37de219be5a5fe7a4bc5cfaad29ed92e9eda2bed08407e0d0f53caf6b3590210067d8b9ef16f9a8f5612315dfa415f1efc8d7349394143a149480ce3ccd60ccaff0d9a8a797820f41b431ce3afc4adb2e07cde16015087e09e08bd13471dee960db35cbc3b53c187a5bca7ed50017e09b2ae2c837b1f6557753c7f5b004332ffa2b52d8a2269e7cf9cc397c6079aa5add61d7a560a894e71510e104f52a93622e34037b1db70a05bcfc546ea2ec7153e69a8df18fa9eadaae2c1438710477a9a23e0f7092c310c5288e2d39d362a0a33f9e3d8d9792b51a71d9014abcff66ee509baa3dad341b1e4b6c601a2966f77172a4df0f32170f3386a6600b0b63699fe21e26eeb475507e99f666e0ac349b9e23463450f4fa4498356887d9e1c5f7d18ade51e526d27ccae799d6775336ca9ca8e54d707639ecb0618a3c675533494e2435c0b3780a66defddd217d2cc464014bef8a051d8f292abf9e5cafa78c600c21ed3d40ede937b1e162a1e14757d39d77d4fad8711b6b46ae707b82ced0739f9fb6bcd9b557982e89bb3af5f3fb5448ea960f454f4475ee78970acda37501a8825a04cecf3e544651eea8933379da3c3e7de0a875d689003c00d276470fda3b6ed6473cf8094ab91784d1c0f9468379e8e9729dc1032a5ca14378f8147409f13cd6994de961e2245b35c814596087625d3d3267fc0c1e5614a4af94993091ead40bc9e1d3093228b70c188855ae9e914b15aacfd4f83fde83072af92b2cc968c93cac74e15322eaff32a7bbbb982fb725aeb71f34bf16323d9c0a11dbaf3ab676a9cd1dcfc3f8a0c66d1f082f23806133002c50b59d4513dbe3419d5002263287ff47abdba0862341effe669f26b375337170c8e0742113e1063e8141c4aa9eb4970471f3187f581b71e6f7fe2f8043d065620da8a066d112fedeb33525eb1061c0d0fe9fb415bddae8ed2eb5c3ae6aa0549230e436afacaddc389b2c66499d7fdec2090e7e13560ca0a64803554c7cd9cfcc1cb48427cf9ccd954bb7446c887e2756db2882ff12eaa64efae3a24b35d1d0402922efe90319510495420301d3360f4486d3f87e3dc4f9337bf3fb4e3c6a82850a840153a1936e7cf74086757b72a8db19d33a62a29f3dd4fdef454d9222031aa0958af21851b66aebc09a5c08efd204f3ff18cb1055e8181d6630309fcc91c0d6daef19e618a3ee23e817a586d02364710cfab0b9f2cf18502a34e67d112f1730d44ccae54dc221d7f3877bb828e7109878109f8e95e2e1407df4e588801d25d9c2a1c501e74890631e9a92d823ebbe6b5635488f7d48788ef77658e3bbaf287536b37d3a7ab1ec1749656f2ebfe562765e71dd3e1b895d9b5c315fcf2b3a063c57e74ad1e7586b293ede4c77732f38d316c14210a121153fc50007f78ed64a8e207e9d04b312ae7f97a946c74d2a1181b67e845c3ac6e340b2428c8a5546679707fded3406fc221900b118a3279e13b74926c793e27fc4cc32ae478b4421d6eef75d3a273ff61d0e95b4981e8dd57e16bb00e09bfbbc2ce60cd844a9abb839b8b671fabddfd6e86a30c0a24e73c3c17770f34641951e5dc73ca11d8f8419a7407d483e0f5f1714df0a1775574b5500e8a5a28c655dbc28d7a1ca4b83fd4ebcc7ef2e4994c97c87659681acebe7417328c8612e8570e7ade7ead7f4fc711c9c539362779e6be525bdf5ec037f670b5235c06a1acd89b4ffc21668a7269cc73bf6d1399852eebb8b1dde8ef072e8d80832ba32c8e9480da2c4f5c3209c557f31beef41c00d22ee7c7e2c1bf9952ba8a03c1afae9b4aa63135d2b131f2b2804afcdcc762e1bcec8c8151f471572888933ce97dd787121ced446aa9718bf3766bb6d8a752692c59489d5b565e1693aa0f67b352f915808e415cba13a9864bbd33ebc97dfdc0d357d6769f2f545cc6529c0f634da901ae63bfcbab0a3896bc43faed6a6c23bb4e92f3d669d2e0ff485287cce322b98d02866f026cc556ec8aba6608ac2b5dbc29e104ef2e28d7b51ce63110025bdbfc5d44e8aa7a04ecece07b9860618a162e7289e8d672bb9b15b6ffc87f738b0c7a2b733c5794afe58b1beee4b6780ed453bf2ef2b584dcf32bf732c98fe359abced05fc115e531b088c61b0d5d5058af10120581d7db192e13a5b7b17874f000343aecc8d5005b91b13720bc831de5f1de5e3ddce27ba05213cd126a7cda0afa9745f498200269a5736f63b0faec36bbd646a868100c17cb7f6639f2f14b6c52198fab04c1645bed8763799acf8fef62b82fda1825a3379c000255002788d686695b4c17be3931e69db8980d0216024e9b7b0588cdf8c8102d11f55f971b3163c392cfaa796e0b85dd0bbacd6ca50b3ab80c2e90fa0c18d3526e05b2a46c2eab823c0511b43c71122d533e27ee6d6e34706fc411c67a3b87440a3429df3009996743ed3e4dc244fac98a789f17818a926a0aae81ecde260982b80acc299f57a570a86ee28d0414edc91fb6d5f9a88aeb31bf22270bf3517aefe1140b05be97123cc43df6e8e8e4df96803fdd59715c87afcf0189fb5448663eb35d2c4e5b13dd0233a95f8d6187bf0d5d3ba35adba59e162e877d5a0397d9495ebfc771ae68283be15d883e91b81b1bb0cd8da6c300df7e2bc8a21094cadc974c8270d8ee37fc7e7501a57eaecbc244ed61cfc8d556e38c0611a5269c3b930ee5f37a9771f0c152a5e28df07a104360c973b9a83d3ec5c0aa012bff141842e9b68222647c7d022753dbaae024877f421ff36b3721c26a39b3009683c8c510ba0ba8b5dc1033f9b56e9a43b3141a92599378622a2ca8136f5f1f51cf7b7dce7d043f65f8562b33c4864adc30e7d4c808b10abbbd92f94272b68b063f7d7baf7fd6eb31cc76690042233bc8dee7253f89ce23de7a535af022dae95ac321694d6ce311744d9c152e4424a0a502d221b2e602ada71c60a2f15b7086d75867476b0633063297681fbb0a3e154efe552cdbd9d3203f2e447b60b643b823ea12f504f33f6b6c3bd20e54cf38e3c45c5d472814db60741687894e6cc3c78196d5e722499d202334fb742f14dc2ccb7d114ae0c4cd61ce2ed0cc7fe25a395d6b73c1dfee9174e59d129e7f3c42f93a246d918028d4e2dc804438799 -").unwrap(); - let signature = ledger.sign_transaction(&address, &huge_tx); - println!("Got {:?}", signature); - assert!(signature.is_ok()); - } -} diff --git a/accounts/hw/src/lib.rs b/accounts/hw/src/lib.rs deleted file mode 100644 index a7da8da45e..0000000000 --- a/accounts/hw/src/lib.rs +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Hardware wallet management. - -#![warn(missing_docs)] -#![warn(warnings)] - -extern crate ethereum_types; -extern crate ethkey; -extern crate hidapi; -extern crate libusb; -extern crate parking_lot; -extern crate protobuf; -extern crate semver; -extern crate trezor_sys; - -#[macro_use] extern crate log; -#[cfg(test)] extern crate rustc_hex; - -mod ledger; -mod trezor; - -use std::sync::{Arc, atomic, atomic::AtomicBool, Weak}; -use std::{fmt, time::Duration}; -use std::thread; - -use ethereum_types::U256; -use ethkey::{Address, Signature}; -use parking_lot::Mutex; - -const HID_GLOBAL_USAGE_PAGE: u16 = 0xFF00; -const HID_USB_DEVICE_CLASS: u8 = 0; -const MAX_POLLING_DURATION: Duration = Duration::from_millis(500); -const USB_EVENT_POLLING_INTERVAL: Duration = Duration::from_millis(500); - -/// `HardwareWallet` device -#[derive(Debug)] -pub struct Device { - path: String, - info: WalletInfo, -} - -/// `Wallet` trait -pub trait Wallet<'a> { - /// Error - type Error; - /// Transaction data format - type Transaction; - - /// Sign transaction data with wallet managing `address`. - fn sign_transaction(&self, address: &Address, transaction: Self::Transaction) -> Result; - - /// 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)] -pub enum Error { - /// Ledger device error. - LedgerDevice(ledger::Error), - /// Trezor device error - TrezorDevice(trezor::Error), - /// USB error. - Usb(libusb::Error), - /// HID error - Hid(String), - /// Hardware wallet not found for specified key. - KeyNotFound, -} - -/// This is the transaction info we need to supply to Trezor message. It's more -/// or less a duplicate of `ethcore::transaction::Transaction`, but we can't -/// import ethcore here as that would be a circular dependency. -pub struct TransactionInfo { - /// Nonce - pub nonce: U256, - /// Gas price - pub gas_price: U256, - /// Gas limit - pub gas_limit: U256, - /// Receiver - pub to: Option
, - /// Value - pub value: U256, - /// Data - pub data: Vec, - /// Chain ID - pub chain_id: Option, -} - -/// Hardware wallet information. -#[derive(Debug, Clone)] -pub struct WalletInfo { - /// Wallet device name. - pub name: String, - /// Wallet device manufacturer. - pub manufacturer: String, - /// Wallet device serial number. - pub serial: String, - /// Ethereum address. - pub address: Address, -} - -/// Key derivation paths used on hardware wallets. -#[derive(Debug, Clone, Copy)] -pub enum KeyPath { - /// Ethereum. - Ethereum, - /// Ethereum classic. - EthereumClassic, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::KeyNotFound => write!(f, "Key not found for given address."), - Error::LedgerDevice(ref e) => write!(f, "{}", e), - Error::TrezorDevice(ref e) => write!(f, "{}", e), - Error::Usb(ref e) => write!(f, "{}", e), - Error::Hid(ref e) => write!(f, "{}", e), - } - } -} - -impl From for Error { - fn from(err: ledger::Error) -> Self { - match err { - ledger::Error::KeyNotFound => Error::KeyNotFound, - _ => Error::LedgerDevice(err), - } - } -} - -impl From for Error { - fn from(err: trezor::Error) -> Self { - match err { - trezor::Error::KeyNotFound => Error::KeyNotFound, - _ => Error::TrezorDevice(err), - } - } -} - -impl From for Error { - fn from(err: libusb::Error) -> Self { - Error::Usb(err) - } -} - -/// Specifies the direction of the `HardwareWallet` i.e, whether it arrived or left -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum DeviceDirection { - /// Device arrived - Arrived, - /// Device left - Left, -} - -impl fmt::Display for DeviceDirection { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - DeviceDirection::Arrived => write!(f, "arrived"), - DeviceDirection::Left => write!(f, "left"), - } - } -} - -/// Hardware wallet management interface. -pub struct HardwareWalletManager { - exiting: Arc, - ledger: Arc, - trezor: Arc, -} - -impl HardwareWalletManager { - /// Hardware wallet constructor - 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 = ledger::Manager::new(hidapi.clone()); - let trezor = trezor::Manager::new(hidapi.clone()); - let usb_context = Arc::new(libusb::Context::new()?); - - let l = ledger.clone(); - let t = trezor.clone(); - let exit = exiting.clone(); - - // Subscribe to all vendor IDs (VIDs) and product IDs (PIDs) - // This means that the `HardwareWalletManager` is responsible to validate the detected device - usb_context.register_callback( - None, None, Some(HID_USB_DEVICE_CLASS), - Box::new(EventHandler::new( - Arc::downgrade(&ledger), - Arc::downgrade(&trezor) - )) - )?; - - // Hardware event subscriber thread - thread::Builder::new() - .name("hw_wallet_manager".to_string()) - .spawn(move || { - if let Err(e) = l.update_devices(DeviceDirection::Arrived) { - debug!(target: "hw", "Ledger couldn't connect at startup, error: {}", e); - } - if let Err(e) = t.update_devices(DeviceDirection::Arrived) { - debug!(target: "hw", "Trezor couldn't connect at startup, error: {}", e); - } - - while !exit.load(atomic::Ordering::Acquire) { - if let Err(e) = usb_context.handle_events(Some(USB_EVENT_POLLING_INTERVAL)) { - debug!(target: "hw", "HardwareWalletManager event handler error: {}", e); - } - } - }) - .ok(); - - Ok(Self { - exiting, - trezor, - ledger, - }) - } - - /// Select key derivation path for a chain. - /// Currently, only one hard-coded keypath is supported - /// It is managed by `ethcore/account_provider` - pub fn set_key_path(&self, key_path: KeyPath) { - self.ledger.set_key_path(key_path); - self.trezor.set_key_path(key_path); - } - - /// List connected wallets. This only returns wallets that are ready to be used. - pub fn list_wallets(&self) -> Vec { - let mut wallets = Vec::new(); - wallets.extend(self.ledger.list_devices()); - wallets.extend(self.trezor.list_devices()); - wallets - } - - /// Return a list of paths to locked hardware wallets - /// This is only applicable to Trezor because Ledger only appears as - /// a device when it is unlocked - pub fn list_locked_wallets(&self) -> Result, 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.get_wallet(address) { - Some(info) - } else { - self.trezor.get_wallet(address) - } - } - - /// Sign a message with the wallet (only supported by Ledger) - pub fn sign_message(&self, address: &Address, msg: &[u8]) -> Result { - 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.get_wallet(address).is_some() { - Ok(self.ledger.sign_transaction(address, encoded_transaction)?) - } else if self.trezor.get_wallet(address).is_some() { - Ok(self.trezor.sign_transaction(address, t_info)?) - } else { - Err(Error::KeyNotFound) - } - } - - /// Send a pin to a device at a certain path to unlock it - /// This is only applicable to Trezor because Ledger only appears as - /// a device when it is unlocked - pub fn pin_matrix_ack(&self, path: &str, pin: &str) -> Result { - self.trezor.pin_matrix_ack(path, pin).map_err(Error::TrezorDevice) - } -} - -impl Drop for HardwareWalletManager { - fn drop(&mut self) { - // Indicate to the USB Hotplug handler that it - // shall terminate but don't wait for it to terminate. - // If it doesn't terminate for some reason USB Hotplug events will be handled - // even if the HardwareWalletManger has been dropped - self.exiting.store(true, atomic::Ordering::Release); - } -} - -/// Hardware wallet event handler -/// -/// Note, that this runs to completion and race-conditions can't occur but it can -/// stop other events for being processed with an infinite loop or similar -struct EventHandler { - ledger: Weak, - trezor: Weak, -} - -impl EventHandler { - /// Trezor event handler constructor - pub fn new(ledger: Weak, trezor: Weak) -> Self { - Self { ledger, trezor } - } - - fn extract_device_info(device: &libusb::Device) -> Result<(u16, u16), Error> { - let desc = device.device_descriptor()?; - Ok((desc.vendor_id(), desc.product_id())) - } -} - -impl libusb::Hotplug for EventHandler { - fn device_arrived(&mut self, device: libusb::Device) { - // Upgrade reference to an Arc - if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) { - // Version ID and Product ID are available - if let Ok((vid, pid)) = Self::extract_device_info(&device) { - if trezor::is_valid_trezor(vid, pid) { - if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived) { - trace!(target: "hw", "Trezor device was detected but connection failed"); - } - } else if ledger::is_valid_ledger(vid, pid) { - if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Arrived) { - trace!(target: "hw", "Ledger device was detected but connection failed"); - } - } - } - } - } - - fn device_left(&mut self, device: libusb::Device) { - // Upgrade reference to an Arc - if let (Some(ledger), Some(trezor)) = (self.ledger.upgrade(), self.trezor.upgrade()) { - // Version ID and Product ID are available - if let Ok((vid, pid)) = Self::extract_device_info(&device) { - if trezor::is_valid_trezor(vid, pid) { - if !trezor::try_connect_polling(&trezor, &MAX_POLLING_DURATION, DeviceDirection::Left) { - trace!(target: "hw", "Trezor device was detected but disconnection failed"); - } - } else if ledger::is_valid_ledger(vid, pid) { - if !ledger::try_connect_polling(&ledger, &MAX_POLLING_DURATION, DeviceDirection::Left) { - trace!(target: "hw", "Ledger device was detected but disconnection failed"); - } - } - } - } - } -} - -/// Helper to determine if a device is a valid HID -pub fn is_valid_hid_device(usage_page: u16, interface_number: i32) -> bool { - usage_page == HID_GLOBAL_USAGE_PAGE || interface_number == HID_USB_DEVICE_CLASS as i32 -} diff --git a/accounts/hw/src/trezor.rs b/accounts/hw/src/trezor.rs deleted file mode 100644 index b20123ed8b..0000000000 --- a/accounts/hw/src/trezor.rs +++ /dev/null @@ -1,463 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Trezor hardware wallet module. Supports Trezor v1. -//! See -//! and -//! for protocol details. - -use std::cmp::{min, max}; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use std::fmt; - -use ethereum_types::{U256, H256, Address}; -use ethkey::Signature; -use hidapi; -use libusb; -use parking_lot::{Mutex, RwLock}; -use protobuf::{self, Message, ProtobufEnum}; -use super::{DeviceDirection, WalletInfo, TransactionInfo, KeyPath, Wallet, Device, is_valid_hid_device}; -use trezor_sys::messages::{EthereumAddress, PinMatrixAck, MessageType, EthereumTxRequest, EthereumSignTx, EthereumGetAddress, EthereumTxAck, ButtonAck}; - -/// Trezor v1 vendor ID -const TREZOR_VID: u16 = 0x534c; -/// Trezor product IDs -const TREZOR_PIDS: [u16; 1] = [0x0001]; - -const ETH_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003C, 0x8000_0000, 0, 0]; // m/44'/60'/0'/0/0 -const ETC_DERIVATION_PATH: [u32; 5] = [0x8000_002C, 0x8000_003D, 0x8000_0000, 0, 0]; // m/44'/61'/0'/0/0 - -/// Hardware wallet error. -#[derive(Debug)] -pub enum Error { - /// Ethereum wallet protocol error. - Protocol(&'static str), - /// Hidapi error. - Usb(hidapi::HidError), - /// Libusb error - LibUsb(libusb::Error), - /// Device with request key is not available. - KeyNotFound, - /// Signing has been cancelled by user. - UserCancel, - /// The Message Type given in the trezor RPC call is not something we recognize - BadMessageType, - /// Trying to read from a closed device at the given path - LockedDevice(String), - /// Signing messages are not supported by Trezor - NoSigningMessage, - /// No device arrived - NoDeviceArrived, - /// No device left - NoDeviceLeft, - /// Invalid PID or VID - InvalidDevice, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::Protocol(ref s) => write!(f, "Trezor protocol error: {}", s), - Error::Usb(ref e) => write!(f, "USB communication error: {}", e), - Error::LibUsb(ref e) => write!(f, "LibUSB communication error: {}", e), - Error::KeyNotFound => write!(f, "Key not found"), - Error::UserCancel => write!(f, "Operation has been cancelled"), - Error::BadMessageType => write!(f, "Bad Message Type in RPC call"), - Error::LockedDevice(ref s) => write!(f, "Device is locked, needs PIN to perform operations: {}", s), - Error::NoSigningMessage=> write!(f, "Signing messages are not supported by Trezor"), - Error::NoDeviceArrived => write!(f, "No device arrived"), - Error::NoDeviceLeft => write!(f, "No device left"), - Error::InvalidDevice => write!(f, "Device with non-supported product ID or vendor ID was detected"), - } - } -} - -impl From for Error { - fn from(err: hidapi::HidError) -> Self { - Error::Usb(err) - } -} - -impl From for Error { - fn from(err: libusb::Error) -> Self { - Error::LibUsb(err) - } -} - -impl From for Error { - fn from(_: protobuf::ProtobufError) -> Self { - Error::Protocol(&"Could not read response from Trezor Device") - } -} - -/// Trezor device manager -pub struct Manager { - usb: Arc>, - devices: RwLock>, - locked_devices: RwLock>, - key_path: RwLock, -} - -/// HID Version used for the Trezor device -enum HidVersion { - V1, - V2, -} - -impl Manager { - /// Create a new instance. - pub fn new(usb: Arc>) -> Arc { - Arc::new(Self { - usb, - devices: RwLock::new(Vec::new()), - locked_devices: RwLock::new(Vec::new()), - key_path: RwLock::new(KeyPath::Ethereum), - }) - } - - pub fn pin_matrix_ack(&self, device_path: &str, pin: &str) -> Result { - let unlocked = { - let usb = self.usb.lock(); - let device = self.open_path(|| usb.open_path(&device_path))?; - let t = MessageType::MessageType_PinMatrixAck; - let mut m = PinMatrixAck::new(); - m.set_pin(pin.to_string()); - self.send_device_message(&device, t, &m)?; - let (resp_type, _) = self.read_device_response(&device)?; - match resp_type { - // Getting an Address back means it's unlocked, this is undocumented behavior - MessageType::MessageType_EthereumAddress => Ok(true), - // Getting anything else means we didn't unlock it - _ => Ok(false), - - } - }; - self.update_devices(DeviceDirection::Arrived)?; - unlocked - } - - fn u256_to_be_vec(&self, val: &U256) -> Vec { - let mut buf = [0_u8; 32]; - val.to_big_endian(&mut buf); - buf.iter().skip_while(|x| **x == 0).cloned().collect() - } - - fn signing_loop(&self, handle: &hidapi::HidDevice, chain_id: &Option, data: &[u8]) -> Result { - let (resp_type, bytes) = self.read_device_response(&handle)?; - match resp_type { - MessageType::MessageType_Cancel => Err(Error::UserCancel), - MessageType::MessageType_ButtonRequest => { - self.send_device_message(handle, MessageType::MessageType_ButtonAck, &ButtonAck::new())?; - // Signing loop goes back to the top and reading blocks - // for up to 5 minutes waiting for response from the device - // if the user doesn't click any button within 5 minutes you - // get a signing error and the device sort of locks up on the signing screen - self.signing_loop(handle, chain_id, data) - } - MessageType::MessageType_EthereumTxRequest => { - let resp: EthereumTxRequest = protobuf::core::parse_from_bytes(&bytes)?; - if resp.has_data_length() { - let mut msg = EthereumTxAck::new(); - let len = resp.get_data_length() as usize; - msg.set_data_chunk(data[..len].to_vec()); - self.send_device_message(handle, MessageType::MessageType_EthereumTxAck, &msg)?; - self.signing_loop(handle, chain_id, &data[len..]) - } else { - let v = resp.get_signature_v(); - let r = H256::from_slice(resp.get_signature_r()); - let s = H256::from_slice(resp.get_signature_s()); - if let Some(c_id) = *chain_id { - // If there is a chain_id supplied, Trezor will return a v - // part of the signature that is already adjusted for EIP-155, - // so v' = v + 2 * chain_id + 35, but code further down the - // pipeline will already do this transformation, so remove it here - let adjustment = 35 + 2 * c_id as u32; - Ok(Signature::from_rsv(&r, &s, (max(v, adjustment) - adjustment) as u8)) - } else { - // If there isn't a chain_id, v will be returned as v + 27 - let adjusted_v = if v < 27 { v } else { v - 27 }; - Ok(Signature::from_rsv(&r, &s, adjusted_v as u8)) - } - } - } - MessageType::MessageType_Failure => Err(Error::Protocol("Last message sent to Trezor failed")), - _ => Err(Error::Protocol("Unexpected response from Trezor device.")), - } - } - - fn send_device_message(&self, device: &hidapi::HidDevice, msg_type: MessageType, msg: &Message) -> Result { - let msg_id = msg_type as u16; - let mut message = msg.write_to_bytes()?; - let msg_size = message.len(); - let mut data = Vec::new(); - let hid_version = self.probe_hid_version(device)?; - // Magic constants - data.push(b'#'); - data.push(b'#'); - // Convert msg_id to BE and split into bytes - data.push(((msg_id >> 8) & 0xFF) as u8); - data.push((msg_id & 0xFF) as u8); - // Convert msg_size to BE and split into bytes - data.push(((msg_size >> 24) & 0xFF) as u8); - data.push(((msg_size >> 16) & 0xFF) as u8); - data.push(((msg_size >> 8) & 0xFF) as u8); - data.push((msg_size & 0xFF) as u8); - data.append(&mut message); - while data.len() % 63 > 0 { - data.push(0); - } - let mut total_written = 0; - for chunk in data.chunks(63) { - let mut padded_chunk = match hid_version { - HidVersion::V1 => vec![b'?'], - HidVersion::V2 => vec![0, b'?'], - }; - padded_chunk.extend_from_slice(&chunk); - total_written += device.write(&padded_chunk)?; - } - Ok(total_written) - } - - fn probe_hid_version(&self, device: &hidapi::HidDevice) -> Result { - let mut buf2 = [0xFF_u8; 65]; - buf2[0] = 0; - buf2[1] = 63; - let mut buf1 = [0xFF_u8; 64]; - buf1[0] = 63; - if device.write(&buf2)? == 65 { - Ok(HidVersion::V2) - } else if device.write(&buf1)? == 64 { - Ok(HidVersion::V1) - } else { - Err(Error::Usb("Unable to determine HID Version")) - } - } - - fn read_device_response(&self, device: &hidapi::HidDevice) -> Result<(MessageType, Vec), Error> { - let protocol_err = Error::Protocol(&"Unexpected wire response from Trezor Device"); - let mut buf = vec![0; 64]; - - let first_chunk = device.read_timeout(&mut buf, 300_000)?; - if first_chunk < 9 || buf[0] != b'?' || buf[1] != b'#' || buf[2] != b'#' { - return Err(protocol_err); - } - let msg_type = MessageType::from_i32(((buf[3] as i32 & 0xFF) << 8) + (buf[4] as i32 & 0xFF)).ok_or(protocol_err)?; - let msg_size = ((buf[5] as u32 & 0xFF) << 24) + ((buf[6] as u32 & 0xFF) << 16) + ((buf[7] as u32 & 0xFF) << 8) + (buf[8] as u32 & 0xFF); - let mut data = Vec::new(); - data.extend_from_slice(&buf[9..]); - while data.len() < (msg_size as usize) { - device.read_timeout(&mut buf, 10_000)?; - data.extend_from_slice(&buf[1..]); - } - Ok((msg_type, data[..msg_size as usize].to_vec())) - } -} - -impl<'a> Wallet<'a> for Manager { - type Error = Error; - type Transaction = &'a TransactionInfo; - - fn sign_transaction(&self, address: &Address, t_info: Self::Transaction) -> - Result { - 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| is_valid_trezor(d.vendor_id, d.product_id) && - is_valid_hid_device(d.usage_page, d.interface_number) - ) - .fold(Vec::new(), |mut v, d| { - match self.read_device(&usb, &d) { - Ok(info) => { - trace!(target: "hw", "Found device: {:?}", info); - v.push(info); - } - Err(e) => trace!(target: "hw", "Error reading device info: {}", e), - }; - v - }); - - let num_curr_devices = detected_devices.len(); - *self.devices.write() = detected_devices; - - match device_direction { - DeviceDirection::Arrived => { - if num_curr_devices > num_prev_devices { - Ok(num_curr_devices - num_prev_devices) - } else { - Err(Error::NoDeviceArrived) - } - } - DeviceDirection::Left => { - if num_prev_devices > num_curr_devices { - Ok(num_prev_devices - num_curr_devices) - } else { - Err(Error::NoDeviceLeft) - } - } - } - } - - fn read_device(&self, usb: &hidapi::HidApi, dev_info: &hidapi::HidDeviceInfo) -> Result { - 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) - } -} - -/// Poll the device in maximum `max_polling_duration` if it doesn't succeed -pub fn try_connect_polling(trezor: &Manager, duration: &Duration, dir: DeviceDirection) -> bool { - let start_time = Instant::now(); - while start_time.elapsed() <= *duration { - if let Ok(num_devices) = trezor.update_devices(dir) { - trace!(target: "hw", "{} Trezor devices {}", num_devices, dir); - return true - } - } - false -} - -/// Check if the detected device is a Trezor device by checking both the product ID and the vendor ID -pub fn is_valid_trezor(vid: u16, pid: u16) -> bool { - vid == TREZOR_VID && TREZOR_PIDS.contains(&pid) -} - -#[test] -#[ignore] -/// This test can't be run without an actual trezor device connected -/// (and unlocked) attached to the machine that's running the test -fn test_signature() { - use ethereum_types::Address; - use MAX_POLLING_DURATION; - use super::HardwareWalletManager; - - let manager = HardwareWalletManager::new().unwrap(); - - assert_eq!(try_connect_polling(&manager.trezor, &MAX_POLLING_DURATION, DeviceDirection::Arrived), true); - - let addr: Address = manager.list_wallets() - .iter() - .filter(|d| d.name == "TREZOR".to_string() && d.manufacturer == "SatoshiLabs".to_string()) - .nth(0) - .map(|d| d.address) - .unwrap(); - - let t_info = TransactionInfo { - nonce: U256::from(1), - gas_price: U256::from(100), - gas_limit: U256::from(21_000), - to: Some(Address::from(1337)), - chain_id: Some(1), - value: U256::from(1_000_000), - data: (&[1u8; 3000]).to_vec(), - }; - - let signature = manager.trezor.sign_transaction(&addr, &t_info); - assert!(signature.is_ok()); -} diff --git a/accounts/src/account_data.rs b/accounts/src/account_data.rs index a36d38740e..cceb74bbcd 100644 --- a/accounts/src/account_data.rs +++ b/accounts/src/account_data.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,7 +21,8 @@ use std::{ time::Instant, }; -use ethkey::{Address, Password}; +use parity_crypto::publickey::Address; +use ethkey::Password; use serde_derive::{Serialize, Deserialize}; use serde_json; diff --git a/accounts/src/error.rs b/accounts/src/error.rs index 2aa3564efd..1bbcf35ad4 100644 --- a/accounts/src/error.rs +++ b/accounts/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 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 @@ use std::fmt; use ethstore::{Error as SSError}; -use hardware_wallet::{Error as HardwareError}; /// Signing error #[derive(Debug)] @@ -26,8 +25,6 @@ pub enum SignError { NotUnlocked, /// Account does not exist. NotFound, - /// Low-level hardware device error. - Hardware(HardwareError), /// Low-level error from store SStore(SSError), } @@ -37,18 +34,11 @@ impl fmt::Display for SignError { match *self { SignError::NotUnlocked => write!(f, "Account is locked"), SignError::NotFound => write!(f, "Account does not exist"), - SignError::Hardware(ref e) => write!(f, "{}", e), SignError::SStore(ref e) => write!(f, "{}", e), } } } -impl From for SignError { - fn from(e: HardwareError) -> Self { - SignError::Hardware(e) - } -} - impl From for SignError { fn from(e: SSError) -> Self { SignError::SStore(e) diff --git a/accounts/src/lib.rs b/accounts/src/lib.rs index 0107eadad0..1e470b784f 100644 --- a/accounts/src/lib.rs +++ b/accounts/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,28 +22,23 @@ mod account_data; mod error; mod stores; -#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))] -extern crate fake_hardware_wallet as hardware_wallet; - use self::account_data::{Unlock, AccountData}; use self::stores::AddressBook; use std::collections::HashMap; use std::time::{Instant, Duration}; -use common_types::transaction::{Action, Transaction}; -use ethkey::{Address, Message, Public, Secret, Password, Random, Generator}; +use ethkey::Password; +use parity_crypto::publickey::{Address, Message, Public, Secret, Random, Generator, Signature}; use ethstore::accounts_dir::MemoryDirectory; use ethstore::{ SimpleSecretStore, SecretStore, EthStore, EthMultiStore, random_string, SecretVaultRef, StoreAccountRef, OpaqueSecret, }; -use log::{warn, debug}; +use log::warn; use parking_lot::RwLock; -pub use ethkey::Signature; pub use ethstore::{Derivation, IndexDerivation, KeyFile, Error}; -pub use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath, TransactionInfo}; pub use self::account_data::AccountMeta; pub use self::error::SignError; @@ -53,10 +48,6 @@ type AccountToken = Password; /// Account management settings. #[derive(Debug, Default)] pub struct AccountProviderSettings { - /// Enable hardware wallet support. - pub enable_hardware_wallets: bool, - /// Use the classic chain key on the hardware wallet. - pub hardware_wallet_classic_key: bool, /// Store raw account secret when unlocking the account permanently. pub unlock_keep_secret: bool, /// Disallowed accounts. @@ -73,11 +64,9 @@ pub struct AccountProvider { /// Address book. address_book: RwLock, /// Accounts on disk - sstore: Box, + sstore: Box, /// Accounts unlocked with rolling tokens transient_sstore: EthMultiStore, - /// Accounts in hardware wallets. - hardware_store: Option, /// When unlocking account permanently we additionally keep a raw secret in memory /// to increase the performance of transaction signing. unlock_keep_secret: bool, @@ -91,19 +80,7 @@ fn transient_sstore() -> EthMultiStore { impl AccountProvider { /// Creates new account provider. - pub fn new(sstore: Box, settings: AccountProviderSettings) -> Self { - let mut hardware_store = None; - - if settings.enable_hardware_wallets { - match HardwareWalletManager::new() { - Ok(manager) => { - manager.set_key_path(if settings.hardware_wallet_classic_key { KeyPath::EthereumClassic } else { KeyPath::Ethereum }); - hardware_store = Some(manager) - }, - Err(e) => debug!("Error initializing hardware wallets: {}", e), - } - } - + pub fn new(sstore: Box, settings: AccountProviderSettings) -> Self { if let Ok(accounts) = sstore.accounts() { for account in accounts.into_iter().filter(|a| settings.blacklisted_accounts.contains(&a.address)) { warn!("Local Account {} has a blacklisted (known to be weak) address and will be ignored", @@ -121,9 +98,8 @@ impl AccountProvider { unlocked_secrets: RwLock::new(HashMap::new()), unlocked: RwLock::new(HashMap::new()), address_book: RwLock::new(address_book), - sstore: sstore, + sstore, transient_sstore: transient_sstore(), - hardware_store: hardware_store, unlock_keep_secret: settings.unlock_keep_secret, blacklisted_accounts: settings.blacklisted_accounts, } @@ -137,7 +113,6 @@ impl AccountProvider { address_book: RwLock::new(AddressBook::transient()), sstore: Box::new(EthStore::open(Box::new(MemoryDirectory::default())).expect("MemoryDirectory load always succeeds; qed")), transient_sstore: transient_sstore(), - hardware_store: None, unlock_keep_secret: false, blacklisted_accounts: vec![], } @@ -219,34 +194,6 @@ impl AccountProvider { Ok(self.accounts()?.first().cloned().unwrap_or_default()) } - /// Returns addresses of hardware accounts. - pub fn hardware_accounts(&self) -> Result, Error> { - if let Some(accounts) = self.hardware_store.as_ref().map(|h| h.list_wallets()) { - if !accounts.is_empty() { - return Ok(accounts.into_iter().map(|a| a.address).collect()); - } - } - Err(Error::Custom("No hardware wallet accounts were found".into())) - } - - /// Get a list of paths to locked hardware wallets - pub fn locked_hardware_accounts(&self) -> Result, SignError> { - match self.hardware_store.as_ref().map(|h| h.list_locked_wallets()) { - None => Err(SignError::NotFound), - Some(Err(e)) => Err(SignError::Hardware(e)), - Some(Ok(s)) => Ok(s), - } - } - - /// Provide a pin to a locked hardware wallet on USB path to unlock it - pub fn hardware_pin_matrix_ack(&self, path: &str, pin: &str) -> Result { - match self.hardware_store.as_ref().map(|h| h.pin_matrix_ack(path, pin)) { - None => Err(SignError::NotFound), - Some(Err(e)) => Err(SignError::Hardware(e)), - Some(Ok(s)) => Ok(s), - } - } - /// Returns each address along with metadata. pub fn addresses_info(&self) -> HashMap { self.address_book.read().get() @@ -277,36 +224,14 @@ impl AccountProvider { Ok(r) } - /// Returns each hardware account along with name and meta. - pub fn hardware_accounts_info(&self) -> Result, Error> { - let r = self.hardware_accounts()? - .into_iter() - .map(|address| (address.clone(), self.account_meta(address).ok().unwrap_or_default())) - .collect(); - Ok(r) - } - - /// Returns each hardware account along with name and meta. - pub fn is_hardware_address(&self, address: &Address) -> bool { - self.hardware_store.as_ref().and_then(|s| s.wallet_info(address)).is_some() - } - /// Returns each account along with name and meta. pub fn account_meta(&self, address: Address) -> Result { - if let Some(info) = self.hardware_store.as_ref().and_then(|s| s.wallet_info(&address)) { - Ok(AccountMeta { - name: info.name, - meta: info.manufacturer, - uuid: None, - }) - } else { - let account = self.sstore.account_ref(&address)?; - Ok(AccountMeta { - name: self.sstore.name(&account)?, - meta: self.sstore.meta(&account)?, - uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid - }) - } + let account = self.sstore.account_ref(&address)?; + Ok(AccountMeta { + name: self.sstore.name(&account)?, + meta: self.sstore.meta(&account)?, + uuid: self.sstore.uuid(&account).ok().map(Into::into), // allowed to not have a Uuid + }) } /// Returns account public key. @@ -370,10 +295,7 @@ impl AccountProvider { let _ = self.sstore.sign(&account, &password, &Default::default())?; } - let data = AccountData { - unlock: unlock, - password: password, - }; + let data = AccountData { unlock, password }; unlocked.insert(account, data); Ok(()) @@ -575,43 +497,13 @@ impl AccountProvider { self.sstore.set_vault_meta(name, meta) .map_err(Into::into) } - - /// Sign message with hardware wallet. - pub fn sign_message_with_hardware(&self, address: &Address, message: &[u8]) -> Result { - match self.hardware_store.as_ref().map(|s| s.sign_message(address, message)) { - None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound), - Some(Err(e)) => Err(From::from(e)), - Some(Ok(s)) => Ok(s), - } - } - - /// Sign transaction with hardware wallet. - pub fn sign_transaction_with_hardware(&self, address: &Address, transaction: &Transaction, chain_id: Option, rlp_encoded_transaction: &[u8]) -> Result { - let t_info = TransactionInfo { - nonce: transaction.nonce, - gas_price: transaction.gas_price, - gas_limit: transaction.gas, - to: match transaction.action { - Action::Create => None, - Action::Call(ref to) => Some(to.clone()), - }, - value: transaction.value, - data: transaction.data.to_vec(), - chain_id: chain_id, - }; - match self.hardware_store.as_ref().map(|s| s.sign_transaction(&address, &t_info, rlp_encoded_transaction)) { - None | Some(Err(HardwareError::KeyNotFound)) => Err(SignError::NotFound), - Some(Err(e)) => Err(From::from(e)), - Some(Ok(s)) => Ok(s), - } - } } #[cfg(test)] mod tests { use super::{AccountProvider, Unlock}; use std::time::{Duration, Instant}; - use ethkey::{Generator, Random, Address}; + use parity_crypto::publickey::{Generator, Random, Address}; use ethstore::{StoreAccountRef, Derivation}; use ethereum_types::H256; @@ -636,7 +528,7 @@ mod tests { let derived_addr = ap.derive_account( &kp.address(), None, - Derivation::SoftHash(H256::from(999)), + Derivation::SoftHash(H256::from_low_u64_be(999)), false, ).expect("Derivation should not fail"); @@ -654,7 +546,7 @@ mod tests { let derived_addr = ap.derive_account( &kp.address(), None, - Derivation::SoftHash(H256::from(999)), + Derivation::SoftHash(H256::from_low_u64_be(999)), true, ).expect("Derivation should not fail"); @@ -675,7 +567,7 @@ mod tests { let derived_addr = ap.derive_account( &kp.address(), None, - Derivation::SoftHash(H256::from(1999)), + Derivation::SoftHash(H256::from_low_u64_be(1999)), true, ).expect("Derivation should not fail"); ap.unlock_account_permanently(derived_addr, "base".into()) @@ -687,7 +579,7 @@ mod tests { let signed_msg2 = ap.sign_derived( &kp.address(), None, - Derivation::SoftHash(H256::from(1999)), + Derivation::SoftHash(H256::from_low_u64_be(1999)), msg, ).expect("Derived signing with existing unlocked account should not fail"); diff --git a/accounts/src/stores.rs b/accounts/src/stores.rs index baa26cc48b..1d1d0ea713 100644 --- a/accounts/src/stores.rs +++ b/accounts/src/stores.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use std::{fs, fmt, hash, ops}; use std::collections::HashMap; use std::path::{Path, PathBuf}; -use ethkey::Address; +use parity_crypto::publickey::Address; use log::{trace, warn}; use crate::AccountMeta; @@ -130,7 +130,7 @@ impl DiskMap { trace!(target: "diskmap", "revert {:?}", self.path); let _ = fs::File::open(self.path.clone()) .map_err(|e| trace!(target: "diskmap", "Couldn't open disk map: {}", e)) - .and_then(|f| read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map: {}", e))) + .and_then(|f| read(f).map_err(|e| warn!(target: "diskmap", "Couldn't read disk map at: {:?} {}", self.path, e))) .and_then(|m| { self.cache = m; Ok(()) @@ -144,16 +144,16 @@ impl DiskMap { if self.transient { return; } trace!(target: "diskmap", "save {:?}", self.path); let _ = fs::File::create(self.path.clone()) - .map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing: {}", e)) + .map_err(|e| warn!(target: "diskmap", "Couldn't open disk map for writing at: {:?} {}", self.path, e)) .and_then(|mut f| { - write(&self.cache, &mut f).map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map: {}", e)) + write(&self.cache, &mut f).map_err(|e| warn!(target: "diskmap", "Couldn't write to disk map at: {:?} {}", self.path, e)) }); } } #[cfg(test)] mod tests { - use super::AddressBook; + use super::{AddressBook, Address}; use std::collections::HashMap; use tempdir::TempDir; use crate::account_data::AccountMeta; @@ -162,12 +162,12 @@ mod tests { fn should_save_and_reload_address_book() { let tempdir = TempDir::new("").unwrap(); let mut b = AddressBook::new(tempdir.path()); - b.set_name(1.into(), "One".to_owned()); - b.set_meta(1.into(), "{1:1}".to_owned()); + b.set_name(Address::from_low_u64_be(1), "One".to_owned()); + b.set_meta(Address::from_low_u64_be(1), "{1:1}".to_owned()); let b = AddressBook::new(tempdir.path()); assert_eq!(b.get(), vec![ - (1, AccountMeta {name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}) - ].into_iter().map(|(a, b)| (a.into(), b)).collect::>()); + (Address::from_low_u64_be(1), AccountMeta {name: "One".to_owned(), meta: "{1:1}".to_owned(), uuid: None}) + ].into_iter().collect::>()); } #[test] @@ -175,15 +175,15 @@ mod tests { let tempdir = TempDir::new("").unwrap(); let mut b = AddressBook::new(tempdir.path()); - b.set_name(1.into(), "One".to_owned()); - b.set_name(2.into(), "Two".to_owned()); - b.set_name(3.into(), "Three".to_owned()); - b.remove(2.into()); + b.set_name(Address::from_low_u64_be(1), "One".to_owned()); + b.set_name(Address::from_low_u64_be(2), "Two".to_owned()); + b.set_name(Address::from_low_u64_be(3), "Three".to_owned()); + b.remove(Address::from_low_u64_be(2).into()); let b = AddressBook::new(tempdir.path()); assert_eq!(b.get(), vec![ - (1, AccountMeta{name: "One".to_owned(), meta: "{}".to_owned(), uuid: None}), - (3, AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}), - ].into_iter().map(|(a, b)| (a.into(), b)).collect::>()); + (Address::from_low_u64_be(1), AccountMeta{name: "One".to_owned(), meta: "{}".to_owned(), uuid: None}), + (Address::from_low_u64_be(3), AccountMeta{name: "Three".to_owned(), meta: "{}".to_owned(), uuid: None}), + ].into_iter().collect::>()); } } diff --git a/chainspec/src/main.rs b/chainspec/src/main.rs index 45490fe7f6..360558f43c 100644 --- a/chainspec/src/main.rs +++ b/chainspec/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/cli-signer/Cargo.toml b/cli-signer/Cargo.toml index aa56da6f85..3fc0b33298 100644 --- a/cli-signer/Cargo.toml +++ b/cli-signer/Cargo.toml @@ -7,7 +7,7 @@ version = "1.4.0" authors = ["Parity "] [dependencies] -ethereum-types = "0.4" +ethereum-types = "0.8.0" futures = "0.1" rpassword = "1.0" parity-rpc = { path = "../rpc" } diff --git a/cli-signer/rpc-client/Cargo.toml b/cli-signer/rpc-client/Cargo.toml index 50565c0b34..c8c568fc19 100644 --- a/cli-signer/rpc-client/Cargo.toml +++ b/cli-signer/rpc-client/Cargo.toml @@ -7,15 +7,15 @@ version = "1.4.0" authors = ["Parity "] [dependencies] -ethereum-types = "0.4" +ethereum-types = "0.8.0" futures = "0.1" log = "0.4" serde = "1.0" serde_json = "1.0" -url = "1.2.0" +url = "2.1.0" matches = "0.1" -parking_lot = "0.7" -jsonrpc-core = "10.0.1" -jsonrpc-ws-server = "10.0.1" +parking_lot = "0.9" +jsonrpc-core = "14.0.3" +jsonrpc-ws-server = "14.0.3" parity-rpc = { path = "../../rpc" } -keccak-hash = "0.1" +keccak-hash = "0.4.0" diff --git a/cli-signer/rpc-client/src/client.rs b/cli-signer/rpc-client/src/client.rs index a9ca5e68a0..c9038baccb 100644 --- a/cli-signer/rpc-client/src/client.rs +++ b/cli-signer/rpc-client/src/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/cli-signer/rpc-client/src/lib.rs b/cli-signer/rpc-client/src/lib.rs index d0e087e59d..2618f756b4 100644 --- a/cli-signer/rpc-client/src/lib.rs +++ b/cli-signer/rpc-client/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -36,7 +36,7 @@ extern crate log; extern crate matches; /// Boxed future response. -pub type BoxFuture = Box + Send>; +pub type BoxFuture = Box + Send>; #[cfg(test)] mod tests { diff --git a/cli-signer/rpc-client/src/signer_client.rs b/cli-signer/rpc-client/src/signer_client.rs index 997841936d..339aafce46 100644 --- a/cli-signer/rpc-client/src/signer_client.rs +++ b/cli-signer/rpc-client/src/signer_client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/cli-signer/src/lib.rs b/cli-signer/src/lib.rs index 3ef6e70549..b954e099eb 100644 --- a/cli-signer/src/lib.rs +++ b/cli-signer/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/docs/CHANGELOG-2.5.md b/docs/CHANGELOG-2.5.md new file mode 100644 index 0000000000..4e623b2ef8 --- /dev/null +++ b/docs/CHANGELOG-2.5.md @@ -0,0 +1,220 @@ +## Parity-Ethereum [v2.5.13](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.13) + +Parity Ethereum v2.5.13-stable is a security release. Valid blocks with manipulated transactions (added/replaced) cause the client to stall. + +The full list of included changes: +* Make sure to not mark block header hash as invalid if only the body is wrong (#11356) + +## Parity-Ethereum [v2.5.12](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.12) + +Parity Ethereum v2.5.12-stable is a patch release that adds Istanbul hardfork +block numbers for POA and xDai networks, implements ECIP-1056 and implements +EIP-2384/2387 - Muir Glacier. + +The full list of included changes: +* Enable EIP-2384 for ice age hard fork (#11281) +* ethcore/res: activate agharta on classic 9573000 (#11331) +* Istanbul HF in xDai (2019-12-12) (#11299) +* Istanbul HF in POA Core (2019-12-19) (#11298) +* Istanbul HF in POA Sokol (2019-12-05) (#11282) +* Activate ecip-1061 on kotti and mordor (#11338) +* Enable basic verification of local transactions (#11332) +* Disallow EIP-86 style null signatures for transactions outside tests (#11335) + + +## Parity-Ethereum [v2.5.11](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.11) + +Parity Ethereum v2.5.11-stable is an emergency patch release that adds the missing +eip1344_transition for mainnet - Users are advised to update as soon as possible +to prevent any issues with the imminent Istanbul hardfork + +The full list of included changes: +- [chainspec]: add `eip1344_transition` for istanbul (#11301) + +## Parity-Ethereum [v2.5.10](https://github.com/paritytech/parity-ethereum/releases/tag/2.5.10) + +Parity Ethereum v2.5.10-stable is a patch release that adds block numbers for +activating the Istanbul hardfork on mainnet, as well as a large number of +various bugfixes, QoL changes, some code cleanup/refactoring and other +miscellaneous changes. + +This release removes legacy aliases for the mainnet. If you specify `--chain homestead`, `--chain frontier` or `--chain byzantium`, this will need to be changed to one of: `--chain eth`, `--chain ethereum`, `--chain foundation` or `--chain mainnet`. + +The full list of included changes: + +* ropsten #6631425 foundation #8798209 (#11201) +* [stable] builtin, istanbul and mordor testnet backports (#11234) + * ethcore-builtin (#10850) + * [builtin]: support `multiple prices and activations` in chain spec (#11039) + * [chain specs]: activate `Istanbul` on mainnet (#11228) + * ethcore/res: add mordor testnet configuration (#11200) +* Update list of bootnodes for xDai chain (#11236) +* ethcore: remove `test-helper feat` from build (#11047) +* Secret store: fix Instant::now() related race in net_keep_alive (#11155) (#11159) +* [stable]: backport #10691 and #10683 (#11143) + * Fix compiler warning (that will become an error) (#10683) + * Refactor Clique stepping (#10691) +* Add Constantinople eips to the dev (instant_seal) config (#10809) +* Add cargo-remote dir to .gitignore (?) +* Insert explicit warning into the panic hook (#11225) +* Fix docker centos build (#11226) +* Update MIX bootnodes. (#11203) +* Use provided usd-per-eth value if an endpoint is specified (#11209) +* Add new line after writing block to hex file. (#10984) +* Type annotation for next_key() matching of json filter options (#11192) (but no `FilterOption` in 2.5 so…) +* Upgrade jsonrpc to latest (#11206) +* [CI] check evmbin build (#11096) +* Correct EIP-712 encoding (#11092) +* [client]: Fix for incorrectly dropped consensus messages (#11086) +* Fix block detail updating (#11015) +* Switching sccache from local to Redis (#10971) +* Made ecrecover implementation trait public (#11188) +* [dependencies]: jsonrpc `14.0.1` (#11183) +* [receipt]: add `sender` & `receiver` to `RichReceipts` (#11179) +* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180) +* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175) +* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172) +* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127) +* Cleanup stratum a bit (#11161) +* Upgrade to jsonrpc v14 (#11151) +* SecretStore: expose restore_key_public in HTTP API (#10241) + +## Parity-Ethereum [v2.5.9](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.9) + +Parity Ethereum v2.5.9-stable is a patch release that adds the block numbers for activating the Istanbul hardfork on test networks: Ropsten, Görli, Rinkeby and Kovan. + +The full list of included changes: + +* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068) +* [json-spec] make blake2 pricing spec more readable (#11034) + +## Parity-Ethereum [v2.5.8](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.8) + +Parity Ethereum v2.5.8-stable is a patch release that improves security, stability and performance. + +* The most noteworthy improvement in this release is incorporating all the EIPs required for the Istanbul hard fork. +* This release also fixes certain security and performance issues, one of which was suspected to be consensus-threatening but turned out to be benign. Thanks to Martin Holst Swende and Felix Lange from the Ethereum Foundation for bringing the suspicious issue to our attention. + +The full list of included changes: + +* add more tx tests (#11038) +* Fix parallel transactions race-condition (#10995) +* Add blake2_f precompile (#11017) +* [trace] introduce trace failed to Ext (#11019) +* Edit publish-onchain.sh to use https (#11016) +* Fix deadlock in network-devp2p (#11013) +* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008) +* xDai chain support and nodes list update (#10989) +* EIP 2028: transaction gas lowered from 68 to 16 (#10987) +* EIP-1344 Add CHAINID op-code (#10983) +* manual publish jobs for releases, no changes for nightlies (#10977) +* [blooms-db] Fix benchmarks (#10974) +* Verify transaction against its block during import (#10954) +* Better error message for rpc gas price errors (#10931) +* tx-pool: accept local tx with higher gas price when pool full (#10901) +* Fix fork choice (#10837) +* Cleanup unused vm dependencies (#10787) +* Fix compilation on recent nightlies (#10991) +* Don't build rpc with ethcore test-helpers (#11048) +* EIP 1884 Re-pricing of trie-size dependent operations (#10992) +* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191) + +## Parity-Ethereum [v2.5.7](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.7) + +Parity Ethereum v2.5.7-stable is a bugfix release that fixes a potential DoS attack in the trace_call RPC method. This is a critical upgrade for anyone running Parity nodes with RPC exposed to the public internet (and highly recommended for anyone else). For details see this blog post. + +## Parity-Ethereum [v2.5.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.6) + +Parity-Ethereum v2.5.6-stable is a bugfix release that improves stability. + +* Allow specifying hostnames for node URLs +* Fix a bug where archive nodes were losing peers + +The full list of included changes: + +* Kaspersky AV whitelisting (#10919) +* Avast whitelist script (#10900) +* Docker images renaming (#10863) +* Remove excessive warning (#10831) +* Allow --nat extip:your.host.here.org (#10830) +* When updating the client or when called from RPC, sleep should mean sleep (#10814) +* added new ropsten-bootnode and removed old one (#10794) +* ethkey no longer uses byteorder (#10786) +* Do not drop the peer with None difficulty (#10772) +* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652) + +## Parity-Ethereum [v2.5.5](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.5) + +Parity-Ethereum v2.5.5-stable is a minor release that improves performance and stability. +This release stabilises the 2.5 branch. + +As of today, Parity-Ethereum 2.4 reaches end of life and everyone is +encouraged to upgrade. + +## Parity-Ethereum [v2.5.4](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.4) + +Parity Ethereum v2.5.4-beta is a security update that addresses servo/rust-smallvec#148 + +The full list of included changes: + +* cargo update -p smallvec ([#10822](https://github.com/paritytech/parity-ethereum/pull/10822)) + +## Parity-Ethereum [v2.5.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.3) + +Parity-Ethereum 2.5.3-beta is a bugfix release that improves performance and stability. + +* EthereumClassic: activate the Atlantis Hardfork +* Clique: fix time overflow +* State tests: treat empty accounts the same as non-existant accounts (EIP 1052) +* Networking: support discovery-only peers (geth bootnodes) +* Snapshotting: fix unclean shutdown while snappshotting is under way + +The full list of included changes: + +* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/paritytech/parity-ethereum/pull/10766)) +* fix docker tags for publishing ([#10741](https://github.com/paritytech/parity-ethereum/pull/10741)) +* fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/paritytech/parity-ethereum/pull/10720)) +* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/paritytech/parity-ethereum/pull/10775)) +* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet ([#10705](https://github.com/paritytech/parity-ethereum/pull/10705)) +* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/paritytech/parity-ethereum/pull/10744)) + +## Parity-Ethereum [v2.5.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.2) + +Parity-Ethereum 2.5.2-beta is a bugfix release that improves performance and stability. + +Among others, it enables the _Atlantis_ hardfork on **Morden** and **Kotti** Classic networks. + +The full list of included changes: + +* [CI] allow cargo audit to fail ([#10676](https://github.com/paritytech/parity-ethereum/pull/10676)) +* Reset blockchain properly ([#10669](https://github.com/paritytech/parity-ethereum/pull/10669)) +* new image ([#10673](https://github.com/paritytech/parity-ethereum/pull/10673)) +* Update publishing ([#10644](https://github.com/paritytech/parity-ethereum/pull/10644)) +* enable lto for release builds ([#10717](https://github.com/paritytech/parity-ethereum/pull/10717)) +* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/paritytech/parity-ethereum/pull/10719)) +* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/paritytech/parity-ethereum/pull/10731)) + +## Parity-Ethereum [v2.5.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.1) + +Parity-Ethereum 2.5.1-beta is a bugfix release that improves performance and stability. + +Among others, it enables the Petersburg hardfork on **Rinkeby** and **POA-Core** Network, as well as the **Kovan** Network community hardfork. + +The full list of included changes: + +* ci: publish docs debug ([#10638](https://github.com/paritytech/parity-ethereum/pull/10638)) + +## Parity-Ethereum [v2.5.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.5.0) + +Parity-Ethereum 2.5.0-beta is a minor release that improves performance and stabilizes the 2.5 branch by marking it as beta release. + +- This release adds support for the Clique consensus engine ([#9981](https://github.com/paritytech/parity-ethereum/pull/9981)) + - This enables Parity-Ethereum users to use the Görli, the Kotti Classic, and the legacy Rinkeby testnet. To get started try `parity --chain goerli`; note that light client support is currently not yet fully functional. +- This release removes the dead chain configs for Easthub and Ethereum Social ([#10531](https://github.com/paritytech/parity-ethereum/pull/10531)) + +As of today, Parity-Ethereum 2.3 reaches end of life and everyone is encouraged to upgrade. + +The full list of included changes: + +* fix(light cull): poll light cull instead of timer ([#10559](https://github.com/paritytech/parity-ethereum/pull/10559)) + diff --git a/docs/CHANGELOG-2.6.md b/docs/CHANGELOG-2.6.md new file mode 100644 index 0000000000..ce704ffa1c --- /dev/null +++ b/docs/CHANGELOG-2.6.md @@ -0,0 +1,307 @@ +## Parity-Ethereum [v2.6.8](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.8) + +Parity Ethereum v2.6.8-beta is a security release. Valid blocks with manipulated transactions (added/replaced) cause the client to stall. + +The full list of included changes: +* Make sure to not mark block header hash as invalid if only the body is wrong (#11356) + +## Parity-Ethereum [v2.6.7](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.7) + +Parity Ethereum v2.6.7-beta is a patch release that adds Istanbul hardfork +block numbers for POA and xDai networks, implements ECIP-1056 and implements +EIP-2384/2387 - Muir Glacier. + +The full list of included changes: +* Enable EIP-2384 for ice age hard fork (#11281) +* ethcore/res: activate agharta on classic 9573000 (#11331) +* Istanbul HF in xDai (2019-12-12) (#11299) +* Istanbul HF in POA Core (2019-12-19) (#11298) +* Istanbul HF in POA Sokol (2019-12-05) (#11282) +* Activate ecip-1061 on kotti and mordor (#11338) +* Enable basic verification of local transactions (#11332) +* Disallow EIP-86 style null signatures for transactions outside tests (#11335) +* SecretStore database migration to v4 (#11322) + +## Parity-Ethereum [v2.6.6](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.6) + +Parity Ethereum v2.6.6-beta is an emergency patch release that adds the missing +eip1344_transition for mainnet - Users are advised to update as soon as possible +to prevent any issues with the imminent Istanbul hardfork + +The full list of included changes: +* [chainspec]: add `eip1344_transition` for istanbul (#11301) + +## Parity-Ethereum [v2.6.5](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.5) + +Parity Ethereum v2.6.5-beta is a patch release that adds block numbers for activating the Istanbul hardfork on mainnet, as well as a large number of various bugfixes, QoL changes, some code cleanup/refactoring and other miscellaneous changes. + +This release removes legacy aliases for the mainnet. If you specify `--chain homestead`, `--chain frontier` or `--chain byzantium`, this will need to be changed to one of: `--chain eth`, `--chain ethereum`, `--chain foundation` or `--chain mainnet`. + +This release includes important changes to how snapshots are produced. The size of the Ethereum account state means that producing a snapshot takes a long while; most nodes today are not able to finish before the relevant state is pruned. Starting with v2.6.5, pruning is paused while a snapshot is underway, hopefully fixing the current dearth of recent snapshots. The downside to this is that memory usage goes up while a snapshot is produced. + +The full list of included changes: + +* [CI] check evmbin build (#11096) +* Correct EIP-712 encoding (#11092) +* [client]: Fix for incorrectly dropped consensus messages (#11082) (#11086) +* Update hardcoded headers (foundation, classic, kovan, xdai, ewc, ...) (#11053) +* Add cargo-remote dir to .gitignore (?) +* Update light client headers: ropsten 6631425 foundation 8798209 (#11201) +* Update list of bootnodes for xDai chain (#11236) +* ethcore/res: add mordor testnet configuration (#11200) +* [chain specs]: activate Istanbul on mainnet (#11228) +* [builtin]: support multiple prices and activations in chain spec (#11039) +* [receipt]: add sender & receiver to RichReceipts (#11179) +* [ethcore/builtin]: do not panic in blake2pricer on short input (#11180) +* Made ecrecover implementation trait public (#11188) +* Fix docker centos build (#11226) +* Update MIX bootnodes. (#11203) +* Insert explicit warning into the panic hook (#11225) +* Use provided usd-per-eth value if an endpoint is specified (#11209) +* Cleanup stratum a bit (#11161) +* Add Constantinople EIPs to the dev (instant_seal) config (#10809) (already backported) +* util Host: fix a double Read Lock bug in fn Host::session_readable() (#11175) +* ethcore client: fix a double Read Lock bug in fn Client::logs() (#11172) +* Type annotation for next_key() matching of json filter options (#11192) +* Upgrade jsonrpc to latest (#11206) +* [dependencies]: jsonrpc 14.0.1 (#11183) +* Upgrade to jsonrpc v14 (#11151) +* Switching sccache from local to Redis (#10971) +* Snapshot restoration overhaul (#11219) +* Add new line after writing block to hex file. (#10984) +* Pause pruning while snapshotting (#11178) +* Change how RPCs eth_call and eth_estimateGas handle "Pending" (#11127) +* Fix block detail updating (#11015) +* Make InstantSeal Instant again #11186 +* Filter out some bad ropsten warp snapshots (#11247) +* Allow default block parameter to be blockHash (#10932) + +## Parity-Ethereum [v2.6.4](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.4) + +Parity Ethereum v2.6.4-stable is a patch release that adds the block numbers for activating the Istanbul hardfork on test networks: Ropsten, Görli, Rinkeby and Kovan. + +A full list of included changes: + +* ethcore/res: activate Istanbul on Ropsten, Görli, Rinkeby, Kovan (#11068) +* cleanup json crate (#11027) +* [json-spec] make blake2 pricing spec more readable (#11034) +* Update JSON tests to d4f86ecf4aa7c (#11054) + +## Parity-Ethereum [v2.6.3](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.3) + +Parity Ethereum v2.6.3-stable is a patch release that improves security, stability and performance. + +* The most noteworthy improvement in this release is incorporating all the EIPs required for the Istanbul hard fork. +* This release also fixes certain security and performance issues, one of which was suspected to be consensus-threatening but turned out to be benign. Thanks to Martin Holst Swende and Felix Lange from the Ethereum Foundation for bringing the suspicious issue to our attention. + +The full list of included changes: + +* add more tx tests (#11038) +* Fix parallel transactions race-condition (#10995) +* Add blake2_f precompile (#11017) +* [trace] introduce trace failed to Ext (#11019) +* Edit publish-onchain.sh to use https (#11016) +* Fix deadlock in network-devp2p (#11013) +* EIP 1108: Reduce alt_bn128 precompile gas costs (#11008) +* xDai chain support and nodes list update (#10989) +* EIP 2028: transaction gas lowered from 68 to 16 (#10987) +* EIP-1344 Add CHAINID op-code (#10983) +* manual publish jobs for releases, no changes for nightlies (#10977) +* [blooms-db] Fix benchmarks (#10974) +* Verify transaction against its block during import (#10954) +* Better error message for rpc gas price errors (#10931) +* Fix fork choice (#10837) +* Fix compilation on recent nightlies (#10991) +* Don't build rpc with ethcore test-helpers (#11048) +* EIP 1884 Re-pricing of trie-size dependent operations (#10992) +* Implement EIP-1283 reenable transition, EIP-1706 and EIP-2200 (#10191) + +## Parity-Ethereum [v2.6.2](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.2) + +Parity Ethereum v2.6.2-stable is a bugfix release that fixes a potential DoS attack in the trace_call RPC method. This is a critical upgrade for anyone running Parity nodes with RPC exposed to the public internet (and highly recommended for anyone else). For details see this blog post. + +## Parity-Ethereum [v2.6.1](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.1) + +Parity-Ethereum 2.6.1-beta is a patch release that improves stability. + +This release includes: + * Allow specifying hostnames for node URLs + * Fix a bug where archive nodes were losing peers + * Add support for Energy Web Foundations new chains 'Volta' and 'EWC', and remove their deprecated 'Tobalaba' chain. + +The full list of included changes: + * Add support for Energy Web Foundation's new chains (#10957) + * Kaspersky AV whitelisting (#10919) + * Avast whitelist script (#10900) + * Docker images renaming (#10863) + * Remove excessive warning (#10831) + * Allow --nat extip:your.host.here.org (#10830) + * When updating the client or when called from RPC, sleep should mean sleep (#10814) + * added new ropsten-bootnode and removed old one (#10794) + * ethkey no longer uses byteorder (#10786) + * docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions (#10652) + +## Parity-Ethereum [v2.6.0](https://github.com/paritytech/parity-ethereum/releases/tag/v2.6.0) + +Parity-Ethereum 2.6.0-beta is a minor release that stabilizes the 2.6 branch by +marking it as a beta release. + +This release includes: + * Major refactoring of the codebase + * Many bugfixes + * Significant improvements to logging, error and warning message clarity. + * SecretStore: remove support of old database formats (#10757) + * This is a potentially breaking change if you have not upgraded for + quite some time. + + As of today, Parity-Ethereum 2.4 reaches end of life, and everyone is + encouraged to upgrade. + +The full list of included changes: +* update jsonrpc to 12.0 ([#10841](https://github.com/paritytech/parity-ethereum/pull/10841)) +* Move more code into state-account ([#10840](https://github.com/paritytech/parity-ethereum/pull/10840)) +* Extract AccountDB to account-db ([#10839](https://github.com/paritytech/parity-ethereum/pull/10839)) +* Extricate PodAccount and state Account to own crates ([#10838](https://github.com/paritytech/parity-ethereum/pull/10838)) +* Fix fork choice ([#10837](https://github.com/paritytech/parity-ethereum/pull/10837)) +* tests: Relates to #10655: Test instructions for Readme ([#10835](https://github.com/paritytech/parity-ethereum/pull/10835)) +* idiomatic changes to PodState ([#10834](https://github.com/paritytech/parity-ethereum/pull/10834)) +* Break circular dependency between Client and Engine (part 1) ([#10833](https://github.com/paritytech/parity-ethereum/pull/10833)) +* Remove excessive warning ([#10831](https://github.com/paritytech/parity-ethereum/pull/10831)) +* Allow --nat extip:your.host.here.org ([#10830](https://github.com/paritytech/parity-ethereum/pull/10830)) +* ethcore does not use byteorder ([#10829](https://github.com/paritytech/parity-ethereum/pull/10829)) +* Fix typo in README.md ([#10828](https://github.com/paritytech/parity-ethereum/pull/10828)) +* Update wordlist to v1.3 ([#10823](https://github.com/paritytech/parity-ethereum/pull/10823)) +* bump `smallvec 0.6.10` to fix vulnerability ([#10822](https://github.com/paritytech/parity-ethereum/pull/10822)) +* removed additional_params method ([#10818](https://github.com/paritytech/parity-ethereum/pull/10818)) +* Improve logging when remote peer is unknown ([#10817](https://github.com/paritytech/parity-ethereum/pull/10817)) +* replace memzero with zeroize crate ([#10816](https://github.com/paritytech/parity-ethereum/pull/10816)) +* When updating the client or when called from RPC, sleep should mean sleep ([#10814](https://github.com/paritytech/parity-ethereum/pull/10814)) +* Don't reimplement the logic from the Default impl ([#10813](https://github.com/paritytech/parity-ethereum/pull/10813)) +* refactor: whisper: Add type aliases and update rustdocs in message.rs ([#10812](https://github.com/paritytech/parity-ethereum/pull/10812)) +* test: whisper/cli `add invalid pool size test depending on processor` ([#10811](https://github.com/paritytech/parity-ethereum/pull/10811)) +* Add Constantinople EIPs to the dev (instant_seal) config ([#10809](https://github.com/paritytech/parity-ethereum/pull/10809)) +* fix spurious test failure ([#10808](https://github.com/paritytech/parity-ethereum/pull/10808)) +* revert temp changes to .gitlab-ci.yml ([#10807](https://github.com/paritytech/parity-ethereum/pull/10807)) +* removed redundant fmt::Display implementations ([#10806](https://github.com/paritytech/parity-ethereum/pull/10806)) +* removed EthEngine alias ([#10805](https://github.com/paritytech/parity-ethereum/pull/10805)) +* ethcore-bloom-journal updated to 2018 ([#10804](https://github.com/paritytech/parity-ethereum/pull/10804)) +* Fix a few typos and unused warnings. ([#10803](https://github.com/paritytech/parity-ethereum/pull/10803)) +* updated price-info to edition 2018 ([#10801](https://github.com/paritytech/parity-ethereum/pull/10801)) +* updated parity-local-store to edition 2018 ([#10800](https://github.com/paritytech/parity-ethereum/pull/10800)) +* updated project to ansi_term 0.11 ([#10799](https://github.com/paritytech/parity-ethereum/pull/10799)) +* ethcore-light uses bincode 1.1 ([#10798](https://github.com/paritytech/parity-ethereum/pull/10798)) +* ethcore-network-devp2p uses igd 0.9 ([#10797](https://github.com/paritytech/parity-ethereum/pull/10797)) +* Better logging when backfilling ancient blocks fail ([#10796](https://github.com/paritytech/parity-ethereum/pull/10796)) +* added new ropsten-bootnode and removed old one ([#10794](https://github.com/paritytech/parity-ethereum/pull/10794)) +* Removed machine abstraction from ethcore ([#10791](https://github.com/paritytech/parity-ethereum/pull/10791)) +* Removed redundant ethcore-service error type ([#10788](https://github.com/paritytech/parity-ethereum/pull/10788)) +* Cleanup unused vm dependencies ([#10787](https://github.com/paritytech/parity-ethereum/pull/10787)) +* ethkey no longer uses byteorder ([#10786](https://github.com/paritytech/parity-ethereum/pull/10786)) +* Updated blooms-db to rust 2018 and removed redundant deps ([#10785](https://github.com/paritytech/parity-ethereum/pull/10785)) +* Treat empty account the same as non-exist accounts in EIP-1052 ([#10775](https://github.com/paritytech/parity-ethereum/pull/10775)) +* Do not drop the peer with None difficulty ([#10772](https://github.com/paritytech/parity-ethereum/pull/10772)) +* EIP-1702: Generalized Account Versioning Scheme ([#10771](https://github.com/paritytech/parity-ethereum/pull/10771)) +* Move Engine::register_client to be before other I/O handler registration ([#10767](https://github.com/paritytech/parity-ethereum/pull/10767)) +* ethcore/res: activate atlantis classic hf on block 8772000 ([#10766](https://github.com/paritytech/parity-ethereum/pull/10766)) +* Updated Bn128PairingImpl to use optimized batch pairing ([#10765](https://github.com/paritytech/parity-ethereum/pull/10765)) +* Remove unused code ([#10762](https://github.com/paritytech/parity-ethereum/pull/10762)) +* Initialize private tx logger only if private tx functionality is enabled ([#10758](https://github.com/paritytech/parity-ethereum/pull/10758)) +* SecretStore: remove support of old database formats ([#10757](https://github.com/paritytech/parity-ethereum/pull/10757)) +* Enable aesni ([#10756](https://github.com/paritytech/parity-ethereum/pull/10756)) +* updater: fix static id hashes initialization ([#10755](https://github.com/paritytech/parity-ethereum/pull/10755)) +* Use fewer threads for snapshotting ([#10752](https://github.com/paritytech/parity-ethereum/pull/10752)) +* Die error_chain, die ([#10747](https://github.com/paritytech/parity-ethereum/pull/10747)) +* Fix deprectation warnings on nightly ([#10746](https://github.com/paritytech/parity-ethereum/pull/10746)) +* Improve logging and cleanup in miner around block sealing ([#10745](https://github.com/paritytech/parity-ethereum/pull/10745)) +* Add a way to signal shutdown to snapshotting threads ([#10744](https://github.com/paritytech/parity-ethereum/pull/10744)) +* fix docker tags for publishing ([#10741](https://github.com/paritytech/parity-ethereum/pull/10741)) +* refactor: Fix indentation in ethjson ([#10740](https://github.com/paritytech/parity-ethereum/pull/10740)) +* Log validator set changes in EpochManager ([#10734](https://github.com/paritytech/parity-ethereum/pull/10734)) +* Print warnings when using dangerous settings for ValidatorSet ([#10733](https://github.com/paritytech/parity-ethereum/pull/10733)) +* ethcore: enable ECIP-1054 for classic ([#10731](https://github.com/paritytech/parity-ethereum/pull/10731)) +* Stop breaking out of loop if a non-canonical hash is found ([#10729](https://github.com/paritytech/parity-ethereum/pull/10729)) +* Removed secret_store folder ([#10722](https://github.com/paritytech/parity-ethereum/pull/10722)) +* Revert "enable lto for release builds (#10717)" ([#10721](https://github.com/paritytech/parity-ethereum/pull/10721)) +* fix: aura don't add `SystemTime::now()` ([#10720](https://github.com/paritytech/parity-ethereum/pull/10720)) +* Use RUSTFLAGS to set the optimization level ([#10719](https://github.com/paritytech/parity-ethereum/pull/10719)) +* enable lto for release builds ([#10717](https://github.com/paritytech/parity-ethereum/pull/10717)) +* [devp2p] Update to 2018 edition ([#10716](https://github.com/paritytech/parity-ethereum/pull/10716)) +* [devp2p] Don't use `rust-crypto` ([#10714](https://github.com/paritytech/parity-ethereum/pull/10714)) +* [devp2p] Fix warnings and re-org imports ([#10710](https://github.com/paritytech/parity-ethereum/pull/10710)) +* DevP2p: Get node IP address and udp port from Socket, if not included in PING packet ([#10705](https://github.com/paritytech/parity-ethereum/pull/10705)) +* introduce MissingParent Error, fixes #10699 ([#10700](https://github.com/paritytech/parity-ethereum/pull/10700)) +* Refactor Clique stepping ([#10691](https://github.com/paritytech/parity-ethereum/pull/10691)) +* add_sync_notifier in EthPubSubClient holds on to a Client for too long ([#10689](https://github.com/paritytech/parity-ethereum/pull/10689)) +* Fix compiler warning (that will become an error) ([#10683](https://github.com/paritytech/parity-ethereum/pull/10683)) +* Don't panic if extra_data is longer than VANITY_LENGTH ([#10682](https://github.com/paritytech/parity-ethereum/pull/10682)) +* Remove annoying compiler warnings ([#10679](https://github.com/paritytech/parity-ethereum/pull/10679)) +* Remove support for hardware wallets ([#10678](https://github.com/paritytech/parity-ethereum/pull/10678)) +* [CI] allow cargo audit to fail ([#10676](https://github.com/paritytech/parity-ethereum/pull/10676)) +* new image ([#10673](https://github.com/paritytech/parity-ethereum/pull/10673)) +* Upgrade ethereum types ([#10670](https://github.com/paritytech/parity-ethereum/pull/10670)) +* Reset blockchain properly ([#10669](https://github.com/paritytech/parity-ethereum/pull/10669)) +* fix: Move PR template into .github/ folder ([#10663](https://github.com/paritytech/parity-ethereum/pull/10663)) +* docs: evmbin - Update Rust docs ([#10658](https://github.com/paritytech/parity-ethereum/pull/10658)) +* refactor: Related #9459 - evmbin: replace untyped json! macro with fully typed serde serialization using Rust structs ([#10657](https://github.com/paritytech/parity-ethereum/pull/10657)) +* docs: Add PR template ([#10654](https://github.com/paritytech/parity-ethereum/pull/10654)) +* docs: Add ProgPoW Rust docs to ethash module ([#10653](https://github.com/paritytech/parity-ethereum/pull/10653)) +* docs: Update Readme with TOC, Contributor Guideline. Update Cargo package descriptions ([#10652](https://github.com/paritytech/parity-ethereum/pull/10652)) +* Upgrade to parity-crypto 0.4 ([#10650](https://github.com/paritytech/parity-ethereum/pull/10650)) +* fix(compilation warnings) ([#10649](https://github.com/paritytech/parity-ethereum/pull/10649)) +* [whisper] Move needed aes_gcm crypto in-crate ([#10647](https://github.com/paritytech/parity-ethereum/pull/10647)) +* Update publishing ([#10644](https://github.com/paritytech/parity-ethereum/pull/10644)) +* ci: publish docs debug ([#10638](https://github.com/paritytech/parity-ethereum/pull/10638)) +* Fix publish docs ([#10635](https://github.com/paritytech/parity-ethereum/pull/10635)) +* Fix rinkeby petersburg fork ([#10632](https://github.com/paritytech/parity-ethereum/pull/10632)) +* Update kovan.json to switch Kovan validator set to POA Consensus Contracts ([#10628](https://github.com/paritytech/parity-ethereum/pull/10628)) +* [ethcore] remove error_chain ([#10616](https://github.com/paritytech/parity-ethereum/pull/10616)) +* Remove unused import ([#10615](https://github.com/paritytech/parity-ethereum/pull/10615)) +* Adds parity_getRawBlockByNumber, parity_submitRawBlock ([#10609](https://github.com/paritytech/parity-ethereum/pull/10609)) +* adds rpc error message for --no-ancient-blocks ([#10608](https://github.com/paritytech/parity-ethereum/pull/10608)) +* Constantinople HF on POA Core ([#10606](https://github.com/paritytech/parity-ethereum/pull/10606)) +* Clique: zero-fill extradata when the supplied value is less than 32 bytes in length ([#10605](https://github.com/paritytech/parity-ethereum/pull/10605)) +* evm: add some mulmod benches ([#10600](https://github.com/paritytech/parity-ethereum/pull/10600)) +* sccache logs to stdout ([#10596](https://github.com/paritytech/parity-ethereum/pull/10596)) +* update bootnodes ([#10595](https://github.com/paritytech/parity-ethereum/pull/10595)) +* Merge `Notifier` and `TransactionsPoolNotifier` ([#10591](https://github.com/paritytech/parity-ethereum/pull/10591)) +* fix(whisper): change expiry `unix_time + ttl + work` ([#10587](https://github.com/paritytech/parity-ethereum/pull/10587)) +* fix(evmbin): make benches compile again ([#10586](https://github.com/paritytech/parity-ethereum/pull/10586)) +* fix issue with compilation when 'slow-blocks' feature enabled ([#10585](https://github.com/paritytech/parity-ethereum/pull/10585)) +* Allow CORS requests in Secret Store API ([#10584](https://github.com/paritytech/parity-ethereum/pull/10584)) +* CI improvements ([#10579](https://github.com/paritytech/parity-ethereum/pull/10579)) +* ethcore: improve timestamp handling ([#10574](https://github.com/paritytech/parity-ethereum/pull/10574)) +* Update Issue Template to direct security issue to email ([#10562](https://github.com/paritytech/parity-ethereum/pull/10562)) +* version: bump master to 2.6 ([#10560](https://github.com/paritytech/parity-ethereum/pull/10560)) +* fix(light cull): poll light cull instead of timer ([#10559](https://github.com/paritytech/parity-ethereum/pull/10559)) +* Watch transactions pool ([#10558](https://github.com/paritytech/parity-ethereum/pull/10558)) +* Add SealingState; don't prepare block when not ready. ([#10529](https://github.com/paritytech/parity-ethereum/pull/10529)) +* Explicitly enable or disable Stratum in config file (Issue 9785) ([#10521](https://github.com/paritytech/parity-ethereum/pull/10521)) +* Add filtering capability to `parity_pendingTransactions` (issue 8269) ([#10506](https://github.com/paritytech/parity-ethereum/pull/10506)) +* Remove calls to heapsize ([#10432](https://github.com/paritytech/parity-ethereum/pull/10432)) +* RPC: Implements eth_subscribe("syncing") ([#10311](https://github.com/paritytech/parity-ethereum/pull/10311)) +* SecretStore: non-blocking wait of session completion ([#10303](https://github.com/paritytech/parity-ethereum/pull/10303)) +* Node table limiting and cache for node filter ([#10288](https://github.com/paritytech/parity-ethereum/pull/10288)) +* SecretStore: expose restore_key_public in HTTP API ([#10241](https://github.com/paritytech/parity-ethereum/pull/10241)) +* Trivial journal for private transactions ([#10056](https://github.com/paritytech/parity-ethereum/pull/10056)) + +## Previous releases + +- [CHANGELOG-2.5](docs/CHANGELOG-2.5.md) (_stable_) +- [CHANGELOG-2.4](docs/CHANGELOG-2.4.md) (EOL: 2019-07-08) +- [CHANGELOG-2.3](docs/CHANGELOG-2.3.md) (EOL: 2019-04-09) +- [CHANGELOG-2.2](docs/CHANGELOG-2.2.md) (EOL: 2019-02-25) +- [CHANGELOG-2.1](docs/CHANGELOG-2.1.md) (EOL: 2019-01-16) +- [CHANGELOG-2.0](docs/CHANGELOG-2.0.md) (EOL: 2018-11-15) +- [CHANGELOG-1.11](docs/CHANGELOG-1.11.md) (EOL: 2018-09-19) +- [CHANGELOG-1.10](docs/CHANGELOG-1.10.md) (EOL: 2018-07-18) +- [CHANGELOG-1.9](docs/CHANGELOG-1.9.md) (EOL: 2018-05-09) +- [CHANGELOG-1.8](docs/CHANGELOG-1.8.md) (EOL: 2018-03-22) +- [CHANGELOG-1.7](docs/CHANGELOG-1.7.md) (EOL: 2018-01-25) +- [CHANGELOG-1.6](docs/CHANGELOG-1.6.md) (EOL: 2017-10-15) +- [CHANGELOG-1.5](docs/CHANGELOG-1.5.md) (EOL: 2017-07-28) +- [CHANGELOG-1.4](docs/CHANGELOG-1.4.md) (EOL: 2017-03-13) +- [CHANGELOG-1.3](docs/CHANGELOG-1.3.md) (EOL: 2017-01-19) +- [CHANGELOG-1.2](docs/CHANGELOG-1.2.md) (EOL: 2016-11-07) +- [CHANGELOG-1.1](docs/CHANGELOG-1.1.md) (EOL: 2016-08-12) +- [CHANGELOG-1.0](docs/CHANGELOG-1.0.md) (EOL: 2016-06-24) +- [CHANGELOG-0.9](docs/CHANGELOG-0.9.md) (EOL: 2016-05-02) diff --git a/ethash/Cargo.toml b/ethash/Cargo.toml index 59d26e128e..71284c5bb5 100644 --- a/ethash/Cargo.toml +++ b/ethash/Cargo.toml @@ -5,17 +5,18 @@ version = "1.12.0" authors = ["Parity Technologies "] [dependencies] -crunchy = "0.1.0" +common-types = { path = "../ethcore/types" } either = "1.0.0" -ethereum-types = "0.4" -keccak-hash = "0.1" +ethereum-types = "0.8.0" +keccak-hash = "0.4.0" log = "0.4" memmap = "0.6" -parking_lot = "0.7" +parking_lot = "0.9" primal = "0.2.3" +static_assertions = "0.3.3" [dev-dependencies] -criterion = "0.2" +criterion = "0.3" rustc-hex = "1.0" serde_json = "1.0" tempdir = "0.3" @@ -27,7 +28,9 @@ bench = [] [[bench]] name = "basic" harness = false +required-features = ['bench'] [[bench]] name = "progpow" harness = false +required-features = ['bench'] diff --git a/ethash/benches/basic.rs b/ethash/benches/basic.rs index 5bc10e948e..0337ecee9c 100644 --- a/ethash/benches/basic.rs +++ b/ethash/benches/basic.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,26 +17,53 @@ #[macro_use] extern crate criterion; extern crate ethash; +extern crate common_types; use criterion::Criterion; -use ethash::{NodeCacheBuilder, OptimizeFor}; - -const HASH: [u8; 32] = [0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, 0x5b, 0xf2, 0xbe, - 0xe4, 0x0a, 0xb3, 0x35, 0x8a, 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, - 0x5e, 0x59, 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72]; +use ethash::{NodeCacheBuilder, keccak}; +use common_types::engines::OptimizeFor; + +const HASH: [u8; 32] = [ + 0xf5, 0x7e, 0x6f, 0x3a, 0xcf, 0xc0, 0xdd, 0x4b, + 0x5b, 0xf2, 0xbe, 0xe4, 0x0a, 0xb3, 0x35, 0x8a, + 0xa6, 0x87, 0x73, 0xa8, 0xd0, 0x9f, 0x5e, 0x59, + 0x5e, 0xab, 0x55, 0x94, 0x05, 0x52, 0x7d, 0x72, +]; +const MIX_HASH: [u8; 32] = [ + 0x1f, 0xff, 0x04, 0xce, 0xc9, 0x41, 0x73, 0xfd, + 0x59, 0x1e, 0x3d, 0x89, 0x60, 0xce, 0x6b, 0xdf, + 0x8b, 0x19, 0x71, 0x04, 0x8c, 0x71, 0xff, 0x93, + 0x7b, 0xb2, 0xd3, 0x2a, 0x64, 0x31, 0xab, 0x6d, +]; const NONCE: u64 = 0xd7b3ac70a301a249; -criterion_group!( - basic, - bench_light_compute_memmap, - bench_light_compute_memory, - bench_light_new_round_trip_memmap, - bench_light_new_round_trip_memory, - bench_light_from_file_round_trip_memory, - bench_light_from_file_round_trip_memmap -); +criterion_group! { + name = basic; + config = dont_take_an_eternity_to_run(); + targets = bench_keccak_512_inplace, + bench_light_compute_memmap, + bench_light_compute_memory, + bench_light_new_round_trip_memmap, + bench_light_new_round_trip_memory, + bench_light_from_file_round_trip_memory, + bench_light_from_file_round_trip_memmap, + bench_quick_get_difficulty, +} criterion_main!(basic); +fn dont_take_an_eternity_to_run() -> Criterion { + Criterion::default().nresamples(1_000) + .without_plots() + .sample_size(10) +} + +fn bench_keccak_512_inplace(b: &mut Criterion) { + b.bench_function("bench_keccak_512_inplace", move |b| b.iter(|| { + let mut data = [4u8; 64]; + keccak::keccak_512::inplace(&mut data); + })); +} + fn bench_light_compute_memmap(b: &mut Criterion) { use std::env; @@ -52,13 +79,13 @@ fn bench_light_compute_memory(b: &mut Criterion) { let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); let light = builder.light(&env::temp_dir(), 486382); - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))); + b.bench_function("bench_light_compute_memory", move |b| b.iter(|| light.compute(&HASH, NONCE, u64::max_value()))); } fn bench_light_new_round_trip_memmap(b: &mut Criterion) { use std::env; - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { + b.bench_function("bench_light_new_round_trip_memmap", move |b| b.iter(|| { let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); let light = builder.light(&env::temp_dir(), 486382); light.compute(&HASH, NONCE, u64::max_value()); @@ -68,7 +95,7 @@ fn bench_light_new_round_trip_memmap(b: &mut Criterion) { fn bench_light_new_round_trip_memory(b: &mut Criterion) { use std::env; - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { + b.bench_function("bench_light_new_round_trip_memory", move |b| b.iter(|| { let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); let light = builder.light(&env::temp_dir(), 486382); light.compute(&HASH, NONCE, u64::max_value()); @@ -86,7 +113,7 @@ fn bench_light_from_file_round_trip_memory(b: &mut Criterion) { dummy.to_file().unwrap(); } - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { + b.bench_function("bench_light_from_file_round_trip_memory", move |b| b.iter(|| { let builder = NodeCacheBuilder::new(OptimizeFor::Cpu, u64::max_value()); let light = builder.light_from_file(&dir, 486382).unwrap(); light.compute(&HASH, NONCE, u64::max_value()); @@ -105,9 +132,21 @@ fn bench_light_from_file_round_trip_memmap(b: &mut Criterion) { dummy.to_file().unwrap(); } - b.bench_function("bench_light_compute_memmap", move |b| b.iter(|| { + b.bench_function("bench_light_from_file_round_trip_memmap", move |b| b.iter(|| { let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); let light = builder.light_from_file(&dir, 486382).unwrap(); light.compute(&HASH, NONCE, u64::max_value()); })); } + +fn bench_quick_get_difficulty(b: &mut Criterion) { + b.bench_function("bench_quick_get_difficulty", move |b| b.iter(|| { + let d = ethash::quick_get_difficulty(&HASH, NONCE, &MIX_HASH, false); + let boundary_good = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3e, 0x9b, 0x6c, 0x69, 0xbc, 0x2c, 0xe2, 0xa2, + 0x4a, 0x8e, 0x95, 0x69, 0xef, 0xc7, 0xd7, 0x1b, 0x33, 0x35, 0xdf, 0x36, 0x8c, 0x9a, + 0xe9, 0x7e, 0x53, 0x84, + ]; + assert_eq!(d[..], boundary_good[..]); + })); +} diff --git a/ethash/benches/progpow.rs b/ethash/benches/progpow.rs index e086a14b42..fdf8415f1e 100644 --- a/ethash/benches/progpow.rs +++ b/ethash/benches/progpow.rs @@ -3,14 +3,16 @@ extern crate criterion; extern crate ethash; extern crate rustc_hex; extern crate tempdir; +extern crate common_types; use criterion::Criterion; use ethash::progpow; use tempdir::TempDir; use rustc_hex::FromHex; -use ethash::{NodeCacheBuilder, OptimizeFor}; +use ethash::NodeCacheBuilder; use ethash::compute::light_compute; +use common_types::engines::OptimizeFor; fn bench_hashimoto_light(c: &mut Criterion) { let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); diff --git a/ethash/res/progpow_testvectors.json b/ethash/res/progpow_testvectors.json index 2939f7106c..6fe7a6f8b9 100644 --- a/ethash/res/progpow_testvectors.json +++ b/ethash/res/progpow_testvectors.json @@ -3,84 +3,84 @@ 0, "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000", - "faeb1be51075b03a4ff44b335067951ead07a3b078539ace76fd56fc410557a3", - "63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b" + "f4ac202715ded4136e72887c39e63a4738331c57fd9eb79f6ec421c281aa8743", + "b3bad9ca6f7c566cf0377d1f8cce29d6516a96562c122d924626281ec948ef02" ], [ 49, - "63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b", + "b3bad9ca6f7c566cf0377d1f8cce29d6516a96562c122d924626281ec948ef02", "0000000006ff2c47", - "c789c1180f890ec555ff42042913465481e8e6bc512cb981e1c1108dc3f2227d", - "9e7248f20914913a73d80a70174c331b1d34f260535ac3631d770e656b5dd922" + "7730596f128f675ef9a6bb7281f268e4077d302f2b9078da1ece4349248561dd", + "0b9ed0c11157f1365143e329a6e1cea4248d9d6cb44b9c6daf492c7a076654a4" ], [ 50, - "9e7248f20914913a73d80a70174c331b1d34f260535ac3631d770e656b5dd922", + "0b9ed0c11157f1365143e329a6e1cea4248d9d6cb44b9c6daf492c7a076654a4", "00000000076e482e", - "c7340542c2a06b3a7dc7222635f7cd402abf8b528ae971ddac6bbe2b0c7cb518", - "de37e1824c86d35d154cf65a88de6d9286aec4f7f10c3fc9f0fa1bcc2687188d" + "829136d4a704eb8d06da773f1a90466e7b5ed12119c44526f045bbff4475d891", + "e2e881c5b893c2f1ef06b96a10cfcbcf7255b307f0818e7d30eb12b2edfc237b" ], [ 99, - "de37e1824c86d35d154cf65a88de6d9286aec4f7f10c3fc9f0fa1bcc2687188d", + "e2e881c5b893c2f1ef06b96a10cfcbcf7255b307f0818e7d30eb12b2edfc237b", "000000003917afab", - "f5e60b2c5bfddd136167a30cbc3c8dbdbd15a512257dee7964e0bc6daa9f8ba7", - "ac7b55e801511b77e11d52e9599206101550144525b5679f2dab19386f23dcce" + "deb3d8b45bdc596c56aa37a5eba456f478c82e60e5c028ce95f2e654e4bb7b57", + "9bdc2ad2286eaa051d6ca1f5196d2dd1c9a039f1d7ce3e1c856b793deed01778" ], [ 29950, - "ac7b55e801511b77e11d52e9599206101550144525b5679f2dab19386f23dcce", + "9bdc2ad2286eaa051d6ca1f5196d2dd1c9a039f1d7ce3e1c856b793deed01778", "005d409dbc23a62a", - "07393d15805eb08ee6fc6cb3ad4ad1010533bd0ff92d6006850246829f18fd6e", - "e43d7e0bdc8a4a3f6e291a5ed790b9fa1a0948a2b9e33c844888690847de19f5" + "c01e6d339cc687c77f653b81c74cb9de8b595554f2c5db671a7dde3846d2fa01", + "de0d693e597cf2fd70a4cfaa73f6baafc29e1eee695a81295b278c1116580b72" ], [ 29999, - "e43d7e0bdc8a4a3f6e291a5ed790b9fa1a0948a2b9e33c844888690847de19f5", + "de0d693e597cf2fd70a4cfaa73f6baafc29e1eee695a81295b278c1116580b72", "005db5fa4c2a3d03", - "7551bddf977491da2f6cfc1679299544b23483e8f8ee0931c4c16a796558a0b8", - "d34519f72c97cae8892c277776259db3320820cb5279a299d0ef1e155e5c6454" + "8b664cdbf396a7a185446c93dddd6611f5a736b11097381ae6bea45e802cec16", + "21ec5d1984a4fd4394b042aa96365085225d964727a45def245ceab326e28128" ], [ 30000, - "d34519f72c97cae8892c277776259db3320820cb5279a299d0ef1e155e5c6454", + "21ec5d1984a4fd4394b042aa96365085225d964727a45def245ceab326e28128", "005db8607994ff30", - "f1c2c7c32266af9635462e6ce1c98ebe4e7e3ecab7a38aaabfbf2e731e0fbff4", - "8b6ce5da0b06d18db7bd8492d9e5717f8b53e7e098d9fef7886d58a6e913ef64" + "276951d89c1ed262bcac00df4fb9bf7af36991532744a2e287b0b758a56e15aa", + "dc070b76cc311cd82267f98936acbbbd3ec1c1ab25b55e2c885af6474e1e6841" ], [ 30049, - "8b6ce5da0b06d18db7bd8492d9e5717f8b53e7e098d9fef7886d58a6e913ef64", + "dc070b76cc311cd82267f98936acbbbd3ec1c1ab25b55e2c885af6474e1e6841", "005e2e215a8ca2e7", - "57fe6a9fbf920b4e91deeb66cb0efa971e08229d1a160330e08da54af0689add", - "c2c46173481b9ced61123d2e293b42ede5a1b323210eb2a684df0874ffe09047" + "6248ba0157d0f0592dacfe2963337948fffb37f67e7451a6862c1321d894cebe", + "6fdecf719e2547f585a6ee807d8237db8e9489f63d3f259ab5236451eaded433" ], [ 30050, - "c2c46173481b9ced61123d2e293b42ede5a1b323210eb2a684df0874ffe09047", + "6fdecf719e2547f585a6ee807d8237db8e9489f63d3f259ab5236451eaded433", "005e30899481055e", - "ba30c61cc5a2c74a5ecaf505965140a08f24a296d687e78720f0b48baf712f2d", - "ea42197eb2ba79c63cb5e655b8b1f612c5f08aae1a49ff236795a3516d87bc71" + "512d8f2bb0441fcfa1764c67e8dbed2afcbe9141de4bbebc5b51e0661dede550", + "cb1587a1c372642cbd9ce4c1ba2f433985d44c571a676a032bc1e8c1ad066e24" ], [ 30099, - "ea42197eb2ba79c63cb5e655b8b1f612c5f08aae1a49ff236795a3516d87bc71", + "cb1587a1c372642cbd9ce4c1ba2f433985d44c571a676a032bc1e8c1ad066e24", "005ea6aef136f88b", - "cfd5e46048cd133d40f261fe8704e51d3f497fc14203ac6a9ef6a0841780b1cd", - "49e15ba4bf501ce8fe8876101c808e24c69a859be15de554bf85dbc095491bd6" + "be0e7d6afa6edd483ccc304afa9bf0abaca5e0f037a4f05bf5550b9309d1d12c", + "78be18f20569a834d839dad48e0e51d6df6b6537575f0ad29898c7cf357f12cb" ], [ 59950, - "49e15ba4bf501ce8fe8876101c808e24c69a859be15de554bf85dbc095491bd6", + "78be18f20569a834d839dad48e0e51d6df6b6537575f0ad29898c7cf357f12cb", "02ebe0503bd7b1da", - "21511fbaa31fb9f5fc4998a754e97b3083a866f4de86fa7500a633346f56d773", - "f5c50ba5c0d6210ddb16250ec3efda178de857b2b1703d8d5403bd0f848e19cf" + "b85be51fce670aa437f28c02ea4fd7995fa8b6ac224e959b8dbfb5bdbc6f77ce", + "a68a620ba17e0cf2817bc4397cf4b85f5770983aa7b7931319a7f61bd6f905b1" ], [ 59999, - "f5c50ba5c0d6210ddb16250ec3efda178de857b2b1703d8d5403bd0f848e19cf", + "a68a620ba17e0cf2817bc4397cf4b85f5770983aa7b7931319a7f61bd6f905b1", "02edb6275bd221e3", - "653eda37d337e39d311d22be9bbd3458d3abee4e643bee4a7280a6d08106ef98", - "341562d10d4afb706ec2c8d5537cb0c810de02b4ebb0a0eea5ae335af6fb2e88" + "ffe745a932c21c0704291bb416fe8bffec76621cd3434861885beab42cec1734", + "9e6667a151ac6f5186a05cb20877a2b3df02317046256a762cb8ec2d96aa34f0" ] ] diff --git a/ethash/src/cache.rs b/ethash/src/cache.rs index b16d273145..594496a1e5 100644 --- a/ethash/src/cache.rs +++ b/ethash/src/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use memmap::MmapMut; use parking_lot::Mutex; use seed_compute::SeedHashCompute; -use shared::{ETHASH_CACHE_ROUNDS, NODE_BYTES, NODE_DWORDS, Node, epoch, get_cache_size, to_hex}; +use shared::{ETHASH_CACHE_ROUNDS, NODE_BYTES, Node, epoch, get_cache_size, to_hex}; use std::borrow::Cow; use std::fs; @@ -30,19 +30,9 @@ use std::path::{Path, PathBuf}; use std::slice; use std::sync::Arc; -type Cache = Either, MmapMut>; - -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -pub enum OptimizeFor { - Cpu, - Memory, -} +use common_types::engines::OptimizeFor; -impl Default for OptimizeFor { - fn default() -> Self { - OptimizeFor::Cpu - } -} +type Cache = Either, MmapMut>; fn byte_size(cache: &Cache) -> usize { use self::Either::{Left, Right}; @@ -327,11 +317,6 @@ unsafe fn initialize_memory(memory: *mut Node, num_nodes: usize, ident: &H256) { // Now this is initialized, we can treat it as a slice. let nodes: &mut [Node] = slice::from_raw_parts_mut(memory, num_nodes); - // For `unroll!`, see below. If the literal in `unroll!` is not the same as the RHS here then - // these have got out of sync! Don't let this happen! - debug_assert_eq!(NODE_DWORDS, 8); - - // This _should_ get unrolled by the compiler, since it's not using the loop variable. for _ in 0..ETHASH_CACHE_ROUNDS { for i in 0..num_nodes { let data_idx = (num_nodes - 1 + i) % num_nodes; @@ -341,11 +326,8 @@ unsafe fn initialize_memory(memory: *mut Node, num_nodes: usize, ident: &H256) { let mut data: Node = nodes.get_unchecked(data_idx).clone(); let rhs: &Node = nodes.get_unchecked(idx); - unroll! { - for w in 0..8 { - *data.as_dwords_mut().get_unchecked_mut(w) ^= - *rhs.as_dwords().get_unchecked(w); - } + for (a, b) in data.as_dwords_mut().iter_mut().zip(rhs.as_dwords()) { + *a ^= *b; } data diff --git a/ethash/src/compute.rs b/ethash/src/compute.rs index 36826121db..f516d7e737 100644 --- a/ethash/src/compute.rs +++ b/ethash/src/compute.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -26,7 +26,7 @@ use seed_compute::SeedHashCompute; use shared::*; use std::io; -use std::{mem, ptr}; +use std::mem; use std::path::Path; const MIX_WORDS: usize = ETHASH_MIX_BYTES / 4; @@ -135,22 +135,16 @@ pub fn quick_get_difficulty(header_hash: &H256, nonce: u64, mix_hash: &H256, pro let seed = keccak_f800_short(*header_hash, nonce, [0u32; 8]); keccak_f800_long(*header_hash, seed, mem::transmute(*mix_hash)) } else { - // This is safe - the `keccak_512` call below reads the first 40 bytes (which we explicitly set - // with two `copy_nonoverlapping` calls) but writes the first 64, and then we explicitly write - // the next 32 bytes before we read the whole thing with `keccak_256`. - // - // This cannot be elided by the compiler as it doesn't know the implementation of - // `keccak_512`. - let mut buf: [u8; 64 + 32] = mem::uninitialized(); + let mut buf = [0u8; 64 + 32]; - ptr::copy_nonoverlapping(header_hash.as_ptr(), buf.as_mut_ptr(), 32); - ptr::copy_nonoverlapping(&nonce as *const u64 as *const u8, buf[32..].as_mut_ptr(), 8); + let hash_len = header_hash.len(); + buf[..hash_len].copy_from_slice(header_hash); + buf[hash_len..hash_len + mem::size_of::()].copy_from_slice(&nonce.to_ne_bytes()); 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); + buf[64..].copy_from_slice(mix_hash); - // This is initialized in `keccak_256` - let mut hash: [u8; 32] = mem::uninitialized(); + let mut hash = [0u8; 32]; keccak_256::unchecked(hash.as_mut_ptr(), hash.len(), buf.as_ptr(), buf.len()); hash @@ -205,17 +199,11 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) let mut buf: MixBuf = MixBuf { half_mix: unsafe { // Pack `header_hash` and `nonce` together - // We explicitly write the first 40 bytes, leaving the last 24 as uninitialized. Then - // `keccak_512` reads the first 40 bytes (4th parameter) and overwrites the entire array, - // leaving it fully initialized. - let mut out: [u8; NODE_BYTES] = mem::uninitialized(); - - ptr::copy_nonoverlapping(header_hash.as_ptr(), out.as_mut_ptr(), header_hash.len()); - ptr::copy_nonoverlapping( - &nonce as *const u64 as *const u8, - out[header_hash.len()..].as_mut_ptr(), - mem::size_of::(), - ); + let mut out = [0u8; NODE_BYTES]; + + let hash_len = header_hash.len(); + out[..hash_len].copy_from_slice(header_hash); + out[hash_len..hash_len + mem::size_of::()].copy_from_slice(&nonce.to_ne_bytes()); // compute keccak-512 hash and replicate across mix keccak_512::unchecked( @@ -227,8 +215,7 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) Node { bytes: out } }, - // This is fully initialized before being read, see `let mut compress = ...` below - compress_bytes: unsafe { mem::uninitialized() }, + compress_bytes: [0u8; MIX_WORDS], }; let mut mix: [_; MIX_NODES] = [buf.half_mix.clone(), buf.half_mix.clone()]; @@ -252,24 +239,16 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) fnv_hash(first_val ^ i, mix_words[i as usize % MIX_WORDS]) % num_full_pages }; - unroll! { - // MIX_NODES - for n in 0..2 { - let tmp_node = calculate_dag_item( - index * MIX_NODES as u32 + n as u32, - cache, - ); + // MIX_NODES + for n in 0..2 { + let tmp_node = calculate_dag_item( + index * MIX_NODES as u32 + n as u32, + cache, + ); - unroll! { - // NODE_WORDS - for w in 0..16 { - mix[n].as_words_mut()[w] = - fnv_hash( - mix[n].as_words()[w], - tmp_node.as_words()[w], - ); - } - } + // NODE_WORDS + for (a, b) in mix[n].as_words_mut().iter_mut().zip(tmp_node.as_words()) { + *a = fnv_hash(*a, *b); } } } @@ -277,25 +256,27 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) let mix_words: [u32; MIX_WORDS] = unsafe { mem::transmute(mix) }; { - // This is an uninitialized buffer to begin with, but we iterate precisely `compress.len()` - // times and set each index, leaving the array fully initialized. THIS ONLY WORKS ON LITTLE- - // ENDIAN MACHINES. See a future PR to make this and the rest of the code work correctly on + // We iterate precisely `compress.len()` times and set each index, + // leaving the array fully initialized. THIS ONLY WORKS ON LITTLE-ENDIAN MACHINES. + // See a future PR to make this and the rest of the code work correctly on // big-endian arches like mips. let compress: &mut [u32; MIX_WORDS / 4] = unsafe { make_const_array!(MIX_WORDS / 4, &mut buf.compress_bytes) }; + #[cfg(target_endian = "big")] + { + compile_error!("parity-ethereum currently only supports little-endian targets"); + } // Compress mix debug_assert_eq!(MIX_WORDS / 4, 8); - unroll! { - for i in 0..8 { - let w = i * 4; - - let mut reduction = mix_words[w + 0]; - reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 1]; - reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 2]; - reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 3]; - compress[i] = reduction; - } + for i in 0..8 { + let w = i * 4; + + let mut reduction = mix_words[w + 0]; + reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 1]; + reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 2]; + reduction = reduction.wrapping_mul(FNV_PRIME) ^ mix_words[w + 3]; + compress[i] = reduction; } } @@ -321,7 +302,6 @@ fn hash_compute(light: &Light, full_size: usize, header_hash: &H256, nonce: u64) ProofOfWork { mix_hash: mix_hash, value: value } } -// TODO: Use the `simd` crate pub fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node { let num_parent_nodes = cache.len(); let mut ret = cache[node_index as usize % num_parent_nodes].clone(); @@ -335,10 +315,8 @@ pub fn calculate_dag_item(node_index: u32, cache: &[Node]) -> Node { num_parent_nodes as u32; let parent = &cache[parent_index as usize]; - unroll! { - for w in 0..16 { - ret.as_words_mut()[w] = fnv_hash(ret.as_words()[w], parent.as_words()[w]); - } + for (a, b) in ret.as_words_mut().iter_mut().zip(parent.as_words()) { + *a = fnv_hash(*a, *b); } } diff --git a/ethash/src/keccak.rs b/ethash/src/keccak.rs index 3f7576c7bb..7e5ea39b62 100644 --- a/ethash/src/keccak.rs +++ b/ethash/src/keccak.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethash/src/lib.rs b/ethash/src/lib.rs index e40c08920c..8fc4510a44 100644 --- a/ethash/src/lib.rs +++ b/ethash/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,16 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +extern crate common_types; extern crate either; extern crate ethereum_types; extern crate memmap; extern crate parking_lot; extern crate primal; -#[macro_use] -extern crate crunchy; #[macro_use] extern crate log; +#[macro_use] +extern crate static_assertions; #[cfg(test)] extern crate rustc_hex; @@ -41,6 +42,9 @@ mod compute; mod seed_compute; mod cache; +#[cfg(feature = "bench")] +pub mod keccak; +#[cfg(not(feature = "bench"))] mod keccak; mod shared; @@ -49,17 +53,20 @@ pub mod progpow; #[cfg(not(feature = "bench"))] mod progpow; -pub use cache::{NodeCacheBuilder, OptimizeFor}; +pub use cache::NodeCacheBuilder; pub use compute::{ProofOfWork, quick_get_difficulty, slow_hash_block_number}; +pub use seed_compute::SeedHashCompute; +pub use shared::ETHASH_EPOCH_LENGTH; + +use common_types::engines::OptimizeFor; use compute::Light; -use ethereum_types::{U256, U512}; +use ethereum_types::{BigEndianHash, U256, U512}; use keccak::H256; use parking_lot::Mutex; -pub use seed_compute::SeedHashCompute; -pub use shared::ETHASH_EPOCH_LENGTH; + use std::mem; use std::path::{Path, PathBuf}; - +use std::convert::TryFrom; use std::sync::Arc; struct LightCache { @@ -83,7 +90,7 @@ impl EthashManager { EthashManager { cache_dir: cache_dir.to_path_buf(), nodecache_builder: NodeCacheBuilder::new(optimize_for.into().unwrap_or_default(), progpow_transition), - progpow_transition: progpow_transition, + progpow_transition, cache: Mutex::new(LightCache { recent_epoch: None, recent: None, @@ -161,12 +168,12 @@ impl EthashManager { /// Convert an Ethash boundary to its original difficulty. Basically just `f(x) = 2^256 / x`. pub fn boundary_to_difficulty(boundary: ðereum_types::H256) -> U256 { - difficulty_to_boundary_aux(&**boundary) + difficulty_to_boundary_aux(&boundary.into_uint()) } /// Convert an Ethash difficulty to the target boundary. Basically just `f(x) = 2^256 / x`. pub fn difficulty_to_boundary(difficulty: &U256) -> ethereum_types::H256 { - difficulty_to_boundary_aux(difficulty).into() + BigEndianHash::from_uint(&difficulty_to_boundary_aux(difficulty)) } fn difficulty_to_boundary_aux>(difficulty: T) -> ethereum_types::U256 { @@ -177,8 +184,8 @@ fn difficulty_to_boundary_aux>(difficulty: T) -> ethereum_types::U if difficulty == U512::one() { U256::max_value() } else { - // difficulty > 1, so result should never overflow 256 bits - U256::from((U512::one() << 256) / difficulty) + const PROOF: &str = "difficulty > 1, so result never overflows 256 bits; qed"; + U256::try_from((U512::one() << 256) / difficulty).expect(PROOF) } } @@ -203,10 +210,10 @@ fn test_lru() { #[test] fn test_difficulty_to_boundary() { - use ethereum_types::H256; + use ethereum_types::{H256, BigEndianHash}; use std::str::FromStr; - assert_eq!(difficulty_to_boundary(&U256::from(1)), H256::from(U256::max_value())); + assert_eq!(difficulty_to_boundary(&U256::from(1)), BigEndianHash::from_uint(&U256::max_value())); assert_eq!(difficulty_to_boundary(&U256::from(2)), H256::from_str("8000000000000000000000000000000000000000000000000000000000000000").unwrap()); assert_eq!(difficulty_to_boundary(&U256::from(4)), H256::from_str("4000000000000000000000000000000000000000000000000000000000000000").unwrap()); assert_eq!(difficulty_to_boundary(&U256::from(32)), H256::from_str("0800000000000000000000000000000000000000000000000000000000000000").unwrap()); @@ -220,9 +227,18 @@ fn test_difficulty_to_boundary_regression() { // https://github.com/paritytech/parity-ethereum/issues/8397 for difficulty in 1..9 { assert_eq!(U256::from(difficulty), boundary_to_difficulty(&difficulty_to_boundary(&difficulty.into()))); - assert_eq!(H256::from(difficulty), difficulty_to_boundary(&boundary_to_difficulty(&difficulty.into()))); - assert_eq!(U256::from(difficulty), boundary_to_difficulty(&boundary_to_difficulty(&difficulty.into()).into())); - assert_eq!(H256::from(difficulty), difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into())); + assert_eq!( + H256::from_low_u64_be(difficulty), + difficulty_to_boundary(&boundary_to_difficulty(&H256::from_low_u64_be(difficulty))), + ); + assert_eq!( + U256::from(difficulty), + boundary_to_difficulty(&BigEndianHash::from_uint(&boundary_to_difficulty(&H256::from_low_u64_be(difficulty)))), + ); + assert_eq!( + H256::from_low_u64_be(difficulty), + difficulty_to_boundary(&difficulty_to_boundary(&difficulty.into()).into_uint()), + ); } } @@ -235,5 +251,5 @@ fn test_difficulty_to_boundary_panics_on_zero() { #[test] #[should_panic] fn test_boundary_to_difficulty_panics_on_zero() { - boundary_to_difficulty(ðereum_types::H256::from(0)); + boundary_to_difficulty(ðereum_types::H256::zero()); } diff --git a/ethash/src/progpow.rs b/ethash/src/progpow.rs index 038f38c225..4c18c255a9 100644 --- a/ethash/src/progpow.rs +++ b/ethash/src/progpow.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -14,18 +14,37 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! ProgPoW (Programmatic Proof-of-Work) is the Ethereum network's proposed new Application-Specific Integrated +//! Circuit (ASIC) resistant Proof-of-Work mining algorithm. +//! +//! ProgPoW's aim is to reduce the efficiencies of specialized mining devices known as ASIC chips +//! (and accelerated GPU-based setups), and to maximize the performance of General Purpose Hardware (GPUs) to enable +//! more users to compete for new cryptocurrency awarded by the protocol. +//! +//! ASIC chips are those that are tailored to efficiently mining cryptocurrency based on a specific hashing algorithm. +//! +//! GPU mining setups are less specialised are struggle to compete for mining rewards. +//! +//! It would be a change from Ethereum's current PoW mining algorithm known as Ethash. +//! +//! ProgPoW audits have been proposed to analyse the efficiency of a ProgPoW ASICs over +//! GPUs and analysis of the economic impact on the Ethereum protocol. +//! +//! We use ProgPoW 0.9.3 version as suggested on Specification +//! https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1057.md#specification + use compute::{FNV_PRIME, calculate_dag_item}; use keccak::H256; use shared::{ETHASH_ACCESSES, ETHASH_MIX_BYTES, Node, get_data_size}; const PROGPOW_CACHE_BYTES: usize = 16 * 1024; const PROGPOW_CACHE_WORDS: usize = PROGPOW_CACHE_BYTES / 4; -const PROGPOW_CNT_CACHE: usize = 12; -const PROGPOW_CNT_MATH: usize = 20; +const PROGPOW_CNT_CACHE: usize = 11; +const PROGPOW_CNT_MATH: usize = 18; const PROGPOW_CNT_DAG: usize = ETHASH_ACCESSES; const PROGPOW_DAG_LOADS: usize = 4; const PROGPOW_MIX_BYTES: usize = 2 * ETHASH_MIX_BYTES; -const PROGPOW_PERIOD_LENGTH: usize = 50; // blocks per progpow epoch (N) +const PROGPOW_PERIOD_LENGTH: usize = 10; // blocks per progpow epoch (N) const PROGPOW_LANES: usize = 16; const PROGPOW_REGS: usize = 32; @@ -403,7 +422,8 @@ pub fn generate_cdag(cache: &[Node]) -> CDag { mod test { use tempdir::TempDir; - use cache::{NodeCacheBuilder, OptimizeFor}; + use common_types::engines::OptimizeFor; + use cache::NodeCacheBuilder; use keccak::H256; use rustc_hex::FromHex; use serde_json::{self, Value}; @@ -529,8 +549,8 @@ mod test { &c_dag, ); - let expected_digest = FromHex::from_hex("63155f732f2bf556967f906155b510c917e48e99685ead76ea83f4eca03ab12b").unwrap(); - let expected_result = FromHex::from_hex("faeb1be51075b03a4ff44b335067951ead07a3b078539ace76fd56fc410557a3").unwrap(); + let expected_digest = FromHex::from_hex("b3bad9ca6f7c566cf0377d1f8cce29d6516a96562c122d924626281ec948ef02").unwrap(); + let expected_result = FromHex::from_hex("f4ac202715ded4136e72887c39e63a4738331c57fd9eb79f6ec421c281aa8743").unwrap(); assert_eq!( digest.to_vec(), diff --git a/ethash/src/seed_compute.rs b/ethash/src/seed_compute.rs index 5178280328..ff29fdb96f 100644 --- a/ethash/src/seed_compute.rs +++ b/ethash/src/seed_compute.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethash/src/shared.rs b/ethash/src/shared.rs index 2c9a9fa9d0..70f6dd96ce 100644 --- a/ethash/src/shared.rs +++ b/ethash/src/shared.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -69,34 +69,7 @@ pub type NodeBytes = [u8; NODE_BYTES]; pub type NodeWords = [u32; NODE_WORDS]; pub type NodeDwords = [u64; NODE_DWORDS]; -macro_rules! static_assert_size_eq { - (@inner $a:ty, $b:ty, $($rest:ty),*) => { - fn first() { - static_assert_size_eq!($a, $b); - } - - fn second() { - static_assert_size_eq!($b, $($rest),*); - } - }; - (@inner $a:ty, $b:ty) => { - unsafe { - let val: $b = ::std::mem::uninitialized(); - let _: $a = ::std::mem::transmute(val); - } - }; - ($($rest:ty),*) => { - static_assert_size_eq!(size_eq: $($rest),*); - }; - ($name:ident : $($rest:ty),*) => { - #[allow(dead_code)] - fn $name() { - static_assert_size_eq!(@inner $($rest),*); - } - }; -} - -static_assert_size_eq!(Node, NodeBytes, NodeWords, NodeDwords); +assert_eq_size!(node; Node, NodeBytes, NodeWords, NodeDwords); #[repr(C)] pub union Node { diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 16013d5479..c4cc43f377 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -7,91 +7,100 @@ version = "1.12.0" authors = ["Parity Technologies "] [dependencies] -ansi_term = "0.10" +account-state = { path = "account-state" } +ansi_term = "0.11" +basic-authority = { path = "./engines/basic-authority", optional = true} # used by test-helpers feature blooms-db = { path = "../util/blooms-db", optional = true } -bn = { git = "https://github.com/paritytech/bn", default-features = false } -byteorder = "1.0" -common-types = { path = "types" } -crossbeam-utils = "0.6" -eip-152 = { version = "0.1", path = "../util/EIP-152" } +client-traits = { path = "./client-traits" } +common-types = { path = "./types" } +engine = { path = "./engine" } env_logger = { version = "0.5", optional = true } -error-chain = { version = "0.12", default-features = false } -ethabi = "6.0" -ethabi-contract = "6.0" -ethabi-derive = "6.0" -ethash = { path = "../ethash" } +ethash = { path = "../ethash", optional = true } +ethjson = { path = "../json", optional = true } ethcore-blockchain = { path = "./blockchain" } -ethcore-bloom-journal = { path = "../util/bloom" } ethcore-call-contract = { path = "./call-contract" } ethcore-db = { path = "./db" } ethcore-io = { path = "../util/io" } ethcore-miner = { path = "../miner" } ethcore-stratum = { path = "../miner/stratum", optional = true } -ethereum-types = "0.4" -ethjson = { path = "../json" } -ethkey = { path = "../accounts/ethkey" } +ethereum-types = "0.8.0" evm = { path = "evm" } -hash-db = "0.11.0" -heapsize = "0.4" +executive-state = { path = "executive-state" } +futures = "0.1" +hash-db = "0.15.0" itertools = "0.5" journaldb = { path = "../util/journaldb" } -keccak-hash = "0.1" -keccak-hasher = { path = "../util/keccak-hasher" } -kvdb = "0.1" -kvdb-memorydb = "0.1" -kvdb-rocksdb = { version = "0.1.3", optional = true } -lazy_static = "1.2.0" -len-caching-lock = { path = "../util/len-caching-lock" } +keccak-hash = "0.4.0" +kvdb = "0.3.1" +kvdb-memorydb = { version = "0.3.1", optional = true } +kvdb-rocksdb = { version = "0.4.1", optional = true } +lazy_static = { version = "1.3", optional = true } log = "0.4" -lru-cache = "0.1" -macros = { path = "../util/macros" } +macros = { path = "../util/macros", optional = true } +machine = { path = "./machine" } memory-cache = { path = "../util/memory-cache" } -memory-db = "0.11.0" -num = { version = "0.1", default-features = false, features = ["bigint"] } -num_cpus = "1.2" parity-bytes = "0.1" -parity-crypto = "0.3.0" -parity-snappy = "0.1" -parking_lot = "0.7" -trie-db = "0.11.0" +parking_lot = "0.9" +pod = { path = "pod", optional = true } +trie-db = "0.18.0" +parity-crypto = { version = "0.4.2", features = ["publickey"], optional = true } patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" } -rand = "0.4" +rand = "0.7" +rand_xorshift = "0.2" rayon = "1.1" -rlp = { version = "0.3.0", features = ["ethereum"] } -rlp_derive = { path = "../util/rlp-derive" } -rustc-hex = "1.0" +registrar = { path = "../util/registrar" } +rlp = "0.4.0" +rustc-hex = "2" +scopeguard = "1.0.0" serde = "1.0" serde_derive = "1.0" -stats = { path = "../util/stats" } -tempdir = {version="0.3", optional = true} -time-utils = { path = "../util/time-utils" } +snapshot = { path = "snapshot" } +spec = { path = "spec" } +state-db = { path = "state-db" } +tempdir = { version = "0.3", optional = true } +trace = { path = "trace" } trace-time = "0.1" +trie-vm-factories = { path = "trie-vm-factories" } triehash-ethereum = { version = "0.2", path = "../util/triehash-ethereum" } unexpected = { path = "../util/unexpected" } using_queue = { path = "../miner/using-queue" } +verification = { path = "./verification" } vm = { path = "vm" } -wasm = { path = "wasm" } [dev-dependencies] +account-db = { path = "account-db" } blooms-db = { path = "../util/blooms-db" } -criterion = "0.2" +criterion = "0.3" +engine = { path = "./engine", features = ["test-helpers"] } env_logger = "0.5" ethcore-accounts = { path = "../accounts" } +ethcore-builtin = { path = "./builtin" } +ethjson = { path = "../json", features = ["test-helpers"] } +parity-crypto = { version = "0.4.2", features = ["publickey"] } fetch = { path = "../util/fetch" } -hex-literal = "0.2.1" -kvdb-rocksdb = "0.1.3" +kvdb-memorydb = "0.3.1" +kvdb-rocksdb = "0.4.1" +lazy_static = "1.3" +machine = { path = "./machine", features = ["test-helpers"] } +macros = { path = "../util/macros" } parity-runtime = { path = "../util/runtime" } -rlp_compress = { path = "../util/rlp-compress" } +serde_json = "1.0" +stats = { path = "../util/stats" } +pod = { path = "pod" } tempdir = "0.3" -trie-standardmap = "0.1" +trie-standardmap = "0.15.0" [features] -parity = ["work-notify", "price-info", "stratum"] +parity = ["work-notify", "price-info", "stratum", "macros"] # Large optional features that are enabled by default for Parity, # but might be omitted for other dependent crates. work-notify = ["ethcore-miner/work-notify"] price-info = ["ethcore-miner/price-info"] -stratum = ["ethcore-stratum"] +stratum = [ + "ethash", + "ethcore-stratum" +] + # Disables seal verification for mined blocks. # This allows you to submit any seal via RPC to test and benchmark @@ -107,15 +116,22 @@ evm-debug-tests = ["evm-debug", "evm/evm-debug-tests"] # EVM debug traces are printed. slow-blocks = [] # Run JSON consensus tests. -json-tests = ["env_logger", "test-helpers", "to-pod-full"] -# Skip JSON consensus tests with pending issues. -ci-skip-tests = [] +json-tests = ["env_logger", "test-helpers", "lazy_static", "machine/test-helpers", "common-types/test-helpers"] # Run memory/cpu heavy tests. test-heavy = [] # Compile test helpers -test-helpers = ["tempdir", "kvdb-rocksdb", "blooms-db"] -# Enables slow 'to-pod-full' method for use in tests and evmbin. -to-pod-full = [] +# note[dvdplm]: "basic-authority/test-helpers" is needed so that `generate_dummy_client_with_spec` works +test-helpers = [ + "blooms-db", + "ethjson/test-helpers", + "parity-crypto", + "kvdb-memorydb", + "kvdb-rocksdb", + "macros", + "pod", + "tempdir", + "basic-authority/test-helpers" + ] [[bench]] name = "builtin" diff --git a/ethcore/account-db/Cargo.toml b/ethcore/account-db/Cargo.toml new file mode 100644 index 0000000000..a32d50e329 --- /dev/null +++ b/ethcore/account-db/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "account-db" +description = "DB backend wrapper for Account trie" +authors = ["Parity Technologies "] +license = "GPL-3.0" +version = "0.1.0" +edition = "2018" + +[dependencies] +ethereum-types = "0.8.0" +hash-db = "0.15.0" +keccak-hash = "0.4.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +kvdb = "0.3.1" +rlp = "0.4" diff --git a/ethcore/src/account_db.rs b/ethcore/account-db/src/lib.rs similarity index 50% rename from ethcore/src/account_db.rs rename to ethcore/account-db/src/lib.rs index a389c009bf..970380084e 100644 --- a/ethcore/src/account_db.rs +++ b/ethcore/account-db/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,23 +16,20 @@ //! DB backend wrapper for Account trie use ethereum_types::H256; -use hash::{KECCAK_NULL_RLP, keccak}; -use hash_db::{HashDB, AsHashDB}; +use keccak_hash::{KECCAK_NULL_RLP, keccak}; +use hash_db::{HashDB, AsHashDB, Prefix}; use keccak_hasher::KeccakHasher; use kvdb::DBValue; use rlp::NULL_RLP; -#[cfg(test)] -use ethereum_types::Address; - -// combines a key with an address hash to ensure uniqueness. +// Combines a key with an address hash to ensure uniqueness. // leaves the first 96 bits untouched in order to support partial key lookup. #[inline] fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 { let mut dst = key.clone(); { - let last_src: &[u8] = &*address_hash; - let last_dst: &mut [u8] = &mut *dst; + let last_src: &[u8] = address_hash.as_bytes(); + let last_dst: &mut [u8] = dst.as_bytes_mut(); for (k, a) in last_dst[12..].iter_mut().zip(&last_src[12..]) { *k ^= *a } @@ -57,7 +54,7 @@ impl Default for Factory { impl Factory { /// Create a read-only accountdb. /// This will panic when write operations are called. - pub fn readonly<'db>(&self, db: &'db HashDB, address_hash: H256) -> Box + 'db> { + pub fn readonly<'db>(&self, db: &'db dyn HashDB, address_hash: H256) -> Box + 'db> { match *self { Factory::Mangled => Box::new(AccountDB::from_hash(db, address_hash)), Factory::Plain => Box::new(Wrapping(db)), @@ -65,7 +62,7 @@ impl Factory { } /// Create a new mutable hashdb. - pub fn create<'db>(&self, db: &'db mut HashDB, address_hash: H256) -> Box + 'db> { + pub fn create<'db>(&self, db: &'db mut dyn HashDB, address_hash: H256) -> Box + 'db> { match *self { Factory::Mangled => Box::new(AccountDBMut::from_hash(db, address_hash)), Factory::Plain => Box::new(WrappingMut(db)), @@ -77,207 +74,189 @@ impl Factory { /// DB backend wrapper for Account trie /// Transforms trie node keys for the database pub struct AccountDB<'db> { - db: &'db HashDB, + db: &'db dyn HashDB, address_hash: H256, } impl<'db> AccountDB<'db> { - /// Create a new AccountDB from an address. - #[cfg(test)] - pub fn new(db: &'db HashDB, address: &Address) -> Self { - Self::from_hash(db, keccak(address)) - } - - /// Create a new AcountDB from an address' hash. - pub fn from_hash(db: &'db HashDB, address_hash: H256) -> Self { - AccountDB { - db: db, - address_hash: address_hash, - } + /// Create a new AccountDB from an address' hash. + pub fn from_hash(db: &'db dyn HashDB, address_hash: H256) -> Self { + AccountDB { db, address_hash } } } impl<'db> AsHashDB for AccountDB<'db> { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl<'db> HashDB for AccountDB<'db> { - fn get(&self, key: &H256) -> Option { + fn get(&self, key: &H256, prefix: Prefix) -> Option { if key == &KECCAK_NULL_RLP { - return Some(DBValue::from_slice(&NULL_RLP)); + return Some(NULL_RLP.to_vec()); } - self.db.get(&combine_key(&self.address_hash, key)) + self.db.get(&combine_key(&self.address_hash, key), prefix) } - fn contains(&self, key: &H256) -> bool { + fn contains(&self, key: &H256, prefix: Prefix) -> bool { if key == &KECCAK_NULL_RLP { return true; } - self.db.contains(&combine_key(&self.address_hash, key)) + self.db.contains(&combine_key(&self.address_hash, key), prefix) } - fn insert(&mut self, _value: &[u8]) -> H256 { + fn insert(&mut self, _prefix: Prefix, _value: &[u8]) -> H256 { unimplemented!() } - fn emplace(&mut self, _key: H256, _value: DBValue) { + fn emplace(&mut self, _key: H256, _prefix: Prefix, _value: DBValue) { unimplemented!() } - fn remove(&mut self, _key: &H256) { + fn remove(&mut self, _key: &H256, _prefix: Prefix) { unimplemented!() } } /// DB backend wrapper for Account trie pub struct AccountDBMut<'db> { - db: &'db mut HashDB, + db: &'db mut dyn HashDB, address_hash: H256, } impl<'db> AccountDBMut<'db> { - /// Create a new AccountDB from an address. - #[cfg(test)] - pub fn new(db: &'db mut HashDB, address: &Address) -> Self { - Self::from_hash(db, keccak(address)) - } - - /// Create a new AcountDB from an address' hash. - pub fn from_hash(db: &'db mut HashDB, address_hash: H256) -> Self { - AccountDBMut { - db: db, - address_hash: address_hash, - } + /// Create a new `AccountDBMut` from an address' hash. + pub fn from_hash(db: &'db mut dyn HashDB, address_hash: H256) -> Self { + AccountDBMut { db, address_hash } } - #[cfg(test)] + /// Create an `AccountDB` from an `AccountDBMut` (used in tests). pub fn immutable(&'db self) -> AccountDB<'db> { AccountDB { db: self.db, address_hash: self.address_hash.clone() } } } impl<'db> HashDB for AccountDBMut<'db>{ - fn get(&self, key: &H256) -> Option { + fn get(&self, key: &H256, prefix: Prefix) -> Option { if key == &KECCAK_NULL_RLP { - return Some(DBValue::from_slice(&NULL_RLP)); + return Some(NULL_RLP.to_vec()); } - self.db.get(&combine_key(&self.address_hash, key)) + self.db.get(&combine_key(&self.address_hash, key), prefix) } - fn contains(&self, key: &H256) -> bool { + fn contains(&self, key: &H256, prefix: Prefix) -> bool { if key == &KECCAK_NULL_RLP { return true; } - self.db.contains(&combine_key(&self.address_hash, key)) + self.db.contains(&combine_key(&self.address_hash, key), prefix) } - fn insert(&mut self, value: &[u8]) -> H256 { + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { if value == &NULL_RLP { return KECCAK_NULL_RLP.clone(); } let k = keccak(value); let ak = combine_key(&self.address_hash, &k); - self.db.emplace(ak, DBValue::from_slice(value)); + self.db.emplace(ak, prefix, value.to_vec()); k } - fn emplace(&mut self, key: H256, value: DBValue) { + fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { if key == KECCAK_NULL_RLP { return; } let key = combine_key(&self.address_hash, &key); - self.db.emplace(key, value) + self.db.emplace(key, prefix, value) } - fn remove(&mut self, key: &H256) { + fn remove(&mut self, key: &H256, prefix: Prefix) { if key == &KECCAK_NULL_RLP { return; } let key = combine_key(&self.address_hash, key); - self.db.remove(&key) + self.db.remove(&key, prefix) } } impl<'db> AsHashDB for AccountDBMut<'db> { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } -struct Wrapping<'db>(&'db HashDB); +struct Wrapping<'db>(&'db dyn HashDB); impl<'db> AsHashDB for Wrapping<'db> { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl<'db> HashDB for Wrapping<'db> { - fn get(&self, key: &H256) -> Option { + fn get(&self, key: &H256, prefix: Prefix) -> Option { if key == &KECCAK_NULL_RLP { - return Some(DBValue::from_slice(&NULL_RLP)); + return Some(NULL_RLP.to_vec()); } - self.0.get(key) + self.0.get(key, prefix) } - fn contains(&self, key: &H256) -> bool { + fn contains(&self, key: &H256, prefix: Prefix) -> bool { if key == &KECCAK_NULL_RLP { return true; } - self.0.contains(key) + self.0.contains(key, prefix) } - fn insert(&mut self, _value: &[u8]) -> H256 { + fn insert(&mut self, _prefix: Prefix, _value: &[u8]) -> H256 { unimplemented!() } - fn emplace(&mut self, _key: H256, _value: DBValue) { + fn emplace(&mut self, _key: H256, _prefix: Prefix, _value: DBValue) { unimplemented!() } - fn remove(&mut self, _key: &H256) { + fn remove(&mut self, _key: &H256, _prefix: Prefix) { unimplemented!() } } -struct WrappingMut<'db>(&'db mut HashDB); +struct WrappingMut<'db>(&'db mut dyn HashDB); impl<'db> AsHashDB for WrappingMut<'db> { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl<'db> HashDB for WrappingMut<'db>{ - fn get(&self, key: &H256) -> Option { + fn get(&self, key: &H256, prefix: Prefix) -> Option { if key == &KECCAK_NULL_RLP { - return Some(DBValue::from_slice(&NULL_RLP)); + return Some(NULL_RLP.to_vec()); } - self.0.get(key) + self.0.get(key, prefix) } - fn contains(&self, key: &H256) -> bool { + fn contains(&self, key: &H256, prefix: Prefix) -> bool { if key == &KECCAK_NULL_RLP { return true; } - self.0.contains(key) + self.0.contains(key, prefix) } - fn insert(&mut self, value: &[u8]) -> H256 { + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { if value == &NULL_RLP { return KECCAK_NULL_RLP.clone(); } - self.0.insert(value) + self.0.insert(prefix, value) } - fn emplace(&mut self, key: H256, value: DBValue) { + fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { if key == KECCAK_NULL_RLP { return; } - self.0.emplace(key, value) + self.0.emplace(key, prefix, value) } - fn remove(&mut self, key: &H256) { + fn remove(&mut self, key: &H256, prefix: Prefix) { if key == &KECCAK_NULL_RLP { return; } - self.0.remove(key) + self.0.remove(key, prefix) } } diff --git a/ethcore/account-state/Cargo.toml b/ethcore/account-state/Cargo.toml new file mode 100644 index 0000000000..6d0aee0f0b --- /dev/null +++ b/ethcore/account-state/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "account-state" +description = "Ethereum accounts, keeps track of changes to the code and storage" +authors = ["Parity Technologies "] +license = "GPL-3.0" +version = "0.1.0" +edition = "2018" + +[dependencies] +common-types = { path = "../types"} +derive_more = "0.15.0" +ethereum-types = "0.8.0" +ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +trie-vm-factories = { path = "../trie-vm-factories" } +hash-db = "0.15.0" +journaldb = { path = "../../util/journaldb" } +keccak-hash = "0.4.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +kvdb = "0.3.1" +log = "0.4" +lru-cache = "0.1.2" +memory-db = "0.18.0" +parity-bytes = "0.1.0" +parity-util-mem = "0.3.0" +parking_lot = "0.9" +pod = { path = "../pod" } +rlp = "0.4.0" +serde = { version = "1.0", features = ["derive"] } +trie-db = "0.18.0" + +[dev-dependencies] +account-db = { path = "../account-db" } +journaldb = { path = "../../util/journaldb" } +parity-bytes = "0.1.0" +rlp_compress = { path = "../../util/rlp-compress" } diff --git a/ethcore/src/state/account.rs b/ethcore/account-state/src/account.rs similarity index 78% rename from ethcore/src/state/account.rs rename to ethcore/account-state/src/account.rs index fea9444b1c..572443f2a6 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/account-state/src/account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,25 +15,24 @@ // along with Parity Ethereum. If not, see . //! Single account in the system. - +use std::cell::{Cell, RefCell}; +use std::collections::{BTreeMap, HashMap}; use std::fmt; 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 ethereum_types::{Address, BigEndianHash, H256, U256}; use hash_db::HashDB; -use keccak_hasher::KeccakHasher; +use keccak_hash::{keccak, KECCAK_EMPTY, KECCAK_NULL_RLP}; use kvdb::DBValue; -use bytes::{Bytes, ToPretty}; -use trie::{Trie, Recorder}; -use ethtrie::{TrieFactory, TrieDB, SecTrieDB, Result as TrieResult}; -use pod_account::*; -use rlp::{RlpStream, encode}; +use log::{trace, warn}; use lru_cache::LruCache; -use types::basic_account::BasicAccount; - -use std::cell::{RefCell, Cell}; +use parity_bytes::{Bytes, ToPretty}; +use rlp::{DecoderError, encode}; +use trie_db::{Recorder, Trie}; +use common_types::basic_account::BasicAccount; +use ethtrie::{Result as TrieResult, SecTrieDB, TrieDB, TrieFactory}; +use keccak_hasher::KeccakHasher; +use pod::PodAccount; const STORAGE_CACHE_ITEMS: usize = 8192; @@ -72,6 +71,8 @@ pub struct Account { code_size: Option, // Code cache of the account. code_cache: Arc, + // Version of the account. + code_version: U256, // Account code new or has been modified. code_filth: Filth, // Cached address hash. @@ -90,6 +91,7 @@ impl From for Account { code_hash: basic.code_hash, code_size: None, code_cache: Arc::new(vec![]), + code_version: basic.code_version, code_filth: Filth::Clean, address_hash: Cell::new(None), } @@ -99,7 +101,7 @@ impl From for Account { impl Account { #[cfg(test)] /// General constructor. - pub fn new(balance: U256, nonce: U256, storage: HashMap, code: Bytes) -> Account { + pub fn new(balance: U256, nonce: U256, storage: HashMap, code: Bytes, version: U256) -> Account { Account { balance: balance, nonce: nonce, @@ -110,6 +112,7 @@ impl Account { code_hash: keccak(&code), code_size: Some(code.len()), code_cache: Arc::new(code), + code_version: version, code_filth: Filth::Dirty, address_hash: Cell::new(None), } @@ -132,10 +135,26 @@ impl Account { code_filth: Filth::Dirty, code_size: Some(pod.code.as_ref().map_or(0, |c| c.len())), code_cache: Arc::new(pod.code.map_or_else(|| { warn!("POD account with unknown code is being created! Assuming no code."); vec![] }, |c| c)), + code_version: pod.version, address_hash: Cell::new(None), } } + /// Convert Account to a PodAccount. + /// NOTE: This will silently fail unless the account is fully cached. + pub fn to_pod(&self) -> PodAccount { + PodAccount { + balance: self.balance, + nonce: self.nonce, + storage: self.storage_changes.iter().fold(BTreeMap::new(), |mut m, (k, v)| { + m.insert(k.clone(), v.clone()); + m + }), + code: self.code().map(|x| x.to_vec()), + version: self.code_version, + } + } + /// Create a new account with the given balance. pub fn new_basic(balance: U256, nonce: U256) -> Account { Account { @@ -148,24 +167,24 @@ impl Account { code_hash: KECCAK_EMPTY, code_cache: Arc::new(vec![]), code_size: Some(0), + code_version: U256::zero(), code_filth: Filth::Clean, address_hash: Cell::new(None), } } /// Create a new account from RLP. - pub fn from_rlp(rlp: &[u8]) -> Result { + pub fn from_rlp(rlp: &[u8]) -> Result { ::rlp::decode::(rlp) .map(|ba| ba.into()) - .map_err(|e| e.into()) } /// Create a new contract account. /// NOTE: make sure you use `init_code` on this before `commit`ing. - pub fn new_contract(balance: U256, nonce: U256, original_storage_root: H256) -> Account { + pub fn new_contract(balance: U256, nonce: U256, version: U256, original_storage_root: H256) -> Account { Account { - balance: balance, - nonce: nonce, + balance, + nonce, storage_root: KECCAK_NULL_RLP, storage_cache: Self::empty_storage_cache(), original_storage_cache: if original_storage_root == KECCAK_NULL_RLP { @@ -177,6 +196,7 @@ impl Account { code_hash: KECCAK_EMPTY, code_cache: Arc::new(vec![]), code_size: None, + code_version: version, code_filth: Filth::Clean, address_hash: Cell::new(None), } @@ -217,7 +237,7 @@ impl Account { /// Get (and cache) the contents of the trie's storage at `key`. /// Takes modified storage into account. - pub fn storage_at(&self, db: &HashDB, key: &H256) -> TrieResult { + pub fn storage_at(&self, db: &dyn HashDB, key: &H256) -> TrieResult { if let Some(value) = self.cached_storage_at(key) { return Ok(value); } @@ -230,7 +250,7 @@ impl Account { /// Get (and cache) the contents of the trie's storage at `key`. /// Does not take modified storage into account. - pub fn original_storage_at(&self, db: &HashDB, key: &H256) -> TrieResult { + pub fn original_storage_at(&self, db: &dyn HashDB, key: &H256) -> TrieResult { if let Some(value) = self.cached_original_storage_at(key) { return Ok(value); } @@ -252,11 +272,11 @@ impl Account { } } - fn get_and_cache_storage(storage_root: &H256, storage_cache: &mut LruCache, db: &HashDB, key: &H256) -> TrieResult { + fn get_and_cache_storage(storage_root: &H256, storage_cache: &mut LruCache, db: &dyn HashDB, key: &H256) -> TrieResult { let db = SecTrieDB::new(&db, storage_root)?; 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(); + let item: U256 = db.get_with(key.as_bytes(), panicky_decoder)?.unwrap_or_else(U256::zero); + let value: H256 = BigEndianHash::from_uint(&item); storage_cache.insert(key.clone(), value.clone()); Ok(value) } @@ -291,7 +311,7 @@ impl Account { // If storage root is empty RLP, then early return zero value. Practically, this makes it so that if // `original_storage_cache` is used, then `storage_cache` will always remain empty. if self.storage_root == KECCAK_NULL_RLP { - return Some(H256::new()); + return Some(H256::zero()); } if let Some(value) = self.storage_cache.borrow_mut().get_mut(key) { @@ -307,6 +327,9 @@ impl Account { /// return the nonce associated with this account. pub fn nonce(&self) -> &U256 { &self.nonce } + /// return the code version associated with this account. + pub fn code_version(&self) -> &U256 { &self.code_version } + /// return the code hash associated with this account. pub fn code_hash(&self) -> H256 { self.code_hash.clone() @@ -358,16 +381,16 @@ impl Account { /// Provide a database to get `code_hash`. Should not be called if it is a contract without code. Returns the cached code, if successful. #[must_use] - pub fn cache_code(&mut self, db: &HashDB) -> Option> { + pub fn cache_code(&mut self, db: &dyn HashDB) -> Option> { // TODO: fill out self.code_cache; trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); if self.is_cached() { return Some(self.code_cache.clone()); } - match db.get(&self.code_hash) { + match db.get(&self.code_hash, hash_db::EMPTY_PREFIX) { Some(x) => { self.code_size = Some(x.len()); - self.code_cache = Arc::new(x.into_vec()); + self.code_cache = Arc::new(x); Some(self.code_cache.clone()) }, _ => { @@ -388,12 +411,12 @@ impl Account { /// Provide a database to get `code_size`. Should not be called if it is a contract without code. Returns whether /// the cache succeeds. #[must_use] - pub fn cache_code_size(&mut self, db: &HashDB) -> bool { + pub fn cache_code_size(&mut self, db: &dyn HashDB) -> bool { // TODO: fill out self.code_cache; trace!("Account::cache_code_size: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); self.code_size.is_some() || if self.code_hash != KECCAK_EMPTY { - match db.get(&self.code_hash) { + match db.get(&self.code_hash, hash_db::EMPTY_PREFIX) { Some(x) => { self.code_size = Some(x.len()); true @@ -482,14 +505,14 @@ impl Account { } /// Commit the `storage_changes` to the backing DB and update `storage_root`. - pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut HashDB) -> TrieResult<()> { + pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut dyn HashDB) -> TrieResult<()> { let mut t = trie_factory.from_existing(db, &mut self.storage_root)?; for (k, v) in self.storage_changes.drain() { // cast key and value to trait type, // so we can call overloaded `to_bytes` method match v.is_zero() { - true => t.remove(&k)?, - false => t.insert(&k, &encode(&U256::from(&*v)))?, + true => t.remove(k.as_bytes())?, + false => t.insert(k.as_bytes(), &encode(&v.into_uint()))?, }; self.storage_cache.borrow_mut().insert(k, v); @@ -499,7 +522,7 @@ impl Account { } /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. - pub fn commit_code(&mut self, db: &mut HashDB) { + pub fn commit_code(&mut self, db: &mut dyn HashDB) { trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_filth == Filth::Dirty, self.code_cache.is_empty()); match (self.code_filth == Filth::Dirty, self.code_cache.is_empty()) { (true, true) => { @@ -507,7 +530,7 @@ impl Account { self.code_filth = Filth::Clean; }, (true, false) => { - db.emplace(self.code_hash.clone(), DBValue::from_slice(&*self.code_cache)); + db.emplace(self.code_hash.clone(), hash_db::EMPTY_PREFIX, self.code_cache.to_vec()); self.code_size = Some(self.code_cache.len()); self.code_filth = Filth::Clean; }, @@ -517,12 +540,15 @@ impl Account { /// Export to RLP. pub fn rlp(&self) -> Bytes { - let mut stream = RlpStream::new_list(4); - stream.append(&self.nonce); - stream.append(&self.balance); - stream.append(&self.storage_root); - stream.append(&self.code_hash); - stream.out() + let basic = BasicAccount { + nonce: self.nonce, + balance: self.balance, + storage_root: self.storage_root, + code_hash: self.code_hash, + code_version: self.code_version, + }; + + rlp::encode(&basic) } /// Clone basic account data @@ -537,6 +563,7 @@ impl Account { code_hash: self.code_hash.clone(), code_size: self.code_size.clone(), code_cache: self.code_cache.clone(), + code_version: self.code_version, code_filth: self.code_filth, address_hash: self.address_hash.clone(), } @@ -567,6 +594,7 @@ impl Account { self.code_filth = other.code_filth; self.code_cache = other.code_cache; self.code_size = other.code_size; + self.code_version = other.code_version; self.address_hash = other.address_hash; if self.storage_root == other.storage_root { let mut cache = self.storage_cache.borrow_mut(); @@ -588,17 +616,17 @@ impl Account { /// trie. /// `storage_key` is the hash of the desired storage key, meaning /// this will only work correctly under a secure trie. - pub fn prove_storage(&self, db: &HashDB, storage_key: H256) -> TrieResult<(Vec, H256)> { + pub fn prove_storage(&self, db: &dyn HashDB, storage_key: H256) -> TrieResult<(Vec, H256)> { let mut recorder = Recorder::new(); let trie = TrieDB::new(&db, &self.storage_root)?; let item: U256 = { 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) + trie.get_with(storage_key.as_bytes(), query)?.unwrap_or_else(U256::zero) }; - Ok((recorder.drain().into_iter().map(|r| r.data).collect(), item.into())) + Ok((recorder.drain().into_iter().map(|r| r.data).collect(), BigEndianHash::from_uint(&item))) } } @@ -615,12 +643,16 @@ impl fmt::Debug for Account { #[cfg(test)] mod tests { - use rlp_compress::{compress, decompress, snapshot_swapper}; - use ethereum_types::{H256, Address}; + use std::str::FromStr; + + use ethereum_types::{Address, H256}; + use parity_bytes::Bytes; + + use account_db::*; use journaldb::new_memory_db; - use bytes::Bytes; + use rlp_compress::{compress, decompress, snapshot_swapper}; + use super::*; - use account_db::*; #[test] fn account_compress() { @@ -634,10 +666,10 @@ mod tests { #[test] fn storage_at() { let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); let rlp = { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); - a.set_storage(0x00u64.into(), 0x1234u64.into()); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); + a.set_storage(H256::zero(), H256::from_low_u64_be(0x1234)); a.commit_storage(&Default::default(), &mut db).unwrap(); a.init_code(vec![]); a.commit_code(&mut db); @@ -645,18 +677,18 @@ mod tests { }; 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()); + assert_eq!(a.storage_root().unwrap(), H256::from_str("c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2").unwrap()); + assert_eq!(a.storage_at(&db.immutable(), &H256::zero()).unwrap(), H256::from_low_u64_be(0x1234)); + assert_eq!(a.storage_at(&db.immutable(), &H256::from_low_u64_be(0x01)).unwrap(), H256::zero()); } #[test] fn note_code() { let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); let rlp = { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); a.init_code(vec![0x55, 0x44, 0xffu8]); a.commit_code(&mut db); a.rlp() @@ -671,60 +703,60 @@ mod tests { #[test] fn commit_storage() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); - a.set_storage(0.into(), 0x1234.into()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); + a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234)); assert_eq!(a.storage_root(), None); a.commit_storage(&Default::default(), &mut db).unwrap(); - assert_eq!(a.storage_root().unwrap(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2".into()); + assert_eq!(a.storage_root().unwrap(), H256::from_str("c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2").unwrap()); } #[test] fn commit_remove_commit_storage() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); - a.set_storage(0.into(), 0x1234.into()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); + a.set_storage(H256::from_low_u64_be(0), H256::from_low_u64_be(0x1234)); a.commit_storage(&Default::default(), &mut db).unwrap(); - a.set_storage(1.into(), 0x1234.into()); + a.set_storage(H256::from_low_u64_be(1), H256::from_low_u64_be(0x1234)); a.commit_storage(&Default::default(), &mut db).unwrap(); - a.set_storage(1.into(), 0.into()); + a.set_storage(H256::from_low_u64_be(1), H256::from_low_u64_be(0)); a.commit_storage(&Default::default(), &mut db).unwrap(); - assert_eq!(a.storage_root().unwrap(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2".into()); + assert_eq!(a.storage_root().unwrap(), H256::from_str("c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2").unwrap()); } #[test] fn commit_code() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); a.init_code(vec![0x55, 0x44, 0xffu8]); assert_eq!(a.code_filth, Filth::Dirty); assert_eq!(a.code_size(), Some(3)); a.commit_code(&mut db); - assert_eq!(a.code_hash(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb".into()); + assert_eq!(a.code_hash(), H256::from_str("af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb").unwrap()); } #[test] fn reset_code() { - let mut a = Account::new_contract(69.into(), 0.into(), KECCAK_NULL_RLP); + let mut a = Account::new_contract(69.into(), 0.into(), 0.into(), KECCAK_NULL_RLP); let mut db = new_memory_db(); - let mut db = AccountDBMut::new(&mut db, &Address::new()); + let mut db = AccountDBMut::from_hash(&mut db, keccak(&Address::zero())); a.init_code(vec![0x55, 0x44, 0xffu8]); assert_eq!(a.code_filth, Filth::Dirty); a.commit_code(&mut db); assert_eq!(a.code_filth, Filth::Clean); - assert_eq!(a.code_hash(), "af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb".into()); + assert_eq!(a.code_hash(), H256::from_str("af231e631776a517ca23125370d542873eca1fb4d613ed9b5d5335a46ae5b7eb").unwrap()); a.reset_code(vec![0x55]); assert_eq!(a.code_filth, Filth::Dirty); a.commit_code(&mut db); - assert_eq!(a.code_hash(), "37bf2238b11b68cdc8382cece82651b59d3c3988873b6e0f33d79694aa45f1be".into()); + assert_eq!(a.code_hash(), H256::from_str("37bf2238b11b68cdc8382cece82651b59d3c3988873b6e0f33d79694aa45f1be").unwrap()); } #[test] fn rlpio() { - let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); + let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new(), 0.into()); let b = Account::from_rlp(&a.rlp()).unwrap(); assert_eq!(a.balance(), b.balance()); assert_eq!(a.nonce(), b.nonce()); @@ -734,7 +766,7 @@ mod tests { #[test] fn new_account() { - let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new()); + let a = Account::new(69u8.into(), 0u8.into(), HashMap::new(), Bytes::new(), 0.into()); assert_eq!(a.rlp().to_hex(), "f8448045a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); assert_eq!(*a.balance(), 69u8.into()); assert_eq!(*a.nonce(), 0u8.into()); diff --git a/ethcore/src/state/backend.rs b/ethcore/account-state/src/backend.rs similarity index 72% rename from ethcore/src/state/backend.rs rename to ethcore/account-state/src/backend.rs index 11e73edb3a..5607119d1e 100644 --- a/ethcore/src/state/backend.rs +++ b/ethcore/account-state/src/backend.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,25 +21,25 @@ //! should become general over time to the point where not even a //! merkle trie is strictly necessary. -use std::collections::{HashSet, HashMap}; +use std::collections::HashSet; use std::sync::Arc; -use state::Account; -use parking_lot::Mutex; use ethereum_types::{Address, H256}; -use memory_db::MemoryDB; -use hash_db::{AsHashDB, HashDB}; +use hash_db::{AsHashDB, EMPTY_PREFIX, HashDB, Prefix}; use kvdb::DBValue; +use memory_db::{HashKey, MemoryDB}; +use parking_lot::Mutex; use keccak_hasher::KeccakHasher; -use journaldb::AsKeyedHashDB; + +use crate::account::Account; /// State backend. See module docs for more details. pub trait Backend: Send { /// Treat the backend as a read-only hashdb. - fn as_hash_db(&self) -> &HashDB; + fn as_hash_db(&self) -> &dyn HashDB; /// Treat the backend as a writeable hashdb. - fn as_hash_db_mut(&mut self) -> &mut HashDB; + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB; /// Add an account entry to the cache. fn add_to_account_cache(&mut self, addr: Address, data: Option, modified: bool); @@ -78,49 +78,45 @@ pub trait Backend: Send { // TODO: when account lookup moved into backends, this won't rely as tenuously on intended // usage. #[derive(Clone, PartialEq)] -pub struct ProofCheck(MemoryDB); +pub struct ProofCheck(MemoryDB, DBValue>); impl ProofCheck { /// Create a new `ProofCheck` backend from the given state items. pub fn new(proof: &[DBValue]) -> Self { let mut db = journaldb::new_memory_db(); - for item in proof { db.insert(item); } + for item in proof { db.insert(EMPTY_PREFIX, item); } ProofCheck(db) } } -impl journaldb::KeyedHashDB for ProofCheck { - fn keys(&self) -> HashMap { self.0.keys() } -} - impl HashDB for ProofCheck { - fn get(&self, key: &H256) -> Option { - self.0.get(key) + fn get(&self, key: &H256, prefix: Prefix) -> Option { + self.0.get(key, prefix) } - fn contains(&self, key: &H256) -> bool { - self.0.contains(key) + fn contains(&self, key: &H256, prefix: Prefix) -> bool { + self.0.contains(key, prefix) } - fn insert(&mut self, value: &[u8]) -> H256 { - self.0.insert(value) + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { + self.0.insert(prefix, value) } - fn emplace(&mut self, key: H256, value: DBValue) { - self.0.emplace(key, value) + fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { + self.0.emplace(key, prefix, value) } - fn remove(&mut self, _key: &H256) { } + fn remove(&mut self, _key: &H256, _prefix: Prefix) { } } impl AsHashDB for ProofCheck { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl Backend for ProofCheck { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } fn add_to_account_cache(&mut self, _addr: Address, _data: Option, _modified: bool) {} fn cache_code(&self, _hash: H256, _code: Arc>) {} fn get_cached_account(&self, _addr: &Address) -> Option> { None } @@ -141,62 +137,50 @@ impl Backend for ProofCheck { /// This doesn't cache anything or rely on the canonical state caches. pub struct Proving { base: H, // state we're proving values from. - changed: MemoryDB, // changed state via insertions. + changed: MemoryDB, DBValue>, // changed state via insertions. proof: Mutex>, } -impl AsKeyedHashDB for Proving { - fn as_keyed_hash_db(&self) -> &journaldb::KeyedHashDB { self } -} - impl + Send + Sync> AsHashDB for Proving { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } -} - -impl journaldb::KeyedHashDB for Proving { - fn keys(&self) -> HashMap { - let mut keys = self.base.as_keyed_hash_db().keys(); - keys.extend(self.changed.keys()); - keys - } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl + Send + Sync> HashDB for Proving { - fn get(&self, key: &H256) -> Option { - match self.base.as_hash_db().get(key) { + fn get(&self, key: &H256, prefix: Prefix) -> Option { + match self.base.as_hash_db().get(key, prefix) { Some(val) => { self.proof.lock().insert(val.clone()); Some(val) } - None => self.changed.get(key) + None => self.changed.get(key, prefix) } } - fn contains(&self, key: &H256) -> bool { - self.get(key).is_some() + fn contains(&self, key: &H256, prefix: Prefix) -> bool { + self.get(key, prefix).is_some() } - fn insert(&mut self, value: &[u8]) -> H256 { - self.changed.insert(value) + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { + self.changed.insert(prefix, value) } - fn emplace(&mut self, key: H256, value: DBValue) { - self.changed.emplace(key, value) + fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { + self.changed.emplace(key, prefix, value) } - fn remove(&mut self, key: &H256) { + fn remove(&mut self, key: &H256, prefix: Prefix) { // only remove from `changed` - if self.changed.contains(key) { - self.changed.remove(key) + if self.changed.contains(key, prefix) { + self.changed.remove(key, prefix) } } } impl + Send + Sync> Backend for Proving { - fn as_hash_db(&self) -> &HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } fn add_to_account_cache(&mut self, _: Address, _: Option, _: bool) { } @@ -220,7 +204,7 @@ impl> Proving { /// This will store all values ever fetched from that base. pub fn new(base: H) -> Self { Proving { - base: base, + base, changed: journaldb::new_memory_db(), proof: Mutex::new(HashSet::new()), } @@ -248,11 +232,11 @@ impl + Clone> Clone for Proving { pub struct Basic(pub H); impl + Send + Sync> Backend for Basic { - fn as_hash_db(&self) -> &HashDB { + fn as_hash_db(&self) -> &dyn HashDB { self.0.as_hash_db() } - fn as_hash_db_mut(&mut self) -> &mut HashDB { + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self.0.as_hash_db_mut() } diff --git a/ethcore/account-state/src/lib.rs b/ethcore/account-state/src/lib.rs new file mode 100644 index 0000000000..b7a8c421db --- /dev/null +++ b/ethcore/account-state/src/lib.rs @@ -0,0 +1,33 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Account state +//! This crate contains code used to create, convert, and update Accounts and the code and storage +//! associated with it. It also defines the trait used to construct a backend to build a complete +//! caching state database. +//! Note: the code that needs access to `ethcore` types such as `Machine` and `Executive` is found in +//! the `executive_state` module in `ethcore`. Most tests for the `State` module in this crate are +//! also found in `executive_state` (for the same reason). + +pub mod account; +pub mod backend; +pub mod state; + +pub use { + account::Account, + backend::Backend, + state::{State, CleanupMode}, +}; diff --git a/ethcore/account-state/src/state.rs b/ethcore/account-state/src/state.rs new file mode 100644 index 0000000000..6155b7f09f --- /dev/null +++ b/ethcore/account-state/src/state.rs @@ -0,0 +1,1163 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! A mutable state representation suitable to execute transactions. +//! Generic over a `Backend`. Deals with `Account`s. +//! Unconfirmed sub-states are managed with `checkpoint`s which may be canonicalized +//! or rolled back. + +// NOTE: state tests are found in ethcore/src/executive_state.rs + +use std::{ + cell::{RefCell, RefMut}, + collections::{BTreeMap, BTreeSet, HashMap, HashSet}, + collections::hash_map::Entry, + sync::Arc, + fmt, +}; + +use common_types::{ + state_diff::StateDiff, + basic_account::BasicAccount, + errors::EthcoreError as Error, +}; +use ethereum_types::{Address, H256, U256}; +use ethtrie::{TrieDB, Result as TrieResult}; +use trie_vm_factories::{Factories, VmFactory}; +use hash_db::HashDB; +use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP}; +use keccak_hasher::KeccakHasher; +use kvdb::DBValue; +use log::{warn, trace}; +use parity_bytes::Bytes; +use pod::{self, PodAccount, PodState}; +use trie_db::{Trie, TrieError, Recorder}; + +use crate::{ + account::Account, + backend::Backend, +}; + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +/// Account modification state. Used to check if the account was +/// Modified in between commits and overall. +enum AccountState { + /// Account was loaded from disk and never modified in this state object. + CleanFresh, + /// Account was loaded from the global cache and never modified. + CleanCached, + /// Account has been modified and is not committed to the trie yet. + /// This is set if any of the account data is changed, including + /// storage and code. + Dirty, + /// Account was modified and committed to the trie. + Committed, +} + +#[derive(Debug)] +/// In-memory copy of the account data. Holds the optional account +/// and the modification status. +/// Account entry can contain existing (`Some`) or non-existing +/// account (`None`) +struct AccountEntry { + /// Account entry. `None` if account known to be non-existent. + account: Option, + /// Unmodified account balance. + old_balance: Option, + /// Entry state. + state: AccountState, +} + +// Account cache item. Contains account data and +// modification state +impl AccountEntry { + fn is_dirty(&self) -> bool { + self.state == AccountState::Dirty + } + + fn exists_and_is_null(&self) -> bool { + self.account.as_ref().map_or(false, |a| a.is_null()) + } + + /// Clone dirty data into new `AccountEntry`. This includes + /// basic account data and modified storage keys. + /// Returns None if clean. + fn clone_if_dirty(&self) -> Option { + match self.is_dirty() { + true => Some(self.clone_dirty()), + false => None, + } + } + + /// Clone dirty data into new `AccountEntry`. This includes + /// basic account data and modified storage keys. + fn clone_dirty(&self) -> AccountEntry { + AccountEntry { + old_balance: self.old_balance, + account: self.account.as_ref().map(Account::clone_dirty), + state: self.state, + } + } + + // Create a new account entry and mark it as dirty. + fn new_dirty(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account, + state: AccountState::Dirty, + } + } + + // Create a new account entry and mark it as clean. + fn new_clean(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account, + state: AccountState::CleanFresh, + } + } + + // Create a new account entry and mark it as clean and cached. + fn new_clean_cached(account: Option) -> AccountEntry { + AccountEntry { + old_balance: account.as_ref().map(|a| a.balance().clone()), + account, + state: AccountState::CleanCached, + } + } + + // Replace data with another entry but preserve storage cache. + fn overwrite_with(&mut self, other: AccountEntry) { + self.state = other.state; + match other.account { + Some(acc) => { + if let Some(ref mut ours) = self.account { + ours.overwrite_with(acc); + } else { + self.account = Some(acc); + } + }, + None => self.account = None, + } + } +} + +/// Representation of the entire state of all accounts in the system. +/// +/// `State` can work together with `StateDB` to share account cache. +/// +/// Local cache contains changes made locally and changes accumulated +/// locally from previous commits. Global cache reflects the database +/// state and never contains any changes. +/// +/// Cache items contains account data, or the flag that account does not exist +/// and modification state (see `AccountState`) +/// +/// Account data can be in the following cache states: +/// * In global but not local - something that was queried from the database, +/// but never modified +/// * In local but not global - something that was just added (e.g. new account) +/// * In both with the same value - something that was changed to a new value, +/// but changed back to a previous block in the same block (same State instance) +/// * In both with different values - something that was overwritten with a +/// new value. +/// +/// All read-only state queries check local cache/modifications first, +/// then global state cache. If data is not found in any of the caches +/// it is loaded from the DB to the local cache. +/// +/// **** IMPORTANT ************************************************************* +/// All the modifications to the account data must set the `Dirty` state in the +/// `AccountEntry`. This is done in `require` and `require_or_from`. So just +/// use that. +/// **************************************************************************** +/// +/// Upon destruction all the local cache data propagated into the global cache. +/// Propagated items might be rejected if current state is non-canonical. +/// +/// State checkpointing. +/// +/// A new checkpoint can be created with `checkpoint()`. checkpoints can be +/// created in a hierarchy. +/// When a checkpoint is active all changes are applied directly into +/// `cache` and the original value is copied into an active checkpoint. +/// Reverting a checkpoint with `revert_to_checkpoint` involves copying +/// original values from the latest checkpoint back into `cache`. The code +/// takes care not to overwrite cached storage while doing that. +/// A checkpoint can be discarded with `discard_checkpoint`. All of the original +/// backed-up values are moved into a parent checkpoint (if any). +/// +pub struct State { + db: B, + root: H256, + cache: RefCell>, + // The original account is preserved in + checkpoints: RefCell>>>, + account_start_nonce: U256, + factories: Factories, +} + +#[derive(Copy, Clone)] +enum RequireCache { + None, + CodeSize, + Code, +} + +/// Mode of dealing with null accounts. +#[derive(Debug, PartialEq)] +pub enum CleanupMode<'a> { + /// Create accounts which would be null. + ForceCreate, + /// Don't delete null accounts upon touching, but also don't create them. + NoEmpty, + /// Mark all touched accounts. + TrackTouched(&'a mut HashSet
), +} + +/// Provides subset of `State` methods to query state information +pub trait StateInfo { + /// Get the nonce of account `a`. + fn nonce(&self, a: &Address) -> TrieResult; + + /// Get the balance of account `a`. + fn balance(&self, a: &Address) -> TrieResult; + + /// Mutate storage of account `address` so that it is `value` for `key`. + fn storage_at(&self, address: &Address, key: &H256) -> TrieResult; + + /// Get accounts' code. + fn code(&self, a: &Address) -> TrieResult>>; +} + +impl StateInfo for State { + fn nonce(&self, a: &Address) -> TrieResult { State::nonce(self, a) } + fn balance(&self, a: &Address) -> TrieResult { State::balance(self, a) } + fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { State::storage_at(self, address, key) } + fn code(&self, address: &Address) -> TrieResult>> { State::code(self, address) } +} + +const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ + Therefore creating a SecTrieDB with this state's root will not fail."; + +impl State { + /// Creates new state with empty state root + /// Used for tests. + pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State { + let mut root = H256::zero(); + { + // init trie and reset root to null + let _ = factories.trie.create(db.as_hash_db_mut(), &mut root); + } + + State { + db, + root, + cache: RefCell::new(HashMap::new()), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce, + factories, + } + } + + /// Creates new state with existing state root + pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> TrieResult> { + if !db.as_hash_db().contains(&root, hash_db::EMPTY_PREFIX) { + return Err(Box::new(TrieError::InvalidStateRoot(root))); + } + + let state = State { + db, + root, + cache: RefCell::new(HashMap::new()), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce, + factories, + }; + + Ok(state) + } + + /// Get a VM factory that can execute on this state. + pub fn vm_factory(&self) -> VmFactory { + self.factories.vm.clone() + } + + /// Create a recoverable checkpoint of this state. Return the checkpoint index. + pub fn checkpoint(&mut self) -> usize { + let checkpoints = self.checkpoints.get_mut(); + let index = checkpoints.len(); + checkpoints.push(HashMap::new()); + index + } + + /// Merge last checkpoint with previous. + pub fn discard_checkpoint(&mut self) { + // merge with previous checkpoint + let last = self.checkpoints.get_mut().pop(); + if let Some(mut checkpoint) = last { + if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() { + if prev.is_empty() { + **prev = checkpoint; + } else { + for (k, v) in checkpoint.drain() { + prev.entry(k).or_insert(v); + } + } + } + } + } + + /// Revert to the last checkpoint and discard it. + pub fn revert_to_checkpoint(&mut self) { + if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() { + for (k, v) in checkpoint.drain() { + match v { + Some(v) => { + match self.cache.get_mut().entry(k) { + Entry::Occupied(mut e) => { + // Merge checkpointed changes back into the main account + // storage preserving the cache. + e.get_mut().overwrite_with(v); + }, + Entry::Vacant(e) => { + e.insert(v); + } + } + }, + None => { + if let Entry::Occupied(e) = self.cache.get_mut().entry(k) { + if e.get().is_dirty() { + e.remove(); + } + } + } + } + } + } + } + + fn insert_cache(&self, address: &Address, account: AccountEntry) { + // Dirty account which is not in the cache means this is a new account. + // It goes directly into the checkpoint as there's nothing to rever to. + // + // In all other cases account is read as clean first, and after that made + // dirty in and added to the checkpoint with `note_cache`. + let is_dirty = account.is_dirty(); + let old_value = self.cache.borrow_mut().insert(*address, account); + if is_dirty { + if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { + checkpoint.entry(*address).or_insert(old_value); + } + } + } + + fn note_cache(&self, address: &Address) { + if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { + checkpoint.entry(*address) + .or_insert_with(|| self.cache.borrow().get(address).map(AccountEntry::clone_dirty)); + } + } + + /// Destroy the current object and return root and database. + pub fn drop(mut self) -> (H256, B) { + self.propagate_to_global_cache(); + (self.root, self.db) + } + + /// Destroy the current object and return single account data. + pub fn into_account(self, account: &Address) -> TrieResult<(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 + } + + /// Create a new contract at address `contract`. If there is already an account at the address + /// it will have its code reset, ready for `init_code()`. + pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256, version: U256) -> TrieResult<()> { + let original_storage_root = self.original_storage_root(contract)?; + let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); + if overflow { + return Err(Box::new(TrieError::DecoderError(H256::from(*contract), rlp::DecoderError::Custom("Nonce overflow".into())))); + } + self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, version, original_storage_root)))); + Ok(()) + } + + /// Remove an existing account. + pub fn kill_account(&mut self, account: &Address) { + self.insert_cache(account, AccountEntry::new_dirty(None)); + } + + /// Determine whether an account exists. + pub fn exists(&self, a: &Address) -> TrieResult { + // Bloom filter does not contain empty accounts, so it is important here to + // check if account exists in the database directly before EIP-161 is in effect. + self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) + } + + /// Determine whether an account exists and if not empty. + pub fn exists_and_not_null(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) + } + + /// Determine whether an account exists and has code or non-zero nonce. + pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::CodeSize, false, + |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) + } + + /// Get the balance of account `a`. + pub fn balance(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) + } + + /// Get the nonce of account `a`. + pub fn nonce(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) + } + + /// Whether the base storage root of an account remains unchanged. + pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult { + Ok(self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))? + .unwrap_or(true)) + } + + /// Get the storage root of account `a`. + pub fn storage_root(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().and_then(|account| account.storage_root())) + } + + /// Get the original storage root since last commit of account `a`. + pub fn original_storage_root(&self, a: &Address) -> TrieResult { + Ok(self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|account| account.original_storage_root()))? + .unwrap_or(KECCAK_NULL_RLP)) + } + + + /// Get the value of storage at a specific checkpoint. + pub fn checkpoint_storage_at(&self, start_checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult> { + #[must_use] + enum ReturnKind { + /// Use original storage at value at this address. + OriginalAt, + /// The checkpoint storage value is the same as the checkpoint storage value at the next checkpoint. + SameAsNext, + } + + let kind = { + let checkpoints = self.checkpoints.borrow(); + + if start_checkpoint_index >= checkpoints.len() { + // The checkpoint was not found. Return None. + return Ok(None); + } + + let mut kind = None; + + for checkpoint in checkpoints.iter().skip(start_checkpoint_index) { + match checkpoint.get(address) { + // The account exists at this checkpoint. + Some(Some(AccountEntry { account: Some(ref account), .. })) => { + if let Some(value) = account.cached_storage_at(key) { + return Ok(Some(value)); + } else { + // This account has checkpoint entry, but the key is not in the entry's cache. We can use + // original_storage_at if current account's original storage root is the same as checkpoint + // account's original storage root. Otherwise, the account must be a newly created contract. + if account.base_storage_root() == self.original_storage_root(address)? { + kind = Some(ReturnKind::OriginalAt); + break + } else { + // If account base storage root is different from the original storage root since last + // commit, then it can only be created from a new contract, where the base storage root + // would always be empty. Note that this branch is actually never called, because + // `cached_storage_at` handled this case. + warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty."); + return Ok(Some(H256::zero())); + } + } + }, + // The account didn't exist at that point. Return empty value. + Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::zero())), + // The value was not cached at that checkpoint, meaning it was not modified at all. + Some(None) => { + kind = Some(ReturnKind::OriginalAt); + break + }, + // This key does not have a checkpoint entry. + None => { + kind = Some(ReturnKind::SameAsNext); + }, + } + } + + kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed") + }; + + match kind { + ReturnKind::SameAsNext => { + // If we reached here, all previous SameAsNext failed to early return. It means that the value we want + // to fetch is the same as current. + Ok(Some(self.storage_at(address, key)?)) + }, + ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)), + } + } + + fn storage_at_inner( + &self, address: &Address, key: &H256, f_cached_at: FCachedStorageAt, f_at: FStorageAt, + ) -> TrieResult where + FCachedStorageAt: Fn(&Account, &H256) -> Option, + FStorageAt: Fn(&Account, &dyn HashDB, &H256) -> TrieResult + { + // Storage key search and update works like this: + // 1. If there's an entry for the account in the local cache check for the key and return it if found. + // 2. If there's an entry for the account in the global cache check for the key or load it into that account. + // 3. If account is missing in the global cache load it into the local cache and cache the key there. + + { + // check local cache first without updating + let local_cache = self.cache.borrow_mut(); + let mut local_account = None; + if let Some(maybe_acc) = local_cache.get(address) { + match maybe_acc.account { + Some(ref account) => { + if let Some(value) = f_cached_at(account, key) { + return Ok(value); + } else { + local_account = Some(maybe_acc); + } + }, + _ => return Ok(H256::zero()), + } + } + // check the global cache and and cache storage key there if found, + let trie_res = self.db.get_cached(address, |acc| match acc { + None => Ok(H256::zero()), + Some(a) => { + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); + f_at(a, account_db.as_hash_db(), key) + } + }); + + if let Some(res) = trie_res { + return res; + } + + // otherwise cache the account locally and cache storage key there. + if let Some(ref mut acc) = local_account { + if let Some(ref account) = acc.account { + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(address)); + return f_at(account, account_db.as_hash_db(), key) + } else { + return Ok(H256::zero()) + } + } + } + + // check if the account could exist before any requests to trie + if self.db.is_known_null(address) { return Ok(H256::zero()) } + + // account is not found in the global cache, get from the DB and insert into local + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let maybe_acc = db.get_with(address.as_bytes(), from_rlp)?; + let r = maybe_acc.as_ref().map_or(Ok(H256::zero()), |a| { + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); + f_at(a, account_db.as_hash_db(), key) + }); + self.insert_cache(address, AccountEntry::new_clean(maybe_acc)); + r + } + + /// Mutate storage of account `address` so that it is `value` for `key`. + pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { + self.storage_at_inner( + address, + key, + |account, key| { account.cached_storage_at(key) }, + |account, db, key| { account.storage_at(db, key) }, + ) + } + + /// Get the value of storage after last state commitment. + pub fn original_storage_at(&self, address: &Address, key: &H256) -> TrieResult { + self.storage_at_inner( + address, + key, + |account, key| { account.cached_original_storage_at(key) }, + |account, db, key| { account.original_storage_at(db, key) }, + ) + } + + /// Get accounts' code. + pub fn code(&self, a: &Address) -> TrieResult>> { + self.ensure_cached(a, RequireCache::Code, true, + |a| a.as_ref().map_or(None, |a| a.code().clone())) + } + + /// Get an account's code hash. + pub fn code_hash(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|a| a.code_hash())) + } + + /// Get an account's code version. + pub fn code_version(&self, a: &Address) -> TrieResult { + self.ensure_cached(a, RequireCache::None, true, + |a| a.as_ref().map(|a| *a.code_version()).unwrap_or(U256::zero())) + } + + /// Get accounts' code size. + pub fn code_size(&self, a: &Address) -> TrieResult> { + self.ensure_cached(a, RequireCache::CodeSize, true, + |a| a.as_ref().and_then(|a| a.code_size())) + } + + /// Add `incr` to the balance of account `a`. + pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> TrieResult<()> { + trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); + let is_value_transfer = !incr.is_zero(); + if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) { + self.require(a, false)?.add_balance(incr); + } else if let CleanupMode::TrackTouched(set) = cleanup_mode { + if self.exists(a)? { + set.insert(*a); + self.touch(a)?; + } + } + Ok(()) + } + + /// Subtract `decr` from the balance of account `a`. + pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> TrieResult<()> { + trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?); + if !decr.is_zero() || !self.exists(a)? { + self.require(a, false)?.sub_balance(decr); + } + if let CleanupMode::TrackTouched(ref mut set) = *cleanup_mode { + set.insert(*a); + } + Ok(()) + } + + /// Subtracts `by` from the balance of `from` and adds it to that of `to`. + pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> TrieResult<()> { + self.sub_balance(from, by, &mut cleanup_mode)?; + self.add_balance(to, by, cleanup_mode)?; + Ok(()) + } + + /// Increment the nonce of account `a` by 1. + pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> { + self.require(a, false).map(|mut x| x.inc_nonce()) + } + + /// Mutate storage of account `a` so that it is `value` for `key`. + pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> { + trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value); + if self.storage_at(a, &key)? != value { + self.require(a, false)?.set_storage(key, value) + } + + Ok(()) + } + + /// Initialise the code of account `a` so that it is `code`. + /// NOTE: Account should have been created with `new_contract`. + pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(),KECCAK_NULL_RLP), |_| {})?.init_code(code); + Ok(()) + } + + /// Reset the code of account `a` so that it is `code`. + pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { + self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, 0.into(), KECCAK_NULL_RLP), |_| {})?.reset_code(code); + Ok(()) + } + + fn touch(&mut self, a: &Address) -> TrieResult<()> { + self.require(a, false)?; + Ok(()) + } + + /// Commits our cached account changes into the trie. + pub fn commit(&mut self) -> Result<(), Error> { + assert!(self.checkpoints.borrow().is_empty()); + // first, commit the sub trees. + let mut accounts = self.cache.borrow_mut(); + for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { + if let Some(ref mut account) = a.account { + let addr_hash = account.address_hash(address); + { + let mut account_db = self.factories.accountdb.create(self.db.as_hash_db_mut(), addr_hash); + account.commit_storage(&self.factories.trie, account_db.as_hash_db_mut())?; + account.commit_code(account_db.as_hash_db_mut()); + } + if !account.is_empty() { + self.db.note_non_null_account(address); + } + } + } + + { + let mut trie = self.factories.trie.from_existing(self.db.as_hash_db_mut(), &mut self.root)?; + for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { + a.state = AccountState::Committed; + match a.account { + Some(ref mut account) => { + trie.insert(address.as_bytes(), &account.rlp())?; + }, + None => { + trie.remove(address.as_bytes())?; + }, + }; + } + } + + Ok(()) + } + + /// Propagate local cache into shared canonical state cache. + fn propagate_to_global_cache(&mut self) { + let mut addresses = self.cache.borrow_mut(); + trace!(target: "state", "Committing cache {:?} entries", addresses.len()); + for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) { + self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed); + } + } + + /// Clear state cache + pub fn clear(&mut self) { + assert!(self.checkpoints.borrow().is_empty()); + self.cache.borrow_mut().clear(); + } + + /// Remove any touched empty or dust accounts. + pub fn kill_garbage(&mut self, touched: &HashSet
, remove_empty_touched: bool, min_balance: &Option, kill_contracts: bool) -> TrieResult<()> { + let to_kill: HashSet<_> = { + self.cache.borrow().iter().filter_map(|(address, ref entry)| + if touched.contains(address) && // Check all touched accounts + ((remove_empty_touched && entry.exists_and_is_null()) // Remove all empty touched accounts. + || min_balance.map_or(false, |ref balance| entry.account.as_ref().map_or(false, |account| + (account.is_basic() || kill_contracts) // Remove all basic and optionally contract accounts where balance has been decreased. + && account.balance() < balance && entry.old_balance.as_ref().map_or(false, |b| account.balance() < b)))) { + + Some(address.clone()) + } else { None }).collect() + }; + for address in to_kill { + self.kill_account(&address); + } + Ok(()) + } + + /// Populate the state from `accounts`. + /// Used for tests. + pub fn populate_from(&mut self, accounts: PodState) { + assert!(self.checkpoints.borrow().is_empty()); + for (add, acc) in accounts.drain().into_iter() { + self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); + } + } + + /// Populate a PodAccount map from this state. + fn to_pod_cache(&self) -> PodState { + assert!(self.checkpoints.borrow().is_empty()); + PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { + if let Some(ref acc) = opt.account { + m.insert(*add, acc.to_pod()); + } + m + })) + } + + /// Populate a PodAccount map from this state. + /// Warning this is not for real time use. + /// Use of this method requires FatDB mode to be able + /// to iterate on accounts. + pub fn to_pod_full(&self) -> Result { + + assert!(self.checkpoints.borrow().is_empty()); + assert!(self.factories.trie.is_fat()); + + let mut result = BTreeMap::new(); + + let db = &self.db.as_hash_db(); + let trie = self.factories.trie.readonly(db, &self.root)?; + + // put trie in cache + for item in trie.iter()? { + if let Ok((addr, _dbval)) = item { + let address = Address::from_slice(&addr); + let _ = self.require(&address, true); + } + } + + // Resolve missing part + for (add, opt) in self.cache.borrow().iter() { + if let Some(ref acc) = opt.account { + let pod_account = self.account_to_pod_account(acc, add)?; + result.insert(add.clone(), pod_account); + } + } + + Ok(PodState::from(result)) + } + + /// Create a PodAccount from an account. + /// Differs from existing method by including all storage + /// values of the account to the PodAccount. + /// This function is only intended for use in small tests or with fresh accounts. + /// It requires FatDB. + fn account_to_pod_account(&self, account: &Account, address: &Address) -> Result { + use ethereum_types::BigEndianHash; + assert!(self.factories.trie.is_fat()); + + let mut pod_storage = BTreeMap::new(); + let addr_hash = account.address_hash(address); + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); + let root = account.base_storage_root(); + + let accountdb = &accountdb.as_hash_db(); + let trie = self.factories.trie.readonly(accountdb, &root)?; + for o_kv in trie.iter()? { + if let Ok((key, val)) = o_kv { + pod_storage.insert( + H256::from_slice(&key[..]), + BigEndianHash::from_uint( + &rlp::decode::(&val[..]).expect("Decoded from trie which was encoded from the same type; qed") + ), + ); + } + } + + let mut pod_account = account.to_pod(); + // cached one first + pod_storage.append(&mut pod_account.storage); + pod_account.storage = pod_storage; + Ok(pod_account) + } + + /// Populate a PodAccount map from this state, with another state as the account and storage query. + fn to_pod_diff(&mut self, query: &State) -> TrieResult { + 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: TrieResult<_>, 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::>() + } + }; + + // Storage must be fetched after ensure_cached to avoid borrow problem. + (*acc.balance(), *acc.nonce(), all_keys, acc.code().map(|x| x.to_vec()), *acc.code_version()) + }) + })?; + + if let Some((balance, nonce, storage_keys, code, version)) = account { + let storage = storage_keys.into_iter().fold(Ok(BTreeMap::new()), |s: TrieResult<_>, key| { + let mut s = s?; + + s.insert(key, self.storage_at(&address, &key)?); + Ok(s) + })?; + + m.insert(address, PodAccount { + balance, nonce, storage, code, version + }); + } + + Ok(m) + })?)) + } + + /// Returns a `StateDiff` describing the difference from `orig` to `self`. + /// Consumes self. + pub fn diff_from(&self, mut orig: State) -> TrieResult { + let pod_state_post = self.to_pod_cache(); + 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. Returns whether the cache succeeds. + #[must_use] + fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &dyn HashDB) -> bool { + if let RequireCache::None = require { + return true; + } + + if account.is_cached() { + return true; + } + + // if there's already code in the global cache, always cache it localy + let hash = account.code_hash(); + match state_db.get_cached_code(&hash) { + Some(code) => { + account.cache_given_code(code); + true + }, + None => match require { + RequireCache::None => true, + RequireCache::Code => { + if let Some(code) = account.cache_code(db) { + // propagate code loaded from the database to + // the global code cache. + state_db.cache_code(hash, code); + true + } else { + false + } + }, + RequireCache::CodeSize => { + account.cache_code_size(db) + } + } + } + } + + /// Check caches for required data + /// First searches for account in the local, then the shared cache. + /// Populates local cache if nothing found. + fn ensure_cached(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> TrieResult + where F: Fn(Option<&Account>) -> U { + // check local cache first + if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { + if let Some(ref mut account) = maybe_acc.account { + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); + if Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Ok(f(Some(account))); + } else { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); + } + } + return Ok(f(None)); + } + // check global cache + let result = self.db.get_cached(a, |mut acc| { + if let Some(ref mut account) = acc { + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); + if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); + } + } + Ok(f(acc.map(|a| &*a))) + }); + match result { + Some(r) => Ok(r?), + None => { + // first check if it is not in database for sure + if check_null && self.db.is_known_null(a) { return Ok(f(None)); } + + // not found in the global cache, get from the DB and insert into local + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root)?; + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let mut maybe_acc = db.get_with(a.as_bytes(), from_rlp)?; + if let Some(ref mut account) = maybe_acc.as_mut() { + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); + if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))); + } + } + let r = f(maybe_acc.as_ref()); + self.insert_cache(a, AccountEntry::new_clean(maybe_acc)); + Ok(r) + } + } + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + pub fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { + self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {}) + } + + /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. + /// If it doesn't exist, make account equal the evaluation of `default`. + pub fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> + where F: FnOnce() -> Account, G: FnOnce(&mut Account), + { + let contains_key = self.cache.borrow().contains_key(a); + if !contains_key { + match self.db.get_cached_account(a) { + Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), + None => { + let maybe_acc = if !self.db.is_known_null(a) { + let db = &self.db.as_hash_db(); + let db = self.factories.trie.readonly(db, &self.root)?; + let from_rlp = |b:&[u8]| { Account::from_rlp(b).expect("decoding db value failed") }; + AccountEntry::new_clean(db.get_with(a.as_bytes(), from_rlp)?) + } else { + AccountEntry::new_clean(None) + }; + self.insert_cache(a, maybe_acc); + } + } + } + self.note_cache(a); + + // at this point the entry is guaranteed to be in the cache. + let mut account = RefMut::map(self.cache.borrow_mut(), |c| { + let entry = c.get_mut(a).expect("entry known to exist in the cache; qed"); + + match &mut entry.account { + &mut Some(ref mut acc) => not_default(acc), + slot => *slot = Some(default()), + } + + // set the dirty flag after changing account data. + entry.state = AccountState::Dirty; + entry.account.as_mut().expect("Required account must always exist; qed") + }); + + if require_code { + let addr_hash = account.address_hash(a); + let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); + + if !Self::update_account_cache(RequireCache::Code, &mut account, &self.db, accountdb.as_hash_db()) { + return Err(Box::new(TrieError::IncompleteDatabase(H256::from(*a)))) + } + } + + Ok(account) + } + + /// Replace account code and storage. Creates account if it does not exist. + pub fn patch_account(&self, a: &Address, code: Arc, storage: HashMap) -> TrieResult<()> { + Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) + } +} + +// State proof implementations; useful for light client protocols. +impl State { + /// Prove an account's existence or nonexistence in the state trie. + /// Returns a merkle proof of the account's trie node omitted or an encountered trie error. + /// If the account doesn't exist in the trie, prove that and return defaults. + /// Requires a secure trie to be used for accurate results. + /// `account_key` == keccak(address) + pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec, BasicAccount)> { + let mut recorder = Recorder::new(); + let db = &self.db.as_hash_db(); + let trie = TrieDB::new(db, &self.root)?; + let maybe_account: Option = { + let panicky_decoder = |bytes: &[u8]| { + ::rlp::decode(bytes).unwrap_or_else(|_| panic!("prove_account, could not query trie for account key={}", &account_key)) + }; + let query = (&mut recorder, panicky_decoder); + trie.get_with(account_key.as_bytes(), query)? + }; + let account = maybe_account.unwrap_or_else(|| BasicAccount { + balance: 0.into(), + nonce: self.account_start_nonce, + code_hash: KECCAK_EMPTY, + storage_root: KECCAK_NULL_RLP, + code_version: 0.into(), + }); + + Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) + } + + /// Prove an account's storage key's existence or nonexistence in the state. + /// Returns a merkle proof of the account's storage trie. + /// Requires a secure trie to be used for correctness. + /// `account_key` == keccak(address) + /// `storage_key` == keccak(key) + pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> TrieResult<(Vec, H256)> { + // TODO: probably could look into cache somehow but it's keyed by + // address, not keccak(address). + let db = &self.db.as_hash_db(); + let trie = TrieDB::new(db, &self.root)?; + let from_rlp = |b: &[u8]| Account::from_rlp(b).expect("decoding db value failed"); + let acc = match trie.get_with(account_key.as_bytes(), from_rlp)? { + Some(acc) => acc, + None => return Ok((Vec::new(), H256::zero())), + }; + + let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account_key); + acc.prove_storage(account_db.as_hash_db(), storage_key) + } +} + +impl fmt::Debug for State { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.cache.borrow()) + } +} + +impl State { + /// Get a reference to the underlying state DB. + pub fn db(&self) -> &B { + &self.db + } +} + +//// TODO: cloning for `State` shouldn't be possible in general; Remove this and use +//// checkpoints where possible. +impl Clone for State { + fn clone(&self) -> State { + let cache = { + let mut cache: HashMap = HashMap::new(); + for (key, val) in self.cache.borrow().iter() { + if let Some(entry) = val.clone_if_dirty() { + cache.insert(key.clone(), entry); + } + } + cache + }; + + State { + db: self.db.clone(), + root: self.root.clone(), + cache: RefCell::new(cache), + checkpoints: RefCell::new(Vec::new()), + account_start_nonce: self.account_start_nonce.clone(), + factories: self.factories.clone(), + } + } +} diff --git a/ethcore/benches/builtin.rs b/ethcore/benches/builtin.rs index d7ed483dd0..b518b8d507 100644 --- a/ethcore/benches/builtin.rs +++ b/ethcore/benches/builtin.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,21 +20,23 @@ extern crate criterion; #[macro_use] extern crate lazy_static; +extern crate machine; extern crate ethcore; +extern crate ethcore_builtin; extern crate ethereum_types; extern crate parity_bytes as bytes; extern crate rustc_hex; use criterion::{Criterion, Bencher}; use bytes::BytesRef; -use ethcore::builtin::Builtin; -use ethcore::machine::EthereumMachine; -use ethereum_types::U256; -use ethcore::ethereum::new_byzantium_test_machine; +use ethcore_builtin::Builtin; +use ethereum_types::H160; +use machine::Machine; +use machine::test_helpers::new_byzantium_test_machine; use rustc_hex::FromHex; lazy_static! { - static ref BYZANTIUM_MACHINE: EthereumMachine = new_byzantium_test_machine(); + static ref BYZANTIUM_MACHINE: Machine = new_byzantium_test_machine(); } struct BuiltinBenchmark<'a> { @@ -46,8 +48,9 @@ struct BuiltinBenchmark<'a> { impl<'a> BuiltinBenchmark<'a> { fn new(builtin_address: &'static str, input: &str, expected: &str) -> BuiltinBenchmark<'a> { let builtins = BYZANTIUM_MACHINE.builtins(); - - let builtin = builtins.get(&builtin_address.into()).unwrap().clone(); + use std::str::FromStr; + let addr = H160::from_str(builtin_address).unwrap(); + let builtin = builtins.get(&addr).unwrap().clone(); let input = FromHex::from_hex(input).unwrap(); let expected = FromHex::from_hex(expected).unwrap(); @@ -56,10 +59,6 @@ impl<'a> BuiltinBenchmark<'a> { } } - fn gas_cost(&self) -> U256 { - self.builtin.cost(&self.input) - } - fn run(&self, b: &mut Bencher) { let mut output = vec![0; self.expected.len()]; diff --git a/ethcore/block-gas-limit/Cargo.toml b/ethcore/block-gas-limit/Cargo.toml new file mode 100644 index 0000000000..1e34dac7cc --- /dev/null +++ b/ethcore/block-gas-limit/Cargo.toml @@ -0,0 +1,20 @@ +[package] +description = "A crate to interact with the block gas limit contract" +name = "block-gas-limit" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +client-traits = { path = "../client-traits" } +common-types = { path = "../types" } +ethabi = "9.0.1" +ethabi-derive = "9.0.1" +ethabi-contract = "9.0.0" +ethereum-types = "0.8.0" +log = "0.4" + +[dev-dependencies] +ethcore = { path = "..", features = ["test-helpers"] } +spec = { path = "../spec" } diff --git a/ethcore/block-gas-limit/res/block_gas_limit.json b/ethcore/block-gas-limit/res/block_gas_limit.json new file mode 100644 index 0000000000..bb2671b457 --- /dev/null +++ b/ethcore/block-gas-limit/res/block_gas_limit.json @@ -0,0 +1,16 @@ +[ + { + "constant": true, + "inputs": [], + "name": "blockGasLimit", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/ethcore/block-gas-limit/src/lib.rs b/ethcore/block-gas-limit/src/lib.rs new file mode 100644 index 0000000000..c83689816c --- /dev/null +++ b/ethcore/block-gas-limit/src/lib.rs @@ -0,0 +1,39 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! A client interface for interacting with the block gas limit contract. + +use client_traits::BlockChainClient; +use common_types::{header::Header, ids::BlockId}; +use ethabi::FunctionOutputDecoder; +use ethabi_contract::use_contract; +use ethereum_types::{Address, U256}; +use log::{debug, error}; + +use_contract!(contract, "res/block_gas_limit.json"); + +pub fn block_gas_limit(full_client: &dyn BlockChainClient, header: &Header, address: Address) -> Option { + let (data, decoder) = contract::functions::block_gas_limit::call(); + let value = full_client.call_contract(BlockId::Hash(*header.parent_hash()), address, data).map_err(|err| { + error!(target: "block_gas_limit", "Contract call failed. Not changing the block gas limit. {:?}", err); + }).ok()?; + if value.is_empty() { + debug!(target: "block_gas_limit", "Contract call returned nothing. Not changing the block gas limit."); + None + } else { + decoder.decode(&value).ok() + } +} diff --git a/ethcore/block-reward/Cargo.toml b/ethcore/block-reward/Cargo.toml new file mode 100644 index 0000000000..7ba774b819 --- /dev/null +++ b/ethcore/block-reward/Cargo.toml @@ -0,0 +1,22 @@ +[package] +description = "A crate to interact with the block rewards contract." +name = "block-reward" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +common-types = { path = "../types" } +engine = { path = "../engine" } +ethabi = "9.0.1" +ethabi-derive = "9.0.1" +ethabi-contract = "9.0.0" +ethereum-types = "0.8.0" +keccak-hash = "0.4.0" +machine = { path = "../machine" } +trace = { path = "../trace" } + +[dev-dependencies] +ethcore = { path = "..", features = ["test-helpers"] } +spec = { path = "../spec" } diff --git a/ethcore/res/contracts/block_reward.json b/ethcore/block-reward/res/block_reward.json similarity index 100% rename from ethcore/res/contracts/block_reward.json rename to ethcore/block-reward/res/block_reward.json diff --git a/ethcore/src/engines/block_reward.rs b/ethcore/block-reward/src/lib.rs similarity index 65% rename from ethcore/src/engines/block_reward.rs rename to ethcore/block-reward/src/lib.rs index 58b55408eb..ca7ab95138 100644 --- a/ethcore/src/engines/block_reward.rs +++ b/ethcore/block-reward/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,24 +14,25 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! A module with types for declaring block rewards and a client interface for interacting with a +//! 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 std::sync::Arc; -use hash::keccak; -use error::Error; -use machine::Machine; + +use ethabi::FunctionOutputDecoder; +use ethabi_contract::use_contract; +use ethereum_types::{Address, U256}; +use common_types::{ + BlockNumber, + errors::{EngineError, EthcoreError as Error}, +}; +use keccak_hash::keccak; +use machine::{Machine, ExecutedBlock}; +use engine::{SystemOrCodeCall, SystemOrCodeCallKind}; use trace; -use types::BlockNumber; -use super::{SystemOrCodeCall, SystemOrCodeCallKind}; use trace::{Tracer, ExecutiveTracer, Tracing}; -use block::ExecutedBlock; -use_contract!(block_reward_contract, "res/contracts/block_reward.json"); +use_contract!(block_reward_contract, "res/block_reward.json"); /// The kind of block reward. /// Depending on the consensus engine the allocated block reward might have @@ -110,54 +111,37 @@ impl BlockRewardContract { /// `machine.execute_as_system`). pub fn reward( &self, - beneficiaries: &[(Address, RewardKind)], + beneficiaries: Vec<(Address, RewardKind)>, caller: &mut SystemOrCodeCall, ) -> Result, Error> { - let input = block_reward_contract::functions::reward::encode_input( - beneficiaries.iter().map(|&(address, _)| H160::from(address)), - beneficiaries.iter().map(|&(_, ref reward_kind)| u16::from(*reward_kind)), - ); + let (addresses, rewards): (Vec<_>, Vec<_>) = beneficiaries.into_iter().unzip(); + let (input, decoder) = block_reward_contract::functions::reward::call(addresses, rewards.into_iter().map(u16::from)); let output = caller(self.kind.clone(), 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))), - ]; + .map_err(EngineError::FailedSystemCall)?; - let tokens = ethabi::decode(types, &output) + let (addresses, rewards) = decoder.decode(&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"); + .map_err(EngineError::FailedSystemCall)?; if addresses.len() != rewards.len() { - return Err(::engines::EngineError::FailedSystemCall( + return Err(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()) + Ok(addresses.into_iter().zip(rewards.into_iter()).collect()) } } /// Applies the given block rewards, i.e. adds the given balance to each beneficiary' address. /// If tracing is enabled the operations are recorded. -pub fn apply_block_rewards( +pub fn apply_block_rewards( rewards: &[(Address, RewardKind, U256)], block: &mut ExecutedBlock, - machine: &M, -) -> Result<(), M::Error> { + machine: &Machine, +) -> Result<(), Error> { for &(ref author, _, ref block_reward) in rewards { machine.add_balance(block, author, block_reward)?; } @@ -177,28 +161,31 @@ pub fn apply_block_rewards( #[cfg(test)] mod test { - use client::PrepareOpenBlock; - use ethereum_types::U256; - use spec::Spec; - use test_helpers::generate_dummy_client_with_spec; + use std::str::FromStr; + use ethcore::{ + client::PrepareOpenBlock, + test_helpers::generate_dummy_client_with_spec, + }; + use ethereum_types::{U256, Address}; + use engine::SystemOrCodeCallKind; + use spec; - use engines::SystemOrCodeCallKind; - use super::{BlockRewardContract, RewardKind}; + use crate::{BlockRewardContract, RewardKind}; #[test] fn block_reward_contract() { - let client = generate_dummy_client_with_spec(Spec::new_test_round_block_reward_contract); + let client = generate_dummy_client_with_spec(spec::new_test_round_block_reward_contract); - let machine = Spec::new_test_machine(); + let machine = spec::new_test_machine(); // the spec has a block reward contract defined at the given address let block_reward_contract = BlockRewardContract::new_from_address( - "0000000000000000000000000000000000000042".into(), + Address::from_str("0000000000000000000000000000000000000042").unwrap(), ); let mut call = |to, data| { let mut block = client.prepare_open_block( - "0000000000000000000000000000000000000001".into(), + Address::from_str("0000000000000000000000000000000000000001").unwrap(), (3141562.into(), 31415620.into()), vec![], ).unwrap(); @@ -219,20 +206,20 @@ mod test { }; // if no beneficiaries are given no rewards are attributed - assert!(block_reward_contract.reward(&vec![], &mut call).unwrap().is_empty()); + assert!(block_reward_contract.reward(vec![], &mut call).unwrap().is_empty()); // the contract rewards (1000 + kind) for each benefactor let beneficiaries = vec![ - ("0000000000000000000000000000000000000033".into(), RewardKind::Author), - ("0000000000000000000000000000000000000034".into(), RewardKind::Uncle(1)), - ("0000000000000000000000000000000000000035".into(), RewardKind::EmptyStep), + (Address::from_str("0000000000000000000000000000000000000033").unwrap(), RewardKind::Author), + (Address::from_str("0000000000000000000000000000000000000034").unwrap(), RewardKind::Uncle(1)), + (Address::from_str("0000000000000000000000000000000000000035").unwrap(), RewardKind::EmptyStep), ]; - let rewards = block_reward_contract.reward(&beneficiaries, &mut call).unwrap(); + let rewards = block_reward_contract.reward(beneficiaries, &mut call).unwrap(); let expected = vec![ - ("0000000000000000000000000000000000000033".into(), U256::from(1000)), - ("0000000000000000000000000000000000000034".into(), U256::from(1000 + 101)), - ("0000000000000000000000000000000000000035".into(), U256::from(1000 + 2)), + (Address::from_str("0000000000000000000000000000000000000033").unwrap(), U256::from(1000)), + (Address::from_str("0000000000000000000000000000000000000034").unwrap(), U256::from(1000 + 101)), + (Address::from_str("0000000000000000000000000000000000000035").unwrap(), U256::from(1000 + 2)), ]; assert_eq!(expected, rewards); diff --git a/ethcore/blockchain/Cargo.toml b/ethcore/blockchain/Cargo.toml index be434c51e1..ad4dd27418 100644 --- a/ethcore/blockchain/Cargo.toml +++ b/ethcore/blockchain/Cargo.toml @@ -12,24 +12,24 @@ ansi_term = "0.11" blooms-db = { path = "../../util/blooms-db" } common-types = { path = "../types" } ethcore-db = { path = "../db" } -ethereum-types = "0.4" -heapsize = "0.4" +ethereum-types = "0.8.0" +keccak-hash = "0.4.0" +parity-util-mem = "0.3.0" itertools = "0.5" -keccak-hash = "0.1" -kvdb = "0.1" +kvdb = "0.3.1" log = "0.4" parity-bytes = "0.1" -parking_lot = "0.7" -rand = "0.6" -rayon = "1.1" -rlp = { version = "0.3.0", features = ["ethereum"] } +rand = "0.7" +parking_lot = "0.9" +rayon = "1.0" +rlp = "0.4.0" rlp_compress = { path = "../../util/rlp-compress" } rlp_derive = { path = "../../util/rlp-derive" } triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" } [dev-dependencies] env_logger = "0.5" -ethkey = { path = "../../accounts/ethkey" } +parity-crypto = { version = "0.4.2", features = ["publickey"] } rustc-hex = "1.0" tempdir = "0.3" -kvdb-memorydb = "0.1" +kvdb-memorydb = "0.3.1" diff --git a/ethcore/blockchain/src/best_block.rs b/ethcore/blockchain/src/best_block.rs index 20f247391d..b9ce1bd838 100644 --- a/ethcore/blockchain/src/best_block.rs +++ b/ethcore/blockchain/src/best_block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,7 +24,8 @@ use common_types::header::Header; /// For GHOST fork-choice rule it would typically describe the block with highest /// combined difficulty (usually the block with the highest block number). /// -/// Sometimes refered as 'latest block'. +/// Sometimes referred as 'latest block'. +#[derive(Debug)] pub struct BestBlock { /// Best block decoded header. pub header: Header, @@ -35,7 +36,7 @@ pub struct BestBlock { } /// Best ancient block info. If the blockchain has a gap this keeps track of where it starts. -#[derive(Default)] +#[derive(Debug, Default)] pub struct BestAncientBlock { /// Best block hash. pub hash: H256, diff --git a/ethcore/blockchain/src/block_info.rs b/ethcore/blockchain/src/block_info.rs deleted file mode 100644 index 15f71ecb85..0000000000 --- a/ethcore/blockchain/src/block_info.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use ethereum_types::{H256, U256}; -use common_types::BlockNumber; - -/// Brief info about inserted block. -#[derive(Clone)] -pub struct BlockInfo { - /// Block hash. - pub hash: H256, - /// Block number. - pub number: BlockNumber, - /// Total block difficulty. - pub total_difficulty: U256, - /// Block location in blockchain. - pub location: BlockLocation -} - -/// Describes location of newly inserted block. -#[derive(Debug, Clone, PartialEq)] -pub enum BlockLocation { - /// It's part of the canon chain. - CanonChain, - /// It's not a part of the canon chain. - Branch, - /// It's part of the fork which should become canon chain, - /// because its total difficulty is higher than current - /// canon chain difficulty. - BranchBecomingCanonChain(BranchBecomingCanonChainData), -} - -#[derive(Debug, Clone, PartialEq)] -pub struct BranchBecomingCanonChainData { - /// Hash of the newest common ancestor with old canon chain. - pub ancestor: H256, - /// Hashes of the blocks between ancestor and this block. - pub enacted: Vec, - /// Hashes of the blocks which were invalidated. - pub retracted: Vec, -} diff --git a/ethcore/blockchain/src/blockchain.rs b/ethcore/blockchain/src/blockchain.rs index c5759d3462..e862b3ea87 100644 --- a/ethcore/blockchain/src/blockchain.rs +++ b/ethcore/blockchain/src/blockchain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -23,26 +23,30 @@ use std::sync::Arc; use ansi_term::Colour; use blooms_db; -use common_types::BlockNumber; -use common_types::blockchain_info::BlockChainInfo; -use common_types::encoded; -use common_types::engines::ForkChoice; -use common_types::engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition}; -use common_types::header::{Header, ExtendedHeader}; -use common_types::log_entry::{LogEntry, LocalizedLogEntry}; -use common_types::receipt::Receipt; -use common_types::transaction::LocalizedTransaction; -use common_types::tree_route::TreeRoute; -use common_types::view; -use common_types::views::{BlockView, HeaderView}; +use common_types::{ + BlockNumber, + blockchain_info::BlockChainInfo, + block::{BlockInfo, BlockLocation, BranchBecomingCanonChainData}, + encoded, + engines::ForkChoice, + engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition}, + header::{Header, ExtendedHeader}, + import_route::ImportRoute, + log_entry::{LogEntry, LocalizedLogEntry}, + receipt::Receipt, + transaction::LocalizedTransaction, + tree_route::TreeRoute, + view, + views::{BlockView, HeaderView}, +}; use ethcore_db::cache_manager::CacheManager; use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, EPOCH_KEY_PREFIX, EpochTransitions}; use ethcore_db::{self as db, Writable, Readable, CacheUpdatePolicy}; use ethereum_types::{H256, Bloom, BloomRef, U256}; -use heapsize::HeapSizeOf; +use util_mem::{MallocSizeOf, allocators::new_malloc_size_ops}; use itertools::Itertools; use kvdb::{DBTransaction, KeyValueDB}; -use log::{trace, warn, info}; +use log::{trace, debug, warn, info}; use parity_bytes::Bytes; use parking_lot::{Mutex, RwLock}; use rayon::prelude::*; @@ -50,14 +54,13 @@ use rlp::RlpStream; use rlp_compress::{compress, decompress, blocks_swapper}; use crate::best_block::{BestBlock, BestAncientBlock}; -use crate::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData}; use crate::update::{ExtrasUpdate, ExtrasInsert}; -use crate::{CacheSize, ImportRoute, Config}; +use crate::{CacheSize, Config}; /// Database backing `BlockChain`. pub trait BlockChainDB: Send + Sync { /// Generic key value store. - fn key_value(&self) -> &Arc; + fn key_value(&self) -> &Arc; /// Header blooms database. fn blooms(&self) -> &blooms_db::Database; @@ -85,7 +88,7 @@ pub trait BlockChainDB: Send + Sync { /// predefined config. pub trait BlockChainDBHandler: Send + Sync { /// Open the predefined key-value database. - fn open(&self, path: &Path) -> io::Result>; + fn open(&self, path: &Path) -> io::Result>; } /// Interface for querying blocks by hash and by number. @@ -195,6 +198,12 @@ pub trait BlockProvider { where F: Fn(&LogEntry) -> bool + Send + Sync, Self: Sized; } +/// Interface for querying blocks with pending db transaction by hash and by number. +trait InTransactionBlockProvider { + /// Get the familial details concerning a block. + fn uncommitted_block_details(&self, hash: &H256) -> Option; +} + #[derive(Debug, Hash, Eq, PartialEq, Clone)] enum CacheId { BlockHeader(H256), @@ -214,8 +223,9 @@ pub struct BlockChain { // Stores best block of the first uninterrupted sequence of blocks. `None` if there are no gaps. // Only updated with `insert_unordered_block`. best_ancient_block: RwLock>, - // Stores the last block of the last sequence of blocks. `None` if there are no gaps. - // This is calculated on start and does not get updated. + // Stores the hash of the first block of the last sequence of blocks. `None` means that there + // are no gaps in the chain; `Some(hash)` means that the database was warp-synced. + // This is calculated on start and is not updated. first_block: Option, // block cache @@ -228,7 +238,7 @@ pub struct BlockChain { transaction_addresses: RwLock>, block_receipts: RwLock>, - db: Arc, + db: Arc, cache_man: Mutex>, @@ -284,7 +294,7 @@ impl BlockProvider for BlockChain { } // Read from DB and populate cache - let b = self.db.key_value().get(db::COL_HEADERS, hash) + let b = self.db.key_value().get(db::COL_HEADERS, hash.as_bytes()) .expect("Low level database error when fetching block header data. Some issue with disk?")?; let header = encoded::Header::new(decompress(&b, blocks_swapper()).into_vec()); @@ -314,7 +324,7 @@ impl BlockProvider for BlockChain { } // Read from DB and populate cache - let b = self.db.key_value().get(db::COL_BODIES, hash) + let b = self.db.key_value().get(db::COL_BODIES, hash.as_bytes()) .expect("Low level database error when fetching block body data. Some issue with disk?")?; let body = encoded::Body::new(decompress(&b, blocks_swapper()).into_vec()); @@ -424,6 +434,19 @@ impl BlockProvider for BlockChain { } } +impl InTransactionBlockProvider for BlockChain { + fn uncommitted_block_details(&self, hash: &H256) -> Option { + let result = self.db.key_value().read_with_two_layer_cache( + db::COL_EXTRA, + &self.pending_block_details, + &self.block_details, + hash + )?; + self.cache_man.lock().note_used(CacheId::BlockDetails(*hash)); + Some(result) + } +} + /// An iterator which walks the blockchain towards the genesis. #[derive(Clone)] pub struct AncestryIter<'a> { @@ -469,7 +492,7 @@ impl<'a> Iterator for AncestryWithMetadataIter<'a> { }) }, _ => { - self.current = H256::default(); + self.current = H256::zero(); None }, } @@ -481,7 +504,7 @@ impl<'a> Iterator for AncestryWithMetadataIter<'a> { /// Returns epoch transitions. pub struct EpochTransitionIter<'a> { chain: &'a BlockChain, - prefix_iter: Box, Box<[u8]>)> + 'a>, + prefix_iter: Box, Box<[u8]>)> + 'a>, } impl<'a> Iterator for EpochTransitionIter<'a> { @@ -521,7 +544,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 { + pub fn new(config: Config, genesis: &[u8], db: Arc) -> BlockChain { // 400 is the average size of the key let cache_man = CacheManager::new(config.pref_cache_size, config.max_cache_size, 400); @@ -572,13 +595,13 @@ impl BlockChain { }; let mut batch = DBTransaction::new(); - batch.put(db::COL_HEADERS, &hash, block.header_rlp().as_raw()); - batch.put(db::COL_BODIES, &hash, &Self::block_to_body(genesis)); + batch.put(db::COL_HEADERS, hash.as_bytes(), block.header_rlp().as_raw()); + batch.put(db::COL_BODIES, hash.as_bytes(), &Self::block_to_body(genesis)); batch.write(db::COL_EXTRA, &hash, &details); batch.write(db::COL_EXTRA, &header.number(), &hash); - batch.put(db::COL_EXTRA, b"best", &hash); + batch.put(db::COL_EXTRA, b"best", hash.as_bytes()); bc.db.key_value().write(batch).expect("Low level database error when fetching 'best' block. Some issue with disk?"); hash } @@ -592,7 +615,7 @@ impl BlockChain { let best_block_rlp = bc.block(&best_block_hash) .expect("Best block is from a known block hash; qed"); - // and write them + // and write them to the cache. let mut best_block = bc.best_block.write(); *best_block = BestBlock { total_difficulty: best_block_total_difficulty, @@ -605,8 +628,7 @@ impl BlockChain { let best_block_number = bc.best_block.read().header.number(); // Fetch first and best ancient block details let raw_first = bc.db.key_value().get(db::COL_EXTRA, b"first") - .expect("Low level database error when fetching 'first' block. Some issue with disk?") - .map(|v| v.into_vec()); + .expect("Low level database error when fetching 'first' block. Some issue with disk?"); let mut best_ancient = bc.db.key_value().get(db::COL_EXTRA, b"ancient") .expect("Low level database error when fetching 'best ancient' block. Some issue with disk?") .map(|h| H256::from_slice(&h)); @@ -618,12 +640,12 @@ impl BlockChain { best_ancient_number = best_ancient.as_ref().and_then(|h| bc.block_number(h)); } - // binary search for the first block. + // binary search for the first block (unless they warp synced their db we'll search back to genesis). match raw_first { None => { let (mut f, mut hash) = (best_block_number, best_block_hash); let mut l = best_ancient_number.unwrap_or(0); - + trace!(target: "blockchain", "Looking for first block. Starting binary search between f={} and l={}", f, l); loop { if l >= f { break; } @@ -637,9 +659,9 @@ impl BlockChain { } if hash != bc.genesis_hash() { - trace!("First block calculated: {:?}", hash); + trace!(target:"blockchain", "First block calculated: #{}/{:?}; writing to disk", f, hash); let mut batch = db.key_value().transaction(); - batch.put(db::COL_EXTRA, b"first", &hash); + batch.put(db::COL_EXTRA, b"first", hash.as_bytes()); db.key_value().write(batch).expect("Low level database error when writing 'first' block. Some issue with disk?"); bc.first_block = Some(hash); } @@ -652,10 +674,7 @@ impl BlockChain { // and write them if let (Some(hash), Some(number)) = (best_ancient, best_ancient_number) { let mut best_ancient_block = bc.best_ancient_block.write(); - *best_ancient_block = Some(BestAncientBlock { - hash: hash, - number: number, - }); + *best_ancient_block = Some(BestAncientBlock { hash, number }); } } @@ -762,8 +781,8 @@ impl BlockChain { Some(TreeRoute { blocks: from_branch, ancestor: current_from, - index: index, - is_from_route_finalized: is_from_route_finalized, + index, + is_from_route_finalized, }) } @@ -792,10 +811,10 @@ impl BlockChain { let compressed_body = compress(&Self::block_to_body(block.raw()), blocks_swapper()); // store block in db - batch.put(db::COL_HEADERS, &hash, &compressed_header); - batch.put(db::COL_BODIES, &hash, &compressed_body); + batch.put(db::COL_HEADERS, hash.as_bytes(), &compressed_header); + batch.put(db::COL_BODIES, hash.as_bytes(), &compressed_body); - let maybe_parent = self.block_details(&block_parent_hash); + let maybe_parent = self.uncommitted_block_details(&block_parent_hash); if let Some(parent_details) = maybe_parent { // parent known to be in chain. @@ -858,12 +877,31 @@ impl BlockChain { } } - /// clears all caches for testing purposes + /// clears all caches, re-loads best block from disk for testing purposes pub fn clear_cache(&self) { self.block_bodies.write().clear(); self.block_details.write().clear(); self.block_hashes.write().clear(); self.block_headers.write().clear(); + // Fetch best block details from disk + let best_block_hash = self.db.key_value().get(db::COL_EXTRA, b"best") + .expect("Low-level database error when fetching 'best' block. Some issue with disk?") + .as_ref() + .map(|r| H256::from_slice(r)) + .unwrap(); + let best_block_total_difficulty = self.block_details(&best_block_hash) + .expect("Best block is from a known block hash; a known block hash always comes with a known block detail; qed") + .total_difficulty; + let best_block_rlp = self.block(&best_block_hash) + .expect("Best block is from a known block hash; qed"); + + // and write them to the cache + let mut best_block = self.best_block.write(); + *best_block = BestBlock { + total_difficulty: best_block_total_difficulty, + header: best_block_rlp.decode_header(), + block: best_block_rlp, + }; } /// Update the best ancient block to the given hash, after checking that @@ -936,7 +974,7 @@ impl BlockChain { *pending_best_ancient_block = Some(None); } else if block_number > ancient_number { trace!(target: "blockchain", "Updating the best ancient block to {}.", block_number); - batch.put(db::COL_EXTRA, b"ancient", &block_hash); + batch.put(db::COL_EXTRA, b"ancient", block_hash.as_bytes()); *pending_best_ancient_block = Some(Some(BestAncientBlock { hash: *block_hash, number: block_number, @@ -967,6 +1005,7 @@ impl BlockChain { /// Iterate over all epoch transitions. /// This will only return transitions within the canonical chain. pub fn epoch_transitions(&self) -> EpochTransitionIter { + debug!(target: "blockchain", "Iterating over all epoch transitions"); let iter = self.db.key_value().iter_from_prefix(db::COL_EXTRA, &EPOCH_KEY_PREFIX[..]); EpochTransitionIter { chain: self, @@ -992,7 +1031,9 @@ impl BlockChain { pub fn epoch_transition_for(&self, parent_hash: H256) -> Option { // slow path: loop back block by block for hash in self.ancestry_iter(parent_hash)? { + trace!(target: "blockchain", "Got parent hash {} from ancestry_iter", hash); let details = self.block_details(&hash)?; + trace!(target: "blockchain", "Block #{}: Got block details for parent hash {}", details.number, hash); // look for transition in database. if let Some(transition) = self.epoch_transition(details.number, hash) { @@ -1004,11 +1045,22 @@ impl BlockChain { // // if `block_hash` is canonical it will only return transitions up to // the parent. - if self.block_hash(details.number)? == hash { - return self.epoch_transitions() - .map(|(_, t)| t) - .take_while(|t| t.block_number <= details.number) - .last() + match self.block_hash(details.number) { + Some(h) if h == hash => { + return self.epoch_transitions() + .map(|(_, t)| t) + .take_while(|t| t.block_number <= details.number) + .last() + }, + Some(h) => { + warn!(target: "blockchain", "Block #{}: Found non-canonical block hash {} (expected {})", details.number, h, hash); + + trace!(target: "blockchain", "Block #{} Mismatched hashes. Ancestor {} != Own {}", details.number, hash, h); + trace!(target: "blockchain", " Ancestor {}: #{:#?}", hash, details); + trace!(target: "blockchain", " Own {}: #{:#?}", h, self.block_details(&h)); + + }, + None => trace!(target: "blockchain", "Block #{}: hash {} not found in cache or DB", details.number, hash), } } @@ -1033,7 +1085,7 @@ impl BlockChain { /// /// Used in snapshots to glue the chunks together at the end. pub fn add_child(&self, batch: &mut DBTransaction, block_hash: H256, child_hash: H256) { - let mut parent_details = self.block_details(&block_hash) + let mut parent_details = self.uncommitted_block_details(&block_hash) .unwrap_or_else(|| panic!("Invalid block hash: {:?}", block_hash)); parent_details.children.push(child_hash); @@ -1076,8 +1128,8 @@ impl BlockChain { let compressed_body = compress(&Self::block_to_body(block.raw()), blocks_swapper()); // store block in db - batch.put(db::COL_HEADERS, &hash, &compressed_header); - batch.put(db::COL_BODIES, &hash, &compressed_body); + batch.put(db::COL_HEADERS, hash.as_bytes(), &compressed_header); + batch.put(db::COL_BODIES, hash.as_bytes(), &compressed_body); let info = self.block_info(&block.header_view(), route, &extras); @@ -1140,7 +1192,7 @@ impl BlockChain { /// Mark a block to be considered finalized. Returns `Some(())` if the operation succeeds, and `None` if the block /// hash is not found. pub fn mark_finalized(&self, batch: &mut DBTransaction, block_hash: H256) -> Option<()> { - let mut block_details = self.block_details(&block_hash)?; + let mut block_details = self.uncommitted_block_details(&block_hash)?; block_details.is_finalized = true; self.update_block_details(batch, block_hash, block_details); @@ -1176,7 +1228,7 @@ impl BlockChain { { let mut best_block = self.pending_best_block.write(); if is_best && update.info.location != BlockLocation::Branch { - batch.put(db::COL_EXTRA, b"best", &update.info.hash); + batch.put(db::COL_EXTRA, b"best", update.info.hash.as_bytes()); *best_block = Some(BestBlock { total_difficulty: update.info.total_difficulty, header: update.block.decode_header(), @@ -1263,20 +1315,21 @@ impl BlockChain { current: if self.is_known(&first) { first } else { - H256::default() // zero hash + H256::zero() // 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> { + pub fn find_uncle_headers(&self, parent: &H256, uncle_generations: u64) -> 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. - pub fn find_uncle_hashes(&self, parent: &H256, uncle_generations: usize) -> Option> { + pub fn find_uncle_hashes(&self, parent: &H256, uncle_generations: u64) -> Option> { + let uncle_generations = uncle_generations as usize; if !self.is_known(parent) { return None; } @@ -1333,7 +1386,7 @@ impl BlockChain { /// Uses the given parent details or attempts to load them from the database. fn prepare_block_details_update(&self, parent_hash: H256, info: &BlockInfo, is_finalized: bool) -> HashMap { // update parent - let mut parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); + let mut parent_details = self.uncommitted_block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); parent_details.children.push(info.hash); // create current block details. @@ -1479,11 +1532,12 @@ impl BlockChain { /// Get current cache size. pub fn cache_size(&self) -> CacheSize { + let mut ops = new_malloc_size_ops(); CacheSize { - blocks: self.block_headers.read().heap_size_of_children() + self.block_bodies.read().heap_size_of_children(), - block_details: self.block_details.read().heap_size_of_children(), - transaction_addresses: self.transaction_addresses.read().heap_size_of_children(), - block_receipts: self.block_receipts.read().heap_size_of_children(), + blocks: self.block_headers.size_of(&mut ops) + self.block_bodies.size_of(&mut ops), + block_details: self.block_details.size_of(&mut ops), + transaction_addresses: self.transaction_addresses.size_of(&mut ops), + block_receipts: self.block_receipts.size_of(&mut ops), } } @@ -1518,12 +1572,13 @@ impl BlockChain { transaction_addresses.shrink_to_fit(); block_receipts.shrink_to_fit(); - block_headers.heap_size_of_children() + - block_bodies.heap_size_of_children() + - block_details.heap_size_of_children() + - block_hashes.heap_size_of_children() + - transaction_addresses.heap_size_of_children() + - block_receipts.heap_size_of_children() + let mut ops = new_malloc_size_ops(); + block_headers.size_of(&mut ops) + + block_bodies.size_of(&mut ops) + + block_details.size_of(&mut ops) + + block_hashes.size_of(&mut ops) + + transaction_addresses.size_of(&mut ops) + + block_receipts.size_of(&mut ops) }); } @@ -1544,7 +1599,7 @@ impl BlockChain { let first_block_number = self.first_block_number().into(); let genesis_hash = self.genesis_hash(); - // ensure data consistencly by locking everything first + // ensure data consistency by locking everything first let best_block = self.best_block.read(); let best_ancient_block = self.best_ancient_block.read(); BlockChainInfo { @@ -1571,21 +1626,22 @@ mod tests { use common_types::receipt::{Receipt, TransactionOutcome}; use common_types::transaction::{Transaction, Action}; use crate::generator::{BlockGenerator, BlockBuilder, BlockOptions}; - use ethkey::Secret; + use parity_crypto::publickey::Secret; use keccak_hash::keccak; use rustc_hex::FromHex; use tempdir::TempDir; + use std::str::FromStr; struct TestBlockChainDB { _blooms_dir: TempDir, _trace_blooms_dir: TempDir, blooms: blooms_db::Database, trace_blooms: blooms_db::Database, - key_value: Arc, + key_value: Arc, } impl BlockChainDB for TestBlockChainDB { - fn key_value(&self) -> &Arc { + fn key_value(&self) -> &Arc { &self.key_value } @@ -1599,7 +1655,7 @@ mod tests { } /// Creates new test instance of `BlockChainDB` - pub fn new_db() -> Arc { + pub fn new_db() -> Arc { let blooms_dir = TempDir::new("").unwrap(); let trace_blooms_dir = TempDir::new("").unwrap(); @@ -1608,21 +1664,21 @@ mod tests { trace_blooms: blooms_db::Database::open(trace_blooms_dir.path()).unwrap(), _blooms_dir: blooms_dir, _trace_blooms_dir: trace_blooms_dir, - key_value: Arc::new(kvdb_memorydb::create(ethcore_db::NUM_COLUMNS.unwrap())) + key_value: Arc::new(kvdb_memorydb::create(ethcore_db::NUM_COLUMNS)) }; Arc::new(db) } - fn new_chain(genesis: encoded::Block, db: Arc) -> BlockChain { + fn new_chain(genesis: encoded::Block, db: Arc) -> BlockChain { BlockChain::new(Config::default(), genesis.raw(), db) } - fn insert_block(db: &Arc, bc: &BlockChain, block: encoded::Block, receipts: Vec) -> ImportRoute { + fn insert_block(db: &Arc, bc: &BlockChain, block: encoded::Block, receipts: Vec) -> ImportRoute { insert_block_commit(db, bc, block, receipts, true) } - fn insert_block_commit(db: &Arc, bc: &BlockChain, block: encoded::Block, receipts: Vec, commit: bool) -> ImportRoute { + fn insert_block_commit(db: &Arc, bc: &BlockChain, block: encoded::Block, receipts: Vec, commit: bool) -> ImportRoute { let mut batch = db.key_value().transaction(); let res = insert_block_batch(&mut batch, bc, block, receipts); db.key_value().write(batch).unwrap(); @@ -1633,12 +1689,10 @@ mod tests { } fn insert_block_batch(batch: &mut DBTransaction, bc: &BlockChain, block: encoded::Block, receipts: Vec) -> ImportRoute { - use crate::ExtrasInsert; - let fork_choice = { 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 parent_details = bc.uncommitted_block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash)); let block_total_difficulty = parent_details.total_difficulty + header.difficulty(); if block_total_difficulty > bc.best_block_total_difficulty() { common_types::engines::ForkChoice::New @@ -2061,7 +2115,7 @@ mod tests { fn find_transaction_by_hash() { let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap(); let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap(); - let b1_hash: H256 = "f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3".into(); + let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap(); let db = new_db(); let bc = new_chain(encoded::Block::new(genesis), db.clone()); @@ -2134,7 +2188,7 @@ mod tests { let db = new_db(); let bc = new_chain(genesis.last().encoded(), db.clone()); insert_block(&db, &bc, b1.last().encoded(), vec![Receipt { - outcome: TransactionOutcome::StateRoot(H256::default()), + outcome: TransactionOutcome::StateRoot(H256::zero()), gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ @@ -2143,7 +2197,7 @@ mod tests { ], }, Receipt { - outcome: TransactionOutcome::StateRoot(H256::default()), + outcome: TransactionOutcome::StateRoot(H256::zero()), gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ @@ -2152,7 +2206,7 @@ mod tests { }]); insert_block(&db, &bc, b2.last().encoded(), vec![ Receipt { - outcome: TransactionOutcome::StateRoot(H256::default()), + outcome: TransactionOutcome::StateRoot(H256::zero()), gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ @@ -2162,7 +2216,7 @@ mod tests { ]); insert_block(&db, &bc, b3.last().encoded(), vec![ Receipt { - outcome: TransactionOutcome::StateRoot(H256::default()), + outcome: TransactionOutcome::StateRoot(H256::zero()), gas_used: 10_000.into(), log_bloom: Default::default(), logs: vec![ @@ -2241,11 +2295,11 @@ mod tests { #[test] fn test_bloom_filter_simple() { - let bloom_b1: Bloom = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into(); + let bloom_b1 = Bloom::from_str("00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000").unwrap(); - let bloom_b2: Bloom = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); + let bloom_b2 = Bloom::from_str("00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let bloom_ba: Bloom = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); + let bloom_ba = Bloom::from_str("00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); let genesis = BlockBuilder::genesis(); let b1 = genesis.add_block_with(|| BlockOptions { @@ -2309,11 +2363,11 @@ mod tests { #[test] fn test_insert_unordered() { - let bloom_b1: Bloom = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into(); + let bloom_b1 = Bloom::from_str("00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000").unwrap(); - let bloom_b2: Bloom = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); + let bloom_b2 = Bloom::from_str("00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); - let bloom_b3: Bloom = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); + let bloom_b3 = Bloom::from_str("00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); let genesis = BlockBuilder::genesis(); let b1 = genesis.add_block_with_bloom(bloom_b1); diff --git a/ethcore/blockchain/src/cache.rs b/ethcore/blockchain/src/cache.rs index f17afbb278..08c250028e 100644 --- a/ethcore/blockchain/src/cache.rs +++ b/ethcore/blockchain/src/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/blockchain/src/config.rs b/ethcore/blockchain/src/config.rs index 8cd84b5937..48b85a4158 100644 --- a/ethcore/blockchain/src/config.rs +++ b/ethcore/blockchain/src/config.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/blockchain/src/generator.rs b/ethcore/blockchain/src/generator.rs index e5161d4098..6ca4f4704d 100644 --- a/ethcore/blockchain/src/generator.rs +++ b/ethcore/blockchain/src/generator.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/blockchain/src/lib.rs b/ethcore/blockchain/src/lib.rs index 3f07a6d807..1ab921f898 100644 --- a/ethcore/blockchain/src/lib.rs +++ b/ethcore/blockchain/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,20 +18,23 @@ #![warn(missing_docs)] +extern crate parity_util_mem as util_mem; +extern crate parity_util_mem as malloc_size_of; + mod best_block; -mod block_info; mod blockchain; mod cache; mod config; -mod import_route; mod update; pub mod generator; -pub use self::blockchain::{BlockProvider, BlockChain, BlockChainDB, BlockChainDBHandler}; -pub use self::cache::CacheSize; -pub use self::config::Config; -pub use self::import_route::ImportRoute; -pub use self::update::ExtrasInsert; +pub use crate::{ + blockchain::{BlockProvider, BlockChain, BlockChainDB, BlockChainDBHandler}, + cache::CacheSize, + config::Config, + update::ExtrasInsert, +}; pub use ethcore_db::keys::{BlockReceipts, BlockDetails, TransactionAddress, BlockNumberKey}; pub use common_types::tree_route::TreeRoute; + diff --git a/ethcore/blockchain/src/update.rs b/ethcore/blockchain/src/update.rs index 959f55fdff..3b8e8d344c 100644 --- a/ethcore/blockchain/src/update.rs +++ b/ethcore/blockchain/src/update.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,14 +16,15 @@ use std::collections::HashMap; -use common_types::BlockNumber; -use common_types::encoded::Block; -use common_types::engines::ForkChoice; +use common_types::{ + BlockNumber, + encoded::Block, + engines::ForkChoice, + block::BlockInfo, +}; use ethcore_db::keys::{BlockDetails, BlockReceipts, TransactionAddress}; use ethereum_types::{H256, Bloom}; -use crate::block_info::BlockInfo; - /// Block extras update info. pub struct ExtrasUpdate { /// Block info. diff --git a/ethcore/builtin/Cargo.toml b/ethcore/builtin/Cargo.toml new file mode 100644 index 0000000000..0ce7f315e9 --- /dev/null +++ b/ethcore/builtin/Cargo.toml @@ -0,0 +1,23 @@ +[package] +description = "ethereum vm builtin" +name = "ethcore-builtin" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +bn = { git = "https://github.com/paritytech/bn", default-features = false } +byteorder = "1.3.2" +common-types = { path = "../types" } +eip-152 = { path = "../../util/EIP-152" } +ethereum-types = "0.8.0" +ethjson = { path = "../../json" } +keccak-hash = "0.4.0" +log = "0.4" +num = { version = "0.1", default-features = false, features = ["bigint"] } +parity-bytes = "0.1" +parity-crypto = { version = "0.4.2", features = ["publickey"] } + +[dev-dependencies] +hex-literal = "0.2.1" +macros = { path = "../../util/macros" } diff --git a/ethcore/src/builtin.rs b/ethcore/builtin/src/lib.rs similarity index 73% rename from ethcore/src/builtin.rs rename to ethcore/builtin/src/lib.rs index bca2aeeaac..cd07720ab5 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/builtin/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,51 +16,38 @@ //! Standard built-in contracts. +#![warn(missing_docs)] + use std::{ cmp::{max, min}, + collections::BTreeMap, + convert::{TryFrom, TryInto}, io::{self, Read, Cursor}, mem::size_of, + str::FromStr }; -use bn; -use byteorder::{BigEndian, LittleEndian, ByteOrder, ReadBytesExt}; +use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; +use common_types::errors::EthcoreError; use ethereum_types::{H256, U256}; -use ethjson; -use ethkey::{Signature, recover as ec_recover}; -use hash::keccak; +use parity_crypto::publickey::{Signature, recover as ec_recover}; +use keccak_hash::keccak; use log::{warn, trace}; use num::{BigUint, Zero, One}; -use bytes::BytesRef; +use parity_bytes::BytesRef; use parity_crypto::digest; use eip_152::compress; -/// Execution error. -#[derive(Debug, PartialEq)] -pub struct Error(pub &'static str); - -impl From<&'static str> for Error { - fn from(val: &'static str) -> Self { - Error(val) - } -} - -impl Into<::vm::Error> for Error { - fn into(self) -> ::vm::Error { - ::vm::Error::BuiltIn(self.0) - } -} - /// Native implementation of a built-in contract. -pub trait Impl: Send + Sync { +pub trait Implementation: Send + Sync { /// execute this built-in on the given input, writing to the given output. - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error>; + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str>; } /// A gas pricing scheme for built-in contracts. -// TODO: refactor this trait, see https://github.com/paritytech/parity-ethereum/issues/11014 trait Pricer: Send + Sync { /// The gas cost of running this built-in for the given input data at block number `at` - fn cost(&self, input: &[u8], at: u64) -> U256; + fn cost(&self, input: &[u8]) -> U256; } /// Pricing for the Blake2 compression function (aka "F"). @@ -69,86 +56,101 @@ trait Pricer: Send + Sync { pub type Blake2FPricer = u64; impl Pricer for Blake2FPricer { - fn cost(&self, input: &[u8], _at: u64) -> U256 { - use std::convert::TryInto; - let (rounds_bytes, _) = input.split_at(std::mem::size_of::()); + fn cost(&self, input: &[u8]) -> U256 { + const FOUR: usize = std::mem::size_of::(); // Returning zero if the conversion fails is fine because `execute()` will check the length // and bail with the appropriate error. - let rounds = u32::from_be_bytes(rounds_bytes.try_into().unwrap_or([0u8; 4])); -// U256::from(*self as u128 * rounds as u128) - U256::from(*self as u64 * rounds as u64) + if input.len() < FOUR { + return U256::zero(); + } + let (rounds_bytes, _) = input.split_at(FOUR); + let rounds = u32::from_be_bytes(rounds_bytes.try_into().unwrap_or([0u8; FOUR])); + U256::from(*self as u128 * rounds as u128) + } +} + +/// Pricing model +#[derive(Debug)] +enum Pricing { + AltBn128Pairing(AltBn128PairingPricer), + AltBn128ConstOperations(AltBn128ConstOperations), + Blake2F(Blake2FPricer), + Linear(Linear), + Modexp(ModexpPricer), +} + +impl Pricer for Pricing { + fn cost(&self, input: &[u8]) -> U256 { + match self { + Pricing::AltBn128Pairing(inner) => inner.cost(input), + Pricing::AltBn128ConstOperations(inner) => inner.cost(input), + Pricing::Blake2F(inner) => inner.cost(input), + Pricing::Linear(inner) => inner.cost(input), + Pricing::Modexp(inner) => inner.cost(input), + } } } /// A linear pricing model. This computes a price using a base cost and a cost per-word. +#[derive(Debug)] struct Linear { - base: usize, - word: usize, + base: u64, + word: u64, } /// A special pricing model for modular exponentiation. +#[derive(Debug)] struct ModexpPricer { - divisor: usize, + divisor: u64, } impl Pricer for Linear { - fn cost(&self, input: &[u8], _at: u64) -> U256 { + fn cost(&self, input: &[u8]) -> U256 { U256::from(self.base) + U256::from(self.word) * U256::from((input.len() + 31) / 32) } } -/// alt_bn128 constant operations (add and mul) pricing model. -struct AltBn128ConstOperations { - price: usize, - eip1108_transition_at: u64, - eip1108_transition_price: usize, -} - -impl Pricer for AltBn128ConstOperations { - fn cost(&self, _input: &[u8], at: u64) -> U256 { - if at >= self.eip1108_transition_at { - self.eip1108_transition_price.into() - } else { - self.price.into() - } - } -} - /// alt_bn128 pairing price #[derive(Debug, Copy, Clone)] struct AltBn128PairingPrice { - base: usize, - pair: usize, + base: u64, + pair: u64, } /// alt_bn128_pairing pricing model. This computes a price using a base cost and a cost per pair. +#[derive(Debug)] struct AltBn128PairingPricer { price: AltBn128PairingPrice, - eip1108_transition_at: u64, - eip1108_transition_price: AltBn128PairingPrice, } -impl Pricer for AltBn128PairingPricer { - fn cost(&self, input: &[u8], at: u64) -> U256 { - let price = if at >= self.eip1108_transition_at { - self.eip1108_transition_price - } else { - self.price - }; +/// Pricing for constant alt_bn128 operations (ECADD and ECMUL) +#[derive(Debug, Copy, Clone)] +pub struct AltBn128ConstOperations { + /// Fixed price. + pub price: u64, +} + +impl Pricer for AltBn128ConstOperations { + fn cost(&self, _input: &[u8]) -> U256 { + self.price.into() + } +} - U256::from(price.base) + U256::from(price.pair) * U256::from(input.len() / 192) +impl Pricer for AltBn128PairingPricer { + fn cost(&self, input: &[u8]) -> U256 { + U256::from(self.price.base) + U256::from(self.price.pair) * U256::from(input.len() / 192) } } impl Pricer for ModexpPricer { - fn cost(&self, input: &[u8], _at: u64) -> U256 { + fn cost(&self, input: &[u8]) -> U256 { let mut reader = input.chain(io::repeat(0)); let mut buf = [0; 32]; // read lengths as U256 here for accurate gas calculation. let mut read_len = || { reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - U256::from(H256::from_slice(&buf[..])) + U256::from_big_endian(&buf[..]) }; let base_len = read_len(); let exp_len = read_len(); @@ -166,12 +168,14 @@ impl Pricer for ModexpPricer { let m = max(mod_len, base_len); // read fist 32-byte word of the exponent. - let exp_low = if base_len + 96 >= input.len() as u64 { U256::zero() } else { - let mut buf = [0; 32]; + let exp_low = if base_len + 96 >= input.len() as u64 { + U256::zero() + } else { + buf.iter_mut().for_each(|b| *b = 0); let mut reader = input[(96 + base_len as usize)..].chain(io::repeat(0)); let len = min(exp_len, 32) as usize; reader.read_exact(&mut buf[(32 - len)..]).expect("reading from zero-extended memory cannot fail; qed"); - U256::from(H256::from_slice(&buf[..])) + U256::from_big_endian(&buf[..]) }; let adjusted_exp_len = Self::adjusted_exp_len(exp_len, exp_low); @@ -198,7 +202,7 @@ impl ModexpPricer { match x { x if x <= 64 => x * x, x if x <= 1024 => (x * x) / 4 + 96 * x - 3072, - x => (x * x) / 16 + 480 * x - 199680, + x => (x * x) / 16 + 480 * x - 199_680, } } } @@ -207,147 +211,198 @@ impl ModexpPricer { /// /// Call `cost` to compute cost for the given input, `execute` to execute the contract /// on the given input, and `is_active` to determine whether the contract is active. -/// -/// Unless `is_active` is true, pub struct Builtin { - pricer: Box, - native: Box, - activate_at: u64, + pricer: BTreeMap, + native: EthereumBuiltin, } impl Builtin { /// Simple forwarder for cost. + /// + /// Return the cost of the most recently activated pricer at the current block number. + /// + /// If no pricer is actived `zero` is returned + /// + /// If multiple `activation_at` has the same block number the last one is used + /// (follows `BTreeMap` semantics). + #[inline] pub fn cost(&self, input: &[u8], at: u64) -> U256 { - self.pricer.cost(input, at) + if let Some((_, pricer)) = self.pricer.range(0..=at).last() { + pricer.cost(input) + } else { + U256::zero() + } } /// Simple forwarder for execute. - pub fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + #[inline] + pub fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { self.native.execute(input, output) } /// Whether the builtin is activated at the given block number. + #[inline] pub fn is_active(&self, at: u64) -> bool { - at >= self.activate_at + self.pricer.range(0..=at).last().is_some() + } +} + +impl TryFrom for Builtin { + type Error = EthcoreError; + + fn try_from(b: ethjson::spec::builtin::Builtin) -> Result { + let native = EthereumBuiltin::from_str(&b.name)?; + let mut pricer = BTreeMap::new(); + + for (activate_at, p) in b.pricing { + pricer.insert(activate_at, p.price.into()); + } + + Ok(Self { pricer, native }) } } -impl From for Builtin { - fn from(b: ethjson::spec::Builtin) -> Self { - let pricer: Box = match b.pricing { - ethjson::spec::Pricing::Blake2F { gas_per_round } => { - Box::new(gas_per_round) - }, - ethjson::spec::Pricing::Linear(linear) => { - Box::new(Linear { +impl From for Pricing { + fn from(pricing: ethjson::spec::builtin::Pricing) -> Self { + match pricing { + ethjson::spec::builtin::Pricing::Blake2F { gas_per_round } => { + Pricing::Blake2F(gas_per_round) + } + ethjson::spec::builtin::Pricing::Linear(linear) => { + Pricing::Linear(Linear { base: linear.base, word: linear.word, }) } - ethjson::spec::Pricing::Modexp(exp) => { - Box::new(ModexpPricer { + ethjson::spec::builtin::Pricing::Modexp(exp) => { + Pricing::Modexp(ModexpPricer { divisor: if exp.divisor == 0 { - warn!(target: "builtin", "Zero modexp divisor specified. Falling back to default."); + warn!(target: "builtin", "Zero modexp divisor specified. Falling back to default: 10."); 10 } else { exp.divisor } }) } - ethjson::spec::Pricing::AltBn128Pairing(pricer) => { - Box::new(AltBn128PairingPricer { + ethjson::spec::builtin::Pricing::AltBn128Pairing(pricer) => { + Pricing::AltBn128Pairing(AltBn128PairingPricer { price: AltBn128PairingPrice { base: pricer.base, pair: pricer.pair, }, - eip1108_transition_at: b.eip1108_transition.map_or(u64::max_value(), Into::into), - eip1108_transition_price: AltBn128PairingPrice { - base: pricer.eip1108_transition_base, - pair: pricer.eip1108_transition_pair, - }, }) } - ethjson::spec::Pricing::AltBn128ConstOperations(pricer) => { - Box::new(AltBn128ConstOperations { - price: pricer.price, - eip1108_transition_price: pricer.eip1108_transition_price, - eip1108_transition_at: b.eip1108_transition.map_or(u64::max_value(), Into::into) + ethjson::spec::builtin::Pricing::AltBn128ConstOperations(pricer) => { + Pricing::AltBn128ConstOperations(AltBn128ConstOperations { + price: pricer.price }) } - }; - - Builtin { - pricer, - native: ethereum_builtin(&b.name), - activate_at: b.activate_at.map_or(0, Into::into), } } } -/// Ethereum built-in factory. -pub fn ethereum_builtin(name: &str) -> Box { - match name { - "identity" => Box::new(Identity) as Box, - "ecrecover" => Box::new(EcRecover) as Box, - "sha256" => Box::new(Sha256) as Box, - "ripemd160" => Box::new(Ripemd160) as Box, - "modexp" => Box::new(ModexpImpl) as Box, - "alt_bn128_add" => Box::new(Bn128AddImpl) as Box, - "alt_bn128_mul" => Box::new(Bn128MulImpl) as Box, - "alt_bn128_pairing" => Box::new(Bn128PairingImpl) as Box, - "blake2_f" => Box::new(Blake2F) as Box, - _ => panic!("invalid builtin name: {}", name), +/// Ethereum builtins: +enum EthereumBuiltin { + /// The identity function + Identity(Identity), + /// ec recovery + EcRecover(EcRecover), + /// sha256 + Sha256(Sha256), + /// ripemd160 + Ripemd160(Ripemd160), + /// modexp (EIP 198) + Modexp(Modexp), + /// alt_bn128_add + Bn128Add(Bn128Add), + /// alt_bn128_mul + Bn128Mul(Bn128Mul), + /// alt_bn128_pairing + Bn128Pairing(Bn128Pairing), + /// blake2_f (The Blake2 compression function F, EIP-152) + Blake2F(Blake2F) +} + +impl FromStr for EthereumBuiltin { + type Err = EthcoreError; + + fn from_str(name: &str) -> Result { + match name { + "identity" => Ok(EthereumBuiltin::Identity(Identity)), + "ecrecover" => Ok(EthereumBuiltin::EcRecover(EcRecover)), + "sha256" => Ok(EthereumBuiltin::Sha256(Sha256)), + "ripemd160" => Ok(EthereumBuiltin::Ripemd160(Ripemd160)), + "modexp" => Ok(EthereumBuiltin::Modexp(Modexp)), + "alt_bn128_add" => Ok(EthereumBuiltin::Bn128Add(Bn128Add)), + "alt_bn128_mul" => Ok(EthereumBuiltin::Bn128Mul(Bn128Mul)), + "alt_bn128_pairing" => Ok(EthereumBuiltin::Bn128Pairing(Bn128Pairing)), + "blake2_f" => Ok(EthereumBuiltin::Blake2F(Blake2F)), + _ => return Err(EthcoreError::Msg(format!("invalid builtin name: {}", name))), + } } } -// Ethereum builtins: -// -// - The identity function -// - ec recovery -// - sha256 -// - ripemd160 -// - modexp (EIP198) -// - alt_bn128_add -// - alt_bn128_mul -// - alt_bn128_pairing -// - blake2_f (The Blake2 compression function F, EIP-152) +impl Implementation for EthereumBuiltin { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + match self { + EthereumBuiltin::Identity(inner) => inner.execute(input, output), + EthereumBuiltin::EcRecover(inner) => inner.execute(input, output), + EthereumBuiltin::Sha256(inner) => inner.execute(input, output), + EthereumBuiltin::Ripemd160(inner) => inner.execute(input, output), + EthereumBuiltin::Modexp(inner) => inner.execute(input, output), + EthereumBuiltin::Bn128Add(inner) => inner.execute(input, output), + EthereumBuiltin::Bn128Mul(inner) => inner.execute(input, output), + EthereumBuiltin::Bn128Pairing(inner) => inner.execute(input, output), + EthereumBuiltin::Blake2F(inner) => inner.execute(input, output), + } + } +} #[derive(Debug)] -struct Identity; +/// The identity builtin +pub struct Identity; #[derive(Debug)] -struct EcRecover; +/// The EC Recover builtin +pub struct EcRecover; #[derive(Debug)] -struct Sha256; +/// The Sha256 builtin +pub struct Sha256; #[derive(Debug)] -struct Ripemd160; +/// The Ripemd160 builtin +pub struct Ripemd160; #[derive(Debug)] -struct ModexpImpl; +/// The Modexp builtin +pub struct Modexp; #[derive(Debug)] -struct Bn128AddImpl; +/// The Bn128Add builtin +pub struct Bn128Add; #[derive(Debug)] -struct Bn128MulImpl; +/// The Bn128Mul builtin +pub struct Bn128Mul; #[derive(Debug)] -struct Bn128PairingImpl; +/// The Bn128Pairing builtin +pub struct Bn128Pairing; #[derive(Debug)] -struct Blake2F; +/// The Blake2F builtin +pub struct Blake2F; -impl Impl for Identity { -fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { +impl Implementation for Identity { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { output.write(0, input); Ok(()) } } -impl Impl for EcRecover { - fn execute(&self, i: &[u8], output: &mut BytesRef) -> Result<(), Error> { +impl Implementation for EcRecover { + fn execute(&self, i: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { let len = min(i.len(), 128); let mut input = [0; 128]; @@ -359,7 +414,7 @@ impl Impl for EcRecover { let s = H256::from_slice(&input[96..128]); let bit = match v[31] { - 27 | 28 if &v.0[..31] == &[0; 31] => v[31] - 27, + 27 | 28 if v.0[..31] == [0; 31] => v[31] - 27, _ => { return Ok(()); }, }; @@ -368,7 +423,7 @@ impl Impl for EcRecover { if let Ok(p) = ec_recover(&s, &hash) { let r = keccak(p); output.write(0, &[0; 12]); - output.write(12, &r[12..r.len()]); + output.write(12, &r.as_bytes()[12..]); } } @@ -376,24 +431,24 @@ impl Impl for EcRecover { } } -impl Impl for Sha256 { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { +impl Implementation for Sha256 { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { let d = digest::sha256(input); output.write(0, &*d); Ok(()) } } -impl Impl for Blake2F { +impl Implementation for Blake2F { /// Format of `input`: /// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { const BLAKE2_F_ARG_LEN: usize = 213; const PROOF: &str = "Checked the length of the input above; qed"; if input.len() != BLAKE2_F_ARG_LEN { trace!(target: "builtin", "input length for Blake2 F precompile should be exactly 213 bytes, was {}", input.len()); - return Err("input length for Blake2 F precompile should be exactly 213 bytes".into()) + return Err("input length for Blake2 F precompile should be exactly 213 bytes") } let mut cursor = Cursor::new(input); @@ -401,13 +456,13 @@ impl Impl for Blake2F { // state vector, h let mut h = [0u64; 8]; - for state_word in h.iter_mut() { + for state_word in &mut h { *state_word = cursor.read_u64::().expect(PROOF); } // message block vector, m let mut m = [0u64; 16]; - for msg_word in m.iter_mut() { + for msg_word in &mut m { *msg_word = cursor.read_u64::().expect(PROOF); } @@ -423,7 +478,7 @@ impl Impl for Blake2F { Some(0) => false, _ => { trace!(target: "builtin", "incorrect final block indicator flag, was: {:?}", input.last()); - return Err("incorrect final block indicator flag".into()) + return Err("incorrect final block indicator flag") } }; @@ -438,8 +493,8 @@ impl Impl for Blake2F { } } -impl Impl for Ripemd160 { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { +impl Implementation for Ripemd160 { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { let hash = digest::ripemd160(input); output.write(0, &[0; 12][..]); output.write(12, &hash); @@ -460,7 +515,7 @@ fn modexp(mut base: BigUint, exp: Vec, modulus: BigUint) -> BigUint { let mut exp = exp.into_iter().skip_while(|d| *d == 0).peekable(); // n^0 % m - if let None = exp.peek() { + if exp.peek().is_none() { return BigUint::one(); } @@ -469,7 +524,7 @@ fn modexp(mut base: BigUint, exp: Vec, modulus: BigUint) -> BigUint { return BigUint::zero(); } - base = base % &modulus; + base %= &modulus; // Fast path for base divisible by modulus. if base.is_zero() { return BigUint::zero() } @@ -495,8 +550,8 @@ fn modexp(mut base: BigUint, exp: Vec, modulus: BigUint) -> BigUint { result } -impl Impl for ModexpImpl { - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { +impl Implementation for Modexp { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { let mut reader = input.chain(io::repeat(0)); let mut buf = [0; 32]; @@ -505,7 +560,9 @@ impl Impl for ModexpImpl { // but so would running out of addressable memory! let mut read_len = |reader: &mut io::Chain<&[u8], io::Repeat>| { reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - BigEndian::read_u64(&buf[24..]) as usize + let mut len_bytes = [0u8; 8]; + len_bytes.copy_from_slice(&buf[24..]); + u64::from_be_bytes(len_bytes) as usize }; let base_len = read_len(&mut reader); @@ -547,35 +604,35 @@ impl Impl for ModexpImpl { } } -fn read_fr(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<::bn::Fr, Error> { +fn read_fr(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result { let mut buf = [0u8; 32]; reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - ::bn::Fr::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid field element")) + bn::Fr::from_slice(&buf[0..32]).map_err(|_| "Invalid field element") } -fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<::bn::G1, Error> { +fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result { use bn::{Fq, AffineG1, G1, Group}; let mut buf = [0u8; 32]; reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - let px = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?; + let px = Fq::from_slice(&buf[0..32]).map_err(|_| "Invalid point x coordinate")?; reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); - let py = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point y coordinate"))?; + let py = Fq::from_slice(&buf[0..32]).map_err(|_| "Invalid point y coordinate")?; Ok( if px == Fq::zero() && py == Fq::zero() { G1::zero() } else { - AffineG1::new(px, py).map_err(|_| Error::from("Invalid curve point"))?.into() + AffineG1::new(px, py).map_err(|_| "Invalid curve point")?.into() } ) } -impl Impl for Bn128AddImpl { +impl Implementation for Bn128Add { // Can fail if any of the 2 points does not belong the bn128 curve - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { use bn::AffineG1; let mut padded_input = input.chain(io::repeat(0)); @@ -594,9 +651,9 @@ impl Impl for Bn128AddImpl { } } -impl Impl for Bn128MulImpl { +impl Implementation for Bn128Mul { // Can fail if first paramter (bn128 curve point) does not actually belong to the curve - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { use bn::AffineG1; let mut padded_input = input.chain(io::repeat(0)); @@ -614,14 +671,14 @@ impl Impl for Bn128MulImpl { } } -impl Impl for Bn128PairingImpl { +impl Implementation for Bn128Pairing { /// Can fail if: /// - input length is not a multiple of 192 /// - any of odd points does not belong to bn128 curve /// - any of even points does not belong to the twisted bn128 curve over the field F_p^2 = F_p[i] / (i^2 + 1) - fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { + fn execute(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { if input.len() % 192 != 0 { - return Err("Invalid input length, must be multiple of 192 (3 * (32*2))".into()) + return Err("Invalid input length, must be multiple of 192 (3 * (32*2))") } if let Err(err) = self.execute_with_error(input, output) { @@ -632,50 +689,51 @@ impl Impl for Bn128PairingImpl { } } -impl Bn128PairingImpl { - fn execute_with_error(&self, input: &[u8], output: &mut BytesRef) -> Result<(), Error> { - use bn::{AffineG1, AffineG2, Fq, Fq2, pairing, G1, G2, Gt, Group}; +impl Bn128Pairing { + fn execute_with_error(&self, input: &[u8], output: &mut BytesRef) -> Result<(), &'static str> { + use bn::{AffineG1, AffineG2, Fq, Fq2, pairing_batch, G1, G2, Gt, Group}; - let elements = input.len() / 192; // (a, b_a, b_b - each 64-byte affine coordinates) - let ret_val = if input.len() == 0 { + let ret_val = if input.is_empty() { U256::one() } else { + // (a, b_a, b_b - each 64-byte affine coordinates) + let elements = input.len() / 192; let mut vals = Vec::new(); for idx in 0..elements { let a_x = Fq::from_slice(&input[idx*192..idx*192+32]) - .map_err(|_| Error::from("Invalid a argument x coordinate"))?; + .map_err(|_| "Invalid a argument x coordinate")?; let a_y = Fq::from_slice(&input[idx*192+32..idx*192+64]) - .map_err(|_| Error::from("Invalid a argument y coordinate"))?; + .map_err(|_| "Invalid a argument y coordinate")?; let b_a_y = Fq::from_slice(&input[idx*192+64..idx*192+96]) - .map_err(|_| Error::from("Invalid b argument imaginary coeff x coordinate"))?; + .map_err(|_| "Invalid b argument imaginary coeff x coordinate")?; let b_a_x = Fq::from_slice(&input[idx*192+96..idx*192+128]) - .map_err(|_| Error::from("Invalid b argument imaginary coeff y coordinate"))?; + .map_err(|_| "Invalid b argument imaginary coeff y coordinate")?; let b_b_y = Fq::from_slice(&input[idx*192+128..idx*192+160]) - .map_err(|_| Error::from("Invalid b argument real coeff x coordinate"))?; + .map_err(|_| "Invalid b argument real coeff x coordinate")?; let b_b_x = Fq::from_slice(&input[idx*192+160..idx*192+192]) - .map_err(|_| Error::from("Invalid b argument real coeff y coordinate"))?; + .map_err(|_| "Invalid b argument real coeff y coordinate")?; let b_a = Fq2::new(b_a_x, b_a_y); let b_b = Fq2::new(b_b_x, b_b_y); let b = if b_a.is_zero() && b_b.is_zero() { G2::zero() } else { - G2::from(AffineG2::new(b_a, b_b).map_err(|_| Error::from("Invalid b argument - not on curve"))?) + G2::from(AffineG2::new(b_a, b_b).map_err(|_| "Invalid b argument - not on curve")?) }; let a = if a_x.is_zero() && a_y.is_zero() { G1::zero() } else { - G1::from(AffineG1::new(a_x, a_y).map_err(|_| Error::from("Invalid a argument - not on curve"))?) + G1::from(AffineG1::new(a_x, a_y).map_err(|_| "Invalid a argument - not on curve")?) }; vals.push((a, b)); }; - let mul = vals.into_iter().fold(Gt::one(), |s, (a, b)| s * pairing(a, b)); + let mul = pairing_batch(&vals); if mul == Gt::one() { U256::one() @@ -694,19 +752,26 @@ impl Bn128PairingImpl { #[cfg(test)] mod tests { + use std::convert::TryFrom; use ethereum_types::U256; - use ethjson::uint::Uint; + use ethjson::spec::builtin::{ + Builtin as JsonBuiltin, Linear as JsonLinearPricing, + PricingAt, AltBn128Pairing as JsonAltBn128PairingPricing, Pricing as JsonPricing, + }; + use hex_literal::hex; + use macros::map; use num::{BigUint, Zero, One}; - use bytes::BytesRef; -use hex_literal::hex; - use super::{Builtin, Linear, ethereum_builtin, Pricer, ModexpPricer, modexp as me}; + use parity_bytes::BytesRef; + use super::{ + BTreeMap, Builtin, EthereumBuiltin, FromStr, Implementation, Linear, + ModexpPricer, modexp as me, Pricing + }; #[test] fn blake2f_cost() { let f = Builtin { - pricer: Box::new(123), - native: ethereum_builtin("blake2_f"), - activate_at: 0, + pricer: map![0 => Pricing::Blake2F(123)], + native: EthereumBuiltin::from_str("blake2_f").unwrap(), }; // 5 rounds let input = hex!("0000000548c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); @@ -716,45 +781,57 @@ use hex_literal::hex; assert_eq!(f.cost(&input[..], 0), U256::from(123*5)); } + #[test] + fn blake2f_cost_on_invalid_length() { + let f = Builtin { + pricer: map![0 => Pricing::Blake2F(123)], + native: EthereumBuiltin::from_str("blake2_f").expect("known builtin"), + }; + // invalid input (too short) + let input = hex!("00"); + + assert_eq!(f.cost(&input[..], 0), U256::from(0)); + } + #[test] fn blake2_f_is_err_on_invalid_length() { - let blake2 = ethereum_builtin("blake2_f"); + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); // Test vector 1 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-1 let input = hex!("00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let mut out = [0u8; 64]; let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), "input length for Blake2 F precompile should be exactly 213 bytes".into()); + assert_eq!(result.unwrap_err(), "input length for Blake2 F precompile should be exactly 213 bytes"); } #[test] fn blake2_f_is_err_on_invalid_length_2() { - let blake2 = ethereum_builtin("blake2_f"); + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); // Test vector 2 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-2 let input = hex!("000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let mut out = [0u8; 64]; let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), "input length for Blake2 F precompile should be exactly 213 bytes".into()); + assert_eq!(result.unwrap_err(), "input length for Blake2 F precompile should be exactly 213 bytes"); } #[test] fn blake2_f_is_err_on_bad_finalization_flag() { - let blake2 = ethereum_builtin("blake2_f"); + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); // Test vector 3 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-3 let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002"); let mut out = [0u8; 64]; let result = blake2.execute(&input[..], &mut BytesRef::Fixed(&mut out[..])); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), "incorrect final block indicator flag".into()); + assert_eq!(result.unwrap_err(), "incorrect final block indicator flag"); } #[test] fn blake2_f_zero_rounds_is_ok_test_vector_4() { - let blake2 = ethereum_builtin("blake2_f"); + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); // Test vector 4 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-4 let input = hex!("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let expected = hex!("08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"); @@ -765,7 +842,7 @@ use hex_literal::hex; #[test] fn blake2_f_test_vector_5() { - let blake2 = ethereum_builtin("blake2_f"); + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); // Test vector 5 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-5 let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let expected = hex!("ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923"); @@ -776,7 +853,7 @@ use hex_literal::hex; #[test] fn blake2_f_test_vector_6() { - let blake2 = ethereum_builtin("blake2_f"); + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); // Test vector 6 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-6 let input = hex!("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000"); let expected = hex!("75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735"); @@ -787,7 +864,7 @@ use hex_literal::hex; #[test] fn blake2_f_test_vector_7() { - let blake2 = ethereum_builtin("blake2_f"); + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); // Test vector 7 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-7 let input = hex!("0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); let expected = hex!("b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421"); @@ -799,7 +876,7 @@ use hex_literal::hex; #[ignore] #[test] fn blake2_f_test_vector_8() { - let blake2 = ethereum_builtin("blake2_f"); + let blake2 = EthereumBuiltin::from_str("blake2_f").unwrap(); // Test vector 8 and expected output from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-152.md#test-vector-8 // Note this test is slow, 4294967295/0xffffffff rounds take a while. let input = hex!("ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"); @@ -844,8 +921,7 @@ use hex_literal::hex; #[test] fn identity() { - let f = ethereum_builtin("identity"); - + let f = EthereumBuiltin::from_str("identity").unwrap(); let i = [0u8, 1, 2, 3]; let mut o2 = [255u8; 2]; @@ -864,8 +940,7 @@ use hex_literal::hex; #[test] fn sha256() { - let f = ethereum_builtin("sha256"); - + let f = EthereumBuiltin::from_str("sha256").unwrap(); let i = [0u8; 0]; let mut o = [255u8; 32]; @@ -887,8 +962,7 @@ use hex_literal::hex; #[test] fn ripemd160() { - let f = ethereum_builtin("ripemd160"); - + let f = EthereumBuiltin::from_str("ripemd160").unwrap(); let i = [0u8; 0]; let mut o = [255u8; 32]; @@ -906,7 +980,7 @@ use hex_literal::hex; #[test] fn ecrecover() { - let f = ethereum_builtin("ecrecover"); + let f = EthereumBuiltin::from_str("ecrecover").unwrap(); let i = hex!("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03"); @@ -956,18 +1030,16 @@ use hex_literal::hex; #[test] fn modexp() { - let f = Builtin { - pricer: Box::new(ModexpPricer { divisor: 20 }), - native: ethereum_builtin("modexp"), - activate_at: 0, + pricer: map![0 => Pricing::Modexp(ModexpPricer { divisor: 20 })], + native: EthereumBuiltin::from_str("modexp").unwrap(), }; // test for potential gas cost multiplication overflow { let input = hex!("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000003b27bafd00000000000000000000000000000000000000000000000000000000503c8ac3"); let expected_cost = U256::max_value(); - assert_eq!(f.cost(&input[..], 0), expected_cost.into()); + assert_eq!(f.cost(&input[..], 0), expected_cost); } // test for potential exp len overflow @@ -984,7 +1056,7 @@ use hex_literal::hex; f.execute(&input[..], &mut BytesRef::Fixed(&mut output[..])).expect("Builtin should fail"); assert_eq!(output, expected); - assert_eq!(f.cost(&input[..], 0), expected_cost.into()); + assert_eq!(f.cost(&input[..], 0), expected_cost); } // fermat's little theorem example. @@ -1069,9 +1141,8 @@ use hex_literal::hex; fn bn128_add() { let f = Builtin { - pricer: Box::new(Linear { base: 0, word: 0 }), - native: ethereum_builtin("alt_bn128_add"), - activate_at: 0, + pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })], + native: EthereumBuiltin::from_str("alt_bn128_add").unwrap(), }; // zero-points additions @@ -1128,9 +1199,8 @@ use hex_literal::hex; fn bn128_mul() { let f = Builtin { - pricer: Box::new(Linear { base: 0, word: 0 }), - native: ethereum_builtin("alt_bn128_mul"), - activate_at: 0, + pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })], + native: EthereumBuiltin::from_str("alt_bn128_mul").unwrap(), }; // zero-point multiplication @@ -1168,9 +1238,8 @@ use hex_literal::hex; fn builtin_pairing() -> Builtin { Builtin { - pricer: Box::new(Linear { base: 0, word: 0 }), - native: ethereum_builtin("alt_bn128_pairing"), - activate_at: 0, + pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })], + native: EthereumBuiltin::from_str("alt_bn128_pairing").unwrap(), } } @@ -1189,8 +1258,8 @@ use hex_literal::hex; let res = f.execute(input, &mut BytesRef::Fixed(&mut output[..])); if let Some(msg) = msg_contains { if let Err(e) = res { - if !e.0.contains(msg) { - panic!("There should be error containing '{}' here, but got: '{}'", msg, e.0); + if !e.contains(msg) { + panic!("There should be error containing '{}' here, but got: '{}'", msg, e); } } } else { @@ -1241,16 +1310,15 @@ use hex_literal::hex; #[test] #[should_panic] fn from_unknown_linear() { - let _ = ethereum_builtin("foo"); + let _ = EthereumBuiltin::from_str("foo").unwrap(); } #[test] fn is_active() { - let pricer = Box::new(Linear { base: 10, word: 20} ); + let pricer = Pricing::Linear(Linear { base: 10, word: 20 }); let b = Builtin { - pricer: pricer as Box, - native: ethereum_builtin("identity"), - activate_at: 100_000, + pricer: map![100_000 => pricer], + native: EthereumBuiltin::from_str("identity").unwrap(), }; assert!(!b.is_active(99_999)); @@ -1260,11 +1328,10 @@ use hex_literal::hex; #[test] fn from_named_linear() { - let pricer = Box::new(Linear { base: 10, word: 20 }); + let pricer = Pricing::Linear(Linear { base: 10, word: 20 }); let b = Builtin { - pricer: pricer as Box, - native: ethereum_builtin("identity"), - activate_at: 1, + pricer: map![0 => pricer], + native: EthereumBuiltin::from_str("identity").unwrap(), }; assert_eq!(b.cost(&[0; 0], 0), U256::from(10)); @@ -1280,15 +1347,15 @@ use hex_literal::hex; #[test] fn from_json() { - let b = Builtin::from(ethjson::spec::Builtin { + let b = Builtin::try_from(ethjson::spec::Builtin { name: "identity".to_owned(), - pricing: ethjson::spec::Pricing::Linear(ethjson::spec::Linear { - base: 10, - word: 20, - }), - activate_at: None, - eip1108_transition: None, - }); + pricing: map![ + 0 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { base: 10, word: 20 }) + } + ] + }).expect("known builtin"); assert_eq!(b.cost(&[0; 0], 0), U256::from(10)); assert_eq!(b.cost(&[0; 1], 0), U256::from(30)); @@ -1303,17 +1370,25 @@ use hex_literal::hex; #[test] fn bn128_pairing_eip1108_transition() { - let b = Builtin::from(ethjson::spec::Builtin { + let b = Builtin::try_from(JsonBuiltin { name: "alt_bn128_pairing".to_owned(), - pricing: ethjson::spec::Pricing::AltBn128Pairing(ethjson::spec::builtin::AltBn128Pairing { - base: 100_000, - pair: 80_000, - eip1108_transition_base: 45_000, - eip1108_transition_pair: 34_000, - }), - activate_at: Some(Uint(U256::from(10))), - eip1108_transition: Some(Uint(U256::from(20))), - }); + pricing: map![ + 10 => PricingAt { + info: None, + price: JsonPricing::AltBn128Pairing(JsonAltBn128PairingPricing { + base: 100_000, + pair: 80_000, + }), + }, + 20 => PricingAt { + info: None, + price: JsonPricing::AltBn128Pairing(JsonAltBn128PairingPricing { + base: 45_000, + pair: 34_000, + }), + } + ], + }).unwrap(); assert_eq!(b.cost(&[0; 192 * 3], 10), U256::from(340_000), "80 000 * 3 + 100 000 == 340 000"); assert_eq!(b.cost(&[0; 192 * 7], 20), U256::from(283_000), "34 000 * 7 + 45 000 == 283 000"); @@ -1321,15 +1396,25 @@ use hex_literal::hex; #[test] fn bn128_add_eip1108_transition() { - let b = Builtin::from(ethjson::spec::Builtin { + let b = Builtin::try_from(JsonBuiltin { name: "alt_bn128_add".to_owned(), - pricing: ethjson::spec::Pricing::AltBn128ConstOperations(ethjson::spec::builtin::AltBn128ConstOperations { - price: 500, - eip1108_transition_price: 150, - }), - activate_at: Some(Uint(U256::from(10))), - eip1108_transition: Some(Uint(U256::from(20))), - }); + pricing: map![ + 10 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 500, + word: 0, + }), + }, + 20 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 150, + word: 0, + }), + } + ], + }).unwrap(); assert_eq!(b.cost(&[0; 192], 10), U256::from(500)); assert_eq!(b.cost(&[0; 10], 20), U256::from(150), "after istanbul hardfork gas cost for add should be 150"); @@ -1337,17 +1422,99 @@ use hex_literal::hex; #[test] fn bn128_mul_eip1108_transition() { - let b = Builtin::from(ethjson::spec::Builtin { + let b = Builtin::try_from(JsonBuiltin { name: "alt_bn128_mul".to_owned(), - pricing: ethjson::spec::Pricing::AltBn128ConstOperations(ethjson::spec::builtin::AltBn128ConstOperations { - price: 40_000, - eip1108_transition_price: 6000, - }), - activate_at: Some(Uint(U256::from(10))), - eip1108_transition: Some(Uint(U256::from(20))), - }); + pricing: map![ + 10 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 40_000, + word: 0, + }), + }, + 20 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 6_000, + word: 0, + }), + } + ], + }).unwrap(); assert_eq!(b.cost(&[0; 192], 10), U256::from(40_000)); assert_eq!(b.cost(&[0; 10], 20), U256::from(6_000), "after istanbul hardfork gas cost for mul should be 6 000"); } + + + #[test] + fn multimap_use_most_recent_on_activate() { + let b = Builtin::try_from(JsonBuiltin { + name: "alt_bn128_mul".to_owned(), + pricing: map![ + 10 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 40_000, + word: 0, + }), + }, + 20 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 6_000, + word: 0, + }) + }, + 100 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 1_337, + word: 0, + }) + } + ] + }).unwrap(); + + assert_eq!(b.cost(&[0; 2], 0), U256::zero(), "not activated yet; should be zero"); + assert_eq!(b.cost(&[0; 3], 10), U256::from(40_000), "use price #1"); + assert_eq!(b.cost(&[0; 4], 20), U256::from(6_000), "use price #2"); + assert_eq!(b.cost(&[0; 1], 99), U256::from(6_000), "use price #2"); + assert_eq!(b.cost(&[0; 1], 100), U256::from(1_337), "use price #3"); + assert_eq!(b.cost(&[0; 1], u64::max_value()), U256::from(1_337), "use price #3 indefinitely"); + } + + + #[test] + fn multimap_use_last_with_same_activate_at() { + let b = Builtin::try_from(JsonBuiltin { + name: "alt_bn128_mul".to_owned(), + pricing: map![ + 1 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 40_000, + word: 0, + }), + }, + 1 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 6_000, + word: 0, + }), + }, + 1 => PricingAt { + info: None, + price: JsonPricing::Linear(JsonLinearPricing { + base: 1_337, + word: 0, + }), + } + ], + }).unwrap(); + + assert_eq!(b.cost(&[0; 1], 0), U256::from(0), "not activated yet"); + assert_eq!(b.cost(&[0; 1], 1), U256::from(1_337), "use price #3"); + } } diff --git a/ethcore/call-contract/Cargo.toml b/ethcore/call-contract/Cargo.toml index 7ee9bb7e65..03bd32475e 100644 --- a/ethcore/call-contract/Cargo.toml +++ b/ethcore/call-contract/Cargo.toml @@ -8,5 +8,5 @@ edition = "2018" [dependencies] types = { path = "../types", package = "common-types" } -ethereum-types = "0.4" +ethereum-types = "0.8.0" bytes = { version = "0.1", package = "parity-bytes" } diff --git a/ethcore/call-contract/src/call_contract.rs b/ethcore/call-contract/src/call_contract.rs index 8b042f0833..68d7fefafc 100644 --- a/ethcore/call-contract/src/call_contract.rs +++ b/ethcore/call-contract/src/call_contract.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Provides CallContract and RegistryInfo traits +//! Provides CallContract trait use bytes::Bytes; use ethereum_types::Address; @@ -23,11 +23,10 @@ use types::ids::BlockId; /// Provides `call_contract` method pub trait CallContract { /// Like `call`, but with various defaults. Designed to be used for calling contracts. - fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result; -} - -/// Provides information on a blockchain service and it's registry -pub trait RegistryInfo { - /// Get the address of a particular blockchain service, if available. - fn registry_address(&self, name: String, block: BlockId) -> Option
; + fn call_contract( + &self, + block_id: BlockId, + address: Address, + data: Bytes + ) -> Result; } diff --git a/ethcore/call-contract/src/lib.rs b/ethcore/call-contract/src/lib.rs index 1cbfb11378..a3f058798a 100644 --- a/ethcore/call-contract/src/lib.rs +++ b/ethcore/call-contract/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/client-traits/Cargo.toml b/ethcore/client-traits/Cargo.toml new file mode 100644 index 0000000000..e891539397 --- /dev/null +++ b/ethcore/client-traits/Cargo.toml @@ -0,0 +1,22 @@ +[package] +description = "Trait definitions relative the ethereum client" +name = "client-traits" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +account-state = { path = "../account-state" } +blockchain = { package = "ethcore-blockchain", path = "../blockchain" } +bytes = { package = "parity-bytes", version = "0.1.0" } +call-contract = { package = "ethcore-call-contract", path = "../call-contract" } +common-types = { path = "../types" } +ethcore-db = { path = "../db" } +ethcore-miner = { path = "../../miner" } +ethereum-types = "0.8.0" +kvdb = "0.3.1" +registrar = { path = "../../util/registrar" } +stats = { path = "../../util/stats" } +trace = { path = "../trace" } +vm = { path = "../vm" } diff --git a/ethcore/client-traits/src/lib.rs b/ethcore/client-traits/src/lib.rs new file mode 100644 index 0000000000..3968d3577b --- /dev/null +++ b/ethcore/client-traits/src/lib.rs @@ -0,0 +1,578 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +use std::{ + collections::BTreeMap, + sync::Arc, +}; + +use account_state::state::StateInfo; +use blockchain::BlockProvider; +use bytes::Bytes; +use call_contract::CallContract; +use registrar::RegistrarClient; +use common_types::{ + basic_account::BasicAccount, + block_status::BlockStatus, + blockchain_info::BlockChainInfo, + BlockNumber, + call_analytics::CallAnalytics, + chain_notify::{NewBlocks, ChainMessageType}, + client_types::Mode, + encoded, + engines::{epoch::Transition as EpochTransition, machine::Executed}, + errors::{EthcoreError, EthcoreResult}, + filter::Filter, + header::Header, + ids::{BlockId, TransactionId, TraceId, UncleId}, + log_entry::LocalizedLogEntry, + pruning_info::PruningInfo, + receipt::LocalizedReceipt, + trace_filter::Filter as TraceFilter, + transaction::{self, Action, LocalizedTransaction, CallError, SignedTransaction, UnverifiedTransaction}, + tree_route::TreeRoute, + verification::{VerificationQueueInfo, Unverified}, +}; +use ethereum_types::{Address, H256, U256}; +use ethcore_db::keys::BlockReceipts; +use ethcore_miner::pool::VerifiedTransaction; +use kvdb::DBValue; +use stats; +use trace::{ + FlatTrace, + localized::LocalizedTrace, + VMTrace, +}; +use common_types::{ + data_format::DataFormat, + client_types::StateResult +}; +use vm::{LastHashes, Schedule}; + +/// State information to be used during client query +pub enum StateOrBlock { + /// State to be used, may be pending + State(Box), + + /// Id of an existing block from a chain to get state from + Block(BlockId) +} + +impl From> for StateOrBlock { + fn from(info: Box) -> StateOrBlock { + StateOrBlock::State(info) + } +} + +impl From for StateOrBlock { + fn from(id: BlockId) -> StateOrBlock { + StateOrBlock::Block(id) + } +} + +/// Provides `nonce` and `latest_nonce` methods +pub trait Nonce { + /// Attempt to get address nonce at given block. + /// May not fail on BlockId::Latest. + fn nonce(&self, address: &Address, id: BlockId) -> Option; + + /// Get address nonce at the latest block's state. + fn latest_nonce(&self, address: &Address) -> U256 { + self.nonce(address, BlockId::Latest) + .expect("nonce will return Some when given BlockId::Latest. nonce was given BlockId::Latest. \ + Therefore nonce has returned Some; qed") + } +} + +/// Provides `balance` and `latest_balance` methods +pub trait Balance { + /// Get address balance at the given block's state. + /// + /// May not return None if given BlockId::Latest. + /// Returns None if and only if the block's root hash has been pruned from the DB. + fn balance(&self, address: &Address, state: StateOrBlock) -> Option; + + /// Get address balance at the latest block's state. + fn latest_balance(&self, address: &Address) -> U256 { + self.balance(address, BlockId::Latest.into()) + .expect("balance will return Some if given BlockId::Latest. balance was given BlockId::Latest \ + Therefore balance has returned Some; qed") + } +} + +/// Provides methods to access account info +pub trait AccountData: Nonce + Balance {} + +/// Provides `chain_info` method +pub trait ChainInfo { + /// Get blockchain information. + fn chain_info(&self) -> BlockChainInfo; +} + +/// Provides various information on a block by it's ID +pub trait BlockInfo: Send + Sync { + /// Get raw block header data by block id. + fn block_header(&self, id: BlockId) -> Option; + + /// Get the best block header. + fn best_block_header(&self) -> Header; + + /// Get raw block data by block header hash. + fn block(&self, id: BlockId) -> Option; + + /// Get address code hash at given block's state. + fn code_hash(&self, address: &Address, id: BlockId) -> Option; +} + +/// Provides various information on a transaction by it's ID +pub trait TransactionInfo { + /// Get the hash of block that contains the transaction, if any. + fn transaction_block(&self, id: TransactionId) -> Option; +} + +/// Provides various blockchain information, like block header, chain state etc. +pub trait BlockChain: ChainInfo + BlockInfo + TransactionInfo {} + +/// Do we want to force update sealing? +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ForceUpdateSealing { + /// Ideally you want to use `No` at all times as `Yes` skips `reseal_required` checks. + Yes, + /// Don't skip `reseal_required` checks + No +} +/// Client facilities used by internally sealing Engines. +pub trait EngineClient: Sync + Send + ChainInfo { + /// Make a new block and seal it. + fn update_sealing(&self, force: ForceUpdateSealing); + + /// Submit a seal for a block in the mining queue. + fn submit_seal(&self, block_hash: H256, seal: Vec); + + /// Broadcast a consensus message to the network. + fn broadcast_consensus_message(&self, message: Bytes); + + /// Get the transition to the epoch the given parent hash is part of + /// or transitions to. + /// This will give the epoch that any children of this parent belong to. + /// + /// The block corresponding the the parent hash must be stored already. + fn epoch_transition_for(&self, parent_hash: H256) -> Option; + + /// Attempt to cast the engine client to a full client. + fn as_full_client(&self) -> Option<&dyn BlockChainClient>; + + /// Get a block number by ID. + fn block_number(&self, id: BlockId) -> Option; + + /// Get raw block header data by block id. + fn block_header(&self, id: BlockId) -> Option; +} + +/// Provides methods to import block into blockchain +pub trait ImportBlock { + /// Import a block into the blockchain. + fn import_block(&self, block: Unverified) -> EthcoreResult; + + /// Triggered by a message from a block queue when the block is ready for insertion. + /// Returns the number of blocks imported. + fn import_verified_blocks(&self) -> usize; +} + +/// 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: Unverified, receipts_bytes: Bytes) -> EthcoreResult; + + /// Queue consensus engine message. + fn queue_consensus_message(&self, message: Bytes); +} + +/// Implement this for clients that need logic to decide when/how to advance. +pub trait Tick { + /// Tick the client + fn tick(&self, _prevent_sleep: bool) {} +} + +impl Tick for () {} + +/// Provides recently seen bad blocks. +pub trait BadBlocks { + /// Returns a list of blocks that were recently not imported because they were invalid. + fn bad_blocks(&self) -> Vec<(Unverified, String)>; +} + + +/// Blockchain database client. Owns and manages a blockchain and a block queue. +pub trait BlockChainClient: + Sync + Send + AccountData + BlockChain + CallContract + RegistrarClient + + ImportBlock + IoClient + BadBlocks +{ + /// Look up the block number for the given block ID. + fn block_number(&self, id: BlockId) -> Option; + + /// Get raw block body data by block id. + /// Block body is an RLP list of two items: uncles and transactions. + fn block_body(&self, id: BlockId) -> Option; + + /// Get block status by block header hash. + fn block_status(&self, id: BlockId) -> BlockStatus; + + /// Get block total difficulty. + fn block_total_difficulty(&self, id: BlockId) -> Option; + + /// Attempt to get address storage root at given block. + /// May not fail on BlockId::Latest. + fn storage_root(&self, address: &Address, id: BlockId) -> Option; + + /// Get block hash. + fn block_hash(&self, id: BlockId) -> Option; + + /// Get address code at given block's state. + fn code(&self, address: &Address, state: StateOrBlock) -> StateResult>; + + /// Get address code at the latest block's state. + fn latest_code(&self, address: &Address) -> Option { + match self.code(address, BlockId::Latest.into()) { + StateResult::Missing => panic!("code will return Some if given BlockId::Latest; qed"), + StateResult::Some(t) => t, + } + } + + /// Get a reference to the `BlockProvider`. + fn chain(&self) -> Arc; + + /// Get block queue information. + fn queue_info(&self) -> VerificationQueueInfo; + + /// Get address code hash at given block's state. + + /// Get value of the storage at given position at the given block's state. + /// + /// May not return None if given BlockId::Latest. + /// Returns None if and only if the block's root hash has been pruned from the DB. + fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option; + + /// Get value of the storage at given position at the latest block's state. + fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 { + self.storage_at(address, position, BlockId::Latest.into()) + .expect("storage_at will return Some if given BlockId::Latest. storage_at was given BlockId::Latest. \ + Therefore storage_at has returned Some; qed") + } + + /// Get a list of all accounts in the block `id`, if fat DB is in operation, otherwise `None`. + /// If `after` is set the list starts with the following item. + fn list_accounts(&self, id: BlockId, after: Option<&Address>, count: u64) -> Option>; + + /// Get a list of all storage keys in the block `id`, if fat DB is in operation, otherwise `None`. + /// If `after` is set the list starts with the following item. + fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: Option) -> Option>; + + /// Get transaction with given hash. + fn transaction(&self, id: TransactionId) -> Option; + + /// Get uncle with given id. + fn uncle(&self, id: UncleId) -> Option; + + /// Get transaction receipt with given hash. + fn transaction_receipt(&self, id: TransactionId) -> Option; + + /// Get localized receipts for all transaction in given block. + fn localized_block_receipts(&self, id: BlockId) -> Option>; + + /// Get a tree route between `from` and `to`. + /// See `BlockChain::tree_route`. + fn tree_route(&self, from: &H256, to: &H256) -> Option; + + /// Get all possible uncle hashes for a block. + fn find_uncles(&self, hash: &H256) -> Option>; + + /// Get latest state node + fn state_data(&self, hash: &H256) -> Option; + + /// Get block receipts data by block header hash. + fn block_receipts(&self, hash: &H256) -> Option; + + /// Returns true if block queue is empty. + fn is_queue_empty(&self) -> bool { + self.queue_info().is_empty() + } + + /// Clear block queue and abort all import activity. + fn clear_queue(&self); + + /// Returns logs matching given filter. If one of the filtering block cannot be found, returns the block id that caused the error. + fn logs(&self, filter: Filter) -> Result, BlockId>; + + /// Replays a given transaction for inspection. + fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result, CallError>; + + /// Replays all the transactions in a given block for inspection. + fn replay_block_transactions(&self, block: BlockId, analytics: CallAnalytics) -> Result)>>, CallError>; + + /// Returns traces matching given filter. + fn filter_traces(&self, filter: TraceFilter) -> Option>; + + /// Returns trace with given id. + fn trace(&self, trace: TraceId) -> Option; + + /// Returns traces created by transaction. + fn transaction_traces(&self, trace: TransactionId) -> Option>; + + /// Returns traces created by transaction from block. + fn block_traces(&self, trace: BlockId) -> Option>; + + /// Get last hashes starting from best block. + fn last_hashes(&self) -> LastHashes; + + /// List all ready transactions that should be propagated to other peers. + fn transactions_to_propagate(&self) -> Vec>; + + /// Sorted list of transaction gas prices from at least last sample_size blocks. + fn gas_price_corpus(&self, sample_size: usize) -> stats::Corpus { + let mut h = self.chain_info().best_block_hash; + let mut corpus = Vec::new(); + while corpus.is_empty() { + for _ in 0..sample_size { + let block = match self.block(BlockId::Hash(h)) { + Some(block) => block, + None => return corpus.into(), + }; + + if block.number() == 0 { + return corpus.into(); + } + for t in block.transaction_views().iter() { + corpus.push( t.gas_price() ) + } + h = block.parent_hash().clone(); + } + } + corpus.into() + } + + /// Get the preferred chain ID to sign on + fn signing_chain_id(&self) -> Option; + + /// Get the mode. + fn mode(&self) -> Mode; + + /// Set the mode. + fn set_mode(&self, mode: Mode); + + /// Get the chain spec name. + fn spec_name(&self) -> String; + + /// Set the chain via a spec name. + fn set_spec_name(&self, spec_name: String) -> Result<(), ()>; + + /// Disable the client from importing blocks. This cannot be undone in this session and indicates + /// that a subsystem has reason to believe this executable incapable of syncing the chain. + fn disable(&self); + + /// Returns engine-related extra info for `BlockId`. + fn block_extra_info(&self, id: BlockId) -> Option>; + + /// Returns engine-related extra info for `UncleId`. + fn uncle_extra_info(&self, id: UncleId) -> Option>; + + /// Returns information about pruning/data availability. + fn pruning_info(&self) -> PruningInfo; + + /// Returns a transaction signed with the key configured in the engine signer. + fn create_transaction(&self, tx_request: TransactionRequest) -> Result; + + /// Schedule state-altering transaction to be executed on the next pending + /// block with the given gas and nonce parameters. + fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error>; +} + +/// The data required for a `Client` to create a transaction. +/// +/// Gas limit, gas price, or nonce can be set explicitly, e.g. to create service +/// transactions with zero gas price, or sequences of transactions with consecutive nonces. +pub struct TransactionRequest { + pub action: Action, + pub data: Bytes, + pub gas: Option, + pub gas_price: Option, + pub nonce: Option, +} + +impl TransactionRequest { + /// Creates a request to call a contract at `address` with the specified call data. + pub fn call(address: Address, data: Bytes) -> TransactionRequest { + TransactionRequest { + action: Action::Call(address), + data, + gas: None, + gas_price: None, + nonce: None, + } + } + + /// Creates a request to create a new contract, with the specified bytecode. + pub fn create(data: Bytes) -> TransactionRequest { + TransactionRequest { + action: Action::Create, + data, + gas: None, + gas_price: None, + nonce: None, + } + } + + /// Sets a gas limit. If this is not specified, a sensible default is used. + pub fn gas(mut self, gas: U256) -> TransactionRequest { + self.gas = Some(gas); + self + } + + /// Sets a gas price. If this is not specified, a sensible default is used. + pub fn gas_price(mut self, gas_price: U256) -> TransactionRequest { + self.gas_price = Some(gas_price); + self + } + + /// Sets a nonce. If this is not specified, the appropriate latest nonce for the author is used. + pub fn nonce(mut self, nonce: U256) -> TransactionRequest { + self.nonce = Some(nonce); + self + } +} + +/// resets the blockchain +pub trait BlockChainReset { + /// reset to best_block - n + fn reset(&self, num: u32) -> Result<(), String>; + + /// Number of eras kept in a journal before they are pruned + fn pruning_history(&self) -> u64; +} + + +/// Provides `latest_schedule` method +pub trait ScheduleInfo { + /// Returns latest schedule. + fn latest_schedule(&self) -> Schedule; +} + +/// Provides methods to access chain state +pub trait StateClient { + /// Type representing chain state + type State: StateInfo; + + /// Get a copy of the best block's state and header. + fn latest_state_and_header(&self) -> (Self::State, Header); + + /// Attempt to get a copy of a specific block's final state. + /// + /// This will not fail if given BlockId::Latest. + /// Otherwise, this can fail (but may not) if the DB prunes state or the block + /// is unknown. + fn state_at(&self, id: BlockId) -> Option; +} + +/// Extended client interface for providing proofs of the state. +pub trait ProvingBlockChainClient: BlockChainClient { + /// Prove account storage at a specific block id. + /// + /// Both provided keys assume a secure trie. + /// Returns a vector of raw trie nodes (in order from the root) proving the storage query. + fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec, H256)>; + + /// Prove account existence at a specific block id. + /// The key is the keccak hash of the account's address. + /// Returns a vector of raw trie nodes (in order from the root) proving the query. + fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec, BasicAccount)>; + + /// Prove execution of a transaction at the given block. + /// Returns the output of the call and a vector of database items necessary + /// to reproduce it. + fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec)>; + + /// Get an epoch change signal by block hash. + fn epoch_signal(&self, hash: H256) -> Option>; +} + +/// External database restoration handler +pub trait DatabaseRestore: Send + Sync { + /// Restart with a new backend. Takes ownership of passed database and moves it to a new location. + fn restore_db(&self, new_db: &str) -> Result<(), EthcoreError>; +} + +/// Represents what has to be handled by actor listening to chain events +pub trait ChainNotify: Send + Sync { + /// fires when chain has new blocks. + fn new_blocks(&self, _new_blocks: NewBlocks) { + // does nothing by default + } + + /// fires when chain achieves active mode + fn start(&self) { + // does nothing by default + } + + /// fires when chain achieves passive mode + fn stop(&self) { + // does nothing by default + } + + /// fires when chain broadcasts a message + fn broadcast(&self, _message_type: ChainMessageType) { + // does nothing by default + } + + /// fires when new block is about to be imported + /// implementations should be light + fn block_pre_import(&self, _bytes: &Bytes, _hash: &H256, _difficulty: &U256) { + // does nothing by default + } + + /// fires when new transactions are received from a peer + fn transactions_received(&self, _txs: &[UnverifiedTransaction], _peer_id: usize) { + // does nothing by default + } +} + +/// Provides a method for importing/exporting blocks +pub trait ImportExportBlocks { + /// Export blocks to destination, with the given from, to and format argument. + /// destination could be a file or stdout. + /// If the format is hex, each block is written on a new line. + /// For binary exports, all block data is written to the same line. + fn export_blocks<'a>( + &self, + destination: Box, + from: BlockId, + to: BlockId, + format: Option + ) -> Result<(), String>; + + /// Import blocks from destination, with the given format argument + /// Source could be a file or stdout. + /// For hex format imports, it attempts to read the blocks on a line by line basis. + /// For binary format imports, reads the 8 byte RLP header in order to decode the block + /// length to be read. + fn import_blocks<'a>( + &self, + source: Box, + format: Option + ) -> Result<(), String>; +} diff --git a/ethcore/db/Cargo.toml b/ethcore/db/Cargo.toml index 53ec9f7b88..af1eb9e9b9 100644 --- a/ethcore/db/Cargo.toml +++ b/ethcore/db/Cargo.toml @@ -9,9 +9,9 @@ edition = "2018" [dependencies] common-types = { path = "../types" } -ethereum-types = "0.4" -heapsize = "0.4" -kvdb = "0.1" -parking_lot = "0.7" -rlp = { version = "0.3.0", features = ["ethereum"] } +ethereum-types = "0.8.0" +kvdb = "0.3.1" +parity-util-mem = "0.3.0" +parking_lot = "0.9" +rlp = "0.4.0" rlp_derive = { path = "../../util/rlp-derive" } diff --git a/ethcore/db/src/cache_manager.rs b/ethcore/db/src/cache_manager.rs index 34a02d7213..b08f4c0bc2 100644 --- a/ethcore/db/src/cache_manager.rs +++ b/ethcore/db/src/cache_manager.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/db/src/db.rs b/ethcore/db/src/db.rs index c00f63eac2..811666ec7c 100644 --- a/ethcore/db/src/db.rs +++ b/ethcore/db/src/db.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ //! Database utilities and definitions. -use std::ops::Deref; +use std::convert::AsRef; use std::hash::Hash; use std::collections::HashMap; use parking_lot::RwLock; @@ -24,25 +24,27 @@ use kvdb::{DBTransaction, KeyValueDB}; use rlp; -// database columns +// Database column indexes. /// Column for State -pub const COL_STATE: Option = Some(0); +pub const COL_STATE: u32 = 0; /// Column for Block headers -pub const COL_HEADERS: Option = Some(1); +pub const COL_HEADERS: u32 = 1; /// Column for Block bodies -pub const COL_BODIES: Option = Some(2); +pub const COL_BODIES: u32 = 2; /// Column for Extras -pub const COL_EXTRA: Option = Some(3); +pub const COL_EXTRA: u32 = 3; /// Column for Traces -pub const COL_TRACE: Option = Some(4); +pub const COL_TRACE: u32 = 4; /// Column for the empty accounts bloom filter. -pub const COL_ACCOUNT_BLOOM: Option = Some(5); +pub const COL_ACCOUNT_BLOOM: u32 = 5; /// Column for general information from the local node which can persist. -pub const COL_NODE_INFO: Option = Some(6); +pub const COL_NODE_INFO: u32 = 6; /// Column for the light client chain. -pub const COL_LIGHT_CHAIN: Option = Some(7); +pub const COL_LIGHT_CHAIN: u32 = 7; +/// Column for the private transactions state. +pub const COL_PRIVATE_TRANSACTIONS_STATE: u32 = 8; /// Number of columns in DB -pub const NUM_COLUMNS: Option = Some(8); +pub const NUM_COLUMNS: u32 = 9; /// Modes for updating caches. #[derive(Clone, Copy)] @@ -82,7 +84,7 @@ impl Cache for HashMap where K: Hash + Eq { /// Should be used to get database key associated with given value. pub trait Key { /// The db key associated with this value. - type Target: Deref; + type Target: AsRef<[u8]>; /// Returns db key. fn key(&self) -> Self::Target; @@ -91,16 +93,25 @@ pub trait Key { /// Should be used to write value into database. pub trait Writable { /// Writes the value into the database. - fn write(&mut self, col: Option, key: &Key, value: &T) where T: rlp::Encodable, R: Deref; + fn write(&mut self, col: u32, key: &dyn Key, value: &T) where T: rlp::Encodable, R: AsRef<[u8]>; - /// Deletes key from the databse. - fn delete(&mut self, col: Option, key: &Key) where T: rlp::Encodable, R: Deref; + /// Deletes key from the database. + fn delete(&mut self, col: u32, key: &dyn Key) where T: rlp::Encodable, R: AsRef<[u8]>; /// Writes the value into the database and updates the cache. - fn write_with_cache(&mut self, col: Option, cache: &mut Cache, key: K, value: T, policy: CacheUpdatePolicy) where - K: Key + Hash + Eq, - T: rlp::Encodable, - R: Deref { + fn write_with_cache( + &mut self, + col: u32, + cache: &mut dyn Cache, + key: K, + value: T, + policy: CacheUpdatePolicy + ) + where + K: Key + Hash + Eq, + T: rlp::Encodable, + R: AsRef<[u8]> + { self.write(col, &key, &value); match policy { CacheUpdatePolicy::Overwrite => { @@ -113,10 +124,18 @@ pub trait Writable { } /// Writes the values into the database and updates the cache. - fn extend_with_cache(&mut self, col: Option, cache: &mut Cache, values: HashMap, policy: CacheUpdatePolicy) where - K: Key + Hash + Eq, - T: rlp::Encodable, - R: Deref { + fn extend_with_cache( + &mut self, + col: u32, + cache: &mut dyn Cache, + values: HashMap, + policy: CacheUpdatePolicy + ) + where + K: Key + Hash + Eq, + T: rlp::Encodable, + R: AsRef<[u8]> + { match policy { CacheUpdatePolicy::Overwrite => { for (key, value) in values { @@ -134,10 +153,18 @@ pub trait Writable { } /// Writes and removes the values into the database and updates the cache. - fn extend_with_option_cache(&mut self, col: Option, cache: &mut Cache>, values: HashMap>, policy: CacheUpdatePolicy) where - K: Key + Hash + Eq, - T: rlp::Encodable, - R: Deref { + fn extend_with_option_cache( + &mut self, + col: u32, + cache: &mut dyn Cache>, + values: HashMap>, + policy: CacheUpdatePolicy + ) + where + K: Key + Hash + Eq, + T: rlp::Encodable, + R: AsRef<[u8]> + { match policy { CacheUpdatePolicy::Overwrite => { for (key, value) in values { @@ -165,12 +192,12 @@ pub trait Writable { /// Should be used to read values from database. pub trait Readable { /// Returns value for given key. - fn read(&self, col: Option, key: &Key) -> Option where + fn read(&self, col: u32, key: &dyn Key) -> Option where T: rlp::Decodable, - R: Deref; + R: AsRef<[u8]>; /// Returns value for given key either in cache or in database. - fn read_with_cache(&self, col: Option, cache: &RwLock, key: &K) -> Option where + fn read_with_cache(&self, col: u32, cache: &RwLock, key: &K) -> Option where K: Key + Eq + Hash + Clone, T: Clone + rlp::Decodable, C: Cache { @@ -188,13 +215,36 @@ pub trait Readable { }) } + /// Returns value for given key either in two-layered cache or in database. + fn read_with_two_layer_cache( + &self, + col: u32, + l1_cache: &RwLock, + l2_cache: &RwLock, + key: &K + ) -> Option + where + K: Key + Eq + Hash + Clone, + T: Clone + rlp::Decodable, + C: Cache + { + { + let read = l1_cache.read(); + if let Some(v) = read.get(key) { + return Some(v.clone()); + } + } + + self.read_with_cache(col, l2_cache, key) + } + /// Returns true if given value exists. - fn exists(&self, col: Option, key: &Key) -> bool where R: Deref; + fn exists(&self, col: u32, key: &dyn Key) -> bool where R: AsRef<[u8]>; /// Returns true if given value exists either in cache or in database. - fn exists_with_cache(&self, col: Option, cache: &RwLock, key: &K) -> bool where + fn exists_with_cache(&self, col: u32, cache: &RwLock, key: &K) -> bool where K: Eq + Hash + Key, - R: Deref, + R: AsRef<[u8]>, C: Cache { { let read = cache.read(); @@ -208,31 +258,31 @@ pub trait Readable { } impl Writable for DBTransaction { - fn write(&mut self, col: Option, key: &Key, value: &T) where T: rlp::Encodable, R: Deref { - self.put(col, &key.key(), &rlp::encode(value)); + fn write(&mut self, col: u32, key: &dyn Key, value: &T) where T: rlp::Encodable, R: AsRef<[u8]> { + self.put(col, key.key().as_ref(), &rlp::encode(value)); } - fn delete(&mut self, col: Option, key: &Key) where T: rlp::Encodable, R: Deref { - self.delete(col, &key.key()); + fn delete(&mut self, col: u32, key: &dyn Key) where T: rlp::Encodable, R: AsRef<[u8]> { + self.delete(col, key.key().as_ref()); } } impl Readable for KVDB { - 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])) + fn read(&self, col: u32, key: &dyn Key) -> Option + where T: rlp::Decodable, R: AsRef<[u8]> { + self.get(col, key.key().as_ref()) + .expect(&format!("db get failed, key: {:?}", key.key().as_ref())) .map(|v| rlp::decode(&v).expect("decode db value failed") ) } - fn exists(&self, col: Option, key: &Key) -> bool where R: Deref { - let result = self.get(col, &key.key()); + fn exists(&self, col: u32, key: &dyn Key) -> bool where R: AsRef<[u8]> { + let result = self.get(col, key.key().as_ref()); match result { Ok(v) => v.is_some(), Err(err) => { - panic!("db get failed, key: {:?}, err: {:?}", &key.key() as &[u8], err); + panic!("db get failed, key: {:?}, err: {:?}", key.key().as_ref(), err); } } } diff --git a/ethcore/db/src/keys.rs b/ethcore/db/src/keys.rs index 96ecde85fb..a13396ef0c 100644 --- a/ethcore/db/src/keys.rs +++ b/ethcore/db/src/keys.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,13 +17,13 @@ //! Blockchain DB extras. use std::io::Write; -use std::ops; +use std::convert::AsRef; use common_types::BlockNumber; use common_types::engines::epoch::Transition as EpochTransition; use common_types::receipt::Receipt; use ethereum_types::{H256, H264, U256}; -use heapsize::HeapSizeOf; +use parity_util_mem::MallocSizeOf; use kvdb::PREFIX_LEN as DB_PREFIX_LEN; use rlp; use rlp_derive::{RlpEncodableWrapper, RlpDecodableWrapper, RlpEncodable, RlpDecodable}; @@ -49,19 +49,17 @@ pub enum ExtrasIndex { fn with_index(hash: &H256, i: ExtrasIndex) -> H264 { let mut result = H264::default(); - result[0] = i as u8; - (*result)[1..].clone_from_slice(hash); + result.as_bytes_mut()[0] = i as u8; + result.as_bytes_mut()[1..].clone_from_slice(hash.as_bytes()); result } /// Wrapper for block number used as a DB key. pub struct BlockNumberKey([u8; 5]); -impl ops::Deref for BlockNumberKey { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - &self.0 +impl AsRef<[u8]> for BlockNumberKey { + fn as_ref(&self) -> &[u8] { + &self.0[..] } } @@ -123,10 +121,8 @@ pub const EPOCH_KEY_PREFIX: &'static [u8; DB_PREFIX_LEN] = &[ /// Epoch transitions key pub struct EpochTransitionsKey([u8; EPOCH_KEY_LEN]); -impl ops::Deref for EpochTransitionsKey { - type Target = [u8]; - - fn deref(&self) -> &[u8] { &self.0[..] } +impl AsRef<[u8]> for EpochTransitionsKey { + fn as_ref(&self) -> &[u8] { &self.0[..] } } impl Key for u64 { @@ -144,7 +140,7 @@ impl Key for u64 { } /// Familial details concerning a block -#[derive(Debug, Clone)] +#[derive(Debug, Clone, MallocSizeOf)] pub struct BlockDetails { /// Block number pub number: BlockNumber, @@ -199,14 +195,8 @@ impl rlp::Decodable for BlockDetails { } } -impl HeapSizeOf for BlockDetails { - fn heap_size_of_children(&self) -> usize { - self.children.heap_size_of_children() - } -} - /// Represents address of certain transaction within block -#[derive(Debug, PartialEq, Clone, RlpEncodable, RlpDecodable)] +#[derive(Debug, PartialEq, Clone, RlpEncodable, RlpDecodable, MallocSizeOf)] pub struct TransactionAddress { /// Block hash pub block_hash: H256, @@ -214,12 +204,8 @@ pub struct TransactionAddress { pub index: usize } -impl HeapSizeOf for TransactionAddress { - fn heap_size_of_children(&self) -> usize { 0 } -} - /// Contains all block receipts. -#[derive(Clone, RlpEncodableWrapper, RlpDecodableWrapper)] +#[derive(Debug, Clone, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] pub struct BlockReceipts { /// Block receipts pub receipts: Vec, @@ -228,15 +214,7 @@ pub struct BlockReceipts { impl BlockReceipts { /// Create new block receipts wrapper. pub fn new(receipts: Vec) -> Self { - BlockReceipts { - receipts: receipts - } - } -} - -impl HeapSizeOf for BlockReceipts { - fn heap_size_of_children(&self) -> usize { - self.receipts.heap_size_of_children() + BlockReceipts { receipts } } } diff --git a/ethcore/db/src/lib.rs b/ethcore/db/src/lib.rs index 3fdb368a1a..30be0c40a4 100644 --- a/ethcore/db/src/lib.rs +++ b/ethcore/db/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,6 +18,9 @@ #![warn(missing_docs)] +extern crate parity_util_mem as mem; +extern crate parity_util_mem as malloc_size_of; + mod db; pub mod keys; diff --git a/ethcore/engine/Cargo.toml b/ethcore/engine/Cargo.toml new file mode 100644 index 0000000000..fe527ade8b --- /dev/null +++ b/ethcore/engine/Cargo.toml @@ -0,0 +1,31 @@ +[package] +description = "Ethereum engine trait definition" +name = "engine" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +blockchain = { package = "ethcore-blockchain", path = "../blockchain" } +builtin = { path = "../builtin", package = "ethcore-builtin" } +bytes = { package = "parity-bytes", version = "0.1.0" } +client-traits = { path = "../client-traits" } +common-types = { path = "../types" } +ethereum-types = "0.8.0" +parity-crypto = { version = "0.4.2", features = ["publickey"] } +machine = { path = "../machine" } +vm = { path = "../vm" } + +# used from test-helpers +accounts = { package = "ethcore-accounts", path = "../../accounts", optional = true } +log = { version = "0.4.8", optional = true } +ethkey = { path = "../../accounts/ethkey", optional = true } + +[dev-dependencies] +accounts = { package = "ethcore-accounts", path = "../../accounts" } +ethkey = { path = "../../accounts/ethkey" } +log = "0.4.8" + +[features] +test-helpers = ["accounts", "log", "ethkey"] diff --git a/ethcore/src/engines/mod.rs b/ethcore/engine/src/engine.rs similarity index 52% rename from ethcore/src/engines/mod.rs rename to ethcore/engine/src/engine.rs index 5124f079db..3fa13a0747 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/engine/src/engine.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,155 +16,41 @@ //! Consensus engine specification and basic implementations. -mod authority_round; -mod basic_authority; -mod clique; -mod instant_seal; -mod null_engine; -mod validator_set; - -pub mod block_reward; -pub mod signer; - -pub use self::authority_round::AuthorityRound; -pub use self::basic_authority::BasicAuthority; -pub use self::instant_seal::{InstantSeal, InstantSealParams}; -pub use self::null_engine::NullEngine; -pub use self::signer::EngineSigner; -pub use self::clique::Clique; - -// TODO [ToDr] Remove re-export (#10130) -pub use types::engines::ForkChoice; -pub use types::engines::epoch::{self, Transition as EpochTransition}; - use std::sync::{Weak, Arc}; -use std::collections::{BTreeMap, HashMap}; -use std::{fmt, error}; +use std::collections::BTreeMap; use builtin::Builtin; -use vm::{EnvInfo, Schedule, CreateContractAddress, CallType, ActionValue}; -use error::Error; -use types::BlockNumber; -use types::header::{Header, ExtendedHeader}; -use snapshot::SnapshotComponents; -use spec::CommonParams; -use types::transaction::{self, UnverifiedTransaction, SignedTransaction}; - -use ethkey::{Signature}; -use machine::{self, Machine, AuxiliaryRequest, AuxiliaryData}; -use ethereum_types::{H64, H256, U256, Address}; -use unexpected::{Mismatch, OutOfBounds}; -use bytes::Bytes; -use types::ancestry_action::AncestryAction; -use block::ExecutedBlock; - -/// Default EIP-210 contract code. -/// As defined in https://github.com/ethereum/EIPs/pull/210 -pub const DEFAULT_BLOCKHASH_CONTRACT: &'static str = "73fffffffffffffffffffffffffffffffffffffffe33141561006a5760014303600035610100820755610100810715156100455760003561010061010083050761010001555b6201000081071515610064576000356101006201000083050761020001555b5061013e565b4360003512151561008457600060405260206040f361013d565b61010060003543031315156100a857610100600035075460605260206060f361013c565b6101006000350715156100c55762010000600035430313156100c8565b60005b156100ea576101006101006000350507610100015460805260206080f361013b565b620100006000350715156101095763010000006000354303131561010c565b60005b1561012f57610100620100006000350507610200015460a052602060a0f361013a565b600060c052602060c0f35b5b5b5b5b"; -/// The number of generations back that uncles can be. -pub const MAX_UNCLE_AGE: usize = 6; - -/// Voting errors. -#[derive(Debug)] -pub enum EngineError { - /// Signature or author field does not belong to an authority. - NotAuthorized(Address), - /// The same author issued different votes at the same step. - DoubleVote(Address), - /// The received block is from an incorrect proposer. - NotProposer(Mismatch
), - /// Message was not expected. - UnexpectedMessage, - /// Seal field has an unexpected size. - BadSealFieldSize(OutOfBounds), - /// Validation proof insufficient. - InsufficientProof(String), - /// Failed system call. - FailedSystemCall(String), - /// Malformed consensus message. - MalformedMessage(String), - /// Requires client ref, but none registered. - RequiresClient, - /// Invalid engine specification or implementation. - InvalidEngine, - /// Requires signer ref, but none registered. - RequiresSigner, - /// Checkpoint is missing - CliqueMissingCheckpoint(H256), - /// Missing vanity data - CliqueMissingVanity, - /// Missing signature - CliqueMissingSignature, - /// Missing signers - CliqueCheckpointNoSigner, - /// List of signers is invalid - CliqueCheckpointInvalidSigners(usize), - /// Wrong author on a checkpoint - CliqueWrongAuthorCheckpoint(Mismatch
), - /// Wrong checkpoint authors recovered - CliqueFaultyRecoveredSigners(Vec), - /// Invalid nonce (should contain vote) - CliqueInvalidNonce(H64), - /// The signer signed a block to recently - CliqueTooRecentlySigned(Address), - /// Custom - Custom(String), -} - -impl fmt::Display for EngineError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::EngineError::*; - let msg = match *self { - CliqueMissingCheckpoint(ref hash) => format!("Missing checkpoint block: {}", hash), - CliqueMissingVanity => format!("Extra data is missing vanity data"), - CliqueMissingSignature => format!("Extra data is missing signature"), - CliqueCheckpointInvalidSigners(len) => format!("Checkpoint block list was of length: {} of checkpoint but - it needs to be bigger than zero and a divisible by 20", len), - CliqueCheckpointNoSigner => format!("Checkpoint block list of signers was empty"), - CliqueInvalidNonce(ref mis) => format!("Unexpected nonce {} expected {} or {}", mis, 0_u64, u64::max_value()), - CliqueWrongAuthorCheckpoint(ref oob) => format!("Unexpected checkpoint author: {}", oob), - CliqueFaultyRecoveredSigners(ref mis) => format!("Faulty recovered signers {:?}", mis), - CliqueTooRecentlySigned(ref address) => format!("The signer: {} has signed a block too recently", address), - Custom(ref s) => s.clone(), - DoubleVote(ref address) => format!("Author {} issued too many blocks.", address), - NotProposer(ref mis) => format!("Author is not a current proposer: {}", mis), - NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), - UnexpectedMessage => "This Engine should not be fed messages.".into(), - BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), - InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg), - FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg), - MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg), - RequiresClient => format!("Call requires client but none registered"), - RequiresSigner => format!("Call requires signer but none registered"), - InvalidEngine => format!("Invalid engine specification or implementation"), - }; - - f.write_fmt(format_args!("Engine error ({})", msg)) - } -} - -impl error::Error for EngineError { - fn description(&self) -> &str { - "Engine error" - } -} - -/// Seal type. -#[derive(Debug, PartialEq, Eq)] -pub enum Seal { - /// Proposal seal; should be broadcasted, but not inserted into blockchain. - Proposal(Vec), - /// Regular block seal; should be part of the blockchain. - Regular(Vec), - /// Engine does not generate seal for this block right now. - None, -} +use common_types::{ + BlockNumber, + ancestry_action::AncestryAction, + header::{Header, ExtendedHeader}, + engines::{ + Seal, SealingState, Headers, PendingTransitionStore, + params::CommonParams, + machine as machine_types, + machine::{AuxiliaryData, AuxiliaryRequest}, + }, + errors::{EthcoreError as Error, EngineError}, + snapshot::Snapshotting, + transaction::{self, SignedTransaction, UnverifiedTransaction}, +}; +use client_traits::EngineClient; + +use ethereum_types::{H256, U256, Address}; +use parity_crypto::publickey::Signature; +use machine::{ + Machine, + executed_block::ExecutedBlock, +}; +use vm::{EnvInfo, Schedule, ActionType, ActionValue}; + +use crate::signer::EngineSigner; /// 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; +pub type SystemCall<'a> = dyn FnMut(Address, Vec) -> Result, String> + 'a; /// A system-calling closure. Enacts calls on a block's state with code either from an on-chain contract, or hard-coded EVM or WASM (if enabled on-chain) codes. -pub type SystemOrCodeCall<'a> = FnMut(SystemOrCodeCallKind, Vec) -> Result, String> + 'a; +pub type SystemOrCodeCall<'a> = dyn FnMut(SystemOrCodeCallKind, Vec) -> Result, String> + 'a; /// Kind of SystemOrCodeCall, this is either an on-chain address, or code. #[derive(PartialEq, Debug, Clone)] @@ -176,7 +62,7 @@ pub enum SystemOrCodeCallKind { } /// Default SystemOrCodeCall implementation. -pub fn default_system_or_code_call<'a>(machine: &'a ::machine::EthereumMachine, block: &'a mut ::block::ExecutedBlock) -> impl FnMut(SystemOrCodeCallKind, Vec) -> Result, String> + 'a { +pub fn default_system_or_code_call<'a>(machine: &'a Machine, block: &'a mut ExecutedBlock) -> impl FnMut(SystemOrCodeCallKind, Vec) -> Result, String> + 'a { move |to, data| { let result = match to { SystemOrCodeCallKind::Address(address) => { @@ -196,7 +82,7 @@ pub fn default_system_or_code_call<'a>(machine: &'a ::machine::EthereumMachine, Some(ActionValue::Apparent(U256::zero())), U256::max_value(), Some(data), - Some(CallType::StaticCall), + Some(ActionType::StaticCall), ) }, }; @@ -205,45 +91,39 @@ pub fn default_system_or_code_call<'a>(machine: &'a ::machine::EthereumMachine, } } -/// Type alias for a function we can get headers by hash through. -pub type Headers<'a, H> = Fn(H256) -> Option + 'a; - -/// Type alias for a function we can query pending transitions by block hash through. -pub type PendingTransitionStore<'a> = Fn(H256) -> Option + 'a; - /// Proof dependent on state. -pub trait StateDependentProof: Send + Sync { +pub trait StateDependentProof: Send + Sync { /// Generate a proof, given the state. - fn generate_proof<'a>(&self, state: &machine::Call) -> Result, String>; + fn generate_proof<'a>(&self, state: &machine_types::Call) -> Result, String>; /// Check a proof generated elsewhere (potentially by a peer). // `engine` needed to check state proofs, while really this should // just be state machine params. - fn check_proof(&self, machine: &M, proof: &[u8]) -> Result<(), String>; + fn check_proof(&self, machine: &Machine, proof: &[u8]) -> Result<(), String>; } /// Proof generated on epoch change. -pub enum Proof { +pub enum Proof { /// Known proof (extracted from signal) Known(Vec), /// State dependent proof. - WithState(Arc>), + WithState(Arc), } /// Generated epoch verifier. -pub enum ConstructedVerifier<'a, M: Machine> { +pub enum ConstructedVerifier<'a> { /// Fully trusted verifier. - Trusted(Box>), + Trusted(Box), /// Verifier unconfirmed. Check whether given finality proof finalizes given hash /// under previous epoch. - Unconfirmed(Box>, &'a [u8], H256), + Unconfirmed(Box, &'a [u8], H256), /// Error constructing verifier. Err(Error), } -impl<'a, M: Machine> ConstructedVerifier<'a, M> { +impl<'a> ConstructedVerifier<'a> { /// Convert to a result, indicating that any necessary confirmation has been done /// already. - pub fn known_confirmed(self) -> Result>, Error> { + pub fn known_confirmed(self) -> Result, Error> { match self { ConstructedVerifier::Trusted(v) | ConstructedVerifier::Unconfirmed(v, _, _) => Ok(v), ConstructedVerifier::Err(e) => Err(e), @@ -252,24 +132,24 @@ impl<'a, M: Machine> ConstructedVerifier<'a, M> { } /// Results of a query of whether an epoch change occurred at the given block. -pub enum EpochChange { +pub enum EpochChange { /// Cannot determine until more data is passed. Unsure(AuxiliaryRequest), /// No epoch change. No, /// The epoch will change, with proof. - Yes(Proof), + Yes(Proof), } /// A consensus mechanism for the chain. Generally either proof-of-work or proof-of-stake-based. /// Provides hooks into each of the major parts of block import. -pub trait Engine: Sync + Send { +pub trait Engine: Sync + Send { /// The name of this engine. fn name(&self) -> &str; /// Get access to the underlying state machine. // TODO: decouple. - fn machine(&self) -> &M; + fn machine(&self) -> &Machine; /// The number of additional header fields required for this engine. fn seal_fields(&self, _header: &Header) -> usize { 0 } @@ -289,23 +169,40 @@ pub trait Engine: Sync + Send { &self, _block: &mut ExecutedBlock, _epoch_begin: bool, - _ancestry: &mut Iterator, - ) -> Result<(), M::Error> { + ) -> Result<(), Error> { Ok(()) } /// Block transformation functions, after the transactions. - fn on_close_block(&self, _block: &mut ExecutedBlock) -> Result<(), M::Error> { + fn on_close_block( + &self, + _block: &mut ExecutedBlock, + _parent_header: &Header, + ) -> Result<(), Error> { Ok(()) } /// Allow mutating the header during seal generation. Currently only used by Clique. fn on_seal_block(&self, _block: &mut ExecutedBlock) -> Result<(), Error> { Ok(()) } - /// None means that it requires external input (e.g. PoW) to seal a block. - /// Some(true) means the engine is currently prime for seal generation (i.e. node is the current validator). - /// Some(false) means that the node might seal internally but is not qualified now. - fn seals_internally(&self) -> Option { None } + /// Returns a list of transactions for a new block if we are the author. + /// + /// This is called when the miner prepares a new block that this node will author and seal. It returns a list of + /// transactions that will be added to the block before any other transactions from the queue. + fn generate_engine_transactions(&self, _block: &ExecutedBlock) -> Result, Error> { + Ok(Vec::new()) + } + + /// Returns the engine's current sealing state. + fn sealing_state(&self) -> SealingState { SealingState::External } + + /// Called in `miner.chain_new_blocks` if the engine wishes to `update_sealing` + /// after a block was recently sealed. + /// + /// returns false by default + fn should_reseal_on_update(&self) -> bool { + false + } /// Attempt to seal the block internally. /// @@ -328,25 +225,25 @@ pub trait Engine: Sync + Send { /// /// It is fine to require access to state or a full client for this function, since /// light clients do not generate seals. - fn verify_local_seal(&self, header: &Header) -> Result<(), M::Error>; + fn verify_local_seal(&self, header: &Header) -> Result<(), Error>; /// Phase 1 quick block verification. Only does checks that are cheap. Returns either a null `Ok` or a general error detailing the problem with import. /// The verification module can optionally avoid checking the seal (`check_seal`), if seal verification is disabled this method won't be called. - fn verify_block_basic(&self, _header: &Header) -> Result<(), M::Error> { Ok(()) } + fn verify_block_basic(&self, _header: &Header) -> Result<(), Error> { Ok(()) } /// Phase 2 verification. Perform costly checks such as transaction signatures. Returns either a null `Ok` or a general error detailing the problem with import. /// The verification module can optionally avoid checking the seal (`check_seal`), if seal verification is disabled this method won't be called. - fn verify_block_unordered(&self, _header: &Header) -> Result<(), M::Error> { Ok(()) } + fn verify_block_unordered(&self, _header: &Header) -> Result<(), 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: &Header, _parent: &Header) -> Result<(), M::Error> { Ok(()) } + fn verify_block_family(&self, _header: &Header, _parent: &Header) -> Result<(), 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: &Header) -> Result<(), M::Error> { Ok(()) } + fn verify_block_external(&self, _header: &Header) -> Result<(), Error> { Ok(()) } /// Genesis epoch data. - fn genesis_epoch_data<'a>(&self, _header: &Header, _state: &machine::Call) -> Result, String> { Ok(Vec::new()) } + fn genesis_epoch_data<'a>(&self, _header: &Header, _state: &machine_types::Call) -> Result, String> { Ok(Vec::new()) } /// Whether an epoch change is signalled at the given header but will require finality. /// If a change can be enacted immediately then return `No` from this function but @@ -357,9 +254,7 @@ pub trait Engine: Sync + Send { /// Return `Yes` or `No` when the answer is definitively known. /// /// Should not interact with state. - fn signals_epoch_end<'a>(&self, _header: &Header, _aux: AuxiliaryData<'a>) - -> EpochChange - { + fn signals_epoch_end<'a>(&self, _header: &Header, _aux: AuxiliaryData<'a>) -> EpochChange { EpochChange::No } @@ -401,7 +296,7 @@ pub trait Engine: Sync + Send { /// Create an epoch verifier from validation proof and a flag indicating /// whether finality is required. - fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> ConstructedVerifier<'a, M> { + fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> ConstructedVerifier<'a> { ConstructedVerifier::Trusted(Box::new(NoOp)) } @@ -414,30 +309,19 @@ pub trait Engine: Sync + Send { fn handle_message(&self, _message: &[u8]) -> Result<(), EngineError> { Err(EngineError::UnexpectedMessage) } /// Register a component which signs consensus messages. - fn set_signer(&self, _signer: Box) {} + fn set_signer(&self, _signer: Option>) {} /// 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) {} + fn register_client(&self, _client: Weak) {} /// Trigger next step of the consensus engine. fn step(&self) {} - /// Stops any services that the may hold the Engine and makes it safe to drop. - fn stop(&mut self) {} - - /// Create a factory for building snapshot chunks and restoring from them. - /// Returning `None` indicates that this engine doesn't support snapshot creation. - fn snapshot_components(&self) -> Option> { - None - } - - /// Whether this engine supports warp sync. - fn supports_warp(&self) -> bool { - self.snapshot_components().is_some() - } + /// Snapshot mode for the engine: Unsupported, PoW or PoA + fn snapshot_mode(&self) -> Snapshotting { Snapshotting::Unsupported } /// Return a new open block header timestamp based on the parent timestamp. fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 { @@ -454,37 +338,23 @@ pub trait Engine: Sync + Send { /// 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, _header: &Header, _ancestry: &mut Iterator) -> Vec { + fn ancestry_actions(&self, _header: &Header, _ancestry: &mut dyn Iterator) -> Vec { Vec::new() } - /// Check whether the given new block is the best block, after finalization check. - fn fork_choice(&self, new: &ExtendedHeader, best: &ExtendedHeader) -> ForkChoice; - /// Returns author should used when executing tx's for this block. fn executive_author(&self, header: &Header) -> Result { Ok(*header.author()) } -} -/// Check whether a given block is the best block based on the default total difficulty rule. -pub fn total_difficulty_fork_choice(new: &ExtendedHeader, best: &ExtendedHeader) -> ForkChoice { - if new.total_score() > best.total_score() { - ForkChoice::New - } else { - ForkChoice::Old + /// Overrides the block gas limit. Whenever this returns `Some` for a header, the next block's gas limit must be + /// exactly that value. + fn gas_limit_override(&self, _header: &Header) -> Option { + None } -} -/// Common type alias for an engine coupled with an Ethereum-like state machine. -// TODO: make this a _trait_ alias when those exist. -// fortunately the effect is largely the same since engines are mostly used -// via trait objects. -pub trait EthEngine: Engine<::machine::EthereumMachine> { /// Get the general parameters of the chain. - fn params(&self) -> &CommonParams { - self.machine().params() - } + fn params(&self) -> &CommonParams; /// Get the EVM schedule for the given block number. fn schedule(&self, block_number: BlockNumber) -> Schedule { @@ -503,9 +373,7 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> { } /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. - fn maximum_extra_data_size(&self) -> usize { - self.machine().maximum_extra_data_size() - } + fn maximum_extra_data_size(&self) -> usize { self.params().maximum_extra_data_size } /// The nonce with which accounts begin at given block. fn account_start_nonce(&self, block: BlockNumber) -> U256 { @@ -517,23 +385,6 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> { self.machine().signing_chain_id(env_info) } - /// Returns new contract address generation scheme at given block number. - fn create_address_scheme(&self, number: BlockNumber) -> CreateContractAddress { - self.machine().create_address_scheme(number) - } - - /// Verify a particular transaction is valid. - /// - /// Unordered verification doesn't rely on the transaction execution order, - /// i.e. it should only verify stuff that doesn't assume any previous transactions - /// 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) - } - /// Perform basic/cheap transaction verification. /// /// This should include all cheap checks that can be done before @@ -548,28 +399,25 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> { self.machine().verify_transaction_basic(t, header) } - /// Additional information. - 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. -impl EthEngine for T where T: Engine<::machine::EthereumMachine> { } + /// The configured minimum gas limit. + fn min_gas_limit(&self) -> U256 { + self.params().min_gas_limit + } +} /// Verifier for all blocks within an epoch with self-contained state. -pub trait EpochVerifier: Send + Sync { +pub trait EpochVerifier: Send + Sync { /// Lightly verify the next block header. /// This may not be a header belonging to a different epoch. - fn verify_light(&self, header: &Header) -> Result<(), M::Error>; + fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } /// Perform potentially heavier checks on the next block header. - fn verify_heavy(&self, header: &Header) -> Result<(), M::Error> { + fn verify_heavy(&self, header: &Header) -> Result<(), Error> { self.verify_light(header) } @@ -584,6 +432,4 @@ pub trait EpochVerifier: Send + Sync { /// Special "no-op" verifier for stateless, epoch-less engines. pub struct NoOp; -impl EpochVerifier for NoOp { - fn verify_light(&self, _header: &Header) -> Result<(), M::Error> { Ok(()) } -} +impl EpochVerifier for NoOp {} diff --git a/json/src/blockchain/transaction.rs b/ethcore/engine/src/lib.rs similarity index 62% rename from json/src/blockchain/transaction.rs rename to ethcore/engine/src/lib.rs index 4e519f394e..3234790143 100644 --- a/json/src/blockchain/transaction.rs +++ b/ethcore/engine/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,21 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Blockchain test transaction deserialization. +//! This crate defines the Engine trait and related types. -use uint::Uint; -use bytes::Bytes; +mod engine; +pub mod signer; -/// Blockchain test transaction deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Transaction { - data: Bytes, - gas_limit: Uint, - gas_price: Uint, - nonce: Uint, - r: Uint, - s: Uint, - v: Uint, - value: Uint -} +pub use crate::engine::{ + Engine, + EpochVerifier, + StateDependentProof, + ConstructedVerifier, + EpochChange, + Proof, + SystemCall, + SystemOrCodeCall, + SystemOrCodeCallKind, + default_system_or_code_call, +}; + +#[cfg(any(test, feature = "test-helpers"))] +pub mod test_helpers; diff --git a/ethcore/engine/src/signer.rs b/ethcore/engine/src/signer.rs new file mode 100644 index 0000000000..540ede3fbb --- /dev/null +++ b/ethcore/engine/src/signer.rs @@ -0,0 +1,60 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! A signer used by Engines which need to sign messages. + +use ethereum_types::{H256, Address}; +use parity_crypto::publickey::{ecies, Public, Signature, KeyPair, Error}; + +/// Everything that an Engine needs to sign messages. +pub trait EngineSigner: Send + Sync { + /// Sign a consensus message hash. + fn sign(&self, hash: H256) -> Result; + + /// Signing address + fn address(&self) -> Address; + + /// Decrypt a message that was encrypted to this signer's key. + fn decrypt(&self, auth_data: &[u8], cipher: &[u8]) -> Result, Error>; + + /// The signer's public key, if available. + fn public(&self) -> Option; +} + +/// Creates a new `EngineSigner` from given key pair. +pub fn from_keypair(keypair: KeyPair) -> Box { + Box::new(Signer(keypair)) +} + +struct Signer(KeyPair); + +impl EngineSigner for Signer { + fn sign(&self, hash: H256) -> Result { + parity_crypto::publickey::sign(self.0.secret(), &hash) + } + + fn decrypt(&self, auth_data: &[u8], cipher: &[u8]) -> Result, Error> { + ecies::decrypt(self.0.secret(), auth_data, cipher) + } + + fn address(&self) -> Address { + self.0.address() + } + + fn public(&self) -> Option { + Some(*self.0.public()) + } +} diff --git a/ethcore/src/snapshot/consensus/mod.rs b/ethcore/engine/src/snapshot.rs similarity index 78% rename from ethcore/src/snapshot/consensus/mod.rs rename to ethcore/engine/src/snapshot.rs index 907e9c520b..7758682145 100644 --- a/ethcore/src/snapshot/consensus/mod.rs +++ b/ethcore/engine/src/snapshot.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,26 +14,41 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Secondary chunk creation and restoration, implementations for different consensus -//! engines. +//! This module contains traits used while creating/restoring snapshots. They +//! are here because they use and are used by the Engine trait itself. -use std::sync::atomic::AtomicBool; -use std::sync::Arc; - -use blockchain::{BlockChain, BlockChainDB}; -use engines::EthEngine; -use snapshot::{Error, ManifestData, Progress}; +use std::sync::{Arc, atomic::AtomicBool}; use ethereum_types::H256; -mod authority; -mod work; +use blockchain::{BlockChain, BlockChainDB}; +use common_types::{ + errors::{EthcoreError as Error, SnapshotError}, + snapshot::{ManifestData, ChunkSink, Progress}, +}; -pub use self::authority::*; -pub use self::work::*; +use crate::engine::Engine; + +/// Restore from secondary snapshot chunks. +pub trait Rebuilder: Send { + /// Feed a chunk, potentially out of order. + /// + /// Check `abort_flag` periodically while doing heavy work. If set to `false`, should bail with + /// `Error::RestorationAborted`. + fn feed( + &mut self, + chunk: &[u8], + engine: &dyn Engine, + abort_flag: &AtomicBool, + ) -> Result<(), Error>; -/// A sink for produced chunks. -pub type ChunkSink<'a> = FnMut(&[u8]) -> ::std::io::Result<()> + 'a; + /// Finalize the restoration. Will be done after all chunks have been + /// fed successfully. + /// + /// This should apply the necessary "glue" between chunks, + /// and verify against the restored state. + fn finalize(&mut self) -> Result<(), Error>; +} /// Components necessary for snapshot creation and restoration. pub trait SnapshotComponents: Send { @@ -51,9 +66,9 @@ pub trait SnapshotComponents: Send { chunk_sink: &mut ChunkSink, progress: &Progress, preferred_size: usize, - ) -> Result<(), Error>; + ) -> Result<(), SnapshotError>; - /// Create a rebuilder, which will have chunks fed into it in aribtrary + /// Create a rebuilder, which will have chunks fed into it in arbitrary /// order and then be finalized. /// /// The manifest, a database, and fresh `BlockChain` are supplied. @@ -63,9 +78,9 @@ pub trait SnapshotComponents: Send { fn rebuilder( &self, chain: BlockChain, - db: Arc, + db: Arc, manifest: &ManifestData, - ) -> Result, ::error::Error>; + ) -> Result, Error>; /// Minimum supported snapshot version number. fn min_supported_version(&self) -> u64; @@ -73,24 +88,3 @@ pub trait SnapshotComponents: Send { /// Current version number fn current_version(&self) -> u64; } - -/// Restore from secondary snapshot chunks. -pub trait Rebuilder: Send { - /// Feed a chunk, potentially out of order. - /// - /// Check `abort_flag` periodically while doing heavy work. If set to `false`, should bail with - /// `Error::RestorationAborted`. - fn feed( - &mut self, - chunk: &[u8], - engine: &EthEngine, - abort_flag: &AtomicBool, - ) -> Result<(), ::error::Error>; - - /// Finalize the restoration. Will be done after all chunks have been - /// fed successfully. - /// - /// This should apply the necessary "glue" between chunks, - /// and verify against the restored state. - fn finalize(&mut self, engine: &EthEngine) -> Result<(), ::error::Error>; -} diff --git a/ethcore/engine/src/test_helpers.rs b/ethcore/engine/src/test_helpers.rs new file mode 100644 index 0000000000..e10a12cbb9 --- /dev/null +++ b/ethcore/engine/src/test_helpers.rs @@ -0,0 +1,61 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Test helpers for engine related tests + +use std::sync::Arc; + +use ethereum_types::{Address, H256}; +use ethkey::Password; +use parity_crypto::publickey::{Public, Signature, Error}; +use log::warn; +use accounts::{self, AccountProvider, SignError}; + +use crate::signer::EngineSigner; + +impl EngineSigner for (Arc, Address, Password) { + fn sign(&self, hash: H256) -> Result { + match self.0.sign(self.1, Some(self.2.clone()), hash) { + Err(SignError::NotUnlocked) => unreachable!(), + Err(SignError::NotFound) => Err(Error::InvalidAddress), + Err(SignError::SStore(accounts::Error::EthCrypto(err))) => Err(Error::Custom(err.to_string())), + Err(SignError::SStore(accounts::Error::EthPublicKeyCrypto(err))) => { + warn!("Low level crypto error: {:?}", err); + Err(Error::InvalidSecretKey) + }, + Err(SignError::SStore(err)) => { + warn!("Error signing for engine: {:?}", err); + Err(Error::InvalidSignature) + }, + Ok(ok) => Ok(ok), + } + } + + fn decrypt(&self, auth_data: &[u8], cipher: &[u8]) -> Result, Error> { + self.0.decrypt(self.1, None, auth_data, cipher).map_err(|e| { + warn!("Unable to decrypt message: {:?}", e); + Error::InvalidMessage + }) + } + + fn address(&self) -> Address { + self.1 + } + + fn public(&self) -> Option { + self.0.account_public(self.1, &self.2).ok() + } +} diff --git a/ethcore/engines/authority-round/Cargo.toml b/ethcore/engines/authority-round/Cargo.toml new file mode 100644 index 0000000000..a1eaae1bc9 --- /dev/null +++ b/ethcore/engines/authority-round/Cargo.toml @@ -0,0 +1,46 @@ +[package] +description = "Non-instant BFT proof-of-authority blockchain engine" +name = "authority-round" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +block-gas-limit = { path = "../../block-gas-limit" } +block-reward = { path = "../../block-reward" } +client-traits = { path = "../../client-traits" } +common-types = { path = "../../types" } +derive_more = "0.15.0" +ethabi = "9.0.1" +ethabi-contract = "9.0.0" +ethabi-derive = "9.0.1" +ethereum-types = "0.8.0" +ethjson = { path = "../../../json" } +parity-crypto = { version = "0.4.2", features = ["publickey"] } +engine = { path = "../../engine" } +io = { package = "ethcore-io", path = "../../../util/io" } +itertools = "0.5" +keccak-hash = "0.4.0" +lazy_static = "1.3.0" +log = "0.4" +lru-cache = "0.1" +machine = { path = "../../machine" } +macros = { path = "../../../util/macros" } +parity-bytes = "0.1" +parking_lot = "0.9" +rand = "0.7" +rlp = "0.4.0" +time-utils = { path = "../../../util/time-utils" } +unexpected = { path = "../../../util/unexpected" } +validator-set = { path = "../validator-set" } + +[dev-dependencies] +accounts = { package = "ethcore-accounts", path = "../../../accounts" } +engine = { path = "../../engine", features = ["test-helpers"] } +env_logger = "0.6.2" +ethcore = { path = "../..", features = ["test-helpers"] } +spec = { path = "../../spec" } +state-db = { path = "../../state-db" } +validator-set = { path = "../validator-set", features = ["test-helpers"] } +serde_json = "1" diff --git a/ethcore/engines/authority-round/src/finality.rs b/ethcore/engines/authority-round/src/finality.rs new file mode 100644 index 0000000000..f0f5bd54af --- /dev/null +++ b/ethcore/engines/authority-round/src/finality.rs @@ -0,0 +1,315 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Finality proof generation and checking. + +use std::collections::{VecDeque}; +use std::collections::hash_map::{HashMap, Entry}; + +use common_types::BlockNumber; +use ethereum_types::{H256, Address}; +use log::{trace, warn}; +use validator_set::SimpleList; + +/// Error indicating unknown validator. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct UnknownValidator; + +/// Rolling finality checker for authority round consensus. +/// Stores a chain of unfinalized hashes that can be pushed onto. +pub struct RollingFinality { + headers: VecDeque<(H256, BlockNumber, Vec
)>, + signers: SimpleList, + sign_count: HashMap, + last_pushed: Option, + /// First block for which a 2/3 quorum (instead of 1/2) is required. + two_thirds_majority_transition: BlockNumber, +} + +impl RollingFinality { + /// Create a blank finality checker under the given validator set. + pub fn blank(signers: Vec
, two_thirds_majority_transition: BlockNumber) -> Self { + trace!(target: "finality", "Instantiating blank RollingFinality with {} signers: {:?}", signers.len(), signers); + RollingFinality { + headers: VecDeque::new(), + signers: SimpleList::new(signers), + sign_count: HashMap::new(), + last_pushed: None, + two_thirds_majority_transition, + } + } + + /// Extract unfinalized subchain from ancestry iterator. + /// Clears the current subchain. + /// + /// Fails if any provided signature isn't part of the signers set. + pub fn build_ancestry_subchain(&mut self, iterable: I) -> Result<(), UnknownValidator> + where I: IntoIterator)>, + { + self.clear(); + for (hash, number, signers) in iterable { + if signers.iter().any(|s| !self.signers.contains(s)) { return Err(UnknownValidator) } + if self.last_pushed.is_none() { self.last_pushed = Some(hash) } + self.add_signers(&signers); + self.headers.push_front((hash, number, signers)); + // break when we've got our first finalized block. + if self.is_finalized() { + let (hash, _, signers) = self.headers.pop_front().expect("we just pushed a block; qed"); + self.remove_signers(&signers); + trace!(target: "finality", "Encountered already finalized block {}", hash); + break + } + } + + trace!(target: "finality", "Rolling finality state: {:?}", self.headers); + Ok(()) + } + + /// Clears the finality status, but keeps the validator set. + pub fn clear(&mut self) { + self.headers.clear(); + self.sign_count.clear(); + self.last_pushed = None; + } + + /// Returns the last pushed hash. + pub fn subchain_head(&self) -> Option { + self.last_pushed + } + + /// Get an iterator over stored hashes in order. + #[cfg(test)] + pub fn unfinalized_hashes(&self) -> impl Iterator { + self.headers.iter().map(|(h, _, _)| h) + } + + /// Get the validator set. + pub fn validators(&self) -> &SimpleList { &self.signers } + + /// Push a hash onto the rolling finality checker (implying `subchain_head` == head.parent) + /// + /// Fails if `signer` isn't a member of the active validator set. + /// Returns a list of all newly finalized headers. + // TODO: optimize with smallvec. + pub fn push_hash(&mut self, head: H256, number: BlockNumber, signers: Vec
) + -> Result, UnknownValidator> + { + for their_signer in signers.iter() { + if !self.signers.contains(their_signer) { + warn!(target: "finality", "Unknown validator: {}", their_signer); + return Err(UnknownValidator) + } + } + + self.add_signers(&signers); + self.headers.push_back((head, number, signers)); + + let mut newly_finalized = Vec::new(); + + while self.is_finalized() { + let (hash, _, signers) = self.headers.pop_front() + .expect("headers length always greater than sign count length; qed"); + self.remove_signers(&signers); + newly_finalized.push(hash); + } + + trace!(target: "finality", "{} Blocks finalized by {:?}: {:?}", newly_finalized.len(), head, newly_finalized); + + self.last_pushed = Some(head); + Ok(newly_finalized) + } + + /// Returns the first block for which a 2/3 quorum (instead of 1/2) is required. + pub fn two_thirds_majority_transition(&self) -> BlockNumber { + self.two_thirds_majority_transition + } + + /// Returns whether the first entry in `self.headers` is finalized. + fn is_finalized(&self) -> bool { + match self.headers.front() { + None => false, + Some((_, number, _)) if *number < self.two_thirds_majority_transition => { + self.sign_count.len() * 2 > self.signers.len() + } + Some((_, _, _)) => { + self.sign_count.len() * 3 > self.signers.len() * 2 + } + } + } + + /// Adds the signers to the sign count. + fn add_signers(&mut self, signers: &[Address]) { + for signer in signers { + *self.sign_count.entry(*signer).or_insert(0) += 1; + } + } + + /// Removes the signers from the sign count. + fn remove_signers(&mut self, signers: &[Address]) { + for signer in signers { + match self.sign_count.entry(*signer) { + Entry::Occupied(mut entry) => { + // decrement count for this signer and purge on zero. + if *entry.get() <= 1 { + entry.remove(); + } else { + *entry.get_mut() -= 1; + } + } + Entry::Vacant(_) => { + panic!("all hashes in `header` should have entries in `sign_count` for their signers; qed"); + } + } + } + } +} + +#[cfg(test)] +mod tests { + use common_types::BlockNumber; + use ethereum_types::{H256, Address}; + use super::RollingFinality; + + #[test] + fn rejects_unknown_signers() { + let signers = (0..3).map(|_| Address::random()).collect::>(); + let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value()); + assert!(finality.push_hash(H256::random(), 0, vec![signers[0], Address::random()]).is_err()); + } + + #[test] + fn finalize_multiple() { + let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); + + let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value()); + let hashes: Vec<_> = (0..7).map(|_| H256::random()).collect(); + + // 3 / 6 signers is < 51% so no finality. + for (i, hash) in hashes.iter().take(6).cloned().enumerate() { + let i = i % 3; + assert!(finality.push_hash(hash, i as u64, vec![signers[i]]).unwrap().len() == 0); + } + + // after pushing a block signed by a fourth validator, the first four + // blocks of the unverified chain become verified. + assert_eq!(finality.push_hash(hashes[6], 6, vec![signers[4]]).unwrap(), + vec![hashes[0], hashes[1], hashes[2], hashes[3]]); + } + + #[test] + fn finalize_multiple_signers() { + let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); + let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value()); + let hash = H256::random(); + + // after pushing a block signed by four validators, it becomes verified right away. + assert_eq!(finality.push_hash(hash, 0, signers[0..4].to_vec()).unwrap(), vec![hash]); + } + + #[test] + fn from_ancestry() { + let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); + let hashes: Vec<_> = (0..12).map(|i| (H256::random(), i as u64, vec![signers[i % 6]])).collect(); + + let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value()); + finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap(); + + assert_eq!(finality.unfinalized_hashes().count(), 3); + assert_eq!(finality.subchain_head(), Some(hashes[11].0)); + } + + #[test] + fn from_ancestry_multiple_signers() { + let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); + let hashes: Vec<_> = (0..12).map(|i| { + (H256::random(), i as u64, vec![signers[i % 6], signers[(i + 1) % 6], signers[(i + 2) % 6]]) + }).collect(); + + let mut finality = RollingFinality::blank(signers.clone(), BlockNumber::max_value()); + finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap(); + + // only the last hash has < 51% of authorities' signatures + assert_eq!(finality.unfinalized_hashes().count(), 1); + assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0)); + assert_eq!(finality.subchain_head(), Some(hashes[11].0)); + } + + #[test] + fn rejects_unknown_signers_2_3() { + let signers = (0..3).map(|_| Address::random()).collect::>(); + let mut finality = RollingFinality::blank(signers.clone(), 0); + assert!(finality.push_hash(H256::random(), 0, vec![signers[0], Address::random()]).is_err()); + } + + #[test] + fn finalize_multiple_2_3() { + let signers: Vec<_> = (0..7).map(|_| Address::random()).collect(); + + let mut finality = RollingFinality::blank(signers.clone(), 0); + let hashes: Vec<_> = (0..9).map(|_| H256::random()).collect(); + + // 4 / 7 signers is < 67% so no finality. + for (i, hash) in hashes.iter().take(8).cloned().enumerate() { + let i = i % 4; + assert!(finality.push_hash(hash, i as u64, vec![signers[i]]).unwrap().len() == 0); + } + + // after pushing a block signed by a fifth validator, the first five + // blocks of the unverified chain become verified. + assert_eq!(finality.push_hash(hashes[8], 8, vec![signers[4]]).unwrap(), + vec![hashes[0], hashes[1], hashes[2], hashes[3], hashes[4]]); + } + + #[test] + fn finalize_multiple_signers_2_3() { + let signers: Vec<_> = (0..5).map(|_| Address::random()).collect(); + let mut finality = RollingFinality::blank(signers.clone(), 0); + let hash = H256::random(); + + // after pushing a block signed by four validators, it becomes verified right away. + assert_eq!(finality.push_hash(hash, 0, signers[0..4].to_vec()).unwrap(), vec![hash]); + } + + #[test] + fn from_ancestry_2_3() { + let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); + let hashes: Vec<_> = (0..12).map(|i| (H256::random(), i as u64, vec![signers[i % 6]])).collect(); + + let mut finality = RollingFinality::blank(signers, 0); + finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap(); + + // The last four hashes, with index 11, 10, 9, and 8, have been pushed. 7 would have finalized a block. + assert_eq!(finality.unfinalized_hashes().count(), 4); + assert_eq!(finality.subchain_head(), Some(hashes[11].0)); + } + + #[test] + fn from_ancestry_multiple_signers_2_3() { + let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); + let hashes: Vec<_> = (0..12).map(|i| { + let hash_signers = signers.iter().cycle().skip(i).take(4).cloned().collect(); + (H256::random(), i as u64, hash_signers) + }).collect(); + + let mut finality = RollingFinality::blank(signers.clone(), 0); + finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap(); + + // only the last hash has < 67% of authorities' signatures + assert_eq!(finality.unfinalized_hashes().count(), 1); + assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0)); + assert_eq!(finality.subchain_head(), Some(hashes[11].0)); + } +} diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/engines/authority-round/src/lib.rs similarity index 61% rename from ethcore/src/engines/authority_round/mod.rs rename to ethcore/engines/authority-round/src/lib.rs index c2101a4d06..49861c0265 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/engines/authority-round/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,53 +15,92 @@ // along with Parity Ethereum. If not, see . //! A blockchain engine that supports a non-instant BFT proof-of-authority. +//! +//! It is recommended to use the `two_thirds_majority_transition` option, to defend against the +//! ["Attack of the Clones"](https://arxiv.org/pdf/1902.10244.pdf). Newly started networks can +//! set this option to `0`, to use a 2/3 quorum from the beginning. +//! +//! To support on-chain governance, the [ValidatorSet] is pluggable: Aura supports simple +//! constant lists of validators as well as smart contract-based dynamic validator sets. +//! Misbehavior is reported to the [ValidatorSet] as well, so that e.g. governance contracts +//! can penalize or ban attacker's nodes. +//! +//! * "Benign" misbehavior are faults that can happen in normal operation, like failing +//! to propose a block in your slot, which could be due to a temporary network outage, or +//! wrong timestamps (due to out-of-sync clocks). +//! * "Malicious" reports are made only if the sender misbehaved deliberately (or due to a +//! software bug), e.g. if they proposed multiple blocks with the same step number. use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::{cmp, fmt}; -use std::iter::FromIterator; +use std::iter::{self, FromIterator}; use std::ops::Deref; -use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; +use std::sync::atomic::{AtomicU64, AtomicBool, Ordering as AtomicOrdering}; use std::sync::{Weak, Arc}; use std::time::{UNIX_EPOCH, Duration}; +use std::u64; -use block::*; -use client::EngineClient; -use engines::{Engine, Seal, EngineError, ConstructedVerifier}; -use engines::block_reward; -use engines::block_reward::{BlockRewardContract, RewardKind}; -use error::{Error, ErrorKind, BlockError}; +use client_traits::{EngineClient, ForceUpdateSealing, TransactionRequest}; +use engine::{Engine, ConstructedVerifier}; +use block_gas_limit::block_gas_limit; +use block_reward::{self, BlockRewardContract, RewardKind}; use ethjson; -use machine::{AuxiliaryData, Call, EthereumMachine}; -use hash::keccak; -use super::signer::EngineSigner; -use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; -use self::finality::RollingFinality; -use ethkey::{self, Signature}; +use machine::{ + ExecutedBlock, + Machine, +}; +use macros::map; +use keccak_hash::keccak; +use log::{info, debug, error, trace, warn}; +use lru_cache::LruCache; +use engine::signer::EngineSigner; +use parity_crypto::publickey::Signature; use io::{IoContext, IoHandler, TimerToken, IoService}; use itertools::{self, Itertools}; +use rand::rngs::OsRng; use rlp::{encode, Decodable, DecoderError, Encodable, RlpStream, Rlp}; use ethereum_types::{H256, H520, Address, U128, U256}; use parking_lot::{Mutex, RwLock}; use time_utils::CheckedSystemTime; -use types::BlockNumber; -use types::header::{Header, ExtendedHeader}; -use types::ancestry_action::AncestryAction; +use common_types::{ + ancestry_action::AncestryAction, + BlockNumber, + header::{Header, ExtendedHeader}, + engines::{ + Headers, + params::CommonParams, + PendingTransitionStore, + Seal, + SealingState, + machine::{Call, AuxiliaryData}, + }, + errors::{BlockError, EthcoreError as Error, EngineError}, + ids::BlockId, + snapshot::Snapshotting, + transaction::SignedTransaction, +}; use unexpected::{Mismatch, OutOfBounds}; +use validator_set::{ValidatorSet, SimpleList, new_validator_set}; mod finality; +mod randomness; +pub(crate) mod util; + +use self::finality::RollingFinality; /// `AuthorityRound` params. pub struct AuthorityRoundParams { - /// Time to wait before next block or authority switching, - /// in seconds. + /// A map defining intervals of blocks with the given times (in seconds) to wait before next + /// block or authority switching. The keys in the map are steps of starting blocks of those + /// periods. The entry at `0` should be defined. /// - /// Deliberately typed as u16 as too high of a value leads - /// to slow block issuance. - pub step_duration: u16, + /// Wait times (durations) are additionally required to be less than 65535 since larger values + /// lead to slow block issuance. + pub step_durations: BTreeMap, /// Starting step, pub start_step: Option, /// Valid validators. - pub validators: Box, + pub validators: Box, /// Chain score validation transition block. pub validate_score_transition: u64, /// Monotonic step validation transition block. @@ -70,102 +109,194 @@ 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, + /// Block reward contract addresses with their associated starting block numbers. + pub block_reward_contract_transitions: BTreeMap, /// Number of accepted uncles transition block. pub maximum_uncle_count_transition: u64, /// Number of accepted uncles. pub maximum_uncle_count: usize, /// Empty step messages transition block. pub empty_steps_transition: u64, + /// First block for which a 2/3 quorum (instead of 1/2) is required. + pub two_thirds_majority_transition: BlockNumber, /// Number of accepted empty steps. pub maximum_empty_steps: usize, /// Transition block to strict empty steps validation. pub strict_empty_steps_transition: u64, + /// If set, enables random number contract integration. It maps the transition block to the contract address. + pub randomness_contract_address: BTreeMap, + /// The addresses of contracts that determine the block gas limit with their associated block + /// numbers. + pub block_gas_limit_contract_transitions: BTreeMap, } const U16_MAX: usize = ::std::u16::MAX as usize; +/// The number of recent block hashes for which the gas limit override is memoized. +const GAS_LIMIT_OVERRIDE_CACHE_CAPACITY: usize = 10; + impl From for AuthorityRoundParams { fn from(p: ethjson::spec::AuthorityRoundParams) -> Self { - let mut step_duration_usize: usize = p.step_duration.into(); - if step_duration_usize > U16_MAX { - step_duration_usize = U16_MAX; - warn!(target: "engine", "step_duration is too high ({}), setting it to {}", step_duration_usize, U16_MAX); + let map_step_duration = |u: ethjson::uint::Uint| { + let mut step_duration_usize: usize = u.into(); + if step_duration_usize == 0 { + panic!("AuthorityRoundParams: step duration cannot be 0"); + } + if step_duration_usize > U16_MAX { + warn!(target: "engine", "step duration is too high ({}), setting it to {}", step_duration_usize, U16_MAX); + step_duration_usize = U16_MAX; + } + step_duration_usize as u64 + }; + let step_durations: BTreeMap<_, _> = match p.step_duration { + ethjson::spec::StepDuration::Single(u) => + iter::once((0, map_step_duration(u))).collect(), + ethjson::spec::StepDuration::Transitions(tr) => { + if tr.is_empty() { + panic!("AuthorityRoundParams: step duration transitions cannot be empty"); + } + tr.into_iter().map(|(timestamp, u)| (timestamp.into(), map_step_duration(u))).collect() + } + }; + let transition_block_num = p.block_reward_contract_transition.map_or(0, Into::into); + let mut br_transitions: BTreeMap<_, _> = p.block_reward_contract_transitions + .unwrap_or_default() + .into_iter() + .map(|(block_num, address)| + (block_num.into(), BlockRewardContract::new_from_address(address.into()))) + .collect(); + if (p.block_reward_contract_code.is_some() || p.block_reward_contract_address.is_some()) && + br_transitions.keys().next().map_or(false, |&block_num| block_num <= transition_block_num) + { + let s = "blockRewardContractTransition"; + panic!("{} should be less than any of the keys in {}s", s, s); } + if let Some(code) = p.block_reward_contract_code { + br_transitions.insert( + transition_block_num, + BlockRewardContract::new_from_code(Arc::new(code.into())) + ); + } else if let Some(address) = p.block_reward_contract_address { + br_transitions.insert( + transition_block_num, + BlockRewardContract::new_from_address(address.into()) + ); + } + let randomness_contract_address = p.randomness_contract_address.map_or_else(BTreeMap::new, |transitions| { + transitions.into_iter().map(|(ethjson::uint::Uint(block), addr)| { + (block.as_u64(), addr.into()) + }).collect() + }); + let block_gas_limit_contract_transitions: BTreeMap<_, _> = + p.block_gas_limit_contract_transitions + .unwrap_or_default() + .into_iter() + .map(|(block_num, address)| (block_num.into(), address.into())) + .collect(); AuthorityRoundParams { - step_duration: step_duration_usize as u16, + step_durations, validators: new_validator_set(p.validators), start_step: p.start_step.map(Into::into), validate_score_transition: p.validate_score_transition.map_or(0, Into::into), 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: match (p.block_reward_contract_code, p.block_reward_contract_address) { - (Some(code), _) => Some(BlockRewardContract::new_from_code(Arc::new(code.into()))), - (_, Some(address)) => Some(BlockRewardContract::new_from_address(address.into())), - (None, None) => None, - }, + block_reward_contract_transitions: br_transitions, 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)), maximum_empty_steps: p.maximum_empty_steps.map_or(0, Into::into), + two_thirds_majority_transition: p.two_thirds_majority_transition.map_or_else(BlockNumber::max_value, Into::into), strict_empty_steps_transition: p.strict_empty_steps_transition.map_or(0, Into::into), + randomness_contract_address, + block_gas_limit_contract_transitions, } } } -// Helper for managing the step. +/// A triple containing the first step number and the starting timestamp of the given step duration. +#[derive(Clone, Copy, Debug)] +struct StepDurationInfo { + transition_step: u64, + transition_timestamp: u64, + step_duration: u64, +} + +/// Helper for managing the step. #[derive(Debug)] struct Step { calibrate: bool, // whether calibration is enabled. - inner: AtomicUsize, - duration: u16, + inner: AtomicU64, + /// Planned durations of steps. + durations: Vec, } impl Step { - fn load(&self) -> u64 { self.inner.load(AtomicOrdering::SeqCst) as u64 } + fn load(&self) -> u64 { self.inner.load(AtomicOrdering::SeqCst) } + + /// Finds the remaining duration of the current step. Panics if there was a counter under- or + /// overflow. fn duration_remaining(&self) -> Duration { - let now = unix_now(); - let expected_seconds = self.load() - .checked_add(1) - .and_then(|ctr| ctr.checked_mul(self.duration as u64)) - .map(Duration::from_secs); - - match expected_seconds { - Some(step_end) if step_end > now => step_end - now, - Some(_) => Duration::from_secs(0), - None => { - let ctr = self.load(); - error!(target: "engine", "Step counter is too high: {}, aborting", ctr); - panic!("step counter is too high: {}", ctr) - }, - } + self.opt_duration_remaining().unwrap_or_else(|| { + let ctr = self.load(); + error!(target: "engine", "Step counter under- or overflow: {}, aborting", ctr); + panic!("step counter under- or overflow: {}", ctr) + }) + } + /// Finds the remaining duration of the current step. Returns `None` if there was a counter + /// under- or overflow. + fn opt_duration_remaining(&self) -> Option { + let next_step = self.load().checked_add(1)?; + let StepDurationInfo { transition_step, transition_timestamp, step_duration } = + self.durations.iter() + .take_while(|info| info.transition_step < next_step) + .last() + .expect("durations cannot be empty") + .clone(); + let next_time = transition_timestamp + .checked_add(next_step.checked_sub(transition_step)?.checked_mul(step_duration)?)?; + Some(Duration::from_secs(next_time.saturating_sub(unix_now().as_secs()))) } + /// Increments the step number. + /// + /// Panics if the new step number is `u64::MAX`. fn increment(&self) { - use std::usize; // fetch_add won't panic on overflow but will rather wrap // around, leading to zero as the step counter, which might // lead to unexpected situations, so it's better to shut down. - if self.inner.fetch_add(1, AtomicOrdering::SeqCst) == usize::MAX { - error!(target: "engine", "Step counter is too high: {}, aborting", usize::MAX); - panic!("step counter is too high: {}", usize::MAX); + if self.inner.fetch_add(1, AtomicOrdering::SeqCst) == u64::MAX { + error!(target: "engine", "Step counter is too high: {}, aborting", u64::MAX); + panic!("step counter is too high: {}", u64::MAX); } - } fn calibrate(&self) { if self.calibrate { - let new_step = unix_now().as_secs() / (self.duration as u64); - self.inner.store(new_step as usize, AtomicOrdering::SeqCst); + if self.opt_calibrate().is_none() { + let ctr = self.load(); + error!(target: "engine", "Step counter under- or overflow: {}, aborting", ctr); + panic!("step counter under- or overflow: {}", ctr) + } } } + /// Calibrates the AuRa step number according to the current time. + fn opt_calibrate(&self) -> Option<()> { + let now = unix_now().as_secs(); + let StepDurationInfo { transition_step, transition_timestamp, step_duration } = + self.durations.iter() + .take_while(|info| info.transition_timestamp < now) + .last() + .expect("durations cannot be empty") + .clone(); + let new_step = (now.checked_sub(transition_timestamp)? / step_duration) + .checked_add(transition_step)?; + self.inner.store(new_step, AtomicOrdering::SeqCst); + Some(()) + } + fn check_future(&self, given: u64) -> Result<(), Option>> { const REJECTED_STEP_DRIFT: u64 = 4; @@ -183,7 +314,9 @@ impl Step { Err(None) // wait a bit for blocks in near future } else if given > current { - let d = self.duration as u64; + let d = self.durations.iter().take_while(|info| info.transition_step <= current).last() + .expect("Duration map has at least a 0 entry.") + .step_duration; Err(Some(OutOfBounds { min: None, max: Some(d * current), @@ -208,18 +341,24 @@ struct EpochManager { } impl EpochManager { - fn blank() -> Self { + fn blank(two_thirds_majority_transition: BlockNumber) -> Self { EpochManager { - epoch_transition_hash: H256::default(), + epoch_transition_hash: H256::zero(), epoch_transition_number: 0, - finality_checker: RollingFinality::blank(Vec::new()), + finality_checker: RollingFinality::blank(Vec::new(), two_thirds_majority_transition), force: true, } } - // zoom to epoch for given header. returns true if succeeded, false otherwise. - fn zoom_to(&mut self, client: &EngineClient, machine: &EthereumMachine, validators: &ValidatorSet, header: &Header) -> bool { - let last_was_parent = self.finality_checker.subchain_head() == Some(*header.parent_hash()); + // Zooms to the epoch after the header with the given hash. Returns true if succeeded, false otherwise. + fn zoom_to_after( + &mut self, + client: &dyn EngineClient, + machine: &Machine, + validators: &dyn ValidatorSet, + hash: H256 + ) -> bool { + let last_was_parent = self.finality_checker.subchain_head() == Some(hash); // early exit for current target == chain head, but only if the epochs are // the same. @@ -228,18 +367,20 @@ impl EpochManager { } self.force = false; - debug!(target: "engine", "Zooming to epoch for block {}", header.hash()); + debug!(target: "engine", "Zooming to epoch after block {}", hash); + trace!(target: "engine", "Current validator set: {:?}", self.validators()); + // epoch_transition_for can be an expensive call, but in the absence of // forks it will only need to be called for the block directly after // epoch transition, in which case it will be O(1) and require a single // DB lookup. - let last_transition = match client.epoch_transition_for(*header.parent_hash()) { + let last_transition = match client.epoch_transition_for(hash) { Some(t) => t, None => { // this really should never happen unless the block passed // hasn't got a parent in the database. - debug!(target: "engine", "No genesis transition found."); + warn!(target: "engine", "No genesis transition found. Block hash {} does not have a parent in the DB", hash); return false; } }; @@ -249,7 +390,7 @@ impl EpochManager { let (signal_number, set_proof, _) = destructure_proofs(&last_transition.proof) .expect("proof produced by this engine; therefore it is valid; qed"); - trace!(target: "engine", "extracting epoch set for epoch ({}, {}) signalled at #{}", + trace!(target: "engine", "extracting epoch validator set for epoch ({}, {}) signalled at #{}", last_transition.block_number, last_transition.block_hash, signal_number); let first = signal_number == 0; @@ -260,10 +401,16 @@ impl EpochManager { set_proof, ) .ok() - .map(|(list, _)| list.into_inner()) + .map(|(list, _)| { + trace!(target: "engine", "Updating finality checker with new validator set extracted from epoch ({}, {}): {:?}", + last_transition.block_number, last_transition.block_hash, &list); + + list.into_inner() + }) .expect("proof produced by this engine; therefore it is valid; qed"); - self.finality_checker = RollingFinality::blank(epoch_set); + let two_thirds_majority_transition = self.finality_checker.two_thirds_majority_transition(); + self.finality_checker = RollingFinality::blank(epoch_set, two_thirds_majority_transition); } self.epoch_transition_hash = last_transition.block_hash; @@ -272,8 +419,8 @@ impl EpochManager { true } - // note new epoch hash. this will force the next block to re-load - // the epoch set + // Note new epoch hash. This will force the next block to re-load + // the epoch set. // TODO: optimize and don't require re-loading after epoch change. fn note_new_epoch(&mut self) { self.force = true; @@ -316,18 +463,18 @@ impl EmptyStep { EmptyStep { signature, step, parent_hash } } - fn verify(&self, validators: &ValidatorSet) -> Result { + fn verify(&self, validators: &dyn ValidatorSet) -> Result { let message = keccak(empty_step_rlp(self.step, &self.parent_hash)); let correct_proposer = step_proposer(validators, &self.parent_hash, self.step); - ethkey::verify_address(&correct_proposer, &self.signature.into(), &message) + parity_crypto::publickey::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 = ethkey::recover(&self.signature.into(), &message)?; - Ok(ethkey::public_to_address(&public)) + let public = parity_crypto::publickey::recover(&self.signature.into(), &message)?; + Ok(parity_crypto::publickey::public_to_address(&public)) } fn sealed(&self) -> SealedEmptyStep { @@ -411,23 +558,31 @@ struct PermissionedStep { pub struct AuthorityRound { transition_service: IoService<()>, step: Arc, - client: Arc>>>, - signer: RwLock>>, - validators: Box, + client: Arc>>>, + signer: RwLock>>, + validators: Box, validate_score_transition: u64, validate_step_transition: u64, empty_steps: Mutex>, epoch_manager: Mutex, immediate_transitions: bool, block_reward: U256, - block_reward_contract_transition: u64, - block_reward_contract: Option, + block_reward_contract_transitions: BTreeMap, maximum_uncle_count_transition: u64, maximum_uncle_count: usize, empty_steps_transition: u64, strict_empty_steps_transition: u64, + two_thirds_majority_transition: BlockNumber, maximum_empty_steps: usize, - machine: EthereumMachine, + machine: Machine, + /// History of step hashes recently received from peers. + received_step_hashes: RwLock>, + /// If set, enables random number contract integration. It maps the transition block to the contract address. + randomness_contract_address: BTreeMap, + /// The addresses of contracts that determine the block gas limit. + block_gas_limit_contract_transitions: BTreeMap, + /// Memoized gas limit overrides, by block hash. + gas_limit_override_cache: Mutex>>, } // header-chain validator. @@ -435,9 +590,11 @@ struct EpochVerifier { step: Arc, subchain_validators: SimpleList, empty_steps_transition: u64, + /// First block for which a 2/3 quorum (instead of 1/2) is required. + two_thirds_majority_transition: BlockNumber, } -impl super::EpochVerifier for EpochVerifier { +impl engine::EpochVerifier for EpochVerifier { fn verify_light(&self, header: &Header) -> Result<(), Error> { // Validate the timestamp verify_timestamp(&self.step.inner, header_step(header, self.empty_steps_transition)?)?; @@ -447,7 +604,8 @@ impl super::EpochVerifier for EpochVerifier { } fn check_finality_proof(&self, proof: &[u8]) -> Option> { - let mut finality_checker = RollingFinality::blank(self.subchain_validators.clone().into_inner()); + let signers = self.subchain_validators.clone().into_inner(); + let mut finality_checker = RollingFinality::blank(signers, self.two_thirds_majority_transition); let mut finalized = Vec::new(); let headers: Vec
= Rlp::new(proof).as_list().ok()?; @@ -472,7 +630,8 @@ impl super::EpochVerifier for EpochVerifier { }; signers.push(*parent_header.author()); - let newly_finalized = finality_checker.push_hash(parent_header.hash(), signers).ok()?; + let newly_finalized = + finality_checker.push_hash(parent_header.hash(), parent_header.number(), signers).ok()?; finalized.extend(newly_finalized); Some(()) @@ -494,7 +653,7 @@ impl super::EpochVerifier for EpochVerifier { fn header_seal_hash(header: &Header, empty_steps_rlp: Option<&[u8]>) -> H256 { match empty_steps_rlp { Some(empty_steps_rlp) => { - let mut message = header.bare_hash().to_vec(); + let mut message = header.bare_hash().as_bytes().to_vec(); message.extend_from_slice(empty_steps_rlp); keccak(message) }, @@ -514,7 +673,7 @@ fn header_expected_seal_fields(header: &Header, empty_steps_transition: u64) -> fn header_step(header: &Header, empty_steps_transition: u64) -> Result { Rlp::new(&header.seal().get(0).unwrap_or_else(|| - panic!("was either checked with verify_block_basic or is genesis; has {} fields; qed (Make sure the spec + panic!("was either checked with verify_block_basic or is genesis; has {} fields; qed (Make sure the spec \ file has a correct genesis seal)", header_expected_seal_fields(header, empty_steps_transition)) )) .as_val() @@ -523,7 +682,7 @@ fn header_step(header: &Header, empty_steps_transition: u64) -> Result Result { Rlp::new(&header.seal().get(1).unwrap_or_else(|| panic!("was checked with verify_block_basic; has {} fields; qed", - header_expected_seal_fields(header, empty_steps_transition)) + header_expected_seal_fields(header, empty_steps_transition)) )) .as_val::().map(Into::into) } @@ -555,13 +714,13 @@ fn header_empty_steps_signers(header: &Header, empty_steps_transition: u64) -> R } } -fn step_proposer(validators: &ValidatorSet, bh: &H256, step: u64) -> Address { +fn step_proposer(validators: &dyn ValidatorSet, bh: &H256, step: u64) -> Address { let proposer = validators.get(bh, step as usize); - trace!(target: "engine", "Fetched proposer for step {}: {}", step, proposer); + trace!(target: "engine", "step_proposer: Fetched proposer for step {}: {}", step, proposer); proposer } -fn is_step_proposer(validators: &ValidatorSet, bh: &H256, step: u64, address: &Address) -> bool { +fn is_step_proposer(validators: &dyn ValidatorSet, bh: &H256, step: u64, address: &Address) -> bool { step_proposer(validators, bh, step) == *address } @@ -583,13 +742,13 @@ fn verify_timestamp(step: &Step, header_step: u64) -> Result<(), BlockError> { let new_oob = OutOfBounds { min, max, found }; - Err(BlockError::TemporarilyInvalid(new_oob).into()) + Err(BlockError::TemporarilyInvalid(new_oob.into())) }, Ok(_) => Ok(()), } } -fn verify_external(header: &Header, validators: &ValidatorSet, empty_steps_transition: u64) -> Result<(), Error> { +fn verify_external(header: &Header, validators: &dyn ValidatorSet, empty_steps_transition: u64) -> Result<(), Error> { let header_step = header_step(header, empty_steps_transition)?; let proposer_signature = header_signature(header, empty_steps_transition)?; @@ -602,11 +761,11 @@ fn verify_external(header: &Header, validators: &ValidatorSet, empty_steps_trans }; let header_seal_hash = header_seal_hash(header, empty_steps_rlp); - !ethkey::verify_address(&correct_proposer, &proposer_signature, &header_seal_hash)? + !parity_crypto::publickey::verify_address(&correct_proposer, &proposer_signature, &header_seal_hash)? }; if is_invalid_proposer { - trace!(target: "engine", "verify_block_external: bad proposer for step: {}", header_step); + warn!(target: "engine", "verify_block_external: bad proposer for step: {}", header_step); Err(EngineError::NotProposer(Mismatch { expected: correct_proposer, found: *header.author() }))? } else { Ok(()) @@ -658,41 +817,76 @@ impl<'a, A: ?Sized, B> Deref for CowLike<'a, A, B> where B: AsRef { impl AuthorityRound { /// Create a new instance of AuthorityRound engine. - pub fn new(our_params: AuthorityRoundParams, machine: EthereumMachine) -> Result, Error> { - if our_params.step_duration == 0 { - error!(target: "engine", "Authority Round step duration can't be zero, aborting"); - panic!("authority_round: step duration can't be zero") + pub fn new(our_params: AuthorityRoundParams, machine: Machine) -> Result, Error> { + if !our_params.step_durations.contains_key(&0) { + error!(target: "engine", "Authority Round step 0 duration is undefined, aborting"); + return Err(Error::Engine(EngineError::Custom(String::from("step 0 duration is undefined")))); + } + if our_params.step_durations.values().any(|v| *v == 0) { + error!(target: "engine", "Authority Round step duration cannot be 0"); + return Err(Error::Engine(EngineError::Custom(String::from("step duration cannot be 0")))); } let should_timeout = our_params.start_step.is_none(); - let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / (our_params.step_duration as u64))); + let initial_step = our_params.start_step.unwrap_or(0); + + let mut durations = Vec::new(); + let mut prev_step = 0u64; + let mut prev_time = 0u64; + let mut prev_dur = our_params.step_durations[&0]; + durations.push(StepDurationInfo { + transition_step: prev_step, + transition_timestamp: prev_time, + step_duration: prev_dur + }); + for (time, dur) in our_params.step_durations.iter().skip(1) { + let (step, time) = next_step_time_duration( + StepDurationInfo{ + transition_step: prev_step, + transition_timestamp: prev_time, + step_duration: prev_dur, + }, *time) + .ok_or(BlockError::TimestampOverflow)?; + durations.push(StepDurationInfo { + transition_step: step, + transition_timestamp: time, + step_duration: *dur + }); + prev_step = step; + prev_time = time; + prev_dur = *dur; + } + + let step = Step { + inner: AtomicU64::new(initial_step), + calibrate: our_params.start_step.is_none(), + durations, + }; + step.calibrate(); let engine = Arc::new( AuthorityRound { transition_service: IoService::<()>::start()?, - step: Arc::new(PermissionedStep { - inner: Step { - inner: AtomicUsize::new(initial_step as usize), - calibrate: our_params.start_step.is_none(), - duration: our_params.step_duration, - }, - can_propose: AtomicBool::new(true), - }), + step: Arc::new(PermissionedStep { inner: step, can_propose: AtomicBool::new(true) }), client: Arc::new(RwLock::new(None)), signer: RwLock::new(None), validators: our_params.validators, validate_score_transition: our_params.validate_score_transition, validate_step_transition: our_params.validate_step_transition, empty_steps: Default::default(), - epoch_manager: Mutex::new(EpochManager::blank()), + epoch_manager: Mutex::new(EpochManager::blank(our_params.two_thirds_majority_transition)), 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, + block_reward_contract_transitions: our_params.block_reward_contract_transitions, 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, maximum_empty_steps: our_params.maximum_empty_steps, + two_thirds_majority_transition: our_params.two_thirds_majority_transition, strict_empty_steps_transition: our_params.strict_empty_steps_transition, - machine: machine, + machine, + received_step_hashes: RwLock::new(Default::default()), + randomness_contract_address: our_params.randomness_contract_address, + block_gas_limit_contract_transitions: our_params.block_gas_limit_contract_transitions, + gas_limit_override_cache: Mutex::new(LruCache::new(GAS_LIMIT_OVERRIDE_CACHE_CAPACITY)), }); // Do not initialize timeouts for tests. @@ -708,7 +902,7 @@ impl AuthorityRound { // fetch correct validator set for epoch at header, taking into account // finality of previous transitions. - fn epoch_set<'a>(&'a self, header: &Header) -> Result<(CowLike, BlockNumber), Error> { + fn epoch_set<'a>(&'a self, header: &Header) -> Result<(CowLike, BlockNumber), Error> { Ok(if self.immediate_transitions { (CowLike::Borrowed(&*self.validators), header.number()) } else { @@ -721,9 +915,9 @@ impl AuthorityRound { } }; - if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, header) { + if !epoch_manager.zoom_to_after(&*client, &self.machine, &*self.validators, *header.parent_hash()) { debug!(target: "engine", "Unable to zoom to epoch."); - return Err(EngineError::RequiresClient.into()) + return Err(EngineError::MissingParent(*header.parent_hash()).into()) } (CowLike::Owned(epoch_manager.validators().clone()), epoch_manager.epoch_transition_number) @@ -794,15 +988,15 @@ impl AuthorityRound { } } - fn report_skipped(&self, header: &Header, current_step: u64, parent_step: u64, validators: &ValidatorSet, set_number: u64) { + fn report_skipped(&self, header: &Header, current_step: u64, parent_step: u64, validators: &dyn ValidatorSet, set_number: u64) { // we're building on top of the genesis block so don't report any skipped steps if header.number() == 1 { return; } - if let (true, Some(me)) = (current_step > parent_step + 1, self.signer.read().as_ref().map(|s| s.address())) { + if let (true, Some(me)) = (current_step > parent_step + 1, self.address()) { debug!(target: "engine", "Author {} built block with step gap. current step: {}, parent step: {}", - header.author(), current_step, parent_step); + header.author(), current_step, parent_step); let mut reported = HashSet::new(); for step in parent_step + 1..current_step { let skipped_primary = step_proposer(validators, header.parent_hash(), step); @@ -810,14 +1004,18 @@ impl AuthorityRound { if skipped_primary != me { // Stop reporting once validators start repeating. if !reported.insert(skipped_primary) { break; } + trace!(target: "engine", "Reporting benign misbehaviour (cause: skipped step) at block #{}, epoch set number {}, step proposer={:#x}. Own address: {}", + header.number(), set_number, skipped_primary, me); self.validators.report_benign(&skipped_primary, set_number, header.number()); - } - } + } else { + trace!(target: "engine", "Primary that skipped is self, not self-reporting. Own address: {}", me); + } + } } } // Returns the hashes of all ancestor blocks that are finalized by the given `chain_head`. - fn build_finality(&self, chain_head: &Header, ancestry: &mut Iterator) -> Vec { + fn build_finality(&self, chain_head: &Header, ancestry: &mut dyn Iterator) -> Vec { if self.immediate_transitions { return Vec::new() } let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { @@ -829,14 +1027,14 @@ impl AuthorityRound { }; let mut epoch_manager = self.epoch_manager.lock(); - if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, chain_head) { + if !epoch_manager.zoom_to_after(&*client, &self.machine, &*self.validators, *chain_head.parent_hash()) { return Vec::new(); } if epoch_manager.finality_checker.subchain_head() != Some(*chain_head.parent_hash()) { // build new finality checker from unfinalized ancestry of chain head, not including chain head itself yet. trace!(target: "finality", "Building finality up to parent of {} ({})", - chain_head.hash(), chain_head.parent_hash()); + chain_head.hash(), chain_head.parent_hash()); // the empty steps messages in a header signal approval of the // parent header. @@ -854,7 +1052,7 @@ impl AuthorityRound { signers.extend(parent_empty_steps_signers.drain(..)); if let Ok(empty_step_signers) = header_empty_steps_signers(&header, self.empty_steps_transition) { - let res = (header.hash(), signers); + let res = (header.hash(), header.number(), signers); trace!(target: "finality", "Ancestry iteration: yielding {:?}", res); parent_empty_steps_signers = empty_step_signers; @@ -867,7 +1065,7 @@ impl AuthorityRound { } }) .while_some() - .take_while(|&(h, _)| h != epoch_transition_hash); + .take_while(|&(h, _, _)| h != epoch_transition_hash); if let Err(e) = epoch_manager.finality_checker.build_ancestry_subchain(ancestry_iter) { debug!(target: "engine", "inconsistent validator set within epoch: {:?}", e); @@ -875,9 +1073,49 @@ impl AuthorityRound { } } - let finalized = epoch_manager.finality_checker.push_hash(chain_head.hash(), vec![*chain_head.author()]); + let finalized = epoch_manager.finality_checker.push_hash( + chain_head.hash(), chain_head.number(), vec![*chain_head.author()]); finalized.unwrap_or_default() } + + fn address(&self) -> Option
{ + self.signer.read().as_ref().map(|s| s.address() ) + } + + /// Make calls to the randomness contract. + fn run_randomness_phase(&self, block: &ExecutedBlock) -> Result, Error> { + let contract_addr = match self.randomness_contract_address.range(..=block.header.number()).last() { + Some((_, &contract_addr)) => contract_addr, + None => return Ok(Vec::new()), // No randomness contract. + }; + + let opt_signer = self.signer.read(); + let signer = match opt_signer.as_ref() { + Some(signer) => signer, + None => return Ok(Vec::new()), // We are not a validator, so we shouldn't call the contracts. + }; + let our_addr = signer.address(); + let client = self.client.read().as_ref().and_then(|weak| weak.upgrade()).ok_or_else(|| { + debug!(target: "engine", "Unable to prepare block: missing client ref."); + EngineError::RequiresClient + })?; + let full_client = client.as_full_client() + .ok_or_else(|| EngineError::FailedSystemCall("Failed to upgrade to BlockchainClient.".to_string()))?; + + // Random number generation + let contract = util::BoundContract::new(&*client, BlockId::Latest, contract_addr); + let phase = randomness::RandomnessPhase::load(&contract, our_addr) + .map_err(|err| EngineError::Custom(format!("Randomness error in load(): {:?}", err)))?; + let data = match phase.advance(&contract, &mut OsRng, signer.as_ref()) + .map_err(|err| EngineError::Custom(format!("Randomness error in advance(): {:?}", err)))? { + Some(data) => data, + None => return Ok(Vec::new()), // Nothing to commit or reveal at the moment. + }; + + let nonce = block.state.nonce(&our_addr)?; + let tx_request = TransactionRequest::call(contract_addr, data).gas_price(U256::zero()).nonce(nonce); + Ok(vec![full_client.create_transaction(tx_request)?]) + } } fn unix_now() -> Duration { @@ -886,7 +1124,7 @@ fn unix_now() -> Duration { struct TransitionHandler { step: Arc, - client: Arc>>>, + client: Arc>>>, } const ENGINE_TIMEOUT_TOKEN: TimerToken = 23; @@ -908,22 +1146,24 @@ impl IoHandler<()> for TransitionHandler { 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(); + c.update_sealing(ForceUpdateSealing::No); } } } - 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)) + let next_run_at = Duration::from_millis( + AsMillis::as_millis(&self.step.inner.duration_remaining()) >> 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)) } } } -impl Engine for AuthorityRound { +impl Engine for AuthorityRound { fn name(&self) -> &str { "AuthorityRound" } - fn machine(&self) -> &EthereumMachine { &self.machine } + fn machine(&self) -> &Machine { &self.machine } /// Three fields - consensus step and the corresponding proposer signature, and a list of empty /// step messages (which should be empty if no steps are skipped) @@ -936,7 +1176,7 @@ impl Engine for AuthorityRound { 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(); + c.update_sealing(ForceUpdateSealing::No); } } } @@ -953,21 +1193,19 @@ impl Engine for AuthorityRound { let signature = header_signature(header, self.empty_steps_transition).as_ref() .map(ToString::to_string) .unwrap_or_default(); - let validator_count = self.validators.count(&header.hash()); let mut info = map![ "step".into() => step, - "signature".into() => signature, - "validatorCount".into() => format!("{:#x}", validator_count) + "signature".into() => signature ]; if header.number() >= self.empty_steps_transition { let empty_steps = if let Ok(empty_steps) = header_empty_steps(header).as_ref() { format!("[{}]", - empty_steps.iter().fold( - "".to_string(), - |acc, e| if acc.len() > 0 { acc + ","} else { acc } + &e.to_string())) + empty_steps.iter().fold( + "".to_string(), + |acc, e| if acc.len() > 0 { acc + ","} else { acc } + &e.to_string())) } else { "".into() @@ -1000,11 +1238,61 @@ impl Engine for AuthorityRound { let score = calculate_score(parent_step, current_step, current_empty_steps_len); header.set_difficulty(score); + if let Some(gas_limit) = self.gas_limit_override(header) { + trace!(target: "engine", "Setting gas limit to {} for block {}.", gas_limit, header.number()); + let parent_gas_limit = *parent.gas_limit(); + header.set_gas_limit(gas_limit); + if parent_gas_limit != gas_limit { + info!(target: "engine", "Block gas limit was changed from {} to {}.", parent_gas_limit, gas_limit); + } + } } - fn seals_internally(&self) -> Option { - // TODO: accept a `&Call` here so we can query the validator set. - Some(self.signer.read().is_some()) + fn sealing_state(&self) -> SealingState { + let our_addr = match *self.signer.read() { + Some(ref signer) => signer.address(), + None => { + warn!(target: "engine", "Not preparing block; cannot sign."); + return SealingState::NotReady; + } + }; + + let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { + Some(client) => client, + None => { + warn!(target: "engine", "Not preparing block: missing client ref."); + return SealingState::NotReady; + } + }; + + let parent = match client.as_full_client() { + Some(full_client) => full_client.best_block_header(), + None => { + debug!(target: "engine", "Not preparing block: not a full client."); + return SealingState::NotReady; + }, + }; + + let validators = if self.immediate_transitions { + CowLike::Borrowed(&*self.validators) + } else { + let mut epoch_manager = self.epoch_manager.lock(); + if !epoch_manager.zoom_to_after(&*client, &self.machine, &*self.validators, parent.hash()) { + debug!(target: "engine", "Not preparing block: Unable to zoom to epoch."); + return SealingState::NotReady; + } + CowLike::Owned(epoch_manager.validators().clone()) + }; + + let step = self.step.inner.load(); + + if !is_step_proposer(&*validators, &parent.hash(), step, &our_addr) { + trace!(target: "engine", "Not preparing block: not a proposer for step {}. (Our address: {})", + step, our_addr); + return SealingState::NotReady; + } + + SealingState::Ready } fn handle_message(&self, rlp: &[u8]) -> Result<(), EngineError> { @@ -1013,7 +1301,7 @@ impl Engine for AuthorityRound { } let rlp = Rlp::new(rlp); - let empty_step: EmptyStep = rlp.as_val().map_err(fmt_err)?;; + let empty_step: EmptyStep = rlp.as_val().map_err(fmt_err)?; if empty_step.verify(&*self.validators).unwrap_or(false) { if self.step.inner.check_future(empty_step.step).is_ok() { @@ -1049,25 +1337,30 @@ impl Engine for AuthorityRound { // filter messages from old and future steps and different parents let empty_steps = if header.number() >= self.empty_steps_transition { - self.empty_steps(parent_step.into(), step.into(), *header.parent_hash()) + self.empty_steps(parent_step, step, *header.parent_hash()) } else { Vec::new() }; - let expected_diff = calculate_score(parent_step, step.into(), empty_steps.len().into()); + let expected_diff = calculate_score(parent_step, step, empty_steps.len()); if header.difficulty() != &expected_diff { debug!(target: "engine", "Aborting seal generation. The step or empty_steps have changed in the meantime. {:?} != {:?}", - header.difficulty(), expected_diff); + header.difficulty(), expected_diff); return Seal::None; } - if parent_step > step.into() { + if parent_step > step { warn!(target: "engine", "Aborting seal generation for invalid step: {} > {}", parent_step, step); return Seal::None; + } else if parent_step == step { + // this is guarded against by `can_propose` unless the block was signed + // on the same step (implies same key) and on a different node. + warn!("Attempted to seal block on the same step as parent. Is this authority sealing with more than one node?"); + return Seal::None; } - let (validators, set_number) = match self.epoch_set(header) { + let (validators, epoch_transition_number) = match self.epoch_set(header) { Err(err) => { warn!(target: "engine", "Unable to generate seal: {}", err); return Seal::None; @@ -1076,13 +1369,7 @@ impl Engine for AuthorityRound { }; if is_step_proposer(&*validators, header.parent_hash(), step, header.author()) { - // this is guarded against by `can_propose` unless the block was signed - // on the same step (implies same key) and on a different node. - if parent_step == step { - warn!("Attempted to seal block on the same step as parent. Is this authority sealing with more than one node?"); - return Seal::None; - } - + trace!(target: "engine", "generate_seal: we are step proposer for step={}, block=#{}", step, header.number()); // if there are no transactions to include in the block, we don't seal and instead broadcast a signed // `EmptyStep(step, parent_hash)` message. If we exceed the maximum amount of `empty_step` rounds we proceed // with the seal. @@ -1091,6 +1378,7 @@ impl Engine for AuthorityRound { empty_steps.len() < self.maximum_empty_steps { if self.step.can_propose.compare_and_swap(true, false, AtomicOrdering::SeqCst) { + trace!(target: "engine", "generate_seal: generating empty step at step={}, block=#{}", step, header.number()); self.generate_empty_step(header.parent_hash()); } @@ -1117,18 +1405,18 @@ impl Engine for AuthorityRound { // report any skipped primaries between the parent block and // the block we're sealing, unless we have empty steps enabled if header.number() < self.empty_steps_transition { - self.report_skipped(header, step, parent_step, &*validators, set_number); + self.report_skipped(header, step, parent_step, &*validators, epoch_transition_number); } let mut fields = vec![ encode(&step), - encode(&(&H520::from(signature) as &[u8])), + encode(&(H520::from(signature).as_bytes())), ]; if let Some(empty_steps_rlp) = empty_steps_rlp { fields.push(empty_steps_rlp); } - + trace!(target: "engine", "generate_seal: returning Seal::Regular for step={}, block=#{}", step, header.number()); return Seal::Regular(fields); } } else { @@ -1138,7 +1426,7 @@ impl Engine for AuthorityRound { trace!(target: "engine", "generate_seal: {} not a proposer for step {}.", header.author(), step); } - + trace!(target: "engine", "generate_seal: returning Seal::None for step={}, block=#{}", step, header.number()); Seal::None } @@ -1150,7 +1438,6 @@ 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. @@ -1175,26 +1462,23 @@ impl Engine for AuthorityRound { } /// Apply the block reward on finalisation of the block. - fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + fn on_close_block( + &self, + block: &mut ExecutedBlock, + parent: &Header, + ) -> Result<(), Error> { let mut beneficiaries = Vec::new(); + + if block.header.number() == self.two_thirds_majority_transition { + info!(target: "engine", "Block {}: Transitioning to 2/3 quorum.", self.two_thirds_majority_transition); + } + 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 - let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { - Some(client) => client, - None => { - debug!(target: "engine", "Unable to close block: missing client ref."); - return Err(EngineError::RequiresClient.into()) - }, - }; - - let parent = client.block_header(::client::BlockId::Hash(*block.header.parent_hash())) - .expect("hash is from parent; parent header must exist; qed") - .decode()?; - - let parent_step = header_step(&parent, self.empty_steps_transition)?; + let parent_step = header_step(parent, self.empty_steps_transition)?; let current_step = self.step.inner.load(); - self.empty_steps(parent_step.into(), current_step.into(), parent.hash()) + self.empty_steps(parent_step, current_step, parent.hash()) } else { // we're verifying a block, extract empty steps from the seal header_empty_steps(&block.header)? @@ -1209,21 +1493,25 @@ impl Engine for AuthorityRound { let author = *block.header.author(); beneficiaries.push((author, RewardKind::Author)); - let rewards: Vec<_> = match self.block_reward_contract { - Some(ref c) if block.header.number() >= self.block_reward_contract_transition => { - let mut call = super::default_system_or_code_call(&self.machine, block); - - let rewards = c.reward(&beneficiaries, &mut call)?; - rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect() - }, - _ => { - beneficiaries.into_iter().map(|(author, reward_kind)| (author, reward_kind, self.block_reward)).collect() - }, + let block_reward_contract_transition = self + .block_reward_contract_transitions + .range(..=block.header.number()) + .last(); + let rewards: Vec<_> = if let Some((_, contract)) = block_reward_contract_transition { + let mut call = engine::default_system_or_code_call(&self.machine, block); + let rewards = contract.reward(beneficiaries, &mut call)?; + rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect() + } else { + beneficiaries.into_iter().map(|(author, reward_kind)| (author, reward_kind, self.block_reward)).collect() }; block_reward::apply_block_rewards(&rewards, block, &self.machine) } + fn generate_engine_transactions(&self, block: &ExecutedBlock) -> Result, Error> { + self.run_randomness_phase(block) + } + /// Check the number of seal fields. fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) { @@ -1237,13 +1525,16 @@ impl Engine for AuthorityRound { // This check runs in Phase 1 where there is no guarantee that the parent block is // already imported, therefore the call to `epoch_set` may fail. In that case we // won't report the misbehavior but this is not a concern because: - // - Only authorities can report and it's expected that they'll be up-to-date and - // importing, therefore the parent header will most likely be available + // - Authorities will have a signing key available to report and it's expected that + // they'll be up-to-date and importing, therefore the parent header will most likely + // be available // - Even if you are an authority that is syncing the chain, the contract will most // likely ignore old reports // - This specific check is only relevant if you're importing (since it checks // against wall clock) if let Ok((_, set_number)) = self.epoch_set(header) { + trace!(target: "engine", "Reporting benign misbehaviour (cause: InvalidSeal) at block #{}, epoch set number {}. Own address: {}", + header.number(), set_number, self.address().unwrap_or_default()); self.validators.report_benign(header.author(), set_number, header.number()); } @@ -1264,12 +1555,32 @@ impl Engine for AuthorityRound { // Ensure header is from the step after parent. if step == parent_step || (header.number() >= self.validate_step_transition && step <= parent_step) { - trace!(target: "engine", "Multiple blocks proposed for step {}.", parent_step); + warn!(target: "engine", "Multiple blocks proposed for step {}.", parent_step); self.validators.report_malicious(header.author(), set_number, header.number(), Default::default()); Err(EngineError::DoubleVote(*header.author()))?; } + // Report malice if the validator produced other sibling blocks in the same step. + let received_step_key = (step, *header.author()); + let new_hash = header.hash(); + if self.received_step_hashes.read().get(&received_step_key).map_or(false, |h| *h != new_hash) { + trace!(target: "engine", "Validator {} produced sibling blocks in the same step", header.author()); + self.validators.report_malicious(header.author(), set_number, header.number(), Default::default()); + } else { + self.received_step_hashes.write().insert(received_step_key, new_hash); + } + + // Remove hash records older than two full rounds of steps (picked as a reasonable trade-off between + // memory consumption and fault-tolerance). + let sibling_malice_detection_period = 2 * validators.count(&parent.hash()) as u64; + let oldest_step = parent_step.saturating_sub(sibling_malice_detection_period); + if oldest_step > 0 { + let mut rsh = self.received_step_hashes.write(); + let new_rsh = rsh.split_off(&(oldest_step, Address::zero())); + *rsh = new_rsh; + } + // If empty step messages are enabled we will validate the messages in the seal, missing messages are not // reported as there's no way to tell whether the empty step message was never sent or simply not included. let empty_steps_len = if header.number() >= self.empty_steps_transition { @@ -1314,6 +1625,8 @@ impl Engine for AuthorityRound { match validate_empty_steps() { Ok(len) => len, Err(err) => { + trace!(target: "engine", "Reporting benign misbehaviour (cause: invalid empty steps) at block #{}, epoch set number {}. Own address: {}", + header.number(), set_number, self.address().unwrap_or_default()); self.validators.report_benign(header.author(), set_number, header.number()); return Err(err); }, @@ -1342,7 +1655,9 @@ impl Engine for AuthorityRound { // contract itself. let res = verify_external(header, &*validators, self.empty_steps_transition); match res { - Err(Error(ErrorKind::Engine(EngineError::NotProposer(_)), _)) => { + Err(Error::Engine(EngineError::NotProposer(_))) => { + trace!(target: "engine", "Reporting benign misbehaviour (cause: block from incorrect proposer) at block #{}, epoch set number {}. Own address: {}", + header.number(), set_number, self.address().unwrap_or_default()); self.validators.report_benign(header.author(), set_number, header.number()); }, Ok(_) => { @@ -1360,10 +1675,8 @@ impl Engine for AuthorityRound { .map(|set_proof| combine_proofs(0, &set_proof, &[])) } - fn signals_epoch_end(&self, header: &Header, aux: AuxiliaryData) - -> super::EpochChange - { - if self.immediate_transitions { return super::EpochChange::No } + fn signals_epoch_end(&self, header: &Header, aux: AuxiliaryData) -> engine::EpochChange { + if self.immediate_transitions { return engine::EpochChange::No } let first = header.number() == 0; self.validators.signals_epoch_end(first, header, aux) @@ -1372,8 +1685,8 @@ impl Engine for AuthorityRound { fn is_epoch_end_light( &self, chain_head: &Header, - chain: &super::Headers
, - transition_store: &super::PendingTransitionStore, + chain: &Headers
, + transition_store: &PendingTransitionStore, ) -> Option> { // epochs only matter if we want to support light clients. if self.immediate_transitions { return None } @@ -1388,7 +1701,7 @@ impl Engine for AuthorityRound { }; let mut epoch_manager = self.epoch_manager.lock(); - if !epoch_manager.zoom_to(&*client, &self.machine, &*self.validators, chain_head) { + if !epoch_manager.zoom_to_after(&*client, &self.machine, &*self.validators, *chain_head.parent_hash()) { return None; } @@ -1397,7 +1710,7 @@ impl Engine for AuthorityRound { let mut hash = *chain_head.parent_hash(); - let mut ancestry = itertools::repeat_call(move || { + let mut ancestry = std::iter::repeat_with(move || { chain(hash).and_then(|header| { if header.number() == 0 { return None } hash = *header.parent_hash(); @@ -1416,8 +1729,8 @@ impl Engine for AuthorityRound { &self, chain_head: &Header, finalized: &[H256], - chain: &super::Headers
, - transition_store: &super::PendingTransitionStore, + chain: &Headers
, + transition_store: &PendingTransitionStore, ) -> Option> { // epochs only matter if we want to support light clients. if self.immediate_transitions { return None } @@ -1439,7 +1752,7 @@ impl Engine for AuthorityRound { // to construct transition proof. author == ec_recover(sig) known // since the blocks are in the DB. let mut hash = chain_head.hash(); - let mut finality_proof: Vec<_> = itertools::repeat_call(move || { + let mut finality_proof: Vec<_> = std::iter::repeat_with(move || { chain(hash).and_then(|header| { hash = *header.parent_hash(); if header.number() == 0 { None } @@ -1483,7 +1796,7 @@ impl Engine for AuthorityRound { None } - fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> { + fn epoch_verifier<'a>(&self, _header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> { let (signal_number, set_proof, finality_proof) = match destructure_proofs(proof) { Ok(x) => x, Err(e) => return ConstructedVerifier::Err(e), @@ -1496,6 +1809,7 @@ impl Engine for AuthorityRound { step: self.step.clone(), subchain_validators: list, empty_steps_transition: self.empty_steps_transition, + two_thirds_majority_transition: self.two_thirds_majority_transition, }); match finalize { @@ -1507,36 +1821,32 @@ impl Engine for AuthorityRound { } } - fn register_client(&self, client: Weak) { + fn register_client(&self, client: Weak) { *self.client.write() = Some(client.clone()); self.validators.register_client(client); } - fn set_signer(&self, signer: Box) { - *self.signer.write() = Some(signer); + fn set_signer(&self, signer: Option>) { + *self.signer.write() = signer; } fn sign(&self, hash: H256) -> Result { Ok(self.signer.read() .as_ref() - .ok_or(ethkey::Error::InvalidAddress)? + .ok_or(parity_crypto::publickey::Error::InvalidAddress)? .sign(hash)? ) } - fn snapshot_components(&self) -> Option> { + fn snapshot_mode(&self) -> Snapshotting { if self.immediate_transitions { - None + Snapshotting::Unsupported } else { - Some(Box::new(::snapshot::PoaSnapshot)) + Snapshotting::PoA } } - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { - super::total_difficulty_fork_choice(new, current) - } - - fn ancestry_actions(&self, header: &Header, ancestry: &mut Iterator) -> Vec { + fn ancestry_actions(&self, header: &Header, ancestry: &mut dyn Iterator) -> Vec { let finalized = self.build_finality( header, &mut ancestry.take_while(|e| !e.is_finalized).map(|e| e.header), @@ -1548,36 +1858,97 @@ impl Engine for AuthorityRound { finalized.into_iter().map(AncestryAction::MarkFinalized).collect() } + + fn params(&self) -> &CommonParams { + self.machine.params() + } + + fn gas_limit_override(&self, header: &Header) -> Option { + let (_, &address) = self.block_gas_limit_contract_transitions.range(..=header.number()).last()?; + let client = match self.client.read().as_ref().and_then(|weak| weak.upgrade()) { + Some(client) => client, + None => { + error!(target: "engine", "Unable to prepare block: missing client ref."); + return None; + } + }; + let full_client = match client.as_full_client() { + Some(full_client) => full_client, + None => { + error!(target: "engine", "Failed to upgrade to BlockchainClient."); + return None; + } + }; + if let Some(limit) = self.gas_limit_override_cache.lock().get_mut(&header.hash()) { + return *limit; + } + let limit = block_gas_limit(full_client, header, address); + self.gas_limit_override_cache.lock().insert(header.hash(), limit); + limit + } +} + +/// A helper accumulator function mapping a step duration and a step duration transition timestamp +/// to the corresponding step number and the correct starting second of the step. +fn next_step_time_duration(info: StepDurationInfo, time: u64) -> Option<(u64, u64)> +{ + let step_diff = time.checked_add(info.step_duration)? + .checked_sub(1)? + .checked_sub(info.transition_timestamp)? + .checked_div(info.step_duration)?; + let time_diff = step_diff.checked_mul(info.step_duration)?; + Some(( + info.transition_step.checked_add(step_diff)?, + info.transition_timestamp.checked_add(time_diff)?, + )) } #[cfg(test)] mod tests { use std::collections::BTreeMap; + use std::str::FromStr; use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; - use hash::keccak; + use std::sync::atomic::{AtomicUsize, AtomicU64, Ordering as AtomicOrdering}; + use std::time::Duration; + use keccak_hash::keccak; use accounts::AccountProvider; + use ethabi_contract::use_contract; use ethereum_types::{Address, H520, H256, U256}; - use ethkey::Signature; - use types::header::Header; + use parity_crypto::publickey::Signature; + use common_types::{ + header::Header, + engines::{Seal, params::CommonParams}, + ids::BlockId, + errors::{EthcoreError as Error, EngineError}, + transaction::{Action, Transaction}, + }; use rlp::encode; - use block::*; - use test_helpers::{ - generate_dummy_client_with_spec, get_temp_state_db, - TestNotify + use ethcore::{ + block::*, + miner::{Author, MinerService}, + test_helpers::{ + generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data, get_temp_state_db, + TestNotify + }, + }; + use engine::Engine; + use block_reward::BlockRewardContract; + use machine::Machine; + use spec::{self, Spec}; + use validator_set::{TestSet, SimpleList}; + use ethjson; + use serde_json; + + use super::{ + AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep, StepDurationInfo, + calculate_score, util::BoundContract, next_step_time_duration, }; - use spec::Spec; - use types::transaction::{Action, Transaction}; - use engines::{Seal, Engine, EngineError, EthEngine}; - use engines::validator_set::{TestSet, SimpleList}; - use error::{Error, ErrorKind}; - use super::{AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep, calculate_score}; - - fn aura(f: F) -> Arc where + + fn build_aura(f: F) -> Arc where F: FnOnce(&mut AuthorityRoundParams), { let mut params = AuthorityRoundParams { - step_duration: 1, + step_durations: [(0, 1)].to_vec().into_iter().collect(), start_step: Some(1), validators: Box::new(TestSet::default()), validate_score_transition: 0, @@ -1588,30 +1959,31 @@ 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(), + block_reward_contract_transitions: Default::default(), strict_empty_steps_transition: 0, + two_thirds_majority_transition: 0, + randomness_contract_address: BTreeMap::new(), + block_gas_limit_contract_transitions: BTreeMap::new(), }; // mutate aura params f(&mut params); - // create engine - let mut c_params = ::spec::CommonParams::default(); + let mut c_params = CommonParams::default(); c_params.gas_limit_bound_divisor = 5.into(); - let machine = ::machine::EthereumMachine::regular(c_params, Default::default()); + let machine = Machine::regular(c_params, Default::default()); AuthorityRound::new(params, machine).unwrap() } #[test] fn has_valid_metadata() { - let engine = Spec::new_test_round().engine; - assert!(!engine.name().is_empty()); + let engine = spec::new_test_round().engine; + assert_eq!(engine.name(), "AuthorityRound"); } #[test] fn can_return_schedule() { - let engine = Spec::new_test_round().engine; + let engine = spec::new_test_round().engine; let schedule = engine.schedule(10000000); assert!(schedule.stack_limit > 0); @@ -1619,7 +1991,7 @@ mod tests { #[test] fn can_do_signature_verification_fail() { - let engine = Spec::new_test_round().engine; + let engine = spec::new_test_round().engine; let mut header: Header = Header::default(); header.set_seal(vec![encode(&H520::default())]); @@ -1631,31 +2003,72 @@ mod tests { fn generates_seal_and_does_not_double_propose() { let tap = Arc::new(AccountProvider::transient_provider()); let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); - let addr2 = tap.insert_account(keccak("2").into(), &"2".into()).unwrap(); - - let spec = Spec::new_test_round(); + let spec = spec::new_test_round(); 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 b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b1 = b1.close_and_lock().unwrap(); - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); - let b2 = b2.close_and_lock().unwrap(); - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); if let Seal::Regular(seal) = engine.generate_seal(&b1, &genesis_header) { assert!(b1.clone().try_seal(engine, seal).is_ok()); // Second proposal is forbidden. assert!(engine.generate_seal(&b1, &genesis_header) == Seal::None); + } else { + panic!("block 1 not sealed"); } + } - engine.set_signer(Box::new((tap, addr2, "2".into()))); - if let Seal::Regular(seal) = engine.generate_seal(&b2, &genesis_header) { + #[test] + fn generates_seal_iff_sealer_is_set() { + let tap = Arc::new(AccountProvider::transient_provider()); + let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); + let spec = spec::new_test_round(); + let engine = &*spec.engine; + let genesis_header = spec.genesis_header(); + 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().close_and_lock().unwrap(); + // Not a signer. A seal cannot be generated. + assert!(engine.generate_seal(&b1, &genesis_header) == Seal::None); + // Become a signer. + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); + if let Seal::Regular(seal) = engine.generate_seal(&b1, &genesis_header) { + assert!(b1.clone().try_seal(engine, seal).is_ok()); + // Second proposal is forbidden. + assert!(engine.generate_seal(&b1, &genesis_header) == Seal::None); + } else { + panic!("block 1 not sealed"); + } + // Stop being a signer. + engine.set_signer(None); + // Make a step first and then create a new block in that new step. + engine.step(); + let addr2 = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); + let mut header2 = genesis_header.clone(); + header2.set_number(2); + header2.set_author(addr2); + header2.set_parent_hash(header2.hash()); + let db2 = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); + let b2 = OpenBlock::new(engine, Default::default(), false, db2, &header2, + last_hashes, addr2, (3141562.into(), 31415620.into()), + vec![], false) + .unwrap().close_and_lock().unwrap(); + // Not a signer. A seal cannot be generated. + assert!(engine.generate_seal(&b2, &header2) == Seal::None); + // Become a signer once more. + engine.set_signer(Some(Box::new((tap, addr2, "0".into())))); + if let Seal::Regular(seal) = engine.generate_seal(&b2, &header2) { assert!(b2.clone().try_seal(engine, seal).is_ok()); // Second proposal is forbidden. - assert!(engine.generate_seal(&b2, &genesis_header) == Seal::None); + assert!(engine.generate_seal(&b2, &header2) == Seal::None); + } else { + panic!("block 2 not sealed"); } } @@ -1665,7 +2078,7 @@ mod tests { let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); let addr2 = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); - let spec = Spec::new_test_round(); + let spec = spec::new_test_round(); let engine = &*spec.engine; let genesis_header = spec.genesis_header(); @@ -1673,20 +2086,20 @@ 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, None).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b1 = b1.close_and_lock().unwrap(); - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); + let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b2 = b2.close_and_lock().unwrap(); - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); match engine.generate_seal(&b1, &genesis_header) { - Seal::None | Seal::Proposal(_) => panic!("wrong seal"), + Seal::None => panic!("wrong seal"), Seal::Regular(_) => { engine.step(); - engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr2, "0".into())))); match engine.generate_seal(&b2, &genesis_header) { - Seal::Regular(_) | Seal::Proposal(_) => panic!("sealed despite wrong difficulty"), + Seal::Regular(_) => panic!("sealed despite wrong difficulty"), Seal::None => {} } } @@ -1705,7 +2118,7 @@ mod tests { header.set_gas_limit("222222".parse::().unwrap()); header.set_author(addr); - let engine = Spec::new_test_round().engine; + let engine = spec::new_test_round().engine; // Two validators. // Spec starts with step 2. @@ -1734,7 +2147,7 @@ mod tests { header.set_gas_limit("222222".parse::().unwrap()); header.set_author(addr); - let engine = Spec::new_test_round().engine; + let engine = spec::new_test_round().engine; // Two validators. // Spec starts with step 2. @@ -1760,7 +2173,7 @@ mod tests { header.set_gas_limit("222222".parse::().unwrap()); header.set_author(addr); - let engine = Spec::new_test_round().engine; + let engine = spec::new_test_round().engine; let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap(); // Two validators. @@ -1775,9 +2188,17 @@ mod tests { #[test] fn reports_skipped() { + let validator1 = Address::from_low_u64_be(1); + let validator2 = Address::from_low_u64_be(2); let last_benign = Arc::new(AtomicUsize::new(0)); - let aura = aura(|p| { - p.validators = Box::new(TestSet::new(Default::default(), last_benign.clone())); + + let aura = build_aura(|p| { + let validator_set = TestSet::new( + Default::default(), + last_benign.clone(), + vec![validator1, validator2], + ); + p.validators = Box::new(validator_set); }); let mut parent_header: Header = Header::default(); @@ -1792,7 +2213,11 @@ mod tests { assert!(aura.verify_block_family(&header, &parent_header).is_ok()); assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 0); - aura.set_signer(Box::new((Arc::new(AccountProvider::transient_provider()), Default::default(), "".into()))); + aura.set_signer(Some(Box::new(( + Arc::new(AccountProvider::transient_provider()), + validator2, + "".into(), + )))); // Do not report on steps skipped between genesis and first block. header.set_number(1); @@ -1805,9 +2230,41 @@ mod tests { assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 2); } + #[test] + fn reports_multiple_blocks_per_step() { + let tap = AccountProvider::transient_provider(); + let addr0 = tap.insert_account(keccak("0").into(), &"0".into()).unwrap(); + let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); + + let validator_set = TestSet::from_validators(vec![addr0, addr1]); + let aura = build_aura(|p| p.validators = Box::new(validator_set.clone())); + + aura.set_signer(Some(Box::new((Arc::new(tap), addr0, "0".into())))); + + let mut parent_header: Header = Header::default(); + parent_header.set_number(2); + parent_header.set_seal(vec![encode(&1usize)]); + parent_header.set_gas_limit("222222".parse::().unwrap()); + let mut header: Header = Header::default(); + header.set_number(3); + header.set_difficulty(calculate_score(1, 2, 0)); + header.set_gas_limit("222222".parse::().unwrap()); + header.set_seal(vec![encode(&2usize)]); + header.set_author(addr1); + + // First sibling block. + assert!(aura.verify_block_family(&header, &parent_header).is_ok()); + assert_eq!(validator_set.last_malicious(), 0); + + // Second sibling block: should be reported. + header.set_gas_limit("222223".parse::().unwrap()); + assert!(aura.verify_block_family(&header, &parent_header).is_ok()); + assert_eq!(validator_set.last_malicious(), 3); + } + #[test] fn test_uncles_transition() { - let aura = aura(|params| { + let aura = build_aura(|params| { params.maximum_uncle_count_transition = 1; }); @@ -1816,40 +2273,94 @@ mod tests { assert_eq!(aura.maximum_uncle_count(100), 0); } - #[test] - #[should_panic(expected="counter is too high")] - fn test_counter_increment_too_high() { - use super::Step; - let step = Step { - calibrate: false, - inner: AtomicUsize::new(::std::usize::MAX), - duration: 1, - }; - step.increment(); + #[test] + #[should_panic(expected="counter is too high")] + fn test_counter_increment_too_high() { + use super::Step; + let step = Step { + calibrate: false, + inner: AtomicU64::new(::std::u64::MAX), + durations: [StepDurationInfo { + transition_step: 0, + transition_timestamp: 0, + step_duration: 1, + }].to_vec().into_iter().collect(), + }; + step.increment(); } #[test] - #[should_panic(expected="counter is too high")] + #[should_panic(expected="step counter under- or overflow")] fn test_counter_duration_remaining_too_high() { use super::Step; let step = Step { calibrate: false, - inner: AtomicUsize::new(::std::usize::MAX), - duration: 1, + inner: AtomicU64::new(::std::u64::MAX), + durations: [StepDurationInfo { + transition_step: 0, + transition_timestamp: 0, + step_duration: 1, + }].to_vec().into_iter().collect(), }; step.duration_remaining(); } #[test] - #[should_panic(expected="authority_round: step duration can't be zero")] + fn test_next_step_time_duration() { + // At step 7 (time 1000), we transitioned to step duration 10. + let info = StepDurationInfo { + step_duration: 10, + transition_step: 7, + transition_timestamp: 1000, + }; + // So the next transition can happen e.g. at step 12 (time 1050) or 13 (time 1060). + assert_eq!(Some((12, 1050)), next_step_time_duration(info, 1050)); + assert_eq!(Some((13, 1060)), next_step_time_duration(info, 1051)); + assert_eq!(Some((13, 1060)), next_step_time_duration(info, 1055)); + // The next transition could also happen immediately. + assert_eq!(Some((7, 1000)), next_step_time_duration(info, 1000)); + } + + #[test] + fn test_change_step_duration() { + use super::Step; + use std::thread; + + let now = super::unix_now().as_secs(); + let step = Step { + calibrate: true, + inner: AtomicU64::new(::std::u64::MAX), + durations: [ + StepDurationInfo { transition_step: 0, transition_timestamp: 0, step_duration: 1 }, + StepDurationInfo { transition_step: now, transition_timestamp: now, step_duration: 2 }, + StepDurationInfo { transition_step: now + 1, transition_timestamp: now + 2, step_duration: 4 }, + ].to_vec().into_iter().collect(), + }; + // calibrated step `now` + step.calibrate(); + let duration_remaining = step.duration_remaining(); + assert_eq!(step.inner.load(AtomicOrdering::SeqCst), now); + assert!(duration_remaining <= Duration::from_secs(2)); + thread::sleep(duration_remaining); + step.increment(); + // calibrated step `now + 1` + step.calibrate(); + let duration_remaining = step.duration_remaining(); + assert_eq!(step.inner.load(AtomicOrdering::SeqCst), now + 1); + assert!(duration_remaining > Duration::from_secs(2)); + assert!(duration_remaining <= Duration::from_secs(4)); + } + + #[test] + #[should_panic(expected="called `Result::unwrap()` on an `Err` value: Engine(Custom(\"step duration cannot be 0\"))")] fn test_step_duration_zero() { - aura(|params| { - params.step_duration = 0; + build_aura(|params| { + params.step_durations = [(0, 0)].to_vec().into_iter().collect(); }); } fn setup_empty_steps() -> (Spec, Arc, Vec
) { - let spec = Spec::new_test_round_empty_steps(); + let spec = spec::new_test_round_empty_steps(); let tap = Arc::new(AccountProvider::transient_provider()); let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); @@ -1860,20 +2371,20 @@ mod tests { (spec, tap, accounts) } - fn empty_step(engine: &EthEngine, step: u64, parent_hash: &H256) -> EmptyStep { + fn empty_step(engine: &dyn Engine, step: u64, parent_hash: &H256) -> EmptyStep { let empty_step_rlp = super::empty_step_rlp(step, parent_hash); let signature = engine.sign(keccak(&empty_step_rlp)).unwrap().into(); let parent_hash = parent_hash.clone(); EmptyStep { step, signature, parent_hash } } - fn sealed_empty_step(engine: &EthEngine, step: u64, parent_hash: &H256) -> SealedEmptyStep { + fn sealed_empty_step(engine: &dyn Engine, step: u64, parent_hash: &H256) -> SealedEmptyStep { let empty_step_rlp = super::empty_step_rlp(step, parent_hash); let signature = engine.sign(keccak(&empty_step_rlp)).unwrap().into(); SealedEmptyStep { signature, step } } - fn set_empty_steps_seal(header: &mut Header, step: u64, block_signature: ðkey::Signature, empty_steps: &[SealedEmptyStep]) { + fn set_empty_steps_seal(header: &mut Header, step: u64, block_signature: &Signature, empty_steps: &[SealedEmptyStep]) { header.set_seal(vec![ encode(&(step as usize)), encode(&(&**block_signature as &[u8])), @@ -1883,7 +2394,7 @@ mod tests { fn assert_insufficient_proof(result: Result, contains: &str) { match result { - Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _)) =>{ + Err(Error::Engine(EngineError::InsufficientProof(ref s))) =>{ assert!(s.contains(contains), "Expected {:?} to contain {:?}", s, contains); }, e => assert!(false, "Unexpected result: {:?}", e), @@ -1902,14 +2413,14 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); - let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); + let client = generate_dummy_client_with_spec(spec::new_test_round_empty_steps); let notify = Arc::new(TestNotify::default()); client.add_notify(notify.clone()); engine.register_client(Arc::downgrade(&client) as _); - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false, None).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b1 = b1.close_and_lock().unwrap(); // the block is empty so we don't seal and instead broadcast an empty step message @@ -1941,22 +2452,22 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); - let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); + let client = generate_dummy_client_with_spec(spec::new_test_round_empty_steps); let notify = Arc::new(TestNotify::default()); client.add_notify(notify.clone()); 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, None).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b1 = b1.close_and_lock().unwrap(); // since the block is empty it isn't sealed and we generate empty steps - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); 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, None).unwrap(); + let mut b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap(); b2.push_transaction(Transaction { action: Action::Create, nonce: U256::from(0), @@ -1968,9 +2479,9 @@ mod tests { let b2 = b2.close_and_lock().unwrap(); // we will now seal a block with 1tx and include the accumulated empty step message - engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr2, "0".into())))); if let Seal::Regular(seal) = engine.generate_seal(&b2, &genesis_header) { - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); let empty_step2 = sealed_empty_step(engine, 2, &genesis_header.hash()); let empty_steps = ::rlp::encode_list(&vec![empty_step2]); @@ -1994,36 +2505,36 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); - let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); + let client = generate_dummy_client_with_spec(spec::new_test_round_empty_steps); let notify = Arc::new(TestNotify::default()); client.add_notify(notify.clone()); 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, None).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b1 = b1.close_and_lock().unwrap(); // since the block is empty it isn't sealed and we generate empty steps - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); 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, None).unwrap(); + let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr2, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b2 = b2.close_and_lock().unwrap(); - engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr2, "0".into())))); assert_eq!(engine.generate_seal(&b2, &genesis_header), Seal::None); engine.step(); // 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, None).unwrap(); + let b3 = OpenBlock::new(engine, Default::default(), false, db3, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b3 = b3.close_and_lock().unwrap(); - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); if let Seal::Regular(seal) = engine.generate_seal(&b3, &genesis_header) { let empty_step2 = sealed_empty_step(engine, 2, &genesis_header.hash()); - engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr2, "0".into())))); let empty_step3 = sealed_empty_step(engine, 3, &genesis_header.hash()); let empty_steps = ::rlp::encode_list(&vec![empty_step2, empty_step3]); @@ -2046,21 +2557,21 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); - let client = generate_dummy_client_with_spec(Spec::new_test_round_empty_steps); + let client = generate_dummy_client_with_spec(spec::new_test_round_empty_steps); 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, None).unwrap(); + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b1 = b1.close_and_lock().unwrap(); // since the block is empty it isn't sealed and we generate empty steps - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); assert_eq!(engine.generate_seal(&b1, &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, None).unwrap(); + let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let addr1_balance = b2.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 @@ -2090,7 +2601,7 @@ mod tests { let signature = tap.sign(addr1, Some("1".into()), header.bare_hash()).unwrap(); // empty step with invalid step - let empty_steps = vec![SealedEmptyStep { signature: 0.into(), step: 2 }]; + let empty_steps = vec![SealedEmptyStep { signature: H520::zero(), step: 2 }]; set_empty_steps_seal(&mut header, 2, &signature, &empty_steps); assert_insufficient_proof( @@ -2099,7 +2610,7 @@ mod tests { ); // empty step with invalid signature - let empty_steps = vec![SealedEmptyStep { signature: 0.into(), step: 1 }]; + let empty_steps = vec![SealedEmptyStep { signature: H520::zero(), step: 1 }]; set_empty_steps_seal(&mut header, 2, &signature, &empty_steps); assert_insufficient_proof( @@ -2108,7 +2619,7 @@ mod tests { ); // empty step with valid signature from incorrect proposer for step - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); let empty_steps = vec![sealed_empty_step(engine, 1, &parent_header.hash())]; set_empty_steps_seal(&mut header, 2, &signature, &empty_steps); @@ -2118,9 +2629,9 @@ mod tests { ); // valid empty steps - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); let empty_step2 = sealed_empty_step(engine, 2, &parent_header.hash()); - engine.set_signer(Box::new((tap.clone(), addr2, "0".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr2, "0".into())))); let empty_step3 = sealed_empty_step(engine, 3, &parent_header.hash()); let empty_steps = vec![empty_step2, empty_step3]; @@ -2133,7 +2644,7 @@ mod tests { #[test] fn block_reward_contract() { - let spec = Spec::new_test_round_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".into()).unwrap(); @@ -2145,7 +2656,7 @@ mod tests { let last_hashes = Arc::new(vec![genesis_header.hash()]); - let client = generate_dummy_client_with_spec(Spec::new_test_round_block_reward_contract); + let client = generate_dummy_client_with_spec(spec::new_test_round_block_reward_contract); engine.register_client(Arc::downgrade(&client) as _); // step 2 @@ -2160,12 +2671,11 @@ mod tests { (3141562.into(), 31415620.into()), vec![], false, - None, ).unwrap(); let b1 = b1.close_and_lock().unwrap(); // since the block is empty it isn't sealed and we generate empty steps - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); assert_eq!(engine.generate_seal(&b1, &genesis_header), Seal::None); engine.step(); @@ -2182,7 +2692,6 @@ mod tests { (3141562.into(), 31415620.into()), vec![], false, - None, ).unwrap(); let addr1_balance = b2.state.balance(&addr1).unwrap(); @@ -2197,13 +2706,61 @@ mod tests { ) } + #[test] + fn randomness_contract() -> Result<(), super::util::CallError> { + use_contract!(rand_contract, "../../res/contracts/test_authority_round_random.json"); + + env_logger::init(); + + let contract_addr = Address::from_str("0000000000000000000000000000000000000042").unwrap(); + let client = generate_dummy_client_with_spec_and_data( + spec::new_test_round_randomness_contract, 0, 0, &[], true + ); + + let tap = Arc::new(AccountProvider::transient_provider()); + + let addr1 = tap.insert_account(keccak("1").into(), &"1".into()).unwrap(); + // Unlock account so that the engine can decrypt the secret. + tap.unlock_account_permanently(addr1, "1".into()).expect("unlock"); + + let signer = Box::new((tap.clone(), addr1, "1".into())); + client.miner().set_author(Author::Sealer(signer.clone())); + client.miner().set_gas_range_target((U256::from(1000000), U256::from(1000000))); + + let engine = client.engine(); + engine.set_signer(Some(signer)); + engine.register_client(Arc::downgrade(&client) as _); + let bc = BoundContract::new(&*client, BlockId::Latest, contract_addr); + + // First the contract is in the commit phase, and we haven't committed yet. + assert!(bc.call_const(rand_contract::functions::is_commit_phase::call())?); + assert!(!bc.call_const(rand_contract::functions::is_committed::call(0, addr1))?); + + // We produce a block and commit. + engine.step(); + assert!(bc.call_const(rand_contract::functions::is_committed::call(0, addr1))?); + + // After two more blocks we are in the reveal phase... + engine.step(); + engine.step(); + assert!(bc.call_const(rand_contract::functions::is_reveal_phase::call())?); + assert!(!bc.call_const(rand_contract::functions::sent_reveal::call(0, addr1))?); + assert!(bc.call_const(rand_contract::functions::get_value::call())?.is_zero()); + + // ...so in the next step, we reveal our random value, and the contract's random value is not zero anymore. + engine.step(); + assert!(bc.call_const(rand_contract::functions::sent_reveal::call(0, addr1))?); + assert!(!bc.call_const(rand_contract::functions::get_value::call())?.is_zero()); + Ok(()) + } + #[test] fn extra_info_from_seal() { let (spec, tap, accounts) = setup_empty_steps(); let engine = &*spec.engine; let addr1 = accounts[0]; - engine.set_signer(Box::new((tap.clone(), addr1, "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), addr1, "1".into())))); let mut header: Header = Header::default(); let empty_step = empty_step(engine, 1, &header.parent_hash()); @@ -2235,13 +2792,13 @@ mod tests { #[test] fn test_empty_steps() { - let engine = aura(|p| { - p.step_duration = 4; + let engine = build_aura(|p| { + p.step_durations = [(0, 4)].to_vec().into_iter().collect(); p.empty_steps_transition = 0; p.maximum_empty_steps = 0; }); - let parent_hash: H256 = 1.into(); + let parent_hash = H256::from_low_u64_be(1); let signature = H520::default(); let step = |step: u64| EmptyStep { step, @@ -2268,9 +2825,9 @@ mod tests { fn should_reject_duplicate_empty_steps() { // given let (_spec, tap, accounts) = setup_empty_steps(); - let engine = aura(|p| { + let engine = build_aura(|p| { p.validators = Box::new(SimpleList::new(accounts.clone())); - p.step_duration = 4; + p.step_durations = [(0, 4)].to_vec().into_iter().collect(); p.empty_steps_transition = 0; p.maximum_empty_steps = 0; }); @@ -2284,7 +2841,7 @@ mod tests { header.set_author(accounts[0]); // when - engine.set_signer(Box::new((tap.clone(), accounts[1], "0".into()))); + engine.set_signer(Some(Box::new((tap.clone(), accounts[1], "0".into())))); let empty_steps = vec![ sealed_empty_step(&*engine, 1, &parent.hash()), sealed_empty_step(&*engine, 1, &parent.hash()), @@ -2305,9 +2862,9 @@ mod tests { fn should_reject_empty_steps_out_of_order() { // given let (_spec, tap, accounts) = setup_empty_steps(); - let engine = aura(|p| { + let engine = build_aura(|p| { p.validators = Box::new(SimpleList::new(accounts.clone())); - p.step_duration = 4; + p.step_durations = [(0, 4)].to_vec().into_iter().collect(); p.empty_steps_transition = 0; p.maximum_empty_steps = 0; }); @@ -2321,9 +2878,9 @@ mod tests { header.set_author(accounts[0]); // when - engine.set_signer(Box::new((tap.clone(), accounts[1], "0".into()))); + engine.set_signer(Some(Box::new((tap.clone(), accounts[1], "0".into())))); let es1 = sealed_empty_step(&*engine, 1, &parent.hash()); - engine.set_signer(Box::new((tap.clone(), accounts[0], "1".into()))); + engine.set_signer(Some(Box::new((tap.clone(), accounts[0], "1".into())))); let es2 = sealed_empty_step(&*engine, 2, &parent.hash()); let mut empty_steps = vec![es2, es1]; @@ -2344,4 +2901,55 @@ mod tests { set_empty_steps_seal(&mut header, step, &signature, &empty_steps); assert_eq!(engine.verify_block_family(&header, &parent).unwrap(), ()); } + + #[test] + fn should_collect_block_reward_transitions() { + let config = r#"{ + "params": { + "stepDuration": "5", + "validators": { + "list" : ["0x1000000000000000000000000000000000000001"] + }, + "blockRewardContractTransition": "0", + "blockRewardContractAddress": "0x2000000000000000000000000000000000000002", + "blockRewardContractTransitions": { + "7": "0x3000000000000000000000000000000000000003", + "42": "0x4000000000000000000000000000000000000004" + } + } + }"#; + let deserialized: ethjson::spec::AuthorityRound = serde_json::from_str(config).unwrap(); + let params = AuthorityRoundParams::from(deserialized.params); + for ((block_num1, address1), (block_num2, address2)) in + params.block_reward_contract_transitions.iter().zip( + [(0u64, BlockRewardContract::new_from_address(Address::from_str("2000000000000000000000000000000000000002").unwrap())), + (7u64, BlockRewardContract::new_from_address(Address::from_str("3000000000000000000000000000000000000003").unwrap())), + (42u64, BlockRewardContract::new_from_address(Address::from_str("4000000000000000000000000000000000000004").unwrap())), + ].iter()) + { + assert_eq!(block_num1, block_num2); + assert_eq!(address1, address2); + } + } + + #[test] + #[should_panic(expected="blockRewardContractTransition should be less than any of the keys in blockRewardContractTransitions")] + fn should_reject_out_of_order_block_reward_transition() { + let config = r#"{ + "params": { + "stepDuration": "5", + "validators": { + "list" : ["0x1000000000000000000000000000000000000001"] + }, + "blockRewardContractTransition": "7", + "blockRewardContractAddress": "0x2000000000000000000000000000000000000002", + "blockRewardContractTransitions": { + "0": "0x3000000000000000000000000000000000000003", + "42": "0x4000000000000000000000000000000000000004" + } + } + }"#; + let deserialized: ethjson::spec::AuthorityRound = serde_json::from_str(config).unwrap(); + AuthorityRoundParams::from(deserialized.params); + } } diff --git a/ethcore/engines/authority-round/src/randomness.rs b/ethcore/engines/authority-round/src/randomness.rs new file mode 100644 index 0000000000..d300c602d9 --- /dev/null +++ b/ethcore/engines/authority-round/src/randomness.rs @@ -0,0 +1,257 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! On-chain randomness generation for authority round +//! +//! This module contains the support code for the on-chain randomness generation used by AuRa. Its +//! core is the finite state machine `RandomnessPhase`, which can be loaded from the blockchain +//! state, then asked to perform potentially necessary transaction afterwards using the `advance()` +//! method. +//! +//! No additional state is kept inside the `RandomnessPhase`, it must be passed in each time. +//! +//! The process of generating random numbers is a simple finite state machine: +//! +//! ```text +//! + +//! | +//! | +//! | +//! +--------------+ +-------v-------+ +//! | | | | +//! | BeforeCommit <------------------------------+ Waiting | +//! | | enter commit phase | | +//! +------+-------+ +-------^-------+ +//! | | +//! | call | +//! | `commitHash()` | call +//! | | `revealNumber()` +//! | | +//! +------v-------+ +-------+-------+ +//! | | | | +//! | Committed +------------------------------> Reveal | +//! | | enter reveal phase | | +//! +--------------+ +---------------+ +//! ``` +//! +//! Phase transitions are performed by the smart contract and simply queried by the engine. +//! +//! Randomness generation works as follows: +//! * During the commit phase, all validators locally generate a random number, and commit that number's hash to the +//! contract. +//! * During the reveal phase, all validators reveal their local random number to the contract. The contract should +//! verify that it matches the committed hash. +//! * Finally, the XOR of all revealed numbers is used as an on-chain random number. +//! +//! An adversary can only influence that number by either controlling _all_ validators who committed, or, to a lesser +//! extent, by not revealing committed numbers. +//! The length of the commit and reveal phases, as well as any penalties for failure to reveal, are defined by the +//! contract. +//! +//! A typical case of using `RandomnessPhase` is: +//! +//! 1. `RandomnessPhase::load()` the phase from the blockchain data. +//! 2. Call `RandomnessPhase::advance()`. +//! +//! A production implementation of a randomness contract can be found here: +//! https://github.com/poanetwork/posdao-contracts/blob/4fddb108993d4962951717b49222327f3d94275b/contracts/RandomAuRa.sol + +use derive_more::Display; +use ethabi::Hash; +use ethabi_contract::use_contract; +use ethereum_types::{Address, H256, U256}; +use keccak_hash::keccak; +use log::{debug, error}; +use parity_crypto::publickey::{ecies, Error as CryptoError}; +use parity_bytes::Bytes; +use rand::Rng; +use engine::signer::EngineSigner; + +use crate::util::{BoundContract, CallError}; + +/// Random number type expected by the contract: This is generated locally, kept secret during the commit phase, and +/// published in the reveal phase. +pub type RandNumber = H256; + +use_contract!(aura_random, "../../res/contracts/authority_round_random.json"); + +/// Validated randomness phase state. +#[derive(Debug)] +pub enum RandomnessPhase { + // NOTE: Some states include information already gathered during `load` (e.g. `our_address`, + // `round`) for efficiency reasons. + /// Waiting for the next phase. + /// + /// This state indicates either the successful revelation in this round or having missed the + /// window to make a commitment, i.e. having failed to commit during the commit phase. + Waiting, + /// Indicates a commitment is possible, but still missing. + BeforeCommit, + /// Indicates a successful commitment, waiting for the commit phase to end. + Committed, + /// Indicates revealing is expected as the next step. + Reveal { our_address: Address, round: U256 }, +} + +/// Phase loading error for randomness generation state machine. +/// +/// This error usually indicates a bug in either the smart contract, the phase loading function or +/// some state being lost. +/// +/// `BadRandNumber` will usually result in punishment by the contract or the other validators. +#[derive(Debug, Display)] +pub enum PhaseError { + /// The smart contract reported that we already revealed something while still being in the + /// commit phase. + #[display(fmt = "Revealed during commit phase")] + RevealedInCommit, + /// Failed to load contract information. + #[display(fmt = "Error loading randomness contract information: {:?}", _0)] + LoadFailed(CallError), + /// Failed to load the stored encrypted random number. + #[display(fmt = "Failed to load random number from the randomness contract")] + BadRandNumber, + /// Failed to encrypt random number. + #[display(fmt = "Failed to encrypt random number: {}", _0)] + Crypto(CryptoError), + /// Failed to get the engine signer's public key. + #[display(fmt = "Failed to get the engine signer's public key")] + MissingPublicKey, +} + +impl From for PhaseError { + fn from(err: CryptoError) -> PhaseError { + PhaseError::Crypto(err) + } +} + +impl RandomnessPhase { + /// Determine randomness generation state from the contract. + /// + /// Calls various constant contract functions to determine the precise state that needs to be + /// handled (that is, the phase and whether or not the current validator still needs to send + /// commitments or reveal random numbers). + pub fn load( + contract: &BoundContract, + our_address: Address, + ) -> Result { + // Determine the current round and which phase we are in. + let round = contract + .call_const(aura_random::functions::current_collect_round::call()) + .map_err(PhaseError::LoadFailed)?; + let is_commit_phase = contract + .call_const(aura_random::functions::is_commit_phase::call()) + .map_err(PhaseError::LoadFailed)?; + + // Ensure we are not committing or revealing twice. + let committed = contract + .call_const(aura_random::functions::is_committed::call( + round, + our_address, + )) + .map_err(PhaseError::LoadFailed)?; + let revealed: bool = contract + .call_const(aura_random::functions::sent_reveal::call( + round, + our_address, + )) + .map_err(PhaseError::LoadFailed)?; + + // With all the information known, we can determine the actual state we are in. + if is_commit_phase { + if revealed { + return Err(PhaseError::RevealedInCommit); + } + + if !committed { + Ok(RandomnessPhase::BeforeCommit) + } else { + Ok(RandomnessPhase::Committed) + } + } else { + if !committed { + // We apparently entered too late to make a commitment, wait until we get a chance again. + return Ok(RandomnessPhase::Waiting); + } + + if !revealed { + Ok(RandomnessPhase::Reveal { our_address, round }) + } else { + Ok(RandomnessPhase::Waiting) + } + } + } + + /// Advance the random seed construction process as far as possible. + /// + /// Returns the encoded contract call necessary to advance the randomness contract's state. + /// + /// **Warning**: After calling the `advance()` function, wait until the returned transaction has been included in + /// a block before calling it again; otherwise spurious transactions resulting in punishments might be executed. + pub fn advance( + self, + contract: &BoundContract, + rng: &mut R, + signer: &dyn EngineSigner, + ) -> Result, PhaseError> { + match self { + RandomnessPhase::Waiting | RandomnessPhase::Committed => Ok(None), + RandomnessPhase::BeforeCommit => { + // Generate a new random number, but don't reveal it yet. Instead, we publish its hash to the + // randomness contract, together with the number encrypted to ourselves. That way we will later be + // able to decrypt and reveal it, and other parties are able to verify it against the hash. + let number: RandNumber = rng.gen(); + let number_hash: Hash = keccak(number.as_bytes()); + let public = signer.public().ok_or(PhaseError::MissingPublicKey)?; + let cipher = ecies::encrypt(&public, &number_hash.0, number.as_bytes())?; + + debug!(target: "engine", "Randomness contract: committing {}.", number_hash); + // Return the call data for the transaction that commits the hash and the encrypted number. + let (data, _decoder) = aura_random::functions::commit_hash::call(number_hash, cipher); + Ok(Some(data)) + } + RandomnessPhase::Reveal { round, our_address } => { + // Load the hash and encrypted number that we stored in the commit phase. + let call = aura_random::functions::get_commit_and_cipher::call(round, our_address); + let (committed_hash, cipher) = contract + .call_const(call) + .map_err(PhaseError::LoadFailed)?; + + // Decrypt the number and check against the hash. + let number_bytes = signer.decrypt(&committed_hash.0, &cipher)?; + let number = if number_bytes.len() == 32 { + RandNumber::from_slice(&number_bytes) + } else { + // This can only happen if there is a bug in the smart contract, + // or if the entire network goes awry. + error!(target: "engine", "Decrypted random number has the wrong length."); + return Err(PhaseError::BadRandNumber); + }; + let number_hash: Hash = keccak(number.as_bytes()); + if number_hash != committed_hash { + error!(target: "engine", "Decrypted random number doesn't agree with the hash."); + return Err(PhaseError::BadRandNumber); + } + + debug!(target: "engine", "Randomness contract: scheduling tx to reveal our random number {} (round={}, our_address={}).", number_hash, round, our_address); + // We are now sure that we have the correct secret and can reveal it. So we return the call data for the + // transaction that stores the revealed random bytes on the contract. + let (data, _decoder) = aura_random::functions::reveal_number::call(number.as_bytes()); + Ok(Some(data)) + } + } + } +} diff --git a/ethcore/engines/authority-round/src/util.rs b/ethcore/engines/authority-round/src/util.rs new file mode 100644 index 0000000000..9a99979dbc --- /dev/null +++ b/ethcore/engines/authority-round/src/util.rs @@ -0,0 +1,94 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Utility functions. +//! +//! Contains small functions used by the AuRa engine that are not strictly limited to that scope. + +use std::fmt; + +use client_traits::EngineClient; +use common_types::ids::BlockId; +use ethabi; +use ethereum_types::Address; + +/// A contract bound to a client and block number. +/// +/// A bound contract is a combination of a `Client` reference, a `BlockId` and a contract `Address`. +/// These three parts are enough to call a contract's function; return values are automatically +/// decoded. +pub struct BoundContract<'a> { + client: &'a dyn EngineClient, + block_id: BlockId, + contract_addr: Address, +} + +/// Contract call failed error. +#[derive(Debug)] +pub enum CallError { + /// The call itself failed. + CallFailed(String), + /// Decoding the return value failed or the decoded value was a failure. + DecodeFailed(ethabi::Error), + /// The passed in client reference could not be upgraded to a `BlockchainClient`. + NotFullClient, +} + +impl<'a> fmt::Debug for BoundContract<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("BoundContract") + .field("client", &(self.client as *const dyn EngineClient)) + .field("block_id", &self.block_id) + .field("contract_addr", &self.contract_addr) + .finish() + } +} + +impl<'a> BoundContract<'a> { + /// Create a new `BoundContract`. + pub fn new(client: &dyn EngineClient, block_id: BlockId, contract_addr: Address) -> BoundContract { + BoundContract { + client, + block_id, + contract_addr, + } + } + + /// Perform a function call to an Ethereum machine that doesn't create a transaction or change the state. + /// + /// Runs a constant function call on `client`. The `call` value can be serialized by calling any + /// api function generated by the `use_contract!` macro. This does not create any transactions, it only produces a + /// result based on the state at the current block: It is constant in the sense that it does not alter the EVM + /// state. + pub fn call_const(&self, call: (ethabi::Bytes, D)) -> Result + where + D: ethabi::FunctionOutputDecoder, + { + let (data, output_decoder) = call; + + let call_return = self + .client + .as_full_client() + .ok_or(CallError::NotFullClient)? + .call_contract(self.block_id, self.contract_addr, data) + .map_err(CallError::CallFailed)?; + + // Decode the result and return it. + output_decoder + .decode(call_return.as_slice()) + .map_err(CallError::DecodeFailed) + } +} diff --git a/ethcore/engines/basic-authority/Cargo.toml b/ethcore/engines/basic-authority/Cargo.toml new file mode 100644 index 0000000000..849014aec4 --- /dev/null +++ b/ethcore/engines/basic-authority/Cargo.toml @@ -0,0 +1,31 @@ +[package] +description = "Basic PoA blockchain engine." +name = "basic-authority" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +client-traits = { path = "../../client-traits" } +common-types = { path = "../../types" } +engine = { path = "../../engine" } +ethereum-types = "0.8.0" +ethjson = { path = "../../../json" } +parity-crypto = { version = "0.4.2", features = ["publickey"] } +log = "0.4.8" +machine = { path = "../../machine" } +parking_lot = "0.9" +rlp = "0.4.2" +validator-set = { path = "../validator-set" } + +[dev-dependencies] +accounts = { package = "ethcore-accounts", path = "../../../accounts" } +engine = { path = "../../engine", features = ["test-helpers"] } +ethcore = { path = "../..", features = ["test-helpers"] } +keccak-hash = "0.4.0" +tempdir = "0.3" +spec = { path = "../../spec" } + +[features] +test-helpers = [] diff --git a/ethcore/res/basic_authority.json b/ethcore/engines/basic-authority/res/basic_authority.json similarity index 80% rename from ethcore/res/basic_authority.json rename to ethcore/engines/basic-authority/res/basic_authority.json index 2d92355e3a..463b14afca 100644 --- a/ethcore/res/basic_authority.json +++ b/ethcore/engines/basic-authority/res/basic_authority.json @@ -42,12 +42,12 @@ "balance": "1", "builtin": { "name": "alt_bn128_add", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0x0": { + "price": { "linear": { "base": 500, "word": 0 }} + }, + "0x7fffffffffffff": { + "price": { "linear": { "base": 150, "word": 0 }} } } } @@ -56,12 +56,12 @@ "balance": "1", "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0x0": { + "price": { "linear": { "base": 40000, "word": 0 }} + }, + "0x7fffffffffffff": { + "price": { "linear": { "base": 6000, "word": 0 }} } } } @@ -70,14 +70,12 @@ "balance": "1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0x0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/engines/basic-authority/src/lib.rs similarity index 69% rename from ethcore/src/engines/basic_authority.rs rename to ethcore/engines/basic-authority/src/lib.rs index 69b8d07c52..f4501f698d 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/engines/basic-authority/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,18 +17,28 @@ //! A blockchain engine that supports a basic, non-BFT proof-of-authority. use std::sync::Weak; + +use common_types::{ + header::Header, + engines::{ + Headers, + PendingTransitionStore, + SealingState, + Seal, + params::CommonParams, + machine::{AuxiliaryData, Call}, + }, + errors::{EngineError, BlockError, EthcoreError as Error}, +}; +use client_traits::EngineClient; use ethereum_types::{H256, H520}; use parking_lot::RwLock; -use ethkey::{self, Signature}; -use block::*; -use engines::{Engine, Seal, ConstructedVerifier, EngineError}; -use engines::signer::EngineSigner; -use error::{BlockError, Error}; -use ethjson; -use client::EngineClient; -use machine::{AuxiliaryData, Call, EthereumMachine}; -use types::header::{Header, ExtendedHeader}; -use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; +use engine::{Engine, ConstructedVerifier, signer::EngineSigner}; +use parity_crypto::publickey::Signature; +use log::trace; +use machine::{Machine, executed_block::ExecutedBlock}; +use rlp::Rlp; +use validator_set::{ValidatorSet, SimpleList, new_validator_set}; /// `BasicAuthority` params. #[derive(Debug, PartialEq)] @@ -49,18 +59,16 @@ struct EpochVerifier { list: SimpleList, } -impl super::EpochVerifier for EpochVerifier { +impl engine::EpochVerifier for EpochVerifier { fn verify_light(&self, header: &Header) -> Result<(), Error> { verify_external(header, &self.list) } } -fn verify_external(header: &Header, validators: &ValidatorSet) -> Result<(), Error> { - use rlp::Rlp; - +fn verify_external(header: &Header, validators: &dyn ValidatorSet) -> Result<(), Error> { // Check if the signature belongs to a validator, can depend on parent state. let sig = Rlp::new(&header.seal()[0]).as_val::()?; - let signer = ethkey::public_to_address(ðkey::recover(&sig.into(), &header.bare_hash())?); + let signer = parity_crypto::publickey::public_to_address(&parity_crypto::publickey::recover(&sig.into(), &header.bare_hash())?); if *header.author() != signer { return Err(EngineError::NotAuthorized(*header.author()).into()) @@ -74,14 +82,14 @@ fn verify_external(header: &Header, validators: &ValidatorSet) -> Result<(), Err /// Engine using `BasicAuthority`, trivial proof-of-authority consensus. pub struct BasicAuthority { - machine: EthereumMachine, - signer: RwLock>>, - validators: Box, + machine: Machine, + signer: RwLock>>, + validators: Box, } impl BasicAuthority { /// Create a new instance of BasicAuthority engine - pub fn new(our_params: BasicAuthorityParams, machine: EthereumMachine) -> Self { + pub fn new(our_params: BasicAuthorityParams, machine: Machine) -> Self { BasicAuthority { machine: machine, signer: RwLock::new(None), @@ -90,16 +98,20 @@ impl BasicAuthority { } } -impl Engine for BasicAuthority { +impl Engine for BasicAuthority { fn name(&self) -> &str { "BasicAuthority" } - fn machine(&self) -> &EthereumMachine { &self.machine } + fn machine(&self) -> &Machine { &self.machine } // One field - the signature fn seal_fields(&self, _header: &Header) -> usize { 1 } - fn seals_internally(&self) -> Option { - Some(self.signer.read().is_some()) + fn sealing_state(&self) -> SealingState { + if self.signer.read().is_some() { + SealingState::Ready + } else { + SealingState::NotReady + } } /// Attempt to seal the block internally. @@ -109,7 +121,7 @@ impl Engine for BasicAuthority { if self.validators.contains(header.parent_hash(), author) { // account should be pernamently unlocked, otherwise sealing will fail if let Ok(signature) = self.sign(header.bare_hash()) { - return Seal::Regular(vec![::rlp::encode(&(&H520::from(signature) as &[u8]))]); + return Seal::Regular(vec![rlp::encode(&(H520::from(signature).as_bytes()))]); } else { trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable"); } @@ -129,18 +141,14 @@ impl Engine for BasicAuthority { self.validators.genesis_epoch_data(header, call) } - #[cfg(not(test))] - fn signals_epoch_end(&self, _header: &Header, _auxiliary: AuxiliaryData) - -> super::EpochChange - { + #[cfg(not(any(test, feature = "test-helpers")))] + fn signals_epoch_end(&self, _header: &Header, _auxiliary: AuxiliaryData) -> engine::EpochChange { // don't bother signalling even though a contract might try. - super::EpochChange::No + engine::EpochChange::No } - #[cfg(test)] - fn signals_epoch_end(&self, header: &Header, auxiliary: AuxiliaryData) - -> super::EpochChange - { + #[cfg(any(test, feature = "test-helpers"))] + fn signals_epoch_end(&self, header: &Header, auxiliary: AuxiliaryData) -> engine::EpochChange { // in test mode, always signal even though they don't be finalized. let first = header.number() == 0; self.validators.signals_epoch_end(first, header, auxiliary) @@ -150,8 +158,8 @@ impl Engine for BasicAuthority { &self, chain_head: &Header, _finalized: &[H256], - _chain: &super::Headers
, - _transition_store: &super::PendingTransitionStore, + _chain: &Headers
, + _transition_store: &PendingTransitionStore, ) -> Option> { let first = chain_head.number() == 0; @@ -162,18 +170,18 @@ impl Engine for BasicAuthority { fn is_epoch_end_light( &self, chain_head: &Header, - chain: &super::Headers
, - transition_store: &super::PendingTransitionStore, + chain: &Headers
, + transition_store: &PendingTransitionStore, ) -> Option> { self.is_epoch_end(chain_head, &[], chain, transition_store) } - fn epoch_verifier<'a>(&self, header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a, EthereumMachine> { + fn epoch_verifier<'a>(&self, header: &Header, proof: &'a [u8]) -> ConstructedVerifier<'a> { let first = header.number() == 0; match self.validators.epoch_set(first, &self.machine, header.number(), proof) { Ok((list, finalize)) => { - let verifier = Box::new(EpochVerifier { list: list }); + let verifier = Box::new(EpochVerifier { list }); // our epoch verifier will ensure no unverified verifier is ever verified. match finalize { @@ -185,47 +193,47 @@ impl Engine for BasicAuthority { } } - fn register_client(&self, client: Weak) { - self.validators.register_client(client); - } - - fn set_signer(&self, signer: Box) { - *self.signer.write() = Some(signer); + fn set_signer(&self, signer: Option>) { + *self.signer.write() = signer; } fn sign(&self, hash: H256) -> Result { Ok(self.signer.read() .as_ref() - .ok_or_else(|| ethkey::Error::InvalidAddress)? + .ok_or_else(|| parity_crypto::publickey::Error::InvalidAddress)? .sign(hash)? ) } - fn snapshot_components(&self) -> Option> { - None + fn register_client(&self, client: Weak) { + self.validators.register_client(client); } - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { - super::total_difficulty_fork_choice(new, current) + fn params(&self) -> &CommonParams { + self.machine.params() } } #[cfg(test)] mod tests { use std::sync::Arc; - use hash::keccak; + use keccak_hash::keccak; use ethereum_types::H520; - use block::*; - use test_helpers::get_temp_state_db; + use ethcore::{ + block::*, + test_helpers::get_temp_state_db + }; use accounts::AccountProvider; - use types::header::Header; use spec::Spec; - use engines::Seal; + use common_types::{ + header::Header, + engines::{Seal, SealingState} + }; use tempdir::TempDir; /// Create a new test chain spec with `BasicAuthority` consensus engine. fn new_test_authority() -> Spec { - let bytes: &[u8] = include_bytes!("../../res/basic_authority.json"); + let bytes: &[u8] = include_bytes!("../res/basic_authority.json"); let tempdir = TempDir::new("").unwrap(); Spec::load(&tempdir.path(), bytes).expect("invalid chain spec") } @@ -233,7 +241,7 @@ mod tests { #[test] fn has_valid_metadata() { let engine = new_test_authority().engine; - assert!(!engine.name().is_empty()); + assert_eq!(engine.name(), "BasicAuthority"); } #[test] @@ -247,7 +255,7 @@ mod tests { fn can_do_signature_verification_fail() { let engine = new_test_authority().engine; let mut header: Header = Header::default(); - header.set_seal(vec![::rlp::encode(&H520::default())]); + header.set_seal(vec![rlp::encode(&H520::default())]); let verify_result = engine.verify_block_external(&header); assert!(verify_result.is_err()); @@ -260,11 +268,11 @@ mod tests { let spec = new_test_authority(); let engine = &*spec.engine; - engine.set_signer(Box::new((Arc::new(tap), addr, "".into()))); + engine.set_signer(Some(Box::new((Arc::new(tap), addr, "".into())))); 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, None).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b = b.close_and_lock().unwrap(); if let Seal::Regular(seal) = engine.generate_seal(&b, &genesis_header) { assert!(b.try_seal(engine, seal).is_ok()); @@ -272,13 +280,15 @@ mod tests { } #[test] - fn seals_internally() { + fn sealing_state() { let tap = AccountProvider::transient_provider(); let authority = tap.insert_account(keccak("").into(), &"".into()).unwrap(); let engine = new_test_authority().engine; - assert!(!engine.seals_internally().unwrap()); - engine.set_signer(Box::new((Arc::new(tap), authority, "".into()))); - assert!(engine.seals_internally().unwrap()); + assert_eq!(SealingState::NotReady, engine.sealing_state()); + engine.set_signer(Some(Box::new((Arc::new(tap), authority, "".into())))); + assert_eq!(SealingState::Ready, engine.sealing_state()); + engine.set_signer(None); + assert_eq!(SealingState::NotReady, engine.sealing_state()); } } diff --git a/ethcore/engines/clique/Cargo.toml b/ethcore/engines/clique/Cargo.toml new file mode 100644 index 0000000000..7a73f04c81 --- /dev/null +++ b/ethcore/engines/clique/Cargo.toml @@ -0,0 +1,31 @@ +[package] +description = "Clique consensus engine" +name = "clique" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +client-traits = { path = "../../client-traits" } +common-types = { path = "../../types" } +ethereum-types = "0.8.0" +ethjson = { path = "../../../json" } +parity-crypto = { version = "0.4.2", features = ["publickey"] } +engine = { path = "../../engine" } +keccak-hash = "0.4.0" +lazy_static = "1.3.0" +log = "0.4" +lru-cache = "0.1" +machine = { path = "../../machine" } +macros = { path = "../../../util/macros" } +rand = "0.7" +parking_lot = "0.9" +rlp = "0.4.0" +time-utils = { path = "../../../util/time-utils" } +unexpected = { path = "../../../util/unexpected" } + +[dev-dependencies] +ethcore = { path = "../..", features = ["test-helpers"] } +spec = { path = "../../spec" } +state-db = { path = "../../state-db" } diff --git a/ethcore/engines/clique/src/block_state.rs b/ethcore/engines/clique/src/block_state.rs new file mode 100644 index 0000000000..a6f8103571 --- /dev/null +++ b/ethcore/engines/clique/src/block_state.rs @@ -0,0 +1,374 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +use std::{ + collections::{HashMap, BTreeSet, VecDeque}, + fmt, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +use common_types::{ + BlockNumber, + header::Header, + errors::{BlockError, EthcoreError as Error, EngineError}, +}; +use ethereum_types::{Address, H64}; +use log::{debug, trace}; +use rand::Rng; +use time_utils::CheckedSystemTime; +use unexpected::Mismatch; + +use crate::{ + util::{extract_signers, recover_creator}, + {VoteType, DIFF_INTURN, DIFF_NOTURN, NULL_AUTHOR, SIGNING_DELAY_NOTURN_MS}, +}; + +/// Type that keeps track of the state for a given vote +// Votes that go against the proposal aren't counted since it's equivalent to not voting +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +pub struct VoteState { + kind: VoteType, + votes: u64, +} + +/// Type that represent a vote +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +pub struct Vote { + block_number: BlockNumber, + beneficiary: Address, + kind: VoteType, + signer: Address, + reverted: bool, +} + +/// Type that represent a pending vote +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd)] +pub struct PendingVote { + signer: Address, + beneficiary: Address, +} + +/// Clique state for each block. +#[cfg(not(test))] +#[derive(Clone, Debug, Default)] +pub struct CliqueBlockState { + /// Current votes for a beneficiary + votes: HashMap, + /// A list of all votes for the given epoch + votes_history: Vec, + /// a list of all valid signer, sorted by ascending order. + signers: BTreeSet
, + /// a deque of recent signer, new entry should be pushed front, apply() modifies this. + recent_signers: VecDeque
, + /// inturn signing should wait until this time + pub next_timestamp_inturn: Option, + /// noturn signing should wait until this time + pub next_timestamp_noturn: Option, +} + +#[cfg(test)] +#[derive(Clone, Debug, Default)] +pub struct CliqueBlockState { + /// All recorded votes for a given signer, `Vec` is a stack of votes + pub votes: HashMap, + /// A list of all votes for the given epoch + pub votes_history: Vec, + /// a list of all valid signer, sorted by ascending order. + pub signers: BTreeSet
, + /// a deque of recent signer, new entry should be pushed front, apply() modifies this. + pub recent_signers: VecDeque
, + /// inturn signing should wait until this time + pub next_timestamp_inturn: Option, + /// noturn signing should wait until this time + pub next_timestamp_noturn: Option, +} + +impl fmt::Display for CliqueBlockState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let signers: Vec = self.signers.iter() + .map(|s| + format!("{} {:?}", + s, + self.votes.iter().map(|(v, s)| format!("[beneficiary {}, votes: {}]", v.beneficiary, s.votes)) + .collect::>() + ) + ) + .collect(); + + let recent_signers: Vec = self.recent_signers.iter().map(|s| format!("{}", s)).collect(); + let num_votes = self.votes_history.len(); + let add_votes = self.votes_history.iter().filter(|v| v.kind == VoteType::Add).count(); + let rm_votes = self.votes_history.iter().filter(|v| v.kind == VoteType::Remove).count(); + let reverted_votes = self.votes_history.iter().filter(|v| v.reverted).count(); + + write!(f, + "Votes {{ \n signers: {:?} \n recent_signers: {:?} \n number of votes: {} \n number of add votes {} + \r number of remove votes {} \n number of reverted votes: {}}}", + signers, recent_signers, num_votes, add_votes, rm_votes, reverted_votes) + } +} + +impl CliqueBlockState { + /// Create new state with given information, this is used creating new state from Checkpoint block. + pub fn new(signers: BTreeSet
) -> Self { + CliqueBlockState { + signers, + ..Default::default() + } + } + + // see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L474 + fn verify(&self, header: &Header) -> Result { + let creator = recover_creator(header)?.clone(); + + // The signer is not authorized + if !self.signers.contains(&creator) { + trace!(target: "engine", "current state: {}", self); + Err(EngineError::NotAuthorized(creator))? + } + + // The signer has signed a block too recently + if self.recent_signers.contains(&creator) { + trace!(target: "engine", "current state: {}", self); + Err(EngineError::CliqueTooRecentlySigned(creator))? + } + + // Wrong difficulty + let inturn = self.is_inturn(header.number(), &creator); + + if inturn && *header.difficulty() != DIFF_INTURN { + Err(BlockError::InvalidDifficulty(Mismatch { + expected: DIFF_INTURN, + found: *header.difficulty(), + }))? + } + + if !inturn && *header.difficulty() != DIFF_NOTURN { + Err(BlockError::InvalidDifficulty(Mismatch { + expected: DIFF_NOTURN, + found: *header.difficulty(), + }))? + } + + Ok(creator) + } + + /// Verify and apply a new header to current state + pub fn apply(&mut self, header: &Header, is_checkpoint: bool) -> Result { + let creator = self.verify(header)?; + self.recent_signers.push_front(creator); + self.rotate_recent_signers(); + + if is_checkpoint { + // checkpoint block should not affect previous tallying, so we check that. + let signers = extract_signers(header)?; + if self.signers != signers { + let invalid_signers: Vec = signers.into_iter() + .filter(|s| !self.signers.contains(s)) + .map(|s| format!("{}", s)) + .collect(); + Err(EngineError::CliqueFaultyRecoveredSigners(invalid_signers))? + }; + + // TODO(niklasad1): I'm not sure if we should shrink here because it is likely that next epoch + // will need some memory and might be better for allocation algorithm to decide whether to shrink or not + // (typically doubles or halves the allocted memory when necessary) + self.votes.clear(); + self.votes_history.clear(); + self.votes.shrink_to_fit(); + self.votes_history.shrink_to_fit(); + } + + // Contains vote + if *header.author() != NULL_AUTHOR { + let decoded_seal = header.decode_seal::>()?; + if decoded_seal.len() != 2 { + Err(BlockError::InvalidSealArity(Mismatch { expected: 2, found: decoded_seal.len() }))? + } + + let nonce = H64::from_slice(decoded_seal[1]); + self.update_signers_on_vote(VoteType::from_nonce(nonce)?, creator, *header.author(), header.number())?; + } + + Ok(creator) + } + + fn update_signers_on_vote( + &mut self, + kind: VoteType, + signer: Address, + beneficiary: Address, + block_number: u64 + ) -> Result<(), Error> { + + trace!(target: "engine", "Attempt vote {:?} {:?}", kind, beneficiary); + + let pending_vote = PendingVote { signer, beneficiary }; + + let reverted = if self.is_valid_vote(&beneficiary, kind) { + self.add_vote(pending_vote, kind) + } else { + // This case only happens if a `signer` wants to revert their previous vote + // (does nothing if no previous vote was found) + self.revert_vote(pending_vote) + }; + + // Add all votes to the history + self.votes_history.push( + Vote { + block_number, + beneficiary, + kind, + signer, + reverted, + }); + + // If no vote was found for the beneficiary return `early` but don't propogate an error + let (votes, vote_kind) = match self.get_current_votes_and_kind(beneficiary) { + Some((v, k)) => (v, k), + None => return Ok(()), + }; + let threshold = self.signers.len() / 2; + + debug!(target: "engine", "{}/{} votes to have consensus", votes, threshold + 1); + trace!(target: "engine", "votes: {:?}", votes); + + if votes > threshold { + match vote_kind { + VoteType::Add => { + if self.signers.insert(beneficiary) { + debug!(target: "engine", "added new signer: {}", beneficiary); + } + } + VoteType::Remove => { + if self.signers.remove(&beneficiary) { + debug!(target: "engine", "removed signer: {}", beneficiary); + } + } + } + + self.rotate_recent_signers(); + self.remove_all_votes_from(beneficiary); + } + + Ok(()) + } + + /// Calculate the next timestamp for `inturn` and `noturn` fails if any of them can't be represented as + /// `SystemTime` + // TODO(niklasad1): refactor this method to be in constructor of `CliqueBlockState` instead. + // This is a quite bad API because we must mutate both variables even when already `inturn` fails + // That's why we can't return early and must have the `if-else` in the end + pub fn calc_next_timestamp(&mut self, timestamp: u64, period: u64) -> Result<(), Error> { + let inturn = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(timestamp.saturating_add(period))); + + self.next_timestamp_inturn = inturn; + + let delay = Duration::from_millis( + rand::thread_rng().gen_range(0u64, (self.signers.len() as u64 / 2 + 1) * SIGNING_DELAY_NOTURN_MS)); + self.next_timestamp_noturn = inturn.map(|inturn| { + inturn + delay + }); + + if self.next_timestamp_inturn.is_some() && self.next_timestamp_noturn.is_some() { + Ok(()) + } else { + Err(BlockError::TimestampOverflow)? + } + } + + /// Returns true if the block difficulty should be `inturn` + pub fn is_inturn(&self, current_block_number: u64, author: &Address) -> bool { + if let Some(pos) = self.signers.iter().position(|x| *author == *x) { + return current_block_number % self.signers.len() as u64 == pos as u64; + } + false + } + + /// Returns whether the signer is authorized to sign a block + pub fn is_authorized(&self, author: &Address) -> bool { + self.signers.contains(author) && !self.recent_signers.contains(author) + } + + /// Returns whether it makes sense to cast the specified vote in the + /// current state (e.g. don't try to add an already authorized signer). + pub fn is_valid_vote(&self, address: &Address, vote_type: VoteType) -> bool { + let in_signer = self.signers.contains(address); + match vote_type { + VoteType::Add => !in_signer, + VoteType::Remove => in_signer, + } + } + + /// Returns the list of current signers + pub fn signers(&self) -> &BTreeSet
{ + &self.signers + } + + // Note this method will always return `true` but it is intended for a uniform `API` + fn add_vote(&mut self, pending_vote: PendingVote, kind: VoteType) -> bool { + + self.votes.entry(pending_vote) + .and_modify(|state| { + state.votes = state.votes.saturating_add(1); + }) + .or_insert_with(|| VoteState { kind, votes: 1 }); + true + } + + fn revert_vote(&mut self, pending_vote: PendingVote) -> bool { + let mut revert = false; + let mut remove = false; + + self.votes.entry(pending_vote).and_modify(|state| { + if state.votes.saturating_sub(1) == 0 { + remove = true; + } + revert = true; + }); + + if remove { + self.votes.remove(&pending_vote); + } + + revert + } + + fn get_current_votes_and_kind(&self, beneficiary: Address) -> Option<(usize, VoteType)> { + let kind = self.votes.iter() + .find(|(v, _t)| v.beneficiary == beneficiary) + .map(|(_v, t)| t.kind)?; + + let votes = self.votes.keys() + .filter(|vote| vote.beneficiary == beneficiary) + .count(); + + Some((votes, kind)) + } + + fn rotate_recent_signers(&mut self) { + if self.recent_signers.len() >= ( self.signers.len() / 2 ) + 1 { + self.recent_signers.pop_back(); + } + } + + fn remove_all_votes_from(&mut self, beneficiary: Address) { + self.votes = std::mem::replace(&mut self.votes, HashMap::new()) + .into_iter() + .filter(|(v, _t)| v.signer != beneficiary && v.beneficiary != beneficiary) + .collect(); + } +} diff --git a/ethcore/engines/clique/src/lib.rs b/ethcore/engines/clique/src/lib.rs new file mode 100644 index 0000000000..6638897edb --- /dev/null +++ b/ethcore/engines/clique/src/lib.rs @@ -0,0 +1,808 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Implementation of the Clique PoA Engine. +//! +//! File structure: +//! - mod.rs -> Provides the engine API implementation, with additional block state tracking +//! - block_state.rs -> Records the Clique state for given block. +//! - params.rs -> Contains the parameters for the Clique engine. +//! - step_service.rs -> An event loop to trigger sealing. +//! - util.rs -> Various standalone utility functions. +//! - tests.rs -> Consensus tests as defined in EIP-225. + +/// How syncing works: +/// +/// 1. Client will call: +/// - `Clique::verify_block_basic()` +/// - `Clique::verify_block_unordered()` +/// - `Clique::verify_block_family()` +/// 2. Using `Clique::state()` we try and retrieve the parent state. If this isn't found +/// we need to back-fill it from the last known checkpoint. +/// 3. Once we have a good state, we can record it using `CliqueBlockState::apply()`. + +/// How sealing works: +/// +/// 1. Set a signer using `Engine::set_signer()`. If a miner account was set up through +/// a config file or CLI flag `MinerService::set_author()` will eventually set the signer +/// 2. We check that the engine is ready for sealing through `Clique::sealing_state()` +/// Note: This is always `SealingState::Ready` for Clique +/// 3. Calling `Clique::new()` will spawn a `StepService` thread. This thread will call `Engine::step()` +/// periodically. Internally, the Clique `step()` function calls `Client::update_sealing()`, which is +/// what makes and seals a block. +/// 4. `Clique::generate_seal()` will then be called by `miner`. This will return a `Seal` which +/// is either a `Seal::None` or `Seal:Regular`. The following shows how a `Seal` variant is chosen: +/// a. We return `Seal::None` if no signer is available or the signer is not authorized. +/// b. If period == 0 and block has transactions, we return `Seal::Regular`, otherwise return `Seal::None`. +/// c. If we're `INTURN`, wait for at least `period` since last block before trying to seal. +/// d. If we're not `INTURN`, we wait for a random amount of time using the algorithm specified +/// in EIP-225 before trying to seal again. +/// 5. Miner will create new block, in process it will call several engine methods to do following: +/// a. `Clique::open_block_header_timestamp()` must set timestamp correctly. +/// b. `Clique::populate_from_parent()` must set difficulty to correct value. +/// Note: `Clique::populate_from_parent()` is used in both the syncing and sealing code paths. +/// 6. We call `Clique::on_seal_block()` which will allow us to modify the block header during seal generation. +/// 7. Finally, `Clique::verify_local_seal()` is called. After this, the syncing code path will be followed +/// in order to import the new block. + +use std::{ + cmp, + collections::{HashMap, VecDeque, BTreeMap}, + sync::{Arc, Weak}, + thread, + time::{self, Instant, Duration, SystemTime, UNIX_EPOCH}, +}; + +use client_traits::{EngineClient, ForceUpdateSealing}; +use engine::{ + Engine, + signer::EngineSigner, +}; +use ethereum_types::{Address, H64, H160, H256, U256}; +use parity_crypto::publickey::Signature; +use keccak_hash::KECCAK_EMPTY_LIST_RLP; +use log::{trace, warn}; +use lru_cache::LruCache; +use machine::{ + ExecutedBlock, + Machine, +}; +use macros::map; +use parking_lot::RwLock; +use rand::Rng; +use unexpected::{Mismatch, OutOfBounds}; +use time_utils::CheckedSystemTime; +use common_types::{ + BlockNumber, + ids::BlockId, + header::Header, + engines::{ + EthashSeal, + Seal, + SealingState, + params::CommonParams, + machine::Call, + }, + errors::{BlockError, EthcoreError as Error, EngineError}, +}; + +use crate::{ + util::{extract_signers, recover_creator}, + block_state::CliqueBlockState, + params::CliqueParams, +}; + + +mod params; +mod block_state; +mod util; + +// TODO(niklasad1): extract tester types into a separate mod to be shared in the code base +#[cfg(test)] +mod tests; + +// Protocol constants +/// Fixed number of extra-data prefix bytes reserved for signer vanity +pub const VANITY_LENGTH: usize = 32; +/// Fixed number of extra-data suffix bytes reserved for signer signature +pub const SIGNATURE_LENGTH: usize = 65; +/// Address length of signer +pub const ADDRESS_LENGTH: usize = 20; +/// Nonce value for DROP vote +pub const NONCE_DROP_VOTE: H64 = H64([0; 8]); +/// Nonce value for AUTH vote +pub const NONCE_AUTH_VOTE: H64 = H64([0xff; 8]); +/// Difficulty for INTURN block +pub const DIFF_INTURN: U256 = U256([2, 0, 0, 0]); +/// Difficulty for NOTURN block +pub const DIFF_NOTURN: U256 = U256([1, 0, 0, 0]); +/// Default empty author field value +pub const NULL_AUTHOR: Address = H160([0x00; 20]); +/// Default empty nonce value +pub const NULL_NONCE: H64 = NONCE_DROP_VOTE; +/// Default value for mixhash +pub const NULL_MIXHASH: H256 = H256([0; 32]); +/// Default value for uncles hash +pub const NULL_UNCLES_HASH: H256 = KECCAK_EMPTY_LIST_RLP; +/// Default noturn block wiggle factor defined in spec. +pub const SIGNING_DELAY_NOTURN_MS: u64 = 500; + +/// How many CliqueBlockState to cache in the memory. +pub const STATE_CACHE_NUM: usize = 128; + +/// Vote to add or remove the beneficiary +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +pub enum VoteType { + Add, + Remove, +} + +impl VoteType { + /// Try to construct a `Vote` from a nonce + pub fn from_nonce(nonce: H64) -> Result { + if nonce == NONCE_AUTH_VOTE { + Ok(VoteType::Add) + } else if nonce == NONCE_DROP_VOTE { + Ok(VoteType::Remove) + } else { + Err(EngineError::CliqueInvalidNonce(nonce))? + } + } + + /// Get the rlp encoding of the vote + pub fn as_rlp(&self) -> Vec> { + match self { + VoteType::Add => vec![rlp::encode(&NULL_MIXHASH), rlp::encode(&NONCE_AUTH_VOTE)], + VoteType::Remove => vec![rlp::encode(&NULL_MIXHASH), rlp::encode(&NONCE_DROP_VOTE)], + } + } +} + +/// Clique Engine implementation +// block_state_by_hash -> block state indexed by header hash. +#[cfg(not(test))] +pub struct Clique { + epoch_length: u64, + period: u64, + machine: Machine, + client: RwLock>>, + block_state_by_hash: RwLock>, + proposals: RwLock>, + signer: RwLock>>, +} + +#[cfg(test)] +/// Test version of `CliqueEngine` to make all fields public +pub struct Clique { + pub epoch_length: u64, + pub period: u64, + pub machine: Machine, + pub client: RwLock>>, + pub block_state_by_hash: RwLock>, + pub proposals: RwLock>, + pub signer: RwLock>>, +} + +impl Clique { + /// Initialize Clique engine from empty state. + pub fn new(params: CliqueParams, machine: Machine) -> Result, Error> { + /// Step Clique at most every 2 seconds + const SEALING_FREQ: Duration = Duration::from_secs(2); + + let engine = Clique { + epoch_length: params.epoch, + period: params.period, + client: Default::default(), + block_state_by_hash: RwLock::new(LruCache::new(STATE_CACHE_NUM)), + proposals: Default::default(), + signer: Default::default(), + machine, + }; + let engine = Arc::new(engine); + let weak_eng = Arc::downgrade(&engine); + + thread::Builder::new().name("StepService".into()) + .spawn(move || { + loop { + let next_step_at = Instant::now() + SEALING_FREQ; + trace!(target: "miner", "StepService: triggering sealing"); + if let Some(eng) = weak_eng.upgrade() { + eng.step() + } else { + warn!(target: "shutdown", "StepService: engine is dropped; exiting."); + break; + } + + let now = Instant::now(); + if now < next_step_at { + thread::sleep(next_step_at - now); + } + } + })?; + Ok(engine) + } + + #[cfg(test)] + /// Initialize test variant of `CliqueEngine`, + /// Note we need to `mock` the miner and it is introduced to test block verification to trigger new blocks + /// to mainly test consensus edge cases + pub fn with_test(epoch_length: u64, period: u64) -> Self { + Self { + epoch_length, + period, + client: Default::default(), + block_state_by_hash: RwLock::new(LruCache::new(STATE_CACHE_NUM)), + proposals: Default::default(), + signer: Default::default(), + machine: spec::new_test_machine(), + } + } + + fn sign_header(&self, header: &Header) -> Result<(Signature, H256), Error> { + + match self.signer.read().as_ref() { + None => { + Err(EngineError::RequiresSigner)? + } + Some(signer) => { + let digest = header.hash(); + match signer.sign(digest) { + Ok(sig) => Ok((sig, digest)), + Err(e) => Err(EngineError::Custom(e.into()))?, + } + } + } + } + + /// Construct an new state from given checkpoint header. + fn new_checkpoint_state(&self, header: &Header) -> Result { + debug_assert_eq!(header.number() % self.epoch_length, 0); + + let mut state = CliqueBlockState::new(extract_signers(header)?); + + // TODO(niklasad1): refactor to perform this check in the `CliqueBlockState` constructor instead + state.calc_next_timestamp(header.timestamp(), self.period)?; + + Ok(state) + } + + fn state_no_backfill(&self, hash: &H256) -> Option { + self.block_state_by_hash.write().get_mut(hash).cloned() + } + + /// Get `CliqueBlockState` for given header, backfill from last checkpoint if needed. + fn state(&self, header: &Header) -> Result { + let mut block_state_by_hash = self.block_state_by_hash.write(); + if let Some(state) = block_state_by_hash.get_mut(&header.hash()) { + return Ok(state.clone()); + } + // If we are looking for an checkpoint block state, we can directly reconstruct it. + if header.number() % self.epoch_length == 0 { + let state = self.new_checkpoint_state(header)?; + block_state_by_hash.insert(header.hash(), state.clone()); + return Ok(state); + } + // BlockState is not found in memory, which means we need to reconstruct state from last checkpoint. + match self.client.read().as_ref().and_then(|w| w.upgrade()) { + None => { + return Err(EngineError::RequiresClient)?; + } + Some(c) => { + let last_checkpoint_number = header.number() - header.number() % self.epoch_length as u64; + debug_assert_ne!(last_checkpoint_number, header.number()); + + // Catching up state, note that we don't really store block state for intermediary blocks, + // for speed. + let backfill_start = time::Instant::now(); + trace!(target: "engine", + "Back-filling block state. last_checkpoint_number: {}, target: {}({}).", + last_checkpoint_number, header.number(), header.hash()); + + let mut chain = VecDeque::with_capacity((header.number() - last_checkpoint_number + 1) as usize); + + // Put ourselves in. + chain.push_front(header.clone()); + + // populate chain to last checkpoint + loop { + let (last_parent_hash, last_num) = { + let l = chain.front().expect("chain has at least one element; qed"); + (*l.parent_hash(), l.number()) + }; + + if last_num == last_checkpoint_number + 1 { + break; + } + match c.block_header(BlockId::Hash(last_parent_hash)) { + None => { + return Err(BlockError::UnknownParent(last_parent_hash))?; + } + Some(next) => { + chain.push_front(next.decode()?); + } + } + } + + // Get the state for last checkpoint. + let last_checkpoint_hash = *chain.front() + .expect("chain has at least one element; qed") + .parent_hash(); + + let last_checkpoint_header = match c.block_header(BlockId::Hash(last_checkpoint_hash)) { + None => return Err(EngineError::CliqueMissingCheckpoint(last_checkpoint_hash))?, + Some(header) => header.decode()?, + }; + + let last_checkpoint_state = match block_state_by_hash.get_mut(&last_checkpoint_hash) { + Some(state) => state.clone(), + None => self.new_checkpoint_state(&last_checkpoint_header)?, + }; + + block_state_by_hash.insert(last_checkpoint_header.hash(), last_checkpoint_state.clone()); + + // Backfill! + let mut new_state = last_checkpoint_state.clone(); + for item in &chain { + new_state.apply(item, false)?; + } + new_state.calc_next_timestamp(header.timestamp(), self.period)?; + block_state_by_hash.insert(header.hash(), new_state.clone()); + + let elapsed = backfill_start.elapsed(); + trace!(target: "engine", "Back-filling succeed, took {} ms.", elapsed.as_millis()); + Ok(new_state) + } + } + } +} + +impl Engine for Clique { + fn name(&self) -> &str { "Clique" } + + fn machine(&self) -> &Machine { &self.machine } + + // Clique use same fields, nonce + mixHash + fn seal_fields(&self, _header: &Header) -> usize { 2 } + + fn extra_info(&self, header: &Header) -> BTreeMap { + // clique engine seal fields are the same as ethash seal fields + match EthashSeal::parse_seal(header.seal()) { + Ok(seal) => map![ + "nonce".to_owned() => format!("{:#x}", seal.nonce), + "mixHash".to_owned() => format!("{:#x}", seal.mix_hash) + ], + _ => BTreeMap::default() + } + } + + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 } + + fn on_new_block( + &self, + _block: &mut ExecutedBlock, + _epoch_begin: bool, + ) -> Result<(), Error> { + Ok(()) + } + + // Clique has no block reward. + fn on_close_block( + &self, + _block: &mut ExecutedBlock, + _parent_header: &Header + ) -> Result<(), Error> { + Ok(()) + } + + fn on_seal_block(&self, block: &mut ExecutedBlock) -> Result<(), Error> { + trace!(target: "engine", "on_seal_block"); + + let header = &mut block.header; + + let state = self.state_no_backfill(header.parent_hash()) + .ok_or_else(|| BlockError::UnknownParent(*header.parent_hash()))?; + + let is_checkpoint = header.number() % self.epoch_length == 0; + + header.set_author(NULL_AUTHOR); + + // Cast a random Vote if not checkpoint + if !is_checkpoint { + // TODO(niklasad1): this will always be false because `proposals` is never written to + let votes = self.proposals.read().iter() + .filter(|(address, vote_type)| state.is_valid_vote(*address, **vote_type)) + .map(|(address, vote_type)| (*address, *vote_type)) + .collect::>(); + + if !votes.is_empty() { + // Pick a random vote. + let random_vote = rand::thread_rng().gen_range(0 as usize, votes.len()); + let (beneficiary, vote_type) = votes[random_vote]; + + trace!(target: "engine", "Casting vote: beneficiary {}, type {:?} ", beneficiary, vote_type); + + header.set_author(beneficiary); + header.set_seal(vote_type.as_rlp()); + } + } + + // Work on clique seal. + + let mut seal: Vec = Vec::with_capacity(VANITY_LENGTH + SIGNATURE_LENGTH); + + // At this point, extra_data should only contain miner vanity. + if header.extra_data().len() != VANITY_LENGTH { + Err(BlockError::ExtraDataOutOfBounds(OutOfBounds { + min: Some(VANITY_LENGTH), + max: Some(VANITY_LENGTH), + found: header.extra_data().len() + }))?; + } + // vanity + { + seal.extend_from_slice(&header.extra_data()[0..VANITY_LENGTH]); + } + + // If we are building an checkpoint block, add all signers now. + if is_checkpoint { + seal.reserve(state.signers().len() * 20); + for signer in state.signers() { + seal.extend_from_slice(&signer[..]); + } + } + + header.set_extra_data(seal.clone()); + + // append signature onto extra_data + let (sig, _msg) = self.sign_header(&header)?; + seal.extend_from_slice(&sig[..]); + header.set_extra_data(seal.clone()); + + header.compute_hash(); + + // locally sealed block don't go through valid_block_family(), so we have to record state here. + let mut new_state = state.clone(); + new_state.apply(&header, is_checkpoint)?; + new_state.calc_next_timestamp(header.timestamp(), self.period)?; + self.block_state_by_hash.write().insert(header.hash(), new_state); + + trace!(target: "engine", "on_seal_block: finished, final header: {:?}", header); + + Ok(()) + } + + /// Clique doesn't require external work to seal, so we always return true here. + fn sealing_state(&self) -> SealingState { + SealingState::Ready + } + + /// Returns if we are ready to seal, the real sealing (signing extra_data) is actually done in `on_seal_block()`. + fn generate_seal(&self, block: &ExecutedBlock, parent: &Header) -> Seal { + trace!(target: "engine", "tried to generate_seal"); + let null_seal = util::null_seal(); + + if block.header.number() == 0 { + trace!(target: "engine", "attempted to seal genesis block"); + return Seal::None; + } + + // if sealing period is 0, and not an checkpoint block, refuse to seal + if self.period == 0 { + if block.transactions.is_empty() && block.header.number() % self.epoch_length != 0 { + return Seal::None; + } + return Seal::Regular(null_seal); + } + + // Check we actually have authority to seal. + if let Some(author) = self.signer.read().as_ref().map(|x| x.address()) { + + // ensure the voting state exists + match self.state(&parent) { + Err(e) => { + warn!(target: "engine", "generate_seal: can't get parent state(number: {}, hash: {}): {} ", + parent.number(), parent.hash(), e); + return Seal::None; + } + Ok(state) => { + // Are we authorized to seal? + if !state.is_authorized(&author) { + trace!(target: "engine", "generate_seal: Not authorized to sign right now."); + // wait for one third of period to try again. + thread::sleep(Duration::from_secs(self.period / 3 + 1)); + return Seal::None; + } + + let inturn = state.is_inturn(block.header.number(), &author); + + let now = SystemTime::now(); + + let limit = match inturn { + true => state.next_timestamp_inturn.unwrap_or(now), + false => state.next_timestamp_noturn.unwrap_or(now), + }; + + // Wait for the right moment. + if now < limit { + trace!(target: "engine", "generate_seal: sleeping to sign: inturn: {}, now: {:?}, to: {:?}.", + inturn, now, limit); + match limit.duration_since(SystemTime::now()) { + Ok(duration) => { + thread::sleep(duration); + }, + Err(e) => { + warn!(target:"engine", "generate_seal: unable to sleep, err: {}", e); + return Seal::None; + } + } + } + + trace!(target: "engine", "generate_seal: seal ready for block {}, txs: {}.", + block.header.number(), block.transactions.len()); + return Seal::Regular(null_seal); + } + } + } + Seal::None + } + + fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { Ok(()) } + + fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { + // Largely same as https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L275 + + // Ignore genesis block. + if header.number() == 0 { + return Ok(()); + } + + // Don't waste time checking blocks from the future + { + let limit = CheckedSystemTime::checked_add(SystemTime::now(), Duration::from_secs(self.period)) + .ok_or(BlockError::TimestampOverflow)?; + + // This should succeed under the constraints that the system clock works + let limit_as_dur = limit.duration_since(UNIX_EPOCH).map_err(|e| { + Box::new(format!("Converting SystemTime to Duration failed: {}", e)) + })?; + + let hdr = Duration::from_secs(header.timestamp()); + if hdr > limit_as_dur { + let found = CheckedSystemTime::checked_add(UNIX_EPOCH, hdr).ok_or(BlockError::TimestampOverflow)?; + + Err(BlockError::TemporarilyInvalid(OutOfBounds { + min: None, + max: Some(limit), + found, + }.into()))? + } + } + + let is_checkpoint = header.number() % self.epoch_length == 0; + + if is_checkpoint && *header.author() != NULL_AUTHOR { + return Err(EngineError::CliqueWrongAuthorCheckpoint(Mismatch { + expected: H160::zero(), + found: *header.author(), + }))?; + } + + let seal_fields = header.decode_seal::>()?; + if seal_fields.len() != 2 { + Err(BlockError::InvalidSealArity(Mismatch { + expected: 2, + found: seal_fields.len(), + }))? + } + + let mixhash = H256::from_slice(seal_fields[0]); + let nonce = H64::from_slice(seal_fields[1]); + + // Nonce must be 0x00..0 or 0xff..f + if nonce != NONCE_DROP_VOTE && nonce != NONCE_AUTH_VOTE { + Err(EngineError::CliqueInvalidNonce(nonce))?; + } + + if is_checkpoint && nonce != NULL_NONCE { + Err(EngineError::CliqueInvalidNonce(nonce))?; + } + + // Ensure that the mix digest is zero as Clique don't have fork protection currently + if mixhash != NULL_MIXHASH { + Err(BlockError::MismatchedH256SealElement(Mismatch { + expected: NULL_MIXHASH, + found: mixhash, + }))? + } + + let extra_data_len = header.extra_data().len(); + + if extra_data_len < VANITY_LENGTH { + Err(EngineError::CliqueMissingVanity)? + } + + if extra_data_len < VANITY_LENGTH + SIGNATURE_LENGTH { + Err(EngineError::CliqueMissingSignature)? + } + + let signers = extra_data_len - (VANITY_LENGTH + SIGNATURE_LENGTH); + + // Checkpoint blocks must at least contain one signer + if is_checkpoint && signers == 0 { + Err(EngineError::CliqueCheckpointNoSigner)? + } + + // Addresses must be be divisable by 20 + if is_checkpoint && signers % ADDRESS_LENGTH != 0 { + Err(EngineError::CliqueCheckpointInvalidSigners(signers))? + } + + // Ensure that the block doesn't contain any uncles which are meaningless in PoA + if *header.uncles_hash() != NULL_UNCLES_HASH { + Err(BlockError::InvalidUnclesHash(Mismatch { + expected: NULL_UNCLES_HASH, + found: *header.uncles_hash(), + }))? + } + + // Ensure that the block's difficulty is meaningful (may not be correct at this point) + if *header.difficulty() != DIFF_INTURN && *header.difficulty() != DIFF_NOTURN { + Err(BlockError::DifficultyOutOfBounds(OutOfBounds { + min: Some(DIFF_NOTURN), + max: Some(DIFF_INTURN), + found: *header.difficulty(), + }))? + } + + // All basic checks passed, continue to next phase + Ok(()) + } + + fn verify_block_unordered(&self, _header: &Header) -> Result<(), Error> { + // Nothing to check here. + Ok(()) + } + + /// Verify block family by looking up parent state (backfill if needed), then try to apply current header. + /// see https://github.com/ethereum/go-ethereum/blob/master/consensus/clique/clique.go#L338 + fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { + // Ignore genesis block. + if header.number() == 0 { + return Ok(()); + } + + // parent sanity check + if parent.hash() != *header.parent_hash() || header.number() != parent.number() + 1 { + Err(BlockError::UnknownParent(parent.hash()))? + } + + // Ensure that the block's timestamp isn't too close to it's parent + let limit = parent.timestamp().saturating_add(self.period); + if limit > header.timestamp() { + let max = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp())); + let found = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(limit)) + .ok_or(BlockError::TimestampOverflow)?; + + Err(BlockError::InvalidTimestamp(OutOfBounds { + min: None, + max, + found, + }.into()))? + } + + // Retrieve the parent state + let parent_state = self.state(&parent)?; + // Try to apply current state, apply() will further check signer and recent signer. + let mut new_state = parent_state.clone(); + new_state.apply(header, header.number() % self.epoch_length == 0)?; + new_state.calc_next_timestamp(header.timestamp(), self.period)?; + self.block_state_by_hash.write().insert(header.hash(), new_state); + + Ok(()) + } + + fn genesis_epoch_data(&self, header: &Header, _call: &Call) -> Result, String> { + let mut state = self.new_checkpoint_state(header).expect("Unable to parse genesis data."); + state.calc_next_timestamp(header.timestamp(), self.period).map_err(|e| format!("{}", e))?; + self.block_state_by_hash.write().insert(header.hash(), state); + + // no proof. + Ok(Vec::new()) + } + + // Our task here is to set difficulty + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { + // TODO(https://github.com/paritytech/parity-ethereum/issues/10410): this is a horrible hack, + // it is due to the fact that enact and miner both use OpenBlock::new() which will both call + // this function. more refactoring is definitely needed. + if header.extra_data().len() < VANITY_LENGTH + SIGNATURE_LENGTH { + trace!(target: "engine", "populate_from_parent in sealing"); + + // It's unclear how to prevent creating new blocks unless we are authorized, the best way (and geth does this too) + // it's just to ignore setting a correct difficulty here, we will check authorization in next step in generate_seal anyway. + if let Some(signer) = self.signer.read().as_ref() { + let state = match self.state(&parent) { + Err(e) => { + trace!(target: "engine", "populate_from_parent: Unable to find parent state: {}, ignored.", e); + return; + } + Ok(state) => state, + }; + + if state.is_authorized(&signer.address()) { + if state.is_inturn(header.number(), &signer.address()) { + header.set_difficulty(DIFF_INTURN); + } else { + header.set_difficulty(DIFF_NOTURN); + } + } + + let zero_padding_len = VANITY_LENGTH.saturating_sub(header.extra_data().len()); + if zero_padding_len > 0 { + let mut resized_extra_data = header.extra_data().clone(); + resized_extra_data.resize(VANITY_LENGTH, 0); + header.set_extra_data(resized_extra_data); + } + } else { + trace!(target: "engine", "populate_from_parent: no signer registered"); + } + } + } + + fn set_signer(&self, signer: Option>) { + let mut current_signer = self.signer.write(); + if let Some(signer) = signer.as_ref() { + trace!(target: "engine", "set_signer: {:?}", signer.address()); + } else if let Some(signer) = &*current_signer { + trace!(target: "engine", "set_signer: cleared; previous signer: {:?})", signer.address()); + } + *current_signer = signer; + } + + fn register_client(&self, client: Weak) { + *self.client.write() = Some(client.clone()); + } + + fn step(&self) { + if self.signer.read().is_some() { + if let Some(ref weak) = *self.client.read() { + if let Some(c) = weak.upgrade() { + c.update_sealing(ForceUpdateSealing::No); + } + } + } + } + + /// Clique timestamp is set to parent + period , or current time which ever is higher. + fn open_block_header_timestamp(&self, parent_timestamp: u64) -> u64 { + let now = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap_or_default(); + cmp::max(now.as_secs() as u64, parent_timestamp.saturating_add(self.period)) + } + + fn is_timestamp_valid(&self, header_timestamp: u64, parent_timestamp: u64) -> bool { + header_timestamp >= parent_timestamp.saturating_add(self.period) + } + + // Clique uses the author field for voting, the real author is hidden in the `extra_data` field. + // So when executing tx's (like in `enact()`) we want to use the executive author + fn executive_author(&self, header: &Header) -> Result { + recover_creator(header) + } + + fn params(&self) -> &CommonParams { + self.machine.params() + } +} diff --git a/ethcore/src/engines/clique/params.rs b/ethcore/engines/clique/src/params.rs similarity index 95% rename from ethcore/src/engines/clique/params.rs rename to ethcore/engines/clique/src/params.rs index e24edfcbac..b1fd563310 100644 --- a/ethcore/src/engines/clique/params.rs +++ b/ethcore/engines/clique/src/params.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/engines/clique/src/tests.rs b/ethcore/engines/clique/src/tests.rs new file mode 100644 index 0000000000..c293ff775a --- /dev/null +++ b/ethcore/engines/clique/src/tests.rs @@ -0,0 +1,806 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Consensus tests for `PoA Clique Engine`, see http://eips.ethereum.org/EIPS/eip-225 for more information + +use std::sync::Arc; +use std::collections::HashMap; + +use common_types::errors::{EthcoreError as Error, EngineError}; +use ethcore::{ + block::*, + test_helpers::get_temp_state_db, +}; +use engine::Engine; +use ethereum_types::{Address, H256}; +use parity_crypto::publickey::{Secret, KeyPair}; +use state_db::StateDB; + +use super::*; + +/// Possible signers +pub const SIGNER_TAGS: [char; 6] = ['A', 'B', 'C', 'D', 'E', 'F']; + +/// Clique block types +pub enum CliqueBlockType { + /// Epoch transition block must contain list of signers + Checkpoint, + /// Block with no votes + Empty, + /// Vote + Vote(VoteType), +} + +/// Clique tester +pub struct CliqueTester { + /// Mocked Clique + pub clique: Clique, + /// Mocked genesis state + pub genesis: Header, + /// StateDB + pub db: StateDB, + /// List of signers + pub signers: HashMap, +} + +impl CliqueTester { + /// Create a `Clique` tester with settings + pub fn with(epoch: u64, period: u64, initial_signers: Vec) -> Self { + assert_eq!(initial_signers.iter().all(|s| SIGNER_TAGS.contains(s)), true, + "Not all the initial signers is in SIGNER_TAGS, possible keys are 'A' ..= 'F'"); + + let clique = Clique::with_test(epoch, period); + let mut genesis = Header::default(); + let mut signers = HashMap::new(); + + let call = |_a, _b| { + unimplemented!("Clique doesn't use Engine::Call"); + }; + + let mut extra_data = vec![0; VANITY_LENGTH]; + + for &signer in SIGNER_TAGS.iter() { + let secret = Secret::from(H256::from_low_u64_be(signer as u64)); + let keypair = KeyPair::from_secret(secret).unwrap(); + if initial_signers.contains(&signer) { + extra_data.extend(keypair.address().as_bytes()); + } + signers.insert(signer, keypair); + } + + // append dummy signature + extra_data.extend(std::iter::repeat(0).take(SIGNATURE_LENGTH)); + + genesis.set_extra_data(extra_data); + genesis.set_gas_limit(U256::from(0xa00000)); + genesis.set_difficulty(U256::from(1)); + genesis.set_seal(util::null_seal()); + + clique.genesis_epoch_data(&genesis, &call).expect("Create genesis failed"); + Self {clique, genesis, db: get_temp_state_db(), signers} + } + + /// Get difficulty for a given block + pub fn get_difficulty(&self, block_num: BlockNumber, header: &Header, signer: &Address) -> U256 { + let state = self.clique.state(header).unwrap(); + if state.is_inturn(block_num, signer) { + DIFF_INTURN + } else { + DIFF_NOTURN + } + } + + /// Get the state of a given block + // Note, this will read the cache and `will` not work with more than 128 blocks + pub fn get_state_at_block(&self, hash: &H256) -> CliqueBlockState { + self.clique.block_state_by_hash.write() + .get_mut(hash) + .expect("CliqueBlockState not found tested failed") + .clone() + } + + /// Get signers after a certain state + // This is generally used to fetch the state after a test has been executed and checked against + // the initial list of signers provided in the test + pub fn clique_signers(&self, hash: &H256) -> impl Iterator { + self.get_state_at_block(hash).signers().clone().into_iter() + } + + /// Fetches all addresses at current `block` and converts them back to `tags (char)` and sorts them + /// Addresses are supposed sorted based on address but these tests are using `tags` just for simplicity + /// and the order is not important! + pub fn into_tags>(&self, addr: T) -> Vec { + let mut tags: Vec = addr.filter_map(|addr| { + for (t, kp) in self.signers.iter() { + if addr == kp.address() { + return Some(*t) + } + } + None + }) + .collect(); + + tags.sort(); + tags + } + + /// Create a new `Clique` block and import + pub fn new_block_and_import( + &self, + block_type: CliqueBlockType, + last_header: &Header, + beneficary: Option
, + signer: char, + ) -> Result { + + let mut extra_data = vec![0; VANITY_LENGTH]; + let mut seal = util::null_seal(); + let last_hash = last_header.hash(); + + match block_type { + CliqueBlockType::Checkpoint => { + let signers = self.clique.state(&last_header).unwrap().signers().clone(); + for signer in signers { + extra_data.extend(signer.as_bytes()); + } + } + CliqueBlockType::Vote(v) => seal = v.as_rlp(), + CliqueBlockType::Empty => (), + }; + + let db = self.db.boxed_clone(); + + let mut block = OpenBlock::new( + &self.clique, + Default::default(), + false, + db, + &last_header.clone(), + Arc::new(vec![last_hash]), + beneficary.unwrap_or_default(), + (3141562.into(), 31415620.into()), + extra_data, + false, + ).unwrap(); + + { + let difficulty = self.get_difficulty(block.header.number(), last_header, &self.signers[&signer].address()); + let b = block.block_mut(); + b.header.set_timestamp(last_header.timestamp() + self.clique.period); + b.header.set_difficulty(difficulty); + b.header.set_seal(seal); + + let sign = parity_crypto::publickey::sign(self.signers[&signer].secret(), &b.header.hash()).unwrap(); + let mut extra_data = b.header.extra_data().clone(); + extra_data.extend_from_slice(&*sign); + b.header.set_extra_data(extra_data); + } + + let current_header = &block.header; + self.clique.verify_block_basic(current_header)?; + self.clique.verify_block_family(current_header, &last_header)?; + + Ok(current_header.clone()) + } +} + +#[test] +fn one_signer_with_no_votes() { + let tester = CliqueTester::with(10, 1, vec!['A']); + + let empty_block = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&empty_block.hash())); + assert_eq!(&tags, &['A']); +} + +#[test] +fn one_signer_two_votes() { + let tester = CliqueTester::with(10, 1, vec!['A']); + + // Add a vote for `B` signed by `A` + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, + Some(tester.signers[&'B'].address()), 'A').unwrap(); + let tags = tester.into_tags(tester.clique_signers(&vote.hash())); + assert_eq!(&tags, &['A', 'B']); + + // Add a empty block signed by `B` + let empty = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap(); + + // Add vote for `C` signed by A but should not be accepted + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &empty, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&vote.hash())); + assert_eq!(&tags, &['A', 'B']); +} + +#[test] +fn two_signers_six_votes_deny_last() { + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + + let mut prev_header = tester.genesis.clone(); + + // Add two votes for `C` signed by `A` and `B` + for &signer in SIGNER_TAGS.iter().take(2) { + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, + Some(tester.signers[&'C'].address()), signer).unwrap(); + prev_header = vote.clone(); + } + + // Add two votes for `D` signed by `A` and `B` + for &signer in SIGNER_TAGS.iter().take(2) { + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, + Some(tester.signers[&'D'].address()), signer).unwrap(); + prev_header = vote.clone(); + } + + // Add a empty block signed by `C` + let empty = tester.new_block_and_import(CliqueBlockType::Empty, &prev_header, None, 'C').unwrap(); + prev_header = empty.clone(); + + // Add two votes for `E` signed by `A` and `B` + for &signer in SIGNER_TAGS.iter().take(2) { + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, + Some(tester.signers[&'E'].address()), signer).unwrap(); + prev_header = vote.clone(); + } + + let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); + assert_eq!(&tags, &['A', 'B', 'C', 'D']); +} + +#[test] +fn one_signer_dropping_itself() { + let tester = CliqueTester::with(10, 1, vec!['A']); + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, + Some(tester.signers[&'A'].address()), 'A').unwrap(); + let signers = tester.clique_signers(&vote.hash()); + assert!(signers.count() == 0); +} + +#[test] +fn two_signers_one_remove_vote_no_consensus() { + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, + Some(tester.signers[&'B'].address()), 'A').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&vote.hash())); + assert_eq!(&tags, &['A', 'B']); +} + +#[test] +fn two_signers_consensus_remove_b() { + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + let first_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, + Some(tester.signers[&'B'].address()), 'A').unwrap(); + let second_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &first_vote, + Some(tester.signers[&'B'].address()), 'B').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&second_vote.hash())); + assert_eq!(&tags, &['A']); +} + +#[test] +fn three_signers_consensus_remove_c() { + let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C']); + let first_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + let second_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &first_vote, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&second_vote.hash())); + assert_eq!(&tags, &['A', 'B']); +} + +#[test] +fn four_signers_half_no_consensus() { + let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C', 'D']); + let first_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + + let second_vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &first_vote, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&second_vote.hash())); + assert_eq!(&tags, &['A', 'B', 'C', 'D']); +} + +#[test] +fn four_signers_three_consensus_rm() { + let tester = CliqueTester::with(10, 1, vec!['A', 'B', 'C', 'D']); + + let mut prev_header = tester.genesis.clone(); + + // Three votes to remove `D` signed by ['A', 'B', 'C'] + for signer in SIGNER_TAGS.iter().take(3) { + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header, + Some(tester.signers[&'D'].address()), *signer).unwrap(); + prev_header = vote.clone(); + } + + let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); + assert_eq!(&tags, &['A', 'B', 'C']); +} + +#[test] +fn vote_add_only_counted_once_per_signer() { + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + + // Add a vote for `C` signed by `A` + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + // Empty block signed by B` + let empty = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap(); + + // Add a vote for `C` signed by `A` + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &empty, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + // Empty block signed by `B` + let empty = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap(); + + // Add a vote for `C` signed by `A` + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &empty, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&vote.hash())); + assert_eq!(&tags, &['A', 'B']); +} + +#[test] +fn vote_add_concurrently_is_permitted() { + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + + // Add a vote for `C` signed by `A` + let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + + // Empty block signed by `B` + let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'B').unwrap(); + + // Add a vote for `D` signed by `A` + let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &b, + Some(tester.signers[&'D'].address()), 'A').unwrap(); + + // Empty block signed by `B` + let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'B').unwrap(); + + // Empty block signed by `A` + let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'A').unwrap(); + + // Add a vote for `D` signed by `B` + let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &b, + Some(tester.signers[&'D'].address()), 'B').unwrap(); + + // Empty block signed by `A` + let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'A').unwrap(); + + // Add a vote for `C` signed by `B` + let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &b, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&b.hash())); + assert_eq!(&tags, &['A', 'B', 'C', 'D']); +} + +#[test] +fn vote_rm_only_counted_once_per_signer() { + let tester = CliqueTester::with(10, 1, vec!['A', 'B']); + + let mut prev_header = tester.genesis.clone(); + + for _ in 0..2 { + // Vote to remove `B` signed by `A` + let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header, + Some(tester.signers[&'B'].address()), 'A').unwrap(); + // Empty block signed by `B` + let b = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'B').unwrap(); + + prev_header = b.clone(); + } + + // Add a vote for `B` signed by `A` + let b = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header, + Some(tester.signers[&'B'].address()), 'A').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&b.hash())); + assert_eq!(&tags, &['A', 'B']); +} + +#[test] +fn vote_rm_concurrently_is_permitted() { + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); + + // Add a vote for `C` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + + // Empty block signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); + // Empty block signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); + + // Add a vote for `D` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'A').unwrap(); + + // Empty block signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); + // Empty block signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); + // Empty block signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); + + // Add a vote for `D` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'B').unwrap(); + // Add a vote for `D` signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'C').unwrap(); + + // Empty block signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); + // Add a vote for `C` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B']); +} + +#[test] +fn vote_to_rm_are_immediate_and_ensure_votes_are_rm() { + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C']); + + // Vote to remove `B` signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, + Some(tester.signers[&'B'].address()), 'C').unwrap(); + // Vote to remove `C` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + // Vote to remove `C` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + // Vote to remove `B` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'B'].address()), 'A').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B']); +} + +#[test] +fn vote_to_rm_are_immediate_and_votes_should_be_dropped_from_kicked_signer() { + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C']); + + // Vote to add `D` signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, + Some(tester.signers[&'D'].address()), 'C').unwrap(); + // Vote to remove `C` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + + // Vote to remove `C` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + + // Vote to add `D` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block, + Some(tester.signers[&'D'].address()), 'A').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B']); +} + +#[test] +fn cascading_not_allowed() { + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); + + // Vote against `C` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + + // Empty block signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); + + // Empty block signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); + + // Vote against `D` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'A').unwrap(); + + // Vote against `C` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + + // Empty block signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); + + // Empty block signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); + + // Vote against `D` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'B').unwrap(); + + // Vote against `D` signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'C').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B', 'C']); +} + +#[test] +fn consensus_out_of_bounds_consensus_execute_on_touch() { + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); + + // Vote against `C` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + + // Empty block signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); + + // Empty block signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); + + // Vote against `D` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'A').unwrap(); + + // Vote against `C` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + + // Empty block signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); + + // Empty block signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); + + // Vote against `D` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'B').unwrap(); + + // Vote against `D` signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'C').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B', 'C'], "D should have been removed after 3/4 remove votes"); + + // Empty block signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); + + // Vote for `C` signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block, + Some(tester.signers[&'C'].address()), 'C').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B']); +} + +#[test] +fn consensus_out_of_bounds_first_touch() { + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); + + // Vote against `C` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &tester.genesis, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + + // Empty block signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); + + // Empty block signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); + + // Vote against `D` signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'A').unwrap(); + + // Vote against `C` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + + // Empty block signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'C').unwrap(); + + // Empty block signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); + + // Vote against `D` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'B').unwrap(); + + // Vote against `D` signed by `C` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &block, + Some(tester.signers[&'D'].address()), 'C').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B', 'C']); + + // Empty block signed by `A` + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap(); + + // Vote for `C` signed by `B` + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B', 'C']); +} + +#[test] +fn pending_votes_doesnt_survive_authorization_changes() { + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D', 'E']); + + let mut prev_header = tester.genesis.clone(); + + // Vote for `F` from [`A`, `B`, `C`] + for sign in SIGNER_TAGS.iter().take(3) { + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, + Some(tester.signers[&'F'].address()), *sign).unwrap(); + prev_header = block.clone(); + } + + let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); + assert_eq!(&tags, &['A', 'B', 'C', 'D', 'E', 'F'], "F should have been added"); + + // Vote against `F` from [`D`, `E`, `B`, `C`] + for sign in SIGNER_TAGS.iter().skip(3).chain(SIGNER_TAGS.iter().skip(1).take(2)) { + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header, + Some(tester.signers[&'F'].address()), *sign).unwrap(); + prev_header = block.clone(); + } + + let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); + assert_eq!(&tags, &['A', 'B', 'C', 'D', 'E'], "F should have been removed"); + + // Vote for `F` from [`D`, `E`] + for sign in SIGNER_TAGS.iter().skip(3).take(2) { + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, + Some(tester.signers[&'F'].address()), *sign).unwrap(); + prev_header = block.clone(); + } + + // Vote against `A` from [`B`, `C`, `D`] + for sign in SIGNER_TAGS.iter().skip(1).take(3) { + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Remove), &prev_header, + Some(tester.signers[&'A'].address()), *sign).unwrap(); + prev_header = block.clone(); + } + + let tags = tester.into_tags(tester.clique_signers(&prev_header.hash())); + assert_eq!(&tags, &['B', 'C', 'D', 'E'], "A should have been removed"); + + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &prev_header, + Some(tester.signers[&'F'].address()), 'B').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['B', 'C', 'D', 'E', 'F'], "F should have been added again"); +} + +#[test] +fn epoch_transition_reset_all_votes() { + let tester = CliqueTester::with(3, 1, vec!['A', 'B']); + + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, + Some(tester.signers[&'C'].address()), 'A').unwrap(); + + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); + let block = tester.new_block_and_import(CliqueBlockType::Checkpoint, &block, None, 'A').unwrap(); + + let block = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &block, + Some(tester.signers[&'C'].address()), 'B').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&block.hash())); + assert_eq!(&tags, &['A', 'B'], "Votes should have been reset after checkpoint"); +} + +#[test] +fn unauthorized_signer_should_not_be_able_to_sign_block() { + let tester = CliqueTester::with(3, 1, vec!['A']); + let err = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'B').unwrap_err(); + + match err { + Error::Engine(EngineError::NotAuthorized(_)) => (), + _ => assert!(true == false, "Wrong error kind"), + } +} + +#[test] +fn signer_should_not_be_able_to_sign_two_consequtive_blocks() { + let tester = CliqueTester::with(3, 1, vec!['A', 'B']); + let b = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A').unwrap(); + let err = tester.new_block_and_import(CliqueBlockType::Empty, &b, None, 'A').unwrap_err(); + + match err { + Error::Engine(EngineError::CliqueTooRecentlySigned(_)) => (), + _ => assert!(true == false, "Wrong error kind"), + } +} + + +#[test] +fn recent_signers_should_not_reset_on_checkpoint() { + let tester = CliqueTester::with(3, 1, vec!['A', 'B', 'C']); + + let block = tester.new_block_and_import(CliqueBlockType::Empty, &tester.genesis, None, 'A').unwrap(); + let block = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'B').unwrap(); + let block = tester.new_block_and_import(CliqueBlockType::Checkpoint, &block, None, 'A').unwrap(); + + let err = tester.new_block_and_import(CliqueBlockType::Empty, &block, None, 'A').unwrap_err(); + + match err { + Error::Engine(EngineError::CliqueTooRecentlySigned(_)) => (), + _ => assert!(true == false, "Wrong error kind"), + } +} + +// Not part of http://eips.ethereum.org/EIPS/eip-225 +#[test] +fn bonus_consensus_should_keep_track_of_votes_before_latest_per_signer() { + let tester = CliqueTester::with(100, 1, vec!['A', 'B', 'C', 'D']); + + // Add a vote for `E` signed by `A` + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &tester.genesis, + Some(tester.signers[&'E'].address()), 'A').unwrap(); + // Empty block signed by `B` + let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'B').unwrap(); + + // Empty block signed by `C` + let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C').unwrap(); + + // Empty block signed by `D` + let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D').unwrap(); + + // Add a vote for `F` signed by `A` + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote, + Some(tester.signers[&'F'].address()), 'A').unwrap(); + // Empty block signed by `C` + let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C').unwrap(); + + // Empty block signed by `D` + let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D').unwrap(); + + // Add a vote for `E` signed by `B` + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote, + Some(tester.signers[&'E'].address()), 'B').unwrap(); + // Empty block signed by `A` + let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'A').unwrap(); + + // Empty block signed by `C` + let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'C').unwrap(); + + // Empty block signed by `D` + let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'D').unwrap(); + + // Add a vote for `F` signed by `B` + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote, + Some(tester.signers[&'F'].address()), 'B').unwrap(); + + // Empty block signed by A` + let vote = tester.new_block_and_import(CliqueBlockType::Empty, &vote, None, 'A').unwrap(); + + // Add a vote for `E` signed by `C` + let vote = tester.new_block_and_import(CliqueBlockType::Vote(VoteType::Add), &vote, + Some(tester.signers[&'E'].address()), 'C').unwrap(); + + let tags = tester.into_tags(tester.clique_signers(&vote.hash())); + assert_eq!(&tags, &['A', 'B', 'C', 'D', 'E']); +} diff --git a/ethcore/engines/clique/src/util.rs b/ethcore/engines/clique/src/util.rs new file mode 100644 index 0000000000..3c28f86b82 --- /dev/null +++ b/ethcore/engines/clique/src/util.rs @@ -0,0 +1,117 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +use std::collections::BTreeSet; + +use common_types::{ + header::Header, + errors::{EthcoreError as Error, EngineError}, +}; +use ethereum_types::{Address, H256}; +use parity_crypto::publickey::{public_to_address, recover as ec_recover, Signature}; +use lazy_static::lazy_static; +use lru_cache::LruCache; +use parking_lot::RwLock; +use rlp::encode; +use crate::{ADDRESS_LENGTH, SIGNATURE_LENGTH, VANITY_LENGTH, NULL_NONCE, NULL_MIXHASH}; + +/// How many recovered signature to cache in the memory. +pub const CREATOR_CACHE_NUM: usize = 4096; +lazy_static! { + /// key: header hash + /// value: creator address + static ref CREATOR_BY_HASH: RwLock> = RwLock::new(LruCache::new(CREATOR_CACHE_NUM)); +} + +/// Recover block creator from signature +pub fn recover_creator(header: &Header) -> Result { + // Initialization + let mut cache = CREATOR_BY_HASH.write(); + + if let Some(creator) = cache.get_mut(&header.hash()) { + return Ok(*creator); + } + + let data = header.extra_data(); + if data.len() < VANITY_LENGTH { + Err(EngineError::CliqueMissingVanity)? + } + + if data.len() < VANITY_LENGTH + SIGNATURE_LENGTH { + Err(EngineError::CliqueMissingSignature)? + } + + // Split `signed_extra data` and `signature` + let (signed_data_slice, signature_slice) = data.split_at(data.len() - SIGNATURE_LENGTH); + + // convert `&[u8]` to `[u8; 65]` + let signature = { + let mut s = [0; SIGNATURE_LENGTH]; + s.copy_from_slice(signature_slice); + s + }; + + // modify header and hash it + let unsigned_header = &mut header.clone(); + unsigned_header.set_extra_data(signed_data_slice.to_vec()); + let msg = unsigned_header.hash(); + + let pubkey = ec_recover(&Signature::from(signature), &msg)?; + let creator = public_to_address(&pubkey); + + cache.insert(header.hash(), creator.clone()); + Ok(creator) +} + +/// Extract signer list from extra_data. +/// +/// Layout of extra_data: +/// ---- +/// VANITY: 32 bytes +/// Signers: N * 32 bytes as hex encoded (20 characters) +/// Signature: 65 bytes +/// -- +pub fn extract_signers(header: &Header) -> Result, Error> { + let data = header.extra_data(); + + if data.len() <= VANITY_LENGTH + SIGNATURE_LENGTH { + Err(EngineError::CliqueCheckpointNoSigner)? + } + + // extract only the portion of extra_data which includes the signer list + let signers_raw = &data[(VANITY_LENGTH)..data.len() - (SIGNATURE_LENGTH)]; + + if signers_raw.len() % ADDRESS_LENGTH != 0 { + Err(EngineError::CliqueCheckpointInvalidSigners(signers_raw.len()))? + } + + let num_signers = signers_raw.len() / 20; + + let signers: BTreeSet
= (0..num_signers) + .map(|i| { + let start = i * ADDRESS_LENGTH; + let end = start + ADDRESS_LENGTH; + Address::from_slice(&signers_raw[start..end]) + }) + .collect(); + + Ok(signers) +} + +/// Retrieve `null_seal` +pub fn null_seal() -> Vec> { + vec![encode(&NULL_MIXHASH.as_bytes().to_vec()), encode(&NULL_NONCE.as_bytes().to_vec())] +} diff --git a/ethcore/engines/ethash/Cargo.toml b/ethcore/engines/ethash/Cargo.toml new file mode 100644 index 0000000000..9ea56f08af --- /dev/null +++ b/ethcore/engines/ethash/Cargo.toml @@ -0,0 +1,27 @@ +[package] +description = "Ethash PoW blockchain engine" +name = "ethash-engine" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +block-reward = { path = "../../block-reward" } +common-types = { path = "../../types" } +engine = { path = "../../engine" } +ethash= { path = "../../../ethash" } +ethereum-types = "0.8.0" +ethjson = { path = "../../../json" } +keccak-hash = "0.4.0" +log = "0.4.8" +machine = { path = "../../machine" } +macros = { path = "../../../util/macros" } +unexpected = { path = "../../../util/unexpected" } + +[dev-dependencies] +ethcore = { path = "../..", features = ["test-helpers"] } +keccak-hash = "0.4.0" +rlp = "0.4.2" +spec = { path = "../../spec" } +tempdir = "0.3" diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/engines/ethash/src/lib.rs similarity index 85% rename from ethcore/src/ethereum/ethash.rs rename to ethcore/engines/ethash/src/lib.rs index 698e23cf7e..d445e5b1eb 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/engines/ethash/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,59 +19,37 @@ use std::collections::BTreeMap; use std::path::Path; use std::sync::Arc; -use ethereum_types::{H256, H64, U256}; +use block_reward::{self, BlockRewardContract, RewardKind}; +use common_types::{ + BlockNumber, + header::Header, + engines::{ + EthashSeal, + OptimizeFor, + params::CommonParams, + }, + errors::{BlockError, EthcoreError as Error}, + snapshot::Snapshotting, +}; +use engine::Engine; +use ethereum_types::{H256, U256}; use ethjson; -use hash::{KECCAK_EMPTY_LIST_RLP}; -use rlp::Rlp; -use types::header::{Header, ExtendedHeader}; -use types::BlockNumber; +use ethash::{self, quick_get_difficulty, slow_hash_block_number, EthashManager}; +use keccak_hash::{KECCAK_EMPTY_LIST_RLP}; +use log::trace; +use macros::map; +use machine::{ + ExecutedBlock, + Machine, +}; use unexpected::{OutOfBounds, Mismatch}; -use block::ExecutedBlock; -use engines::block_reward::{self, BlockRewardContract, RewardKind}; -use engines::{self, Engine}; -use error::{BlockError, Error}; -use ethash::{self, quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor}; -use machine::EthereumMachine; - /// Number of blocks in an ethash snapshot. -// make dependent on difficulty incrment divisor? +// make dependent on difficulty increment divisor? const SNAPSHOT_BLOCKS: u64 = 5000; /// Maximum number of blocks allowed in an ethash snapshot. const MAX_SNAPSHOT_BLOCKS: u64 = 30000; -/// Ethash specific seal -#[derive(Debug, PartialEq)] -pub struct Seal { - /// Ethash seal mix_hash - pub mix_hash: H256, - /// Ethash seal nonce - pub nonce: H64, -} - -impl Seal { - /// Tries to parse rlp as ethash seal. - pub fn parse_seal>(seal: &[T]) -> Result { - if seal.len() != 2 { - return Err(BlockError::InvalidSealArity( - Mismatch { - expected: 2, - found: seal.len() - } - ).into()); - } - - 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, - }; - - Ok(seal) - } -} - /// Ethash params. #[derive(Debug, PartialEq)] pub struct EthashParams { @@ -173,8 +151,8 @@ impl From for EthashParams { /// mainnet chains in the Olympic, Frontier and Homestead eras. pub struct Ethash { ethash_params: EthashParams, - pow: EthashManager, - machine: EthereumMachine, + pow: Arc, + machine: Machine, } impl Ethash { @@ -182,19 +160,50 @@ impl Ethash { pub fn new>>( cache_dir: &Path, ethash_params: EthashParams, - machine: EthereumMachine, + machine: Machine, optimize_for: T, - ) -> Arc { + ) -> Self { let progpow_transition = ethash_params.progpow_transition; - Arc::new(Ethash { + Ethash { ethash_params, machine, - pow: EthashManager::new(cache_dir.as_ref(), optimize_for.into(), progpow_transition), - }) + pow: Arc::new(EthashManager::new( + cache_dir.as_ref(), + optimize_for.into(), + progpow_transition + )), + } } } +fn verify_block_unordered(pow: &Arc, header: &Header) -> Result<(), Error> { + let seal = EthashSeal::parse_seal(header.seal())?; + + let result = pow.compute_light( + header.number() as u64, + &header.bare_hash().0, + seal.nonce.to_low_u64_be() + ); + let mix = H256(result.mix_hash); + let difficulty = ethash::boundary_to_difficulty(&H256(result.value)); + trace!(target: "miner", "num: {num}, seed: {seed}, h: {h}, non: {non}, mix: {mix}, res: {res}", + num = header.number() as u64, + seed = H256(slow_hash_block_number(header.number() as u64)), + h = header.bare_hash(), + non = seal.nonce.to_low_u64_be(), + mix = H256(result.mix_hash), + res = H256(result.value)); + if mix != seal.mix_hash { + return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: seal.mix_hash }))); + } + if &difficulty < header.difficulty() { + return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); + } + Ok(()) +} + + // TODO [rphmeier] // // for now, this is different than Ethash's own epochs, and signal @@ -203,23 +212,27 @@ impl Ethash { // for any block in the chain. // in the future, we might move the Ethash epoch // caching onto this mechanism as well. -impl engines::EpochVerifier for Arc { - fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) } +struct EpochVerifier { + pow: Arc +} + +impl engine::EpochVerifier for EpochVerifier { fn verify_heavy(&self, header: &Header) -> Result<(), Error> { - self.verify_block_unordered(header) + verify_block_unordered(&self.pow, header) } } -impl Engine for Arc { +impl Engine for Ethash { fn name(&self) -> &str { "Ethash" } - fn machine(&self) -> &EthereumMachine { &self.machine } + + fn machine(&self) -> &Machine { &self.machine } // Two fields - nonce and mix. fn seal_fields(&self, _header: &Header) -> usize { 2 } /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, header: &Header) -> BTreeMap { - match Seal::parse_seal(header.seal()) { + match EthashSeal::parse_seal(header.seal()) { Ok(seal) => map![ "nonce".to_owned() => format!("0x{:x}", seal.nonce), "mixHash".to_owned() => format!("0x{:x}", seal.mix_hash) @@ -232,14 +245,9 @@ impl Engine for Arc { fn maximum_gas_limit(&self) -> Option { Some(0x7fff_ffff_ffff_ffffu64.into()) } - fn populate_from_parent(&self, header: &mut Header, parent: &Header) { - let difficulty = self.calculate_difficulty(header, parent); - header.set_difficulty(difficulty); - } - /// 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> { + fn on_close_block(&self, block: &mut ExecutedBlock, _parent_header: &Header) -> Result<(), Error> { use std::ops::Shr; let author = *block.header.author(); @@ -255,9 +263,9 @@ impl Engine for Arc { beneficiaries.push((*uncle_author, RewardKind::uncle(number, u.number()))); } - let mut call = engines::default_system_or_code_call(&self.machine, block); + let mut call = engine::default_system_or_code_call(&self.machine, block); - let rewards = c.reward(&beneficiaries, &mut call)?; + let rewards = c.reward(beneficiaries, &mut call)?; rewards.into_iter().map(|(author, amount)| (author, RewardKind::External, amount)).collect() }, _ => { @@ -277,7 +285,7 @@ impl Engine for Arc { let n_uncles = block.uncles.len(); // Bestow block rewards. - let mut result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); + let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles); rewards.push((author, RewardKind::Author, result_block_reward)); @@ -314,7 +322,7 @@ impl Engine for Arc { fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { // check the seal fields. - let seal = Seal::parse_seal(header.seal())?; + let seal = EthashSeal::parse_seal(header.seal())?; // TODO: consider removing these lines. let min_difficulty = self.ethash_params.minimum_difficulty; @@ -324,7 +332,7 @@ impl Engine for Arc { let difficulty = ethash::boundary_to_difficulty(&H256(quick_get_difficulty( &header.bare_hash().0, - seal.nonce.low_u64(), + seal.nonce.to_low_u64_be(), &seal.mix_hash.0, header.number() >= self.ethash_params.progpow_transition ))); @@ -337,25 +345,7 @@ impl Engine for Arc { } fn verify_block_unordered(&self, header: &Header) -> Result<(), Error> { - let seal = Seal::parse_seal(header.seal())?; - - let result = self.pow.compute_light(header.number() as u64, &header.bare_hash().0, seal.nonce.low_u64()); - let mix = H256(result.mix_hash); - let difficulty = ethash::boundary_to_difficulty(&H256(result.value)); - trace!(target: "miner", "num: {num}, seed: {seed}, h: {h}, non: {non}, mix: {mix}, res: {res}", - num = header.number() as u64, - seed = H256(slow_hash_block_number(header.number() as u64)), - h = header.bare_hash(), - non = seal.nonce.low_u64(), - mix = H256(result.mix_hash), - res = H256(result.value)); - if mix != seal.mix_hash { - return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: seal.mix_hash }))); - } - if &difficulty < header.difficulty() { - return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); - } - Ok(()) + verify_block_unordered(&self.pow, header) } fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { @@ -373,17 +363,24 @@ impl Engine for Arc { Ok(()) } - fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> engines::ConstructedVerifier<'a, EthereumMachine> { - engines::ConstructedVerifier::Trusted(Box::new(self.clone())) + fn epoch_verifier<'a>(&self, _header: &Header, _proof: &'a [u8]) -> engine::ConstructedVerifier<'a> { + let v = EpochVerifier{pow: self.pow.clone()}; + engine::ConstructedVerifier::Trusted(Box::new(v)) } - fn snapshot_components(&self) -> Option> { - Some(Box::new(::snapshot::PowSnapshot::new(SNAPSHOT_BLOCKS, MAX_SNAPSHOT_BLOCKS))) + fn populate_from_parent(&self, header: &mut Header, parent: &Header) { + let difficulty = self.calculate_difficulty(header, parent); + header.set_difficulty(difficulty); } - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> engines::ForkChoice { - engines::total_difficulty_fork_choice(new, current) + fn snapshot_mode(&self) -> Snapshotting { + Snapshotting::PoW { + blocks: SNAPSHOT_BLOCKS, + max_restore_blocks: MAX_SNAPSHOT_BLOCKS + } } + + fn params(&self) -> &CommonParams { self.machine.params() } } impl Ethash { @@ -467,7 +464,16 @@ impl Ethash { } } -fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number:u64) -> (u64, U256) { + +/// Calculates the number of eras and reward +/// +/// # Panics +/// +/// This function will panic if `era_rounds` is less than `2` +fn ecip1017_eras_block_reward(era_rounds: u64, mut reward: U256, block_number: u64) -> (u64, U256) { + // NOTE(niklasad1): all numbers is divisible by 1, it will cause the if below + // to succeed except for the first block. Thus, `era_rounds - 1 == 0` and cause `divide by zero` + assert!(era_rounds > 1, "ecip1017EraRounds must be bigger than 1"); let eras = if block_number != 0 && block_number % era_rounds == 0 { block_number / era_rounds - 1 } else { @@ -487,18 +493,23 @@ mod tests { use std::str::FromStr; use std::sync::Arc; use std::collections::BTreeMap; + + use common_types::{ + header::Header, + errors::{BlockError, EthcoreError as Error} + }; + use engine::Engine; use ethereum_types::{H64, H256, U256, Address}; - use block::*; - use test_helpers::get_temp_state_db; - use error::{BlockError, Error, ErrorKind}; - use types::header::Header; - use spec::Spec; - use engines::Engine; - use super::super::{new_morden, new_mcip3_test, new_homestead_test_machine}; - use super::{Ethash, EthashParams, ecip1017_eras_block_reward}; + use ethcore::{ + block::*, + test_helpers::get_temp_state_db, + }; use rlp; + use spec::{new_morden, new_mcip3_test, new_homestead_test_machine, Spec}; use tempdir::TempDir; + use super::{Ethash, EthashParams, ecip1017_eras_block_reward}; + fn test_spec() -> Spec { let tempdir = TempDir::new("").unwrap(); new_morden(&tempdir.path()) @@ -540,7 +551,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, None).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b = b.close().unwrap(); assert_eq!(b.state.balance(&Address::zero()).unwrap(), U256::from_str("4563918244f40000").unwrap()); } @@ -589,9 +600,9 @@ 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, None).unwrap(); + 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 uncle = Header::new(); - let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); + let uncle_author = Address::from_str("ef2d6d194084c2de36e0dabfce45d046b37d1106").unwrap(); uncle.set_author(uncle_author); b.push_uncle(uncle).unwrap(); @@ -607,11 +618,11 @@ 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, None).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b = b.close().unwrap(); - let ubi_contract: Address = "00efdd5883ec628983e9063c7d969fe268bbf310".into(); - let dev_contract: Address = "00756cf8159095948496617f5fb17ed95059f536".into(); + let ubi_contract = Address::from_str("00efdd5883ec628983e9063c7d969fe268bbf310").unwrap(); + let dev_contract = Address::from_str("00756cf8159095948496617f5fb17ed95059f536").unwrap(); assert_eq!(b.state.balance(&Address::zero()).unwrap(), U256::from_str("d8d726b7177a80000").unwrap()); assert_eq!(b.state.balance(&ubi_contract).unwrap(), U256::from_str("2b5e3af16b1880000").unwrap()); assert_eq!(b.state.balance(&dev_contract).unwrap(), U256::from_str("c249fdd327780000").unwrap()); @@ -620,7 +631,7 @@ mod tests { #[test] fn has_valid_metadata() { let engine = test_spec().engine; - assert!(!engine.name().is_empty()); + assert_eq!(engine.name(), "Ethash"); } #[test] @@ -641,7 +652,7 @@ mod tests { let verify_result = engine.verify_block_basic(&header); match verify_result { - Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {}, + Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -656,7 +667,7 @@ mod tests { let verify_result = engine.verify_block_basic(&header); match verify_result { - Err(Error(ErrorKind::Block(BlockError::DifficultyOutOfBounds(_)), _)) => {}, + Err(Error::Block(BlockError::DifficultyOutOfBounds(_))) => {}, Err(_) => { panic!("should be block difficulty error (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -672,7 +683,7 @@ mod tests { let verify_result = engine.verify_block_basic(&header); match verify_result { - Err(Error(ErrorKind::Block(BlockError::InvalidProofOfWork(_)), _)) => {}, + Err(Error::Block(BlockError::InvalidProofOfWork(_))) => {}, Err(_) => { panic!("should be invalid proof of work error (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -686,7 +697,7 @@ mod tests { let verify_result = engine.verify_block_unordered(&header); match verify_result { - Err(Error(ErrorKind::Block(BlockError::InvalidSealArity(_)), _)) => {}, + Err(Error::Block(BlockError::InvalidSealArity(_))) => {}, Err(_) => { panic!("should be block seal-arity mismatch error (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -711,7 +722,7 @@ mod tests { let verify_result = engine.verify_block_unordered(&header); match verify_result { - Err(Error(ErrorKind::Block(BlockError::MismatchedH256SealElement(_)), _)) => {}, + Err(Error::Block(BlockError::MismatchedH256SealElement(_))) => {}, Err(_) => { panic!("should be invalid 256-bit seal fail (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -721,13 +732,13 @@ mod tests { fn can_do_proof_of_work_unordered_verification_fail() { let engine = test_spec().engine; let mut header: Header = Header::default(); - header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")), rlp::encode(&H64::zero())]); + header.set_seal(vec![rlp::encode(&H256::from_str("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d").unwrap()), rlp::encode(&H64::zero())]); header.set_difficulty(U256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap()); let verify_result = engine.verify_block_unordered(&header); match verify_result { - Err(Error(ErrorKind::Block(BlockError::InvalidProofOfWork(_)), _)) => {}, + Err(Error::Block(BlockError::InvalidProofOfWork(_))) => {}, Err(_) => { panic!("should be invalid proof-of-work fail (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -742,7 +753,7 @@ mod tests { let verify_result = engine.verify_block_family(&header, &parent_header); match verify_result { - Err(Error(ErrorKind::Block(BlockError::RidiculousNumber(_)), _)) => {}, + Err(Error::Block(BlockError::RidiculousNumber(_))) => {}, Err(_) => { panic!("should be invalid block number fail (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -759,7 +770,7 @@ mod tests { let verify_result = engine.verify_block_family(&header, &parent_header); match verify_result { - Err(Error(ErrorKind::Block(BlockError::InvalidDifficulty(_)), _)) => {}, + Err(Error::Block(BlockError::InvalidDifficulty(_))) => {}, Err(_) => { panic!("should be invalid difficulty fail (got {:?})", verify_result); }, _ => { panic!("Should be error, got Ok"); }, } @@ -914,7 +925,7 @@ mod tests { let tempdir = TempDir::new("").unwrap(); let ethash = Ethash::new(tempdir.path(), ethparams, machine, None); let mut header = Header::default(); - header.set_seal(vec![rlp::encode(&H256::from("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d")), rlp::encode(&H64::zero())]); + header.set_seal(vec![rlp::encode(&H256::from_str("b251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d").unwrap()), rlp::encode(&H64::zero())]); let info = ethash.extra_info(&header); assert_eq!(info["nonce"], "0x0000000000000000"); assert_eq!(info["mixHash"], "0xb251bd2e0283d0658f2cadfdc8ca619b5de94eca5742725e2e757dd13ed7503d"); diff --git a/ethcore/engines/instant-seal/Cargo.toml b/ethcore/engines/instant-seal/Cargo.toml new file mode 100644 index 0000000000..c3a4eb2b48 --- /dev/null +++ b/ethcore/engines/instant-seal/Cargo.toml @@ -0,0 +1,22 @@ +[package] +description = "Engine that seals instantly" +name = "instant-seal" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +client-traits = { path = "../../client-traits" } +common-types = { path = "../../types" } +engine = { path = "../../engine" } +ethjson = { path = "../../../json" } +ethereum-types = "0.8.0" +keccak-hash = "0.4.0" +machine = { path = "../../machine" } +trace = { path = "../../trace" } + +[dev-dependencies] +ethcore = { path = "../..", features = ["test-helpers"] } +spec = { path = "../../spec" } +rlp = "0.4.2" diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/engines/instant-seal/src/lib.rs similarity index 68% rename from ethcore/src/engines/instant_seal.rs rename to ethcore/engines/instant-seal/src/lib.rs index ec6f42bc7d..ff0376b336 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/engines/instant-seal/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,12 +14,25 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use engines::{Engine, Seal}; -use machine::Machine; -use types::header::{Header, ExtendedHeader}; -use block::ExecutedBlock; use std::sync::atomic::{AtomicU64, Ordering}; +use common_types::{ + header::Header, + engines::{ + Seal, + SealingState, + params::CommonParams, + }, + errors::EthcoreError as Error, +}; +use engine::Engine; +use ethjson; +use machine::{ + ExecutedBlock, + Machine +}; + + /// `InstantSeal` params. #[derive(Default, Debug, PartialEq)] pub struct InstantSealParams { @@ -27,8 +40,8 @@ pub struct InstantSealParams { pub millisecond_timestamp: bool, } -impl From<::ethjson::spec::InstantSealParams> for InstantSealParams { - fn from(p: ::ethjson::spec::InstantSealParams) -> Self { +impl From for InstantSealParams { + fn from(p: ethjson::spec::InstantSealParams) -> Self { InstantSealParams { millisecond_timestamp: p.millisecond_timestamp, } @@ -37,15 +50,15 @@ impl From<::ethjson::spec::InstantSealParams> for InstantSealParams { /// An engine which does not provide any consensus mechanism, just seals blocks internally. /// Only seals blocks which have transactions. -pub struct InstantSeal { +pub struct InstantSeal { params: InstantSealParams, - machine: M, - last_sealed_block: AtomicU64, + machine: Machine, + last_sealed_block: AtomicU64, } -impl InstantSeal { +impl InstantSeal { /// Returns new instance of InstantSeal over the given state machine. - pub fn new(params: InstantSealParams, machine: M) -> Self { + pub fn new(params: InstantSealParams, machine: Machine) -> Self { InstantSeal { params, machine, @@ -54,14 +67,19 @@ impl InstantSeal { } } -impl Engine for InstantSeal { - fn name(&self) -> &str { - "InstantSeal" - } +impl Engine for InstantSeal { + fn name(&self) -> &str { "InstantSeal" } - fn machine(&self) -> &M { &self.machine } + fn machine(&self) -> &Machine { &self.machine } - fn seals_internally(&self) -> Option { Some(true) } + fn sealing_state(&self) -> SealingState { SealingState::Ready } + + fn should_reseal_on_update(&self) -> bool { + // We would like for the miner to `update_sealing` if there are local_pending_transactions + // in the pool to prevent transactions sent in parallel from stalling in the transaction + // pool. (see #9660) + true + } fn generate_seal(&self, block: &ExecutedBlock, _parent: &Header) -> Seal { if !block.transactions.is_empty() { @@ -79,7 +97,7 @@ impl Engine for InstantSeal { Seal::None } - fn verify_local_seal(&self, _header: &Header) -> Result<(), M::Error> { + fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { Ok(()) } @@ -98,29 +116,35 @@ impl Engine for InstantSeal { header_timestamp >= parent_timestamp } - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { - super::total_difficulty_fork_choice(new, current) + fn params(&self) -> &CommonParams { + self.machine.params() } + } + #[cfg(test)] mod tests { use std::sync::Arc; + use common_types::{ + header::Header, + engines::Seal, + }; use ethereum_types::{H520, Address}; - use test_helpers::get_temp_state_db; - use spec::Spec; - use types::header::Header; - use block::*; - use engines::Seal; + use ethcore::{ + test_helpers::get_temp_state_db, + block::*, + }; + use spec; #[test] fn instant_can_seal() { - let spec = Spec::new_instant(); + let spec = spec::new_instant(); let engine = &*spec.engine; 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, None).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap(); let b = b.close_and_lock().unwrap(); if let Seal::Regular(seal) = engine.generate_seal(&b, &genesis_header) { assert!(b.try_seal(engine, seal).is_ok()); @@ -129,12 +153,12 @@ mod tests { #[test] fn instant_cant_verify() { - let engine = Spec::new_instant().engine; + let engine = spec::new_instant().engine; let mut header: Header = Header::default(); assert!(engine.verify_block_basic(&header).is_ok()); - header.set_seal(vec![::rlp::encode(&H520::default())]); + header.set_seal(vec![rlp::encode(&H520::default())]); assert!(engine.verify_block_unordered(&header).is_ok()); } diff --git a/ethcore/engines/null-engine/Cargo.toml b/ethcore/engines/null-engine/Cargo.toml new file mode 100644 index 0000000000..2fa5edea1d --- /dev/null +++ b/ethcore/engines/null-engine/Cargo.toml @@ -0,0 +1,15 @@ +[package] +description = "An ethereum engine which does not provide any consensus mechanism and does not seal blocks." +name = "null-engine" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +common-types = { path = "../../types" } +block-reward = { path = "../../block-reward" } +engine = { path = "../../engine" } +ethjson = { path = "../../../json" } +ethereum-types = "0.8.0" +machine = { path = "../../machine" } diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/engines/null-engine/src/lib.rs similarity index 56% rename from ethcore/src/engines/null_engine.rs rename to ethcore/engines/null-engine/src/lib.rs index 27138985ad..51633b9e53 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/engines/null-engine/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,59 +14,70 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use engines::Engine; -use engines::block_reward::{self, RewardKind}; +use common_types::{ + BlockNumber, + header::Header, + engines::params::CommonParams, + errors::EthcoreError as Error, +}; +use engine::Engine; +use block_reward::{self, RewardKind}; use ethereum_types::U256; -use machine::Machine; -use types::BlockNumber; -use types::header::{Header, ExtendedHeader}; -use block::ExecutedBlock; +use machine::{ + ExecutedBlock, + Machine, +}; +use common_types::{ + ancestry_action::AncestryAction, + header::ExtendedHeader, + snapshot::Snapshotting +}; /// Params for a null engine. #[derive(Clone, Default)] pub struct NullEngineParams { /// base reward for a block. pub block_reward: U256, + /// Immediate finalization. + pub immediate_finalization: bool } -impl From<::ethjson::spec::NullEngineParams> for NullEngineParams { - fn from(p: ::ethjson::spec::NullEngineParams) -> Self { +impl From for NullEngineParams { + fn from(p: ethjson::spec::NullEngineParams) -> Self { NullEngineParams { block_reward: p.block_reward.map_or_else(Default::default, Into::into), + immediate_finalization: p.immediate_finalization.unwrap_or(false) } } } /// An engine which does not provide any consensus mechanism and does not seal blocks. -pub struct NullEngine { +pub struct NullEngine { params: NullEngineParams, - machine: M, + machine: Machine, } -impl NullEngine { +impl NullEngine { /// Returns new instance of NullEngine with default VM Factory - pub fn new(params: NullEngineParams, machine: M) -> Self { + pub fn new(params: NullEngineParams, machine: Machine) -> Self { NullEngine { - params: params, - machine: machine, + params, + machine, } } } +impl Engine for NullEngine { + fn name(&self) -> &str { "NullEngine" } -impl Default for NullEngine { - fn default() -> Self { - Self::new(Default::default(), Default::default()) - } -} + fn machine(&self) -> &Machine { &self.machine } -impl Engine for NullEngine { - fn name(&self) -> &str { - "NullEngine" - } - - fn machine(&self) -> &M { &self.machine } + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } - fn on_close_block(&self, block: &mut ExecutedBlock) -> Result<(), M::Error> { + fn on_close_block( + &self, + block: &mut ExecutedBlock, + _parent_header: &Header + ) -> Result<(), Error> { use std::ops::Shr; let author = *block.header.author(); @@ -93,17 +104,24 @@ impl Engine for NullEngine { block_reward::apply_block_rewards(&rewards, block, &self.machine) } - fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } - - fn verify_local_seal(&self, _header: &Header) -> Result<(), M::Error> { + fn verify_local_seal(&self, _header: &Header) -> Result<(), Error> { Ok(()) } - fn snapshot_components(&self) -> Option> { - Some(Box::new(::snapshot::PowSnapshot::new(10000, 10000))) + fn snapshot_mode(&self) -> Snapshotting { + Snapshotting::PoW { blocks: 10_000, max_restore_blocks: 10_000 } } - fn fork_choice(&self, new: &ExtendedHeader, current: &ExtendedHeader) -> super::ForkChoice { - super::total_difficulty_fork_choice(new, current) + fn params(&self) -> &CommonParams { + self.machine.params() + } + + fn ancestry_actions(&self, _header: &Header, ancestry: &mut dyn Iterator) -> Vec { + if self.params.immediate_finalization { + // always mark parent finalized + ancestry.take(1).map(|e| AncestryAction::MarkFinalized(e.header.hash())).collect() + } else { + Vec::new() + } } } diff --git a/ethcore/engines/validator-set/Cargo.toml b/ethcore/engines/validator-set/Cargo.toml new file mode 100644 index 0000000000..bd63c380d3 --- /dev/null +++ b/ethcore/engines/validator-set/Cargo.toml @@ -0,0 +1,45 @@ +[package] +description = "Manage validators and sets of validators: creation, calling and validation of contracts, epoch management, proofs and proving" +name = "validator-set" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +client-traits = { path = "../../client-traits" } +common-types = { path = "../../types" } +engine = { path = "../../engine" } +ethabi = "9.0.1" +ethabi-contract = "9.0.0" +ethabi-derive = "9.0.1" +ethereum-types = "0.8.0" +ethjson = { path = "../../../json" } +executive-state = { path = "../../executive-state" } +keccak-hash = "0.4.0" +kvdb = "0.3.1" +lazy_static = "1.3.0" +log = "0.4.8" +machine = { path = "../../machine" } +memory-cache = { path = "../../../util/memory-cache" } +parity-bytes = "0.1.0" +parity-util-mem = "0.3.0" +parking_lot = "0.9" +rlp = "0.4.2" +triehash = { package = "triehash-ethereum", version = "0.2", path = "../../../util/triehash-ethereum" } +unexpected = { path = "../../../util/unexpected" } +vm = { path = "../../vm" } + +[dev-dependencies] +accounts = { package = "ethcore-accounts", path = "../../../accounts" } +call-contract = { package = "ethcore-call-contract", path = "../../call-contract" } +engine = { path = "../../engine", features = ["test-helpers"] } +env_logger = "0.6.2" +ethcore = { path = "../..", features = ["test-helpers"] } +parity-crypto = { version = "0.4.2", features = ["publickey"] } +keccak-hash = "0.4.0" +rustc-hex = "1.0" +spec = { path = "../../spec" } + +[features] +test-helpers = [] diff --git a/ethcore/res/contracts/validator_report.json b/ethcore/engines/validator-set/res/validator_report.json similarity index 100% rename from ethcore/res/contracts/validator_report.json rename to ethcore/engines/validator-set/res/validator_report.json diff --git a/ethcore/res/contracts/validator_set.json b/ethcore/engines/validator-set/res/validator_set.json similarity index 100% rename from ethcore/res/contracts/validator_set.json rename to ethcore/engines/validator-set/res/validator_set.json diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/engines/validator-set/src/contract.rs similarity index 78% rename from ethcore/src/engines/validator_set/contract.rs rename to ethcore/engines/validator-set/src/contract.rs index f0064a8c20..b64a0e8612 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/engines/validator-set/src/contract.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,25 +19,35 @@ use std::sync::Weak; -use bytes::Bytes; +use parity_bytes::Bytes; +use ethabi_contract::use_contract; use ethereum_types::{H256, Address}; -use machine::{AuxiliaryData, Call, EthereumMachine}; +use log::{warn, trace}; +use machine::Machine; use parking_lot::RwLock; -use types::BlockNumber; -use types::header::Header; +use common_types::{ + BlockNumber, + ids::BlockId, + header::Header, + errors::EthcoreError, + engines::machine::{Call, AuxiliaryData}, +}; -use client::EngineClient; +use client_traits::{EngineClient, TransactionRequest}; +use engine::SystemCall; -use super::{ValidatorSet, SimpleList, SystemCall}; -use super::safe_contract::ValidatorSafeContract; +use crate::{ + ValidatorSet, SimpleList, + safe_contract::ValidatorSafeContract +}; -use_contract!(validator_report, "res/contracts/validator_report.json"); +use_contract!(validator_report, "res/validator_report.json"); /// A validator contract with reporting. pub struct ValidatorContract { contract_address: Address, validators: ValidatorSafeContract, - client: RwLock>>, // TODO [keorn]: remove + client: RwLock>>, // TODO [keorn]: remove } impl ValidatorContract { @@ -58,7 +68,7 @@ impl ValidatorContract { match client.as_full_client() { Some(c) => { - c.transact_contract(self.contract_address, data) + c.transact(TransactionRequest::call(self.contract_address, data)) .map_err(|e| format!("Transaction import error: {}", e))?; Ok(()) }, @@ -68,11 +78,11 @@ impl ValidatorContract { } impl ValidatorSet for ValidatorContract { - fn default_caller(&self, id: ::types::ids::BlockId) -> Box { + fn default_caller(&self, id: BlockId) -> Box { self.validators.default_caller(id) } - fn on_epoch_begin(&self, first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> { + fn on_epoch_begin(&self, first: bool, header: &Header, call: &mut SystemCall) -> Result<(), EthcoreError> { self.validators.on_epoch_begin(first, header, call) } @@ -89,11 +99,11 @@ impl ValidatorSet for ValidatorContract { first: bool, header: &Header, aux: AuxiliaryData, - ) -> ::engines::EpochChange { + ) -> engine::EpochChange { self.validators.signals_epoch_end(first, header, aux) } - fn epoch_set(&self, first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { + fn epoch_set(&self, first: bool, machine: &Machine, number: BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), EthcoreError> { self.validators.epoch_set(first, machine, number, proof) } @@ -118,6 +128,7 @@ impl ValidatorSet for ValidatorContract { } fn report_benign(&self, address: &Address, _set_block: BlockNumber, block: BlockNumber) { + trace!(target: "engine", "validator set recording benign misbehaviour at block #{} by {:#x}", block, address); let data = validator_report::functions::report_benign::encode_input(*address, block); match self.transact(data) { Ok(_) => warn!(target: "engine", "Reported benign validator misbehaviour {}", address), @@ -125,7 +136,7 @@ impl ValidatorSet for ValidatorContract { } } - fn register_client(&self, client: Weak) { + fn register_client(&self, client: Weak) { self.validators.register_client(client.clone()); *self.client.write() = Some(client); } @@ -134,25 +145,28 @@ impl ValidatorSet for ValidatorContract { #[cfg(test)] mod tests { use std::sync::Arc; - use rustc_hex::FromHex; - use hash::keccak; - use ethereum_types::{H520, Address}; - use bytes::ToPretty; - use rlp::encode; - use spec::Spec; - use types::header::Header; + use accounts::AccountProvider; - use miner::{self, MinerService}; - use types::ids::BlockId; - use test_helpers::generate_dummy_client_with_spec; use call_contract::CallContract; - use client::{BlockChainClient, ChainInfo, BlockInfo}; + use common_types::{header::Header, ids::BlockId}; + use client_traits::{BlockChainClient, ChainInfo, BlockInfo, TransactionRequest}; + use ethcore::{ + miner::{self, MinerService}, + test_helpers::generate_dummy_client_with_spec, + }; + use ethereum_types::{H520, Address}; + use keccak_hash::keccak; + use parity_bytes::ToPretty; + use rlp::encode; + use rustc_hex::FromHex; + use spec; + use super::super::ValidatorSet; use super::ValidatorContract; #[test] fn fetches_validators() { - let client = generate_dummy_client_with_spec(Spec::new_validator_contract); + let client = generate_dummy_client_with_spec(spec::new_validator_contract); let vc = Arc::new(ValidatorContract::new("0000000000000000000000000000000000000005".parse::
().unwrap())); vc.register_client(Arc::downgrade(&client) as _); let last_hash = client.best_block_header().hash(); @@ -162,9 +176,10 @@ mod tests { #[test] fn reports_validators() { + let _ = ::env_logger::try_init(); let tap = Arc::new(AccountProvider::transient_provider()); let v1 = tap.insert_account(keccak("1").into(), &"".into()).unwrap(); - let client = generate_dummy_client_with_spec(Spec::new_validator_contract); + let client = generate_dummy_client_with_spec(spec::new_validator_contract); client.engine().register_client(Arc::downgrade(&client) as _); let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); @@ -175,7 +190,7 @@ mod tests { // Check a block that is a bit in future, reject it but don't report the validator. let mut header = Header::default(); - let seal = vec![encode(&4u8), encode(&(&H520::default() as &[u8]))]; + let seal = vec![encode(&4u8), encode(&H520::zero().as_bytes())]; header.set_seal(seal); header.set_author(v1); header.set_number(2); @@ -186,7 +201,7 @@ mod tests { // Now create one that is more in future. That one should be rejected and validator should be reported. let mut header = Header::default(); - let seal = vec![encode(&8u8), encode(&(&H520::default() as &[u8]))]; + let seal = vec![encode(&8u8), encode(&H520::zero().as_bytes())]; header.set_seal(seal); header.set_author(v1); header.set_number(2); @@ -210,7 +225,7 @@ mod tests { assert_eq!(client.chain_info().best_block_number, 2); // Check if misbehaving validator was removed. - client.transact_contract(Default::default(), Default::default()).unwrap(); + client.transact(TransactionRequest::call(Default::default(), Default::default())).unwrap(); client.engine().step(); client.engine().step(); assert_eq!(client.chain_info().best_block_number, 2); diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/engines/validator-set/src/lib.rs similarity index 85% rename from ethcore/src/engines/validator_set/mod.rs rename to ethcore/engines/validator-set/src/lib.rs index 915a3f9a15..05a86bf069 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/engines/validator-set/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ /// Validator lists. -#[cfg(test)] +#[cfg(any(test, feature = "test-helpers"))] mod test; mod simple_list; mod safe_contract; @@ -25,26 +25,32 @@ mod multi; use std::sync::Weak; -use bytes::Bytes; +use client_traits::EngineClient; +use common_types::{ + BlockNumber, + header::Header, + ids::BlockId, + errors::EthcoreError, + engines::machine::{Call, AuxiliaryData}, +}; +use engine::SystemCall; use ethereum_types::{H256, Address}; use ethjson::spec::ValidatorSet as ValidatorSpec; -use machine::{AuxiliaryData, Call, EthereumMachine}; -use types::BlockNumber; -use types::header::Header; -use types::ids::BlockId; +use machine::Machine; +// The MallocSizeOf derive looks for this in the root +use parity_util_mem as malloc_size_of; +use parity_bytes::Bytes; -use client::EngineClient; - -#[cfg(test)] +#[cfg(any(test, feature = "test-helpers"))] pub use self::test::TestSet; pub use self::simple_list::SimpleList; + use self::contract::ValidatorContract; use self::safe_contract::ValidatorSafeContract; use self::multi::Multi; -use super::SystemCall; /// Creates a validator set from spec. -pub fn new_validator_set(spec: ValidatorSpec) -> Box { +pub fn new_validator_set(spec: ValidatorSpec) -> Box { match spec { ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())), ValidatorSpec::SafeContract(address) => Box::new(ValidatorSafeContract::new(address.into())), @@ -88,7 +94,7 @@ pub trait ValidatorSet: Send + Sync + 'static { /// The caller provided here may not generate proofs. /// /// `first` is true if this is the first block in the set. - fn on_epoch_begin(&self, _first: bool, _header: &Header, _call: &mut SystemCall) -> Result<(), ::error::Error> { + fn on_epoch_begin(&self, _first: bool, _header: &Header, _call: &mut SystemCall) -> Result<(), EthcoreError> { Ok(()) } @@ -113,7 +119,7 @@ pub trait ValidatorSet: Send + Sync + 'static { first: bool, header: &Header, aux: AuxiliaryData, - ) -> ::engines::EpochChange; + ) -> engine::EpochChange; /// Recover the validator set from the given proof, the block number, and /// whether this header is first in its set. @@ -123,8 +129,8 @@ pub trait ValidatorSet: Send + Sync + 'static { /// /// Returns the set, along with a flag indicating whether finality of a specific /// hash should be proven. - fn epoch_set(&self, first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) - -> Result<(SimpleList, Option), ::error::Error>; + fn epoch_set(&self, first: bool, machine: &Machine, number: BlockNumber, proof: &[u8]) + -> Result<(SimpleList, Option), EthcoreError>; /// Checks if a given address is a validator, with the given function /// for executing synchronous calls to contracts. @@ -141,5 +147,5 @@ pub trait ValidatorSet: Send + Sync + 'static { /// Notifies about benign misbehaviour. fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, _block: BlockNumber) {} /// Allows blockchain state access. - fn register_client(&self, _client: Weak) {} + fn register_client(&self, _client: Weak) {} } diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/engines/validator-set/src/multi.rs similarity index 76% rename from ethcore/src/engines/validator_set/multi.rs rename to ethcore/engines/validator-set/src/multi.rs index b9ef677478..76d7a4669c 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/engines/validator-set/src/multi.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,26 +19,31 @@ use std::collections::BTreeMap; use std::sync::Weak; -use bytes::Bytes; +use common_types::{ + BlockNumber, + header::Header, + ids::BlockId, + errors::EthcoreError, + engines::machine::{Call, AuxiliaryData}, +}; +use client_traits::EngineClient; use ethereum_types::{H256, Address}; +use log::{debug, trace}; +use parity_bytes::Bytes; use parking_lot::RwLock; -use types::BlockNumber; -use types::header::Header; -use types::ids::BlockId; +use machine::Machine; -use client::EngineClient; -use machine::{AuxiliaryData, Call, EthereumMachine}; use super::{SystemCall, ValidatorSet}; -type BlockNumberLookup = Box Result + Send + Sync + 'static>; +type BlockNumberLookup = Box Result + Send + Sync + 'static>; pub struct Multi { - sets: BTreeMap>, + sets: BTreeMap>, block_number: RwLock, } impl Multi { - pub fn new(set_map: BTreeMap>) -> Self { + pub fn new(set_map: BTreeMap>) -> Self { assert!(set_map.get(&0u64).is_some(), "ValidatorSet has to be specified from block 0."); Multi { sets: set_map, @@ -46,7 +51,7 @@ impl Multi { } } - fn correct_set(&self, id: BlockId) -> Option<&ValidatorSet> { + fn correct_set(&self, id: BlockId) -> Option<&dyn ValidatorSet> { match self.block_number.read()(id).map(|parent_block| self.correct_set_by_number(parent_block)) { Ok((_, set)) => Some(set), Err(e) => { @@ -58,7 +63,7 @@ impl Multi { // get correct set by block number, along with block number at which // this set was activated. - fn correct_set_by_number(&self, parent_block: BlockNumber) -> (BlockNumber, &ValidatorSet) { + fn correct_set_by_number(&self, parent_block: BlockNumber) -> (BlockNumber, &dyn ValidatorSet) { let (block, set) = self.sets.iter() .rev() .find(|&(block, _)| *block <= parent_block + 1) @@ -77,7 +82,7 @@ impl ValidatorSet for Multi { .unwrap_or_else(|| Box::new(|_, _| Err("No validator set for given ID.".into()))) } - fn on_epoch_begin(&self, _first: bool, header: &Header, call: &mut SystemCall) -> Result<(), ::error::Error> { + fn on_epoch_begin(&self, _first: bool, header: &Header, call: &mut SystemCall) -> Result<(), EthcoreError> { let (set_block, set) = self.correct_set_by_number(header.number()); let first = set_block == header.number(); @@ -96,7 +101,7 @@ impl ValidatorSet for Multi { } fn signals_epoch_end(&self, _first: bool, header: &Header, aux: AuxiliaryData) - -> ::engines::EpochChange + -> engine::EpochChange { let (set_block, set) = self.correct_set_by_number(header.number()); let first = set_block == header.number(); @@ -104,7 +109,7 @@ impl ValidatorSet for Multi { set.signals_epoch_end(first, header, aux) } - fn epoch_set(&self, _first: bool, machine: &EthereumMachine, number: BlockNumber, proof: &[u8]) -> Result<(super::SimpleList, Option), ::error::Error> { + fn epoch_set(&self, _first: bool, machine: &Machine, number: BlockNumber, proof: &[u8]) -> Result<(super::SimpleList, Option), EthcoreError> { let (set_block, set) = self.correct_set_by_number(number); let first = set_block == number; @@ -134,7 +139,7 @@ impl ValidatorSet for Multi { self.correct_set_by_number(set_block).1.report_benign(validator, set_block, block); } - fn register_client(&self, client: Weak) { + fn register_client(&self, client: Weak) { for set in self.sets.values() { set.register_client(client.clone()); } @@ -149,20 +154,27 @@ impl ValidatorSet for Multi { mod tests { use std::sync::Arc; use std::collections::BTreeMap; - use hash::keccak; + use accounts::AccountProvider; - use client::{BlockChainClient, ChainInfo, BlockInfo, ImportBlock}; - use engines::EpochChange; - use engines::validator_set::ValidatorSet; - use ethkey::Secret; - use types::header::Header; - use miner::{self, MinerService}; - use spec::Spec; - use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data}; - use types::ids::BlockId; + use common_types::{ + header::Header, + ids::BlockId, + verification::Unverified, + }; + use client_traits::{ + BlockChainClient, BlockInfo, ChainInfo, ImportBlock, EngineClient, ForceUpdateSealing, TransactionRequest + }; + use engine::EpochChange; + use ethcore::{ + miner::{self, MinerService}, + test_helpers::generate_dummy_client_with_spec, + }; use ethereum_types::Address; - use verification::queue::kind::blocks::Unverified; + use parity_crypto::publickey::Secret; + use keccak_hash::keccak; + use spec; + use crate::ValidatorSet; use super::Multi; #[test] @@ -171,7 +183,7 @@ mod tests { let s0: Secret = keccak("0").into(); let v0 = tap.insert_account(s0.clone(), &"".into()).unwrap(); let v1 = tap.insert_account(keccak("1").into(), &"".into()).unwrap(); - let client = generate_dummy_client_with_spec(Spec::new_validator_multi); + let client = generate_dummy_client_with_spec(spec::new_validator_multi); client.engine().register_client(Arc::downgrade(&client) as _); // Make sure txs go through. @@ -180,29 +192,29 @@ mod tests { // Wrong signer for the first block. let signer = Box::new((tap.clone(), v1, "".into())); client.miner().set_author(miner::Author::Sealer(signer)); - client.transact_contract(Default::default(), Default::default()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + client.transact(TransactionRequest::call(Default::default(), Default::default())).unwrap(); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); assert_eq!(client.chain_info().best_block_number, 0); // Right signer for the first block. let signer = Box::new((tap.clone(), v0, "".into())); client.miner().set_author(miner::Author::Sealer(signer)); - ::client::EngineClient::update_sealing(&*client); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); 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); + client.transact(TransactionRequest::call(Default::default(), Default::default())).unwrap(); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); assert_eq!(client.chain_info().best_block_number, 1); let signer = Box::new((tap.clone(), v1, "".into())); client.miner().set_author(miner::Author::Sealer(signer)); - ::client::EngineClient::update_sealing(&*client); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); assert_eq!(client.chain_info().best_block_number, 2); // v1 is still good. - client.transact_contract(Default::default(), Default::default()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + client.transact(TransactionRequest::call(Default::default(), Default::default())).unwrap(); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); assert_eq!(client.chain_info().best_block_number, 3); // Check syncing. - let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_multi, 0, 0, &[]); + let sync_client = generate_dummy_client_with_spec(spec::new_validator_multi); sync_client.engine().register_client(Arc::downgrade(&sync_client) as _); for i in 1..4 { sync_client.import_block(Unverified::from_rlp(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap()).unwrap(); @@ -215,7 +227,7 @@ mod tests { fn transition_to_fixed_list_instant() { use super::super::SimpleList; - let mut map: BTreeMap<_, Box> = BTreeMap::new(); + let mut map: BTreeMap<_, Box> = BTreeMap::new(); let list1: Vec<_> = (0..10).map(|_| Address::random()).collect(); let list2 = { let mut list = list1.clone(); diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/engines/validator-set/src/safe_contract.rs similarity index 79% rename from ethcore/src/engines/validator_set/safe_contract.rs rename to ethcore/engines/validator-set/src/safe_contract.rs index 49d539df3f..de470e027d 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/engines/validator-set/src/safe_contract.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,26 +18,34 @@ use std::sync::{Weak, Arc}; -use bytes::Bytes; +use client_traits::EngineClient; +use common_types::{ + BlockNumber, + header::Header, + errors::{EngineError, EthcoreError, BlockError}, + ids::BlockId, + log_entry::LogEntry, + engines::machine::{Call, AuxiliaryData, AuxiliaryRequest}, + receipt::Receipt, +}; use ethabi::FunctionOutputDecoder; +use ethabi_contract::use_contract; use ethereum_types::{H256, U256, Address, Bloom}; -use hash::keccak; +use keccak_hash::keccak; use kvdb::DBValue; +use lazy_static::lazy_static; +use log::{debug, info, trace}; +use machine::Machine; use memory_cache::MemoryLruCache; +use parity_bytes::Bytes; use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; -use types::header::Header; -use types::ids::BlockId; -use types::log_entry::LogEntry; -use types::receipt::Receipt; use unexpected::Mismatch; -use client::EngineClient; -use machine::{AuxiliaryData, Call, EthereumMachine, AuxiliaryRequest}; use super::{SystemCall, ValidatorSet}; use super::simple_list::SimpleList; -use_contract!(validator_set, "res/contracts/validator_set.json"); +use_contract!(validator_set, "res/validator_set.json"); const MEMOIZE_CAPACITY: usize = 500; @@ -55,12 +63,12 @@ struct StateProof { header: Header, } -impl ::engines::StateDependentProof for StateProof { +impl engine::StateDependentProof for StateProof { fn generate_proof(&self, caller: &Call) -> Result, String> { prove_initial(self.contract_address, &self.header, caller) } - fn check_proof(&self, machine: &EthereumMachine, proof: &[u8]) -> Result<(), String> { + fn check_proof(&self, machine: &Machine, proof: &[u8]) -> Result<(), String> { let (header, state_items) = decode_first_proof(&Rlp::new(proof)) .map_err(|e| format!("proof incorrectly encoded: {}", e))?; if &header != &self.header { @@ -74,8 +82,10 @@ impl ::engines::StateDependentProof for StateProof { /// The validator contract should have the following interface: pub struct ValidatorSafeContract { contract_address: Address, + /// The LRU cache is indexed by the parent_hash, so given a hash, the value + /// is the validator set valid for the blocks following that hash. validators: RwLock>, - client: RwLock>>, // TODO [keorn]: remove + client: RwLock>>, // TODO [keorn]: remove } // first proof is just a state proof call of `getValidators` at header's state. @@ -90,23 +100,23 @@ fn encode_first_proof(header: &Header, state_items: &[Vec]) -> Bytes { } // check a first proof: fetch the validator set at the given block. -fn check_first_proof(machine: &EthereumMachine, contract_address: Address, old_header: Header, state_items: &[DBValue]) +fn check_first_proof(machine: &Machine, contract_address: Address, old_header: Header, state_items: &[DBValue]) -> Result, String> { - use types::transaction::{Action, Transaction}; + use common_types::transaction::{Action, Transaction}; // TODO: match client contract_call_tx more cleanly without duplication. const PROVIDED_GAS: u64 = 50_000_000; - let env_info = ::vm::EnvInfo { + let env_info = vm::EnvInfo { number: old_header.number(), author: *old_header.author(), difficulty: *old_header.difficulty(), gas_limit: PROVIDED_GAS.into(), timestamp: old_header.timestamp(), last_hashes: { - // this will break if we don't inclue all 256 last hashes. - let mut last_hashes: Vec<_> = (0..256).map(|_| H256::default()).collect(); + // this will break if we don't include all 256 last hashes. + let mut last_hashes: Vec<_> = (0..256).map(|_| H256::zero()).collect(); last_hashes[255] = *old_header.parent_hash(); Arc::new(last_hashes) }, @@ -117,7 +127,7 @@ fn check_first_proof(machine: &EthereumMachine, contract_address: Address, old_h let number = old_header.number(); let (data, decoder) = validator_set::functions::get_validators::call(); - let from = Address::default(); + let from = Address::zero(); let tx = Transaction { nonce: machine.account_start_nonce(number), action: Action::Call(contract_address), @@ -127,7 +137,7 @@ fn check_first_proof(machine: &EthereumMachine, contract_address: Address, old_h data, }.fake_sign(from); - let res = ::state::check_proof( + let res = executive_state::check_proof( state_items, *old_header.state_root(), &tx, @@ -136,19 +146,18 @@ fn check_first_proof(machine: &EthereumMachine, contract_address: Address, old_h ); match res { - ::state::ProvedExecution::BadProof => Err("Bad proof".into()), - ::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)), - ::state::ProvedExecution::Complete(e) => decoder.decode(&e.output).map_err(|e| e.to_string()), + executive_state::ProvedExecution::BadProof => Err("Bad proof".into()), + executive_state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)), + executive_state::ProvedExecution::Complete(e) => decoder.decode(&e.output).map_err(|e| e.to_string()), } } -fn decode_first_proof(rlp: &Rlp) -> Result<(Header, Vec), ::error::Error> { +fn decode_first_proof(rlp: &Rlp) -> Result<(Header, Vec), EthcoreError> { let header = rlp.val_at(0)?; - let state_items = rlp.at(1)?.iter().map(|x| { - let mut val = DBValue::new(); - val.append_slice(x.data()?); - Ok(val) - }).collect::>()?; + let state_items = rlp.at(1)? + .iter() + .map(|x| Ok(x.data()?.to_vec()) ) + .collect::>()?; Ok((header, state_items)) } @@ -162,7 +171,7 @@ fn encode_proof(header: &Header, receipts: &[Receipt]) -> Bytes { stream.drain() } -fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec), ::error::Error> { +fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec), EthcoreError> { Ok((rlp.val_at(0)?, rlp.list_at(1)?)) } @@ -208,11 +217,11 @@ impl ValidatorSafeContract { match value { Ok(new) => { - debug!(target: "engine", "Set of validators obtained: {:?}", new); + debug!(target: "engine", "Got validator set from contract: {:?}", new); Some(SimpleList::new(new)) }, Err(s) => { - debug!(target: "engine", "Set of validators could not be updated: {}", s); + debug!(target: "engine", "Could not get validator set from contract: {}", s); None }, } @@ -241,7 +250,7 @@ impl ValidatorSafeContract { LogEntry { address: self.contract_address, - topics: topics, + topics, data: Vec::new(), // irrelevant for bloom. }.bloom() } @@ -291,11 +300,11 @@ impl ValidatorSet for ValidatorSafeContract { .map(|out| (out, Vec::new()))) // generate no proofs in general } - fn on_epoch_begin(&self, _first: bool, _header: &Header, caller: &mut SystemCall) -> Result<(), ::error::Error> { + fn on_epoch_begin(&self, _first: bool, _header: &Header, caller: &mut SystemCall) -> Result<(), EthcoreError> { let data = validator_set::functions::finalize_change::encode_input(); caller(self.contract_address, data) .map(|_| ()) - .map_err(::engines::EngineError::FailedSystemCall) + .map_err(EngineError::FailedSystemCall) .map_err(Into::into) } @@ -308,7 +317,7 @@ impl ValidatorSet for ValidatorSafeContract { } fn signals_epoch_end(&self, first: bool, header: &Header, aux: AuxiliaryData) - -> ::engines::EpochChange + -> engine::EpochChange { let receipts = aux.receipts; @@ -319,34 +328,34 @@ impl ValidatorSet for ValidatorSafeContract { contract_address: self.contract_address, header: header.clone(), }); - return ::engines::EpochChange::Yes(::engines::Proof::WithState(state_proof as Arc<_>)); + return engine::EpochChange::Yes(engine::Proof::WithState(state_proof as Arc<_>)); } // otherwise, we're checking for logs. let bloom = self.expected_bloom(header); let header_bloom = header.log_bloom(); - if &bloom & header_bloom != bloom { return ::engines::EpochChange::No } + if &bloom & header_bloom != bloom { return engine::EpochChange::No } trace!(target: "engine", "detected epoch change event bloom"); match receipts { - None => ::engines::EpochChange::Unsure(AuxiliaryRequest::Receipts), + None => engine::EpochChange::Unsure(AuxiliaryRequest::Receipts), Some(receipts) => match self.extract_from_event(bloom, header, receipts) { - None => ::engines::EpochChange::No, + None => engine::EpochChange::No, Some(list) => { - info!(target: "engine", "Signal for transition within contract. New list: {:?}", + info!(target: "engine", "Signal for transition within contract. New validator list: {:?}", &*list); let proof = encode_proof(&header, receipts); - ::engines::EpochChange::Yes(::engines::Proof::Known(proof)) + engine::EpochChange::Yes(engine::Proof::Known(proof)) } }, } } - fn epoch_set(&self, first: bool, machine: &EthereumMachine, _number: ::types::BlockNumber, proof: &[u8]) - -> Result<(SimpleList, Option), ::error::Error> + fn epoch_set(&self, first: bool, machine: &Machine, _number: BlockNumber, proof: &[u8]) + -> Result<(SimpleList, Option), EthcoreError> { let rlp = Rlp::new(proof); @@ -357,9 +366,9 @@ impl ValidatorSet for ValidatorSafeContract { let number = old_header.number(); let old_hash = old_header.hash(); let addresses = check_first_proof(machine, self.contract_address, old_header, &state_items) - .map_err(::engines::EngineError::InsufficientProof)?; + .map_err(EngineError::InsufficientProof)?; - trace!(target: "engine", "extracted epoch set at #{}: {} addresses", + trace!(target: "engine", "Extracted epoch validator set at block #{}: {} addresses", number, addresses.len()); Ok((SimpleList::new(addresses), Some(old_hash))) @@ -368,11 +377,11 @@ impl ValidatorSet for ValidatorSafeContract { // ensure receipts match header. // TODO: optimize? these were just decoded. - let found_root = ::triehash::ordered_trie_root( + let found_root = triehash::ordered_trie_root( receipts.iter().map(::rlp::encode) ); if found_root != *old_header.receipts_root() { - return Err(::error::BlockError::InvalidReceiptsRoot( + return Err(BlockError::InvalidReceiptsRoot( Mismatch { expected: *old_header.receipts_root(), found: found_root } ).into()); } @@ -380,8 +389,12 @@ impl ValidatorSet for ValidatorSafeContract { let bloom = self.expected_bloom(&old_header); match self.extract_from_event(bloom, &old_header, &receipts) { - Some(list) => Ok((list, Some(old_header.hash()))), - None => Err(::engines::EngineError::InsufficientProof("No log event in proof.".into()).into()), + Some(list) => { + trace!(target: "engine", "Extracted epoch validator set at block #{}: {} addresses", old_header.number(), list.len()); + + Ok((list, Some(old_header.hash()))) + }, + None => Err(EngineError::InsufficientProof("No log event in proof.".into()).into()), } } } @@ -431,7 +444,7 @@ impl ValidatorSet for ValidatorSafeContract { })) } - fn register_client(&self, client: Weak) { + fn register_client(&self, client: Weak) { trace!(target: "engine", "Setting up contract caller."); *self.client.write() = Some(client); } @@ -440,24 +453,34 @@ impl ValidatorSet for ValidatorSafeContract { #[cfg(test)] mod tests { use std::sync::Arc; - use rustc_hex::FromHex; - use hash::keccak; - use ethereum_types::Address; - use types::ids::BlockId; - use spec::Spec; + use accounts::AccountProvider; - use types::transaction::{Transaction, Action}; - use client::{ChainInfo, BlockInfo, ImportBlock}; - use ethkey::Secret; - use miner::{self, MinerService}; - use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data}; + use common_types::{ + ids::BlockId, + engines::machine::AuxiliaryRequest, + header::Header, + log_entry::LogEntry, + transaction::{Transaction, Action}, + verification::Unverified, + }; + use client_traits::{BlockInfo, ChainInfo, ImportBlock, EngineClient, ForceUpdateSealing}; + use engine::{EpochChange, Proof}; + use ethcore::{ + miner::{self, MinerService}, + test_helpers::generate_dummy_client_with_spec + }; + use parity_crypto::publickey::Secret; + use ethereum_types::Address; + use keccak_hash::keccak; + use rustc_hex::FromHex; + use spec; + use super::super::ValidatorSet; use super::{ValidatorSafeContract, EVENT_NAME_HASH}; - use verification::queue::kind::blocks::Unverified; #[test] fn fetches_validators() { - let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); + let client = generate_dummy_client_with_spec(spec::new_validator_safe_contract); let vc = Arc::new(ValidatorSafeContract::new("0000000000000000000000000000000000000005".parse::
().unwrap())); vc.register_client(Arc::downgrade(&client) as _); let last_hash = client.best_block_header().hash(); @@ -467,12 +490,13 @@ mod tests { #[test] fn knows_validators() { + let _ = env_logger::try_init(); let tap = Arc::new(AccountProvider::transient_provider()); let s0: Secret = keccak("1").into(); let v0 = tap.insert_account(s0.clone(), &"".into()).unwrap(); let v1 = tap.insert_account(keccak("0").into(), &"".into()).unwrap(); - let chain_id = Spec::new_validator_safe_contract().chain_id(); - let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); + let chain_id = spec::new_validator_safe_contract().chain_id(); + let client = generate_dummy_client_with_spec(spec::new_validator_safe_contract); client.engine().register_client(Arc::downgrade(&client) as _); let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); let signer = Box::new((tap.clone(), v1, "".into())); @@ -488,7 +512,7 @@ mod tests { data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), }.sign(&s0, Some(chain_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); assert_eq!(client.chain_info().best_block_number, 1); // Add "1" validator back in. let tx = Transaction { @@ -500,14 +524,14 @@ mod tests { data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), }.sign(&s0, Some(chain_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); // The transaction is not yet included so still unable to seal. assert_eq!(client.chain_info().best_block_number, 1); // Switch to the validator that is still there. let signer = Box::new((tap.clone(), v0, "".into())); client.miner().set_author(miner::Author::Sealer(signer)); - ::client::EngineClient::update_sealing(&*client); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); assert_eq!(client.chain_info().best_block_number, 2); // Switch back to the added validator, since the state is updated. let signer = Box::new((tap.clone(), v1, "".into())); @@ -516,17 +540,17 @@ mod tests { nonce: 2.into(), gas_price: 0.into(), gas: 21000.into(), - action: Action::Call(Address::default()), + action: Action::Call(Address::zero()), value: 0.into(), data: Vec::new(), }.sign(&s0, Some(chain_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + EngineClient::update_sealing(&*client, ForceUpdateSealing::No); // Able to seal again. assert_eq!(client.chain_info().best_block_number, 3); // Check syncing. - let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_safe_contract, 0, 0, &[]); + let sync_client = generate_dummy_client_with_spec(spec::new_validator_safe_contract); sync_client.engine().register_client(Arc::downgrade(&sync_client) as _); for i in 1..4 { sync_client.import_block(Unverified::from_rlp(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap()).unwrap(); @@ -537,12 +561,7 @@ mod tests { #[test] fn detects_bloom() { - use engines::EpochChange; - use machine::AuxiliaryRequest; - use types::header::Header; - use types::log_entry::LogEntry; - - let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); + let client = generate_dummy_client_with_spec(spec::new_validator_safe_contract); let engine = client.engine().clone(); let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); @@ -576,10 +595,7 @@ mod tests { #[test] fn initial_contract_is_signal() { - use types::header::Header; - use engines::{EpochChange, Proof}; - - let client = generate_dummy_client_with_spec(Spec::new_validator_safe_contract); + let client = generate_dummy_client_with_spec(spec::new_validator_safe_contract); let engine = client.engine().clone(); let mut new_header = Header::default(); diff --git a/ethcore/src/engines/validator_set/simple_list.rs b/ethcore/engines/validator-set/src/simple_list.rs similarity index 72% rename from ethcore/src/engines/validator_set/simple_list.rs rename to ethcore/engines/validator-set/src/simple_list.rs index 0a0294be96..cbcfd6cf11 100644 --- a/ethcore/src/engines/validator_set/simple_list.rs +++ b/ethcore/engines/validator-set/src/simple_list.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,16 +16,22 @@ /// Preconfigured validator list. -use heapsize::HeapSizeOf; +use common_types::{ + BlockNumber, + ids::BlockId, + header::Header, + errors::EthcoreError, + engines::machine::{Call, AuxiliaryData}, +}; use ethereum_types::{H256, Address}; +use log::warn; +use machine::Machine; +use parity_util_mem::MallocSizeOf; -use machine::{AuxiliaryData, Call, EthereumMachine}; -use types::BlockNumber; -use types::header::Header; use super::ValidatorSet; /// Validator set containing a known set of addresses. -#[derive(Clone, Debug, PartialEq, Eq, Default)] +#[derive(Clone, Debug, PartialEq, Eq, Default, MallocSizeOf)] pub struct SimpleList { validators: Vec
, } @@ -33,9 +39,13 @@ pub struct SimpleList { impl SimpleList { /// Create a new `SimpleList`. pub fn new(validators: Vec
) -> Self { - SimpleList { - validators: validators, + let validator_count = validators.len(); + if validator_count == 1 { + warn!(target: "engine", "Running AuRa with a single validator implies instant finality. Use a database?"); + } else if validator_count != 0 && validator_count % 2 == 0 { + warn!(target: "engine", "Running AuRa with an even number of validators ({}) is not recommended (risk of network split).", validator_count); } + SimpleList { validators } } /// Convert into inner representation. @@ -52,20 +62,12 @@ impl ::std::ops::Deref for SimpleList { impl From> for SimpleList { fn from(validators: Vec
) -> Self { - SimpleList { - validators: validators, - } - } -} - -impl HeapSizeOf for SimpleList { - fn heap_size_of_children(&self) -> usize { - self.validators.heap_size_of_children() + SimpleList::new(validators) } } impl ValidatorSet for SimpleList { - fn default_caller(&self, _block_id: ::types::ids::BlockId) -> Box { + fn default_caller(&self, _block_id: BlockId) -> Box { Box::new(|_, _| Err("Simple list doesn't require calls.".into())) } @@ -77,12 +79,12 @@ impl ValidatorSet for SimpleList { } fn signals_epoch_end(&self, _: bool, _: &Header, _: AuxiliaryData) - -> ::engines::EpochChange + -> engine::EpochChange { - ::engines::EpochChange::No + engine::EpochChange::No } - fn epoch_set(&self, _first: bool, _: &EthereumMachine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { + fn epoch_set(&self, _first: bool, _: &Machine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), EthcoreError> { Ok((self.clone(), None)) } @@ -105,8 +107,8 @@ impl ValidatorSet for SimpleList { } } -impl AsRef for SimpleList { - fn as_ref(&self) -> &ValidatorSet { +impl AsRef for SimpleList { + fn as_ref(&self) -> &dyn ValidatorSet { self } } diff --git a/ethcore/src/engines/validator_set/test.rs b/ethcore/engines/validator-set/src/test.rs similarity index 63% rename from ethcore/src/engines/validator_set/test.rs rename to ethcore/engines/validator-set/src/test.rs index c66ff14ad4..9544f1eaa8 100644 --- a/ethcore/src/engines/validator_set/test.rs +++ b/ethcore/engines/validator-set/src/test.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,58 +20,77 @@ use std::str::FromStr; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; -use bytes::Bytes; +use log::trace; +use parity_util_mem::MallocSizeOf; +use common_types::{ + BlockNumber, + ids::BlockId, + header::Header, + errors::EthcoreError, + engines::machine::{Call, AuxiliaryData}, +}; use ethereum_types::{H256, Address}; -use heapsize::HeapSizeOf; -use types::BlockNumber; -use types::header::Header; +use machine::Machine; +use parity_bytes::Bytes; -use machine::{AuxiliaryData, Call, EthereumMachine}; use super::{ValidatorSet, SimpleList}; /// Set used for testing with a single validator. +#[derive(Clone, MallocSizeOf, Debug)] pub struct TestSet { validator: SimpleList, + #[ignore_malloc_size_of = "zero sized"] last_malicious: Arc, + #[ignore_malloc_size_of = "zero sized"] last_benign: Arc, } impl Default for TestSet { fn default() -> Self { - TestSet::new(Default::default(), Default::default()) + TestSet::new( + Default::default(), + Default::default(), + vec![Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()] + ) } } impl TestSet { - pub fn new(last_malicious: Arc, last_benign: Arc) -> Self { + pub fn new(last_malicious: Arc, last_benign: Arc, validators: Vec
) -> Self { TestSet { - validator: SimpleList::new(vec![Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()]), + validator: SimpleList::new(validators), last_malicious, last_benign, } } -} -impl HeapSizeOf for TestSet { - fn heap_size_of_children(&self) -> usize { - self.validator.heap_size_of_children() + pub fn from_validators(validators: Vec
) -> Self { + TestSet::new(Default::default(), Default::default(), validators) + } + + pub fn last_malicious(&self) -> usize { + self.last_malicious.load(AtomicOrdering::SeqCst) + } + + pub fn last_benign(&self) -> usize { + self.last_benign.load(AtomicOrdering::SeqCst) } } impl ValidatorSet for TestSet { - fn default_caller(&self, _block_id: ::types::ids::BlockId) -> Box { + fn default_caller(&self, _block_id: BlockId) -> Box { Box::new(|_, _| Err("Test set doesn't require calls.".into())) } fn is_epoch_end(&self, _first: bool, _chain_head: &Header) -> Option> { None } fn signals_epoch_end(&self, _: bool, _: &Header, _: AuxiliaryData) - -> ::engines::EpochChange + -> engine::EpochChange { - ::engines::EpochChange::No + engine::EpochChange::No } - fn epoch_set(&self, _: bool, _: &EthereumMachine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { + fn epoch_set(&self, _: bool, _: &Machine, _: BlockNumber, _: &[u8]) -> Result<(SimpleList, Option), EthcoreError> { Ok((self.validator.clone(), None)) } @@ -92,6 +111,7 @@ impl ValidatorSet for TestSet { } fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, block: BlockNumber) { + trace!(target: "engine", "test validator set recording benign misbehaviour"); self.last_benign.store(block as usize, AtomicOrdering::SeqCst) } } diff --git a/ethcore/evm/Cargo.toml b/ethcore/evm/Cargo.toml index b5f4d0685f..31aeadd026 100644 --- a/ethcore/evm/Cargo.toml +++ b/ethcore/evm/Cargo.toml @@ -7,19 +7,18 @@ authors = ["Parity Technologies "] [dependencies] bit-set = "0.4" parity-bytes = "0.1" -ethereum-types = "0.4" -heapsize = "0.4" +ethereum-types = "0.8.0" +parity-util-mem = "0.3.0" lazy_static = "1.0" log = "0.4" vm = { path = "../vm" } -keccak-hash = "0.1" -parking_lot = "0.7" +keccak-hash = "0.4.0" +parking_lot = "0.9" memory-cache = { path = "../../util/memory-cache" } -num-bigint = "0.2" [dev-dependencies] rustc-hex = "1.0" -criterion = "0.2" +criterion = "0.3" hex-literal = "0.2.0" [features] diff --git a/ethcore/evm/benches/basic.rs b/ethcore/evm/benches/basic.rs index c86afcc575..ed3a5b22a0 100644 --- a/ethcore/evm/benches/basic.rs +++ b/ethcore/evm/benches/basic.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ extern crate criterion; extern crate bit_set; extern crate ethereum_types; extern crate parking_lot; -extern crate heapsize; +extern crate parity_util_mem as mem; extern crate vm; extern crate evm; extern crate keccak_hash as hash; @@ -32,6 +32,7 @@ extern crate rustc_hex; use criterion::{Criterion, Bencher, black_box}; use std::str::FromStr; use std::sync::Arc; +use bytes::Bytes; use ethereum_types::{U256, Address}; use vm::{ActionParams, Result, GasLeft, Ext}; use vm::tests::FakeExt; @@ -40,6 +41,28 @@ use rustc_hex::FromHex; criterion_group!( basic, + mul500, + mul1000, + div500, + div1000, + sdiv500, + sdiv1000, + mod500, + mod1000, + smod500, + smod1000, + addmod500, + addmod1000, + mulmod500, + mulmod1000, + mulmod1_500, + mulmod1_1000, + mulmod5_500, + mulmod5_1000, + mulmod11_500, + mulmod11_1000, + mulmod_big_500, + mulmod_big_1000, simple_loop_log0_usize, simple_loop_log0_u256, mem_gas_calculation_same_usize, @@ -207,3 +230,188 @@ fn result(r: Result) -> U256 { _ => U256::zero(), } } + +/// Runs a given EVM bytecode. +fn run_code(b: &mut Bencher, code: Bytes) { + let factory = Factory::default(); + let mut ext = FakeExt::new(); + b.iter(|| { + let mut params = ActionParams::default(); + params.address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + params.gas = U256::MAX; + params.code = Some(Arc::new(black_box(code.clone()))); + let vm = factory.create(params, ext.schedule(), 0); + result(vm.exec(&mut ext).ok().unwrap()) + }); +} + +/// Compute mulmod(U256::MAX, U256::MAX, 1) 500 times. +fn mulmod1_500(b: &mut Criterion) { + b.bench_function("mulmod modulo 1, 500 times", |b| { + run_code(b, "6101f45b6001900360017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + }); +} + +/// Compute mulmod(U256::MAX, U256::MAX, 1) 1000 times. +fn mulmod1_1000(b: &mut Criterion) { + b.bench_function("mulmod modulo 1, 1000 times", |b| { + run_code(b, "6103e85b6001900360017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + }); +} + +/// Compute mulmod(U256::MAX, U256::MAX, 5) 500 times. +fn mulmod5_500(b: &mut Criterion) { + b.bench_function("mulmod modulo 5, 500 times", |b| { + run_code(b, "6101f45b6001900360057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + }); +} + +/// Compute mulmod(U256::MAX, U256::MAX, 5) 1000 times. +fn mulmod5_1000(b: &mut Criterion) { + b.bench_function("mulmod modulo 5, 1000 times", |b| { + run_code(b, "6103e85b6001900360057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + }); +} + +/// Compute mulmod(U256::MAX, U256::MAX, 11) 500 times. +fn mulmod11_500(b: &mut Criterion) { + b.bench_function("mulmod modulo 11, 500 times", |b| { + run_code(b, "6101f45b60019003600b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + }); +} + +/// Compute mulmod(U256::MAX, U256::MAX, 11) 1000 times. +fn mulmod11_1000(b: &mut Criterion) { + b.bench_function("mulmod modulo 11, 1000 times", |b| { + run_code(b, "6103e85b60019003600b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + }); +} + +/// Compute mulmod(U256::MAX, U256::MAX, 0x58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd591) 500 times. +fn mulmod_big_500(b: &mut Criterion) { + b.bench_function("mulmod modulo random 256-bit number, 500 times", |b| { + run_code(b, "6101f45b600190037f58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd5917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + }); +} + +/// Compute mulmod(U256::MAX, U256::MAX, 0x58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd591) 1000 times. +fn mulmod_big_1000(b: &mut Criterion) { + b.bench_function("mulmod modulo random 256-bit number, 1000 times", |b| { + run_code(b, "6103e85b600190037f58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd5917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + }); +} + +/// Compute mulmod(a, b, c) for random 256-bit a, b and c. Iterate 500 times. +/// +/// Source: +/// ``` +/// PUSH2 0x01F4 +/// JUMPDEST +/// PUSH1 0x01 +/// SWAP1 +/// SUB +/// PUSH32 0x5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a937 +/// PUSH32 0xb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db +/// PUSH32 0xcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca +/// MULMOD +/// POP +/// DUP1 +/// PUSH1 0x03 +/// JUMPI +/// ``` +fn mulmod500(b: &mut Criterion) { + b.bench_function("mulmod randomly generated ints, 500 times", |b| { + run_code(b, "6101f45b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca095080600357".from_hex().unwrap()); + }); +} + +/// Compute mulmod(a, b, c) for random 256-bit a, b and c. Iterate 1000 times. +fn mulmod1000(b: &mut Criterion) { + b.bench_function("mulmod randomly generated ints, 1000 times", |b| { + run_code(b, "6103e85b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca095080600357".from_hex().unwrap()); + }); +} + +/// Compute addmod(a, b, c) for random 256-bit a, b and c. Iterate 500 times. +fn addmod500(b: &mut Criterion) { + b.bench_function("addmod randomly generated ints, 500 times", |b| { + run_code(b, "6101f45b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca085080600357".from_hex().unwrap()); + }); +} + +/// Compute addmod(a, b, c) for random 256-bit a, b and c. Iterate 1000 times. +fn addmod1000(b: &mut Criterion) { + b.bench_function("addmod randomly generated ints, 1000 times", |b| { + run_code(b, "6103e85b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca085080600357".from_hex().unwrap()); + }); +} + +/// Compute mul(a, b) for random 256-bit a and b. Iterate 500 times. +fn mul500(b: &mut Criterion) { + b.bench_function("mul randomly generated ints, 500 times", |b| { + run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca025080600357".from_hex().unwrap()); + }); +} + +/// Compute mul(a, b) for random 256-bit a and b. Iterate 1000 times. +fn mul1000(b: &mut Criterion) { + b.bench_function("mul randomly generated ints, 1000 times", |b| { + run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca025080600357".from_hex().unwrap()); + }); +} + +/// Compute div(a, b) for random 256-bit a and b. Iterate 500 times. +fn div500(b: &mut Criterion) { + b.bench_function("div randomly generated ints, 500 times", |b| { + run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca045080600357".from_hex().unwrap()); + }); +} + +/// Compute div(a, b) for random 256-bit a and b. Iterate 1000 times. +fn div1000(b: &mut Criterion) { + b.bench_function("div randomly generated ints, 1000 times", |b| { + run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca045080600357".from_hex().unwrap()); + }); +} + +/// Compute sdiv(a, b) for random 256-bit a and b. Iterate 500 times. +fn sdiv500(b: &mut Criterion) { + b.bench_function("sdiv randomly generated ints, 500 times", |b| { + run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca055080600357".from_hex().unwrap()); + }); +} + +/// Compute sdiv(a, b) for random 256-bit a and b. Iterate 1000 times. +fn sdiv1000(b: &mut Criterion) { + b.bench_function("sdiv randomly generated ints, 1000 times", |b| { + run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca055080600357".from_hex().unwrap()); + }); +} + +/// Compute mod(a, b) for random 256-bit a and b. Iterate 500 times. +fn mod500(b: &mut Criterion) { + b.bench_function("mod randomly generated ints, 500 times", |b| { + run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca065080600357".from_hex().unwrap()); + }); +} + +/// Compute mod(a, b) for random 256-bit a and b. Iterate 1000 times. +fn mod1000(b: &mut Criterion) { + b.bench_function("mod randomly generated ints, 1000 times", |b| { + run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca065080600357".from_hex().unwrap()); + }); +} + +/// Compute smod(a, b) for random 256-bit a and b. Iterate 500 times. +fn smod500(b: &mut Criterion) { + b.bench_function("smod randomly generated ints, 500 times", |b| { + run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca075080600357".from_hex().unwrap()); + }); +} + +/// Compute smod(a, b) for random 256-bit a and b. Iterate 1000 times. +fn smod1000(b: &mut Criterion) { + b.bench_function("smod randomly generated ints, 1000 times", |b| { + run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca075080600357".from_hex().unwrap()); + }); +} diff --git a/ethcore/evm/src/evm.rs b/ethcore/evm/src/evm.rs index 3c88155f2f..e219aa8381 100644 --- a/ethcore/evm/src/evm.rs +++ b/ethcore/evm/src/evm.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/evm/src/factory.rs b/ethcore/evm/src/factory.rs index 5dbaf4f829..e1a95ca9a8 100644 --- a/ethcore/evm/src/factory.rs +++ b/ethcore/evm/src/factory.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,33 +21,28 @@ use vm::{Exec, Schedule}; use ethereum_types::U256; use super::vm::ActionParams; use super::interpreter::SharedCache; -use super::vmtype::VMType; /// Evm factory. Creates appropriate Evm. #[derive(Clone)] pub struct Factory { - evm: VMType, evm_cache: Arc, } impl Factory { /// Create fresh instance of VM /// Might choose implementation depending on supplied gas. - pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { - match self.evm { - VMType::Interpreter => if Self::can_fit_in_usize(¶ms.gas) { - Box::new(super::interpreter::Interpreter::::new(params, self.evm_cache.clone(), schedule, depth)) - } else { - Box::new(super::interpreter::Interpreter::::new(params, self.evm_cache.clone(), schedule, depth)) - } + pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { + if Self::can_fit_in_usize(¶ms.gas) { + Box::new(super::interpreter::Interpreter::::new(params, self.evm_cache.clone(), schedule, depth)) + } else { + Box::new(super::interpreter::Interpreter::::new(params, self.evm_cache.clone(), schedule, depth)) } } - /// Create new instance of specific `VMType` factory, with a size in bytes + /// Create new instance of a factory, with a size in bytes /// for caching jump destinations. - pub fn new(evm: VMType, cache_size: usize) -> Self { + pub fn new(cache_size: usize) -> Self { Factory { - evm, evm_cache: Arc::new(SharedCache::new(cache_size)), } } @@ -61,7 +56,6 @@ impl Default for Factory { /// Returns native rust evm factory fn default() -> Factory { Factory { - evm: VMType::Interpreter, evm_cache: Arc::new(SharedCache::default()), } } @@ -85,7 +79,7 @@ macro_rules! evm_test( ($name_test: ident: $name_int: ident) => { #[test] fn $name_int() { - $name_test(Factory::new(VMType::Interpreter, 1024 * 32)); + $name_test(Factory::new(1024 * 32)); } } ); @@ -98,7 +92,7 @@ macro_rules! evm_test_ignore( #[ignore] #[cfg(feature = "ignored-tests")] fn $name_int() { - $name_test(Factory::new(VMType::Interpreter, 1024 * 32)); + $name_test(Factory::new(1024 * 32)); } } ); diff --git a/ethcore/evm/src/instructions.rs b/ethcore/evm/src/instructions.rs index b0a66c159e..e8fef9e74b 100644 --- a/ethcore/evm/src/instructions.rs +++ b/ethcore/evm/src/instructions.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/evm/src/interpreter/gasometer.rs b/ethcore/evm/src/interpreter/gasometer.rs index b90540d9e9..2bf65d4a43 100644 --- a/ethcore/evm/src/interpreter/gasometer.rs +++ b/ethcore/evm/src/interpreter/gasometer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . use std::cmp; -use ethereum_types::{U256, H256}; +use ethereum_types::{BigEndianHash, U256}; use super::u256_to_address; use {evm, vm}; @@ -106,10 +106,10 @@ impl Gasometer { /// it will be the amount of gas that the current context provides to the child context. pub fn requirements( &mut self, - ext: &vm::Ext, + ext: &dyn vm::Ext, instruction: Instruction, info: &InstructionInfo, - stack: &Stack, + stack: &dyn Stack, current_mem_size: usize, ) -> vm::Result> { let schedule = ext.schedule(); @@ -124,12 +124,13 @@ impl Gasometer { if schedule.eip1706 && self.current_gas <= Gas::from(schedule.call_stipend) { return Err(vm::Error::OutOfGas); } - let address = H256::from(stack.peek(0)); + + let address = BigEndianHash::from_uint(stack.peek(0)); let newval = stack.peek(1); - let val = U256::from(&*ext.storage_at(&address)?); + let val = ext.storage_at(&address)?.into_uint(); let gas = if schedule.eip1283 { - let orig = U256::from(&*ext.initial_storage_at(&address)?); + let orig = ext.initial_storage_at(&address)?.into_uint(); calculate_eip1283_sstore_gas(schedule, &orig, &val, &newval) } else { if val.is_zero() && !newval.is_zero() { @@ -374,65 +375,79 @@ fn to_word_size(value: Gas) -> (Gas, bool) { fn calculate_eip1283_sstore_gas(schedule: &Schedule, original: &U256, current: &U256, new: &U256) -> Gas { Gas::from( if current == new { - // 1. If current value equals new value (this is a no-op), 200 gas is deducted. - schedule.sload_gas + // 1. If current value equals new value (this is a no-op), `SSTORE_DIRTY_GAS` + // (or if not set, `SLOAD_GAS`) is deducted. + schedule.sstore_dirty_gas.unwrap_or(schedule.sload_gas) } else { // 2. If current value does not equal new value if original == current { - // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context) + // 2.1. If original value equals current value (this storage slot has not + // been changed by the current execution context) if original.is_zero() { - // 2.1.1. If original value is 0, 20000 gas is deducted. + // 2.1.1. If original value is 0, `SSTORE_SET_GAS` is deducted. schedule.sstore_set_gas } else { - // 2.1.2. Otherwise, 5000 gas is deducted. + // 2.1.2. Otherwise, `SSTORE_RESET_GAS` gas is deducted. schedule.sstore_reset_gas - // 2.1.2.1. If new value is 0, add 15000 gas to refund counter. + // 2.1.2.1. If new value is 0, add `SSTORE_CLEARS_SCHEDULE` to refund counter. } } else { - // 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. - schedule.sload_gas + // 2.2. If original value does not equal current value (this storage slot is + // dirty), `SSTORE_DIRTY_GAS` (or if not set, `SLOAD_GAS`) is deducted. + // Apply both of the following clauses. + schedule.sstore_dirty_gas.unwrap_or(schedule.sload_gas) // 2.2.1. If original value is not 0 - // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. - // 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. + // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove + // `SSTORE_SET_GAS - SSTORE_DIRTY_GAS` from refund counter. + // 2.2.1.2. If new value is 0 (also means that current value is not 0), add + // `SSTORE_CLEARS_SCHEDULE` to refund counter. // 2.2.2. If original value equals new value (this storage slot is reset) - // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. - // 2.2.2.2. Otherwise, add 4800 gas to refund counter. + // 2.2.2.1. If original value is 0, add `SSTORE_SET_GAS - SSTORE_DIRTY_GAS` + // to refund counter. + // 2.2.2.2. Otherwise, add `SSTORE_RESET_GAS - SSTORE_DIRTY_GAS` + // to refund counter. } } ) } -pub fn handle_eip1283_sstore_clears_refund(ext: &mut vm::Ext, original: &U256, current: &U256, new: &U256) { +pub fn handle_eip1283_sstore_clears_refund(ext: &mut dyn vm::Ext, original: &U256, current: &U256, new: &U256) { let sstore_clears_schedule = ext.schedule().sstore_refund_gas; if current == new { - // 1. If current value equals new value (this is a no-op), 200 gas is deducted. + // 1. If current value equals new value (this is a no-op), `SSTORE_DIRTY_GAS` + // (or if not set, `SLOAD_GAS`) is deducted. } else { // 2. If current value does not equal new value if original == current { - // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context) + // 2.1. If original value equals current value (this storage slot has not + // been changed by the current execution context) if original.is_zero() { - // 2.1.1. If original value is 0, 20000 gas is deducted. + // 2.1.1. If original value is 0, `SSTORE_SET_GAS` is deducted. } else { - // 2.1.2. Otherwise, 5000 gas is deducted. + // 2.1.2. Otherwise, `SSTORE_RESET_GAS` gas is deducted. if new.is_zero() { - // 2.1.2.1. If new value is 0, add 15000 gas to refund counter. + // 2.1.2.1. If new value is 0, add `SSTORE_CLEARS_SCHEDULE` to refund counter. ext.add_sstore_refund(sstore_clears_schedule); } } } else { - // 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses. + // 2.2. If original value does not equal current value (this storage slot is + // dirty), `SSTORE_DIRTY_GAS` (or if not set, `SLOAD_GAS`) is deducted. + // Apply both of the following clauses. if !original.is_zero() { // 2.2.1. If original value is not 0 if current.is_zero() { - // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. + // 2.2.1.1. If current value is 0 (also means that new value is not 0), remove + // `SSTORE_SET_GAS - SSTORE_DIRTY_GAS` from refund counter. ext.sub_sstore_refund(sstore_clears_schedule); } else if new.is_zero() { - // 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. + // 2.2.1.2. If new value is 0 (also means that current value is not 0), add + // `SSTORE_CLEARS_SCHEDULE` to refund counter. ext.add_sstore_refund(sstore_clears_schedule); } } @@ -440,12 +455,16 @@ pub fn handle_eip1283_sstore_clears_refund(ext: &mut vm::Ext, original: &U256, c if original == new { // 2.2.2. If original value equals new value (this storage slot is reset) if original.is_zero() { - // 2.2.2.1. If original value is 0, add 19800 gas to refund counter. - let refund = ext.schedule().sstore_set_gas - ext.schedule().sload_gas; + // 2.2.2.1. If original value is 0, add `SSTORE_SET_GAS - SSTORE_DIRTY_GAS` + // to refund counter. + let refund = ext.schedule().sstore_set_gas + - ext.schedule().sstore_dirty_gas.unwrap_or(ext.schedule().sload_gas); ext.add_sstore_refund(refund); } else { - // 2.2.2.2. Otherwise, add 4800 gas to refund counter. - let refund = ext.schedule().sstore_reset_gas - ext.schedule().sload_gas; + // 2.2.2.2. Otherwise, add `SSTORE_RESET_GAS - SSTORE_DIRTY_GAS` + // to refund counter. + let refund = ext.schedule().sstore_reset_gas + - ext.schedule().sstore_dirty_gas.unwrap_or(ext.schedule().sload_gas); ext.add_sstore_refund(refund); } } diff --git a/ethcore/evm/src/interpreter/informant.rs b/ethcore/evm/src/interpreter/informant.rs index 93d459f417..216bb54005 100644 --- a/ethcore/evm/src/interpreter/informant.rs +++ b/ethcore/evm/src/interpreter/informant.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/evm/src/interpreter/memory.rs b/ethcore/evm/src/interpreter/memory.rs index 16c575d5e5..ff05a495bc 100644 --- a/ethcore/evm/src/interpreter/memory.rs +++ b/ethcore/evm/src/interpreter/memory.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -136,7 +136,7 @@ mod tests { #[test] fn test_memory_read_and_write() { // given - let mem: &mut Memory = &mut vec![]; + let mem: &mut dyn Memory = &mut vec![]; mem.resize(0x80 + 32); // when @@ -149,7 +149,7 @@ mod tests { #[test] fn test_memory_read_and_write_byte() { // given - let mem: &mut Memory = &mut vec![]; + let mem: &mut dyn Memory = &mut vec![]; mem.resize(32); // when @@ -163,7 +163,7 @@ mod tests { #[test] fn test_memory_read_slice_and_write_slice() { - let mem: &mut Memory = &mut vec![]; + let mem: &mut dyn Memory = &mut vec![]; mem.resize(32); { diff --git a/ethcore/evm/src/interpreter/mod.rs b/ethcore/evm/src/interpreter/mod.rs index 21e7b463b2..799566eebd 100644 --- a/ethcore/evm/src/interpreter/mod.rs +++ b/ethcore/evm/src/interpreter/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -26,13 +26,14 @@ mod shared_cache; use std::marker::PhantomData; use std::{cmp, mem}; use std::sync::Arc; +use std::convert::TryFrom; use hash::keccak; use bytes::Bytes; -use ethereum_types::{U256, H256, Address}; -use num_bigint::BigUint; +use ethereum_types::{U256, U512, H256, Address, BigEndianHash}; + use vm::{ - self, ActionParams, ParamsType, ActionValue, CallType, MessageCallResult, + self, ActionParams, ParamsType, ActionValue, ActionType, MessageCallResult, ContractCreateResult, CreateContractAddress, ReturnData, GasLeft, Schedule, TrapKind, TrapError }; @@ -62,17 +63,6 @@ const TWO_POW_96: U256 = U256([0, 0x100000000, 0, 0]); //0x1 00000000 00000000 0 const TWO_POW_224: U256 = U256([0, 0, 0, 0x100000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 const TWO_POW_248: U256 = U256([0, 0, 0, 0x100000000000000]); //0x1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000 -fn to_biguint(x: U256) -> BigUint { - let mut bytes = [0u8; 32]; - x.to_little_endian(&mut bytes); - BigUint::from_bytes_le(&bytes) -} - -fn from_biguint(x: BigUint) -> U256 { - let bytes = x.to_bytes_le(); - U256::from_little_endian(&bytes) -} - /// Abstraction over raw vector of Bytes. Easier state management of PC. struct CodeReader { position: ProgramCounter, @@ -126,6 +116,8 @@ struct InterpreterParams { pub code_address: Address, /// Hash of currently executed code. pub code_hash: Option, + /// Code version. + pub code_version: U256, /// Receive address. Usually equal to code_address, /// except when called using CALLCODE. pub address: Address, @@ -141,8 +133,8 @@ struct InterpreterParams { pub value: ActionValue, /// Input data. pub data: Option, - /// Type of call - pub call_type: CallType, + /// Type of action + pub action_type: ActionType, /// Param types encoding pub params_type: ParamsType, } @@ -152,6 +144,7 @@ impl From for InterpreterParams { InterpreterParams { code_address: params.code_address, code_hash: params.code_hash, + code_version: params.code_version, address: params.address, sender: params.sender, origin: params.origin, @@ -159,7 +152,7 @@ impl From for InterpreterParams { gas_price: params.gas_price, value: params.value, data: params.data, - call_type: params.call_type, + action_type: params.action_type, params_type: params.params_type, } } @@ -196,7 +189,7 @@ pub struct Interpreter { } impl vm::Exec for Interpreter { - fn exec(mut self: Box, ext: &mut vm::Ext) -> vm::ExecTrapResult { + fn exec(mut self: Box, ext: &mut dyn vm::Ext) -> vm::ExecTrapResult { loop { let result = self.step(ext); match result { @@ -217,7 +210,7 @@ impl vm::Exec for Interpreter { } impl vm::ResumeCall for Interpreter { - fn resume_call(mut self: Box, result: MessageCallResult) -> Box { + fn resume_call(mut self: Box, result: MessageCallResult) -> Box { { let this = &mut *self; let (out_off, out_size) = this.resume_output_range.take().expect("Box is obtained from a call opcode; resume_output_range is always set after those opcodes are executed; qed"); @@ -252,7 +245,7 @@ impl vm::ResumeCall for Interpreter { } impl vm::ResumeCreate for Interpreter { - fn resume_create(mut self: Box, result: ContractCreateResult) -> Box { + fn resume_create(mut self: Box, result: ContractCreateResult) -> Box { match result { ContractCreateResult::Created(address, gas_left) => { self.stack.push(address_to_u256(address)); @@ -300,7 +293,7 @@ impl Interpreter { /// Execute a single step on the VM. #[inline(always)] - pub fn step(&mut self, ext: &mut vm::Ext) -> InterpreterResult { + pub fn step(&mut self, ext: &mut dyn vm::Ext) -> InterpreterResult { if self.done { return InterpreterResult::Stopped; } @@ -441,7 +434,7 @@ impl Interpreter { InterpreterResult::Continue } - fn verify_instruction(&self, ext: &vm::Ext, instruction: Instruction, info: &InstructionInfo) -> vm::Result<()> { + fn verify_instruction(&self, ext: &dyn vm::Ext, instruction: Instruction, info: &InstructionInfo) -> vm::Result<()> { let schedule = ext.schedule(); if (instruction == instructions::DELEGATECALL && !schedule.have_delegate_call) || @@ -478,7 +471,7 @@ impl Interpreter { fn mem_written( instruction: Instruction, - stack: &Stack + stack: &dyn Stack ) -> Option<(usize, usize)> { let read = |pos| stack.peek(pos).low_u64() as usize; let written = match instruction { @@ -499,7 +492,7 @@ impl Interpreter { fn store_written( instruction: Instruction, - stack: &Stack + stack: &dyn Stack ) -> Option<(U256, U256)> { match instruction { instructions::SSTORE => Some((stack.peek(0).clone(), stack.peek(1).clone())), @@ -510,7 +503,7 @@ impl Interpreter { fn exec_instruction( &mut self, gas: Cost, - ext: &mut vm::Ext, + ext: &mut dyn vm::Ext, instruction: Instruction, provided: Option ) -> vm::Result> { @@ -539,7 +532,9 @@ impl Interpreter { let init_size = self.stack.pop_back(); let address_scheme = match instruction { instructions::CREATE => CreateContractAddress::FromSenderAndNonce, - instructions::CREATE2 => CreateContractAddress::FromSenderSaltAndCodeHash(self.stack.pop_back().into()), + instructions::CREATE2 => CreateContractAddress::FromSenderSaltAndCodeHash( + BigEndianHash::from_uint(&self.stack.pop_back()) + ), _ => unreachable!("instruction can only be CREATE/CREATE2 checked above; qed"), }; @@ -560,7 +555,14 @@ impl Interpreter { let contract_code = self.mem.read_slice(init_off, init_size); - let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme, true); + let create_result = ext.create( + &create_gas.as_u256(), + &endowment, + contract_code, + &self.params.code_version, + address_scheme, + true, + ); return match create_result { Ok(ContractCreateResult::Created(address, gas_left)) => { self.stack.push(address_to_u256(address)); @@ -614,14 +616,14 @@ impl Interpreter { return Err(vm::Error::MutableCallInStaticContext); } let has_balance = ext.balance(&self.params.address)? >= value.expect("value set for all but delegate call; qed"); - (&self.params.address, &code_address, has_balance, CallType::Call) + (&self.params.address, &code_address, has_balance, ActionType::Call) }, instructions::CALLCODE => { let has_balance = ext.balance(&self.params.address)? >= value.expect("value set for all but delegate call; qed"); - (&self.params.address, &self.params.address, has_balance, CallType::CallCode) + (&self.params.address, &self.params.address, has_balance, ActionType::CallCode) }, - instructions::DELEGATECALL => (&self.params.sender, &self.params.address, true, CallType::DelegateCall), - instructions::STATICCALL => (&self.params.address, &code_address, true, CallType::StaticCall), + instructions::DELEGATECALL => (&self.params.sender, &self.params.address, true, ActionType::DelegateCall), + instructions::STATICCALL => (&self.params.address, &code_address, true, ActionType::StaticCall), _ => panic!(format!("Unexpected instruction {:?} in CALL branch.", instruction)) }; @@ -696,7 +698,7 @@ impl Interpreter { let size = self.stack.pop_back(); let topics = self.stack.pop_n(no_of_topics) .iter() - .map(H256::from) + .map(BigEndianHash::from_uint) .collect(); ext.log(topics, self.mem.read_slice(offset, size))?; }, @@ -733,21 +735,21 @@ impl Interpreter { let offset = self.stack.pop_back(); let size = self.stack.pop_back(); let k = keccak(self.mem.read_slice(offset, size)); - self.stack.push(U256::from(&*k)); + self.stack.push(k.into_uint()); }, instructions::SLOAD => { - let key = H256::from(&self.stack.pop_back()); - let word = U256::from(&*ext.storage_at(&key)?); + let key = BigEndianHash::from_uint(&self.stack.pop_back()); + let word = ext.storage_at(&key)?.into_uint(); self.stack.push(word); }, instructions::SSTORE => { - let address = H256::from(&self.stack.pop_back()); + let address = BigEndianHash::from_uint(&self.stack.pop_back()); let val = self.stack.pop_back(); - let current_val = U256::from(&*ext.storage_at(&address)?); + let current_val = ext.storage_at(&address)?.into_uint(); // Increase refund for clear if ext.schedule().eip1283 { - let original_val = U256::from(&*ext.initial_storage_at(&address)?); + let original_val = ext.initial_storage_at(&address)?.into_uint(); gasometer::handle_eip1283_sstore_clears_refund(ext, &original_val, ¤t_val, &val); } else { if !current_val.is_zero() && val.is_zero() { @@ -755,7 +757,7 @@ impl Interpreter { ext.add_sstore_refund(sstore_clears_schedule); } } - ext.set_storage(address, H256::from(&val))?; + ext.set_storage(address, BigEndianHash::from_uint(&val))?; }, instructions::PC => { self.stack.push(U256::from(self.reader.position - 1)); @@ -816,7 +818,7 @@ impl Interpreter { instructions::EXTCODEHASH => { let address = u256_to_address(&self.stack.pop_back()); let hash = ext.extcodehash(&address)?.unwrap_or_else(H256::zero); - self.stack.push(U256::from(hash)); + self.stack.push(hash.into_uint()); }, instructions::CALLDATACOPY => { Self::copy_data_to_memory(&mut self.mem, &mut self.stack, &self.params.data.as_ref().map_or_else(|| &[] as &[u8], |d| &*d as &[u8])); @@ -850,7 +852,7 @@ impl Interpreter { instructions::BLOCKHASH => { let block_number = self.stack.pop_back(); let block_hash = ext.blockhash(&block_number); - self.stack.push(U256::from(&*block_hash)); + self.stack.push(block_hash.into_uint()); }, instructions::COINBASE => { self.stack.push(address_to_u256(ext.env_info().author.clone())); @@ -1046,12 +1048,12 @@ impl Interpreter { let c = self.stack.pop_back(); self.stack.push(if !c.is_zero() { - let a_num = to_biguint(a); - let b_num = to_biguint(b); - let c_num = to_biguint(c); - let res = a_num + b_num; - let x = res % c_num; - from_biguint(x) + let a_512 = U512::from(a); + let b_512 = U512::from(b); + let c_512 = U512::from(c); + let res = a_512 + b_512; + let x = res % c_512; + U256::try_from(x).expect("U512 % U256 fits U256; qed") } else { U256::zero() }); @@ -1062,12 +1064,12 @@ impl Interpreter { let c = self.stack.pop_back(); self.stack.push(if !c.is_zero() { - let a_num = to_biguint(a); - let b_num = to_biguint(b); - let c_num = to_biguint(c); - let res = a_num * b_num; - let x = res % c_num; - from_biguint(x) + let a_512 = U512::from(a); + let b_512 = U512::from(b); + let c_512 = U512::from(c); + let res = a_512 * b_512; + let x = res % c_512; + U256::try_from(x).expect("U512 % U256 fits U256; qed") } else { U256::zero() }); @@ -1143,7 +1145,7 @@ impl Interpreter { Ok(InstructionResult::Ok) } - fn copy_data_to_memory(mem: &mut Vec, stack: &mut Stack, source: &[u8]) { + fn copy_data_to_memory(mem: &mut Vec, stack: &mut dyn Stack, source: &[u8]) { let dest_offset = stack.pop_back(); let source_offset = stack.pop_back(); let size = stack.pop_back(); @@ -1207,25 +1209,26 @@ fn set_sign(value: U256, sign: bool) -> U256 { #[inline] fn u256_to_address(value: &U256) -> Address { - Address::from(H256::from(value)) + let addr: H256 = BigEndianHash::from_uint(value); + Address::from(addr) } #[inline] fn address_to_u256(value: Address) -> U256 { - U256::from(&*H256::from(value)) + H256::from(value).into_uint() } #[cfg(test)] mod tests { use std::sync::Arc; use rustc_hex::FromHex; - use vmtype::VMType; use factory::Factory; use vm::{self, Exec, ActionParams, ActionValue}; use vm::tests::{FakeExt, test_finalize}; + use ethereum_types::Address; - fn interpreter(params: ActionParams, ext: &vm::Ext) -> Box { - Factory::new(VMType::Interpreter, 1).create(params, ext.schedule(), ext.depth()) + fn interpreter(params: ActionParams, ext: &dyn vm::Ext) -> Box { + Factory::new(1).create(params, ext.schedule(), ext.depth()) } #[test] @@ -1233,17 +1236,17 @@ mod tests { let code = "7feeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa6020526000620f120660406000601773945304eb96065b2a98b57a48a06ae28d285a71b56101f4f1600055".from_hex().unwrap(); let mut params = ActionParams::default(); - params.address = 5.into(); + params.address = Address::from_low_u64_be(5); params.gas = 300_000.into(); params.gas_price = 1.into(); params.value = ActionValue::Transfer(100_000.into()); params.code = Some(Arc::new(code)); let mut ext = FakeExt::new(); - ext.balances.insert(5.into(), 1_000_000_000.into()); + ext.balances.insert(Address::from_low_u64_be(5), 1_000_000_000.into()); ext.tracing = true; let gas_left = { - let mut vm = interpreter(params, &ext); + let vm = interpreter(params, &ext); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -1256,16 +1259,16 @@ mod tests { let code = "6001600160000360003e00".from_hex().unwrap(); let mut params = ActionParams::default(); - params.address = 5.into(); + params.address = Address::from_low_u64_be(5); params.gas = 300_000.into(); params.gas_price = 1.into(); params.code = Some(Arc::new(code)); let mut ext = FakeExt::new_byzantium(); - ext.balances.insert(5.into(), 1_000_000_000.into()); + ext.balances.insert(Address::from_low_u64_be(5), 1_000_000_000.into()); ext.tracing = true; let err = { - let mut vm = interpreter(params, &ext); + let vm = interpreter(params, &ext); test_finalize(vm.exec(&mut ext).ok().unwrap()).err().unwrap() }; diff --git a/ethcore/evm/src/interpreter/shared_cache.rs b/ethcore/evm/src/interpreter/shared_cache.rs index da7c03efa0..7e6f2c3b57 100644 --- a/ethcore/evm/src/interpreter/shared_cache.rs +++ b/ethcore/evm/src/interpreter/shared_cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ use std::sync::Arc; use hash::KECCAK_EMPTY; -use heapsize::HeapSizeOf; +use parity_util_mem::{MallocSizeOf, MallocSizeOfOps}; use ethereum_types::H256; use parking_lot::Mutex; use memory_cache::MemoryLruCache; @@ -25,11 +25,12 @@ use super::super::instructions::{self, Instruction}; const DEFAULT_CACHE_SIZE: usize = 4 * 1024 * 1024; -// stub for a HeapSizeOf implementation. +/// Stub for a sharing `BitSet` data in cache (reference counted) +/// and implementing MallocSizeOf on it. struct Bits(Arc); -impl HeapSizeOf for Bits { - fn heap_size_of_children(&self) -> usize { +impl MallocSizeOf for Bits { + fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { // dealing in bits here self.0.capacity() * 8 } diff --git a/ethcore/evm/src/interpreter/stack.rs b/ethcore/evm/src/interpreter/stack.rs index 87e14bdadb..bec47576de 100644 --- a/ethcore/evm/src/interpreter/stack.rs +++ b/ethcore/evm/src/interpreter/stack.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/evm/src/lib.rs b/ethcore/evm/src/lib.rs index 6e9409375b..2a1fd39494 100644 --- a/ethcore/evm/src/lib.rs +++ b/ethcore/evm/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,12 +19,11 @@ extern crate bit_set; extern crate ethereum_types; extern crate parking_lot; -extern crate heapsize; +extern crate parity_util_mem; extern crate vm; extern crate keccak_hash as hash; extern crate memory_cache; extern crate parity_bytes as bytes; -extern crate num_bigint; #[macro_use] extern crate lazy_static; @@ -42,18 +41,16 @@ pub mod interpreter; #[macro_use] pub mod factory; -mod vmtype; mod instructions; #[cfg(test)] mod tests; pub use vm::{ - Schedule, CleanDustMode, EnvInfo, CallType, ActionParams, Ext, + Schedule, CleanDustMode, EnvInfo, ActionType, ActionParams, Ext, ContractCreateResult, MessageCallResult, CreateContractAddress, GasLeft, ReturnData }; pub use self::evm::{Finalize, FinalizationResult, CostType}; pub use self::instructions::{InstructionInfo, Instruction}; -pub use self::vmtype::VMType; pub use self::factory::Factory; diff --git a/ethcore/evm/src/tests.rs b/ethcore/evm/src/tests.rs index 73a176ee09..dce9deda94 100644 --- a/ethcore/evm/src/tests.rs +++ b/ethcore/evm/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,18 +19,21 @@ use std::str::FromStr; use std::hash::Hash; use std::sync::Arc; use std::collections::{HashMap, HashSet}; -use rustc_hex::FromHex; use ethereum_types::{U256, H256, Address}; use vm::{self, ActionParams, ActionValue, Ext}; use vm::tests::{FakeExt, FakeCall, FakeCallType, test_finalize}; use factory::Factory; -use vmtype::VMType; use hex_literal::hex; evm_test!{test_add: test_add_int} fn test_add(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055".from_hex().unwrap(); + // 7f PUSH32 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff + // 7f PUSH32 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff + // 01 ADD + // 60 00 PUSH 0 + // 55 SSTORE + let code = hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055").to_vec(); let mut params = ActionParams::default(); params.address = address.clone(); @@ -39,7 +42,7 @@ fn test_add(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -50,7 +53,12 @@ fn test_add(factory: super::Factory) { 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(); + // 60 00 PUSH 0 + // 60 00 PUSH 0 + // 20 SHA3 + // 60 00 PUSH 0 + // 55 SSTORE + let code = hex!("6000600020600055").to_vec(); let mut params = ActionParams::default(); params.address = address.clone(); @@ -59,7 +67,7 @@ fn test_sha3(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -70,7 +78,10 @@ fn test_sha3(factory: super::Factory) { 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(); + // 30 ADDRESS + // 60 00 PUSH 0 + // 55 SSTORE + let code = hex!("30600055").to_vec(); let mut params = ActionParams::default(); params.address = address.clone(); @@ -79,7 +90,7 @@ fn test_address(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -91,7 +102,10 @@ 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(); - let code = "32600055".from_hex().unwrap(); + // 32 ORIGIN + // 60 00 PUSH 0 + // 55 SSTORE + let code = hex!("32600055").to_vec(); let mut params = ActionParams::default(); params.address = address.clone(); @@ -101,7 +115,7 @@ fn test_origin(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -139,7 +153,10 @@ 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(); - let code = "33600055".from_hex().unwrap(); + // 33 CALLER + // 60 00 PUSH 0 + // 55 SSTORE + let code = hex!("33600055").to_vec(); let mut params = ActionParams::default(); params.address = address.clone(); @@ -149,7 +166,7 @@ fn test_sender(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -193,8 +210,8 @@ fn test_extcodecopy(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let sender = Address::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); - let code = "333b60006000333c600051600055".from_hex().unwrap(); - let sender_code = "6005600055".from_hex().unwrap(); + let code = hex!("333b60006000333c600051600055").to_vec(); + let sender_code = hex!("6005600055").to_vec(); let mut params = ActionParams::default(); params.address = address.clone(); @@ -205,7 +222,7 @@ fn test_extcodecopy(factory: super::Factory) { ext.codes.insert(sender, Arc::new(sender_code)); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -216,7 +233,7 @@ fn test_extcodecopy(factory: super::Factory) { 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(); + let code = hex!("60006000a0").to_vec(); let mut params = ActionParams::default(); params.address = address.clone(); @@ -225,7 +242,7 @@ fn test_log_empty(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -247,7 +264,7 @@ fn test_log_sender(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let code = "60ff6000533360206000a1".from_hex().unwrap(); + let code = hex!("60ff6000533360206000a1").to_vec(); let mut params = ActionParams::default(); params.address = address.clone(); @@ -257,7 +274,7 @@ fn test_log_sender(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -265,13 +282,13 @@ fn test_log_sender(factory: super::Factory) { assert_eq!(ext.logs.len(), 1); assert_eq!(ext.logs[0].topics.len(), 1); assert_eq!(ext.logs[0].topics[0], H256::from_str("000000000000000000000000cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()); - assert_eq!(ext.logs[0].data, "ff00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); + assert_eq!(ext.logs[0].data, hex!("ff00000000000000000000000000000000000000000000000000000000000000").to_vec()); } 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(); + let code = hex!("600040600055").to_vec(); let blockhash = H256::from_str("123400000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap(); let mut params = ActionParams::default(); @@ -282,19 +299,19 @@ fn test_blockhash(factory: super::Factory) { ext.blockhashes.insert(U256::zero(), blockhash.clone()); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; assert_eq!(gas_left, U256::from(79_974)); - assert_eq!(ext.store.get(&H256::new()).unwrap(), &blockhash); + assert_eq!(ext.store.get(&H256::zero()).unwrap(), &blockhash); } 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(); - let data = "0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23".from_hex().unwrap(); + let code = hex!("600135600055").to_vec(); + let data = hex!("0123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff23").to_vec(); let mut params = ActionParams::default(); params.address = address.clone(); @@ -304,7 +321,7 @@ fn test_calldataload(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -315,7 +332,7 @@ fn test_calldataload(factory: super::Factory) { 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(); + let code = hex!("41600055").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -324,7 +341,7 @@ fn test_author(factory: super::Factory) { ext.info.author = author; let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -335,7 +352,7 @@ fn test_author(factory: super::Factory) { evm_test!{test_timestamp: test_timestamp_int} fn test_timestamp(factory: super::Factory) { let timestamp = 0x1234; - let code = "42600055".from_hex().unwrap(); + let code = hex!("42600055").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -344,7 +361,7 @@ fn test_timestamp(factory: super::Factory) { ext.info.timestamp = timestamp; let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -355,7 +372,7 @@ fn test_timestamp(factory: super::Factory) { evm_test!{test_number: test_number_int} fn test_number(factory: super::Factory) { let number = 0x1234; - let code = "43600055".from_hex().unwrap(); + let code = hex!("43600055").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -364,7 +381,7 @@ fn test_number(factory: super::Factory) { ext.info.number = number; let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -375,7 +392,7 @@ fn test_number(factory: super::Factory) { evm_test!{test_difficulty: test_difficulty_int} fn test_difficulty(factory: super::Factory) { let difficulty = U256::from(0x1234); - let code = "44600055".from_hex().unwrap(); + let code = hex!("44600055").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -384,7 +401,7 @@ fn test_difficulty(factory: super::Factory) { ext.info.difficulty = difficulty; let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -395,7 +412,7 @@ fn test_difficulty(factory: super::Factory) { 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(); + let code = hex!("45600055").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -404,7 +421,7 @@ fn test_gas_limit(factory: super::Factory) { ext.info.gas_limit = gas_limit; let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -414,7 +431,7 @@ fn test_gas_limit(factory: super::Factory) { evm_test!{test_mul: test_mul_int} fn test_mul(factory: super::Factory) { - let code = "65012365124623626543219002600055".from_hex().unwrap(); + let code = hex!("65012365124623626543219002600055").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -422,7 +439,7 @@ fn test_mul(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -432,7 +449,7 @@ fn test_mul(factory: super::Factory) { evm_test!{test_sub: test_sub_int} fn test_sub(factory: super::Factory) { - let code = "65012365124623626543219003600055".from_hex().unwrap(); + let code = hex!("65012365124623626543219003600055").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -440,7 +457,7 @@ fn test_sub(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -450,7 +467,7 @@ fn test_sub(factory: super::Factory) { evm_test!{test_div: test_div_int} fn test_div(factory: super::Factory) { - let code = "65012365124623626543219004600055".from_hex().unwrap(); + let code = hex!("65012365124623626543219004600055").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -458,7 +475,7 @@ fn test_div(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -468,7 +485,7 @@ fn test_div(factory: super::Factory) { evm_test!{test_div_zero: test_div_zero_int} fn test_div_zero(factory: super::Factory) { - let code = "6501236512462360009004600055".from_hex().unwrap(); + let code = hex!("6501236512462360009004600055").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -476,7 +493,7 @@ fn test_div_zero(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -486,7 +503,7 @@ fn test_div_zero(factory: super::Factory) { evm_test!{test_mod: test_mod_int} fn test_mod(factory: super::Factory) { - let code = "650123651246236265432290066000556501236512462360009006600155".from_hex().unwrap(); + let code = hex!("650123651246236265432290066000556501236512462360009006600155").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -494,7 +511,7 @@ fn test_mod(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -505,7 +522,7 @@ fn test_mod(factory: super::Factory) { evm_test!{test_smod: test_smod_int} fn test_smod(factory: super::Factory) { - let code = "650123651246236265432290076000556501236512462360009007600155".from_hex().unwrap(); + let code = hex!("650123651246236265432290076000556501236512462360009007600155").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -513,7 +530,7 @@ fn test_smod(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -524,7 +541,7 @@ fn test_smod(factory: super::Factory) { evm_test!{test_sdiv: test_sdiv_int} fn test_sdiv(factory: super::Factory) { - let code = "650123651246236265432290056000556501236512462360009005600155".from_hex().unwrap(); + let code = hex!("650123651246236265432290056000556501236512462360009005600155").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -532,7 +549,7 @@ fn test_sdiv(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -543,7 +560,7 @@ fn test_sdiv(factory: super::Factory) { evm_test!{test_exp: test_exp_int} fn test_exp(factory: super::Factory) { - let code = "6016650123651246230a6000556001650123651246230a6001556000650123651246230a600255".from_hex().unwrap(); + let code = hex!("6016650123651246230a6000556001650123651246230a6001556000650123651246230a600255").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -551,7 +568,7 @@ fn test_exp(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -563,7 +580,7 @@ fn test_exp(factory: super::Factory) { evm_test!{test_comparison: test_comparison_int} fn test_comparison(factory: super::Factory) { - let code = "601665012365124623818181811060005511600155146002556415235412358014600355".from_hex().unwrap(); + let code = hex!("601665012365124623818181811060005511600155146002556415235412358014600355").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -571,7 +588,7 @@ fn test_comparison(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -584,7 +601,7 @@ fn test_comparison(factory: super::Factory) { evm_test!{test_signed_comparison: test_signed_comparison_int} fn test_signed_comparison(factory: super::Factory) { - let code = "60106000036010818112600055136001556010601060000381811260025513600355".from_hex().unwrap(); + let code = hex!("60106000036010818112600055136001556010601060000381811260025513600355").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -592,7 +609,7 @@ fn test_signed_comparison(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -605,7 +622,7 @@ fn test_signed_comparison(factory: super::Factory) { evm_test!{test_bitops: test_bitops_int} fn test_bitops(factory: super::Factory) { - let code = "60ff610ff08181818116600055176001551860025560008015600355198015600455600555".from_hex().unwrap(); + let code = hex!("60ff610ff08181818116600055176001551860025560008015600355198015600455600555").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(150_000); @@ -613,7 +630,7 @@ fn test_bitops(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -628,7 +645,7 @@ fn test_bitops(factory: super::Factory) { evm_test!{test_addmod_mulmod: test_addmod_mulmod_int} fn test_addmod_mulmod(factory: super::Factory) { - let code = "60ff60f060108282820860005509600155600060f0601082828208196002550919600355".from_hex().unwrap(); + let code = hex!("60ff60f060108282820860005509600155600060f0601082828208196002550919600355").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -636,7 +653,7 @@ fn test_addmod_mulmod(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -649,7 +666,7 @@ fn test_addmod_mulmod(factory: super::Factory) { evm_test!{test_byte: test_byte_int} fn test_byte(factory: super::Factory) { - let code = "60f061ffff1a600055610fff601f1a600155".from_hex().unwrap(); + let code = hex!("60f061ffff1a600055610fff601f1a600155").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -657,7 +674,7 @@ fn test_byte(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -668,7 +685,7 @@ fn test_byte(factory: super::Factory) { evm_test!{test_signextend: test_signextend_int} fn test_signextend(factory: super::Factory) { - let code = "610fff60020b60005560ff60200b600155".from_hex().unwrap(); + let code = hex!("610fff60020b60005560ff60200b600155").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -676,7 +693,7 @@ fn test_signextend(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -687,8 +704,8 @@ fn test_signextend(factory: super::Factory) { #[test] // JIT just returns out of gas fn test_badinstruction_int() { - let factory = super::Factory::new(VMType::Interpreter, 1024 * 32); - let code = "af".from_hex().unwrap(); + let factory = super::Factory::new(1024 * 32); + let code = hex!("af").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -696,7 +713,7 @@ fn test_badinstruction_int() { let mut ext = FakeExt::new(); let err = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap_err() }; @@ -708,7 +725,7 @@ fn test_badinstruction_int() { evm_test!{test_pop: test_pop_int} fn test_pop(factory: super::Factory) { - let code = "60f060aa50600055".from_hex().unwrap(); + let code = hex!("60f060aa50600055").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(100_000); @@ -716,7 +733,7 @@ fn test_pop(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -726,7 +743,7 @@ fn test_pop(factory: super::Factory) { evm_test!{test_extops: test_extops_int} fn test_extops(factory: super::Factory) { - let code = "5a6001555836553a600255386003553460045560016001526016590454600555".from_hex().unwrap(); + let code = hex!("5a6001555836553a600255386003553460045560016001526016590454600555").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(150_000); @@ -736,7 +753,7 @@ fn test_extops(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -751,7 +768,7 @@ fn test_extops(factory: super::Factory) { evm_test!{test_jumps: test_jumps_int} fn test_jumps(factory: super::Factory) { - let code = "600160015560066000555b60016000540380806000551560245760015402600155600a565b".from_hex().unwrap(); + let code = hex!("600160015560066000555b60016000540380806000551560245760015402600155600a565b").to_vec(); let mut params = ActionParams::default(); params.gas = U256::from(150_000); @@ -759,7 +776,7 @@ fn test_jumps(factory: super::Factory) { let mut ext = FakeExt::new(); let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -771,10 +788,10 @@ fn test_jumps(factory: super::Factory) { evm_test!{test_calls: test_calls_int} fn test_calls(factory: super::Factory) { - let code = "600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b".from_hex().unwrap(); + let code = hex!("600054602d57600160005560006000600060006050610998610100f160006000600060006050610998610100f25b").to_vec(); - let address = Address::from(0x155); - let code_address = Address::from(0x998); + let address = Address::from_low_u64_be(0x155); + let code_address = Address::from_low_u64_be(0x998); let mut params = ActionParams::default(); params.gas = U256::from(150_000); params.code = Some(Arc::new(code)); @@ -787,7 +804,7 @@ fn test_calls(factory: super::Factory) { }; let gas_left = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -817,9 +834,9 @@ fn test_calls(factory: super::Factory) { evm_test!{test_create_in_staticcall: test_create_in_staticcall_int} fn test_create_in_staticcall(factory: super::Factory) { - let code = "600060006064f000".from_hex().unwrap(); + let code = hex!("600060006064f000").to_vec(); - let address = Address::from(0x155); + let address = Address::from_low_u64_be(0x155); let mut params = ActionParams::default(); params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); @@ -828,7 +845,7 @@ fn test_create_in_staticcall(factory: super::Factory) { ext.is_static = true; let err = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap_err() }; @@ -841,68 +858,68 @@ fn test_shl(factory: super::Factory) { push_two_pop_one_constantinople_test( &factory, 0x1b, - "0000000000000000000000000000000000000000000000000000000000000001", - "00", + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), + hex!("00").to_vec(), "0000000000000000000000000000000000000000000000000000000000000001"); push_two_pop_one_constantinople_test( &factory, 0x1b, - "0000000000000000000000000000000000000000000000000000000000000001", - "01", + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), + hex!("01").to_vec(), "0000000000000000000000000000000000000000000000000000000000000002"); push_two_pop_one_constantinople_test( &factory, 0x1b, - "0000000000000000000000000000000000000000000000000000000000000001", - "ff", + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), + hex!("ff").to_vec(), "8000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1b, - "0000000000000000000000000000000000000000000000000000000000000001", - "0100", + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), + hex!("0100").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1b, - "0000000000000000000000000000000000000000000000000000000000000001", - "0101", + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), + hex!("0101").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1b, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "00", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("00").to_vec(), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); push_two_pop_one_constantinople_test( &factory, 0x1b, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "01", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("01").to_vec(), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); push_two_pop_one_constantinople_test( &factory, 0x1b, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ff", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("ff").to_vec(), "8000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1b, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0100", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("0100").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1b, - "0000000000000000000000000000000000000000000000000000000000000000", - "01", + hex!("0000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("01").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1b, - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "01", + hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("01").to_vec(), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); } @@ -911,68 +928,68 @@ fn test_shr(factory: super::Factory) { push_two_pop_one_constantinople_test( &factory, 0x1c, - "0000000000000000000000000000000000000000000000000000000000000001", - "00", + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), + hex!("00").to_vec(), "0000000000000000000000000000000000000000000000000000000000000001"); push_two_pop_one_constantinople_test( &factory, 0x1c, - "0000000000000000000000000000000000000000000000000000000000000001", - "01", + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), + hex!("01").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1c, - "8000000000000000000000000000000000000000000000000000000000000000", - "01", + hex!("8000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("01").to_vec(), "4000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1c, - "8000000000000000000000000000000000000000000000000000000000000000", - "ff", + hex!("8000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("ff").to_vec(), "0000000000000000000000000000000000000000000000000000000000000001"); push_two_pop_one_constantinople_test( &factory, 0x1c, - "8000000000000000000000000000000000000000000000000000000000000000", - "0100", + hex!("8000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("0100").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1c, - "8000000000000000000000000000000000000000000000000000000000000000", - "0101", + hex!("8000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("0101").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1c, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "00", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("00").to_vec(), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); push_two_pop_one_constantinople_test( &factory, 0x1c, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "01", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("01").to_vec(), "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); push_two_pop_one_constantinople_test( &factory, 0x1c, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ff", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("ff").to_vec(), "0000000000000000000000000000000000000000000000000000000000000001"); push_two_pop_one_constantinople_test( &factory, 0x1c, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0100", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("0100").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1c, - "0000000000000000000000000000000000000000000000000000000000000000", - "01", + hex!("0000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("01").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); } @@ -981,104 +998,102 @@ fn test_sar(factory: super::Factory) { push_two_pop_one_constantinople_test( &factory, 0x1d, - "0000000000000000000000000000000000000000000000000000000000000001", - "00", + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), + hex!("00").to_vec(), "0000000000000000000000000000000000000000000000000000000000000001"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "0000000000000000000000000000000000000000000000000000000000000001", - "01", + hex!("0000000000000000000000000000000000000000000000000000000000000001").to_vec(), + hex!("01").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "8000000000000000000000000000000000000000000000000000000000000000", - "01", + hex!("8000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("01").to_vec(), "c000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "8000000000000000000000000000000000000000000000000000000000000000", - "ff", + hex!("8000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("ff").to_vec(), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "8000000000000000000000000000000000000000000000000000000000000000", - "0100", + hex!("8000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("0100").to_vec(), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "8000000000000000000000000000000000000000000000000000000000000000", - "0101", + hex!("8000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("0101").to_vec(), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "00", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("00").to_vec(), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "01", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("01").to_vec(), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ff", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("ff").to_vec(), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0100", + hex!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("0100").to_vec(), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "0000000000000000000000000000000000000000000000000000000000000000", - "01", + hex!("0000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("01").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "4000000000000000000000000000000000000000000000000000000000000000", - "fe", + hex!("4000000000000000000000000000000000000000000000000000000000000000").to_vec(), + hex!("fe").to_vec(), "0000000000000000000000000000000000000000000000000000000000000001"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "f8", + hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("f8").to_vec(), "000000000000000000000000000000000000000000000000000000000000007f"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "fe", + hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("fe").to_vec(), "0000000000000000000000000000000000000000000000000000000000000001"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ff", + hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("ff").to_vec(), "0000000000000000000000000000000000000000000000000000000000000000"); push_two_pop_one_constantinople_test( &factory, 0x1d, - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "0100", + hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(), + hex!("0100").to_vec(), "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(); +fn push_two_pop_one_constantinople_test(factory: &super::Factory, opcode: u8, mut push1: Vec, mut push2: Vec, result: &str) { assert!(push1.len() <= 32 && push1.len() != 0); assert!(push2.len() <= 32 && push2.len() != 0); @@ -1096,7 +1111,7 @@ fn push_two_pop_one_constantinople_test(factory: &super::Factory, opcode: u8, pu let mut ext = FakeExt::new_constantinople(); let _ = { - let mut vm = factory.create(params, ext.schedule(), ext.depth()); + let vm = factory.create(params, ext.schedule(), ext.depth()); test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -1113,5 +1128,5 @@ 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()); + assert_eq!(ext.store.get(&H256::from_low_u64_be(pos)).unwrap(), &H256::from_str(val).unwrap()); } diff --git a/ethcore/executive-state/Cargo.toml b/ethcore/executive-state/Cargo.toml new file mode 100644 index 0000000000..0236c0a420 --- /dev/null +++ b/ethcore/executive-state/Cargo.toml @@ -0,0 +1,34 @@ +[package] +description = "Execute transactions producing a receipt and an optional trace." +name = "executive-state" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +account-db = { path = "../account-db" } +account-state = { path = "../account-state" } +bytes = { package = "parity-bytes", version = "0.1.0" } +common-types = { path = "../types" } +ethereum-types = "0.8.0" +hash-db = "0.15.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +kvdb = "0.3.1" +log = "0.4.8" +machine = { path = "../machine" } +trace = { path = "../trace" } +trie-vm-factories = { path = "../trie-vm-factories" } +vm = { path = "../vm" } + +[dev-dependencies] +env_logger = "0.5" +ethcore = { path = "..", features = ["test-helpers"] } +parity-crypto = { version = "0.4.2", features = ["publickey"] } +evm = { path = "../evm" } +keccak-hash = "0.4.0" +pod = { path = "../pod" } +rustc-hex = "1.0" +spec = { path = "../spec" } +trie-db = "0.18.0" +ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } diff --git a/ethcore/executive-state/src/lib.rs b/ethcore/executive-state/src/lib.rs new file mode 100644 index 0000000000..a2f979b101 --- /dev/null +++ b/ethcore/executive-state/src/lib.rs @@ -0,0 +1,1704 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Execute transactions and modify State. This is glue code between the `ethcore` and +//! `account-state` crates and contains everything that requires `Machine` or `Executive` (or types +//! thereof). + +use account_state::{ + backend::{self, Backend}, + state::State, +}; +use bytes::Bytes; +use common_types::{ + engines::machine::Executed as RawExecuted, + errors::{ExecutionError, EthcoreError as Error}, + transaction::SignedTransaction, + receipt::{TransactionOutcome, Receipt}, +}; +use ethereum_types::H256; +use hash_db::AsHashDB; +use keccak_hasher::KeccakHasher; +use kvdb::DBValue; +use log::trace; +use machine::{ + machine::Machine, + executive::{Executive, TransactOptions}, + executed::Executed, +}; +use trace::{FlatTrace, VMTrace}; +use trie_vm_factories::Factories; +use vm::EnvInfo; + +/// Return type of proof validity check. +#[derive(Debug, Clone)] +pub enum ProvedExecution { + /// Proof wasn't enough to complete execution. + BadProof, + /// The transaction failed, but not due to a bad proof. + Failed(ExecutionError), + /// The transaction successfully completed with the given proof. + Complete(Box), +} + +/// Used to return information about an `State::apply` operation. +pub struct ApplyOutcome { + /// The receipt for the applied transaction. + pub receipt: Receipt, + /// The output of the applied transaction. + pub output: Bytes, + /// The trace for the applied transaction, empty if tracing was not produced. + pub trace: Vec, + /// The VM trace for the applied transaction, None if tracing was not produced. + pub vm_trace: Option +} + +/// Result type for the execution ("application") of a transaction. +pub type ApplyResult = Result, Error>; + +/// Check the given proof of execution. +/// `Err(ExecutionError::Internal)` indicates failure, everything else indicates +/// a successful proof (as the transaction itself may be poorly chosen). +pub fn check_proof( + proof: &[DBValue], + root: H256, + transaction: &SignedTransaction, + machine: &Machine, + env_info: &EnvInfo, +) -> ProvedExecution { + let backend = self::backend::ProofCheck::new(proof); + let mut factories = Factories::default(); + factories.accountdb = account_db::Factory::Plain; + + let res = State::from_existing( + backend, + root, + machine.account_start_nonce(env_info.number), + factories + ); + + let mut state = match res { + Ok(state) => state, + Err(_) => return ProvedExecution::BadProof, + }; + + let options = TransactOptions::with_no_tracing().save_output_from_contract(); + match execute(&mut state, env_info, machine, transaction, options, true) { + Ok(executed) => ProvedExecution::Complete(Box::new(executed)), + Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, + Err(e) => ProvedExecution::Failed(e), + } +} + +/// Prove a `virtual` transaction on the given state. +/// Returns `None` when the transaction could not be proved, +/// and a proof otherwise. +pub fn prove_transaction_virtual + Send + Sync>( + db: H, + root: H256, + transaction: &SignedTransaction, + machine: &Machine, + env_info: &EnvInfo, + factories: Factories, +) -> Option<(Bytes, Vec)> { + use account_state::backend::Proving; + + let backend = Proving::new(db); + let res = State::from_existing( + backend, + root, + machine.account_start_nonce(env_info.number), + factories, + ); + + let mut state = match res { + Ok(state) => state, + Err(_) => return None, + }; + + let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); + match execute(&mut state, env_info, machine, transaction, options, true) { + Err(ExecutionError::Internal(_)) => None, + Err(e) => { + trace!(target: "state", "Proved call failed: {}", e); + Some((Vec::new(), state.drop().1.extract_proof())) + } + Ok(res) => Some((res.output, state.drop().1.extract_proof())), + } +} + +/// Collects code that needs a Machine and/or Executive +pub trait ExecutiveState { + /// Execute a given transaction, producing a receipt and an optional trace. + /// This will change the state accordingly. + fn apply( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracing: bool + ) -> ApplyResult; + + /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. + /// This will change the state accordingly. + fn apply_with_tracing( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracer: T, + vm_tracer: V, + ) -> ApplyResult + where + T: trace::Tracer, + V: trace::VMTracer; +} + +impl ExecutiveState for State { + /// Execute a given transaction, producing a receipt and an optional trace. + /// This will change the state accordingly. + fn apply( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracing: bool + ) -> ApplyResult { + if tracing { + let options = TransactOptions::with_tracing(); + self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) + } else { + let options = TransactOptions::with_no_tracing(); + self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) + } + } + + /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. + /// This will change the state accordingly. + fn apply_with_tracing( + &mut self, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + tracer: T, + vm_tracer: V, + ) -> ApplyResult + where + T: trace::Tracer, + V: trace::VMTracer, + { + let options = TransactOptions::new(tracer, vm_tracer); + let e = execute(self, env_info, machine, t, options, false)?; + let params = machine.params(); + + let eip658 = env_info.number >= params.eip658_transition; + let no_intermediate_commits = + eip658 || + (env_info.number >= params.eip98_transition && env_info.number >= params.validate_receipts_transition); + + let outcome = if no_intermediate_commits { + if eip658 { + TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 }) + } else { + TransactionOutcome::Unknown + } + } else { + self.commit()?; + TransactionOutcome::StateRoot(self.root().clone()) + }; + + let output = e.output; + let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs); + trace!(target: "state", "Transaction receipt: {:?}", receipt); + + Ok(ApplyOutcome { + receipt, + output, + trace: e.trace, + vm_trace: e.vm_trace, + }) + } +} + +// Execute a given transaction without committing changes. +// +// `virt` signals that we are executing outside of a block set and restrictions like +// gas limits and gas costs should be lifted. +fn execute( + state: &mut State, + env_info: &EnvInfo, + machine: &Machine, + t: &SignedTransaction, + options: TransactOptions, + virt: bool +) -> Result, ExecutionError> + where + B: Backend, + T: trace::Tracer, + V: trace::VMTracer, +{ + let schedule = machine.schedule(env_info.number); + let mut e = Executive::new(state, env_info, machine, &schedule); + + match virt { + true => e.transact_virtual(t, options), + false => e.transact(t, options), + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + use std::str::FromStr; + use std::collections::HashSet; + + use super::*; + + use account_state::{Account, CleanupMode}; + use common_types::transaction::*; + use keccak_hash::{keccak, KECCAK_NULL_RLP}; + use parity_crypto::publickey::Secret; + use ethereum_types::{H256, U256, Address, BigEndianHash}; + use ethcore::{ + test_helpers::{get_temp_state, get_temp_state_db} + }; + use ethtrie; + use machine::Machine; + use pod::{self, PodAccount, PodState}; + use rustc_hex::FromHex; + use spec; + use ::trace::{FlatTrace, TraceError, trace}; + use trie_db::{TrieFactory, TrieSpec}; + use vm::EnvInfo; + + fn secret() -> Secret { + keccak("").into() + } + + fn make_frontier_machine(max_depth: usize) -> Machine { + let mut machine = spec::new_frontier_test_machine(); + machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); + machine + } + + #[test] + fn should_apply_create_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), + }.sign(&secret(), None); + + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Create(trace::Create { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + value: 100.into(), + gas: 77412.into(), + init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], + creation_method: Some(trace::CreationMethod::Create), + }), + result: trace::Res::Create(trace::CreateResult { + gas_used: U256::from(3224), + address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), + code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_work_when_cloned() { + let _ = env_logger::try_init(); + + let a = Address::zero(); + + let mut state = { + let mut state = get_temp_state(); + assert_eq!(state.exists(&a).unwrap(), false); + state.inc_nonce(&a).unwrap(); + state.commit().unwrap(); + state.clone() + }; + + state.inc_nonce(&a).unwrap(); + state.commit().unwrap(); + } + + #[test] + fn should_trace_failed_create_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Create, + value: 100.into(), + data: FromHex::from_hex("5b600056").unwrap(), + }.sign(&secret(), None); + + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Create(trace::Create { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + value: 100.into(), + gas: 78792.into(), + init: vec![91, 96, 0, 86], + creation_method: Some(trace::CreationMethod::Create), + }), + result: trace::Res::FailedCreate(TraceError::OutOfGas), + subtraces: 0 + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("6000").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_basic_call_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(0), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_transaction_to_builtin() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0x1)), + value: 0.into(), + data: vec![], + }.sign(&secret(), None); + + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_str("0000000000000000000000000000000000000001").unwrap(), + value: 0.into(), + gas: 79_000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3000), + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_subcall_transaction_to_builtin() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 0.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3_721), // in post-eip150 + output: vec![] + }), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_callcode_properly() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 0.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("6000").unwrap()).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 724.into(), // in post-eip150 + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 4096.into(), + input: vec![], + call_type: Some(trace::CallType::CallCode).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![], + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_delegatecall_properly() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + info.number = 0x789b0; + let machine = spec::new_test_machine(); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 0.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("6000600060006000600b618000f4").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60056000526001601ff3").unwrap()).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 0.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(736), // in post-eip150 + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 32768.into(), + input: vec![], + call_type: Some(trace::CallType::DelegateCall).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 18.into(), + output: vec![5], + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_call_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("5b600056").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + subtraces: 0, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("6000").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_basic_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(31761), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 69.into(), + gas: 2300.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult::default()), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_not_trace_call_with_invalid_basic_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()).unwrap(); // not enough funds. + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(31761), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![],//600480600b6000396000f35b600056 + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("5b600056").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(79_000), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_call_with_subcall_with_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xc), FromHex::from_hex("6000").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(135), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(69), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xb), + to: Address::from_low_u64_be(0xc), + value: 0.into(), + gas: 78868.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_failed_subcall_with_subcall_transaction() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![],//600480600b6000396000f35b600056 + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xc), FromHex::from_hex("6000").unwrap()).unwrap(); + state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(79_000), + output: vec![] + }) + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xa), + to: Address::from_low_u64_be(0xb), + value: 0.into(), + gas: 78934.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::FailedCall(TraceError::OutOfGas), + }, FlatTrace { + trace_address: vec![0, 0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Call(trace::Call { + from: Address::from_low_u64_be(0xb), + to: Address::from_low_u64_be(0xc), + value: 0.into(), + gas: 78868.into(), + call_type: Some(trace::CallType::Call).into(), + input: vec![], + }), + result: trace::Res::Call(trace::CallResult { + gas_used: U256::from(3), + output: vec![] + }), + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn should_trace_suicide() { + let _ = env_logger::try_init(); + + let mut state = get_temp_state(); + + let mut info = EnvInfo::default(); + info.gas_limit = 1_000_000.into(); + let machine = make_frontier_machine(5); + + let t = Transaction { + nonce: 0.into(), + gas_price: 0.into(), + gas: 100_000.into(), + action: Action::Call(Address::from_low_u64_be(0xa)), + value: 100.into(), + data: vec![], + }.sign(&secret(), None); + + state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()).unwrap(); + state.add_balance(&Address::from_low_u64_be(0xa), &50.into(), CleanupMode::NoEmpty).unwrap(); + state.add_balance(&t.sender(), &100.into(), CleanupMode::NoEmpty).unwrap(); + let result = state.apply(&info, &machine, &t, true).unwrap(); + let expected_trace = vec![FlatTrace { + trace_address: Default::default(), + subtraces: 1, + action: trace::Action::Call(trace::Call { + from: Address::from_str("9cce34f7ab185c7aba1b7c8140d620b4bda941d6").unwrap(), + to: Address::from_low_u64_be(0xa), + value: 100.into(), + gas: 79000.into(), + input: vec![], + call_type: Some(trace::CallType::Call).into(), + }), + result: trace::Res::Call(trace::CallResult { + gas_used: 3.into(), + output: vec![] + }), + }, FlatTrace { + trace_address: vec![0].into_iter().collect(), + subtraces: 0, + action: trace::Action::Suicide(trace::Suicide { + address: Address::from_low_u64_be(0xa), + refund_address: Address::from_low_u64_be(0xb), + balance: 150.into(), + }), + result: trace::Res::None, + }]; + + assert_eq!(result.trace, expected_trace); + } + + #[test] + fn code_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state.require_or_from(&a, false, || Account::new_contract(42.into(), 0.into(), 0.into(), KECCAK_NULL_RLP), |_|{}).unwrap(); + state.init_code(&a, vec![1, 2, 3]).unwrap(); + assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); + state.commit().unwrap(); + assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); + } + + #[test] + fn storage_at_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state.set_storage(&a, BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(69u64))).unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let h1 = BigEndianHash::from_uint(&U256::from(1u64)); + let h2 = BigEndianHash::from_uint(&U256::from(69u64)); + assert_eq!(s.storage_at(&a, &h1).unwrap(), h2); + } + + #[test] + fn get_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state.inc_nonce(&a).unwrap(); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + } + + #[test] + fn remove() { + let a = Address::zero(); + let mut state = get_temp_state(); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.exists_and_not_null(&a).unwrap(), false); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.exists(&a).unwrap(), true); + assert_eq!(state.exists_and_not_null(&a).unwrap(), true); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.kill_account(&a); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.exists_and_not_null(&a).unwrap(), false); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + } + + #[test] + fn empty_account_is_not_created() { + let a = Address::zero(); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty).unwrap(); // create an empty account + state.commit().unwrap(); + state.drop() + }; + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert!(!state.exists(&a).unwrap()); + assert!(!state.exists_and_not_null(&a).unwrap()); + } + + #[test] + fn empty_account_exists_when_creation_forced() { + let a = Address::zero(); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account + state.commit().unwrap(); + state.drop() + }; + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert!(state.exists(&a).unwrap()); + assert!(!state.exists_and_not_null(&a).unwrap()); + } + + #[test] + fn remove_from_database() { + let a = Address::zero(); + let (root, db) = { + let mut state = get_temp_state(); + state.inc_nonce(&a).unwrap(); + state.commit().unwrap(); + assert_eq!(state.exists(&a).unwrap(), true); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.drop() + }; + + let (root, db) = { + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.exists(&a).unwrap(), true); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.kill_account(&a); + state.commit().unwrap(); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + state.drop() + }; + + let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + assert_eq!(state.exists(&a).unwrap(), false); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + } + + #[test] + fn alter_balance() { + let mut state = get_temp_state(); + let a = Address::zero(); + let b = Address::from_low_u64_be(1u64); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.sub_balance(&a, &U256::from(42u64), &mut CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); + state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); + assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); + assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); + } + + #[test] + fn alter_nonce() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); + state.commit().unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); + state.inc_nonce(&a).unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); + state.commit().unwrap(); + assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); + } + + #[test] + fn balance_nonce() { + let mut state = get_temp_state(); + let a = Address::zero(); + assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + state.commit().unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); + assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); + } + + #[test] + fn ensure_cached() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.require(&a, false).unwrap(); + state.commit().unwrap(); + assert_eq!(*state.root(), H256::from_str("0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785").unwrap()); + } + + #[test] + fn checkpoint_basic() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.checkpoint(); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.discard_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.checkpoint(); + state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(70u64)); + state.revert_to_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + } + + #[test] + fn checkpoint_nested() { + let mut state = get_temp_state(); + let a = Address::zero(); + state.checkpoint(); + state.checkpoint(); + state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.discard_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); + state.revert_to_checkpoint(); + assert_eq!(state.balance(&a).unwrap(), U256::from(0)); + } + + #[test] + fn checkpoint_revert_to_get_storage_at() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = BigEndianHash::from_uint(&U256::from(0)); + + let c0 = state.checkpoint(); + let c1 = state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); + + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(1))); + + state.revert_to_checkpoint(); // Revert to c1. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); + } + + #[test] + fn checkpoint_from_empty_get_storage_at() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = BigEndianHash::from_uint(&U256::from(0)); + let k2 = BigEndianHash::from_uint(&U256::from(1)); + + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); + state.clear(); + + let c0 = state.checkpoint(); + state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap(); + let c1 = state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); + let c2 = state.checkpoint(); + let c3 = state.checkpoint(); + state.set_storage(&a, k2, BigEndianHash::from_uint(&U256::from(3))).unwrap(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(3))).unwrap(); + let c4 = state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(4))).unwrap(); + let c5 = state.checkpoint(); + + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); + assert_eq!(state.checkpoint_storage_at(c5, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(4)))); + + state.discard_checkpoint(); // Commit/discard c5. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); + + state.revert_to_checkpoint(); // Revert to c4. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + + state.discard_checkpoint(); // Commit/discard c3. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + + state.revert_to_checkpoint(); // Revert to c2. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + + state.discard_checkpoint(); // Commit/discard c1. + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + } + + #[test] + fn checkpoint_get_storage_at() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = BigEndianHash::from_uint(&U256::from(0)); + let k2 = BigEndianHash::from_uint(&U256::from(1)); + + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(0xffff))).unwrap(); + state.commit().unwrap(); + state.clear(); + + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0xffff))); + state.clear(); + + let cm1 = state.checkpoint(); + let c0 = state.checkpoint(); + state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap(); + let c1 = state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); + let c2 = state.checkpoint(); + let c3 = state.checkpoint(); + state.set_storage(&a, k2, BigEndianHash::from_uint(&U256::from(3))).unwrap(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(3))).unwrap(); + let c4 = state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(4))).unwrap(); + let c5 = state.checkpoint(); + + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); + assert_eq!(state.checkpoint_storage_at(c5, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(4)))); + + state.discard_checkpoint(); // Commit/discard c5. + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(3)))); + + state.revert_to_checkpoint(); // Revert to c4. + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + + state.discard_checkpoint(); // Commit/discard c3. + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(1)))); + + state.revert_to_checkpoint(); // Revert to c2. + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0)))); + + state.discard_checkpoint(); // Commit/discard c1. + assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(BigEndianHash::from_uint(&U256::from(0xffff)))); + } + + #[test] + fn kill_account_with_checkpoints() { + let mut state = get_temp_state(); + let a = Address::zero(); + let k = BigEndianHash::from_uint(&U256::from(0)); + state.checkpoint(); + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(1))).unwrap(); + state.checkpoint(); + state.kill_account(&a); + + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); + state.revert_to_checkpoint(); + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(1))); + } + + #[test] + fn create_contract_fail() { + let mut state = get_temp_state(); + let orig_root = state.root().clone(); + let a = Address::from_low_u64_be(1000); + + state.checkpoint(); // c1 + state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap(); + state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); + state.checkpoint(); // c2 + state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); + state.discard_checkpoint(); // discard c2 + state.revert_to_checkpoint(); // revert to c1 + assert_eq!(state.exists(&a).unwrap(), false); + + state.commit().unwrap(); + assert_eq!(orig_root, state.root().clone()); + } + + #[test] + fn create_contract_fail_previous_storage() { + let mut state = get_temp_state(); + let a = Address::from_low_u64_be(1000); + let k = BigEndianHash::from_uint(&U256::from(0)); + + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(0xffff))).unwrap(); + state.commit().unwrap(); + state.clear(); + + let orig_root = state.root().clone(); + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0xffff))); + state.clear(); + + state.checkpoint(); // c1 + state.new_contract(&a, U256::zero(), U256::zero(), U256::zero()).unwrap(); + state.checkpoint(); // c2 + state.set_storage(&a, k, BigEndianHash::from_uint(&U256::from(2))).unwrap(); + state.revert_to_checkpoint(); // revert to c2 + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); + state.revert_to_checkpoint(); // revert to c1 + assert_eq!(state.storage_at(&a, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0xffff))); + + state.commit().unwrap(); + assert_eq!(orig_root, state.root().clone()); + } + + #[test] + fn create_empty() { + let mut state = get_temp_state(); + state.commit().unwrap(); + assert_eq!(*state.root(), H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap()); + } + + #[test] + fn should_not_panic_on_state_diff_with_storage() { + let mut state = get_temp_state(); + let a = Address::from_low_u64_be(0xa); + state.init_code(&a, b"abcdefg".to_vec()).unwrap(); + state.add_balance(&a, &256.into(), CleanupMode::NoEmpty).unwrap(); + state.set_storage(&a, H256::from_low_u64_be(0xb), H256::from_low_u64_be(0xc).into()).unwrap(); + + let mut new_state = state.clone(); + new_state.set_storage(&a, H256::from_low_u64_be(0xb), H256::from_low_u64_be(0xd).into()).unwrap(); + + new_state.diff_from(state).unwrap(); + } + + #[test] + fn should_kill_garbage() { + let a = Address::from_low_u64_be(10); + let b = Address::from_low_u64_be(20); + let c = Address::from_low_u64_be(30); + let d = Address::from_low_u64_be(40); + let e = Address::from_low_u64_be(50); + let x = Address::from_low_u64_be(0); + let db = get_temp_state_db(); + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account + state.add_balance(&b, &100.into(), CleanupMode::ForceCreate).unwrap(); // create a dust account + state.add_balance(&c, &101.into(), CleanupMode::ForceCreate).unwrap(); // create a normal account + state.add_balance(&d, &99.into(), CleanupMode::ForceCreate).unwrap(); // create another dust account + state.new_contract(&e, 100.into(), 1.into(), 0.into()).unwrap(); // create a contract account + state.init_code(&e, vec![0x00]).unwrap(); + state.commit().unwrap(); + state.drop() + }; + + let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); + let mut touched = HashSet::new(); + state.add_balance(&a, &U256::default(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account + state.transfer_balance(&b, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance + state.transfer_balance(&c, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance + state.transfer_balance(&e, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance + state.kill_garbage(&touched, true, &None, false).unwrap(); + assert!(!state.exists(&a).unwrap()); + assert!(state.exists(&b).unwrap()); + state.kill_garbage(&touched, true, &Some(100.into()), false).unwrap(); + assert!(!state.exists(&b).unwrap()); + assert!(state.exists(&c).unwrap()); + assert!(state.exists(&d).unwrap()); + assert!(state.exists(&e).unwrap()); + state.kill_garbage(&touched, true, &Some(100.into()), true).unwrap(); + assert!(state.exists(&c).unwrap()); + assert!(state.exists(&d).unwrap()); + assert!(!state.exists(&e).unwrap()); + } + + #[test] + fn should_trace_diff_suicided_accounts() { + let a = Address::from_low_u64_be(10); + 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.raw; + 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(), + version: U256::zero(), + }), None).as_ref()); + } + + #[test] + fn should_trace_diff_unmodified_storage() { + let a = Address::from_low_u64_be(10); + let db = get_temp_state_db(); + + let (root, db) = { + let mut state = State::new(db, U256::from(0), Default::default()); + state.set_storage(&a, BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&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, BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64))).unwrap(); + + let diff = state.diff_from(original).unwrap(); + let diff_map = diff.raw; + 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![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(20u64)))].into_iter().collect(), + version: U256::zero(), + }), + Some(&PodAccount { + balance: U256::zero(), + nonce: U256::zero(), + code: Some(Default::default()), + storage: vec![(BigEndianHash::from_uint(&U256::from(1u64)), BigEndianHash::from_uint(&U256::from(100u64)))].into_iter().collect(), + version: U256::zero(), + })).as_ref()); + } + + #[test] + fn should_get_full_pod_storage_values() { + let a = Address::from_low_u64_be(10); + let db = get_temp_state_db(); + + let factories = Factories { + vm: Default::default(), + trie: TrieFactory::new(TrieSpec::Fat, ethtrie::Layout), + accountdb: Default::default(), + }; + + let get_pod_state_val = |pod_state : &PodState, ak, k| { + pod_state.get().get(ak).unwrap().storage.get(&k).unwrap().clone() + }; + + let storage_address: H256 = BigEndianHash::from_uint(&U256::from(1u64)); + + let (root, db) = { + let mut state = State::new(db, U256::from(0), factories.clone()); + state.set_storage(&a, storage_address.clone(), BigEndianHash::from_uint(&U256::from(20u64))).unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(20u64))); + state.commit().unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(20u64))); + state.drop() + }; + + let mut state = State::from_existing(db, root, U256::from(0u8), factories).unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(20u64))); + state.set_storage(&a, storage_address.clone(), BigEndianHash::from_uint(&U256::from(21u64))).unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(21u64))); + state.commit().unwrap(); + state.set_storage(&a, storage_address.clone(), BigEndianHash::from_uint(&U256::from(0u64))).unwrap(); + let dump = state.to_pod_full().unwrap(); + assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), BigEndianHash::from_uint(&U256::from(0u64))); + } +} diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index bf5866846c..a2d4c8fac5 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -9,43 +9,48 @@ authors = ["Parity Technologies "] [dependencies] log = "0.4" parity-bytes = "0.1" +client-traits = { path = "../client-traits" } common-types = { path = "../types" } -ethcore = { path = ".."} +derive_more = "0.14.0" +engine = { path = "../engine" } ethcore-db = { path = "../db" } ethcore-blockchain = { path = "../blockchain" } -ethereum-types = "0.4" -memory-db = "0.11.0" -trie-db = "0.11.0" +ethereum-types = "0.8.0" +executive-state = { path = "../executive-state" } +machine = { path = "../machine" } +memory-db = "0.18.0" +trie-db = "0.18.0" patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" } ethcore-network = { path = "../../util/network" } +ethcore-miner = { path = "../../miner" } ethcore-io = { path = "../../util/io" } -hash-db = "0.11.0" -heapsize = "0.4" +hash-db = "0.15.0" +parity-util-mem = "0.3.0" vm = { path = "../vm" } fastmap = { path = "../../util/fastmap" } failsafe = { version = "0.3.0", default-features = false, features = ["parking_lot_mutex"] } -rlp = { version = "0.3.0", features = ["ethereum"] } +rlp = "0.4.0" rlp_derive = { path = "../../util/rlp-derive" } smallvec = "0.6" futures = "0.1" -rand = "0.4" -itertools = "0.5" -bincode = "0.8.0" +rand = "0.7" +bincode = "1.1" serde = "1.0" serde_derive = "1.0" -parking_lot = "0.7" +spec = { path = "../spec" } +parking_lot = "0.9" stats = { path = "../../util/stats" } -keccak-hash = "0.1" +keccak-hash = "0.4.0" keccak-hasher = { path = "../../util/keccak-hasher" } triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" } -kvdb = "0.1" +kvdb = "0.3.1" memory-cache = { path = "../../util/memory-cache" } -error-chain = { version = "0.12", default-features = false } journaldb = { path = "../../util/journaldb" } +verification = { path = "../verification" } [dev-dependencies] ethcore = { path = "..", features = ["test-helpers"] } -kvdb-memorydb = "0.1" +kvdb-memorydb = "0.3.1" tempdir = "0.3" [features] diff --git a/ethcore/light/src/cache.rs b/ethcore/light/src/cache.rs index d75e0ff7d3..646144be56 100644 --- a/ethcore/light/src/cache.rs +++ b/ethcore/light/src/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,12 +21,12 @@ //! vector of all gas prices from a recent range of blocks. use std::time::{Instant, Duration}; +use parity_util_mem::{MallocSizeOf, MallocSizeOfOps, MallocSizeOfExt}; use common_types::encoded; use common_types::BlockNumber; use common_types::receipt::Receipt; use ethereum_types::{H256, U256}; -use heapsize::HeapSizeOf; use memory_cache::MemoryLruCache; use stats::Corpus; @@ -157,18 +157,20 @@ impl Cache { /// Get the memory used. pub fn mem_used(&self) -> usize { - self.heap_size_of_children() + self.malloc_size_of() } } -impl HeapSizeOf for Cache { - fn heap_size_of_children(&self) -> usize { + +// This is fast method: it is possible to have a more exhaustive implementation +impl MallocSizeOf for Cache { + fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { self.headers.current_size() + self.canon_hashes.current_size() + self.bodies.current_size() + self.receipts.current_size() + self.chain_score.current_size() - // TODO: + corpus + // `self.corpus` is skipped } } diff --git a/ethcore/light/src/cht.rs b/ethcore/light/src/cht.rs index a9bc5d7f26..be98b3c1b3 100644 --- a/ethcore/light/src/cht.rs +++ b/ethcore/light/src/cht.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -74,7 +74,7 @@ impl> CHT { if block_to_cht_number(num) != Some(self.number) { return Ok(None) } let mut recorder = Recorder::with_depth(from_level); - let db: &HashDB<_,_> = &self.db; + let db: &dyn HashDB<_,_> = &self.db; let t = TrieDB::new(&db, &self.root)?; t.get_with(&key!(num), &mut recorder)?; @@ -95,7 +95,8 @@ pub struct BlockInfo { /// Build an in-memory CHT from a closure which provides necessary information /// about blocks. If the fetcher ever fails to provide the info, the CHT /// will not be generated. -pub fn build(cht_num: u64, mut fetcher: F) -> Option>> +pub fn build(cht_num: u64, mut fetcher: F) + -> Option, DBValue>>> where F: FnMut(BlockId) -> Option { let mut db = new_memory_db(); @@ -104,7 +105,7 @@ pub fn build(cht_num: u64, mut fetcher: F) -> Option(cht_num: u64, iterable: I) -> Option pub fn check_proof(proof: &[Bytes], num: u64, root: H256) -> Option<(H256, U256)> { let mut db = new_memory_db(); - for node in proof { db.insert(&node[..]); } + for node in proof { db.insert(hash_db::EMPTY_PREFIX, &node[..]); } let res = match TrieDB::new(&db, &root) { Err(_) => return None, Ok(trie) => trie.get_with(&key!(num), |val: &[u8]| { diff --git a/ethcore/light/src/client/fetch.rs b/ethcore/light/src/client/fetch.rs index 86a3770bf8..896e5f5e27 100644 --- a/ethcore/light/src/client/fetch.rs +++ b/ethcore/light/src/client/fetch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,11 +18,12 @@ use std::sync::Arc; -use common_types::encoded; -use common_types::header::Header; -use common_types::receipt::Receipt; -use ethcore::engines::{EthEngine, StateDependentProof}; -use ethcore::machine::EthereumMachine; +use common_types::{ + header::Header, + encoded, + receipt::Receipt, +}; +use engine::{Engine, StateDependentProof}; use ethereum_types::H256; use futures::future::IntoFuture; @@ -48,8 +49,8 @@ pub trait ChainDataFetcher: Send + Sync + 'static { fn epoch_transition( &self, _hash: H256, - _engine: Arc, - _checker: Arc> + _engine: Arc, + _checker: Arc ) -> Self::Transition; } @@ -77,8 +78,8 @@ impl ChainDataFetcher for Unavailable { fn epoch_transition( &self, _hash: H256, - _engine: Arc, - _checker: Arc> + _engine: Arc, + _checker: Arc ) -> Self::Transition { Err("fetching epoch transition proofs unavailable") } diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index b72a099d08..d21d705323 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -30,15 +30,20 @@ use std::sync::Arc; use cache::Cache; use cht; -use common_types::block_status::BlockStatus; -use common_types::encoded; -use common_types::header::Header; -use common_types::ids::BlockId; -use ethcore::engines::epoch::{Transition as EpochTransition, PendingTransition as PendingEpochTransition}; -use ethcore::error::{Error, EthcoreResult, ErrorKind as EthcoreErrorKind, BlockError}; -use ethcore::spec::{Spec, SpecHardcodedSync}; +use common_types::{ + block_status::BlockStatus, + encoded, + engines::epoch::{ + Transition as EpochTransition, + PendingTransition as PendingEpochTransition, + }, + errors::{EthcoreError as Error, BlockError, EthcoreResult}, + header::Header, + ids::BlockId, +}; +use spec::{Spec, SpecHardcodedSync}; use ethereum_types::{H256, H264, U256}; -use heapsize::HeapSizeOf; +use parity_util_mem::{MallocSizeOf, MallocSizeOfOps}; use kvdb::{DBTransaction, KeyValueDB}; use parking_lot::{Mutex, RwLock}; use fastmap::H256FastMap; @@ -95,8 +100,8 @@ struct Entry { canonical_hash: H256, } -impl HeapSizeOf for Entry { - fn heap_size_of_children(&self) -> usize { +impl MallocSizeOf for Entry { + fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { if self.candidates.spilled() { self.candidates.capacity() * ::std::mem::size_of::() } else { @@ -154,8 +159,11 @@ fn pending_transition_key(block_hash: H256) -> H264 { let mut key = H264::default(); - key[0] = LEADING; - key.0[1..].copy_from_slice(&block_hash.0[..]); + { + let bytes = key.as_bytes_mut(); + bytes[0] = LEADING; + bytes[1..].copy_from_slice(block_hash.as_bytes()); + } key } @@ -165,8 +173,11 @@ fn transition_key(block_hash: H256) -> H264 { let mut key = H264::default(); - key[0] = LEADING; - key.0[1..].copy_from_slice(&block_hash.0[..]); + { + let bytes = key.as_bytes_mut(); + bytes[0] = LEADING; + bytes[1..].copy_from_slice(block_hash.as_bytes()); + } key } @@ -196,22 +207,29 @@ pub enum HardcodedSync { Deny, } +#[derive(MallocSizeOf)] /// Header chain. See module docs for more details. pub struct HeaderChain { + #[ignore_malloc_size_of = "ignored for performance reason"] genesis_header: encoded::Header, // special-case the genesis. candidates: RwLock>, + #[ignore_malloc_size_of = "ignored for performance reason"] best_block: RwLock, + #[ignore_malloc_size_of = "ignored for performance reason"] live_epoch_proofs: RwLock>, - db: Arc, - col: Option, + #[ignore_malloc_size_of = "ignored for performance reason"] + db: Arc, + #[ignore_malloc_size_of = "ignored for performance reason"] + col: u32, + #[ignore_malloc_size_of = "ignored for performance reason"] cache: Arc>, } impl HeaderChain { /// Create a new header chain given this genesis block and database to read from. pub fn new( - db: Arc, - col: Option, + db: Arc, + col: u32, spec: &Spec, cache: Arc>, allow_hs: HardcodedSync, @@ -237,11 +255,11 @@ impl HeaderChain { for c in &entry.candidates { let key = transition_key(c.hash); - if let Some(proof) = db.get(col, &*key)? { + if let Some(proof) = db.get(col, key.as_bytes())? { live_epoch_proofs.insert(c.hash, EpochTransition { block_hash: c.hash, block_number: cur_number, - proof: proof.into_vec(), + proof, }); } } @@ -254,7 +272,7 @@ impl HeaderChain { let best_block = { let era = match candidates.get(&curr.best_num) { Some(era) => era, - None => bail!("Database corrupt: highest block referenced but no data."), + None => return Err("Database corrupt: highest block referenced but no data.".into()), }; let best = &era.candidates[0]; @@ -403,7 +421,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(EthcoreErrorKind::Block)? + .map_err(Error::Block)? }; parent_td + *header.difficulty() @@ -431,7 +449,7 @@ impl HeaderChain { } if let Some(transition) = transition { - transaction.put(self.col, &*transition_key(hash), &transition.proof); + transaction.put(self.col, transition_key(hash).as_bytes(), &transition.proof); self.live_epoch_proofs.write().insert(hash, transition); } @@ -508,10 +526,10 @@ impl HeaderChain { for ancient in &era_entry.candidates { let maybe_transition = live_epoch_proofs.remove(&ancient.hash); if let Some(epoch_transition) = maybe_transition { - transaction.delete(self.col, &*transition_key(ancient.hash)); + transaction.delete(self.col, transition_key(ancient.hash).as_bytes()); if ancient.hash == era_entry.canonical_hash { - last_canonical_transition = match self.db.get(self.col, &ancient.hash) { + last_canonical_transition = match self.db.get(self.col, ancient.hash.as_bytes()) { Err(e) => { warn!(target: "chain", "Error reading from DB: {}\n ", e); @@ -526,13 +544,13 @@ impl HeaderChain { } } - transaction.delete(self.col, &ancient.hash); + transaction.delete(self.col, ancient.hash.as_bytes()); } let canon = &era_entry.candidates[0]; (canon.hash, canon.total_difficulty) }; - cht::compute_root(cht_num, ::itertools::repeat_call(iter)) + cht::compute_root(cht_num, std::iter::repeat_with(iter)) .expect("fails only when too few items; this is checked; qed") }; @@ -576,7 +594,7 @@ impl HeaderChain { } else { let msg = format!("header of block #{} not found in DB ; database in an \ inconsistent state", h_num); - bail!(msg); + return Err(msg.into()); }; let decoded = header.decode().expect("decoding db value failed"); @@ -647,9 +665,10 @@ impl HeaderChain { match cache.block_header(&hash) { Some(header) => Some(header), None => { - match self.db.get(self.col, &hash) { + match self.db.get(self.col, hash.as_bytes()) { Ok(db_value) => { - db_value.map(|x| x.into_vec()).map(encoded::Header::new) + db_value + .map(encoded::Header::new) .and_then(|header| { cache.insert_block_header(hash, header.clone()); Some(header) @@ -772,7 +791,7 @@ impl HeaderChain { /// Get block status. pub fn status(&self, hash: &H256) -> BlockStatus { - if self.db.get(self.col, hash).ok().map_or(false, |x| x.is_some()) { + if self.db.get(self.col, hash.as_bytes()).ok().map_or(false, |x| x.is_some()) { BlockStatus::InChain } else { BlockStatus::Unknown @@ -782,13 +801,13 @@ impl HeaderChain { /// Insert a pending transition. pub fn insert_pending_transition(&self, batch: &mut DBTransaction, hash: H256, t: &PendingEpochTransition) { let key = pending_transition_key(hash); - batch.put(self.col, &*key, &*::rlp::encode(t)); + batch.put(self.col, key.as_bytes(), &*::rlp::encode(t)); } /// Get pending transition for a specific block hash. pub fn pending_transition(&self, hash: H256) -> Option { let key = pending_transition_key(hash); - match self.db.get(self.col, &*key) { + match self.db.get(self.col, key.as_bytes()) { 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); @@ -832,12 +851,6 @@ impl HeaderChain { } } -impl HeapSizeOf for HeaderChain { - fn heap_size_of_children(&self) -> usize { - self.candidates.read().heap_size_of_children() - } -} - /// Iterator over a block's ancestry. pub struct AncestryIter<'a> { next: Option, @@ -865,7 +878,7 @@ mod tests { use cache::Cache; use common_types::header::Header; use common_types::ids::BlockId; - use ethcore::spec::Spec; + use spec; use ethereum_types::U256; use kvdb::KeyValueDB; use kvdb_memorydb; @@ -873,19 +886,19 @@ mod tests { use std::time::Duration; use parking_lot::Mutex; - fn make_db() -> Arc { - Arc::new(kvdb_memorydb::create(0)) + fn make_db() -> Arc { + Arc::new(kvdb_memorydb::create(1)) } #[test] fn basic_chain() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let genesis_header = spec.genesis_header(); let db = make_db(); 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(), 0, &spec, cache, HardcodedSync::Allow).unwrap(); let mut parent_hash = genesis_header.hash(); let mut rolling_timestamp = genesis_header.timestamp(); @@ -913,12 +926,12 @@ mod tests { #[test] fn reorganize() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let genesis_header = spec.genesis_header(); let db = make_db(); 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(), 0, &spec, cache, HardcodedSync::Allow).unwrap(); let mut parent_hash = genesis_header.hash(); let mut rolling_timestamp = genesis_header.timestamp(); @@ -996,11 +1009,11 @@ mod tests { #[test] fn earliest_is_latest() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let db = make_db(); 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(), 0, &spec, cache, HardcodedSync::Allow).unwrap(); assert!(chain.block_header(BlockId::Earliest).is_some()); assert!(chain.block_header(BlockId::Latest).is_some()); @@ -1008,13 +1021,13 @@ mod tests { #[test] fn restore_from_db() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let genesis_header = spec.genesis_header(); let db = make_db(); let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); { - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), + let chain = HeaderChain::new(db.clone(), 0, &spec, cache.clone(), HardcodedSync::Allow).unwrap(); let mut parent_hash = genesis_header.hash(); let mut rolling_timestamp = genesis_header.timestamp(); @@ -1035,7 +1048,7 @@ mod tests { } } - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), + let chain = HeaderChain::new(db.clone(), 0, &spec, cache.clone(), HardcodedSync::Allow).unwrap(); assert!(chain.block_header(BlockId::Number(10)).is_none()); assert!(chain.block_header(BlockId::Number(9000)).is_some()); @@ -1046,13 +1059,13 @@ mod tests { #[test] fn restore_higher_non_canonical() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let genesis_header = spec.genesis_header(); let db = make_db(); let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); { - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), + let chain = HeaderChain::new(db.clone(), 0, &spec, cache.clone(), HardcodedSync::Allow).unwrap(); let mut parent_hash = genesis_header.hash(); let mut rolling_timestamp = genesis_header.timestamp(); @@ -1095,7 +1108,7 @@ mod tests { } // after restoration, non-canonical eras should still be loaded. - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), + let chain = HeaderChain::new(db.clone(), 0, &spec, cache.clone(), HardcodedSync::Allow).unwrap(); assert_eq!(chain.block_header(BlockId::Latest).unwrap().number(), 10); assert!(chain.candidates.read().get(&100).is_some()) @@ -1103,12 +1116,12 @@ mod tests { #[test] fn genesis_header_available() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let genesis_header = spec.genesis_header(); let db = make_db(); let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone(), + let chain = HeaderChain::new(db.clone(), 0, &spec, cache.clone(), HardcodedSync::Allow).unwrap(); assert!(chain.block_header(BlockId::Earliest).is_some()); @@ -1118,12 +1131,12 @@ mod tests { #[test] fn epoch_transitions_available_after_cht() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let genesis_header = spec.genesis_header(); let db = make_db(); 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(), 0, &spec, cache, HardcodedSync::Allow).unwrap(); let mut parent_hash = genesis_header.hash(); let mut rolling_timestamp = genesis_header.timestamp(); @@ -1184,13 +1197,13 @@ mod tests { #[test] fn hardcoded_sync_gen() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let genesis_header = spec.genesis_header(); let db = make_db(); 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).expect("failed to instantiate a new HeaderChain"); + let chain = HeaderChain::new(db.clone(), 0, &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(); diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index 8205ae2ab3..e835711e5b 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,24 +18,29 @@ use std::sync::{Weak, Arc}; -use ethcore::client::{ClientReport, EnvInfo, ClientIoMessage}; -use ethcore::engines::{epoch, EthEngine, EpochChange, EpochTransition, Proof}; -use ethcore::machine::EthereumMachine; -use ethcore::error::{Error, EthcoreResult}; -use ethcore::verification::queue::{self, HeaderQueue}; -use ethcore::spec::{Spec, SpecHardcodedSync}; +use engine::{Engine, EpochChange, Proof}; +use verification::queue::{self, HeaderQueue}; +use spec::{Spec, SpecHardcodedSync}; use io::IoChannel; use parking_lot::{Mutex, RwLock}; use ethereum_types::{H256, U256}; use futures::{IntoFuture, Future}; -use common_types::BlockNumber; -use common_types::block_status::BlockStatus; -use common_types::blockchain_info::BlockChainInfo; -use common_types::encoded; -use common_types::header::Header; -use common_types::ids::BlockId; - +use common_types::{ + BlockNumber, + block_status::BlockStatus, + blockchain_info::BlockChainInfo, + client_types::ClientReport, + encoded, + engines::epoch::{Transition as EpochTransition, PendingTransition}, + errors::EthcoreError as Error, + errors::EthcoreResult, + header::Header, + ids::BlockId, + io_message::ClientIoMessage, + verification::VerificationQueueInfo as BlockQueueInfo, +}; use kvdb::KeyValueDB; +use vm::EnvInfo; use self::fetch::ChainDataFetcher; use self::header_chain::{AncestryIter, HeaderChain, HardcodedSync}; @@ -43,6 +48,7 @@ use self::header_chain::{AncestryIter, HeaderChain, HardcodedSync}; use cache::Cache; pub use self::service::Service; +use client_traits::ForceUpdateSealing; mod header_chain; mod service; @@ -55,7 +61,7 @@ pub struct Config { /// Verification queue config. pub queue: queue::Config, /// Chain column in database. - pub chain_column: Option, + pub chain_column: u32, /// Should it do full verification of blocks? pub verify_full: bool, /// Should it check the seal of blocks? @@ -68,7 +74,7 @@ impl Default for Config { fn default() -> Config { Config { queue: Default::default(), - chain_column: None, + chain_column: 0, verify_full: true, check_seal: true, no_hardcoded_sync: false, @@ -79,7 +85,7 @@ impl Default for Config { /// Trait for interacting with the header chain abstractly. pub trait LightChainClient: Send + Sync { /// Adds a new `LightChainNotify` listener. - fn add_listener(&self, listener: Weak); + fn add_listener(&self, listener: Weak); /// Get chain info. fn chain_info(&self) -> BlockChainInfo; @@ -91,6 +97,9 @@ pub trait LightChainClient: Send + Sync { /// Attempt to get a block hash by block id. fn block_hash(&self, id: BlockId) -> Option; + /// Get block queue information. + fn queue_info(&self) -> BlockQueueInfo; + /// Attempt to get block header by block id. fn block_header(&self, id: BlockId) -> Option; @@ -101,7 +110,7 @@ pub trait LightChainClient: Send + Sync { fn score(&self, id: BlockId) -> Option; /// Get an iterator over a block and its ancestry. - fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box + 'a>; + fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box + 'a>; /// Get the signing chain ID. fn signing_chain_id(&self) -> Option; @@ -111,7 +120,7 @@ pub trait LightChainClient: Send + Sync { fn env_info(&self, id: BlockId) -> Option; /// Get a handle to the consensus engine. - fn engine(&self) -> &Arc; + fn engine(&self) -> &Arc; /// Query whether a block is known. fn is_known(&self, hash: &H256) -> bool; @@ -125,9 +134,6 @@ pub trait LightChainClient: Send + Sync { /// Flush the queue. fn flush_queue(&self); - /// Get queue info. - fn queue_info(&self) -> queue::QueueInfo; - /// Get the `i`th CHT root. fn cht_root(&self, i: usize) -> Option; @@ -158,28 +164,28 @@ impl AsLightClient for T { /// Light client implementation. pub struct Client { - queue: HeaderQueue, - engine: Arc, + queue: HeaderQueue<()>, + engine: Arc, chain: HeaderChain, report: RwLock, import_lock: Mutex<()>, - db: Arc, - listeners: RwLock>>, + db: Arc, + listeners: RwLock>>, fetcher: T, verify_full: bool, /// A closure to call when we want to restart the client - exit_handler: Mutex>>, + exit_handler: Mutex>>, } impl Client { /// Create a new `Client`. pub fn new( config: Config, - db: Arc, - chain_col: Option, + db: Arc, + chain_col: u32, spec: &Spec, fetcher: T, - io_channel: IoChannel, + io_channel: IoChannel>, cache: Arc> ) -> Result { Ok(Self { @@ -208,13 +214,13 @@ impl Client { } /// Adds a new `LightChainNotify` listener. - pub fn add_listener(&self, listener: Weak) { + pub fn add_listener(&self, listener: Weak) { self.listeners.write().push(listener); } /// Import a header to the queue for additional verification. pub fn import_header(&self, header: Header) -> EthcoreResult { - self.queue.import(header).map_err(|(_, e)| e) + self.queue.import(header).map_err(|(e, _)| e) } /// Inquire about the status of a given header. @@ -248,7 +254,7 @@ impl Client { } /// Get the header queue info. - pub fn queue_info(&self) -> queue::QueueInfo { + pub fn queue_info(&self) -> BlockQueueInfo { self.queue.queue_info() } @@ -361,9 +367,9 @@ impl Client { /// Get blockchain mem usage in bytes. pub fn chain_mem_used(&self) -> usize { - use heapsize::HeapSizeOf; + use parity_util_mem::MallocSizeOfExt; - self.chain.heap_size_of_children() + self.chain.malloc_size_of() } /// Set a closure to call when the client wants to be restarted. @@ -375,7 +381,7 @@ impl Client { } /// Get a handle to the verification engine. - pub fn engine(&self) -> &Arc { + pub fn engine(&self) -> &Arc { &self.engine } @@ -416,7 +422,7 @@ impl Client { Arc::new(v) } - fn notify(&self, f: F) { + fn notify(&self, f: F) { for listener in &*self.listeners.read() { if let Some(listener) = listener.upgrade() { f(&*listener) @@ -467,8 +473,8 @@ impl Client { true } - fn check_epoch_signal(&self, verified_header: &Header) -> Result>, T::Error> { - use ethcore::machine::{AuxiliaryRequest, AuxiliaryData}; + fn check_epoch_signal(&self, verified_header: &Header) -> Result, T::Error> { + use common_types::engines::machine::{AuxiliaryRequest, AuxiliaryData}; let mut block: Option> = None; let mut receipts: Option> = None; @@ -513,7 +519,7 @@ impl Client { } // attempts to fetch the epoch proof from the network until successful. - fn write_pending_proof(&self, header: &Header, proof: Proof) -> Result<(), T::Error> { + fn write_pending_proof(&self, header: &Header, proof: Proof) -> Result<(), T::Error> { let proof = match proof { Proof::Known(known) => known, Proof::WithState(state_dependent) => { @@ -526,7 +532,7 @@ impl Client { }; let mut batch = self.db.transaction(); - self.chain.insert_pending_transition(&mut batch, header.hash(), &epoch::PendingTransition { + self.chain.insert_pending_transition(&mut batch, header.hash(), &PendingTransition { proof, }); self.db.write_buffered(batch); @@ -534,13 +540,18 @@ impl Client { } } + impl LightChainClient for Client { - fn add_listener(&self, listener: Weak) { + fn add_listener(&self, listener: Weak) { Client::add_listener(self, listener) } fn chain_info(&self) -> BlockChainInfo { Client::chain_info(self) } + fn queue_info(&self) -> BlockQueueInfo { + self.queue.queue_info() + } + fn queue_header(&self, header: Header) -> EthcoreResult { self.import_header(header) } @@ -561,7 +572,7 @@ impl LightChainClient for Client { Client::score(self, id) } - fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box + 'a> { + fn ancestry_iter<'a>(&'a self, start: BlockId) -> Box + 'a> { Box::new(Client::ancestry_iter(self, start)) } @@ -573,7 +584,7 @@ impl LightChainClient for Client { Client::env_info(self, id) } - fn engine(&self) -> &Arc { + fn engine(&self) -> &Arc { Client::engine(self) } @@ -600,10 +611,6 @@ impl LightChainClient for Client { Client::flush_queue(self); } - fn queue_info(&self) -> queue::QueueInfo { - self.queue.queue_info() - } - fn cht_root(&self, i: usize) -> Option { Client::cht_root(self, i) } @@ -613,14 +620,14 @@ impl LightChainClient for Client { } } -impl ::ethcore::client::ChainInfo for Client { +impl client_traits::ChainInfo for Client { fn chain_info(&self) -> BlockChainInfo { Client::chain_info(self) } } -impl ::ethcore::client::EngineClient for Client { - fn update_sealing(&self) { } +impl client_traits::EngineClient for Client { + fn update_sealing(&self, _force: ForceUpdateSealing) {} fn submit_seal(&self, _block_hash: H256, _seal: Vec>) { } fn broadcast_consensus_message(&self, _message: Vec) { } @@ -632,7 +639,7 @@ impl ::ethcore::client::EngineClient for Client { }) } - fn as_full_client(&self) -> Option<&::ethcore::client::BlockChainClient> { + fn as_full_client(&self) -> Option<&dyn (client_traits::BlockChainClient)> { None } @@ -644,3 +651,5 @@ impl ::ethcore::client::EngineClient for Client { Client::block_header(self, id) } } + +impl client_traits::Tick for Client {} diff --git a/ethcore/light/src/client/service.rs b/ethcore/light/src/client/service.rs index 9672974fc2..0d901cdb83 100644 --- a/ethcore/light/src/client/service.rs +++ b/ethcore/light/src/client/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,11 +20,13 @@ use std::fmt; use std::sync::Arc; +use common_types::{ + errors::EthcoreError as CoreError, + io_message::ClientIoMessage, +}; use ethcore_db as db; use ethcore_blockchain::BlockChainDB; -use ethcore::client::ClientIoMessage; -use ethcore::error::Error as CoreError; -use ethcore::spec::Spec; +use spec::Spec; use io::{IoContext, IoError, IoHandler, IoService}; use cache::Cache; @@ -58,15 +60,15 @@ impl fmt::Display for Error { } /// Light client service. -pub struct Service { +pub struct Service { client: Arc>, - io_service: IoService, + io_service: IoService>, } impl Service { /// Start the service: initialize I/O workers and client itself. - pub fn start(config: ClientConfig, spec: &Spec, fetcher: T, db: Arc, cache: Arc>) -> Result { - let io_service = IoService::::start().map_err(Error::Io)?; + pub fn start(config: ClientConfig, spec: &Spec, fetcher: T, db: Arc, cache: Arc>) -> Result { + let io_service = IoService::>::start().map_err(Error::Io)?; let client = Arc::new(Client::new(config, db.key_value().clone(), db::COL_LIGHT_CHAIN, @@ -75,9 +77,8 @@ impl Service { io_service.channel(), cache, )?); - - io_service.register_handler(Arc::new(ImportBlocks(client.clone()))).map_err(Error::Io)?; spec.engine.register_client(Arc::downgrade(&client) as _); + io_service.register_handler(Arc::new(ImportBlocks(client.clone()))).map_err(Error::Io)?; Ok(Service { client, @@ -86,12 +87,12 @@ impl Service { } /// Set the actor to be notified on certain chain events - pub fn add_notify(&self, notify: Arc) { + 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> { + pub fn register_handler(&self, handler: Arc> + Send>) -> Result<(), IoError> { self.io_service.register_handler(handler) } @@ -103,8 +104,8 @@ impl Service { struct ImportBlocks(Arc>); -impl IoHandler for ImportBlocks { - fn message(&self, _io: &IoContext, message: &ClientIoMessage) { +impl IoHandler> for ImportBlocks { + fn message(&self, _io: &IoContext>, message: &ClientIoMessage<()>) { if let ClientIoMessage::BlockVerified = *message { self.0.import_verified(); } @@ -114,7 +115,7 @@ impl IoHandler for ImportBlocks { #[cfg(test)] mod tests { use super::Service; - use ethcore::spec::Spec; + use spec; use std::sync::Arc; use cache::Cache; @@ -126,7 +127,7 @@ mod tests { #[test] fn it_works() { let db = test_helpers::new_db(); - let spec = Spec::new_test(); + let spec = spec::new_test(); let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); Service::start(Default::default(), &spec, fetch::unavailable(), db, cache).unwrap(); diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index 93e912e1d6..3fa28888f7 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -54,20 +54,24 @@ extern crate serde_derive; extern crate log; extern crate bincode; +extern crate client_traits; extern crate common_types; +extern crate engine; extern crate ethcore_blockchain; extern crate ethcore_db; extern crate ethcore_io as io; extern crate ethcore_network as network; +extern crate executive_state; extern crate parity_bytes as bytes; extern crate ethereum_types; -extern crate ethcore; +extern crate ethcore_miner as miner; extern crate hash_db; -extern crate heapsize; +extern crate parity_util_mem; +extern crate parity_util_mem as malloc_size_of; extern crate failsafe; extern crate futures; -extern crate itertools; extern crate keccak_hasher; +extern crate machine; extern crate memory_db; extern crate trie_db as trie; extern crate patricia_trie_ethereum as ethtrie; @@ -78,6 +82,7 @@ extern crate parking_lot; #[macro_use] extern crate rlp_derive; extern crate serde; +extern crate spec; extern crate smallvec; extern crate stats; extern crate vm; @@ -85,9 +90,11 @@ extern crate keccak_hash as hash; extern crate triehash_ethereum as triehash; extern crate kvdb; extern crate memory_cache; -#[macro_use] -extern crate error_chain; +extern crate derive_more; +extern crate verification; +#[cfg(test)] +extern crate ethcore; #[cfg(test)] extern crate kvdb_memorydb; #[cfg(test)] diff --git a/ethcore/light/src/net/context.rs b/ethcore/light/src/net/context.rs index 6e38959cc0..8238981056 100644 --- a/ethcore/light/src/net/context.rs +++ b/ethcore/light/src/net/context.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -116,13 +116,13 @@ pub trait EventContext: BasicContext { fn peer(&self) -> PeerId; /// Treat the event context as a basic context. - fn as_basic(&self) -> &BasicContext; + fn as_basic(&self) -> &dyn BasicContext; } /// Basic context. pub struct TickCtx<'a> { /// Io context to enable dispatch. - pub io: &'a IoContext, + pub io: &'a dyn IoContext, /// Protocol implementation. pub proto: &'a LightProtocol, } @@ -153,7 +153,7 @@ impl<'a> BasicContext for TickCtx<'a> { /// an io context. pub struct Ctx<'a> { /// Io context to enable immediate response to events. - pub io: &'a IoContext, + pub io: &'a dyn IoContext, /// Protocol implementation. pub proto: &'a LightProtocol, /// Relevant peer for event. @@ -187,7 +187,7 @@ impl<'a> EventContext for Ctx<'a> { self.peer } - fn as_basic(&self) -> &BasicContext { + fn as_basic(&self) -> &dyn BasicContext { &*self } } diff --git a/ethcore/light/src/net/error.rs b/ethcore/light/src/net/error.rs index b29327c600..0efa50ab2a 100644 --- a/ethcore/light/src/net/error.rs +++ b/ethcore/light/src/net/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/net/load_timer.rs b/ethcore/light/src/net/load_timer.rs index 9dc033c47a..9c0117f9b4 100644 --- a/ethcore/light/src/net/load_timer.rs +++ b/ethcore/light/src/net/load_timer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -82,7 +82,7 @@ pub struct LoadDistribution { impl LoadDistribution { /// Load rolling samples from the given store. - pub fn load(store: &SampleStore) -> Self { + pub fn load(store: &dyn SampleStore) -> Self { let mut samples = store.load(); for kind_samples in samples.values_mut() { @@ -133,7 +133,7 @@ impl LoadDistribution { } /// End the current time period. Provide a store to - pub fn end_period(&self, store: &SampleStore) { + pub fn end_period(&self, store: &dyn SampleStore) { let active_period = self.active_period.read(); let mut samples = self.samples.write(); @@ -199,15 +199,15 @@ pub struct FileStore(pub PathBuf); impl SampleStore for FileStore { fn load(&self) -> HashMap> { File::open(&self.0) - .map_err(|e| Box::new(bincode::ErrorKind::IoError(e))) - .and_then(|mut file| bincode::deserialize_from(&mut file, bincode::Infinite)) + .map_err(|e| Box::new(bincode::ErrorKind::Io(e))) + .and_then(|mut file| bincode::deserialize_from(&mut file)) .unwrap_or_else(|_| HashMap::new()) } fn store(&self, samples: &HashMap>) { let res = File::create(&self.0) - .map_err(|e| Box::new(bincode::ErrorKind::IoError(e))) - .and_then(|mut file| bincode::serialize_into(&mut file, samples, bincode::Infinite)); + .map_err(|e| Box::new(bincode::ErrorKind::Io(e))) + .and_then(|mut file| bincode::serialize_into(&mut file, samples)); if let Err(e) = res { warn!(target: "pip", "Error writing light request timing samples to file: {}", e); diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 2fd6c340e0..a20197beff 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -236,25 +236,25 @@ pub trait Handler: Send + Sync { /// Called when a peer connects. fn on_connect( &self, - _ctx: &EventContext, + _ctx: &dyn EventContext, _status: &Status, _capabilities: &Capabilities ) -> PeerStatus { PeerStatus::Kept } /// Called when a peer disconnects, with a list of unfulfilled request IDs as /// of yet. - fn on_disconnect(&self, _ctx: &EventContext, _unfulfilled: &[ReqId]) { } + fn on_disconnect(&self, _ctx: &dyn EventContext, _unfulfilled: &[ReqId]) { } /// Called when a peer makes an announcement. - fn on_announcement(&self, _ctx: &EventContext, _announcement: &Announcement) { } + fn on_announcement(&self, _ctx: &dyn EventContext, _announcement: &Announcement) { } /// Called when a peer requests relay of some transactions. - fn on_transactions(&self, _ctx: &EventContext, _relay: &[UnverifiedTransaction]) { } + fn on_transactions(&self, _ctx: &dyn EventContext, _relay: &[UnverifiedTransaction]) { } /// Called when a peer responds to requests. /// Responses not guaranteed to contain valid data and are not yet checked against /// the requests they correspond to. - fn on_responses(&self, _ctx: &EventContext, _req_id: ReqId, _responses: &[Response]) { } + fn on_responses(&self, _ctx: &dyn EventContext, _req_id: ReqId, _responses: &[Response]) { } /// Called when a peer responds with a transaction proof. Each proof is a vector of state items. - fn on_transaction_proof(&self, _ctx: &EventContext, _req_id: ReqId, _state_items: &[DBValue]) { } + fn on_transaction_proof(&self, _ctx: &dyn EventContext, _req_id: ReqId, _state_items: &[DBValue]) { } /// Called to "tick" the handler periodically. - fn tick(&self, _ctx: &BasicContext) { } + fn tick(&self, _ctx: &dyn BasicContext) { } /// Called on abort. This signals to handlers that they should clean up /// and ignore peers. // TODO: coreresponding `on_activate`? @@ -290,7 +290,7 @@ pub struct Params { /// Initial capabilities. pub capabilities: Capabilities, /// The sample store (`None` if data shouldn't persist between runs). - pub sample_store: Option>, + pub sample_store: Option>, } /// Type alias for convenience. @@ -391,7 +391,7 @@ impl Statistics { // Locks must be acquired in the order declared, and when holding a read lock // on the peers, only one peer may be held at a time. pub struct LightProtocol { - provider: Arc, + provider: Arc, config: Config, genesis_hash: H256, network_id: u64, @@ -400,16 +400,16 @@ pub struct LightProtocol { capabilities: RwLock, flow_params: RwLock>, free_flow_params: Arc, - handlers: Vec>, + handlers: Vec>, req_id: AtomicUsize, - sample_store: Box, + sample_store: Box, load_distribution: LoadDistribution, statistics: RwLock, } impl LightProtocol { /// Create a new instance of the protocol manager. - pub fn new(provider: Arc, params: Params) -> Self { + pub fn new(provider: Arc, params: Params) -> Self { debug!(target: "pip", "Initializing light protocol handler"); let genesis_hash = provider.chain_info().genesis_hash; @@ -473,7 +473,7 @@ impl LightProtocol { /// insufficient credits. Does not check capabilities before sending. /// On success, returns a request id which can later be coordinated /// with an event. - pub fn request_from(&self, io: &IoContext, peer_id: PeerId, requests: Requests) -> Result { + pub fn request_from(&self, io: &dyn IoContext, peer_id: PeerId, requests: Requests) -> Result { let peers = self.peers.read(); let peer = match peers.get(&peer_id) { Some(peer) => peer, @@ -518,7 +518,7 @@ impl LightProtocol { /// Make an announcement of new chain head and capabilities to all peers. /// The announcement is expected to be valid. - pub fn make_announcement(&self, io: &IoContext, mut announcement: Announcement) { + pub fn make_announcement(&self, io: &dyn IoContext, mut announcement: Announcement) { let mut reorgs_map = HashMap::new(); let now = Instant::now(); @@ -568,7 +568,7 @@ impl LightProtocol { /// These are intended to be added when the protocol structure /// is initialized as a means of customizing its behavior, /// and dispatching requests immediately upon events. - pub fn add_handler(&mut self, handler: Arc) { + pub fn add_handler(&mut self, handler: Arc) { self.handlers.push(handler); } @@ -635,7 +635,7 @@ impl LightProtocol { /// Handle a packet using the given io context. /// 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]) { + pub fn handle_packet(&self, io: &dyn IoContext, peer: PeerId, packet_id: u8, data: &[u8]) { let rlp = Rlp::new(data); trace!(target: "pip", "Incoming packet {} from peer {}", packet_id, peer); @@ -664,7 +664,7 @@ impl LightProtocol { } // check timeouts and punish peers. - fn timeout_check(&self, io: &IoContext) { + fn timeout_check(&self, io: &dyn IoContext) { let now = Instant::now(); // handshake timeout @@ -706,7 +706,7 @@ impl LightProtocol { // propagate transactions to relay peers. // if we aren't on the mainnet, we just propagate to all relay peers - fn propagate_transactions(&self, io: &IoContext) { + fn propagate_transactions(&self, io: &dyn IoContext) { if self.capabilities.read().tx_relay { return } let ready_transactions = self.provider.transactions_to_propagate(); @@ -746,7 +746,7 @@ impl LightProtocol { } /// called when a peer connects. - pub fn on_connect(&self, peer: PeerId, io: &IoContext) { + pub fn on_connect(&self, peer: PeerId, io: &dyn IoContext) { let proto_version = match io.protocol_version(peer).ok_or(Error::WrongNetwork) { Ok(pv) => pv, Err(e) => { punish(peer, io, &e); return } @@ -788,7 +788,7 @@ impl LightProtocol { } /// called when a peer disconnects. - pub fn on_disconnect(&self, peer: PeerId, io: &IoContext) { + pub fn on_disconnect(&self, peer: PeerId, io: &dyn IoContext) { trace!(target: "pip", "Peer {} disconnecting", peer); self.pending_peers.write().remove(&peer); @@ -813,8 +813,8 @@ impl LightProtocol { } /// Execute the given closure with a basic context derived from the I/O context. - pub fn with_context(&self, io: &IoContext, f: F) -> T - where F: FnOnce(&BasicContext) -> T + pub fn with_context(&self, io: &dyn IoContext, f: F) -> T + where F: FnOnce(&dyn BasicContext) -> T { f(&TickCtx { io, @@ -822,7 +822,7 @@ impl LightProtocol { }) } - fn tick_handlers(&self, io: &IoContext) { + fn tick_handlers(&self, io: &dyn IoContext) { for handler in &self.handlers { handler.tick(&TickCtx { io, @@ -831,7 +831,7 @@ impl LightProtocol { } } - fn begin_new_cost_period(&self, io: &IoContext) { + fn begin_new_cost_period(&self, io: &dyn IoContext) { self.load_distribution.end_period(&*self.sample_store); let avg_peer_count = self.statistics.read().avg_peer_count(); @@ -872,7 +872,7 @@ impl LightProtocol { impl LightProtocol { // Handle status message from peer. - fn status(&self, peer: PeerId, io: &IoContext, data: &Rlp) -> Result<(), Error> { + fn status(&self, peer: PeerId, io: &dyn IoContext, data: &Rlp) -> Result<(), Error> { let pending = match self.pending_peers.write().remove(&peer) { Some(pending) => pending, None => { @@ -937,7 +937,7 @@ impl LightProtocol { } // Handle an announcement. - fn announcement(&self, peer: PeerId, io: &IoContext, data: &Rlp) -> Result<(), Error> { + fn announcement(&self, peer: PeerId, io: &dyn IoContext, data: &Rlp) -> Result<(), Error> { if !self.peers.read().contains_key(&peer) { debug!(target: "pip", "Ignoring announcement from unknown peer"); return Ok(()) @@ -982,7 +982,7 @@ impl LightProtocol { } // Receive requests from a peer. - fn request(&self, peer_id: PeerId, io: &IoContext, raw: &Rlp) -> Result<(), Error> { + fn request(&self, peer_id: PeerId, io: &dyn IoContext, raw: &Rlp) -> Result<(), Error> { // the maximum amount of requests we'll fill in a single packet. const MAX_REQUESTS: usize = 256; @@ -1050,7 +1050,7 @@ impl LightProtocol { } // handle a packet with responses. - fn response(&self, peer: PeerId, io: &IoContext, raw: &Rlp) -> Result<(), Error> { + fn response(&self, peer: PeerId, io: &dyn 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)?; @@ -1069,7 +1069,7 @@ impl LightProtocol { } // handle an update of request credits parameters. - fn update_credits(&self, peer_id: PeerId, io: &IoContext, raw: &Rlp) -> Result<(), Error> { + fn update_credits(&self, peer_id: PeerId, io: &dyn IoContext, raw: &Rlp) -> Result<(), Error> { let peers = self.peers.read(); let peer = peers.get(&peer_id).ok_or(Error::UnknownPeer)?; @@ -1104,7 +1104,7 @@ impl LightProtocol { } // handle an acknowledgement of request credits update. - fn acknowledge_update(&self, peer_id: PeerId, _io: &IoContext, _raw: &Rlp) -> Result<(), Error> { + fn acknowledge_update(&self, peer_id: PeerId, _io: &dyn 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(); @@ -1123,7 +1123,7 @@ impl LightProtocol { } // Receive a set of transactions to relay. - fn relay_transactions(&self, peer: PeerId, io: &IoContext, data: &Rlp) -> Result<(), Error> { + fn relay_transactions(&self, peer: PeerId, io: &dyn IoContext, data: &Rlp) -> Result<(), Error> { const MAX_TRANSACTIONS: usize = 256; let txs: Vec<_> = data.iter() @@ -1146,7 +1146,7 @@ impl LightProtocol { } // if something went wrong, figure out how much to punish the peer. -fn punish(peer: PeerId, io: &IoContext, e: &Error) { +fn punish(peer: PeerId, io: &dyn IoContext, e: &Error) { match e.punishment() { Punishment::None => {} Punishment::Disconnect => { @@ -1161,7 +1161,7 @@ fn punish(peer: PeerId, io: &IoContext, e: &Error) { } impl NetworkProtocolHandler for LightProtocol { - fn initialize(&self, io: &NetworkContext) { + fn initialize(&self, io: &dyn NetworkContext) { io.register_timer(TIMEOUT, TIMEOUT_INTERVAL) .expect("Error registering sync timer."); io.register_timer(TICK_TIMEOUT, TICK_TIMEOUT_INTERVAL) @@ -1174,19 +1174,19 @@ impl NetworkProtocolHandler for LightProtocol { .expect("Error registering statistics timer."); } - fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { + fn read(&self, io: &dyn NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { self.handle_packet(&io, *peer, packet_id, data); } - fn connected(&self, io: &NetworkContext, peer: &PeerId) { + fn connected(&self, io: &dyn NetworkContext, peer: &PeerId) { self.on_connect(*peer, &io); } - fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { + fn disconnected(&self, io: &dyn NetworkContext, peer: &PeerId) { self.on_disconnect(*peer, &io); } - fn timeout(&self, io: &NetworkContext, timer: TimerToken) { + fn timeout(&self, io: &dyn NetworkContext, timer: TimerToken) { match timer { TIMEOUT => self.timeout_check(&io), TICK_TIMEOUT => self.tick_handlers(&io), diff --git a/ethcore/light/src/net/request_credits.rs b/ethcore/light/src/net/request_credits.rs index c3fc139f4f..36d4478f41 100644 --- a/ethcore/light/src/net/request_credits.rs +++ b/ethcore/light/src/net/request_credits.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/net/request_set.rs b/ethcore/light/src/net/request_set.rs index f3ec635472..18f3b73cf6 100644 --- a/ethcore/light/src/net/request_set.rs +++ b/ethcore/light/src/net/request_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs index ecbe1d3cca..b2f0525fe9 100644 --- a/ethcore/light/src/net/status.rs +++ b/ethcore/light/src/net/status.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -382,7 +382,7 @@ mod tests { protocol_version: 1, network_id: 1, head_td: U256::default(), - head_hash: H256::default(), + head_hash: H256::zero(), head_num: 10, genesis_hash: H256::zero(), last_head: None, @@ -417,7 +417,7 @@ mod tests { protocol_version: 1, network_id: 1, head_td: U256::default(), - head_hash: H256::default(), + head_hash: H256::zero(), head_num: 10, genesis_hash: H256::zero(), last_head: None, @@ -452,7 +452,7 @@ mod tests { protocol_version: 1, network_id: 1, head_td: U256::default(), - head_hash: H256::default(), + head_hash: H256::zero(), head_num: 10, genesis_hash: H256::zero(), last_head: None, @@ -550,7 +550,7 @@ mod tests { protocol_version: 1, network_id: 1, head_td: U256::default(), - head_hash: H256::default(), + head_hash: H256::zero(), head_num: 10, genesis_hash: H256::zero(), last_head: None, diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 2ca7477f2b..43b1feea03 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,8 +21,8 @@ use common_types::blockchain_info::BlockChainInfo; use common_types::encoded; use common_types::ids::BlockId; use common_types::transaction::{Action, PendingTransaction}; -use ethcore::client::{EachBlockWith, TestBlockChainClient}; -use ethereum_types::{H256, U256, Address}; +use ethcore::test_helpers::{EachBlockWith, TestBlockChainClient}; +use ethereum_types::{H256, U256, Address, BigEndianHash}; use net::context::IoContext; use net::load_timer::MOVING_SAMPLE_SIZE; use net::status::{Capabilities, Status}; @@ -158,7 +158,7 @@ impl Provider for TestProvider { fn contract_code(&self, req: request::CompleteCodeRequest) -> Option { Some(CodeResponse { - code: req.block_hash.iter().chain(req.code_hash.iter()).cloned().collect(), + code: req.block_hash.as_bytes().iter().chain(req.code_hash.as_bytes().iter()).cloned().collect(), }) } @@ -261,7 +261,7 @@ fn genesis_mismatch() { let (provider, proto) = setup(capabilities); let mut status = status(provider.client.chain_info()); - status.genesis_hash = H256::default(); + status.genesis_hash = H256::zero(); let packet_body = write_handshake(&status, &capabilities, &proto); @@ -472,16 +472,16 @@ fn get_state_proofs() { } let req_id = 112; - let key1: H256 = U256::from(11223344).into(); - let key2: H256 = U256::from(99988887).into(); + let key1: H256 = BigEndianHash::from_uint(&U256::from(11223344)); + let key2: H256 = BigEndianHash::from_uint(&U256::from(99988887)); let mut builder = Builder::default(); builder.push(Request::Account(IncompleteAccountRequest { - block_hash: H256::default().into(), + block_hash: H256::zero().into(), address_hash: key1.into(), })).unwrap(); builder.push(Request::Storage(IncompleteStorageRequest { - block_hash: H256::default().into(), + block_hash: H256::zero().into(), address_hash: key1.into(), key_hash: key2.into(), })).unwrap(); @@ -492,11 +492,11 @@ fn get_state_proofs() { let response = { let responses = vec![ Response::Account(provider.account_proof(CompleteAccountRequest { - block_hash: H256::default(), + block_hash: H256::zero(), address_hash: key1, }).unwrap()), Response::Storage(provider.storage_proof(CompleteStorageRequest { - block_hash: H256::default(), + block_hash: H256::zero(), address_hash: key1, key_hash: key2, }).unwrap()), @@ -529,8 +529,8 @@ fn get_contract_code() { } let req_id = 112; - let key1: H256 = U256::from(11223344).into(); - let key2: H256 = U256::from(99988887).into(); + let key1: H256 = BigEndianHash::from_uint(&U256::from(11223344)); + let key2: H256 = BigEndianHash::from_uint(&U256::from(99988887)); let request = Request::Code(IncompleteCodeRequest { block_hash: key1.into(), @@ -541,7 +541,7 @@ fn get_contract_code() { let request_body = make_packet(req_id, &requests); let response = { let response = vec![Response::Code(CodeResponse { - code: key1.iter().chain(key2.iter()).cloned().collect(), + code: key1.as_bytes().iter().chain(key2.as_bytes().iter()).cloned().collect(), })]; let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap(); @@ -616,9 +616,9 @@ fn proof_of_execution() { let req_id = 112; let mut request = Request::Execution(request::IncompleteExecutionRequest { - block_hash: H256::default().into(), - from: Address::default(), - action: Action::Call(Address::default()), + block_hash: H256::zero().into(), + from: Address::zero(), + action: Action::Call(Address::zero()), gas: 100.into(), gas_price: 0.into(), value: 0.into(), @@ -755,7 +755,7 @@ fn get_transaction_index() { } let req_id = 112; - let key1: H256 = U256::from(11223344).into(); + let key1: H256 = BigEndianHash::from_uint(&U256::from(11223344)); let request = Request::TransactionIndex(IncompleteTransactionIndexRequest { hash: key1.into(), diff --git a/ethcore/light/src/on_demand/mod.rs b/ethcore/light/src/on_demand/mod.rs index 7d1f4fabf8..121c7c045a 100644 --- a/ethcore/light/src/on_demand/mod.rs +++ b/ethcore/light/src/on_demand/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -39,8 +39,8 @@ use net::{ use cache::Cache; use request::{self as basic_request, Request as NetworkRequest}; use self::request::CheckedRequest; +use machine::executed::ExecutionResult; -pub use ethcore::executed::ExecutionResult; pub use self::request::{Request, Response, HeaderRef, Error as ValidityError}; pub use self::request_guard::{RequestGuard, Error as RequestError}; pub use self::response_guard::{ResponseGuard, Error as ResponseGuardError, Inner as ResponseGuardInner}; @@ -66,32 +66,31 @@ pub const DEFAULT_NUM_CONSECUTIVE_FAILED_REQUESTS: usize = 1; /// OnDemand related errors pub mod error { - // Silence: `use of deprecated item 'std::error::Error::cause': replaced by Error::source, which can support downcasting` - // https://github.com/paritytech/parity-ethereum/issues/10302 - #![allow(deprecated)] - use futures::sync::oneshot::Canceled; - error_chain! { - - foreign_links { - ChannelCanceled(Canceled) #[doc = "Canceled oneshot channel"]; - } - - errors { - #[doc = "Timeout bad response"] - BadResponse(err: String) { - description("Max response evaluation time exceeded") - display("{}", err) - } + /// OnDemand Error + #[derive(Debug, derive_more::Display, derive_more::From)] + pub enum Error { + /// Canceled oneshot channel + ChannelCanceled(Canceled), + /// Timeout bad response + BadResponse(String), + /// OnDemand requests limit exceeded + #[display(fmt = "OnDemand request maximum backoff iterations exceeded")] + RequestLimit, + } - #[doc = "OnDemand requests limit exceeded"] - RequestLimit { - description("OnDemand request maximum backoff iterations exceeded") - display("OnDemand request maximum backoff iterations exceeded") + impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::ChannelCanceled(err) => Some(err), + _ => None, } } } + + /// OnDemand Result + pub type Result = std::result::Result; } /// Public interface for performing network requests `OnDemand` @@ -99,7 +98,7 @@ pub trait OnDemandRequester: Send + Sync { /// Submit a strongly-typed batch of requests. /// /// Fails if back-reference are not coherent. - fn request(&self, ctx: &BasicContext, requests: T) -> Result, basic_request::NoSuchOutput> + fn request(&self, ctx: &dyn BasicContext, requests: T) -> Result, basic_request::NoSuchOutput> where T: request::RequestAdapter; @@ -107,7 +106,7 @@ pub trait OnDemandRequester: Send + Sync { /// /// Fails if back-references are not coherent. /// The returned vector of responses will correspond to the requests exactly. - fn request_raw(&self, ctx: &BasicContext, requests: Vec) + fn request_raw(&self, ctx: &dyn BasicContext, requests: Vec) -> Result, basic_request::NoSuchOutput>; } @@ -272,7 +271,7 @@ impl Pending { response_err ); - let err = self::error::ErrorKind::BadResponse(err); + let err = self::error::Error::BadResponse(err); if self.sender.send(Err(err.into())).is_err() { debug!(target: "on_demand", "Dropped oneshot channel receiver on no response"); } @@ -280,7 +279,7 @@ impl Pending { // returning a peer discovery timeout during query attempts fn request_limit_reached(self) { - let err = self::error::ErrorKind::RequestLimit; + let err = self::error::Error::RequestLimit; if self.sender.send(Err(err.into())).is_err() { debug!(target: "on_demand", "Dropped oneshot channel receiver on time out"); } @@ -374,7 +373,7 @@ pub struct OnDemand { } impl OnDemandRequester for OnDemand { - fn request_raw(&self, ctx: &BasicContext, requests: Vec) + fn request_raw(&self, ctx: &dyn BasicContext, requests: Vec) -> Result, basic_request::NoSuchOutput> { let (sender, receiver) = oneshot::channel(); @@ -430,7 +429,7 @@ impl OnDemandRequester for OnDemand { Ok(receiver) } - fn request(&self, ctx: &BasicContext, requests: T) -> Result, basic_request::NoSuchOutput> + fn request(&self, ctx: &dyn BasicContext, requests: T) -> Result, basic_request::NoSuchOutput> where T: request::RequestAdapter { self.request_raw(ctx, requests.make_requests()).map(|recv| OnResponses { @@ -504,7 +503,7 @@ impl OnDemand { // maybe dispatch pending requests. // sometimes - fn attempt_dispatch(&self, ctx: &BasicContext) { + fn attempt_dispatch(&self, ctx: &dyn BasicContext) { if !self.no_immediate_dispatch { self.dispatch_pending(ctx) } @@ -512,7 +511,7 @@ impl OnDemand { // dispatch pending requests, and discard those for which the corresponding // receiver has been dropped. - fn dispatch_pending(&self, ctx: &BasicContext) { + fn dispatch_pending(&self, ctx: &dyn BasicContext) { if self.pending.read().is_empty() { return } @@ -567,7 +566,7 @@ impl OnDemand { // submit a pending request set. attempts to answer from cache before // going to the network. if complete, sends response and consumes the struct. - fn submit_pending(&self, ctx: &BasicContext, mut pending: Pending) { + fn submit_pending(&self, ctx: &dyn BasicContext, mut pending: Pending) { // answer as many requests from cache as we can, and schedule for dispatch // if incomplete. @@ -586,7 +585,7 @@ impl OnDemand { impl Handler for OnDemand { fn on_connect( &self, - ctx: &EventContext, + ctx: &dyn EventContext, status: &Status, capabilities: &Capabilities ) -> PeerStatus { @@ -598,7 +597,7 @@ impl Handler for OnDemand { PeerStatus::Kept } - fn on_disconnect(&self, ctx: &EventContext, unfulfilled: &[ReqId]) { + fn on_disconnect(&self, ctx: &dyn EventContext, unfulfilled: &[ReqId]) { self.peers.write().remove(&ctx.peer()); let ctx = ctx.as_basic(); @@ -615,7 +614,7 @@ impl Handler for OnDemand { self.attempt_dispatch(ctx); } - fn on_announcement(&self, ctx: &EventContext, announcement: &Announcement) { + fn on_announcement(&self, ctx: &dyn EventContext, announcement: &Announcement) { { let mut peers = self.peers.write(); if let Some(ref mut peer) = peers.get_mut(&ctx.peer()) { @@ -627,7 +626,7 @@ impl Handler for OnDemand { self.attempt_dispatch(ctx.as_basic()); } - fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[basic_request::Response]) { + fn on_responses(&self, ctx: &dyn EventContext, req_id: ReqId, responses: &[basic_request::Response]) { let mut pending = match self.in_transit.write().remove(&req_id) { Some(req) => req, None => return, @@ -663,7 +662,7 @@ impl Handler for OnDemand { self.submit_pending(ctx.as_basic(), pending); } - fn tick(&self, ctx: &BasicContext) { + fn tick(&self, ctx: &dyn BasicContext) { self.attempt_dispatch(ctx) } } diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index a183dcbcab..5fec132022 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,9 +24,8 @@ use common_types::basic_account::BasicAccount; use common_types::encoded; use common_types::receipt::Receipt; use common_types::transaction::SignedTransaction; -use ethcore::engines::{EthEngine, StateDependentProof}; -use ethcore::machine::EthereumMachine; -use ethcore::state::{self, ProvedExecution}; +use engine::{Engine, StateDependentProof}; +use executive_state::{ProvedExecution, self}; use ethereum_types::{H256, U256, Address}; use ethtrie::{TrieError, TrieDB}; use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY, KECCAK_EMPTY_LIST_RLP, keccak}; @@ -34,7 +33,7 @@ use hash_db::HashDB; use kvdb::DBValue; use parking_lot::Mutex; use request::{self as net_request, IncompleteRequest, CompleteRequest, Output, OutputKind, Field}; -use rlp::{RlpStream, Rlp}; +use rlp::RlpStream; use trie::Trie; use vm::EnvInfo; @@ -981,17 +980,11 @@ impl Account { let state_root = header.state_root(); let mut db = journaldb::new_memory_db(); - for node in proof { db.insert(&node[..]); } + for node in proof { db.insert(hash_db::EMPTY_PREFIX, &node[..]); } - match TrieDB::new(&db, &state_root).and_then(|t| t.get(&keccak(&self.address)))? { + match TrieDB::new(&db, &state_root).and_then(|t| t.get(keccak(&self.address).as_bytes()))? { Some(val) => { - let rlp = Rlp::new(&val); - Ok(Some(BasicAccount { - nonce: rlp.val_at(0)?, - balance: rlp.val_at(1)?, - storage_root: rlp.val_at(2)?, - code_hash: rlp.val_at(3)?, - })) + Ok(Some(rlp::decode::(&val)?)) }, None => { trace!(target: "on_demand", "Account {:?} not found", self.address); @@ -1038,7 +1031,7 @@ pub struct TransactionProof { // TODO: it's not really possible to provide this if the header is unknown. pub env_info: EnvInfo, /// Consensus engine. - pub engine: Arc, + pub engine: Arc, } impl TransactionProof { @@ -1049,7 +1042,7 @@ impl TransactionProof { let mut env_info = self.env_info.clone(); env_info.gas_limit = self.tx.gas; - let proved_execution = state::check_proof( + let proved_execution = executive_state::check_proof( state_items, root, &self.tx, @@ -1081,9 +1074,9 @@ pub struct Signal { /// Block hash and number to fetch proof for. pub hash: H256, /// Consensus engine, used to check the proof. - pub engine: Arc, + pub engine: Arc, /// Special checker for the proof. - pub proof_check: Arc>, + pub proof_check: Arc, } impl Signal { @@ -1106,7 +1099,8 @@ mod tests { use trie::Recorder; use hash::keccak; - use ethcore::client::{BlockChainClient, BlockInfo, TestBlockChainClient, EachBlockWith}; + use ethcore::test_helpers::{TestBlockChainClient, EachBlockWith}; + use client_traits::{BlockInfo, BlockChainClient}; use common_types::header::Header; use common_types::encoded; use common_types::receipt::{Receipt, TransactionOutcome}; @@ -1162,7 +1156,7 @@ mod tests { #[test] fn check_header_with_ancestors() { - let mut last_header_hash = H256::default(); + let mut last_header_hash = H256::zero(); let mut headers = (0..11).map(|num| { let mut header = Header::new(); header.set_number(num); @@ -1209,32 +1203,32 @@ mod tests { // Incorrect responses assert_eq!(header_with_ancestors(invalid_successor.hash().into(), 0) - .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..1]), - Err(Error::WrongHash(invalid_successor.hash(), headers[0].hash()))); + .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..1]), + Err(Error::WrongHash(invalid_successor.hash(), headers[0].hash()))); assert_eq!(header_with_ancestors(headers[0].hash().into(), 0) - .check_response(&cache, &headers[0].hash().into(), &[]), - Err(Error::Empty)); + .check_response(&cache, &headers[0].hash().into(), &[]), + Err(Error::Empty)); assert_eq!(header_with_ancestors(headers[0].hash().into(), 10) - .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..10]), - Err(Error::TooFewResults(11, 10))); + .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..10]), + Err(Error::TooFewResults(11, 10))); assert_eq!(header_with_ancestors(headers[0].hash().into(), 9) - .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..11]), - Err(Error::TooManyResults(10, 11))); + .check_response(&cache, &headers[0].hash().into(), &raw_headers[0..11]), + Err(Error::TooManyResults(10, 11))); let response = &[raw_headers[0].clone(), raw_headers[2].clone()]; assert_eq!(header_with_ancestors(headers[0].hash().into(), 1) - .check_response(&cache, &headers[0].hash().into(), response), - Err(Error::WrongHeaderSequence)); + .check_response(&cache, &headers[0].hash().into(), response), + Err(Error::WrongHeaderSequence)); let response = &[raw_invalid_successor.clone(), raw_headers[0].clone()]; assert_eq!(header_with_ancestors(invalid_successor.hash().into(), 1) - .check_response(&cache, &invalid_successor.hash().into(), response), - Err(Error::WrongHeaderSequence)); + .check_response(&cache, &invalid_successor.hash().into(), response), + Err(Error::WrongHeaderSequence)); let response = &[raw_invalid_successor.clone(), raw_headers[1].clone()]; assert_eq!(header_with_ancestors(invalid_successor.hash().into(), 1) - .check_response(&cache, &invalid_successor.hash().into(), response), - Err(Error::WrongHeaderSequence)); + .check_response(&cache, &invalid_successor.hash().into(), response), + Err(Error::WrongHeaderSequence)); } #[test] @@ -1278,7 +1272,7 @@ mod tests { fn check_state_proof() { use rlp::RlpStream; - let mut root = H256::default(); + let mut root = H256::zero(); let mut db = journaldb::new_memory_db(); let mut header = Header::new(); header.set_number(123_456); @@ -1298,17 +1292,17 @@ mod tests { let mut trie = SecTrieDBMut::new(&mut db, &mut root); for _ in 0..100 { let address = Address::random(); - trie.insert(&*address, &rand_acc()).unwrap(); + trie.insert(address.as_bytes(), &rand_acc()).unwrap(); } - trie.insert(&*addr, &rand_acc()).unwrap(); + trie.insert(addr.as_bytes(), &rand_acc()).unwrap(); } let proof = { let trie = SecTrieDB::new(&db, &root).unwrap(); let mut recorder = Recorder::new(); - trie.get_with(&*addr, &mut recorder).unwrap().unwrap(); + trie.get_with(addr.as_bytes(), &mut recorder).unwrap().unwrap(); recorder.drain().into_iter().map(|r| r.data).collect::>() }; diff --git a/ethcore/light/src/on_demand/request_guard.rs b/ethcore/light/src/on_demand/request_guard.rs index 1c67ab0c87..b81ca1d1d8 100644 --- a/ethcore/light/src/on_demand/request_guard.rs +++ b/ethcore/light/src/on_demand/request_guard.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/on_demand/response_guard.rs b/ethcore/light/src/on_demand/response_guard.rs index c4c2ac23ae..9a1c801dfa 100644 --- a/ethcore/light/src/on_demand/response_guard.rs +++ b/ethcore/light/src/on_demand/response_guard.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/on_demand/tests.rs b/ethcore/light/src/on_demand/tests.rs index 49ec35f10d..0e6ebf5395 100644 --- a/ethcore/light/src/on_demand/tests.rs +++ b/ethcore/light/src/on_demand/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -51,7 +51,7 @@ impl EventContext for Context { } } - fn as_basic(&self) -> &BasicContext { self } + fn as_basic(&self) -> &dyn BasicContext { self } } impl BasicContext for Context { @@ -117,9 +117,9 @@ fn dummy_status() -> Status { protocol_version: 1, network_id: 999, head_td: 1.into(), - head_hash: H256::default(), + head_hash: H256::zero(), head_num: 1359, - genesis_hash: H256::default(), + genesis_hash: H256::zero(), last_head: None, } } @@ -138,7 +138,7 @@ fn detects_hangup() { let on_demand = Harness::create().service; let result = on_demand.request_raw( &Context::NoOp, - vec![request::HeaderByHash(H256::default().into()).into()], + vec![request::HeaderByHash(H256::zero().into()).into()], ); assert_eq!(on_demand.pending.read().len(), 1); @@ -199,7 +199,7 @@ fn no_capabilities() { let _recv = harness.service.request_raw( &Context::NoOp, - vec![request::HeaderByHash(H256::default().into()).into()] + vec![request::HeaderByHash(H256::zero().into()).into()] ).unwrap(); assert_eq!(harness.service.pending.read().len(), 1); @@ -395,7 +395,7 @@ fn wrong_kind() { let _recv = harness.service.request_raw( &Context::NoOp, - vec![request::HeaderByHash(H256::default().into()).into()] + vec![request::HeaderByHash(H256::zero().into()).into()] ).unwrap(); assert_eq!(harness.service.pending.read().len(), 1); diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index 309ff6ec11..6b2f600a61 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,11 +19,18 @@ use std::sync::Arc; -use common_types::blockchain_info::BlockChainInfo; -use common_types::encoded; -use common_types::ids::BlockId; -use common_types::transaction::PendingTransaction; -use ethcore::client::{BlockChainClient, ProvingBlockChainClient, ChainInfo, BlockInfo as ClientBlockInfo}; +use common_types::{ + blockchain_info::BlockChainInfo, + encoded, + ids::BlockId, + transaction::PendingTransaction, +}; +use client_traits::{ + BlockChainClient, + BlockInfo as ClientBlockInfo, + ChainInfo, + ProvingBlockChainClient, +}; use ethereum_types::H256; use parking_lot::RwLock; @@ -389,7 +396,7 @@ impl AsLightClient for LightProvider { #[cfg(test)] mod tests { - use ethcore::client::{EachBlockWith, TestBlockChainClient}; + use ethcore::test_helpers::{EachBlockWith, TestBlockChainClient}; use super::Provider; #[test] diff --git a/ethcore/light/src/transaction_queue.rs b/ethcore/light/src/transaction_queue.rs index 65e646d846..75dd95eefc 100644 --- a/ethcore/light/src/transaction_queue.rs +++ b/ethcore/light/src/transaction_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,12 +24,15 @@ //! address-wise manner. use std::fmt; +use std::sync::Arc; use std::collections::{BTreeMap, HashMap}; use std::collections::hash_map::Entry; use common_types::transaction::{self, Condition, PendingTransaction, SignedTransaction}; use ethereum_types::{H256, U256, Address}; use fastmap::H256FastMap; +use futures::sync::mpsc; +use miner::pool::TxStatus; // Knowledge of an account's current nonce. #[derive(Debug, Clone, PartialEq, Eq)] @@ -126,14 +129,13 @@ pub enum ImportDestination { Future, } -type Listener = Box; - /// Light transaction queue. See module docs for more details. #[derive(Default)] pub struct TransactionQueue { by_account: HashMap, by_hash: H256FastMap, - listeners: Vec, + pending_listeners: Vec>>>, + full_listeners: Vec>>>, } impl fmt::Debug for TransactionQueue { @@ -141,7 +143,8 @@ impl fmt::Debug for TransactionQueue { fmt.debug_struct("TransactionQueue") .field("by_account", &self.by_account) .field("by_hash", &self.by_hash) - .field("listeners", &self.listeners.len()) + .field("pending_listeners", &self.pending_listeners.len()) + .field("full_listeners", &self.pending_listeners.len()) .finish() } } @@ -231,7 +234,7 @@ impl TransactionQueue { }; self.by_hash.insert(hash, tx); - self.notify(&promoted); + self.notify(&promoted, TxStatus::Added); Ok(res) } @@ -343,6 +346,8 @@ impl TransactionQueue { trace!(target: "txqueue", "Culled {} old transactions from sender {} (nonce={})", removed_hashes.len(), address, cur_nonce); + self.notify(&removed_hashes, TxStatus::Culled); + for hash in removed_hashes { self.by_hash.remove(&hash); } @@ -354,15 +359,40 @@ impl TransactionQueue { } /// Add a transaction queue listener. - pub fn add_listener(&mut self, f: Listener) { - self.listeners.push(f); + pub fn pending_transactions_receiver(&mut self) -> mpsc::UnboundedReceiver>> { + let (sender, receiver) = mpsc::unbounded(); + self.pending_listeners.push(sender); + receiver + } + + /// Add a transaction queue listener. + pub fn full_transactions_receiver(&mut self) -> mpsc::UnboundedReceiver>> { + let (sender, receiver) = mpsc::unbounded(); + self.full_listeners.push(sender); + receiver } /// Notifies all listeners about new pending transaction. - fn notify(&self, hashes: &[H256]) { - for listener in &self.listeners { - listener(hashes) + fn notify(&mut self, hashes: &[H256], status: TxStatus) { + if status == TxStatus::Added { + let to_pending_send: Arc> = Arc::new( + hashes + .into_iter() + .map(|hash| hash.clone()) + .collect() + ); + self.pending_listeners.retain(|listener| listener.unbounded_send(to_pending_send.clone()).is_ok()); + } + + let to_full_send: Arc> = Arc::new( + hashes + .into_iter() + .map(|hash| (hash.clone(), status)) + .collect() + ); + + self.full_listeners.retain(|listener| listener.unbounded_send(to_full_send.clone()).is_ok()); } } @@ -374,7 +404,7 @@ mod tests { #[test] fn queued_senders() { - let sender = Address::default(); + let sender = Address::zero(); let mut txq = TransactionQueue::default(); let tx = Transaction::default().fake_sign(sender); @@ -390,7 +420,7 @@ mod tests { #[test] fn next_nonce() { - let sender = Address::default(); + let sender = Address::zero(); let mut txq = TransactionQueue::default(); for i in (0..5).chain(10..15) { @@ -421,7 +451,7 @@ mod tests { #[test] fn current_to_future() { - let sender = Address::default(); + let sender = Address::zero(); let mut txq = TransactionQueue::default(); for i in 5..10 { @@ -464,7 +494,7 @@ mod tests { #[test] fn conditional() { let mut txq = TransactionQueue::default(); - let sender = Address::default(); + let sender = Address::zero(); for i in 0..5 { let mut tx = Transaction::default(); @@ -486,7 +516,7 @@ mod tests { #[test] fn cull_from_future() { - let sender = Address::default(); + let sender = Address::zero(); let mut txq = TransactionQueue::default(); for i in (0..1).chain(3..10) { @@ -506,7 +536,7 @@ mod tests { #[test] fn import_old() { - let sender = Address::default(); + let sender = Address::zero(); let mut txq = TransactionQueue::default(); let mut tx_a = Transaction::default(); @@ -523,7 +553,7 @@ mod tests { #[test] fn replace_is_removed() { - let sender = Address::default(); + let sender = Address::zero(); let mut txq = TransactionQueue::default(); let tx_b: PendingTransaction = Transaction::default().fake_sign(sender).into(); @@ -543,7 +573,7 @@ mod tests { #[test] fn future_transactions() { - let sender = Address::default(); + let sender = Address::zero(); let mut txq = TransactionQueue::default(); for i in (0..1).chain(3..10) { diff --git a/ethcore/light/src/types/mod.rs b/ethcore/light/src/types/mod.rs index 702654b7fd..8b5ab62906 100644 --- a/ethcore/light/src/types/mod.rs +++ b/ethcore/light/src/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/light/src/types/request/batch.rs b/ethcore/light/src/types/request/batch.rs index 63641b5daa..1d4c61b096 100644 --- a/ethcore/light/src/types/request/batch.rs +++ b/ethcore/light/src/types/request/batch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -220,7 +220,7 @@ mod tests { num: 100.into(), })).unwrap(); builder.push(Request::Receipts(IncompleteReceiptsRequest { - hash: H256::default().into(), + hash: H256::zero().into(), })).unwrap(); } @@ -267,7 +267,7 @@ mod tests { })).unwrap(); let mut batch = builder.build(); - batch.requests[1].fill(|_req_idx, _out_idx| Ok(Output::Hash(42.into()))); + batch.requests[1].fill(|_req_idx, _out_idx| Ok(Output::Hash(H256::from_low_u64_be(42)))); assert!(batch.next_complete().is_some()); batch.answered += 1; @@ -289,7 +289,7 @@ mod tests { assert!(batch.next_complete().is_some()); let hdr_proof_res = header_proof::Response { proof: vec![], - hash: 12.into(), + hash: H256::from_low_u64_be(12), td: 21.into(), }; batch.supply_response_unchecked(&hdr_proof_res); @@ -308,7 +308,7 @@ mod tests { })).unwrap(); let mut batch = builder.build(); - batch.requests[1].fill(|_req_idx, _out_idx| Ok(Output::Hash(42.into()))); + batch.requests[1].fill(|_req_idx, _out_idx| Ok(Output::Hash(H256::from_low_u64_be(42)))); assert!(batch.next_complete().is_some()); batch.answered += 1; diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs index cacfbcbe50..e7b5d51069 100644 --- a/ethcore/light/src/types/request/mod.rs +++ b/ethcore/light/src/types/request/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -1507,9 +1507,7 @@ pub mod execution { fn decode(rlp: &Rlp) -> Result { let mut items = Vec::new(); for raw_item in rlp.iter() { - let mut item = DBValue::new(); - item.append_slice(raw_item.data()?); - items.push(item); + items.push(raw_item.data()?.to_vec()); } Ok(Response { items }) @@ -1648,7 +1646,7 @@ mod tests { #[test] fn hash_or_number_roundtrip() { - let hash = HashOrNumber::Hash(H256::default()); + let hash = HashOrNumber::Hash(H256::zero()); let number = HashOrNumber::Number(5); check_roundtrip(hash); @@ -1808,7 +1806,7 @@ mod tests { let full_req = Request::Storage(req.clone()); let res = StorageResponse { proof: vec![vec![1, 2, 3], vec![4, 5, 6]], - value: H256::default(), + value: H256::zero(), }; let full_res = Response::Storage(res.clone()); @@ -1839,8 +1837,6 @@ mod tests { #[test] fn execution_roundtrip() { - use kvdb::DBValue; - let req = IncompleteExecutionRequest { block_hash: Field::Scalar(Default::default()), from: Default::default(), @@ -1852,13 +1848,7 @@ mod tests { }; let full_req = Request::Execution(req.clone()); - let res = ExecutionResponse { - items: vec![DBValue::new(), { - let mut value = DBValue::new(); - value.append_slice(&[1, 1, 1, 2, 3]); - value - }], - }; + let res = ExecutionResponse { items: vec![vec![], vec![1, 1, 1, 2, 3]] }; let full_res = Response::Execution(res.clone()); check_roundtrip(req); @@ -1909,7 +1899,7 @@ mod tests { code_hash: Default::default(), storage_root: Default::default() }), - Response::Storage(StorageResponse { proof: vec![], value: H256::default() }), + Response::Storage(StorageResponse { proof: vec![], value: H256::zero() }), Response::Code(CodeResponse { code: vec![1, 2, 3, 4, 5] }), Response::Execution(ExecutionResponse { items: vec![] }), ]; diff --git a/ethcore/machine/Cargo.toml b/ethcore/machine/Cargo.toml new file mode 100644 index 0000000000..f4db91a6f5 --- /dev/null +++ b/ethcore/machine/Cargo.toml @@ -0,0 +1,54 @@ +[package] +description = "Ethereum state machine" +name = "machine" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[[bench]] +name = "builtin_contract" +harness = false +required-features = ["test-helpers"] + +[dependencies] +account-state = { path = "../account-state" } +client-traits = { path = "../client-traits" } +common-types = { path = "../types" } +crossbeam-utils = "0.6" +ethabi = "9.0.1" +ethabi-contract = "9.0.0" +ethabi-derive = "9.0.1" +ethcore-builtin = { path = "../builtin" } +ethcore-call-contract = { path = "../call-contract" } +ethcore-io = { path = "../../util/io" } +ethereum-types = "0.8.0" +# Used for tests in other crates through the `test-helpers` feature +ethjson = { path = "../../json", optional = true } +evm = { path = "../evm" } +keccak-hash = "0.4.0" +log = "0.4" +lru-cache = "0.1.2" +parity-bytes = "0.1.0" +parking_lot = "0.9" +rlp = "0.4.2" +state-db = { path = "../state-db" } +trace = { path = "../trace" } +trie-vm-factories = { path = "../trie-vm-factories" } +vm = { path = "../vm" } + +[dev-dependencies] +common-types = { path = "../types", features = ["test-helpers"] } +criterion = "0.3" +ethcore = { path = "../", features = ["test-helpers"] } +ethcore-io = { path = "../../util/io" } +ethjson = { path = "../../json" } +parity-crypto = { version = "0.4.2", features = ["publickey"] } +macros = { path = "../../util/macros" } +rustc-hex = "1.0" +spec = { path = "../spec" } +tempdir = "0.3" +trace = { path = "../trace" } + +[features] +test-helpers = ["ethjson"] diff --git a/ethcore/machine/benches/builtin_contract.rs b/ethcore/machine/benches/builtin_contract.rs new file mode 100644 index 0000000000..af8d32b993 --- /dev/null +++ b/ethcore/machine/benches/builtin_contract.rs @@ -0,0 +1,149 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Benchmarking of calling builtin contract + +use std::str::FromStr; + +use account_state::State; +use parity_bytes::Bytes; +use ethcore::test_helpers::get_temp_state_db; +use ethereum_types::{H160, U256}; +use criterion::{black_box, criterion_main, criterion_group, Criterion}; +use machine::{test_helpers, Machine}; +use machine::executive::CallCreateExecutive; +use machine::substate::Substate; +use trace::{NoopTracer, NoopVMTracer}; +use trie_vm_factories::VmFactory; +use vm::{ActionParams, EnvInfo, Schedule}; + +const ECRECOVER: &str = "0000000000000000000000000000000000000001"; +const SHA256: &str = "0000000000000000000000000000000000000002"; +const SIGNED_DATA: &str = "hash000000000001v000000000000002r000000000000003s000000000000004"; + +fn single_builtin_pricing() -> Machine { + test_helpers::load_machine(include_bytes!("../../res/ethereum/builtin_one_activation_bench.json")) +} + +fn multiple_builtin_pricing() -> Machine { + test_helpers::load_machine(include_bytes!("../../res/ethereum/builtin_multi_bench.json")) +} + +fn builtin_params(address: H160, execute: bool) -> ActionParams { + let mut params = ActionParams::default(); + params.code_address = address; + params.gas = u64::max_value().into(); + if execute { + params.data = Some(SIGNED_DATA.bytes().collect::()); + } + params +} + +fn single_activation(c: &mut Criterion) { + let contract = H160::from_str(ECRECOVER).unwrap(); + let params = builtin_params(contract, false); + + let env_info = EnvInfo::default(); + let machine = single_builtin_pricing(); + let schedule = Schedule::default(); + let factory = VmFactory::default(); + let depth = 0; + let stack_depth = 0; + let parent_static_flag = false; + + let db = get_temp_state_db(); + let mut state = State::new(db, U256::from(0), Default::default()); + let mut substate = Substate::new(); + + c.bench_function("single activation", move |b| { + b.iter(|| black_box(CallCreateExecutive::new_call_raw( + params.clone(), + &env_info, + &machine, + &schedule, + &factory, + depth, + stack_depth, + parent_static_flag, + ).exec(&mut state, &mut substate, &mut NoopTracer, &mut NoopVMTracer)) + ) + }); +} + +fn ten_multiple_activations(c: &mut Criterion) { + let contract = H160::from_str(ECRECOVER).unwrap(); + let params = builtin_params(contract, false); + + let env_info = EnvInfo::default(); + let machine = multiple_builtin_pricing(); + let schedule = Schedule::default(); + let factory = VmFactory::default(); + let depth = 0; + let stack_depth = 0; + let parent_static_flag = false; + + let db = get_temp_state_db(); + let mut state = State::new(db, U256::from(0), Default::default()); + let mut substate = Substate::new(); + + c.bench_function("ten activations", move |b| { + b.iter(|| black_box(CallCreateExecutive::new_call_raw( + params.clone(), + &env_info, + &machine, + &schedule, + &factory, + depth, + stack_depth, + parent_static_flag, + ).exec(&mut state, &mut substate, &mut NoopTracer, &mut NoopVMTracer)) + ) + }); +} + +fn fourty_multiple_activations(c: &mut Criterion) { + let contract = H160::from_str(SHA256).unwrap(); + let params = builtin_params(contract, false); + + let env_info = EnvInfo::default(); + let machine = multiple_builtin_pricing(); + let schedule = Schedule::default(); + let factory = VmFactory::default(); + let depth = 0; + let stack_depth = 0; + let parent_static_flag = false; + + let db = get_temp_state_db(); + let mut state = State::new(db, U256::from(0), Default::default()); + let mut substate = Substate::new(); + + c.bench_function("fourty activations", move |b| { + b.iter(|| black_box(CallCreateExecutive::new_call_raw( + params.clone(), + &env_info, + &machine, + &schedule, + &factory, + depth, + stack_depth, + parent_static_flag, + ).exec(&mut state, &mut substate, &mut NoopTracer, &mut NoopVMTracer)) + ) + }); +} + +criterion_group!(benches, single_activation, ten_multiple_activations, fourty_multiple_activations); +criterion_main!(benches); diff --git a/ethcore/res/contracts/tx_acl.json b/ethcore/machine/res/tx_acl.json similarity index 100% rename from ethcore/res/contracts/tx_acl.json rename to ethcore/machine/res/tx_acl.json diff --git a/ethcore/res/contracts/tx_acl_deprecated.json b/ethcore/machine/res/tx_acl_deprecated.json similarity index 100% rename from ethcore/res/contracts/tx_acl_deprecated.json rename to ethcore/machine/res/tx_acl_deprecated.json diff --git a/ethcore/machine/res/tx_acl_gas_price.json b/ethcore/machine/res/tx_acl_gas_price.json new file mode 100644 index 0000000000..bc355dee7e --- /dev/null +++ b/ethcore/machine/res/tx_acl_gas_price.json @@ -0,0 +1,83 @@ +[ + { + "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": "gasPrice", + "type": "uint256" + }, + { + "name": "data", + "type": "bytes" + } + ], + "name": "allowedTxTypes", + "outputs": [ + { + "name": "", + "type": "uint32" + }, + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/accounts/ethkey/src/keccak.rs b/ethcore/machine/src/executed.rs similarity index 61% rename from accounts/ethkey/src/keccak.rs rename to ethcore/machine/src/executed.rs index 202c211933..d83a5832e6 100644 --- a/accounts/ethkey/src/keccak.rs +++ b/ethcore/machine/src/executed.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,18 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use tiny_keccak::Keccak; +//! Transaction execution format module. -pub trait Keccak256 { - fn keccak256(&self) -> T where T: Sized; -} +use trace::{VMTrace, FlatTrace}; +use common_types::{ + engines::machine, + errors::ExecutionError, +}; -impl Keccak256<[u8; 32]> for [u8] { - fn keccak256(&self) -> [u8; 32] { - let mut keccak = Keccak::new_keccak256(); - let mut result = [0u8; 32]; - keccak.update(self); - keccak.finalize(&mut result); - result - } -} +/// /// Transaction execution receipt, parametrised with convenient defaults. +pub type Executed = machine::Executed; + +/// Transaction execution result. +pub type ExecutionResult = Result, ExecutionError>; diff --git a/ethcore/machine/src/executed_block.rs b/ethcore/machine/src/executed_block.rs new file mode 100644 index 0000000000..dac690ece7 --- /dev/null +++ b/ethcore/machine/src/executed_block.rs @@ -0,0 +1,101 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! `ExecutedBlock` is the underlying data structure used by other block types to store block +//! related info. As a block goes through processing we use different types to signal its state: +//! "open", "closed", "locked", "sealed". They all embed an `ExecutedBlock`. + +use std::{ + collections::HashSet, + sync::Arc, +}; + +use ethereum_types::{H256, U256}; + +use account_state::State; +use common_types::{ + header::Header, + receipt::Receipt, + transaction::SignedTransaction, +}; +use state_db::StateDB; +use trace::Tracing; +use vm::{EnvInfo, LastHashes}; + +/// An internal type for a block's common elements. +#[derive(Clone)] +pub struct ExecutedBlock { + /// Executed block header. + pub header: Header, + /// Executed transactions. + pub transactions: Vec, + /// Uncles. + pub uncles: Vec
, + /// Transaction receipts. + pub receipts: Vec, + /// Hashes of already executed transactions. + pub transactions_set: HashSet, + /// Underlying state. + pub state: State, + /// Transaction traces. + pub traces: Tracing, + /// Hashes of last 256 blocks. + pub last_hashes: Arc, +} + +impl ExecutedBlock { + /// Create a new block from the given `state`. + pub fn new(state: State, last_hashes: Arc, tracing: bool) -> ExecutedBlock { + ExecutedBlock { + header: Default::default(), + transactions: Default::default(), + uncles: Default::default(), + receipts: Default::default(), + transactions_set: Default::default(), + state, + traces: if tracing { + Tracing::enabled() + } else { + Tracing::Disabled + }, + last_hashes, + } + } + + /// Get the environment info concerning this block. + pub fn env_info(&self) -> EnvInfo { + // TODO: memoise. + EnvInfo { + number: self.header.number(), + author: self.header.author().clone(), + timestamp: self.header.timestamp(), + difficulty: self.header.difficulty().clone(), + last_hashes: self.last_hashes.clone(), + gas_used: self.receipts.last().map_or(U256::zero(), |r| r.gas_used), + gas_limit: self.header.gas_limit().clone(), + } + } + + /// Get mutable access to a state. + pub fn state_mut(&mut self) -> &mut State { + &mut self.state + } + + /// Get mutable reference to traces. + pub fn traces_mut(&mut self) -> &mut Tracing { + &mut self.traces + } +} diff --git a/ethcore/src/executive.rs b/ethcore/machine/src/executive.rs similarity index 89% rename from ethcore/src/executive.rs rename to ethcore/machine/src/executive.rs index cac6dc7638..9307b0de19 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/machine/src/executive.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,26 +15,36 @@ // along with Parity Ethereum. If not, see . //! Transaction Execution environment. -use std::cmp; -use std::sync::Arc; -use hash::keccak; + +use std::{cmp, convert::TryFrom, sync::Arc}; + +use crossbeam_utils::thread; use ethereum_types::{H256, U256, U512, Address}; -use bytes::{Bytes, BytesRef}; -use state::{Backend as StateBackend, State, Substate, CleanupMode}; -use executed::ExecutionError; -use machine::EthereumMachine as Machine; -use evm::{CallType, Finalize, FinalizationResult}; +use keccak_hash::keccak; +use parity_bytes::{Bytes, BytesRef}; +use rlp::RlpStream; +use log::trace; + +use account_state::{Backend as StateBackend, State, CleanupMode}; +use evm::{ActionType, Finalize, FinalizationResult}; use vm::{ self, EnvInfo, CreateContractAddress, ReturnData, CleanDustMode, ActionParams, ActionValue, Schedule, TrapError, ResumeCall, ResumeCreate }; -use factory::VmFactory; -use externalities::*; +use trie_vm_factories::VmFactory; use trace::{self, Tracer, VMTracer}; -use types::transaction::{Action, SignedTransaction}; -use transaction_ext::Transaction; -use crossbeam_utils::thread; -pub use executed::{Executed, ExecutionResult}; +use common_types::{ + errors::ExecutionError, + transaction::{Action, SignedTransaction}, + engines::machine::Executed, +}; + +use crate::{ + Machine, + substate::Substate, + externalities::{Externalities, OutputPolicy, OriginInfo}, + transaction_ext::Transaction, +}; #[cfg(debug_assertions)] /// Roughly estimate what stack size each level of evm depth will use. (Debug build) @@ -54,8 +64,6 @@ 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) { - use rlp::RlpStream; - match address_scheme { CreateContractAddress::FromSenderAndNonce => { let mut stream = RlpStream::new_list(2); @@ -105,6 +113,15 @@ pub fn into_contract_create_result(result: vm::Result, addre } } +/// Get the cleanup mode object from this. +pub fn cleanup_mode<'a>(substate: &'a mut Substate, schedule: &Schedule) -> CleanupMode<'a> { + match (schedule.kill_dust != CleanDustMode::Off, schedule.no_empty, schedule.kill_empty) { + (false, false, _) => CleanupMode::ForceCreate, + (false, true, false) => CleanupMode::NoEmpty, + (false, true, true) | (true, _, _,) => CleanupMode::TrackTouched(&mut substate.touched), + } +} + /// Transaction execution options. #[derive(Copy, Clone, PartialEq)] pub struct TransactOptions { @@ -200,8 +217,8 @@ enum CallCreateExecutiveKind { CallBuiltin(ActionParams), ExecCall(ActionParams, Substate), ExecCreate(ActionParams, Substate), - ResumeCall(OriginInfo, Box, Substate), - ResumeCreate(OriginInfo, Box, Substate), + ResumeCall(OriginInfo, Box, Substate), + ResumeCreate(OriginInfo, Box, Substate), } /// Executive for a raw call/create action. @@ -224,7 +241,7 @@ impl<'a> CallCreateExecutive<'a> { trace!("Executive::call(params={:?}) self.env_info={:?}, parent_static={}", params, info, parent_static_flag); let gas = params.gas; - let static_flag = parent_static_flag || params.call_type == CallType::StaticCall; + let static_flag = parent_static_flag || params.action_type == ActionType::StaticCall; // if destination is builtin, try to execute it let kind = if let Some(builtin) = machine.builtin(¶ms.code_address, info.number) { @@ -281,7 +298,7 @@ impl<'a> CallCreateExecutive<'a> { } } else { if (static_flag && - (params.call_type == CallType::StaticCall || params.call_type == CallType::Call)) && + (params.action_type == ActionType::StaticCall || params.action_type == ActionType::Call)) && params.value.value() > U256::zero() { return Err(vm::Error::MutableCallInStaticContext); @@ -301,20 +318,20 @@ impl<'a> CallCreateExecutive<'a> { fn transfer_exec_balance(params: &ActionParams, schedule: &Schedule, state: &mut State, substate: &mut Substate) -> vm::Result<()> { if let ActionValue::Transfer(val) = params.value { - state.transfer_balance(¶ms.sender, ¶ms.address, &val, substate.to_cleanup_mode(&schedule))?; + state.transfer_balance(¶ms.sender, ¶ms.address, &val, cleanup_mode(substate, &schedule))?; } Ok(()) } fn transfer_exec_balance_and_init_contract(params: &ActionParams, schedule: &Schedule, state: &mut State, substate: &mut Substate) -> vm::Result<()> { - let nonce_offset = if schedule.no_empty {1} else {0}.into(); + let nonce_offset = if schedule.no_empty { 1 } else { 0 }.into(); let prev_bal = state.balance(¶ms.address)?; if let ActionValue::Transfer(val) = params.value { - state.sub_balance(¶ms.sender, &val, &mut substate.to_cleanup_mode(&schedule))?; - state.new_contract(¶ms.address, val.saturating_add(prev_bal), nonce_offset)?; + state.sub_balance(¶ms.sender, &val, &mut cleanup_mode(substate, &schedule))?; + state.new_contract(¶ms.address, val.saturating_add(prev_bal), nonce_offset, params.code_version)?; } else { - state.new_contract(¶ms.address, prev_bal, nonce_offset)?; + state.new_contract(¶ms.address, prev_bal, nonce_offset, params.code_version)?; } Ok(()) @@ -406,7 +423,7 @@ impl<'a> CallCreateExecutive<'a> { if let Err(e) = result { state.revert_to_checkpoint(); - Err(e.into()) + Err(vm::Error::BuiltIn(e)) } else { state.discard_checkpoint(); @@ -451,12 +468,15 @@ impl<'a> CallCreateExecutive<'a> { let origin_info = OriginInfo::from(¶ms); let exec = self.factory.create(params, self.schedule, self.depth); - let out = { - let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::Return, tracer, vm_tracer); - match exec.exec(&mut ext) { - Ok(val) => Ok(val.finalize(ext)), - Err(err) => Err(err), - } + let out = match exec { + Some(exec) => { + let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::Return, tracer, vm_tracer); + match exec.exec(&mut ext) { + Ok(val) => Ok(val.finalize(ext)), + Err(err) => Err(err), + } + }, + None => Ok(Err(vm::Error::OutOfGas)), }; let res = match out { @@ -499,12 +519,15 @@ impl<'a> CallCreateExecutive<'a> { let origin_info = OriginInfo::from(¶ms); let exec = self.factory.create(params, self.schedule, self.depth); - let out = { - let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::InitContract, tracer, vm_tracer); - match exec.exec(&mut ext) { - Ok(val) => Ok(val.finalize(ext)), - Err(err) => Err(err), - } + let out = match exec { + Some(exec) => { + let mut ext = Self::as_externalities(state, self.info, self.machine, self.schedule, self.depth, self.stack_depth, self.static_flag, &origin_info, &mut unconfirmed_substate, OutputPolicy::InitContract, tracer, vm_tracer); + match exec.exec(&mut ext) { + Ok(val) => Ok(val.finalize(ext)), + Err(err) => Err(err), + } + }, + None => Ok(Err(vm::Error::OutOfGas)), }; let res = match out { @@ -783,7 +806,13 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result, ExecutionError> where T: Tracer, V: VMTracer, { - self.transact_with_tracer(t, options.check_nonce, options.output_from_init_contract, options.tracer, options.vm_tracer) + self.transact_with_tracer( + t, + options.check_nonce, + options.output_from_init_contract, + options.tracer, + options.vm_tracer + ) } /// Execute a transaction in a "virtual" context. @@ -859,11 +888,15 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { if !schedule.keep_unsigned_nonce || !t.is_unsigned() { self.state.inc_nonce(&sender)?; } - self.state.sub_balance(&sender, &U256::from(gas_cost), &mut substate.to_cleanup_mode(&schedule))?; + self.state.sub_balance( + &sender, + &U256::try_from(gas_cost).expect("Total cost (value + gas_cost) is lower than max allowed balance (U256); gas_cost has to fit U256; qed"), + &mut cleanup_mode(&mut substate, &schedule) + )?; let (result, output) = match t.action { Action::Create => { - let (new_address, code_hash) = contract_address(self.machine.create_address_scheme(self.info.number), &sender, &nonce, &t.data); + let (new_address, code_hash) = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &nonce, &t.data); let params = ActionParams { code_address: new_address.clone(), code_hash: code_hash, @@ -874,8 +907,9 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { gas_price: t.gas_price, value: ActionValue::Transfer(t.value), code: Some(Arc::new(t.data.clone())), + code_version: schedule.latest_version, data: None, - call_type: CallType::None, + action_type: ActionType::Create, params_type: vm::ParamsType::Embedded, }; let res = self.create(params, &mut substate, &mut tracer, &mut vm_tracer); @@ -896,8 +930,9 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { value: ActionValue::Transfer(t.value), code: self.state.code(address)?, code_hash: self.state.code_hash(address)?, + code_version: self.state.code_version(address)?, data: Some(t.data.clone()), - call_type: CallType::Call, + action_type: ActionType::Call, params_type: vm::ParamsType::Separate, }; let res = self.call(params, &mut substate, &mut tracer, &mut vm_tracer); @@ -971,7 +1006,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { tracer: &mut T, vm_tracer: &mut V ) -> vm::Result where T: Tracer, V: VMTracer { - let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get()); + let local_stack_size = ethcore_io::LOCAL_STACK_SIZE.with(|sz| sz.get()); let depth_threshold = local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH; if stack_depth != depth_threshold { @@ -1062,7 +1097,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { tracer: &mut T, vm_tracer: &mut V, ) -> vm::Result where T: Tracer, V: VMTracer { - let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get()); + let local_stack_size = ethcore_io::LOCAL_STACK_SIZE.with(|sz| sz.get()); let depth_threshold = local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH; if stack_depth != depth_threshold { @@ -1113,7 +1148,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let suicide_refunds = U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len()); let refunds_bound = sstore_refunds + suicide_refunds; - // real ammount to refund + // real amount to refund let gas_left_prerefund = match result { Ok(FinalizationResult{ gas_left, .. }) => gas_left, _ => 0.into() }; let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1); let gas_left = gas_left_prerefund + refunded; @@ -1126,15 +1161,15 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { } - trace!("exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n", + trace!(target: "executive", "exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n", t.gas, sstore_refunds, suicide_refunds, refunds_bound, gas_left_prerefund, refunded, gas_left, gas_used, refund_value, fees_value); let sender = t.sender(); - trace!("exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, sender); + trace!(target: "executive", "exec::finalize: Refunding refund_value={}, sender={}\n", refund_value, sender); // Below: NoEmpty is safe since the sender must already be non-null to have sent this transaction self.state.add_balance(&sender, &refund_value, CleanupMode::NoEmpty)?; - trace!("exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author); - self.state.add_balance(&self.info.author, &fees_value, substate.to_cleanup_mode(&schedule))?; + trace!(target: "executive", "exec::finalize: Compensating author: fees_value={}, author={}\n", fees_value, &self.info.author); + self.state.add_balance(&self.info.author, &fees_value, cleanup_mode(&mut substate, &schedule))?; // perform suicides for address in &substate.suicides { @@ -1184,35 +1219,83 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { #[cfg(test)] #[allow(dead_code)] mod tests { - use std::sync::Arc; - use std::str::FromStr; + use std::{ + sync::Arc, + str::FromStr, + collections::HashSet, + }; + use rustc_hex::FromHex; - use ethkey::{Generator, Random}; + use ethereum_types::{H256, U256, U512, Address, BigEndianHash}; + + use account_state::CleanupMode; + use common_types::{ + errors::ExecutionError, + transaction::{Action, Transaction}, + }; + use parity_crypto::publickey::{Generator, Random}; + use evm::{Factory, evm_test, evm_test_ignore}; + use macros::vec_into; + use vm::{ActionParams, ActionValue, EnvInfo, CreateContractAddress}; + use ::trace::{ + trace, + FlatTrace, Tracer, NoopTracer, ExecutiveTracer, + VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, VMTracer, NoopVMTracer, ExecutiveVMTracer, + }; + use super::*; - use ethereum_types::{H256, U256, U512, Address}; - use vm::{ActionParams, ActionValue, CallType, EnvInfo, CreateContractAddress}; - use evm::{Factory, VMType}; - use error::ExecutionError; - use machine::EthereumMachine; - use state::{Substate, CleanupMode}; - 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}; - use types::transaction::{Action, Transaction}; - - fn make_frontier_machine(max_depth: usize) -> EthereumMachine { - let mut machine = ::ethereum::new_frontier_test_machine(); + + use crate::{ + Machine, + substate::Substate, + test_helpers::{ + new_frontier_test_machine, + new_byzantium_test_machine, + new_constantinople_test_machine, + new_kovan_wasm_test_machine, + }, + }; + use ethcore::test_helpers::{get_temp_state_with_factory, get_temp_state}; + + fn make_frontier_machine(max_depth: usize) -> Machine { + let mut machine = new_frontier_test_machine(); machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); machine } - fn make_byzantium_machine(max_depth: usize) -> EthereumMachine { - let mut machine = ::ethereum::new_byzantium_test_machine(); + fn make_byzantium_machine(max_depth: usize) -> Machine { + let mut machine = new_byzantium_test_machine(); machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); machine } + #[test] + fn test_cleanup_mode() { + let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); + let mut touched = HashSet::new(); + touched.insert(address); + + let mut substate = Substate::default(); + substate.touched = touched.clone(); + + assert_eq!(CleanupMode::ForceCreate, cleanup_mode(&mut substate, &Schedule::new_frontier())); + assert_eq!(CleanupMode::ForceCreate, cleanup_mode(&mut substate, &Schedule::new_homestead())); + assert_eq!(CleanupMode::TrackTouched(&mut touched), cleanup_mode(&mut substate, &Schedule::new_byzantium())); + assert_eq!(CleanupMode::TrackTouched(&mut touched), cleanup_mode(&mut substate, &Schedule::new_constantinople())); + + assert_eq!(CleanupMode::TrackTouched(&mut touched), cleanup_mode(&mut substate, &{ + let mut schedule = Schedule::new_homestead(); + schedule.kill_dust = CleanDustMode::BasicOnly; + schedule + })); + + assert_eq!(CleanupMode::NoEmpty, cleanup_mode(&mut substate, &{ + let mut schedule = Schedule::new_homestead(); + schedule.no_empty = true; + schedule + })); + } + #[test] fn test_contract_address() { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); @@ -1244,7 +1327,7 @@ mod tests { }; assert_eq!(gas_left, U256::from(79_975)); - assert_eq!(state.storage_at(&address, &H256::new()).unwrap(), H256::from(&U256::from(0xf9u64))); + assert_eq!(state.storage_at(&address, &H256::zero()).unwrap(), BigEndianHash::from_uint(&U256::from(0xf9u64))); assert_eq!(state.balance(&sender).unwrap(), U256::from(0xf9)); assert_eq!(state.balance(&address).unwrap(), U256::from(0x7)); assert_eq!(substate.contracts_created.len(), 0); @@ -1331,7 +1414,7 @@ mod tests { params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); - params.call_type = CallType::Call; + params.action_type = ActionType::Call; let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); @@ -1346,12 +1429,12 @@ mod tests { assert_eq!(tracer.drain(), vec![FlatTrace { action: trace::Action::Call(trace::Call { - from: "4444444444444444444444444444444444444444".into(), - to: "5555555555555555555555555555555555555555".into(), + from: Address::from_str("4444444444444444444444444444444444444444").unwrap(), + to: Address::from_str("5555555555555555555555555555555555555555").unwrap(), value: 100.into(), gas: 100_000.into(), input: vec![], - call_type: CallType::Call + call_type: Some(trace::CallType::Call).into(), }), result: trace::Res::Call(trace::CallResult { gas_used: 33021.into(), @@ -1361,12 +1444,12 @@ mod tests { trace_address: Default::default() }, FlatTrace { action: trace::Action::Call(trace::Call { - from: "5555555555555555555555555555555555555555".into(), - to: "0000000000000000000000000000000000000003".into(), + from: Address::from_str("5555555555555555555555555555555555555555").unwrap(), + to: Address::from_str("0000000000000000000000000000000000000003").unwrap(), value: 1.into(), gas: 66560.into(), input: vec![], - call_type: CallType::Call + call_type: Some(trace::CallType::Call).into(), }), result: trace::Res::Call(trace::CallResult { gas_used: 600.into(), output: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 17, 133, 165, 197, 233, 252, 84, 97, 40, 8, 151, 126, 232, 245, 72, 178, 37, 141, 49] @@ -1415,7 +1498,7 @@ mod tests { params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); - params.call_type = CallType::Call; + params.action_type = ActionType::Call; let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); @@ -1436,12 +1519,12 @@ mod tests { trace_address: Default::default(), subtraces: 1, action: trace::Action::Call(trace::Call { - from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(), - to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + from: Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(), + to: Address::from_str("b010143a42d5980c7e5ef0e4a4416dc098a4fed3").unwrap(), value: 100.into(), gas: 100000.into(), input: vec![], - call_type: CallType::Call, + call_type: Some(trace::CallType::Call).into(), }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(55_248), @@ -1451,10 +1534,11 @@ mod tests { trace_address: vec![0].into_iter().collect(), subtraces: 0, action: trace::Action::Create(trace::Create { - from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + from: Address::from_str("b010143a42d5980c7e5ef0e4a4416dc098a4fed3").unwrap(), value: 23.into(), gas: 67979.into(), - init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85] + init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], + creation_method: Some(trace::CreationMethod::Create), }), result: trace::Res::Create(trace::CreateResult { gas_used: U256::from(3224), @@ -1531,11 +1615,11 @@ mod tests { params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::from(100)); - params.call_type = CallType::Call; + params.action_type = ActionType::Call; let mut state = get_temp_state(); state.add_balance(&sender, &U256::from(100), CleanupMode::NoEmpty).unwrap(); let info = EnvInfo::default(); - let machine = ::ethereum::new_byzantium_test_machine(); + let machine = new_byzantium_test_machine(); let schedule = machine.schedule(info.number); let mut substate = Substate::new(); let mut tracer = ExecutiveTracer::default(); @@ -1552,12 +1636,12 @@ mod tests { trace_address: Default::default(), subtraces: 1, action: trace::Action::Call(trace::Call { - from: "cd1722f3947def4cf144679da39c4c32bdc35681".into(), - to: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + from: Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(), + to: Address::from_str("b010143a42d5980c7e5ef0e4a4416dc098a4fed3").unwrap(), value: 100.into(), gas: 100_000.into(), input: vec![], - call_type: CallType::Call, + call_type: Some(trace::CallType::Call).into(), }), result: trace::Res::Call(trace::CallResult { gas_used: U256::from(37_033), @@ -1567,10 +1651,11 @@ mod tests { trace_address: vec![0].into_iter().collect(), subtraces: 0, action: trace::Action::Create(trace::Create { - from: "b010143a42d5980c7e5ef0e4a4416dc098a4fed3".into(), + from: Address::from_str("b010143a42d5980c7e5ef0e4a4416dc098a4fed3").unwrap(), value: 23.into(), gas: 66_917.into(), - init: vec![0x60, 0x01, 0x60, 0x00, 0xfd] + init: vec![0x60, 0x01, 0x60, 0x00, 0xfd], + creation_method: Some(trace::CreationMethod::Create), }), result: trace::Res::FailedCreate(vm::Error::Reverted.into()), }]; @@ -1628,6 +1713,7 @@ mod tests { value: 100.into(), gas: params.gas, init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], + creation_method: Some(trace::CreationMethod::Create), }), result: trace::Res::Create(trace::CreateResult { gas_used: U256::from(3224), @@ -1818,7 +1904,13 @@ mod tests { }; assert_eq!(gas_left, U256::from(73_237)); - assert_eq!(state.storage_at(&address_a, &H256::from(&U256::from(0x23))).unwrap(), H256::from(&U256::from(1))); + assert_eq!( + state.storage_at( + &address_a, + &BigEndianHash::from_uint(&U256::from(0x23)), + ).unwrap(), + BigEndianHash::from_uint(&U256::from(1)), + ); } // test is incorrect, mk @@ -1863,8 +1955,8 @@ mod tests { }; assert_eq!(gas_left, U256::from(59_870)); - assert_eq!(state.storage_at(&address, &H256::from(&U256::zero())).unwrap(), H256::from(&U256::from(1))); - assert_eq!(state.storage_at(&address, &H256::from(&U256::one())).unwrap(), H256::from(&U256::from(1))); + assert_eq!(state.storage_at(&address, &BigEndianHash::from_uint(&U256::zero())).unwrap(), BigEndianHash::from_uint(&U256::from(1))); + assert_eq!(state.storage_at(&address, &BigEndianHash::from_uint(&U256::one())).unwrap(), BigEndianHash::from_uint(&U256::from(1))); } // test is incorrect, mk @@ -1905,7 +1997,7 @@ mod tests { assert_eq!(state.balance(&sender).unwrap(), U256::from(1)); assert_eq!(state.balance(&contract).unwrap(), U256::from(17)); assert_eq!(state.nonce(&sender).unwrap(), U256::from(1)); - assert_eq!(state.storage_at(&contract, &H256::new()).unwrap(), H256::from(&U256::from(1))); + assert_eq!(state.storage_at(&contract, &H256::zero()).unwrap(), BigEndianHash::from_uint(&U256::from(1))); } evm_test!{test_transact_invalid_nonce: test_transact_invalid_nonce_int} @@ -2061,7 +2153,7 @@ mod tests { params.code = Some(Arc::new(code)); params.value = ActionValue::Transfer(U256::zero()); let info = EnvInfo::default(); - let machine = ::ethereum::new_byzantium_test_machine(); + let machine = new_byzantium_test_machine(); let schedule = machine.schedule(info.number); let mut substate = Substate::new(); @@ -2074,33 +2166,33 @@ mod tests { assert_eq!(result, U256::from(1)); assert_eq!(output[..], returns[..]); - assert_eq!(state.storage_at(&contract_address, &H256::from(&U256::zero())).unwrap(), H256::from(&U256::from(0))); + assert_eq!(state.storage_at(&contract_address, &H256::zero()).unwrap(), H256::zero()); } evm_test!{test_eip1283: test_eip1283_int} fn test_eip1283(factory: Factory) { - let x1 = Address::from(0x1000); - let x2 = Address::from(0x1001); - let y1 = Address::from(0x2001); - let y2 = Address::from(0x2002); - let operating_address = Address::from(0); - let k = H256::new(); + let x1 = Address::from_low_u64_be(0x1000); + let x2 = Address::from_low_u64_be(0x1001); + let y1 = Address::from_low_u64_be(0x2001); + let y2 = Address::from_low_u64_be(0x2002); + let operating_address = Address::zero(); + let k = H256::zero(); let mut state = get_temp_state_with_factory(factory.clone()); - state.new_contract(&x1, U256::zero(), U256::from(1)).unwrap(); + state.new_contract(&x1, U256::zero(), U256::from(1), U256::zero()).unwrap(); state.init_code(&x1, "600160005560006000556001600055".from_hex().unwrap()).unwrap(); - state.new_contract(&x2, U256::zero(), U256::from(1)).unwrap(); + state.new_contract(&x2, U256::zero(), U256::from(1), U256::zero()).unwrap(); state.init_code(&x2, "600060005560016000556000600055".from_hex().unwrap()).unwrap(); - state.new_contract(&y1, U256::zero(), U256::from(1)).unwrap(); + state.new_contract(&y1, U256::zero(), U256::from(1), U256::zero()).unwrap(); state.init_code(&y1, "600060006000600061100062fffffff4".from_hex().unwrap()).unwrap(); - state.new_contract(&y2, U256::zero(), U256::from(1)).unwrap(); + state.new_contract(&y2, U256::zero(), U256::from(1), U256::zero()).unwrap(); state.init_code(&y2, "600060006000600061100162fffffff4".from_hex().unwrap()).unwrap(); let info = EnvInfo::default(); - let machine = ::ethereum::new_constantinople_test_machine(); + let machine = new_constantinople_test_machine(); let schedule = machine.schedule(info.number); - assert_eq!(state.storage_at(&operating_address, &k).unwrap(), H256::from(U256::from(0))); + assert_eq!(state.storage_at(&operating_address, &k).unwrap(), BigEndianHash::from_uint(&U256::from(0))); // Test a call via top-level -> y1 -> x1 let (FinalizationResult { gas_left, .. }, refund, gas) = { let gas = U256::from(0xffffffffffu64); @@ -2118,7 +2210,7 @@ mod tests { assert_eq!(gas_used, U256::from(41860)); assert_eq!(refund, 19800); - assert_eq!(state.storage_at(&operating_address, &k).unwrap(), H256::from(U256::from(1))); + assert_eq!(state.storage_at(&operating_address, &k).unwrap(), BigEndianHash::from_uint(&U256::from(1))); // Test a call via top-level -> y2 -> x2 let (FinalizationResult { gas_left, .. }, refund, gas) = { let gas = U256::from(0xffffffffffu64); @@ -2167,7 +2259,7 @@ mod tests { info.number = 100; // Network with wasm activated at block 10 - let machine = ::ethereum::new_kovan_wasm_test_machine(); + let machine = new_kovan_wasm_test_machine(); let mut output = [0u8; 20]; let FinalizationResult { gas_left: result, return_data, .. } = { diff --git a/ethcore/src/externalities.rs b/ethcore/machine/src/externalities.rs similarity index 85% rename from ethcore/src/externalities.rs rename to ethcore/machine/src/externalities.rs index 27ef053650..0808ebed2d 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/machine/src/externalities.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,20 +15,36 @@ // along with Parity Ethereum. If not, see . //! Transaction Execution environment. -use std::cmp; -use std::sync::Arc; -use ethereum_types::{H256, U256, Address}; -use bytes::Bytes; -use state::{Backend as StateBackend, State, Substate, CleanupMode}; -use machine::EthereumMachine as Machine; -use executive::*; + +use std::{cmp, sync::Arc}; + +use ethereum_types::{H256, U256, Address, BigEndianHash}; +use parity_bytes::Bytes; +use log::{debug, trace, warn}; + +use account_state::{Backend as StateBackend, State, CleanupMode}; +use common_types::{ + transaction::UNSIGNED_SENDER, + log_entry::LogEntry, +}; +use trace::{Tracer, VMTracer}; use vm::{ - self, ActionParams, ActionValue, EnvInfo, CallType, Schedule, + self, ActionParams, ActionValue, EnvInfo, ActionType, Schedule, Ext, ContractCreateResult, MessageCallResult, CreateContractAddress, ReturnData, TrapKind }; -use types::transaction::UNSIGNED_SENDER; -use trace::{Tracer, VMTracer}; + +use crate::{ + Machine, + substate::Substate, + executive::{ + Executive, + contract_address, + into_message_call_result, + into_contract_create_result, + cleanup_mode + }, +}; /// Policy for handling output data on `RETURN` opcode. pub enum OutputPolicy { @@ -96,18 +112,18 @@ impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B> static_flag: bool, ) -> Self { Externalities { - state: state, - env_info: env_info, - depth: depth, - stack_depth: stack_depth, - origin_info: origin_info, - substate: substate, - machine: machine, - schedule: schedule, - output: output, - tracer: tracer, - vm_tracer: vm_tracer, - static_flag: static_flag, + state, + env_info, + depth, + stack_depth, + origin_info, + substate, + machine, + schedule, + output, + tracer, + vm_tracer, + static_flag, } } } @@ -136,10 +152,6 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> } } - fn is_static(&self) -> bool { - return self.static_flag - } - fn exists(&self, address: &Address) -> vm::Result { self.state.exists(address).map_err(Into::into) } @@ -160,12 +172,14 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> if self.env_info.number + 256 >= self.machine.params().eip210_transition { let blockhash_contract_address = self.machine.params().eip210_contract_address; let code_res = self.state.code(&blockhash_contract_address) - .and_then(|code| self.state.code_hash(&blockhash_contract_address).map(|hash| (code, hash))); + .and_then(|code| self.state.code_hash(&blockhash_contract_address).map(|hash| (code, hash))) + .and_then(|(code, hash)| self.state.code_version(&blockhash_contract_address).map(|version| (code, hash, version))); - let (code, code_hash) = match code_res { - Ok((code, hash)) => (code, hash), + let (code, code_hash, code_version) = match code_res { + Ok((code, hash, version)) => (code, hash, version), Err(_) => return H256::zero(), }; + let data: H256 = BigEndianHash::from_uint(number); let params = ActionParams { sender: self.origin_info.address.clone(), @@ -175,18 +189,19 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> origin: self.origin_info.origin.clone(), gas: self.machine.params().eip210_contract_gas, gas_price: 0.into(), - code: code, - code_hash: code_hash, - data: Some(H256::from(number).to_vec()), - call_type: CallType::Call, + code, + code_hash, + code_version, + data: Some(data.as_bytes().to_vec()), + action_type: ActionType::Call, params_type: vm::ParamsType::Separate, }; let mut ex = Executive::new(self.state, self.env_info, self.machine, self.schedule); let r = ex.call_with_crossbeam(params, self.substate, self.stack_depth + 1, self.tracer, self.vm_tracer); let output = match &r { - Ok(ref r) => H256::from(&r.return_data[..32]), - _ => H256::new(), + Ok(ref r) => H256::from_slice(&r.return_data[..32]), + _ => H256::zero(), }; trace!("ext: blockhash contract({}) -> {:?}({}) self.env_info.number={}\n", number, r, output, self.env_info.number); output @@ -213,6 +228,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> gas: &U256, value: &U256, code: &[u8], + parent_version: &U256, address_scheme: CreateContractAddress, trap: bool, ) -> ::std::result::Result { @@ -225,6 +241,12 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> } }; + let create_type = match address_scheme { + CreateContractAddress::FromSenderAndNonce => ActionType::Create, + CreateContractAddress::FromSenderSaltAndCodeHash(_) => ActionType::Create2, + CreateContractAddress::FromSenderAndCodeHash => ActionType::Create2, + }; + // prepare the params let params = ActionParams { code_address: address.clone(), @@ -235,9 +257,10 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> gas_price: self.origin_info.gas_price, value: ActionValue::Transfer(*value), code: Some(Arc::new(code.to_vec())), - code_hash: code_hash, + code_hash, + code_version: *parent_version, data: None, - call_type: CallType::None, + action_type: create_type, params_type: vm::ParamsType::Embedded, }; @@ -268,16 +291,17 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> value: Option, data: &[u8], code_address: &Address, - call_type: CallType, + call_type: ActionType, trap: bool, ) -> ::std::result::Result { trace!(target: "externalities", "call"); let code_res = self.state.code(code_address) - .and_then(|code| self.state.code_hash(code_address).map(|hash| (code, hash))); + .and_then(|code| self.state.code_hash(code_address).map(|hash| (code, hash))) + .and_then(|(code, hash)| self.state.code_version(code_address).map(|version| (code, hash, version))); - let (code, code_hash) = match code_res { - Ok((code, hash)) => (code, hash), + let (code, code_hash, code_version) = match code_res { + Ok((code, hash, version)) => (code, hash, version), Err(_) => return Ok(MessageCallResult::Failed), }; @@ -289,10 +313,11 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> origin: self.origin_info.origin.clone(), gas: *gas, gas_price: self.origin_info.gas_price, - code: code, - code_hash: code_hash, + code, + code_hash, + code_version, data: Some(data.to_vec()), - call_type: call_type, + action_type: call_type, params_type: vm::ParamsType::Separate, }; @@ -325,6 +350,21 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> Ok(self.state.code_size(address)?) } + fn log(&mut self, topics: Vec, data: &[u8]) -> vm::Result<()> { + if self.static_flag { + return Err(vm::Error::MutableCallInStaticContext); + } + + let address = self.origin_info.address.clone(); + self.substate.logs.push(LogEntry { + address, + topics, + data: data.to_vec() + }); + + Ok(()) + } + fn ret(self, gas: &U256, data: &ReturnData, apply_state: bool) -> vm::Result where Self: Sized { match self.output { @@ -348,23 +388,6 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> } } - fn log(&mut self, topics: Vec, data: &[u8]) -> vm::Result<()> { - use types::log_entry::LogEntry; - - if self.static_flag { - return Err(vm::Error::MutableCallInStaticContext); - } - - let address = self.origin_info.address.clone(); - self.substate.logs.push(LogEntry { - address: address, - topics: topics, - data: data.to_vec() - }); - - Ok(()) - } - fn suicide(&mut self, refund_address: &Address) -> vm::Result<()> { if self.static_flag { return Err(vm::Error::MutableCallInStaticContext); @@ -381,7 +404,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> &address, refund_address, &balance, - self.substate.to_cleanup_mode(&self.schedule) + cleanup_mode(&mut self.substate, &self.schedule) )?; } @@ -430,17 +453,28 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) { self.vm_tracer.trace_executed(gas_used, stack_push, mem) } + + fn is_static(&self) -> bool { + return self.static_flag + } } #[cfg(test)] mod tests { + use std::str::FromStr; use ethereum_types::{U256, Address}; - use evm::{EnvInfo, Ext, CallType}; - use state::{State, Substate}; - use test_helpers::get_temp_state; - use super::*; + use evm::{EnvInfo, Ext, ActionType}; + use account_state::State; + use ethcore::test_helpers::get_temp_state; use trace::{NoopTracer, NoopVMTracer}; + use crate::{ + machine::Machine, + substate::Substate, + test_helpers, + }; + use super::*; + fn get_test_origin() -> OriginInfo { OriginInfo { address: Address::zero(), @@ -453,7 +487,7 @@ mod tests { fn get_test_env_info() -> EnvInfo { EnvInfo { number: 100, - author: 0.into(), + author: Address::from_low_u64_be(0), timestamp: 0, difficulty: 0.into(), last_hashes: Arc::new(vec![]), @@ -464,7 +498,7 @@ mod tests { struct TestSetup { state: State<::state_db::StateDB>, - machine: ::machine::EthereumMachine, + machine: Machine, schedule: Schedule, sub_state: Substate, env_info: EnvInfo @@ -478,15 +512,15 @@ mod tests { impl TestSetup { fn new() -> Self { - let machine = ::spec::Spec::new_test_machine(); + let machine = test_helpers::load_machine(include_bytes!("../../res/null_morden.json")); let env_info = get_test_env_info(); let schedule = machine.schedule(env_info.number); TestSetup { state: get_temp_state(), - schedule: schedule, - machine: machine, + schedule, + machine, sub_state: Substate::new(), - env_info: env_info, + env_info, } } } @@ -521,7 +555,7 @@ mod tests { #[test] fn can_return_block_hash() { - let test_hash = H256::from("afafafafafafafafafafafbcbcbcbcbcbcbcbcbcbeeeeeeeeeeeeedddddddddd"); + let test_hash = H256::from_str("afafafafafafafafafafafbcbcbcbcbcbcbcbcbcbeeeeeeeeeeeeedddddddddd").unwrap(); let test_env_number = 0x120001; let mut setup = TestSetup::new(); @@ -558,12 +592,12 @@ mod tests { // this should panic because we have no balance on any account ext.call( &"0000000000000000000000000000000000000000000000000000000000120000".parse::().unwrap(), - &Address::new(), - &Address::new(), + &Address::zero(), + &Address::zero(), Some("0000000000000000000000000000000000000000000000000000000000150000".parse::().unwrap()), &[], - &Address::new(), - CallType::Call, + &Address::zero(), + ActionType::Call, false, ).ok().unwrap(); } @@ -571,7 +605,7 @@ mod tests { #[test] fn can_log() { let log_data = vec![120u8, 110u8]; - let log_topics = vec![H256::from("af0fa234a6af46afa23faf23bcbc1c1cb4bcb7bcbe7e7e7ee3ee2edddddddddd")]; + let log_topics = vec![H256::from_str("af0fa234a6af46afa23faf23bcbc1c1cb4bcb7bcbe7e7e7ee3ee2edddddddddd").unwrap()]; let mut setup = TestSetup::new(); let state = &mut setup.state; @@ -589,7 +623,7 @@ mod tests { #[test] fn can_suicide() { - let refund_account = &Address::new(); + let refund_account = &Address::zero(); let mut setup = TestSetup::new(); let state = &mut setup.state; @@ -617,7 +651,7 @@ mod tests { let address = { let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderAndNonce, false) { + match ext.create(&U256::max_value(), &U256::zero(), &[], &U256::zero(), CreateContractAddress::FromSenderAndNonce, false) { Ok(ContractCreateResult::Created(address, _)) => address, _ => panic!("Test create failed; expected Created, got Failed/Reverted."), } @@ -639,7 +673,7 @@ mod tests { let address = { let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); - match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderSaltAndCodeHash(H256::default()), false) { + match ext.create(&U256::max_value(), &U256::zero(), &[], &U256::zero(), CreateContractAddress::FromSenderSaltAndCodeHash(H256::zero()), false) { Ok(ContractCreateResult::Created(address, _)) => address, _ => panic!("Test create failed; expected Created, got Failed/Reverted."), } diff --git a/ethcore/service/src/stop_guard.rs b/ethcore/machine/src/lib.rs similarity index 54% rename from ethcore/service/src/stop_guard.rs rename to ethcore/machine/src/lib.rs index 168219520a..b2456e9fc1 100644 --- a/ethcore/service/src/stop_guard.rs +++ b/ethcore/machine/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,27 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Stop guard mod - -use std::sync::Arc; -use std::sync::atomic::*; - -/// Stop guard that will set a stop flag on drop -pub struct StopGuard { - flag: Arc, -} - -impl StopGuard { - /// Create a stop guard - pub fn new() -> StopGuard { - StopGuard { - flag: Arc::new(AtomicBool::new(false)) - } - } -} - -impl Drop for StopGuard { - fn drop(&mut self) { - self.flag.store(true, Ordering::Relaxed) - } -} +//! This crate provides a state machine and the facilities needed to execute transactions and the +//! code contained therein, as well as contract based transaction permissions. All ethereum +//! engines embed a `Machine`. + +pub mod executed; +pub mod executed_block; +pub mod executive; +pub mod externalities; +pub mod machine; +pub mod substate; +pub mod transaction_ext; +pub mod tx_filter; + +pub use crate::{ + executed_block::ExecutedBlock, + machine::Machine +}; + +#[cfg(any(test, feature = "test-helpers"))] +pub mod test_helpers; diff --git a/ethcore/src/machine/impls.rs b/ethcore/machine/src/machine.rs similarity index 75% rename from ethcore/src/machine/impls.rs rename to ethcore/machine/src/machine.rs index fca611d4e8..43151d0a02 100644 --- a/ethcore/src/machine/impls.rs +++ b/ethcore/machine/src/machine.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,61 +16,48 @@ //! Ethereum-like state machine definition. -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::cmp; use std::sync::Arc; use ethereum_types::{U256, H256, Address}; use rlp::Rlp; -use types::transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction}; -use types::BlockNumber; -use types::header::Header; -use vm::{CallType, ActionParams, ActionValue, ParamsType}; -use vm::{EnvInfo, Schedule, CreateContractAddress}; - -use block::ExecutedBlock; -use builtin::Builtin; -use call_contract::CallContract; -use client::BlockInfo; -use error::Error; -use executive::Executive; -use spec::CommonParams; -use state::{CleanupMode, Substate}; +use log::debug; + +use common_types::{ + BlockNumber, + header::Header, + engines::{ + EthashExtensions, + params::CommonParams, + }, + errors::{EngineError, EthcoreError as Error}, + transaction::{self, SYSTEM_ADDRESS, UNSIGNED_SENDER, UnverifiedTransaction, SignedTransaction}, +}; +use vm::{ActionType, ActionParams, ActionValue, ParamsType}; +use vm::{EnvInfo, Schedule}; + +use account_state::CleanupMode; +use client_traits::BlockInfo; +use ethcore_builtin::Builtin; +use ethcore_call_contract::CallContract; use trace::{NoopTracer, NoopVMTracer}; -use tx_filter::TransactionFilter; + +use crate::{ + executed_block::ExecutedBlock, + executive::Executive, + substate::Substate, + tx_filter::TransactionFilter, +}; /// Parity tries to round block.gas_limit to multiple of this constant pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]); -/// Ethash-specific extensions. -#[derive(Debug, Clone)] -pub struct EthashExtensions { - /// Homestead transition block number. - pub homestead_transition: BlockNumber, - /// DAO hard-fork transition block (X). - pub dao_hardfork_transition: u64, - /// DAO hard-fork refund contract address (C). - pub dao_hardfork_beneficiary: Address, - /// DAO hard-fork DAO accounts list (L) - pub dao_hardfork_accounts: Vec
, -} - -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), - 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(), - } - } -} - /// Special rules to be applied to the schedule. -pub type ScheduleCreationRules = Fn(&mut Schedule, BlockNumber) + Sync + Send; +pub type ScheduleCreationRules = dyn Fn(&mut Schedule, BlockNumber) + Sync + Send; /// An ethereum-like state machine. -pub struct EthereumMachine { +pub struct Machine { params: CommonParams, builtins: Arc>, tx_filter: Option>, @@ -78,14 +65,14 @@ pub struct EthereumMachine { schedule_rules: Option>, } -impl EthereumMachine { +impl Machine { /// Regular ethereum machine. - pub fn regular(params: CommonParams, builtins: BTreeMap) -> EthereumMachine { + pub fn regular(params: CommonParams, builtins: BTreeMap) -> Machine { let tx_filter = TransactionFilter::from_params(¶ms).map(Arc::new); - EthereumMachine { - params: params, + Machine { + params, builtins: Arc::new(builtins), - tx_filter: tx_filter, + tx_filter, ethash_extensions: None, schedule_rules: None, } @@ -93,8 +80,8 @@ impl EthereumMachine { /// Ethereum machine with ethash extensions. // TODO: either unify or specify to mainnet specifically and include other specific-chain HFs? - pub fn with_ethash_extensions(params: CommonParams, builtins: BTreeMap, extensions: EthashExtensions) -> EthereumMachine { - let mut machine = EthereumMachine::regular(params, builtins); + pub fn with_ethash_extensions(params: CommonParams, builtins: BTreeMap, extensions: EthashExtensions) -> Machine { + let mut machine = Machine::regular(params, builtins); machine.ethash_extensions = Some(extensions); machine } @@ -108,12 +95,10 @@ impl EthereumMachine { pub fn ethash_extensions(&self) -> Option<&EthashExtensions> { self.ethash_extensions.as_ref() } -} -impl EthereumMachine { /// 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 + /// gas, 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 @@ -156,7 +141,7 @@ impl EthereumMachine { value: Option, gas: U256, data: Option>, - call_type: Option, + action_type: Option, ) -> Result, Error> { let env_info = { let mut env_info = block.env_info(); @@ -176,15 +161,16 @@ impl EthereumMachine { value: value.unwrap_or_else(|| ActionValue::Transfer(0.into())), code, code_hash, + code_version: 0.into(), data, - call_type: call_type.unwrap_or(CallType::Call), + action_type: action_type.unwrap_or(ActionType::Call), params_type: ParamsType::Separate, }; let schedule = self.schedule(env_info.number); let mut ex = Executive::new(&mut state, &env_info, self, &schedule); let mut substate = Substate::new(); - let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).map_err(|e| ::engines::EngineError::FailedSystemCall(format!("{}", e)))?; + let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).map_err(|e| EngineError::FailedSystemCall(format!("{}", e)))?; let output = res.return_data.to_vec(); Ok(output) @@ -203,7 +189,7 @@ impl EthereumMachine { block, params.eip210_contract_address, params.eip210_contract_gas, - Some(parent_hash.to_vec()), + Some(parent_hash.as_bytes().to_vec()), )?; } Ok(()) @@ -343,16 +329,6 @@ impl EthereumMachine { } } - /// Returns new contract address generation scheme at given block number. - pub fn create_address_scheme(&self, _number: BlockNumber) -> CreateContractAddress { - CreateContractAddress::FromSenderAndNonce - } - - /// Verify a particular transaction is valid, regardless of order. - 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<(), transaction::Error> { let check_low_s = match self.ethash_extensions { @@ -367,15 +343,18 @@ impl EthereumMachine { } else { None }; - t.verify_basic(check_low_s, chain_id, false)?; + t.verify_basic(check_low_s, chain_id)?; Ok(()) } /// Does verification of the transaction against the parent state. - pub fn verify_transaction(&self, t: &SignedTransaction, parent: &Header, client: &C) - -> Result<(), transaction::Error> - { + pub fn verify_transaction( + &self, + t: &SignedTransaction, + parent: &Header, + client: &C + ) -> Result<(), transaction::Error> { if let Some(ref filter) = self.tx_filter.as_ref() { if !filter.transaction_allowed(&parent.hash(), parent.number() + 1, t, client) { return Err(transaction::Error::NotAllowed.into()) @@ -385,13 +364,6 @@ impl EthereumMachine { Ok(()) } - /// Additional params. - pub fn additional_params(&self) -> HashMap { - hash_map![ - "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); @@ -401,43 +373,15 @@ impl EthereumMachine { } rlp.as_val().map_err(|e| transaction::Error::InvalidRlp(e.to_string())) } -} - -/// Auxiliary data fetcher for an Ethereum machine. In Ethereum-like machines -/// there are two kinds of auxiliary data: bodies and receipts. -#[derive(Default, Clone)] -pub struct AuxiliaryData<'a> { - /// The full block bytes, including the header. - pub bytes: Option<&'a [u8]>, - /// The block receipts. - pub receipts: Option<&'a [::types::receipt::Receipt]>, -} - -/// Type alias for a function we can make calls through synchronously. -/// Returns the call result and state proof for each call. -pub type Call<'a> = Fn(Address, Vec) -> Result<(Vec, Vec>), String> + 'a; - -/// Request for auxiliary data of a block. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum AuxiliaryRequest { - /// Needs the body. - Body, - /// Needs the receipts. - Receipts, - /// Needs both body and receipts. - Both, -} - -impl super::Machine for EthereumMachine { - type EngineClient = ::client::EngineClient; - - type Error = Error; - fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result { + /// Get the balance, in base units, associated with an account. + /// Extracts data from the live block. + pub fn balance(&self, live: &ExecutedBlock, address: &Address) -> Result { live.state.balance(address).map_err(Into::into) } - fn add_balance(&self, live: &mut ExecutedBlock, address: &Address, amount: &U256) -> Result<(), Error> { + /// Increment the balance of an account in the state of the live block. + pub 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) } } @@ -461,13 +405,16 @@ fn round_block_gas_limit(gas_limit: U256, lower_limit: U256, upper_limit: U256) #[cfg(test)] mod tests { + use std::str::FromStr; + use common_types::header::Header; use super::*; + use spec; fn get_default_ethash_extensions() -> EthashExtensions { EthashExtensions { homestead_transition: 1150000, dao_hardfork_transition: u64::max_value(), - dao_hardfork_beneficiary: "0000000000000000000000000000000000000001".into(), + dao_hardfork_beneficiary: Address::from_str("0000000000000000000000000000000000000001").unwrap(), dao_hardfork_accounts: Vec::new(), } } @@ -476,36 +423,36 @@ mod tests { fn should_disallow_unsigned_transactions() { let rlp = "ea80843b9aca0083015f90948921ebb5f79e9e3920abe571004d0b1d5119c154865af3107a400080038080"; let transaction: UnverifiedTransaction = ::rlp::decode(&::rustc_hex::FromHex::from_hex(rlp).unwrap()).unwrap(); - let spec = ::ethereum::new_ropsten_test(); + let spec = spec::new_ropsten_test(); let ethparams = get_default_ethash_extensions(); - let machine = EthereumMachine::with_ethash_extensions( + let machine = Machine::with_ethash_extensions( spec.params().clone(), Default::default(), ethparams, ); - let mut header = ::types::header::Header::new(); + let mut 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()))); + assert_eq!(res, Err(transaction::Error::InvalidSignature("invalid EC signature".into()))); } #[test] fn ethash_gas_limit_is_multiple_of_determinant() { use ethereum_types::U256; - let spec = ::ethereum::new_homestead_test(); + let spec = spec::new_homestead_test(); let ethparams = get_default_ethash_extensions(); - let machine = EthereumMachine::with_ethash_extensions( + let machine = Machine::with_ethash_extensions( spec.params().clone(), Default::default(), ethparams, ); - let mut parent = ::types::header::Header::new(); - let mut header = ::types::header::Header::new(); + let mut parent = Header::new(); + let mut header = Header::new(); header.set_number(1); // this test will work for this constant only diff --git a/ethcore/src/state/substate.rs b/ethcore/machine/src/substate.rs similarity index 75% rename from ethcore/src/state/substate.rs rename to ethcore/machine/src/substate.rs index 86f6e37f8d..c8cac6f505 100644 --- a/ethcore/src/state/substate.rs +++ b/ethcore/machine/src/substate.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,11 +15,10 @@ // along with Parity Ethereum. If not, see . //! Execution environment substate. + use std::collections::HashSet; use ethereum_types::Address; -use types::log_entry::LogEntry; -use evm::{Schedule, CleanDustMode}; -use super::CleanupMode; +use common_types::log_entry::LogEntry; /// State changes which should be applied in finalize, /// after transaction is fully executed. @@ -55,21 +54,13 @@ impl Substate { self.sstore_clears_refund += s.sstore_clears_refund; self.contracts_created.extend(s.contracts_created); } - - /// Get the cleanup mode object from this. - pub fn to_cleanup_mode(&mut self, schedule: &Schedule) -> CleanupMode { - match (schedule.kill_dust != CleanDustMode::Off, schedule.no_empty, schedule.kill_empty) { - (false, false, _) => CleanupMode::ForceCreate, - (false, true, false) => CleanupMode::NoEmpty, - (false, true, true) | (true, _, _,) => CleanupMode::TrackTouched(&mut self.touched), - } - } } #[cfg(test)] mod tests { + use ethereum_types::Address; + use common_types::log_entry::LogEntry; use super::Substate; - use types::log_entry::LogEntry; #[test] fn created() { @@ -80,19 +71,19 @@ mod tests { #[test] fn accrue() { let mut sub_state = Substate::new(); - sub_state.contracts_created.push(1u64.into()); + sub_state.contracts_created.push(Address::from_low_u64_be(1)); sub_state.logs.push(LogEntry { - address: 1u64.into(), + address: Address::from_low_u64_be(1), topics: vec![], data: vec![] }); sub_state.sstore_clears_refund = (15000 * 5).into(); - sub_state.suicides.insert(10u64.into()); + sub_state.suicides.insert(Address::from_low_u64_be(10)); let mut sub_state_2 = Substate::new(); - sub_state_2.contracts_created.push(2u64.into()); + sub_state_2.contracts_created.push(Address::from_low_u64_be(2u64)); sub_state_2.logs.push(LogEntry { - address: 1u64.into(), + address: Address::from_low_u64_be(1), topics: vec![], data: vec![] }); diff --git a/ethcore/machine/src/test_helpers.rs b/ethcore/machine/src/test_helpers.rs new file mode 100644 index 0000000000..2a1b6ac8d9 --- /dev/null +++ b/ethcore/machine/src/test_helpers.rs @@ -0,0 +1,62 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Provide facilities to create `Machine` instances for testing various networks. + +use std::convert::TryFrom; +use common_types::engines::params::CommonParams; +use ethcore_builtin::Builtin; +use crate::Machine; + +pub fn load_machine(reader: &[u8]) -> Machine { + let spec = ethjson::spec::Spec::load(reader).expect("chain spec is invalid"); + + let builtins = spec.accounts.builtins().into_iter().map(|p| (p.0.into(), Builtin::try_from(p.1).expect("chain spec is invalid"))).collect(); + let params = CommonParams::from(spec.params); + + if let ethjson::spec::Engine::Ethash(ref ethash) = spec.engine { + Machine::with_ethash_extensions(params, builtins, ethash.params.clone().into()) + } else { + Machine::regular(params, builtins) + } +} + +/// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead. +pub fn new_frontier_test_machine() -> Machine { load_machine(include_bytes!("../../res/ethereum/frontier_test.json")) } + +/// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier. +pub fn new_homestead_test_machine() -> Machine { load_machine(include_bytes!("../../res/ethereum/homestead_test.json")) } + +/// Create a new Foundation Homestead-EIP210-era chain spec as though it never changed from Homestead/Frontier. +pub fn new_eip210_test_machine() -> Machine { load_machine(include_bytes!("../../res/ethereum/eip210_test.json")) } + +/// Create a new Foundation Byzantium era spec. +pub fn new_byzantium_test_machine() -> Machine { load_machine(include_bytes!("../../res/ethereum/byzantium_test.json")) } + +/// Create a new Foundation Constantinople era spec. +pub fn new_constantinople_test_machine() -> Machine { load_machine(include_bytes!("../../res/ethereum/constantinople_test.json")) } + +/// Create a new Foundation St. Peter's (Contantinople Fix) era spec. +pub fn new_constantinople_fix_test_machine() -> Machine { load_machine(include_bytes!("../../res/ethereum/st_peters_test.json")) } + +/// Create a new Foundation Istanbul era spec. +pub fn new_istanbul_test_machine() -> Machine { load_machine(include_bytes!("../../res/ethereum/istanbul_test.json")) } + +/// Create a new Musicoin-MCIP3-era spec. +pub fn new_mcip3_test_machine() -> Machine { load_machine(include_bytes!("../../res/ethereum/mcip3_test.json")) } + +/// Create new Kovan spec with wasm activated at certain block +pub fn new_kovan_wasm_test_machine() -> Machine { load_machine(include_bytes!("../../res/ethereum/kovan_wasm_test.json")) } diff --git a/ethcore/src/transaction_ext.rs b/ethcore/machine/src/transaction_ext.rs similarity index 93% rename from ethcore/src/transaction_ext.rs rename to ethcore/machine/src/transaction_ext.rs index fefcd91a34..2182d83514 100644 --- a/ethcore/src/transaction_ext.rs +++ b/ethcore/machine/src/transaction_ext.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Ethereum transaction use evm::Schedule; -use types::transaction::{self, Action}; +use common_types::transaction::{self, Action}; /// Extends transaction with gas verification method. pub trait Transaction { diff --git a/ethcore/src/tx_filter.rs b/ethcore/machine/src/tx_filter.rs similarity index 64% rename from ethcore/src/tx_filter.rs rename to ethcore/machine/src/tx_filter.rs index 3f32ab365a..bbf2a6eb9d 100644 --- a/ethcore/src/tx_filter.rs +++ b/ethcore/machine/src/tx_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,20 +16,26 @@ //! Smart contract based transaction filter. +use ethabi::FunctionOutputDecoder; +use ethabi_contract::use_contract; use ethereum_types::{H256, U256, Address}; +use log::{trace, error}; use lru_cache::LruCache; -use ethabi::FunctionOutputDecoder; -use call_contract::CallContract; -use client::{BlockInfo, BlockId}; +use ethcore_call_contract::CallContract; +use client_traits::BlockInfo; use parking_lot::Mutex; -use spec::CommonParams; -use types::transaction::{Action, SignedTransaction}; -use types::BlockNumber; -use hash::KECCAK_EMPTY; - -use_contract!(transact_acl_deprecated, "res/contracts/tx_acl_deprecated.json"); -use_contract!(transact_acl, "res/contracts/tx_acl.json"); +use common_types::{ + BlockNumber, + ids::BlockId, + engines::params::CommonParams, + transaction::{Action, SignedTransaction} +}; +use keccak_hash::KECCAK_EMPTY; + +use_contract!(transact_acl_deprecated, "res/tx_acl_deprecated.json"); +use_contract!(transact_acl, "res/tx_acl.json"); +use_contract!(transact_acl_gas_price, "res/tx_acl_gas_price.json"); const MAX_CACHE_SIZE: usize = 4096; @@ -47,7 +53,7 @@ pub struct TransactionFilter { contract_address: Address, transition_block: BlockNumber, permission_cache: Mutex>, - contract_version_cache: Mutex>> + contract_version_cache: Mutex>> } impl TransactionFilter { @@ -71,7 +77,7 @@ impl TransactionFilter { let mut contract_version_cache = self.contract_version_cache.lock(); let (tx_type, to) = match transaction.action { - Action::Create => (tx_permissions::CREATE, Address::new()), + Action::Create => (tx_permissions::CREATE, Address::zero()), Action::Call(address) => if client.code_hash(&address, BlockId::Hash(*parent_hash)).map_or(false, |c| c != KECCAK_EMPTY) { (tx_permissions::CALL, address) } else { @@ -81,6 +87,7 @@ impl TransactionFilter { let sender = transaction.sender(); let value = transaction.value; + let gas_price = transaction.gas_price; let key = (*parent_hash, sender); if let Some(permissions) = permission_cache.get_mut(&key) { @@ -110,6 +117,19 @@ impl TransactionFilter { (tx_permissions::NONE, true) }) }, + 3 => { + trace!(target: "tx_filter", "Using filter with gas price and data"); + let (data, decoder) = transact_acl_gas_price::functions::allowed_tx_types::call( + sender, to, value, gas_price, transaction.data.clone() + ); + client.call_contract(BlockId::Hash(*parent_hash), contract_address, data) + .and_then(|value| decoder.decode(&value).map_err(|e| e.to_string())) + .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) @@ -133,8 +153,8 @@ impl TransactionFilter { 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 + "Given transaction data: sender: {:?} to: {:?} value: {}, gas_price: {}. Permissions required: {:X}, got: {:X}", + sender, to, value, gas_price, tx_type, permissions ); permissions & tx_type != 0 } @@ -143,21 +163,31 @@ impl TransactionFilter { #[cfg(test)] mod test { use std::sync::Arc; - use spec::Spec; - use client::{BlockChainClient, Client, ClientConfig, BlockId}; - use miner::Miner; + use std::str::FromStr; + + use tempdir::TempDir; use ethereum_types::{U256, Address}; - use io::IoChannel; - use ethkey::{Secret, KeyPair}; + + use client_traits::BlockChainClient; + use common_types::{ + ids::BlockId, + transaction::{Transaction, Action} + }; + use ethcore::{ + client::{Client, ClientConfig}, + miner::Miner, + test_helpers, + }; + use parity_crypto::publickey::{Secret, KeyPair}; + use ethcore_io::IoChannel; + use spec::Spec; + use super::TransactionFilter; - use types::transaction::{Transaction, Action}; - use tempdir::TempDir; - use test_helpers; /// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f #[test] - fn transaction_filter() { - let spec_data = include_str!("../res/tx_permission_tests/contract_ver_2_genesis.json"); + fn transaction_filter_ver_2() { + let spec_data = include_str!("../../res/tx_permission_tests/contract_ver_2_genesis.json"); let db = test_helpers::new_db(); let tempdir = TempDir::new("").unwrap(); @@ -170,32 +200,32 @@ mod test { 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 key1 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()).unwrap(); + let key2 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000002").unwrap()).unwrap(); + let key3 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000003").unwrap()).unwrap(); + let key4 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000004").unwrap()).unwrap(); + let key5 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000005").unwrap()).unwrap(); + let key6 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000006").unwrap()).unwrap(); + let key7 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000007").unwrap()).unwrap(); let filter = TransactionFilter::from_params(spec.params()).unwrap(); let mut basic_tx = Transaction::default(); - basic_tx.action = Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb")); + basic_tx.action = Action::Call(Address::from_str("d41c057fd1c78805aac12b0a94a405c0461a6fbb").unwrap()); let create_tx = Transaction::default(); let mut call_tx = Transaction::default(); - call_tx.action = Action::Call(Address::from("0000000000000000000000000000000000000005")); + call_tx.action = Action::Call(Address::from_str("0000000000000000000000000000000000000005").unwrap()); 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.action = Action::Call(Address::from_str("d41c057fd1c78805aac12b0a94a405c0461a6fbb").unwrap()); 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.action = Action::Call(Address::from_str("0000000000000000000000000000000000000005").unwrap()); 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")); + basic_tx_to_key6.action = Action::Call(Address::from_str("e57bfe9f44b819898f47bf37e5af72a0783e1141").unwrap()); 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.action = Action::Call(Address::from_str("e57bfe9f44b819898f47bf37e5af72a0783e1141").unwrap()); basic_tx_with_ether_and_to_key6.value = U256::from(123123); let genesis = client.block_hash(BlockId::Latest).unwrap(); @@ -233,10 +263,52 @@ mod test { assert!(!filter.transaction_allowed(&genesis, block_number, &basic_tx_with_ether_and_to_key6.clone().sign(key7.secret(), None), &*client)); } + /// Contract code: res/tx_permission_tests/contract_ver_3.sol + #[test] + fn transaction_filter_ver_3() { + let spec_data = include_str!("../../res/tx_permission_tests/contract_ver_3_genesis.json"); + + let db = test_helpers::new_db(); + let tempdir = TempDir::new("").unwrap(); + let spec = Spec::load(&tempdir.path(), spec_data.as_bytes()).unwrap(); + + let client = Client::new( + ClientConfig::default(), + &spec, + db, + Arc::new(Miner::new_for_tests(&spec, None)), + IoChannel::disconnected(), + ).unwrap(); + let key1 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()).unwrap(); + + // The only difference to version 2 is that the contract now knows the transaction's gas price and data. + // So we only test those: The contract allows only transactions with either nonzero gas price or short data. + + let filter = TransactionFilter::from_params(spec.params()).unwrap(); + let mut tx = Transaction::default(); + tx.action = Action::Call(Address::from_str("0000000000000000000000000000000000000042").unwrap()); + tx.data = b"01234567".to_vec(); + tx.gas_price = 0.into(); + + let genesis = client.block_hash(BlockId::Latest).unwrap(); + let block_number = 1; + + // Data too long and gas price zero. This transaction is not allowed. + assert!(!filter.transaction_allowed(&genesis, block_number, &tx.clone().sign(key1.secret(), None), &*client)); + + // But if we either set a nonzero gas price or short data or both, it is allowed. + tx.gas_price = 1.into(); + assert!(filter.transaction_allowed(&genesis, block_number, &tx.clone().sign(key1.secret(), None), &*client)); + tx.data = b"01".to_vec(); + assert!(filter.transaction_allowed(&genesis, block_number, &tx.clone().sign(key1.secret(), None), &*client)); + tx.gas_price = 0.into(); + assert!(filter.transaction_allowed(&genesis, block_number, &tx.clone().sign(key1.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 spec_data = include_str!("../../res/tx_permission_tests/deprecated_contract_genesis.json"); let db = test_helpers::new_db(); let tempdir = TempDir::new("").unwrap(); @@ -249,17 +321,17 @@ mod test { 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 key1 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap()).unwrap(); + let key2 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000002").unwrap()).unwrap(); + let key3 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000003").unwrap()).unwrap(); + let key4 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000004").unwrap()).unwrap(); let filter = TransactionFilter::from_params(spec.params()).unwrap(); let mut basic_tx = Transaction::default(); - basic_tx.action = Action::Call(Address::from("000000000000000000000000000000000000032")); + basic_tx.action = Action::Call(Address::from_str("0000000000000000000000000000000000000032").unwrap()); let create_tx = Transaction::default(); let mut call_tx = Transaction::default(); - call_tx.action = Action::Call(Address::from("0000000000000000000000000000000000000005")); + call_tx.action = Action::Call(Address::from_str("0000000000000000000000000000000000000005").unwrap()); let genesis = client.block_hash(BlockId::Latest).unwrap(); let block_number = 1; diff --git a/ethcore/node-filter/Cargo.toml b/ethcore/node-filter/Cargo.toml index 80823290c2..cc8430c435 100644 --- a/ethcore/node-filter/Cargo.toml +++ b/ethcore/node-filter/Cargo.toml @@ -7,19 +7,22 @@ version = "1.12.0" authors = ["Parity Technologies "] [dependencies] +client-traits = { path = "../client-traits" } +common-types = { path = "../types" } ethcore = { path = ".."} ethcore-network = { path = "../../util/network" } ethcore-network-devp2p = { path = "../../util/network-devp2p" } -ethereum-types = "0.4" +ethereum-types = "0.8.0" log = "0.4" -parking_lot = "0.7" -ethabi = "6.0" -ethabi-derive = "6.0" -ethabi-contract = "6.0" +parking_lot = "0.9" +ethabi = "9.0.1" +ethabi-derive = "9.0.1" +ethabi-contract = "9.0.0" lru-cache = "0.1" [dev-dependencies] ethcore = { path = "..", features = ["test-helpers"] } -kvdb-memorydb = "0.1" +kvdb-memorydb = "0.3.1" ethcore-io = { path = "../../util/io" } +spec = { path = "../spec" } tempdir = "0.3" diff --git a/ethcore/node-filter/src/lib.rs b/ethcore/node-filter/src/lib.rs index 816bb84a83..a3fb878157 100644 --- a/ethcore/node-filter/src/lib.rs +++ b/ethcore/node-filter/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,6 +16,8 @@ //! Smart contract based node filter. +extern crate client_traits; +extern crate common_types; extern crate ethabi; extern crate ethcore; extern crate ethcore_network as network; @@ -24,8 +26,6 @@ extern crate ethereum_types; extern crate lru_cache; extern crate parking_lot; -#[macro_use] -extern crate ethabi_derive; #[macro_use] extern crate ethabi_contract; #[cfg(test)] @@ -34,31 +34,53 @@ extern crate ethcore_io as io; extern crate kvdb_memorydb; #[cfg(test)] extern crate tempdir; +#[cfg(test)] +extern crate spec; #[macro_use] extern crate log; +use std::collections::{HashMap, VecDeque}; use std::sync::Weak; -use ethcore::client::{BlockChainClient, BlockId}; +use common_types::{ + ids::BlockId, + chain_notify::NewBlocks, +}; +use client_traits::{BlockChainClient, ChainNotify}; use ethereum_types::{H256, Address}; use ethabi::FunctionOutputDecoder; use network::{ConnectionFilter, ConnectionDirection}; use devp2p::NodeId; +use devp2p::MAX_NODES_IN_TABLE; +use parking_lot::RwLock; use_contract!(peer_set, "res/peer_set.json"); /// Connection filter that uses a contract to manage permissions. pub struct NodeFilter { - client: Weak, + client: Weak, contract_address: Address, + cache: RwLock +} + +struct Cache { + cache: HashMap, + order: VecDeque } +// Increase cache size due to possible reserved peers, which do not count in the node table size +pub const CACHE_SIZE: usize = MAX_NODES_IN_TABLE + 1024; + impl NodeFilter { /// Create a new instance. Accepts a contract address. - pub fn new(client: Weak, contract_address: Address) -> NodeFilter { + pub fn new(client: Weak, contract_address: Address) -> NodeFilter { NodeFilter { client, contract_address, + cache: RwLock::new(Cache{ + cache: HashMap::with_capacity(CACHE_SIZE), + order: VecDeque::with_capacity(CACHE_SIZE) + }) } } } @@ -70,6 +92,10 @@ impl ConnectionFilter for NodeFilter { None => return false, }; + if let Some(allowed) = self.cache.read().cache.get(connecting_id) { + return *allowed; + } + let address = self.contract_address; let own_low = H256::from_slice(&own_id[0..32]); let own_high = H256::from_slice(&own_id[32..64]); @@ -83,27 +109,46 @@ impl ConnectionFilter for NodeFilter { debug!("Error callling peer set contract: {:?}", e); false }); - + let mut cache = self.cache.write(); + if cache.cache.len() == CACHE_SIZE { + let popped = cache.order.pop_front().expect("the cache is full so there's at least one item we can pop; qed"); + cache.cache.remove(&popped); + }; + if cache.cache.insert(*connecting_id, allowed).is_none() { + cache.order.push_back(*connecting_id); + } allowed } } +impl ChainNotify for NodeFilter { + fn new_blocks(&self, _new_blocks: NewBlocks) { + let mut cache = self.cache.write(); + cache.cache.clear(); + cache.order.clear(); + } +} + #[cfg(test)] mod test { use std::sync::{Arc, Weak}; - use ethcore::spec::Spec; - use ethcore::client::{BlockChainClient, Client, ClientConfig}; + + use client_traits::BlockChainClient; + use spec::Spec; + use ethcore::client::{Client, ClientConfig}; use ethcore::miner::Miner; use ethcore::test_helpers; use network::{ConnectionDirection, ConnectionFilter, NodeId}; use io::IoChannel; use super::NodeFilter; use tempdir::TempDir; + use ethereum_types::Address; + use std::str::FromStr; /// Contract code: https://gist.github.com/arkpar/467dbcc73cbb85b0997a7a10ffa0695f #[test] fn node_filter() { - let contract_addr = "0000000000000000000000000000000000000005".into(); + let contract_addr = Address::from_str("0000000000000000000000000000000000000005").unwrap(); let data = include_bytes!("../res/node_filter.json"); let tempdir = TempDir::new("").unwrap(); let spec = Spec::load(&tempdir.path(), &data[..]).unwrap(); @@ -116,12 +161,12 @@ mod test { 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 = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002".into(); - let self2: NodeId = "00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003".into(); - let node1: NodeId = "00000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012".into(); - let node2: NodeId = "00000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022".into(); - let nodex: NodeId = "77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); + 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(); assert!(filter.connection_allowed(&self1, &node1, ConnectionDirection::Inbound)); assert!(filter.connection_allowed(&self1, &nodex, ConnectionDirection::Inbound)); diff --git a/ethcore/pod/Cargo.toml b/ethcore/pod/Cargo.toml new file mode 100644 index 0000000000..d3a25d17d2 --- /dev/null +++ b/ethcore/pod/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "pod" +description = "State/Account system expressed in Plain Old Data." +authors = ["Parity Technologies "] +license = "GPL-3.0" +version = "0.1.0" +edition = "2018" + +[dependencies] +common-types = { path = "../types" } +ethereum-types = "0.8.0" +ethjson = { path = "../../json" } +ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +hash-db = "0.15.0" +itertools = "0.8" +keccak-hash = "0.4.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +kvdb = "0.3.1" +log = "0.4" +parity-bytes = "0.1.0" +rlp = "0.4" +rustc-hex = "1" +serde = { version = "1.0", features = ["derive"] } +trie-db = "0.18.0" +triehash = { package = "triehash-ethereum", version = "0.2", path = "../../util/triehash-ethereum" } + +[dev-dependencies] +macros = { path = "../../util/macros" } diff --git a/ethcore/src/pod_account.rs b/ethcore/pod/src/account.rs similarity index 62% rename from ethcore/src/pod_account.rs rename to ethcore/pod/src/account.rs index 39dce6e36d..811bb1eb8c 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/pod/src/account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,24 +15,22 @@ // along with Parity Ethereum. If not, see . //! Account system expressed in Plain Old Data. - -use std::fmt; +use log::warn; use std::collections::BTreeMap; use itertools::Itertools; -use hash::{keccak}; -use ethereum_types::{H256, U256}; +use keccak_hash::keccak; +use ethereum_types::{H256, U256, BigEndianHash}; use hash_db::HashDB; use kvdb::DBValue; use keccak_hasher::KeccakHasher; use triehash::sec_trie_root; -use bytes::Bytes; -use trie::TrieFactory; -use ethtrie::RlpCodec; -use state::Account; +use parity_bytes::Bytes; +use trie_db::TrieFactory; +use ethtrie::Layout; use ethjson; -use types::account_diff::*; +use common_types::account_diff::*; use rlp::{self, RlpStream}; -use serde::Serializer; +use serde::{Serializer, Serialize}; use rustc_hex::ToHex; #[derive(Debug, Clone, PartialEq, Eq, Serialize)] @@ -48,67 +46,45 @@ pub struct PodAccount { pub code: Option, /// The storage of the account. pub storage: BTreeMap, + /// The version of the account. + #[serde(default)] + pub version: U256, } fn opt_bytes_to_hex(opt_bytes: &Option, serializer: S) -> Result where S: Serializer { - serializer.collect_str(&format_args!("0x{}",opt_bytes.as_ref().map_or("".to_string(), |b|b.to_hex()))) + let readable = opt_bytes.as_ref().map(|b| b.to_hex()).unwrap_or_default(); + serializer.collect_str(&format_args!("0x{}", readable)) } impl PodAccount { - /// Convert Account to a PodAccount. - /// NOTE: This will silently fail unless the account is fully cached. - pub fn from_account(acc: &Account) -> PodAccount { - PodAccount { - balance: *acc.balance(), - nonce: *acc.nonce(), - storage: acc.storage_changes().iter().fold(BTreeMap::new(), |mut m, (k, v)| {m.insert(k.clone(), v.clone()); m}), - code: acc.code().map(|x| x.to_vec()), - } - } - /// Returns the RLP for this account. pub fn rlp(&self) -> Bytes { let mut stream = RlpStream::new_list(4); stream.append(&self.nonce); stream.append(&self.balance); - stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k, rlp::encode(&U256::from(&**v)))))); + stream.append(&sec_trie_root(self.storage.iter().map(|(k, v)| (k, rlp::encode(&v.into_uint()))))); stream.append(&keccak(&self.code.as_ref().unwrap_or(&vec![]))); stream.out() } /// Place additional data into given hash DB. - pub fn insert_additional(&self, db: &mut HashDB, factory: &TrieFactory) { + pub fn insert_additional(&self, db: &mut dyn HashDB, factory: &TrieFactory) { match self.code { - Some(ref c) if !c.is_empty() => { db.insert(c); } + Some(ref c) if !c.is_empty() => { db.insert(hash_db::EMPTY_PREFIX, c); } _ => {} } - let mut r = H256::new(); + let mut r = H256::zero(); let mut t = factory.create(db, &mut r); for (k, v) in &self.storage { - if let Err(e) = t.insert(k, &rlp::encode(&U256::from(&**v))) { + if let Err(e) = t.insert(k.as_bytes(), &rlp::encode(&v.into_uint())) { warn!("Encountered potential DB corruption: {}", e); } } } } -impl From for PodAccount { - fn from(a: ethjson::blockchain::Account) -> Self { - PodAccount { - balance: a.balance.into(), - nonce: a.nonce.into(), - code: Some(a.code.into()), - storage: a.storage.into_iter().map(|(key, value)| { - let key: U256 = key.into(); - let value: U256 = value.into(); - (H256::from(key), H256::from(value)) - }).collect(), - } - } -} - impl From for PodAccount { fn from(a: ethjson::spec::Account) -> Self { PodAccount { @@ -118,24 +94,13 @@ impl From for PodAccount { storage: a.storage.map_or_else(BTreeMap::new, |s| s.into_iter().map(|(key, value)| { let key: U256 = key.into(); let value: U256 = value.into(); - (H256::from(key), H256::from(value)) + (BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&value)) }).collect()), + version: a.version.map_or_else(U256::zero, Into::into), } } } -impl fmt::Display for PodAccount { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", - self.balance, - self.nonce, - self.code.as_ref().map_or(0, |c| c.len()), - self.code.as_ref().map_or_else(H256::new, |c| keccak(c)), - self.storage.len(), - ) - } -} - /// Determine difference between two optionally existant `Account`s. Returns None /// if they are the same. pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option { @@ -154,7 +119,7 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option { let storage: Vec<_> = pre.storage.keys().merge(post.storage.keys()) - .filter(|k| pre.storage.get(k).unwrap_or(&H256::new()) != post.storage.get(k).unwrap_or(&H256::new())) + .filter(|k| pre.storage.get(k).unwrap_or(&H256::zero()) != post.storage.get(k).unwrap_or(&H256::zero())) .collect(); let r = AccountDiff { balance: Diff::new(pre.balance, post.balance), @@ -165,8 +130,8 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option, post: Option<&PodAccount>) -> Option 1, 2 => 2, 3 => 3, 4 => 4, 5 => 0, 6 => 0, 7 => 0] + storage: map![ + H256::from_low_u64_be(1) => H256::from_low_u64_be(1), + H256::from_low_u64_be(2) => H256::from_low_u64_be(2), + H256::from_low_u64_be(3) => H256::from_low_u64_be(3), + H256::from_low_u64_be(4) => H256::from_low_u64_be(4), + H256::from_low_u64_be(5) => H256::from_low_u64_be(0), + H256::from_low_u64_be(6) => H256::from_low_u64_be(0), + H256::from_low_u64_be(7) => H256::from_low_u64_be(0) + ], + version: 0.into(), }; let b = PodAccount { balance: 0.into(), nonce: 0.into(), code: Some(vec![]), - storage: map_into![1 => 1, 2 => 3, 3 => 0, 5 => 0, 7 => 7, 8 => 0, 9 => 9] + storage: map![ + H256::from_low_u64_be(1) => H256::from_low_u64_be(1), + H256::from_low_u64_be(2) => H256::from_low_u64_be(3), + H256::from_low_u64_be(3) => H256::from_low_u64_be(0), + H256::from_low_u64_be(5) => H256::from_low_u64_be(0), + H256::from_low_u64_be(7) => H256::from_low_u64_be(7), + H256::from_low_u64_be(8) => H256::from_low_u64_be(0), + H256::from_low_u64_be(9) => H256::from_low_u64_be(9) + ], + version: 0.into(), }; assert_eq!(diff_pod(Some(&a), Some(&b)), Some(AccountDiff { balance: Diff::Same, nonce: Diff::Same, code: Diff::Same, storage: map![ - 2.into() => Diff::new(2.into(), 3.into()), - 3.into() => Diff::new(3.into(), 0.into()), - 4.into() => Diff::new(4.into(), 0.into()), - 7.into() => Diff::new(0.into(), 7.into()), - 9.into() => Diff::new(0.into(), 9.into()) + H256::from_low_u64_be(2) => Diff::new(H256::from_low_u64_be(2), H256::from_low_u64_be(3)), + H256::from_low_u64_be(3) => Diff::new(H256::from_low_u64_be(3), H256::from_low_u64_be(0)), + H256::from_low_u64_be(4) => Diff::new(H256::from_low_u64_be(4), H256::from_low_u64_be(0)), + H256::from_low_u64_be(7) => Diff::new(H256::from_low_u64_be(0), H256::from_low_u64_be(7)), + H256::from_low_u64_be(9) => Diff::new(H256::from_low_u64_be(0), H256::from_low_u64_be(9)) ], })); } diff --git a/json/src/trie/mod.rs b/ethcore/pod/src/lib.rs similarity index 78% rename from json/src/trie/mod.rs rename to ethcore/pod/src/lib.rs index 68fec08761..489512d86f 100644 --- a/json/src/trie/mod.rs +++ b/ethcore/pod/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,12 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Trie test deserialization. +pub mod account; +pub mod state; -mod input; -mod trie; -mod test; - -pub use self::input::Input; -pub use self::trie::Trie; -pub use self::test::Test; +pub use { + account::PodAccount, + state::PodState, +}; diff --git a/ethcore/src/pod_state.rs b/ethcore/pod/src/state.rs similarity index 69% rename from ethcore/src/pod_state.rs rename to ethcore/pod/src/state.rs index c1130faa72..d249813f97 100644 --- a/ethcore/src/pod_state.rs +++ b/ethcore/pod/src/state.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,28 +16,24 @@ //! State of all accounts in the system expressed in Plain Old Data. -use std::fmt; use std::collections::BTreeMap; -use itertools::Itertools; use ethereum_types::{H256, Address}; use triehash::sec_trie_root; -use pod_account::{self, PodAccount}; -use types::state_diff::StateDiff; +use common_types::state_diff::StateDiff; use ethjson; +use serde::Serialize; + +use crate::account::PodAccount; /// State of all accounts in the system expressed in Plain Old Data. #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] pub struct PodState(BTreeMap); impl PodState { - /// Contruct a new object from the `m`. - pub fn new() -> PodState { Default::default() } - - /// Contruct a new object from the `m`. - pub fn from(m: BTreeMap) -> PodState { PodState(m) } - /// Get the underlying map. - pub fn get(&self) -> &BTreeMap { &self.0 } + pub fn get(&self) -> &BTreeMap { + &self.0 + } /// Get the root hash of the trie of the RLP of this. pub fn root(&self) -> H256 { @@ -45,13 +41,8 @@ impl PodState { } /// Drain object to get the underlying map. - pub fn drain(self) -> BTreeMap { self.0 } -} - -impl From for PodState { - fn from(s: ethjson::blockchain::State) -> PodState { - let state = s.into_iter().map(|(addr, acc)| (addr.into(), PodAccount::from(acc))).collect(); - PodState(state) + pub fn drain(self) -> BTreeMap { + self.0 } } @@ -65,21 +56,18 @@ impl From for PodState { } } -impl fmt::Display for PodState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for (add, acc) in &self.0 { - writeln!(f, "{} => {}", add, acc)?; - } - Ok(()) +impl From> for PodState { + fn from(s: BTreeMap) -> Self { + PodState(s) } } /// Calculate and return diff between `pre` state and `post` state. pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { StateDiff { - raw: pre.get().keys() - .merge(post.get().keys()) - .filter_map(|acc| pod_account::diff_pod(pre.get().get(acc), post.get().get(acc)).map(|d| (acc.clone(), d))) + raw: pre.0.keys() + .chain(post.0.keys()) + .filter_map(|acc| crate::account::diff_pod(pre.0.get(acc), post.0.get(acc)).map(|d| (*acc, d))) .collect() } } @@ -87,31 +75,36 @@ pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { #[cfg(test)] mod test { use std::collections::BTreeMap; - use types::state_diff::*; - use types::account_diff::*; - use pod_account::PodAccount; + use common_types::{ + account_diff::{AccountDiff, Diff}, + state_diff::StateDiff, + }; + use ethereum_types::Address; + use crate::account::PodAccount; use super::PodState; + use macros::map; #[test] fn create_delete() { let a = PodState::from(map![ - 1.into() => PodAccount { + Address::from_low_u64_be(1) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), storage: map![], + version: 0.into(), } ]); - assert_eq!(super::diff_pod(&a, &PodState::new()), StateDiff { raw: map![ - 1.into() => AccountDiff{ + assert_eq!(super::diff_pod(&a, &PodState::default()), StateDiff { raw: map![ + Address::from_low_u64_be(1) => AccountDiff{ balance: Diff::Died(69.into()), nonce: Diff::Died(0.into()), code: Diff::Died(vec![]), storage: map![], } ]}); - assert_eq!(super::diff_pod(&PodState::new(), &a), StateDiff{ raw: map![ - 1.into() => AccountDiff{ + assert_eq!(super::diff_pod(&PodState::default(), &a), StateDiff { raw: map![ + Address::from_low_u64_be(1) => AccountDiff{ balance: Diff::Born(69.into()), nonce: Diff::Born(0.into()), code: Diff::Born(vec![]), @@ -123,29 +116,32 @@ mod test { #[test] fn create_delete_with_unchanged() { let a = PodState::from(map![ - 1.into() => PodAccount { + Address::from_low_u64_be(1) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), storage: map![], + version: 0.into(), } ]); let b = PodState::from(map![ - 1.into() => PodAccount { + Address::from_low_u64_be(1) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), storage: map![], + version: 0.into(), }, - 2.into() => PodAccount { + Address::from_low_u64_be(2) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), storage: map![], + version: 0.into(), } ]); assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ - 2.into() => AccountDiff{ + Address::from_low_u64_be(2) => AccountDiff { balance: Diff::Born(69.into()), nonce: Diff::Born(0.into()), code: Diff::Born(vec![]), @@ -153,7 +149,7 @@ mod test { } ]}); assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![ - 2.into() => AccountDiff{ + Address::from_low_u64_be(2) => AccountDiff { balance: Diff::Died(69.into()), nonce: Diff::Died(0.into()), code: Diff::Died(vec![]), @@ -165,35 +161,39 @@ mod test { #[test] fn change_with_unchanged() { let a = PodState::from(map![ - 1.into() => PodAccount { + Address::from_low_u64_be(1) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), storage: map![], + version: 0.into(), }, - 2.into() => PodAccount { + Address::from_low_u64_be(2) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), storage: map![], + version: 0.into(), } ]); let b = PodState::from(map![ - 1.into() => PodAccount { + Address::from_low_u64_be(1) => PodAccount { balance: 69.into(), nonce: 1.into(), code: Some(Vec::new()), storage: map![], + version: 0.into(), }, - 2.into() => PodAccount { + Address::from_low_u64_be(2) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), storage: map![], + version: 0.into(), } ]); assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ - 1.into() => AccountDiff{ + Address::from_low_u64_be(1) => AccountDiff { balance: Diff::Same, nonce: Diff::Changed(0.into(), 1.into()), code: Diff::Same, diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index ae8f83d872..485e42fc6d 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -6,38 +6,50 @@ license = "GPL-3.0" authors = ["Parity Technologies "] [dependencies] +account-state = { path = "../account-state" } +client-traits = { path = "../client-traits" } common-types = { path = "../types" } derive_more = "0.14.0" -ethabi = "6.0" -ethabi-contract = "6.0" -ethabi-derive = "6.0" +ethabi = "9.0.1" +ethabi-contract = "9.0.0" +ethabi-derive = "9.0.1" ethcore = { path = ".." } +ethcore-db = { path = "../db" } ethcore-call-contract = { path = "../call-contract" } ethcore-io = { path = "../../util/io" } ethcore-miner = { path = "../../miner" } -ethereum-types = "0.4" +ethereum-types = "0.8.0" ethjson = { path = "../../json" } -ethkey = { path = "../../accounts/ethkey" } fetch = { path = "../../util/fetch" } futures = "0.1" -heapsize = "0.4" -keccak-hash = "0.1.2" +parity-util-mem = "0.3.0" +hash-db = "0.15.0" +keccak-hash = "0.4.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +kvdb = "0.3.1" log = "0.4" +machine = { path = "../machine" } +journaldb = { path = "../../util/journaldb" } parity-bytes = "0.1" -parity-crypto = "0.3.0" -parking_lot = "0.7" -trie-db = "0.11.0" +parity-crypto = { version = "0.4.2", features = ["publickey"] } +parking_lot = "0.9" +trie-db = "0.18.0" patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" } -rand = "0.3" -rlp = { version = "0.3.0", features = ["ethereum"] } +registrar = { path = "../../util/registrar" } +rlp = "0.4.0" rlp_derive = { path = "../../util/rlp-derive" } rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +spec = { path = "../spec" } +state-db = { path = "../state-db" } +time-utils = { path = "../../util/time-utils" } tiny-keccak = "1.4" +trace = { path = "../trace" } transaction-pool = "2.0.1" -url = "1" +url = "2" +vm = { path = "../vm" } [dev-dependencies] env_logger = "0.5" diff --git a/ethcore/private-tx/src/encryptor.rs b/ethcore/private-tx/src/encryptor.rs index 6a24cf9300..949419af73 100644 --- a/ethcore/private-tx/src/encryptor.rs +++ b/ethcore/private-tx/src/encryptor.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -26,7 +26,7 @@ use std::collections::hash_map::Entry; use parking_lot::Mutex; use ethereum_types::{H128, H256, Address}; use ethjson; -use ethkey::{Signature, Public}; +use crypto::publickey::{Signature, Public}; use crypto; use futures::Future; use fetch::{Fetch, Client as FetchClient, Method, BodyReader, Request}; @@ -81,7 +81,7 @@ pub struct SecretStoreEncryptor { config: EncryptorConfig, client: FetchClient, sessions: Mutex>, - signer: Arc, + signer: Arc, } impl SecretStoreEncryptor { @@ -89,7 +89,7 @@ impl SecretStoreEncryptor { pub fn new( config: EncryptorConfig, client: FetchClient, - signer: Arc, + signer: Arc, ) -> Result { Ok(SecretStoreEncryptor { config, @@ -114,7 +114,7 @@ impl SecretStoreEncryptor { let requester = self.config.key_server_account.ok_or_else(|| Error::KeyServerAccountNotSet)?; // key id in SS is H256 && we have H160 here => expand with assitional zeros - let contract_address_extended: H256 = contract_address.into(); + let contract_address_extended: H256 = (*contract_address).into(); let base_url = self.config.base_url.clone().ok_or_else(|| Error::KeyServerNotSet)?; // prepare request url @@ -156,7 +156,7 @@ impl SecretStoreEncryptor { let decrypted_key = Public::from_slice(&decrypted_bytes); // and now take x coordinate of Public as a key - let key: Bytes = (*decrypted_key)[..INIT_VEC_LEN].into(); + let key: Bytes = decrypted_key.as_bytes()[..INIT_VEC_LEN].into(); // cache the key in the session and clear expired sessions self.sessions.lock().insert(*contract_address, EncryptionSession{ @@ -212,11 +212,11 @@ impl Encryptor for SecretStoreEncryptor { }?; // encrypt data - let mut cypher = Vec::with_capacity(plain_data.len() + initialisation_vector.len()); + let mut cypher = Vec::with_capacity(plain_data.len() + initialisation_vector.as_bytes().len()); cypher.extend(repeat(0).take(plain_data.len())); - crypto::aes::encrypt_128_ctr(&key, initialisation_vector, plain_data, &mut cypher) + crypto::aes::encrypt_128_ctr(&key, initialisation_vector.as_bytes(), plain_data, &mut cypher) .map_err(|e| Error::Encrypt(e.to_string()))?; - cypher.extend_from_slice(&initialisation_vector); + cypher.extend_from_slice(&initialisation_vector.as_bytes()); Ok(cypher) } diff --git a/ethcore/private-tx/src/error.rs b/ethcore/private-tx/src/error.rs index eda08b2a56..4dea9c091c 100644 --- a/ethcore/private-tx/src/error.rs +++ b/ethcore/private-tx/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,12 +19,14 @@ use derive_more::Display; use ethereum_types::Address; use rlp::DecoderError; use ethtrie::TrieError; -use ethcore::error::{Error as EthcoreError, ExecutionError}; -use types::transaction::Error as TransactionError; -use ethkey::Error as KeyError; -use ethkey::crypto::Error as CryptoError; +use types::{ + errors::{EthcoreError, ExecutionError}, + transaction::Error as TransactionError, +}; +use crypto::publickey::Error as CryptoError; use txpool::VerifiedTransaction; use private_transactions::VerifiedPrivateTransaction; +use serde_json::{Error as SerdeError}; type TxPoolError = txpool::Error<::Hash>; @@ -45,6 +47,9 @@ pub enum Error { /// Crypto error. #[display(fmt = "Crypto Error {}", _0)] Crypto(CryptoError), + /// Serialization error. + #[display(fmt = "Serialization Error {}", _0)] + Json(SerdeError), /// Encryption error. #[display(fmt = "Encryption error. ({})", _0)] Encrypt(String), @@ -93,18 +98,30 @@ pub enum Error { /// Account for signing requests to key server not set. #[display(fmt = "Account for signing requests to key server not set.")] KeyServerAccountNotSet, + /// Private state for the contract was not found in the local storage. + #[display(fmt = "Private state for the contract was not found in the local storage.")] + PrivateStateNotFound, + /// Cannot write state to the local database. + #[display(fmt = "Cannot write state to the local database.")] + DatabaseWriteError, /// Encryption key is not found on key server. #[display(fmt = "Encryption key is not found on key server for {}", _0)] EncryptionKeyNotFound(Address), /// Key server URL is not set. #[display(fmt = "Key server URL is not set.")] KeyServerNotSet, + /// Transaction not found in logs. + #[display(fmt = "Private transaction not found in logs.")] + TxNotFoundInLog, + /// Path for logging not set. + #[display(fmt = "Path for logging not set.")] + LoggingPathNotSet, + /// Timestamp overflow error. + #[display(fmt = "Timestamp overflow error.")] + TimestampOverflow, /// VM execution error. #[display(fmt = "VM execution error {}", _0)] Execution(ExecutionError), - /// General signing error. - #[display(fmt = "General signing error {}", _0)] - Key(KeyError), /// Error of transactions processing. #[display(fmt = "Error of transactions processing {}", _0)] Transaction(TransactionError), @@ -117,15 +134,15 @@ pub enum Error { } impl error::Error for Error { - fn source(&self) -> Option<&(error::Error + 'static)> { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::Io(e) => Some(e), Error::Decoder(e) => Some(e), Error::Trie(e) => Some(e), Error::TxPool(e) => Some(e), + Error::Json(e) => Some(e), Error::Crypto(e) => Some(e), Error::Execution(e) => Some(e), - Error::Key(e) => Some(e), Error::Transaction(e) => Some(e), Error::Ethcore(e) => Some(e), _ => None, @@ -145,12 +162,6 @@ impl From for Error { } } -impl From for Error { - fn from(err: KeyError) -> Self { - Error::Key(err).into() - } -} - impl From for Error { fn from(err: CryptoError) -> Self { Error::Crypto(err).into() @@ -187,6 +198,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: SerdeError) -> Self { + Error::Json(err).into() + } +} + impl From for Error { fn from(err: EthcoreError) -> Self { Error::Ethcore(err).into() diff --git a/ethcore/private-tx/src/key_server_keys.rs b/ethcore/private-tx/src/key_server_keys.rs index 28d9b3cb91..13da472530 100644 --- a/ethcore/private-tx/src/key_server_keys.rs +++ b/ethcore/private-tx/src/key_server_keys.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -19,8 +19,9 @@ use std::sync::Arc; use parking_lot::RwLock; use ethereum_types::{H256, Address}; -use call_contract::{CallContract, RegistryInfo}; -use ethcore::client::BlockId; +use call_contract::CallContract; +use registrar::RegistrarClient; +use types::ids::BlockId; use ethabi::FunctionOutputDecoder; const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker"; @@ -29,15 +30,15 @@ use_contract!(keys_acl_contract, "res/keys_acl.json"); /// Returns the address (of the contract), that corresponds to the key pub fn key_to_address(key: &H256) -> Address { - Address::from_slice(&key.to_vec()[..10]) + Address::from_slice(&key.as_bytes()[..10]) } /// Returns the key from the key server associated with the contract pub fn address_to_key(contract_address: &Address) -> H256 { // Current solution uses contract address extended with 0 as id - let contract_address_extended: H256 = contract_address.into(); + let contract_address_extended: H256 = (*contract_address).into(); - H256::from_slice(&contract_address_extended) + H256::from_slice(contract_address_extended.as_bytes()) } /// Trait for keys server keys provider. @@ -53,13 +54,13 @@ pub trait KeyProvider: Send + Sync + 'static { } /// Secret Store keys provider -pub struct SecretStoreKeys where C: CallContract + RegistryInfo + Send + Sync + 'static { +pub struct SecretStoreKeys where C: CallContract + RegistrarClient + Send + Sync + 'static { client: Arc, key_server_account: Option
, keys_acl_contract: RwLock>, } -impl SecretStoreKeys where C: CallContract + RegistryInfo + Send + Sync + 'static { +impl SecretStoreKeys where C: CallContract + RegistrarClient + Send + Sync + 'static { /// Create provider pub fn new(client: Arc, key_server_account: Option
) -> Self { SecretStoreKeys { @@ -70,7 +71,9 @@ impl SecretStoreKeys where C: CallContract + RegistryInfo + Send + Sync + } } -impl KeyProvider for SecretStoreKeys where C: CallContract + RegistryInfo + Send + Sync + 'static { +impl KeyProvider for SecretStoreKeys + where C: CallContract + RegistrarClient + Send + Sync + 'static +{ fn key_server_account(&self) -> Option
{ self.key_server_account } @@ -92,7 +95,11 @@ impl KeyProvider for SecretStoreKeys where C: CallContract + RegistryInfo } fn update_acl_contract(&self) { - let contract_address = self.client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.into(), BlockId::Latest); + let contract_address = self.client.get_address( + ACL_CHECKER_CONTRACT_REGISTRY_NAME, + BlockId::Latest + ).unwrap_or(None); + if *self.keys_acl_contract.read() != contract_address { trace!(target: "privatetx", "Configuring for ACL checker contract from address {:?}", contract_address); @@ -118,7 +125,7 @@ impl Default for StoringKeyProvider { fn default() -> Self { StoringKeyProvider { available_keys: RwLock::new(None), - key_server_account: Some(Address::default()), + key_server_account: Some(Address::zero()), } } } @@ -138,9 +145,11 @@ impl KeyProvider for StoringKeyProvider { #[cfg(test)] mod tests { use std::sync::Arc; - use ethkey::{Secret, KeyPair}; + use std::str::FromStr; + use crypto::publickey::{Secret, KeyPair}; use bytes::Bytes; use super::*; + use registrar::RegistrarClient; struct DummyRegistryClient { registry_address: Option
, @@ -154,20 +163,33 @@ mod tests { } } - impl RegistryInfo for DummyRegistryClient { - fn registry_address(&self, _name: String, _block: BlockId) -> Option
{ self.registry_address } + impl RegistrarClient for DummyRegistryClient { + fn registrar_address(&self) -> Option
{ + unimplemented!() + } + + fn get_address(&self, _name: &str, _block: BlockId) -> Result, String> { + Ok(self.registry_address) + } } impl CallContract for DummyRegistryClient { - fn call_contract(&self, _id: BlockId, _address: Address, _data: Bytes) -> Result { Ok(vec![]) } + fn call_contract( + &self, + _block_id: BlockId, + _address: Address, + _data: Bytes + ) -> Result { + Ok(vec![]) + } } #[test] fn should_update_acl_contract() { - let key = KeyPair::from_secret(Secret::from("0000000000000000000000000000000000000000000000000000000000000011")).unwrap(); + let key = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000011").unwrap()).unwrap(); let client = DummyRegistryClient::new(Some(key.address())); let keys_data = SecretStoreKeys::new(Arc::new(client), None); keys_data.update_acl_contract(); assert_eq!(keys_data.keys_acl_contract.read().unwrap(), key.address()); } -} \ No newline at end of file +} diff --git a/ethcore/private-tx/src/lib.rs b/ethcore/private-tx/src/lib.rs index dfe898b637..a0ebb2cbdb 100644 --- a/ethcore/private-tx/src/lib.rs +++ b/ethcore/private-tx/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,50 +16,64 @@ //! Private transactions module. -// Recursion limit required because of -// error_chain foreign_links. -#![recursion_limit="256"] - mod encryptor; mod key_server_keys; mod private_transactions; mod messages; mod error; +mod log; +mod state_store; +mod private_state_db; +extern crate account_state; +extern crate client_traits; extern crate common_types as types; extern crate ethabi; extern crate ethcore; extern crate ethcore_call_contract as call_contract; +extern crate ethcore_db; extern crate ethcore_io as io; extern crate ethcore_miner; extern crate ethereum_types; extern crate ethjson; -extern crate ethkey; extern crate fetch; extern crate futures; -extern crate heapsize; +extern crate parity_util_mem; +extern crate hash_db; extern crate keccak_hash as hash; +extern crate keccak_hasher; +extern crate kvdb; +extern crate machine; +extern crate journaldb; extern crate parity_bytes as bytes; extern crate parity_crypto as crypto; extern crate parking_lot; extern crate trie_db as trie; extern crate patricia_trie_ethereum as ethtrie; +extern crate registrar; extern crate rlp; +#[macro_use] +extern crate serde_derive; +extern crate serde; +extern crate serde_json; extern crate rustc_hex; +extern crate state_db; +extern crate trace; extern crate transaction_pool as txpool; extern crate url; #[macro_use] -extern crate log; -#[macro_use] +extern crate log as ethlog; extern crate ethabi_derive; #[macro_use] extern crate ethabi_contract; extern crate derive_more; #[macro_use] extern crate rlp_derive; +extern crate vm; + +#[cfg(not(time_checked_add))] +extern crate time_utils; -#[cfg(test)] -extern crate rand; #[cfg(test)] extern crate env_logger; @@ -67,34 +81,46 @@ pub use encryptor::{Encryptor, SecretStoreEncryptor, EncryptorConfig, NoopEncryp pub use key_server_keys::{KeyProvider, SecretStoreKeys, StoringKeyProvider}; pub use private_transactions::{VerifiedPrivateTransaction, VerificationStore, PrivateTransactionSigningDesc, SigningStore}; pub use messages::{PrivateTransaction, SignedPrivateTransaction}; +pub use private_state_db::PrivateStateDB; pub use error::Error; +pub use log::{Logging, TransactionLog, ValidatorLog, PrivateTxStatus, FileLogsSerializer}; +use state_store::{PrivateStateStorage, RequestType}; use std::sync::{Arc, Weak}; use std::collections::{HashMap, HashSet, BTreeMap}; -use ethereum_types::{H128, H256, U256, Address}; +use std::time::Duration; +use ethereum_types::{H128, H256, U256, Address, BigEndianHash}; use hash::keccak; use rlp::*; use parking_lot::RwLock; use bytes::Bytes; -use ethkey::{Signature, recover, public_to_address}; -use io::IoChannel; -use ethcore::executive::{Executive, TransactOptions}; -use ethcore::executed::{Executed}; -use types::transaction::{SignedTransaction, Transaction, Action, UnverifiedTransaction}; -use ethcore::{contract_address as ethcore_contract_address}; -use ethcore::client::{ - Client, ChainNotify, NewBlocks, ChainMessageType, ClientIoMessage, BlockId, - Call, BlockInfo +use crypto::publickey::{Signature, recover, public_to_address, Message, KeyPair}; +use io::{IoChannel, IoHandler, IoContext, TimerToken}; +use machine::{ + executive::{Executive, TransactOptions, contract_address as ethcore_contract_address}, + executed::Executed as FlatExecuted, +}; +use types::{ + chain_notify::{NewBlocks, ChainMessageType}, + ids::BlockId, + io_message::ClientIoMessage, + transaction::{SignedTransaction, Transaction, Action, UnverifiedTransaction}, + engines::machine::Executed, }; +use ethcore::client::{Client, Call}; +use client_traits::{BlockInfo, ChainNotify}; use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache}; -use ethcore::{state, state_db}; -use ethcore::trace::{Tracer, VMTracer}; +use state_db::StateDB; +use account_state::State; +use trace::{Tracer, VMTracer}; use call_contract::CallContract; +use kvdb::KeyValueDB; use rustc_hex::FromHex; use ethabi::FunctionOutputDecoder; +use vm::CreateContractAddress; // 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"); +const DEFAULT_STUB_CONTRACT: &str = include_str!("../res/private.evm"); use_contract!(private_contract, "res/private.json"); @@ -110,6 +136,12 @@ const INITIAL_PRIVATE_CONTRACT_VER: usize = 1; /// Version for the private contract notification about private state changes added const PRIVATE_CONTRACT_WITH_NOTIFICATION_VER: usize = 2; +/// Timer for private state retrieval +const STATE_RETRIEVAL_TIMER: TimerToken = 0; + +/// Timer for private state retrieval, 5 secs duration +const STATE_RETRIEVAL_TICK: Duration = Duration::from_secs(5); + /// Configurtion for private transaction provider #[derive(Default, PartialEq, Debug, Clone)] pub struct ProviderConfig { @@ -117,6 +149,10 @@ pub struct ProviderConfig { pub validator_accounts: Vec
, /// Account used for signing public transactions created from private transactions pub signer_account: Option
, + /// Path to private tx logs + pub logs_path: Option, + /// Provider should store the state of the private contract offchain (in DB) + pub use_offchain_storage: bool, } #[derive(Debug)] @@ -135,7 +171,7 @@ pub trait Signer: Send + Sync { /// Decrypt payload using private key of given address. fn decrypt(&self, account: Address, shared_mac: &[u8], payload: &[u8]) -> Result, Error>; /// Sign given hash using provided account. - fn sign(&self, account: Address, hash: ethkey::Message) -> Result; + fn sign(&self, account: Address, hash: Message) -> Result; } /// Signer implementation that errors on any request. @@ -145,38 +181,41 @@ impl Signer for DummySigner { Err("Decrypting is not supported.".to_owned())? } - fn sign(&self, _account: Address, _hash: ethkey::Message) -> Result { + fn sign(&self, _account: Address, _hash: Message) -> Result { Err("Signing is not supported.".to_owned())? } } /// Signer implementation using multiple keypairs -pub struct KeyPairSigner(pub Vec); +pub struct KeyPairSigner(pub Vec); impl Signer for KeyPairSigner { fn decrypt(&self, account: Address, shared_mac: &[u8], payload: &[u8]) -> Result, Error> { - let kp = self.0.iter().find(|k| k.address() == account).ok_or(ethkey::Error::InvalidAddress)?; - Ok(ethkey::crypto::ecies::decrypt(kp.secret(), shared_mac, payload)?) + let kp = self.0.iter().find(|k| k.address() == account).ok_or(crypto::publickey::Error::InvalidAddress)?; + Ok(crypto::publickey::ecies::decrypt(kp.secret(), shared_mac, payload)?) } - fn sign(&self, account: Address, hash: ethkey::Message) -> Result { - let kp = self.0.iter().find(|k| k.address() == account).ok_or(ethkey::Error::InvalidAddress)?; - Ok(ethkey::sign(kp.secret(), &hash)?) + fn sign(&self, account: Address, hash: Message) -> Result { + let kp = self.0.iter().find(|k| k.address() == account).ok_or(crypto::publickey::Error::InvalidAddress)?; + Ok(crypto::publickey::sign(kp.secret(), &hash)?) } } /// Manager of private transactions pub struct Provider { - encryptor: Box, + encryptor: Box, validator_accounts: HashSet
, signer_account: Option
, - notify: RwLock>>, + notify: RwLock>>, transactions_for_signing: RwLock, transactions_for_verification: VerificationStore, client: Arc, miner: Arc, - accounts: Arc, - channel: IoChannel, - keys_provider: Arc, + accounts: Arc, + channel: IoChannel>, + keys_provider: Arc, + logging: Option, + use_offchain_storage: bool, + state_storage: PrivateStateStorage, } #[derive(Debug)] @@ -192,11 +231,12 @@ impl Provider { pub fn new( client: Arc, miner: Arc, - accounts: Arc, - encryptor: Box, + accounts: Arc, + encryptor: Box, config: ProviderConfig, - channel: IoChannel, - keys_provider: Arc, + channel: IoChannel>, + keys_provider: Arc, + db: Arc, ) -> Self { keys_provider.update_acl_contract(); Provider { @@ -211,17 +251,25 @@ impl Provider { accounts, channel, keys_provider, + logging: config.logs_path.map(|path| Logging::new(Arc::new(FileLogsSerializer::with_path(path)))), + use_offchain_storage: config.use_offchain_storage, + state_storage: PrivateStateStorage::new(db), } } + /// Returns private state DB + pub fn private_state_db(&self) -> Arc { + self.state_storage.private_state_db() + } + // 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) { + pub fn add_notify(&self, target: Arc) { self.notify.write().push(Arc::downgrade(&target)); } - fn notify(&self, f: F) where F: Fn(&ChainNotify) { + fn notify(&self, f: F) where F: Fn(&dyn ChainNotify) { for np in self.notify.read().iter() { if let Some(n) = np.upgrade() { f(&*n); @@ -251,28 +299,52 @@ impl Provider { // 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!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state); - let contract_validators = self.get_validators(BlockId::Latest, &contract)?; - trace!(target: "privatetx", "Required validators: {:?}", contract_validators); - let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); - trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash); - self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, contract_validators, private_state, contract_nonce)?; - self.broadcast_private_transaction(private.hash(), private.rlp_bytes()); - Ok(Receipt { - hash: tx_hash, - contract_address: contract, - status_code: 0, - }) + let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction); + match private_state { + Err(err) => { + match err { + Error::PrivateStateNotFound => { + trace!(target: "privatetx", "Private state for the contract not found, requesting from peers"); + if let Some(ref logging) = self.logging { + let contract_validators = self.get_validators(BlockId::Latest, &contract)?; + logging.private_tx_created(&tx_hash, &contract_validators); + logging.private_state_request(&tx_hash); + } + let request = RequestType::Creation(signed_transaction); + self.request_private_state(&contract, request)?; + }, + _ => {}, + } + Err(err) + } + Ok(private_state) => { + trace!(target: "privatetx", "Private transaction created, encrypted transaction: {:?}, private state: {:?}", private, private_state); + let contract_validators = self.get_validators(BlockId::Latest, &contract)?; + trace!(target: "privatetx", "Required validators: {:?}", contract_validators); + let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); + trace!(target: "privatetx", "Hashed effective private state for sender: {:?}", private_state_hash); + self.transactions_for_signing.write().add_transaction(private.hash(), signed_transaction, &contract_validators, private_state, contract_nonce)?; + self.broadcast_private_transaction(private.hash(), private.rlp_bytes()); + if let Some(ref logging) = self.logging { + logging.private_tx_created(&tx_hash, &contract_validators); + } + Ok(Receipt { + hash: tx_hash, + contract_address: contract, + 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 nonce_h256: H256 = BigEndianHash::from_uint(&nonce); let mut state_buf = [0u8; 64]; - state_buf[..32].clone_from_slice(&state_hash); - state_buf[32..].clone_from_slice(&H256::from(nonce)); - keccak(AsRef::<[u8]>::as_ref(&state_buf as &[u8])) + state_buf[..32].clone_from_slice(state_hash.as_bytes()); + state_buf[32..].clone_from_slice(nonce_h256.as_bytes()); + keccak(AsRef::<[u8]>::as_ref(&state_buf[..])) } fn pool_client<'a>(&'a self, nonce_cache: &'a NonceCache, local_accounts: &'a HashSet
) -> miner::pool_client::PoolClient<'a, Client> { @@ -282,59 +354,56 @@ impl Provider { nonce_cache, engine, local_accounts, - None, // refuse_service_transactions = true + None, // refuse_service_transactions = true ) } - /// Retrieve and verify the first available private transaction for every sender - fn process_verification_queue(&self) -> Result<(), Error> { - let process_transaction = |transaction: &VerifiedPrivateTransaction| -> Result<_, String> { - let private_hash = transaction.private_transaction.hash(); - match transaction.validator_account { - None => { + fn process_verification_transaction(&self, transaction: &VerifiedPrivateTransaction) -> Result<(), Error> { + let private_hash = transaction.private_transaction.hash(); + match transaction.validator_account { + None => { + trace!(target: "privatetx", "Propagating transaction further"); + self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes()); + return Ok(()); + } + Some(validator_account) => { + if !self.validator_accounts.contains(&validator_account) { trace!(target: "privatetx", "Propagating transaction further"); self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes()); return Ok(()); } - Some(validator_account) => { - if !self.validator_accounts.contains(&validator_account) { - trace!(target: "privatetx", "Propagating transaction further"); - self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes()); - return Ok(()); - } - let contract = Self::contract_address_from_transaction(&transaction.transaction) - .map_err(|_| "Incorrect type of action for the transaction")?; - // TODO #9825 [ToDr] Usage of BlockId::Latest - let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest); - if let Err(e) = contract_nonce { - return Err(format!("Cannot retrieve contract nonce: {:?}", e).into()); - } - let contract_nonce = contract_nonce.expect("Error was checked before"); - let private_state = self.execute_private_transaction(BlockId::Latest, &transaction.transaction); - if let Err(e) = private_state { - return Err(format!("Cannot retrieve private state: {:?}", e).into()); - } - let private_state = private_state.expect("Error was checked before"); - let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); - trace!(target: "privatetx", "Hashed effective private state for validator: {:?}", private_state_hash); - let signed_state = self.accounts.sign(validator_account, private_state_hash); - if let Err(e) = signed_state { - return Err(format!("Cannot sign the state: {:?}", e).into()); - } - let signed_state = signed_state.expect("Error was checked before"); - let signed_private_transaction = SignedPrivateTransaction::new(private_hash, signed_state, None); - trace!(target: "privatetx", "Sending signature for private transaction: {:?}", signed_private_transaction); - self.broadcast_signed_private_transaction(signed_private_transaction.hash(), signed_private_transaction.rlp_bytes()); - } + let contract = Self::contract_address_from_transaction(&transaction.transaction)?; + // TODO #9825 [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.transaction)?; + let private_state_hash = self.calculate_state_hash(&private_state, contract_nonce); + trace!(target: "privatetx", "Hashed effective private state for validator: {:?}", private_state_hash); + let signed_state = self.accounts.sign(validator_account, private_state_hash)?; + let signed_private_transaction = SignedPrivateTransaction::new(private_hash, signed_state, None); + trace!(target: "privatetx", "Sending signature for private transaction: {:?}", signed_private_transaction); + self.broadcast_signed_private_transaction(signed_private_transaction.hash(), signed_private_transaction.rlp_bytes()); } - Ok(()) - }; + } + Ok(()) + } + + /// Retrieve and verify the first available private transaction for every sender + fn process_verification_queue(&self) -> Result<(), Error> { let nonce_cache = NonceCache::new(NONCE_CACHE_SIZE); let local_accounts = HashSet::new(); let ready_transactions = self.transactions_for_verification.drain(self.pool_client(&nonce_cache, &local_accounts)); for transaction in ready_transactions { - if let Err(e) = process_transaction(&transaction) { - warn!(target: "privatetx", "Error: {:?}", e); + if let Err(err) = self.process_verification_transaction(&transaction) { + warn!(target: "privatetx", "Error: {:?}", err); + match err { + Error::PrivateStateNotFound => { + let contract = transaction.private_transaction.contract(); + trace!(target: "privatetx", "Private state for the contract {:?} not found, requesting from peers", &contract); + let request = RequestType::Verification(transaction); + self.request_private_state(&contract, request)?; + } + _ => {} + } } } Ok(()) @@ -354,8 +423,10 @@ impl Provider { Some(desc) => desc, }; let last = self.last_required_signature(&desc, signed_tx.signature())?; + let original_tx_hash = desc.original_transaction.hash(); - if last { + if last.0 { + let contract = Self::contract_address_from_transaction(&desc.original_transaction)?; let mut signatures = desc.received_signatures.clone(); signatures.push(signed_tx.signature()); let rsv: Vec = signatures.into_iter().map(|sign| sign.into_electrum().into()).collect(); @@ -373,8 +444,8 @@ impl Provider { trace!(target: "privatetx", "Last required signature received, public transaction created: {:?}", public_tx); // Sign and add it to the queue let chain_id = desc.original_transaction.chain_id(); - let hash = public_tx.hash(chain_id); - let signature = self.accounts.sign(signer_account, hash)?; + let public_tx_hash = public_tx.hash(chain_id); + let signature = self.accounts.sign(signer_account, public_tx_hash)?; let signed = SignedTransaction::new(public_tx.with_signature(signature, chain_id))?; match self.miner.import_own_transaction(&*self.client, signed.into()) { Ok(_) => trace!(target: "privatetx", "Public transaction added to queue"), @@ -384,7 +455,6 @@ impl Provider { } } // Notify about state changes - let contract = Self::contract_address_from_transaction(&desc.original_transaction)?; // TODO #9825 Usage of BlockId::Latest if self.get_contract_version(BlockId::Latest, &contract) >= PRIVATE_CONTRACT_WITH_NOTIFICATION_VER { match self.state_changes_notify(BlockId::Latest, &contract, &desc.original_transaction.sender(), desc.original_transaction.hash()) { @@ -392,6 +462,11 @@ impl Provider { Err(err) => warn!(target: "privatetx", "Failed to send private state changed notification, error: {:?}", err), } } + // Store logs + if let Some(ref logging) = self.logging { + logging.signature_added(&original_tx_hash, &last.1); + logging.tx_deployed(&original_tx_hash, &public_tx_hash); + } // Remove from store for signing if let Err(err) = self.transactions_for_signing.write().remove(&private_hash) { warn!(target: "privatetx", "Failed to remove transaction from signing store, error: {:?}", err); @@ -400,7 +475,12 @@ impl Provider { } else { // Add signature to the store match self.transactions_for_signing.write().add_signature(&private_hash, signed_tx.signature()) { - Ok(_) => trace!(target: "privatetx", "Signature stored for private transaction"), + Ok(_) => { + trace!(target: "privatetx", "Signature stored for private transaction"); + if let Some(ref logging) = self.logging { + logging.signature_added(&original_tx_hash, &last.1); + } + } Err(err) => { warn!(target: "privatetx", "Failed to add signature to signing store, error: {:?}", err); return Err(err); @@ -408,7 +488,7 @@ impl Provider { } } Ok(()) - } + } fn contract_address_from_transaction(transaction: &SignedTransaction) -> Result { match transaction.action { @@ -420,17 +500,14 @@ impl Provider { } } - fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result { - if desc.received_signatures.contains(&sign) { - return Ok(false); - } + fn last_required_signature(&self, desc: &PrivateTransactionSigningDesc, sign: Signature) -> Result<(bool, Address), Error> { 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()) + Ok((desc.received_signatures.len() + 1 == desc.validators.len(), sender)) } false => { warn!(target: "privatetx", "Sender's state doesn't correspond to validator's"); @@ -455,15 +532,84 @@ impl Provider { self.notify(|notify| notify.broadcast(ChainMessageType::SignedPrivateTransaction(transaction_hash, message.clone()))); } + fn request_private_state(&self, address: &Address, request_type: RequestType) -> Result<(), Error> { + // Define the list of available contracts + let mut private_contracts = Vec::new(); + private_contracts.push(*address); + if let Some(key_server_account) = self.keys_provider.key_server_account() { + if let Some(available_contracts) = self.keys_provider.available_keys(BlockId::Latest, &key_server_account) { + for private_contract in available_contracts { + if private_contract == *address { + continue; + } + private_contracts.push(private_contract); + } + } + } + // Check states for the avaialble contracts, if they're outdated + let mut stalled_contracts_hashes: HashSet = HashSet::new(); + for address in private_contracts { + if let Ok(state_hash) = self.get_decrypted_state_from_contract(&address, BlockId::Latest) { + if state_hash.len() != H256::len_bytes() { + return Err(Error::StateIncorrect); + } + let state_hash = H256::from_slice(&state_hash); + if let Err(_) = self.state_storage.private_state_db().state(&state_hash) { + // State not found in the local db + stalled_contracts_hashes.insert(state_hash); + } + } + } + let hashes_to_sync = self.state_storage.add_request(request_type, stalled_contracts_hashes); + if !hashes_to_sync.is_empty() { + trace!(target: "privatetx", "Requesting states for the following hashes: {:?}", hashes_to_sync); + for hash in hashes_to_sync { + self.notify(|notify| notify.broadcast(ChainMessageType::PrivateStateRequest(hash))); + } + } + Ok(()) + } + + fn private_state_sync_completed(&self, hash: &H256) -> Result<(), Error> { + self.state_storage.state_sync_completed(hash); + if self.state_storage.requests_ready() { + trace!(target: "privatetx", "Private state sync completed, processing pending requests"); + let ready_requests = self.state_storage.drain_ready_requests(); + for request in ready_requests { + match request { + RequestType::Creation(transaction) => { + match self.create_private_transaction(transaction) { + Ok(receipt) => trace!(target: "privatetx", "Creation request processed, receipt: {:?}", receipt), + Err(e) => error!(target: "privatetx", "Cannot process creation request with error: {:?}", e), + } + } + RequestType::Verification(transaction) => { + if let Err(err) = self.process_verification_transaction(&transaction) { + warn!(target: "privatetx", "Error while processing pending verification request: {:?}", err); + match err { + Error::PrivateStateNotFound => { + let contract = transaction.private_transaction.contract(); + error!(target: "privatetx", "Cannot retrieve private state after sync for {:?}", &contract); + } + _ => {} + } + } + } + } + } + } + Ok(()) + } + fn iv_from_transaction(transaction: &SignedTransaction) -> H128 { let nonce = keccak(&transaction.nonce.rlp_bytes()); - let (iv, _) = nonce.split_at(INIT_VEC_LEN); + let (iv, _) = nonce.as_bytes().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); + let (iv, _) = address.as_bytes().split_at(INIT_VEC_LEN); H128::from_slice(iv) } @@ -478,10 +624,28 @@ impl Provider { } fn get_decrypted_state(&self, address: &Address, block: BlockId) -> Result { + match self.use_offchain_storage { + true => { + let hashed_state = self.get_decrypted_state_from_contract(address, block)?; + if hashed_state.len() != H256::len_bytes() { + return Err(Error::StateIncorrect); + } + let hashed_state = H256::from_slice(&hashed_state); + let stored_state_data = self.state_storage.private_state_db().state(&hashed_state)?; + self.decrypt(address, &stored_state_data) + } + false => self.get_decrypted_state_from_contract(address, block), + } + } + + fn get_decrypted_state_from_contract(&self, address: &Address, block: BlockId) -> Result { let (data, decoder) = private_contract::functions::state::call(); let value = self.client.call_contract(block, *address, data)?; let state = decoder.decode(&value).map_err(|e| Error::Call(format!("Contract call failed {:?}", e)))?; - self.decrypt(address, &state) + match self.use_offchain_storage { + true => Ok(state), + false => self.decrypt(address, &state), + } } fn get_decrypted_code(&self, address: &Address, block: BlockId) -> Result { @@ -512,13 +676,13 @@ impl Provider { // Sort the storage to guarantee the order for all parties let sorted_storage: BTreeMap<&H256, &H256> = storage.iter().collect(); for (key, value) in sorted_storage { - raw.extend_from_slice(key); - raw.extend_from_slice(value); + raw.extend_from_slice(key.as_bytes()); + raw.extend_from_slice(value.as_bytes()); }; raw } - fn patch_account_state(&self, contract_address: &Address, block: BlockId, state: &mut state::State) -> Result<(), Error> { + fn patch_account_state(&self, contract_address: &Address, block: BlockId, state: &mut State) -> Result<(), Error> { let contract_code = Arc::new(self.get_decrypted_code(contract_address, block)?); let contract_state = self.get_decrypted_state(contract_address, block)?; trace!(target: "privatetx", "Patching contract at {:?}, code: {:?}, state: {:?}", contract_address, contract_code, contract_state); @@ -549,7 +713,7 @@ impl Provider { let sender = transaction.sender(); let nonce = state.nonce(&sender)?; let contract_address = contract_address.unwrap_or_else(|| { - let (new_address, _) = ethcore_contract_address(engine.create_address_scheme(env_info.number), &sender, &nonce, &transaction.data); + let (new_address, _) = ethcore_contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &nonce, &transaction.data); new_address }); // Patch other available private contracts' states as well @@ -576,9 +740,14 @@ impl Provider { }; (enc_code, self.encrypt(&contract_address, &Self::iv_from_transaction(transaction), &Self::snapshot_from_storage(&storage))?) }; + let mut saved_state = encrypted_storage; + if self.use_offchain_storage { + // Save state into the storage and return its hash + saved_state = self.state_storage.private_state_db().save_state(&saved_state)?.0.to_vec(); + } Ok(PrivateExecutionResult { code: encrypted_code, - state: encrypted_storage, + state: saved_state, contract_address: contract_address, result, }) @@ -597,8 +766,8 @@ impl Provider { v[31] = s.v(); v }).collect::>(), - signatures.iter().map(|s| s.r()).collect::>(), - signatures.iter().map(|s| s.s()).collect::>() + signatures.iter().map(|s| H256::from_slice(s.r())).collect::>(), + signatures.iter().map(|s| H256::from_slice(s.s())).collect::>(), ) } @@ -669,11 +838,19 @@ impl Provider { } /// Call into private contract. - pub fn private_call(&self, block: BlockId, transaction: &SignedTransaction) -> Result { + pub fn private_call(&self, block: BlockId, transaction: &SignedTransaction) -> Result { let result = self.execute_private(transaction, TransactOptions::with_no_tracing(), block)?; Ok(result.result) } + /// Retrieves log information about private transaction + pub fn private_log(&self, tx_hash: H256) -> Result { + match self.logging { + Some(ref logging) => logging.tx_log(&tx_hash).ok_or(Error::TxNotFoundInLog), + None => Err(Error::LoggingPathNotSet), + } + } + /// Returns private validators for a contract. pub fn get_validators(&self, block: BlockId, address: &Address) -> Result, Error> { let (data, decoder) = private_contract::functions::get_validators::call(); @@ -697,6 +874,21 @@ impl Provider { } } +impl IoHandler> for Provider { + fn initialize(&self, io: &IoContext>) { + if self.use_offchain_storage { + io.register_timer(STATE_RETRIEVAL_TIMER, STATE_RETRIEVAL_TICK).expect("Error registering state retrieval timer"); + } + } + + fn timeout(&self, _io: &IoContext>, timer: TimerToken) { + match timer { + STATE_RETRIEVAL_TIMER => self.state_storage.tick(&self.logging), + _ => warn!("IO service triggered unregistered timer '{}'", timer), + } + } +} + pub trait Importer { /// Process received private transaction fn import_private_transaction(&self, _rlp: &[u8]) -> Result; @@ -705,6 +897,9 @@ pub trait Importer { /// /// Creates corresponding public transaction if last required signature collected and sends it to the chain fn import_signed_private_transaction(&self, _rlp: &[u8]) -> Result; + + /// Function called when requested private state retrieved from peer and saved to DB. + fn private_state_synced(&self, hash: &H256) -> Result<(), String>; } // TODO [ToDr] Offload more heavy stuff to the IoService thread. @@ -768,6 +963,23 @@ impl Importer for Arc { } Ok(private_hash) } + + fn private_state_synced(&self, hash: &H256) -> Result<(), String> { + trace!(target: "privatetx", "Private state synced, hash: {:?}", hash); + let provider = Arc::downgrade(self); + let completed_hash = *hash; + let result = self.channel.send(ClientIoMessage::execute(move |_| { + if let Some(provider) = provider.upgrade() { + if let Err(e) = provider.private_state_sync_completed(&completed_hash) { + warn!(target: "privatetx", "Unable to process the state synced signal: {}", e); + } + } + })); + if let Err(e) = result { + warn!(target: "privatetx", "Error sending private state synced message: {:?}", e); + } + Ok(()) + } } impl ChainNotify for Provider { diff --git a/ethcore/private-tx/src/log.rs b/ethcore/private-tx/src/log.rs new file mode 100644 index 0000000000..44b473bca2 --- /dev/null +++ b/ethcore/private-tx/src/log.rs @@ -0,0 +1,439 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Private transactions logs. + +use ethereum_types::{H256, Address}; +use std::collections::HashMap; +use std::fs::File; +use std::path::PathBuf; +use std::sync::Arc; +use std::time::{SystemTime, Duration, Instant}; +use parking_lot::RwLock; +use serde::ser::{Serializer, SerializeSeq}; +use error::Error; + +#[cfg(not(time_checked_add))] +use time_utils::CheckedSystemTime; + +/// Maximum amount of stored private transaction logs. +const MAX_JOURNAL_LEN: usize = 1000; + +/// Maximum period for storing private transaction logs. +/// Logs older than 20 days will not be processed +const MAX_STORING_TIME: Duration = Duration::from_secs(60 * 60 * 24 * 20); + +/// Source of monotonic time for log timestamps +struct MonoTime { + start_time: SystemTime, + start_inst: Instant +} + +impl MonoTime { + fn new(start: SystemTime) -> Self { + Self { + start_time: start, + start_inst: Instant::now() + } + } + + fn elapsed(&self) -> Duration { + self.start_inst.elapsed() + } + + fn to_system_time(&self) -> SystemTime { + self.start_time + self.elapsed() + } +} + +impl Default for MonoTime { + fn default() -> Self { + MonoTime::new(SystemTime::now()) + } +} + +/// Current status of the private transaction +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] +pub enum PrivateTxStatus { + /// Private tx was created but no validation received yet + Created, + /// Private state not found locally and being retrived from peers + PrivateStateSync, + /// Retrieval of the private state failed, transaction not created + PrivateStateSyncFailed, + /// Several validators (but not all) validated the transaction + Validating, + /// All validators has validated the private tx + /// Corresponding public tx was created and added into the pool + Deployed, +} + +/// Information about private tx validation +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ValidatorLog { + /// Account of the validator + pub account: Address, + /// Validation timestamp, None if the transaction is not validated + pub validation_timestamp: Option, +} + +#[cfg(test)] +impl PartialEq for ValidatorLog { + fn eq(&self, other: &Self) -> bool { + self.account == other.account + } +} + +/// Information about the private transaction +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TransactionLog { + /// Original signed transaction hash (used as a source for private tx) + pub tx_hash: H256, + /// Current status of the private transaction + pub status: PrivateTxStatus, + /// Creation timestamp + pub creation_timestamp: SystemTime, + /// List of validations + pub validators: Vec, + /// Timestamp of the resulting public tx deployment + pub deployment_timestamp: Option, + /// Hash of the resulting public tx + pub public_tx_hash: Option, +} + +#[cfg(test)] +impl PartialEq for TransactionLog { + fn eq(&self, other: &Self) -> bool { + self.tx_hash == other.tx_hash && + self.status == other.status && + self.validators == other.validators && + self.public_tx_hash == other.public_tx_hash + } +} + +/// Wrapper other JSON serializer +pub trait LogsSerializer: Send + Sync + 'static { + /// Read logs from the source + fn read_logs(&self) -> Result, Error>; + + /// Write all logs to the source + fn flush_logs(&self, logs: &HashMap) -> Result<(), Error>; +} + +/// Logs serializer to the json file +pub struct FileLogsSerializer { + logs_dir: PathBuf, +} + +impl FileLogsSerializer { + pub fn with_path>(logs_dir: P) -> Self { + FileLogsSerializer { + logs_dir: logs_dir.into(), + } + } + + fn open_file(&self, to_create: bool) -> Result { + let file_path = self.logs_dir.with_file_name("private_tx.log"); + if to_create { + File::create(&file_path).map_err(From::from) + } else { + File::open(&file_path).map_err(From::from) + } + } +} + +impl LogsSerializer for FileLogsSerializer { + fn read_logs(&self) -> Result, Error> { + let log_file = self.open_file(false)?; + match serde_json::from_reader(log_file) { + Ok(logs) => Ok(logs), + Err(err) => { + error!(target: "privatetx", "Cannot deserialize logs from file: {}", err); + return Err(format!("Cannot deserialize logs from file: {:?}", err).into()); + } + } + } + + fn flush_logs(&self, logs: &HashMap) -> Result<(), Error> { + if logs.is_empty() { + // Do not create empty file + return Ok(()); + } + let log_file = self.open_file(true)?; + let mut json = serde_json::Serializer::new(log_file); + let mut json_array = json.serialize_seq(Some(logs.len()))?; + for v in logs.values() { + json_array.serialize_element(v)?; + } + json_array.end()?; + Ok(()) + } +} + +/// Private transactions logging +pub struct Logging { + logs: RwLock>, + logs_serializer: Arc, + mono_time: MonoTime, +} + +impl Logging { + /// Creates the logging object + pub fn new(logs_serializer: Arc) -> Self { + let mut logging = Logging { + logs: RwLock::new(HashMap::new()), + logs_serializer, + mono_time: MonoTime::default(), + }; + match logging.read_logs() { + // Initialize time source by max from current system time and max creation time from already saved logs + Ok(initial_time) => logging.mono_time = MonoTime::new(initial_time), + Err(err) => warn!(target: "privatetx", "Cannot read logs: {:?}", err), + } + logging + } + + /// Retrieves log for the corresponding tx hash + pub fn tx_log(&self, tx_hash: &H256) -> Option { + self.logs.read().get(&tx_hash).cloned() + } + + /// Logs the creation of the private transaction + pub fn private_tx_created(&self, tx_hash: &H256, validators: &[Address]) { + let mut logs = self.logs.write(); + if let Some(transaction_log) = logs.get_mut(&tx_hash) { + if transaction_log.status == PrivateTxStatus::PrivateStateSync { + // Transaction was already created before, its private state was being retrieved + transaction_log.status = PrivateTxStatus::Created; + return; + } else { + error!(target: "privatetx", "Attempt to create a duplicate transaction"); + return; + } + } + let mut validator_logs = Vec::new(); + for account in validators { + validator_logs.push(ValidatorLog { + account: *account, + validation_timestamp: None, + }); + } + if logs.len() > MAX_JOURNAL_LEN { + // Remove the oldest log + if let Some(tx_hash) = logs.values() + .min_by(|x, y| x.creation_timestamp.cmp(&y.creation_timestamp)) + .map(|oldest| oldest.tx_hash) + { + logs.remove(&tx_hash); + } + } + logs.insert(*tx_hash, TransactionLog { + tx_hash: *tx_hash, + status: PrivateTxStatus::Created, + creation_timestamp: self.mono_time.to_system_time(), + validators: validator_logs, + deployment_timestamp: None, + public_tx_hash: None, + }); + } + + /// Private state retrieval started + pub fn private_state_request(&self, tx_hash: &H256) { + let mut logs = self.logs.write(); + if let Some(transaction_log) = logs.get_mut(&tx_hash) { + transaction_log.status = PrivateTxStatus::PrivateStateSync; + } + } + + /// Private state retrieval failed + pub fn private_state_sync_failed(&self, tx_hash: &H256) { + let mut logs = self.logs.write(); + if let Some(transaction_log) = logs.get_mut(&tx_hash) { + transaction_log.status = PrivateTxStatus::PrivateStateSyncFailed; + } + } + + /// Logs the validation of the private transaction by one of its validators + pub fn signature_added(&self, tx_hash: &H256, validator: &Address) { + let mut logs = self.logs.write(); + if let Some(transaction_log) = logs.get_mut(&tx_hash) { + if let Some(ref mut validator_log) = transaction_log.validators.iter_mut().find(|log| log.account == *validator) { + transaction_log.status = PrivateTxStatus::Validating; + validator_log.validation_timestamp = Some(self.mono_time.to_system_time()); + } + } + } + + /// Logs the final deployment of the resulting public transaction + pub fn tx_deployed(&self, tx_hash: &H256, public_tx_hash: &H256) { + let mut logs = self.logs.write(); + if let Some(log) = logs.get_mut(&tx_hash) { + log.status = PrivateTxStatus::Deployed; + log.deployment_timestamp = Some(self.mono_time.to_system_time()); + log.public_tx_hash = Some(*public_tx_hash); + } + } + + fn read_logs(&self) -> Result { + let mut transaction_logs = self.logs_serializer.read_logs()?; + // Drop old logs + let earliest_possible = SystemTime::now().checked_sub(MAX_STORING_TIME).ok_or(Error::TimestampOverflow)?; + transaction_logs.retain(|tx_log| tx_log.creation_timestamp > earliest_possible); + // Sort logs by their creation time in order to find the most recent + transaction_logs.sort_by(|a, b| b.creation_timestamp.cmp(&a.creation_timestamp)); + let initial_timestamp = transaction_logs.first() + .map_or(SystemTime::now(), |l| std::cmp::max(SystemTime::now(), l.creation_timestamp)); + let mut logs = self.logs.write(); + for log in transaction_logs { + logs.insert(log.tx_hash, log); + } + Ok(initial_timestamp) + } + + fn flush_logs(&self) -> Result<(), Error> { + let logs = self.logs.read(); + self.logs_serializer.flush_logs(&logs) + } +} + +// Flush all logs on drop +impl Drop for Logging { + fn drop(&mut self) { + if let Err(err) = self.flush_logs() { + warn!(target: "privatetx", "Cannot write logs: {:?}", err); + } + } +} + +#[cfg(test)] +mod tests { + use serde_json; + use error::Error; + use ethereum_types::{H256, Address}; + use std::collections::{HashMap, BTreeMap}; + use std::sync::Arc; + use std::time::{SystemTime, Duration}; + use std::str::FromStr; + use types::transaction::Transaction; + use parking_lot::RwLock; + use super::{TransactionLog, Logging, PrivateTxStatus, LogsSerializer, ValidatorLog}; + + #[cfg(not(time_checked_add))] + use time_utils::CheckedSystemTime; + + struct StringLogSerializer { + string_log: RwLock, + } + + impl StringLogSerializer { + fn new(source: String) -> Self { + StringLogSerializer { + string_log: RwLock::new(source), + } + } + + fn log(&self) -> String { + let log = self.string_log.read(); + log.clone() + } + } + + impl LogsSerializer for StringLogSerializer { + fn read_logs(&self) -> Result, Error> { + let source = self.string_log.read(); + if source.is_empty() { + return Ok(Vec::new()) + } + let logs = serde_json::from_str(&source).unwrap(); + Ok(logs) + } + + fn flush_logs(&self, logs: &HashMap) -> Result<(), Error> { + // Sort logs in order to have the same order + let sorted_logs: BTreeMap<&H256, &TransactionLog> = logs.iter().collect(); + *self.string_log.write() = serde_json::to_string(&sorted_logs.values().collect::>())?; + Ok(()) + } + } + + #[test] + fn private_log_format() { + let s = r#"{ + "tx_hash":"0x64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6", + "status":"Deployed", + "creation_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053}, + "validators":[{ + "account":"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1", + "validation_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053} + }], + "deployment_timestamp":{"secs_since_epoch":1557220355,"nanos_since_epoch":196382053}, + "public_tx_hash":"0x69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da" + }"#; + + let _deserialized: TransactionLog = serde_json::from_str(s).unwrap(); + } + + #[test] + fn private_log_status() { + let logger = Logging::new(Arc::new(StringLogSerializer::new("".into()))); + let private_tx = Transaction::default(); + let hash = private_tx.hash(None); + logger.private_tx_created(&hash, &vec![Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()]); + logger.signature_added(&hash, &Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()); + logger.tx_deployed(&hash, &hash); + let tx_log = logger.tx_log(&hash).unwrap(); + assert_eq!(tx_log.status, PrivateTxStatus::Deployed); + } + + #[test] + fn serialization() { + let current_timestamp = SystemTime::now(); + let initial_validator_log = ValidatorLog { + account: Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap(), + validation_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(1)).unwrap()), + }; + let initial_log = TransactionLog { + tx_hash: H256::from_str("64f648ca7ae7f4138014f860ae56164d8d5732969b1cea54d8be9d144d8aa6f6").unwrap(), + status: PrivateTxStatus::Deployed, + creation_timestamp: current_timestamp, + validators: vec![initial_validator_log], + deployment_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(2)).unwrap()), + public_tx_hash: Some(H256::from_str("69b9c691ede7993effbcc88911c309af1c82be67b04b3882dd446b808ae146da").unwrap()), + }; + let serializer = Arc::new(StringLogSerializer::new(serde_json::to_string(&vec![initial_log.clone()]).unwrap())); + let logger = Logging::new(serializer.clone()); + let hash = H256::from_str("63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6").unwrap(); + logger.private_tx_created(&hash, &vec![Address::from_str("7ffbe3512782069be388f41be4d8eb350672d3a5").unwrap()]); + logger.signature_added(&hash, &Address::from_str("7ffbe3512782069be388f41be4d8eb350672d3a5").unwrap()); + logger.tx_deployed(&hash, &H256::from_str("de2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7").unwrap()); + drop(logger); + let added_validator_log = ValidatorLog { + account: Address::from_str("7ffbe3512782069be388f41be4d8eb350672d3a5").unwrap(), + validation_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(7)).unwrap()), + }; + let added_log = TransactionLog { + tx_hash: H256::from_str("63c715e88f7291e66069302f6fcbb4f28a19ef5d7cbd1832d0c01e221c0061c6").unwrap(), + status: PrivateTxStatus::Deployed, + creation_timestamp: current_timestamp.checked_add(Duration::from_secs(6)).unwrap(), + validators: vec![added_validator_log], + deployment_timestamp: Some(current_timestamp.checked_add(Duration::from_secs(8)).unwrap()), + public_tx_hash: Some(H256::from_str("de2209a8635b9cab9eceb67928b217c70ab53f6498e5144492ec01e6f43547d7").unwrap()), + }; + let should_be_final = vec![added_log, initial_log]; + let deserialized_logs: Vec = serde_json::from_str(&serializer.log()).unwrap(); + assert_eq!(deserialized_logs, should_be_final); + } +} diff --git a/ethcore/private-tx/src/messages.rs b/ethcore/private-tx/src/messages.rs index 2990fb9b09..45c2eb53a1 100644 --- a/ethcore/private-tx/src/messages.rs +++ b/ethcore/private-tx/src/messages.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,11 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{H256, U256, Address, BigEndianHash}; use bytes::Bytes; use hash::keccak; use rlp::Encodable; -use ethkey::Signature; +use crypto::publickey::Signature; use types::transaction::signature::{add_chain_replay_protection, check_replay_protection}; /// Message with private transaction encrypted @@ -38,7 +38,7 @@ impl PrivateTransaction { PrivateTransaction { encrypted, contract, - hash: 0.into(), + hash: H256::zero(), }.compute_hash() } @@ -87,7 +87,7 @@ impl SignedPrivateTransaction { r: sig.r().into(), s: sig.s().into(), v: add_chain_replay_protection(sig.v() as u64, chain_id), - hash: 0.into(), + hash: H256::zero(), }.compute_hash() } @@ -100,7 +100,11 @@ impl SignedPrivateTransaction { /// Construct a signature object from the sig. pub fn signature(&self) -> Signature { - Signature::from_rsv(&self.r.into(), &self.s.into(), self.standard_v()) + Signature::from_rsv( + &BigEndianHash::from_uint(&self.r), + &BigEndianHash::from_uint(&self.s), + self.standard_v(), + ) } /// Get the hash of of the original transaction. diff --git a/ethcore/private-tx/src/private_state_db.rs b/ethcore/private-tx/src/private_state_db.rs new file mode 100644 index 0000000000..c241cac61b --- /dev/null +++ b/ethcore/private-tx/src/private_state_db.rs @@ -0,0 +1,62 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +use std::sync::Arc; +use ethereum_types::H256; +use bytes::Bytes; +use kvdb::{KeyValueDB, DBTransaction}; +use keccak_hasher::KeccakHasher; +use hash_db::Hasher; +use ethcore_db::COL_PRIVATE_TRANSACTIONS_STATE; +use error::Error; + +/// Wrapper around local db with private state for sync purposes +pub struct PrivateStateDB { + db: Arc, +} + +impl PrivateStateDB { + /// Constructs the object + pub fn new(db: Arc) -> Self { + PrivateStateDB { + db, + } + } + + /// Returns saved state for the hash + pub fn state(&self, state_hash: &H256) -> Result { + trace!(target: "privatetx", "Retrieve private state from db with hash: {:?}", state_hash); + self.db.get(COL_PRIVATE_TRANSACTIONS_STATE, state_hash.as_bytes()) + .expect("Low-level database error. Some issue with your hard disk?") + .map(|s| s.to_vec()) + .ok_or(Error::PrivateStateNotFound) + } + + /// Stores state for the hash + pub fn save_state(&self, storage: &Bytes) -> Result { + let state_hash = self.state_hash(storage)?; + let mut transaction = DBTransaction::new(); + transaction.put(COL_PRIVATE_TRANSACTIONS_STATE, state_hash.as_bytes(), storage); + self.db.write(transaction).map_err(|_| Error::DatabaseWriteError)?; + trace!(target: "privatetx", "Private state saved to db, its hash: {:?}", state_hash); + Ok(state_hash) + } + + /// Returns state's hash without committing it to DB + pub fn state_hash(&self, state: &Bytes) -> Result { + Ok(KeccakHasher::hash(state)) + } +} diff --git a/ethcore/private-tx/src/private_transactions.rs b/ethcore/private-tx/src/private_transactions.rs index d0456657b0..6509ccd8e2 100644 --- a/ethcore/private-tx/src/private_transactions.rs +++ b/ethcore/private-tx/src/private_transactions.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,8 +21,8 @@ use std::collections::{HashMap, HashSet}; use bytes::Bytes; use ethcore_miner::pool; use ethereum_types::{H256, U256, Address}; -use heapsize::HeapSizeOf; -use ethkey::Signature; +use parity_util_mem::MallocSizeOfExt; +use crypto::publickey::Signature; use messages::PrivateTransaction; use parking_lot::RwLock; use types::transaction::{UnverifiedTransaction, SignedTransaction}; @@ -59,7 +59,7 @@ impl txpool::VerifiedTransaction for VerifiedPrivateTransaction { } fn mem_usage(&self) -> usize { - self.transaction.heap_size_of_children() + self.transaction.malloc_size_of() } fn sender(&self) -> &Address { @@ -224,7 +224,7 @@ impl SigningStore { &mut self, private_hash: H256, transaction: SignedTransaction, - validators: Vec
, + validators: &Vec
, state: Bytes, contract_nonce: U256, ) -> Result<(), Error> { diff --git a/ethcore/private-tx/src/state_store.rs b/ethcore/private-tx/src/state_store.rs new file mode 100644 index 0000000000..87f046c9e5 --- /dev/null +++ b/ethcore/private-tx/src/state_store.rs @@ -0,0 +1,165 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +use std::collections::{HashSet, HashMap}; +use std::sync::Arc; +use std::time::{Instant, Duration}; +use parking_lot::RwLock; +use ethereum_types::H256; +use kvdb::KeyValueDB; +use types::transaction::SignedTransaction; +use private_transactions::VerifiedPrivateTransaction; +use private_state_db::PrivateStateDB; +use log::Logging; + +/// Max duration of retrieving state (in ms) +const MAX_REQUEST_SESSION_DURATION: u64 = 120 * 1000; + +/// Type of the stored reques +pub enum RequestType { + /// Verification of private transaction + Verification(Arc), + /// Creation of the private transaction + Creation(SignedTransaction), +} + +#[derive(Clone, PartialEq)] +enum RequestState { + Syncing, + Ready, +} + +struct StateRequest { + request_type: RequestType, + request_hashes: HashSet, + state: RequestState, +} + +/// Wrapper over storage for the private states +pub struct PrivateStateStorage { + private_state_db: Arc, + requests: RwLock>, + syncing_hashes: RwLock>, +} + +impl PrivateStateStorage { + /// Constructs the object + pub fn new(db: Arc) -> Self { + PrivateStateStorage { + private_state_db: Arc::new(PrivateStateDB::new(db)), + requests: RwLock::new(Vec::new()), + syncing_hashes: RwLock::default(), + } + } + + /// Checks if ready for processing requests exist in queue + pub fn requests_ready(&self) -> bool { + let requests = self.requests.read(); + requests.iter().find(|r| r.state == RequestState::Ready).is_some() + } + + /// Signals that corresponding private state retrieved and added into the local db + pub fn state_sync_completed(&self, synced_state_hash: &H256) { + let mut syncing_hashes = self.syncing_hashes.write(); + syncing_hashes.remove(synced_state_hash); + self.mark_hash_ready(synced_state_hash); + } + + /// Returns underlying DB + pub fn private_state_db(&self) -> Arc { + self.private_state_db.clone() + } + + /// Store a request for state's sync and later processing, returns new hashes, which sync is required + pub fn add_request(&self, request_type: RequestType, request_hashes: HashSet) -> Vec { + let request = StateRequest { + request_type: request_type, + request_hashes: request_hashes.clone(), + state: RequestState::Syncing, + }; + let mut requests = self.requests.write(); + requests.push(request); + let mut new_hashes = Vec::new(); + for hash in request_hashes { + let mut hashes = self.syncing_hashes.write(); + if hashes.insert(hash, Instant::now() + Duration::from_millis(MAX_REQUEST_SESSION_DURATION)).is_none() { + new_hashes.push(hash); + } + } + new_hashes + } + + /// Drains ready requests to process + pub fn drain_ready_requests(&self) -> Vec { + let mut requests_queue = self.requests.write(); + let mut drained = Vec::new(); + let mut i = 0; + while i != requests_queue.len() { + if requests_queue[i].state == RequestState::Ready { + let request = requests_queue.remove(i); + drained.push(request.request_type); + } else { + i += 1; + } + } + drained + } + + /// State retrieval timer's tick + pub fn tick(&self, logging: &Option) { + let mut syncing_hashes = self.syncing_hashes.write(); + let current_time = Instant::now(); + syncing_hashes + .iter() + .filter(|&(_, expiration_time)| *expiration_time >= current_time) + .for_each(|(hash, _)| self.mark_hash_stale(&hash, logging)); + syncing_hashes.retain(|_, expiration_time| *expiration_time < current_time); + } + + fn mark_hash_ready(&self, ready_hash: &H256) { + let mut requests = self.requests.write(); + for request in requests.iter_mut() { + request.request_hashes.remove(ready_hash); + if request.request_hashes.is_empty() && request.state == RequestState::Syncing { + request.state = RequestState::Ready; + } + } + } + + fn mark_hash_stale(&self, stale_hash: &H256, logging: &Option) { + let mut requests = self.requests.write(); + requests.retain(|request| { + let mut delete_request = false; + if request.request_hashes.contains(stale_hash) { + let tx_hash; + match &request.request_type { + RequestType::Verification(transaction) => { + tx_hash = transaction.transaction_hash; + } + RequestType::Creation(transaction) => { + tx_hash = transaction.hash(); + if let Some(ref logging) = logging { + logging.private_state_sync_failed(&tx_hash); + } + } + } + trace!(target: "privatetx", "Private state request for {:?} staled due to timeout", &tx_hash); + delete_request = true; + } + !delete_request + }); + } +} diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs index 6365b10eec..6989d90525 100644 --- a/ethcore/private-tx/tests/private_contract.rs +++ b/ethcore/private-tx/tests/private_contract.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,29 +16,33 @@ //! Contract for private transactions tests. +extern crate client_traits; extern crate common_types as types; extern crate env_logger; extern crate ethcore; extern crate ethcore_io; extern crate ethcore_private_tx; -extern crate ethkey; +extern crate parity_crypto; extern crate keccak_hash as hash; extern crate rustc_hex; +extern crate machine; +extern crate spec; #[macro_use] extern crate log; use std::sync::Arc; +use std::str::FromStr; use rustc_hex::{FromHex, ToHex}; - use types::ids::BlockId; use types::transaction::{Transaction, Action}; -use ethcore::CreateContractAddress; -use ethcore::client::BlockChainClient; -use ethcore::executive::{contract_address}; -use ethcore::miner::Miner; -use ethcore::test_helpers::{generate_dummy_client, push_block_with_transactions}; -use ethkey::{Secret, KeyPair, Signature}; +use ethcore::{ + test_helpers::{CreateContractAddress, generate_dummy_client, push_block_with_transactions, new_db}, + miner::Miner, +}; +use client_traits::BlockChainClient; +use parity_crypto::publickey::{Secret, KeyPair, Signature}; +use machine::executive::contract_address; use hash::keccak; use ethcore_private_tx::{NoopEncryptor, Provider, ProviderConfig, StoringKeyProvider}; @@ -49,21 +53,24 @@ fn private_contract() { let _ = ::env_logger::try_init(); 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 key1 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000011").unwrap()).unwrap(); + let _key2 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000012").unwrap()).unwrap(); + let key3 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000013").unwrap()).unwrap(); + let key4 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000014").unwrap()).unwrap(); let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![key1.clone(), key3.clone(), key4.clone()])); let config = ProviderConfig{ validator_accounts: vec![key3.address(), key4.address()], signer_account: None, + logs_path: None, + use_offchain_storage: false, }; let io = ethcore_io::IoChannel::disconnected(); - let miner = Arc::new(Miner::new_for_tests(&::ethcore::spec::Spec::new_test(), None)); + let miner = Arc::new(Miner::new_for_tests(&spec::new_test(), None)); let private_keys = Arc::new(StoringKeyProvider::default()); + let db = new_db(); let pm = Arc::new(Provider::new( client.clone(), miner, @@ -72,6 +79,7 @@ fn private_contract() { config, io, private_keys, + db.key_value().clone(), )); let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &key1.address(), &0.into(), &[]); @@ -111,7 +119,7 @@ fn private_contract() { 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(); + Signature::from(parity_crypto::publickey::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]); @@ -138,7 +146,7 @@ fn private_contract() { 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(); + Signature::from(parity_crypto::publickey::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]); @@ -184,20 +192,23 @@ fn call_other_private_contract() { // Create client and provider 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 key1 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000011").unwrap()).unwrap(); + let _key2 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000012").unwrap()).unwrap(); + let key3 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000013").unwrap()).unwrap(); + let key4 = KeyPair::from_secret(Secret::from_str("0000000000000000000000000000000000000000000000000000000000000014").unwrap()).unwrap(); let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![key1.clone(), key3.clone(), key4.clone()])); let config = ProviderConfig{ validator_accounts: vec![key3.address(), key4.address()], signer_account: None, + logs_path: None, + use_offchain_storage: false, }; let io = ethcore_io::IoChannel::disconnected(); - let miner = Arc::new(Miner::new_for_tests(&::ethcore::spec::Spec::new_test(), None)); + let miner = Arc::new(Miner::new_for_tests(&spec::new_test(), None)); let private_keys = Arc::new(StoringKeyProvider::default()); + let db = new_db(); let pm = Arc::new(Provider::new( client.clone(), miner, @@ -206,6 +217,7 @@ fn call_other_private_contract() { config, io, private_keys.clone(), + db.key_value().clone(), )); // Deploy contract A @@ -229,7 +241,7 @@ fn call_other_private_contract() { trace!("Creating private contract B"); // Build constructor data let mut deploy_data = "6060604052341561000f57600080fd5b6040516020806101c583398101604052808051906020019091905050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061014a8061007b6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635197c7aa14610046575b600080fd5b341561005157600080fd5b61005961006f565b6040518082815260200191505060405180910390f35b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630c55699c6000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15156100fe57600080fd5b6102c65a03f1151561010f57600080fd5b505050604051805190509050905600a165627a7a723058207f8994e02725b47d76ec73e5c54a338d27b306dd1c830276bff2d75fcd1a5c920029000000000000000000000000".to_string(); - deploy_data.push_str(&address_a.to_vec().to_hex()); + deploy_data.push_str(&address_a.as_bytes().to_vec().to_hex()); let private_contract_b_test = deploy_data.from_hex().unwrap(); let mut private_create_tx2 = Transaction::default(); private_create_tx2.action = Action::Create; @@ -257,7 +269,7 @@ fn call_other_private_contract() { 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(); + Signature::from(parity_crypto::publickey::sign(&k.secret(), &nonced_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]); diff --git a/ethcore/res/authority_round.json b/ethcore/res/authority_round.json index 33afa45871..292760444d 100644 --- a/ethcore/res/authority_round.json +++ b/ethcore/res/authority_round.json @@ -50,12 +50,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_add", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -64,12 +65,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_mul", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -78,14 +80,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/authority_round_block_reward_contract.json b/ethcore/res/authority_round_block_reward_contract.json index 73893d44d1..4adb9a8d43 100644 --- a/ethcore/res/authority_round_block_reward_contract.json +++ b/ethcore/res/authority_round_block_reward_contract.json @@ -53,12 +53,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_add", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -67,12 +68,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_mul", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -81,14 +83,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/authority_round_randomness_contract.json b/ethcore/res/authority_round_randomness_contract.json new file mode 100644 index 0000000000..a93d1e6c11 --- /dev/null +++ b/ethcore/res/authority_round_randomness_contract.json @@ -0,0 +1,100 @@ +{ + "name": "TestAuthorityRoundRandomnessContract", + "engine": { + "authorityRound": { + "params": { + "stepDuration": 1, + "startStep": 2, + "validators": { + "list": [ + "0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e" + ] + }, + "immediateTransitions": true, + "maximumEmptySteps": "2", + "randomnessContractAddress": { + "0": "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": { + "0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { "balance": "100000000000" }, + "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", + "pricing": { + "0x0": { + "price": { "linear": { "base": 500, "word": 0 }} + }, + "0x7fffffffffffff": { + "price": { "linear": { "base": 150, "word": 0 }} + } + } + } + }, + "0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0x0": { + "price": { "linear": { "base": 40000, "word": 0 }} + }, + "0x7fffffffffffff": { + "price": { "linear": { "base": 6000, "word": 0 }} + } + } + } + }, + "0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0x0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0000000000000000000000000000000000000042": { + "balance": "1", + "constructor": "608060405234801561001057600080fd5b50610820806100206000396000f3fe608060405234801561001057600080fd5b50600436106100ec576000357c01000000000000000000000000000000000000000000000000000000009004806363f160e6116100a95780637a3e286b116100835780637a3e286b14610378578063baf11cab14610380578063c358ced0146103ac578063fe7d567d146103b4576100ec565b806363f160e614610285578063695e89f6146102c557806374ce906714610370576100ec565b806304fdb016146100f15780630b61ba8514610192578063209652551461020b5780632e8a8dd5146102255780633fa4f245146102515780635580e58b14610259575b600080fd5b61011d6004803603604081101561010757600080fd5b5080359060200135600160a060020a03166103d1565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561015757818101518382015260200161013f565b50505050905090810190601f1680156101845780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610209600480360360408110156101a857600080fd5b813591908101906040810160208201356401000000008111156101ca57600080fd5b8201836020820111156101dc57600080fd5b803590602001918460018302840111640100000000831117156101fe57600080fd5b509092509050610475565b005b6102136104fa565b60408051918252519081900360200190f35b6102136004803603604081101561023b57600080fd5b5080359060200135600160a060020a0316610501565b61021361051b565b6102136004803603604081101561026f57600080fd5b5080359060200135600160a060020a0316610521565b6102b16004803603604081101561029b57600080fd5b5080359060200135600160a060020a031661053e565b604080519115158252519081900360200190f35b6102f1600480360360408110156102db57600080fd5b5080359060200135600160a060020a0316610568565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561033457818101518382015260200161031c565b50505050905090810190601f1680156103615780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b6102b1610639565b610213610649565b6102b16004803603604081101561039657600080fd5b5080359060200135600160a060020a0316610654565b6102b161067c565b610209600480360360208110156103ca57600080fd5b5035610687565b600160208181526000938452604080852082529284529282902080548351600293821615610100026000190190911692909204601f8101859004850283018501909352828252909290919083018282801561046d5780601f106104425761010080835404028352916020019161046d565b820191906000526020600020905b81548152906001019060200180831161045057829003601f168201915b505050505081565b41331461048157600080fd5b61048d60014303610735565b61049657600080fd5b60006104a460014303610740565b90506104b08133610654565b156104ba57600080fd5b600081815260208181526040808320338085529083528184208890558484526001835281842090845290915290206104f3908484610753565b5050505050565b6003545b90565b600060208181529281526040808220909352908152205481565b60035481565b600260209081526000928352604080842090915290825290205481565b6000918252600260209081526040808420600160a060020a03939093168452919052902054151590565b600082815260208181526040808320600160a060020a03851680855290835281842054868552600180855283862092865291845282852080548451600294821615610100026000190190911693909304601f810186900486028401860190945283835260609491939092918391908301828280156106275780601f106105fc57610100808354040283529160200191610627565b820191906000526020600020905b81548152906001019060200180831161060a57829003601f168201915b50505050509050915091509250929050565b600061064443610735565b905090565b600061064443610740565b600091825260208281526040808420600160a060020a03939093168452919052902054151590565b600061064443610747565b41331461069357600080fd5b61069f60014303610747565b6106a857600080fd5b60006106b660014303610740565b90506106c2813361053e565b156106cc57600080fd5b60408051602080820185905282518083038201815291830183528151918101919091206000848152808352838120338252909252919020541461070e57600080fd5b60009081526002602090815260408083203384529091529020819055600380549091189055565b600360069091061090565b6006900490565b60036006909106101590565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106107945782800160ff198235161785556107c1565b828001600101855582156107c1579182015b828111156107c15782358255916020019190600101906107a6565b506107cd9291506107d1565b5090565b6104fe91905b808211156107cd57600081556001016107d756fea265627a7a7230582008bb7311af9026bd70ddb998741333d414a366275b9b433a2943bbd6bedc27ae64736f6c634300050a0032" + } + } +} diff --git a/ethcore/res/constructor.json b/ethcore/res/constructor.json index 1383337351..2e64335b58 100644 --- a/ethcore/res/constructor.json +++ b/ethcore/res/constructor.json @@ -38,12 +38,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_add", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "linear": { "base": 500, "word": 0 }} + }, + "0x7fffffffffffff": { + "info": "EIP1108 transition", + "price": { "linear": { "base": 150, "word": 0 }} } } } @@ -52,12 +53,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_mul", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "linear": {"base": 40000, "word": 0 }} + }, + "0x7fffffffffffff": { + "info": "EIP1108 transition", + "price": { "linear": { "base": 6000, "word": 0 }} } } } @@ -66,14 +68,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/contracts/authority_round_random.json b/ethcore/res/contracts/authority_round_random.json new file mode 100644 index 0000000000..2ac5440c81 --- /dev/null +++ b/ethcore/res/contracts/authority_round_random.json @@ -0,0 +1,133 @@ +[{ + "constant": false, + "inputs": [{ + "name": "_secretHash", + "type": "bytes32" + }, + { + "name": "_cipher", + "type": "bytes" + } + ], + "name": "commitHash", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ + "name": "_number", + "type": "uint256" + }], + "name": "revealNumber", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "currentCollectRound", + "outputs": [{ + "name": "", + "type": "uint256" + }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_collectRound", + "type": "uint256" + }, + { + "name": "_miningAddress", + "type": "address" + } + ], + "name": "getCommitAndCipher", + "outputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ + "name": "_collectRound", + "type": "uint256" + }, + { + "name": "_validator", + "type": "address" + } + ], + "name": "isCommitted", + "outputs": [{ + "name": "", + "type": "bool" + }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isCommitPhase", + "outputs": [{ + "name": "", + "type": "bool" + }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isRevealPhase", + "outputs": [{ + "name": "", + "type": "bool" + }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ + "name": "_collectRound", + "type": "uint256" + }, + { + "name": "_validator", + "type": "address" + } + ], + "name": "sentReveal", + "outputs": [{ + "name": "", + "type": "bool" + }], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/ethcore/res/contracts/registrar.json b/ethcore/res/contracts/registrar.json deleted file mode 100644 index 38edcc7877..0000000000 --- a/ethcore/res/contracts/registrar.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - {"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"string"}],"name":"confirmReverse","outputs":[{"name":"success","type":"bool"}],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[{"name":"success","type":"bool"}],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"bytes32"}],"name":"set","outputs":[{"name":"success","type":"bool"}],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"drop","outputs":[{"name":"success","type":"bool"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getAddress","outputs":[{"name":"","type":"address"}],"type":"function"}, - {"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"setFee","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_to","type":"address"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"type":"function"}, - {"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserved","outputs":[{"name":"reserved","type":"bool"}],"type":"function"}, - {"constant":false,"inputs":[],"name":"drain","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"string"},{"name":"_who","type":"address"}],"name":"proposeReverse","outputs":[{"name":"success","type":"bool"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"getUint","outputs":[{"name":"","type":"uint256"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"}],"name":"get","outputs":[{"name":"","type":"bytes32"}],"type":"function"}, - {"constant":true,"inputs":[],"name":"fee","outputs":[{"name":"","type":"uint256"}],"type":"function"}, - {"constant":true,"inputs":[{"name":"","type":"address"}],"name":"reverse","outputs":[{"name":"","type":"string"}],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"uint256"}],"name":"setUint","outputs":[{"name":"success","type":"bool"}],"type":"function"}, - {"constant":false,"inputs":[],"name":"removeReverse","outputs":[],"type":"function"}, - {"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_key","type":"string"},{"name":"_value","type":"address"}],"name":"setAddress","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"Drained","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"FeeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"owner","type":"address"}],"name":"Reserved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"oldOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"Transferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"owner","type":"address"}],"name":"Dropped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"key","type":"string"}],"name":"DataChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":true,"name":"reverse","type":"address"}],"name":"ReverseProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":true,"name":"reverse","type":"address"}],"name":"ReverseConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":true,"name":"reverse","type":"address"}],"name":"ReverseRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"old","type":"address"},{"indexed":true,"name":"current","type":"address"}],"name":"NewOwner","type":"event"} -] diff --git a/ethcore/res/contracts/test_authority_round_random.json b/ethcore/res/contracts/test_authority_round_random.json new file mode 100644 index 0000000000..3961986057 --- /dev/null +++ b/ethcore/res/contracts/test_authority_round_random.json @@ -0,0 +1,265 @@ +[ + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "address" + } + ], + "name": "ciphers", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_collectRound", + "type": "uint256" + }, + { + "name": "_miningAddress", + "type": "address" + } + ], + "name": "getCipher", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_secretHash", + "type": "bytes32" + }, + { + "name": "_cipher", + "type": "bytes" + } + ], + "name": "commitHash", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getValue", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "address" + } + ], + "name": "hashes", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "value", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + }, + { + "name": "", + "type": "address" + } + ], + "name": "secrets", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_collectRound", + "type": "uint256" + }, + { + "name": "_miningAddress", + "type": "address" + } + ], + "name": "sentReveal", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isCommitPhase", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_number", + "type": "uint256" + } + ], + "name": "revealNumber", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRound", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_collectRound", + "type": "uint256" + }, + { + "name": "_miningAddress", + "type": "address" + } + ], + "name": "isCommitted", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isRevealPhase", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_collectRound", + "type": "uint256" + }, + { + "name": "_miningAddress", + "type": "address" + } + ], + "name": "getCommit", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/ethcore/res/contracts/test_authority_round_random.sol b/ethcore/res/contracts/test_authority_round_random.sol new file mode 100644 index 0000000000..e1dfde0134 --- /dev/null +++ b/ethcore/res/contracts/test_authority_round_random.sol @@ -0,0 +1,102 @@ +pragma solidity 0.5.10; + +/// @dev Randomness test contract based on https://github.com/poanetwork/posdao-contracts. +/// Generates and stores random numbers in a RANDAO manner and accumulates a random seed. +contract Random { + mapping(uint256 => mapping(address => bytes32)) public hashes; + mapping(uint256 => mapping(address => bytes)) public ciphers; + mapping(uint256 => mapping(address => uint256)) public secrets; + uint256 public value; + + /// @dev Called by the validator's node to store a hash and a cipher of the validator's secret on each collection + /// round. The validator's node must use its mining address to call this function. + /// This function can only be called once per collection round (during the `commits phase`). + /// @param _secretHash The Keccak-256 hash of the validator's secret. + /// @param _cipher The cipher of the validator's secret. Can be used by the node to decrypt and reveal. + function commitHash(bytes32 _secretHash, bytes calldata _cipher) external { + require(block.coinbase == msg.sender); + require(_isCommitPhase(block.number - 1)); + uint256 round = _collectRound(block.number - 1); + require(!isCommitted(round, msg.sender)); + hashes[round][msg.sender] = _secretHash; + ciphers[round][msg.sender] = _cipher; + } + + /// @dev Called by the validator's node to XOR its secret with the current random seed. + /// The validator's node must use its mining address to call this function. + /// This function can only be called once per collection round (during the `reveals phase`). + /// @param _number The validator's secret. + function revealNumber(uint256 _number) external { + require(block.coinbase == msg.sender); + require(_isRevealPhase(block.number - 1)); + uint256 round = _collectRound(block.number - 1); + require(!sentReveal(round, msg.sender)); + require(hashes[round][msg.sender] == keccak256(abi.encodePacked(_number))); + secrets[round][msg.sender] = _number; + value ^= _number; + } + + /// @dev Returns the Keccak-256 hash and cipher of the validator's secret for the specified collection round + /// and the specified validator stored by the validator through the `commitHash` function. + /// @param _collectRound The serial number of the collection round for which hash and cipher should be retrieved. + /// @param _miningAddress The mining address of validator. + function getCommitAndCipher( + uint256 _collectRound, + address _miningAddress + ) public view returns(bytes32, bytes memory) { + return (hashes[_collectRound][_miningAddress], ciphers[_collectRound][_miningAddress]); + } + + /// @dev Returns a boolean flag indicating whether the specified validator has committed their secret's hash for the + /// specified collection round. + /// @param _collectRound The serial number of the collection round for which the checkup should be done. + /// @param _miningAddress The mining address of the validator. + function isCommitted(uint256 _collectRound, address _miningAddress) public view returns(bool) { + return hashes[_collectRound][_miningAddress] != bytes32(0); + } + + /// @dev Returns a boolean flag indicating whether the current phase of the current collection round + /// is a `commits phase`. Used by the validator's node to determine if it should commit the hash of + /// the secret during the current collection round. + function isCommitPhase() public view returns(bool) { + return _isCommitPhase(block.number); + } + + /// @dev Returns a boolean flag indicating whether the current phase of the current collection round + /// is a `reveals phase`. Used by the validator's node to determine if it should reveal the secret during + /// the current collection round. + function isRevealPhase() public view returns(bool) { + return _isRevealPhase(block.number); + } + + /// @dev Returns a boolean flag of whether the specified validator has revealed their secret for the + /// specified collection round. + /// @param _collectRound The serial number of the collection round for which the checkup should be done. + /// @param _miningAddress The mining address of the validator. + function sentReveal(uint256 _collectRound, address _miningAddress) public view returns(bool) { + return secrets[_collectRound][_miningAddress] != uint256(0); + } + + /// @dev Returns the current collect round number. + function currentCollectRound() public view returns(uint256) { + return _collectRound(block.number); + } + + /// @dev Returns the current random value. + function getValue() public view returns(uint256) { + return value; + } + + function _collectRound(uint256 blockNumber) private pure returns(uint256) { + return blockNumber / 6; + } + + function _isCommitPhase(uint256 blockNumber) private pure returns(bool) { + return blockNumber % 6 < 3; + } + + function _isRevealPhase(uint256 blockNumber) private pure returns(bool) { + return blockNumber % 6 >= 3; + } +} + diff --git a/ethcore/res/ethereum/builtin_multi_bench.json b/ethcore/res/ethereum/builtin_multi_bench.json new file mode 100644 index 0000000000..a37273936f --- /dev/null +++ b/ethcore/res/ethereum/builtin_multi_bench.json @@ -0,0 +1,112 @@ +{ + "name": "ecrecover legacy", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "homesteadTransition": "0x0" + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x1", + "eip150Transition": "0x0", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", + "eip155Transition": "0x7fffffffffffffff", + "maxCodeSize": 24576, + "maxCodeSizeTransition": "0x7fffffffffffffff" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388" + }, + "accounts": { + "0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "pricing": { + "0": {"price": {"linear": { "base": 1, "word": 0 }}}, + "1": {"price": {"linear": { "base": 2, "word": 0 }}}, + "2": {"price": {"linear": { "base": 3, "word": 0 }}}, + "3": {"price": {"linear": { "base": 4, "word": 0 }}}, + "4": {"price": {"linear": { "base": 5, "word": 0 }}}, + "5": {"price": {"linear": { "base": 6, "word": 0 }}}, + "6": {"price": {"linear": { "base": 7, "word": 0 }}}, + "7": {"price": {"linear": { "base": 8, "word": 0 }}}, + "8": {"price": {"linear": { "base": 9, "word": 0 }}}, + "9": {"price": {"linear": { "base": 1, "word": 0 }}}, + "10": {"price": {"linear": { "base": 2, "word": 0 }}} + } + } + }, + "0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "pricing": { + "0": {"price": {"linear": { "base": 1, "word": 0 }}}, + "1": {"price": {"linear": { "base": 2, "word": 0 }}}, + "2": {"price": {"linear": { "base": 3, "word": 0 }}}, + "3": {"price": {"linear": { "base": 4, "word": 0 }}}, + "4": {"price": {"linear": { "base": 5, "word": 0 }}}, + "5": {"price": {"linear": { "base": 6, "word": 0 }}}, + "6": {"price": {"linear": { "base": 7, "word": 0 }}}, + "7": {"price": {"linear": { "base": 8, "word": 0 }}}, + "8": {"price": {"linear": { "base": 9, "word": 0 }}}, + "9": {"price": {"linear": { "base": 1, "word": 0 }}}, + "10": {"price": {"linear": { "base": 2, "word": 0 }}}, + "11": {"price": {"linear": { "base": 3, "word": 0 }}}, + "12": {"price": {"linear": { "base": 4, "word": 0 }}}, + "13": {"price": {"linear": { "base": 5, "word": 0 }}}, + "14": {"price": {"linear": { "base": 6, "word": 0 }}}, + "15": {"price": {"linear": { "base": 7, "word": 0 }}}, + "16": {"price": {"linear": { "base": 8, "word": 0 }}}, + "17": {"price": {"linear": { "base": 9, "word": 0 }}}, + "18": {"price": {"linear": { "base": 1, "word": 0 }}}, + "19": {"price": {"linear": { "base": 2, "word": 0 }}}, + "20": {"price": {"linear": { "base": 3, "word": 0 }}}, + "21": {"price": {"linear": { "base": 4, "word": 0 }}}, + "22": {"price": {"linear": { "base": 5, "word": 0 }}}, + "23": {"price": {"linear": { "base": 6, "word": 0 }}}, + "24": {"price": {"linear": { "base": 7, "word": 0 }}}, + "25": {"price": {"linear": { "base": 8, "word": 0 }}}, + "26": {"price": {"linear": { "base": 9, "word": 0 }}}, + "27": {"price": {"linear": { "base": 1, "word": 0 }}}, + "28": {"price": {"linear": { "base": 2, "word": 0 }}}, + "29": {"price": {"linear": { "base": 3, "word": 0 }}}, + "30": {"price": {"linear": { "base": 4, "word": 0 }}}, + "31": {"price": {"linear": { "base": 5, "word": 0 }}}, + "32": {"price": {"linear": { "base": 6, "word": 0 }}}, + "33": {"price": {"linear": { "base": 7, "word": 0 }}}, + "34": {"price": {"linear": { "base": 8, "word": 0 }}}, + "35": {"price": {"linear": { "base": 9, "word": 0 }}}, + "36": {"price": {"linear": { "base": 10, "word": 0 }}}, + "37": {"price": {"linear": { "base": 10, "word": 0 }}}, + "38": {"price": {"linear": { "base": 10, "word": 0 }}}, + "39": {"price": {"linear": { "base": 10, "word": 0 }}} + } + } + } + } +} diff --git a/ethcore/res/ethereum/builtin_one_activation_bench.json b/ethcore/res/ethereum/builtin_one_activation_bench.json new file mode 100644 index 0000000000..e746d7b2a1 --- /dev/null +++ b/ethcore/res/ethereum/builtin_one_activation_bench.json @@ -0,0 +1,54 @@ +{ + "name": "ecrecover legacy chain spec for benchmarking", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "homesteadTransition": "0x0" + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "accountStartNonce": "0x00", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x1", + "eip150Transition": "0x0", + "eip160Transition": "0x7fffffffffffffff", + "eip161abcTransition": "0x7fffffffffffffff", + "eip161dTransition": "0x7fffffffffffffff", + "eip155Transition": "0x7fffffffffffffff", + "maxCodeSize": 24576, + "maxCodeSizeTransition": "0x7fffffffffffffff" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x400000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388" + }, + "accounts": { + "0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "pricing": { + "linear": { "base": 3000, "word": 0 } + } + } + } + } +} diff --git a/ethcore/res/ethereum/byzantium_test.json b/ethcore/res/ethereum/byzantium_test.json index b4df953739..bc5ac30207 100644 --- a/ethcore/res/ethereum/byzantium_test.json +++ b/ethcore/res/ethereum/byzantium_test.json @@ -57,12 +57,13 @@ "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x00", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -70,12 +71,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x00", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -83,14 +85,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x00", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/callisto.json b/ethcore/res/ethereum/callisto.json index 02dc3d2763..a1cae6f3c5 100644 --- a/ethcore/res/ethereum/callisto.json +++ b/ethcore/res/ethereum/callisto.json @@ -76,12 +76,12 @@ "balance": "1", "builtin": { "name": "alt_bn128_add", - "activate_at": 20, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "20": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -90,12 +90,12 @@ "balance": "1", "builtin": { "name": "alt_bn128_mul", - "activate_at": 20, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "20": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -104,14 +104,12 @@ "balance": "1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": 20, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "20": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/classic.json b/ethcore/res/ethereum/classic.json index f824f7667d..f9fe4a1098 100644 --- a/ethcore/res/ethereum/classic.json +++ b/ethcore/res/ethereum/classic.json @@ -37,7 +37,15 @@ "eip140Transition": "0x85d9a0", "eip211Transition": "0x85d9a0", "eip214Transition": "0x85d9a0", - "eip658Transition": "0x85d9a0" + "eip658Transition": "0x85d9a0", + "eip145Transition": "0x921288", + "eip1014Transition": "0x921288", + "eip1052Transition": "0x921288", + "eip1283Transition": "0xa03ae7", + "eip1344Transition": "0xa03ae7", + "eip1706Transition": "0xa03ae7", + "eip2028Transition": "0xa03ae7", + "eip2200AdvanceTransition": "0xa03ae7" }, "genesis": { "seal": { @@ -55,8 +63,8 @@ "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" }, "hardcodedSync": { - "header": "f9021ba0a281d23475d3d5ff03df8636c9f528cdd91498af274a3b2f8989bbd51bfeb809a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794004730417cd2b1d19f6be2679906ded4fa8a64e2a0d5fdd62e7e29dc3da1cd3fe5ad549e000260bfdb55f523fde008f26390220d23a0370ff78457d6c7469ff333a13165f4bb8057d00cfa68365cb4d1a8c8a1da46d5a0dea37365a20f5bb5ad3766a24a9fe7b04b946e35aaba74f862467a7c5cdb7d67b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000004000000000040000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000100000000000000000000000080000000000000000020000000000000000000000000000000000000000000000000000000000000000000000020000000000001000000000000000000000000000000080010000000000000008000000000000000866fc181925a9783763801837a121d830c317c845c9d1efe9b457468657265756d436c6173736963534f4c4f2f326d696e657273a0bb6e626c7ee3d827da18e1b303d9552ba17e92cefcedfc58bdfc5bac6a8ebabe882d8c26402344c24b", - "totalDifficulty": "598584828374329723203", + "header": "f901fda0f46341de105659d5897cc2c49b97df3f74a7535f2f0a7c86b12a0f23b2c436afa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479421479eb8cb1a27861c902f07a952b72b10fd53efa0c409d60020629ffe77f7aa91603b8fe5df8811957d231638b29471a0b0cb695ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000868527ce839a798385e8018379f57980845d7b0d1c80a051d85cf8feba52d2c0cf244ceb037441ec276d88a07ad755e6ff3cc92d52097e88f3906b50ebf30eec", + "totalDifficulty": "729996985215121503838", "CHTs": [ "0x0eb474b7721727204978e92e27d31cddff56471911e424a4c8271c35f9c982cc", "0xe10e94515fb5ffb7ffa9bf50db4a959b3f50c2ff75e0b8bd5f5e038749e52a11", @@ -3840,7 +3848,509 @@ "0xc893aee6d249c152f5db3d7763f34a3311345dff721ae9c71ab5fb3d2b3e2559", "0x250caa98ea3e682be9c866990f19647f443d57690052229ac0ccfa0ab30a5a71", "0xc32fd5318214071a41cd8e98499b2b65942c5837c686a06b536146fd0bf294bf", - "0xac390c012eecd83fa8f4cc77a59992914b5c95af36b28747e07adea13228acbc" + "0xac390c012eecd83fa8f4cc77a59992914b5c95af36b28747e07adea13228acbc", + "0x5baa5a91d5e9d4632ba89304ca9a3b252f873477f3f0dbbe82a2656eb11cc74b", + "0x063db9182a0d7061dd6872e56c0fc9c5ce9dcd50bba1c1be4e066ac31baea300", + "0x30718f710304c59b8438a03539aca2d431d0a751fd718d905bd14b9b14587674", + "0xeff0df83d65ca2db9b93f92245f8f2be2777ac0eb8b05960c63f9761c4a609b2", + "0xad3e2da2fe9dfa45f6c2655ebbf57f4291db03ee48d4cc9c9dce8ba77068192c", + "0x1c2da7631ac5be07f69d7a33ab11c19677f037339197eb6019ad7a85b81ccc73", + "0xad135339b16d46696f1d630a041af6ff703e50f03720a2391a7437cca1e86c6b", + "0x18a288ba23d438c58600d500f8a2f5b66fbf8cac2fdf21c92602715e1d7d9c0a", + "0xc41f27d18eeac02197b67d185163b407d7ee75e776cd73e36e568f05f7528631", + "0x5243561b8e640354f82568049d4b8ce7f30e092384398f22ec4f0558da756c86", + "0x0fc498227a7c657eec7dfe09a668db967d6ed74bbabe16dc2108406870d7d7f2", + "0xf229992ca93866a74fe61e3d0a742e7b756db30e5c1b20038cece1c7e0a20c2a", + "0x6ec77884426881486865322f73f26c09cca3446081380d91730d19fca46adb61", + "0xed8ce85df6404d421b09dda6e3e4539df7963067f2ee0190de80faf9589249a7", + "0x5fe506925192e162787bafa20c4829755d2616c27fb448d08a9594383608b114", + "0x18808bdd50aa3f52f045cfa8eb2a669c52004c38b12b68536ac3e742f3a11a15", + "0xded04cbdd4942959435b197403ac9d38b7239f51693440d88e3fe4cc2f90a3a0", + "0x6ad413fbdd413fc14decf9aae5c37dae63fd4ec2d11844c6058023a462040053", + "0xa1cf0f465f9af59340b2e3f49242e02282f6a0ab82ea4721bfdae4f995e2da99", + "0xe1794440b5173a4a6cde5595e3d34df38aaaaed9489af182af314b29357748b2", + "0x1d427d41658ea2a40d9da5e2f03ad0b3060a9693572d2e188e0ae4e9e1852f36", + "0x0799a99fb0ef1edbcb102022b3d86abd3a4d771e583814a292f6a499233a0fc4", + "0x63cc0d8e879d5c25fb4001b90e0e9e8fb8201cf50aaad3c9ae7f6354760bd976", + "0xc94df53c16fad900034e2b3fca50038979b8eb94032f745073f10a56bce1e027", + "0xc587fb5f436afaf00310ec86dbfd622afdb3f694c08ff71db952f903a4bb8a55", + "0x70fda21e86b108ac5e16109a12f51f26d374c164899898106d80d5a641b7dd03", + "0xa48bf905f87aab400fb9f4e456d27de102763156bb33a3db37f8fc8aeccbb135", + "0xb934c6f92f574a1ce070dfefbc2fbc6764ea22a2565a8083ac5b1063b3a237d3", + "0x474b3eb3e73312269d3598161bfbcae30b7e1056919a6f6afeca3a2abb853897", + "0x122e4343fe46c9cd740381675fba4a0258e0f42dfa569073bdf055bb66cc6543", + "0x8c4a235053e9fb225eaed1ad6f61747d1ca9f29fa520465ee0471f4fb028c619", + "0x5228999495c587f7000ec00e18e09cccaa1ac2eb8e0415307439f3927450832d", + "0x81ab2253864c7e7ce4d7184f2280cb080e38a7d672e9f8dc3c18907a51894509", + "0xf8bc443f7984839c9024c2649ef329cca6c7f05fffe90e116436267aa0bcc27a", + "0x99553484d427f255d14c1b03ceeac1cdb5e08134ad4f4a06a1b02ec3fb1657f4", + "0xf8fce679a0ef4119057ee408a5bb7dfea18cb93d8d41f4ca7e663ca6c1c8a1cf", + "0xa432fffcec6116e11426685b7d73d37945e884c2f261a139f5cb8c458e501f1e", + "0x2729cf60d8531d0620ef907cd4f5cb51f6eabfb94a5af5b1e1f674bdb916cbbe", + "0xa5977b367bb45c53c85f40d28e9ff0215790e94054dbc11510222137e55736cf", + "0x0c4378b1341796a2ae2143795df0a7c5e3a85c57e5a2ed62d14fe6296fa98d84", + "0x07d744b148a65d6535c0b7ec4789323c10cf19062177780cb196e2d7ff5b169e", + "0x9bbf777e8fbb1cbdb773eb18a47bf9cf0946e501653584a8364e8f6bda245d34", + "0xdd70ea9aa94dffda9c2dc901e84e621135c139405e2136ac6f7e54e71aaad593", + "0xde5e19af4bc19137ead84d3db93a511a7c253eb1d71ca3cc9284163facf2ecc7", + "0xa75e5454a26c2f26768e17aa56fc1768503d7c8b2d3d0bf8a65380cdcb3348cc", + "0x37f55a4f1df17bdf0813e0781d0227f539e9633f6ff924fe73d9cbb248e2741c", + "0x2834deeb897f9f9cb7f8902825a106b0c7389f5a151f61f7bcf38fa569968893", + "0x31e583216ab016d456e9e5cfda5a27b564a822f164ee3b70dc956392bd504a18", + "0x92bbc94b98ce4dcc8bc0f98b5cec91618904021c721f175676fe59ce308cf6e1", + "0xeb3ca726b5bd919e41376a95d41deafba80d8ab52f79f976dbb1eab219af8653", + "0x1b4ff093569939e006a6854a2a1af06b8220151968240d80fbd5ac56a0837cd4", + "0xc87b28f21420b002c2b79c12388d714661ba67e55a587aca1a390f2082505168", + "0x866a67c833049cfc8491d672696d591d1163f8fe21b67f385030e6a153c3399f", + "0x61b4ddb584fc452f47db7d9912388da253ac97ae4270676c09dcfab3df91e4fe", + "0x132616810148d4491328b6893edb9a66db1b20184aef5efae481505d43ee78db", + "0x21c644596d384f59307e7e6defcf9547794b30a1f61758ec24dd0362690bf22b", + "0xc4905b2f5798c1e88ce47e7f2144bb5f8c2650b44926be59f9336b9a76477f14", + "0x281a805a9dff1fb6ffc3bc1660063d2d92ce2cdf1de4da98e007c217a8eb2b2b", + "0x4f52a327469d9fb0978cb57f8061d7c3c4c39b73aa7eb771173ac5f375850832", + "0xb690d892e39e838dda77b78ffcb4e3dc6fc4621856388233d559eddeb25f89c3", + "0x1dd206d1bfaf594caeab38e041f30f2bbcff635f8818bf5c8407a7a957e44a97", + "0x6d5189f970052250a91f304dd419742b1b96655566f4426b33335c3bdcc3e6fe", + "0x7aec3247a3d6e0ffd3e4bfd60dd143455dfebd7a5587e531575772f215bc45e0", + "0x810187dfc969f107494d9f9891d9f1cdcc0978d48efdec0ac8bbff5ef9843977", + "0xe104b79045c6efaf4b5cdab9ab1c25d6afac28a9165bdd22a601a0baed63b1f5", + "0x258f8300d1e177e19666c69dc714b71d7a441d251e4841d73bac817814ef2f91", + "0x3e78915853a869348a65035fec30987d4e7cf23f42e06f6f6b932e0d94d45d1c", + "0x60f0fff616c4014b36320f632b50e011521a4804b7cf87071f0d4b138e1540a0", + "0x91efbedd8494c07e4e77e826dc0cc557f55b903854cfd45358a7b10deaf9f814", + "0xb8d2abfbdb710b8a2694a60e72536f0e99b455614c1e2f0050ff6be1e2c0d89e", + "0x26f7662a23f6b3f20e9ade429977f4c3c56b7e53ff72f09d40d2dc0cd3d002ef", + "0x91e9906b0591fbecef8e154903c4dbeb3d2d6f853b1d2bd85af26fa6ec6b12b8", + "0xa932981dc47fee204a3a57fe11465fd836936b7cf0d314ebbfd92f5a700935c0", + "0x49ea0da0300b7864b201bc474c841641b920741f95607714d59b8ce62c11be14", + "0x778fc84c0b7e7aaaa9a12478156c352c3eb88c9703066e7744183eff8615f529", + "0x28467d644dbe57aecf7486e729c5dac2e23d1850f2d1c0e1fd6b641c9c57a72b", + "0x4a5a47a6978dc72d46dd823e9a5056d37367ee9b31b620baec372a60a5d923b1", + "0xb3ffab40553877702b0401133e512958bedbc667076cb19afc3ac17ad812558d", + "0xf0fdef8c21a19c70e3eec8f5cd8cbd8e7189ab12924adc6a913739da5b259814", + "0x3052012ceb30e8a8805a289c1a702451d8d144325e960b24214659dbbee92aa0", + "0xad90c660afa7d924632728d2462d6e12621292436d5608d6f8f4b9edd6e8fac1", + "0xa77f30230e8444590e86c0062585e299a42fc11f1bb4fe0bdd54270bc56d41bb", + "0x3b95bc0943de7c51628110c942c96445bf6e66e22817803f62a961fe5e5a7842", + "0xf1d18b53e1753fe6ae35451ed5416871df5ef3d11f298fe0c4728f2a45dc4a36", + "0x9f583b0e5aaa4ecaec2f364d15fcc92127b7975d21d73d88cc8a4dac794c4c4d", + "0x8b35fafdfae45c2084821d8232f39515f0eae1a33b54a7b068c27ef577f17401", + "0x9164a7d5479e4e017762a6d23f9615aefb7908309aac0d7ca5136c5f12038be6", + "0xbdc2907185dc191216024480db13512f447bfce61a26cada361b13519f4c8750", + "0x26aae1e9fc65bc757b8b5214a53dd3181716148fe5ee8b6024def720e8f2dfb1", + "0xdd27ddb445b4e0ebbcaee4e4ee16cd82026053ba710d203a8f12d98be49bd192", + "0xa65e6212135dc4800e8db31eaffecc5fd102e27392eeb29a0408027b8296aa6c", + "0x48be60400a417b4278f6a4462df5b21ed84478aef95832d6bc84d268043a58d4", + "0x15064498fd0d9306f93b049c09e2ae34bde26666a68398d43334bde79e81a8c5", + "0x1675cfc2488fbcc07abb552be0d9330863d72109cd326df28fc29ebe31a9f0c3", + "0x355dd1ddbc5d1661798bc2e6c14bab637c865ef2f9269cbc9de65c000e8ffd6f", + "0x3d43b77d3cc2496287b70876afa5fd95091b9c7be2404ba7d4beb33f63a59946", + "0x67d7b72f12b310423f7255c23adddba8b254955fbd26f6e1a04d2894b986d2ed", + "0x474266653b5c0fb8cdec08165fd372e0621666cec9518dd760ecf4a17532909d", + "0x21789112f6735fba468d54bc2fb0e36944dc6600714a5a91559a2d3c58a88506", + "0x4f7b92f944a137f4fabe94e79dead83d0751d67fc576d3bf0ed12f45ae7ab19a", + "0x8e8b807df4d55d066e557499e338a0c4469d0dc60c1f1049ab10c0793bbc1341", + "0x0c80c5a951364b0535c30cb2377359330e62a3b22baa9e46def2bd34335808df", + "0xed0e8622188e86095f0c00451330a8c6c66c3c4ded762c24cbc5d9583ca6c14a", + "0x32ebe89b887a32f373619861777bd4848834e2f15d32caefa86cc68a828b60e1", + "0x219dc3416bd64cc30ea4d235962476703ef07371b894e2fc6921831a400668b1", + "0x15094b0e7221259ed675e59abecae8a7b06639b8af7c8275331fc4fa2ff28bfa", + "0x39eac3cfdbcc36436bb4dc9549921b3da0b698bf6811a524f5645a43ddbb4152", + "0x50434374e8cdd1d864073732729f22a42b396973a94966afb61787a6a016878f", + "0x65b62ec39e4e8191eced64fdd511f8631bdc91361e7340b04b0bcd36f64a1450", + "0x108449a43e9c8162c067f1a7cc592c64d77646fa56bf752b4f9d8cf509df8ca3", + "0x0a01e40f6cae91f422eb3f6ccc2bc1df1ecd74102e31ca7c108cb830fe960dbd", + "0x687a6e0d33f87f4218a27806dad2cfcc03ee83a60b129bdad30640210bc0ceba", + "0xbe9514f263b1e396879f7e826e70eb610f9324d6b5b1bcef613184d858b20268", + "0x5240516bb101ef84c1ade7f6d4859f1de174f8d71a58651eee8aaaf625812c92", + "0xc92b4d735fe7b91d55d4d9dd7be705010c5046e734e7269e00a8a2d0b6eda469", + "0x989cbd034d9785f6391387ff80280bb871f5fcda1ed4491558deedc0642d539d", + "0x8b1f0fa6557b231bcfa3c1e3e441b0307d0143cce4147404cc2251e04f2e7c6e", + "0xcd6c7d23c13c62de5c2d1b284e95eec5aa938146963bfc0d928dc4f2bedbb990", + "0x6598cb8c83478b0169bdd9efc51e31c03029a193962cde282eebc57ca50209a8", + "0x6ac406d7f3c720ef0e511482353fd1764a59e236681dcc21b5b0c5608065c254", + "0xc4f81e27afca02ffdb9cc75b10755714d9f0ed585c49a02ff56dca68ea81a4b9", + "0x73f18bb87df70b65d87f5857b7ed56a0aeee9e5782f02c731b2a088dc87ffa90", + "0x292c53312388cab0d16e1ef0b5e0664260e6192a2eef5466cf2f55b9d884e84f", + "0xe12f08a92be7116c0f2124e00e31a0e365862c02f24b969d11d73eca742e9d20", + "0xff4dcaf5ecacd0a0c79ffd05fed7064a42aa34b7e4d4c2ef06cf93866ab11b0d", + "0x09b269a7bda10a51f41a382195997c1eb999e7d5b95cd80c2bbcdca1516ad2df", + "0x84a3219e02c807319ccb729b8b75884ef07828362f41352ba5366c8a971ac8ad", + "0x00625ef937b27ea4d4cdea0877a8ea6c1c12a30fedaf9ffa616c4bfc03fdad41", + "0xb260408afa75b0dd271c42d03329d18b4a52eac1b366284e4dce49228c93aee4", + "0xf6aab365a58d33619568bb5b048f0832bf56464b8ff18e5e5cb01221d2c9153e", + "0x3c1cca7e56641f2513ed3a8200af452f07c57ba719a03f0ad152924d5025f78c", + "0x4d8fe151b547736943fc9461162fd0c38c3c587543ed07919f0899c33571c02a", + "0x1f96e14e7f4b0d88667caa9358f71537ae8259058cdd5bf4d4548a596aafd6fe", + "0x47269a44adf8d4da7387b4472a6fe6fc73ff036bfd3d18539baa7a994cbb08b6", + "0x99dc1f31c4372c92c111e07f9204b0c02a65c60cb98945b48f36f91e4ee3ebbc", + "0xb386f83f1dfb377542729637b7c9fa5aaeda1b1e80a943814b595455169df4d5", + "0xc658d48da7857f2428e842f082058f67b5a207f1e89755be975d63f2536d052d", + "0xb4ac984c3973d2b29dab339b22f24184a71a8f2095687730e2e983c7ead1debc", + "0xe18a17e73707dfda4dff0d6d265dbefb30ade501f66ddb271e4acc14abe33a67", + "0x54094994c0767621e6991968292626ea6b96ded08b7db4911ca813addd6c97cd", + "0x66ebf14e3b67586988821edc1d1359b5e2b7a1308ebf74e8f262936eaf094a79", + "0xe4ec70730abb9ca432449ade5fa755063f000e4f951b9bd66427a7c81f0e2693", + "0xba2d8fd5e35c180ab7f82bd9756bc0d6d9869517dfa955c4eca986ba24e77879", + "0x01a94449579d72a037bc9b3f2c7e5b2a9fa271efbba739dcfc4317f46f7f6351", + "0x58acf073f978e3429fec96bdb141370e950c4b6262455e187901a8ae9cd996c5", + "0xf8dfd826ef39b866257bb1735a6f90e42cf5c43b0141bef9e8a40ad27fce51cb", + "0x13bc935c01bb67d74f990026ee7c71af40fda4ba767477811806033c9eda0627", + "0x06be0395e7baf7411391eb25aa6d686d17bc9b5234da75e222513aee2fd57667", + "0x79c8f4cf27457be49ba894d8f9941d07537c6efc8403e2409fefd48cdf57ff72", + "0xa3e8dec2fe3b7ae4a72bb335ffd73ba83b6f554a241bad37f57e91fe0489e0be", + "0xa09c11f4d0a7987b2b13645db856b0075aa7cf2ae6d192df34e47ac3e382e91d", + "0x6bbd7d58f36dabf050aed607cf4cf0cad4b77cd8d8fabd7e8102f37d57d0b431", + "0x64f8a336f48ba487fce269f2a50f96b27854e821a9ed8a7a37d234531919fffc", + "0xeadd50546049b0fe946d16fb473694d67cc3a3a47b66c95e36e2c5c07d5c00cb", + "0x7ff4c90b3926f3d1e0e05256afca46888c873a518d4cb31e2a8e09774c8377fa", + "0x071beac76eea71ff246007f02a36cc8a622280dfaec5ee03ad4b30c7de4e7db7", + "0x32700ffce5a146bcd52564e00a98452038e19871f766a49734705b0715b4813e", + "0x8d2266050b03ef7e8a04338443bde3838d70c093ee018ef1b78175a96de98f1e", + "0xfae1a03d9e78ac16e03720f2195ea151b266fc1af5c9cc394846a3005b939944", + "0xa9db306a833357705f982694f681ad88e6b4f01f8bbe788ebc94e46d6e57de88", + "0x0bcd8dfe6604fafeebbd60243236c8b087f39525a764b069114c254d9b3a0660", + "0x32233199d897fdca97996fda5ccade8627169b0f674fdd2dde0a9fc7fa90c44e", + "0x2457dd631c1eb6142f65a4b3de12fe569a40d44345d0bde097341acda75a40cb", + "0x484bd77e0c06fecbcdcdcd28b07baf8de611b732123a08bcc4a3a94a49e6a6c7", + "0xfb9cb098723627e59ea6904dddcf688d8df3f7128a613b0d2519d445034d79dc", + "0x0804bb070784b8eb843c54abbd0a3047cf0c8a0ca7cad1853b2c9e9e38058d61", + "0x3c8d85c1cc596e45cf5f2e956ed42e1f1b87e8d38a9e043520b23721a8dbfa6d", + "0x718be0e6a7902f2464021258cc453038fd16baf96ae5c23d4649d4f12b405841", + "0x050660b3a33d96bab20fc6325ad84aea2727cd15e6908b438ba781ea92b83b51", + "0xd1c630d407eea28e5ac5bdaa26ac32a942896b5df088ba3adf67bdbe379b0adc", + "0xe76ea018d89c46a82381d930814e79580bdccc0acee6040162ebf44d174b0be1", + "0x25193d452b79ba224d3099ab5037af2420604d533773dcef5809bf60a46d5a42", + "0xf6576b28692002a1af1e3a6b0c3955cdd9c152b8b393e23dfd35b89786ea9067", + "0xe72cbedb7d4d7be50c52a0c0341b6a1e2b8d2b9b5a337eeb848840eb67eb435e", + "0xd1db44c28724f464268c4167f10ee0b7926178c181693ff60314b6b8d1eaee83", + "0x85b058f6028aac3720e6357d3f422c698b40dbfbc669c4edbfea775feb9a4463", + "0xa4fe8bd82ae34471453c6555314c4eda49f9273e996d6d54f7d1623229a96f30", + "0xa1fe7219b3f459c299f74678c1dff85cfa71e0286fa648022670ddcdad71a14e", + "0x43a9107554de014df984318d14a69e3c2f944ce5f63740e0c234e72ad9a7d05c", + "0x7aeb4bde654f62f16272018905520fdcd5aff603c3b5f400ce308cb4220458fa", + "0x8b2c8a696b029571caccbdc6c62b88e81216fcef074a9962e2b8da9589e4eee5", + "0x70ca647133e53019d1eb2ad9f88a39cedfff6613ba145eff700a2e6430f5edbc", + "0x56b6e937851f9fff38f8482fd739d2534bbea1d9f7bdff37593f2b6e24c6c892", + "0x6fbbbb8c4b2194b3f29d9233b251d33ad2b35e44ce51ceaaad6c982bb27ad6af", + "0xc76aafb66d6506356a83bf7bb4f618fa8b4e8fe5c5eab6361b6381ee519d7299", + "0xd839e351e77b21da7b42028f0102982703785b7ab17844c8fccc08385a85fdae", + "0x18a0082277bd4521c73f506f775f7b9193c81e3985c1a0341b97c9eb532a2aae", + "0x18dfbfa1771b2e60c8e859352550104d4f4ed4f788e4acb5e1700eda9bd2aae6", + "0x4c637fec82e7c993ae0c1f1ca76ef7ce02cae03eef1d40d3651c0ca945b37223", + "0x59fb0b58a885ba8dc9fb49cae638505bb609ad4fb8ff6769cb5174df461de748", + "0x97c58e7dfd2d45c4162f3590d0a4307b3d489223167df97177a11a9a2c4f107a", + "0xeb3a84563ab5b034eb5b301ed927db60c7c9918a73c88725bcb4f391ec80e264", + "0x1e24665e197511c1d5f369b6cd5d9655e39ccc71dc96481c3786294aa95f7716", + "0xd99d4053a8937501b5f33c2a891dc1c5c68596540f46be4bb95829b0fc8fe518", + "0xcacd99e02e6d95871337755edd5078fac19d0765a296a256401cb36772450fc8", + "0x0d8b1ad0070f13966c5c1a793f8596a1c4546e0deca4d2eab770abaea7d8c788", + "0xae12f968a8d9e854a55213c7d8c3522014fabfa31a41c5620bf4c1fa4b412ed8", + "0xae8d86547cd68065a8201df6f5e5e7ff7d0f253354e3a11f109b4c307197bacf", + "0x9aa0e527eb19eb7ad49e98387b7c367a8af94bbaa1493e9cb1f82ffba95439b7", + "0x4bc7af1e8d91864331f06e261b71ed690a2f95bce4a10b32ce8e57d41ed434ea", + "0xb7e3303f5aea61f4198720b67fec535db3a67a236271897c40a5b3626324b1e5", + "0x1c28333ddb6b715dc49390d7603124a32aa79f2d1a3df439972b796fcda24b75", + "0x1142fe51525a5e1c35e710e39136e9272c3ffe17e93070e29f543e2ce15a1747", + "0x13d2fdd1a280e4cbf56c256a0874b37d1c96b720c57d6baa58ce5e53c878778d", + "0x0697c729147f0bfcd8140ece6818594d9700a06eb642dca83c82d71ee3fe58c3", + "0x575577dea847a6dbdae4996ee5d580233f72a14862674c5d3e7a10a42e2d0351", + "0x8be0771aa3c46db990fcf44e5695cc68e0eaab17a27c2edd8ad133071b213d08", + "0x97c00d772b74e819d1ce3bd8f960ed146876c92a6832fe550af02ee1a5fcdb6e", + "0x252574a6c2b3d4c1256555c92b15bb319c65c281ec7182bb846556039aac9ae8", + "0x2c8c35d0884c1722087df8453554bb41c4dc0ccd2c733a4cb0ef96e82afdce29", + "0xed9bd6b0a0935fea6255c1165c3cd4f4a679e7682b2c1ddf56576a4ff4e2899d", + "0x15930523b35b7dcb5c3667235530f65c6dcb6e50be95a566bae3ab8462e09d6a", + "0xb496381c8bceed672708b285db8faaa4a88685c82cb3300aef2eec432ba5d510", + "0xaeb6db62dc87ee9c11b22dcb1f6e2edc8704614ca8339f46fd5ca93c9a6dfdef", + "0x51d2858b08f1fce5e913eb191bf00e892101b28cc052bc101fdd3b1431f33566", + "0x6e116778db7bea0bcc0ca8091ddd25d79e49380e8ae1db3f6c5d02a58108019b", + "0xc8bfa6406b64178e69982aa5b0469e3c673335d02b95c4e92e063b63f3c4822e", + "0x99dc14bb9eac3b98ec17c47dde323eca58c03995819fd77064e67c39fd4bd267", + "0xc1ead1e67df08872a479b65e113dfc2b526798fe6710498d23f85860f975f19d", + "0xd02d56db80e0f440b8a341070e9fdbf4367d4f00de0d20540fe0366ceb805c43", + "0x5e729e9b1c67af239a175145dd70231e27c803e74f41978e2c2a1c57bb44e01d", + "0x8278da9eee31e624a0a18d3fbda12a01212ec3b768efb17ed0104861d0685c8c", + "0x9fc130daf463021658a868f4e04ed7ead2f7eee4f7fdaf69f6d8e56e08fb52bc", + "0x65527a873087e8be55671750d2c747dc1fd6786097824cc7e6a38014d4cc7a40", + "0xa5e696a432c64bc1839ac343f4b421d480f86050f463a32bf6f4886c271b579a", + "0x59b5c415bbc0e63238a465b9f97d246a71a67265796568e15cad26bcecafd656", + "0x1dd2cd36fdf907adc8056ab2d95ea1271d750afda532e72b1e4934831acf4c9e", + "0x56003948c7d0b3ba9a2f2b750e59435f6c07931ba7b268e921f92641a99f1ea5", + "0x4aff1adfe1ca51079258e8af4459dec78140e41ea008ecbc4f6c0ee6da9c0379", + "0xf06bd8fe34e7ef3adcccc9ec96bb8a353375d46eaf4e5b96d75a81605f04abe2", + "0xf3e751999b43f851b5c03da4c204c516af1c48a427e918b1c06881f19df9e698", + "0xa3096446ca5a29ff568afd1054d37f9c1cee28ef5e285a73ab478dd4aa7166b1", + "0x481c53551e35f05d14e14449c0baa5595c482e36dc5f71cd1a6ac94027eb1d08", + "0x4604fbec1af30c8a30524ac648da5ad50d35a2cd2d7a74ae3d91a485c5f6c627", + "0x527dc65c0cefe3734029ed170d6a555729b83ae0934bb28230aad0c1b6f81579", + "0x7df2ef4eee8774415151f49c3f2936778f660f9086e389261fdb7838e6805e86", + "0xf7a6da82f8adba9187bc45cb1e23428c7c16cedd3b8313816baeee93784b3bd3", + "0xe69fec45b3b22ee02e540adea0a45dc05c0268e6ad6e9a6d180ca3a407eacc79", + "0x67052e1622bf5bd9227e63cd0fc3dcaf8db70012649d5676150121e73f17c314", + "0xc253d564634365b785ee7f83f28a9fa2f975e24063a2e6b8744631ffb7f47add", + "0xb0c858b95a5761e46aec676ec81ba4d84b76f4a77c968267e835905a035b1d79", + "0x8e400499cab14c1b3c172758ab8c1ae6130d986d4252bb5a08ff2d01ba9a5ca8", + "0x919fe24156e68e047e059048771ebdd7c521bdfaceeecabc00d2985eccf85f23", + "0xb848d66d8ef6611b5b8aed3c2b52514d122065ff823ae2791b8fb2a0da0012e6", + "0x4e2b2ac13f7d8fe7c3610d9f9ca72a2e8c3b087a30340dc602cda5dcb34ee1be", + "0x7fb678e57abe63f107a2f551f02119ea8f4818b33d24254e83a50ad31cd9e003", + "0xe20d018271635eda8809a6e33e212356145dd760e0396ca4c7c20b174e0903d1", + "0xbbd32616b6405ebf16a849c52466f44d1b6a75781144ca26bbd9a0a800ab579c", + "0x66e56cde1d5725159dc25161a7fdeae566286102197da75d3b2a0061c5e72e17", + "0xed78c34110a34a035c272d23177b1e89711aa6fe5d86bc310639e2be281cbbc8", + "0xdfd222650f64f50bcbdcbb0cde52f2f595e2bbc6c407f2331d6261710039a229", + "0xf417a3bac12e37cc34af45941d125364e967d733960dcf532f5884f0123cce8c", + "0x71bc627f16637e45f324d328ee77753405f386e6eb09d728751484b669bb39a3", + "0x8c1ddc7575b40e933bf78d79ffc5e7a1576f57d815188135c9b69f95cb7a07c2", + "0xcade8b3aa016d5c38fcd25e437efd7faa7c7b348ccc0f5739e432ea764c43934", + "0x0faeaac8367dfa7befe0d1fe8faa0dda63e52b2a3fd6c1949ad3cb573b65f812", + "0x788c80ff254701d0875226bf44d1979f0630d62e61e462af607030ff0d9b3368", + "0xe0e6e03423b59807bb8b13902fd122e9a54948a62ab1c7166d70b04827732b9f", + "0x9d719c9624361d38acbbfa953d138455aed6219988f4c0efb81943b9914aa667", + "0x110fe7e2bce9d595b165bfa4b1623b8d68aadc009dacffaa7f5b9b22e156429f", + "0x8584f37f802adfc366bc7e4495381a5118a1748cf7092263af6ae821639b375f", + "0x43ba36909165e888100e3bfa68c143608d2957e6e12e8e5a5afc505723f2681f", + "0xe3502faa04ecda52f9e2991e9b0a4bb12336568f8639b2d5df79302293ba0e80", + "0xcb5b16709d72464e9cf8006dee81c0a13134ba0fc7cff79a3c148c023906b46c", + "0x39042255a95890e5e25fe5b337ec6f67752a525c3f16c13c192278501d1cf98b", + "0x6acb1a8761b4dea3bbfb0ff36acbfd9691306760128021645ff996047956ce52", + "0x7d271348df01d06078731f840dae289c83ad9c62a053a6fc4c1604ac3761059a", + "0xfded75a4f5fc836ba1e7c361fd8c9859c8837c9378e85382ff9fd5ce68c35f6e", + "0x7be16ddbe3440ca448b35ef2096e5908df7c6fe5ebb947d36fb8b850296be6b0", + "0x231f208f3cd518ffb2fe8247839c85028e5f571294145568041d485cf890818a", + "0xfd6b89d1df0963e9f1e7cd20a04c8c64d4acf080068945d369c147818876661e", + "0xe6b04006a037628d7d5b92daae5a658f0609a67a1e3ab5ba192c4b1b89124fd2", + "0x64e4c31781178ad1cda0f4692fcb64f712fb2168662c62c329a5583e57b18108", + "0xa2d8b3ae5cd5426986f9b9c7efb7b310048a0a0d2f67334bdf7f624733f6c848", + "0xd561bf8889d91b742fdbe91568ff515ed00ee4b00f0c348b7ccbf886b77def48", + "0xc75f30604271aa0866c4bce63a36843ac97393eaaf049d1f298bbe5a222a7e84", + "0xe1311ecd05a64af66424105d836cd841f24109e95dac07c88bb64ee78c449200", + "0xdf9926e89a65d1bb3207adfd7b6db4a3244d97be45142fa06bb3e998dab0a86a", + "0xedeb275850750b783ec429af321dfc027e5d39dd803f4c9a5b203ddf953a1b27", + "0xee6148cfb49cf82a27d1dfcb14f80147f690e01f3d9a890616bf5dc1e607c046", + "0x3b69daf8b05fae34985bf38b252453dfea52213cb5bfecd90f6ca38a8e58fd1e", + "0x348b4cd39e5d417085e043f8a8025a3e68ccd6bfcbdbdcb90c66d70a28709494", + "0x9002d69e7e739b8f776ecff6d118435b900e505a7c16fca31f64ae71dce82986", + "0x29a96ec2a1293ba421682f5d1c74f83aec5d4a6f0fa3c1c394df14b9e235d974", + "0xda53cb82c0ad9c5c26ddb6562d8af102334477c2f7435bb53c8fc4e4a877b0a5", + "0x13b9d65a3a66d855a206f6f63312eaed4e405cc4ffeab4fe7263539360755b26", + "0x4a4cec2d543aea8d1418a1fecc9f0e1038457d753da7f450b7d3293c851ea512", + "0x35630db49b5997eedc830b7b47fe0428a95fdabd954e2c8af32a0a98309e0761", + "0x7f50e590df1675cd2385dd13c5f82cb0c8faba9690dd294b5ca90bafb6b588cb", + "0x2b6cc6065a1804cf831874955a03df8f106fe271ab60832c0f33c3b0ca94bcc0", + "0x258fe7a7fa5a2628512f9a1cf775ed428e5d333213584e40df40e4e1e4b53e99", + "0xd1b148d8d8212698f4f5f99c0b615fe3d1d0d81983149d87c0fadd3f308425e5", + "0xda4b8ef0abc842ddcdcf78fe71a9526120585fbff658bde79024c43fdbc86551", + "0xb446a4dec3b4f517a705e58d699ff5c07290648db07de60bb09e62877ab86584", + "0x33830123a250cd8638561396c1b840001e1e2b6d169ad0bb5bc59286547fef91", + "0xa1576028b9a8c08f4492bab43160fd3dbae34ce6be0de51ebfd2a6d2d6a38dc4", + "0x1d962eafcd83751feade5c58c63e76a56253602775521ab1a202006b391c4e89", + "0x86ead56e6284fa402779a9e4553664ec6ae0ec1803b850a614868b21ce8586d3", + "0xd4cb116199be898e4b56769751a9e85b8ab87818d259e916a9db1fc73ac06e0f", + "0x882652ae9949e0ac72673999486bcad68ff9baa2956c7372670e51c98cefc8af", + "0xf6e6ff41c774c0becd16b43ef716775181a925d274c31c768f74ad1c3a32b1ef", + "0xb09e6534374ef307453dc3f2a0602fe47a346c62f2d001f6697236d2f2e11a98", + "0x75597bbdfc11290134402983aa34f4b5af723ae2e4a825a4b5c0819db72ac240", + "0xb9110e79b2f2330cbe3b4535ea080c5ee1fa80403da4fea5ecb6084ecb6d277d", + "0x55ae4ff2123e5101cd3e4f4fdeed8545bf9c59397db8e8f9d8901e0a7e75ddeb", + "0x3238b14dff5a6eaab7fff9091fed04ab2789b1f368926d86035a82a140ae49c7", + "0x2514e5d19c4a00387e11dc98cef2763692da4a7f90342d2adb6f4802bb70ea2d", + "0xeb5643fe5d8e8fe13f285be6b76b1eac831c57fe46cb6fa7a59e239676676959", + "0x1f1ef022a02bd26891c039b75462be51fd13865c07a3c60dc98688bd45f4c543", + "0x668e62f2513afc51ccbb179aa126c14ab0317c2278702938337949da86e36417", + "0xe16db22afa2c8f38121d6e67250691e124474e4f7fbc8d3c6aa9b285ae7c92b3", + "0x8793a39fb3262ee2c50d11bf33a5604d9e0cc42b39375683ef6a6cd89ab5d5c1", + "0xd8b11d3391a1007d7aa716743c452e6eebcf4bd5695c1ed5533a071899cc543b", + "0x192b38f9be07ef6b42b5217376352c95769e5e07997b9d5baf51663581f232b3", + "0xec13d6b476922aa74a40f50eb782581ba47ec6327082df32473952de25c83d0c", + "0x5cc1c80c881738bc133377ea71be4b6802c99567247719a387cda27668d58778", + "0x34b7a9633cd49ad3a895f772ac73a8e715ce9c4f0fd5507356c8e3eaf33cb29b", + "0x46f1cdf66e60d898ae032f06bfe93f61b7cd69fd5a0eaccb9db620380339083b", + "0xfe6d2f1ba7e4ef4b294b41970fa68dbf900b4e85e58a9af27c4a42cfc30f1dbf", + "0x352f6b99a68daaf5dce5221592a142103b7cda46731ed187c3e1103b96e82dce", + "0x6e99833da7b5b8956d716cb9a2960a392eddfe06a0bc5d78fd564c86fb0ccb52", + "0xfff814a33bac8f75288186ef922ece3b606dcbbbf737c9c4e8a5f1f4b8aeb043", + "0x9e76a2096f56a74a8c7e7087e0fb800cab4c8cbef8f74561f084b744427a57b3", + "0x3ec126ac97179bc3b6b5c6e705e99add70c05fa0944d94cc7d03dea80cf99a22", + "0xd3bcc11fff5b05e8f68dcc7ce18fc859b4694ac6054274eba2d57b429bfd4684", + "0x2dcc7c43c813f86f5384bf873f80719ba130564fd762f79f997afb8b78400ddf", + "0x99dd99812eaf582974ea9dfaa34af648b8c9285f83739064ba5c1a27b15b3a9e", + "0x4dbf1bb339d6bd7aff3cc30cf88fbb1ad477ff86b03003c27c00d04daebe3b9e", + "0x965d253eddefce112690eda236fd4b3c0bc886cf1fbd8f7169baf01180db62f9", + "0x65b655141981bb0fd37f940a0e9cd4ec0da22dea05ea76a218b282c5c3bf6870", + "0x206ced27047c5943fcbe0a3e8aea8319eedfb74f9ef987e325e960c1e5de6c1b", + "0xafe29dce7d2d5fab4e25dbf8feb9e9140b7c9ec32e85998094b45bd567c501f7", + "0xdd9587a1ee2979c22d3c5f9c1f75197c6106fdef4edf96b6c031a145275c3411", + "0x92923394097069edace46aacc259122d57e839a88d8279164c060860c02b558f", + "0xf0bdf2797e8e26c50e72790b8e2eb9f021cdd28d9f787c5be91f9ae9aa7c4254", + "0x375b5ec9a57ec7e138d83d587557756c8a257c35826647d6fb8722483f6ad6ca", + "0x97565f3583e044d82b34538bc1ae4ae73b1438bd293353bcefd8074fc4d57d9a", + "0xccaf1047024f3879cc6c24be5f2f032bd66c811f3e31c43ed4e659cf615d5c22", + "0xe0feb551c2f9d3abb1aafcd39ad8f6e2cecadf58452c4cf618855829e51296f4", + "0x61b00166b1e2418340130739836b55e629e71121c03d951f216a5ffe790c0bb0", + "0x224940c2a88acd6c08d383f9266cd58cc30733778419a1eebb9b3f29c7d63ea5", + "0xf7ee4ced3c20f765bb217df4f9ed3acffeba11adcb3ce780cce4534f0d1f76fd", + "0x6ec6d073da3ba16c979d73d89a484805a61f89ae5aa7da63cb3b4142e672c37f", + "0xf13edccf3c2ba5b07e51aacaac79390d48407f172369fb3b5d0fae5b0362e81e", + "0x36bf725db4b4a913b9da3b9239cfed8cd8c2920b9f6fa225fd9e01cf50f7b015", + "0x313761a59054f3fcb6575d84dcaf36a0b2b98de4ec7c946c0e52d98390977396", + "0xfb2f6e111aca75e4a7eee5e45e9bd019242751208874f70f130631e6e70f652c", + "0x5f669e23137ce3b13c3370a2a9bb69d678c2d4832bf83e92f3d3fe4004e74f65", + "0x3e4e8e594981033e13bfd7c966806f29c488a8fd28b4d4c41762909cb1eed0c1", + "0x012223f48eb911be09c85d7c497383ed56198483a624fdb6fee2647bd225773d", + "0xff1873be4d0bd3b5f631b9838e690a359849b33f7f3fc1e19ccbb72c88756767", + "0xbd687d562bcb4ce7c97533750e6b960ce0c70e651cf4e48a86471d9c2d697bc9", + "0xadf6c679a8bad9a1debac26bc47ea20d85fd583d1ecb6686e04fa9e302f9115a", + "0xbaf772ae7b9297ca5506f5dcf2c38b8342d25cef39d38a3565b59b053f3bc3e9", + "0x75e82f9e9b1c1bf35165642cb5eaf663282e19a865b8ed357955da5357807040", + "0x1434b0eb9da6e3d6b303fb90ff53b1e9437a48c0a76a0c4b697e0cb35be1f2ce", + "0x9374a8ccb290ac7092baaa3015833b85162dd8a25f5bc9085bdb3cec74ab74cb", + "0x3f70a231bbe16e811656114d9f56e2f2189a8c09150c771671fde8dcc810349e", + "0xa35119becf58b88b1047cf99c6e1505fe95084b3a7bed9671086586af9f73492", + "0x80529edeba202929712f3cdce41d5e62e64b400be30367628c00755ff6b9d413", + "0xbc3760b47f838830baa4ffb46e2565e1d46b5ebc23b3b1edf6ec7c805f09c2b6", + "0x62a71459985ab84b9d41cdf869e3662fdd893cba3d0bb4a927153de686599a5f", + "0x5ef1e0728bf7beccf56ba47d4845bb46390cd3c6e93dcb2cca731606f95d1500", + "0x7779015d840f8a88c1d0cf67f92cf127d3c9794ff3dc054aaaf2a3e4b3c1aae4", + "0xa9080b382667b7441685f821da557df676a42816d09315675c7a006a72270a46", + "0xa47b186f5816a5e491f340f6ddbcaea411fdb2f18927434c453af3e516823600", + "0x7d8c351d6adbdd697adced72bf78d872cbe0863ac9ec8d2dba3a710586164cd0", + "0x405866bdea47ef0bea80299efade675f38d3d3fe7099ef81a72e38e1a3cb3986", + "0x4dfc49055b63fba170f839e40b4937db2d526e417b91663315e2b30d6f457659", + "0x305c768a219d7cb2680e318db51a56316b4cb5031ea3054881391223f14d0d68", + "0x068059d70253d4b9fa6c6b9aa5f1e630216dc86909d9d0ef85efebf035c7e117", + "0x8511119d6b7ad71610dc7f6885d629cbb5b29de798ccbab2d19c933c4b19cdc1", + "0x5c07b07624f978be768a9ee75c921008fe568b26fd72fcecac9bb86cafdcca2c", + "0x3fa37280dc8af0b49c5336a212d4ad0eade7c396aa819d1875843912d926d44d", + "0x927f2c496e370470948f98840189bd42e1069c19084d286d92b99e18657fce11", + "0x1e16eade51d54a2571949f47a60f3c8fac1fef64017f4aa89e352d59ebaed60c", + "0x4d192d0b3676f2318ce425322362039570bed3a631712c77b2a7ec83e533be56", + "0x772edb64509e35b4787a4a00b9744970af1611b5a99d54369d72285c1b8cebbc", + "0xf7e8a2805741e7026c16e5725abdbb79e41ae67d7e8244102fb9335825b7c121", + "0xcff6ba50d31a3517b6b8a90f369c1926487097ab1aa8f94db8b1825d732da641", + "0x32785079f4953cb089335c558734e7db5bc56ce9ff0fa6b3eeba5c17ccddf712", + "0xb1bf5ae4a4c4d15951737a8512907b55d6861b4469eb93f0126131f0e960db76", + "0xa1f6ad11014379152a2d9ce3cfcc9ccb78c7d368fc8d6fe6c7d3f0cd9b18d243", + "0x29acf19bbd78df2552e096d34865103b67bcb675e1f7637d7e3126ea81328383", + "0x5fa035769737cea3a671055117fcee6e49f87dbb91f405bd8e57917f40d43258", + "0x9376967d393c7f162b3137a8d5223eba30523efb840a77f9ffb3c54f6562cea0", + "0xeb906fcea9d31514f9a9ca677a1a5446c59f55b00988f31288be7b793d4d4a0d", + "0x28df5982f0ec1bab9f53634f84812fab8474ef1f1ea445bd5abcce7160654af3", + "0xe8db1f0c5c36b93e6a624faab49798068ad7048e0e119b85d2bc390a47ff86fe", + "0x035eed70ca4e86cfeb63724e4497097fb5732f9e35186499f32305e1cb62aaf7", + "0xce7e3d7445800b883a6afe66de6ef645ff64860ecca5138c0114efb0b98658c1", + "0x507d639abbbde60fe1a793cbb74b3390159177b7ed439382e1c6bb519b93ab4f", + "0x8165b07162d41891743b8dc66ae9e74b53eb0b2148ee0a279004ab4fede787b7", + "0xc591ba93abf2754767db7bb127c72be6acd719913c36ff7fa21bdbd05ec3c928", + "0x54f15e8785546f8a30e21ab3674d46a6e891f9bebead810b9f1366d5072fb538", + "0x61d3b6d1e22ec3ce39e01b3a2156bff745eb28f405450e7415b6aa75bc2165ae", + "0xa0710473b2f9c580c38ed05d49c3a70ec3d62b8da444bf60b025dc06865b7913", + "0xd7060eafb6ce0e0290392ff840b36014b279707c96eb8a6874d539c17417db77", + "0xe6cdcd01dbee6f9bebb2b8d6ecea9128adbaae36645e95402a3141f720c61607", + "0x7defe0a4cca43220d9e734eae2c391140146a1429893c4e22872b322097ce083", + "0x0889381511a1c287d44de2b97892bc42a7187f192a40130c02b64fe4ed307979", + "0x7b51f005bd2251f2505c166af288afce15c5daa7e579d84dec64da2611663213", + "0x78590863017925ae20ecd0ab3d894ee379eb6b317fd555762d690afb6c7571c9", + "0xe5364e7972dfd48a13d283faef5048eb5cbf9cefdacfca2f344bab62f553a3e9", + "0xce082eedc1cf0e59648767fb5e6111cd52c2384142bd0e64b53327eed71b9bda", + "0xb55bb94834fd25c07be869a595ecc8c7ca073bcd10a52736ae090b5333a7c01a", + "0xb7ba53ed094df02bbcaa3f5e14093050fd7ce3b5f56581da3ef39b20faa52ff6", + "0x14f91b36704989c461c3c91fdd387af8a0e5cb9de83564768d12a3161dfb6570", + "0xb783126d4aff67331c09a8c2dd67975e390c5e30d491f387eba3ab460d61e48f", + "0x1df18339d1cd450b00eef7c5ffc629ddde7ce7c66635d7e3f6173ba67c465f09", + "0xc3afbe9680722e1c6e6e2f1c4ca05578f5cc03ee471c3a874629500375772ce2", + "0xe0762a2f4dd50008f509b090512a9f3b61da31ca15e89565de1a20a5e82498c8", + "0xb40798c2bc2f2f1d4ca733cf725067d93b8a7ce79e825b0d8c3d75de4b7b75f2", + "0xbbed0630817e3be3756769ac03f8cc47773a89b4d108dfb0f7c8134426d311e8", + "0x363ecf42cce3b2f9fae5351a3b1720f7ebb103d98ab98e83d070169c92f6eacd", + "0x5e2cf0cec878c48132bdc509d673617bdb5c8700cdaf0079594e9f0a852638c0", + "0xa41f1ab43326da96742e3ea121302eb78d699f7dfe5819874502c49922d32547", + "0xc57973f78786bc0da7d145c24029ca002ad18c305a2cb4a797702c2b5935afc7", + "0xa5c244713fdf07819cf73ad4e39370df2f9bd16017bf949235f709253ce8196f", + "0x5a2330c7eefceec26683cec8c78b03b31ed8d487f5a5fbc56ed1a7d70f30b4bf", + "0x4ba6253ac69cd862741d80b6c8cfef6bfb371148dfec35d53b2fda83a1d8d032", + "0x4f508f16eaddfab93cd2c54237c88175593f430efa30de624658ba1c07f7beb0", + "0xc7dc55bfd18e467d6ffd1fab5fd848976541aa5a57b6c959dee421c6a4b7eca1", + "0x261269c42408993cc6c818784e86b6c717c4991aa762d3b0b8478116e3703003", + "0xee2b50c384ec0b8033b3ff955369a91421bf56567495336a428a5e50cb6b27e9", + "0x133644fa7416af0ff9fff35be0ca69cec4624be54b9e578c9498a25942c8b583", + "0x04a76a83d9175aaae387188e53004a97856f509afb1d7b46d894381e73994741", + "0xab10e84cbf8e2b02a2e9c5a8235a1a9df4517d4df3af1830ad50d225c4644721", + "0x2a0aa09df678f7b485b8a3d96e61b79bc0879cff33093410bb833a6ae8dcc9f3", + "0xa8eb8a0459295693d0024bcb8cf86bd0d829ac391694253c99b478a1325a4fac", + "0xa7d00c2f790abbee2aa9664f03814c8639538a8d3c897d573964a4a295b40fa2", + "0x9cb474bd11aeaad0b80dff8f86f053d3590b427a1a47976ae43be8620f1dcad7", + "0x3652c1b1014d272fcecfb5afd5f3cde1767a275054a009204ba41816741aee74", + "0x0b7f0b3f88b29597ce10c687133a97910a285ea3e9b9cc7f961482559c4db446", + "0x28e0f502a6feafa88d1f9839aa1ef05f885edf07b82c8a459c8be2dfe27011fe", + "0x3ed7c0a277f5c487db5ef55c873000051748845d33eccef3db934b4292eeface", + "0x9079da1da700f461597e4f986d0b4cde30873ddb400aaf087a827636f44a7ffd", + "0xfa44d95c7dcaaa534031536f3bfd7630776f1862a3585a6daa5e86e3dc5264b5", + "0x959dbcca720dfd5e920e618c28c663c925b589ae2bd82faab265b09e4cf6db08", + "0xbbe07491ee234bf9ecbb35560ad6e2d1a7ec8cb2690cd68b46cbad79538aba1f", + "0x589d7e261ca64bafa4d7f9cb101f5f3417ef71c501b5317e5162c3774291081b", + "0x5b97793b3b83b554beac60b2b6b3e22b6c257c81c32adeaf11f58cb70794bf5d", + "0xb37dc9e9418dc0178364af954fdb1a9395655955ff138fb0f66a81275ccfe0ed", + "0x704631d8f514805cf51530ba31f3c9f25d1bf8828a410cd89e4d31e98711f278", + "0x81ee7855bbd2ae8e1aefa5141efeaceff850b2223b8aae2fe65f2e2076d11b87", + "0x226370d2bb775c69f3432fdc1545c263311948acbee525e3848317cd9fc3da7d", + "0x4e6b200866b090eb2dc26259b7f8adabf57740fa0a73f73df0ada6d9f80d13ff", + "0xd164106da1db225979ad7f6fc9e11b40e5fb5ae345a818014d810e1a871b2b48", + "0x68087615488020a515970c41ef40392a4877e59f05d07444ec0b7f2467a099b3", + "0x21575bcaf329553469449cdf9d68faea0218728b09627a36ba8426bcf8115791", + "0x1e11dc27c4932814b0234bd17cff690aa0f9c8a258d57e11564484b4b17a3e0b", + "0xa8a409601f945aa2dcb36574966f6b8f1942f83d5110dab917d730016b3703aa", + "0x4eb77c78b2157e63b6ca0ddb36afcda138561ba409552529ab49ea9324e1796a", + "0xb66cd6e9e19f50d0346b5ca40410cf6d334dae9c41ab8fd5402c538c11a94a78", + "0x5069ac42df7f89261f4ba36452190e0ebe938ab46eb422090e9ef458f2fea8bc", + "0xf77faca1a86c25db5a348fc916b7f3724855b136b21eda7779b874459d6b7562", + "0x3fd875f36e535bbca2ba2f539d45b8e60487415a96d34846b110690cfb45fbff", + "0x2cf97dbc6bd70524c91ff72718b5ebc2b3ffb38b46a6430e9f16a1a8c3545271", + "0x0e4bd06ec123f3510d059c03439da7e19397473a2b144868b3fd70daccbc6e25", + "0x36d07bd92969b5465a70ea63ec814f18033990a941b38e76114e2173264505bd", + "0x7d8a17ff014fda45bf5c43a83316f53069214949be5eefa80b0fcb9c447573ac", + "0x70f8d99ac59af894e9f136a46e650ed1dcc022a7f23f7c7f7d3d899f0a934729", + "0xccdc7473316bf9faf3196c11f65eb560cc5be04988ed4aaeb2ae2fc7a76cd7cb", + "0xb95e9102f44161346459f9166695ad4d01b02cf827333bc04b73e9bfc76a6b4a", + "0xa0073174e023480efe99e1ee414a3a6fe7a4289a9146eafab77bc2a2f74ce041", + "0x71a170f1bd3eec872eb86ca28f5a07a16d34df621841c9a0327c5a4842d14cff", + "0xb46b6570cc7b9b9c6530f1395628201390559117744bb3ae3289a1329e2a9fe5", + "0x6612ab632dfb5fa48c92c28c1ebdc606d6456defa2cba07fee5c7637a9f0881e", + "0xbdc0d3c7bef21e95e10e012e8b3beb1d9d6afbf0f6d4431671c5e2c9d3d558d2", + "0x1317011bd4eded75c83e9a7a96e70556b3c056a996b90efc54cf983eb832234e", + "0xb63e87384e2b36fda06a1600031b9e8f76d8ab3ab2d1a50fdd24dbd0d7aba82c", + "0xc266ed0d55603ff6781b55b850d6b03fd2f0e96298b4cdfb5f05897a0fd2d143", + "0x3a6d30c03b37b50fee8cc592e39077f2577c1cca654fe65b30b8e6b7175d7c2a", + "0x6c1cfd1c1ba9c00cf25d42fc53fea15b3d436dc5b58f2cb3afeb8bb2a353a5c0", + "0x63d68be124625c2604e5297a5f8e655a6d44a43d0254f538d3f8e095c75c0e0a", + "0xcad240da9f6d07760ff04cb649dd5614d9ba8d1254734f2d99583cd24ec35d89", + "0x0ec939ec5cef73b863b964f3d7b852626d9d2428ce174fe998a45bf01aa8a203", + "0x9b526692a719f47c3c645818165283d9fcf0fe42e4dfbd780cd2628059278f14", + "0x1896c29de4883b1a6c91cb95db2dcdc0ff176b65ae1fd4bebc18afeda41eb425", + "0xb71cef6adb860bbde041254126bbaf54fd2a385f1870045c4b7986e22bfee34b", + "0x05c0644792c8d246675f4882045a2226aeec17443c8f796d63c84533c627b343", + "0xb3d2c5ed1089cc20ff4bb0882ac778417190dca446c7b83f5d58b194ecc55a66", + "0x723ef30c89606acf024be95e383372d42e07214de1d0040f386f268ec216df76", + "0x7dee5122c011954e14f6eb35957ead3b728e181331420b3919105f073d16bb05", + "0x0d10c86c88fbefe4f397db0e19ef7ffd5833d53ba90f4754987e89c13fb67d9a", + "0x5c744c979c84b49dcd4139c7c179910bb2150239979fb5a3b4a1053c6d398dbb", + "0xbe3c32d2059814004ec49e460dfe39cca8202877a8a83c8911f01c3ba234c921", + "0x8adfc2c122e2cf58b3c032981574255cf40b4559df4e62d49d550f04ed0553a0", + "0x80bf3ac9eaa6dc061e488a431c4cf08d4d042ea47453c44892ec50e966081065", + "0x817a4b2f3e63e983f6fc37476b1b1f467099a50a25f356738126479839c084dd", + "0x5dee0513b55736564262a7fc472ff671358d4fd66eeb9596ec170e054d954faf", + "0xce34b38ac7d48c0655885f7378948deed4f107f5fc4268608ca0a9850fd15085", + "0x6c95a9df5aaf661a2f32c96a25a091f66c38cb4b7479cbfd7a9a162dd4bc12ce", + "0xa63bfeca9998140d7aebc8d8603d6ebf91dec849445e454dc6539dcde35481f1", + "0xed514ea523571fd4eb31623e0da3dd8a41bdffc26ab4f8fdd90e2cd9324b1bf2", + "0x4037b9d183c4d81add87254e50219cf87c4cc94128535e50f2f04331b1baa3a6", + "0x1f009704b3245fd77ecdca0ad95322e6ada6f4075e1289108675caa5da480de3", + "0xab62a47d7ef09bb1f1e78289127d993aee91bfb76f95bb3264f6577df6d54684", + "0xc70538d7614ec964fea3227e60d9d46a5c6215c185d1d0db8e8c39763f50421c", + "0x2f294fdbc0f5a6407267d525fafd59211baf345e566db91717209349f655a31f", + "0xe867ce4a2df3e263044479d9c6f64a72a4d5b1f3426c54b9fb217451f81e6d6e", + "0x5a00548bb8369aaf20d16a63141a778346c70a37a3851eec1aac38c69c3c8c7f" ] }, "nodes": [ @@ -3916,12 +4426,12 @@ "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x85d9a0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0x85d9a0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0xa03ae7": { + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -3929,12 +4439,12 @@ "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x85d9a0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0x85d9a0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0xa03ae7": { + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -3942,14 +4452,23 @@ "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x85d9a0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0x85d9a0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0xa03ae7": { + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "activate_at": "0xa03ae7", + "pricing": { + "blake2_f": { + "gas_per_round": 1 } } } diff --git a/ethcore/res/ethereum/constantinople_test.json b/ethcore/res/ethereum/constantinople_test.json index c61711e7a7..cc5690e0d3 100644 --- a/ethcore/res/ethereum/constantinople_test.json +++ b/ethcore/res/ethereum/constantinople_test.json @@ -61,12 +61,13 @@ "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x00", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -74,12 +75,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x00", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -87,14 +89,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x00", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/eip210_test.json b/ethcore/res/ethereum/eip210_test.json index 782e99e5e9..2cfeb7c61c 100644 --- a/ethcore/res/ethereum/eip210_test.json +++ b/ethcore/res/ethereum/eip210_test.json @@ -49,12 +49,13 @@ "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x00", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -62,12 +63,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x00", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -75,14 +77,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x00", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/ellaism.json b/ethcore/res/ethereum/ellaism.json index d1333fb7f3..eda799805b 100644 --- a/ethcore/res/ethereum/ellaism.json +++ b/ethcore/res/ethereum/ellaism.json @@ -49,16 +49,18 @@ "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", - "enode://5120308ebf25261c8423866a3a082e8d0f31106343d8b3b6c4dfe9d41bd900f5e03c64356ba51b6d343a951846a3f5ede5c5dd05925eaea4e4b9c35b1be9237c@95.53.247.188:30303" + "enode://b5e153bf33b2b77e6a09c1c3c20dbffc964b3622d53f25c1088fd5f4b88ec434af3c1ecea3318ad0cc3cdf48fad37dfec748ed6d9ebe271181d7e910fda7f340@142.93.163.95:30303", + "enode://a34e38cecf83bde932aca28f8a66487ef195ea9b253558d50dc3c5e3261909c7057ff102d3ba73bd3e887807a9fec42ec9f0c1c825263c1e4f8de0a5a4e857ed@157.245.181.78:30303", + "enode://2e9a4eaa046b45714ebb6052dee53c320b720e3ea5eee2cc0614d3dbeb19eb1a2e738491b46a3298c101fd6dd24b8b2e1b6e2c50a209e6c467f8b980ba1acf15@69.164.212.225:30303", + "enode://a7c84e5a44c6909ee4046a07101f2705b8cc0964d35c0f1b852e206dbbb787a5b6709ca3ba29d5df32ef431d06d14c1e0008bf1632415ba8b7b525cf80834723@176.99.9.182:30303", + "enode://4a383990e702b7ed4ec2eea42eebed3142905ddbba5c97865125d2e4b0f09990763f127e1781fc6357ae0578f4b6d322a92ce29b4e1704e2744a7ce9908e51e6@185.163.116.77:30303", + "enode://5fc0737f0cad2b9724edf8320878fc6e197b9968e5e9e798bbe24728c6e431858cb2ea4f8b151ee24bbc228820087db3c60fbde24b7d167896a25065f7dfb4c3@84.247.76.138:30303", + "enode://5364bd8aa566b79a4a9b88916f50b0140ea7ee1d4485394f60e09ba9c85927888dd7145b0a7d835663fc142ea44ce8f11405e34cb02d774e4edef94c634c570c@77.13.29.32:33003", + "enode://b9d6b7c82f9e8f6ba6c164da31cc2093dae3e1f1e1c315c71bd17a78b55a6f23e4d87b3ad0e3db6adaae4e561e7354b9d4d4be103b13f65e0638d7c5cdf48e4c@93.90.206.247:30303", + "enode://09284d2f3161e963168e3b69f6a83f27a129f30aa07a57bd8eafdccb66b9d653080721f4c3b51c2a119f113d1b9f9f736b14bbfcca7d2e2f9eb118c2e5ce3e81@83.219.143.198:30307", + "enode://93800f7367b4aba7a76d7a98fed44fa97cbcb62c6062bccdfad328e5786967c24f1f75c468e6a530425ec8839cac9c4b455b49ac5cdf37b6e67ba549439d285b@89.33.253.232:30303", + "enode://f15fcfb4e881d247b28db2ff7195ea380b642eda8601b646502cd31b827113c2f32ae9c98ac2673ce37a5df6ff1fdc737199c608a5defdd8b799e584461a4ba5@89.163.148.73:31058", + "enode://85d95b320452b745e45d4bb9353ad2eb180c98c9ffb85f9a572b972302f06dd2d130fa2ef723ac6cc6418eb842fd08fa5ad9d934288c756f9a63389bd354210e@18.228.30.92:21000" ], "accounts": { "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, @@ -69,12 +71,13 @@ "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": 2000000, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "2000000": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -82,12 +85,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": 2000000, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "2000000": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -95,14 +99,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": 2000000, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "2000000": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/ethercore.json b/ethcore/res/ethereum/ethercore.json new file mode 100644 index 0000000000..16091ac357 --- /dev/null +++ b/ethcore/res/ethereum/ethercore.json @@ -0,0 +1,189 @@ +{ + "name": "EtherCore", + "dataDir": "ethercore", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x20000", + "difficultyBoundDivisor": "0x800", + "durationLimit": "0xd", + "blockReward": "0xde0b6b3a7640000", + "homesteadTransition": "0x0", + "eip100bTransition": "0x0", + "bombDefuseTransition":"0x0", + "ecip1017EraRounds":"0x5f5e100", + "progpowTransition": "0x0" + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x400", + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID": "0x1d2", + "eip150Transition": "0x0", + "eip160Transition": "0x0", + "eip161abcTransition": "0x0", + "eip161dTransition": "0x0", + "eip155Transition": "0x0", + "maxCodeSize":"0x6000", + "maxCodeSizeTransition":"0x0", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "eip1283Transition": "0x0", + "eip1344Transition": "0x0", + "eip1706Transition": "0x0", + "eip1884Transition": "0x0", + "eip2028Transition": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x0000000000000042", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x80000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x5e0be100", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x7a1200" + }, + "nodes": [ + "enode://dfcfd268e7d4631cec5a94d7eeb5981a2ed3f30235c774e1bf0a4e843672405e5a1c93502e1631edf108b6f1ea8701fb97e6d892eb5e3775c70dc53b2f773b9f@207.148.105.65:30303", + "enode://6c67afac3a018ee5641b0aba996f180fbb718aa7857174cad568b00440a43cccb1845124cd1cbe43c7ce1e95d597a3d20d175f0bf8494d875e83ec15e0f42cb4@207.148.105.65:30503" + ], + "accounts": { + "0x0000000000000000000000000000000000000001":{ + "builtin":{ + "name":"ecrecover", + "pricing":{ + "linear":{ + "base":3000, + "word":0 + } + } + } + }, + "0x0000000000000000000000000000000000000002":{ + "builtin":{ + "name":"sha256", + "pricing":{ + "linear":{ + "base":60, + "word":12 + } + } + } + }, + "0x0000000000000000000000000000000000000003":{ + "builtin":{ + "name":"ripemd160", + "pricing":{ + "linear":{ + "base":600, + "word":120 + } + } + } + }, + "0x0000000000000000000000000000000000000004":{ + "builtin":{ + "name":"identity", + "pricing":{ + "linear":{ + "base":15, + "word":3 + } + } + } + }, + "0x0000000000000000000000000000000000000005":{ + "builtin":{ + "name":"modexp", + "activate_at":"0x0", + "pricing":{ + "modexp":{ + "divisor":20 + } + } + } + }, + "0x0000000000000000000000000000000000000006":{ + "builtin":{ + "name":"alt_bn128_add", + "pricing":{ + "0x0":{ + "info":"EIP 1108 transition", + "price":{ + "alt_bn128_const_operations":{ + "price":150 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000007":{ + "builtin":{ + "name":"alt_bn128_mul", + "pricing":{ + "0x0":{ + "info":"EIP 1108 transition", + "price":{ + "alt_bn128_const_operations":{ + "price":6000 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000008":{ + "builtin":{ + "name":"alt_bn128_pairing", + "pricing":{ + "0x0":{ + "info":"EIP 1108 transition", + "price":{ + "alt_bn128_pairing":{ + "base":45000, + "pair":34000 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000009":{ + "builtin":{ + "name":"blake2_f", + "activate_at":"0x0", + "pricing":{ + "blake2_f":{ + "gas_per_round":1 + } + } + } + }, + "0xaf6F001FdB3CD98CD38A3f6C7306706D98689dF0":{ + "balance":"0x295be96e64066972000000" + }, + "0xf182a7D9e7789E82e362A98eBC5fCAcdC2904182":{ + "balance":"0x295be96e64066972000000" + }, + "0x5A23b7d2ee9dccbb7C33103d22852e1cC88548b2":{ + "balance":"0xf8277896582678ac000000" + }, + "0x22e0176a4aDD34A2a32B4423437B6cf23F7dc638":{ + "balance":"0x6342fd08f00f6378000000" + } + } +} diff --git a/ethcore/res/ethereum/evancore.json b/ethcore/res/ethereum/evancore.json new file mode 100644 index 0000000000..6f25a0bd1c --- /dev/null +++ b/ethcore/res/ethereum/evancore.json @@ -0,0 +1,75 @@ +{ + "name": "evan.network core", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "3", + "validators": { + "multi": { + "0": { "contract": "0x1000000000000000000000000000000000000001" } + } + }, + "blockRewardContractAddress": "0x1000000000000000000000000000000000000002", + "blockRewardContractTransition": "0x0" + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x400", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x3938700", + "networkID": "0xC06E", + "transactionPermissionContract": "0x1000000000000000000000000000000000000003", + "transactionPermissionContractTransition": "203100", + "registrar": "0xB88E14514C8b5983EDBAB3c33534b75911eae302", + "eip140Transition": "0", + "eip211Transition": "0", + "eip214Transition": "0", + "eip658Transition": "0", + "wasmActivationTransition": "0", + "eip145Transition": "0", + "eip1014Transition": "0", + "eip1052Transition": "0", + "eip1283Transition": "0", + "kip4Transition": "0", + "kip6Transition": "0" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x3938700" + }, + "nodes": [ + "enode://b5ce9c707e006d960aaae056d0344be00578a90cfa3e3b66b8d8696702a30f8b654f5c3cd91e0372931460dd3d3da13a70d8264ce5b7c731699889010e8af51c@35.180.217.147:30303", + "enode://0e41b902051c851f825b49736964805a6d38d762aad6f415a1d8f9776527effc0a00c912762285fb0eda73f31978cfbd47a3a1cd4ee6d53f7fa99d5ba09e7b78@206.189.249.234:30303", + "enode://780fc8110250a2495b52215e939c80e24149d2724cfc24f83fe0e7637281709552ed1daa004209786015a3a9edc009ef4f78625fe926de8ba54fe337eda079e4@139.59.205.76:30303", + "enode://13b20228e4677df5e2efdc3211e2c911dafe221460bcd1f4310878060d9ff285c64f03a34bf9d6b9b31b8c5ca05e01b5bac8d999236943655d4523da2641be53@52.236.131.101:30304", + "enode://8f6c5bbd28f5b2e98bd4056cc7932ea79cc78d94eabec280018a3b5d928bd146a58b136e9e857b354f9b539509af71c1befb542d2573e131a65fd290722a1082@52.236.131.101:30303", + "enode://5102db2af9d69ebbd15b1f75425606070761789c87e546acebc68f16c8f651191e4bea2cbe79f10efddf8bc6f20a3b88706635e84f9d135872f22d30a6352965@52.236.131.123:30304", + "enode://c6858667bb8d30f818bc0c7d539ff597f4ce5aa7f8a7a4b615864514019061415c321a22bf0f89b906cf97a3476d4d52c5b7bc199386ef37d4638d9e9e0a9053@52.236.131.123:30304", + "enode://0e41b902051c851f825b49736964805a6d38d762aad6f415a1d8f9776527effc0a00c912762285fb0eda73f31978cfbd47a3a1cd4ee6d53f7fa99d5ba09e7b78@206.189.249.234:30303", + "enode://780fc8110250a2495b52215e939c80e24149d2724cfc24f83fe0e7637281709552ed1daa004209786015a3a9edc009ef4f78625fe926de8ba54fe337eda079e4@139.59.205.76:30303", + "enode://71dcd02a30d3759d3b7ef0b7f927c16a42394f204b674e77639bd65dee5e7eb1028f88d65c70e4a6e5f5d282719d06c35e4e5c6c1a0629bf9a5dddd7a2796e0e@167.71.77.11:30303", + "enode://f978c280404411006ed0f2bfcb9e6833f3a173cdcb41ec4c07022acf90cd3ba2fc799463cf2ab53df25f8472193242ac32c54a55c3441b708434d362bca68abe@68.183.243.214:30303" + ], + "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 } } } }, + "0x0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0x0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0x0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } }, + "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x0", "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x0", "pricing": { "linear": { "base": 40000, "word": 0 } } } }, + "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x0", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0x1000000000000000000000000000000000000001": { "balance": "1", "constructor" : "0x6080604052336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060146001553480156200005657600080fd5b5060405162001a9438038062001a948339810180604052810190808051820192919050505080600073fca3df5cfca13005b2710138b460c84567bb330f6000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508160039080519060200190620000ea9291906200030f565b50600373649672b93da8951bfa9ee6813f0332f68f21fb3890806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050600090505b6003805490508110156200029c576001600460006003848154811015156200018e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690831515021790555080600460006003848154811015156200022257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018190555080806001019150506200016a565b60036002908054620002b09291906200039e565b50505073fffffffffffffffffffffffffffffffffffffffe600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506200043b565b8280548282559060005260206000209081019282156200038b579160200282015b828111156200038a5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000330565b5b5090506200039a9190620003f5565b5090565b828054828255906000526020600020908101928215620003e25760005260206000209182015b82811115620003e1578254825591600101919060010190620003c4565b5b509050620003f19190620003f5565b5090565b6200043891905b808211156200043457600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550600101620003fc565b5090565b90565b611649806200044b6000396000f3006080604052600436106100c5576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c857805146100ca57806311ae9ed2146100f557806313af40351461016157806340a141ff146101a45780634d238c8e146101e7578063752862111461022a5780638c90ac07146102415780638da5cb5b1461026e578063b3f05b97146102c5578063b7ab4db5146102f4578063c476dd4014610360578063d3e848f1146103c5578063d69f13bb1461041c575b600080fd5b3480156100d657600080fd5b506100df610469565b6040518082815260200191505060405180910390f35b34801561010157600080fd5b5061010a61046f565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561014d578082015181840152602081019050610132565b505050509050019250505060405180910390f35b34801561016d57600080fd5b506101a2600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506104fd565b005b3480156101b057600080fd5b506101e5600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610616565b005b3480156101f357600080fd5b50610228600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109bf565b005b34801561023657600080fd5b5061023f610b90565b005b34801561024d57600080fd5b5061026c60048036038101908080359060200190929190505050610bf6565b005b34801561027a57600080fd5b50610283610c5b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102d157600080fd5b506102da610c80565b604051808215151515815260200191505060405180910390f35b34801561030057600080fd5b50610309610c93565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561034c578082015181840152602081019050610331565b505050509050019250505060405180910390f35b34801561036c57600080fd5b506103c3600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001919091929391929390505050610d21565b005b3480156103d157600080fd5b506103da610d65565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561042857600080fd5b50610467600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610d8b565b005b60015481565b606060038054806020026020016040519081016040528092919081815260200182805480156104f357602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116104a9575b5050505050905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561055857600080fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236460405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561067357600080fd5b81600080600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff169150600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154905081801561071f575060028054905081105b801561078f57508273ffffffffffffffffffffffffffffffffffffffff1660028281548110151561074c57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b151561079a57600080fd5b600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154935060036001600380549050038154811015156107f657fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1660038581548110151561083057fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550836004600060038781548110151561088c57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010181905550600360016003805490500381548110151561090f57fe5b9060005260206000200160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905560038054809190600190036109519190611537565b50600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080820160006101000a81549060ff0219169055600182016000905550506109b8610d9a565b5050505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610a1a57600080fd5b80600460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16151515610a7757600080fd5b6001600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff021916908315150217905550600380549050600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018190555060038290806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610b8c610d9a565b5050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610bec57600080fd5b610bf4610dd9565b565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610c5157600080fd5b8060018190555050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1681565b60606002805480602002602001604051908101604052809291908181526020018280548015610d1757602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610ccd575b5050505050905090565b610d5f33858585858080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050610ed1565b50505050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610d963383836111a9565b5050565b600060149054906101000a900460ff161515610db557600080fd5b60008060146101000a81548160ff021916908315150217905550610dd7611480565b565b600060149054906101000a900460ff16151515610df557600080fd5b60036002908054610e07929190611563565b506001600060146101000a81548160ff0219169083151502179055507f8564cd629b15f47dc310d45bcbfc9bcf5420b0d51bf0659a16c67f91d2763253600260405180806020018281038252838181548152602001915080548015610ec157602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610e77575b50509250505060405180910390a1565b83600080600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff169150600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101549050818015610f7d575060028054905081105b8015610fed57508273ffffffffffffffffffffffffffffffffffffffff16600282815481101515610faa57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b1515610ff857600080fd5b85600080600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff169150600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001015490508180156110a4575060028054905081105b801561111457508273ffffffffffffffffffffffffffffffffffffffff166002828154811015156110d157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b151561111f57600080fd5b876001548101431115801561113357504381105b151561113e57600080fd5b600115158a73ffffffffffffffffffffffffffffffffffffffff168c73ffffffffffffffffffffffffffffffffffffffff167f32c78b6140c46745a46e88cd883707d70dbd2f06d13dd76fe5f499c01290da4f60405160405180910390a45050505050505050505050565b82600080600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff169150600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101549050818015611255575060028054905081105b80156112c557508273ffffffffffffffffffffffffffffffffffffffff1660028281548110151561128257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b15156112d057600080fd5b84600080600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff169150600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154905081801561137c575060028054905081105b80156113ec57508273ffffffffffffffffffffffffffffffffffffffff166002828154811015156113a957fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16145b15156113f757600080fd5b866001548101431115801561140b57504381105b151561141657600080fd5b600015158973ffffffffffffffffffffffffffffffffffffffff168b73ffffffffffffffffffffffffffffffffffffffff167f32c78b6140c46745a46e88cd883707d70dbd2f06d13dd76fe5f499c01290da4f60405160405180910390a450505050505050505050565b6001430340600019167f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c8960036040518080602001828103825283818154815260200191508054801561152757602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116114dd575b50509250505060405180910390a2565b81548183558181111561155e5781836000526020600020918201910161155d91906115b5565b5b505050565b8280548282559060005260206000209081019282156115a45760005260206000209182015b828111156115a3578254825591600101919060010190611588565b5b5090506115b191906115da565b5090565b6115d791905b808211156115d35760008160009055506001016115bb565b5090565b90565b61161a91905b8082111561161657600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016115e0565b5090565b905600a165627a7a723058201abe690b07f11e7a70b63b5afa3feaa7eb4f8843f2bdc32a7c9017cea8f45d2f002900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" }, + "0x1000000000000000000000000000000000000002": { "balance": "1", "constructor" : "0x60806040523480156200001157600080fd5b5060405160208062001d3e8339810180604052810190808051906020019092919050505033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a2620000d381620000da640100000000026401000000009004565b506200044e565b62000119336000357fffffffff0000000000000000000000000000000000000000000000000000000016620001ce640100000000026401000000009004565b15156200012557600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156200020f576001905062000448565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141562000270576001905062000448565b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415620002d1576000905062000448565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019350505050602060405180830381600087803b1580156200040857600080fd5b505af11580156200041d573d6000803e3d6000fd5b505050506040513d60208110156200043457600080fd5b810190808051906020019092919050505090505b92915050565b6118e0806200045e6000396000f3006080604052600436106100c5576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806313af4035146100ca57806330f6eb161461010d5780633d84b8c11461016e5780634476d66a146101c5578063553a5c85146102065780635ce94456146102315780637a9e5e4b1461029e5780637fb3a43e146102e15780638da5cb5b14610338578063aa9fa2741461038f578063bf7e214f146103dc578063efdc4d0114610433578063f91c28981461045e575b600080fd5b3480156100d657600080fd5b5061010b600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061054e565b005b34801561011957600080fd5b50610158600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610630565b6040518082815260200191505060405180910390f35b34801561017a57600080fd5b506101af600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061073b565b6040518082815260200191505060405180910390f35b3480156101d157600080fd5b506101f06004803603810190808035906020019092919050505061083d565b6040518082815260200191505060405180910390f35b34801561021257600080fd5b5061021b610913565b6040518082815260200191505060405180910390f35b34801561023d57600080fd5b5061025c6004803603810190808035906020019092919050505061096b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156102aa57600080fd5b506102df600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109fc565b005b3480156102ed57600080fd5b50610322600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610adc565b6040518082815260200191505060405180910390f35b34801561034457600080fd5b5061034d610bde565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561039b57600080fd5b506103da60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610c04565b005b3480156103e857600080fd5b506103f1610cce565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561043f57600080fd5b50610448610cf3565b6040518082815260200191505060405180910390f35b34801561046a57600080fd5b506104af600480360381019080803590602001908201803590602001919091929391929390803590602001908201803590602001919091929391929390505050610d4e565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b838110156104f65780820151818401526020810190506104db565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561053857808201518184015260208101905061051d565b5050505090500194505050505060405180910390f35b61057c336000357fffffffff0000000000000000000000000000000000000000000000000000000016611042565b151561058757600080fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167fce241d7ca1f669fee44b6fc00b8eba2df3bb514eed0f6f668f8f89096e81ed9460405160405180910390a250565b6000600360007f6d696e746564466f724163636f756e74496e426c6f636b00000000000000000085856040516020018084600019166000191681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200193505050506040516020818303038152906040526040518082805190602001908083835b6020831015156106ef57805182526020820191506020810190506020830392506106ca565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191660001916815260200190815260200160002054905092915050565b6000600360007f6d696e746564466f724163636f756e7400000000000000000000000000000000846040516020018083600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831015156107f257805182526020820191506020810190506020830392506107cd565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916600019168152602001908152602001600020549050919050565b6000600360007f6d696e746564496e426c6f636b0000000000000000000000000000000000000084604051602001808360001916600019168152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831015156108c857805182526020820191506020810190506020830392506108a3565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916600019168152602001908152602001600020549050919050565b60006003600060405180807f6d696e746564546f74616c6c7900000000000000000000000000000000000000815250600d01905060405180910390206000191660001916815260200190815260200160002054905090565b60006002600060405180807f6578747261526563656976657273000000000000000000000000000000000000815250600e019050604051809103902060001916600019168152602001908152602001600020828154811015156109ca57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b610a2a336000357fffffffff0000000000000000000000000000000000000000000000000000000016611042565b1515610a3557600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f1abebea81bfa2637f28358c371278fb15ede7ea8dd28d2e03b112ff6d936ada460405160405180910390a250565b6000600360007f6578747261526563656976657273416d6f756e74730000000000000000000000846040516020018083600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001925050506040516020818303038152906040526040518082805190602001908083835b602083101515610b935780518252602082019150602081019050602083039250610b6e565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916600019168152602001908152602001600020549050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000808314151515610c1557600080fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614151515610c5157600080fd5b610c5a82610adc565b90506000811415610c6f57610c6e826112b9565b5b610c7b83820183611370565b8173ffffffffffffffffffffffffffffffffffffffff167f5b4c7f26538bc9d1959f2612cc8d595abab5073872ab2cb3fa78f95567a6b1cc846040518082815260200191505060405180910390a2505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006002600060405180807f6578747261526563656976657273000000000000000000000000000000000000815250600e01905060405180910390206000191660001916815260200190815260200160002080549050905090565b606080600060608060008060008073fffffffffffffffffffffffffffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610daa57600080fd5b8a8a90508d8d9050141515610dbe57600080fd5b60018d8d9050141515610dd057600080fd5b60008b8b60008181101515610de157fe5b9050602002013561ffff1661ffff16141515610dfc57600080fd5b610e04610cf3565b965086604051908082528060200260200182016040528015610e355781602001602082028038833980820191505090505b50955086604051908082528060200260200182016040528015610e675781602001602082028038833980820191505090505b509450600093505b86841015610f1057839250610e838461096b565b9150610e8e82610adc565b9050610e9b600083611370565b818684815181101515610eaa57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050808584815181101515610ef557fe5b90602001906020020181815250508380600101945050610e6f565b600093505b8551841015610f6357610f568585815181101515610f2f57fe5b906020019060200201518786815181101515610f4757fe5b90602001906020020151611471565b8380600101945050610f15565b610f6b611803565b7fb747d4e9a0b39cc47496d84b42e4066ba8b8a0b89776528bcec4ac2997e224538686604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015610fd5578082015181840152602081019050610fba565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015611017578082015181840152602081019050610ffc565b5050505090500194505050505060405180910390a18585985098505050505050505094509492505050565b60003073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561108157600190506112b3565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614156110e057600190506112b3565b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561113f57600090506112b3565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b70096138430856040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019350505050602060405180830381600087803b15801561127557600080fd5b505af1158015611289573d6000803e3d6000fd5b505050506040513d602081101561129f57600080fd5b810190808051906020019092919050505090505b92915050565b6002600060405180807f6578747261526563656976657273000000000000000000000000000000000000815250600e0190506040518091039020600019166000191681526020019081526020016000208190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b81600360007f6578747261526563656976657273416d6f756e74730000000000000000000000846040516020018083600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831015156114265780518252602082019150602081019050602083039250611401565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916600019168152602001908152602001600020819055505050565b60007f6d696e746564466f724163636f756e74496e426c6f636b00000000000000000082436040516020018084600019166000191681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310151561152c5780518252602082019150602081019050602083039250611507565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020905082600360008360001916600019168152602001908152602001600020819055507f6d696e746564466f724163636f756e7400000000000000000000000000000000826040516020018083600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310151561162c5780518252602082019150602081019050602083039250611607565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390209050826003600083600019166000191681526020019081526020016000205401600360008360001916600019168152602001908152602001600020819055507f6d696e746564496e426c6f636b0000000000000000000000000000000000000043604051602001808360001916600019168152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b60208310151561171d57805182526020820191506020810190506020830392506116f8565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902090508260036000836000191660001916815260200190815260200160002054016003600083600019166000191681526020019081526020016000208190555060405180807f6d696e746564546f74616c6c7900000000000000000000000000000000000000815250600d0190506040518091039020905082600360008360001916600019168152602001908152602001600020540160036000836000191660001916815260200190815260200160002081905550505050565b60006002600060405180807f6578747261526563656976657273000000000000000000000000000000000000815250600e019050604051809103902060001916600019168152602001908152602001600020816118609190611863565b50565b81548183558181111561188a57818360005260206000209182019101611889919061188f565b5b505050565b6118b191905b808211156118ad576000816000905550600101611895565b5090565b905600a165627a7a72305820fec62042c94deccd28ffe9f861444d5445f0f139869b466ec6c7488423bf59ab0029000000000000000000000000fca3df5cfca13005b2710138b460c84567bb330f"}, + "0x1000000000000000000000000000000000000003": { "balance": "1", "constructor" : "0x608060405263ffffffff600060146101000a81548163ffffffff021916908363ffffffff16021790555034801561003557600080fd5b50604051602080610a6683398101806040528101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506109be806100a86000396000f3006080604052600436106100d0576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631cd48842146100d5578063384d57b51461010c578063435f0d411461014357806343bf4aac1461017a5780634cd324d3146101cd57806355072fef146102205780636686c2d91461025757806378a75ba11461028a57806381d0974d146102c157806383525394146102f85780638da5cb5b1461032f578063afc2619014610386578063e1751221146103d9578063f2fde38b1461043c575b600080fd5b3480156100e157600080fd5b506100ea61047f565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561011857600080fd5b50610121610484565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561014f57600080fd5b50610158610489565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561018657600080fd5b506101cb600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803563ffffffff169060200190929190505050610491565b005b3480156101d957600080fd5b5061021e600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803563ffffffff16906020019092919050505061059d565b005b34801561022c57600080fd5b506102356106a8565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561026357600080fd5b50610288600480360381019080803563ffffffff1690602001909291905050506106ad565b005b34801561029657600080fd5b5061029f610729565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b3480156102cd57600080fd5b506102d661073f565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561030457600080fd5b5061030d610744565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561033b57600080fd5b50610344610749565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561039257600080fd5b506103d7600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803563ffffffff16906020019092919050505061076e565b005b3480156103e557600080fd5b5061041a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610828565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561044857600080fd5b5061047d600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108f7565b005b600281565b600181565b63ffffffff81565b3373ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156104e957fe5b8019600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900463ffffffff1616600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff1602179055505050565b3373ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156105f557fe5b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900463ffffffff1617600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff1602179055505050565b600881565b3373ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561070557fe5b80600060146101000a81548163ffffffff021916908363ffffffff16021790555050565b600060149054906101000a900463ffffffff1681565b600481565b600081565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b3373ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156107c657fe5b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff1602179055505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561088b5763ffffffff90506108f2565b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900463ffffffff16600060149054906101000a900463ffffffff161790505b919050565b3373ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561094f57fe5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505600a165627a7a723058206f8587c9b4d1831616db3c20c68aa2fdcfe0def330eb86d02fb2c5a087d0f1320029000000000000000000000000fca3df5cfca13005b2710138b460c84567bb330f"}, + + "0xfca3df5cfca13005b2710138b460c84567bb330f": { "balance": "1000000000000000000000" } + } +} \ No newline at end of file diff --git a/ethcore/res/ethereum/evantestcore.json b/ethcore/res/ethereum/evantestcore.json new file mode 100644 index 0000000000..b63109052e --- /dev/null +++ b/ethcore/res/ethereum/evantestcore.json @@ -0,0 +1,128 @@ +{ + "name": "evan.network testcore", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "3", + "validators": { + "multi": { + "0": { + "list": [ + "0x4a6723fc5a926fa150baeaf04bfd673b056ba83d", + "0x53073a4e759a7378f80046b6ddfdcd0854ad46e0" + ] + }, + "43000": { + "safeContract": "0xa081a8d1f01e00ef9be7b81889581e3f727649b4" + }, + "210000": { + "safeContract": "0x73294a0593e2c75f6200315d2316ad732ec83767" + } + } + }, + "blockRewardContractAddress": "0xdccb7f7ec90c99ba744986539dd73b897401954b", + "blockRewardContractTransition": "210000", + "strictEmptyStepsTransition": "0xfffffffff", + "emptyStepsTransition": "1500", + "maximumEmptySteps": "900" + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x400", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x7A1200", + "networkID": "0x1E51C06E", + "registrar": "0xf62711aa6cca7962813a306c0B3eCbA486Eb0eD1", + "transactionPermissionContract": "0xa426f3f28e1a79aa91e46eb4430f25ac09adf16d", + "transactionPermissionContractTransition": "43010", + "eip98Transition": "0x0", + "eip140Transition": "100000", + "eip211Transition": "100000", + "eip214Transition": "100000", + "eip658Transition": "100000", + "wasmActivationTransition": "100000", + "eip145Transition": "100000", + "eip1014Transition": "100000", + "eip1052Transition": "100000", + "eip1283Transition": "100000", + "eip1283DisableTransition": "160000", + "kip4Transition": "100000", + "kip6Transition": "100000" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x7A1200" + }, + "nodes": [ + "enode://cf10233012ebd573b4f946b2edb2be1d9126ccfa460a241cfb5eda107065cc00f35c55cd724f5de593a119dc69d1aee617112a9223b32eda0a7e77cc90959c14@18.196.213.4:30303", + "enode://80ca717cb4cf359952d710cd8bf5485514d13cf6c64b2679a5c1577b876dfd3326facb1d48bb2c2a6d4230cb4ef61c3b8a274a83b149a85634bbf16aabee4bcb@34.241.155.29:30303", + "enode://a7b3b4b91ef49c74c62e8c4612a9481d87ef6b54f8b8832b257af1b1407797f668abde3bc9b66ac94ba329c5e266d2fabfafa627375d246821a8723cd98d7b39@18.195.248.100:30303" + ], + "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 + } + } + } + }, + "0x0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { + "name": "ripemd160", + "pricing": { + "linear": { + "base": 600, + "word": 120 + } + } + } + }, + "0x0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { + "name": "identity", + "pricing": { + "linear": { + "base": 15, + "word": 3 + } + } + } + }, + "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": "0x0", "pricing": { "linear": { "base": 500, "word": 0 } } } }, + "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": "0x0", "pricing": { "linear": { "base": 40000, "word": 0 } } } }, + "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": "0x0", "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } }, + "0x0000000000000000000000000000000000000005": { "balance": "0", "constructor" : "0x6060604052336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550601460015534156200005557600080fd5b604051620013343803806200133483398101604052808051820191905050600081600390805190602001906200008d9291906200019d565b50600090505b600182510381101562000180576001600460008484815181101515620000b557fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff021916908315150217905550806004600084848151811015156200012757fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010181905550808060010191505062000093565b60036002908054620001949291906200022c565b505050620002c9565b82805482825590600052602060002090810192821562000219579160200282015b82811115620002185782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190620001be565b5b50905062000228919062000283565b5090565b828054828255906000526020600020908101928215620002705760005260206000209182015b828111156200026f57825482559160010191906001019062000252565b5b5090506200027f919062000283565b5090565b620002c691905b80821115620002c257600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016200028a565b5090565b90565b61105b80620002d96000396000f3006060604052600436106100ba576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630c857805146100bf57806311ae9ed2146100e857806313af40351461015257806340a141ff1461018b5780634d238c8e146101c457806375286211146101fd5780638c90ac07146102125780638da5cb5b14610235578063b3f05b971461028a578063b7ab4db5146102b7578063c476dd4014610321578063d69f13bb146103a6575b600080fd5b34156100ca57600080fd5b6100d26103e8565b6040518082815260200191505060405180910390f35b34156100f357600080fd5b6100fb6103ee565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561013e578082015181840152602081019050610123565b505050509050019250505060405180910390f35b341561015d57600080fd5b610189600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610482565b005b341561019657600080fd5b6101c2600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061059b565b005b34156101cf57600080fd5b6101fb600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610837565b005b341561020857600080fd5b610210610a05565b005b341561021d57600080fd5b6102336004808035906020019091905050610b1a565b005b341561024057600080fd5b610248610b7f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561029557600080fd5b61029d610ba4565b604051808215151515815260200191505060405180910390f35b34156102c257600080fd5b6102ca610bb7565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561030d5780820151818401526020810190506102f2565b505050509050019250505060405180910390f35b341561032c57600080fd5b6103a4600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610c4b565b005b34156103b157600080fd5b6103e6600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610d1e565b005b60015481565b6103f6610f09565b600380548060200260200160405190810160405280929190818152602001828054801561047857602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161042e575b5050505050905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156104dd57600080fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236460405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156105f657600080fd5b80600460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16151561065257600080fd5b600360016003805490500381548110151561066957fe5b906000526020600020900160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166003600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101548154811015156106e657fe5b906000526020600020900160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360016003805490500381548110151561074657fe5b906000526020600020900160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905560038054809190600190036107899190610f1d565b50600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101600090556000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff021916908315150217905550610833610e48565b5050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561089257600080fd5b80600460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff161515156108ef57600080fd5b6001600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff021916908315150217905550600380549050600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010181905550600380548060010182816109aa9190610f49565b9160005260206000209001600084909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610a01610e48565b5050565b73fffffffffffffffffffffffffffffffffffffffe73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580610a605750600560009054906101000a900460ff165b1515610a6b57600080fd5b60036002908054610a7d929190610f75565b506001600560006101000a81548160ff0219169083151502179055507f8564cd629b15f47dc310d45bcbfc9bcf5420b0d51bf0659a16c67f91d2763253610ac2610bb7565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610b05578082015181840152602081019050610aea565b505050509050019250505060405180910390a1565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610b7557600080fd5b8060018190555050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600560009054906101000a900460ff1681565b610bbf610f09565b6002805480602002602001604051908101604052809291908181526020018280548015610c4157602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610bf7575b5050505050905090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610ca657600080fd5b8160015481014311151515610cba57600080fd5b600115158473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f32c78b6140c46745a46e88cd883707d70dbd2f06d13dd76fe5f499c01290da4f60405160405180910390a450505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610d7957600080fd5b81600460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1615610e43578160015481014311151515610de357600080fd5b600015158473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f32c78b6140c46745a46e88cd883707d70dbd2f06d13dd76fe5f499c01290da4f60405160405180910390a4505b505050565b600560009054906101000a900460ff16151515610e6457600080fd5b6000600560006101000a81548160ff0219169083151502179055506001430340600019167f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89610eb16103ee565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610ef4578082015181840152602081019050610ed9565b505050509050019250505060405180910390a2565b602060405190810160405280600081525090565b815481835581811511610f4457818360005260206000209182019101610f439190610fc7565b5b505050565b815481835581811511610f7057818360005260206000209182019101610f6f9190610fc7565b5b505050565b828054828255906000526020600020908101928215610fb65760005260206000209182015b82811115610fb5578254825591600101919060010190610f9a565b5b509050610fc39190610fec565b5090565b610fe991905b80821115610fe5576000816000905550600101610fcd565b5090565b90565b61102c91905b8082111561102857600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550600101610ff2565b5090565b905600a165627a7a723058209e73f6357692a729a422a0a6bdadf9b728e7b6e0f0f6c9557f676c5e0b3932980029000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004a6723fc5a926fa150baeaf04bfd673b056ba83d00000000000000000000000053073a4e759a7378f80046b6ddfdcd0854ad46e0" }, + "0x0000000000000000000000000000000000000030": { "balance": "0", "constructor": "0x606060405234156200001057600080fd5b6000808054806001018281620000279190620002cb565b9160005260206000209001600080909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506200012081805480602002602001604051908101604052809291908181526020018280548015620000fd57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311620000b2575b505050505060016000620001276401000000000262000ea0176401000000009004565b5062000322565b60006001541415156200013957600080fd5b62000158816200017d6401000000000262001bc2176401000000009004565b620001788383620001bf6401000000000262001bed176401000000009004565b505050565b60006001541415156200018f57600080fd5b8061010581905550620001b5620002b46401000000000262001e3b176401000000009004565b6101078190555050565b600080600154141515620001d257600080fd5b600082111515620001e257600080fd5b81835110151515620001f357600080fd5b8251600181905550600090505b8251811015620002a85782818151811015156200021957fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff16600282600101610100811015156200024d57fe5b018190555080600101610102600085848151811015156200026a57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555080600101905062000200565b81600081905550505050565b60006201518042811515620002c557fe5b04905090565b815481835581811511620002f557818360005260206000209182019101620002f49190620002fa565b5b505050565b6200031f91905b808211156200031b57600081600090555060010162000301565b5090565b90565b611fdc80620003326000396000f300606060405260043610610107576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063173825d91461017e5780632f54bf6e146101b75780634123cb6b1461020857806352375093146102315780635c52c2f51461025a578063659010e71461026f5780637065cb4814610298578063746c9171146102d1578063797af627146102fa57806390ca20e514610339578063b20d30a9146103a5578063b61d27f6146103c8578063b75c7dc61461043a578063ba51a6df14610461578063c2cf732614610484578063c41a360a146104e2578063cbf0b0c014610545578063f00d4b5d1461057e578063f1736d86146105d6575b600034111561017c577fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c3334604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a15b005b341561018957600080fd5b6101b5600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506105ff565b005b34156101c257600080fd5b6101ee600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061073b565b604051808215151515815260200191505060405180910390f35b341561021357600080fd5b61021b610771565b6040518082815260200191505060405180910390f35b341561023c57600080fd5b610244610777565b6040518082815260200191505060405180910390f35b341561026557600080fd5b61026d61077e565b005b341561027a57600080fd5b6102826107b7565b6040518082815260200191505060405180910390f35b34156102a357600080fd5b6102cf600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506107be565b005b34156102dc57600080fd5b6102e4610905565b6040518082815260200191505060405180910390f35b341561030557600080fd5b61031f60048080356000191690602001909190505061090b565b604051808215151515815260200191505060405180910390f35b341561034457600080fd5b6103a3600480803590602001908201803590602001908080602002602001604051908101604052809392919081815260200183836020028082843782019150505050505091908035906020019091908035906020019091905050610ea0565b005b34156103b057600080fd5b6103c66004808035906020019091905050610ec9565b005b34156103d357600080fd5b61041c600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919080359060200190820180359060200191909192905050610f02565b60405180826000191660001916815260200191505060405180910390f35b341561044557600080fd5b61045f600480803560001916906020019091905050611371565b005b341561046c57600080fd5b6104826004808035906020019091905050611485565b005b341561048f57600080fd5b6104c860048080356000191690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061151a565b604051808215151515815260200191505060405180910390f35b34156104ed57600080fd5b610503600480803590602001909190505061159a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561055057600080fd5b61057c600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506115b8565b005b341561058957600080fd5b6105d4600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611601565b005b34156105e157600080fd5b6105e96117ae565b6040518082815260200191505060405180910390f35b60008036604051808383808284378201915050925050506040518091039020610627816117b5565b156107365761010260008473ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549150600082141561066757610735565b6001805403600054111561067a57610735565b60006002836101008110151561068c57fe5b0181905550600061010260008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506106c96119c0565b6106d1611a69565b7f58619076adf5bb0943d100ef88d52d7c3fd691b19d3a9071b555b651fbf418da83604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15b5b505050565b60008061010260008473ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054119050919050565b60015481565b6101075481565b6000366040518083838082843782019150509250505060405180910390206107a5816117b5565b156107b4576000610106819055505b50565b6101065481565b6000366040518083838082843782019150509250505060405180910390206107e5816117b5565b15610901576107f38261073b565b156107fd57610900565b6108056119c0565b60fa60015410151561081a57610819611a69565b5b60fa60015410151561082b57610900565b6001600081548092919060010191905055508173ffffffffffffffffffffffffffffffffffffffff1660026001546101008110151561086657fe5b018190555060015461010260008473ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507f994a936646fe87ffe4f1e469d3d6aa417d6b855598397f323de5b449f765f0c382604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15b5b5050565b60005481565b60008082610918816117b5565b15610e995760006101086000866000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415806109a257506000610108600086600019166000191681526020019081526020016000206001015414155b806109e1575060006101086000866000191660001916815260200190815260200160002060020180546001816001161561010002031660029004905014155b15610e975760006101086000866000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b2b57610b246101086000866000191660001916815260200190815260200160002060010154610108600087600019166000191681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b1a5780601f10610aef57610100808354040283529160200191610b1a565b820191906000526020600020905b815481529060010190602001808311610afd57829003601f168201915b5050505050611bae565b9150610c47565b6101086000856000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166101086000866000191660001916815260200190815260200160002060010154610108600087600019166000191681526020019081526020016000206002016040518082805460018160011615610100020316600290048015610c255780601f10610bfa57610100808354040283529160200191610c25565b820191906000526020600020905b815481529060010190602001808311610c0857829003601f168201915b505091505060006040518083038185875af1925050501515610c4657600080fd5b5b7fe3a3a4111a84df27d76b68dc721e65c7711605ea5eee4afd3a9c58195217365c338561010860008860001916600019168152602001908152602001600020600101546101086000896000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1661010860008a6000191660001916815260200190815260200160002060020187604051808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200186600019166000191681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001806020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828103825284818154600181600116156101000203166002900481526020019150805460018160011615610100020316600290048015610e1d5780601f10610df257610100808354040283529160200191610e1d565b820191906000526020600020905b815481529060010190602001808311610e0057829003601f168201915b505097505050505050505060405180910390a161010860008560001916600019168152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160009055600282016000610e8c9190611e51565b505060019250610e98565b5b5b5050919050565b6000600154141515610eb157600080fd5b610eba81611bc2565b610ec48383611bed565b505050565b600036604051808383808284378201915050925050506040518091039020610ef0816117b5565b15610efe5781610105819055505b5050565b600080610f0e3361073b565b1561136857600084849050148015610f2b5750610f2a85611cda565b5b80610f3857506001600054145b156110e35760008673ffffffffffffffffffffffffffffffffffffffff161415610f9f57610f988585858080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050611bae565b9050610fe9565b8573ffffffffffffffffffffffffffffffffffffffff168585856040518083838082843782019150509250505060006040518083038185875af1925050501515610fe857600080fd5b5b7f9738cd1a8777c86b011f7b01d87d484217dc6ab5154a9d41eda5d14af8caf292338688878786604051808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001806020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281038252858582818152602001925080828437820191505097505050505050505060405180910390a1611367565b6000364360405180848480828437820191505082815260200193505050506040518091039020915060006101086000846000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614801561118f575060006101086000846000191660001916815260200190815260200160002060010154145b80156111ce5750600061010860008460001916600019168152602001908152602001600020600201805460018160011615610100020316600290049050145b1561128557856101086000846000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508461010860008460001916600019168152602001908152602001600020600101819055508383610108600085600019166000191681526020019081526020016000206002019190611283929190611e99565b505b61128e8261090b565b1515611366577f1733cbb53659d713b79580f79f3f9ff215f78a7c7aa45890f3b89fc5cddfbf328233878988886040518087600019166000191681526020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001806020018281038252848482818152602001925080828437820191505097505050505050505060405180910390a15b5b5b50949350505050565b600080600061010260003373ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054925060008314156113b15761147f565b8260020a9150610103600085600019166000191681526020019081526020016000209050600082826001015416111561147e5780600001600081548092919060010191905055508181600101600082825403925050819055507fc7fb647e59b18047309aa15aad418e5d7ca96d173ad704f1031a2c3d7591734b3385604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182600019166000191681526020019250505060405180910390a15b5b50505050565b6000366040518083838082843782019150509250505060405180910390206114ac816117b5565b156115165760008214156114bf57611515565b6001548211156114ce57611515565b816000819055506114dd6119c0565b7facbdb084c721332ac59f9b8e392196c9eb0e4932862da8eb9beaf0dad4f550da826040518082815260200191505060405180910390a15b5b5050565b60008060008061010360008760001916600019168152602001908152602001600020925061010260008673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549150600082141561157d5760009350611591565b8160020a9050600081846001015416141593505b50505092915050565b6000600260018301610100811015156115af57fe5b01549050919050565b6000366040518083838082843782019150509250505060405180910390206115df816117b5565b156115fd578173ffffffffffffffffffffffffffffffffffffffff16ff5b5050565b60008036604051808383808284378201915050925050506040518091039020611629816117b5565b156117a8576116378361073b565b15611641576117a7565b61010260008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549150600082141561167c576117a7565b6116846119c0565b8273ffffffffffffffffffffffffffffffffffffffff16600283610100811015156116ab57fe5b0181905550600061010260008673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508161010260008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055507fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c8484604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a15b5b50505050565b6101055481565b60008060008061010260003373ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054925060008314156117f6576119b8565b61010360008660001916600019168152602001908152602001600020915060008260000154141561187c57600054826000018190555060008260010181905550610104805480919060010161184b9190611f19565b826002018190555084610104836002015481548110151561186857fe5b906000526020600020900181600019169055505b8260020a905060008183600101541614156119b7577fe1c52dc63b719ade82e8bea94cc41a0d5d28e4aaf536adb5e9cccc9ff8c1aeda3386604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182600019166000191681526020019250505060405180910390a160018260000154141561198f57610104610103600087600019166000191681526020019081526020016000206002015481548110151561194057fe5b90600052602060002090016000905561010360008660001916600019168152602001908152602001600020600080820160009055600182016000905560028201600090555050600193506119b8565b8160000160008154809291906001900391905055508082600101600082825417925050819055505b5b505050919050565b600080610104805490509150600090505b81811015611a5d576101086000610104838154811015156119ee57fe5b90600052602060002090015460001916600019168152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160009055600282016000611a509190611e51565b50508060010190506119d1565b611a65611d5f565b5050565b6000600190505b600154811015611bab575b60015481108015611a9e5750600060028261010081101515611a9957fe5b015414155b15611ab0578080600101915050611a7b565b5b60018054118015611ad557506000600260015461010081101515611ad157fe5b0154145b15611af25760016000815480929190600190039190505550611ab1565b60015481108015611b1757506000600260015461010081101515611b1257fe5b015414155b8015611b345750600060028261010081101515611b3057fe5b0154145b15611ba657600260015461010081101515611b4b57fe5b015460028261010081101515611b5d57fe5b018190555080610102600060028461010081101515611b7857fe5b01548152602001908152602001600020819055506000600260015461010081101515611ba057fe5b01819055505b611a70565b50565b6000611bba8383611e15565b905092915050565b6000600154141515611bd357600080fd5b8061010581905550611be3611e3b565b6101078190555050565b600080600154141515611bff57600080fd5b600082111515611c0e57600080fd5b81835110151515611c1e57600080fd5b8251600181905550600090505b8251811015611cce578281815181101515611c4257fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1660028260010161010081101515611c7557fe5b01819055508060010161010260008584815181101515611c9157fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550806001019050611c2b565b81600081905550505050565b6000611ce53361073b565b15611d5a5761010754611cf6611e3b565b1115611d1557600061010681905550611d0d611e3b565b610107819055505b6101065482610106540110158015611d3557506101055482610106540111155b15611d5457816101066000828254019250508190555060019050611d59565b600090505b5b919050565b600080610104805490509150600090505b81811015611e0257600060010261010482815481101515611d8d57fe5b90600052602060002090015460001916141515611df757610103600061010483815481101515611db957fe5b906000526020600020900154600019166000191681526020019081526020016000206000808201600090556001820160009055600282016000905550505b806001019050611d70565b6101046000611e119190611f45565b5050565b60008082516020840185f09150813b15905080151515611e3457600080fd5b5092915050565b60006201518042811515611e4b57fe5b04905090565b50805460018160011615610100020316600290046000825580601f10611e775750611e96565b601f016020900490600052602060002090810190611e959190611f66565b5b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611eda57803560ff1916838001178555611f08565b82800160010185558215611f08579182015b82811115611f07578235825591602001919060010190611eec565b5b509050611f159190611f66565b5090565b815481835581811511611f4057818360005260206000209182019101611f3f9190611f8b565b5b505050565b5080546000825590600052602060002090810190611f639190611f8b565b50565b611f8891905b80821115611f84576000816000905550600101611f6c565b5090565b90565b611fad91905b80821115611fa9576000816000905550600101611f91565b5090565b905600a165627a7a723058209b7ff153768f2a2d389913396ca992463a3b329f10d23c8e64577505b5fa6eb90029"}, + "0x0000000000000000000000000000000000000040": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "constructor": "0x6060604052341561000f57600080fd5b6040516101f93803806101f98339810160405280805182019190602001805190602001909190805190602001909190505060008060008060006390ca20e57c0100000000000000000000000000000000000000000000000000000000029450875160020193506020846002010292508260040191506100a0826100d96401000000000261002c176401000000009004565b90508481528283380360048301396100cb81836100f064010000000002610043176401000000009004565b505050505050505050610141565b600080604051905082810160405280915050919050565b6000806000806030925061011760206100d96401000000000261002c176401000000009004565b91506020828688866127105a03f41590508015151561013557600080fd5b81935050505092915050565b60aa8061014f6000396000f30060606040526000806000803690509250601683602c565b91508260008337602582846043565b9050602081f35b600080604051905082810160405280915050919050565b6000806000806030925060556020602c565b91506020828688866127105a03f415905080151515607257600080fd5b819350505050929150505600a165627a7a723058203595ce46387768542d96199d0e5c83d8a4a5f1ed2108b14fae0038af6eb04c970029000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000d45cdc6ebc066361691539d30b8102207c747000000000000000000000000000a71373da6e26f134b87fad634abbb154c8778d000000000000000000000000003095339e7bb73ac30b198c48331fa52527ec56"}, + "0x0000000000000000000000000000000000000041": { "balance": "0", "constructor": "0x6060604052341561000f57600080fd5b6040516101f93803806101f98339810160405280805182019190602001805190602001909190805190602001909190505060008060008060006390ca20e57c0100000000000000000000000000000000000000000000000000000000029450875160020193506020846002010292508260040191506100a0826100d96401000000000261002c176401000000009004565b90508481528283380360048301396100cb81836100f064010000000002610043176401000000009004565b505050505050505050610141565b600080604051905082810160405280915050919050565b6000806000806030925061011760206100d96401000000000261002c176401000000009004565b91506020828688866127105a03f41590508015151561013557600080fd5b81935050505092915050565b60aa8061014f6000396000f30060606040526000806000803690509250601683602c565b91508260008337602582846043565b9050602081f35b600080604051905082810160405280915050919050565b6000806000806030925060556020602c565b91506020828688866127105a03f415905080151515607257600080fd5b819350505050929150505600a165627a7a723058203595ce46387768542d96199d0e5c83d8a4a5f1ed2108b14fae0038af6eb04c970029000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000d45cdc6ebc066361691539d30b8102207c747000000000000000000000000000a71373da6e26f134b87fad634abbb154c8778d000000000000000000000000003095339e7bb73ac30b198c48331fa52527ec56"}, + "0x00d45Cdc6eBc066361691539d30B8102207c7470": { "balance": "1000000000000000000000" }, + "0x00a71373dA6e26F134B87faD634AbBB154C8778d": { "balance": "1000000000000000000000" }, + "0x003095339E7bb73ac30B198c48331FA52527EC56": { "balance": "1000000000000000000000" } + } +} \ No newline at end of file diff --git a/ethcore/res/ethereum/ewc.json b/ethcore/res/ethereum/ewc.json index 1290526504..f281e6533d 100644 --- a/ethcore/res/ethereum/ewc.json +++ b/ethcore/res/ethereum/ewc.json @@ -1,213 +1,955 @@ { - "name": "EnergyWebChain", - "engine": { - "authorityRound": { - "params": { - "stepDuration": "5", - "validators": { - "contract": "0x1204700000000000000000000000000000000000" - }, - "maximumUncleCountTransition": "0", - "maximumUncleCount": "0", - "blockRewardContractAddress": "0x1204700000000000000000000000000000000002", - "blockRewardContractTransition": "0" - } - } - }, - "params": { - "networkID": "0xF6", - "maximumExtraDataSize": "0x20", - "gasLimitBoundDivisor": "0x400", - "minGasLimit": "0x1388", - "maxCodeSize": "0x6000", - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0", - "eip145Transition": "0x0", - "eip1014Transition": "0x0", - "eip1052Transition": "0x0", - "registrar": "0x1204700000000000000000000000000000000006" - }, - "genesis": { - "seal": { - "authorityRound": { - "step": "0x0", - "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x20000", - "gasLimit": "0x5B8D80" - }, - "accounts": { - "0x0000000000000000000000000000000000000001": { - "balance": "1", - "builtin": { - "name": "ecrecover", - "activate_at": "0", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000002": { - "balance": "1", - "builtin": { - "name": "sha256", - "activate_at": "0", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0x0000000000000000000000000000000000000003": { - "balance": "1", - "builtin": { - "name": "ripemd160", - "activate_at": "0", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0x0000000000000000000000000000000000000004": { - "balance": "1", - "builtin": { - "name": "identity", - "activate_at": "0", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - }, - "0x0000000000000000000000000000000000000005": { - "balance": "1", - "builtin": { - "name": "modexp", - "activate_at": "0", - "pricing": { - "modexp": { - "divisor": 20 - } - } - } - }, - "0x0000000000000000000000000000000000000006": { - "balance": "1", - "builtin": { - "name": "alt_bn128_add", - "activate_at": "0", - "eip1108_transition": "0x7fffffffffffff", - "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 - } - } - } - }, - "0x0000000000000000000000000000000000000007": { - "balance": "1", - "builtin": { - "name": "alt_bn128_mul", - "activate_at": "0", - "eip1108_transition": "0x7fffffffffffff", - "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 - } - } - } - }, - "0x0000000000000000000000000000000000000008": { - "balance": "1", - "builtin": { - "name": "alt_bn128_pairing", - "activate_at": "0", - "eip1108_transition": "0x7fffffffffffff", - "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 - } - } - } - }, - "0x1204700000000000000000000000000000000005": { - "constructor": "0x60806040523480156200001157600080fd5b50604051620024c8380380620024c883398101806040528101908080518201929190602001805190602001909291905050506000825182603282111580156200005a5750818111155b801562000068575060008114155b801562000076575060008214155b15156200008257600080fd5b600092505b8451831015620001bd57600260008685815181101515620000a457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16158015620001335750600085848151811015156200011057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013f57600080fd5b60016002600087868151811015156200015457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550828060010193505062000087565b8460039080519060200190620001d5929190620001e8565b50836004819055505050505050620002bd565b82805482825590600052602060002090810192821562000264579160200282015b82811115620002635782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000209565b5b50905062000273919062000277565b5090565b620002ba91905b80821115620002b657600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016200027e565b5090565b90565b6121fb80620002cd6000396000f30060806040526004361061011d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101e457806320ea8d86146102275780632f54bf6e146102545780633411c81c146102af57806354741525146103145780637065cb4814610363578063784547a7146103a65780638b51d13f146103eb5780639ace38c21461042c578063a0e67e2b14610517578063a8abe69a14610583578063b5dc40c314610627578063b77bf600146106a9578063ba51a6df146106d4578063c01a8c8414610701578063c64274741461072e578063d74f8edd146107d5578063dc8452cd14610800578063e20056e61461082b578063ee22610b1461088e575b6000341115610175573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b34801561018357600080fd5b506101a2600480360381019080803590602001909291905050506108bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f057600080fd5b50610225600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108f9565b005b34801561023357600080fd5b5061025260048036038101908080359060200190929190505050610b92565b005b34801561026057600080fd5b50610295600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d3a565b604051808215151515815260200191505060405180910390f35b3480156102bb57600080fd5b506102fa60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d5a565b604051808215151515815260200191505060405180910390f35b34801561032057600080fd5b5061034d600480360381019080803515159060200190929190803515159060200190929190505050610d89565b6040518082815260200191505060405180910390f35b34801561036f57600080fd5b506103a4600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e1b565b005b3480156103b257600080fd5b506103d160048036038101908080359060200190929190505050611020565b604051808215151515815260200191505060405180910390f35b3480156103f757600080fd5b5061041660048036038101908080359060200190929190505050611105565b6040518082815260200191505060405180910390f35b34801561043857600080fd5b50610457600480360381019080803590602001909291905050506111d0565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156104d95780820151818401526020810190506104be565b50505050905090810190601f1680156105065780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561052357600080fd5b5061052c6112c5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561056f578082015181840152602081019050610554565b505050509050019250505060405180910390f35b34801561058f57600080fd5b506105d06004803603810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611353565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106135780820151818401526020810190506105f8565b505050509050019250505060405180910390f35b34801561063357600080fd5b50610652600480360381019080803590602001909291905050506114c4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561069557808201518184015260208101905061067a565b505050509050019250505060405180910390f35b3480156106b557600080fd5b506106be611701565b6040518082815260200191505060405180910390f35b3480156106e057600080fd5b506106ff60048036038101908080359060200190929190505050611707565b005b34801561070d57600080fd5b5061072c600480360381019080803590602001909291905050506117c1565b005b34801561073a57600080fd5b506107bf600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061199e565b6040518082815260200191505060405180910390f35b3480156107e157600080fd5b506107ea6119bd565b6040518082815260200191505060405180910390f35b34801561080c57600080fd5b506108156119c2565b6040518082815260200191505060405180910390f35b34801561083757600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506119c8565b005b34801561089a57600080fd5b506108b960048036038101908080359060200190929190505050611cdd565b005b6003818154811015156108ca57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561093557600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561098e57600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610b13578273ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b06576003600160038054905003815481101515610a7f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610ab957fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b13565b81806001019250506109eb565b6001600381818054905003915081610b2b91906120fe565b506003805490506004541115610b4a57610b49600380549050611707565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a2505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610beb57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610c5657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610c8657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610e1457838015610dc8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610dfb5750828015610dfa575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610e07576001820191505b8080600101915050610d91565b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e5557600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610eaf57600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610ed657600080fd5b60016003805490500160045460328211158015610ef35750818111155b8015610f00575060008114155b8015610f0d575060008214155b1515610f1857600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000809150600090505b6003805490508110156110fd5760016000858152602001908152602001600020600060038381548110151561105e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156110dd576001820191505b6004548214156110f057600192506110fe565b808060010191505061102d565b5b5050919050565b600080600090505b6003805490508110156111ca5760016000848152602001908152602001600020600060038381548110151561113e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111bd576001820191505b808060010191505061110d565b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156112a85780601f1061127d576101008083540402835291602001916112a8565b820191906000526020600020905b81548152906001019060200180831161128b57829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b6060600380548060200260200160405190810160405280929190818152602001828054801561134957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ff575b5050505050905090565b60608060008060055460405190808252806020026020018201604052801561138a5781602001602082028038833980820191505090505b50925060009150600090505b600554811015611436578580156113cd575060008082815260200190815260200160002060030160009054906101000a900460ff16155b8061140057508480156113ff575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b156114295780838381518110151561141457fe5b90602001906020020181815250506001820191505b8080600101915050611396565b8787036040519080825280602002602001820160405280156114675781602001602082028038833980820191505090505b5093508790505b868110156114b957828181518110151561148457fe5b906020019060200201518489830381518110151561149e57fe5b9060200190602002018181525050808060010191505061146e565b505050949350505050565b6060806000806003805490506040519080825280602002602001820160405280156114fe5781602001602082028038833980820191505090505b50925060009150600090505b60038054905081101561164b5760016000868152602001908152602001600020600060038381548110151561153b57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561163e576003818154811015156115c257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156115fb57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b808060010191505061150a565b8160405190808252806020026020018201604052801561167a5781602001602082028038833980820191505090505b509350600090505b818110156116f957828181518110151561169857fe5b9060200190602002015184828151811015156116b057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080600101915050611682565b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561174157600080fd5b60038054905081603282111580156117595750818111155b8015611766575060008114155b8015611773575060008214155b151561177e57600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561181a57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561187657600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156118e257600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361199785611cdd565b5050505050565b60006119ab848484611f85565b90506119b6816117c1565b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611a0457600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611a5d57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515611ab757600080fd5b600092505b600380549050831015611ba0578473ffffffffffffffffffffffffffffffffffffffff16600384815481101515611aef57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b935783600384815481101515611b4657fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611ba0565b8280600101935050611abc565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611d3857600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611da357600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611dd357600080fd5b611ddc86611020565b15611f7d57600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611efa8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611ef05780601f10611ec557610100808354040283529160200191611ef0565b820191906000526020600020905b815481529060010190602001808311611ed357829003601f168201915b50505050506120d7565b15611f3157857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611f7c565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b505050505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611fae57600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155604082015181600201908051906020019061206d92919061212a565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6000806040516020840160008287838a8c6187965a03f19250505080915050949350505050565b8154818355818111156121255781836000526020600020918201910161212491906121aa565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061216b57805160ff1916838001178555612199565b82800160010185558215612199579182015b8281111561219857825182559160200191906001019061217d565b5b5090506121a691906121aa565b5090565b6121cc91905b808211156121c85760008160009055506001016121b0565b5090565b905600a165627a7a72305820b0d992f257e70d565448b927a3ae764342039a864112d5d5bb1ac1bd52e4104e00290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc1900000000000000000000000090ae948bb410838bff9d024ed849df6a7267dacf000000000000000000000000740a5c5742833bed72489fe6bf7efc9b79428989" - }, - "0x1204700000000000000000000000000000000003": { - "constructor": "0x60806040523480156200001157600080fd5b50604051620024c8380380620024c883398101806040528101908080518201929190602001805190602001909291905050506000825182603282111580156200005a5750818111155b801562000068575060008114155b801562000076575060008214155b15156200008257600080fd5b600092505b8451831015620001bd57600260008685815181101515620000a457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16158015620001335750600085848151811015156200011057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013f57600080fd5b60016002600087868151811015156200015457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550828060010193505062000087565b8460039080519060200190620001d5929190620001e8565b50836004819055505050505050620002bd565b82805482825590600052602060002090810192821562000264579160200282015b82811115620002635782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000209565b5b50905062000273919062000277565b5090565b620002ba91905b80821115620002b657600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016200027e565b5090565b90565b6121fb80620002cd6000396000f30060806040526004361061011d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101e457806320ea8d86146102275780632f54bf6e146102545780633411c81c146102af57806354741525146103145780637065cb4814610363578063784547a7146103a65780638b51d13f146103eb5780639ace38c21461042c578063a0e67e2b14610517578063a8abe69a14610583578063b5dc40c314610627578063b77bf600146106a9578063ba51a6df146106d4578063c01a8c8414610701578063c64274741461072e578063d74f8edd146107d5578063dc8452cd14610800578063e20056e61461082b578063ee22610b1461088e575b6000341115610175573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b34801561018357600080fd5b506101a2600480360381019080803590602001909291905050506108bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f057600080fd5b50610225600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108f9565b005b34801561023357600080fd5b5061025260048036038101908080359060200190929190505050610b92565b005b34801561026057600080fd5b50610295600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d3a565b604051808215151515815260200191505060405180910390f35b3480156102bb57600080fd5b506102fa60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d5a565b604051808215151515815260200191505060405180910390f35b34801561032057600080fd5b5061034d600480360381019080803515159060200190929190803515159060200190929190505050610d89565b6040518082815260200191505060405180910390f35b34801561036f57600080fd5b506103a4600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e1b565b005b3480156103b257600080fd5b506103d160048036038101908080359060200190929190505050611020565b604051808215151515815260200191505060405180910390f35b3480156103f757600080fd5b5061041660048036038101908080359060200190929190505050611105565b6040518082815260200191505060405180910390f35b34801561043857600080fd5b50610457600480360381019080803590602001909291905050506111d0565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156104d95780820151818401526020810190506104be565b50505050905090810190601f1680156105065780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561052357600080fd5b5061052c6112c5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561056f578082015181840152602081019050610554565b505050509050019250505060405180910390f35b34801561058f57600080fd5b506105d06004803603810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611353565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106135780820151818401526020810190506105f8565b505050509050019250505060405180910390f35b34801561063357600080fd5b50610652600480360381019080803590602001909291905050506114c4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561069557808201518184015260208101905061067a565b505050509050019250505060405180910390f35b3480156106b557600080fd5b506106be611701565b6040518082815260200191505060405180910390f35b3480156106e057600080fd5b506106ff60048036038101908080359060200190929190505050611707565b005b34801561070d57600080fd5b5061072c600480360381019080803590602001909291905050506117c1565b005b34801561073a57600080fd5b506107bf600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061199e565b6040518082815260200191505060405180910390f35b3480156107e157600080fd5b506107ea6119bd565b6040518082815260200191505060405180910390f35b34801561080c57600080fd5b506108156119c2565b6040518082815260200191505060405180910390f35b34801561083757600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506119c8565b005b34801561089a57600080fd5b506108b960048036038101908080359060200190929190505050611cdd565b005b6003818154811015156108ca57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561093557600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561098e57600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610b13578273ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b06576003600160038054905003815481101515610a7f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610ab957fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b13565b81806001019250506109eb565b6001600381818054905003915081610b2b91906120fe565b506003805490506004541115610b4a57610b49600380549050611707565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a2505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610beb57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610c5657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610c8657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610e1457838015610dc8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610dfb5750828015610dfa575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610e07576001820191505b8080600101915050610d91565b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e5557600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610eaf57600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610ed657600080fd5b60016003805490500160045460328211158015610ef35750818111155b8015610f00575060008114155b8015610f0d575060008214155b1515610f1857600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000809150600090505b6003805490508110156110fd5760016000858152602001908152602001600020600060038381548110151561105e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156110dd576001820191505b6004548214156110f057600192506110fe565b808060010191505061102d565b5b5050919050565b600080600090505b6003805490508110156111ca5760016000848152602001908152602001600020600060038381548110151561113e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111bd576001820191505b808060010191505061110d565b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156112a85780601f1061127d576101008083540402835291602001916112a8565b820191906000526020600020905b81548152906001019060200180831161128b57829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b6060600380548060200260200160405190810160405280929190818152602001828054801561134957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ff575b5050505050905090565b60608060008060055460405190808252806020026020018201604052801561138a5781602001602082028038833980820191505090505b50925060009150600090505b600554811015611436578580156113cd575060008082815260200190815260200160002060030160009054906101000a900460ff16155b8061140057508480156113ff575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b156114295780838381518110151561141457fe5b90602001906020020181815250506001820191505b8080600101915050611396565b8787036040519080825280602002602001820160405280156114675781602001602082028038833980820191505090505b5093508790505b868110156114b957828181518110151561148457fe5b906020019060200201518489830381518110151561149e57fe5b9060200190602002018181525050808060010191505061146e565b505050949350505050565b6060806000806003805490506040519080825280602002602001820160405280156114fe5781602001602082028038833980820191505090505b50925060009150600090505b60038054905081101561164b5760016000868152602001908152602001600020600060038381548110151561153b57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561163e576003818154811015156115c257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156115fb57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b808060010191505061150a565b8160405190808252806020026020018201604052801561167a5781602001602082028038833980820191505090505b509350600090505b818110156116f957828181518110151561169857fe5b9060200190602002015184828151811015156116b057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080600101915050611682565b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561174157600080fd5b60038054905081603282111580156117595750818111155b8015611766575060008114155b8015611773575060008214155b151561177e57600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561181a57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561187657600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156118e257600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361199785611cdd565b5050505050565b60006119ab848484611f85565b90506119b6816117c1565b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611a0457600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611a5d57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515611ab757600080fd5b600092505b600380549050831015611ba0578473ffffffffffffffffffffffffffffffffffffffff16600384815481101515611aef57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b935783600384815481101515611b4657fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611ba0565b8280600101935050611abc565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611d3857600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611da357600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611dd357600080fd5b611ddc86611020565b15611f7d57600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611efa8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611ef05780601f10611ec557610100808354040283529160200191611ef0565b820191906000526020600020905b815481529060010190602001808311611ed357829003601f168201915b50505050506120d7565b15611f3157857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611f7c565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b505050505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611fae57600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155604082015181600201908051906020019061206d92919061212a565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6000806040516020840160008287838a8c6187965a03f19250505080915050949350505050565b8154818355818111156121255781836000526020600020918201910161212491906121aa565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061216b57805160ff1916838001178555612199565b82800160010185558215612199579182015b8281111561219857825182559160200191906001019061217d565b5b5090506121a691906121aa565b5090565b6121cc91905b808211156121c85760008160009055506001016121b0565b5090565b905600a165627a7a72305820b0d992f257e70d565448b927a3ae764342039a864112d5d5bb1ac1bd52e4104e00290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc1900000000000000000000000090ae948bb410838bff9d024ed849df6a7267dacf000000000000000000000000740a5c5742833bed72489fe6bf7efc9b79428989" - }, - "0x120470000000000000000000000000000000000a": { - "balance": "10901790566666700000000000", - "constructor": "0x60806040523480156200001157600080fd5b50604051620024c8380380620024c883398101806040528101908080518201929190602001805190602001909291905050506000825182603282111580156200005a5750818111155b801562000068575060008114155b801562000076575060008214155b15156200008257600080fd5b600092505b8451831015620001bd57600260008685815181101515620000a457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16158015620001335750600085848151811015156200011057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013f57600080fd5b60016002600087868151811015156200015457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550828060010193505062000087565b8460039080519060200190620001d5929190620001e8565b50836004819055505050505050620002bd565b82805482825590600052602060002090810192821562000264579160200282015b82811115620002635782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000209565b5b50905062000273919062000277565b5090565b620002ba91905b80821115620002b657600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016200027e565b5090565b90565b6121fb80620002cd6000396000f30060806040526004361061011d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101e457806320ea8d86146102275780632f54bf6e146102545780633411c81c146102af57806354741525146103145780637065cb4814610363578063784547a7146103a65780638b51d13f146103eb5780639ace38c21461042c578063a0e67e2b14610517578063a8abe69a14610583578063b5dc40c314610627578063b77bf600146106a9578063ba51a6df146106d4578063c01a8c8414610701578063c64274741461072e578063d74f8edd146107d5578063dc8452cd14610800578063e20056e61461082b578063ee22610b1461088e575b6000341115610175573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b34801561018357600080fd5b506101a2600480360381019080803590602001909291905050506108bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f057600080fd5b50610225600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108f9565b005b34801561023357600080fd5b5061025260048036038101908080359060200190929190505050610b92565b005b34801561026057600080fd5b50610295600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d3a565b604051808215151515815260200191505060405180910390f35b3480156102bb57600080fd5b506102fa60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d5a565b604051808215151515815260200191505060405180910390f35b34801561032057600080fd5b5061034d600480360381019080803515159060200190929190803515159060200190929190505050610d89565b6040518082815260200191505060405180910390f35b34801561036f57600080fd5b506103a4600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e1b565b005b3480156103b257600080fd5b506103d160048036038101908080359060200190929190505050611020565b604051808215151515815260200191505060405180910390f35b3480156103f757600080fd5b5061041660048036038101908080359060200190929190505050611105565b6040518082815260200191505060405180910390f35b34801561043857600080fd5b50610457600480360381019080803590602001909291905050506111d0565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156104d95780820151818401526020810190506104be565b50505050905090810190601f1680156105065780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561052357600080fd5b5061052c6112c5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561056f578082015181840152602081019050610554565b505050509050019250505060405180910390f35b34801561058f57600080fd5b506105d06004803603810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611353565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106135780820151818401526020810190506105f8565b505050509050019250505060405180910390f35b34801561063357600080fd5b50610652600480360381019080803590602001909291905050506114c4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561069557808201518184015260208101905061067a565b505050509050019250505060405180910390f35b3480156106b557600080fd5b506106be611701565b6040518082815260200191505060405180910390f35b3480156106e057600080fd5b506106ff60048036038101908080359060200190929190505050611707565b005b34801561070d57600080fd5b5061072c600480360381019080803590602001909291905050506117c1565b005b34801561073a57600080fd5b506107bf600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061199e565b6040518082815260200191505060405180910390f35b3480156107e157600080fd5b506107ea6119bd565b6040518082815260200191505060405180910390f35b34801561080c57600080fd5b506108156119c2565b6040518082815260200191505060405180910390f35b34801561083757600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506119c8565b005b34801561089a57600080fd5b506108b960048036038101908080359060200190929190505050611cdd565b005b6003818154811015156108ca57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561093557600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561098e57600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610b13578273ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b06576003600160038054905003815481101515610a7f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610ab957fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b13565b81806001019250506109eb565b6001600381818054905003915081610b2b91906120fe565b506003805490506004541115610b4a57610b49600380549050611707565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a2505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610beb57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610c5657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610c8657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610e1457838015610dc8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610dfb5750828015610dfa575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610e07576001820191505b8080600101915050610d91565b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e5557600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610eaf57600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610ed657600080fd5b60016003805490500160045460328211158015610ef35750818111155b8015610f00575060008114155b8015610f0d575060008214155b1515610f1857600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000809150600090505b6003805490508110156110fd5760016000858152602001908152602001600020600060038381548110151561105e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156110dd576001820191505b6004548214156110f057600192506110fe565b808060010191505061102d565b5b5050919050565b600080600090505b6003805490508110156111ca5760016000848152602001908152602001600020600060038381548110151561113e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111bd576001820191505b808060010191505061110d565b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156112a85780601f1061127d576101008083540402835291602001916112a8565b820191906000526020600020905b81548152906001019060200180831161128b57829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b6060600380548060200260200160405190810160405280929190818152602001828054801561134957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ff575b5050505050905090565b60608060008060055460405190808252806020026020018201604052801561138a5781602001602082028038833980820191505090505b50925060009150600090505b600554811015611436578580156113cd575060008082815260200190815260200160002060030160009054906101000a900460ff16155b8061140057508480156113ff575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b156114295780838381518110151561141457fe5b90602001906020020181815250506001820191505b8080600101915050611396565b8787036040519080825280602002602001820160405280156114675781602001602082028038833980820191505090505b5093508790505b868110156114b957828181518110151561148457fe5b906020019060200201518489830381518110151561149e57fe5b9060200190602002018181525050808060010191505061146e565b505050949350505050565b6060806000806003805490506040519080825280602002602001820160405280156114fe5781602001602082028038833980820191505090505b50925060009150600090505b60038054905081101561164b5760016000868152602001908152602001600020600060038381548110151561153b57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561163e576003818154811015156115c257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156115fb57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b808060010191505061150a565b8160405190808252806020026020018201604052801561167a5781602001602082028038833980820191505090505b509350600090505b818110156116f957828181518110151561169857fe5b9060200190602002015184828151811015156116b057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080600101915050611682565b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561174157600080fd5b60038054905081603282111580156117595750818111155b8015611766575060008114155b8015611773575060008214155b151561177e57600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561181a57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561187657600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156118e257600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361199785611cdd565b5050505050565b60006119ab848484611f85565b90506119b6816117c1565b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611a0457600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611a5d57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515611ab757600080fd5b600092505b600380549050831015611ba0578473ffffffffffffffffffffffffffffffffffffffff16600384815481101515611aef57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b935783600384815481101515611b4657fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611ba0565b8280600101935050611abc565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611d3857600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611da357600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611dd357600080fd5b611ddc86611020565b15611f7d57600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611efa8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611ef05780601f10611ec557610100808354040283529160200191611ef0565b820191906000526020600020905b815481529060010190602001808311611ed357829003601f168201915b50505050506120d7565b15611f3157857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611f7c565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b505050505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611fae57600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155604082015181600201908051906020019061206d92919061212a565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6000806040516020840160008287838a8c6187965a03f19250505080915050949350505050565b8154818355818111156121255781836000526020600020918201910161212491906121aa565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061216b57805160ff1916838001178555612199565b82800160010185558215612199579182015b8281111561219857825182559160200191906001019061217d565b5b5090506121a691906121aa565b5090565b6121cc91905b808211156121c85760008160009055506001016121b0565b5090565b905600a165627a7a72305820b0d992f257e70d565448b927a3ae764342039a864112d5d5bb1ac1bd52e4104e00290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc1900000000000000000000000090ae948bb410838bff9d024ed849df6a7267dacf000000000000000000000000740a5c5742833bed72489fe6bf7efc9b79428989" - }, - "0x0cB1437200aea736788f1Fc56F327c0456c3598D": { - "balance": "250000000000000000" - }, - "0x74dd76E24B2CFB43C1b1a4498295d553D0843746": { - "balance": "250000000000000000" - }, - "0xeeB4CEEe443F9e0D17BdBD6Daa241681EE5E51c2": { - "balance": "250000000000000000" - }, - "0xA005caEa55375ae20e3aAEF746113535503ABC19": { - "balance": "250000000000000000" - }, - "0x90ae948bB410838bff9D024ed849dF6A7267Dacf": { - "balance": "250000000000000000" - }, - "0x740a5C5742833bEd72489fE6bf7EFc9B79428989": { - "balance": "250000000000000000" - }, - "0x1204700000000000000000000000000000000001": { - "constructor": "0x60806040523480156200001157600080fd5b5060405162002d7138038062002d71833981018060405260608110156200003757600080fd5b81019080805190602001909291908051906020019092919080516401000000008111156200006457600080fd5b828101905060208101848111156200007b57600080fd5b81518560208202830111640100000000821117156200009957600080fd5b5050929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620001e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602481526020018062002d216024913960400191505060405180910390fd5b60018151101562000242576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018062002d45602c913960400191505060405180910390fd5b62000253836200046160201b60201c565b6200026482620005c360201b60201c565b60008090505b81518110156200040e57600073ffffffffffffffffffffffffffffffffffffffff168282815181106200029957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156200032c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b6001600360008484815181106200033f57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115620003a057fe5b02179055508060036000848481518110620003b757fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018190555080806001019150506200026a565b508060029080519060200190620004279291906200064a565b50600260019080546200043c929190620006d9565b506001600060146101000a81548160ff02191690831515021790555050505062000776565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000505576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b828054828255906000526020600020908101928215620006c6579160200282015b82811115620006c55782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906200066b565b5b509050620006d5919062000730565b5090565b8280548282559060005260206000209081019282156200071d5760005260206000209182015b828111156200071c578254825591600101919060010190620006ff565b5b5090506200072c919062000730565b5090565b6200077391905b808211156200076f57600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000737565b5090565b90565b61259b80620007866000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80639f723637116100b8578063b98049091161007c578063b980490914610562578063bd21442a146105be578063c805f68b14610681578063d826b7f1146106c5578063f2fde38b14610733578063f3aeac021461077757610137565b80639f723637146103dc578063a00745b61461043b578063a6940b0714610497578063b3f05b97146104e1578063b7ab4db51461050357610137565b8063455701d6116100ff578063455701d6146102c35780634d238c8e1461032257806375286211146103665780638da5cb5b146103705780638f32d59b146103ba57610137565b8063267fa41d1461013c57806334ba3c1b1461019857806340550a1c146101b657806340a141ff146102125780634183495514610256575b600080fd5b61017e6004803603602081101561015257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107d3565b604051808215151515815260200191505060405180910390f35b6101a0610845565b6040518082815260200191505060405180910390f35b6101f8600480360360208110156101cc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610852565b604051808215151515815260200191505060405180910390f35b6102546004803603602081101561022857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610933565b005b6102986004803603602081101561026c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610a7e565b604051808360038111156102a857fe5b60ff1681526020018281526020019250505060405180910390f35b6102cb610aaf565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561030e5780820151818401526020810190506102f3565b505050509050019250505060405180910390f35b6103646004803603602081101561033857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b3d565b005b61036e610d47565b005b6103786111b2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103c26111db565b604051808215151515815260200191505060405180910390f35b6103e4611232565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561042757808201518184015260208101905061040c565b505050509050019250505060405180910390f35b61047d6004803603602081101561045157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611361565b604051808215151515815260200191505060405180910390f35b61049f611442565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6104e9611468565b604051808215151515815260200191505060405180910390f35b61050b61147b565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561054e578082015181840152602081019050610533565b505050509050019250505060405180910390f35b6105a46004803603602081101561057857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611509565b604051808215151515815260200191505060405180910390f35b61067f600480360360808110156105d457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561063b57600080fd5b82018360208201111561064d57600080fd5b8035906020019184600183028401116401000000008311171561066f57600080fd5b909192939192939050505061157a565b005b6106c36004803603602081101561069757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506117d6565b005b610731600480360360608110156106db57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611989565b005b6107756004803603602081101561074957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611be3565b005b6107b96004803603602081101561078d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c69565b604051808215151515815260200191505060405180910390f35b6000600160038111156107e257fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561083d57fe5b149050919050565b6000600180549050905090565b60006001600381111561086157fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156108bc57fe5b148061092c57506003808111156108cf57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561092a57fe5b145b9050919050565b61093b6111db565b6109ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610a12576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124e16022913960400191505060405180910390fd5b80610a1c81610852565b610a71576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b610a7a82611cdb565b5050565b60036020528060005260406000206000915090508060000160009054906101000a900460ff16908060010154905082565b60606002805480602002602001604051908101604052809291908181526020018280548015610b3357602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610ae9575b5050505050905090565b610b456111db565b610bb7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610c1c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124e16022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610cbf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b610cc881610852565b15610d3b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f546869732076616c696461746f7220697320616c72656164792061637469766581525060200191505060405180910390fd5b610d4481611f2e565b50565b600060149054906101000a900460ff1615610dca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f56616c696461746f72207365742069732066696e616c697a656400000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e8d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b6001600060146101000a81548160ff021916908315150217905550600073ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610fbe57600060036000600260016002805490500381548110610f1a57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060018160000160006101000a81548160ff02191690836003811115610fa257fe5b02179055506001600280549050038160010181905550506110f1565b600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff0219169083600381111561103f57fe5b0217905550600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506000600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b6002600190805461110392919061238d565b507f8564cd629b15f47dc310d45bcbfc9bcf5420b0d51bf0659a16c67f91d27632536001604051808060200182810382528381815481526020019150805480156111a257602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611158575b50509250505060405180910390a1565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b606060018054905060028054905011156112d45760028054806020026020016040519081016040528092919081815260200182805480156112c857602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161127e575b5050505050905061135e565b600180548060200260200160405190810160405280929190818152602001828054801561135657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161130c575b505050505090505b90565b60006002600381111561137057fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156113cb57fe5b148061143b57506003808111156113de57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561143957fe5b145b9050919050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1681565b606060018054806020026020016040519081016040528092919081815260200182805480156114ff57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116114b5575b5050505050905090565b600060038081111561151757fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561157257fe5b149050919050565b8461158481610852565b6115d9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b846115e381610852565b611638576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b844381106116ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611771576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b858773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff167f729a19138e072a5a8d3a56d74ae0b5c84530f09aacd6e12b24c5b2fdc3f8a3d060405160405180910390a45050505050505050565b6117de6111db565b611850576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156118d6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001806124746024913960400191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561197d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260408152602001806125036040913960400191505060405180910390fd5b61198681612003565b50565b8261199381610852565b6119e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b826119f281610852565b611a47576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b82438110611abd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b80576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b838573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fbc459bd9db54016b1966d0fe812bbe0a82cd627ae3eacd01727dc63a432ca41b60405160405180910390a4505050505050565b611beb6111db565b611c5d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b611c668161208a565b50565b600060026003811115611c7857fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff166003811115611cd357fe5b149050919050565b600160028054905011611d39576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806124986027913960400191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154905060006001600280549050039050600060028281548110611d9c57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508060028481548110611dd757fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506002805480919060019003611e7b91906123df565b5060038060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115611eda57fe5b021790555083600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611f286121eb565b50505050565b6002600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115611f8d57fe5b021790555060028190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506120006121eb565b50565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561212d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060146101000a81548160ff021916908315150217905550600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a084718a600143034060026040518363ffffffff1660e01b8152600401808381526020018060200182810382528381815481526020019150805480156122da57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311612290575b50509350505050602060405180830381600087803b1580156122fb57600080fd5b505af115801561230f573d6000803e3d6000fd5b505050506040513d602081101561232557600080fd5b810190808051906020019092919050505061238b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d815260200180612543602d913960400191505060405180910390fd5b565b8280548282559060005260206000209081019282156123ce5760005260206000209182015b828111156123cd5782548255916001019190600101906123b2565b5b5090506123db919061240b565b5090565b81548183558181111561240657818360005260206000209182019101612405919061244e565b5b505050565b61244b91905b8082111561244757600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550600101612411565b5090565b90565b61247091905b8082111561246c576000816000905550600101612454565b5090565b9056fe52656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f72206c65667441646472657373206973206e6f7420616e206163746976652076616c696461746f7256616c696461746f7220736574206973206e6f742066696e616c697a6564207965744e65772072656c617920636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6552656c617920636f6e747261637420496e6974696174654368616e67652063616c6c6261636b206661696c6564a165627a7a72305820f9ae418c0431b0892a4b904426be15d82d8486d433a3d6b9814bd5d1a377bb30002952656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f7220696e697469616c6c790000000000000000000000001204700000000000000000000000000000000005000000000000000000000000120470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003000000000000000000000000d65b4c25a4ce1e024ff13425df1e0e574a1a0e9b00000000000000000000000083329c3fd90d7ee2efd546e0dc6453e9172a0643000000000000000000000000de15831ac319dab5eae5fd1fd9d52876c5e50f20" - }, - "0x1204700000000000000000000000000000000000": { - "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561006557600080fd5b506040516040806114d48339810180604052604081101561008557600080fd5b810190808051906020019092919080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a361016b8261018160201b60201c565b61017a816102e260201b60201c565b50506104f4565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610224576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610386576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561042d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260428152602001806114926042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b610f8f806105036000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063b7ab4db511610071578063b7ab4db514610209578063bd96567714610268578063c476dd40146102ac578063d3e848f11461034f578063d69f13bb14610399578063f2fde38b146103e7576100a9565b806375286211146100ae5780638da5cb5b146100b85780638f32d59b14610102578063a084718a14610124578063ae3783d6146101bf575b600080fd5b6100b661042b565b005b6100c0610572565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61010a61059b565b604051808215151515815260200191505060405180910390f35b6101a56004803603604081101561013a57600080fd5b81019080803590602001909291908035906020019064010000000081111561016157600080fd5b82018360208201111561017357600080fd5b8035906020019184602083028401116401000000008311171561019557600080fd5b90919293919293905050506105f2565b604051808215151515815260200191505060405180910390f35b6101c761070c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610211610732565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610254578082015181840152602081019050610239565b505050509050019250505060405180910390f35b6102aa6004803603602081101561027e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610832565b005b61034d600480360360608110156102c257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561030957600080fd5b82018360208201111561031b57600080fd5b8035906020019184600183028401116401000000008311171561033d57600080fd5b90919293919293905050506108b8565b005b6103576109e7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103e5600480360360408110156103af57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a0d565b005b610429600480360360208110156103fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b06565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f53656e646572206973206e6f742073797374656d00000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663752862116040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561055857600080fd5b505af115801561056c573d6000803e3d6000fd5b50505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461069a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180610f426022913960400191505060405180910390fd5b837f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89848460405180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600190509392505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b7ab4db56040518163ffffffff1660e01b815260040160006040518083038186803b15801561079c57600080fd5b505afa1580156107b0573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060208110156107da57600080fd5b8101908080516401000000008111156107f257600080fd5b8281019050602081018481111561080857600080fd5b815185602082028301116401000000008211171561082557600080fd5b5050929190505050905090565b61083a61059b565b6108ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6108b581610b8c565b50565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bd21442a33868686866040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b1580156109c957600080fd5b505af11580156109dd573d6000803e3d6000fd5b5050505050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d826b7f13384846040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050600060405180830381600087803b158015610aea57600080fd5b505af1158015610afe573d6000803e3d6000fd5b505050505050565b610b0e61059b565b610b80576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b610b8981610d9e565b50565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610c30576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610cd7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526042815260200180610f006042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610e41576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fe4e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6553656e646572206973206e6f74207468652052656c6179656420636f6e7472616374a165627a7a723058204045b88d4bc25183a0d657cfec6f8f8b976fd6aca20ab02e07cc6d76f43e7bd100294e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6500000000000000000000000012047000000000000000000000000000000000050000000000000000000000001204700000000000000000000000000000000001" - }, - "0x1204700000000000000000000000000000000002": { - "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503480156200006657600080fd5b5060405160408062001bad833981018060405260408110156200008857600080fd5b810190808051906020019092919080519060200190929190505050620000b36200016360201b60201c565b60786000805490501462000113576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018062001b866027913960400191505060405180910390fd5b81600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600a81905550505062000878565b60405180610f0001604052806704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704395680a6af46808152602001670438cfa9de4a0e808152602001670437eeee904c06808152602001670436b44ebcb52e8081526020016704351fca638586808152602001670433316184bd0e808152602001670430e914205bc680815260200167042e46e23661ae80815260200167042b4acbc6cec6808152602001670427f4d0d1a30e80815260200167042444f156de868081526020016704203b2d56812e80815260200167041bd784d08b0680815260200167041719f7c4fc0e808152602001670412028633d44680815260200167040c91301d13ae808152602001670406c5f580ba46808152602001670400a0d65ec80e8081526020016703fa21d2b73d068081526020016703f348ea8a192e8081526020016703ec161dd75c868081526020016703e4896c9f070e8081526020016703dca2d6e118c68081526020016703d4625c9d91ae8081526020016703cbc7fdd471c68081526020016703c2d3ba85b90e8081526020016703b98592b167868081526020016703afdd86577d2e8081526020016703a5db9577fa0680815260200167039b7fc012de0e808152602001670390ca06282946808152602001670385ba67b7dbae80815260200167037a50e4c1f54680815260200167036e8d7d46760e8081526020016703627031455e06808152602001670355f900bead2e80815260200167034927ebb2638680815260200167033bfcf220810e80815260200167032e78140905c680815260200167032099516bf1ae80815260200167031260aa4944c6808152602001670303ce1ea0ff0e8081526020016702f4e1ae7320868081526020016702e59b59bfa92e8081526020016702d5fb208699068081526020016702c60102c7f00e8081526020016702b5ad0083ae468081526020016702a4ff19b9d3ae808152602001670293f74e6a6046808152602001670282959e95540e808152602001670270da0a3aaf0680815260200167025ec4915a712e80815260200167024c5533f49a86808152602001670238da1d6b04400081526020016702260c5cdf4d240081526020016702138f83989f90008152602001670201639196fb840081526020016701ef8886da61000081526020016701ddfe6362d0040081526020016701ccc5273048900081526020016701bbdcd242caa40081526020016701ab45649a564000815260200167019afede36eb6400815260200167018b093f188a1000815260200167017b64873f324400815260200167016c10b6aae40000815260200167015d0dcd5b9f4400815260200167014e5bcb51641000815260200167013ffab08c3264008152602001670131ea7d0c0a400081526020016701242b30d0eba4008152602001670116bccbdad6900081526020016701099f4e29cb0400815260200166fcd2b7bdc90000815260200166f0570896d08400815260200166e42c40b4e19000815260200166d8526017fc2400815260200166ccc966c0204000815260200166c19154ad4de400815260200166b6aa29df851000815260200166ac13e656c5c400815260200166a1ce8a1310000081526020016697da151463c4008152602001668e36875ac1100081526020016684e3e0e627e4008152602001667be221b6984000815260200166733149cc1224008152602001666ad1592695900081526020016662c24fc62284008152602001665b042daab900008152602001665396f2d45904008152602001664c7a9f4302900081526020016645af32f6b5a4008152602001663f34adef724000815260200166390b102d386400815260200166333259b00810008152602001662daa8a77e144008152602001662873a284c40000815260200166238da1d6b044008152602001661ef8886da610008152602001661ab45649a5640081526020016616c10b6aae4000815260200166131ea7d0c0a4008152602001660fcd2b7bdc90008152602001660ccc966c0204008152602001660a1ce8a131000081526020016607be221b69840081526020016605b042daab900081526020016603f34adef7240081526020016602873a284c4000815260200166016c10b6aae400815260200165a1ce8a1310008152602001652873a284c4008152506000906078620007e5929190620007fe565b5062080520600181905550607860015402600281905550565b8280548282559060005260206000209081019282156200083d579160200282015b828111156200083c5782518255916020019190600101906200081f565b5b5090506200084c919062000850565b5090565b6200087591905b808211156200087157600081600090555060010162000857565b5090565b90565b6112fe80620008886000396000f3fe608060405234801561001057600080fd5b50600436106101205760003560e01c80634476d66a116100ad57806394f7f62b1161007157806394f7f62b1461045d578063df6a50301461049f578063e6e100db146104bd578063f91c289814610515578063fb1ac5251461068057610120565b80634476d66a146103515780634f4013fb14610393578063553a5c85146103b157806358ceb672146103cf57806360d4b8be146103d957610120565b80631a488047116100f45780631a488047146101f157806330f6eb161461020f57806333ea51a81461027157806337339a16146102b55780633d84b8c1146102f957610120565b8062f380f414610125578063078d8e7a1461016f5780630f411cdb1461019157806318129375146101af575b600080fd5b61012d61069e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101776106c4565b604051808215151515815260200191505060405180910390f35b6101996106d4565b6040518082815260200191505060405180910390f35b6101db600480360360208110156101c557600080fd5b81019080803590602001909291905050506106da565b6040518082815260200191505060405180910390f35b6101f96106fb565b6040518082815260200191505060405180910390f35b61025b6004803603604081101561022557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610701565b6040518082815260200191505060405180910390f35b6102b36004803603602081101561028757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610726565b005b6102f7600480360360208110156102cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107a7565b005b61033b6004803603602081101561030f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108ae565b6040518082815260200191505060405180910390f35b61037d6004803603602081101561036757600080fd5b81019080803590602001909291905050506108c6565b6040518082815260200191505060405180910390f35b61039b6108de565b6040518082815260200191505060405180910390f35b6103b96108e3565b6040518082815260200191505060405180910390f35b6103d76108e9565b005b61041b600480360360208110156103ef57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6104896004803603602081101561047357600080fd5b8101908080359060200190929190505050610980565b6040518082815260200191505060405180910390f35b6104a76109c4565b6040518082815260200191505060405180910390f35b6104ff600480360360208110156104d357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109ca565b6040518082815260200191505060405180910390f35b6105e16004803603604081101561052b57600080fd5b810190808035906020019064010000000081111561054857600080fd5b82018360208201111561055a57600080fd5b8035906020019184602083028401116401000000008311171561057c57600080fd5b90919293919293908035906020019064010000000081111561059d57600080fd5b8201836020820111156105af57600080fd5b803590602001918460208302840111640100000000831117156105d157600080fd5b90919293919293905050506109e2565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561062857808201518184015260208101905061060d565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561066a57808201518184015260208101905061064f565b5050505090500194505050505060405180910390f35b610688610edb565b6040518082815260200191505060405180910390f35b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006106cf43610ee1565b905090565b60025481565b600081815481106106e757fe5b906000526020600020016000915090505481565b600a5481565b6008602052816000526040600020602052806000526040600020600091509150505481565b80600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461086a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f742074686520636f6d6d756e6974792066756e6481525060200191505060405180910390fd5b80600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60066020528060005260406000206000915090505481565b60076020528060005260406000206000915090505481565b607881565b60035481565b600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055565b600c6020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061098b82610ee1565b1561099957600090506109bf565b600060015483816109a657fe5b04815481106109b157fe5b906000526020600020015490505b919050565b60045481565b60056020528060005260406000206000915090505481565b606080600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610aa8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f43616c6c6572206973206e6f74207468652073797374656d000000000000000081525060200191505060405180910390fd5b838390508686905014610b06576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260258152602001806112ae6025913960400191505060405180910390fd5b60018686905014610b7f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f42656e65666163746f7273206c697374206c656e677468206973206e6f74203181525060200191505060405180910390fd5b600084846000818110610b8e57fe5b9050602002013561ffff1661ffff1614610bf3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061128c6022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1686866000818110610c1857fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161480610c5c5750610c5b43610ee1565b5b15610cd2576000604051908082528060200260200182016040528015610c915781602001602082028038833980820191505090505b506000604051908082528060200260200182016040528015610cc25781602001602082028038833980820191505090505b5081915080905091509150610ed2565b60606002604051908082528060200260200182016040528015610d045781602001602082028038833980820191505090505b50905060608151604051908082528060200260200182016040528015610d395781602001602082028038833980820191505090505b509050610d6e88886000818110610d4c57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff16610ef0565b82600081518110610d7b57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050610dbe43610980565b81600081518110610dcb57fe5b602002602001018181525050610e02600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16610ef0565b82600181518110610e0f57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600a5481600181518110610e5957fe5b602002602001018181525050610e9782600081518110610e7557fe5b602002602001015182600081518110610e8a57fe5b6020026020010151610f9d565b610ec982600181518110610ea757fe5b602002602001015182600181518110610ebc57fe5b6020026020010151611145565b81819350935050505b94509492505050565b60015481565b60006002548210159050919050565b600080600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f935782915050610f98565b809150505b919050565b61100081600860008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060004381526020019081526020016000205461120390919063ffffffff16565b600860008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000438152602001908152602001600020819055506110a681600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461120390919063ffffffff16565b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061110f81600760004381526020019081526020016000205461120390919063ffffffff16565b600760004381526020019081526020016000208190555061113b8160035461120390919063ffffffff16565b6003819055505050565b61115a8160045461120390919063ffffffff16565b6004819055506111b281600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461120390919063ffffffff16565b600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506111ff8282610f9d565b5050565b600080828401905083811015611281576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4f766572666c6f77206572726f7200000000000000000000000000000000000081525060200191505060405180910390fd5b809150509291505056fe42656e65666163746f72206973206e6f742074686520626c6f636b20617574686f7242656e65666163746f72732f7479706573206c697374206c656e6774682064696666657273a165627a7a72305820ef1c2590551666e2ac227034529ee69c33ec91868d23c0a0a887d514094ec6030029526577617264206375727665206973206e6f7420746865207265717569726564206c656e67746800000000000000000000000012047000000000000000000000000000000000030000000000000000000000000000000000000000000000000856d3dfb6e26d00" - }, - "0x1204700000000000000000000000000000000004": { - "balance": "41198207933333333690000000", - "constructor": "0x608060405260006001556a22140f53c2d216263ba2803073ffffffffffffffffffffffffffffffffffffffff16311462000085576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806200150b6023913960400191505060405180910390fd5b620000956200010260201b60201c565b6a22140f53c2d216263ba28060015414620000fc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180620014ba6028913960400191505060405180910390fd5b62001133565b62000136722d4606b65c033769968bcdc63881b90b0853f569264876ff9c85a7a8ee00635df22bc06200105960201b60201c565b6200016a726e371c454a2d081f3966c180ba2c6165d87de66901c3c03b52c63f027880635df22bc06200105960201b60201c565b6200019e72c2f65230815d30eaa1a4d057bcf0b72fe3cc4e6902a5a058fc295ed00000635df22bc06200105960201b60201c565b620001d37303cccdc799d4dc37d56e3f9dba7f9c210fa1086f6902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200020873047d955877a55fbdac768573a9259f29b103a0666902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200023d7306920bb91f7027176cf373996d39b539ba436d876969e10de76676d0800000635d79dee06200105960201b60201c565b620002727306fdb93aa64f33a8fb40a36c462a3f7a074d632c6901c3c03b52c63f027880635df22bc06200105960201b60201c565b620002a7730ad7ba4af33b485e6f2505c417554631a3e5643f6902a5a058fc295ed00000635df22bc06200105960201b60201c565b620002dc730dd959deb4c458cc2ac379898bf2c99f7a8f399b6901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000311731153818a2eb49f0a71b27313c32814fc02e4db50694220bb939da668600000635df22bc06200105960201b60201c565b6200034773120470000000000000000000000000000000000a6a084595161401484a000000635d79dee06200105960201b60201c565b6200037c7315696134ebeed360dc90dc97ddd00bd07e1c11e96934f086f3b33b68400000635df22bc06200105960201b60201c565b620003b1731f0c30b1aa4c468b5beb02bac8df8f27617a2296692346646590efbaf71200635df22bc06200105960201b60201c565b620003e6732540ded041b6fedc0ff6f0cf26b891ec97c954006901c3c03b52c63f027880635df22bc06200105960201b60201c565b6200041b7325ae7b45d8646580dfcae403d29164729eb8642f691a784379d99db4200000635df22bc06200105960201b60201c565b6200045073349ebc5a6e853df121c84e999081e5992928e64f6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000485733885d15573e45228dd54cd4fde9bfac64d702ed46901c3c03b52c63f027880635df22bc06200105960201b60201c565b620004ba733a9d83766c03c465851a38daa364ef7deccd1ece6934f086f3b33b68400000635df22bc06200105960201b60201c565b620004ef733abaa3f24428d6028f5a7fc5b18ce9d04ccec2296902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000524733c9f867d9b3a595987e198786fa9ab722e5c2f9b6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000559733f11b4ad17fde4695cad64e109ae92a679d87bfc6969e10de76676d0800000635d79dee06200105960201b60201c565b6200058e733f12af735238c6e2fa45efb5b2f3fae82df4c9226901c3c03b52c63f027880635df22bc06200105960201b60201c565b620005c373404bb9c13364522133b363d5c4adb7a88056b19d6902d2cd2bb7a39658b500635df22bc06200105960201b60201c565b620005f873428ab4b019ee3a9b9863b2b4bf1885ce6dff9a736902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200062d7347428fc08e56388372e7c81ad4a1140d932d10966969e10de76676d0800000635d79dee06200105960201b60201c565b620006627348ee57faf61c0b963113e7921e6173629e6bc4436902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000697734d0aa1c3459bf41e3ad4e4f40bbf029cb5723d836934f086f3b33b68400000635df22bc06200105960201b60201c565b620006cc7357f33efad76d4b783cf42c9e6cb08f4425dfe96e6902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000701735b3fb4e1d6040615f3e681bec4c80b5d7c9580716901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000736735fbb9c482034d287c5b3848fc2f9272abdd5bfa26903878076a58c80674b00635df22bc06200105960201b60201c565b6200076b73656e5569bef7781bf0db199d32027766053501ff69438ec266600555e00000635df22bc06200105960201b60201c565b620007a073664f991cdb2ffe6b6a568ede65b0208dbcce6f72691a784379d99db4200000635df22bc06200105960201b60201c565b620007d67369af0912dd44dce2b2373db4021788cbad84ff356a0422ca8b0a00a4250000006360c3f9006200105960201b60201c565b6200080b736a0a5da2a48ea87c2a906c53b3373642c29a4b6c6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000840736cf32cc52e220c023c2d92b1d62310f46a6e2a136901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000875736d516767e4068fc331bdb331fba7578bdb07a68c69234b04a2777d0408ee00635df22bc06200105960201b60201c565b620008aa736dd10e41a7a84fe23ab35fefa2f46c9895f87a2d693c8c4bde2deef1680000635df22bc06200105960201b60201c565b620008df737030892dbf9c2048e796296dda597f145754a1856969e10de76676d0800000635d79dee06200105960201b60201c565b62000914737ed62cf71d519d3bf293ef90829508f92f4ccccb6902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200094a73871ba4266793ad11da537d4857de7ad49eab662b6a017293b0a9e69fd9c00000635df22bc06200105960201b60201c565b6200097f73880e8b0ece0171edd0247f8d13d348d77a6b9b296903878076a58c80674b00635df22bc06200105960201b60201c565b620009b373887f2b16847248bc757b69f3c695f24ff344daf268e9062e03b5c2908780635df22bc06200105960201b60201c565b620009e8738c994ada51d35b8519424368807fb99c103366866969e10de76676d0800000635d79dee06200105960201b60201c565b62000a1d739196e46d664ceda55cb45a2cc5ab5bd1b7e614e26902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000a5273943c85b13f24083ec73815f7ba763b7c42ae02886902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000a87739467b762550673f08b14423f8562048d5e3694226902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000abc73949423db1bfee1ddec99c9d24a12a6ea27cb34896901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000af17396a5eb172efdf262ed6beaaf0e20c6af71831fc96934f086f3b33b68400000635df22bc06200105960201b60201c565b62000b2673a69dca0814eaadc89b6dbe94c5e2110497690f6c6903878076a58c80674b00635df22bc06200105960201b60201c565b62000b5b73a720a8ee90f5013cae9bf7bcac1d153e42815454691a784379d99db4200000635df22bc06200105960201b60201c565b62000b9073b080454f190e76eb8e719560fa8cae50c71bcea96901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000bc573b476ee7d610dae7b23b671ebc7bd6112e97729696902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000bfb73b561618a3ea959a5e363643b267c4cb8fe4b1df76a0422ca8b0a00a4250000006360c3f9006200105960201b60201c565b62000c3073b5b6d8885fbf28f843cc7886de242b811d6952056901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000c6573b61c11b6e42d459efaee8995c44db08507e468e169477d5529f68a63000000635df22bc06200105960201b60201c565b62000c9a73b999004b49c6b907d4278067da5c85195dcd7fc769081e0dddaff653f8b500635df22bc06200105960201b60201c565b62000ccf73be4888c5b021e5f16cd254de2d4eaf17625685c46934f086f3b33b68400000635df22bc06200105960201b60201c565b62000d0473c1d441a2ad43af7b4a3d8e3200d2ceb3a973099d6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000d3973c58a20e290e858542d8e8bb07b600aeb9195fe306903878076a58c80674b00635df22bc06200105960201b60201c565b62000d6f73cfe7964b0b6412b013dc019bdf3afef58be565936a055084cc99f0bdbadd0000635df22bc06200105960201b60201c565b62000da473d33d4f83e85c92e0b53ffe4fc0e18b0e3632c0976901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000dd973d44fb8de580d34f44789408cc9335c9a9ce0ce4d691ad0235eb930a0540000635df22bc06200105960201b60201c565b62000e0e73dacd80d8e1d4f117515caa477ee7599cdfc766196902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000e4373db6cc57168c07b83a00f1f8871538446068824fc691a784379d99db4200000635df22bc06200105960201b60201c565b62000e7873de6b493d368316b9078454e37dce4968482dfbe96969e10de76676d0800000635d79dee06200105960201b60201c565b62000ead73e23c7cb60189bb2fd60625d2c2747b1e68f107766934f086f3b33b68400000635df22bc06200105960201b60201c565b62000ee273e6e8a111c89b05337049de9349c7c4880a396ef1691a784379d99db4200000635df22bc06200105960201b60201c565b62000f1773ebbddf28bf3224791b0510a2ab8813f182fe4e2b6901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000f4c73f4e31018a926f64cb780cb9f5f027377bcfb26fc6907f0e10af47c1c700000635df22bc06200105960201b60201c565b62000f8173f8e6ecb4b0f17576525749bdf85524652cbf002e6901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000fb773fd679097fe0f914642af9857e5799332fe2efa296a01a784379d99db42000000635d79dee06200105960201b60201c565b62000fed73fd7a30d3c2bd017a458610274c275059d308b2e76a01ff1675f219f5a8780000635df22bc06200105960201b60201c565b6200102273ffcf98c62c1bad480ab6846717b173a72e2dd090691a784379d99db4200000635df22bc06200105960201b60201c565b6200105773ffd9b871df6e93803c0877e98fc1722b39c00d786902a5a058fc295ed00000635df22bc06200105960201b60201c565b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000154148015620010b4575060008160010154145b6200110b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526029815260200180620014e26029913960400191505060405180910390fd5b8260016000828254019250508190555082816000018190555081816001018190555050505050565b61037780620011436000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806318a5bbdc14610051578063192e7a7b146100b057806330f0dbe0146100f45780639976e12f14610112575b600080fd5b6100936004803603602081101561006757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610130565b604051808381526020018281526020019250505060405180910390f35b6100f2600480360360208110156100c657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610154565b005b6100fc610336565b6040518082815260200191505060405180910390f35b61011a610345565b6040518082815260200191505060405180910390f35b60006020528060005260406000206000915090508060000154908060010154905082565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816000015411610210576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f417661696c61626c6520616d6f756e742069732030000000000000000000000081525060200191505060405180910390fd5b80600101544211610289576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f486f6c64696e6720706572696f64206973206e6f74206f76657200000000000081525060200191505060405180910390fd5b600081600001549050600082600001819055508273ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156102e2573d6000803e3d6000fd5b508273ffffffffffffffffffffffffffffffffffffffff167f221c08a06b07a64803b3787861a3f276212fcccb51c2e6234077a9b8cb13047a826040518082815260200191505060405180910390a2505050565b6a22140f53c2d216263ba28081565b6001548156fea165627a7a72305820b0165b18a29fae78ec1f58bf69134a67ab02e21d059e757412318d8362284866002954617267657420616d6f756e742073686f756c6420657175616c2061637475616c20616d6f756e74486f6c64696e6720666f72207468697320616464726573732077617320616c7265616479207365742e42616c616e63652073686f756c6420657175616c2074617267657420616d6f756e742e" - }, - "0x1204700000000000000000000000000000000006": { - "constructor": "0x60806040523480156200001157600080fd5b5060405160208062003ed2833981018060405260208110156200003357600080fd5b8101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a362000111816200011860201b60201c565b506200027a565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001bc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b613c48806200028a6000396000f3fe608060405234801561001057600080fd5b50600436106101585760003560e01c806392698814116100c3578063e30bd7401161007c578063e30bd740146109b5578063eadf976014610a72578063ef5454d614610b17578063f25eb5c114610bc8578063f2fde38b14610bd2578063f6d339e414610c1657610158565b80639269881414610705578063ac4e73f91461074b578063ac72c120146107fc578063c3a3582514610842578063deb931a2146108d9578063df57b7421461094757610158565b80636795dbcd116101155780636795dbcd1461041c5780636a1acc3f146104df57806379ce9fac1461059c5780638da5cb5b146106025780638f32d59b1461064c57806390b97fc11461066e57610158565b806306b2ff471461015d57806319362a28146101b9578063267b69221461025e5780633f3935d1146102ff578063432ced04146103905780634f39ca59146103d6575b600080fd5b61019f6004803603602081101561017357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cd1565b604051808215151515815260200191505060405180910390f35b610244600480360360608110156101cf57600080fd5b8101908080359060200190929190803590602001906401000000008111156101f657600080fd5b82018360208201111561020857600080fd5b8035906020019184600183028401116401000000008311171561022a57600080fd5b909192939192939080359060200190929190505050610d31565b604051808215151515815260200191505060405180910390f35b61028a6004803603602081101561027457600080fd5b8101908080359060200190929190505050610fcb565b604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390f35b6103766004803603602081101561031557600080fd5b810190808035906020019064010000000081111561033257600080fd5b82018360208201111561034457600080fd5b8035906020019184600183028401116401000000008311171561036657600080fd5b909192939192939050505061102f565b604051808215151515815260200191505060405180910390f35b6103bc600480360360208110156103a657600080fd5b810190808035906020019092919050505061134e565b604051808215151515815260200191505060405180910390f35b610402600480360360208110156103ec57600080fd5b8101908080359060200190929190505050611546565b604051808215151515815260200191505060405180910390f35b61049d6004803603604081101561043257600080fd5b81019080803590602001909291908035906020019064010000000081111561045957600080fd5b82018360208201111561046b57600080fd5b8035906020019184600183028401116401000000008311171561048d57600080fd5b9091929391929390505050611a8d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610521600480360360208110156104f557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611bb2565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610561578082015181840152602081019050610546565b50505050905090810190601f16801561058e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6105e8600480360360408110156105b257600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c62565b604051808215151515815260200191505060405180910390f35b61060a611f58565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610654611f81565b604051808215151515815260200191505060405180910390f35b6106ef6004803603604081101561068457600080fd5b8101908080359060200190929190803590602001906401000000008111156106ab57600080fd5b8201836020820111156106bd57600080fd5b803590602001918460018302840111640100000000831117156106df57600080fd5b9091929391929390505050611fd8565b6040518082815260200191505060405180910390f35b6107316004803603602081101561071b57600080fd5b81019080803590602001909291905050506120fa565b604051808215151515815260200191505060405180910390f35b6107e26004803603604081101561076157600080fd5b810190808035906020019064010000000081111561077e57600080fd5b82018360208201111561079057600080fd5b803590602001918460018302840111640100000000831117156107b257600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612245565b604051808215151515815260200191505060405180910390f35b6108286004803603602081101561081257600080fd5b81019080803590602001909291905050506127e7565b604051808215151515815260200191505060405180910390f35b6108c36004803603604081101561085857600080fd5b81019080803590602001909291908035906020019064010000000081111561087f57600080fd5b82018360208201111561089157600080fd5b803590602001918460018302840111640100000000831117156108b357600080fd5b9091929391929390505050612932565b6040518082815260200191505060405180910390f35b610905600480360360208110156108ef57600080fd5b8101908080359060200190929190505050612a57565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6109736004803603602081101561095d57600080fd5b8101908080359060200190929190505050612b72565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6109f7600480360360208110156109cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612c8d565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610a37578082015181840152602081019050610a1c565b50505050905090810190601f168015610a645780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610afd60048036036060811015610a8857600080fd5b810190808035906020019092919080359060200190640100000000811115610aaf57600080fd5b820183602082011115610ac157600080fd5b80359060200191846001830284011164010000000083111715610ae357600080fd5b909192939192939080359060200190929190505050612d6e565b604051808215151515815260200191505060405180910390f35b610bae60048036036040811015610b2d57600080fd5b8101908080359060200190640100000000811115610b4a57600080fd5b820183602082011115610b5c57600080fd5b80359060200191846001830284011164010000000083111715610b7e57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061300b565b604051808215151515815260200191505060405180910390f35b610bd0613281565b005b610c1460048036036020811015610be857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613674565b005b610cb760048036036060811015610c2c57600080fd5b810190808035906020019092919080359060200190640100000000811115610c5357600080fd5b820183602082011115610c6557600080fd5b80359060200191846001830284011164010000000083111715610c8757600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506136fa565b604051808215151515815260200191505060405180910390f35b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080546001816001161561010002031660029004905014159050919050565b600084600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610e0d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610ee5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b60016020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082565b600082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611156576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b83838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050503373ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611279576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4572726f723a204f6e6c79207768656e2070726f706f7365640000000000000081525060200191505060405180910390fd5b8484600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002091906112c7929190613b0e565b503373ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019250505092915050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611429576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f4572726f723a204f6e6c79207768656e20756e7265736572766564000000000081525060200191505060405180910390fd5b611431611f81565b6114a3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b336001600085815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff16837f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405160405180910390a36001915050919050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611622576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b823373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146116fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600260006001600088815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156117cb5780601f106117a95761010080835404028352918201916117cb565b820191906000526020600020905b8154815290600101906020018083116117b7575b5050915050604051809103902014156119da576001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd600260006001600089815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051808060200182810382528381815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561194a5780601f1061191f5761010080835404028352916020019161194a565b820191906000526020600020905b81548152906001019060200180831161192d57829003601f168201915b50509250505060405180910390a2600260006001600087815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006119d99190613b8e565b5b60016000858152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550503373ffffffffffffffffffffffffffffffffffffffff16847fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405160405180910390a3600192505050919050565b600083600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b69576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b60016000868152602001908152602001600020600201848460405180838380828437808301925050509250505090815260200160405180910390205460001c9150509392505050565b60026020528060005260406000206000915090508054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611c5a5780601f10611c2f57610100808354040283529160200191611c5a565b820191906000526020600020905b815481529060010190602001808311611c3d57829003601f168201915b505050505081565b600082600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611d3e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b833373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611e16576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415611e9c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180613bfc6021913960400191505060405180910390fd5b836001600087815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16867f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405160405180910390a460019250505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b600083600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156120b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600086815260200190815260200160002060020184846040518083838082843780830192505050925050509081526020016040518091039020549150509392505050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156121d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561236c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b848460405180838380828437808301925050509250505060405180910390203373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612462576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b6000868660405180838380828437808301925050509250505060405180910390209050600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141580156125d3575080600260006001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156125c45780601f106125a25761010080835404028352918201916125c4565b820191906000526020600020905b8154815290600101906020018083116125b0575b50509150506040518091039020145b1561270a57600260006001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006126599190613b8e565b6001600082815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a25b846001600083815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508473ffffffffffffffffffffffffffffffffffffffff167f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600193505050509392505050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156128c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b600083600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612a0e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b60016000868152602001908152602001600020600201848460405180838380828437808301925050509250505090815260200160405180910390205460001c9150509392505050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612b33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612c4e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612d625780601f10612d3757610100808354040283529160200191612d62565b820191906000526020600020905b815481529060010190602001808311612d4557829003601f168201915b50505050509050919050565b600084600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612e4a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612f22576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b8360001b600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415613132576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b61313a611f81565b6131ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8484600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002091906131fa929190613b0e565b508273ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019150509392505050565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156133545780601f1061332957610100808354040283529160200191613354565b820191906000526020600020905b81548152906001019060200180831161333757829003601f168201915b5050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415613439576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180806020018281038252838181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156135395780601f1061350e57610100808354040283529160200191613539565b820191906000526020600020905b81548152906001019060200180831161351c57829003601f168201915b50509250505060405180910390a260016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156135e55780601f106135c35761010080835404028352918201916135e5565b820191906000526020600020905b8154815290600101906020018083116135d1575b50509150506040518091039020815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006136719190613b8e565b50565b61367c611f81565b6136ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6136f7816139ad565b50565b600084600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156137d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146138ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff1660001b600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613a50576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10613b4f57803560ff1916838001178555613b7d565b82800160010185558215613b7d579182015b82811115613b7c578235825591602001919060010190613b61565b5b509050613b8a9190613bd6565b5090565b50805460018160011615610100020316600290046000825580601f10613bb45750613bd3565b601f016020900490600052602060002090810190613bd29190613bd6565b5b50565b613bf891905b80821115613bf4576000816000905550600101613bdc565b5090565b9056fe4572726f723a206e6f207472616e7366657220746f206164647265737320307830a165627a7a72305820fe186a600f547c59523f333a7eb66cd1de74492306acaa3db2cd4c8a1b426b5a00290000000000000000000000001204700000000000000000000000000000000005" - }, - "0x1204700000000000000000000000000000000007": { - "constructor": "0x608060405234801561001057600080fd5b506040516040806108658339810180604052604081101561003057600080fd5b810190808051906020019092919080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3610116816101a160201b60201c565b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167fcc57a97257d7703be97e9d538cb08b3fa1ff2784b776d505942d49a95dc2891460405160405180910390a25050610302565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610244576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610554806103116000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80636ddc4a071461005c5780638da5cb5b146100a65780638f32d59b146100f0578063f2fde38b14610112578063fe64d6ff14610156575b600080fd5b61006461019a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100ae6101c0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100f86101e9565b604051808215151515815260200191505060405180910390f35b6101546004803603602081101561012857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610240565b005b6101986004803603602081101561016c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102c6565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6102486101e9565b6102ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6102c3816103c7565b50565b6102ce6101e9565b610340576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fcc57a97257d7703be97e9d538cb08b3fa1ff2784b776d505942d49a95dc2891460405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561046a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea165627a7a723058204d73401a5cbb107ba765457948a3c2a2612436446f9125c3696f0f4cc7e06980002900000000000000000000000012047000000000000000000000000000000000090000000000000000000000001204700000000000000000000000000000000005" - }, - "0x1204700000000000000000000000000000000008": { - "constructor": "0x60806040523480156200001157600080fd5b506040516040806200298b8339810180604052620000339190810190620002a8565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062000141816200014960201b60201c565b5050620003da565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001bc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001b3906200032b565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000815190506200028b81620003a6565b92915050565b600081519050620002a281620003c0565b92915050565b60008060408385031215620002bc57600080fd5b6000620002cc8582860162000291565b9250506020620002df858286016200027a565b9150509250929050565b6000620002f8601f836200034d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034681620002e9565b9050919050565b600082825260208201905092915050565b60006200036b8262000386565b9050919050565b60006200037f826200035e565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b620003b1816200035e565b8114620003bd57600080fd5b50565b620003cb8162000372565b8114620003d757600080fd5b50565b6125a180620003ea6000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c8063954029c911610097578063eab7ad9211610066578063eab7ad92146102b2578063ee7ee0fd146102ce578063f2fde38b146102fe578063fbd74f841461031a576100f5565b8063954029c91461021a578063a999809f14610236578063b71da0d114610266578063da5393d514610296576100f5565b80636a564261116100d35780636a5642611461017857806370a05b18146101ae5780638da5cb5b146101de5780638f32d59b146101fc576100f5565b80631bab58f5146100fa5780632c5653e21461012a57806332d91c0e14610148575b600080fd5b610114600480360361010f9190810190611d82565b61034a565b60405161012191906123f7565b60405180910390f35b610132610767565b60405161013f919061233a565b60405180910390f35b610162600480360361015d9190810190611d82565b61078d565b60405161016f9190612355565b60405180910390f35b610192600480360361018d9190810190611d82565b61097e565b6040516101a597969594939291906122af565b60405180910390f35b6101c860048036036101c39190810190611d82565b610c2d565b6040516101d5919061228d565b60405180910390f35b6101e6610e1e565b6040516101f39190612257565b60405180910390f35b610204610e47565b6040516102119190612272565b60405180910390f35b610234600480360361022f9190810190611d82565b610e9e565b005b610250600480360361024b9190810190611d82565b610ff5565b60405161025d9190612355565b60405180910390f35b610280600480360361027b9190810190611d82565b6111e6565b60405161028d9190612272565b60405180910390f35b6102b060048036036102ab9190810190611d82565b611276565b005b6102cc60048036036102c79190810190611dd4565b611371565b005b6102e860048036036102e39190810190611d82565b611673565b6040516102f59190612272565b60405180910390f35b61031860048036036103139190810190611d82565b6117d9565b005b610334600480360361032f9190810190611d82565b61182c565b604051610341919061228d565b60405180910390f35b610352611b4b565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156103ba57600080fd5b505afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506103f29190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461045f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045690612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060e0016040529081600082018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105425780601f1061051757610100808354040283529160200191610542565b820191906000526020600020905b81548152906001019060200180831161052557829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105e45780601f106105b9576101008083540402835291602001916105e4565b820191906000526020600020905b8154815290600101906020018083116105c757829003601f168201915b50505050508152602001600282018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106865780601f1061065b57610100808354040283529160200191610686565b820191906000526020600020905b81548152906001019060200180831161066957829003601f168201915b50505050508152602001600382018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107285780601f106106fd57610100808354040283529160200191610728565b820191906000526020600020905b81548152906001019060200180831161070b57829003601f168201915b505050505081526020016004820160009054906101000a900460ff16151515158152602001600582015481526020016006820154815250509050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156107f757600080fd5b505afa15801561080b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061082f9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461089c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161089390612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156109725780601f1061094757610100808354040283529160200191610972565b820191906000526020600020905b81548152906001019060200180831161095557829003601f168201915b50505050509050919050565b6001602052806000526040600020600091509050806000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a2a5780601f106109ff57610100808354040283529160200191610a2a565b820191906000526020600020905b815481529060010190602001808311610a0d57829003601f168201915b505050505090806001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610ac85780601f10610a9d57610100808354040283529160200191610ac8565b820191906000526020600020905b815481529060010190602001808311610aab57829003601f168201915b505050505090806002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b665780601f10610b3b57610100808354040283529160200191610b66565b820191906000526020600020905b815481529060010190602001808311610b4957829003601f168201915b505050505090806003018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c045780601f10610bd957610100808354040283529160200191610c04565b820191906000526020600020905b815481529060010190602001808311610be757829003601f168201915b5050505050908060040160009054906101000a900460ff16908060050154908060060154905087565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b158015610c9757600080fd5b505afa158015610cab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ccf9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610d3c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d3390612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610e125780601f10610de757610100808354040283529160200191610e12565b820191906000526020600020905b815481529060010190602001808311610df557829003601f168201915b50505050509050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b158015610f0657600080fd5b505afa158015610f1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f3e9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610fab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fa290612377565b60405180910390fd5b43600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206006018190555050565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b15801561105f57600080fd5b505afa158015611073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110979190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611104576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110fb90612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156111da5780601f106111af576101008083540402835291602001916111da565b820191906000526020600020905b8154815290600101906020018083116111bd57829003601f168201915b50505050509050919050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060060154600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060050154109050919050565b61127e610e47565b6112bd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112b4906123d7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561132d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161132490612397565b60405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156113d957600080fd5b505afa1580156113ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114119190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461147e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161147590612377565b60405180910390fd5b8888600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000191906114cf929190611b8a565b508686600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001019190611521929190611c0a565b508484600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002019190611573929190611b8a565b508282600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030191906115c5929190611c0a565b5080600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160006101000a81548160ff02191690831515021790555043600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206005018190555050505050505050505050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156116dd57600080fd5b505afa1580156116f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117159190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611782576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161177990612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160009054906101000a900460ff169050919050565b6117e1610e47565b611820576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611817906123d7565b60405180910390fd5b61182981611a1d565b50565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b15801561189657600080fd5b505afa1580156118aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ce9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461193b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161193290612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611a115780601f106119e657610100808354040283529160200191611a11565b820191906000526020600020905b8154815290600101906020018083116119f457829003601f168201915b50505050509050919050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611a8d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a84906123b7565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611bcb57803560ff1916838001178555611bf9565b82800160010185558215611bf9579182015b82811115611bf8578235825591602001919060010190611bdd565b5b509050611c069190611c8a565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611c4b57803560ff1916838001178555611c79565b82800160010185558215611c79579182015b82811115611c78578235825591602001919060010190611c5d565b5b509050611c869190611c8a565b5090565b611cac91905b80821115611ca8576000816000905550600101611c90565b5090565b90565b600081359050611cbe81612539565b92915050565b600081519050611cd381612539565b92915050565b600081359050611ce881612550565b92915050565b60008083601f840112611d0057600080fd5b8235905067ffffffffffffffff811115611d1957600080fd5b602083019150836001820283011115611d3157600080fd5b9250929050565b60008083601f840112611d4a57600080fd5b8235905067ffffffffffffffff811115611d6357600080fd5b602083019150836001820283011115611d7b57600080fd5b9250929050565b600060208284031215611d9457600080fd5b6000611da284828501611caf565b91505092915050565b600060208284031215611dbd57600080fd5b6000611dcb84828501611cc4565b91505092915050565b60008060008060008060008060008060c08b8d031215611df357600080fd5b6000611e018d828e01611caf565b9a505060208b013567ffffffffffffffff811115611e1e57600080fd5b611e2a8d828e01611cee565b995099505060408b013567ffffffffffffffff811115611e4957600080fd5b611e558d828e01611d38565b975097505060608b013567ffffffffffffffff811115611e7457600080fd5b611e808d828e01611cee565b955095505060808b013567ffffffffffffffff811115611e9f57600080fd5b611eab8d828e01611d38565b935093505060a0611ebe8d828e01611cd9565b9150509295989b9194979a5092959850565b611ed981612489565b82525050565b611ee88161249b565b82525050565b611ef78161249b565b82525050565b6000611f0882612424565b611f128185612456565b9350611f228185602086016124f5565b611f2b81612528565b840191505092915050565b6000611f4182612419565b611f4b8185612445565b9350611f5b8185602086016124f5565b611f6481612528565b840191505092915050565b6000611f7a82612419565b611f848185612456565b9350611f948185602086016124f5565b611f9d81612528565b840191505092915050565b611fb1816124d1565b82525050565b6000611fc28261243a565b611fcc8185612478565b9350611fdc8185602086016124f5565b611fe581612528565b840191505092915050565b6000611ffb8261242f565b6120058185612467565b93506120158185602086016124f5565b61201e81612528565b840191505092915050565b60006120348261242f565b61203e8185612478565b935061204e8185602086016124f5565b61205781612528565b840191505092915050565b600061206f601383612478565b91507f4572726f723a206f6e6c794c6f676963204462000000000000000000000000006000830152602082019050919050565b60006120af602983612478565b91507f4572726f723a206e65774c6f6f6b5570206973206e6f7420616c6c6f7765642060008301527f746f2062652030783000000000000000000000000000000000000000000000006020830152604082019050919050565b6000612115601f83612478565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b6000612155601383612478565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600060e08301600083015184820360008601526121a58282611f36565b915050602083015184820360208601526121bf8282611ff0565b915050604083015184820360408601526121d98282611f36565b915050606083015184820360608601526121f38282611ff0565b91505060808301516122086080860182611edf565b5060a083015161221b60a0860182612239565b5060c083015161222e60c0860182612239565b508091505092915050565b612242816124c7565b82525050565b612251816124c7565b82525050565b600060208201905061226c6000830184611ed0565b92915050565b60006020820190506122876000830184611eee565b92915050565b600060208201905081810360008301526122a78184611efd565b905092915050565b600060e08201905081810360008301526122c9818a611f6f565b905081810360208301526122dd8189612029565b905081810360408301526122f18188611f6f565b905081810360608301526123058187612029565b90506123146080830186611eee565b61232160a0830185612248565b61232e60c0830184612248565b98975050505050505050565b600060208201905061234f6000830184611fa8565b92915050565b6000602082019050818103600083015261236f8184611fb7565b905092915050565b6000602082019050818103600083015261239081612062565b9050919050565b600060208201905081810360008301526123b0816120a2565b9050919050565b600060208201905081810360008301526123d081612108565b9050919050565b600060208201905081810360008301526123f081612148565b9050919050565b600060208201905081810360008301526124118184612188565b905092915050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b6000612494826124a7565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006124dc826124e3565b9050919050565b60006124ee826124a7565b9050919050565b60005b838110156125135780820151818401526020810190506124f8565b83811115612522576000848401525b50505050565b6000601f19601f8301169050919050565b61254281612489565b811461254d57600080fd5b50565b6125598161249b565b811461256457600080fd5b5056fea265627a7a7230582045fea88fa096a11a15a5bb8e9f5993ca3ab8a38132e6c7a04eb7cd5458378c906c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000070000000000000000000000001204700000000000000000000000000000000005" - }, - "0x1204700000000000000000000000000000000009": { - "constructor": "0x60806040523480156200001157600080fd5b506040516040806200208d8339810180604052620000339190810190620002a8565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062000141816200014960201b60201c565b5050620003da565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001bc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001b3906200032b565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000815190506200028b81620003a6565b92915050565b600081519050620002a281620003c0565b92915050565b60008060408385031215620002bc57600080fd5b6000620002cc8582860162000291565b9250506020620002df858286016200027a565b9150509250929050565b6000620002f8601f836200034d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034681620002e9565b9050919050565b600082825260208201905092915050565b60006200036b8262000386565b9050919050565b60006200037f826200035e565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b620003b1816200035e565b8114620003bd57600080fd5b50565b620003cb8162000372565b8114620003d757600080fd5b50565b611ca380620003ea6000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80638f32d59b1161005b5780638f32d59b14610101578063b71da0d11461011f578063ec2626111461014f578063f2fde38b1461016d57610088565b806312127ed71461008d5780633f67c333146100a957806389c3ce1e146100b35780638da5cb5b146100e3575b600080fd5b6100a760048036036100a2919081019061118d565b610189565b005b6100b16107d7565b005b6100cd60048036036100c89190810190611164565b610a23565b6040516100da91906119c0565b60405180910390f35b6100eb610aee565b6040516100f891906117b7565b60405180910390f35b610109610b17565b604051610116919061186a565b60405180910390f35b61013960048036036101349190810190611164565b610b6e565b604051610146919061186a565b60405180910390f35b610157610c22565b6040516101649190611885565b60405180910390f35b61018760048036036101829190810190611164565b610c48565b005b610191610b17565b6101d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101c790611940565b60405180910390fd5b600085511415610215576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161020c906118e0565b60405180910390fd5b60008451141561025a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161025190611980565b60405180910390fd5b60008351141561029f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610296906118a0565b60405180910390fd5b6000825114156102e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102db906118c0565b60405180910390fd5b8480519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84886040518263ffffffff1660e01b815260040161034791906117b7565b60006040518083038186803b15801561035f57600080fd5b505afa158015610373573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061039c919081019061129f565b8051906020012014801561046c57508380519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a999809f886040518263ffffffff1660e01b815260040161040e91906117b7565b60006040518083038186803b15801561042657600080fd5b505afa15801561043a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061046391908101906112e0565b80519060200120145b801561053457508280519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a05b18886040518263ffffffff1660e01b81526004016104d691906117b7565b60006040518083038186803b1580156104ee57600080fd5b505afa158015610502573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061052b919081019061129f565b80519060200120145b80156105fc57508180519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166332d91c0e886040518263ffffffff1660e01b815260040161059e91906117b7565b60006040518083038186803b1580156105b657600080fd5b505afa1580156105ca573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506105f391908101906112e0565b80519060200120145b80156106b55750801515600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ee7ee0fd886040518263ffffffff1660e01b815260040161066191906117b7565b60206040518083038186803b15801561067957600080fd5b505afa15801561068d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106b19190810190611276565b1515145b156106f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106ec90611960565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663eab7ad928787878787876040518763ffffffff1660e01b815260040161075a969594939291906117ed565b600060405180830381600087803b15801561077457600080fd5b505af1158015610788573d6000803e3d6000fd5b505050508573ffffffffffffffffffffffffffffffffffffffff167fc9e49a024d50440c73d2d12d0ae05064094dca76b46dc95e99ea6848eb8f27e960405160405180910390a2505050505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84336040518263ffffffff1660e01b815260040161083491906117d2565b60006040518083038186803b15801561084c57600080fd5b505afa158015610860573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610889919081019061129f565b5114156108cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108c2906119a0565b60405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff1663b71da0d1336040518263ffffffff1660e01b815260040161090491906117d2565b60206040518083038186803b15801561091c57600080fd5b505afa158015610930573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109549190810190611276565b15610994576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161098b90611900565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663954029c9336040518263ffffffff1660e01b81526004016109ef91906117d2565b600060405180830381600087803b158015610a0957600080fd5b505af1158015610a1d573d6000803e3d6000fd5b50505050565b610a2b610dc9565b610a33610dc9565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631bab58f5846040518263ffffffff1660e01b8152600401610a8e91906117b7565b60006040518083038186803b158015610aa657600080fd5b505afa158015610aba573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610ae39190810190611321565b905080915050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b71da0d1836040518263ffffffff1660e01b8152600401610bcb91906117b7565b60206040518083038186803b158015610be357600080fd5b505afa158015610bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c1b9190810190611276565b9050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610c50610b17565b610c8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c8690611940565b60405180910390fd5b610c9881610c9b565b50565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610d0b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d0290611920565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b600081359050610e1781611c24565b92915050565b600081359050610e2c81611c3b565b92915050565b600081519050610e4181611c3b565b92915050565b600082601f830112610e5857600080fd5b8151610e6b610e6682611a0f565b6119e2565b91508082526020830160208301858383011115610e8757600080fd5b610e92838284611be0565b50505092915050565b600082601f830112610eac57600080fd5b8135610ebf610eba82611a3b565b6119e2565b91508082526020830160208301858383011115610edb57600080fd5b610ee6838284611bd1565b50505092915050565b600082601f830112610f0057600080fd5b8151610f13610f0e82611a3b565b6119e2565b91508082526020830160208301858383011115610f2f57600080fd5b610f3a838284611be0565b50505092915050565b600082601f830112610f5457600080fd5b8151610f67610f6282611a67565b6119e2565b91508082526020830160208301858383011115610f8357600080fd5b610f8e838284611be0565b50505092915050565b600082601f830112610fa857600080fd5b8135610fbb610fb682611a93565b6119e2565b91508082526020830160208301858383011115610fd757600080fd5b610fe2838284611bd1565b50505092915050565b600082601f830112610ffc57600080fd5b815161100f61100a82611a93565b6119e2565b9150808252602083016020830185838301111561102b57600080fd5b611036838284611be0565b50505092915050565b600060e0828403121561105157600080fd5b61105b60e06119e2565b9050600082015167ffffffffffffffff81111561107757600080fd5b61108384828501610e47565b600083015250602082015167ffffffffffffffff8111156110a357600080fd5b6110af84828501610f43565b602083015250604082015167ffffffffffffffff8111156110cf57600080fd5b6110db84828501610e47565b604083015250606082015167ffffffffffffffff8111156110fb57600080fd5b61110784828501610f43565b606083015250608061111b84828501610e32565b60808301525060a061112f8482850161114f565b60a08301525060c06111438482850161114f565b60c08301525092915050565b60008151905061115e81611c52565b92915050565b60006020828403121561117657600080fd5b600061118484828501610e08565b91505092915050565b60008060008060008060c087890312156111a657600080fd5b60006111b489828a01610e08565b965050602087013567ffffffffffffffff8111156111d157600080fd5b6111dd89828a01610e9b565b955050604087013567ffffffffffffffff8111156111fa57600080fd5b61120689828a01610f97565b945050606087013567ffffffffffffffff81111561122357600080fd5b61122f89828a01610e9b565b935050608087013567ffffffffffffffff81111561124c57600080fd5b61125889828a01610f97565b92505060a061126989828a01610e1d565b9150509295509295509295565b60006020828403121561128857600080fd5b600061129684828501610e32565b91505092915050565b6000602082840312156112b157600080fd5b600082015167ffffffffffffffff8111156112cb57600080fd5b6112d784828501610eef565b91505092915050565b6000602082840312156112f257600080fd5b600082015167ffffffffffffffff81111561130c57600080fd5b61131884828501610feb565b91505092915050565b60006020828403121561133357600080fd5b600082015167ffffffffffffffff81111561134d57600080fd5b6113598482850161103f565b91505092915050565b61136b81611b77565b82525050565b61137a81611b2f565b82525050565b61138981611b41565b82525050565b61139881611b41565b82525050565b60006113a982611aca565b6113b38185611afc565b93506113c3818560208601611be0565b6113cc81611c13565b840191505092915050565b60006113e282611abf565b6113ec8185611aeb565b93506113fc818560208601611be0565b61140581611c13565b840191505092915050565b61141981611b89565b82525050565b600061142a82611ae0565b6114348185611b1e565b9350611444818560208601611be0565b61144d81611c13565b840191505092915050565b600061146382611ad5565b61146d8185611b0d565b935061147d818560208601611be0565b61148681611c13565b840191505092915050565b600061149e602083611b1e565b91507f436861696e537065635368612073686f756c64206e6f7420626520656d7074796000830152602082019050919050565b60006114de602083611b1e565b91507f436861696e5370656355726c2073686f756c64206e6f7420626520656d7074796000830152602082019050919050565b600061151e601d83611b1e565b91507f446f636b65725368612073686f756c64206e6f7420626520656d7074790000006000830152602082019050919050565b600061155e601883611b1e565b91507f4572726f723a20416c726561647920436f6e6669726d656400000000000000006000830152602082019050919050565b600061159e601f83611b1e565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b60006115de601383611b1e565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600061161e602583611b1e565b91507f4572726f723a204e6f206368616e67657320696e20746865207061737365642060008301527f53746174650000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611684601e83611b1e565b91507f446f636b65724e616d652073686f756c64206e6f7420626520656d70747900006000830152602082019050919050565b60006116c4601f83611b1e565b91507f4572726f723a20596f7520617265206e6f7420612076616c696461746f7221006000830152602082019050919050565b600060e083016000830151848203600086015261171482826113d7565b9150506020830151848203602086015261172e8282611458565b9150506040830151848203604086015261174882826113d7565b915050606083015184820360608601526117628282611458565b91505060808301516117776080860182611380565b5060a083015161178a60a08601826117a8565b5060c083015161179d60c08601826117a8565b508091505092915050565b6117b181611b6d565b82525050565b60006020820190506117cc6000830184611371565b92915050565b60006020820190506117e76000830184611362565b92915050565b600060c0820190506118026000830189611371565b8181036020830152611814818861139e565b90508181036040830152611828818761141f565b9050818103606083015261183c818661139e565b90508181036080830152611850818561141f565b905061185f60a083018461138f565b979650505050505050565b600060208201905061187f600083018461138f565b92915050565b600060208201905061189a6000830184611410565b92915050565b600060208201905081810360008301526118b981611491565b9050919050565b600060208201905081810360008301526118d9816114d1565b9050919050565b600060208201905081810360008301526118f981611511565b9050919050565b6000602082019050818103600083015261191981611551565b9050919050565b6000602082019050818103600083015261193981611591565b9050919050565b60006020820190508181036000830152611959816115d1565b9050919050565b6000602082019050818103600083015261197981611611565b9050919050565b6000602082019050818103600083015261199981611677565b9050919050565b600060208201905081810360008301526119b9816116b7565b9050919050565b600060208201905081810360008301526119da81846116f7565b905092915050565b6000604051905081810181811067ffffffffffffffff82111715611a0557600080fd5b8060405250919050565b600067ffffffffffffffff821115611a2657600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611a5257600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611a7e57600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611aaa57600080fd5b601f19601f8301169050602081019050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b6000611b3a82611b4d565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000611b8282611bad565b9050919050565b6000611b9482611b9b565b9050919050565b6000611ba682611b4d565b9050919050565b6000611bb882611bbf565b9050919050565b6000611bca82611b4d565b9050919050565b82818337600083830152505050565b60005b83811015611bfe578082015181840152602081019050611be3565b83811115611c0d576000848401525b50505050565b6000601f19601f8301169050919050565b611c2d81611b2f565b8114611c3857600080fd5b50565b611c4481611b41565b8114611c4f57600080fd5b50565b611c5b81611b6d565b8114611c6657600080fd5b5056fea265627a7a7230582070f7a284b1ab092d7cf8a14ca7bf4b7a68d6e2cb8441f82436e624a898b0ebd46c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000080000000000000000000000001204700000000000000000000000000000000005" - } - }, - "nodes": [ - "enode://cc67008e850c4b64702f1664f18704dd23980fb574138588904270739dca3a9c417ac3f97077b0f4e4872a5db109195f7137c156314dbcc724eabd809044553e@ewc-bootnode-02.energyweb.org:30303", - "enode://34f97743b9c07fac84c2db921337f006b5932e14c29df496951bdcfe3fce71e3d1504c25d66156dae01065623849b4fe940168c7e3f21c49b538af0cabb5805f@ewc-bootnode-03.energyweb.org:30303", - "enode://80e49825bb6bdfbf69576fd4a5f7bb173db98c08439bd7a019e2d42548afbb1f70232b74ede42dbd99cc0de3990a0101d49543c1b299027d2fd6f5a2c81eff75@ewc-bootnode-05.energyweb.org:30303", - "enode://1cd00f54ddaa41793d32500f0cb123d43e95f5ed18220c367d5a4db7d111e5fb59f250c397e40977562881875827f022d88a51e51171daaebf916beaa60e2139@ewc-bootnode-06.energyweb.org:30303", - "enode://3a8588bd883ea3bce606b08e7ca71d55717077cb3d25d3c00421ac371fbae286b94f58ba2e739b17b64c4d0d3e19f1b0ae2e47962703b42d7f36f1b9a448d4ea@ewc-bootnode-09.energyweb.org:30303", - "enode://69749dbe7e6f5fceaeb0a16f60353b3ec04825a12400e6d581fe980275e96fecb464276e61d79fc1628e448f1f74da2985d778ea54e0e8e7c64d980f2b3b6a94@ewc-bootnode-10.energyweb.org:30303" - ] + "name": "EnergyWebChain", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "5", + "validators": { + "contract": "0x1204700000000000000000000000000000000000" + }, + "maximumUncleCountTransition": "0", + "maximumUncleCount": "0", + "blockRewardContractAddress": "0x1204700000000000000000000000000000000002", + "blockRewardContractTransition": "0" + } + } + }, + "params": { + "networkID": "0xF6", + "maximumExtraDataSize": "0x20", + "gasLimitBoundDivisor": "0x400", + "minGasLimit": "0x1388", + "maxCodeSize": "0x6000", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "registrar": "0x1204700000000000000000000000000000000006" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x5B8D80" + }, + "hardcodedSync": { + "header": "f90231a0ef1d354e1a2c136ff5d3063dd3d116708f56b600ad6f5bbdcb27cb05a30aff9ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479417d9c712b71987c084ac11046aceeace393c3cefa0e3252978f6779c72c03d5006ca641b4b781dd02ff35f4bb0f58c4c89b8b7f421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090fffffffffffffffffffffffffffffffe8316f801837a120080845d7b7c1e8c456c656374726f66727569748412b24c06b841619fbfe1b9b43d13e3d972467bbf92ae37df2ec5daada52a82011bac745999d67ed67c1aecceb5e384845bd870b677a69e2a19dfcbbcc1cf1dadb09c63fa536a01", + "totalDifficulty": "512220581561117171220611992449499484793650169", + "CHTs": [ + "0x714aee6de838f6c9152fd3d5cbd18314c29a241a894c1cd1fcc9d15972c7f8e6", + "0x5c6086d6f5cf679f7374c55f6d4b6a876ec40452cb92fb97b048f7c5a759491c", + "0x2cfa5a82758480ead7f60042b398b6077c1b956e5f5ec50ccd86e50c0bee5173", + "0xa5cb0364140cb3159e84d816e1accd65e309f5cf1b86eb83d2a10edc99ab1715", + "0xcc2abea1aee03a7eb8daa84c274d46aa8e6f72f1bb5cc383f4c5f67fd80b6312", + "0xc8d217b113596af546111f4c247b4b3ccd4d84658ddbb98e9b5300f4aaa4a2ab", + "0xe3f6fd83773ba536ea882ad4e6d833b3d23fc5cc6a8dcff2f97aff5a7f27bf6c", + "0x13b4237edaaa376a23abe8b34de7e32216cc588c48059373b1ccc04cc4ef01b5", + "0x28314339da657ea6639c54e0b1c6e351bdcb99093f216205b1b0097d10345749", + "0xd1ca7cd047ccda72f7043f0c60e1c99d4e85feb7a0a492228b1d996e1eef5994", + "0x189a968994620bfa76674de1dfecfcb39c693939b2a30e0b8449980f7beb4a20", + "0xfa9fea734ef0be6e6633e0fd275bf63245000ad43d5869115e480c6a047918a1", + "0x691275f9193dcd3c22e381b4ec23d2faff1ed6bc25afe2347734bbaea89d9753", + "0x13bda92eb5954ca11986fdd513164a43f3c9c8752dc7bcceadf9d935ad459f39", + "0x52083bade8be4b10625c036d4588ce6b3e59bd27da5d663e53b5c5113579d48d", + "0xcf689491a7e4126c5377bc7fa6283df1aadaf99ca82687585404352f4f09bd40", + "0x3703c8b32f15f6fb2d48a66a297ab6bedabc7f1547c646cd3d6929f3d03619da", + "0xef071a6b1ced8b22869dd8ea825889ec500ef7084dfae02ac2c30b0743a25a01", + "0x7d4437b4ad2a1ddcdd1dfb0e1bf3dd013ffbb9e30bcaa1cd08dea9b906c4b6cb", + "0xa64d95e73467548da0b239f12dbeaafcc6cca33503f376d3da08d69b0c4dbee7", + "0x1d0163fe3a0449f62ac45d0b9f06a220fd24df7fff0762784550783172b53f41", + "0xe3f3f82f8409c1ef478881ed0806fa46cbf2097faec716c4aab5a979a7b086c5", + "0xfe21c6d36ab71c121db0852c014714f1478f81c6d03d326a6f35740682259499", + "0x4f907f52d437c5823f20a5a78ecfb5d7d8a67660d963430bc990bf08f666930c", + "0x40d3ce300f6cd0dec0a300e4f2e8613ead69a3f434874612ec01517fa07850c2", + "0x0b52bd9dbffe0e5e7b82120e5b157639328678999cc5fbe0d8df1ee37628be1c", + "0x6f20f4a01fe4461311635a6aecade16769e5183cb7d0c7d1e7920e66d30b8b55", + "0xad06ff74d2d496dbfeb4cf5ef2182c4210b9959677758c3bbaafbb4d77bc2294", + "0x1cf52c489ab70a7a9349a30bb839e62fa27580fe5ed2138e723e1e19a276f138", + "0xa7fa1fdfb4f1c0c3f257756b3fce92631337bf6d728e9f1d8b0c664166f998f1", + "0xca15b546d7e8e4e54aa5ff7ab576fb21b9da5f9495049823f6cd16b174014d10", + "0xbec49f305f6cfef5c4192006d3211fc03801604cd17ed08f1557717cf8eaf082", + "0x12d0863ea2ba7c7f152b494966276252748daf51c78d8e9afb8b1eb242e569f4", + "0x7a5bfe9860427cd13209e973bedc59d6b26f2b26aa51f048580eab80570bb5da", + "0x89153f7a218d56a5aa7c7eb5d603692f1ae7d33ba4ca71811a9ad22e0cb9a961", + "0x3b817286805f105dfbcaa6d6116f55151885632aa5a1e563864a6a752e4ba271", + "0xb7e6558399a7abd11a53a1b59aff4b38f2f72ffc341a9835ac504ccbc9974b8a", + "0x002fa5217510abb7d1c7665c82536595cdeae9065aaa20b3a8ed96e27f84cc62", + "0x7d212d4588f4cc28f0ce414294fb4454ab61ad264e988d0d6c75ecc2d5124216", + "0x4a87e03523d9d8865c6c998a83a46c58a5347343aed9522f8324cdd1bfc087e7", + "0xc1ad37c002703037d78cc742903951bfbcecd955f31d310317f62568655c1f49", + "0xb9369a5db6984bb236495de201a260b34292344cfe189d6f0352c92a2b34e373", + "0xcad20cd17dd6b0569d49813592308a7426d7b38d3e6c68030b63ecf323a9733c", + "0x29dfa65912ab6c45a61002df9b6c1d5efb19420e1c751c915140e50b0571955b", + "0x54d328d8abba4b4235943658648fffb71bbd1f709b869f703981a6daec9d1745", + "0x78e10bd72223d3138b668c209ccf3de9bb3104f983b5f8ded0c077543be4e2eb", + "0x71db915f9eb5e303d092bc04bbdcd7a92fa18aaef0087e057f7cb74ae0c42be6", + "0x5b5fbcb2a155b97fb1c172b9cd8e44230c45ace454d48714aae771b33d1a70f7", + "0xbed5561d7dc0d7536d9919c249e9c7311ef56cf1af46cc4e5ce0d1d94719e13e", + "0x53bbdeb4f2c27a9582ad5121dc96279ea70b4b04a090819d23c651673eea4dfa", + "0x269f7e304c9ef03f70301dd5a62bc4f932e4e7c4d2cf3bff22745a6e00ba6da9", + "0xc581e1f328a0db1245f491f590e80fb7cc6ed8516d505a91b9d0179c3a318963", + "0x75a4d02143c90eb0b4f99c555a4663487b569f7400a0baccf102885812e11de7", + "0x41509e2b972ba8e919e2e36deaaf88d7102f3afc57999a03d2b2019dfff8fea7", + "0x98cf6ad904dbb9d3739ee3fd5dfc8b3809697763f7e9a9ef07f770bf4087b6e9", + "0x0086e9b6d28908553abf7c69d92679ec513d9a2e6b05d7e58e2a73c7c950ce54", + "0xac8d8b99c4def8e8bd19d0103fde949e2d0d3ab1168015c6b51635830185dc72", + "0x1deccdf3e7a2de364456cf4ca26d66af4d053353d2cbcecef2901e8e64eb1107", + "0xa88c69866b2b2ff58c80c7f4858ec89a94d57ae0b2f034fcc33ff926b24ce119", + "0xf6ba8e87bccc4c1ea1ed826aa19b2f9580e6a4b057259287a2115ff5e23789a1", + "0xcb5dadc2403df2e173f0edec17a68926bbb5d738c27312fdd0f2a16a7a76849e", + "0x8a8724ef233d58277d04030f53923cb993984757cbad84a636b147a662a821cb", + "0xc7b8b84d234651d6a1850e06b2ddbe7ce270216d82f399cf48ec9897d036de0a", + "0xbc1ad6afbd752fa72ae21f81c5c5210b9dda3071d010db36395ecbd04590b4b0", + "0xdd386b46fd7030c485996644eb4b40adc61c666d9cde21df40d553a9595e0984", + "0x3434bfe056b388286e5b68b8b7b24d9d4f2873355c344c45e2104568ff55fd08", + "0x9e3d11b88f6e5d76bbef918e1d2532a475d2e90a9234cda64518eb3fece2a68d", + "0xdbe894b91d6a3cab4418b6bae4f266d7978ca9b12796c5ca664720ef2b9f5d45", + "0x0bff7caa357943200d7b5e02a208cc6277c183f307d612c7dee1886988b247e7", + "0xa94e5bcefd87dac0cc2bae19afab04b509178165de79aed98562ae88aa990ab4", + "0x2548c84be89eb2f05ee222fe52f26f2eff73d1a1d57b90ad23584bfdfc523d6c", + "0x0eac1a412389e244a843c733a948b70b72dcc231a8d9e8680b6141760d7d1fbf", + "0x1a6378c731f116e421eeec34afc3dee7b42e3233f4c676aff8571fa377d6fa14", + "0xf35661f3d06ea16b8d199f4613459653a10402f2247427149b0df49112649bca", + "0xb7cf13c95711c7e68f9bd67b2a2c579f10a05e97327c67583bf9516b27c39a53", + "0x8b46a5b7df22c44ffeb097c2bd7c9a4f29430d68210acc721688cc1713415bf9", + "0xc29b90fb6e12aab1007a3d4e1bd196be97224d8e8f6540b2b1c48efe88db4f6e", + "0xc5a1feaddd996c12195c69e84ecfba76df1de87a389b40f3839a77bf26326076", + "0x6b6dff3eb189efc0a6c63efaf81fa62d97e2bf117ee0e336f11a4dd41c338b2d", + "0x39e7e6c3e6cc5c5c2f6869e0fe9125062be7176a416ca970e9d863fbce67f158", + "0x3523c2958628fc823a2ee64460b47a9f407b7838607c238141942b0c0ca466b2", + "0xbefecf8563da251300f67ee0eabc25cda56d6477ff748e10fe52f1708706f008", + "0x3995668e8c932eaf52d4cd1ba671aaf923a5ebcd3a76ecf6d8da7546a9aae5b2", + "0xce966d928535798736714d5302902ef00cf17b82c680dc9b4a3c4076e363a99e", + "0x4be8ec22b32879d8a4ce0f5cb080489179dc7f264f584e42a4c825cb4d9123e0", + "0xc74698ea33ccf4df96969499975e05ef02643a74a101138c85bf44ecea503870", + "0x797328e01ee9e34c81ec02d14bd2344659bf6fbf05c331d6ceb7501773543a06", + "0x0980feb796b32259fe1873cdac1a6a8f35d2cb3b5033a133a65ec154b0dc256a", + "0xc71e68cd250b975612d0507e4d1f93c3b5e701e1df7c5531becec7f8e7bc7ed1", + "0x420e240ec1a8cd42834bcface9583283fd104ffb20d216c804edf5b3c534fc8a", + "0xaee71494321dc715b20c984d901b14c382f1b65a4f90f3e78188a86454ea2a79", + "0x6abedab72e5762047089fbd7a97765776ffa8e5ec7e697f82729bb5f1a0a6988", + "0x1977721d0744ac77e582c462671d6149c49803dc1b9d5178e7a735ef72da8706", + "0x96254fa25f162b4d0af2d64fa6e47c886ac9cc81af6c7949c8ee4aa7e67e45a8", + "0x558a616c487edea428d39a46165c3a059c8fed9edb1d4fe53d18bdaadb3530f0", + "0x3dcfd178eeae5724999bcd99bde031d06fb630703cd62a0a4743ebb179e1d33f", + "0xdc79eb70fc635b5fc0b8c4a6fb4ce8b387aebec78df1825ef242c3387cfa7331", + "0xd879a0d98753df27fc6c9adf37ed41d2eb8fb8a9d0aa427034659c6e50a7dcd4", + "0xcb0ec7547b79371500f9f2e91b835ccacaab21c2aaf81cde48b8de8a0c62cd76", + "0xeed5a9d2b91c6c58ca0c03f5d8d80db4a96da230c45878b4b2fa609b5d250498", + "0x32b8bca91321e18e117d32e37ba9e6aa8bc1f792ecc44779b6783071044cec18", + "0xabf889edea725c99b393a482341e03b82852ac73e27258a708296a3403fbe941", + "0xc55939c05471d73be4de2c1fb3f07326b6796f98a91b7bec0e266b87a7fb2815", + "0xeb8894c24486afa4a2a31e8dad510376ddb903436263b860ac8991aa7d66d1fd", + "0xb105f9b3d63bb3aacbb1101190cf9063d624f0b0399b782d509b0e716df41933", + "0xd5b682f34ae722725d14727428a96de156262712ef6ad49563ebc12a75d0c85c", + "0x8aa5d6861854def6262e86979f5e96bcd5474d7cd0cb1272f3a099d1c03bb04c", + "0x609885f9afc728ec12b427beca31b632eebfad78f30ccd64282a4f9f1593509c", + "0xce2dd1bbdb09ae148f2b6d17636788b27ab7afa2b5b55c437255dea28977e0f2", + "0xb71191d5083196c16481eea99b8b1e8f8d59dcc68a2f7ee908efd5f4c57b9e41", + "0x7239d211d9e97227e77883bc9c31b7762b1f811c7cca948ff8f7b5753bc7559e", + "0x7ef67af5683952e43427227c2f4fc8142c6154d60241b88bbcbcafdffda6ecaa", + "0x1ff6023ee784b370ee7a0f64b29958be3fefd8df5c1ba1cfa3e6902061bffbd8", + "0xb5cffe1c85d4a096beee99b3023c8b0d611c02d525f8fc28bae13ecdec33298d", + "0xcd30a3426ffe2a11178e66031b141599aeba0e8ee9cb4112a599b64435a03af5", + "0xce40b45db8860ce0932f2de0bd9fada52b42dee0af04522800039d345ef11a68", + "0xa8a34d51cda56c627e9b5393121ae1f6fd00167fbb29074d249f22a9569827bb", + "0xe988982bc9adfb197bb73d0658532936478a70a1cb3eb3ce887022ad6140d296", + "0x3507f47e984170b96ea9a1ca6c5aa1f7a3001b65a6ac377b432126395ded2590", + "0xcbd052896ec1093b29db69924583f980b2607b47c02629f33a87854dff2ebcb8", + "0xd8bc082d23f34a737c2ff29dbb4a00cd1e3561e6208e622ca3e2ce0e94a0d195", + "0x5d70fc0230d0f98cdd9ece68eecdd10a138e969bfc1a75d8d05a25fe36e7d9a6", + "0xc288e634c5d346c78d694a6baaf9bee375ae953ddd2f16a24e4869952b66f27a", + "0x3803c0cf512ad51eb8b51e9fe2eac0be9d41113d3fc0575b6f255f8637a3e8bc", + "0x0c6c1bdafa2b072d96ee0af733e2f52fb275fef79b25f64dd8ef40723f1a6c9a", + "0x485a57ed84df32298477c631079691d4906d36bfa881aa4a62915931a21e37e9", + "0xf6eb84d895581d563c393daaf6ea5d82f0a0a4e50b5b4e079b4d70e3ba5bdb44", + "0xe793e01ef0d8058aba5fb4a85f2e6c202a71edd641e323b6f6ba42783ce93898", + "0x652d4b932a634f32a60b27f7c59ab26b35ca296a0dbc5484e602cb134d88a3ef", + "0xf5196d9bf6965d2184a5381c9b71ae005da474806c6926e9c5e3292863e8bede", + "0x0d46341310ee13f4cc693588c0edf899aaea3ae5abda6ffb57415b6842e651f6", + "0x969d3c7dda1caedcb15d7e144511a6e5ed38614c64414ba74a7529bd69ce5cf6", + "0xff9e73f4eb9e19afec1ef71cbcd321fa6e6bfe9dc9f3fa2fce45ae2b899af998", + "0xbe4db0c9bd7fd8cbfb00b3d146ea965258b9de545004f430dc2d7526fe10a910", + "0x9e2529bfdf7052200c855686a046c3be25b93654e56fd5514311929d6bbbe52b", + "0xb900fceac0f3990db4a6aae238271732632402aa838052ac0ad223b218a82b0b", + "0xdfd7f34fbccf7151549876d96ee0a85f77cd9ae41f514ace7218ad526ed59b40", + "0x68ebfe7ed1b187ceb7382780f142084c91d2254761a1a044609e6325b8a5323d", + "0xd04cb8343833c53e4820e60b08573d6d4fb38456b3f2116e29858af483cde125", + "0x69fd9199d578dc51ab7cb55981e3748d166b447764667ea10d70e9cdf7e2ce96", + "0x25f63dec762f9dcc0f8f46357a7c2cebd5988b1a30d66ca71a2a2103d4b4e9fa", + "0x01da2a253712a9d0116dbffe089fc7377d18671b3cf6560ee90c8a1749379605", + "0xee62ef05acc1646e48b6aa2822b54e7e5421f3a22f5abbb9f067c30aea8bb192", + "0x81c8c6a098061ec62e12a87f5702ed7bdcc5eae8450765f77d1a79354b9f2c6a", + "0x95740b63373660ce0a05430bb99da911e63dbce008c4b8d3c51253416f23fcf0", + "0xf18496c657560a475910e2a0bbe4be5df0ac56b25cb04d5b98e028175f49a6a8", + "0xaecd72638133e3f05497c3d8d505dff33a6b6f6d309af35a67c6ac10fe15cea8", + "0xed73a92a9518e5a5e583a750b5d6422c5acf082d9aa671bdf9e4a060fd95c72a", + "0x7f135f642bd31c393281320de0dc30975c17a21c51c4e30d7c42fc3fad5242de", + "0x0a788be501d80914aa2fd995cead69c17fe04bcaf5db86da2bc4c868513a99b9", + "0x538e66a64ff22af3d7b6db368a706e9d981df8a4edb2dfb705e72323aff7e5f3", + "0x50c90a4c8affb981ebb9e88d695050a2b558e340d333822f7373b66cad53b973", + "0xa090d0ee33cd0d8428132b6d87005dd292b7e28be87f553b4e52566f976e6824", + "0xc13b178eeaf966dbbb3d717a163ea003fe99479903e8cb78b53d1e4d240feac4", + "0x9ba9806d4a0b20cdd8b69d4d0197bbe7a42c1cb46a88bd012755fbbd785be857", + "0xb836710b211df481d99b033a9c0d6e09b34fad6f2e8e4931dd1a054dc03bad57", + "0x4792e8b038b6dbbf32a228c292a5f4fb639a0f84f8f70004716c5a47331f4c41", + "0xcafa97e065f36a436f0e4e255aef9d04b57c6b12d50e42e99b4f25d148e9d3df", + "0xe9e2934d6f8841d486db3533ec6421fa0952ba8f3f9020ff9b5e4fbbd8af817d", + "0xe096be0f831214ce1007d352236d2df186cf738b9b3992c0d0943f1b6a76e917", + "0x564e83fff721a1e316572dc507cfbb41ff8dc306436ab26b315d23b8f002e390", + "0xcf3a00d76c6a4645ae555a96e0bb6f0e441baebea9415db5304d6d8639c03bc9", + "0x303013d9ca65b0473921f83a18e5cf38f882e715022554066b72cbc3368056e3", + "0x8e47fb7ff881e845958ae6dea28ed9bc3827c7b296ca1c151b7f24b72174bbeb", + "0xa87d1ea0d2853c966db0073ff09c85a1ff28117f770239b11369eab1e1f31454", + "0xa7d484e51d3c4ca27993f21d5a488ba34aac2ebbd6005ba0ed8f125bc9c9139b", + "0xe2811f00e0d3f86193e59c100d7a12ec43f94c6c2228f3b95c5838244abc6406", + "0x5e5e04bbf350160da90977ddd5620afb3793f57c1b81306920622ccf400d6e9b", + "0xd8c7693ca7de07e9a09e770f93775a7fc3acc3db6ef33e0d84c38ef6d2ca8108", + "0xfdc05ae6bf5d112dce90ca725ba52d85636c4c193a7a65d9b6f6efb7ef6a16e5", + "0x07abd5fd4ffd9fe50a59f0f32bebf2d2d6ec59476efc152b3bb907ae10632ba8", + "0xb6afb22be6200ee06100434548ea3b0b378b00f5282c377f24b04c3d014fb552", + "0x199d2c213ee2277dcdf24af1da8f6340dce3b2b9ec800f1a31a2d53e7429ba08", + "0xa0c0c4e4398e7d8b7e4cca3fa16384c4d911e24db2b3f1702d9a23fa7bd67fc6", + "0x953df624be719579e3f63754b521ff66a5d92c809e3f809e45b0ddd4f1d00227", + "0x89e863130cf4a03ded7581c5879fe3f4ac10953b042c4327e2683941ce234f1d", + "0xbcd8e60a0e0b747b15f43b1b3172395d73be4514bab8c995dc61b5853507f45a", + "0x8efe4df87171eafc2f5516f9bd9c6dec560fd86184f436a3fa9367947ba6acc7", + "0xe78b429f1cb75f18c164d32a394e7490d6ef490f7ec26767506e1531971a2d8e", + "0xa85bdc71975dc7a6f91ff1c71926adced0293139f42d3dfae448b04e194afa39", + "0xb2850db8790941c5b4728e90c60c5761f309ae78a42b7a32f7b6b414417130fa", + "0x45a1698db7c1793b66031dc6b9bdce75e9db03cc6d5068c98a1d39e5c3353959", + "0xda78cb4f7dc73eb3da39b7841373f858e7a7f8983637d3d41337f07e669d5611", + "0x4e7b03eaf17a665906340bfc5d3765c1981b3ad174af2bb936e688520cc7507e", + "0x9d1fc1af5a264d076cc49b46d88857c733929ed63d3a9d098f9d02445ce137c8", + "0xa58779d88ff3e4f15f9cc8f3aa4e31f29010259bfebb358772a032314248b9a1", + "0x85eb21a68a7c544090dde6166e19f18ad527af568cdd662c84098f3e97b6f406", + "0xb95a7f4891bb90cf61b5cdc8aaf30440f901bd68caeae2a3246223c1f145cf4d", + "0xd26f9c16d45ae57ef27e31bf107baeca391a51a0c2ce5634e53115e45e8e7a3e", + "0x34692e04bf78c426ca367759a213bd782e47e2e6842a8f39f62a1030aca31775", + "0xd5dc5c4d619d78682034eafb73ca435db6ab53583a7692848dee27820e819f75", + "0xa23bda165dd7284398b7817fbf85d3eeba7e7330bec4adde2069e224e17dd2de", + "0x4fdc06315460f1e83fe2e92c3d60acd5d7a5bc96066fc8785409e81a1ecf9193", + "0xbf4690969f21951ae06fc3828d40fd4a965682047f0bb6f847d080ca7f2fbe05", + "0xba4f99d6bafa70a9f08fb41e0c3d7a2518a360dbd2ec98893443fbb256174a70", + "0x9366600b95717d1c4840a98fb3c2db95b71374294144341d280f32ec4021c8ce", + "0x8caabf9d8a063f2fb18912ae4b2d1be719975cd03b543b79863ae0396c0ae625", + "0xf0ddf8b613a1059b97141affb960bec51c331741bb19f85a920adb9b7999123a", + "0x81743e8fe0f7c4a2141dd9ab1eec8522a6139df847c9430acbd84df0ade0a9f8", + "0x00f7006ec05ff418048bc59526643321dc05936b3c549fa8136b43fed038320c", + "0x8cb3a50c86abb41376cc01cc22691a8801a67690ba23b88760c8495ebc9f855f", + "0x2279d287f8233c0cff7e31d287d417c1d4721e1c28c3c8ccbfa06610b417a0dc", + "0x6681955ad06c3bf7172c69417505f7b8eaa17fa861fe5d8e6be61d53d9774979", + "0x7130927ed3a412791f0dd02b874b68b3a393c1ac443718eac7afd4b1f5f3367c", + "0x82ddf79f4eaab03c72feec7fcb49d0532ae68dc4dcdb995a06e79efd1875a97a", + "0xe99ca08624ac8fabc9cb11f01bd2968ba705f87724ec4c867107c3e82b72c648", + "0x0349cc1960fb5f891d9c780fc78f44f4af7a2f06e409cbfae5241f754585b873", + "0xc31265d24c7bef15f05303658ab150bb8848e63ef6e5bcd6615ffc59eab4c024", + "0x6d6268a5f5e24edc4af8d01a0ffbb406c2a0df0e940bf2042704627e7e3f1eb4", + "0xefdd6251b7ebe415aec8cbd03314fe387b9fdaaf910fa6d21bd9d264877a253a", + "0x2b77e4b8daeb43198d35aa7787e956aa69ec457129113f2e6fd93d14b1d8be59", + "0x243fbbc8073d636741793c42f05d14dd27ce4ea263aeed9c1d7a1960a8215569", + "0x63db3b3f6d028b7f46af35e5036e1097b31157a24959745eabbf53e5025023fc", + "0x546800bdad232ebcd15ca4a15e74e0bad9c1f0a8488797095900273932280d0c", + "0x85ab077a505c4e0112ddffc52fca1a08d8868b740271d474ff6e5211a451c24d", + "0x8dce8e8789e21e096136fbca59e79511f63cba3d3f91fbd8dd5584a6837e4d5d", + "0x1ba5d8a59539a2dd41cdcf40af0e4e0fe41d0b840a6cb2e388efac0db838341c", + "0xd9503acc8efb33e700c4c70878b46a928e1f61de25fe6e93586cb7a34ab26c0c", + "0xf5b2ec428fea0fca37cb44237e060b472acd11205fc5825b0899235f9950ad9f", + "0x5099403d49c4be182a0838de68324dd50d7c457f84157417adcbaa0f92ddf64c", + "0x40f0ceb3ae983ef73b79b1c3e93d1a741bd2620c50f1f6ee1bdba8b1c8f076c7", + "0xbc4040b4712fa08cffb7868f0b41a7f0f8da7d591da8896f467028437979850c", + "0x39fe703d511c7b9e8527e44d2f17ae7bc01da4845a4ad877acbc14233c1e7280", + "0xada7144fba64caed4cee909d14a306925afef2d240370ec85d0d5dcfa43c18c9", + "0x3862b40aa1f8222f424a6d6fa45348f5e7d572701be890586a6b9087a977ed5e", + "0xbae5129ae5f2cc91c929dd4b716d1736b03af127e633f7a9cb57a2b045fc9f6c", + "0x2a6cad932ac68fc5687871ee322563cbcb6b3eb978581de10737165b015dfe8d", + "0x8e1e6db9c2260c262481896cf22698acab58c529195100a4332fa0aa08385d84", + "0x1f38a384237fcee079b971aa71c97f36ecfce1eeb30c9bdda4ecdeab926072ed", + "0x2b1c080df5f7725c4c69281dcc9612b142fd9532ae060f5cfba1bdce71854188", + "0x7aa78f311a85f28a7c2363bb6b5af105e33f0e5c96af83d4e34bc7ffd5bc930e", + "0xb923a1dbe5611771afa4401e13cb01b10bcc95aee1502590bd2b32f0fe7cd5e7", + "0x13d1db98b9811e2282b49c46c5b6289eda7c2d46ee89b354f878dd7b3a234f3f", + "0x945f821ffb226364cef42e509d4f2af0770ac0d00ca21de639eed0cd4876b77b", + "0xafcf8c8871f48d13cad81dfe3e180d870ca0f4795fa178a3eaac6abead58a4d3", + "0x87851815137e4f8dc33997639c17ac1b9b687ca34747d3d58f34b47ff220cad7", + "0x0bf7f94232ecb6a86e03ec4fe8273e6c6125bb7ad1433fcf217eb53b0c2e5c92", + "0x471f021954e1657ea61acd5f265eddce0dea0c5c731a3ae144d46251d15fc7d8", + "0x5b08773ce3fbb2670564d4bc49c9d2e5e5dfcf7d76f33da3aecdb3acf7a33e54", + "0x7f6f25f1cba164401bd9e59e9d976263b6ea1efffaec863316291f5725a6e705", + "0x42cbd8b2b4c7889c9d5449faedcc7249011332be9010795008cec15666aa9d03", + "0x54fea9d9610cbd23d8013bceb95e4b453b45dac5946e714141fa6675e1a79854", + "0xd110cc1e0ae11a1ba964923c32233fdd07fd5d258c81e420aa53ad2e6f637e28", + "0x1f4547eb2058d3b0beacee9e6d7200cbb5042ba0b33cf3f6522c71b83cda22ff", + "0x0e5b8aa76ed29a6d150b848bbe151c442a7de6c782fa1f5ec9d6b60155e5f1d7", + "0x665989eebcd06908b02db1b022d20047209d38f8840e88bef92eeccc9dedd08b", + "0x6a6ddd746ed465282651edaa1701999ea9b5b2db6c37b37afe0b1f91a03bae3d", + "0x9cde4996bc407146502c950727300683474176dab6751f981b975f20d6a31c10", + "0x07eaf4e65e9961e5b2072e2abaaa3dbe802c1cdc48b9dfedd52843829a54cbc8", + "0xe5293358f5c362425bb44d83e79d2d990dfeb7a6495b43a43ce3bff018f74ada", + "0xf1b1852a310ac269d78f2eaade9aca6717ca19b2c48bbf9c8077358333cfc463", + "0xb57181c61f5a692ee46c19f64c3396bdf448a6b0ab1463f59cf68698bd10bba5", + "0x83fac07398c500fa021f6b79e55279bf49c8c1e0e96f79986e32589d2a7adaeb", + "0xed88db70b42f76c1452e9b410e2584059cb8fcbc95b4480135333ed6c4916b41", + "0x5b296fb879b3fae9103a3e91ed2f7fb74de9e9c5070675354a532ee6edb119bf", + "0x62f5be2297ab556625e80fd9b03291d8fd83e74af992e3a07d0961b185b5b880", + "0x5e108d7fbc6d767c7e27b72cbe10b642ffc9ed80c2e5b9ca219eaef68f021325", + "0x61e77b16f530de4308d2060f35899a9e95ffd73d8e524ef43b80460a3d8ed95d", + "0x86cd27368a871f4c06eaee0e2a141b78c6dc00a5531a3495a12a421546bc034a", + "0xdae627010cb7e75f0b6cbf1fba4f3cf15e59697ce2a865ba8b69cb64405cbe87", + "0xb8673ef29f76daeb7fdba5b75908c6ed83fd96803b20879c449d1fd9af41b070", + "0x08791b77ff600cac68326d00d94933cc853b69359a40fe4e4306fa50f3db64b2", + "0x6111f5356a918b94fa8dfde7fdd1bbae9ce941ea729b15d9ccd86274556767a0", + "0x4b5f974e1b95e0e2c87dbf21c138bd4beb25deb362350a5085712a931811a8cf", + "0xc72c316113911b282b801f7ddb6e870f8e5edf711bdce06382b1430248732875", + "0xf70ea2f05532bed9884c81a1a03855029f22b5066cd97b20cbfbff7b9b68b188", + "0x9d8a61ab53799cc292bff6879a85912b54bd78614a73c2cd0fdfdcccd2c208d9", + "0x56511866e3e3689d00d21b34c7ea471fd1096e8f4b2fb5fb0a6f15095d956c7e", + "0x4e28cfcbf428bbad414a60324ed466352f5d5b41b044d8df1a46e124cfdf4a3a", + "0x6591fd1a46890b0de4c927ca07ea27bb4a120a3637c7cb1a70a4c1ea6f42c7ed", + "0x2e009aa82f01adec2287cc28f22d6368c26d28173d42a5c526e6775f7eb33928", + "0xc7363139b3475fa4a0f6b0dbb86f70660486067ae23eb3406245fb445e7981d1", + "0x6176bef2af27b05ccc2136bab019e9ac25e8b8fd827fc4f19c14741d63d5e76c", + "0xa7c5120a9ddd815b52809d4ec16b73775133413751588f10e707bad274f4cf31", + "0x5eab0a1f651856d2c35ecc6f6b33ad285296021f33991f92e91d018eefa70ad0", + "0xda2c82132749852c24f2857a75da70a12472d442b6cb7e5ac551ef1646514301", + "0x0d49f6c38c25987be6b219ee2d06092790555449190ed95d0f362a0a633f0450", + "0x1099bdca056b138051f5fa67352fc972f9fb12f9544f1ab2250c46d8f8597c5b", + "0x51509e7be3765fc768ec8d53030e6fb23047ae7047819215a6ea5b79f3c96122", + "0x7c9e5291c0d56c49bbd1ef19057b196b044cb1d05ccb70fc287793750c2a82ee", + "0x41397b9513e3a4196d6bb00af809065ad5803e4a3384251b8b733ac820b84241", + "0xed905526fe31e9fc9879b873afc18c0175c315f282ccac73c1e6d13e0daa0c16", + "0x075da3d126c26dcf1f929b3f53561828a6a09f7386e3b169e402d28bd8a0cc2d", + "0x67d54555df755a4417831e623e3d55eee0481eb5e8984faccc45aa0c1255f2b4", + "0xc6b00466e7b14951514d52f62f56b47c016a76c3e4173a47a933d69d12a7188a", + "0x434f9b5e8250b953ef7747d1cb7092f8a911f2ed627095515899a5342b83e767", + "0xb99273603a1c4b2ea6aa1efd51e66dfece99982f11812f6b6cacb028f43f9590", + "0x7a8d8d26cef67c294acc07588626d87969544475f26e771190a278efa59ccd82", + "0x48a7f38cffc5ab62ec2ea284ad9f42f10b0c63ed5561818afad444942bca0895", + "0xbb3c61686b7572906fb600991a401ccfa2b72355412716eabf1f6c3a3f806bac", + "0x69ee9f4b988e650981f7067a22301df3c448c34afc28433b00e27d006b1c712a", + "0x9120d910befcd77d6b104c703f7e3569434eb53ae773dc85b40f0e441cc1f636", + "0xa22dccc8cf91a63b9b331e1f7c9b25c0169300ba4306f9de50e2b374fac789a5", + "0x2c08520e550b1a695b567df1e1f9436ceee1b9c7d09df2c8d84232a1907e40ba", + "0xccc561b243b2cc3f1d79356ca709afbe95982e25478fe9046736b092ac99a21f", + "0x60a293cf021470cb053f5e27e9ffe43b4819755c3d0ddf418a556c033abdc4ca", + "0x7f8a97b56a6b66cf229c8a107f41cdbabc7d99f86f3aa175560eeaa13e062768", + "0xc6cbc4fe07defb00e5498a9c3e45909325ec1d5abe5983cb2f1d1887bf20960d", + "0x70425ee97b08771476a8136e35ac1bce6fde67a96c242d480d17e5a4a90e92bf", + "0xebe932630aca0dc961cb442d9746ad098c721eabe197960b847e867a212baa8a", + "0xb0eb66bfc56500b13bb50ff9c39eca1c00539115cc19e1cd4f5fea8203ace35d", + "0xb85cffbd649d8e197230ba7372b2c59be0380740b06d75052e45021c7512345e", + "0xd1c7606ba6e7beb2bb0aed775e4a40bcff418178ed4e2ae9b9281ea5d4c89a99", + "0x250454e85ab809adaa6eb26c064dd560f0fd55da9a5057043724cd937aa0a442", + "0x4f73342c8a55a77c3b67402d5c58e77289e432d04c4ab0e94afbb96bab22262b", + "0xa71e103396dc63a998fbf2712980341b7da6a47df39e68130ea8abcbf0d997da", + "0xbc3dd28fc3a3ddd169cd0d1d058e9d43cf03c258e89fd1ce2ba66a6b18c86591", + "0xe50edb767f9492393223ab088b4583771b5995187ce146e205287e06f664d30a", + "0x55892af1bb7d980edd5c22930c2a9cace26e74b09b0bf393848592e57a290ff2", + "0xe62e476351a206e25f067f4d80a5b528a228f5dea6c103dd4067339699f10743", + "0xc9125964a34760943e51a561cc5e104be4755628995603c989d638c57dedca28", + "0x7ee5d6bc020739ecb3a405c9e684ae902d6e266492a0e33ac1b42d80452dc3e9", + "0x38030f326e2607f193c1cf8d9c9780b3867b10e04f8057ea682fb28410bd32df", + "0x9d47941c403ceb4209fb51ce3c1ad8b89819f2bacfb9990647f8568984a75b06", + "0x24497cb73e87b012a7363218d1a583b3e081739a302bc040e1c9907656f6c2a1", + "0x5e3e695530ea10576581ab7213466f284d2e377f607e7c59553611fa625c0800", + "0x9534cf9516382d223daa23fc333085704aec15e4b62a6d9fda17c481dfbb2c18", + "0xfc04952efe456ebd951406046fd35c237d6873d98025cdaca976db02ebbeec2a", + "0xec7c47cf69040ba41f49b76eeb79bac1676e206e85ca9b006d873da582312932", + "0x2a58960a118d1a76e34f46d7e6eea90e288ec959ef16e675606c37167c64d927", + "0xfd21855fe75953305a7c8e19cd2d18dc31468609383d8fef3c611d1dca70cc5a", + "0x7c36b9b06490dba54e9c3d8116dd37200bf67e2d3b99e7b5f4b1badbf65f451d", + "0x58b631c11b2e4a81b8648dbc1c72144c8a15ab3b1f906c4b5533ac1e940cb0b0", + "0xce69899317ef20e930429d323827224372cfc7e29379c60cd24c722450951d02", + "0x0ae86d2b84581cd8d9592b296a60a60f7cd394719f4fea13ee9ddaadacd0f9ed", + "0x6c6a0784c5a2d0c3c0506f69e055a02a1febf3f2dded61c2f26ca7740a6fd9db", + "0x3bbe5e8a57741dc74eabc1a231f4722e63c4578c694a5d52ef1f024e1b344bf7", + "0xfab03c10df60220e5fd1d6c3918606bfb90d4bc498af48b747fa217aac63a2eb", + "0xf490a009f14a88780da39859b79aa0c7f8655fbb3261b422f7b8a3ce21aaa14c", + "0x2153ae46fd41c837373748a60d5452f1c4cb0585f2eb6f1f329566b2000c6679", + "0xc91e6a8999eb99fa7e86d4b661e2eb15c306af8a04a14090e915cd1821520142", + "0x1b1d4da071b219b824052198c0cd5685b59a3b9bcdedcda978e28f14579532e2", + "0x5276badc31ceed3b3e596a7595024995e51d091e4fa58f6c405a45e9aed75f91", + "0xef571de5046724ac3584a10243910d2d25263dc3667b5bccccdbad5021e4c502", + "0x17e31638e5e973e28baa4799a920a9ed5921326cc7da05ffbcfc10b2fde376cf", + "0x935aa2b075335f41085f33dcda503a0437740d929c3824afc4cde5f1824b77e3", + "0x43ba24acef98bc089dae5b11869b9be3c7bcf0946ece14d5e196f1f36cb7cc62", + "0x18fc58f6b87d7fcb01a9318df66d7e986b77d7a9d46905767a69026a3d09c550", + "0x91e5d60399482b861a8ed1d9141cdfa7d11445d60ac1a21c28038be23772ac13", + "0x3d9b40dadf84ba03636d18737ce3b577b9e9675f595561938b0fe8d07b42b52e", + "0x145b9b2e5f21b588b0496b467cc2dcf8add6ad8fbfb7a9c9f3e2a04caffa3d7d", + "0xb4a2d0a642e659ce1c3b7b5d215865c08e50c7b33f0a6244329e5a2e53eabc37", + "0x492509b785ce0bf040b59ba9b50f57757090293d840b78e48014e64f92fc4a91", + "0x533ba967170734cca134585f7acfc48f5dd8457c2c051513555490a2cca5f5a1", + "0xd344b4934df76d3fcbeb0343a5c6e61f660a6e3469adcca9906828623faa9c9c", + "0x63a3f3e7a006e04647234ef43b40d33e03da66179c9165a40cc0e91b6fe29ca0", + "0x4be159b2914753375587a3edf94a90964fdd9e89c1592998acb1123d751d0a70", + "0x0f6d760899b6b4d7db6fab9aa3186361a03680c92de8f9f95bcff1f82c14d5e1", + "0x856725ad7e808334d52a234a0b13b9746c6a0219b114cbcd15d3542241340608", + "0xa60eff01b179e0895e2eed8a388f3393ae564a166d1be4dfa4b9bab253fdd008", + "0xa8ea401e7ead4eb084eecedb91d5e5ee547536bc15cb9b801b25f8635216ae90", + "0xcc77a0d7cc348b9bb61935397ae38dd6d6dee596e0f83ee95969128a12350b89", + "0x15d7e3e400ebd2828a6ccc97ed2c069c08458ccad8f520c8bb78fa400eda6a1f", + "0x477b93b3f6677a548b985fc1d00d8c72afe7bc48246865fc3ce5bcbe5502246b", + "0x691516a44309895798db0c5d940f9dc43ea548a3acc986f1bed8fc2d203923a2", + "0xe2a60192e1327bfc29f48179752441413c0bcc401d3faec66ff8acb69f1b7dc0", + "0x788c0d110678f201199f5789083d78e0fadb0b93afd2e08e670d1de09d0c49d3", + "0xb93078ba88b186b277f3174ad85499383ff9fd56db0e4d95ef350c9573f94e19", + "0xdcc3eb77af8786d5064f3d15ab184de056c08b28c7a5bf90d604f080f0db03d3", + "0x8c39ecb82bdc96c995e5926b15e63019f011ece2b35322adc7eb8fe90df95410", + "0x53ce5a4e4f689b47a0a7c1b72540fc87272c577503388cf05656c8e7a5aac5ca", + "0x667dadf6d3ffee26f04850f0dc2db09f88d4e9df28bcfce4fdbfd6c5666c96e8", + "0x8268ef73279798fb3b798609b15d8b57874b247efce7c33966aaff68538af0ef", + "0x493b30f1378e1768056a4082f1694385893c6a4011f03101a81b8377eb27d5d6", + "0xc444d87ee2af7e250642421770ae6d1d6766863520a47147bbb609cf48c6724a", + "0x1408073558d4ec6d97cf1cc3c0f0cacee31283f105300578e3a27d8ab8377bfb", + "0xd72bdf26e22a93a668867753d35868539c011d5bf1a381d36341a676571c1f73", + "0x1080a36ac0be6476902b0aedb832b15e1eb4997a367d18415ba590f426e3c716", + "0xd1cd609b0ed33ba8881b0d83ad46fb9dcea0d6de5b6a7924dc318cf0455f540b", + "0xf0267e1ef1cd4d8e373f662fa3cfda5520512bd33b406726bd3a69a39d24b5a5", + "0xe1458e86aca39813bca0c26599296d59f8c651ea67e627d124f2b7c5a1483e46", + "0xed9c3973a503ccfb9d1fe3791a741af77a95364d0e6559d816e880391d74d4d1", + "0xc0089751f9992f792345e7c555f13615e621b6fc802cdd61e08d676fda864c8e", + "0x8ab8308a652ecf00de7c867fcd2eaacf84f2aac65adf13515af3fe3ee7a69be0", + "0x3081ecee0be32a0b842d93d629adb79fad174fc9c7f6613346c0ee44ffca901e", + "0xe254a689eafd19c6686dcd89a06b1abb9001499aa1f03414387fbfe2b19f5a4d", + "0xa92bd85cf194870d1aa15738db60b6a30ca6e426e099351560a8ce5d3b13a408", + "0xa2f57cf261aab3cfb0928fa160be10a9c257bf2a8f1e51103d4e5ab9f9ba26f0", + "0x4cf15ba1d4ce5e51025a2744c7355001e6c20ab2e1b537bbf26f120726df2bb4", + "0xf70f4f87a77b54faab70f1b0cbbdf3debc966392890b919e1c0509ac1c9b8ee7", + "0x28986d5a5ddf44af8fbe8a68b8656ea5dcbb36551785c6cd5121a17ca33b608f", + "0xa859cd6f693e4065161d56cb22cd0d6d5d956c420b956b85f9c0a6b514b81239", + "0x3142bd457bdef3d22c0ddc07126fa2797dc66f5a6f5cb47f6856e1fae2acd5f5", + "0x13240195f63e790c2a170e0d80fa02eb3cf34dcf201b1e7a02865f70e50ec2c2", + "0xe224513d0d30dcb59244c59cdbe0a8e6e7693437aecb3dd1a6355835699b63d8", + "0x7b219fddafc455ef34378dd31249ceb4cb23fc848f7427f3128b91abc3101a3d", + "0xd4e7b7f004d95074b19c9d18ac954427de24207b3d9dbb7a06581c9cdd4d2366", + "0xc7db291a8868c58260331d4c36b0ae595e8816398a7bbc5fb61e00ffb4a2cc48", + "0x2ae7dff7f6e63d0324956b2b8dbb022b7069f392e0187abade6bc63b01e3ba6d", + "0x81e0b05aad2ac79beb89eb38b6a7455e4b174dcaa9f07a7556875d036d64ac1f", + "0xeae95f8e34186329c5f5c11b4ed62a53d665092b0d6559d7bc91dbaa99ad3ce4", + "0x0bb11d748a73a1b82f42c2df52404e834b30ded843d28460109d5177dd75f3df", + "0x55fb0af273730208f0a197862c7d0defc91b6c47b027b225d3093a723d2b93aa", + "0xfd43b8ec425e95d361c8bafb6a239f4e3c9edc68631e96777264dcf7bb41710e", + "0xc48523fea3c7ea518327a5bea849765f088904f8cd9f69f17c7783e1ba71a436", + "0x0d1a511231e15d7a37f0695422cb652ae319ac71ee98885f4d2c3d14f14711b1", + "0x4bf7cd135402f0055b423920ad92f71b4064ee0d7f2d151f82023c222d0f2286", + "0x0a682e8df046079882a718790725049059fbfa35171d43ea830ba2cae37c0aa5", + "0xa74a929c762e8dde59388c91955eb36b04fb56dbe4a1ee2b05e4fd841222cd29", + "0xd69beba4005e2b8925b6d7fcf0caf3c1587361604152bcba34554ae6f669a1be", + "0xcc56728e8f7dd56d4d6afdb8f694429dfeb4c552707c8354fdb7ea05fb2af24b", + "0x055228dc295905b467901461b4409fa8de5d9ecb5ae74009642918db39a0c9a4", + "0xa83391e094c0d0ce3eee5a8318e40f4035ff66a0c259e3e089781db320a362cc", + "0x517bb360f6246c1317440f8dd5d06bdf09e90c552f65c86141be3a57c3aed460", + "0x156c67e332c3285636136f7984b7a82baef2fd7362b577ca59697fe398be6686", + "0x2b1b1689e6aff6e88d7baa7beb7c8f562c1136b5ec59048b257ee418c84458ba", + "0x51377f007754fda7e2f03df2b57f8156189cd11e99dca9a2b8de264fb899a734", + "0xf553d083b83095e0d28bf5c55e67a5199eb9fa906aca202798fe0b62385aae31", + "0x2aac8ca12a5599a080bd3ca34aab0eb30c05fda6190a156572c6c2b47059eb0f", + "0xe73e24d314e4c9b1022150ad3bf04a7f2bb388b7f098b761c7e7c5645e9e4887", + "0x0fe5f32be6999f5c53c7510a4a4d9fc9fcd43ac628f76bae4e73deca7a0cc248", + "0x13596367afd452947ec1d1e662dd84b4e609656b834c5b5a194947edea1b935d", + "0xa0bf762235b0f768cea2dc903b53d677745871e3e4d4b2a3177e55e3804181ba", + "0xd2f59472fbedf386d58b656919cbae55a8fcf2728ab267e6654c92f8521dd8eb", + "0x80b5903b0ad0752832819d16623b6de6744930f16a4b65e6e247b106e81cc896", + "0x2937d2a0713811cbaacc151165c173faf17bbd48a8b3eedda0abb5d9e4c29688", + "0x624431382bc121c155c1f630739254ea7723af40f9fdd59f805cb86625aec467", + "0x7f25d7f03a06052510c4b273462e0f1de88a75c3d7c9b8bfac7463f87bfef2db", + "0xebab10354a48b7738601c8a126558601c27ac64c99a37e9d96a1e4e0b606b2df", + "0x4fd2600be9f6b69c314ca499198e2ed139c54135d9953f559a597f7a90c33af7", + "0xa6cbb5f10a4fb0fb56e0b5370deaf3e17a5d9923681aafdbe19e9ebf901f0e01", + "0x02fea3f0c4decf9f4415c2cde324f0896c50796a816e967577b908e930bb172f", + "0x66e1956e75b83de2108d3e5a8d420073dd7a7759052b18ba57bc9f73a690b59c", + "0x7c1a01ec0178fa52df7bed4253a53036778d16c5f27e9bd8d551d8566a68b6ca", + "0xdbff162f6abff748698b1132eb5f7083aa663eaa5f0b45d23728723a8fdd8d41", + "0x235f6827df12dd625ee73194e2dffde908478f3ca7b94b39034c069fed46ed55", + "0xcc9aaf19d6e950928e182fb6da4ade8956a9e0e5912df2cdbe40ab27f61784fc", + "0x0631903a6b2342017d657ee0d46a2bc4fa596188dd07049a9a3e8a6c8702e82b", + "0x507efceb9be0e626757b435102d7a457519b380539601dbb0c4b4eb1be0b254e", + "0xefa62044848f585c643b41d1e7b953dbc0aaf48c2e1d1e55352dd0659c2fa862", + "0x394d2d21b55f664af9883e632ab5188d8a590bc32eb512d6a0b37c56415ff62c", + "0xc8866dc79571cb751178bcc23aead8fb58f1b0b4974924c3f5fad97e49e98902", + "0xab28093bd80beedc7f0ba976a3f8f0949dd9215f7e4a58dd6d58e856a1fda7ed", + "0x75b7831f7c721f32836fb9d318e0b59790ad1bcf410f6daaab45a6747f68efc8", + "0x6663609e8afc148de17db41d13fa3243a7b1965c500b0e1c6b4a2ed0d25e6410", + "0xed8e18d8c7e391293d67f47f097572f0eee4ed3025c52a9353ac525b7c011941", + "0x9e878c269e15a6118fa6d817270e83fad9aafd8e02ccbfec77b7cb20854fcf2e", + "0x50f28d14bf0f7d3a9ef50cde5da08bc05ae7a17f1ed49444f2e55613118549e5", + "0x69cf59007a8e108c4919e9dd9a32476939d1458f4c48d3c3c3d578ac133e041b", + "0x1a3a45397a43ff3dc1257821b11c70102c62e2b5944cba93f486926c733d6611", + "0xca9d78286fd94a14d13f7a55ece595045055dee0bf0778b37085f9153f64e8c7", + "0x5f0dc585dcfc2e42b5cfc321000b9f54524d0df126bd39ad88fb2ea6cdd79879", + "0x0b4b39eddfb68dd783652937ae5683f4fa0977209074a6c7fd5eb8ba3c47aaa6", + "0x522170deac3047ab3502a909e9046702786aa3a7afad28d8bab8db83e429d406", + "0x9f8949e4215b2f88a16ed46645032f1464960c7ebac5f8613d45e68fdbaeb907", + "0xce240564d0bb1197d8db238c4cfcc18c812ec19fce22eab81da86d299d85b54f", + "0x0b7189fdab65e2904d39da44e502ed9e802e71c29e0c472ad5aad6c2ca5dd129", + "0xb348ac6fa88bddd70ba78e248a9b68194a941334d2035728da5f435c7ea59be4", + "0x685ffc580369e72f2a955aee3e534c8750ccaa6fca8a006edf70ded1539ebc43", + "0xbd30fc47b0276111ce05f7324c581e9baf6be4f8f295973f765f046675f03b16", + "0xca8e232211e0f9d9d770efb0d4579df6359a62951ed016bdbc347c7a16e2d744", + "0x9e89d6a8bd44cba906026df9efe6035571c4e6ffa61fcbd8bd55c283e7ccd72f", + "0xc825d5d5958a0d9b9c91f184c70ee423771d3eb7c82858f9f061c93062ed1fe5", + "0x13aaae6dcf997bb119feaec6e63f1d820919d43a556fce6fba9c5256b4fa17b1", + "0x8f84d37191c7fc2645235ee786edf175dd8762e421ccfe3e3d208ae506dad5cc", + "0xb15292239abff342e81d8e864a104283a8f34ac314b7309da0e25eb3ca07ef86", + "0xccb205c1bedd182e2d84efd9c904360725ba55948428a27df11c442896902db5", + "0x6ef7ac2bf233dbddbb2f544aa4dbdffc4066871ec46ca0c2d52feffb22c10871", + "0x928c6527362c77cee4d5f210a058f99db0e894f43824d9fa1390f0d12f86f83d", + "0xe40012bc2be876404a7d2e46e522f5d591434affb2aae462bbe141654bbc0e68", + "0x9d84b3d531754063af426594c8517516b506b7513e6522af6364a50537f1255d", + "0x7e702e849342a7bcb54ace9630dd60e25699b5337491c2ab7db5aedbe0742258", + "0x3dafb10928e7924f1f0fe45c5c22d19670cbb249f5367699e8e9534490db4525", + "0xcf5ad9ac52ee98b5158ba11ac59e511641a40a63b5105cd40d2f0e76a03638a2", + "0x726a5fc40b7e00ea477e7934afbbe4717717fb20a63f2a6a23e3d5e4a8c4289c", + "0x700616f25343c8c6ef208845a494c89126905aeb24179ca86310fb94cfddc704", + "0x4a0c9193314e365b26d9e0dd4b3948c01b3a04842a816a82a62e387915178160", + "0x396a4301241851acb28a25ed41899c338e78543c3b4a24ce0945b9cffbc0b5f1", + "0xdd1400c831bd337a24c8a272778482aa72521ca705588757cd15cf9e4edafba7", + "0xf4e30c77914620e0b54eeacaf81d882b37e327667ecb2b00b8e446a9966131f6", + "0x0122c2b14b1a38670dd7b419bae417c92e2738acda9260f9ed7e1c4c478dd489", + "0x13f38613722e1ad77a5711bb5e87800f066519f33c186e395be3020b26f9951d", + "0x697aa448e31fb9567fefe706f338a9aa263a676fdd12884197a51bdef1290491", + "0x1dbfc227e7a118f1721b7918d7c397d5700de868d7f782314e3d15f314821027", + "0x0a1bd08dda388fcbe87c4a1ad36b973d5eb1cd42316e462fff555d51033cefe9", + "0x46468eb78fa82c6e85db8b4698f6dadcd70146b898198e8bbbeeb767a29515b8", + "0xbca2b6e0d6adcc468eb950ef14f6d523a2f69161ee80cbff341ebb2a7ffa1850", + "0x34f9bd97fe1279a33b011dc0dc0be14d4fec140d28cc923be9e0ca371df28561", + "0xf6864309d0005da1f8fb9cd6c829953c2dac386cf278b2a17a1234c18b140e0f", + "0x048eeaecee39626c3e4b74f795b4e114e5e28e2fb43055213f1646b2c368f9ee", + "0x2afb516e8b70d32ff7a3570f79eedbb7938c3c466cc0fcdf26f365bd3dfc17e7", + "0x5f909f050567e6e1e4eb72cfeb32ddfaf21f5a2b11af2a1c986d5c0ae1a0396c", + "0xd4eda1725219b7cc353cba2e52a8a4ba04c7e788c872daea2151a0ded5c2b099", + "0xdd87a715a242cfc1fafa12b377738417b8fec4fb666fca9e0c6b955dab4fe182", + "0x23ada1e98cf5dd103e47256bcd1e2288b95cbf1bf3161c7ebd9fa7a22aebc93f", + "0x783ae20e558493e86af4a10f4c2a263774a468c86a7ae08e2a47bf29a792def4", + "0xadbfb8f049f2b2b55a8a5b1848e99d13208297576d3b293a03d18c95e6e43ecc", + "0x0fee2df760f5abb966a150b120406fb8eb7b7c8970400e0a5446431523840d89", + "0xe0283d1ff97fea7d9cbe32ba780718879c9d117e54a60ff5bfd9b14dd2cb45fd", + "0xd50698f580a53978b1f7a7b8e89efe5ce0e094a6edbb1fae899b28a00c245d4b", + "0x090fb313663f5f5ef50f39e9fa95161cfc2113d78df444039ba775a9c8b319c5", + "0x01730bb508aa57e6662c7496c1f5505680ea5cfd7df0f2be19b53726e4d02b29", + "0x9dfb58edffcdbf0198bce964f17a80bf49198c3188e6bfa7cadd65ad8eb5068d", + "0xa2f367c11bf28bbe6eb9600ab5257e20c5723c4afb00f2c239639f14f08b9568", + "0x959368c2bf88ebc8bdc79855a416f60599a48ad988e2138b4906d68c0b2de560", + "0x7c592090a0f000b0a54a1f87cfef12e8852f768b45c3d215e98c515794ae38c9", + "0xae331f6087d6d7fef950455b69719625d92ef2daa5a43ab87ec011768915c8b6", + "0x0dbafa99763d66a876aec21f04c613e71d990dab0e0b63487d9077123279dfad", + "0x6557a409e545827b8319bbe3bf8af198a861ac72214108f242379ddff0b641ae", + "0x950b7d33192044a93ddda170eae89170c22ef2fcb69276e345501be10885b2a8", + "0x6c448f1a1128f1fdde6413b715c840f08b3a7be02d235bfdebe4fa83689e3c98", + "0x34f0f02fc19b11f7fc6ae3a6462d6ed7902f9fe91c392d5f0045561101f93985", + "0x7c20f9f63293af90f5e74ca780602aee3161c0419dc5082014e267c7740f7d70", + "0xe6a92ee909560901da6af970417e60c90badd5375a06716b31beaf18c0aa8636", + "0xc8407118d397623d12d8f74bf56667c3e1dd5a6d7a0407d0173cc3deaa9b854b", + "0x612345790bff5f5f6404a8351a7fc2a514fb465db99c8d5dd7d2490e210623a1", + "0x4963d52b2c174748924030ce6693b125113550556d0145ad392ee98cadc9f528", + "0xb3c7da54e50a98c3971cc4b72bf975749f2ab5e6a7864f811079d779e7f596f7", + "0xbfa1711dab9f4b0e82b95a5900f60f3195ebf8f93d7d9d489ef9a05414602f41", + "0xd028ce049e1e4a62c3ce1fc9417ec7c2608bacae61f5653da317ca8e83cf6766", + "0xf21938c46a71ed8b5ac97ee6df2388e8c55fdc14d1c11ab525d18862ed6d1bfc", + "0x9b2c3f3396eb2be6b09ad9a9bfd718d50dafd91d232228ea530d68a4a1803223", + "0x672325d948f44f2365efc3e73509e673c2ae763a6292946a0d3a9e55c77cc58a", + "0x72b163a99d809998e87e4679b05a2d13d891016d8cf613d3c3ca8464e06b4b0b", + "0xbbf71a91472273686c2bd961e7ef163673b225c8571cf4d9cdbc25abbe7c986c", + "0x1717c5acf94a4062426475a4da78fc64310edb44bb3a7777597bd38b8243fd36", + "0xe701e3e57d8a162060e4926cd876645f30862a90bcbbf7a1bb25147bc849d7c3", + "0xe1601b1713013c54045b17bd31bd1548c8e88cf5cbc0743745253ed71436abfa", + "0x7e829e487cb8e902e8c573cd8f5f40309d45fad3e18ba95add98742f34021100", + "0xde0bfcfc0a7ea3cb1dbfdc4b97e07ac8961e87b172199e2db96a9f3d50ee08a4", + "0x8a8fa6ec0bfceb9c7936f7cc6710644f14d5d2f53bbe8e61ebdddcea892bfb9b", + "0x937d50777fc97596beb74dd8cd0a873395307f73b25242a3644e4b5e417d63e9", + "0xea42ad880f934c42c4fb31cc390db3839415691d34d07880454d72d8a008fd96", + "0xdcbcd3e2040fcde9541cfd4e2099936ba9631b3bce98161fd00ac478cec29d4c", + "0x4abaf5bf3dc2b12a41d8cee6179b699e72d546e5821537de6afc41b52a8a67e0", + "0x6bae0e2c7e47added25aded1c015e70a7636ef08d69c39a18570d6adce5082d1", + "0xa026ed1ec9d9784359996b33c185795ffca57be09c3b04d797e7869d5e7bac9e", + "0x2f353f90306a8e1ec19d138fe6e05e0e9b2a9fc652cdb0d565bb606e1ef6e906", + "0x7c2c5ac89c841106835551508d6fa058e92290714b7eba2f4900cefecaadc512", + "0x93ab889dd0d40711ce5f7ea8203bf62a45ce4be2a6772e21b3d20031faec4f60", + "0x6648314d63d8b97a43b08d46a8b938c16ab6c9e86726e0b7ea24ce7021bfae22", + "0x61a01393ab59b76fcc93bfccd294395b5985f99416f13375a116d49dc0f4e45f", + "0x48d4448ea0d7533e29be0b6dbdf08e35fcb1d77b5fa77af94f469e718cec597d", + "0x5db14c2c548786dbde38a21c90d5e020f7ff9a42dba40e34596d1542aaa85ceb", + "0xc6da00b09a6a9ded3ae155183ea064f8710665ba8f8c671fd023ee24853fe506", + "0x8aa669698319ed0934720348ba6bbf7c2324ff7cd7da19d70f1cd7a84b320c36", + "0x72cec022c3d512100894c62919cec6b0f023c592533ae789d88c8af41fbfab06", + "0xf4797972fadd05a2f034c25dade1bf465b58d758f712438d1098fb12022cacbf", + "0x2d6e56359df3c2420dfc12d7e519751e86499eb3ae3966e445dea2ce9697e63d", + "0xa2f6740825a17d124f3ffddf79ef11c6153b7c39d58c0705181247ccaffff8c0", + "0xdce5c6427da944caba1124e16970a67787a2c77ad378b9852b2e75f755269617", + "0x3b382bfdd96d188d3499a7d7645d14884b1db9952a7296f639c730ae05355e5a", + "0xa7076ced7a4e838fc71656336d61232e554521d17b8e6f9c019b0da54eac976f", + "0xb7ed718af65740c96796770a34969d077e625d8a54bd88a42a87b4b8f16d16f6", + "0x145f53c61eca53f76fa5f0bd9eaceddffee5057194795a42564a18f2d229a0eb", + "0xc4070964ebfafb3eab3ec4289355c7512d740fa6a2c08d92cf93f3b1b0e3dfe3", + "0x0246c044b729978bc210a52e10dccb89c22f9ed10909f60c137014cfadcdad5d", + "0x2fa31b5ba8d194a5863201d381e14d2cfac7dda5ee5a033aa9a3944cff76dc92", + "0x1df1768ab12b327e696cef043a3dcd8eb75d150bb4434d187acd2acecf89922e", + "0x7dfc139ce57b5f4110a48ce4e1c95b8e9fce7b83d0fe99e92120bd60c30483b0", + "0x63aa478875762360827069f1333ea6c5891829baf30d64d95072c28b14a7a355", + "0x4b468dd25a8826014ac17fdf88c2e9572be84fbee3a5f3945aed657743df5bea", + "0x4916fce234b380a5d68a2ead5434bd2b6916183da350cc03cc8b773c025b7a80", + "0xf41f640714f3514ec18f28ad5cc4c0d3d234df287c31733cd817d628a0a1a0f1", + "0xebfa72e615cae735bc4de3ecf6d22523ee9becd0f2276073504b634881e36de6", + "0xb778e9cd4c25f960f4e841b112c6363ccbc0166e2a968ecb619a50465dd8c86d", + "0xc3fe93c88b5e27b55fb238d94f8cb0bbf5bc93d4ee1ea1cf8450f4cc9ddf7aaf", + "0x0e5a87855596eefbde0f62867bbe3e231b0d5c1feef517dd564212b7c2bd6511", + "0x2151b7a79946aeee1d43f23e12cc9cbac2ae73cce199a30f23983110fd18adc9", + "0xd73c780aa498e8f7d33e8ba6a6b1cf8613d5ef6f50328c92f5f396bf9078ce57", + "0x6a914f2d36f781d4973f4145798730344d780c0bdf5a011f28c8ded1bf21e43b", + "0x7542ad864b4c158b1d2124276b96a6006872cb746506ede1204a5c8ba8843e61", + "0x4cd8e7d558198702e21c38160c7176e933e4bbfeae7bd0d1a127a0dbb9cbe9c4", + "0x83270981afc5f053129845af5a1289d20e9a5c0c59a37239899844ac29e54283", + "0xaf5049a81ee76ab1affca890e645b45301d083dd1c2b184df6a2ceefdc28aecc", + "0x3b126bf9cec4d38408de16abe4ad3e37fda0cdfb9b529a3944cec0fb0002fe18", + "0x207578a3c801ee996262bf48758c64dc5dee340acff86a019645b02d4e6c74c4", + "0x3240b16dc703c5319a04391025c2d580eac2002516a58adfd21e5e91a03adae9", + "0x591c064b39e7ce2796bdc6eecd9a59454271bfcef70c8c8b1e37ada5d6af7460", + "0x0a19a277639a56259a82385ce2f76b5a34f82b766a45a70bf6fdc3faea97b6c5", + "0x3b05954fb191e93a96df6bbdf9ac2c6dab554a395f12e8c8873cb3cd7330ad1d", + "0xd193e6db7ce63d8a872370e1869ca3b2b0f8dfcb03f660409e9291570041c94d", + "0x4104f38d0898f11eb83792364435cda9f9c796717c5f019fad67643d3a5f119e", + "0xb339df1509cd46b01fcd1754bb9aa7896f092a4db4ee948ba142ab99cf7e82a3", + "0x5ff4ceb016e91332e7756aa2ec5abb8c2946399833ffe9475ed36b1c2c0a644f", + "0xf55565169495ebcd3a4e6189e9b57643ed454354947dcbf0e8f36037e021a9f7", + "0x01ec24790062d9cadd54f7eef2fff08f2d4c6ae1ec40e4c14e2edc52d4df387c", + "0x00e1b46ba26791d220ce9bd1466bf7535561e8a5332244777ee125aab8404fbb", + "0x73cf9fc9d87af9c319cb8bfd4d62c7397c0aae246521d5bc65a617e1d42c9888", + "0x903c53b767fefc344149aea5c6f012bf806e2295d26ee09c725e7a7844810279", + "0xd3794732bb5866755f80f98f33a8582aeaa3a6fcd39e8020c3839f18faffc910", + "0x0dc7d1b89601438f6c84b22d78702380e299b042273d1e521d5ab66f78e2ae77", + "0x0e1d28ee0d6792943aba0cd4953e915f0baa572aaf8485295e8ec16d3792e16f", + "0xa317217fc59650d60eb5a170164dac6d67124adffb9759088003cc3995807853", + "0x69f85349fb348be33c270a9b381ab5d04e10c218dc901cf6a2d104873aed4b1f", + "0x6fc876e038a0c7282d68e9e5684ced830cc86529e6d059ac502b9eb1fe2a4a74", + "0x8c683feeed5f2f29cdf7009b6099da17d107a3b976db0971ecad4fe8ad9ca86c", + "0xf6bbf20cfd59c3e48f01bb532aeb3bcd0ca45235069cbc09bd8ccc622b409508", + "0x9c0cabf794a61af956116c0b7182d8ec78b540645d4e967714c72a3ba9ede87d", + "0x3839c20cdd445ec0634a429bb66cef9bbe5f94f8ac0ee5ca9deb6bd78a1d7a9e", + "0x90da2c6b23c61b53730e697cfe3db5978755fd58e69f9384fc69b8084d447cb9", + "0xf1b40a8505c734fc4de4805756aafe46e0263e7118e1116334b224109006329c", + "0x24c3366b0fa68d33b1a0f09d760759036763ef47fd2b7b13e83c4876d554e73c", + "0xbfd80734e935a2af2415d201fb87d1dda5aea542ef4a76dcf26c91fc5bff9603", + "0xa5bd0cd46eafdd399d8a227431745f5f08ad5d6fd8daa509cb1b10f50b5f0e1b", + "0xb4a541b63a8da08fcc5d9a21535f652eee04fc70cfba7da908e7780fa92ca6c8", + "0x26d6434430c7083b1d17415f4f11e53aaa79270dc22045e361329d0c9821f9bc", + "0x0db69286e0bd5f808dc58766d5c301ea9e88e22a79ce026d43639a14162ff973", + "0xc76640923490407a7983c15d05a28f5d3ccf21d908092b444a259e54ee5b3d07", + "0xc0fccc6411f5153146f6c178cc0daea0686c4c471a3423203933ded03baa8be7", + "0xa6b01ae91dfc4eec65d6913f8ab446288a103179c3a8cfccb125f2e3aed6397e", + "0x4ac1e929f0ffa7b020a599d90c252dfcb63836017e31904e32757c4b1fa2984f", + "0x654da4188d385f9eaa3ee0ae3190c7835a9d6b26a9970c092a4bb9c1f54b78cf", + "0x4e29427161cb1c9c1f7dc21c0d6deada7d2144bcfe51dcc2e68d921271edc365", + "0xa3ad2bb2868c5d5a733676398bcec315e546932932e419eadc1debd92b92bbcc", + "0x536472049b93df449ce20ba4daffbb88f9120475f957ad0df6aaf17c3e1a04c0", + "0x25ecb3940fcf854205f5f690886df3d1510cc452cfafe16bbf0e6aba639b321c", + "0xda2749f7287f3f04255cdc9f35e8612f9e5c81255c19fc0520c828db9f74bdbb", + "0x6dbb8ba3285e8773634794b08bc565b6b4aeb6f0b2a2a6473f323ba429e388a3", + "0x6b88e565b448346004b50ce69d71b1213ecb014d40b1ca2b62c75e1e20b7865a", + "0x024a327ff6987da3a1455cd913368fb5224c25c07468d899a9cd0aab51119bcd", + "0xb6efb51174dd044a376d3deeb4f0875c9a79e366cea2c9416e31c2c2f1dfac46", + "0x2e2b073b7a50d2da16698ca9bf07ea4f7ca34c9d1cbf29683f2dc4efbb41e7b1", + "0x9ec5cffe0496577c7d006b980a21d7e2b1845338a4c1f9522b84ee1351078e2c", + "0x94cf61152cd1f41b168a0dd2f92164e77f0eaa2280e74dfbc3a21236f1db67b3", + "0x8353c97cf93c5a4db1ac6b0c8d6ea0baecb5031e728f72dc80dd51eae7a71481", + "0xa826b527f726bf326b4bf5d140e4cf8b424797d768745107515f01f9dacc5c1f", + "0x920c77649a8f3865717ac17498642164350f2a1db0ec7a373d099e446a42c3b2", + "0xfd6eeb07733e1c74c801986432805266837a691009205af683828c3d5466ca53", + "0xcc69a010251b5b609e898c948dadd3a393b8f5911dbc10ec074a3aa5edbb426e", + "0xd60b9792e328706d35ab143240ef40c677f5569630abce688606d3a57740219c", + "0xb6a1a98e987617fd9c51810d39fab31248f2e4e251883e4a71c1dfb3949182a4", + "0x75a3e7c79f4bac4bd1efa81ed568d014b1a465b462ab26c6c26ee90e774d5164", + "0x1808189230382b5150f05b6b5e7aa783685493f3e89cdf190e23f9604d8352e6", + "0x344964d349c217124e6354e667d50309c3cbebd1bf81b9363378a60d8de8cb2c", + "0x12d6b292ff08dc249bb9c38351740018d598382c2ae1f37bb7b625d46f34d033", + "0x3115a238affc7cc50ad40536f87514c33a75935c72520d876fa9011651a819b0", + "0xe9b3c0e687e2cc5f28eb2042decce3039e2d296bec673c98cd9e264aad9f73cb", + "0x0330df9a1c2fe42d1ac3cd393527ffe01e59a808fcd7ffe2fbe918a82b4e1413", + "0x254b23e5a6e403fa1df82d90633c051bd463049d521eca349e33f26a4e22443d", + "0x56d62e6ff41a89670cd0b58f3f5842b7059027c1ed7f1f178052894dd5439b09", + "0x83b738db92b8e789d5502de38a36d20d78e92d6975e07ad803a75a1c181cae70", + "0x439beab00d517d81edd9e3cd2867df5fb9aa335e61b6203700426ad5b0dfc9a7", + "0x28ade6a2d26a001c10eca939e58dea51d9d0c226642d268e230409b301ce0a58", + "0xdbe038170721346bdba08663c6026c9e7573dc7d7ea2f56276ff95040d069f3e", + "0x0500db2e22ad39bf7d4d62c07375ab2f46eb363d8b9b7dcbe4f4f912d9168655", + "0x703dff8ff55e11f57ecd13101a677eacf1a4819b0424943a48da6a4a280a8a34", + "0xc21c01581110d5199576c79b1c6d1a7ec1bb72739454a38b997da81f731e7695", + "0x45cc74ac2b338a887bdc722c482122e39718251cf1319de75b9ba313272d8f5f", + "0x06a4e54aa37b99b3335ab7702d6c4f026c8f27a578373fb5853489b6958b15e3", + "0x12d1423a834f74fcf279e32a639ce5f4542001e421c9743eb905fed7bf0a6cee", + "0x49f1dc5818186e91030e09eb615a0bb82393ce2874f9ee36ea968cd6906c7a71", + "0xcfaff643a75fbf1eb89ae488902b3a9bc8e66b8c300e0e816f223675e5dc65a0", + "0xd228d415cdd5a37109f621d62447fe5906ba7462acb65efda2e2a0965c2b984d", + "0xb38ea82d658e85c65d3ce9a576bf6e6ffb0e1f1c7dd554d124cc8a2c568d1ce9", + "0x4eda30e35f5d4c726201d22a0c4c05c6986b73cd5b19c4af2f2f591bd81c50ca", + "0xde7d2cb40910c98cda5b17bf931b9e2761b5dbcf73de4ca6fd659cac415b7841", + "0xf6320ef9228420d1808b74b63202f96eb291f99616f76f73248e45c3d8c36f10", + "0xc6c03a85d998fe38f84ad79e16bc557722c04ffc3b79ac0e1d0940b2f59648c3", + "0x07be4386488ccccba5f162630241b00e8fccebeaee946867e2808bc2a93b16b0", + "0x2ab690bc438084f7791bcf9589c4fcd3d94a2189f40e98c9420071f2ea5e6573", + "0xcf0fc0827c824378e2c2174d11dea0fe34a2a4bb874e88c50e40d0401514137e", + "0x2ada7bdd5af69bba2b9977b811d6d611790ea9f6817701a1e4c0ff7b7b28cce1", + "0x19f78ed4654d9ea98d26f1fc808890f8ea49232647891973df375b7b4be84dfc", + "0x992f145952e6e56f6e0b6c573c052ad7b38f55ce6a07b72698be9ec4e325839f", + "0x184eba0a8a2df9c1cb81d331347c62336f939cdb6b26ad8f50803b685dea5c0f", + "0xb7f9f64ef6e4b458f6bbc6be094c8bf98a081ed16a9541f9e039a0427a949d26", + "0x1512358c838d7c2c6afa1137868c7ad0fe3cc516e3201549f3cb3d4267ffc261", + "0x8ebf459d29023a6664205d88b73b6fbe03f3a8d58f7190cc15a7c0b82849a166", + "0xf2cceb56f58173ae33637c04f6ebae6bdac71d203b0fe430594d3d6be971dbd3", + "0x559511da5e412b3695bd7c377f55e7883fa08f68dd5660600bc5e09338288b27", + "0x50e872da55a1653cdbce2bc2bdc5ee19f8d1ffaacef6af75bd874fe47b77f371", + "0x7a905e060419b238821bbc5513223483c106412663f64ff6a019cde75b420879", + "0xa328246a599423850df4012528cb5d61d9be303cdea4202f946b0e3a2a12c0d0", + "0x15e4b9a3a475cb5221e7b281c657335d04bb243266e9c75e46f03c0ff8ccffd4", + "0x835fc9e31d0c5a15d7906006d5b291490d52b5c79c3d777b34d497ecd59ddf50", + "0x886b5f07f5dede599b3c692660bd84300e74eea5f48834b4925b6f9dfed6aecb", + "0xfc835d61674acea24b648647df71787400c51dfb5892a2a7043dab45adcc6730", + "0x48cc0e8061b8fd5c812e139b75e0460ea142150dfeab913c82252a26993af6ed", + "0x984eb490ac031f55596a4ecbf57580350e4b9f266c5eb94431f371aa4a14a78d", + "0x3fc77c60434bf79a471724600e12efd8120f9f78e201e46657afa87f68f0a001", + "0x6f234e54de04b9506a5b78233d110d6329e30ea223edcb8e902ef3cd089b2f85", + "0x8068bbc755d0b10e40c8024660ac142d44aede73cd62137a63cbec48613c572e", + "0xe7beec3a0554febce53e90c838e6aab0bf05fb02ab13b446187423e4d374b04e", + "0xbe879a01ddebea34c5f6d401f271db75ac5852c5fa5e5d46beaebc6a7ed66d3e", + "0x67038d88e0920d79dc4083ff02591bdb181ed9778fc3954aad25bb78bbe9123a", + "0x19caa0b1ce83f546d93290a3f77b1ad9b08e7f1bd0245c9fc1a5347bc03c01a1", + "0x9a37436c2a0b2e7e576e95fb6bb6d4c68d301ec1201e72734a0c9303af418474", + "0x95ebbe7bb838501fca3f14ca7ef25dd5be7a1447cec71d62004597ac9c726e61", + "0xa043556767bed1e2519b1cd4192e6c300de50970e0cb369731c02aa7ee17475f", + "0x9b68ba75b041f0dd71145d11c681bda5861b31b43c4c9f8a0cec3fc65847b6db", + "0xd0a4d11096863e0c74387154f58f4c9efb2b947ae4ce7a1da77b8a4ea9f0c869", + "0x074a3e887f280751e7dcd1fd02f93dd12695b9e14a4fff45e2fcb3e26991c768", + "0xa9ea4bde6a319b4f053c92a76d6e72928a089d680eacbb3670decb749f454659", + "0xfed003b25b35e122adaf2a94fbb7069622846f86cdcb79a5e93e3f1f122cbc6a", + "0xa158088c237ee341658d410ff6aa1f43b40a607248479330fce34731cc2de3bc", + "0x62b4f39e1545eee1893e697e634e6e7441e8ceca5fc8be3ca8684cfd70b4b452", + "0x39063f1e2c8fccd7fd2220a595f969d5148b3227a635aee5d3da7793341de814", + "0x02b8c915c81e902a050090bbbcc51dd167f2ff075178bbc00966ee193da6344b", + "0x4d45cab0e4554fb86eb6657df7969092eb77ecc8a099ae09ab0e2ae264db61c8", + "0x484a0d21957e5d0d711bcf630102a793de1c16990d4f0be0f589176f6c9484a0", + "0xb797d1fa33bf9bfff63eec0a30a51d02f1f88bd400d0a6f4ef5f8429997ea547", + "0x5df2b265dc8181782e4e5dd25b32728605a77818598c5ec67fe99c6fc3e35ef7", + "0x31aa525b9dfa0a57cb36206643c4822eb2c1061cc7d5f21e0cfbd72023f8e997", + "0x9d7c6172f750e7d18593c71f36a3ffdea5f0e3565cba656a3b7e2ea361be18bf", + "0xcfb4ac621266fcc2f5b33e249d8989efd98820964f68540ba8b7e3af27b83a9c", + "0xd1c0c7aaca4e2b0dcfca44c2b570eeec6c4982512a50e6a82b65ebb6dbdc3195", + "0xdc4095ea235c260e67c4896e465383605d55c5e8d96b27a512bc8c3f7b72ba1c", + "0x11df12ba1b0bef7378ef88955aa026ec93e87c625a9c7d832eff44ff94aba7d9", + "0x733dd930527eefa4454b56f5eb4c3aa29c4a0e99072e2c1684abfcc6ee09d833", + "0xa7c38c7080fb588e42127de256b7b3d259055c2460900f76de9ef036d3a03377", + "0x27b90a4f0708d7fcbd61dc42570d0d569acd86b7578d479f56d823f35ba4411e", + "0x7cb065f986407568958ed7fb7ba18c2babfb49b02bc9b3b88756695411f31d7e", + "0x648061097b5f547c60290afc290c91696367852d0dcb87be712382f09c9cd1b4", + "0x194bd726e882fb0411c45d51da92d4d8949edf5ea53bb5e3282af1aa25217363", + "0xa3b6c3ae24ca9cd0986610f3cf83d1250c8bad3110c645d9e98a8fd7a85d4867", + "0xcf58b289f156df5285c791d7a8c8ea77104e34b254f600319cb9ef9582638997", + "0xbbbbc3512166bcba6b870a314ec68698b6c36e594e40f73cdead3402ce69f2d4", + "0xf9589e517f83fec8f31d79af06166719dd7870530697716ed1e603050bd3cd08", + "0x63b199bc52c448f48ef989e5cf45b9a9fd3cb7d0b47686ba113dab00f08cd9ce", + "0x366ec4dd2374d0328cc58f74dfe3993e33cf37881506dfd2de0462f01683659f", + "0x24e19f5fee0f703516bc39c4a089bcda7d111e2ef39b138cb760f79272f0382e", + "0x63aa5570647f5bb0aaa8190dd92e6581ac8fd7ca432a6cdca4caa9eb6ca6ebd4", + "0xd04499317d5c640c71617dcd7c2bcb6e7ba84278162c3f7298e2b67a0d50a7cf", + "0x36ea61183d313cae00bf55795ed9d798f18a17a543e9d6de49834cf128cbbdea", + "0x5d6fdce8a398f24f97908926c0528b224f02823116fa2f302cb431ba06c9982b", + "0x34f1e74f0bb9e36ce61aa75847d86e8d4cb9d4e555f0e8a4f93c5edced10a81c", + "0xd40d82620603252580f5cf54f307170ca5868c086ddc345e633325d4f78f07b9", + "0x79b3c34bfd5040d39878501ef0dbec691ed3d806e4adb7782220feb1756a6acd", + "0xc43e045abff53b8077a017596e531359672b0531d129cb6a0302df2a0c59800b", + "0xa5fd2680ea8bec22cbf5da6bf0f8f7f42c569a4a9068de15399c0b386f2afcd0", + "0xe3c0ce648f3c4d5a76d96b30287933ae5a79b60bf5bddfba0f102bb06b9f2b86", + "0x5c068aad633ce416a4340c5425d133616c0eaf30dc042d919786dfafea4e2968", + "0x2a12b2aa6359401efd17d464209547657d01992bcdad081a386628a60ec3924b", + "0x10de6b8c58816feb6145f9b39544a09d67f04ca758ed0b9a80e540ce8b117ead", + "0xf5632116f787967f9954600c783ca2ae46be42d36cc693b5d847a5dd830b6c06", + "0x1798366afb7398e8cce0b35d38c59d8ea2eb3cfb1a85b165fe4ba68a15088c3e", + "0x8cd71c3c3cc517d8997115eefbc7907ab871321f8167e7da90d42c33114d557d", + "0x3795c6454c0d734e71a33e8d4596a2e9af0a4d3aca1bb0452f2da4676f4cafae", + "0x853f13cd1e6c0cad486db441bbc58a266f35bcc367f7ecd9ded5a4b2ce96ede1", + "0x2a557d24d969f703c9fbb2b8efd4c89f43930e21c5a5a7b03c1e53d94b73696f", + "0x66ae745b56d3d5f9082953d9b267e2a98dcab60a302bc783d79aad6c3816ee67", + "0x904f9d8510005aaa955a2654de93af4ec0271205472740a7b86a90375fed50eb", + "0x74e9fef60a9911345324a22e50fc74982b24849f3c4973a5d55b73092dc46ea4", + "0x1571a667030f301ab8657d70607c3dfb02599cac15b44e9e48a4935d68d74028" + ] + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "activate_at": "0", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "activate_at": "0", + "pricing": { + "linear": { + "base": 60, + "word": 12 + } + } + } + }, + "0x0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { + "name": "ripemd160", + "activate_at": "0", + "pricing": { + "linear": { + "base": 600, + "word": 120 + } + } + } + }, + "0x0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { + "name": "identity", + "activate_at": "0", + "pricing": { + "linear": { + "base": 15, + "word": 3 + } + } + } + }, + "0x0000000000000000000000000000000000000005": { + "balance": "1", + "builtin": { + "name": "modexp", + "activate_at": "0", + "pricing": { + "modexp": { + "divisor": 20 + } + } + } + }, + "0x0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0x0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x1204700000000000000000000000000000000005": { + "constructor": "0x60806040523480156200001157600080fd5b50604051620024c8380380620024c883398101806040528101908080518201929190602001805190602001909291905050506000825182603282111580156200005a5750818111155b801562000068575060008114155b801562000076575060008214155b15156200008257600080fd5b600092505b8451831015620001bd57600260008685815181101515620000a457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16158015620001335750600085848151811015156200011057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013f57600080fd5b60016002600087868151811015156200015457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550828060010193505062000087565b8460039080519060200190620001d5929190620001e8565b50836004819055505050505050620002bd565b82805482825590600052602060002090810192821562000264579160200282015b82811115620002635782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000209565b5b50905062000273919062000277565b5090565b620002ba91905b80821115620002b657600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016200027e565b5090565b90565b6121fb80620002cd6000396000f30060806040526004361061011d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101e457806320ea8d86146102275780632f54bf6e146102545780633411c81c146102af57806354741525146103145780637065cb4814610363578063784547a7146103a65780638b51d13f146103eb5780639ace38c21461042c578063a0e67e2b14610517578063a8abe69a14610583578063b5dc40c314610627578063b77bf600146106a9578063ba51a6df146106d4578063c01a8c8414610701578063c64274741461072e578063d74f8edd146107d5578063dc8452cd14610800578063e20056e61461082b578063ee22610b1461088e575b6000341115610175573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b34801561018357600080fd5b506101a2600480360381019080803590602001909291905050506108bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f057600080fd5b50610225600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108f9565b005b34801561023357600080fd5b5061025260048036038101908080359060200190929190505050610b92565b005b34801561026057600080fd5b50610295600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d3a565b604051808215151515815260200191505060405180910390f35b3480156102bb57600080fd5b506102fa60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d5a565b604051808215151515815260200191505060405180910390f35b34801561032057600080fd5b5061034d600480360381019080803515159060200190929190803515159060200190929190505050610d89565b6040518082815260200191505060405180910390f35b34801561036f57600080fd5b506103a4600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e1b565b005b3480156103b257600080fd5b506103d160048036038101908080359060200190929190505050611020565b604051808215151515815260200191505060405180910390f35b3480156103f757600080fd5b5061041660048036038101908080359060200190929190505050611105565b6040518082815260200191505060405180910390f35b34801561043857600080fd5b50610457600480360381019080803590602001909291905050506111d0565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156104d95780820151818401526020810190506104be565b50505050905090810190601f1680156105065780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561052357600080fd5b5061052c6112c5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561056f578082015181840152602081019050610554565b505050509050019250505060405180910390f35b34801561058f57600080fd5b506105d06004803603810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611353565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106135780820151818401526020810190506105f8565b505050509050019250505060405180910390f35b34801561063357600080fd5b50610652600480360381019080803590602001909291905050506114c4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561069557808201518184015260208101905061067a565b505050509050019250505060405180910390f35b3480156106b557600080fd5b506106be611701565b6040518082815260200191505060405180910390f35b3480156106e057600080fd5b506106ff60048036038101908080359060200190929190505050611707565b005b34801561070d57600080fd5b5061072c600480360381019080803590602001909291905050506117c1565b005b34801561073a57600080fd5b506107bf600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061199e565b6040518082815260200191505060405180910390f35b3480156107e157600080fd5b506107ea6119bd565b6040518082815260200191505060405180910390f35b34801561080c57600080fd5b506108156119c2565b6040518082815260200191505060405180910390f35b34801561083757600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506119c8565b005b34801561089a57600080fd5b506108b960048036038101908080359060200190929190505050611cdd565b005b6003818154811015156108ca57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561093557600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561098e57600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610b13578273ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b06576003600160038054905003815481101515610a7f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610ab957fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b13565b81806001019250506109eb565b6001600381818054905003915081610b2b91906120fe565b506003805490506004541115610b4a57610b49600380549050611707565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a2505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610beb57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610c5657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610c8657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610e1457838015610dc8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610dfb5750828015610dfa575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610e07576001820191505b8080600101915050610d91565b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e5557600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610eaf57600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610ed657600080fd5b60016003805490500160045460328211158015610ef35750818111155b8015610f00575060008114155b8015610f0d575060008214155b1515610f1857600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000809150600090505b6003805490508110156110fd5760016000858152602001908152602001600020600060038381548110151561105e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156110dd576001820191505b6004548214156110f057600192506110fe565b808060010191505061102d565b5b5050919050565b600080600090505b6003805490508110156111ca5760016000848152602001908152602001600020600060038381548110151561113e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111bd576001820191505b808060010191505061110d565b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156112a85780601f1061127d576101008083540402835291602001916112a8565b820191906000526020600020905b81548152906001019060200180831161128b57829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b6060600380548060200260200160405190810160405280929190818152602001828054801561134957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ff575b5050505050905090565b60608060008060055460405190808252806020026020018201604052801561138a5781602001602082028038833980820191505090505b50925060009150600090505b600554811015611436578580156113cd575060008082815260200190815260200160002060030160009054906101000a900460ff16155b8061140057508480156113ff575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b156114295780838381518110151561141457fe5b90602001906020020181815250506001820191505b8080600101915050611396565b8787036040519080825280602002602001820160405280156114675781602001602082028038833980820191505090505b5093508790505b868110156114b957828181518110151561148457fe5b906020019060200201518489830381518110151561149e57fe5b9060200190602002018181525050808060010191505061146e565b505050949350505050565b6060806000806003805490506040519080825280602002602001820160405280156114fe5781602001602082028038833980820191505090505b50925060009150600090505b60038054905081101561164b5760016000868152602001908152602001600020600060038381548110151561153b57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561163e576003818154811015156115c257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156115fb57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b808060010191505061150a565b8160405190808252806020026020018201604052801561167a5781602001602082028038833980820191505090505b509350600090505b818110156116f957828181518110151561169857fe5b9060200190602002015184828151811015156116b057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080600101915050611682565b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561174157600080fd5b60038054905081603282111580156117595750818111155b8015611766575060008114155b8015611773575060008214155b151561177e57600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561181a57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561187657600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156118e257600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361199785611cdd565b5050505050565b60006119ab848484611f85565b90506119b6816117c1565b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611a0457600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611a5d57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515611ab757600080fd5b600092505b600380549050831015611ba0578473ffffffffffffffffffffffffffffffffffffffff16600384815481101515611aef57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b935783600384815481101515611b4657fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611ba0565b8280600101935050611abc565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611d3857600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611da357600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611dd357600080fd5b611ddc86611020565b15611f7d57600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611efa8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611ef05780601f10611ec557610100808354040283529160200191611ef0565b820191906000526020600020905b815481529060010190602001808311611ed357829003601f168201915b50505050506120d7565b15611f3157857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611f7c565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b505050505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611fae57600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155604082015181600201908051906020019061206d92919061212a565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6000806040516020840160008287838a8c6187965a03f19250505080915050949350505050565b8154818355818111156121255781836000526020600020918201910161212491906121aa565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061216b57805160ff1916838001178555612199565b82800160010185558215612199579182015b8281111561219857825182559160200191906001019061217d565b5b5090506121a691906121aa565b5090565b6121cc91905b808211156121c85760008160009055506001016121b0565b5090565b905600a165627a7a72305820b0d992f257e70d565448b927a3ae764342039a864112d5d5bb1ac1bd52e4104e00290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc1900000000000000000000000090ae948bb410838bff9d024ed849df6a7267dacf000000000000000000000000740a5c5742833bed72489fe6bf7efc9b79428989" + }, + "0x1204700000000000000000000000000000000003": { + "constructor": "0x60806040523480156200001157600080fd5b50604051620024c8380380620024c883398101806040528101908080518201929190602001805190602001909291905050506000825182603282111580156200005a5750818111155b801562000068575060008114155b801562000076575060008214155b15156200008257600080fd5b600092505b8451831015620001bd57600260008685815181101515620000a457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16158015620001335750600085848151811015156200011057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013f57600080fd5b60016002600087868151811015156200015457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550828060010193505062000087565b8460039080519060200190620001d5929190620001e8565b50836004819055505050505050620002bd565b82805482825590600052602060002090810192821562000264579160200282015b82811115620002635782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000209565b5b50905062000273919062000277565b5090565b620002ba91905b80821115620002b657600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016200027e565b5090565b90565b6121fb80620002cd6000396000f30060806040526004361061011d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101e457806320ea8d86146102275780632f54bf6e146102545780633411c81c146102af57806354741525146103145780637065cb4814610363578063784547a7146103a65780638b51d13f146103eb5780639ace38c21461042c578063a0e67e2b14610517578063a8abe69a14610583578063b5dc40c314610627578063b77bf600146106a9578063ba51a6df146106d4578063c01a8c8414610701578063c64274741461072e578063d74f8edd146107d5578063dc8452cd14610800578063e20056e61461082b578063ee22610b1461088e575b6000341115610175573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b34801561018357600080fd5b506101a2600480360381019080803590602001909291905050506108bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f057600080fd5b50610225600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108f9565b005b34801561023357600080fd5b5061025260048036038101908080359060200190929190505050610b92565b005b34801561026057600080fd5b50610295600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d3a565b604051808215151515815260200191505060405180910390f35b3480156102bb57600080fd5b506102fa60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d5a565b604051808215151515815260200191505060405180910390f35b34801561032057600080fd5b5061034d600480360381019080803515159060200190929190803515159060200190929190505050610d89565b6040518082815260200191505060405180910390f35b34801561036f57600080fd5b506103a4600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e1b565b005b3480156103b257600080fd5b506103d160048036038101908080359060200190929190505050611020565b604051808215151515815260200191505060405180910390f35b3480156103f757600080fd5b5061041660048036038101908080359060200190929190505050611105565b6040518082815260200191505060405180910390f35b34801561043857600080fd5b50610457600480360381019080803590602001909291905050506111d0565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156104d95780820151818401526020810190506104be565b50505050905090810190601f1680156105065780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561052357600080fd5b5061052c6112c5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561056f578082015181840152602081019050610554565b505050509050019250505060405180910390f35b34801561058f57600080fd5b506105d06004803603810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611353565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106135780820151818401526020810190506105f8565b505050509050019250505060405180910390f35b34801561063357600080fd5b50610652600480360381019080803590602001909291905050506114c4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561069557808201518184015260208101905061067a565b505050509050019250505060405180910390f35b3480156106b557600080fd5b506106be611701565b6040518082815260200191505060405180910390f35b3480156106e057600080fd5b506106ff60048036038101908080359060200190929190505050611707565b005b34801561070d57600080fd5b5061072c600480360381019080803590602001909291905050506117c1565b005b34801561073a57600080fd5b506107bf600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061199e565b6040518082815260200191505060405180910390f35b3480156107e157600080fd5b506107ea6119bd565b6040518082815260200191505060405180910390f35b34801561080c57600080fd5b506108156119c2565b6040518082815260200191505060405180910390f35b34801561083757600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506119c8565b005b34801561089a57600080fd5b506108b960048036038101908080359060200190929190505050611cdd565b005b6003818154811015156108ca57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561093557600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561098e57600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610b13578273ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b06576003600160038054905003815481101515610a7f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610ab957fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b13565b81806001019250506109eb565b6001600381818054905003915081610b2b91906120fe565b506003805490506004541115610b4a57610b49600380549050611707565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a2505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610beb57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610c5657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610c8657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610e1457838015610dc8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610dfb5750828015610dfa575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610e07576001820191505b8080600101915050610d91565b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e5557600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610eaf57600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610ed657600080fd5b60016003805490500160045460328211158015610ef35750818111155b8015610f00575060008114155b8015610f0d575060008214155b1515610f1857600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000809150600090505b6003805490508110156110fd5760016000858152602001908152602001600020600060038381548110151561105e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156110dd576001820191505b6004548214156110f057600192506110fe565b808060010191505061102d565b5b5050919050565b600080600090505b6003805490508110156111ca5760016000848152602001908152602001600020600060038381548110151561113e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111bd576001820191505b808060010191505061110d565b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156112a85780601f1061127d576101008083540402835291602001916112a8565b820191906000526020600020905b81548152906001019060200180831161128b57829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b6060600380548060200260200160405190810160405280929190818152602001828054801561134957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ff575b5050505050905090565b60608060008060055460405190808252806020026020018201604052801561138a5781602001602082028038833980820191505090505b50925060009150600090505b600554811015611436578580156113cd575060008082815260200190815260200160002060030160009054906101000a900460ff16155b8061140057508480156113ff575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b156114295780838381518110151561141457fe5b90602001906020020181815250506001820191505b8080600101915050611396565b8787036040519080825280602002602001820160405280156114675781602001602082028038833980820191505090505b5093508790505b868110156114b957828181518110151561148457fe5b906020019060200201518489830381518110151561149e57fe5b9060200190602002018181525050808060010191505061146e565b505050949350505050565b6060806000806003805490506040519080825280602002602001820160405280156114fe5781602001602082028038833980820191505090505b50925060009150600090505b60038054905081101561164b5760016000868152602001908152602001600020600060038381548110151561153b57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561163e576003818154811015156115c257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156115fb57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b808060010191505061150a565b8160405190808252806020026020018201604052801561167a5781602001602082028038833980820191505090505b509350600090505b818110156116f957828181518110151561169857fe5b9060200190602002015184828151811015156116b057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080600101915050611682565b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561174157600080fd5b60038054905081603282111580156117595750818111155b8015611766575060008114155b8015611773575060008214155b151561177e57600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561181a57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561187657600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156118e257600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361199785611cdd565b5050505050565b60006119ab848484611f85565b90506119b6816117c1565b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611a0457600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611a5d57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515611ab757600080fd5b600092505b600380549050831015611ba0578473ffffffffffffffffffffffffffffffffffffffff16600384815481101515611aef57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b935783600384815481101515611b4657fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611ba0565b8280600101935050611abc565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611d3857600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611da357600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611dd357600080fd5b611ddc86611020565b15611f7d57600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611efa8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611ef05780601f10611ec557610100808354040283529160200191611ef0565b820191906000526020600020905b815481529060010190602001808311611ed357829003601f168201915b50505050506120d7565b15611f3157857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611f7c565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b505050505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611fae57600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155604082015181600201908051906020019061206d92919061212a565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6000806040516020840160008287838a8c6187965a03f19250505080915050949350505050565b8154818355818111156121255781836000526020600020918201910161212491906121aa565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061216b57805160ff1916838001178555612199565b82800160010185558215612199579182015b8281111561219857825182559160200191906001019061217d565b5b5090506121a691906121aa565b5090565b6121cc91905b808211156121c85760008160009055506001016121b0565b5090565b905600a165627a7a72305820b0d992f257e70d565448b927a3ae764342039a864112d5d5bb1ac1bd52e4104e00290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc1900000000000000000000000090ae948bb410838bff9d024ed849df6a7267dacf000000000000000000000000740a5c5742833bed72489fe6bf7efc9b79428989" + }, + "0x120470000000000000000000000000000000000a": { + "balance": "10901790566666700000000000", + "constructor": "0x60806040523480156200001157600080fd5b50604051620024c8380380620024c883398101806040528101908080518201929190602001805190602001909291905050506000825182603282111580156200005a5750818111155b801562000068575060008114155b801562000076575060008214155b15156200008257600080fd5b600092505b8451831015620001bd57600260008685815181101515620000a457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16158015620001335750600085848151811015156200011057fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013f57600080fd5b60016002600087868151811015156200015457fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550828060010193505062000087565b8460039080519060200190620001d5929190620001e8565b50836004819055505050505050620002bd565b82805482825590600052602060002090810192821562000264579160200282015b82811115620002635782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000209565b5b50905062000273919062000277565b5090565b620002ba91905b80821115620002b657600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016200027e565b5090565b90565b6121fb80620002cd6000396000f30060806040526004361061011d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101e457806320ea8d86146102275780632f54bf6e146102545780633411c81c146102af57806354741525146103145780637065cb4814610363578063784547a7146103a65780638b51d13f146103eb5780639ace38c21461042c578063a0e67e2b14610517578063a8abe69a14610583578063b5dc40c314610627578063b77bf600146106a9578063ba51a6df146106d4578063c01a8c8414610701578063c64274741461072e578063d74f8edd146107d5578063dc8452cd14610800578063e20056e61461082b578063ee22610b1461088e575b6000341115610175573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b005b34801561018357600080fd5b506101a2600480360381019080803590602001909291905050506108bb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f057600080fd5b50610225600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108f9565b005b34801561023357600080fd5b5061025260048036038101908080359060200190929190505050610b92565b005b34801561026057600080fd5b50610295600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d3a565b604051808215151515815260200191505060405180910390f35b3480156102bb57600080fd5b506102fa60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d5a565b604051808215151515815260200191505060405180910390f35b34801561032057600080fd5b5061034d600480360381019080803515159060200190929190803515159060200190929190505050610d89565b6040518082815260200191505060405180910390f35b34801561036f57600080fd5b506103a4600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e1b565b005b3480156103b257600080fd5b506103d160048036038101908080359060200190929190505050611020565b604051808215151515815260200191505060405180910390f35b3480156103f757600080fd5b5061041660048036038101908080359060200190929190505050611105565b6040518082815260200191505060405180910390f35b34801561043857600080fd5b50610457600480360381019080803590602001909291905050506111d0565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018060200183151515158152602001828103825284818151815260200191508051906020019080838360005b838110156104d95780820151818401526020810190506104be565b50505050905090810190601f1680156105065780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34801561052357600080fd5b5061052c6112c5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561056f578082015181840152602081019050610554565b505050509050019250505060405180910390f35b34801561058f57600080fd5b506105d06004803603810190808035906020019092919080359060200190929190803515159060200190929190803515159060200190929190505050611353565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106135780820151818401526020810190506105f8565b505050509050019250505060405180910390f35b34801561063357600080fd5b50610652600480360381019080803590602001909291905050506114c4565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561069557808201518184015260208101905061067a565b505050509050019250505060405180910390f35b3480156106b557600080fd5b506106be611701565b6040518082815260200191505060405180910390f35b3480156106e057600080fd5b506106ff60048036038101908080359060200190929190505050611707565b005b34801561070d57600080fd5b5061072c600480360381019080803590602001909291905050506117c1565b005b34801561073a57600080fd5b506107bf600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061199e565b6040518082815260200191505060405180910390f35b3480156107e157600080fd5b506107ea6119bd565b6040518082815260200191505060405180910390f35b34801561080c57600080fd5b506108156119c2565b6040518082815260200191505060405180910390f35b34801561083757600080fd5b5061088c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506119c8565b005b34801561089a57600080fd5b506108b960048036038101908080359060200190929190505050611cdd565b005b6003818154811015156108ca57fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561093557600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561098e57600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610b13578273ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2157fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610b06576003600160038054905003815481101515610a7f57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610ab957fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b13565b81806001019250506109eb565b6001600381818054905003915081610b2b91906120fe565b506003805490506004541115610b4a57610b49600380549050611707565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a2505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610beb57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610c5657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610c8657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35050505050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610e1457838015610dc8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610dfb5750828015610dfa575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610e07576001820191505b8080600101915050610d91565b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e5557600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610eaf57600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610ed657600080fd5b60016003805490500160045460328211158015610ef35750818111155b8015610f00575060008114155b8015610f0d575060008214155b1515610f1857600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b6000806000809150600090505b6003805490508110156110fd5760016000858152602001908152602001600020600060038381548110151561105e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156110dd576001820191505b6004548214156110f057600192506110fe565b808060010191505061102d565b5b5050919050565b600080600090505b6003805490508110156111ca5760016000848152602001908152602001600020600060038381548110151561113e57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16156111bd576001820191505b808060010191505061110d565b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690806001015490806002018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156112a85780601f1061127d576101008083540402835291602001916112a8565b820191906000526020600020905b81548152906001019060200180831161128b57829003601f168201915b5050505050908060030160009054906101000a900460ff16905084565b6060600380548060200260200160405190810160405280929190818152602001828054801561134957602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ff575b5050505050905090565b60608060008060055460405190808252806020026020018201604052801561138a5781602001602082028038833980820191505090505b50925060009150600090505b600554811015611436578580156113cd575060008082815260200190815260200160002060030160009054906101000a900460ff16155b8061140057508480156113ff575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b156114295780838381518110151561141457fe5b90602001906020020181815250506001820191505b8080600101915050611396565b8787036040519080825280602002602001820160405280156114675781602001602082028038833980820191505090505b5093508790505b868110156114b957828181518110151561148457fe5b906020019060200201518489830381518110151561149e57fe5b9060200190602002018181525050808060010191505061146e565b505050949350505050565b6060806000806003805490506040519080825280602002602001820160405280156114fe5781602001602082028038833980820191505090505b50925060009150600090505b60038054905081101561164b5760016000868152602001908152602001600020600060038381548110151561153b57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561163e576003818154811015156115c257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156115fb57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b808060010191505061150a565b8160405190808252806020026020018201604052801561167a5781602001602082028038833980820191505090505b509350600090505b818110156116f957828181518110151561169857fe5b9060200190602002015184828151811015156116b057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250508080600101915050611682565b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561174157600080fd5b60038054905081603282111580156117595750818111155b8015611766575060008114155b8015611773575060008214155b151561177e57600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a1505050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561181a57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561187657600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156118e257600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361199785611cdd565b5050505050565b60006119ab848484611f85565b90506119b6816117c1565b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515611a0457600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611a5d57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515611ab757600080fd5b600092505b600380549050831015611ba0578473ffffffffffffffffffffffffffffffffffffffff16600384815481101515611aef57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b935783600384815481101515611b4657fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611ba0565b8280600101935050611abc565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25050505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611d3857600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611da357600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611dd357600080fd5b611ddc86611020565b15611f7d57600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611efa8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611ef05780601f10611ec557610100808354040283529160200191611ef0565b820191906000526020600020905b815481529060010190602001808311611ed357829003601f168201915b50505050506120d7565b15611f3157857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611f7c565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b505050505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611fae57600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060208201518160010155604082015181600201908051906020019061206d92919061212a565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a2509392505050565b6000806040516020840160008287838a8c6187965a03f19250505080915050949350505050565b8154818355818111156121255781836000526020600020918201910161212491906121aa565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061216b57805160ff1916838001178555612199565b82800160010185558215612199579182015b8281111561219857825182559160200191906001019061217d565b5b5090506121a691906121aa565b5090565b6121cc91905b808211156121c85760008160009055506001016121b0565b5090565b905600a165627a7a72305820b0d992f257e70d565448b927a3ae764342039a864112d5d5bb1ac1bd52e4104e00290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000060000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc1900000000000000000000000090ae948bb410838bff9d024ed849df6a7267dacf000000000000000000000000740a5c5742833bed72489fe6bf7efc9b79428989" + }, + "0x0cB1437200aea736788f1Fc56F327c0456c3598D": { + "balance": "250000000000000000" + }, + "0x74dd76E24B2CFB43C1b1a4498295d553D0843746": { + "balance": "250000000000000000" + }, + "0xeeB4CEEe443F9e0D17BdBD6Daa241681EE5E51c2": { + "balance": "250000000000000000" + }, + "0xA005caEa55375ae20e3aAEF746113535503ABC19": { + "balance": "250000000000000000" + }, + "0x90ae948bB410838bff9D024ed849dF6A7267Dacf": { + "balance": "250000000000000000" + }, + "0x740a5C5742833bEd72489fE6bf7EFc9B79428989": { + "balance": "250000000000000000" + }, + "0x1204700000000000000000000000000000000001": { + "constructor": "0x60806040523480156200001157600080fd5b5060405162002d7138038062002d71833981018060405260608110156200003757600080fd5b81019080805190602001909291908051906020019092919080516401000000008111156200006457600080fd5b828101905060208101848111156200007b57600080fd5b81518560208202830111640100000000821117156200009957600080fd5b5050929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620001e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602481526020018062002d216024913960400191505060405180910390fd5b60018151101562000242576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018062002d45602c913960400191505060405180910390fd5b62000253836200046160201b60201c565b6200026482620005c360201b60201c565b60008090505b81518110156200040e57600073ffffffffffffffffffffffffffffffffffffffff168282815181106200029957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156200032c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b6001600360008484815181106200033f57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115620003a057fe5b02179055508060036000848481518110620003b757fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018190555080806001019150506200026a565b508060029080519060200190620004279291906200064a565b50600260019080546200043c929190620006d9565b506001600060146101000a81548160ff02191690831515021790555050505062000776565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000505576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b828054828255906000526020600020908101928215620006c6579160200282015b82811115620006c55782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906200066b565b5b509050620006d5919062000730565b5090565b8280548282559060005260206000209081019282156200071d5760005260206000209182015b828111156200071c578254825591600101919060010190620006ff565b5b5090506200072c919062000730565b5090565b6200077391905b808211156200076f57600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000737565b5090565b90565b61259b80620007866000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80639f723637116100b8578063b98049091161007c578063b980490914610562578063bd21442a146105be578063c805f68b14610681578063d826b7f1146106c5578063f2fde38b14610733578063f3aeac021461077757610137565b80639f723637146103dc578063a00745b61461043b578063a6940b0714610497578063b3f05b97146104e1578063b7ab4db51461050357610137565b8063455701d6116100ff578063455701d6146102c35780634d238c8e1461032257806375286211146103665780638da5cb5b146103705780638f32d59b146103ba57610137565b8063267fa41d1461013c57806334ba3c1b1461019857806340550a1c146101b657806340a141ff146102125780634183495514610256575b600080fd5b61017e6004803603602081101561015257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107d3565b604051808215151515815260200191505060405180910390f35b6101a0610845565b6040518082815260200191505060405180910390f35b6101f8600480360360208110156101cc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610852565b604051808215151515815260200191505060405180910390f35b6102546004803603602081101561022857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610933565b005b6102986004803603602081101561026c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610a7e565b604051808360038111156102a857fe5b60ff1681526020018281526020019250505060405180910390f35b6102cb610aaf565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561030e5780820151818401526020810190506102f3565b505050509050019250505060405180910390f35b6103646004803603602081101561033857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b3d565b005b61036e610d47565b005b6103786111b2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103c26111db565b604051808215151515815260200191505060405180910390f35b6103e4611232565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561042757808201518184015260208101905061040c565b505050509050019250505060405180910390f35b61047d6004803603602081101561045157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611361565b604051808215151515815260200191505060405180910390f35b61049f611442565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6104e9611468565b604051808215151515815260200191505060405180910390f35b61050b61147b565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561054e578082015181840152602081019050610533565b505050509050019250505060405180910390f35b6105a46004803603602081101561057857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611509565b604051808215151515815260200191505060405180910390f35b61067f600480360360808110156105d457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561063b57600080fd5b82018360208201111561064d57600080fd5b8035906020019184600183028401116401000000008311171561066f57600080fd5b909192939192939050505061157a565b005b6106c36004803603602081101561069757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506117d6565b005b610731600480360360608110156106db57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611989565b005b6107756004803603602081101561074957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611be3565b005b6107b96004803603602081101561078d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c69565b604051808215151515815260200191505060405180910390f35b6000600160038111156107e257fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561083d57fe5b149050919050565b6000600180549050905090565b60006001600381111561086157fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156108bc57fe5b148061092c57506003808111156108cf57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561092a57fe5b145b9050919050565b61093b6111db565b6109ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610a12576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124e16022913960400191505060405180910390fd5b80610a1c81610852565b610a71576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b610a7a82611cdb565b5050565b60036020528060005260406000206000915090508060000160009054906101000a900460ff16908060010154905082565b60606002805480602002602001604051908101604052809291908181526020018280548015610b3357602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610ae9575b5050505050905090565b610b456111db565b610bb7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610c1c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124e16022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610cbf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b610cc881610852565b15610d3b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f546869732076616c696461746f7220697320616c72656164792061637469766581525060200191505060405180910390fd5b610d4481611f2e565b50565b600060149054906101000a900460ff1615610dca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f56616c696461746f72207365742069732066696e616c697a656400000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e8d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b6001600060146101000a81548160ff021916908315150217905550600073ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610fbe57600060036000600260016002805490500381548110610f1a57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060018160000160006101000a81548160ff02191690836003811115610fa257fe5b02179055506001600280549050038160010181905550506110f1565b600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff0219169083600381111561103f57fe5b0217905550600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506000600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b6002600190805461110392919061238d565b507f8564cd629b15f47dc310d45bcbfc9bcf5420b0d51bf0659a16c67f91d27632536001604051808060200182810382528381815481526020019150805480156111a257602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611158575b50509250505060405180910390a1565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b606060018054905060028054905011156112d45760028054806020026020016040519081016040528092919081815260200182805480156112c857602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161127e575b5050505050905061135e565b600180548060200260200160405190810160405280929190818152602001828054801561135657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161130c575b505050505090505b90565b60006002600381111561137057fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156113cb57fe5b148061143b57506003808111156113de57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561143957fe5b145b9050919050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1681565b606060018054806020026020016040519081016040528092919081815260200182805480156114ff57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116114b5575b5050505050905090565b600060038081111561151757fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561157257fe5b149050919050565b8461158481610852565b6115d9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b846115e381610852565b611638576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b844381106116ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611771576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b858773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff167f729a19138e072a5a8d3a56d74ae0b5c84530f09aacd6e12b24c5b2fdc3f8a3d060405160405180910390a45050505050505050565b6117de6111db565b611850576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156118d6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001806124746024913960400191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561197d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260408152602001806125036040913960400191505060405180910390fd5b61198681612003565b50565b8261199381610852565b6119e8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b826119f281610852565b611a47576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806124bf6022913960400191505060405180910390fd5b82438110611abd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b80576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b838573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fbc459bd9db54016b1966d0fe812bbe0a82cd627ae3eacd01727dc63a432ca41b60405160405180910390a4505050505050565b611beb6111db565b611c5d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b611c668161208a565b50565b600060026003811115611c7857fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff166003811115611cd357fe5b149050919050565b600160028054905011611d39576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260278152602001806124986027913960400191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154905060006001600280549050039050600060028281548110611d9c57fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508060028481548110611dd757fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506002805480919060019003611e7b91906123df565b5060038060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115611eda57fe5b021790555083600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611f286121eb565b50505050565b6002600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115611f8d57fe5b021790555060028190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506120006121eb565b50565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561212d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060146101000a81548160ff021916908315150217905550600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a084718a600143034060026040518363ffffffff1660e01b8152600401808381526020018060200182810382528381815481526020019150805480156122da57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311612290575b50509350505050602060405180830381600087803b1580156122fb57600080fd5b505af115801561230f573d6000803e3d6000fd5b505050506040513d602081101561232557600080fd5b810190808051906020019092919050505061238b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d815260200180612543602d913960400191505060405180910390fd5b565b8280548282559060005260206000209081019282156123ce5760005260206000209182015b828111156123cd5782548255916001019190600101906123b2565b5b5090506123db919061240b565b5090565b81548183558181111561240657818360005260206000209182019101612405919061244e565b5b505050565b61244b91905b8082111561244757600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550600101612411565b5090565b90565b61247091905b8082111561246c576000816000905550600101612454565b5090565b9056fe52656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f72206c65667441646472657373206973206e6f7420616e206163746976652076616c696461746f7256616c696461746f7220736574206973206e6f742066696e616c697a6564207965744e65772072656c617920636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6552656c617920636f6e747261637420496e6974696174654368616e67652063616c6c6261636b206661696c6564a165627a7a72305820f9ae418c0431b0892a4b904426be15d82d8486d433a3d6b9814bd5d1a377bb30002952656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f7220696e697469616c6c790000000000000000000000001204700000000000000000000000000000000005000000000000000000000000120470000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003000000000000000000000000d65b4c25a4ce1e024ff13425df1e0e574a1a0e9b00000000000000000000000083329c3fd90d7ee2efd546e0dc6453e9172a0643000000000000000000000000de15831ac319dab5eae5fd1fd9d52876c5e50f20" + }, + "0x1204700000000000000000000000000000000000": { + "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561006557600080fd5b506040516040806114d48339810180604052604081101561008557600080fd5b810190808051906020019092919080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a361016b8261018160201b60201c565b61017a816102e260201b60201c565b50506104f4565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610224576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610386576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561042d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260428152602001806114926042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b610f8f806105036000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063b7ab4db511610071578063b7ab4db514610209578063bd96567714610268578063c476dd40146102ac578063d3e848f11461034f578063d69f13bb14610399578063f2fde38b146103e7576100a9565b806375286211146100ae5780638da5cb5b146100b85780638f32d59b14610102578063a084718a14610124578063ae3783d6146101bf575b600080fd5b6100b661042b565b005b6100c0610572565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61010a61059b565b604051808215151515815260200191505060405180910390f35b6101a56004803603604081101561013a57600080fd5b81019080803590602001909291908035906020019064010000000081111561016157600080fd5b82018360208201111561017357600080fd5b8035906020019184602083028401116401000000008311171561019557600080fd5b90919293919293905050506105f2565b604051808215151515815260200191505060405180910390f35b6101c761070c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610211610732565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610254578082015181840152602081019050610239565b505050509050019250505060405180910390f35b6102aa6004803603602081101561027e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610832565b005b61034d600480360360608110156102c257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561030957600080fd5b82018360208201111561031b57600080fd5b8035906020019184600183028401116401000000008311171561033d57600080fd5b90919293919293905050506108b8565b005b6103576109e7565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103e5600480360360408110156103af57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a0d565b005b610429600480360360208110156103fd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b06565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f53656e646572206973206e6f742073797374656d00000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663752862116040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561055857600080fd5b505af115801561056c573d6000803e3d6000fd5b50505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461069a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180610f426022913960400191505060405180910390fd5b837f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89848460405180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600190509392505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b7ab4db56040518163ffffffff1660e01b815260040160006040518083038186803b15801561079c57600080fd5b505afa1580156107b0573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060208110156107da57600080fd5b8101908080516401000000008111156107f257600080fd5b8281019050602081018481111561080857600080fd5b815185602082028301116401000000008211171561082557600080fd5b5050929190505050905090565b61083a61059b565b6108ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6108b581610b8c565b50565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bd21442a33868686866040518663ffffffff1660e01b8152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b1580156109c957600080fd5b505af11580156109dd573d6000803e3d6000fd5b5050505050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d826b7f13384846040518463ffffffff1660e01b8152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050600060405180830381600087803b158015610aea57600080fd5b505af1158015610afe573d6000803e3d6000fd5b505050505050565b610b0e61059b565b610b80576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b610b8981610d9e565b50565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610c30576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610cd7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526042815260200180610f006042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610e41576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fe4e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6553656e646572206973206e6f74207468652052656c6179656420636f6e7472616374a165627a7a723058204045b88d4bc25183a0d657cfec6f8f8b976fd6aca20ab02e07cc6d76f43e7bd100294e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6500000000000000000000000012047000000000000000000000000000000000050000000000000000000000001204700000000000000000000000000000000001" + }, + "0x1204700000000000000000000000000000000002": { + "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503480156200006657600080fd5b5060405160408062001bad833981018060405260408110156200008857600080fd5b810190808051906020019092919080519060200190929190505050620000b36200016360201b60201c565b60786000805490501462000113576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018062001b866027913960400191505060405180910390fd5b81600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600a81905550505062000878565b60405180610f0001604052806704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704395680a6af46808152602001670438cfa9de4a0e808152602001670437eeee904c06808152602001670436b44ebcb52e8081526020016704351fca638586808152602001670433316184bd0e808152602001670430e914205bc680815260200167042e46e23661ae80815260200167042b4acbc6cec6808152602001670427f4d0d1a30e80815260200167042444f156de868081526020016704203b2d56812e80815260200167041bd784d08b0680815260200167041719f7c4fc0e808152602001670412028633d44680815260200167040c91301d13ae808152602001670406c5f580ba46808152602001670400a0d65ec80e8081526020016703fa21d2b73d068081526020016703f348ea8a192e8081526020016703ec161dd75c868081526020016703e4896c9f070e8081526020016703dca2d6e118c68081526020016703d4625c9d91ae8081526020016703cbc7fdd471c68081526020016703c2d3ba85b90e8081526020016703b98592b167868081526020016703afdd86577d2e8081526020016703a5db9577fa0680815260200167039b7fc012de0e808152602001670390ca06282946808152602001670385ba67b7dbae80815260200167037a50e4c1f54680815260200167036e8d7d46760e8081526020016703627031455e06808152602001670355f900bead2e80815260200167034927ebb2638680815260200167033bfcf220810e80815260200167032e78140905c680815260200167032099516bf1ae80815260200167031260aa4944c6808152602001670303ce1ea0ff0e8081526020016702f4e1ae7320868081526020016702e59b59bfa92e8081526020016702d5fb208699068081526020016702c60102c7f00e8081526020016702b5ad0083ae468081526020016702a4ff19b9d3ae808152602001670293f74e6a6046808152602001670282959e95540e808152602001670270da0a3aaf0680815260200167025ec4915a712e80815260200167024c5533f49a86808152602001670238da1d6b04400081526020016702260c5cdf4d240081526020016702138f83989f90008152602001670201639196fb840081526020016701ef8886da61000081526020016701ddfe6362d0040081526020016701ccc5273048900081526020016701bbdcd242caa40081526020016701ab45649a564000815260200167019afede36eb6400815260200167018b093f188a1000815260200167017b64873f324400815260200167016c10b6aae40000815260200167015d0dcd5b9f4400815260200167014e5bcb51641000815260200167013ffab08c3264008152602001670131ea7d0c0a400081526020016701242b30d0eba4008152602001670116bccbdad6900081526020016701099f4e29cb0400815260200166fcd2b7bdc90000815260200166f0570896d08400815260200166e42c40b4e19000815260200166d8526017fc2400815260200166ccc966c0204000815260200166c19154ad4de400815260200166b6aa29df851000815260200166ac13e656c5c400815260200166a1ce8a1310000081526020016697da151463c4008152602001668e36875ac1100081526020016684e3e0e627e4008152602001667be221b6984000815260200166733149cc1224008152602001666ad1592695900081526020016662c24fc62284008152602001665b042daab900008152602001665396f2d45904008152602001664c7a9f4302900081526020016645af32f6b5a4008152602001663f34adef724000815260200166390b102d386400815260200166333259b00810008152602001662daa8a77e144008152602001662873a284c40000815260200166238da1d6b044008152602001661ef8886da610008152602001661ab45649a5640081526020016616c10b6aae4000815260200166131ea7d0c0a4008152602001660fcd2b7bdc90008152602001660ccc966c0204008152602001660a1ce8a131000081526020016607be221b69840081526020016605b042daab900081526020016603f34adef7240081526020016602873a284c4000815260200166016c10b6aae400815260200165a1ce8a1310008152602001652873a284c4008152506000906078620007e5929190620007fe565b5062080520600181905550607860015402600281905550565b8280548282559060005260206000209081019282156200083d579160200282015b828111156200083c5782518255916020019190600101906200081f565b5b5090506200084c919062000850565b5090565b6200087591905b808211156200087157600081600090555060010162000857565b5090565b90565b6112fe80620008886000396000f3fe608060405234801561001057600080fd5b50600436106101205760003560e01c80634476d66a116100ad57806394f7f62b1161007157806394f7f62b1461045d578063df6a50301461049f578063e6e100db146104bd578063f91c289814610515578063fb1ac5251461068057610120565b80634476d66a146103515780634f4013fb14610393578063553a5c85146103b157806358ceb672146103cf57806360d4b8be146103d957610120565b80631a488047116100f45780631a488047146101f157806330f6eb161461020f57806333ea51a81461027157806337339a16146102b55780633d84b8c1146102f957610120565b8062f380f414610125578063078d8e7a1461016f5780630f411cdb1461019157806318129375146101af575b600080fd5b61012d61069e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101776106c4565b604051808215151515815260200191505060405180910390f35b6101996106d4565b6040518082815260200191505060405180910390f35b6101db600480360360208110156101c557600080fd5b81019080803590602001909291905050506106da565b6040518082815260200191505060405180910390f35b6101f96106fb565b6040518082815260200191505060405180910390f35b61025b6004803603604081101561022557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610701565b6040518082815260200191505060405180910390f35b6102b36004803603602081101561028757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610726565b005b6102f7600480360360208110156102cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506107a7565b005b61033b6004803603602081101561030f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108ae565b6040518082815260200191505060405180910390f35b61037d6004803603602081101561036757600080fd5b81019080803590602001909291905050506108c6565b6040518082815260200191505060405180910390f35b61039b6108de565b6040518082815260200191505060405180910390f35b6103b96108e3565b6040518082815260200191505060405180910390f35b6103d76108e9565b005b61041b600480360360208110156103ef57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6104896004803603602081101561047357600080fd5b8101908080359060200190929190505050610980565b6040518082815260200191505060405180910390f35b6104a76109c4565b6040518082815260200191505060405180910390f35b6104ff600480360360208110156104d357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109ca565b6040518082815260200191505060405180910390f35b6105e16004803603604081101561052b57600080fd5b810190808035906020019064010000000081111561054857600080fd5b82018360208201111561055a57600080fd5b8035906020019184602083028401116401000000008311171561057c57600080fd5b90919293919293908035906020019064010000000081111561059d57600080fd5b8201836020820111156105af57600080fd5b803590602001918460208302840111640100000000831117156105d157600080fd5b90919293919293905050506109e2565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561062857808201518184015260208101905061060d565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561066a57808201518184015260208101905061064f565b5050505090500194505050505060405180910390f35b610688610edb565b6040518082815260200191505060405180910390f35b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006106cf43610ee1565b905090565b60025481565b600081815481106106e757fe5b906000526020600020016000915090505481565b600a5481565b6008602052816000526040600020602052806000526040600020600091509150505481565b80600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461086a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f742074686520636f6d6d756e6974792066756e6481525060200191505060405180910390fd5b80600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60066020528060005260406000206000915090505481565b60076020528060005260406000206000915090505481565b607881565b60035481565b600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055565b600c6020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061098b82610ee1565b1561099957600090506109bf565b600060015483816109a657fe5b04815481106109b157fe5b906000526020600020015490505b919050565b60045481565b60056020528060005260406000206000915090505481565b606080600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610aa8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f43616c6c6572206973206e6f74207468652073797374656d000000000000000081525060200191505060405180910390fd5b838390508686905014610b06576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260258152602001806112ae6025913960400191505060405180910390fd5b60018686905014610b7f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f42656e65666163746f7273206c697374206c656e677468206973206e6f74203181525060200191505060405180910390fd5b600084846000818110610b8e57fe5b9050602002013561ffff1661ffff1614610bf3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061128c6022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1686866000818110610c1857fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161480610c5c5750610c5b43610ee1565b5b15610cd2576000604051908082528060200260200182016040528015610c915781602001602082028038833980820191505090505b506000604051908082528060200260200182016040528015610cc25781602001602082028038833980820191505090505b5081915080905091509150610ed2565b60606002604051908082528060200260200182016040528015610d045781602001602082028038833980820191505090505b50905060608151604051908082528060200260200182016040528015610d395781602001602082028038833980820191505090505b509050610d6e88886000818110610d4c57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff16610ef0565b82600081518110610d7b57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050610dbe43610980565b81600081518110610dcb57fe5b602002602001018181525050610e02600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16610ef0565b82600181518110610e0f57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600a5481600181518110610e5957fe5b602002602001018181525050610e9782600081518110610e7557fe5b602002602001015182600081518110610e8a57fe5b6020026020010151610f9d565b610ec982600181518110610ea757fe5b602002602001015182600181518110610ebc57fe5b6020026020010151611145565b81819350935050505b94509492505050565b60015481565b60006002548210159050919050565b600080600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f935782915050610f98565b809150505b919050565b61100081600860008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060004381526020019081526020016000205461120390919063ffffffff16565b600860008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000438152602001908152602001600020819055506110a681600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461120390919063ffffffff16565b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061110f81600760004381526020019081526020016000205461120390919063ffffffff16565b600760004381526020019081526020016000208190555061113b8160035461120390919063ffffffff16565b6003819055505050565b61115a8160045461120390919063ffffffff16565b6004819055506111b281600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205461120390919063ffffffff16565b600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506111ff8282610f9d565b5050565b600080828401905083811015611281576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4f766572666c6f77206572726f7200000000000000000000000000000000000081525060200191505060405180910390fd5b809150509291505056fe42656e65666163746f72206973206e6f742074686520626c6f636b20617574686f7242656e65666163746f72732f7479706573206c697374206c656e6774682064696666657273a165627a7a72305820ef1c2590551666e2ac227034529ee69c33ec91868d23c0a0a887d514094ec6030029526577617264206375727665206973206e6f7420746865207265717569726564206c656e67746800000000000000000000000012047000000000000000000000000000000000030000000000000000000000000000000000000000000000000856d3dfb6e26d00" + }, + "0x1204700000000000000000000000000000000004": { + "balance": "41198207933333333690000000", + "constructor": "0x608060405260006001556a22140f53c2d216263ba2803073ffffffffffffffffffffffffffffffffffffffff16311462000085576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806200150b6023913960400191505060405180910390fd5b620000956200010260201b60201c565b6a22140f53c2d216263ba28060015414620000fc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526028815260200180620014ba6028913960400191505060405180910390fd5b62001133565b62000136722d4606b65c033769968bcdc63881b90b0853f569264876ff9c85a7a8ee00635df22bc06200105960201b60201c565b6200016a726e371c454a2d081f3966c180ba2c6165d87de66901c3c03b52c63f027880635df22bc06200105960201b60201c565b6200019e72c2f65230815d30eaa1a4d057bcf0b72fe3cc4e6902a5a058fc295ed00000635df22bc06200105960201b60201c565b620001d37303cccdc799d4dc37d56e3f9dba7f9c210fa1086f6902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200020873047d955877a55fbdac768573a9259f29b103a0666902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200023d7306920bb91f7027176cf373996d39b539ba436d876969e10de76676d0800000635d79dee06200105960201b60201c565b620002727306fdb93aa64f33a8fb40a36c462a3f7a074d632c6901c3c03b52c63f027880635df22bc06200105960201b60201c565b620002a7730ad7ba4af33b485e6f2505c417554631a3e5643f6902a5a058fc295ed00000635df22bc06200105960201b60201c565b620002dc730dd959deb4c458cc2ac379898bf2c99f7a8f399b6901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000311731153818a2eb49f0a71b27313c32814fc02e4db50694220bb939da668600000635df22bc06200105960201b60201c565b6200034773120470000000000000000000000000000000000a6a084595161401484a000000635d79dee06200105960201b60201c565b6200037c7315696134ebeed360dc90dc97ddd00bd07e1c11e96934f086f3b33b68400000635df22bc06200105960201b60201c565b620003b1731f0c30b1aa4c468b5beb02bac8df8f27617a2296692346646590efbaf71200635df22bc06200105960201b60201c565b620003e6732540ded041b6fedc0ff6f0cf26b891ec97c954006901c3c03b52c63f027880635df22bc06200105960201b60201c565b6200041b7325ae7b45d8646580dfcae403d29164729eb8642f691a784379d99db4200000635df22bc06200105960201b60201c565b6200045073349ebc5a6e853df121c84e999081e5992928e64f6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000485733885d15573e45228dd54cd4fde9bfac64d702ed46901c3c03b52c63f027880635df22bc06200105960201b60201c565b620004ba733a9d83766c03c465851a38daa364ef7deccd1ece6934f086f3b33b68400000635df22bc06200105960201b60201c565b620004ef733abaa3f24428d6028f5a7fc5b18ce9d04ccec2296902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000524733c9f867d9b3a595987e198786fa9ab722e5c2f9b6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000559733f11b4ad17fde4695cad64e109ae92a679d87bfc6969e10de76676d0800000635d79dee06200105960201b60201c565b6200058e733f12af735238c6e2fa45efb5b2f3fae82df4c9226901c3c03b52c63f027880635df22bc06200105960201b60201c565b620005c373404bb9c13364522133b363d5c4adb7a88056b19d6902d2cd2bb7a39658b500635df22bc06200105960201b60201c565b620005f873428ab4b019ee3a9b9863b2b4bf1885ce6dff9a736902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200062d7347428fc08e56388372e7c81ad4a1140d932d10966969e10de76676d0800000635d79dee06200105960201b60201c565b620006627348ee57faf61c0b963113e7921e6173629e6bc4436902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000697734d0aa1c3459bf41e3ad4e4f40bbf029cb5723d836934f086f3b33b68400000635df22bc06200105960201b60201c565b620006cc7357f33efad76d4b783cf42c9e6cb08f4425dfe96e6902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000701735b3fb4e1d6040615f3e681bec4c80b5d7c9580716901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000736735fbb9c482034d287c5b3848fc2f9272abdd5bfa26903878076a58c80674b00635df22bc06200105960201b60201c565b6200076b73656e5569bef7781bf0db199d32027766053501ff69438ec266600555e00000635df22bc06200105960201b60201c565b620007a073664f991cdb2ffe6b6a568ede65b0208dbcce6f72691a784379d99db4200000635df22bc06200105960201b60201c565b620007d67369af0912dd44dce2b2373db4021788cbad84ff356a0422ca8b0a00a4250000006360c3f9006200105960201b60201c565b6200080b736a0a5da2a48ea87c2a906c53b3373642c29a4b6c6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000840736cf32cc52e220c023c2d92b1d62310f46a6e2a136901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000875736d516767e4068fc331bdb331fba7578bdb07a68c69234b04a2777d0408ee00635df22bc06200105960201b60201c565b620008aa736dd10e41a7a84fe23ab35fefa2f46c9895f87a2d693c8c4bde2deef1680000635df22bc06200105960201b60201c565b620008df737030892dbf9c2048e796296dda597f145754a1856969e10de76676d0800000635d79dee06200105960201b60201c565b62000914737ed62cf71d519d3bf293ef90829508f92f4ccccb6902a5a058fc295ed00000635df22bc06200105960201b60201c565b6200094a73871ba4266793ad11da537d4857de7ad49eab662b6a017293b0a9e69fd9c00000635df22bc06200105960201b60201c565b6200097f73880e8b0ece0171edd0247f8d13d348d77a6b9b296903878076a58c80674b00635df22bc06200105960201b60201c565b620009b373887f2b16847248bc757b69f3c695f24ff344daf268e9062e03b5c2908780635df22bc06200105960201b60201c565b620009e8738c994ada51d35b8519424368807fb99c103366866969e10de76676d0800000635d79dee06200105960201b60201c565b62000a1d739196e46d664ceda55cb45a2cc5ab5bd1b7e614e26902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000a5273943c85b13f24083ec73815f7ba763b7c42ae02886902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000a87739467b762550673f08b14423f8562048d5e3694226902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000abc73949423db1bfee1ddec99c9d24a12a6ea27cb34896901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000af17396a5eb172efdf262ed6beaaf0e20c6af71831fc96934f086f3b33b68400000635df22bc06200105960201b60201c565b62000b2673a69dca0814eaadc89b6dbe94c5e2110497690f6c6903878076a58c80674b00635df22bc06200105960201b60201c565b62000b5b73a720a8ee90f5013cae9bf7bcac1d153e42815454691a784379d99db4200000635df22bc06200105960201b60201c565b62000b9073b080454f190e76eb8e719560fa8cae50c71bcea96901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000bc573b476ee7d610dae7b23b671ebc7bd6112e97729696902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000bfb73b561618a3ea959a5e363643b267c4cb8fe4b1df76a0422ca8b0a00a4250000006360c3f9006200105960201b60201c565b62000c3073b5b6d8885fbf28f843cc7886de242b811d6952056901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000c6573b61c11b6e42d459efaee8995c44db08507e468e169477d5529f68a63000000635df22bc06200105960201b60201c565b62000c9a73b999004b49c6b907d4278067da5c85195dcd7fc769081e0dddaff653f8b500635df22bc06200105960201b60201c565b62000ccf73be4888c5b021e5f16cd254de2d4eaf17625685c46934f086f3b33b68400000635df22bc06200105960201b60201c565b62000d0473c1d441a2ad43af7b4a3d8e3200d2ceb3a973099d6934f086f3b33b68400000635df22bc06200105960201b60201c565b62000d3973c58a20e290e858542d8e8bb07b600aeb9195fe306903878076a58c80674b00635df22bc06200105960201b60201c565b62000d6f73cfe7964b0b6412b013dc019bdf3afef58be565936a055084cc99f0bdbadd0000635df22bc06200105960201b60201c565b62000da473d33d4f83e85c92e0b53ffe4fc0e18b0e3632c0976901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000dd973d44fb8de580d34f44789408cc9335c9a9ce0ce4d691ad0235eb930a0540000635df22bc06200105960201b60201c565b62000e0e73dacd80d8e1d4f117515caa477ee7599cdfc766196902a5a058fc295ed00000635df22bc06200105960201b60201c565b62000e4373db6cc57168c07b83a00f1f8871538446068824fc691a784379d99db4200000635df22bc06200105960201b60201c565b62000e7873de6b493d368316b9078454e37dce4968482dfbe96969e10de76676d0800000635d79dee06200105960201b60201c565b62000ead73e23c7cb60189bb2fd60625d2c2747b1e68f107766934f086f3b33b68400000635df22bc06200105960201b60201c565b62000ee273e6e8a111c89b05337049de9349c7c4880a396ef1691a784379d99db4200000635df22bc06200105960201b60201c565b62000f1773ebbddf28bf3224791b0510a2ab8813f182fe4e2b6901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000f4c73f4e31018a926f64cb780cb9f5f027377bcfb26fc6907f0e10af47c1c700000635df22bc06200105960201b60201c565b62000f8173f8e6ecb4b0f17576525749bdf85524652cbf002e6901c3c03b52c63f027880635df22bc06200105960201b60201c565b62000fb773fd679097fe0f914642af9857e5799332fe2efa296a01a784379d99db42000000635d79dee06200105960201b60201c565b62000fed73fd7a30d3c2bd017a458610274c275059d308b2e76a01ff1675f219f5a8780000635df22bc06200105960201b60201c565b6200102273ffcf98c62c1bad480ab6846717b173a72e2dd090691a784379d99db4200000635df22bc06200105960201b60201c565b6200105773ffd9b871df6e93803c0877e98fc1722b39c00d786902a5a058fc295ed00000635df22bc06200105960201b60201c565b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060008160000154148015620010b4575060008160010154145b6200110b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526029815260200180620014e26029913960400191505060405180910390fd5b8260016000828254019250508190555082816000018190555081816001018190555050505050565b61037780620011436000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806318a5bbdc14610051578063192e7a7b146100b057806330f0dbe0146100f45780639976e12f14610112575b600080fd5b6100936004803603602081101561006757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610130565b604051808381526020018281526020019250505060405180910390f35b6100f2600480360360208110156100c657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610154565b005b6100fc610336565b6040518082815260200191505060405180910390f35b61011a610345565b6040518082815260200191505060405180910390f35b60006020528060005260406000206000915090508060000154908060010154905082565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816000015411610210576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f417661696c61626c6520616d6f756e742069732030000000000000000000000081525060200191505060405180910390fd5b80600101544211610289576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f486f6c64696e6720706572696f64206973206e6f74206f76657200000000000081525060200191505060405180910390fd5b600081600001549050600082600001819055508273ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156102e2573d6000803e3d6000fd5b508273ffffffffffffffffffffffffffffffffffffffff167f221c08a06b07a64803b3787861a3f276212fcccb51c2e6234077a9b8cb13047a826040518082815260200191505060405180910390a2505050565b6a22140f53c2d216263ba28081565b6001548156fea165627a7a72305820b0165b18a29fae78ec1f58bf69134a67ab02e21d059e757412318d8362284866002954617267657420616d6f756e742073686f756c6420657175616c2061637475616c20616d6f756e74486f6c64696e6720666f72207468697320616464726573732077617320616c7265616479207365742e42616c616e63652073686f756c6420657175616c2074617267657420616d6f756e742e" + }, + "0x1204700000000000000000000000000000000006": { + "constructor": "0x60806040523480156200001157600080fd5b5060405160208062003ed2833981018060405260208110156200003357600080fd5b8101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a362000111816200011860201b60201c565b506200027a565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001bc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b613c48806200028a6000396000f3fe608060405234801561001057600080fd5b50600436106101585760003560e01c806392698814116100c3578063e30bd7401161007c578063e30bd740146109b5578063eadf976014610a72578063ef5454d614610b17578063f25eb5c114610bc8578063f2fde38b14610bd2578063f6d339e414610c1657610158565b80639269881414610705578063ac4e73f91461074b578063ac72c120146107fc578063c3a3582514610842578063deb931a2146108d9578063df57b7421461094757610158565b80636795dbcd116101155780636795dbcd1461041c5780636a1acc3f146104df57806379ce9fac1461059c5780638da5cb5b146106025780638f32d59b1461064c57806390b97fc11461066e57610158565b806306b2ff471461015d57806319362a28146101b9578063267b69221461025e5780633f3935d1146102ff578063432ced04146103905780634f39ca59146103d6575b600080fd5b61019f6004803603602081101561017357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cd1565b604051808215151515815260200191505060405180910390f35b610244600480360360608110156101cf57600080fd5b8101908080359060200190929190803590602001906401000000008111156101f657600080fd5b82018360208201111561020857600080fd5b8035906020019184600183028401116401000000008311171561022a57600080fd5b909192939192939080359060200190929190505050610d31565b604051808215151515815260200191505060405180910390f35b61028a6004803603602081101561027457600080fd5b8101908080359060200190929190505050610fcb565b604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390f35b6103766004803603602081101561031557600080fd5b810190808035906020019064010000000081111561033257600080fd5b82018360208201111561034457600080fd5b8035906020019184600183028401116401000000008311171561036657600080fd5b909192939192939050505061102f565b604051808215151515815260200191505060405180910390f35b6103bc600480360360208110156103a657600080fd5b810190808035906020019092919050505061134e565b604051808215151515815260200191505060405180910390f35b610402600480360360208110156103ec57600080fd5b8101908080359060200190929190505050611546565b604051808215151515815260200191505060405180910390f35b61049d6004803603604081101561043257600080fd5b81019080803590602001909291908035906020019064010000000081111561045957600080fd5b82018360208201111561046b57600080fd5b8035906020019184600183028401116401000000008311171561048d57600080fd5b9091929391929390505050611a8d565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610521600480360360208110156104f557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611bb2565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610561578082015181840152602081019050610546565b50505050905090810190601f16801561058e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6105e8600480360360408110156105b257600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c62565b604051808215151515815260200191505060405180910390f35b61060a611f58565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610654611f81565b604051808215151515815260200191505060405180910390f35b6106ef6004803603604081101561068457600080fd5b8101908080359060200190929190803590602001906401000000008111156106ab57600080fd5b8201836020820111156106bd57600080fd5b803590602001918460018302840111640100000000831117156106df57600080fd5b9091929391929390505050611fd8565b6040518082815260200191505060405180910390f35b6107316004803603602081101561071b57600080fd5b81019080803590602001909291905050506120fa565b604051808215151515815260200191505060405180910390f35b6107e26004803603604081101561076157600080fd5b810190808035906020019064010000000081111561077e57600080fd5b82018360208201111561079057600080fd5b803590602001918460018302840111640100000000831117156107b257600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612245565b604051808215151515815260200191505060405180910390f35b6108286004803603602081101561081257600080fd5b81019080803590602001909291905050506127e7565b604051808215151515815260200191505060405180910390f35b6108c36004803603604081101561085857600080fd5b81019080803590602001909291908035906020019064010000000081111561087f57600080fd5b82018360208201111561089157600080fd5b803590602001918460018302840111640100000000831117156108b357600080fd5b9091929391929390505050612932565b6040518082815260200191505060405180910390f35b610905600480360360208110156108ef57600080fd5b8101908080359060200190929190505050612a57565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6109736004803603602081101561095d57600080fd5b8101908080359060200190929190505050612b72565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6109f7600480360360208110156109cb57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612c8d565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610a37578082015181840152602081019050610a1c565b50505050905090810190601f168015610a645780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610afd60048036036060811015610a8857600080fd5b810190808035906020019092919080359060200190640100000000811115610aaf57600080fd5b820183602082011115610ac157600080fd5b80359060200191846001830284011164010000000083111715610ae357600080fd5b909192939192939080359060200190929190505050612d6e565b604051808215151515815260200191505060405180910390f35b610bae60048036036040811015610b2d57600080fd5b8101908080359060200190640100000000811115610b4a57600080fd5b820183602082011115610b5c57600080fd5b80359060200191846001830284011164010000000083111715610b7e57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061300b565b604051808215151515815260200191505060405180910390f35b610bd0613281565b005b610c1460048036036020811015610be857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613674565b005b610cb760048036036060811015610c2c57600080fd5b810190808035906020019092919080359060200190640100000000811115610c5357600080fd5b820183602082011115610c6557600080fd5b80359060200191846001830284011164010000000083111715610c8757600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506136fa565b604051808215151515815260200191505060405180910390f35b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080546001816001161561010002031660029004905014159050919050565b600084600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610e0d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610ee5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b60016020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082565b600082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611156576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b83838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050503373ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611279576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4572726f723a204f6e6c79207768656e2070726f706f7365640000000000000081525060200191505060405180910390fd5b8484600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002091906112c7929190613b0e565b503373ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019250505092915050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611429576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f4572726f723a204f6e6c79207768656e20756e7265736572766564000000000081525060200191505060405180910390fd5b611431611f81565b6114a3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b336001600085815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff16837f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405160405180910390a36001915050919050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611622576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b823373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146116fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600260006001600088815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156117cb5780601f106117a95761010080835404028352918201916117cb565b820191906000526020600020905b8154815290600101906020018083116117b7575b5050915050604051809103902014156119da576001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd600260006001600089815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051808060200182810382528381815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561194a5780601f1061191f5761010080835404028352916020019161194a565b820191906000526020600020905b81548152906001019060200180831161192d57829003601f168201915b50509250505060405180910390a2600260006001600087815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006119d99190613b8e565b5b60016000858152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550503373ffffffffffffffffffffffffffffffffffffffff16847fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405160405180910390a3600192505050919050565b600083600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611b69576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b60016000868152602001908152602001600020600201848460405180838380828437808301925050509250505090815260200160405180910390205460001c9150509392505050565b60026020528060005260406000206000915090508054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611c5a5780601f10611c2f57610100808354040283529160200191611c5a565b820191906000526020600020905b815481529060010190602001808311611c3d57829003601f168201915b505050505081565b600082600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611d3e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b833373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611e16576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415611e9c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180613bfc6021913960400191505060405180910390fd5b836001600087815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16867f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405160405180910390a460019250505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b600083600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156120b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600086815260200190815260200160002060020184846040518083838082843780830192505050925050509081526020016040518091039020549150509392505050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156121d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561236c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b848460405180838380828437808301925050509250505060405180910390203373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612462576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b6000868660405180838380828437808301925050509250505060405180910390209050600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141580156125d3575080600260006001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156125c45780601f106125a25761010080835404028352918201916125c4565b820191906000526020600020905b8154815290600101906020018083116125b0575b50509150506040518091039020145b1561270a57600260006001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006126599190613b8e565b6001600082815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a25b846001600083815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508473ffffffffffffffffffffffffffffffffffffffff167f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600193505050509392505050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156128c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b600083600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612a0e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b60016000868152602001908152602001600020600201848460405180838380828437808301925050509250505090815260200160405180910390205460001c9150509392505050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612b33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b600081600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612c4e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612d625780601f10612d3757610100808354040283529160200191612d62565b820191906000526020600020905b815481529060010190602001808311612d4557829003601f168201915b50505050509050919050565b600084600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415612e4a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612f22576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b8360001b600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415613132576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b61313a611f81565b6131ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8484600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002091906131fa929190613b0e565b508273ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019150509392505050565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156133545780601f1061332957610100808354040283529160200191613354565b820191906000526020600020905b81548152906001019060200180831161333757829003601f168201915b5050505050600073ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415613439576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180806020018281038252838181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156135395780601f1061350e57610100808354040283529160200191613539565b820191906000526020600020905b81548152906001019060200180831161351c57829003601f168201915b50509250505060405180910390a260016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156135e55780601f106135c35761010080835404028352918201916135e5565b820191906000526020600020905b8154815290600101906020018083116135d1575b50509150506040518091039020815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006136719190613b8e565b50565b61367c611f81565b6136ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6136f7816139ad565b50565b600084600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156137d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146138ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff1660001b600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613a50576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10613b4f57803560ff1916838001178555613b7d565b82800160010185558215613b7d579182015b82811115613b7c578235825591602001919060010190613b61565b5b509050613b8a9190613bd6565b5090565b50805460018160011615610100020316600290046000825580601f10613bb45750613bd3565b601f016020900490600052602060002090810190613bd29190613bd6565b5b50565b613bf891905b80821115613bf4576000816000905550600101613bdc565b5090565b9056fe4572726f723a206e6f207472616e7366657220746f206164647265737320307830a165627a7a72305820fe186a600f547c59523f333a7eb66cd1de74492306acaa3db2cd4c8a1b426b5a00290000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000007": { + "constructor": "0x608060405234801561001057600080fd5b506040516040806108658339810180604052604081101561003057600080fd5b810190808051906020019092919080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3610116816101a160201b60201c565b81600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff167fcc57a97257d7703be97e9d538cb08b3fa1ff2784b776d505942d49a95dc2891460405160405180910390a25050610302565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610244576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b610554806103116000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80636ddc4a071461005c5780638da5cb5b146100a65780638f32d59b146100f0578063f2fde38b14610112578063fe64d6ff14610156575b600080fd5b61006461019a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100ae6101c0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100f86101e9565b604051808215151515815260200191505060405180910390f35b6101546004803603602081101561012857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610240565b005b6101986004803603602081101561016c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102c6565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6102486101e9565b6102ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6102c3816103c7565b50565b6102ce6101e9565b610340576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fcc57a97257d7703be97e9d538cb08b3fa1ff2784b776d505942d49a95dc2891460405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561046a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea165627a7a723058204d73401a5cbb107ba765457948a3c2a2612436446f9125c3696f0f4cc7e06980002900000000000000000000000012047000000000000000000000000000000000090000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000008": { + "constructor": "0x60806040523480156200001157600080fd5b506040516040806200298b8339810180604052620000339190810190620002a8565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062000141816200014960201b60201c565b5050620003da565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001bc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001b3906200032b565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000815190506200028b81620003a6565b92915050565b600081519050620002a281620003c0565b92915050565b60008060408385031215620002bc57600080fd5b6000620002cc8582860162000291565b9250506020620002df858286016200027a565b9150509250929050565b6000620002f8601f836200034d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034681620002e9565b9050919050565b600082825260208201905092915050565b60006200036b8262000386565b9050919050565b60006200037f826200035e565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b620003b1816200035e565b8114620003bd57600080fd5b50565b620003cb8162000372565b8114620003d757600080fd5b50565b6125a180620003ea6000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c8063954029c911610097578063eab7ad9211610066578063eab7ad92146102b2578063ee7ee0fd146102ce578063f2fde38b146102fe578063fbd74f841461031a576100f5565b8063954029c91461021a578063a999809f14610236578063b71da0d114610266578063da5393d514610296576100f5565b80636a564261116100d35780636a5642611461017857806370a05b18146101ae5780638da5cb5b146101de5780638f32d59b146101fc576100f5565b80631bab58f5146100fa5780632c5653e21461012a57806332d91c0e14610148575b600080fd5b610114600480360361010f9190810190611d82565b61034a565b60405161012191906123f7565b60405180910390f35b610132610767565b60405161013f919061233a565b60405180910390f35b610162600480360361015d9190810190611d82565b61078d565b60405161016f9190612355565b60405180910390f35b610192600480360361018d9190810190611d82565b61097e565b6040516101a597969594939291906122af565b60405180910390f35b6101c860048036036101c39190810190611d82565b610c2d565b6040516101d5919061228d565b60405180910390f35b6101e6610e1e565b6040516101f39190612257565b60405180910390f35b610204610e47565b6040516102119190612272565b60405180910390f35b610234600480360361022f9190810190611d82565b610e9e565b005b610250600480360361024b9190810190611d82565b610ff5565b60405161025d9190612355565b60405180910390f35b610280600480360361027b9190810190611d82565b6111e6565b60405161028d9190612272565b60405180910390f35b6102b060048036036102ab9190810190611d82565b611276565b005b6102cc60048036036102c79190810190611dd4565b611371565b005b6102e860048036036102e39190810190611d82565b611673565b6040516102f59190612272565b60405180910390f35b61031860048036036103139190810190611d82565b6117d9565b005b610334600480360361032f9190810190611d82565b61182c565b604051610341919061228d565b60405180910390f35b610352611b4b565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156103ba57600080fd5b505afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506103f29190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461045f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045690612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060e0016040529081600082018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105425780601f1061051757610100808354040283529160200191610542565b820191906000526020600020905b81548152906001019060200180831161052557829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105e45780601f106105b9576101008083540402835291602001916105e4565b820191906000526020600020905b8154815290600101906020018083116105c757829003601f168201915b50505050508152602001600282018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106865780601f1061065b57610100808354040283529160200191610686565b820191906000526020600020905b81548152906001019060200180831161066957829003601f168201915b50505050508152602001600382018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107285780601f106106fd57610100808354040283529160200191610728565b820191906000526020600020905b81548152906001019060200180831161070b57829003601f168201915b505050505081526020016004820160009054906101000a900460ff16151515158152602001600582015481526020016006820154815250509050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156107f757600080fd5b505afa15801561080b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061082f9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461089c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161089390612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156109725780601f1061094757610100808354040283529160200191610972565b820191906000526020600020905b81548152906001019060200180831161095557829003601f168201915b50505050509050919050565b6001602052806000526040600020600091509050806000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a2a5780601f106109ff57610100808354040283529160200191610a2a565b820191906000526020600020905b815481529060010190602001808311610a0d57829003601f168201915b505050505090806001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610ac85780601f10610a9d57610100808354040283529160200191610ac8565b820191906000526020600020905b815481529060010190602001808311610aab57829003601f168201915b505050505090806002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b665780601f10610b3b57610100808354040283529160200191610b66565b820191906000526020600020905b815481529060010190602001808311610b4957829003601f168201915b505050505090806003018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c045780601f10610bd957610100808354040283529160200191610c04565b820191906000526020600020905b815481529060010190602001808311610be757829003601f168201915b5050505050908060040160009054906101000a900460ff16908060050154908060060154905087565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b158015610c9757600080fd5b505afa158015610cab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ccf9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610d3c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d3390612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610e125780601f10610de757610100808354040283529160200191610e12565b820191906000526020600020905b815481529060010190602001808311610df557829003601f168201915b50505050509050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b158015610f0657600080fd5b505afa158015610f1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f3e9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610fab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fa290612377565b60405180910390fd5b43600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206006018190555050565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b15801561105f57600080fd5b505afa158015611073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110979190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611104576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110fb90612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156111da5780601f106111af576101008083540402835291602001916111da565b820191906000526020600020905b8154815290600101906020018083116111bd57829003601f168201915b50505050509050919050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060060154600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060050154109050919050565b61127e610e47565b6112bd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112b4906123d7565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561132d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161132490612397565b60405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156113d957600080fd5b505afa1580156113ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506114119190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461147e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161147590612377565b60405180910390fd5b8888600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000191906114cf929190611b8a565b508686600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001019190611521929190611c0a565b508484600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002019190611573929190611b8a565b508282600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030191906115c5929190611c0a565b5080600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160006101000a81548160ff02191690831515021790555043600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206005018190555050505050505050505050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b1580156116dd57600080fd5b505afa1580156116f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117159190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611782576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161177990612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160009054906101000a900460ff169050919050565b6117e1610e47565b611820576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611817906123d7565b60405180910390fd5b61182981611a1d565b50565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff1660e01b815260040160206040518083038186803b15801561189657600080fd5b505afa1580156118aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ce9190810190611dab565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461193b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161193290612377565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611a115780601f106119e657610100808354040283529160200191611a11565b820191906000526020600020905b8154815290600101906020018083116119f457829003601f168201915b50505050509050919050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611a8d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a84906123b7565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611bcb57803560ff1916838001178555611bf9565b82800160010185558215611bf9579182015b82811115611bf8578235825591602001919060010190611bdd565b5b509050611c069190611c8a565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611c4b57803560ff1916838001178555611c79565b82800160010185558215611c79579182015b82811115611c78578235825591602001919060010190611c5d565b5b509050611c869190611c8a565b5090565b611cac91905b80821115611ca8576000816000905550600101611c90565b5090565b90565b600081359050611cbe81612539565b92915050565b600081519050611cd381612539565b92915050565b600081359050611ce881612550565b92915050565b60008083601f840112611d0057600080fd5b8235905067ffffffffffffffff811115611d1957600080fd5b602083019150836001820283011115611d3157600080fd5b9250929050565b60008083601f840112611d4a57600080fd5b8235905067ffffffffffffffff811115611d6357600080fd5b602083019150836001820283011115611d7b57600080fd5b9250929050565b600060208284031215611d9457600080fd5b6000611da284828501611caf565b91505092915050565b600060208284031215611dbd57600080fd5b6000611dcb84828501611cc4565b91505092915050565b60008060008060008060008060008060c08b8d031215611df357600080fd5b6000611e018d828e01611caf565b9a505060208b013567ffffffffffffffff811115611e1e57600080fd5b611e2a8d828e01611cee565b995099505060408b013567ffffffffffffffff811115611e4957600080fd5b611e558d828e01611d38565b975097505060608b013567ffffffffffffffff811115611e7457600080fd5b611e808d828e01611cee565b955095505060808b013567ffffffffffffffff811115611e9f57600080fd5b611eab8d828e01611d38565b935093505060a0611ebe8d828e01611cd9565b9150509295989b9194979a5092959850565b611ed981612489565b82525050565b611ee88161249b565b82525050565b611ef78161249b565b82525050565b6000611f0882612424565b611f128185612456565b9350611f228185602086016124f5565b611f2b81612528565b840191505092915050565b6000611f4182612419565b611f4b8185612445565b9350611f5b8185602086016124f5565b611f6481612528565b840191505092915050565b6000611f7a82612419565b611f848185612456565b9350611f948185602086016124f5565b611f9d81612528565b840191505092915050565b611fb1816124d1565b82525050565b6000611fc28261243a565b611fcc8185612478565b9350611fdc8185602086016124f5565b611fe581612528565b840191505092915050565b6000611ffb8261242f565b6120058185612467565b93506120158185602086016124f5565b61201e81612528565b840191505092915050565b60006120348261242f565b61203e8185612478565b935061204e8185602086016124f5565b61205781612528565b840191505092915050565b600061206f601383612478565b91507f4572726f723a206f6e6c794c6f676963204462000000000000000000000000006000830152602082019050919050565b60006120af602983612478565b91507f4572726f723a206e65774c6f6f6b5570206973206e6f7420616c6c6f7765642060008301527f746f2062652030783000000000000000000000000000000000000000000000006020830152604082019050919050565b6000612115601f83612478565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b6000612155601383612478565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600060e08301600083015184820360008601526121a58282611f36565b915050602083015184820360208601526121bf8282611ff0565b915050604083015184820360408601526121d98282611f36565b915050606083015184820360608601526121f38282611ff0565b91505060808301516122086080860182611edf565b5060a083015161221b60a0860182612239565b5060c083015161222e60c0860182612239565b508091505092915050565b612242816124c7565b82525050565b612251816124c7565b82525050565b600060208201905061226c6000830184611ed0565b92915050565b60006020820190506122876000830184611eee565b92915050565b600060208201905081810360008301526122a78184611efd565b905092915050565b600060e08201905081810360008301526122c9818a611f6f565b905081810360208301526122dd8189612029565b905081810360408301526122f18188611f6f565b905081810360608301526123058187612029565b90506123146080830186611eee565b61232160a0830185612248565b61232e60c0830184612248565b98975050505050505050565b600060208201905061234f6000830184611fa8565b92915050565b6000602082019050818103600083015261236f8184611fb7565b905092915050565b6000602082019050818103600083015261239081612062565b9050919050565b600060208201905081810360008301526123b0816120a2565b9050919050565b600060208201905081810360008301526123d081612108565b9050919050565b600060208201905081810360008301526123f081612148565b9050919050565b600060208201905081810360008301526124118184612188565b905092915050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b6000612494826124a7565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006124dc826124e3565b9050919050565b60006124ee826124a7565b9050919050565b60005b838110156125135780820151818401526020810190506124f8565b83811115612522576000848401525b50505050565b6000601f19601f8301169050919050565b61254281612489565b811461254d57600080fd5b50565b6125598161249b565b811461256457600080fd5b5056fea265627a7a7230582045fea88fa096a11a15a5bb8e9f5993ca3ab8a38132e6c7a04eb7cd5458378c906c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000070000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000009": { + "constructor": "0x60806040523480156200001157600080fd5b506040516040806200208d8339810180604052620000339190810190620002a8565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555062000141816200014960201b60201c565b5050620003da565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001bc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001b3906200032b565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000815190506200028b81620003a6565b92915050565b600081519050620002a281620003c0565b92915050565b60008060408385031215620002bc57600080fd5b6000620002cc8582860162000291565b9250506020620002df858286016200027a565b9150509250929050565b6000620002f8601f836200034d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034681620002e9565b9050919050565b600082825260208201905092915050565b60006200036b8262000386565b9050919050565b60006200037f826200035e565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b620003b1816200035e565b8114620003bd57600080fd5b50565b620003cb8162000372565b8114620003d757600080fd5b50565b611ca380620003ea6000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80638f32d59b1161005b5780638f32d59b14610101578063b71da0d11461011f578063ec2626111461014f578063f2fde38b1461016d57610088565b806312127ed71461008d5780633f67c333146100a957806389c3ce1e146100b35780638da5cb5b146100e3575b600080fd5b6100a760048036036100a2919081019061118d565b610189565b005b6100b16107d7565b005b6100cd60048036036100c89190810190611164565b610a23565b6040516100da91906119c0565b60405180910390f35b6100eb610aee565b6040516100f891906117b7565b60405180910390f35b610109610b17565b604051610116919061186a565b60405180910390f35b61013960048036036101349190810190611164565b610b6e565b604051610146919061186a565b60405180910390f35b610157610c22565b6040516101649190611885565b60405180910390f35b61018760048036036101829190810190611164565b610c48565b005b610191610b17565b6101d0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101c790611940565b60405180910390fd5b600085511415610215576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161020c906118e0565b60405180910390fd5b60008451141561025a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161025190611980565b60405180910390fd5b60008351141561029f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610296906118a0565b60405180910390fd5b6000825114156102e4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102db906118c0565b60405180910390fd5b8480519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84886040518263ffffffff1660e01b815260040161034791906117b7565b60006040518083038186803b15801561035f57600080fd5b505afa158015610373573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061039c919081019061129f565b8051906020012014801561046c57508380519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a999809f886040518263ffffffff1660e01b815260040161040e91906117b7565b60006040518083038186803b15801561042657600080fd5b505afa15801561043a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061046391908101906112e0565b80519060200120145b801561053457508280519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a05b18886040518263ffffffff1660e01b81526004016104d691906117b7565b60006040518083038186803b1580156104ee57600080fd5b505afa158015610502573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061052b919081019061129f565b80519060200120145b80156105fc57508180519060200120600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166332d91c0e886040518263ffffffff1660e01b815260040161059e91906117b7565b60006040518083038186803b1580156105b657600080fd5b505afa1580156105ca573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506105f391908101906112e0565b80519060200120145b80156106b55750801515600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ee7ee0fd886040518263ffffffff1660e01b815260040161066191906117b7565b60206040518083038186803b15801561067957600080fd5b505afa15801561068d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506106b19190810190611276565b1515145b156106f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106ec90611960565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663eab7ad928787878787876040518763ffffffff1660e01b815260040161075a969594939291906117ed565b600060405180830381600087803b15801561077457600080fd5b505af1158015610788573d6000803e3d6000fd5b505050508573ffffffffffffffffffffffffffffffffffffffff167fc9e49a024d50440c73d2d12d0ae05064094dca76b46dc95e99ea6848eb8f27e960405160405180910390a2505050505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84336040518263ffffffff1660e01b815260040161083491906117d2565b60006040518083038186803b15801561084c57600080fd5b505afa158015610860573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610889919081019061129f565b5114156108cb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108c2906119a0565b60405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff1663b71da0d1336040518263ffffffff1660e01b815260040161090491906117d2565b60206040518083038186803b15801561091c57600080fd5b505afa158015610930573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506109549190810190611276565b15610994576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161098b90611900565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663954029c9336040518263ffffffff1660e01b81526004016109ef91906117d2565b600060405180830381600087803b158015610a0957600080fd5b505af1158015610a1d573d6000803e3d6000fd5b50505050565b610a2b610dc9565b610a33610dc9565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631bab58f5846040518263ffffffff1660e01b8152600401610a8e91906117b7565b60006040518083038186803b158015610aa657600080fd5b505afa158015610aba573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610ae39190810190611321565b905080915050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b71da0d1836040518263ffffffff1660e01b8152600401610bcb91906117b7565b60206040518083038186803b158015610be357600080fd5b505afa158015610bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c1b9190810190611276565b9050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610c50610b17565b610c8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c8690611940565b60405180910390fd5b610c9881610c9b565b50565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610d0b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d0290611920565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b600081359050610e1781611c24565b92915050565b600081359050610e2c81611c3b565b92915050565b600081519050610e4181611c3b565b92915050565b600082601f830112610e5857600080fd5b8151610e6b610e6682611a0f565b6119e2565b91508082526020830160208301858383011115610e8757600080fd5b610e92838284611be0565b50505092915050565b600082601f830112610eac57600080fd5b8135610ebf610eba82611a3b565b6119e2565b91508082526020830160208301858383011115610edb57600080fd5b610ee6838284611bd1565b50505092915050565b600082601f830112610f0057600080fd5b8151610f13610f0e82611a3b565b6119e2565b91508082526020830160208301858383011115610f2f57600080fd5b610f3a838284611be0565b50505092915050565b600082601f830112610f5457600080fd5b8151610f67610f6282611a67565b6119e2565b91508082526020830160208301858383011115610f8357600080fd5b610f8e838284611be0565b50505092915050565b600082601f830112610fa857600080fd5b8135610fbb610fb682611a93565b6119e2565b91508082526020830160208301858383011115610fd757600080fd5b610fe2838284611bd1565b50505092915050565b600082601f830112610ffc57600080fd5b815161100f61100a82611a93565b6119e2565b9150808252602083016020830185838301111561102b57600080fd5b611036838284611be0565b50505092915050565b600060e0828403121561105157600080fd5b61105b60e06119e2565b9050600082015167ffffffffffffffff81111561107757600080fd5b61108384828501610e47565b600083015250602082015167ffffffffffffffff8111156110a357600080fd5b6110af84828501610f43565b602083015250604082015167ffffffffffffffff8111156110cf57600080fd5b6110db84828501610e47565b604083015250606082015167ffffffffffffffff8111156110fb57600080fd5b61110784828501610f43565b606083015250608061111b84828501610e32565b60808301525060a061112f8482850161114f565b60a08301525060c06111438482850161114f565b60c08301525092915050565b60008151905061115e81611c52565b92915050565b60006020828403121561117657600080fd5b600061118484828501610e08565b91505092915050565b60008060008060008060c087890312156111a657600080fd5b60006111b489828a01610e08565b965050602087013567ffffffffffffffff8111156111d157600080fd5b6111dd89828a01610e9b565b955050604087013567ffffffffffffffff8111156111fa57600080fd5b61120689828a01610f97565b945050606087013567ffffffffffffffff81111561122357600080fd5b61122f89828a01610e9b565b935050608087013567ffffffffffffffff81111561124c57600080fd5b61125889828a01610f97565b92505060a061126989828a01610e1d565b9150509295509295509295565b60006020828403121561128857600080fd5b600061129684828501610e32565b91505092915050565b6000602082840312156112b157600080fd5b600082015167ffffffffffffffff8111156112cb57600080fd5b6112d784828501610eef565b91505092915050565b6000602082840312156112f257600080fd5b600082015167ffffffffffffffff81111561130c57600080fd5b61131884828501610feb565b91505092915050565b60006020828403121561133357600080fd5b600082015167ffffffffffffffff81111561134d57600080fd5b6113598482850161103f565b91505092915050565b61136b81611b77565b82525050565b61137a81611b2f565b82525050565b61138981611b41565b82525050565b61139881611b41565b82525050565b60006113a982611aca565b6113b38185611afc565b93506113c3818560208601611be0565b6113cc81611c13565b840191505092915050565b60006113e282611abf565b6113ec8185611aeb565b93506113fc818560208601611be0565b61140581611c13565b840191505092915050565b61141981611b89565b82525050565b600061142a82611ae0565b6114348185611b1e565b9350611444818560208601611be0565b61144d81611c13565b840191505092915050565b600061146382611ad5565b61146d8185611b0d565b935061147d818560208601611be0565b61148681611c13565b840191505092915050565b600061149e602083611b1e565b91507f436861696e537065635368612073686f756c64206e6f7420626520656d7074796000830152602082019050919050565b60006114de602083611b1e565b91507f436861696e5370656355726c2073686f756c64206e6f7420626520656d7074796000830152602082019050919050565b600061151e601d83611b1e565b91507f446f636b65725368612073686f756c64206e6f7420626520656d7074790000006000830152602082019050919050565b600061155e601883611b1e565b91507f4572726f723a20416c726561647920436f6e6669726d656400000000000000006000830152602082019050919050565b600061159e601f83611b1e565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b60006115de601383611b1e565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600061161e602583611b1e565b91507f4572726f723a204e6f206368616e67657320696e20746865207061737365642060008301527f53746174650000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611684601e83611b1e565b91507f446f636b65724e616d652073686f756c64206e6f7420626520656d70747900006000830152602082019050919050565b60006116c4601f83611b1e565b91507f4572726f723a20596f7520617265206e6f7420612076616c696461746f7221006000830152602082019050919050565b600060e083016000830151848203600086015261171482826113d7565b9150506020830151848203602086015261172e8282611458565b9150506040830151848203604086015261174882826113d7565b915050606083015184820360608601526117628282611458565b91505060808301516117776080860182611380565b5060a083015161178a60a08601826117a8565b5060c083015161179d60c08601826117a8565b508091505092915050565b6117b181611b6d565b82525050565b60006020820190506117cc6000830184611371565b92915050565b60006020820190506117e76000830184611362565b92915050565b600060c0820190506118026000830189611371565b8181036020830152611814818861139e565b90508181036040830152611828818761141f565b9050818103606083015261183c818661139e565b90508181036080830152611850818561141f565b905061185f60a083018461138f565b979650505050505050565b600060208201905061187f600083018461138f565b92915050565b600060208201905061189a6000830184611410565b92915050565b600060208201905081810360008301526118b981611491565b9050919050565b600060208201905081810360008301526118d9816114d1565b9050919050565b600060208201905081810360008301526118f981611511565b9050919050565b6000602082019050818103600083015261191981611551565b9050919050565b6000602082019050818103600083015261193981611591565b9050919050565b60006020820190508181036000830152611959816115d1565b9050919050565b6000602082019050818103600083015261197981611611565b9050919050565b6000602082019050818103600083015261199981611677565b9050919050565b600060208201905081810360008301526119b9816116b7565b9050919050565b600060208201905081810360008301526119da81846116f7565b905092915050565b6000604051905081810181811067ffffffffffffffff82111715611a0557600080fd5b8060405250919050565b600067ffffffffffffffff821115611a2657600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611a5257600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611a7e57600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611aaa57600080fd5b601f19601f8301169050602081019050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b6000611b3a82611b4d565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000611b8282611bad565b9050919050565b6000611b9482611b9b565b9050919050565b6000611ba682611b4d565b9050919050565b6000611bb882611bbf565b9050919050565b6000611bca82611b4d565b9050919050565b82818337600083830152505050565b60005b83811015611bfe578082015181840152602081019050611be3565b83811115611c0d576000848401525b50505050565b6000601f19601f8301169050919050565b611c2d81611b2f565b8114611c3857600080fd5b50565b611c4481611b41565b8114611c4f57600080fd5b50565b611c5b81611b6d565b8114611c6657600080fd5b5056fea265627a7a7230582070f7a284b1ab092d7cf8a14ca7bf4b7a68d6e2cb8441f82436e624a898b0ebd46c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000080000000000000000000000001204700000000000000000000000000000000005" + } + }, + "nodes": [ + "enode://cc67008e850c4b64702f1664f18704dd23980fb574138588904270739dca3a9c417ac3f97077b0f4e4872a5db109195f7137c156314dbcc724eabd809044553e@ewc-bootnode-02.energyweb.org:30303", + "enode://34f97743b9c07fac84c2db921337f006b5932e14c29df496951bdcfe3fce71e3d1504c25d66156dae01065623849b4fe940168c7e3f21c49b538af0cabb5805f@ewc-bootnode-03.energyweb.org:30303", + "enode://80e49825bb6bdfbf69576fd4a5f7bb173db98c08439bd7a019e2d42548afbb1f70232b74ede42dbd99cc0de3990a0101d49543c1b299027d2fd6f5a2c81eff75@ewc-bootnode-05.energyweb.org:30303", + "enode://1cd00f54ddaa41793d32500f0cb123d43e95f5ed18220c367d5a4db7d111e5fb59f250c397e40977562881875827f022d88a51e51171daaebf916beaa60e2139@ewc-bootnode-06.energyweb.org:30303", + "enode://3a8588bd883ea3bce606b08e7ca71d55717077cb3d25d3c00421ac371fbae286b94f58ba2e739b17b64c4d0d3e19f1b0ae2e47962703b42d7f36f1b9a448d4ea@ewc-bootnode-09.energyweb.org:30303", + "enode://69749dbe7e6f5fceaeb0a16f60353b3ec04825a12400e6d581fe980275e96fecb464276e61d79fc1628e448f1f74da2985d778ea54e0e8e7c64d980f2b3b6a94@ewc-bootnode-10.energyweb.org:30303" + ] } diff --git a/ethcore/res/ethereum/expanse.json b/ethcore/res/ethereum/expanse.json index 5b950e28b0..bbaa4275d2 100644 --- a/ethcore/res/ethereum/expanse.json +++ b/ethcore/res/ethereum/expanse.json @@ -79,12 +79,13 @@ "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0xC3500", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0xC3500": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -92,12 +93,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0xC3500", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0xC3500": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -105,14 +107,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0xC3500", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0xC3500": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index f55e5492b8..ba988e5bf4 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -136,7 +136,8 @@ "eip100bTransition": "0x42ae50", "difficultyBombDelays": { "0x42ae50": "0x2dc6c0", - "0x6f1580": "0x1e8480" + "0x6f1580": "0x1e8480", + "0x8c6180": "0x3d0900" } } } @@ -163,7 +164,14 @@ "eip658Transition": "0x42ae50", "eip145Transition": "0x6f1580", "eip1014Transition": "0x6f1580", - "eip1052Transition": "0x6f1580" + "eip1052Transition": "0x6f1580", + "eip1283Transition": "0x0", + "eip1283DisableTransition": "0x0", + "eip1283ReenableTransition": "0x8a61c8", + "eip1344Transition": "0x8a61c8", + "eip1706Transition": "0x8a61c8", + "eip1884Transition": "0x8a61c8", + "eip2028Transition": "0x8a61c8" }, "genesis": { "seal": { @@ -181,8 +189,8 @@ "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" }, "hardcodedSync": { - "header": "f90212a0113ba3b1153987fc483b01ccfe9ecadbdc36a7b264be75d6486cb6b694cc38a1a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479452bc44d5378309ee2abf1539bf71de1b7d7be3b5a0d91db5900e312a1ba8d39505406a95dc0ea20393b723c4bf488ade4e3c4ee4c3a05e63ab076852043b1c2fe010e2f14ab13dcc8e1c546ac497a24ce88c4abf3056a0b72e4a776f895459b6bf0937040990d868b1f45889aeaa26684e07472263fc31b90100b00200000063664554889124822ce2001022840543085d5810042382888542ca3816801780384384cd6851081cdc4d4b2b205a814b049198110a6869d60f4009a79c3854c9000581044889080000611c09142200106552000044027024004038092102814ed6c48040a8715851088a20108e80165f8c0c514019037a0000121304b2343416800b029000024404248238a06818414cb8690244879491855405026bc8250220520992c2380099d10024411c6048424083d1307822442d8444700405147883c4c041300aaa2408bb61084012983825a22830040a180106b5e27182088060b111515832902903a8f1d432a48004d0250437106a503491000048a91587067a897789f4d78371d801837a309c8379ffd7845c9d879e9150505945206e616e6f706f6f6c2e6f7267a0db22736cb3f06f9c86804902731fa6841eaaa1cd6326ea2a30e9c304d48bdd04880323b06837344cb8", - "totalDifficulty": "9633968582330911261986", + "header": "f90215a0eb69af582bc0b9d69952e19b4ab6679d2fd39b60f988b1c355665e0e474d0980a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794829bd824b016326a401d083b33d092293333a830a0ce6320cabd4d4cf2360afcc0434a12ad762ff69676d1568a5aee368eed3af2dba01f896ded15d7ddb851b38dd84b9d6194055ed8d9f145c5a4fbe11bd16c6521cda0c03895b938156bcb6e3da7275cc17ecfd2df3d560c296699d17ead8ee9963991b901001004000004018020200401801909002011408900100021400380000000080800401010000000240000005000008003100000c0002280002200004000000080000000200a000201220000080820401800000a1402000080000000000200201010020000000208800002080040024108000600200102045000010000101100000010001c0a00001020141000180400044202000800820000000004800c02102100000010040004400000000482010800008000030014c51200420000001410100814808002402000022000020043001000140000009000400820200010006030800202000012800000040804010000000048000400a20042020006008008100000870784a6e555c363838ed80183972eeb8314c3b5845e2e4ff6947070796520e4b883e5bda9e7a59ee4bb99e9b1bca03f0fbd3b56a99c123d984d92f5aed72b7840e662b68e16736d2151868b5fb05e882adfb68004d099e0", + "totalDifficulty": "13860828717138620284704", "CHTs": [ "0x0eb474b7721727204978e92e27d31cddff56471911e424a4c8271c35f9c982cc", "0xe10e94515fb5ffb7ffa9bf50db4a959b3f50c2ff75e0b8bd5f5e038749e52a11", @@ -3826,7 +3834,935 @@ "0x64b862e2d5a6c24d569f3352b8524ebfecfd5a3205a3200ec78df72d79a66838", "0x6da8edf169a9c78307258a723c1ac1d96db20a7131018efad16f0606683c0f07", "0xbae1427beab8c3e71cea57e5f9cdd55bc278c6d6073ae2628f0d3efbf9894a42", - "0x389bbd1b3fa390e8d3339cf5b018ec64d9cfc02bbcb801acad0857fe377ed83b" + "0x389bbd1b3fa390e8d3339cf5b018ec64d9cfc02bbcb801acad0857fe377ed83b", + "0x819c3a7f682db122c56c004c1128a9e219f13052c9b78e59e838ca850e7ff191", + "0xaa860c92d60059cc76b9c07d9b942e132b72940f09952a6ea84e03e0d481a409", + "0x3a1ed0df289c45f80aca59d16f9a8fc2363faa591ff2259767965138155057f7", + "0xebd67ae495f5d5f4b2d36676bd9e4619043dc83e00766b92eb0ac9ecc46cb9ba", + "0x2ff9b30bfee54deed34ac25e2c7e1d7825ce27d270b27ac8350f33e0021a894b", + "0x5344883f5098af546dfeed7e76dae5597b6a2eade7eba066c1f3fd2ca37b5a9c", + "0xd8220eed4c1e6c3fc9edf342c7691e0b7a09b1bc93ca8ee101f549d1c416dd13", + "0x89122f929f806e2f345539d74a3b67a59976a996427a301f608f67e9067d337a", + "0xbb288743f3da50ba5ba8c3493e267c30b83c67b77a22df96fb194fbe3d6fbdcf", + "0x1f4d12a9c756fd7a8e6a35df39a35888a47fea0f4191ffb303503c1c007f0959", + "0xf4a47acf8c2ecdf67bfbb0fa49b02a372e16ea554d7911eba4527647564b773a", + "0xc4ea1bb4b007889b8a81b306367ef03d2213a17e44ca13dd0d30f98e0327644a", + "0x1da665d430e25a551e5a2cc6b09942c906559337df136af4b81934a14b44f2fb", + "0x429c1fac7e8ae535baf1024b797708ab758420bdf6dc73331f2c48bb27edc2e2", + "0x3cb6d3d2836e4132b090dc084df9347f6cea34d23b3252301b65d2ae125c057a", + "0x82aa375fa96569741873b0fbe883f5901dfc2bb80dc9cf8c77622fe57ae7922c", + "0x44ecf8444c6f4cf718a9d785b74ee71d483c02b8a4499065d4822d4c4b7dd098", + "0x35879d2288d42165f3329be77fc7125ef63947d99647022cf85d9d78bf5c3376", + "0x5119fb54e3eec69ddb94091f3f446e3dccb7bb91d11345f4895734765ce74740", + "0xcac39a05efa882556d8f2afc987563d61e1728dc00365c2b19182bfea7e9e1f4", + "0x3a5bac1d3d0751d1101245c4fc5594ddf6b2b87e1ca2afe97e3b94659f2d6a6c", + "0x433bfa0e7110d38ac86b893e892f3ba7f5acedccc5482a34b6045fcabeb27a75", + "0x4f25ccd53e8bd308c7aac31f47ca3b0efc803ec7787904d41e4f40ff1a7c9936", + "0xe290ba09a6f6b5a975f2a0b87717d83c64128af3d84ee5a8e3e6b73dc963bfdc", + "0x7a4d734fe57c4ae23fc10f17863eaf0fd35b71f7bd1f03196c4466028b143d0d", + "0x191c097cb3f00ed9f5f8b8e6cee02f8e8a11c4500f92ec2f99fa2140173f6e07", + "0x7f21cd1e87804e63c0973dd9f66e792181aedc5d8cd931eaa1d80d14880f8dce", + "0x4ae1193f849d7833b9efb7c615dc7e88026a3f3d01a72aecaa040b4754eb6c5d", + "0x84e7bd0b8f59390667f6e211ea89a17a13279190de24a33b744f71149eff9165", + "0x5765adf9a49a7594f0e9ed443858f3a822b293cf8bfdae93f36739e1989b1961", + "0x80abd196d81322281597ed14ed996987e15bafcb372c5b386a28e5df8cee57fa", + "0x24e9ac57c579c3edb8b3d40231436c8a5a4316fa73810855f5cf4a89b858c563", + "0x3378c60cb4879b9d4205db9b4cafdba13e9923993dc5e8b155d909ba2bd0b270", + "0x0e4e9289f83c3a8bcc6d6a6d7645ca919bee8c278e3776e2085b4fc347627934", + "0x2879e00c5e77faef512162ec3b28c132cda5377f72fdec476d0f09be9d002e42", + "0x8e0de62a93882ddab17038a7250734c354340e976dd643f609fd497381a3562b", + "0x8730ceb5206279126dbebf0676f922f12b5ecd13be08bf301d7d652473f79001", + "0x0df58042003ef56cd28013d9794180ad11cfb28485970bfd79f62e1893f39dfd", + "0xbf555dc1708cbeb96069194d2d2cb75208ef2cc73fcac3b4f35abae97dfc26ad", + "0xa38a19e71f0b7176b3428cd3e635405fb1df56ce3ab60f711521b1c6dd5921ef", + "0x7748a206bb39f27841e3fd6caec4c00a68ed50ca1e474cf8a943e99a21013742", + "0xb1e82160b63784d3e9b434033c8b79c2f9f8bd713e392581fa7db2290512a437", + "0x0569198adc593a471ea4fba27d0aa659361395b18185de08442aba1128b321b3", + "0x4821034a9f0e2d3badf586be65a04fdbd035c960265d0f2bd07cc5403e1ec002", + "0xfc3b5dfbb770fba26e68a2cf15ab6d9fd0cbc841c8b7e4cb67f975f0d1e9a456", + "0xa85944d5b68b690c8eae612b09e71e422f96234bfc55860159e99371b17db96a", + "0x704ddeb5089de9f58c7677150ca4dc64b9f8eaeb8d1e24dcafaad48ec910dc7e", + "0x223938a7097dddac139a909c0c33880b8da9dc45c86e1493d21909f809531132", + "0xa07403c373e4edbdc13e207241577de01d2cc9baec6529ca28afcdef27ac04d4", + "0x385fcf679306dad9d1d2323caca5d17aa3197316bc4ae3dbe126a7f687280e13", + "0x81c27cce7a06b71cfe276b0440865cf2c0f444bbd6904bc30f2e2380074d1020", + "0x2fbae35e1c82c0d8afda61f1a0e95dd5ca47fd603450a7f2fa4fa5bcd73ac5db", + "0xea14be904f027e364092837e583dc19500addf6e5ecb59af1dab4cd0a132a04b", + "0x409f1a6a99ff08ac262addb8ffaf3fb3d08db086716c92bbccbc2b8c738b8866", + "0xfcd3cf7e04c6eabe6c648368cff1ef62ffff8fd5f3279176781d2238db715f04", + "0x5f38c3238f5b52cfa9f45249651e468a53a2b62f1b62a5c543bd663dbae07204", + "0x265a6954be72496fec70afabb4224f7ce4258caeeebfc4c6d27550e17cce58b9", + "0x58b2fae8d925846b08310b557ca5b7262a9ee1f3b729863e699fbfa91ccb9ef0", + "0xbbb46cd133cbbeea781699fe620831b93db03bc3f7d77da64c0367fe5d8170a2", + "0x9b57cf784efd6d4d484486ed7ac6a7093fe0fe087931eca10e9f7a0084c4fbbb", + "0x1ef9d5604fbb21e365d259c10b0b24070a5d5ba4dda6ab44f37337d62c4a84e4", + "0x2502589834ad081fd22d5df57be4047001ac7df36ebc5733bca560ac5e463b02", + "0x425cac90caa07eed1eaa6c020bd2d7141601b5d007fbd5905d455fa6e00cf804", + "0x65d3376222597121fbf6fa541998a61605f8c344bb68b646c32a5d10a215f955", + "0x087c94ab1aebbf52e158c0caf0ffb1ccdc179b3d3b9ce136653d6e72a918e96a", + "0x5524392fb09e03a0bd8440d8e9f038a82954f95332bf4beb8f8e4d5be6deb904", + "0x134ac78af0626fa56dd62f27ea658570652e5fc3c627c57d737d178a31688f7d", + "0xf2e565457b1d938a18a8fe1c3732d70386f9344be91380d8cae740ffccdac58a", + "0xc203bc327d1085ba09cafbe5645657d73e64450ed314dac5c84e7f356cae01de", + "0x9c847099e82e8b2c1be8a7cf440ecb85c8cc20111931c0bedbb7ce778c7404e0", + "0xc22206c94e653a7439faf69cba997d9feb1c5b34829bf00a4c85d0adfe79d911", + "0xce0d9b43dce7b340e847e7340aa7d58c03b778a57e493a8f8dc9d63ccb06b6a9", + "0x20f416746af1522273b7119f31000c4848ee66597d5945492053cbcce359c6ff", + "0x9f789bec24566fc2ee6179b336be2b08e67fa39a367787063f07b7424f8f77f5", + "0xdaff9cecfb864d02ea03a7fc76d24760303c1d28a93613bfd659dea6997d8cdc", + "0x522c6b0c2334ddeb39a0fa8092851535e6980e3bfb1ceaccf86247ca4209c392", + "0x35e1440e9e6851ec26dd112b07761023637bdeb54c9a4045475b8616eac8f2c7", + "0x160df7b24ed8a0d4e2d6556add5318aed08656dc2f218b75badfa7dcd2d6085d", + "0x1219ccc41d673e6e1d8eb1e2a1edf6360318a611bf5917f40394216c3398af30", + "0x5237d29b94e6427a41e552dfa0db1281381a6f7f5eca72134e1638a8a7acb790", + "0x596d64fb8478cf32fd3d72d70c4ddc8a348b4d2aaefabeffc906a8ce656884a5", + "0x6cd01fc463582e85e433263128bbcb75d8cea197d650034bdf0ca0fba50abf92", + "0xe879011882783b1f085cb14abe05fa2fb008f3f25d303c96532c7b63091fcd50", + "0xad0e0237bfc7ca75073ab1328770fe3fa2099c7aad98d659747446efc271860a", + "0x1ef22d01e144466d68c09600e54425756024d95f323064a7bace055678533223", + "0xeb4f046e6c9eafb07c8615513b096dac0c30a7a9983a9b9d282ced0502baefac", + "0xa5ec8188ed11607e0c166d809bf6f4a624b63a04cdde6c9b38ca991cc363f07d", + "0x757cbd504e40ed1fb80967cbfbf0d73d649f381e07d3d774f6fb6a958872e13c", + "0xa9d67e8749177c72fef84684a3ddf25f280e624fce571e202eac7f4713675f07", + "0x05fffd3d981ec6411342264732be62a03ced7c7fbd2ccaae53fee75c81017316", + "0x673bf4e2016572077879057f6d122ad7e29da0a17c62a97679dfc6913820d42b", + "0x3f3e2d80251eb582daf8057af384c4e4a419873e1fa8b9ec32a6ed7054de1efa", + "0x860291469fec3ecb7c78b2a3c291722f43aaf747fbf3bd88cc4061083516b64a", + "0x3c30c64ffb77b39132c64e8856024abf0736cd2603da0091f52f1e81983e67ba", + "0xae8e21ce50f85fc0ea64696b973f53ae1cd877d64e30f13904d15b34d4bac6c5", + "0xb4ca3d97e33e9526ad1c7796a7f9efa5858ebec2605d1367862c880f78c86272", + "0x6c20893dcf39f711700bf6be5e360ef38102a7f7129ed83e24c5fc24f42584c9", + "0xd520870cbe314b46c1b7edc21399986870303d38cde9cbce3f4e25807124b1fe", + "0x9a24dc78fdfaa16af729b4b27c324a95068b7fd09eaecfa6564c7a815b8e9084", + "0x9b8c6c28860af11376931e082ad7bf12b0cb75f2582c8284c6e587ef2cdb8842", + "0x656f2096414bd7ad74c7abc1cd8222c61697b15ad68d7a5e76c1dc41086129c4", + "0x95bc4027611b90d1fb1ca0eed5bd326464cd1e4de67ff8640683496785c38c04", + "0x20e3c8545a8c05022d3b83a1bcc94db7239a6f509f13053d19b8ff7db126d093", + "0x767a60be20b494d792b1d4ea6fbc35f4affa633841f7f07f6c95b8b14ddad8c9", + "0x521dbc01a89820776a38d4989b6ba794d7c0251ad939dd0caff9a7b99579d0ee", + "0x814c1693d5e763b3e91b49ab8c192dfac0848993839e2cafab76e5cab13c6f7b", + "0x6626910d3525d978e25f14a92e3e2795dcd2f7844dcaae556127716ecbdbf747", + "0x685257b9002ba7808aed5b69dced6fcf1eb9b75e1e3646541aafe90caf3ce1b5", + "0xaa3d255fbf57445aca9fb45babd6fb58c89a95c6625f6b80601bb656a5eab344", + "0x2795d9516f8f2f15da8904741d24cc0675803c9a7ddb342fc66caa7d5050bca9", + "0x283f89476887c625c534bd277fcf5523df8ded877479517728358b71efaacdf2", + "0xc2044a35800edb0b6848341004928307ed023660fd22d7eaad2422146cef294f", + "0x01d35757346541a85a4b86f41d1b5b21701c1efce3f287a034497942483f52db", + "0x43d7f8c173717cfdbb3cbf38cc8cdda9c6ea8f05bfd435efe1f74144c9c84f24", + "0x6463d12f4bd83d544a14948fdce6c7e9e0da0e9812bedfb314b6e91157c0c682", + "0xa7cc32d879feab7508b84fa364fadc0af5a7cb1164b61d7d6cc5d7d1a76c32eb", + "0x37cd4953d262b095fa8a5a0708224d006ed9ef93d1ee5edeb285d410c5d86a8a", + "0xe896567b6be24f3ff1d962e05015f574443ac86c58b07bb943d12255cd94ed11", + "0x51d07ecc06ee4be7bc9700631ae07818756aca34d1eb2ac6eae7112ea9e9df41", + "0x72ba4e79031a1f2616b8e788fa578b9ea830e08823467fc875dd603badf6b79b", + "0xe06f71635f9df9bcecbd6553ab32f1af94275d8ccd59d1969c248822e3b6feb2", + "0x0484a049e43ac9f424ee9b2dfbd4c5932cafca539c1bec00d32d90561746e12e", + "0xe03a17de161d63503962f3d5cfaf28cba22ed622cb88547c8224509e77e2bfea", + "0x203acc2c14b68b80292f381fd440da199c9f8f0e043c42e0b0adc67be71bb807", + "0xba3d7f8a15b76afa8a834b44a922332713014a5d5ec3e52f729007c6717d5324", + "0x01e4a4e25bbfad7dbb0a23adf6fc873c40561319ba7bfbffa3c7a379606433cc", + "0x6252660ed44dc18d448fc1cf49bbae98c4c31817b6e372c71d45832556261a1c", + "0x1442a2a2eb665994c951d8134726c91989f7fd13145ca146de4e87a203ed1c31", + "0x017e10ab064887aaf7354bf1108d6946d31b435838a56d5c43bd70d3b5fe7c6e", + "0xc9f0213c74e6f3ee3bf7505c66620b61b147597ae8a4c3f0324463cb57d07f78", + "0x321a1e72fb858b8df990639bb7b05e10b7c094772af92fea9a8882b84a91d33e", + "0xcd57ed7635c2c59e8d2fa2ff04afc2c851d1a792fcf2e0192769404dd96b3be5", + "0xaf89e85dabaa1fd91efc15f0a60e2fb10be92c6ab2d726d6f7afc8ab94d5639f", + "0xf8425ba5813b811a587ad8b9fffa64adc22ad5660ff77a94ecaf479dadc7deff", + "0x9d602387122f2c064924d32fa3a62ed2e39dbdeb570b55af7a6b138534ace4d1", + "0x2569b3f2c09c18ec14468c4615355afd9fcadf2bb6c9d527e4a3457a068bb808", + "0x96a695ce507be20ae2636449116850e8cf86485eb756bca499aca80790c3dd3a", + "0xbb91a27d4a244d199c1b9dd22d0a6e687fdd340731c13c7016b7009ac49771fb", + "0x468a1b2fd3c8835f1f1b7e16f35a5a401cd4e7e0e50920198cfd3f27b5971f7e", + "0xce2aea8f42295d49480f6ccdb2f0c2d33af2b44f1f3b0b4eb45e3975397ee264", + "0x786c6d63ef22e083b0aa3c1cd2de64c238efa8963532e84471ca40a1fb210f4c", + "0x86f90dae63767ef7819d0c779ac362b2a23e8c457bb7f5c901e89dcab8a5ea14", + "0xf689fdf55a5d1e1474b9dcb1b591f14448cd017d2d708bc522157688a728cc8b", + "0xc4be4c521cc105651c4a2a0449d206c2d7052123d06bb02ba3afc1b22d802177", + "0x64debdb7293aaac3e610bececfcd7b53e103c27b03173f0cfa401db4d385023f", + "0x3d4b4017e6ffe74f2a96cf8cba7b21edd627ba5105bb87e79cb1ea88b3657366", + "0x74161c9fd283d80490b55731ddec4ebf2a1e328e518ac17b2526bb93d069ecdd", + "0x89f522f48230fcbe9fe87ffff8201ce5fd853bf7ff6536d3299368151b833871", + "0x090d72bee8471c51275662ac2fbe285e5a52552348af1eca33e657fac448db8c", + "0x2d76427152f13ec917a6e28430ad5f0f0e69b5abba49ef432a90c4fc2a875894", + "0xd490618c0aa63e1e963e457c6be096bb5242b10765b4797df232def9d0269aba", + "0x980445545153a80c8aa343a8874a80fe7fbb7beac68e58d5d90c4c5fa73d7e9d", + "0x0eeb2df77b486bf20203c9e80981787ae439fc38f39c583ffef93ea0aad82358", + "0xd0897e799d1152432c2d9cbfdbf72d2e522af23766e7ff93d51acd52ed869683", + "0xe740acfe918c168daf28f9e491cf94b3b168de4e807a80b5f1b097beff45f206", + "0x125580c9d014b8cb50a56f8a14f10ba4360fe448f18aa86829bc7f7e1cccbb77", + "0x7c9af1244e12d298778b3320718f096faabed0006acabdb0289bcb7b5c2bf8c1", + "0x76ac2ad7e52c2a1e7b23f03f21d607bb634a4290b800fe479c6c134c87d41703", + "0xdfb4b41974d53773300949ee46ce3edd2b5ea0f8be55d87bf9b2d1ba21dc30e7", + "0xde87fe285a4fb2dfe40dd79462488808c6efc4ca3cf26de46f3ac56c4fcfc0ec", + "0x72ddcbe95df0949ae31eb4ee5d532587ec1d30a358a3fa9588dc6749f3b0b673", + "0x4da323bf0509116d1dedd6ef0c486838a9c8fc7bf0d6f2899fa1c43c89791a5c", + "0x6ab3c6c40001407ecc8b5926692e6bda176b5d9685cbebf72a69a8bd01c8764c", + "0x85e1cd7421da9343f156f573c89d477f166cea452287d9cb8f19e71feb570cdf", + "0xa8f06a7f39e85e9e3c1abf454940c9d4a658f1d5df827935376f556b90670c53", + "0xa6ef83d45fe75050512a767f8d69b74a59a43b2d12a03204bea075839e9401c0", + "0x92d440c5683f941accc4b575e884bc402fedf6499b1a9f74e0500885eaf211e3", + "0xac5536524cc38b40c8f0b421af9eeaf9ed78be65bd1b180a469008d5b807cc1c", + "0x00bf19f0ca582188040b38756dc88388d1aca7cba42aa89e513628a433156b0c", + "0x220b5f691ad4fecf71e471a845862097fd754bd0ffaebc204c048aa48acb2bea", + "0xba7174d367fd0f9487aa9d1f2444a4fdf38f39d3686d7d41d18b0671653cfe6a", + "0x04e281d09ab1ff8fadd252a22c4682beafc436916ac90f81ef85d3c92fefe628", + "0x4aeb3ab7be3dd495dcdc78631a3cfece68ae9797bfbc4b5ac6ab13002d9fe6e0", + "0x0fd273deb137dcc98c8bc54f13098e355568bf56077a58f656e4c22e51ab0360", + "0x55438d435628bef382426565cad284695ebffa4ff7485e10c91c0988ba20ec25", + "0x049e4a80ef77096719ed90d324174d50a0f84cdc3da0c45d0b122a813028d2d5", + "0x200e2da004718c48d4850a0c5ea3fc9821f820af0cc24b733d80dab98d756ec0", + "0x9da660709c2b4ab2807df73bc4d11cd75a102ff5d47ded6781ad5a293ce22415", + "0xf29ee2bc658cac2d28a1e5809becab789b329938c3ce314bec172c6ae3861f4f", + "0xeca0f045b3777c894d3cc2be518cbb06b2f54c4c9d02374e6d27de0721c471f4", + "0x804b27ccfd7440ae1dd05edc6acc4194a288918958541de596530ca2b89b3b54", + "0xe0805890aed8a60e2e60d2bbf29a2d130c64ff69a82e3a78f63c9e483fee94d9", + "0xcdfde2f6274594a0a67a6d7b02dfa0fc735842c28b144a6859ef6eda842d6669", + "0x5e80068e6aa44fe7d9c810607d90e13bc364bdbf91255fa4675a7264bdc38998", + "0x622e0765bef88f51dd013b616f40fb80c6bbf6623fb226d3d811af52e4de7740", + "0x0ee735a5e83a5191c4e531c603179e90381eb43de0abb03e12e5f8d7b821031f", + "0x55eacedd69481b311baac1d17343a78a1587454e6acba2edbc560fc1c95ebc6d", + "0x2eda26ac17139e750560a6b705f60a4408487668df17fdfa29fd1f3d3fd76aac", + "0x18a465896c7cda9912dc2c7ba459224a924d0feca1d49825423277902b7a0094", + "0xf764b9bbab0bde6ce1cfbf84a8c280f1524f396aed6fb4569f59e2791afcf27f", + "0x7d7fbe746c72958cf2dc3d5b6a7f2b680c0e47247e9697a1135384486640a736", + "0xcc595d1e98d13809ed5a22277ec10f25f36f05ff7ef1aa91de45bdd471ce76ca", + "0xd9579a9c16117e08e441401696fe4b3687b40179cda267be384d044d27fea705", + "0x39c73747189563c6ee22de333270178803b17b5d1e300732143a3e553ddac6e0", + "0x52049d4f26ff9bcb9b4444e0a3d4551d2e68ba31787f1c69722754004e6c10c7", + "0xe15d9b70b804cac3efaf1eee6f980b146f2753cca6f60a338800fe67cbf47db6", + "0x150feb1be780bb1e166b4f7b54bd01434820fcb8e63244d259a56bed67eb1fb4", + "0x48441f61086ff68e3d7ea203193df77565ff8c846a5ef3aea5cdf52ead82c6b5", + "0x8fd3355dbe04679233da0c0aebf896128245349e8e3925e2f4b4e044ece35654", + "0x22395d1a8af82cc86af2697b45b86df13d5af07eb790a96624383d2d2cf35fbf", + "0x32c13c2498ca202482c0b2e7e5ed096bde4c36cdf15dbc0076042caea180d6ba", + "0xfb3fa42d8cca9e62d1226bce9a8a3d8e75cc461d2b34979751107ac656130a4f", + "0x2860141a7eb70cb951b3f45f13dd81c95ac8d86c97ee354e65c2ff31ad24f149", + "0x7c02d5bb3b990ac7d5c6a7f183fb4c4e3d2249605f6d963e12b6587e665bbad0", + "0x22a79a905b70c40eebf4aff3f9a595458e9e5a585ae7a18a32d99cb369cdda90", + "0x8e10f60b08c81d0ae43a1932f798e85bf0bac6d22ab3d399c22d1e3744134de7", + "0x03d6dda99b12d94960eeae1f67dd6ff8f41102ab00aa7b48055515262bcbcf01", + "0x2864090dfb4a667388f3c51dea1daac94245f2de71e804bd7e9f8c4a6206a827", + "0xf64e0a38264b190f611d8afe030e948064af722c1d85a16e3e20a5c4d6c9153a", + "0xb7706df2d787d33c13f2f100cdbbeab68dae726cd5a08a046d7fa8b34b029a23", + "0xf91f33d36525beb213fe0761808dc89921ed0d52c5214d704acbca639098622b", + "0xe1825f2cc40a1fefb24edbb3ee31c5118448abdd237a6e27f364623689a70a92", + "0x051450e257cd9e5d03081ab11de949a52b4391f7f7179fabe0e7f43e0cceded4", + "0xd09a4642c0fbbb0f29af7bf0ac06c080e468e2e287d1ef74e388163d5c0057dd", + "0xd7ff8cf1b7744f7c2d79255e5288e8a2c6b58ea7aac62894716149f42f3e020c", + "0xb108bbd52148c62ad106a9cd35aec9d8b111e094bbfcbb4d447745f8a54ae8fc", + "0xa4794deee36a670394a5ec4d908728a1796cea8c8564f61e41201a7ae985b0ab", + "0x4fa697253bab92379a876446a3af734dacb965722d568203a1194e078777c364", + "0x4222754bd0a480cd4e1bf0c9faa267d17a941496e557c0856d2ef9818f96bcfa", + "0x8ee7c73f3d4ba9c2764cc6e980f3b9a53fe7a1306c415c8fd8264a33dd5622f1", + "0x17f40c4b7109e2aec6c8c4eea793e196bc96c73f20c963a923fd311b23814590", + "0xc32018615cf1253161ea102dce96379f6fb9791e8008d75d86642f21bc555b3d", + "0xd38fa39699ea67d3c4892094309335ec3bfd0185790ad117d197866331a294cf", + "0xa770de6526cd0a07d0337cb18e371fa33a6ec0803203e54f1c93b276d1bd24ee", + "0x7377908db9acdbc040fd248b8ab85eefdf7edac2efa6ca90535d4241c2286d46", + "0xcb74aabcd157c732935f0d8b926ecedb3b643ca97ead5077301d2b4768324908", + "0x3885f8b2a341c91cf42d459a0f9e81bc11bea8da8e0080c884e357d79f0143a5", + "0x1c56ed46627a1c6b2495cd4d94473920b8a249c951b227e24b9bafaa00a3575c", + "0xc2e5a1d6afe982eaef9f35cfe30b4f80e224950c094d9ae39430476988199c98", + "0xd5c27345331cde1052278637de22a243e9c5022d4edc254cffb7c574615e661d", + "0xe89ad382c17daf957f02a2472fd509eefeed4a10317ca97085539839a80bc5f3", + "0x2bef80881328dbe76f8f87a6f7285209a1355e9400c31fe1e915d5a0323efec9", + "0x0a79f2fc887da96d5ea26722726d83506c1d5fcbcb50044ba6f74336d9ae8609", + "0x2d79e9b35b5bd7beb0bb392a4db71331c4bf41210c26e2a0f5e64c4de8d659f8", + "0x3a82b6fea11f99de12f1e39202c2fd69a21ea2fe5f4d1d62500ceee7de29c88f", + "0xeb1139baf2c45e867787be6df45acc8d21686cb21adb1f10866ebb1605741ade", + "0x691505332d0a4c6af51e0c49f11d578f834e961898279a21b680438b3dffd917", + "0x8cb46c9273d3ccecf1ddcd4cc86b9ae5f1e1bec57c15cd624853c2333aa7184a", + "0x06f1cb0b68758ada524d604ce8b6d5c273eaba7304d8650687869eb575799362", + "0xce7464c87dda228032c88dd3ed29b41524e847e31ebaa76b2248eee64ca41e70", + "0x64b6e5039609367f5c524c0d5266150515cad29a8a262c76614c5086c83f1bb8", + "0x76cbd65dfe8dd8eb21996529bfa0663bbcd851b675314ed4855689ed9f120939", + "0xea80a1c269e36cc9ef3aa3daa051a08e03deb23add31dd32cff7bbb86f53c8f3", + "0x575ed304982a8b6c44b3f6dbfad271ef13c13986776abfca33a9da2727f587c2", + "0x5a969beca5363e60be1c6ba433f6cc037806e1c617c78b9a3115d47d2e87865e", + "0x2b010bbdd97fbf30ff7b48263c8ef58690a2de5b4cf170829fd458d2309c7c09", + "0x7bbc16e0933d41a60d2fa96563ffe099fcbcb4427cfe884150f645f25af752b3", + "0xba7fabd6d879f55a747a3427b3b2c5e04917b0734d693f759c2ee5fe235c8e6b", + "0xa890505d5778b78e3dc6c9749e8c9c80e490aa84bf91a76f4fde2d39a8e68ad3", + "0x36e27f687d02db5cf074ec89896e557ccf240d6c0271b5858a6af8b2661104e6", + "0x6a18097d71c66f0564a0fd27b05346d2dc74ccbb1ae123ba575a2bde54f1e810", + "0xdf4b61a51fa7f576c212aa0ec1a93118668f8c5fae5e7a55a27d2d0033634734", + "0xb572f4dde7b9282f92de5efa836b9d8ba82476c229bc669eb1956e05a1a02923", + "0x616b7d8a8c29a6dea339ceb8241b86fead820cdbcee279f87680b9973c1ccf85", + "0x3c59231ae02993318e8244435bf7f51ad352ce971a2f174725f2c2456bf62812", + "0x66e778c1de1503722d7a610003596c092360261e4eff24c54f218baf5106a8b6", + "0x5b372b99a39105ab048a83fa36f2ee79de9c920addaaae0cfb5b569ed92d0fc7", + "0x1389b29e130b37d8aae7dd19b46f216c2ca8d8562c7e04be9113ace816b772eb", + "0xe947c173f63c2a4b5bc1d14617dff02d6221915debca30ce90af9c63123dccd7", + "0x18926071dedc42086bc9c90e9f3e80ea1ac09981677b7c8d75159a8b11043501", + "0xd196e45da07cd27ef03abb235166e4937b6027c0fd6de42ed71b5d9ec748767c", + "0x81440dee93f1b7ca2d634549e1f21b834e417df97338c7de56e2d630f15e662c", + "0x228a10cf2a02f134998dab3ac59a71a05681640f115cfea4dccc67baedf4f746", + "0x5ab8b611cb2eef244571059e61540fd338bdb1ee90e7ed7e5769e67f5eb2c793", + "0x3f044afb403947868f4d2deb1dce168dc9332451c1dd1bab4e94e4ff6de5bba7", + "0x6212ebee274a505464940ca808dc61d1f1a2df76820ce3a8cb9c26bfd49b67e2", + "0x6a89fdde963cc5b696beb902d174b99c0cf8ee2e2208cd8f1f25965fc2d7368c", + "0xbc5d4f775e4925e2201214b1e64c7f5080899125a01961b1e5a3d29aa6158d26", + "0x844b1f9e0da0aa6dea81cada5255248b14cc4655d739377feab11d99b77f48ca", + "0x8a972d2e563d3baa95cac6c73b23bb04e57e7db2eb27b2c6bd78f0f2b2285a17", + "0xd25370dc166a5a4ec801c652b9eb3b7f17c9c76f898c6da338dc686ea65fc108", + "0xb1d6e05e234624e144fd8db5189ed47da5befc84581fcee92176951aa7a6adc7", + "0xbae505f675148669eb640bb1183d5235260780e57f62398b244a289f6e920593", + "0xfb0cdb77ad3ad6dc5feab63979743a18d72aa7715f9e3518618be5de6c724d5c", + "0x78964f3f2e28bdc1d9f6bdd9c424cc8b211820f23bc59d61a00a4f5089eb8553", + "0x81985ebb740817d81784174ae06e160d3d37c1a1fd4b79b665fba428e6adf199", + "0xdfe038aa48f96bd190c570b92bb9771b49556a8e618bd17a3183d594cb303297", + "0x4c2866dccd714ca6e233da2349b91e7fea7dfd0055675235788e1bcc012fd297", + "0x4d9e866bf6bf2545608734b914a55f247b9acd17455d1262f6a352cc46499ef7", + "0x53be82bf17d1dc0d3d5e50be3a04fcf2f5191cd3547ca432a7814c038f709a82", + "0x1d5cda51a57e81d33e3caae568b083bb26f71fdb23fb68e1f7ceb4444514c13c", + "0x35633bad7488a5c222d4fcc3111768fb7c126065ad9b32c49dac3a44353539ae", + "0x857564f5cc2b7385db6cfb22e41a30047dfbbb333f5e2984bc5826805653e364", + "0x3346a3292d6d1f716a13fad02af61b18f6993d277cd891a162fb6df932333906", + "0x267114f270cb5c8d7be4016d4ac1e6950cfef777e36e746829963c9c08fa604d", + "0x9b133dba17f550b709ec7891bec8ddaa99a909482ff50dd1ab0f473a64ccbec0", + "0xbb9d1242e6dca8ac62b9cf77f5fea02bf6ca33cd8fc60b75370eca5050e7e3a8", + "0x4b03fff35d5eb647982f79cc8c6fdd3514a8b2d95d45af83a06352a9f857b770", + "0x1251e1e7d8ba37f5ed242c045d14bcee63d044bcbeb300d2036cb746675777c4", + "0x3ea4f140b4dc19071603745962ab84c2751c9fa7add74f165cc4955bd0a23384", + "0xdce6b80eb188a9e240c73c86c13554c89176a0a3a322538480683a9f1187e518", + "0xf67d61c218998a24048c4dd68f903190a0072c80066b53e34df7e3a837e18339", + "0x9f886a4835f3f8fc010ad96e3e2a3004647c0f3c744d98d527980383975dc6f3", + "0x8a7920678b5f2687620340ac774f00239e48bfe1cf443c795d6cf25fd885c98e", + "0x16dfb4a0448f4b05c18115f6e2c128be6d6596b338723c912f22cd80448de1d8", + "0x497588ed59d0e9b9733bb7caa08fc36deeaae3396bd3922272d69f4861f06420", + "0xd96c89c40ba3daeb23f18a83471be008dbf8cc75668a72bcf8d02cefa1efee7e", + "0xee4e9ee2ea8a057649b9105c68792f2479b7eca57276211d70da3097a9fe4904", + "0xfd789aa5a980fee8a509079acc5ad340c98faeaeb31a1774683b93b1753289fb", + "0xf9e19259a14a532c93e935d252145d41988c16faffa4a185df11ed5f23715d20", + "0xbb0b8e1c03cac49cf3b45bc031a229334f9f25aeb59d410a12fc17c7e78f759a", + "0x5993b045dcbc40baf27d780c48b881c864f9aa21a9d59b0d4ff0377aa64e7c7a", + "0x726dd11a76be09acae4dbe103f0ba092d654206dee07511d6ac97c673437d26f", + "0xb43c5f0bbd52398157dd6233a5d8a03c931166150df21e8643f02b6c767239c4", + "0xcad3878512998b146cb2c6eb61efe07206c09677687df4198d9174c66e3ef3c7", + "0xf8a7872ede6770b0f48c924888c3b4d1b410639ddf98944561f837acee7c21fa", + "0xb31f007ab3deec8e37f7b70d4c5102a06eb9b37abf384ba3da0d7f32d29d320e", + "0xde77b2e81f239a80caeb44731b1f272a10528909f7ef6e342e36dc29edf7373e", + "0xa9818a101b730940a6b7698f76d6c08ef7720b12fd267825202355c3edeccc72", + "0x962cdb425d23201004d3f39a045e10aa39994fb8425aca3a2298b4ef1c9cd6ae", + "0x3c2a14b29d91c93978fd5a178adf3cb1aa5d60c8eb0b60d4c05f3f32b79ea42d", + "0x86d861026e8918c570c3190e57244aa89be34d57aa0c0d4f65f98abbb547b255", + "0x91e3b5fdda9b9087f6d267741a383c6c9a566851e03501d9090228046d793a32", + "0xb8e4f9fa0c7ddaec74a87492af4679aec824a08fc3fb267dc6f41b764145040e", + "0x7da2db73f48b0ec869db149bdb9ee85f50587720cb0fd6a765a23586f47e61d0", + "0x04b81ae4616337f5a09700eafec29ec84af8b0babd528913c8155ae3a47545e2", + "0xd2ca213e6a01e43f2eaab755003f25512fde5c1fc9d809c0b6ec437b194d5899", + "0xaced62e93320daedde74a5aa1f72739698c286f9ef4848b7be0682d99fcdd385", + "0x4e2f1707f429a5be0f52b39936d90bd3faa1693a4c18f0926cc300e2c2fdf8f4", + "0xc840e1e29894fb3ff6fe5572075b0a1e98e33b251fd8dd5996cbccf041a48b4e", + "0xe3d69480c26d810001a144e37f251e3ac4bbbc7558a0550a4ed8f8d5a06f09f7", + "0x7bf69d3d3bc804cc504ef7d37fde00fd3649dcc2853541507585e40b67bead0b", + "0xafe6cfb04d00956f24e4ebc3e9e0a23e55b044bde113654a5787a8c05add0a4b", + "0x3d050577e4169f4205e19f28f309142665acfd296c2ab5c234b6b5acabab07be", + "0x2c74c7ab90ae5058daed0c9551f0c9a52a9a5b10871fb0f71733fa6c39969882", + "0xf3d37db16c16218aa47b1ece8b7d178c42f1d06ec0141cfa0e5ffca27b21969c", + "0x5894c5aea4a461941788104ba38e9d8d39718ec8e4948c2675d83195584d1df5", + "0x564b9061ef80ed588a658fb138240e5c6356a5e1bd557ea06f85b4507dc4b73a", + "0x6a802d7836b1db57aa5b6c68b43fc072941e2c8090e73e26de86a0956eda3876", + "0x05fd4b6d5633a83e48fc91638d3f39cc6df164dc4187c78c19f4bbe78dcd12ee", + "0x481d0df4eef16c19e38d8a08889da1c2279d56ad2b6c6961737585686ff42280", + "0x0faa8363d77063a3ec76a6ec5786e9d64e0d028912c3ba5272c06cbf7c1d7c52", + "0x2177ca5c3420989d7493b28534489f2269f28e97ba167772bf5d17e9f9efd274", + "0x0694512d03ba47a5f36d024abed8ce1d15741f56c32baf7deb5b9a5e3afe66c5", + "0x38364d47d110cc930dd32fdc5cb7aa4e96b88e783b7060d2302c8235da7b4e8b", + "0x7f63e39e98887db4dfdda2cdb29ef716425f30480371beea60f2f70a8df7263b", + "0x4069d32514b8216a07b21c23b75cb63f12967dabec053304c990ebc8de9cdb99", + "0x954da7bca7ec16e62189cc6ae2878dc347d1677895cd978f2dfaf31593cd6bce", + "0xe9eeb5d86aec47d3f43f2b899fdad816b0c30e3040cfad14fd3e821cc6962ecd", + "0xae81c8ca19bf6b92752fcfdce9441f7c5b540d911a5e4315e561e9eb22746a4a", + "0xc84bf9c8ea619e0658ff70101fe588dc385e1e997349dbe112d66968ed672113", + "0xb113ba865fbc89fd495934f9139731222940ef72804eaea010f12ee167406fa6", + "0xf70a9e70e90a08c8fd629ab4fe0d1497896ab2d5e00852d18fe672346e337155", + "0xc1e671394b86e70459a39449e8ae9cd0b3e05602639e256a9da4cc3c047f6cf7", + "0x577bbcb4066d1aad7cbb75b5ce0f8f1bd7885511b1a7652b8f8600492656d673", + "0xa836b6615e1a4b6e306399e9da4d2c661985446822c8c05939da1a35414cd328", + "0x7042af635abf9efb3d9f5d6a2d25e638b6e9b8c6ad61591a7f88cedd4b0667aa", + "0xffa0e1f57683d082733344e57ef4aa267dac50dc6d72e8f36125e7ba892d19bb", + "0x2ac6846383bb7ca0ec56883b797c379336d64d9753a16ce48ad14d4086da2388", + "0x21499ae4629bccd17845c58f7fd13a8a72ece3557f4c11cc68af46426d3c3057", + "0x7bbb60b32ca0ec25a830e77790f73d11ac92929d2c9dc8eb70b8be026c7a7489", + "0xbfbd58d0c7f635ecbef3eb27d114068f953c2ef4ba9407a33c5516156d622e77", + "0x08643068ca01cdad8fc46e5190a9c90423e1f95cb4960684977c6c7980e6c0ea", + "0xea194e1527fb2202da5fbac43239179f2cfca95206c34c56042f69ba76ef3cca", + "0xe7e1206b62032646bb310e8b11338d0c94fef89151731fa701767d22105570c4", + "0x222084eaaff69aa8c1addb139d566e71d95dabe7fbf1cfcfb7d604eb3ab133a9", + "0x615c903ae923f5f8769befa3d9850d53b36ee3b47f2f136545efdace34e72de8", + "0x20f73e2ac4f18b1664a1028184367aa3d7b4b6d16492e199991cb0cc334908ff", + "0xe6fba0dd6fca5aca6cdc8b64f1509e6bb2b7819906cae07649bc311cec2829b4", + "0x4d73a9c7d379fd300f03be9e842ceec41a4c00ff35610756e5754f33929e8719", + "0x875ce56746e3303d28b7b6345d21dcc26412625d5972944a9a6039cde722260c", + "0x8fbf9148420b9c2eebc075d757ffbcaf0ee18373f1827b6b7c1051abe1b47bc7", + "0x44f7288451825d7b7bd4e4c8f82d9e017844ab9c08b77b4cbcd01fa39a755420", + "0xc150ccccd85a88fbe1795603c84193173c6de5814952763be4915061bf9490ea", + "0x936a08521d40e87784ccfe3bd50bf5f0ce28e8ddba7e29b4ed4139ae5030ace1", + "0x023d64dab9457ad253e42455b83a48217bf31b6779365694ced4640b640327e6", + "0x89a921ab2c309f0b86689c3d8f1eb6e9daa745c83b0d1a04cbc5331f9f96238a", + "0xb6f2020f682f34cd41dd273f19e6449c6b2b0b35d3bd7bee96363f97f028c27f", + "0xe61f2638f6cdc94607c9e978d6cfdc936a126cbbec0a97e27ed6733683b0794d", + "0xb136303b946b359cdd76c1a588046757221bcbdef6b7c4c070d9603bbe575b29", + "0xabe07f82dab2b17a4990f425a0daa1b78fd22ad33e68c499a7fd418c5f09d6f0", + "0xaeacb7ce46ffc86a5e08d6a71b7304f8f7f5fd794ceb0710888bf1825e7b0620", + "0x5879c7fe56421aa2a41731a4c6aeffdfe690b27c1468f5467ca03f69e7c74149", + "0x8a962ce351c06748908f8565ce0aca5b874c2ff18e99611cf12843a1bf93dbc0", + "0xa7557ac54d37f207d069407ebc80c1bb84a85744763e585c15199ea0ca906b47", + "0x26b637ab94949a1e7243736fff67435d28513b978a9e2173d184a64659f3b225", + "0xb80d141f221fe90d9aa80875c1c7d4f82d16a8b170073c08248878aee057c78d", + "0x1e4e58da25f9c1feddecbb771f6089dabe79079c9c8c98d78f8854b004f0b5c6", + "0xdffa5016e82a215af34cb2d7b54535e32c8e0797dafd1ad0b829ca434ddb7596", + "0xb74197a700d2ac5ac4105291ffe1567d146b3eec5447af86701b1599e42637af", + "0x4104494199a7fae34759bca78e8490f41dfc1eec726f33555a2bf87b3f923a12", + "0x0100ddc8b90291faf2af0a2b36975b7c7e247dc65414ad429f4b1d68a2f2ab75", + "0xe4740fc1ca3cbd20871da439f3af9e7a0aa94ab9e1eed2fb04931272f5546c3b", + "0x69eb680f378798e9dd194e3b985078b71d18ed637199cf854cb8116c5d2ebf56", + "0x8828999c917e550baaa229b3157a7914d4eca4903d96f2778702a7ac578bf897", + "0x3db2bba067a16d29a3ee7c68c9b5ac825f2814fb9b8490a5db3c1bedaafa33a4", + "0x079ebcfdae5f5457bc5cd686d65af96d8c15806ef4152eea5f4681ee472cc303", + "0xd2609a6ffbadd712bca9ea357311b30f79334667973c024e03670e26d2420862", + "0x3168229643f898f3297d3af867e03e1360d99d265c1e83d1440f045b0739767b", + "0xd62aee5a8f14ad052619317a81852cf563bd716a0ec621bfb25a15f9736deb53", + "0x144bb80a52cca2b9813f7f1ac547e96a57485c345f7eb4fbef7a0c6772292848", + "0xd8f21d1aae77e7a8610aef8ebc35f28c9f8dcc07463f4852a5a17db1eccbe795", + "0x8d08043b6144cd3d22c23675390363dd517fe2e8b71e29a7d1107f18d98f7607", + "0xc5f997af5b8c39a7992a3d64ae05d10791c18507c93977477954c4e89b28da42", + "0xcdb5b0e653ff308a24fc98b6cc28f1bdb4739673f2d96ea567edf9ffddf50c05", + "0xecba2883399e952a885fa626663de0c922e98ea1eb57b8b286121f5bc0a6b147", + "0xf32c9760a0ca7966dbd017d7326f50f15daf020daf31a5258819d2d55fb224ee", + "0x8e5cae1773d0aa309991d024ee02ab1ffcd0ffe895503e2f94ae30551de3d7a1", + "0x49d378c0158bfeb25b7ac2914bc1b45ddee970e58001ece549f60033781a39cd", + "0xd19e11ff8d23a64ffae4c886669d7e52d48ac281ad1283885ebafaab53e8ff70", + "0x6c91b89d95efff6ce209d96bf5c1dbeb5d6c01710ba03379be0f0a845d3ac936", + "0x4e514d655f12589e3c57a6f9ca2879658ea9c6ab0a523f5f107ac1a543e2ce67", + "0x44e9876929bdbf0e6ca42c42ae2222812f6bfe6fd957ccbc5f70a9eac80f0892", + "0x545859fe34e436e0d4183b630cd69b103816d883a0648e67ad955328022d5aeb", + "0x121b2a96f0bbb59565fadcbf50a02de161d57a8dd5871ebf72e039832104ab20", + "0xa96f4ab77346235358b680a23cae7f1cacbe21cd0e1fe775c4b0721bbee46bf3", + "0x80b235a11a3114ef45cc9c563b4646c338eda7e7ba2b077b0acf640197fe48eb", + "0x1a11074a30d0cdd88a26e648e673d0c3b6376dd8e5c8bf23012684102a3af830", + "0x51b806c229bc0755a73f256da981ba450d32a008306e6bf59ba4c893e1cc5795", + "0xaa74491a1e7d0edb6bf4e979ebafa5af05dae7d5d0f067303627f00e8fa56afd", + "0xcbf802903c9d29e00b22fef9f2875944f3d0e36a87ce6e49beb230979fb4e838", + "0xb92c3755d629be2c446a19a4a0bba3212f60c1fa5b342d60930379a74d41169b", + "0x07ec15dd0bd56f2acb56a5db6a530a23cdc33b7a7a1c4a8464572e6fd9b8626e", + "0x7f9c7a9b5abe417243e3c74adcd571aa45783a9dece0187a1466760270ba3b6b", + "0xaf9740a3960868f6e759d8f18221f3c5f6a6138afd40672e9c8e37e437be9bbb", + "0x7ec7bbcb17bc86e276a13dcd41513d7effaf08b67e71aa618528087f34efc208", + "0xf2903fcde2aef9045af6f83a3677ab98bb23adc5bda60de7d46509bba777d5ff", + "0xcf83625f4d891af2a93114908f3be5319b89372c31bd23960ad20f8031b20ce6", + "0x18aa4145b728ef0427165710bd0437dd5551a69c5056dfb171b546e35753860a", + "0xe139097ab29aa83f4aac8765e9125b109ddf2984ea325e7a1b4d881495f29dd8", + "0xceaf62b97e71d8326d5f66e81b26ea2b0519fe38c6a4b1b38d693d90d3ebf1dc", + "0xd23c423fe5fa5ca547a84e7e34f401ddc846af42956c6b052eea15950a0fe79b", + "0x947bb7c1e8c90826f78c4139def8e188af907cdb04feddb80360b4d712e51103", + "0xb8c65dbc12478ee24def67d15e845c4a52d9c4ce4467674faa81df72120061e5", + "0x85a642a8743d2d4aba389ae7a7ffa9b0687d28d4702cf45130e2d66eb43a31d1", + "0xe913e63914798d9b3518f204a989328f97520460d0e5925e73c0596d62cfd9c7", + "0x7c7065599f3f5cdf0314cb9ff575feaf604507859c20ff3a63e86c3dfdd2a5b5", + "0xaa2d5c80b189f37cfa00285362abeaaf430b9dc198a042024f1e148b0e888585", + "0xd5c0cb01fcee8af87b0842ad630ea13ea0892c97acd8b1443f6a6c7bcf7bd5b4", + "0x7c41cef1c4af5d15900fb535f0b1f3ae78de50cab2046dd65785feb768ea9437", + "0xf125c0cf53777bf2c49f9b99b804b0df68155c1c3dc554db66bf0327ecd62a70", + "0x18441cdcbc196bc6a4e4a5d9216132d3e075e60d2efbafecfd352621e6b2175b", + "0x0c4b7a2df6e6f1c4a724a8f4cfac0297f3c1f4248ec298a98d4c871820219e40", + "0x43bb93b6bc4c61df7ce7547405fc95a0bcb3ad7874562413dedb1744826fc4cb", + "0xf5f736e014cc04ab828edabde6a104caffe3ec3b286f84d4f6ecc2f7e699974f", + "0x8a912ec8dfa51053430d2e1fcfb67e7961fb7c9eb34006d08848d2d3b21c742d", + "0xd2567779967cfde9018c42f059490cc0c3518b9a32ee5fb1306cd54ca0ced771", + "0x3098a48f2fdf02ac43531168734d661673f82ac0d2e120f10ef0a00460db134e", + "0x9777d31e299aa8fe591612462b85ebb3c1fa27d60248cae8e90e4ec8ff00a811", + "0x880ea730ca93d9639ef1c2ab33136b16eb447b52b89cfe5b74b7327e4d12e4eb", + "0x1ea9f641d8a740ed394d34d7d1648f837667ed622867dd05fcd5f1387efb32f3", + "0xda9f20e5b4a61f72291e1a0438095af7fba8a55196a448c88cf3d1e1ea9bfcd3", + "0x949bd03ef394076512b6cb8f41c09a8d41304e045642c4670585a4e4b54c539b", + "0xafc89365b92724884bce8e10ec1c5c9c9f3f4a7a38effe56392642a73df487c8", + "0x447f42123c8ee209e4940605aa753cba0590bb4cecf366d07ee7e15e11ba7447", + "0x9a99f57a47d31a95f015347d38a3e39f66aa942747dcd65fdfc5355f61ea31cf", + "0xc131af20ca03905d63e74dde7d44d9138d59ec6e95b8a283fea9de870b79ad84", + "0xebfd03f29b2251499f04227ddcff308d5367bf534cd59f42a090a5a6c64458af", + "0x071ee5f0da2f97fca59606b25edfc0642371251d9c07aaf0e8010e5afaad7bf9", + "0x5040af11773c0c1b13fdcf6f2a92dff924d7733ee7d1caa83cfaa84dd204ce3b", + "0xc25afdcda4dff3a3b6a15e6fa90dc54a05a5cf33dea503b9e152a7fde6ceb9d5", + "0x9604f964d6de2c263139cf011baaf97aed1ed08dfbca158ecb495986d461261b", + "0x177e263de95e9c29f3ad31f286117d30ef069e981d825e767bce8031e634d84c", + "0x72f10f3393dea1a41bbe4834d728e8429aa5651c043fdf5b881650328eb62118", + "0x3969a0524ce2ed0d59f839f4abefae7ad9b2750b698716aac552e92079fae19c", + "0x99876fc077b178d584e9c29160ed320fe62c908ed406a2fca7ddf47de487f3d1", + "0xbd211392c93f76c541ddcbd83702db71a90a2ded78d530ae61c00c7ba72d9509", + "0x5e75d857ede2f1af61f846b5f7072225bdec659543c33b24fdd1bddf04de2f93", + "0xdb4c0c2519e081e1a6ed40a3a397b9f093f8dd4b24613d3f18a1e4f7ef94af9d", + "0xb2dfec129fe8cd0f7ed568b0a984fc37dae704c497770110b944d003e53b3807", + "0x6ba3b9ecafc2a469338c398c7588806d499b98720d3bd9c9bb2aa07a0fbf63bf", + "0x69a711d99e3e061cffbc1f6b10f4036de181a9bdb3824ee1a7c83fe15fb2ad41", + "0x2340c3402304d06a71998d35a2431774cd2198a170a3cf62989205545c6f0dd0", + "0xf7b9a019517ef3f4fd6bc5c4a98dc4b97e742e2f0f088d94d8b6442447118c76", + "0xf556a0b838ac7dc6dcd57b0e52f53df4a9f171221d620bbe7958ca3d71671787", + "0x68ebf1d4a6009f64a8a2f1b53611d43404a539e8d51c46dae9873ac1fd9548a3", + "0xb37c2344e14dd698bf0988608ee56a8f6b31fd2d18629c9a7dc1f027955b39a5", + "0x066df722de25d7455976b6be2ec850d97a6afe7f934fc83dde99aa5d49cc912f", + "0xdcaa64c29d2e447d85fe9a0329b269a283e27885ac75d874793bc4989d726f3c", + "0x6ac4870c173b2404d30357e40bb14db4ec0b87f35437da89c07af88d7c265420", + "0xaff8d57640e559db7dbbacbffbe7436d2708f5c3be26370b1b54a8e771eb4141", + "0xec0a42a3464caf7ba5b95d185aed8ce0b9e0696a03e2638f71382a92106b3957", + "0x7b77e84bcd0b0602128148fc8c94dee2e4f28ff1a748ada7ca7f5d0855e1708d", + "0xe0a572dde38708200a814e0120522528bd0ad3be8862293a21c69151fd72659b", + "0x2580506cdacfa687e1dc7344aaeacf2cb2a2eb8a4830c7ad83ec8cc0ef70a1f5", + "0xf5379cfc470fd6989c140d3d3bc51f9ca7a18761928783424c1947ad33e07e4c", + "0x582097b699a9ce614e823fd7e95235e8e6f002d2eea67e87f56ef86735599f24", + "0xab1a683bb247b5f0f3efb6a91f1938e0938326e81e6cdde2278827e25aed99ea", + "0xf21eb6fa7877e3275690765bdc7f0186e5ba0a94b0bbf9ebf1b4b1bf7aad3add", + "0x3168d818b07b7cc71912d7d92f2ec15d10ecf22c9ec20da610068c972c933c4a", + "0xd3d49fa35320e95473a7197c2a9c98bcd13b4b491ee96ac0199f3fde94123706", + "0x193d71d673a1e1de8267df397c41b8e3d9fb5b189b96da91c31be63456afaada", + "0x6579fe55af7c0d58f00f241e257db748566b750a59b7c05372b0b14fb03282ca", + "0xf6195d1b7de28d81a3f7d50f34f2a60f7ba6cf48d4ea9593404e48e6b33b8857", + "0x3bf3d52ff3c498761bbc2598c449d64a0cebbbb28201dec3633c73d5493b048a", + "0x57c4f9be1b0fc5d7c6f86a9297aa4d1375e9f5848bb2f3c1f08851f2c77a614a", + "0xa6ca06afd1b9fae9533d53a9188aa8497a7c92e363ade6ac094023bcb0e5f46b", + "0x09c669b50d1b26685f20817d0a096168b7d2a4949a20e50a2d665be4c3d4086e", + "0x8aa7d92376364e98c4549669e798af1c2e654a776f0561c89c7f7b2540725539", + "0x935240267eb3e1a1a6a34966084681ca4d8792142c4fc45574fa669c518b2e8e", + "0x7e339371cb38e9babe203905f4026cb90f25c8b10def4781ab5330dd0a63af63", + "0xfb588a940601e578fc7c3286fb4ad68f76dc1fa3dd88251193d9655fea77e056", + "0x90a702a71779ee4176ed8eab503deb43c63628535d78f9742ac95ffa5f699464", + "0x122c0341b0838cdcbf934769322e8fb8df77d8f8a666fe106cf87373bb719eeb", + "0x53e7ace007789e4c3513b16eef872dff5b6353bea42d8a3e3912fd62e14b8f83", + "0x3bdad07ec3a785d36f8c41ac9c0716a2cad6004bcf1e7e899b06c640e1aa1d9a", + "0x6d95da2724a3609af3466474e44455a787b9286f0bc016f13690a867f2f89756", + "0x98cd50a72a2fe944b00613dec757270f61e8590002d2e1b35bdb6bd4290dcb31", + "0x3b995783e2812c5d7fdf10f83170c8fd142caf1526698a9d094a24314ebb1a09", + "0x81cf7500dd9e75fd872b00378005a1bd180fe07be7e7cc3dcdb06ee006c8721c", + "0x8e7f5b4940da9de2b59fde73201351d01e5ebdd3ee863cc7653a919c64c35e84", + "0x6bd0c1bae338f0922d5c0fc951d089f9c290a42a3744f0a5b4b0b61a5248a4b1", + "0xca6c19228395454d07246773fe87ea86891a0fdeee12e8bd261625a62a426c05", + "0x83d0b617af7fe6f23019ff4e04bc71b8267a4dd9b79ae4ac29470e99cbe7466b", + "0x901c51cf6fa4726b8217b807f7b0d2e0b4ebb71b6a28e8168d2493016919b267", + "0x6d101782b820ab580dbe1cf9aacbc065279c18407e70e965ba3315bb77537c77", + "0x831996d9ece576f424f0c11abe5f3847229500ca651c452fd6ac234cc48cda4e", + "0x5d8fe79678589811621ff19ebfd3d6c4e2d0035ace9a197368b58deadc8b6fc1", + "0x8eda2c20216008d1c04f5bc93b5c2d9f8c3635e2ee440cee379f0470cd1a3c9a", + "0xef3608f4b6e9e835f3360e78b097af504df75f96796b4e30c9b4e643caf9d07e", + "0xee7fd312bae21e15346a412bd82265cd4231449f5a11824938de30fbc6400b19", + "0xd659949d718dc3522af4a4794d0c46b346109b5de4a8b87247d1f1a4c414b766", + "0x7567bedd64cd79119167944a7e623997ddf134c308a546724e432c36ee2a2d39", + "0xf0adf777a52828b31e70712776b4e56215a1038e0c14f2a5ab5bbf035623067a", + "0x92daf2c7cef9eee82db16820db7c60a3113950448ae4b149a7b32eb6956326fb", + "0x83825ec2c5c8a323cb5ced10e99fa0d7e9df73b9a158c522267ab0fb05ee7030", + "0xbb9e1680cb85ae48da3322345b06055fe4b668ae7b31402fc4d13faf439b199c", + "0xb040722e777fe01ac533eafcc93b869cfab5bc67f9e2e1044ff505c011f55f1f", + "0x0c488f1776428ffdd8eb03083fcc9dde010d7a3d4c08c8f8df2fa93fb206b2bd", + "0x54a0b71a8cba973d26bb89e5e07421b8c73dc59bf95f6190bb3b86e7962788ea", + "0xadf110bccb6d34276e3fe4701119b2527a86431d6f057a02e30118a6c6cfed54", + "0xe01ab7f11ec408b5a17ffbf6cabc3268e16f6be0cd0b15d73ee8b8e2aaaeed54", + "0x7280b73efa14f10d39306f2d2dc7832c0e47fadbc14f990a821c76d60be529f9", + "0x3cd2b491fa5228b4cae420d5c2a2da9a5e07a32f2147f45f3fa35dbde38224f8", + "0xdcc642f2123fc607d991e9794210770453e1befdf52582df0bbaacf85a148048", + "0x79a1710d32bfb57c607c5bd23fbd2b8fe3356f586172e507d2688185be8993ca", + "0x9ed1335deff01eda0b660081d44718b7cfa0fc69c3e9946c9733dd7ea64d6f8f", + "0x37f70e002a78cb3e8266d5ce7959a45649359b6b6ada51482d097b9f32e93179", + "0x814f9b9e4af3c84c31f40af0028086d16900f9c63f519f7bc3c18c0eb5c407dc", + "0x9957e067f5e79fa6f40bef2036c6d19734146ef231ffc22263bda5c36a7a12ec", + "0xa1a240b8ce8973e91dec89bd147a3b7af274fa1e233cc56b9dd83bcc9311a029", + "0xac083bbbb8455d3e145f59424d19a8fd7c6162d32a193c67405291ff64113bab", + "0xf2182db4682212298b31bd8bd086171a719bd68424da98dc133c83ad043ae903", + "0xb3015688c0e0f55c6e97034615de00173151c4334c72aabe048c47c244ea5082", + "0x15ebd9c48235918e2d8b2260acda2610b71fe3c4ba7f853238df940d021cd60c", + "0x847729223cc8abde709cd283dcc4356375ff89651f39c9adab865fea75e10708", + "0x39c9a1bd9e21caf13132b33a272b429c68cb7af1b8db3df08280860dcce141b0", + "0x31f21eb219700c01c3d910b2a922788d790acf6ad91c8de739795f1a740c220e", + "0x7fee191fd2c31b723a1d4a57af9366662e62eec889d886b0325f0c1e2e4f9d57", + "0xc682c555d050a03b8164ca927920ac6f97be4f96eeb8efe8e4fe3d7b48cc8424", + "0x1b887661820a95c845fa5c8e0bf10028b81073bbfa072a2f81f82b4aaf3c68e2", + "0x4e86af7e8a0aaea28c67e2658b97540e84ebd4cd1a9dff33c99a45cdbd0de661", + "0xa93100e0dcab1d0799a13d40ddb459e32f6e95d2c661094d7e8d09e008ed2486", + "0x2d15cbd72ffe23920d63d6916f68c5a75d2400b0417283b54ade9dab8ba3af5c", + "0x72f199719868bb4b4546407f7dc690180b2e8359ab6e747fe3ca4861c59bcfdb", + "0xc3a9a2ad727ee7623ec35671a330ff150e504bd20365a7dfec0295909e9973a7", + "0xe4dec421f674d4917299076c9885c49c51e7cffdc5253696b552b9c3ab307cdc", + "0x426f724354ff7fb3114bc2e66360c996dfdb1e8585d3967738285a2030a89b07", + "0xf39f6051d26d8b21aa5592dffc3be99480a768a0be6afb8b8eb5bbcd4058d4e4", + "0x061bbe83ae46285c935b9bfd646cc54f4da034b33dfa8b3b8c902995a77b7d1e", + "0x37b7086aa13372623ea527ad0dbf08bb2162b274420972c55030c4001e86a1eb", + "0x2de6121506702ddf0eb5ec8e2eae6110ad41b3982c73cf8382bea329e77a8cfa", + "0x4e941c3da784368dc838b7b8a77ee51868160a79362aef6caf0981fed3ba3d36", + "0xad2e32669978cdc836e32d3dae7a7a89b1d0e28e5f938247d1a6233eb055b481", + "0x8d0d13cfe3e40eca8c6ec865aef23fee9f5af445131bf0aeffdc87e4640a1212", + "0x8bf162d69c4565050dd4496e244ebfffa69bf13abaa9e7770b8173999340009e", + "0x21a33a33d92a99de5af52ae238a799e3307258b8b91a959a485238bad31f913a", + "0x21a95996aa2f4abe48a35617de16894f14292afca769679a63a03d68350cc362", + "0x57bfa7d2037da6aaa07f44594cef60a598ed2bc1218b9f6af48dad1b42759e95", + "0x7324fefadaab09a26c065bd98d3e9a56eaa7ef78df1269c1152bbbaaa6693256", + "0x41722de103de41c72010d1f19166ae0ccee2bb72314c719945f4162ffa165ac1", + "0x19b2f1f42353f935f46f8f9421bcf06e2ee573304d0efa13071d676ea174e18d", + "0xd7fc39efd2a392efb8de6d7d1c7807ed7daee0c8b82115acc60beaa7f6d9b86f", + "0x5ae63df9ecf9aee8cac4d0ce1edd37a95069d61c8d8ef886a150eda4bad4c87b", + "0x6e7aba83a3654cae6b12a6f87a9739735dfeb43ff67e309fe4701b8b17937915", + "0xd624f46d01a186da11d15eef4d79250191605e51ce19b9e02b62e65d84dd1c38", + "0xbc134d2f8acb6a60d424d4c8c6087fbacbb5202251c89ef720dded9947e3c4d7", + "0x6e8e39dca2960de07134fc043f73c9757c42c6827ef22d063c44a5573ce6c8f1", + "0xe53089dd7ccb7e0e78f3cd7d7ea342ce5a2316b50d6bd033ae621f6ec4329a46", + "0xca3eca5da2a9ded537dcd471390b7d97716deb0c5cd9e0d6a7e2addbadd97cac", + "0xaa5799394d99d90642d013e8359a40f2cc72e6b49c91233ed930ea1d32b360f6", + "0x8ae22559b0296959ef540aec5702dcc03d2b2bd9411b69af756d7d9e12e6cb90", + "0x5067bf825ad66e89ae1c46f1ebbbee8a77c2d285fb66c01b732b6c3f629b2ca2", + "0xf2f1c4304b5ec8b314fc6a4f9a9df69472cf0a132577217eff1c0a80443885ae", + "0x4c888be868c74ae86a9c0648429bdb207d18794e3ad6b9ff79d9291dfb0d0b46", + "0x3083508ec1669fa69d5fa4ab1d84501868c7a054a24b0a432b74be1f3bd0ed1c", + "0x443f7e4cc89e3c798c788fd40185525d8402c2160e5f3434986922c6f8179ab1", + "0x16d395ee5e915ea950dfc7639f90a29fe04ccbb47a2b0cd303ea719acd48ed58", + "0x901df34bd40035ee95569ea61c8a9874fd9151fef5701e91f02578c896704f20", + "0x4ce474419d801fad788b158bbf40dc7d7bc2ba43e1c2424f91b858055a7b61ea", + "0x10b2d6126ce1183cb227e291b184d7541b3380e100a70d876587a0aef2fe588a", + "0xac68a6a550c4a40c723ae37abd07d9e2d8486a30cd4f2cf7285f6d74882368f7", + "0xa0800069211efb656d0df4a81f1febfb48f251c712ee973afdff37a2baf83b99", + "0x53293ec8346c1a44e3cf78017dd033dd90be4737045e1c55d27130d7f6f36dbf", + "0x0a92d75455eb5c2167e2c07e1575a9580c135debe7bafc8d15d1fc2a25b984ea", + "0x32e6f505ec66fa987746c67a470df78a7a0fd136ef814ed24f3b5e0988cb53b0", + "0x20e6f99aa4b002e35f97900a34322af373397dc98ec28ee668e9921bc41d7219", + "0xc309a8620421c52fdae475d4a29530a2e56dc0051a28f8c4205c09cba5443666", + "0x0fb5bbd851d3860876e1dd038fd3f8d9e5b8e8b4e0d3b83ba0bc3d06222ac9ed", + "0xd71830cb6f3ad0776795865aedf50ceee94ba9ec89533190d3dfe2107a9e0e9a", + "0x768040f6401331b029bb0222b7494ddd21525091d936641f4ef89f00cb5b20e8", + "0xc52b410050774495fb2e22e0a94bc97dc72aa86cba7b20e8ce6c8a20ff03c5e1", + "0x026072e68a13a5ae0721d9e47ace688b75e16ba76ef67ff01d0a11f10e80ddc3", + "0xcfb1040048b9312c0d274a6b2576c7907176a983f0c0e02b120c10bee28f438b", + "0xd3036d04273279c5fa5ec4a096b8fd8a68a46c0536c24519b0ee5368c5103d84", + "0xdb9bdc3edecf7280dabcc104885942eced615311e93c2fa06dea812adc626baf", + "0xa25ca25f83c418bbfcba8a16cf661902499d3651b497e75161aa0d0bc4c3717c", + "0x96221feae35068b02e1540f06be277c27e938459af8718a2c883406b2582ba31", + "0x5584e36296dc2cc61e21c1e6539f66d918b800025fd6460da31f869cbc357289", + "0x7a0de103b391af8b2a9af3e6e5c2c6bf309d61cebe805db82d3addfde5395b4d", + "0xc5b000944c368f67c77e6c6564c5be5bb1fb84eaac76ca2bb3c7ddfde64e5635", + "0x544ea39b4d37a698bcb0c2dbd689ef90590e160db52deefe446d65aa7b60b20b", + "0x32160ce8e604c62c09814f8e13e795b654cfda57fdfef4b01c38ae3d078aaab6", + "0x5def86fc59562b477277bd6a2c1f1bd5a9192cd969c4ac578b16ea73031d5aa5", + "0xc0fc111d2c0eea81e7d6e0221c0465cf950772fe8dad94476c0e33d528928f9a", + "0x1b803a048d3ab8e5b4a49b3550c6586d71dd6f5409da6df0bc6df76de94e64bd", + "0x549d98175538c94e098c93f77ccca9ca0cbd11ab7c270ffcf1d3a908adb4d8e4", + "0x17d331df92fe4d8b57acde9bff8eced101f461e3470691ba7b46df9ceeb8129f", + "0x7a496054ef706cf89c0c55d848cac64779091799d2d64d6cb43907d2222c000b", + "0xe86b6819e9a482600ce2c9b42d248dfa3262547eb7ed42a6c1ebdb138e62206e", + "0xfd49fc9e5f4eeabb74a7d9ab4ae8a0e583ca153ef4c09e697f5c0207e6505ff7", + "0x9a297101369d0cba7b6f7edfe1a4a432d91f74f88ed3473ce14dc91cf616e903", + "0xd66a19a2834cbd910f2278b2bd5cae2ab9b58fe1a9e583cce4e2d22903d767db", + "0xd9cf8987c12672ec0e2bb97b25567f3f914fa8fb89b1186c6b665967834025a2", + "0x54e18efcf10895d51cf8f8dd6541ca097899fbb1880abd3b68aefd6e657a0af2", + "0x759486342c0162d26a8d2a0881affeca579a7cad80c32c6885d910f9e966ed94", + "0x02b2d8cbcb7244efb913aac80895e6cfbc81b60cf831b0a19b2354e0c7e00b72", + "0x414736aea69a7be69bed90c56eea05e5df9db9667f3b0b07ee6622442628fd88", + "0x75db93f08ee2236e4cc9222f8167c15c5bb31b4b9cfa1fe62f5175bac9c4c6de", + "0x07c460d27a5df943f09d114d99cc39c05135953c416591a7dc9bad3e2c064796", + "0x42c32a832d8a9911eeafbf0ed6550cce1be3832304f22a63d6edf1b72d92053d", + "0x2f8226acee0e5cb949592c1e77ac3da86f43f3834df940349cd6a55626ec7d3c", + "0x3f3e88302fffecb9751ee31a36de3abc6fb7963e264988904daf8fe6aec18fc8", + "0x0ea47b2cb04403773d9420ccfde549fe763bcdfe350c7546e3293b4612e096bc", + "0xf03a38c16a3ee05197b7c77985c2640af18479ebfb7db342416bd0baf2645eab", + "0x32c9844dcd2794e987ab14af8aab52d9925d77279377185697bfa362907f4bf3", + "0x72985d640d5ee8da6a1e0aa3ab71a19168802c29e3ab915d61910523d2d8a550", + "0x4453f4c055057ac3fb2780fa8fe131b6e08ef90a5b30930a83d4cfa5120ff6ce", + "0xb13ff4f56efd0f41bdcbc06c8a917f81614ca363f8c06bb3bd748467e146f959", + "0x172419af274d54de1a3ec6b10e5e8f98d0941d07571f9a5eae977597f0ea6e2e", + "0xa70469fc4070b5a675838352457cb6b4566a7a95ef843b0cf49fc0e02a342b0a", + "0x15072136cb1235007413c137115f4246ff22dd33bce99dec813042d6638ee502", + "0xa883cb9646a12a6f13eb4e1fde9422767be38279eef669068db6572d449da45f", + "0xe752e1e6ddc10633853a0c997727644b92aff7e46710c2c3850f6994edf83072", + "0x880e73586a7d6d79b4a7bd30f551d04e928747962ec263867f53dc33709353c4", + "0xcca01d7edd840727db14e657c9edb63a5518b41115abbcd848a60c9800e8b244", + "0xdf231e025e5c85b82fac8c836df78f5ee23509bb7b363b018ab27cf83680ea52", + "0xb42b4d31d1838886a6cb91123748078b5d07384afb1e574146a0174b6e3609fb", + "0x243c0ef11b9cedb39146fc35149291ea31906f60f56642ec3af28753936c7b08", + "0xac496eba34056ac76fd2c20c300bbcb66bc9d5257859aa8b8480fb1ea44cc60f", + "0x1631a82a3927c93bcb93d055ef94963bd00c264816768d7a12b1e6844b65a226", + "0xee630ea7011dc95ef7c9dab11e2c89a8065a85b7ee3ffe35bacf38efc61706e6", + "0x8bcb73df8715723d55d5635d5b1d5332af85b2945871172d22e10a2f19cbc9f9", + "0xa498dea3b8eb89b40f963d9266a2d4e71e3492c614e597e08dcf565e54d2d8cb", + "0xe798586f9f5bd8def445e5b66e1cd1037df1fc1e043b7c4a804d1cf368aaff70", + "0xc7e9da2ee64ed53fa9306e35e019c7fd74030f3065b5514189c50931d70372dc", + "0x557acd95f593728a732f1ef04b94acfd5475da37a18dafda2cac957db46b24a2", + "0x303f4929efdd5f54365d8927aae229df99e222e76e42b494e216d5365a4330c2", + "0x6252147a7a16f988726467394919d25e24a28756726bdefbdec768cc84d125b5", + "0xd9bd80354f25a846ccf3a47dc928b1d47958598474749795d4f1885f172fc996", + "0x356cc71f2169917a76e87355c358eca62bb5c79fc4f38c4992ad57cb35ce493c", + "0xadf1135a2511fd2cdbd33006738725459f33edcc593b1aa936b55889fe9e9687", + "0x8359e90f401234e4a85695c7e369ef5c107b246088baf3355046934197705c89", + "0x8535df28f436584d4132a947eab8332ed0643eef4bc0065654a2b972b8a801b5", + "0xec35162ffcd1d938681cfd400a4cf5b4a7b2e9f2682c6e1ad853f860ea81c67e", + "0x696acb4059074635e79c8c90647ccd6c151af64ea0810649a0ee915c8023e7b6", + "0x359959635111626c197c94a80756fd78c74d1d583ed12850997ad34586ebc4c4", + "0x2d0a0ef70f6d0c311c4ad032f2c59765a34b2c94fd55b62b0170558b8088fc3b", + "0x7b58b0a5f51d0c070dafdc79fedb828d0d1904b73b949007c418feb64dd5d7b6", + "0xe5da8f2899292a74ff2d99be3c14e1f6cc0350833a0b7c8abd20131d7e84c198", + "0xacedd701ddb08cc206275858d53088e7cf79bc27241fe428dae0baf03b22ec28", + "0x62cbc631910deeaa8a5a05eeefecb6767bd5c05037733b7060918eb6acfaeccf", + "0x36fd5949e5f568ff32575c29bca81c8165cc9715d29cef2f540d441a181b1942", + "0xe73486406b547dc4c501d79165bdb2c8ed0bef43ae6cacde7c353629487c1166", + "0xcfe15647f95e85f9eb40b8ab6dd5fd66c29bf219e0ee66281badd6fda8190e64", + "0x2b025a06b965ccba5b1fc2e9cb26f79f5d387442c6ece6c510229fc75ece3a14", + "0x8d970dcdf9304307ed3eb3ea5ec695c9dfdff00cc00376cb875a7772df2ed379", + "0x3840b6b250733e668bad42d0cf5c3881e38a880968ca6f03c514229d10a58736", + "0x237554c197bf6dd80d4e7322b061642639939467c74dec5e9360c60bfeb340fb", + "0x584f53ea37a371a2109d8d7266c6ca0ef86904497e8b601d009f6186c14ba79d", + "0xb3fa4bf7cf06d8647f0364bbbce92bd9c1d6eef7669a63f0fee334b8c667186b", + "0x53ed75ba5cdc1fd69b282656e00c9c5ab0c28cc17cf4f07ea576c070d2006d7b", + "0x679798ac00f5f81c15ea359b54c6a3b4f7eadd1fef0c78fc9fe58c1270e1c69e", + "0x2b7d6c2042392ad1114303a8805e4896955b5810c95b2113123a339566909033", + "0xa6950056e3343d07fe50f82e6647c589da56d265e5ad483c3b486d63e4626bd4", + "0x094df9cd84aab596ba1221ac7a2df1bb43206981bd343e7c3d1661c5810d78ea", + "0x510a2cc01e400a3cd0329fddd35f3211d18ea2099d66067d4a450f6c7acd1bfd", + "0x116865fad5e97d22f04646c04fd8fd653459d43f6d30f7dadf077ac6c8641eae", + "0x956c4adbcf5131420da0c11e5cee43e6047ca9474abad6fec9ea5aec14ef7de2", + "0x884cfa71e8dc327471521096c4f079d1222bc2517f3db31f81be9e51142813de", + "0xed5afbbc767c77af80c23d9c3abecf0e1714e9d49880c07dc45354daf56c9cbb", + "0x963b70dbcc6982cee7b7fc12ac146edb55024774e2f220dc5489cd26a4cef70c", + "0xad57ca104a46d424658d30169b4278280bbd74c09e2408b9a7b43e4faac0ea5b", + "0xb29a1b88766649f3c1f6c6b423854ddc1db310474652daa4468a97385e14e9bd", + "0x326166283a3f89aa63a2adf5cec902749843f1e9fc5210fd1ad7b938297a56f4", + "0x8cdc67cccaece6f483aa6a34a93bebcdbe831c196b5d9e615220afe16c64427e", + "0xcb251c0b203b2403dcfa172dfddde2830bc82427a07ba527c55e4e07c626f6ab", + "0x5c8f6f4029a55491883bde7e19ad15b65d35c509601e257171fc4d78e1120502", + "0x82aaa9afd4e62056c06341937780bd10f3525337e6b7d7a8f570c13cb1c79bf0", + "0x8044f3daa161ecd3eccd6ea18bbb965f66b50ebdeb7afd26988b8b0bdd0df326", + "0x4576daf0813e1ad8b15fd2f4e9744ad831f2ca81bb9edb756445bd74932d66eb", + "0x14fec1e241fc66f25a3a3b912cf3298d4a49adbf3dc331e8bd8fed88c6b44f66", + "0xdbe29cef44d5ee89ab97b52401330929287d5426746c1977062816947db16e36", + "0x712f6ee12819ec1278221335ac661fd658caf2fe26cb55d3dd36da53cc43fe29", + "0xd07de9135ac8b0dc057e974ef506c2aa95b38dbb312b348906e10b78ca32777d", + "0x3180a5c4ddcb91c24b20e5c94338e72f3a6752318a8360aa864ae4d067b73938", + "0xbf9d7a552b2f3bfcb296ab5a972809c5a375142b3736e0d337e3d353db967d76", + "0x19e0e06ebbfc33ab64696e033949b9b370c50688f9708aa0e4e0e02543a73d29", + "0x143a498ca73dc30b166093a9549877291df18bd554af02d5edd3b1283a6afd24", + "0x35934d806b386e094b7fdd90361f8aa74080e525969b76c89c15d35ee827df3f", + "0x7e0fe7fcd1eff987b0cc5ccf3581e820d49a09e8c517c2fec623b0e185bf21bb", + "0x460b1b58336ce7bb039bb33a036a93b62db73abd9bbfa6ccd7159f72dcf48358", + "0x46d76ef9a22d397a4f9a8650298292d5e63daa2080ab1521e4d85434c17e5b8a", + "0x2ee77d9203184bca605312f98e13a22b07e2e32e5c3fb44611b7ee1a2eb055d5", + "0xc6f417425fa37436678d41c84dbf4d46b8a288d5a8618a90b76be3108f66ebd9", + "0x2baa1014784a5ad9ab7eab019b92991406e432c79eaeb4b80678d96bbd8156b1", + "0x94ed0e92d1df6a80efe121129b13cb077ce87180aac2c264ed6c3b55a0e04209", + "0x60b5f9385243ac3951c0bec674d4895665154d8233f9195fbc731f241be05697", + "0xd7d31188b2318ca65f207a782ac3f1cd6b959a6d8e6c3cd3ad48d9f44b677919", + "0x45b29d14c2d11872d0c660781be385ba122fe550b42c66de13fe5398951054fe", + "0xf02957e01c369c8b737ea329fca80bf8c0723e800c57456ab71cc28654be8dc6", + "0x09b3e1d0458254a7654f8b693f3af5e8f3dcb7707960a482f9bb4b974abc5da0", + "0xe392a4875d4c166f22e305449dbcf76aef3348d6624429dad2f8fe3197951cb4", + "0x42f940247049805e59c7f6c9cb84dfeeb18b3d1712a9f66cec28637fa56ca097", + "0xbac109cd949eb8c232b2626dac41bd8449c15285148fad563b712f3d62d8908c", + "0x80e82a3634f10cddbad0c773037b67bb5aa5ac2e2bd7f5e50c94c15b5dd7c175", + "0x512e5f14e3a4de731c7e5811e552884fda33b784442248945f58c3f6a3ad6e92", + "0x067ddead523e9b248464a75eac231442c4fe8aaa04d426fc7f8302863bbede69", + "0x919cf5350be76779ab22160134dda6ce2d552d9bedb2e1480d1096d0b3277a07", + "0x7fda573cfdb44b3606480e9afd5d068cf38fc86fa5b049022971e73c1035d4cf", + "0xce645e4cc98cd298ab9cc56edd512b64f656f820cb1f0164791a8fa9af435ae4", + "0xcd1cbc08b9e421c4ba52da700c365eb51041b6638b485bd6291ab02f25562889", + "0xdf0cf3cb56092d73708d26430216a59619121a5cf82ddbc1b873bb97af362e4c", + "0x1edbec69530e0313d75e026b2147795fa4a164dcc4acc1f895b798375c907e05", + "0x4617c63af546ad1de9e59fadd22163d0752e0e95690afde8f221cc9cfa78a64f", + "0x56adc825a5630ae8cdcada281472409ef92b0ef313feec1ec17464d97d6af1ca", + "0x6511f4474ced03d6274a77e33383152c94ca0c792c46339462352ef079b5886a", + "0x84c1ba380d6214079bea0c8cd7fbcabbb6396056e80ab761eaf3b8e1536f1199", + "0x98ecfd506687c8f1dff93245cb079fdb79f59d959ec3ca83b8af273b722d224d", + "0xa9f62469371d0124f7b67b31be0557ed861328b37c28a953dacbd28736c784f1", + "0x39cea1ef859198776968ccc37413791b1c5ce661ceb1e93831440649187fd835", + "0x982c79352020c951425fe0dfc990e1cd6540077c1c2a230c382a4a4fd50dbd31", + "0x23068bed02bc3f33226dcfbef0f7c2cbdb0bb08b451421d25dce1d45dff81e86", + "0x2f0c135d1edac609c71b2244b6904219f0122c093a2dabd38a3e9dbb42a1802b", + "0x4f9e57f2f2c24e89966e37ca89d7f338733227c1cd0cf14503a987c93271fe4f", + "0xb498f21cfdba4b2593a2718274253e291cee57eec22bad777e8f7b3cbf624354", + "0xf828dccc597fbb1ce5e1c8963b1745ce7c38f26a95fd3e425addae394f204304", + "0xff64478ffdeeb52e57327e03e6bd2cd36c0e997b3a9afdf60aa3285c192768d7", + "0x0c4d2ce62c1c19d22cbb3a8cdb2a1930f940b03aec150c712891e9d76a8deaa9", + "0xea4253d364f234fccab3f2bf1a6ff5c337eaf237936dfa157d229445bb8a6793", + "0xfad0ff9d37be44784e9ebf0dc094bba83ffdd8eebf53b87e9e23ce5812ecc1a2", + "0xa9af96eb625ce3a5b36fff2b942e42959e732291df66b03420b4471cf3ffe2d4", + "0x96af0b12d4a8a4b1ed64989462f6db161354873c9888251a8bee079014ba568e", + "0xa63431e439e9beed0385500d8f384fb760b1782354e35b85e7747b7b75f14c44", + "0x497db911496e50fa8620465c527783a6713c2cc53c9b216af54f3ae31d7d0e43", + "0xf622358d66680a088c06a19977da810d362ae5d1ae8f2b529d467e3923c4a4ae", + "0x150762173038e979bb4eb3e93b12b37346c6b425579fe2ab2cd59e1e90129ca5", + "0x32124e783fd8ffd08d1dc7fbc6a253d125dc6f2d35e940a2d19efcb6fb0f2175", + "0x948f2d37d1dae5f996abbc8320e3107fe8b874bff9b5445c90c45c8833eee073", + "0x64d192829294dda57c3052904705cd6865fdcac40aabe3747b713bebc086428f", + "0xfedd73ca15c1764c597197a0f12f3edb669dd8571561a3654076e7217bf89e9a", + "0xdc7eb8a3e879d09e5e654bcd7f777b06f7f477225ab3d42106c8e7abd2a38ac6", + "0xc61dd8811cc808d956e339d05535e34dded857f5569a9410ba8be9dbec2be8d0", + "0xccaacdca701ade62f769fa9d1b6a095375c24988e4508fa02433408c0cd880fe", + "0x7eaa8e1a0c637e18cb8a599adfe6b0d60c91d72f45fdd5ae644e336a0b393dde", + "0xd7cd7ea1e669b477141e689ee2ce87fe560e37e19448a82e55e4c12887eb894d", + "0xd9619ce534b0586d52fe36ec6a091be77f2586fcd95d4b30e800b6fbbf51fd24", + "0xdcd6bd31e20d46f37f26428c5ed64f6ef03f1297278ba48ee277ba6b52c3db63", + "0x46afa2724b7caf0aa6380745a5dbc311b7f940065d1101d6eaa47051b18c52d8", + "0x2d5c3614ac9bd2167a51592df7832358365016899105ea88ec6d5be39ce8e4c5", + "0xce134db70ffda24e9a43bbad7585bb57d8113075baf7a1df05927d1822105e39", + "0xdf7cf01c99a8155c0f946d0a750b2ad71662f9c47a758fb02bf2b7bcc3f90478", + "0xca23385c281aa563c5ae99c16523e9caf13ef86bbcdf725597724d80f4e71cd2", + "0x77e94add2e8c5b8a60bc19985de43ab08e2f9669a63a822655a1ab78de7680e7", + "0xf56c326d8fba3a7edb2ff772ea782719f2d4542d152fee70799def06e889ba12", + "0xe1f9d2bbb808854b30c1d0f6cc7cb27bfc355681a771373a3c5627766e1cb2d0", + "0xc61bea0b62007e10eb37d0f6435e0d5abed10fc55d8c392c0ccc255c28b35e72", + "0x104e992cb67e8119357a94e385f326b86d8f0dda7877c14cb010c8dc6a25953e", + "0x4b3b9847b2aff1667e9d0bfa214de0ee20b9cef6b1f45977c50afeeb8e371c5c", + "0xedb81d35c7dbcef5aada67c29e122cefde11a3d07cab967f6899bdff38fd5cb0", + "0x497e57ce9b8e4597fc9b6a912608ff99efb5397f1829793f105e19d89447f1ff", + "0x221f419366f448595cc3f27e53469f2249a216aed06c936252fa0b2beecb0e0a", + "0x151655a88b03034cdd501420e97a08b8c1c86687fa76df56b62ad50cbe59ae26", + "0x2d011adadeb11e306469dc7897fa2ef5985b43f6eb07b3eedff62baad6ff500e", + "0x55c3a6c3ea20faa2968955109fa18f21ea0a806c3b9cb651278f41599c2b4043", + "0x920c3eece2b7e3030bc49bba992d71629334c3378a6e673bf2da53644e41fbde", + "0x95dbeac612d6a7d59814157fa3d51bea692c86211f52860c8bc49e09aa7f61a8", + "0xde8fd72c10bbd1eaa2237ada9c740b2b32d72cd4a8f6eb279d8b35f52b4f4cb1", + "0x92ef4d6f15394d453a15febf0620d99200df9905b6d303ed071a6e02b24085f8", + "0xfb6638badfda47360bc8d79763f7449552d78707847fadee2d6ea183efcd438f", + "0x6962595facb337eae608baaa4dea558f3a26da230768cefd1cf340f7bdeca39a", + "0x82b7bc05731496a389eb2ffa93ee9abd69d017d441dc6884178893adadc52ab0", + "0xbfbab6f4bbcff00aacc7992c2b048117784909c16795b9603d7aafe6ceb475e8", + "0x3e1105b20b800f9f2b2ca9728423fa1b8539db63ca9f66f86999f0fa59de83ef", + "0x3a771f083930eee0bf514c50b9a3d944e61443fae93878fd096600f3c3bf4650", + "0x5b5698d069c6ae4057c0f05c9af10e41c8edf7a58d6aaf0cd849ce06432ff862", + "0x7062ff74b45999a22cab0c346848de47c748febb5612f89b288aa1204d0adcfc", + "0x661a3b4dc9c1213e4e30ef69b27238911305b4870f702a7ac9f6625c68906d03", + "0xda31603752ba742aaa67d9098f9549ac865cf063e5ec77862857d904500a307c", + "0x305f1b1b53c0ff442520a1ec4726e7277d317b5018f54abfbbd1b612fd54d837", + "0x19f41e11d68f0fc313371cfe9da1f9f38179bada311eec42bb574d88952c124e", + "0x61c70ee62556830b0c7854c28138bb27518d08651f2cad2fdf9232f052e6b934", + "0x041016447af2c5112b60a0dde5167a6868305defdae3ddca9212ee956c437e3c", + "0xcb6c076c7335056b391018c8ecd6c111640559d7550954cf4b889be1ce04f8b3", + "0xd6c5247a0b05df481cbd1320c65c4e4e4cffa8d4f1a29f067c12fc866bc810ff", + "0x48c1bad3f9e173f9f0e98ce3b41a97c09181744dd63b133a7b5b9f32c721af67", + "0x2f1dbe06f1dfbea359ddc80c5658abf9cdf1a4e7ea0db30695c65ffb6d7d494e", + "0xc531864e3bdb927ad29a266ce60dd2d1c4025c10be27ca940f775b1ce960bccc", + "0x54607e21dac0197afce7f024e620f320c973fb0d4879917968617b9aad0d3bdd", + "0x05185108b2fd94824c85a4d9c65d84d6a022ab589d1cc5fdde5b8b8a3a835e65", + "0x862bd3406445e3e2b756cadb7bffde171355a2d0c937b24a5239b642211c664f", + "0x30865e5872390a13c4104b0579db6a4cf659ccee3e336162759b71a702e9546b", + "0x9d2b64fb969c966172c17c3d02ce3885d9b7fdfb38c7e01cea8ff9c0baeae533", + "0xb88e2b818c89cfa9093be6ad12ece61a9129dbe519b2abdc57a7c6aeaca4d07f", + "0xab62c1a51dba677d86599ad18404d5e1350ae83139ee21e2bff709e64dedcc25", + "0xef1cfdf4877190ecb4dab65c9e16611359661ca3ef4b4cb6d525a7674dd51bd1", + "0xbab01dcff8419ec9cb7d0ed0368ee70cfefcfea59c35662786fafc58146035d9", + "0xc828e57235b02780a29ac510179149a84e3512de24952c02e0d7fdc750a2cd5e", + "0xa5335d1b8fa984d05626aed51856487e26ace6aa34a8506aee35f06a64a100da", + "0x2a1ede8e464806c6336cd7fcc645f9e31056c11e41d82a2affb8cf8d136ad786", + "0x42e96ff594eda54a71ef9a41f0eb5ba0c820b1e9c9ff6991b328d322389dde61", + "0xd58267ea6a6334195e03b52b1d12bc60dd0f616f56125589d7fedb5c8fb0d497", + "0x273464216e94394be8af96cf2bb99ec2b3015571a68c431dcb13db03d45a4707", + "0x6863ca06bd5359fbe9b0aff9cc937cf3bd8e10a02c8f9c7382cb71a3b910d25b", + "0x247997d085025b714f1fc576d0cfdf41668950b38922d15ef9ce1069b2e919b9", + "0xf3db417df6478e3997265c8c2eabfcaf67a66268d3410fc02f5dd6eb49316595", + "0xfefc13d1406a6dc885a712761c8be64836a89a95ce38bb91a5cbea40a46def24", + "0x020ce16ad5c477688563eb67efd44c90deee216add117fc77800b3109f3ad23c", + "0x60a4a7ec9ed3afda184d5f0fcb1b5fe2ec3d10332bcefd805dbeca33e317a943", + "0x77ef13a23110b9a5ac8ad9365cc1edc97baa90cb35986622f0a2ae78df25f62a", + "0xcc05c74a64ff10191f23625c99f4f9a71814eb3457295265b1173d8c47965773", + "0x17f53bd6db63e3561d890f9576b82c8f452620a57dec59946627bd0d83f3530d", + "0xd6930355b21c80f04ff6b14056eba195042a04a9d90ac0b562d744e663cf82ec", + "0x147f229af558fee646b34a48498e404693c83ab49a697fd20fbfeb01091faf7e", + "0x9eaf39128e505d75c1dc768ca606f2aaf9dc741f86bd405500404b5399afbaf0", + "0x704ec157cb9376281f316bee5cb943368985accb7c475dd855ae320e16ce82cb", + "0xfa60605341a4f9247dd276b5617ce7738396906d957e2bee0537f37b8ddf174f", + "0x23e7dd0d3bbe58f293127b1f716ad112c5b5481aed59ba7363c62253df1665f5", + "0x89dee39d74d698f218f8aaae9f0ef80ed936e6ffd5a82b31b5861d4e7dfafb3f", + "0x66f921a058cb0100aaf9557b3f8c178ed7ec0f278e356dcd0ed7db342cac1509", + "0x7a9a91e6177fc85e93b714d54001847489ab13eaae56fdb3a46208f653dd1d83", + "0xf493784f057a9fd543b9fc4556ae935a76c5b5cede3f663b8062462e317a9890", + "0xa9eb993550e2f924d3952ae56d580682b929ff6a8db65fa7d2bd74a56a8c4abb", + "0x3da3f20d7a463c62b55d2ae6a1c1cf30a8eb55e0a30d279b357d335a9433ed19", + "0x8aaf40cd454d438695963949e01102122642820c520b2621ed79108c4c448023", + "0x2732ea2e4ee99a2f0aec7b86377b5bf516e5efbba3fe9245fcda83b0804af3e4", + "0x2500b46a15bf737b69cd667539e2b87092184c25674857bf1cb037f495a3a963", + "0x4b32717d3e31d413c507184c17d2a7d91155f40d22e5325104e6b755b9fa18a6", + "0xd9438a58e2d1ef6ffef11664c7ea19f73bb5111238878da9a088fb27a923c009", + "0x212e9719b6fc86b6beca90135911d95e4b5ba2506e541ebc6ccb68098bffe9fc", + "0xe14c9d170723ea1c6dc70809f21ef9024fdbf3cb33c1cc11933f93c1ae5fdc7c", + "0x4c31d58255e0c27ef0557d7ff005b4a45d594ca9317c1baa9dd00dd355e79606", + "0x8538dd9dcc8ff322ea78ba4cbf881ebc2d81e0c014fefd89e8c709cdccad712b", + "0xf10e245ecf414fedeba676b0d999403bdaf58ca6f78b5734f28b4dd3aecceeb9", + "0x50c7d9c44ddb5570c26909084215f491eca64b4b4ce4ef1d8d5c0bc78dbde7a6", + "0xd718ecd46618745aed0285f85406ef1c781e6233ff438b29ea762332f2c3b0c4", + "0x43c5a55ed1608047a270d9ebc379a9d27736fc55340b1474de397145369369e6", + "0x158e1402b41290dc927acb2183e55192020b9cc5fa4af0234ce87ec0aa2e6342", + "0x4049421295b916aef73a42a77de9ae781cb0da4372009c3e666153fd133dcff9", + "0x778bb3d01a7bb88273c0ee34a1bfd462792bc4b5b70b9e7a14d368d1e03d6094", + "0x2e485a90d5ffae19009607b9e607c4e209fbba22da97912ed75e142d1cad1830", + "0x7177d3f901ddca31c0e867b7c42a46d6a6f7b4a75c270f52593a940de00387a6", + "0xc3841c8bcaffc0ee33a8acf5b12cff19713b59bdd357fdc1425b31be9dd2ebb3", + "0x3dd9fed48979306b789b4cdf7222619adf8747dd025c98cfae6ede792b94b555", + "0xf317199bbf9f2897657b4535c04e4eb1d43f2e1122e3829a017aeec95cd3d549", + "0xbc47a9a1ca0f51179c37c6e7794f20bc6c8e31e70fe7f3851826810c90833e8f", + "0x99b2077c80b98bcbcf3ee09b7f2cd02fda763d6cf9bbb000f63d178d745faa5a", + "0x7347664fc2b96edfdbcfe053a3d918fe79a6e915dee9270a0f79b40ad00abaec", + "0x28689bccd6b23e92a6f25d9d1472cede63c24ce2aad7b3be31b588ecffe696fe", + "0xe4fb6b8d58d2331dc808f74e32402a22596e7ef04d797fd168b4de17daeab7f7", + "0xeb4092f71c76823b574b993f5ad8b850874b87623a47eaa04061c5711e200643", + "0x6145ee4690b9c27b2e170177b5334ff0cd4b1124c77ea0f169c98cb08c80edc9", + "0x34bba5982bdb45bf5c8727b8dcaafa7a455d8f7f806e39f94e95b1dd5391de90", + "0x1685dc8d3c701a733d7020c64fbca2e2d1c05d2addbe455f02ca7199abd029c6", + "0x0ecf921b9552fb177da57e91e59527d05cd18b1fa2f54b5b6c712480e7f1f652", + "0x49d9868a44b8f30b9a797d7388a6a777440c0a3185b65e842b5a55f9baa0f72b", + "0x6ae4e02f08a6b9035b5dbf29a9c3aee25c36bf321c77780e1acfe4cab5c18341", + "0xa6309795a5320b686f745dc6c2bfe6272d4cb4afa6fd3ea9e4b3c790b402845d", + "0x128a4389e0a0b5030b4706b6c9669a700b0ad381906d55984aca4012c56c78af", + "0x94865b066425340665336e738d793ca862d2c382efb482d752c9283c857db870", + "0xe47f3ada60b87ba55e4a340b4cbae02c6ee4f204cbf1f4a62fe1cf084072efbe", + "0x36c204b423567599578c9ede265943b945e3df2571fef565fcc6cbb21c565709", + "0x6691ca4395e410666230a976a462f46b58c83e0ffd727e083bec333402e6c4da", + "0x06a44ec720d3983d7b2c77dadc2c2e20d9477988df395eb88954b6e539150184", + "0xee0c8bf68bffbfdb2c5e15e4d79d708c49389987a6ae8b821033873565ec4e71", + "0xbe2a7cce0a3e1bd0820b47869b8970936e6fb1085aee5941b1252b3938c5d3b2", + "0xb1d3954aa6ac1b678ae51a92788945f5832af634af0e53142fd3a91130914a02", + "0xa973e09ade919a3f8b8557551e69b014b50789e2d94b4be2de142644df8910f0", + "0xb8ae3d08810fd98bcfa76fa93203a24ff3e80e85039cd62a36a3d8958c9a6078", + "0x9f091ad1105256504d83b97f3d102aec95209ce36420138d70b7198df224a54f", + "0xe8ebf358c5f6f885e141a56280b1bf61e418318c5140c26bfb4c64445dc3ed3c", + "0xecb1c3b4123c718ff1866cead5863b731654efa5b49f51a9d9619b8c57dbc9ec", + "0x59e06d4250155af53b7f22aa213b6f20c2ad0b099969571e4d6e6816e070e94f", + "0x44dba4b98d4a34e8284ae9c02a2257de95605d8bd6cb61a67cc491da43b39653", + "0x10398d11516f969f3138322b60c0f8b51a44a7a1d71f3c18b25f4ae21b2df331", + "0x0b8a483658ff718f4e92ec00482186fac08c2160d9e1a6bdeaf7b19357018fe9", + "0xb30056498c240a9c479f2d596abc3c75ba8a6334a1f067fca5dfda9b91d711a0", + "0x91019f2243a4b2923c91d0fd687d0ef4d9803225e9fd43519509fb6b4f463e46", + "0xf53cab26772063887236f703b5185d5c9106dc1c108c602d10871a020b5ce7c8", + "0xfd84e4b201b715a05c1893facbdc63145da00e382ac7d0fb97e63eb235ebb467", + "0xa1d51e9e22424f035aeaf23bd0beb13a81ee0cd4ccb4d420e3b80f2299ae2d2d", + "0x3f2db3426b7ba38947ef4469a15be9f12494b12cd37d1b9de76229d3840ff44d", + "0x14f019c2d20acca222e5084f55cc1c6e073e58a60867d582bed4497603e5db49", + "0x8f307ba4590168fb528672be6a9415a2c83b90d26a1ef896e8618a63b13d7839", + "0xd1e2fd29fc9e4d1a7f28ceb9818ce347aa3007438abc5b0adc43d445603e5f63", + "0xdb8c657ed853031594466edeccd747b78a5f1227149d54c3a3c59cbfc50298f7", + "0x8a3e8343eb490cef559a76f7c03fafe54882bce4f6a35ef5f29021726e8e7eea", + "0xb9886f5a86cefc065e7102d1bf8fdb477f0b246e87ee32c4ea29db8aa9d593ff", + "0x45baf273114e988ddde872ef1170029a69bef4494f99ced58abf226375fd987b", + "0x788bc2dde3599c2cc0153ceee7f867ecf2818cf08e848c4f8de04478315f0f1d", + "0x4d2cf749f89d44345562ef640f8cfaf9bbb087f62d830a0ce16f723e275e5a3e", + "0x0e82c5dd44cccee59469f1f38a66c2288504513ea9c1bcdb3ed415ea7cb323b7", + "0x01ceebf31a7b602585ed0529f8cd9c9da2230b3ac7f874ef404c074be55ff4c9", + "0x9b501cb332488b0755ca9cc01505a165d159fe0d2d4ec907703d1140014ae36b", + "0x07750883cad732656060839f0e67e017f956fef16fdb7fbedd0d73475cafcbe7", + "0x69948e5e2f63f00af534ce9ea0e8516d76f1c3db5da2fafe166cf181666a9cbc", + "0x0a3c572ae36a44530b767357e1aefc971c2946af03587a7d1259fc3500973412", + "0xdcc98654be4ab61012f0c1e1b77d9dc956cbf4b6c3503e621d7a8ffa9f26dd04", + "0x2b625a7f1d22e08c47fd6a96df3aaa652ef0a3def8dcdf86f596cbec09491174", + "0xdba77871f05920945849a2b0ad74548e5d7e1d757741c4d1534c209b6f5bb066", + "0x67db8e1dd94a4ecc2373a645949c43e9cc12bcb6e8650fd3442dfc86e85d3a9d", + "0x70da214c5082b06dc0ef325aa3c1e17bebf8c40e44858e10b0cf796c3b5e9d98", + "0xf461e7b1b74ed4964bf8ffcdfed1ed8f5094b11a72ae57563f6dbbfa457d6ed5", + "0xa3cfd80642855a941c82c21df77e4d2266ac826d54e2871ed80baa3e1dd1437d", + "0xfded6f1edb0917ddc69b2f419206cbbcf943fc66c059b29e86d21461ac963457", + "0x6326c110a24356739d9f42b7338f4e897d19d155ae6cfb1ac0a999b761358df0", + "0x3c719421a6a3ef772fb3ed7e9f0a28860d8e2d40f4856a7425824af2383e90b3", + "0xf5381034b97cf201fefdb94a20b0955ec34edd5e03016d7f2f0312721a9c66fc", + "0x3de46bff4805af7aa3cd53f78ed63851b03efd02ca76d8a0c3dc2e62cff8d007", + "0x34ae46f5d4bb2a394edd69301459743c27acea62d7c0264e1b159588ca577a63", + "0x30dec51438aa911ab34b2d05a23f7fe7e80435067316db7b61ef486b24bafa81", + "0x559fc54c1946b186c1fcac4476ba1a6d17f715b91a3690396c00b6ff416b9a28", + "0xc34598d264187861224e1a40cd815ab3c81bdff89bdf8059ca5d4c9c2192a7ee", + "0x0655d213b40bb5970f5af46c50b968961435d4a1d21cdc59a4138a01c70b8552", + "0x38e1ebf8d3c72eeb287a994ed713b084088ebbfc25c3e8088a24ccdc61f146fc", + "0xbd6a51a83fa510228a197444735b42eda36a35dcb984d40e83c8a6533738dbba", + "0x1a0ff3422d82ccd3a058fd81f986b19c3925da819e6d1e52324c1af082f16b64", + "0x77b475f55af6104796cb8d61d24b6a2aa8a4153491417226ac9e22f277cf75a8" ] }, "nodes": [ @@ -3834,32 +4770,14 @@ "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://1d1f7bcb159d308eb2f3d5e32dc5f8786d714ec696bb2f7e3d982f9bcd04c938c139432f13aadcaf5128304a8005e8606aebf5eebd9ec192a1471c13b5e31d49@138.201.223.35:30303", - "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://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30305", - "enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30308", - "enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30309", - "enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303", - "enode://4cd540b2c3292e17cff39922e864094bf8b0741fcc8c5dcea14957e389d7944c70278d872902e3d0345927f621547efa659013c400865485ab4bfa0c6596936f@138.201.144.135:30303", - "enode://01f76fa0561eca2b9a7e224378dd854278735f1449793c46ad0c4e79e8775d080c21dcc455be391e90a98153c3b05dcc8935c8440de7b56fe6d67251e33f4e3c@51.15.42.252:30303", - "enode://2c9059f05c352b29d559192fe6bca272d965c9f2290632a2cfda7f83da7d2634f3ec45ae3a72c54dd4204926fb8082dcf9686e0d7504257541c86fc8569bcf4b@163.172.171.38:30303", - "enode://efe4f2493f4aff2d641b1db8366b96ddacfe13e7a6e9c8f8f8cf49f9cdba0fdf3258d8c8f8d0c5db529f8123c8f1d95f36d54d590ca1bb366a5818b9a4ba521c@163.172.187.252:30303", - "enode://bcc7240543fe2cf86f5e9093d05753dd83343f8fda7bf0e833f65985c73afccf8f981301e13ef49c4804491eab043647374df1c4adf85766af88a624ecc3330e@136.243.154.244:30303", - "enode://ed4227681ca8c70beb2277b9e870353a9693f12e7c548c35df6bca6a956934d6f659999c2decb31f75ce217822eefca149ace914f1cbe461ed5a2ebaf9501455@88.212.206.70:30303", - "enode://cadc6e573b6bc2a9128f2f635ac0db3353e360b56deef239e9be7e7fce039502e0ec670b595f6288c0d2116812516ad6b6ff8d5728ff45eba176989e40dead1e@37.128.191.230:30303", - "enode://595a9a06f8b9bc9835c8723b6a82105aea5d55c66b029b6d44f229d6d135ac3ecdd3e9309360a961ea39d7bee7bac5d03564077a4e08823acc723370aace65ec@46.20.235.22:30303", - "enode://029178d6d6f9f8026fc0bc17d5d1401aac76ec9d86633bba2320b5eed7b312980c0a210b74b20c4f9a8b0b2bf884b111fa9ea5c5f916bb9bbc0e0c8640a0f56c@216.158.85.185:30303", - "enode://fdd1b9bb613cfbc200bba17ce199a9490edc752a833f88d4134bf52bb0d858aa5524cb3ec9366c7a4ef4637754b8b15b5dc913e4ed9fdb6022f7512d7b63f181@212.47.247.103:30303", - "enode://cc26c9671dffd3ee8388a7c8c5b601ae9fe75fc0a85cedb72d2dd733d5916fad1d4f0dcbebad5f9518b39cc1f96ba214ab36a7fa5103aaf17294af92a89f227b@52.79.241.155:30303", - "enode://140872ce4eee37177fbb7a3c3aa4aaebe3f30bdbf814dd112f6c364fc2e325ba2b6a942f7296677adcdf753c33170cb4999d2573b5ff7197b4c1868f25727e45@52.78.149.82:30303" + "enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303", + "enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303", + "enode://ca6de62fce278f96aea6ec5a2daadb877e51651247cb96ee310a318def462913b653963c155a0ef6c7d50048bba6e6cea881130857413d9f50a621546b590758@34.255.23.113:30303", + "enode://279944d8dcd428dffaa7436f25ca0ca43ae19e7bcf94a8fb7d1641651f92d121e972ac2e8f381414b80cc8e5555811c2ec6e1a99bb009b3f53c4c69923e11bd8@35.158.244.151:30303", + "enode://8499da03c47d637b20eee24eec3c356c9a2e6148d6fe25ca195c7949ab8ec2c03e3556126b0d7ed644675e78c4318b08691b7b57de10e5f0d40d05b09238fa0a@52.187.207.27:30303", + "enode://103858bdb88756c71f15e9b5e09b56dc1be52f0a5021d46301dbbfb7e130029cc9d0d6f73f693bc29b665770fff7da4d34f3c6379fe12721b5d7a0bcb5ca1fc1@191.234.162.198:30303", + "enode://715171f50508aba88aecd1250af392a45a330af91d7b90701c436b618c86aaa1589c9184561907bebbb56439b8f8787bc01f49a7c77276c58c1b09822d75e8e8@52.231.165.108:30303", + "enode://5d6d7cd20d6da4bb83a1d28cadb5d409b64edf314c0335df658c1a54e32c7c4a7ab7823d57c39b6a757556e68ff1df17c748b698544a55cb488b52479a92b60f@104.42.217.25:30303" ], "accounts": { "0x0000000000000000000000000000000000000001": { @@ -3920,12 +4838,13 @@ "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x42ae50", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0x42ae50": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x8a61c8": { + "info": "EIP 1108 transition at block 9_069_000 (0x8a61c8)", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -3933,12 +4852,13 @@ "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x42ae50", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0x42ae50": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x8a61c8": { + "info": "EIP 1108 transition at block 9_069_000 (0x8a61c8)", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -3946,14 +4866,24 @@ "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x42ae50", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0x42ae50": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x8a61c8": { + "info": "EIP 1108 transition at block 9_069_000 (0x8a61c8)", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "activate_at": "0x8a61c8", + "pricing": { + "blake2_f": { + "gas_per_round": 1 } } } diff --git a/ethcore/res/ethereum/goerli.json b/ethcore/res/ethereum/goerli.json index e7aaa4c485..28cd3ef309 100644 --- a/ethcore/res/ethereum/goerli.json +++ b/ethcore/res/ethereum/goerli.json @@ -55,7 +55,7 @@ "nodes": [ "enode://06333009fc9ef3c9e174768e495722a7f98fe7afd4660542e983005f85e556028410fd03278944f44cfe5437b1750b5e6bd1738f700fe7da3626d52010d2954c@51.141.15.254:30303", "enode://176b9417f511d05b6b2cf3e34b756cf0a7096b3094572a8f6ef4cdcb9d1f9d00683bf0f83347eebdf3b81c3521c2332086d9592802230bf528eaf606a1d9677b@13.93.54.137:30303", - "enode://573b6607cd59f241e30e4c4943fd50e99e2b6f42f9bd5ca111659d309c06741247f4f1e93843ad3e8c8c18b6e2d94c161b7ef67479b3938780a97134b618b5ce@52.56.136.200:30303", + "enode://a61215641fb8714a373c80edbfa0ea8878243193f57c96eeb44d0bc019ef295abd4e044fd619bfc4c59731a73fb79afe84e9ab6da0c743ceb479cbb6d263fa91@3.11.147.67:30303", "enode://67913271d14f445689e8310270c304d42f268428f2de7a4ac0275bea97690e021df6f549f462503ff4c7a81d9dd27288867bbfa2271477d0911378b8944fae55@157.230.239.163:30303", "enode://a87685902a0622e9cf18c68e73a0ea45156ec53e857ef049b185a9db2296ca04d776417bf1901c0b4eacb5b26271d8694e88e3f17c20d49eb77e1a41ab26b5b3@51.141.78.53:30303", "enode://ae8658da8d255d1992c3ec6e62e11d6e1c5899aa1566504bc1ff96a0c9c8bd44838372be643342553817f5cc7d78f1c83a8093dee13d77b3b0a583c050c81940@18.232.185.151:30303", @@ -130,12 +130,13 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_add", - "activate_at": "0x00", - "eip1108_transition": "0x17d433", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x17d433": { + "info": "EIP 1108 transition at block 1_561_651 (0x17d433)", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -144,12 +145,13 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x00", - "eip1108_transition": "0x17d433", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x17d433": { + "info": "EIP 1108 transition at block 1_561_651 (0x17d433)", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -158,14 +160,13 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x00", - "eip1108_transition": "0x17d433", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x17d433": { + "info": "EIP 1108 transition at block 1_561_651 (0x17d433)", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/istanbul_test.json b/ethcore/res/ethereum/istanbul_test.json index 1276dc4c53..0139d10c2d 100644 --- a/ethcore/res/ethereum/istanbul_test.json +++ b/ethcore/res/ethereum/istanbul_test.json @@ -67,12 +67,13 @@ "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x00", - "eip1108_transition": "0x0", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -80,12 +81,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x00", - "eip1108_transition": "0x0", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -93,14 +95,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x00", - "eip1108_transition": "0x0", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/kotti.json b/ethcore/res/ethereum/kotti.json index bc696a7db5..aeea86b08c 100644 --- a/ethcore/res/ethereum/kotti.json +++ b/ethcore/res/ethereum/kotti.json @@ -13,6 +13,7 @@ "accountStartNonce": "0x0", "chainID": "0x6", "eip140Transition": "0xaef49", + "eip145Transition": "0x1a064d", "eip150Transition": "0x0", "eip155Transition": "0x0", "eip160Transition": "0x0", @@ -21,6 +22,13 @@ "eip211Transition": "0xaef49", "eip214Transition": "0xaef49", "eip658Transition": "0xaef49", + "eip1014Transition": "0x1a064d", + "eip1052Transition": "0x1a064d", + "eip1283Transition": "0x1f67cf", + "eip1344Transition": "0x1f67cf", + "eip1706Transition": "0x1f67cf", + "eip2028Transition": "0x1f67cf", + "eip2200AdvanceTransition": "0x1f67cf", "gasLimitBoundDivisor": "0x400", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0xaef49", @@ -46,7 +54,8 @@ "enode://06333009fc9ef3c9e174768e495722a7f98fe7afd4660542e983005f85e556028410fd03278944f44cfe5437b1750b5e6bd1738f700fe7da3626d52010d2954c@51.141.15.254:30303", "enode://93c94e999be5dd854c5d82a7cf5c14822973b5d9badb56ad4974586ec4d4f1995c815af795c20bb6e0a6226d3ee55808435c4dc89baf94ee581141b064d19dfc@80.187.116.161:25720", "enode://ae8658da8d255d1992c3ec6e62e11d6e1c5899aa1566504bc1ff96a0c9c8bd44838372be643342553817f5cc7d78f1c83a8093dee13d77b3b0a583c050c81940@18.232.185.151:30303", - "enode://b477ca6d507a3f57070783eb62ba838847635f8b1a0cbffb8b7f8173f5894cf550f0225a5c279341e2d862a606e778b57180a4f1db3db78c51eadcfa4fdc6963@40.68.240.160:30303" + "enode://b477ca6d507a3f57070783eb62ba838847635f8b1a0cbffb8b7f8173f5894cf550f0225a5c279341e2d862a606e778b57180a4f1db3db78c51eadcfa4fdc6963@40.68.240.160:30303", + "enode://a59e33ccd2b3e52d578f1fbd70c6f9babda2650f0760d6ff3b37742fdcdfdb3defba5d56d315b40c46b70198c7621e63ffa3f987389c7118634b0fefbbdfa7fd@51.158.191.43:37956" ], "accounts": { "0x0000000000000000000000000000000000000000": { @@ -116,12 +125,13 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_add", - "activate_at": "0xaef49", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0xaef49": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x1f67cf": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -130,12 +140,13 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_mul", - "activate_at": "0xaef49", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0xaef49": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x1f67cf": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -144,20 +155,28 @@ "balance": "0x1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0xaef49", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0xaef49": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x1f67cf": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } }, "0x0000000000000000000000000000000000000009": { - "balance": "0x1" + "balance": "0x1", + "builtin": { + "name": "blake2_f", + "activate_at": "0x1f67cf", + "pricing": { + "blake2_f": { + "gas_per_round": 1 + } + } + } }, "0x000000000000000000000000000000000000000a": { "balance": "0x1" diff --git a/ethcore/res/ethereum/kovan.json b/ethcore/res/ethereum/kovan.json index 34755e3ec2..7ee636b35b 100644 --- a/ethcore/res/ethereum/kovan.json +++ b/ethcore/res/ethereum/kovan.json @@ -83,8 +83,8 @@ "gasLimit": "0x5B8D80" }, "hardcodedSync": { - "header": "f90247a0d0c0f490e8e5045fa96fd77ce45ed983900feaab964b2b0ba3a2d3a6b5e0f842a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479400d6cc1ba9cf89bd2e58009741f4f7325badc0eda0c91e59c81193c4fead4f64ecaa349e97056409dfbb787772ea3c1e55e7582b2ea08be1332f5e7eac286c7e5e5ccf3760e47f4a919548c15e748ad4351bb8405f9aa054e0533aa3433f4d07afa226230c867fc1e2844b5f49b25aba8df17630d9f8c8b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090fffffffffffffffffffffffffffffffe83a26801837a12008306f78d845c9da0b09fde830203088f5061726974792d457468657265756d86312e33332e30827769841727682cb841d6db93a25c84287603ee26e48db0dd301753840cc7283d411d3febd0485e02b2520086bebfb3aec1ab69403f45f0d59249bf0d458f33aa268dc4badefc73c8d300", - "totalDifficulty": "3571337622391237938633151520660827524104528124", + "header": "f90247a0053fd336e39af5f535a13b7d5d5de48a3288894e5c30ef3c200ef960604ae460a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794596e8221a30bfe6e7eff67fee664a01c73ba3c56a09bf41200496ce2f3f27d2ca493d3fc12a99ad3f86bcd0a5efbcbc763cce534dea0828e82d29bdc7fb6589d86190882a992526f1fe446c8ae3d3c35a77f154d1594a044bfdadad40cbd7c22b59cffdb86ec538447a9105604bcd2fdc71fe8881dd72bb90100000000000300000000000040010000080000000000000000400000000a000000040000400000000000000000000000000000000000000000000100000000000000020000000000000000000800000000000000000000000000002000020002000000000020000000080008000000000200000002040000000000001000000000000000000000000000000000000000000000002000000000000100004020000000000000000000000000000001000400000000000000800080000080008004000000000200000400000000000200000080000100000000010000800000000000000010000000000000000020000000000000000000004000000000000000000090fffffffffffffffffffffffffffffffe83cd7801837a12008308dafe845d7b683c9fde830205058f5061726974792d457468657265756d86312e33362e30826c6984175eda0fb841df67463557c0bceee7f98a615935454b5367183c12cf93717997624523cf40b8275f59ea2eef0af2b8baabf613216378149891e1a6bdc6497e18865ca325977800", + "totalDifficulty": "4531663462502962897665533388776747591449354009", "CHTs": [ "0xdb9557458495268ddd69409fc1f66631ed5ff9bf6c479be6eabe5d83a460acac", "0xd413800c22172be6e0b7a36348c90098955991f119ddad32c5b928e8db4deb02", @@ -5282,7 +5282,1385 @@ "0xa38c3526f5f6bce7e64a195cf8659160bc7318b8862ba206535626c338485c64", "0x4ad34e3c6be8cac9be3fba22eb7e99d951baf5827df5ef921f2b01d63862e116", "0x596670c729beb030c8756bf2ec6c884f9b4edc433a94f5dc5d4d337dbb712d76", - "0x39611d27f11938df810165987ee7edbe87cfb7e4068216cbb45848b4029f8419" + "0x39611d27f11938df810165987ee7edbe87cfb7e4068216cbb45848b4029f8419", + "0x79361e1ba8a7e9aa261fd428ba8d0d03ae3c5c0654517bb625a13b1dadad4c6b", + "0x1e6d182d7a1412ea37275cc95f0f2496b7dec0f4d3a7cd863181804abac3eaab", + "0x4005ca2b01460532862ee5ff0dc3426318f5eac5bada20bc6b1e7e9d55844e0a", + "0xd6a166005e327e4bf470a95a38e1085ba7d6a8d5a7ea9c19018bb6d3c1dbd34f", + "0x535f8ad6cade7c6a52a2832da75fec0a98da14f47da0c1e959f339376b2b9f21", + "0xabf58f192fd7aa6eecbf75a6e97e6312f188bc81317ec63b4b2310ba8b14b6e2", + "0xb8219420146ad1aa94c0c0f47dec2f0cb77dd2f18866e6a2a805e3afb65ad673", + "0xe9280d2e1bd3bce1cdcf4838afea321c448ae1b59fa85167b97dd0dccb667cf7", + "0x9625db916be5d529b3f302a4b9431884a8cc1df2d3ce477cbafe09031e4d5468", + "0xbc760d35898c5a46cd1164d04c61f96f88f812d9851050489581148b928250ee", + "0xc4f0e13e6b40712737133d8db5124599e568e769c17f9706bc275932e4c9914a", + "0x2b156cfa3263770911bf20e621e73ecdc15d9f077ded0313f745c62d425931e8", + "0x5d5b08e05163a3703086f203efc22dca390169343e2ea32fa68b440f3d134448", + "0x93d602e2d775879f637d8190714c8f86e22f921a8f7a90a514025dac15f13091", + "0x41856d09e5cdb1b37ac1590741281d56cd9bbdc72bc7e9cea4266e4139554873", + "0xd9b453a1f9d7176454985dd17a59008b5fc497707b92975e0ab8228bc4f22891", + "0x2475d0292574696312f348cd98e43561e4ba57dad0c01c8779ef45c6a39c9513", + "0xcd5189cd95bc20e47fee0cbe100ef7777679a0f15bdbbc82f80dd211c895bb55", + "0xae7cd67827fb362e335629b140cdfc9ff4d9137b0fec89e988d227e03bc5f649", + "0x345cd8a9a5a389068176e5d54f2d0defc7b39ea6d7babfe432f6c47ae12ebd98", + "0x831357325401bf11ea0adda4abae209ba81a20efc2734dc9f11b4033672e8bb4", + "0x856137a6a7a17e1230e91a9dc64417909a79c9ce57833db5fde5bc4167256df9", + "0xcb64587f0e44bb15c3594d8442d92257e09baf78ec8cbb9092f67953ba60b0e3", + "0xb8bc5a53782f4119b360bcedfc3662c4e102d06c8686e2901a569e486d4196eb", + "0xa92f9c5084cd2fd92dadb9912c5aded43aac05849b36b2d829be26270694fa34", + "0x088e4ab846472171547425a5abcdac4f4f50a7f47259d197389c14845a7dd853", + "0x76d20919518ddea12c635e52842d8bcd1697aa9a41ecd0d1f2fb396831e709fe", + "0x4d34140c828dee4aceeb656ac4d15ccf818ae6fd98ef1eaedf87ae28f0ee93f5", + "0x6526417b1c4138b9ecef717bbf06515bb336d739a6288eae12b53849893dc656", + "0xce9304eda752d4c1c97aa97f74927a927080f313ce252d111cf9825d2f2d0359", + "0xb7e6ce62e4bd14f015ae764880ca440d9ec117508b082b0eb23d427d00ec16dc", + "0x58089838ab29dc5851f38a9d508b2d4ee7a2a49d70e32ed5f2e8001526ccc80d", + "0x9007dcb523bcb584ddc15199c283e4ef90004d09d110117fedf631d42c435568", + "0xb35f232bde16028533c53ff82c69da2d34538f1a41fe6416d69c851677a174d0", + "0x762d5bbedf23e953f9e3fb0648fd1846bd3d8b53cbe02e2207bd2273e817d14a", + "0x048780d1805ee50d85143c62da241888c418c10ebe9479d451e37cc7c2a20642", + "0x3ed2db1a9c033a594ac6bd703bf1d0d3c5caafe9a1446a8e1bcc8cc8322f66fb", + "0x97c713acede99d299997f8e078ead9153ac3392ba132c3825c6009b1727b3d76", + "0x16f5097e86dd4a815c6faad745513497c6ef62331c711f18661777cff090ba22", + "0x623b72fb580ef857f3c81355f6161f34f1140861f92db0ba1dbe7eec8697e54b", + "0xa8216d3356a6966056092ccb9fca328b79db3d9a0036cbcad59e7625c24174d6", + "0xa0cb90be7caa993db5fab7c2354c4333449495edb246b74ce2d83f86cba8268a", + "0x2e86293cd4b75f81bdcfa4d20ae1ba549afd5336748c44216ac9f12a53951bfd", + "0x1615fea52084d8295bff712cd355bef869c7b00db5296be282227018c5eea944", + "0xf0b796fe169c32f3af97525918949562939514bd3c00cc2dee09a64bc38bd24f", + "0x7ce6f6766e08e124c0aedb44c7d3513c0cd63d45a16d3c7597d9f3365e7aa647", + "0x1f5c622d3ee407f0c5ab62862c50b27ae8e6c3792bfbd998bff6f2934c5692fe", + "0x83acd79e6f162c0752fe943143ef47c20ab66e57bfd6fb6b4fae6661a44ddff0", + "0x08b272f48b3738d22d7940e60dc92b4d8fac3452b357b957b3ac4e7d3dfdbe6a", + "0xfdf9c23cdd45c15328cd62e34af53df767e1e6b7f0721e1065bb96547cb56ff2", + "0x0c99557d242de5f706b1cd89464a0d597ed09bc51b50d1834573d4aa7c468f4b", + "0xe7c26735976f706d5245cea6e7e7f469426752ca995ed2247f77868f7ff308c3", + "0xd8208af818056c6686f029dad517a96c7b82e42c557b4f938e466f5641d28675", + "0xf1102bafac29a6b4ce108081b610ca45da5e234efffa34d700867a448dbb9054", + "0xc0d5e5c6635e98d52aab718e9e0ee0440358486806616d8de3daa743d484f51d", + "0x32b548711537aad9e8ffcf8fb015d74e77b6536ee07ad1a1682953bbf6e9fba6", + "0x06f51b30a859c758a4741cebab44754d6f91ddf8644b5afdeda380bd729cb4ee", + "0x238bf40e2d1bebe51cfbb6fb11e3bddcb0757996aad3b7f6aa3ed87fd4e03fce", + "0xa1834870652eb6f5452656b33e738a5829452666f1751c30faba0386d21ea05c", + "0xfa96a2a1256cf47960f128c209c10dade442282acf7c8d15911c7a289d0ff8fe", + "0xe07139f163e7bf90b2ff085d0ef841492710a3ebcd7a64f3a66cd072f1abd684", + "0xd01b44f6e0f9bbb6d13d37110738b72a07ae62962e4231b52fd81b7e209e8e58", + "0xa1afa6ab35fab184a2b90867ae397832bffad96b8d733059c7b97f78d7a72e5a", + "0xb60fa45400097ed0bce21cbce1789a3532a1a4b3135784ca336068b732a12642", + "0xa44ad239bfbdfec0f3422250e4bd25e837866490675852ba64da6b2ee3f5c66c", + "0x89c3432ef75e4302bf5dce9c291909f8eeba8648d1d6fa70f3835a6487e14349", + "0x78334338ef2ad35ced643bad4bb5dc517295ad2cf7839ed8bc8ab3496fcce0f5", + "0xeded53480159288a3f6f8b0cbd14e8c94c3da2e9d97164a58aa27661564cb89f", + "0xe3a6a6be8aa2673db5b56b2a1b9d5233cd9fe7fe8a984036bb588e1fba06d3b7", + "0xd5942f48b6e78b891e2ce860b786633fe4f4e17fae920d527c71d4f8443e7ff5", + "0x667afe5eee63b7dffcaf8c144157a41a151df700512b56a84b2d6286aa9b92dc", + "0x6d670a88d04bfcdf4faecedc04b364006ca09543529af20445d8d7093c786d95", + "0x1b600e382106fc7e492ae3c05a573c8e211683775bd0db9db07b0b0bff5cdd4a", + "0xde15b791fc66efde6e39f37e49d7f1162cde00baf3222da1d1401ef24415265a", + "0xe7ff90ca3dde08a2d1fb38526a0806bab15965209ecd6e0d8bcda24b5f809f7d", + "0x8365adbcbade203c06968b85e71060893fdbc4d4dd66bd6c1bc08ef10c29ab93", + "0xb8222140c83b357fa4e232314746a02ff57aac91d63225c51107d3be5520a38f", + "0x9f4b58889af2a673e3f55cc069b3e9ca5c963504aa2775d8052cdc3758f82dc7", + "0x474dfd45eccc3b8d1b0f1cb36d2545df6ae59a4d3666c6f315772e05b7da9673", + "0x615487c4a61df4686d8dc4950e22b311fd935403beab7d5f154027e7a7802b2d", + "0x811f50edba1e1d0492551855b8cb79a1b4ffda0804a585e433285dc81d8fb057", + "0xfbc0e1d2de44c4d4d92faf0c167f4d137dbe2d93435bd99f751c378d93f7d3a8", + "0x2031497e476b46adef3194f6bdb2326b23f47e55d55103ff1963a79a95c239e6", + "0x40f23373096bf89bc2027a61f48f2e2982d58242fa8adf24b0641761aae900e4", + "0x14cfee91e814e0ea923f21ba4421a02f5f9bce7e04d59878646b4afb31a8e93e", + "0xea3fd4a836bda02c4c0612964dfb25c15f7dc834c9817d4bb194fbfd82d1393d", + "0x09d6c30d525af2d0d6608bcf0379ed7e321d74f16843c96285a3956ef9d06082", + "0x2425f2bbbaf17b1926e9d3610b5e6876f9fe1767352bf4f5b806fcd21ee58bea", + "0xd8bdc3e15a72c6261ab9216cb4cbb812d33c1f9c3cf8f1e630b92e27ae8c6304", + "0x9d896d105c472ebcaec5d487e40e6d409cb6cb91da6a94e306d6f1de5fb3a8e4", + "0xd3a3c729cd0d7010bf7e17d3343b3f6c9405e2cf0948ecf37c3c05177df931c5", + "0x524e7e505ca5e36163c246d4e1b333dd0dd3a7a720877472f0dd8bb659d88b7c", + "0x647d22485023485c0289dd8df83b1331a98f48ffc6adbc864b85c7cae0ab1118", + "0x5eccd294cece358e72b87ce4d0b11e4fb3523fda8f93f27bc288f9229a0cea0c", + "0x6aecfdaae8bb50bd4827035e4c66327fd295dd4f19e53a99dc748056404caab3", + "0xa3af685c6fcaffd91dd6ec6c30214da5285b07f673dbd9ec43d3da0a0a226d7e", + "0xd508d8a544f72a66f1693abb4d07a4c26a95690ddbae628f261832f787eb651b", + "0xc35c69ed5f2f37d3baa8ea32585ada78b9d966b8ce4969404c872390415eca27", + "0xcc08424eb4ee42564ca39cda9661b3c146d576206ea72dc7643dd343e8da4c40", + "0x4abb0d3d3eff0f86dbcf667e99fca2118362090501e03ad04798d02262ab9e1e", + "0x0d0d6542efb9a49042f9e131aa5496d6aedc03a8fbdcf05bf614c3e5d32b72c8", + "0xf1d235f1bbcacc7862c9942ee82db947fd0eacd5039055612968e4febafe7a52", + "0x768681c0cb71a72d9a52bc63ed393be728f095f1ba3f3a424f0c1b67fb4faf50", + "0x68efadd7e49ce40184b29296555c44120a666f234a525b889aa4921dcda83e08", + "0x6aff7489abff5b790c09d53f3da2c8a373761aaedc83a1c5d6b862641b6605a4", + "0x753fd6fb3b007ae63e9cd5d5bc25053d1bd4ac6c9708a07a8df1450c8956531f", + "0x77878cd77842e9e930ef1237ddfe50b137f7352443927363f14aa19a31f7004e", + "0x3a06caf1844a06468ff23005728a45137c2d1c924fa87ac71deeb31c73d6e878", + "0xea1a39ea595083c651e45672ed592b27db5fb47ea0f741bf23fc83f0e950c037", + "0xc3cc7e0714a5b462c5e7dd93ac0ac76cb427c81ea9b94b853f5cd54ad4123f80", + "0xb2b9e0981c9929a2151ff29e2202e908da74f3f1d7006d96220f01af0327ef34", + "0xc6b534eae7d9aa07ab95cdc02a829e97611d0632c7dcb307397e2eeab5aebb77", + "0xee8051029bc9aeb38b39564e9492934eb901735bda6c3b67130418bb0be15c41", + "0xcadbf788591bff225a36f72e5981e3936720ecd3775f2b368e5423384a7ff5a0", + "0x034b4475be71397ca7e1f85ddc15b316b766c6cae74a361931ce18f83f2ef249", + "0x3f16cb1de81b875a2d46aebe3924da1c8b6df3d0dc968ae2e0176ef4d69fa530", + "0x77e1c2a52a947010624cf0fc71498221f7d0f8d46802e03ab1abaa021ba894fa", + "0xb4d184abf7522907c924d770f3aad43d81d7d4dd089b20a0ce2648dc568ef8ca", + "0x2da70b5dcf4d7776c3de078dd6a111d23434fef4e15e7d9125eef5dd63c17999", + "0x90608c7374ce7c0d23063620defbd83d9897db655930a6a5e5c5fc049cd92bab", + "0x5f1e9cfa155d965701e1b93a36da8b2d6059716fc105f3894129ea34e23f8a22", + "0x74f213b0306a98d97ce0c8d5d5707fbf800d762895c5524b9b0f32fdd2662bce", + "0xa4c13a7b4811c83b98fa6f4e57983a3b08517c2b0810c86644395587c7ccbc5a", + "0x592591c140819a3b9456ba251abc65b4bfd4acf6fde0411827ad42a5f760a1e6", + "0x335e25a38bc00f98f3bf193a6e037f7844a825ec09db11af647f2f280205b2ab", + "0x2aba60c841043ef684b3758f9ac56fb336e228c87408dda9cdef4aa2d88ee1c6", + "0xd6736b71810d6ed19026707a4db74816577db0adbb063d2baa9e433bbbd114a6", + "0xe0221560ee13bb475da2689572fb48eec16c50c2b2109e8edebfb72d63feb636", + "0x44304fa487f8eeaf507c2a4787a2ee929ad59b906a3f5ff9396beb1bb3ee0e5f", + "0xa0378f726baa83f7d06096eac4a67d7306547e58266cf45a5b6bb6e5b9c65b6f", + "0x7aeff73e4b94424af1208f5897876a20e2ebd486006c383a35cb8b5ee0136433", + "0xbcb7ae5c1ce4a34c0acef3a982a82146f6fcff13a2312cd7b7977a7a5aea83ea", + "0x51a416dec9b1b26506123c3bd092e36430cd3ce9a1c50aca8c8a2809f21f20bc", + "0x1c981cd88aa31949e2d9ac868501cf3dd4874ca407401c32827eb3ea835e248f", + "0xb7b3a4afed5aed27db9c416c5e8090b82dc2eb14456b052398ec1f817be54bad", + "0x4a493270bd49e8ec56ff21aefb2e40e040564cce968a42c84cddbd810c4f7624", + "0x5d75a8daed22e2b6a0d52861c55a72073ba119a741d0ef52c997ccef0b99be8e", + "0xc61210f854ddc7f9492278eeb94d6770a8da90f3c5d66e54195e0ab18d2808c3", + "0x4a1833384ed195f264c0434f29de3cb20ee1f14c2bc6c31db396f3b2e3815ab0", + "0x24354fcb35dcaf8c7b3a0f96dd58e1e2587a009ee440f59619358ea68390e145", + "0x56a3c570df0e78d2a92b82459c91da9a70ec1610b1aa6c9d6d623d302adcb153", + "0xa9e14bdfb9c85d91bf989531caea2c24a6ef10b8a925fc98aa0444db1213b67c", + "0x36d4ff601992399287ca334ee424dc39542cec26a042ec24c3024c9ae76115fe", + "0xf47f5b1b7dac37e7f93e9adc232296243ee79c3d5ade99e28583ad23b0748de8", + "0xf48b4fe39f0e3684517e79bb667e374fab01f1f1e1f682da3bd6ee401a7ea048", + "0xb099eea0b8c0c94188b9bf73d01e403accd52895c3ddb0850eae86a6b75ed8c4", + "0x76e57574ce65ab49abf1fc9bd72a702b453588b423805224bf13490ee8da9785", + "0x1580b40e92794bbb294cc6e7da1b1f5bf161187ab2caf5860940d5d3f4903f0a", + "0xb884e6f7a70b5afe75c86456dd8e3edb94137b527dbc4786e9c97e37dff5f415", + "0x303c800be72d2ae16798717df9bb01d106a0df6fbbbb36dedcf6b61b11d074e4", + "0x9947becc33b29a805b5f09f4efed31a532820cbf7c42ea34fbcd38039e4e96c3", + "0x122f3bbef2cd60d7c6309d308a7ef662c6fab481748bd4835b715f39f0c812b8", + "0x44ddb87fef882c11b05b7127ce6ebab8cc55e5687c19bf920d2950217788d764", + "0xc721f956bebcc52b6bcabce4617610640581ef72644ccf8a05c940550119041b", + "0x194d0f7ef0f04d297c22d8731fe87666c8ac1acd1b5df44f7e5617f4f73e1613", + "0x1ec7b48db40611fa4a111ad831e7ad4faf22740b3fa9c60869de1ad392939866", + "0x31318fbc18c6ffc3b3e9799325fb8229161fff34649ce5e0e2221a9c4808e418", + "0x6f44a27b1d82962cdc27cdbced9a612e458316fff1200b9ef58861106fb7f886", + "0xb3714e4f771aec3f528480c096e203a9782aff801e2a681f42b134088e529599", + "0xc329250d5a39a0107ef6bd8cc2b179392f721a1208f557b1715548256184505f", + "0x13213473c69aa7189685b9474767422425eec95aee802d4b8bafa02d48a3058a", + "0x6c5f8650cf7228a1580bef2fd480c3d6288e569d5f3fb28bc0340bd651c097fd", + "0x2f21c05048db2ede9c3dda8f0394c6ff17c70cb5ce2f62a2b53a4065a0569737", + "0x356143e10a7ae4cd41c7ae31124d97f0e3b0f9ea255ebd5685d0712a8a4b2f45", + "0x03ad925242a32c9173d6acc1d7b188ac86b00d25777b6b06003345c47c29e10f", + "0x7c4b44d980caf0ad323655a0a4ba7d804debbfaafbda81a95ae1c2f03805afa9", + "0xcc17b50e9eade6ac4a9c0b7d2853536db6003bfdc0129c29973f805c7cbf6807", + "0x98a60de45a13599d56eecd89c3b8d0d0427c35d7fa3c20c182f78d12496e08cd", + "0x80097c7753eddd20f4de6413fc82b0fba8f8f2ff2b6ffc97f58dd173b1dbf999", + "0xc1f4fdb7fe633d21b51828d2bc115d9cc9a5017a98f95e626001d590dcc1de70", + "0xb1ec029ba4cc7d71d86854a39149335041f52582352e9c8c76dbbb898e87fa1e", + "0xac04dcf4b5453b0c478a18963b3aa5ce321fbc917c05b5132c22acf2606dbbd0", + "0x4579dd22fe52519e48e27ee4b5da2896689549326253aca858a96b08c45526ee", + "0x8de7cc9ce507c7489f6429e2eb507582a7006c15a25728a994eccd95fedd9087", + "0x258a66b2e1d0bebb19f08da6323021c5fece90bf3f1114c5787811a368a0dddd", + "0xcb9370e7f65bf64d6d11c5cb3ddd4819840ec5a8464c81445c617cffc1333307", + "0x55a149bc41a31e6eb5f4dabfecee2d8604f936df209492ae356f0ee35806b5b2", + "0x203c5be6454d72976d94e1125f6212426b6ae3e4fbbc7ce6c31b75b9f42de146", + "0xb1c77ff2e55a7671c80d8b7e1fd6286b6b3bd280b7b7aee12388e0f3cc0530c7", + "0xa34f201619aa9d00090fd2d94a8fcb5f456de04b6e1f410d27edeb032a414144", + "0x87bc97f7562bb902f9afd59b8c3584704e3b05dc75114329249f2a8336d3a2d8", + "0xbf5f4092f59f66a8c54d78305af0b5dc6405d320b51088c15f72d8459f687f9d", + "0x189fa28a6e229c42bfd1c60629d425d6a223ceca2f13faed9a79fc7a082ba2f5", + "0x87eed01d1fca18b44aa095fe1e212942642caecda2aed1e8533eee5a357c6e7d", + "0xdb56a9c7bafb526933f2418330f1dc8020c51e4037ffeaf810186c61d405ab5a", + "0xc1283335e76bf62923f94dbb63a9ef6ec6c8a979767dfc66cda6081752819226", + "0x3125af189ba7051d4f314d0a6b351a0cda3a59871167298973f1caddead05279", + "0x1777118bbbeb42ef357fdb3f1ae568a83ce89584519e7f1def3357ac54c8ca18", + "0xe841dabd77e07292985b6e372760473dbb6ba35724c2fc42e1816657e195d59d", + "0x7c00ca2cbd1a49de4566b6c443cff6b7dd077fe86313f809e7d9eb3f0293cbee", + "0xc8df57f46370ae8c793b15c8456f6379c5eed28c78f692c1486948966c0d5420", + "0x02a2160c725ca1632ad87cff68ebf2c107b3bfe0699f55c1441c0367c8f6b73e", + "0x9d85c219fe10869c5a4103dd1f38f2878e7d3333d066737718466324fe2ff875", + "0x535a2e93df79e1ba88f9614b883584ea0d0dd7ee17052f0a8b0eb0cce61ef195", + "0xb35e52037ad5bb1ddc02b0e3268147299c0cb21436dbc65b9383c057a9920c27", + "0x71b8496d5e0cd412d2181f4f02ac3874afb5e98f7e7b3b9cd667c81937516fe5", + "0x3ab5f9107f4b148421862ec6593d98d05f95bd3459e12de54e4301d31e217b90", + "0xaacb7889cd7671c766bb19b362eedb5cf22366a233e07a628e408ff3ff73bf1b", + "0xa9b596c413b107f1712c494f2e48bee6db7a105644afea3d674398007acd55c8", + "0x25d96a4986182f1638de81123d0629178aa851e6fc3b8592d5dd648c3ec76bd3", + "0x49501dfc56ba6cc4a5360f998a03963b5ce648a95ea74ec9a035500c037bc7b6", + "0x11d882e9f070858388f7b1d48080c278ebd94fdece180f3de2015d158e2ffe61", + "0xf2cc3b126cb063b38a0ad5ff222d20288afe8d5cf758c9a9dcda9f2183953bb3", + "0xbaf973cdd22b1169a892310116c5312626a8a7fd8ffa4c3532e9cc7db885f5ed", + "0x9d20c78be225aa2786042cd0667474467fe41c6d7e28a96b729c8a88f9498051", + "0x3c49f270a76b6c2306d4e737c81df74a7b0b82a91c0eb535046d4cf36a6ad575", + "0x61322631f7c967b9fd9de3afd623d60055c527df98b05a6d77ebcb264b762d72", + "0x91143ebf3922994cae94e37c7fab95038055e5dba13cfc38f4efe7f3781b0076", + "0xa90c27e6c0d6e66725c75d6789a876870f865ab653ab69e1313efe663d88db1d", + "0xc27c3ecbb64cb90a02fd47961d9db399f0488f6d122bcf83912c7a061692159d", + "0x7b284b429ad5f51e27bafe14bbdb1033604910d75a3cb3842408e0013cdc4706", + "0xff2f277d41c937f0b5bf1f59b643e57a90237b9a53969431e6ed99762bb19dbd", + "0x6d115a652c44b9c371d4bae78984ad384f930a18f12b4e1c269a16298f32d2e1", + "0xc8694a6280676998b8fc42eed5759e21d4b2a145186b65f20de1a68d852a8c62", + "0x89d434ebc811ab3fe01d841bd8e88dbc6d9113acac1b4bd7f5e2a268ee593260", + "0xa007a550f1c2da564b2faed555863d44cec54c4c12feb4d47000b8b54ae91c60", + "0x49b4fa98d09d322d3150c1cc191f9a39f3784110270145a76c847c4956ea48ef", + "0x7b5f84ab4c44f508f40c2077127d06c68e2cd37191aa584000aabf8147437c33", + "0x0eb9c946479d5109af0344de26570cbf89a3dcd4c42be28c6142a421e3987502", + "0x277cf828a7bfc648f0bfe3739a0958bd330ef782a67c8ea2bfa97f1dfe35d4d8", + "0x7f4a423053a20cd3c1f83872a35d427f97e8009730c0fdbdc92d837227a82b1a", + "0xd1a39c660ff286a2c4569d631ce03dc796adcca239d740963d6d4e1995a98491", + "0xfb578539bf7ad70fdb2a8d0175723bbfba585f7dcc171700dd72ba434400bc80", + "0xdc393f797609167026742c998c4129b4f8461e860829382ec32a1a656910232e", + "0x2acef052cc28d18427a37eac730df39f70dcc827f75077b7bd126956143f458d", + "0xfb6a835892a096de260081c418d51930a8b2e9ae4084d50807f696e042bcbca5", + "0x360750eb1dfa331f711ef01576c4e8a2c46040899d922d68c103c5d19a4f4bbd", + "0x38fa5102d2cc2d812826f0a42caaafb573888228f1f60f1f597ec3e52113fad2", + "0xf8ff8a3b3e04647139410aeca9b77669f439c18edf745509652cef5659e8a4ca", + "0x03782bb8396ef1c82a42af52ecb330dacaac6c5b8a76bec55256cd17e508b5b9", + "0x18837f7dc7536e796752f0b0732b2096c9efb50f9c794814d76fef6a0fb0a583", + "0x8c31eaa69f9645100c4fffcdf1411b529ada1f66e0aa9bad2962f642a4a4806e", + "0xe5a02d2bc773ff7eb5771217a12e97b6a08f99060909cc81c759737f760873d3", + "0x82ec577d573cd3d2a564a2045aba8f9a8072982d0e0a9dd1bd83b240bb12bd90", + "0xb4c458d7c9497708706f2e047bb21739a7c861a97c8421d5219d8d59bd5e06aa", + "0xabca162485a4f50ddfefb7ba39157690c09e4accc9e8e8f5604c2f26ef1a889a", + "0x74f53f12f4d9c6287d8ea75a5369f10210a4392ad2e57964f821f57a3f2c9a1b", + "0x8e5f23c8a5f61555c6bd7f271936ab57d8ef0b21090ef3b93eae113ff6969d89", + "0xa0bc5a06c127c0c7d2c9c8c78b9d532de396a08d922463b9b4b6c8e5a48cd325", + "0xee6711394a08d1b89267d2b37821ad6655e2db6cf03b2f16a8f727164e6ba47d", + "0x1c4afc9f6eb738a1baab3167427609895c66218865bf93d81eac87dc22a982d0", + "0x95d4a5dc4605406a5c3c41aa5d205e62fa06798a977171302201409bc5ab4ab5", + "0x5f5b2e48f6882c24aa1261f08581feb10625600af21e20e48ce8ca0e50999b4f", + "0x8f56df9bef0d1e0696c699a7492de11e0a2d4c66345e21e94736e6e574698d41", + "0x92e4aa8b122286dcad8648d0a11ca810e6b2b96b68918b1ff105ae689ce9e2a2", + "0xbb6f240838a5ee8bd4a7adb5c10e87df74cca7355fe2366e4f3ca33e75d6eb31", + "0x6979e13ca107bec9b492c5757d69ab756919e7d01667876f7773e5e06865044e", + "0x89616898c14ad97ed6747f230a73810dea09a3a08a3f8c8564d67a2e2d9cfdb6", + "0x258e8506660aab992d6d864baa97d729a605b58bcf5ab27c8f3eb295200154b0", + "0x1aaa36df6921a486b328227d89f889732bd8aeabc2a4da63000171adc701c2b5", + "0x7ec0f6dae3a0b9988fc578ce4f330de292cc614c8d5882c3a2356bd46519960c", + "0xf5602eb3e9343de7bb23883ac27dd4224cf5a5e9989f694aee3cdc2f94ee911c", + "0x36a49a1e093c881e648906c132c9b0e327bfaa3fe60ae6337767d5f24426e78f", + "0xa1cc52e5b3e8e53a2ad5eea6443e2d17bd35205acaf8cea018e0bced6c5d1947", + "0x9371ad4857ddbe99f45c4f090477ee695006b676dd5f19b68ab46a8e84b333de", + "0x4db9b49c23665d1957ffd81db523e96dddc36576d24adfff6f7657f06b0b682b", + "0xbd5ed544ed549711562177ca35b00b73992bc9d0bc2483c2b21375a848167542", + "0x7d1c43be74f8c44ff674b037720c02882ff2fa44600f88503e4a92c3b737af29", + "0x305a1a0d9615c6759ed225b643a9dea08c80f9d1bece1837797fc9f1e1636e01", + "0xf3e56f13c0b114e229082b1fae2b2657c46a9cb8357f536ed5d3711c60e62888", + "0xe8433579c7562a2133577e065b326900207b4e74aaf966e7e6b6d15b8f43cfda", + "0xce2293a486af341d536cda69cd227301b3a82f4e19af93cfe40f1fedbebee380", + "0x15dbc3f78e2af1e8e487f48f63900af56b4853cffa08834a6dc98afdb7ffcaf2", + "0x62b19c9444c8fcc8175d493631c7b979a44ce311b18d8042327b6939b4915bd2", + "0x0f57894f096531fe91f8b11c885424459819330f47ae0c40737016cd959afb43", + "0x499db597b03d126ecb04384174c1833e0b601e5021272a0d4053972b8dbbb5fc", + "0x9b12c650ccf5cfdfc70340c2e586b124f4d63b9bffdff2a3380f8b4da297605e", + "0xe89aef785ec565b36259df845ca7aca6977acd71c579cc217a9240b9e2475e5e", + "0x8116f42c9122a34a9d9f99f0b1254421a1e855289bb4b0405a76a7577ad1d9bc", + "0x3735e4cf47bc731a050747b92259c7501279e7a6d67424c905b424dd5a565d0b", + "0x2217842981254f9584e6b59b28e3b06e1ef2fd15198727b9d6be853479429915", + "0x6ca14f879a37f2738506257dadd8759602c139e5cf3c810977c2505d83261027", + "0xf1e529a6403b8c214aefa38adb6242cacb9e2befb9c434842e23df5f5b1c9dcd", + "0x8e6dadc03ade539051d866f9b0b847c712e7f7adcbceefa2a036e302983a1481", + "0x461e86f7849bd5bd51628f0a0ab5ae56bb5d73feac4894fa1bf0352259a2e8d4", + "0x4281cea2e1691c92e672af1c473282bb1612a9dd2712ad550c919705d507ab9c", + "0x1c59075484d4ba22f42a0a8a86782fc01e085fd7940555d9b3bb1f1de78ec5cb", + "0xbf0997f7baa4209bf5a9d42b41c43c4404ca43d9a7c6cbbae7c7887f0a5cb05c", + "0x87542c4ec6269fd9cfb6b61060ca24b03e0cb47ab49e7d707daf1bdc7a68e9f8", + "0x98ce7fccd5e5e9b90e15da6e58915a62c18f3fcf4d335635ba35c7b02ebfeae2", + "0x3a81454e703f389cc945510fa9b3de332c306c151bfe00dcd70291c1be13455f", + "0x36714b94f7ce5b9b9fad3c1e5182029318007864bad6bc62fcbd9edcb231f116", + "0xce455e6baf095e95bcc5e9b6f9c89972cbce67d9a4d2a9079c87bafaa2e50ac3", + "0x5630c43899504d07ac01ca1bc26b5e74c62feb306ede1d1503d585bf70e140eb", + "0xbae92ef0d65ed98c970d4cf3ccc23df6537bd3ed81aed8c6db4c8c1c07dd3733", + "0x7ec1d8a20cffa4ec598b2c1c2835546ab3fe6a35be069c05a29730809bdb5f6b", + "0x23ad33ac1bb08757df2c4dd5e2834bcd38d4126e77372e577939b69920a21c1a", + "0x06c55076bbdf31803a7375a9322a91d0ef0daf7c34ac4f99fbf7dbf3ab51c61b", + "0xc1fb13e18110f796abeeccaec50bfaa3d610e2791e2c1cf4c6b26254a7c56137", + "0x6fd4b3f0e8a88c88f9a4c454728acf81ef10a179592b68f80a38898905f484c2", + "0x1c271c7e322a770396571886393dee5559bca9666684ac503db059d8d06e8cf3", + "0x84d12cdff57aa76078ede5c1c5c53b6898826e78b12b740dff31aa0cb76c042f", + "0x0a63d00ae804a96c8c83e558f8c383a397fc37c8f73a767a6a0617d54604ace5", + "0xfb4b75f8b895092f8f55657e7cb333e7fd035e3df97db2ffd1e1e95b11333d2d", + "0x4341101cfc28f05f453ffdc9607960df403d05fe7dbfe22c0d1d32eb8155a61f", + "0xd13f6c7daa7bfd3a4e8af00e87f9abd161270fdf96753355be05e1411ad0d306", + "0x240edd28c0171d0ca152069d738886c526a61664ff9da3aa34fb74d948a45df1", + "0x80347de64ba027d156d94e8b99971e529aa3dcc34334afdfffd1e155d1db76ce", + "0x949e6b636ca5eb729df3e58e2942dc398fc518f62fff22d41dc728cc6437b17a", + "0x3356f4fe53d30bfe63883c2998bec0545944cb65d882fdef11301695a4e34047", + "0xf99c7baf7be1301e847046f69d8a6457e58762f6b73b7ebece1f8f136504801a", + "0x0e2a09f1a4e502e24d7ddc7c437716af2f7595761ea82d2660fad0f5cd072dba", + "0xe320b9c725ef272ced3d03ebb727d611f83a03b9bbdc13adba131ad612ca6ef1", + "0x16dc8cc884306a139537d93525b6d305965d10a511bcefffb87c64dd3ac1d821", + "0xa44db389eeba12623ade2cad845eb1dc7e9a075d18fe61aa48ac3dcba70f8034", + "0x323273ce75851976a50772d9a992fa243d9accf37e5f6018eb572a3c7d6b9699", + "0x1565e59752faef853a5dfd3c56e38691040c49cb43df2c3c5a5edb97347a2ad1", + "0x132c0c07561a48c1e99fa028c3d2a72168cbda701d317ad83085b26ba0af25b6", + "0xeaa5c5dd1d321ba7b804c182f1626499cf0a0f6b60055f2b39b5eda38a171944", + "0x579afd18396c4f1dd4bd48a50679bfdd755f975cdf8a2d1beddf296509838c18", + "0x35cfe35ccfa7987c7a2c896e0e85ac1d93500a0299ac2f2ac884007c6f588f87", + "0x33dc22a90f9eed67dbcac5a2799b246e5527378c4082e7cb003bb411b75eac61", + "0x465b01aa0ccdf53729d96006f47006789c77ccba118ee2ae2c62c36a3b65ca59", + "0xbf64fe76e5aa647aaec2b78e75027df09b55f5d9cd15bdac884c4d0d73387810", + "0x8a4b78cc5eb00beef53b31244abb196f6e30ce97583000b2a4a4f1aa38a9e2b4", + "0x888aaf8bad01a72bd4e25e7fc206aeccc9f57ebd3e47a79d26e27438b0681415", + "0xb1b97f7463fd0f7e6b63be55ca2ca0830fa1e95acdf36cab53f8115d55743475", + "0x316126f7497868d55e0b93e4404b789cb122937352645cb64e97d29f153c71da", + "0xec8b690fdab3a44ff6d02bf152d70a1ee6c0e1b4a4dabc6890ac7eb7c95c28c9", + "0xc17edc7f5f62e2da1ceea7377553b14f8d8907d1e8b5c2e9a49ef76a341f9db0", + "0xf53d2d5dd4373cb6e169264552b0954a3ac01cdfb00bd0446d20260fb7f8f1e2", + "0xf893d84dcaaff4651a96374b5416f059f9ed6d374ea0902d77bd7658c287b701", + "0x07457924d25ef458efb5b5c2cd7123b65f0ea49c0e4baf2a1947eaa714cdd30e", + "0x61fb1198ca05e2fd088a3a9acec6e6e3cc4bc4bd62f2d06795719b61e7c8227e", + "0xa58e0d4f753504cdacd955aa1803a483ba6be3f906045e7e140871bd70f53efa", + "0xf9cd5bb9e247ebab8d0d1bd7a19402addc299abc3462bf3b3df23f7663e21468", + "0xe027d47765caf5979f8768a21b1f80ac44a9cf7bef082efe4047d5dac5fe8832", + "0x3220f525443bd51384d1a1f654e010819b2be81957959f9763d1aa675aa8918d", + "0xbfbf641147b5ada3d5b0926ee0c788f1ad4656d589bafd440a6d769cc5e18866", + "0x4741b6d978e97c813492b1a1082bf6aa9c4fdcab9def5a994d53010db809e5b0", + "0xc893b72d4314373caa04cdce27f12adf7a23b9676e0ad596152d68468b4c6c51", + "0x02f3e20a39ea806122525cb8e1d98ebeee9fbc045225c9d1c16ea6d187974e0e", + "0xe67b7ee0e1790c932da2abfaa58fba7b2d803ca2cf96e2ace93eff15dd0f3fd8", + "0x5a27f2848b907f86f59c2cb810fbbfcf2d67fa4044888f7ae1afe503eb3de568", + "0x046a04c3459107836a36b8355c812888d29e16aae47990d281d0ce6943734f5b", + "0x51865305591ed16e2625016f363e1b3f987ce313d151b92dcf4a6e62566786af", + "0xf8002f2ab6b6966031b32aa3f09d42a662d28496cc8aaffeac7ccb16a46604b8", + "0x10b212a0e25e2132288beaaad657c39510badbbd6b42476731a82d69e963944d", + "0xbb64198f9ca5f93206316c6238b5b4c9ddd5c39fb60b879d25581a7819b97418", + "0xfec7d7c6d3d183b00b887a6745d94e6f4348cba302249a41f801b295604d6bab", + "0x85af901ee4d4825972a182289a5a59b5e275b67ec0074c4809b97f0faf744ebf", + "0x5e98ea1bf738b930b1b30a53bddd6f941e7feab8c8cbefe86551b0b3d03e4459", + "0xf19e35c3469fbc4fda4526ea071871069c24266ab703a29b68529811aecb92e9", + "0xb4465c80759c8bc007dd1912b0724e843d3ed535009da555183e06fcbf12e450", + "0x358af6cb727ada0183e3dd165883614f078ef382f22ede09082fe5b8727480f4", + "0x95a1e256c8a41aaee3cad1f42838b3f4d75f30c549d3ba6d6c65e74c01c48651", + "0x6aa6c4ba91365662a9d6af58133ef744085657c27122bc532502f46f994f1aef", + "0xe705ce1a2b911383c0e7ade7655ca1e1ff00b9f2a0e30ab17f26803e1bbfacaf", + "0xb77bc079d6ee6232d0346040ee1514b7e9a33f7bdfaa95ba5a8b8747b0f5d2aa", + "0xc00956a4bb7fce7a526c05b445e10574f52b69e6a476e872dae4a2e868d794d5", + "0x8b8a294f774737b4911df5be7a8c152efb8079a04623052deecf9d0a922bb078", + "0xab3de0231b1cc14f35410a0995874bd09fae668435f67a58b7fb8c06276aff68", + "0xe0d30b59ac96620e671e5c90a876c4162446149572ffd6ba7f8a905fa09bc41b", + "0x7eb3fe46886eccaac46446f52a64b9847d2ee7128d93eed280cf130837ab5a1f", + "0xddfd506a3f0d3635e88cf494bbb55db132c35bfaa6e6d21334b1c8550389b8dd", + "0xa64cab1054b5cae8c230f9b23bd813103776485b896d93a7ac9aa969d414563c", + "0xddfdff7f6f219523fe68695e1c3d22b0231f89bf989a1fb1768e4127f88f0ec2", + "0x1af970d5b68b4b142279c2faa8de4bbe77c22cd5768f9d671ef0c5f83263209e", + "0x17b06bbcbe81cb3a40af3ac1f18f30dcae79c9b33bc9b5656a72958d01479660", + "0xb58cedb26aa12e79a6824ee025f8d6dd981328f6a623e29de8434ef6b16a297d", + "0x80c02474ebe1c1ec866c2c619ea6d4428d3c6c8ea9064d027910e9941212e6a7", + "0xd69c83666423872ad16531b36429e5d7954da1f9e5e33cf1af986ce4b9ba21d2", + "0xad8beeea5b6213a74c557828ad1f1a0267a798a3244e04d3f8c243dba61a96cd", + "0x169fbd32d9a9dc6b5c0e63840eb8a06bc7dbf196624475c7d2c8ef308d260bae", + "0x2016992342a513b59032f5596866760c35b99a1b3e2cac3ca81f39db9d440161", + "0x4e527ee22ac26102aa974b9e89d452e5c311ebf5c1358047d4e46ab06ec5aef4", + "0xfd5935b6fc65598d67fbcbbf9408a304cbc6df8cc1aa59b21961d9b7d0b63416", + "0x2fd0c7f5f4f783526836820381053496192165714bde3732af332316a3c46ca7", + "0xdba13071453dee07d34ea469cac3799e39cda6982e93966f8d3c128be1ba716e", + "0xf83f28ca0edf7471c86dae30517477d85d21008dafa37f046a8bcbeae671a5d8", + "0xf71fc49584ab8a4fe44dd7bb4db638c3365714afae0ad63c4bbf5e23190e09a3", + "0x31aa78b2885f7b88aa637d9d98b3afae377c8f47b5f95c97ce024cd6532bfa7a", + "0x36eadbbc6c4920f28f5753090b87fd9a54cd87f244fbd30342fa985b1e55ec72", + "0x823aa54657d1e4ce08e268e786965df6b59fa595be557062a23822bfd22688b2", + "0x87b2b2129f0f3cbc74373f9d0032b65b5087bfa503a7c02f86c067f7eaf96449", + "0x52805e4b4a4fe3f69dab3b42fd6fef5e105a7bd23faddcff3c95cb900cc39b44", + "0x9d2fcdcc474000b26695f982ea7207a1d57534244cf615a88b0b7f7577d9004d", + "0x74d79a93125d4dd5c209a572b00a3933599ba53906532577228c7e79c24de60d", + "0x1dead9738faba14ddc4a597edc1229014d1dfc294ea2ee5e92e0c30615b7631c", + "0xda61b86650c9b6e38309c994b84562c4c8da12512c8c70541bdeb2c23d8927bf", + "0xe8570b423f90947723fe864398f888135f28c7a3c85029fb214da980aa2df06b", + "0xe81d44d4e2cff1eff6eef03c1b081a276d17400e123b20ddbda13728da3e34b4", + "0x70d8a4963627b76f1df110d65affb7e99adcd4482a6d8cfcae6f6e1e581f0f18", + "0x01a6d059272a8989293ceeb086030cdaabfae11b8655011a52136f30f5034f7f", + "0xbd07f2567b5900ba0fcd14cacb234f355756983924b5e2052104996368496cd0", + "0x7cb6516ad3a8ce6402297bc252039d6b48224a4f86ef8883c206791e200523e2", + "0x6676a838348a6d6892f913d0a58e5ed8804b48a58f555beb405394d98a03a4f1", + "0xd06e8cae6a6c06bb371fdf1ca56cbfd15e1e8fe4dc7360f45b98d21b91bb7615", + "0x9b1e6c0e05d80f2fa0fbaa1eae981feec3168005441d2bf0a9f43c662d88d7e5", + "0x789bf11dcb6c13a0ff6bf3f8dea9a362609373ca39703a553c841538c58c43c4", + "0x3a6da260b8ba12e4fc0a7b7a0839af6857c13f687db0137b565f0818e87f65de", + "0xd139370355219ebc86fcfa3d74e9fa89ecf66af05b906db73f27ac916608c6d3", + "0x2da17ecfa4b4d23738bd21f1edb82df610eccee19b34b9af4d8fb20b0cc68831", + "0x5862bd168b3942a04feff917c4ffd3fc2a98a85f0c7f15a98c69e95c125d7e60", + "0xa96dfbcfb084ca08ef255277dc7e66bdd9b7a1879b55c14bbdb35b2a57ea6d91", + "0x6efefdfd1ea71c9c1b18659a4bb96d47cf3f1f799e0d7ff1dca78934419db372", + "0x9ce97f34a6c5352fb346b3ea682c06115bd5663ca052b7b1d27c2a35ed56662f", + "0xa0b309e8747893b3c9c34e5b18898844150c17f1f1077b003579dcfc6ead8f7b", + "0xd18cb764a17ab8fa9fa4d4a4b2c5fae3c1ac5134b8203922e14737320865498e", + "0xd7dcd7e5a22d2f4dfe3a20a59a41d357336943f09e811646d23972548cd0ea01", + "0xf1dd3cddcf8bf80a1309b2fa9b3fdc0b81b1411a20e654d9fc3aa6071ce02ad7", + "0x5f06f5b7abce6ce15ed46e1ed1ab77d0b739ddb7f907a813621649e50a4a9f1c", + "0xd16ff33d2b022bc22f3c29766166595790f52c42e9d5a1a8d698cf58c9cf01af", + "0x3938c8cfd59be1a54fe11f22ebca350c89e560e2980b55783a4b9d09fc338e80", + "0xba8ff0392896eff37d29a0c00d13f1bcf0ce8ca00db955704d0144b24ccd0a55", + "0xc8965b6d8f1d43e914d134b62b529533ef35532a256f82ce70fb5064d25f33e0", + "0x501c2e4daf0d10c3060ea93ed328f9e1fc1047533344ff84e726da831ebba08d", + "0xb59a1dafbf87e7aa08496d9df40399ab0949781858bd14f3db3ab03e534fda41", + "0x0ee9ffdb4d418ff80c88d957e35e9a5c492635bbe6cc5306e855240b5d7695b7", + "0x8c154951c6021e94806bd748d5715a5afe4864b790a32b7fa6d70837a2b27371", + "0xf6edc9bb10c4f3b8815d188f6dd7d481edea1400845d68d2058b939e935b4b3e", + "0x591b78dcbdc9deac15069be69e182b20a851d594e238e2a457d562994fef154c", + "0x2322de73d5f50ea3212afd9cb6ca28c15c5d332ae71e9d435d20584be94697d9", + "0x67380cdff191728cc186acdf9f68e7b8ebda8dafb8a8aaeec02fcb3c1b112660", + "0xb5672d7c7dc17468be89c7cb5788e8c23ea8e1d44077b3a6b0031c2c2cdd59df", + "0x14092334abef09a02aa84427662320f59209e65a2044c50d652e94b89b49d5f8", + "0xd41811be0e64ce85bd54595486ed69d1b0c9a9c6599505b13ce697313d4680f8", + "0xee29bf49a846173a55fc307511b0a75848a3cc31b3af5c1a0647a25374ce4c7e", + "0x9061998c2694a208040b13bd5c937efa643679f0cb0404d3a91ad99dc15049a1", + "0x9b40c5dfdbafd5646bbaf6213796f2ad86ce2821bbd17a2a19bfad2168610e70", + "0x5ed2f34533f9a0b8ef5fc8f6aa999e8eda88cae546da49ba7eef28212221ca53", + "0xe431b770e6f9413be54082865242e7da9dc31a50817d60cc45d6a7b40e7131aa", + "0x8b0c3fabe436f4cfc913b3fbc8c35fbdf044cd9b2afd49eb35176fba71285d75", + "0x01571a6d760fcd4078bf1c27d61bdfbf02d95307ab7ef42e7545681fa4f76f12", + "0x8ca62922c5305fbd6177513698d911db8b12677df1b8a716eb25c3bb12cb4d27", + "0x1f4578b2737277d015826c042dac7815904e0068248e20d5c1af2e7128f42e0c", + "0x476d8595b55cedceb4f0de16f8a527e3d8775f1571b1d876dea5a7c39ba31a81", + "0xf8077f005cc3d38168d0a0cb25d1a35da6c58ef1202900575c744bb59ff6a3ca", + "0xc66643e6dd59e39a37e975e83d3e8e042f69ecebe5714182dcd6281fe9d3993a", + "0xf33b29d865bf76dc0ae4d7e9b200cf0df43d7dc6185790bf5abfff813db0c379", + "0xed5185322524c28ede39ae63dd42ec8edaae0f5a48578fc38a672b3f5214e8bf", + "0x9f9281e5ae8bc19ec95c07b86b29e9e6183742041987225f62f64c0d83d46c3f", + "0x8a7f1790cf9813850452bb361635ef3824fd99c21d59907866173e8ccdc241df", + "0x22e5e66ac9fd6c495ebed822cb31a47b1b247108f06089531fc5d12ada2f823e", + "0x08f7163f591b41326008c51d2a7383a1c72947c8e7a3301d5a9efd01030471da", + "0x4690d041f672d5c5c0e310613e433091d8d71413308136cdb1c20bb0f4746428", + "0xa5e8a98eb544e6a25e2eefa3b235dbcabe9247d075b3f9e5aa3de986a3273f7a", + "0x22394af5d9a39c92e4d1ab36afa368edd4ce3e555f8f46e61f1462b9eb764ee2", + "0x7b3fb200880a7f7b2d233b849999e724662d20742d4e30ab835b6d95e17f7458", + "0xfba264acdffc134c882a42a8b5efa56f0ea84ebbc556044b2337c3b23078b776", + "0x633706b3d93adc46ed8f8ce9a330d47c43ad9183859d697247d3ccc1ad9d6e83", + "0x9203c201c935e2336618ac6e14b60c5ccd0acbeff041d3dcc02091939a6d8a18", + "0xbbc899233e7cfd1bdea6f9f4f4b37f58a85a209dfb3187d4813189618af232f0", + "0x0a4d141596823cf53ff638f4199f54b40c766183c8e5188f7c9ec3d94e60570f", + "0xfd52432e3c1f38bb8daf8942d14432dddde5d68e7dfcbb4e24725c4e9c24eb2d", + "0x16b737cb50bd3b398862712cd1f21d758e2086b403cb16ddf675b79f88e3efcf", + "0x4897c43113e751934fb404cc0fb57b37218736f92786a17c09a951c932c208f9", + "0x14561ac3cee4192d724d9a20db1540b79b3aa4e3778a21601e7c4a65ec820170", + "0xe8faaaafe6888a071f120a0d5d5b901b85c507123784368510561d7d5a9c41c0", + "0xa25d7984c20a32ffb15686f8e5428c6126460a68edcbd286e9cfee4ddd72f4d5", + "0xc27d370da525a750d10fc142aed6c9a69f3f2f8e27d8faba3c00ffa223c0635a", + "0x8855dcfa0b096023d159d5d00a43d06e7c366e12bf242ab71147205ff9463ee6", + "0x770c23397ed31d3fe514a8e476bd6043f5fa70c745bf3e3f75c07138119d4397", + "0xcce47fe2cc25a6303024bcd845e92feb1888032630ccae27c712536783e7862e", + "0x9c3c5a1debc8e4f3072b83190b2144d447b0d44b0970e44ba8b2b7be9c9efbb6", + "0xe91434fb4aeb52b4a473be8a1515e303a05fe98f5a8fff617dc423079ca3d47a", + "0x7369c1fc6eed3ba3c8fe19df33ae2104efe19da039dd17569c1760558455a1ed", + "0x9acf67280047bfd4fc325f3b55bd263abc9c3ce536644c2ad99b764846192738", + "0x893df1125725037bfc4bdeb8a788ed3660950d0af793336f441e606b0814a617", + "0x0f2c848e549a4aa9ffbb634ecc886fa32ed9f18647075f1c675ffaa1cb95f9f3", + "0xf397c011061b7ea5cfbd5ee7cba465eb4771531800519ba9f558ede436b33ea7", + "0x32fb5c6b57388e34503c047f390dad0e5c645187c5ef966b196153c431322d82", + "0x870106c3eef0a1eace14f1ac506fdc5932d6c1b0d0f7c92795081959c0017e76", + "0x4c01c88c0e6714b822e68f88c697f8fedc39bdb01a654b813ecddf5d23511493", + "0x8f566567307058d5ea13c99ce648a08db6a684e9107de70d776a2b6fade58268", + "0x0a0e62dbfe787e59ddcafe41c27754b81d59023fc56a599f0fcaf6418be15655", + "0xe06beb06ed313edcaaba6f446d3095517653de3c59ff6db425bb1530d763041e", + "0x95df68ac253d2f0917abbb34824436707310cc06d79556425ab67852e7dc0392", + "0x20e2542ceaa4edf53523024528d9ee1271afb2b38240cff29d4296f49275b14c", + "0x5225e0c1425806f35312f0ade539bae129059135e851eaee913bb6438de973da", + "0x55579ad752fd5e78c0592c11f55d7bef1a75f76b0d1484d08f058b0175e286cc", + "0x60fd9deb1316063d4c0901556a72d9d6fa699ef03d31cc40cd92ea5ba3117116", + "0xb6bc4d1bf9893568592fc6cc4dfc2c8ec44fa0f7b52731812ab9ec79d6b34ec8", + "0xf0529f5f0403668d4aae20cee5253ca2826a8a1449f9c9f73c58d9162b6f897b", + "0xc09752248e4005a8b3c8d7b451aedefe98416ef39af03b7c1b051d1a67f32640", + "0xe9eead19a1dbf8d8113066fb63e10bbdb84fd93dbfcfa4beeb93da2f95f9e1c9", + "0xfaeaa979259dc56e1026470a0aa0f429c783d018bd53c0852ce78848c21efe6a", + "0x87debcc1d0dfde89237518c29fe88bf9727163c51121b3b70f2271836f4cabb6", + "0xcccba0de1149c73ff868bb11b17222e4f77c8072461532af2f6e244a49c3e6b7", + "0x89e5e51fe496c77e32052d2c33a927cdd2e6fb0b6c9ddc2e3c5abb93c75aeaf3", + "0xb17655e4978b65e78ea30f7d899eeb5e25f69275726e31f675651eb5e118f695", + "0x1e52e92c38a1a337f137c55ee0063806155fa37a5eec07274021109f17d595a3", + "0x054d950bc43225ad7252e9e179ad9cb9a2131d6d7989aec756c68a0075d9b453", + "0x180b5855f6caac5cc410dcb6433ad034092edf7cc7b5ee56a7a0327d92a6d761", + "0x0c1aa994e7526043dfad1798da37c4d32496719ce0b74976bc2cc8bb6eb4f7a2", + "0xb5372c426806c40f68141d25a89f44f7f3672bc533ad19484638ce6a3d779263", + "0xee87e4d870985ab9afc70feb4ab4e2d7b240e3d8c502d4c52484f5a07dc4bf96", + "0x627e08943ee92b663661bb399d3b7471d0b26aa78c631b235078764dd63b33f9", + "0x35a828258181742c8057628f2723bd2ca9a3c902e9821eba595e4c87e968b01f", + "0x93b36d60a8cb5ab02c531f8dc0761f9c9d4d93065147699624f29a13c94fde5c", + "0xe4acdfeba2a9d661af5cdc9d71e72bdd554a70ecd253144378c6e14d5b64f609", + "0xcee5cdad4bbf17341656cbe1f52e1792a7c5f75cef11cbd9c99a4e77d9cb087d", + "0xe12da7a0caa7600b443d71b9f9205ed7251ef9809599a95b8079c3c9ce9b61b8", + "0x6fac962651f5103dc8acd40ce5894c2b9248bfd389ae47e3611a43d1d827153f", + "0xd7e5b7db044515097548d115adcae29f3acac47fbcf422cb9bd2c8f49e2f8124", + "0xe1ab818b7963ec0709a932861efa73cdf194016e03b61f08d50f1b1b7cd86bef", + "0x14689e15d52fd8fc756bd4d25e71e3116732ccfa44ee8752958f123f494b1836", + "0x93356b5016c889bb68458c154d545b4bf3403337e94db1bf3adfad0e35f0b39f", + "0x0e6ef99f176b7d1c2cf0f919b7c972f4f80730367d0dca256bcff0d7627f2475", + "0x9f728c1e603ae7b891f8b263b587226c553869bb254fc1a6571e2946329c48e4", + "0x2b81cb81310b0f1fd7574456413c98790b3cb5aefaf750ce4f894dbae54ee4a4", + "0x8bd68a41e9517db3167a5a447c2e63510b62b3ceaec2b4b392e0b06190336743", + "0x002742385e62e0b58f87b29ad6848286a1e35fc6c89da3409da3d71215ee00ad", + "0x993103de46f579b21a504b0d23167068269f8c52fae61b8e2856045f27730fe6", + "0x5095fa0b8f6a699e83db39242dea4fe49c2c2082405ffa2298cf1a4fbde42d23", + "0x5b375e0efd4c133bdd5a09e0aed5ab29585d37d12b1d6ca7bbdc422e130057c3", + "0xeaa8ce7269eb9b2c5cdea598a6e1118627809bfe4c143bbc0cf4524e3987e53a", + "0x2883c3419e037d664f8ed7de6b26bc02bbd6b2ec7ec66f14a87135122856d809", + "0xd83555e26d67351fab5f14c13f6f8e5e26ad42fb53a31fb38b551820518da585", + "0x7ac3f023bfdf02918d02458cbb2d0f10552a08e26089d1f47c27b1856d91a57e", + "0x361fa39fa9d62f3317340ccaff0c10580d9fbbf8e9916b1b54020a95f1dc4326", + "0x201d2e6ec5e4e59be8e5838f0b9bba9c8927f7edb6e951e9e4eb2e6b5a7e9ce3", + "0xd13782f605df118ff3118bd5f863ea441a6442e49a6281afd147398aa95df0a7", + "0x7c15670f2fa390dde7e6f01b4ae851226a4178cf42eadb5a4b72657453da0639", + "0x0e49b51bffa35eb5bc1d7b46effbe80964e707ed432f0e411577fc879b380a89", + "0x8f5b3297d4eb70fcac8b0838ae4e98e8ed269f77e5e3ff89298df0307bb90331", + "0xe0012e26b4cf028de7daf39686deb7bb0dcaa1d3a18c112ae70687e7c47723d1", + "0x0294f67800a32f59f1a2cc1476681bcdb8469c3187ef642b31b33cc3fe36b2b5", + "0x80b30b85dfb07608788ab143f96d659fbf302ccdce58c0047890e753b1cca079", + "0x808d8b8a8f9892b45bac9b65459002a6942d3fff615e6af5f7577738e6a61d32", + "0x5fe1b7c9f2ef75fd8c9c0e0b11c16aeca6546d972566719f4904de923ca498b0", + "0x0a050e0e62559964ccdc367351326e4270d824a7eb85223fd65bc9af1aee03d5", + "0xe92980235c542a9199b39f828bccb70f69dde9e9273c15f8870a50c2a0521f19", + "0x5288ac241dfdf22e720bd255be9219ecc413266c47dfd074fd6710b3d3d8ff94", + "0xd30f80784043f9b007619675c2bb09910a18381b7519e728c8b6bdb72b1e77f8", + "0xc3c2713688a8e02a529e8f69933a696f1de6a455bf984e5e87846097fafd7e6a", + "0xa30e6be31015c120cf6da8256b4914e9d86c8db47259e85db8d9f493a0307dfc", + "0x11f5b43149dec939381b931afd4890651f94589273a3a314be1c3bce4e1ec7fb", + "0xcc6e9700d5e8b96bcd844b24ad81b27f42726af89ddb410d6ed266314582a89c", + "0xe6b93422a3260f6a58bf85a77e06122f36b01af7087e236b302c4e0609491a2a", + "0xb6e3286b9fe1f2cd89a364720a03f25409345a8cebc882d12f301815aa27025d", + "0x6b8671dba5ae9a797864dbd5beb59c4432062bc5032e8f744228dcea6cf9e05f", + "0xabb8e15f8128c7142e51564bd29b047fa71b13b703c15ac96e74083456af313e", + "0x414c7a7916894eb265e241ce9881f624a508d64dc737cc591585ae567b073ca7", + "0x87d669761986b0cfd8a553d08607762fcf824e99c1382a08edfcbcbe4699777c", + "0x7c29df8e52b3a1fe9e46020f1e9393cbe522bb27aa956da5fe8744ad95ea7433", + "0x140a99b1dd11aaea81e182d656a1c0adbaaf92e3e017a48790600a758ec46efd", + "0xdaf48dd5c602e52f942d9013342e3ea799f9c125ef6029057cf3f7b00ebd3191", + "0x35040f8d57e90afef4e9f6c6b6c3eef24a69b8009d804ae8b54925f1d7855649", + "0x5c3a7dd6b699d2bc89d986881f6b723213019c0b90bd69eca1ed7bf671fc8efe", + "0x07cf700b87cb1699a5088bdbdd0804b206a9e2b366740f11d8d9584d42d35507", + "0xb7099c9f3dd3977ae96264cbb363c5d12674c7c61ae6a7ea26a08936ff288aa9", + "0xb7d8bc35f8aa9fe22eec36cbba88ecc765d0fe30d36e13038baf6d0714a98f94", + "0x21f4f79639eb546973375f02c007f2e5d10748910676ae23fbff4787b5aed252", + "0x0ec7a898df6352be4b42d19542955ad86d0a73fe71c338f08341ea33a57e3796", + "0xca45c9e3154a07d071df9f0250c1f538cc0636a80ceb0a8d69d659c3abdff9ec", + "0x1d04b32c952398e96d372b5ffbc7dc777f56c0b16c607e98561f2052df67f217", + "0x52138ef1375528c3f6cb1bde658fd8ee2045deda5e5933f7a2b0da9425364a95", + "0x79c7fefc02be1a263d2faa73efca168c5ef520e2a7d7bcd74b7cc16c5a33328a", + "0xcaa4c9214759be0f8fdb61a1a2f72d44cb2b3ce0a2d97540cc9d962da134fc74", + "0x8e868f0cd31dc6e677abbc49663f5725a0a472210e4175a7f68614d7fe9cc155", + "0xbe782b4dd84359cd3e3a3264be7a444dda34973184ee64f30b07c5f50be8be28", + "0x3595f3710d4ae34103128af0a6dbfd80adc4357cf618cea23c6a8a682a016fb2", + "0x38c222da977a9a3e066d2c64b6f610512176e00e5e4bb74ad27e616cdb108779", + "0xd6b15123c601c30bb68fb23251e3a767c9cde6e213ea7c6e6753af69558d3dd5", + "0x752df079eaa68517a8f8e3f0dd00e9853eaaccb448f21fe584b7da2a411616df", + "0x8371935353745fd3d46fa34c57ef9815651fa0d217f6dcdcf2e098d43b739f34", + "0xe77a32e1db49b657914bb2bb115739239dc26c51c1bba2c6ac6dae1143465592", + "0x04b8277468b2431518b24bfc3399a3485f095c65ea978452b6384e9313247094", + "0x384fc414b5427f0aa605781128ecdb60ff78d84564102c4a0bdb143d0e0aee23", + "0x3b1161dbc1040921ceafb1f811b89e144752380427305c23993ed150165d9104", + "0xc33a2396123c7b9271b52a64f4f2f9cc3eeaeaeb1dd9b280138cda59db879860", + "0x8a6ac5d3c078fb799e942ba6ac5c5ec511bb417a4983f87ed45ad8a13cb4953d", + "0xc742c66388fa24877fd8d8565aedaceab223510b2d2fc1d609f52187c1448812", + "0x3b83fab50f6bb2cadbef2d53502743d5b7541453e16b7a8eb6548ea1d1eb7fa2", + "0x0e2455377d4c1b324c47be4d7441ee9229b9d2af6990cf875e741184d9cfb536", + "0x58ce1b46eccfdd560f641527e9ecdcb5d1c11d5ee16a12bdb3d49946d3cd9d8e", + "0xb657988244f97545d3ab3f8751f256d3ad19894c260572a066d5de13a5640129", + "0xd4d496a7645e88ca93ac5d6c8e22bc0d168488c396960813fa06ac34c1cf02c1", + "0xaa584567d364cb1e0cdbc993dfe4a219b626abf97307488f70b360e69a868d57", + "0x6fd17ccc2b26f090a0ccc16a8d5f97ab6156a24c2afd7f2ee1d878b5387ade80", + "0x67e0e7d695b70c481659cb0ff5b271441b1cf952a2c4a107f2a4a955c14cd59c", + "0x6783847d7c6a924e68c2b96fb8ee5ce319fceec2d6ef6fe797b4e97e0aee41bf", + "0xdd8355ebac9a182e95579be8858d36f438317c1d57809716bbfa6aacda612fac", + "0x5305027485dd89ac425df5039774a0937c4199e3530a25e48ed866f3fff70230", + "0x71448c41a13bac33c974c2b61c4a43a7269d683f0ef61de6a46a48912761e1b4", + "0x32f045790b40e68f9c58e9c57318e5487823a21b354572425ca55552b25ee843", + "0x192728d6a5f3985ce8d58bce28d3aef9e288c43e46e8f23b2ffb2e4f41210439", + "0x33ace43521d4d20c8119ec700a817bfd904e448b0fc5fe484c7ee33f87065f52", + "0xfbd07a221b1ee24ff23a160cafdd13302b3705c20ceadd1632c4ff5969437a17", + "0xfbb6ad9f84e3b0c34554a72228c4de77780596bd786bc91b8c8df5ef2a978415", + "0xfd29daeda041d5ee72de273bdc7334b1dd26cde97175f7af439b63dc900f6a49", + "0xcfa43f7b2cf6ca0b07f733770d8b48bb64f82e74ac2122d3767b9a90d429d9d8", + "0x3af554a6dde16dd2742581a29d2bd7b6167786fc22b27e0e3c3370dbf797acfb", + "0x10bb7efd672e16ec65e7e3893eaeb54e194d400da563ed1f64840d9af4ef4efd", + "0x5cf70e8c419baf5417816bc1f48ea4b27b272b51c62f214f472f027df8606bfa", + "0xccdcbb8cdc3587ad8f7f4dfd7e3b2e04a08add8d35fdc68c20e8181c491cb93c", + "0x881f3cace7a89a320a0fa020a31acf0e3aab32d25d12e57e66a5d1fa1d788c38", + "0x00b53ca9e83b0cb569a2cba74dfa83df8962f94eabceecd2e2ba16baa001653e", + "0x22c174270dfa68e704958ea19eb6b7c8fb206d66764c6c2325bd4d5977ecff25", + "0x51389354db8c9c3225a34fbde74f4fce7f438f0f0cf374383aafa375043f963e", + "0xeeffb447506ff7ff81c819695ceb3f280ac65c85511915720add5e15fa905e5d", + "0x3ea2626b28d24e1b38b23fc5196bb2d46a25236e6c52d7d757814246eb88cfa2", + "0x2c677cb9aca52b77cd5e7e08450d2db1d7a4158663c0b14a725146d2801e2deb", + "0x2b9274f483da9099589dd74421ab7868e34de0525b33e50211ddc0a298bdc7fb", + "0x84499ca7ef1844988151f8a60edb394802ad348f26f632309a32a24a8231b798", + "0x93b6dc453f83f9418bc5098f11fa8ccf2b05dbaf993a053117cf9d9f657a499d", + "0x118d9cbae9f76261fadc00a9c4845581ae9383bafe2ceaabdd4219cf2bf30f56", + "0xbe59cd216791848d0bb606c6a0c87f00b030a45494fb2dbe11027f51b7763f89", + "0x88c81f767a024b7dc2ade407a9eb495ff6d5955f412924fdd6b7d85d26319767", + "0xaeda3eabdf9524c5e7ad0f5fab76a1cf97be7cbfb701605a22891df2123f1c4b", + "0x6a3814ffa6acb81156e5eae1c2c69f70ebc9123a5de9d9f5839e7616badfd3aa", + "0x5dd082670b71befcc75c6d2b73f8048d45b803e7b2322287ec80f128949c578d", + "0xa2071896d9c5e7d1e93aeec39180c537fc0310d5b8a1a269b391287dec3f2fac", + "0x02bdaf2273b32900f3e4e2c5db604ac3f086c9453378f2a50ada31bdf834cf61", + "0x9480f50924c09f0249b90a490dfe43a08489011321408b9205fabf1a0913969e", + "0xf148ba1cf29df85401df62d9ace7fb155d56ac5fee49d0800041f067aecc70cf", + "0x980c91fe853ed5c7de2b96c8824bf9870de4610e5c6f9fb5e1dfbabea4b70703", + "0x030ca08e96e4f730f34abf019c53e7fc75868098834c901e6e3ec220a09b6194", + "0x770b34bfc995acf4f0c077ac983562435231bf23a41f53a466499136be1427c7", + "0x264d598c12f121efe88c7a47dd3832eeac779b365c3526dbe3df4c7e5a2a5712", + "0x1dbf9800f1af78236d96245f6d5fd302b8625e96733b2d25d194f3c2775b60fb", + "0xd33a4c5b196097e0623befdd7ec12c1126cffaf5660fcbb0c24bc3598a4d8add", + "0xb4841aebc502a303c7dd6c82d764badfdf2afb33ee50a96629de17ee26043526", + "0x029040a2c81c688131a65991685c8288d523a1d49e87d36353635c6982ce4383", + "0x400734f1cf59cee22ec6f7143946cfea000a3cb98e1776ddffc0c123c756e6ff", + "0xf961d0e57c13ef554c17cb4b922330184aae11c2caa87d87e2968003d1113c24", + "0xc71583c3069c4a3725c4fc76b32677a9e4f9edd4671897fd4c16aeaf4032c23d", + "0x56914f7da55fb067109a61b7a74a2b8f08c928a4bf7b8906de061256b5622f4b", + "0x04848926c0eaf1087480fd2f11a4352c26c05dc73dc07042c504c0552c2de12b", + "0x670a92bb5db39f04278280ec649c4bb9338ad0204dc6d81672fc68b5013d796b", + "0xc184626917763f9876aefe94a7c76d0e46346b5f69f4bcf04b77fd02a608bb76", + "0x8ab4ec2cf16a1f5b8504432db9a20da49bbb0fbdce91fd6d3538247c6f298892", + "0x492dd767890a233d139289405a353d2a8a741e60fbb70d6ae100597dcd1a74d5", + "0x57a149a93cb01a93c47d44b3f45b664997c24760e5690944a7bf6ac551c5315b", + "0xe72036f67299688e0fb9a0d4e1b66bc1ddb2c30bf3fa47d03dbd864a00f4cb53", + "0xdb2d433e9a1d18243d978ce9db0f36d6dd4336700ab7815b8938101f5ae64c08", + "0x6e7a1cd78f9b2c5b226f40859e2d78b68d07e87a8cea49fb06e5c0327444863b", + "0x2bd5083f9ee38f37e73d4edf9eec455a5de14667b29ab95f63e9dc8aa47e05b4", + "0xc0fc271d6320d665a40e590d3402ab8ec5dc939c3de5e2b112fe7017a1771b2c", + "0x2386e276400068fff9333eaf1d3881ecf7eea8d8d41bbdcdbec27ec752295d50", + "0xf14a49384ec53383703a9ea5cbfb24b8228cfd23b67d913455cf1f26d813d887", + "0x9bab33ff006aa4ac0da26d65d6c1a38bac71d82839db595f8f336e2df1701b55", + "0x52ad9cbf41b26f5e055797249e0dd3f291bb46c1d429b765c4f4ad87c2d82efc", + "0xc1b74b598dc7a344b8527ff13de0ddd78e98d9a87f062c391a6e0cd67c4f8b2b", + "0x1aa26c9ff4f9ac672b3408eb1257151ff73b50e347d5276c7d33a0103b2ddb5b", + "0xac44a560d914708b71834db193340ae08b8d80f9c376a8082bcb2e9d25d01845", + "0x8642b3b68532ac09b4c486486b374e509d1c2354a19e080a9797ec1a462d30f5", + "0xfa571f7ecc17043abcf28473da95052de87755d15932f2e6cd8cf55b48ecca00", + "0x162c735e838e2ed48d928364e48fe7ae6d6f0e2d7e7abe871002e25b0d4b905e", + "0xce23e08aac7ffdb4c73797226521c0529672ec223353af51d14f0e148cf85918", + "0x26f442fde86cd3b1eeda924516d18e9460b1c4dd039746a647d8a33e57180af5", + "0x9db0e8a1f3d9367dbecac6226b8362c125600542def762e472b6c20ab56132f8", + "0xce87eb064b08083f99bdade4e50a028ac6922ffac70e7b1899db99530dad4171", + "0x6a02fa562ee0e4f370561cb4ece2ad157e9f6848ce13ec7d7e16d0b536d1f628", + "0x3691233b0c51b058b89aaa2b7ee364be3169772c0dfd6183e108c053822be737", + "0xc92106c8165c402260102966da150ab3573c0facdd7f2cd28b4542b3b31cf1cf", + "0x5d7a8f0995edf9e0eecee48643cff9a17555d41b4f59dbf5a05174198db6e474", + "0xd2f8d82a150e1ba155c92ea721a2a144a9d130cba824f892d084906cf44e3aca", + "0x8ca1f229b8564a3fad786168aea7316917204acd0107da1f654e544114c7b0ce", + "0x93468e9d1632de8a66bec7c6afb9bd40e825bfce37dff81a5a52f109eb42a33c", + "0xff4914ec7c996af945f0c86f57550df39c9307233ad381d19d08cff9dbac6f03", + "0xc2b6733e261e5266689a546a5e272b86d84f8aa28f04ca62b8eb4c714341376e", + "0xfb1d2d2fd5cecd9563275df31ab48955e4faaba6f6eb30c6e472afc74909d864", + "0x3784005abf61516c3339c3b438e99b590eff338d172e11d555b39750455344ba", + "0x5e2e7109c959d3d0260244646ada5eb162629e243a671e50e24d0330dfc73dda", + "0xbb3f06fba10f544772346c975e08d2e06dff538e8e070c3b331a0e589558e1d6", + "0x4bd7761437a430ea1ccd19bcb7ecbbf3c916c07d15bab5a7f7d6b702ebbc6a7f", + "0x6ceefbce8b6d729756cdc1f495e8b05fae9fca0111894d4511abf959dfbab06d", + "0x8b660eedc175f58395697e36d4822ca1246bae0cd695616c29ca8fd9a128139b", + "0x45851932c0c56baeb99eabdd9d564836de264a7666872012884116148b897bd8", + "0xeb21fc8d0452542fc0c3ff9586f3090cbe378a89995d260f80924718279326d1", + "0x91ef14a76453906a5ecd71018e05db415f9e0256873ee84e24f896d4305b85c3", + "0x359ccf0bd0c44c1a96916d69037831fb2d5d18ab43ae2a04a8ee48b29eca3700", + "0xc4fcbba454faac503e303ce30bb6d3e5481d96d5d1c78d14892e44101f5253c5", + "0x8b958952bbbe3f8a1b97571515aaacdd30c71c2a78cd92808e2b6ac0e5581990", + "0xa55fc11d7b19ec6fbef7c0cae1f4c2ae51e2c243a10aba186229915c20feb2f6", + "0x381aa0f86c073d57cc8fe0ed3f68f0ae34e63094316a615a0a932e4026c36030", + "0x944e3523e63f32780b265c650a14adc4200ca7c07e18dd012e6b9c5a48a08cda", + "0x5313f918cf5e7069b0a46f942fe806d9309900553d2a1d69f0cbb24fd8aa717c", + "0x1311bc5cbf903b968b67f5c3569c1cfede20ecea05420763f99d7d4ff9d53235", + "0xfcb29ab03f9c5484a95d2ba3d9bc91c5a1bc25ba3d15d27716f027d47da43532", + "0x3227e5019a13128e2ce1be6e29892faff5717345cf162a31d4c8c4ca5c9a4e39", + "0x260417374d206f1143af35a8799dd1199f00f616e29a1bcd1189426e9ec27b70", + "0x02b7d1c385d32466b0a314ad8e59ae5539d503ef86db923954f234443cc0614a", + "0x8b9cb17b6da4bd1c5e8ede7f76f8ff6ba51213e677029ad7a4524f31ed2a0b22", + "0xa18a4d5008de2cee3d7004960afe300dcef777471177325b2c1222c7c9cb8fa2", + "0xfb4d50d21f452fbcffe2a7c5687c6eb9bd27150d7acc9dd5ce87d75ec675213a", + "0x200f8ecc0faebdb0f2a119416d6663681635f3aec47fbb3fd6ee145d0bbee977", + "0xf8c549e9c4a96621b03b8dfe42d9ec6568cdd089d0ccdd922d4c04cb8acc8a04", + "0xcd50646af5e959b1826b31eaf2f231ad318d491f5e6c0edf9848b3ce4dea0a11", + "0xe601576142479668c1290ba7a1c3137604d14fbb02d4d4838831d791eddbfb69", + "0x6e113cd2a740f72326a5d7e7c914a24e16c92aa0c592f1c68d8b95ee54833fe0", + "0x5f189d5630eb53d2942b6cff97fc1e32bd29b1b57188f11f077f06f8f2a404e3", + "0x4c61bff3647382ae3e2f5db3cdc594b78d9669136a0db77ad903cd6a1ae682d2", + "0xc1965ed68004cd809bec8ce6183cc5ac361c2c90130dc3508efc1a08a4cf2000", + "0xee9429a664265af2c364374bfeb1b43603ddafb994e05476e925a9ab0e8c6ce7", + "0xa89ca4641a2f1a375242f105cf7a9e106f7016ac3ad8f06840212018e4a30b4e", + "0xa1053888a7f7ae1ee63463b7c876965a3506215f784dfdf097c768ee3627c87d", + "0xd90881a232860caac7e85edaa3c7c5d31f454481d750b584715fb026644371ee", + "0xf344192331fc810490a21f751f88902af69f0ed6bed316590b9dc237ed475f3a", + "0x5d7475d8f2027187c1ed0bc81cfc87b6d7f50291c40825c301073b663781cef5", + "0x060eee4e1d083ff525d26c13b03cc834e2591178b29b4f59ec639d4da6d64ab2", + "0x50fadf1a3cbc481ad4bb0c109eb3f8c0287bc1ee133de21dbcc6df15b521bd93", + "0x9816639300bb3dfbbe9aec119e3579e42935a864b2f0bfc55b39498d7aa62cd1", + "0xf345128f93180c9f40126c4e906b3e633a039f453f289622c01484d810a225db", + "0x2423732b5ca1a119117d22d4e5fa1f956f4785b9926a3a75eb222ca308815419", + "0x55d92c88c7d43027ae4b10a394da4f6b783a3975d023995b7c259378efaf1557", + "0x4ec7d7ca95aa1014b02d0ce008fa7d9cff024d243229aa5731394337eb6ff319", + "0xe5edd72b8a946c6787a324a612c81b823d9f3bade49aef48f661eec83ad34283", + "0xae301e3bd957e82fd340adb95504b9893d61ff4ba42af02c75b8deeb8b0062b3", + "0xfd3d2afb12d583ec858f73cfeec1f4d5122f1ae8e190786bc7ee3bd56ac789b5", + "0xefa84a2d47e0a7630342e38152b63c325e627e1ad50046d4ebdda955d4cce4d1", + "0x613dca9af6f2ce7d200607692e0ad14f71cf34474f9d7cd067ad08bd3c35dca3", + "0x32d7288f26fcb35116e9917f1d77adca997b1b40ab2c022d58596a7d5556e43f", + "0xf2b1586d86944c4416d5e3b6c618077d8e30b7aca44639f3ae276fe374384f3f", + "0x0ec030e0574539e84c030f800fb97046ed107387f9179f3ef38aa72c6cc9514a", + "0xfc01cbde1040e34a1bd15597217b33ed7cc01f05d40d9e393210df3634fd3dc6", + "0x79791b933d8e37cb85363e505b377d5673ca6992fb0d507f1d82917697e4bcf6", + "0xd916ea2b1e65ccbed93cf16bf798a5950947bcf6d629f7bff201091b08914a7f", + "0x236e2611fbac73cc2bfa91c8da71de673b5fd596fa13a9268c27f02f1fe27242", + "0x352e3a3f161d9064ed259d90c02fd9ebaed912dc8379c25ce9ae7ae4259b4d31", + "0x6f849dcf835c48ba98c9792dc9ef31d42078f46609ee9dc810a47434fbf21e0a", + "0xad4e41d26bda4b01c6eebb813f54f797193b218bda394cad69c587fcb4aae8ee", + "0x01d23d56b15fd5713e382b5ebbd4ae5bb9f7019dacb4ccf90a13dd2adb831cc7", + "0xf6e45a6472b8862a28fd146fa069468567afad92f7b465395b85c5907b0ac712", + "0x9641ea59b539fcd3be5b70fd63ba96972ffd3d03db6911287993bd63f1084620", + "0x5cff48ea376099b86e0a6bca6e8d2b3adee92dfa5b009f834a1f2af9f1290182", + "0x6c712516078f030bc81a4b1289d1eeda1a71e4fb87ed5c52c2fb719991ae979d", + "0xde274bb7017c07ee9fd510214c939a63db8b03a51e7d496fc4052b0a7aebd65b", + "0x2a93055a5494e2a74dcff6a5c04059a9349fff2af1eb476b20c0563550ee2d81", + "0x81655249f8dd7b737d09ad6b4e182bd860a810745ae443e7a9e8e68751a66db7", + "0xf273336c0dcb6db5eb6a2a770d3082beee8c8e6654b3cb11b2a2a4db28cc6076", + "0x3c47bce9c2ff679d8431f41b2df267d08a53411f48b20ec107d9f5d2645cd3ba", + "0x4ee0e8a619f568bca8f947fac93651c3b75104b32a280e6cd96635db2b465f10", + "0xc4c16c4466a42865a5b4eec789876d2175f4e1aaed1b20382534aa549e910e14", + "0x0fbd5bc305caad98d07e7b0bff98c005e48aa3660eced94c535e0865c784ce32", + "0x4fef28a4ad56ef91e6f8ebc36184086149dc044e847d246d3575040f98b8e892", + "0x7a8ff1127fdfb5bb9d787f890908ad3a0597966d0e223d663eafb69758693a41", + "0x4ef4b6376f8b3612ef1a476974e89eb3c488177312e5ae6bdd4443419e1a9c3c", + "0xf8c89c56bc9133aaba352a1ccabc10158897b24e25e60c1a651aef37acbc3fcb", + "0x11e010c64e076309e6b203cf8b89be8c5f9fbe8f37a3777722a29c0fccf7d817", + "0xdbc73688a2f7c4cc5c966007c4564d58a4ff2402ba7a98199dd9452ff9c5b0a9", + "0xf43e7ae4aae84df19deb4324f8965c96413dbc2863bc34879c2b6195f1bed46d", + "0x12cec502a4c3c3ae367bca1c382e4370e9db35d912676d5df6267a3cc3fec111", + "0x577dad33d0b0c1c8b82b2cded76745b2eed255be93da57901781e9ccafe35309", + "0xd515af584e5766c92941e65d1dda5b0c749c5f2779e4a645a958993c2e10d3f0", + "0x2721b1f1e4448dac09d0f411db2db31d860d829b12dac76cfcca90e531d99bcf", + "0x84d10b8a970365a67a24d9a78658623aad8c3108dcdbd8e2034cb5dbea56b969", + "0xe92e957faba22eb8c635cbba86cbd3ba4923fe9ecc23a8f1ace0224f9a32892e", + "0xf97741857da6a5ecf06bfc0d15524341c98ed713369eb1f34fe29fce560f7384", + "0xe115f3b05e94225d3a445f2dae5626158a2b959086025112651423658c1565fe", + "0x2939bc88e716a92f5bf4df9efbbcedb7ab7b0390b22b3248d6256cc2e9ce5a20", + "0x20c84f96c60b1291d0983f15ce36d5a2882b1fc9234c9b3751b2644a4ae4915b", + "0x2b87e57129e8d03dcb632a7621a5069608b449244cf5bc592a875a46006c15fd", + "0xa5f16b8e797860e1ee2efec0c9fb9e1bf5e7c809ed65b6c65beefc390ffab0ec", + "0xc013f5b73dba1ce92f4518fbe6d519c0e566c231818def89f1fee4baa483ff8e", + "0xa38d7c2aa21e5aeee425e18ed754c04d25d536bdd9fba6236699c73ec0243d88", + "0x2981d4a8194c30f3bcb2554127fd2a9cf436360a6749ab5845f4121753b8702f", + "0x8b42c8004fd7661eac046eb17efb99b264772e36b23fe0cd84931f513beed7dd", + "0x98db4074377529c58c097cc4a2f48b172144ac1a4bf8a6a8b9ebe1473218ac91", + "0xe61e22217fbef48d6c9c59d0e0e99dc978c8aa51142d8635c70a175aa61f1c40", + "0x7a91b167f6c90ee984403236be8ab72416f2c26b8fc64f97874ff6da43843991", + "0xdf6c6609c885af97081f3cbbf928857ba118fedc88cb89a2fa045c8f90a1b89f", + "0x0fb8313d4734267834e1c0bd4af5c393637913876f8a47a1ca4c43626d35154d", + "0xea11d81dd349981fd3dd658efaca3ec83182db16457a67173f97df977cbdee51", + "0xd794453f0f37ff6770167666eed3044fa55d387c0afa0448fafb6636f6052132", + "0x0148561754ab954a4214fe426b44fd666392798b8112d4eadb0e8de3e2c77117", + "0x0e5008dfd52d0957b78c5f979c430efd5e06c4b488b9ef06aad127a7ed459ce3", + "0xcbd06ceb03edf99c34d5e8cf28699275d62fed9a57a8eaec58423cb9addb2100", + "0x3ab3a851db4f5868560c6da0317283142be609c3e3c5aa7f8ccaf69d7b6e0200", + "0x6a2ddb2a123ce352f97ae922d6a27a38f12ef2385207198ba249e267c63f3158", + "0x1812c474a351ca9d2a90a99738cd9c92737e297da6127ab0444844103022a492", + "0x01fd960a57d00da007603187e46afffc711e46df834bb39f7f97719873f592cd", + "0x5f26ca223f7ebe602901178aa0322933c93f03c930c5b00fffddfcfb5df60bfc", + "0x513af7b3ca0ea9545501614571840f9f077387db6bf73027adab5fbe856db910", + "0x365a568108240587bc82932bbf8687fad6d7dca09f49eb1e1b04da1713723627", + "0x26fe595ae65b2e70c074cdaa5176fec2460fd5032bb28a8d1c5276154a070043", + "0x02426cbf8ea860db8dce5014eafcaf90edc8879e97b73bfa7bd8b6b847c68102", + "0xd2fe0482ca8ed358026ab765273d7406637fbe6c8f549350165817198397d0e4", + "0x5c681fdae3df6e4009bd9572ad69fcfd5977558ee922e6e8cde1f16a382157da", + "0x00a1c1e7318c555e7750aea0d994daf7e711259a925e3b804af2340f9f4f5adb", + "0xd9c8af3bd0a1dcf4a18c4629bf252ef139b091e4412a35d26f4748cb5fe19b24", + "0xfb84cacf79d93396ec86ece873166ac7c1ce124ee629e7e504d700f1172ac168", + "0x6c5e8b364b2b997a6d5a7550480ae1271da8a4e5dd99f948b3ce1f14a0fdc696", + "0x22dd6ec665595c3a9cb4733b17295acfbaa03d200a9a5686f53fbd987de3c7ce", + "0x61e10b668f3d2fd395f9605836490234992c7bf7ab1af263394cd8766ecc8bfc", + "0x1326ee91d128461709feaeb0c5456f98ab2a431f392e367632b6dead2bb8ff09", + "0xd5f34de924a8daa965d50440b79d9911902ee97f10996c8a1a8026b8b07b2561", + "0x3877faddc384e6b43f4cfd3a90bbcb463d6a84acb24579d4a9327454dcec4431", + "0xd174e8129331c7452a6cc759eb12db2fa9ae162405bd56ed2c68467e31f2a9ba", + "0xfd345cc25089ec4e1a10287437d5203fd757f9e56f28082e31260e3009adac31", + "0x1bc81355761687e63909ac7b55a0253ae6579f30eda498fde64a035f945057e7", + "0x92a376c3796551af52553e4bee62ea9d1512ab55155dd89d08fd664eaf232b80", + "0x75b25deb3f8653956a5206ed1775bfcf2df68fa190324be1af24abb2efee2a71", + "0xc30bb3e552644a69d81c1195863de99b5fc17bea875ba79f208068de79c4c31c", + "0x7e08ce932a3898104cb6cfc7d669b436d86d714ffcb5c8283648927e7e9b9789", + "0x78f87f416b155b56926e42aac5e9c7f111ece976a36d532e75db12bcbd716ec5", + "0x28b158863f5757a96727ba0ab46072a041df5fd443d1e7b7be02168b257a6995", + "0x090efdc265cdd80f980fc3e272d3e5c2bfb4e9d080b775ab8c0bc5bb34571730", + "0x087e2bd9546d1bf351a143b7710e7ef9008fd7d511111b7d91012be5ce9dc1e4", + "0x0fb7e53814a7f37ac961831cee5c8fbb60437e53d69b7967efef75a846fde4a2", + "0xdbd5ff65421bcd6a6edebaf73ef19932ce0daecec5a46e1c1dfb63a637baee3f", + "0xdab21ddd33942b2316b062e796aba2b7be1d756371c1e9fb98abac11aa7cde4b", + "0xaa093823c298a1005da480d4e938d393459b41dee5bd9706e3a870974c4af3e5", + "0x612f7b2eb653f78b06e9a5a7b44047eb46a6dcff7253030be161de1ae5570021", + "0x894fdf7197d74fa8a2abd17aad14924327d919835ec23d951c070485163573d3", + "0x5bb2da4bb6470a671e5b4c0a1ee6378a6247d54cdb98711338e80880f3903448", + "0xadd42e751544cfda95a1897f42aa3910c9e1925998f8012091b92ffd1f0bcd3b", + "0xe1a057ba001ce9218624b7debb68d1fff5038eae1eae7953af05c7fadd2f36d3", + "0x0fa657a5eec4e58c39e6e424c942ed66731464212ecfa1a130f90f34963f12bd", + "0xf58179bc9f1399cbb4a8f80728ee3371e3c8107d92ae2c0de4630cd915a92b84", + "0xba6c36ff5317fd6a247b99d141a5f7aa19bd9cafbb5d90ad3fde52fe74780666", + "0xe67330fdd9046722e4251e85933355404e14d0f74d9b83088b84715df7187585", + "0xe38982a101c44c10aea4df68d20fdb8e9ec0a8d8a4afae279054ecc0610c50cb", + "0x2b937f6207f3e90287531c6a16ac579231f4a843b4f26c2bd7d30c2fee80fed4", + "0xa6a9c711f191671ef9ad36d562d4ada0922b55080ef4e421abfbd4fab28bc23b", + "0x56577db67a581bb8aabd3c0a32aa5667bcdf2a71b58458f77dc0dd4ec574e506", + "0xc7ed8b099b2ee3fed5361717c7414f8f45f8f7325d453fdd976bc2b32738eb31", + "0x5e06e39cb513d3b875751fddd522c532d6ce23eec32610fd82a9282748941546", + "0x5481b83bc8a07b55d2e4ab6bf83c4ca7cfae5bff0dcaa928c5f0415e2ffdb1a8", + "0x36e457af05b49eec22d09100a1e332afde795ef13d26d9e1069a24f3becfc8d5", + "0x5336e1fe0675cc5cbd3872d5f5424c85339267bb8e687272257f5409357ca9b0", + "0x99aba18bd538011cc4ac9eb8ac2915816e5d6b058778ccc2a0ae82d9db14b338", + "0x90b2457413ba2b88c0caafcf174439df5c4101442badcfb7cd065152cec4fe28", + "0xe4f9ea649f811ec0a473fe6d9a79bf25a1c7db9ecf6b78ea72bbd8b4dc1a992a", + "0x8a7a0b4aecebd9ade5d258a0b31aeaad5904ba46fca1cb8dfe51422736075b55", + "0x25b1d6af13da28d92123c3ac0609cb1040fc8fb0c16ad771880b85a6aea7638d", + "0x2e2c332de7b2f10c19c12b2a838661d69af07432891dc5a713a293de439531f0", + "0xa3d0e729079f6cacceac27cdf0965c5fb75aeadaca2d9c496a9a6ec49003720e", + "0x5a504d2b2d76ee5bb9678aaf185ed620726643426d0a7d58be2d1738285d98aa", + "0xe8a682e1ce01002b2198165a08d726fae4346f7345cf8f54e13f53d889d83748", + "0xfdac9eff9a25210b310cf10a01054a3fcf0722d807c5f73b4cc30d6dddd7d0cf", + "0x07bb4786d5dfc4ac0d6787b740b4a3ac268b9b384da29724c92448ef1ded0c41", + "0x74ba7986799962101a235a420be0ed1b3f7f5f723cdee8608d3a25953c3daaf4", + "0xd5cc18bd5ac1c5b3e7cd3dfb6b1de9a2a6346d761f94d84c5a2932d410c1f6cd", + "0x2af20130d88417ee3de99d10c65f4ee3e0573b2bc40ea616f6d02b19f3d17982", + "0x775be1a1fde26f2fb6e98055c9d78368d92e389f88ff059e51fdee64959944b5", + "0x6951f1397564b2499f8927dae7a8754287a6acac347f5d1ace8dd75909557029", + "0x3f76aa4b1fa8be817ff30142dcbea06cd0eb07071e74564d14f1ccfb0dc10785", + "0x306362a0a99b97286a8b286d37371be8d0b7243e698c672a5bcf407c453f254a", + "0x8d7e1501723490cbd383c2d200a7c0956a69c0ebc4411f3f31f73331973e366f", + "0xb25301076684723c33c7cfded7cecd4e47610aa0b9491bf44e02cbab57cf5263", + "0x400c0282269fd9adeebffd4a1277e12534c896c5a65b4fddd4333c82ebbddf2f", + "0xe63a9b51a1198a2ee14a9561e9ac247ea2f27a68595536c8d52a14e6491a0fce", + "0xef664fd3e235c4bd134ebb42ccb1197bbdd33d1d9fd429903e207b8527207802", + "0xa66bcc8b76f3d850afb1bdad0053eacf6f086693c5cb98f8ccc581e7ab42f7f2", + "0x8757bcab0e5e056134d6de28183f1c8ebb09f149152354d979cb19e4b9837933", + "0x331d38e912dd752b04d12b3e02f090ca2dd0c2d944a8395d4be60744e06996c6", + "0x1467b6fba95fafb76e8230d2ba29ce237d0433c400d395495d80de1ab811b32b", + "0xc4df889aab41ba228e099d1f94ca246b5eaa8a428ceafb9d86f8ad5900e3e19b", + "0xf139c9e67c3ecf6510de2104515b8e0519c25e6f47330ff56df68f8eed4ee245", + "0xb7e2c25a68f0d98ba033ced03a465223265409a8afc4cf9a5df65b4ecaa33fba", + "0x1cc0b14a0a4c3efbd07a44a74cc751b91759a5d991d41be21057ab721643e294", + "0x3209d1ada7f24e49cee76d4391122255a308a98f02dcfe4682f890c7d75f5050", + "0xcf325ca04cbcb78545aa2adf3ff6a214b2e7fbd8adf1e36802b56cc75441e7ed", + "0x0763af6d127c792f41c33ba2adbcac76229c13518b67faaf778f56a7006bc129", + "0x3727e81d1a2a16d25b58babb02bc0595f60584f692ae1e898d732e9bf773c9c2", + "0x0046c86ea5174e15da2aa8124bd319a316bc306ef4ca1c773d75fee0b61bb8e2", + "0x354055b8e97323a1e7bb7cf8edb12a2d86424283e3bc79baff6b4f2ee185c57c", + "0x6a9f13d4206fe1d396b93e419452ed5d317cee5272fcd0599d481ad7c9721bc2", + "0xaa6df6c7a77e1c1898d41c428b7d811295e67c6d171a4979f3ce176aa2adae5d", + "0xd4178dd26a71d47f3f23ef249459c79fe95bfe09f8e478b387c1ba86682995fc", + "0x549ea5733c95f3bacc361f3257764837e4ecbf6f23003a719a9bde3df73ef10e", + "0xad14db3a758e95497e2a2597d808cb84a5032f7a254ed68122cc699b9897eace", + "0xceb8aedf4ae93eb2f05ae25ee50f63f555c63f9e43980aedc6e0461f7aae6dfb", + "0xdc2db999c205cc609bc1e4f0b293e70a013cf8d058650740a85b530e844f7c78", + "0x714d6e6ecf3ab0856776033171105bc0590bd3aa5192b90be2b3f4f9bcdb8b72", + "0x88bd87bbee0a182b5487de49418357dcd182aeb67c6d4e6d08ff91f7b64ee1d9", + "0xee8580827ee7d97d488e3ffde3cf53c24bd899181395bcb929a426a6a06a4c8f", + "0x2e6a7289b3f79a697a867e471bf6f3b6edb24432ccadeb13b71fe3517d3e1c0e", + "0xd809c2dc3cc68be936ab1529e5bfd5f27a30d9cc7abee528b2cc7299bdfd3959", + "0x8385006613593a63650ad1a5d0f34fcf2a6fa748566e16d62e588824acab19ac", + "0xce47f9d4706c699956b4ba0e8a724d373280b6e70a9f8200568eae801d0507d7", + "0xf493b68b479b880446279ab61526882172730946067c7ad769c621b3ef6487a7", + "0x8ce7bc26207724f56d3156dec22fa17f86215f2083dd2daf8518f8b986be71f9", + "0x2dd283dabd452905c96e7758cdc51012ff12d7593cd89a4c14103504679963fb", + "0x58e77109bdc0f6b1b0116a434fd370941e739c68aa0bcdf4c860d27a1db007f9", + "0xbaf7feb56fbeb1f2dac13ad5a87e876e6ef60c5baae903784bce368f610733d5", + "0x2124c416476f7658614c1408a35d02995896e6d0e1814003c77eb0080875b868", + "0x62f9df3b5890a27cce07199445b402606aad8bd0fce7a0a186f9e149d6dd2513", + "0xeb6539aa98814a0076aad67912d69485e32fa304faf51a5b2e13d5ed29c46358", + "0x61eff54f6056185f52c97578981279409ffdf4bad951bb72e6c115eb74a15804", + "0xd18730d1194ab0c6645d41803537e9a02af77a021b63f8565148003a1fc0659f", + "0x8fc9345d9e438585610e5c0ecb713b1b7ab37c70f1197b79b520acbb3b67171c", + "0x30f7b3469221aabd534519882347b798d282ebb78732c11b7af88b45c5d8f0fd", + "0x4d63a4aa414af9e6a7b5b218a57452dbc2a791aa500bf2621f1f17370cf19d56", + "0xbce4410637d9d7292a15a59d4a4a749fa5c57ec5970b0d83a40acafd441852ae", + "0x49a1a243a21f6c280e49717818730dc684a22d02744f27b1f09cc15f31f54159", + "0x6f42a43c03eb5dcf5576efa1ad6f95e5111e40923a88dce939888542df4c3ac1", + "0xc898c808f5bb9137b26fd343dab2c639c30e2636e20c688628e5c4ada4927807", + "0xfa3e74c03406d396f01bbbd54f71374f415e447084bd4bfc6ea13610032fa164", + "0xa32d8076fc707f4ddf6de8f9981493a643c12a4b3c07b8f5086be16632534f2d", + "0x51ab733072359d85bcf60782188d5254ed079a095b350284a274022f473a70d5", + "0x3af859e105e807ebee1d1d883c31b9886cfdc997a9eb19382571dc92ae3924c6", + "0xd544fca49ba50fe0e705dbf3bca15b00f8d4c152aee8492a8b77b9809a127133", + "0x1cfabfe25dd086c94364e1832421b8456210b147e1d29a21848b37efbad4494d", + "0xc4bca21258cd6bc3fe6f720a6fc77f6de401f2bd4ac9a976ee74c7f258e70c1f", + "0xfd0e26475c050e84749c52e72ec62f006b6d2dd793219587582652bc75ef5489", + "0x7d5733f95b49b4e22b6f7150a61ae479c036a516f8f38c577974301914a0a85f", + "0x865b75a745769ada76178e16071aa6443f381ee888ff26363800a728a31f04a4", + "0x8675e685293611974cb81282d88c8d7bb548270f4c8487320a41cb37eed72293", + "0x060ec17748183cf41a52592f7099a4161e07cc6a5b31b75266fd9cab28c3add6", + "0xd48a10caa1d1d8e65075d65b3048450ce1f342c06a128ad4a3b16f4cb9d56c28", + "0xc784ca025532d1c9bf559b85f0d2b7c47404d15262e158280ae49b02c7ccf69c", + "0x53abb63f63f6e99581738f3a3cd9be30c896b449f5b0ea95c5a3837272f6cd11", + "0x4d17ea57dcb31b3f025cfb65e33475257e830e44f3d64a0935f1297365b38b15", + "0xc0adea4fc533f7d4ad359ee45f30ba258378b625b40c30bc4039fdb65055cace", + "0x4ec410be85dd1a0a60d6f88b69f239d4c30cd1639b8a7d47c6f7c0aa06916c3e", + "0xa8b4be0dfb27a09561574a8cf927aaa78782ce01c80311f0bee4dd3716569479", + "0x8cab4d2392bdcc6db9d41c850ac8e70de857fb8e55955a2df175afa75adca702", + "0xd02810da4a03f811fb3ae189747a3061b3a1b0f099bd9004fd81904adaeffbe0", + "0x0424780ffb52a37ad7cc42a7b5c1a5f926c3808e7161f3eaf6a78cbdc0582f5a", + "0x23a7eb38becc2dd52ce4e2a1c11d15f7f5bd952bd284f819660705bad4e74f4a", + "0xed9fff16e385e3cc5304fe85a0556aa6b34328a07dd7f4f8bef879130d106946", + "0x041563ae473403837f6535a43b712c9ffa2f294c6b2180888da7918398d45d7c", + "0x8d9f1208cfa1def4fb54561b410457717d49b8a0bf82f6352a7f800010af9dc3", + "0xbd6d54982ef9f8025afccb7b403fcb6f0bf77dbcba9be54051b695657a5acabd", + "0xb3acec9d04318b9fc2a118bc050e5b3e50ef7bfef6f1945f4b18fef7a0140318", + "0x446437b30411e6c7b37e56dd067e957478102998fb9856e8099404347316304f", + "0x99039d52062b422e0e39e084b4804b9bd34327b70cede3e4c531e42674f4e31c", + "0x3a53b77f034840d316e01e7ee3fdbc307972ecbca6769647d041da48d74883e9", + "0xea6d5f43a579b7342b85a4e297c4ce7db530bf6d78d083440534df60dc2970bc", + "0x3aeba08e9a94bb02fb10d1f792bc88cd1da935fe7e1f343dc21b363ed842310c", + "0x6f3d507327393c8bc5c7205375667bdf6f9b2e24140660df2e40545b0b324213", + "0x46d395a3a67152b244ce409bdf4600db2e9c88bc7ec4bdbd4cb5ae68da004156", + "0x3788d2964e66fea160ebf156a58cdb12dc00fb369d59fd619b360fe9cb220451", + "0x1da9772742302f3f0739804e55d56e3704c9d798c7eebf1842bab2a96a211101", + "0x0b2ea292a714276542ec82c54d706460cfe1ef36d38f9c37c7d89a37b30f3053", + "0xc0b885ec3f670317ef2ec7d15e9dbd632280459488accee5841b063be84eb36b", + "0x0a4d0e009acad07c14dd157ccd5f0d15903bca71a7d811de2d5810267e21c212", + "0xb4fb3fc15959279270ba1208c1703d68f9d0156926f6506021f8c315814fc0ab", + "0xcfc3ac8f53afa51fa6586c55d8184b89dbe90d36a7d45684c46742f775f93e69", + "0x5069882d841a7d609353650a8e8295a5d54796b78e5bc6405d71b65a9f8a5430", + "0xc80e20696b11992240e8c0f1b7e7edad06b164c03153b6b941c84dfb372bac82", + "0x944ac596e73950effd2f261eff1edecdf2c2ac140abae84a8e9b52564f083274", + "0x4da2723679e644b3bb86bc689ff98597928233ff6cec8546975d1ad021738c25", + "0x7342a77f048ebbbc794fe48f22f7551a9623af576f68dce579202aea4531bd82", + "0xdd14495fdb8650175c1c64e89545181c638225407a984669000aff01928b0caa", + "0xb87bc2bb341e9aa89edbfd5bf5de198cdb48de74d27f1fa7919ca75fbe38f720", + "0x12b99121c9e7073cd6093eacb38be8a83bcba7d011d31f88a56aa96eaa4cb5c2", + "0x6ecc3893c1bc4f108e207b70008abdbb10a7937599f03b2f296ebb6a7051eb35", + "0xc4d22f046119edff4b989f3ee1780412704a0e1845bb465c743d7c2ec80ac91e", + "0xe211ecee3c193593296ef1d911769facca51fc2d0a026bef8582728724081838", + "0x6af12a1b5c408dfceae71f40be1b94be368ea88cef992ff50c97b4fe40912701", + "0x9001a7ac6fc8b6dcaa29e766d644aa9197d2c862c5d19071486c97798a1ac571", + "0x8ccbcd2015e3900f237662fa4eed51bdedc94979562067dc64fbdf95121f4630", + "0x05464e274c5bed6ba5d97381a03b47f4bedde2abe4fd8962ec5b990995a8f824", + "0x9b5b76e65a7f272910c13f5057b0ed22267617d570369959727ad7d2ea98b2f7", + "0x03aaed743dc6a2105950334ca7eeaf3b263b1735512bcdba2cc5846f02175178", + "0x71c317e5048f2503a3a791d92f5bf086334b1d05e7a03368048fcbc6674e41ac", + "0x08332475c4455b0d8abcf90a5f874c42646e836f248f9051157f399aa546c764", + "0xfc06ea004a1aee97fe02f1f78f1e0bd25a39ab21c1bee01e95786b577674363b", + "0x91490412557d801cbfb90d61d7228d95724455fa8fe525aed9e03c9548052176", + "0x3805836b29867786374c17b5f1515e60f664ccbdeff0c5e676c7ce508ef807c0", + "0x3b6b95835c1dd928829bed69a8f41998668d9ae3e8fdbc32cfcd9eaada1c0dd5", + "0xac630858fafe42d152e88e2969f59597f411abe7cbb406fb569240d639bf7729", + "0xa98ff7f83f2a582d97bf02b6f2eaad7b257d94ec6b15133daf5cde24d53e37c0", + "0x2bdef5e7a3de521d75dbdebaf94a110b932b18cd838f755d57d07e5035c28a7a", + "0x66c71823053a79cebaa5ed7046e707289a03e9d2b14c41b6bd12b3eab17a761f", + "0xba6ff0dfa544eae64384bd6c64e2e5025a149a27df5a5cccc20432b6c2be0b11", + "0x1f32057a1b48894c44d12030e558b6b627f32a510c71d9e27d95231eb127711b", + "0xc6d17e8d10d457563b2a59dc2bf35addfcb54083c2b6bfa7f366d17495896212", + "0x0f6a4408879df507b6414ca2a67a9e78d00d1c4d6b6737cf28d3d81e11e35b5f", + "0xf36b864d14c5d6d8ddd4408790181b7f43c791c8b92385322ff1e1d9ea029256", + "0x801989b11c9b0a479ed53b0cd06971a90c1fbd926bd4b874969505db38d0465c", + "0x90a8b7964dd7091d182599bf59a3471c3d68c1e351a3b4b668b1a61f2ba3625b", + "0x8bb3e6b284fd4e1433196bdd371e53d30beadcc543f2c64da2eee2536b0bb423", + "0x155c04146c14ad44342cc9c073aa83b7fc3df178f2316dbbf75b06b7a9b9e294", + "0x6bb720c69a7d1ce7fa5015bffd87a0468a3c739a253d5b6f667482bf4c14a9f1", + "0x49564265358e022f3fcf65cc2acf0ee3d901837aa1564ecdcb2a09b7d97fc485", + "0x7ecd10fec887a7ebdd1772941533d434030599ed80760c8c583013b28519efd6", + "0xc31822cf47d7e967249203aa651e966ce75d9b7859fbadd7b7a7e197f9235f51", + "0xdcb67df3b6a6ee221d41ceb821297f03e801d067080c190f13714201cf69997e", + "0xe0a1bb3d988450cbde8d118785775b868604a1deb461afb2ccd3fdc55989e302", + "0x9042bc102f573b0de03e8b20f572cd0628f89d38c5f6265f6e33c1c17816adfe", + "0x74d1b1c8bc2c8a9f9f1e9e4aa0346d832c45a9e4bb05b5c9acd2a69ff5fdd0a8", + "0x352b327f2212b26c77688580d6b139c819527a5a28c629840ea36fc9b8d56fee", + "0xfa01789df2d59d59d8288336b9b18c1788a55f4b45a572a5c1c011448894a111", + "0x8fc8461d066094b0de98b89931a336cbe44f3c56edd69db08c86f37879591257", + "0x965c32504eaa03b1466bf527fa9dabc0179a51f8d8d9fe0c0af7ae74a64c408b", + "0x6976da75a83627dc5296a3de8ed421cd721372d57212be09e68e4499487d70fc", + "0x2e7887f46f165c5bad14f1a345c1654e276d221b3eae300dd45e452775efa4b7", + "0xf5d9d014668b8ebb4a9c6cc55cec45fb15b5095ef7e58e2e45a2e4104340ed2c", + "0xc45024ea09aeaa4b567567c5f37233d494e48bdad33b35374852d02cf0caee83", + "0xa9f703e0a60781f6122ec26a425245d1ad24c93d349507911b527633245628b4", + "0xe43b0f0e9d990b7440e4e7be38253998a893bd5c046324524af24c1ca14b4f6b", + "0x8995f70e9923a817475c8d9cfa5166a805b89b1e4832c42cf9d685a056290dcb", + "0xb9474f4f2cd1ed7a9b95b076c1dce5f218d4994a42f7ee30b9f606e7ed1fb82f", + "0xe8f00ec1e863649c88344d8f3cdf31b9bfcfb5bf4c4d375a60645f37eb65a9b9", + "0x91d3be5f24ff20101007ea923d4b0b7471f45a67213a1202d54dfd0bc6cdc7a9", + "0xb457bc54a72ba5895fbd7dd08920d62f840640643ad1933e1291224b6f3d1f39", + "0xe14557b759d6aa4928a23152c5ee9082c2ebf036522bcce3a3589cdc64fda2e0", + "0x66aab67124f102609c3cbf71976fa3467d73832251d0443f2bd0a2256224b69f", + "0xfcf98d61ed1b57d8e6f7fbd38f2d4e83185c58d2f72baee8a8e8d84eb7eeecdc", + "0x43e5ba033a5f71f6274070c62d839c2ec02f2c4bf51f25ea8f0080d48d10be69", + "0xcc0b95b131e0e74495a7ced65e958d54989b32c77e8a4708e60df33783f24aa7", + "0xcf342eedcb16db36e48e4e9e5ca16cf7cab53bac250bb94fea8cb7311000911b", + "0xee052b6d498230450cb78607ff5a81978eafba590434f710d6557bc233bac057", + "0x23e7481cf8b183a1160e6df1b3c8cacaad913dbc646835122985dbb27fdeafe5", + "0x46a49c07b0c2abe49e92f88f1f9ccc54319b27af138f3b2bd7fa723cf31eafd7", + "0xc712c94d35c2839d0f3764dd9575f2e4498888e09d327aa63aa69b819435d5fa", + "0xd2fe68ad31f00383e6b7644d829c5a9451f59f527089cf403a5004f361ddd0e5", + "0x9655ba32a3c0d3963269105eac1f00354403d1d347449902d69ec5910a35d1c4", + "0x577d7ed354232abcee75e373a015819169d56a16bd2348954ee14d3c4bd4ac67", + "0x17afccdbbabb218a51cff50de821a079772428c5150a4ad312dc7762c03c746a", + "0x2cf47ce7c9e725ce513f0c0c805b5dc19921c8d2a8be2d3850f4df6e8fad1aa1", + "0x9ff7cac1a7d00eaedb685520528679c443b5e48836a70fcf264614939b3665c7", + "0xf62bf4ccc1639a81f4d9bfed2c200bf849328045ab5cf056494369f1e2f1407a", + "0x2b145781dd82e2550c8edff97f27188e7c04d858ef153247be8a33fef8954c11", + "0xac44fe07fbbb49c93e6b68390a417a54fd77fb4e7c651a95a4bfd31892e9c45d", + "0x4636d06039fed2ad74a5ef9d8604f3eee68afdc5f3ef2658fadee875c0eaadcf", + "0xf3b645edbb169aebcebce633ed96fb405daa4fc489ebccce7f89ee16bc522200", + "0x4b3db25b4ce2b274193e6bc13d24ef8b2d7a8b4c68cc287b5cbea3ed79441122", + "0x7e84ef907233bf747bc587f4393fee7bd0a064cb4d76d7fd383638e477c378c0", + "0x7b963c30b5dc6a94de0f7dc3b9b4fd1ce558bd22307114d2b737773efaf655f6", + "0x395285ff1712993f9a45fd094a01361324bbb052afa066b78f8c81047f1f653e", + "0x3a229d2f955d3340c9e1f72755cd665dbe85f35b3f3e8f82ef42743d0cc9fcb8", + "0x11e2c95392b1531fe52a90a073647b522505e3adc2c04f91238b8721e68b504f", + "0xf030ac01c72676dee97b0355a4cccab55895a3d1cec349aa9712c2f2f42ee9aa", + "0x088436e9a67374856884af6a5a861e28c9445e3a900198f1af05ddd5452759ad", + "0xa77f11c8d57d34a92b8d3fe42374b0aa120595b8cf6e9fcbeded8bdebb57ad47", + "0xa0658d3e68e35c630ddfb5e06360f0bc0fd243ea4a3261c8a03b07f0b258cb32", + "0xd3cda768c5ea8c2d8efa8c8889c9441c19446e4ce940389089dac8c9f4b043da", + "0xd828e4e54df73131cbc796306fefedc2ead366078cf0f263d254b714f845c8d0", + "0x83002fdfbb7834fd23e87a798214af17240e1fcbe24d94dfcf484c0afcc981fb", + "0x48910c7dc789a64c580534b7aaa073458990c507986a09217bb220c2e2e79418", + "0xa75178130e8a79bd3ded2f9a507cb539cf86b41ef8a652ad3cafb42455f24937", + "0x9fc64134e67472b302a4eb36767aa905690c3a94b8520c0373194a18421c8599", + "0x02e80cc1e1d2ef14e1d947474bad579d9180d810f0597b9877aaaead661a8d40", + "0x654520f90dae9d89735b087f67975497716529b2c0ad2cc82e33c0944f400ea1", + "0x6b89729e11a61a7b328438ac62b8275ea9a996d59bb3a3cb8efe8852f0befc1b", + "0x7df2789bed7c2345bfbc63122099ac0feb0d841fc85d7e964d7a3da6d1af26b3", + "0xc8902a13b376c77868b5354d9539611cc620dc95acaada8a3fc841912cc62dfe", + "0x223b93d9645b13e0d804772efd9895c97822c0eda7bad230b488f69b9f8f7b9c", + "0x70b47472714088d536b397e0ba60036dcca985210d26e3fc206ac2bb65dc75e4", + "0x3417ea69a4dc0a83facf94f404bd62b6cef5e810c172565cc0fc2e31da7b35cf", + "0x7dde5911df63f2bd41204b1b172652e72cc30c7d28ad1618271b7f8e6da1a660", + "0x8a32876fbf081dae31a535a424a3416736f9bf67c9987370734ff9d4e5f31e80", + "0x188a73df80be11cb7d0a32971c2a3019731adc1e6823816c8e403660b28ff0e3", + "0xbf0913df121b17a5bfce4a7c6614f5312f26410e8e4b5ae15f7b3a2c231d8a57", + "0x8335b775a890d42dd3f0d09636814a6063da1f3b57be6dc1771b65add21819f6", + "0xaa1bd38e26e60cba31ac1b081b0ce6a182bf961f63a54ccf1aaeada0a1e1498e", + "0x5a3b14cc9b850c56dbca7150a9bace89f9ca46708f5cc0cd9b5aaf9a4434e342", + "0x2877932303aafffe4a07fcac0b555745eafebe18e64d6ff53cd345b4a5be4ac0", + "0xa1adc043723687b5f3101f5ee357cc3600b58b7dce1a774790707bca45c7d77d", + "0xa449b77f389b614ebeb674e803b2e7ab3883c81b77824296749563a30714827d", + "0x3810da1cec62095a46616d20f8a5e9ac88e06b4fe7f201ea91a14d58cb577162", + "0x4e1fbf5915f71458449d4db3450b0cdea4eaa60e8fc7d1ff302bfb3f9b354c00", + "0x1b737020c252072574f0ab7b0d991b32942a5d538c86f19419561e149fb31189", + "0x24022075dcff3525b236e9d4cc647e9c8206658b39a249ea12761670e39f2229", + "0x25ed6c4595f7e92354fd07b9c6be5543d48ac88503271c7548a3bbd898490357", + "0x3fc6448bc3a4979c433063808eeb4fede58fc999c69133974fb98d3a707d3331", + "0x18e82e4b77716ba9a15d64d170d6dc42d8aee97353e6ccaa3e07650fd59538f2", + "0x1d2b8e43286f1b453b6c9bb4e716878bf83c87e71efb2aae56f1c6c08b5bd0ed", + "0xbe9b9b989b036e66ad1de85dc405b8322f7a0bb1b5707cc3291163cd6ae283b9", + "0xa3cc4cc8e3b91dc630c83945cef2735913dfd34aed2bef51c2002a4b0a5adb77", + "0x1e8676239392a6cbdf4aedf886d3e5c5c810939a691c81e89a818c687c11030b", + "0x099cc703fe8a3b8752d683bac7055035c581cb37016171caea531da21a29d1b8", + "0x812120cc67aa5cd9d137b4ae7e350b56567fe1570118786c85d5cbfe555d4393", + "0x21a835a28b376d25450fb370697ccb98e8ab3464ae477c33dee3b6b358471bf1", + "0x010c51b821f8eb801518b6b73c6707ac692352334d0c9992217a5a2e98cf576b", + "0x813182159515778f145b9709dc23a3239634bb0a49856c27a2243d912c349886", + "0x52c09bdfa21ea2e9501558325084596961f4ed6fbca2f15aee30efd10e79a307", + "0x5473c78bdf99046c855d944c8ab9a7acede166d656ff5bd9ce09fc4c29915b96", + "0xde9963b5b414bdf44e2029a71a2b3304892491df6e111bedeeef820373b206bd", + "0x86ac6b868defc01de9da347e709a9fefa8348519f6744510d4acb296b73d933d", + "0x55e84f83adb647129529abf56cae6f0ecaf507f21f57cd2423fef4c53c20b8a3", + "0xe20212ffc113a004d88e4d6d1b72563732ee1ff52ef19d3ee802bf8eac28e9dd", + "0x9c4f7614001702dd5524ba2cb12e10ae8f4dc9a6b417a08d5dffe2d68c625591", + "0x087c6b06baed6d83d944d5cd5f307756db7f168bfd572c7d4da50d4858d576c7", + "0xdcbcc6f7f2380eab3a2572ab1a34b6d21bf0df1802bdbd863e052814686fba62", + "0x98ff31976eb8aaee55395461387adbec134f9c994a0e08111b9adfc9f6e3137f", + "0x1062d794138610df0cd0fc0d18ba93f8ff258f589352f2b22963cf31bd804a62", + "0x820d8adecce1c2e3907de9482ff253025c791edab639c9465c1cd29270cc4686", + "0x3b5014831968cb8063b8f8ea315d86aad72f7850f980d041150193977a38e983", + "0x0f570708473f87b57072c11d59c76e54cf743939291d9f932250d2cad303de76", + "0x02c34bf9453118e935c318e5c5c478b89155dc3304067014ae8705b131b07ff9", + "0x03d7d919c1666a5f2c69e6d5770677f5e38cc3558c405e6087d6df95443e72fb", + "0x7769e924cdb878e30bc5ba289c7fd6f0cef90a1a27969500723641e068bd6dcf", + "0x2c2de5093038a6fe45223912565a0ccbec511a268e8bd9b64f23a1adfa28113b", + "0x1bf36d76b89b16b14d699ab4c5b5879d9477e382d464cfbd61891a68c76349d2", + "0x25cd231722ea43c802ff592e22b441379930f60dbdbc856b3fc4ac2446158dc5", + "0xebaccb1fe78cc218ac9b4e499924eddce7309a093bff6acd83e8b2af7577f335", + "0x11d031907a3192022f6241a5b553bd5cd202bd17588db7124a197a4d0b64012e", + "0x3750440bc2d32c7dda7c3ac70c59a8a82f63b2a4ed681b080cb0650830f9eaa2", + "0x944434584057c9418ff57dd1be48f53e23419ca0b44450cb7bfe5c33841b6f94", + "0xceccbe89e459719131085e759fd6e1a5e766ee9a119e8fd88978ec6677aba4ec", + "0x2f327ac96f10aced77648e57564a458eaf77c37ea94d905f17398a1913be744b", + "0x54b400f27386a4b33835bec8085deca78b3d290717f8e5f3dead2f41350191c8", + "0xa5ef92f049ab382137d652ee204bf71d7c3a50c693b49af02ef09815922fcb4a", + "0x7142777b188f1e1645bac3def002fa0e30ca0bf3c97fbfa534cb50931347fc1c", + "0x0c3c897123c1cdd0779cb9e5d62a0cfe9a081c311e47ba061d7d8ade7dbb6c01", + "0x90d93f8155958bf8487bfd2d5755574a2036c42c3bcbf3cb7357371d242842fa", + "0xc04c83b13cd282ab935aeb7663b7393d2d5f6dff3a77a502229cacd982289085", + "0x2a3d2a76e34d8d7e54bfcad1056dafe5b38c3d93fb59523b5690f19ed1021517", + "0x1a8574b1c6ae34bd51d5e4b4332a8c4241b8ab2204a24e2684ed7d724cb9dd8b", + "0x4b664de434a4540972c991dae160b085f97fa471d56df35f28bdd09c39f39fac", + "0x236bfea0fe869a6beda81e76459c1480ae4129f8ce8b0ec0320a80c5f1d35597", + "0x8cf50825ac376ea3c6140c36d62843c99ccf555a95407fe11ec6aacec778e4ea", + "0x6a3c62df02e2d98ae3ae787a88bcd1321546f3f51378ef4db843f01d157dd472", + "0xb0a576a9e41a7e18ce27272a3595c239b7cb397164f960483afbc60b41ffc67d", + "0x76c685c01429242f8561b461389583018823512c893167133e464ea68bd77a2d", + "0x1836fc6314586b6d1db8f3fae0674bb365930c221fa9438d36ba2301d64d63d5", + "0x384518444d4962cfdc63351e2c3211993c6958dda2394553597dbf012bf3fcce", + "0x1c9d3a4e0383ce2c3899600165e158d382efe6d0f9a5c0696762b64d0be3b6a0", + "0xe164f6c4f37d0d27d090a71f29d2aa196a677787633211cf1822d0274b8eb1ff", + "0xe2a1ba6f1b4223f203f5f5422cb1c930fc616f174ce7b85daf243eee0fd3cc0b", + "0x7ec36d5b8e4087505ff320a35f89e00e669850d5798aaa1b357949f065a6cb0e", + "0xd945ddb91983af54ffdeac1ad5ba961613953c64af20ce95e50dd0ac5291557b", + "0xf4796c4157fc7d4aa0a1d906eff2adbb9e75559b103ec3dfec6312b3b6bc6bf9", + "0xc332acb88151881f0f187dcb8a3d79b3eb33473c8b5f4e4680a9a5bfcc3d1de2", + "0x6952c14b8928efc53d08ad1a3a6022e97339a66b2b40f70e8d0c6a88e6dbc71b", + "0xc01d168f47521ccec3a548fcc9667a64717637947e5ad403d0734a12499c4822", + "0x6cdae84e58768741d829ed5107ba100f6dcb7e11e74cca1a1517be800ba298db", + "0x271225f43a28198b0546f20562b61f82945da3b54198371e4f33cf9370a872fe", + "0x9a99a7b3f8c9a255d6a3cda707b68d5bd737cd1899c705aa8e28639199973d3c", + "0x9ea4e9bd25cdb33ad5b1ec73826b1594b02c647964e7d469f1bdf1d24553dea1", + "0x3eae88c3633418ad2b8f4369131fb3dca1d66dbd3b9fe170e6351b04e01ba1e9", + "0xd134373c52d4e80a6fdf18369348b9868671ae8573449cbb76717c978016845b", + "0x945d1829acab32c50bec3640f977c2705aab9d38eb41b442f1958554d8e865cd", + "0x59771519fa7dd5de23ba0d9e8453ce293b204321d864c435ed6e9d7933bdfab1", + "0x0a6a9b7d7588823cabc9ee895716bb4a1304aa72e580c5a1d20267f040fbe0e5", + "0x15e879b636c3551036a4d0b0a8d6411708bb7febfdb7e30566a894fc9bafa6e0", + "0x2ba7649f154972b6abde0a2104943b08c74325c5b35a6ab9db080290d4fd0906", + "0x7fc9733215e78655be007fc065f3fc4126c0d48a4eba6d69b3394ffeabdd8887", + "0x734c92e25b921d786d82cc8755f3d29ac97351275288bd23e4039860328f59d2", + "0x26b54f8677f7ed229799fd18fc58571b83b9615e02f61df1a8a042817fe10c5b", + "0xb74222e1858def5aa50383ac53e7b7990a7b07d9c759f61d22a2e0260094dc75", + "0x6291c38c518a47f28bf2542aafed2ac7a3dedf9aa372e0f2aec9316a5e1c46e0", + "0xda941dc8e0e11b972776e6c1cb3d81c472cd4ad9ad3a3af29446123c3b28fc46", + "0x66b573448dc35d44fe40baa2909d463d9d7e1988cc0cadfc25ac9d50787444aa", + "0x7059abee599e3fa7aaa08c4185a07c6c6c3862b45554aeab09e3d925e2f60685", + "0x6fb9e552a5e23d10522d5171fddd08b8ca6fe3afd82dbde310799be0b3435321", + "0x2c3dea19e4015697133d83d89b42b54a7573f5fa8939e918eee3bda97e397f0b", + "0xcf3aed49bfce32e9593c92185de0522811016c2b530b41d503cf4f5c5cd41016", + "0x0dafaff43aa45cc59b2358e1ff3ec7771b3b1c851f3a8c0d63c4b0c2c84bb2cf", + "0xa9ac326743f674c48aeaafa6d7ddeec0120ba60c20c9942a2c7d1351dfbb0882", + "0xaeb86588e1c61dba8d3ae7a355a89928f914ccd00dfe95f9bb273b0d40dfb902", + "0x0e85a587e054c13ff1137f06ec2f978085c9a644c51fe97dcc2e649903e23067", + "0xf4faaf72255ddd4918729bf5b6bae69d12cba89722e42edc4a439047c40bbd3c", + "0xcb0bd670a1dfc23ac3c23e049b60bdfdc30795efc536ceb358160a56cec8d020", + "0x880f4bf45aa3a21f90abcdf757d5c0ba174427302c87118845698d5b470c5445", + "0x55b37c9fa4146a04d73a20423289bb6d334eb12e1a26cc4dedb244143ced160f", + "0xca84b49ca9219adc33e4beba61b6cc87027847bab97360e3a34e827a424f36dc", + "0xfa037a67a4cc6f9f55316d7263c857fc2fb06555594bcbe4cf3564f8243f2f28", + "0x70b700a88d456bcf7831a7b2e3bfe6bc2ef84990c9e539874fd3bf212e0d164e", + "0x3faad07e513696d2c4d3c28491d12077a5104754345bf5cbec69b3dd27e40299", + "0x7dcc9d8fc40d45789081611ddcc40f3db5db5f74c503212b9d8635311d0d8a12", + "0x18af1139203b4448040caa40cbf904398eb27ccbbd8e92e0d850ad90f7605566", + "0x45a54d7e06613b22708dc744aada0dd11b5776655f5eb6c90019399e2488cbf1", + "0x89d661b4edd4755aa069e17b6a3971128477c6a0d333251e4ef5a54ab573ab7f", + "0x60b0e94d5861cbddc5df24c42dcf74434e0a6c357f18665bd3beb54ff535580a", + "0x56f8f6fd965552d64262a7e6ab9b176dcec4b5b4ab9ad29b893413154f4b9830", + "0xcc6af05a8e4f3e4df3dac6fe8bea4643a4c2dde050c17c536beb7b485fb90105", + "0xa1c64078ff1c338613915fc76c4d001f169f07c5deb385d2d2d6500a6565dec7", + "0x6fc592d957bbe8f1f59bd16239d1bb6e611512ff122fc0c9872f568a0d3d9eb0", + "0x2f279bcf18fc0db8c4742371ecbd18f74cd78e2f3e8a3e5ca8be1f2597a36866", + "0x431cc6cece5122e03891749a10a14c3b6d45565ade09b9dfe9b539811d2e78ef", + "0xd0771c4120e097ea21386cf30fab76ea183410350cee75a7120309849167ea41", + "0xe08d10aa656bc57382847564fcf6e6437c6a4b135a7a2be979f7bb5006d12d4c", + "0x24a17d36eab75bc40af6cd1ce1b0056e5d601f77ac5e61a1ee99b3ef0ffcd3bc", + "0xebf1bee5a30c540283a6ac4754d539716765763cc93ad1015e99576209265286", + "0x2730ee2a73c094d108c57e4113501be5967ea4006cd1845cc10d83020cdc5601", + "0xe7c8cfd7e3e40c24051ed11de5ab2c510266f77d6af3fc90ff297a833b4eb94c", + "0x172fecf113089e4db59637d762a64b4b40972502def8d4e92350464456236bb7", + "0xe352ea9bd833cf071c4756929bc99acdca62a30c8b9b7a957d98c7775de5ab76", + "0x9127e8329ba24d6e695b5ec89be4480e4677a3accb1ccb7d8f876c274c0b5a2a", + "0xdf520977ed9beed16e3c22b703b354da3e934d3e3de30b739bffdb9320b1e447", + "0xa89559db702061cc684f4599c1f2d813bfa35ea10cc3f6c7057211a296d8989b", + "0x346e006a9b29524fe4f790d0f602f8b433c16b2b16961e8f02adc2cdf9e2e4f5", + "0x93ba610bdd5fe3c1b67bc69ac35a28a08deb1b27c01e6ad4838595639e62632a", + "0xc5b190cb41e187cc3d2b24801bf2321c6e04172d08fb421a52eaefa9be3df8a7", + "0xae0065bccfcc8c1a912af4e6911f63787da8f7663399f154a9e1c109442aba5c", + "0x1affbec29562279c7676dd192e9d2246cc6070e4331e25e0ec4ee69eed5523ac", + "0xacb0ca530c7c94c68eef206e28f0dd4eaefe02b21f41caad53febbdc9acbd572", + "0x34bb2c1e8603c822531c425e282c74edc3a62a8a57eb558cf6281e6f4e58d958", + "0x5adff85f9023c9b4620d01461e42a367ed1bd2a9a085d92af3a1cc780a9e535a", + "0x3f8665d072418e51bf16b6b5cf8ce6b631c5777a5d64afb2c3f25d90900caecb", + "0xd70dddef450626b7bc2c560979bbdb62b79ecdc87e3580446a746a0c2cca5a72", + "0x14f66dcd7cbe250e176ffbf4f1c2f45543e8fade96d722e5cba0f262785a2cc9", + "0xfb66f7079418503d699b44b75ea39401c7abad80ccf632d3b2ac75c2f739c7e7", + "0xe637b797be85d1371b59258103970f06b2b14c86f8f300fbdc08a4f6eeccda9e", + "0xb39c0555ddce59acfde719f208f740f7d5447d8e91bc7e751a788fb2e21c00a2", + "0x79f09813e7604177979b9812f358f0a1bd8039f0b7c0ecdb05e7dc74a2843ea7", + "0x44cff5097941e0b3b5508308ff853c80d53395d19d4794c16ed1fabf73882aa0", + "0xded0d18a6dbd535a597382b9d98005492c42f5ed9b3b19c0641030fee4c81965", + "0xcf63a485a206b22e8c4427d0be89b2d9d0971a632daa14c4e9d1ea4677209eed", + "0x4a896cc926b88a486612b4f67a7e7ae2dfbcaaea82b6f7be1551dedf2ff6f2e6", + "0xfbbc6be123d52cbb0db10b9b95d16c30b9ee78f78c5a84afcd94f2a394345a21", + "0xf0520f214b780301af2b941b7be9d7a243701e9b2a13e0bc2b480016f0061606", + "0x58633f0cea58cd3bbfc49ddbec4ba00710a8d26bec63f4da35d90cb24bf9dab0", + "0x9a2332e8888d5f53bb017de7bcf18e5825d15b7cb9cd1c21a7fa48a1afa8d1c8", + "0xcfdd401363947cbf28fe2be9ca2d7d406e4297910558c47ecbd4c3e761f39c73", + "0xc6b9a3cf61087c40e327c43348d0f507762ccfc2ef460aa0ba7b7076c130876e", + "0xe9ea0a239ed2e2f40a4a30de6a1a7a5f098facbb35761bb1e8424818f40b8889", + "0x2183c367ca40518a3132162d463d177041dcdbcf84cca1a3c3098007cbcbbc29", + "0x2ccbdf9949c5c690a4a54b90b240a2698af6952f7b6633b4b5647ec69060b3f2", + "0xc799b701e90e26c087cbfe0cc25f486a027a1bdf841eac52373bf3414077ef67", + "0x291d76f5a0938eca15195095f31547b7e4098b033f1b102f26156ba074241d5c", + "0x63e204cc71b84648c5476a346a3436204dd7122b2fb10091e89d38a79e17b0d0", + "0x91ade613fefae719e2f0ce3e92961f7e3460c01f3d2e93e7cc0284aa3ad3064a", + "0x48c5b81042a9ab483d62f4c175c44a7ce1645d9dce872506fd95ecaf8322670f", + "0xc01b2f98b701f0a626116d59137be0b7290d2ceacc143166e5e2481fc186bacf", + "0x9f6a287281f8b383fca4f4753c390c5aa813a193c69c96ed92d93f64a2d919ae", + "0xe56f242cb31a9786dc0111b7035490b262cdfcb5db7f6b3ea2f6bd84a6bb19d6", + "0xf58e39e55328e4d8feff49a9f2add569f9ffc737dee91323253c27a31d5d65e1", + "0x0bdb542abc6416812e466476da50a7f01744b693363ebc6ef35b5305d6baa506", + "0x277c015369cfa422143fed1bb253c883aed11d229f0762785b97724903a4ac2d", + "0x2ed023eeff2e3aaf0f576516ba04b56dd071355e34b1898f9a614f194a7b09dd", + "0x114eeecc90090892c70d3e7d4a904dd302c2215a0017f213a5750fa45025982a", + "0xf0ccbb1fa7d2765c9674a9c2ca35f9ac9b670b2eb33bf7bfe6eec56e3216b574", + "0xa2d567fa3e55e139386ea14400c8033b1c6c01dd529790ef1c8b49fbf49137b2", + "0x222d5751d4df5a6fa1c431be6ed86b78da7ceff359db98129e759a98ea6490f0", + "0xcfb41f41bf990e40a196ea1fdccfedb64377cec5ee8a1283bce5329756e19f39", + "0x0fddfb112bf2598269b849c06905ebda2d7f7a5e214a302f4eedb93c6d8c6d56", + "0xccaa3aa1d4d29c3d3ba114dfdc516309632eb0ff421504a6fb320518832db612", + "0xa5f804dc2b4bbc5c21be30bd873975970767cb6589a37307153eb8c01efefa35", + "0xc250aec07e42d242bb5037fb9a1d6c88f51f6ecae722e76c7a2b18b86e334e1a", + "0x69657f1acc3c7d3fbc0297eddcc4c98daa352b6cb2b11a8c07a726bf7a2b84fa", + "0x3d640d19237a518e3dfc3071114eca52a0021f4a77a95e5cc70c4e2462ece117", + "0x7b7cd7889e65003dd5d6ac3b79c36234c57355dede75e8bc7937e5b596c820b8", + "0x9590ac5d7543bb9ba4615c0486260e1342690a2fce2cb1b97aa8481909e5b321", + "0x67a870c914b55354ca924846d0c10758ed306c8b8b8815c22f54dbe6a16c7136", + "0x74a5bbbf32ee887f3e372222830409576c390ff97de549746f5aed28b1748a94", + "0x6b4b4bfb9a3ab59f26d69dbd318f176ab1c994e508cb91984b56f1d4c4f699a3", + "0xf47099b98a997064531335549f020f4be65d0d040bb640ae44876bffb7d4f956", + "0x0134345568f91fbc2329b31b967e69bc0a4f3e647d55e7a40f31efb5ed96cb19", + "0x57eceeccb9fac85dfdb92113f108e1d057c9a808b98e6592fa5c80c58ab2521c", + "0x8c61808058df1189795ca5f7e340f488738c6382a18ad25db0f3d762919eafc0", + "0x7866b24e17043d00dfd2705dc2af3f55a1e5c5d300d8fdf4cef29470f2ffdcd9", + "0xbf881bf7f4d20d3b2a2426308a8c395f9bd7be4249b45659e07155f34f23149f", + "0x29da2375badba33830b7098a76686f8d5b7ed5dad35e6de805ad9884427e186b", + "0x6ce8255bb7c9368788935d86bd4a76ff4455bcbbaa80b2c34019f018d3fdd84c", + "0x57fa9d963bc2a928d0b5db0a67c485dd336cc1c8eae40170dcda28e6c3c7c5ce", + "0xb745b2f2fb99ea71c0aa6c6409b3850bbf3e6a424e4222a578cecda04c5a6f49", + "0xed0e666eff06c39bb9bc9ba14a8a85dd1a78ee2a88622ddc0bbb7790bb92eea9", + "0xb799af7c4df8af96f35f3d2df550a21b4f08cc2c43ea2e60089f2ed452062454", + "0x3bbebbb7b7e1c7fbcbe92eaac999efbda3544289ec08409ef216ab5c610d64aa", + "0x80fcab4884da4431c3c0163c3b2c6f0d3c1ae742969fc113135c15dc2a1d3362", + "0x52321006f36ac1da3726d2e9c8300db969a6a7a0644615f99ab05603208fb1e0", + "0x1ec7a821e5d9cba58696d4a111fb1d2cb7ec24f4e63cb502b6f1fa2f273092a2", + "0xbc17ba4c01fef3367c8cbf40b9d96a224e0750b0629c0b82b8f204ee29b709e6", + "0x7df56aca1620f51e4304933a359186d14687dbd7abec508be07ccdc9753549cd", + "0x0c3eaa323bb61f0288cb504009e044a324d881b92752d400a49b6f1c191d8cc6", + "0xf6c5f7b1ff1bed9f18fac8ab1617bd17414a9597588fca64abe30498f4a6b887", + "0xe413d557eff0bb7b6c39544103ad140ccab2444f17e60b2c949634ed44aa4307", + "0x263eab148c723b8a117ecc3767f0a956955246d300fee00ea49fa02ae49f014e", + "0xd6adf3b5bfe169322d512ecc7b4d61f78eac9d760d652a8d28c16418997b2d4e", + "0x7cc0da953fc7876fb45ef51fb9fa4ffce02588d283f5623ec545453b04fc37af", + "0x9fe3cd4966c81b4202d1079d6932d050358767e43f8eba35bcffc092309c98d0", + "0x3af5aaea51b4a64e5744288e251e14daa82475260f1930b1e9a5be4dd370cf0f", + "0x8d6016bf1dd509d640d63291c711e2275851548000e5749708f8e5231a5d26a5", + "0xdadc27d890035ed2080b8de26ec807f1f30b975290c190a6186478a4a1031b1d", + "0xc4af334f3ec2b8221af11a706e9d4259629e6f926282993b43517b3562a660a9", + "0x5e3110735db3e4efc759a7e09bc59f9b65832062f5f993ac300a9f2b51f46b99", + "0x47b5f4f541a8fb8d78708ab42511356629899b2928ea133e91312568e8f763e1", + "0x42eac7e1ea340bf27195e63ecc2708d0db957e876710af8b2301c1f7ac5269cb", + "0x5299ce62e987e96fffa5e53e7bb04b20f94fc3f1daf00ce2d1392954ca6b9699", + "0xefa01ad9ae9daa8c263360d6ef0620d088342d689398101083d4df06de56b048", + "0xf2c281c967b09aed71c55bed78f1d35292d73cd876df2b0e46771b0cd8941c8a", + "0x68fbd2f7c9fc4fe8275275d4fa7d5fae2db40ac8f0b1c8e382dc03700603abc8", + "0xd7298a1c2eea40c5b1d1b088a63e036ddafa7160be74813142eef70f2f6f181b", + "0x6d5e2be49697cbff40775fa75b5b5c27fdc9e86267673e7fe79527edc027b4bc", + "0xb1b35c67f7f77ea3829c275afadb972afa39f5974e0efbd174be1ba161b5b422", + "0x7da70998a02d946078b0d719b6b1063bd6f9ebcb4ff06b71a6641ffc960a12d0", + "0x1d0417192cc981cf94b43ff0580b56f6add77ae368a13c985bee4d58188e18b6", + "0x5ff777ddfa6985b529f9a99143a13e9fae37d63d4cfa95cdb8a77c1d0b0708fe", + "0x6a95ac16601fbd7833649451c39b2c5d3cdac4c6006df79a11f152e6a5254db3", + "0xc4ad670ab3a3d0a1af18e8c65cd9c256dd077c5c36b1a4b96f4805f5bd1c9c77", + "0xfa45c77d6496b23c4aafcc5dcda64417311523946bbc44288bf5db8ea6ae4e09", + "0xd9ae10993f9fd74d5380ea9fc0c21957b95e888ece881e904747ad9966c74028", + "0x4099cff844ce6f566153da6052cdca0b471d4c5c2ae6d8f1eb9caf1c84e60da0", + "0xc8551c282276ad14f4b23cd60cfbf68d42ae64ee70677cc6f20fc727954e6127", + "0xb77f4f3a98a4ffc14f00cadaed3a381b2e363faaafc889af3008c284c1ec2040", + "0x94b2a21b0fa1345adcac944c00953bb784abf094cd7eedbcb11d979c8b2f7320", + "0xb1ba02839fb3e04e14ac402247baa5b66ed48242156760a43fb35f52f63e05c8", + "0x7dbc7c1d3ffdb016e7c6c66f319358727815e7ce15b7d3eec89e1b917ae2874e", + "0xd8d67f4a9087321d10fca944afa7ff9d773e8963e13eab791bec78ba4b3b99df", + "0xb92b716fd6fee78699ca8e414f0de0e66ef8c08a33489f5b3dd53181e0cdfc77", + "0x518b6a17ee24e325197f8110ffa9942ac534021c18c30a813ea71a39fc813d88", + "0xcaa49c34dff5104bd576e6a144a255b6ff427098defcee1e52824bf246b8558c", + "0x1ba325e752b6fc6a5dddec23279ab4e6ae85ef70a0d28fb56701aa942e413fbc", + "0xdb149d774889880c874c3d097b2a32a796bc65fefa0e49eef5664fc14c55ad98", + "0xbf46916e98fc553d09fa9bdc8f75620b521c0d9c078b27165f645c99ed9dae5e", + "0xca86114d184c74d4829e240a00663cfed8323c12e8aba20803cca062472b0daa", + "0x0ba03590902a77dcb3d9ef88eac309a4ec6bd22072540ed36a3124db4810c266", + "0x725e84e449abe4271668316075f2e40e3dce40550f976b0173be6e0d7e627dfd", + "0xb6ed7b27beff038203dec9f6a1d42d14a4eb9e74e988b31915b675b7561f158a", + "0x55a4d20a78805596de3b38e43516d199eb576162e1dbe0121b963b84aa2c5efe", + "0x5d2aef427690b4a84eeb833493f92c97fd839368890fef4f371731b86e4f2e6e", + "0x067b77dc070877231ca09d315a81ede341bf6f800902ed1c10d452fda8f5ab31", + "0xdff090ce6085b73f3c85cf4d411836effcda72d5aba328d471c23b54b399fdcf", + "0x24d180aecedb2f192baa422f48667771bf4fccbdbcbb9650fade0645dc36efdf", + "0xd65228efdb329455613a0b7d404a71e7532250228a61f13e2cfbdafcce5c6113", + "0x343c7548bff898a40eeb2569a35d14a4115a9b9d4dbfae91389792aae1989a3f", + "0x31e9169d203d53e327700cb232a20665385050796121e0638d33ce318ccf5239", + "0x8f0a58760055254f062fef6df4084743ef713db2693c995b8ba68b4842a51f7f", + "0x689b80ee1309a76ae841ccf9cb93b2adf8929f8e9c047726f383fd179ac8f514", + "0x99b88daba027ae4b079fd046036e77c7661b8fafddfe6f98cb013713bee7667b", + "0x7f940c1b76578cd002f15dcbb28b5e91bc352f01ea14c4bc738d718e56b9eafe", + "0x6b606128ce2f1b63489921ceaa07d95071f18ca98caddbfe17a888d02ebd7fc4", + "0x9db7dbc20a6d4c018670c2c5d8873e7a2b4ba34d7fd8c5d98cfdd4783b2afed0", + "0xb7067c727a1e83bb3908f1b5d85986691d97f1b0cb5c904af701a7eff924ea0e", + "0xf0a5a80b50faf407fe1b4430d14633f064979fc6b2451d9c6f447004da1e151b", + "0x96eb6abccf0c54f1fd3fa7b7cd776a8b2ec1b5304bb2b05171f525ffed110b7b", + "0xa625e0d07a33e0c344d72272b170e948ccd43e5630a92e2a633118e6ecc950f4", + "0xb9776df4d7cd6132175a5fe97ae80719f53e6d0d994bf575473dd25d90d60afe", + "0xc5b83fad260fbadb96f60c6c7a30a76a64c63407cb0bdc83cb44346763fb8806", + "0x2e72ea5ecb44a8bb44bb78a37d024bd29b9dc7352150f8db57485079dec801b0", + "0x2dd02d750b3e44101b22bea080b81b1d4f887a12160ffe437938505cb49b267f", + "0x65ea584b04c5d86d3fe4de7a6130e2815b02f568dc5735d6cbd32d72946f9876", + "0xd4f6b3c6bbcfd6899dcec97a3b0bb507ce67d7efb11a3e99da8198888e3816ea", + "0x4114cf18882e0ef39457b93a942b430b185d6c7679aab322cb1267e6401680b2", + "0xdad872275f34db852a92620099d7bf937f05b085879b947cf458fb1c9252e4a1", + "0x4c0ac641baff997c814f112b02c2231ea06a7cee4693724ded25fccf8fdfe4bc", + "0x1009c66c3461d2d7a9846db1732464219813555c96c562288cd7343610f1a82c", + "0x9308513530530ae9821c27db7da7ff45a045772c3c1db4568d55836bae007c5d", + "0x23efdbda090a541e0a9a70bdf409ae2462e4992e21b848024cfc7cf9606e99ea", + "0x35a9de9061fb3a0348b39d15f6c6aedd69b05f958e3aa12fbb2277944c455a0d", + "0x6ec24fc092998b496ccc119742c4552d415db85c7377b1a171f954975aaf5f48", + "0x061a29ba95c61f489f8dcc11e9052ced9be6b66b24b20f4f7072b66ded40acc1", + "0x1db8be778ef57fa2b7fe976a3c77116a6884f367654b59df1d82781812a658f3", + "0xda929da23029c9c04024dcc68352b510d21d911ac781efc3588d90df2a0601f5", + "0xc68088f20b90bb5d5fd13c637098c3bbc5a416aa3513fb980eaf8f8e9425d1e7", + "0x72d1599a416a3e584430d50c4d14b91f34f7ef14f132c808def0028a57c9756f", + "0x78b1e5175fb852a04d48f20198c892db5a9acdc4e730940e97e3f54385d29c2e", + "0xb6da060930099d6d154960d73c0efebf0105af23770443e54d396d569123cd02", + "0xd50f6cb28a4153d59afce0f9a1188bc08f0ed24a1d38141b10b4a543f3680196", + "0x389c5e029d346c2a739479194efec6b84b24fcc3fe1a833a549e6141a17ab4da", + "0x2488aa8ba930b371e2a49801b220d02e15f4880cbb7bf71b5243244e8a1ac51e", + "0x30302812b7538724671b0e010d8b30d49913f29ff55931276cd07f1101aa29c0", + "0xc4a8c91079751835d68c54f7797e2c16ffd290d12ca78d2b632e2d1f9219575a", + "0x1a1809450d892c2aad3505a5c2e28691a45efbe34b8837be1216d85c9b3545ed", + "0x5f19a86971a56a2b22271009197105f210742faa5707275f997ef1e6ac8e8153", + "0x705bcbc66400b3f57d80798f6233b5334cd472d3d2efaba7d42733a20754e190", + "0xd035153ee6416cb2486cc2e245ba21b96371354a0b8b1cd45080727dd7e43ac8", + "0x0bf4600821233ae9dc9a8a8522af0c48ce747ff1caa8a5f12f85129955fa6c36", + "0x713038902f066005387e5ad26cac044e4edbc6ffc52bddef1cd2a1005966c5f0", + "0xedaf2690a4d1a6bb433bed47a91b4426466f05561af80b51e55cdef2adb0abc4", + "0x71b4d33ba570ad65c844d005db658b9c6811623b05703101d38b9987dd3ae300", + "0x6dd6156092062a5ea62e17b70c1ea7bcae2b63e306603b6801da2677cdd4e58f", + "0xc868dd95c8ecccc1711478631cbd44bc2148df91a1b38dbc942235fd843c3dc7", + "0x10c84d9fb97d1e8cbccd5cc81e8d734795a0bf6034641286445d6904bb21a936", + "0xaebf2a8aeb738f75bd141a299347c002b3c00144fc07008f012668359d5f1a23", + "0xe7556bcefc28a7bf80a636e028a0f10ce4f056c90a08a8fc1c32af411e847fb3", + "0x81bb53b1b2aea852ca81bf7a65c4d8ae78510eec43d49f17f0cd05d77ffae681", + "0x75a0dec96f0fa321624a353ac31339c75660ae22781aef0dd0b2788ab522a24d", + "0xe3b081f39f11fcd321ccfe69ccabd319d5dc036b950ab2df4fb9b5538192e659", + "0x299a19e5b13a2b4d647df20de53c4cd73812a20a187174a241a814d1405d6f0e", + "0x533a66f5ace9e1566422e6bf3b65edc8e73d7d79153ce1facfe735c9e202cece", + "0xdd2420c915bec5f91a176ef2a14febd2c260d52b1e5cb74f8f30e5be1128cd2e", + "0x00a81352f4ff0dcf5f15e2f853b3ea0f904283f966395f37723f76ab576a8279", + "0x7ff476bd30766e7f1e01fe3f776838ad013a869159f14cbdc9072de9ef979477", + "0xe7ced9998a340991344be1e92c1f231d0300786c8823715aa52e94bc92d056b6", + "0x4b8b9c7ec2b0e7ac7e0462ece6be8e30431ed701d60eb045fd69abd0d583297b", + "0x9b085b2f54b66a353e04f9706ba7716f9100b6367e7cdd843f5daf3a856d8943", + "0x8a1b85165c201b583e44c97fb287294adf9dd853c25ad293992008bc0d9ae511", + "0xddf18674816be13f680e4bfd51aff8dc438a6245e656df8196813efe26153427", + "0x07c35cefa4798aa73bf242cf3ca6679df6974d9951a4fe13e55f3701fe419aaf", + "0x124d270e0b8c3bf1dd8c40d0e08423cbd28adacf95ea42ae7e2d084466897380", + "0xc4c9793bdced1ba9b1aa3cf16a9b17f1fee98fc0eda633359fecdde09eeb1798", + "0x67c7b44a02120601e9dc46afc09ab60cafb1681227d4f052b6dfd54db7e075a4", + "0xfe27f96691158f7e6480ab7fa0bdd8a3f852a6ad012a2f1c15d95ff76929577f", + "0x5165e8459daf4cefa4f724d1a04c62c2b196992cc0c3158b05661af352676e8c", + "0x40ba7d5f0cd2e1409547f3189fd492b5ec9c3f7105f6843638aa3862d5a265cb", + "0x22ed92795680c3c044af56d7defebb14a7aa16a70b8679edd5ef23401639dbb3", + "0x6c678ccdbf38faf5cf7d8729eb407f6f78762683435f580223c52e4391d1f265", + "0xbdc19e8778f7f5770a7e2ca22b12666f09d124abeed21754ef898a7a85e97cfc", + "0x01a74e7c5dcfeebf5c78ff71d9b75fd637725653f4ce6c94f9fa433dbba3df4a", + "0xe4dfd8db98cafbe7c08e9818c6525dec96f5ef3c118e8a2eeabbb76c9cceb4b8", + "0xfcf5b5a656f1e753eb60ec131222382d65bace4a013887ff85ee93c1e3fe02dc", + "0x9c1962c3aa283a5ebcc913f6e9dda8df4d7a5d540ee87d24216892dd51e67949", + "0x528d3a1441daf76afc0eada7011f4341ee6e036094f96807ae6a83d2dbbd39be", + "0x94ffb280cc303a7a31ae28f95cc08088b12071cd87e511df259f386426fa41cd", + "0xd0d1c38812827e586db6baaa98de8d291776fed4dc14f6c86ee230065dded067", + "0x28ea33f9b96f6171719cf52086aac2143802d1fd2834c0cbee5e4727178b39f6", + "0x170db930f1b1cad43a1b8481a05677a41d8f98df397b68805c479bb82fc6aad3", + "0x255a33b189de24aa1cf2b0bc6d0188e336601543de2971f9b67ff38b7dab268c", + "0xef69dac395c3defb1debe0fd0bec48b18fcfb7e4106b41ca2ae053fade4d3d56", + "0xf8fab1c856968bcd8ac7f51950a45cc89ad8867ddebd67ccacd2448d2d238b8c", + "0x6541a8b8c5350b8b99a988393e86327110a25dd641b6a50bb7732194d304ba71", + "0xba3424c169410d8fe25a2e1b7967589663e58bc8b1433e799439c78cbc95e330", + "0xfd64c56c032b9fab0769f87f9a72cdadc39d77aeebc63c18d78fc2d753a24b4d", + "0x4acad6964694e07715de624227cf2c84d46733f687c9c8f88347ebfdc2e96080", + "0x6ac3bf188b35eed7319e8aaf24131978e061ede04a30107925450023c6ba7f1d", + "0x34c23e44f8fb655c1132ed2249c83828e3ab6963d8af1aa7072d43f8ce93581f", + "0x19cd022001ef90b0aec533915dc432ec4aa1eddcff68de4016ab8f41e531f3f4" ] }, "accounts": { @@ -5348,12 +6726,13 @@ "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x4d50f8", - "eip1108_transition": "0xd751a5", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0x4d50f8": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0xd751a5": { + "info": "EIP 1108 transition at block 14_111_141 (0xd751a5)", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -5361,12 +6740,13 @@ "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x4d50f8", - "eip1108_transition": "0xd751a5", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0x4d50f8": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0xd751a5": { + "info": "EIP 1108 transition at block 14_111_141 (0xd751a5)", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -5374,14 +6754,13 @@ "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x4d50f8", - "eip1108_transition": "0xd751a5", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0x4d50f8": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0xd751a5": { + "info": "EIP 1108 transition at block 14_111_141 (0xd751a5)", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } @@ -5402,13 +6781,9 @@ } }, "nodes": [ - "enode://f6e37b943bad3a78cb8589b1798d30d210ffd39cfcd2c8f2de4f098467fd49c667980100d919da7ca46cd50505d30989abda87f0b9339377de13d6592c22caf8@34.198.49.72:30303", "enode://16898006ba2cd4fa8bf9a3dfe32684c178fa861df144bfc21fe800dc4838a03e342056951fa9fd533dcb0be1219e306106442ff2cf1f7e9f8faa5f2fc1a3aa45@116.203.116.241:30303", "enode://2909846f78c37510cc0e306f185323b83bb2209e5ff4fdd279d93c60e3f365e3c6e62ad1d2133ff11f9fd6d23ad9c3dad73bb974d53a22f7d1ac5b7dea79d0b0@3.217.96.11:30303", - "enode://56abaf065581a5985b8c5f4f88bd202526482761ba10be9bfdcd14846dd01f652ec33fde0f8c0fd1db19b59a4c04465681fcef50e11380ca88d25996191c52de@40.71.221.215:30303", - "enode://d07827483dc47b368eaf88454fb04b41b7452cf454e194e2bd4c14f98a3278fed5d819dbecd0d010407fc7688d941ee1e58d4f9c6354d3da3be92f55c17d7ce3@52.166.117.77:30303", - "enode://38e6e7fd416293ed120d567a2675fe078c0205ab0671abf16982ce969823bd1f3443d590c18b321dfae7dcbe1f6ba98ef8702f255c3c9822a188abb82c53adca@51.77.66.187:30303", - "enode://6f289111f7c77c68651b0f4803c3a47bcec801f9c618bb41231a1a24a6dbb9c76f2fdb63ba7a21357c41ebb7f6922c17397c1b5c8f71f7d3ef7965505d4945de@144.217.72.209:30303", - "enode://b6340eb94c3db1362ee517801389fe21cce6354275376b1006f8ce84f8a5cfa2b836268b3727be9db7cd3e581f356f39da39418c4ec1d63d959abc235d99cd86@145.239.7.213:30303" + "enode://740e1c8ea64e71762c71a463a04e2046070a0c9394fcab5891d41301dc473c0cff00ebab5a9bc87fbcb610ab98ac18225ff897bc8b7b38def5975d5ceb0a7d7c@108.61.170.124:30303", + "enode://2909846f78c37510cc0e306f185323b83bb2209e5ff4fdd279d93c60e3f365e3c6e62ad1d2133ff11f9fd6d23ad9c3dad73bb974d53a22f7d1ac5b7dea79d0b0@157.230.31.163:30303" ] } diff --git a/ethcore/res/ethereum/kovan_wasm_test.json b/ethcore/res/ethereum/kovan_wasm_test.json index 54c4a1b83f..70143d8a8d 100644 --- a/ethcore/res/ethereum/kovan_wasm_test.json +++ b/ethcore/res/ethereum/kovan_wasm_test.json @@ -63,12 +63,13 @@ "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": 5067000, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "5067000": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -76,12 +77,13 @@ "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": 5067000, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "5067000": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -89,14 +91,13 @@ "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": 5067000, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "5067000": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/mcip3_test.json b/ethcore/res/ethereum/mcip3_test.json index 8f1426b992..aee04b3657 100644 --- a/ethcore/res/ethereum/mcip3_test.json +++ b/ethcore/res/ethereum/mcip3_test.json @@ -123,12 +123,13 @@ "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at":"0x7fffffffffffff", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0x7fffffffffffff": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -136,12 +137,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at":"0x7fffffffffffff", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0x7fffffffffffff": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -149,14 +151,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at":"0x7fffffffffffff", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0x7fffffffffffff": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/mix.json b/ethcore/res/ethereum/mix.json index f788191c96..ae247a92f8 100644 --- a/ethcore/res/ethereum/mix.json +++ b/ethcore/res/ethereum/mix.json @@ -48,13 +48,23 @@ }, "nodes": [ "enode://aeb6070deb50efeb41c5e4283a6a3b08ff701fef90e3522161c145f30df2852af3dfc51ba74591f7c9d96b11ca4c3c2b354bf58dd243f2d877f6eecc2373fd1d@139.162.15.124:30313", + "enode://cb4b0568607c97a3af4857700152fad9c79908a89319310268c7f49184e3f7e0c88c8d3504696f521c8aafcd00644f9325f76cba9427181b1493ff7ce230df00@139.162.15.124:30303", "enode://e0c926dcdc5c1cf58b2ecba371c577c28c28c91f9b210093178a812389b65e5b53f0e478753b94fceb0b36645b779a915ca57c0c48507fe4d7f786508653656c@74.207.240.177:30313", + "enode://37fe8c0f8d667f110316de3109dcc2e9c9247219998e9ac395db39511bef697f9646f73d86a43da491efce35458f53ac3ac0994b85f7086bbf244809d4e7eb7f@74.207.240.177:30303", "enode://a2a2adb8c12b9b189306050013a44f28db30f92fb3670db9675a049b98b96eb18901d6ff7b961b6e96cfa3923ac29e8f647ef452f0a23ddfef3903ac1cf826af@173.255.195.214:30313", + "enode://ed6fea8e7389bb75ff34846e4aaefa9157c9c79828c6404f4a0ab6b997a613f777aecaed5092c3f51abdf918926c925f7ee4f4cffdb0ac37775d03070a3d6b55@173.255.195.214:30303", "enode://5460fd1ad217941befd0f8d060e6729a0535a0738770aba56827d1313c09aeb68e3098d458aace59faba2c6780b8c9c30cb140b80cd8e30ca3a074ce6d3344d3@50.116.38.52:30313", + "enode://9109e01dec60b67afa5aea77da17e0cb9a716a547469be378eb122c545a3fda9082e553624ff9e673195a715d5628ebf046a6c4ff315e566420e3bdde003bd9a@50.116.38.52:30303", "enode://99fff4ed887d6a6a7b6e03a657c35c06d0eede1909ec289a362bad9d37dd4085886461bbce83aa484ce1327badb3c5958365caa851d71de49dc4530e075b64bc@45.79.128.151:30313", + "enode://4b4c06445175b77ac07eba03ac624f72e4e86065ee8bbdbca5f882a2a333e2608e6ef2ae5b5779816abd8d07ae6ec08f75888edeab3bc06f2b75871feb14afef@45.79.128.151:30303", "enode://fd80e04c75559cfdd9ed8c08ef2c39c5bc95021f7cbaf31acb601914bc7dac7c34b470b90a05e519bc8a8435a46e1ce51053ae07fac31a83567285c34a79c6bf@139.162.224.203:30313", + "enode://d2e678247450c0a7aefdcf03787d4905deda2a6889f02071b241a1309a6bec6ea1c8b1160f69635dee0b4e00cc83656a601b4528cccbd85744b811654ab24b13@139.162.224.203:30303", "enode://4742134a153c108855eb16563424887ed3aa5b6b74e4b713c8e93a10c376d954ff3041442716bdf9ee28fab2ea09f04d07e3366f834ea472c19820b7337eb27a@172.104.130.233:30313", - "enode://799d0a8836e17ef7fcc58b3d5ced5bb1fe474b31a09851f938d381f4556fa8954ca308f6a178d22ed56769a8b878ac8f9cc62c889f9cafab45a3bd4f6024bb29@172.104.68.7:30313" + "enode://9aaeae0129af1bbb2e3590a71cd1ad0c320aa1c03e15c9eb3563cee2d8a7ca43473f43197b6dc0befe5bcef6185196360fa8b4b0d82d8ef11e2ff65553a1efa5@172.104.130.233:30303", + "enode://799d0a8836e17ef7fcc58b3d5ced5bb1fe474b31a09851f938d381f4556fa8954ca308f6a178d22ed56769a8b878ac8f9cc62c889f9cafab45a3bd4f6024bb29@172.104.68.7:30313", + "enode://ab7b1aab2439aafadcb52f8353e60fc1eea55ee5a01b4ddf46ecdeaa2e869c4bf305249757dc74baa78cf05c5d98ffe5c2a008851f08cab6096c78a08dee7c17@172.104.68.7:30303", + "enode://ab4a7fb0963e4951bebbefd2ec09ddac018bb27600a6063d755dfc10be118cbfe3954ada561afbce4cbda6f62364c12902b9e6bdef258aff36b8a73db3c0f161@172.105.16.240:30313", + "enode://30ff1bd89f58a1adae1f2b2ed1e36fa95caf5c2c7e085cf3a49f14705427ca7d7b2508060d058ceda57c708e8aa4661304f3c17eaae4da2e9cea6b64b3893c2f@172.105.16.240:30303" ], "accounts": { "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, @@ -65,12 +75,13 @@ "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": 3000000, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "3000000": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -78,12 +89,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": 3000000, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "3000000": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -91,14 +103,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": 3000000, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "3000000": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index a4be222c16..5db2a4c04b 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -37,7 +37,10 @@ "eip140Transition": "0x4829ba", "eip211Transition": "0x4829ba", "eip214Transition": "0x4829ba", - "eip658Transition": "0x4829ba" + "eip658Transition": "0x4829ba", + "eip145Transition": "0x4c4cbd", + "eip1014Transition": "0x4c4cbd", + "eip1052Transition": "0x4c4cbd" }, "genesis": { "seal": { @@ -75,7 +78,7 @@ "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 } } } }, - "0x0000000000000000000000000000000000000005": { + "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x4829ba", @@ -86,43 +89,44 @@ } } }, - "0x0000000000000000000000000000000000000006": { + "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x4829ba", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0x4829ba": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } }, - "0x0000000000000000000000000000000000000007": { + "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x4829ba", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0x4829ba": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } }, - "0x0000000000000000000000000000000000000008": { + "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x4829ba", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0x4829ba": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/mordor.json b/ethcore/res/ethereum/mordor.json new file mode 100644 index 0000000000..e33c14518f --- /dev/null +++ b/ethcore/res/ethereum/mordor.json @@ -0,0 +1,209 @@ +{ + "name":"Mordor Classic Testnet", + "dataDir":"mordor", + "engine":{ + "Ethash":{ + "params":{ + "minimumDifficulty":"0x20000", + "difficultyBoundDivisor":"0x800", + "durationLimit":"0xd", + "blockReward":"0x4563918244F40000", + "homesteadTransition":"0x0", + "ecip1010PauseTransition":"0x0", + "ecip1010ContinueTransition":"0x0", + "ecip1017EraRounds":"0x1e8480", + "bombDefuseTransition":"0x0", + "eip100bTransition":"0x0" + } + } + }, + "params":{ + "gasLimitBoundDivisor":"0x400", + "accountStartNonce":"0x0", + "maximumExtraDataSize":"0x20", + "minGasLimit":"0x1388", + "networkID":"0x7", + "chainID":"0x3f", + "eip150Transition":"0x0", + "eip160Transition":"0x0", + "eip161abcTransition":"0x0", + "eip161dTransition":"0x0", + "eip155Transition":"0x0", + "maxCodeSize":"0x6000", + "maxCodeSizeTransition":"0x0", + "eip140Transition":"0x0", + "eip211Transition":"0x0", + "eip214Transition":"0x0", + "eip658Transition":"0x0", + "eip145Transition":"0x498bb", + "eip1014Transition":"0x498bb", + "eip1052Transition":"0x498bb", + "eip1283Transition":"0xbe10b", + "eip1344Transition":"0xbe10b", + "eip1706Transition":"0xbe10b", + "eip2028Transition":"0xbe10b", + "eip2200AdvanceTransition": "0xbe10b" + }, + "genesis":{ + "seal":{ + "ethereum":{ + "nonce":"0x0000000000000000", + "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty":"0x20000", + "author":"0x0000000000000000000000000000000000000000", + "timestamp":"0x5d9676db", + "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData":"0x70686f656e697820636869636b656e206162737572642062616e616e61", + "gasLimit":"0x2fefd8" + }, + "nodes":[ + "enode://2b69a3926f36a7748c9021c34050be5e0b64346225e477fe7377070f6289bd363b2be73a06010fd516e6ea3ee90778dd0399bc007bb1281923a79374f842675a@51.15.116.226:30303", + "enode://a59e33ccd2b3e52d578f1fbd70c6f9babda2650f0760d6ff3b37742fdcdfdb3defba5d56d315b40c46b70198c7621e63ffa3f987389c7118634b0fefbbdfa7fd@51.158.191.43:38556", + "enode://5a1399e6ba3268721dd7656530cd81230dbeb950570c6e3ec2a45effc50c032f66633c5eaaa1a49c51ba1849db37a7bef6e402779ad12dc9943f117e058d7760@34.69.121.227:30303", + "enode://03b133f731049e3f7be827339c3759be92778c05e54a1847d178c0fdb56fa168aa1e7e61fc77791a7afdd0328a00318f73c01212eb3f3bbe919f5ce8f5b4a314@192.227.105.4:32000", + "enode://621e28e529146fd501709194885f50540c494f1a2985d1fb4ec8769226b5cb0b0d1a11545926077821474c2767cdd87888ead8a2509a2c9069dd5584e4b1c3b8@10.28.223.8:30000", + "enode://2592745efd35b4be443b8ee25fd2099de132e037951f9f6d3e8805e0a78f213537f71264b973f1a83a57372f57bbe6acac8d6ae678f39393221c045ccbe3b18c@51.15.116.226:30304", + "enode://a59e33ccd2b3e52d578f1fbd70c6f9babda2650f0760d6ff3b37742fdcdfdb3defba5d56d315b40c46b70198c7621e63ffa3f987389c7118634b0fefbbdfa7fd@51.15.116.226:30303", + "enode://f840b007500f50c98ea6f9c9e56dabf4690bbbbb7036d43682c531204341aff8315013547e5bee54117eb22bd3603585ae6bf713d9fa710659533fcab65d5b84@35.238.101.58:30303", + "enode://1813e90a0afdd7c1e4892c5376960e3577a9e6c5a4f86fa405a405c7421a4a1608248d77cc90333842f13d8954d82113dec480cfb76b4fef8cb475157cf4d5f2@10.28.224.3:30000", + "enode://06fdbeb591d26f53b2e7250025fe955ca013431ded930920cf1e3cd1f0c920e9a5e727949d209bc25a07288327b525279b11c5551315c50ff0db483e69fc159b@34.218.225.178:32000" + ], + "accounts":{ + "0x0000000000000000000000000000000000000001":{ + "builtin":{ + "name":"ecrecover", + "pricing":{ + "linear":{ + "base":3000, + "word":0 + } + } + } + }, + "0x0000000000000000000000000000000000000002":{ + "builtin":{ + "name":"sha256", + "pricing":{ + "linear":{ + "base":60, + "word":12 + } + } + } + }, + "0x0000000000000000000000000000000000000003":{ + "builtin":{ + "name":"ripemd160", + "pricing":{ + "linear":{ + "base":600, + "word":120 + } + } + } + }, + "0x0000000000000000000000000000000000000004":{ + "builtin":{ + "name":"identity", + "pricing":{ + "linear":{ + "base":15, + "word":3 + } + } + } + }, + "0x0000000000000000000000000000000000000005":{ + "builtin":{ + "activate_at":"0x0", + "name":"modexp", + "pricing":{ + "modexp":{ + "divisor":20 + } + } + } + }, + "0x0000000000000000000000000000000000000006":{ + "builtin":{ + "name":"alt_bn128_add", + "pricing":{ + "0x0":{ + "price":{ + "alt_bn128_const_operations":{ + "price":500 + } + } + }, + "0xbe10b":{ + "info":"EIP 1108 transition", + "price":{ + "alt_bn128_const_operations":{ + "price":150 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000007":{ + "builtin":{ + "name":"alt_bn128_mul", + "pricing":{ + "0x0":{ + "price":{ + "alt_bn128_const_operations":{ + "price":40000 + } + } + }, + "0xbe10b":{ + "info":"EIP 1108 transition", + "price":{ + "alt_bn128_const_operations":{ + "price":6000 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000008":{ + "builtin":{ + "name":"alt_bn128_pairing", + "pricing":{ + "0x0":{ + "price":{ + "alt_bn128_pairing":{ + "base":100000, + "pair":80000 + } + } + }, + "0xbe10b":{ + "info":"EIP 1108 transition", + "price":{ + "alt_bn128_pairing":{ + "base":45000, + "pair":34000 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "activate_at": "0xbe10b", + "pricing": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } +} diff --git a/ethcore/res/ethereum/musicoin.json b/ethcore/res/ethereum/musicoin.json index a7cc956b47..cc60dc1bd5 100644 --- a/ethcore/res/ethereum/musicoin.json +++ b/ethcore/res/ethereum/musicoin.json @@ -131,12 +131,13 @@ "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at":"0x21e88e", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0x21e88e": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -144,12 +145,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at":"0x21e88e", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0x21e88e": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -157,14 +159,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at":"0x21e88e", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0x21e88e": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/poacore.json b/ethcore/res/ethereum/poacore.json index 0b1f0f4677..9c32922a58 100644 --- a/ethcore/res/ethereum/poacore.json +++ b/ethcore/res/ethereum/poacore.json @@ -37,7 +37,12 @@ "eip658Transition": "0x0", "eip145Transition": 8582254, "eip1014Transition": 8582254, - "eip1052Transition": 8582254 + "eip1052Transition": 8582254, + "eip1283Transition": 12598600, + "eip1344Transition": 12598600, + "eip1706Transition": 12598600, + "eip1884Transition": 12598600, + "eip2028Transition": 12598600 }, "genesis": { "seal": { @@ -49,53 +54,5430 @@ "difficulty": "0x20000", "gasLimit": "0x663BE0" }, + "hardcodedSync": { + "header": "f90247a0772e13a9543c196be67a515d933cff9ff79d8899653483775cdb9d10e2c70b0fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794bc70e7a838ef3d468e25efa5ecb8946d3d0f913ba04672ae73f248d4652e94e918e0bdd27e1560e04d56dffd32fcab89f9b262d294a0f15a0113dee7336d026bad7428a8fd82ada0750eb25239a1e46f6090dfb17e30a06a5fa0d239faa080ed6d45bd6882c64e39942b3bdd7d2cc1cce9ce7aa335f04db901000000002000000000000000000200000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000008000800000000000810000000000000000000000000000004000000100000000020000000000000000042000000000000000000000000001000000000000000000000000000002000000000000000000008000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000200400000000000000040000000000000200000000000000001002000000002000002000000000080000000000000000000000000000000000004000490fffffffffffffffffffffffffffffffe83a6f001837a120083016a16845d7b921c9fde830203028f5061726974792d457468657265756d86312e33312e31826c698412b2506cb841e1c1b52051bfb56087bad8fb9f19175bf8b74e96cd0398e79516568e27199eb51b4003ccf7ded13d783f8b2c897eb96c1a38777367e49e144a1b82908b08d58700", + "totalDifficulty": "3722830991862072821628582432514843280348331923", + "CHTs": [ + "0xbeba2cec156176230bcd2ee41e51130450106b3e75737de6239ecf7725870cce", + "0x7c23422b4834908f67521cc10e438fdfa3fdca51c1784b1067af1268632f9ac1", + "0x5e47882459bdc3a772c6d25d5ac01b8ca22b2652c7af981bfef32836cb797c6d", + "0x03c6d1ad063feff2fad2226e61aae2c9e9ca6ea3d9117c201b0632b891a69e23", + "0x76e1bc738d3d217592a0c12abe160c2b78c17e3ab8d0170c7cc3236abe333c1c", + "0x0fbb954984d4010190a590ca8871eb0a3845200ac314c14a6554b50c9e25ef3e", + "0x9bbc072588873b9c50d2c16e4a7b0a35ce8ff2a8b124a5d6b7514c0375092f5c", + "0x084007f0239344edb3f144babdd0f6d1c7de13d68f1f3c86883f1f11957e087c", + "0x22606868c336b43f3532c68cda37b496bf0321f3491a73b7233b40d2eab1c172", + "0xc2fa3893909817e9be5c33f88c6f247776313c8d2a5e096cb1006ee2a9e9015b", + "0xe4c6ee0dbba11ce908cf0a307fbcf78979a29ef94a7aebb35add47514835a164", + "0xa898211ea2e27a15191568acaea0358f1b4bbce620eee56f9e6e32fa38ab51f6", + "0xf3e51b064d364a914f7a973556650f848deef395b05a39e02815ff12ebfd49d5", + "0xfb4765769a5ad16a9cc4fcf2b7caf0368191277988aaf18f171fa9f22ee34ad3", + "0x3ba76fc6cd568960379b1fb3fc05d94545ef36b5fbd9724485196fbd88e160f2", + "0x86a4b1c94fc8fdcc9cd2e9a65b1905b1c78c29046baa5390734d31e423ee7590", + "0x11d7a031a78fd88f7707946006dc059f026b9be86c3837aa148248c02cfb27b0", + "0xbfc9836b038674254335070be50c0c1a3e223824c410dd2ae9f575e3a1d32411", + "0xbdab01f77ad955a81286af2d8f0662270990117e38d2d4d87af5345e26aefd5d", + "0xbeec9ff0b5926250455d300f9a25d9a8e871982fa8b8cd02f18c3715358405f3", + "0xa1fb55283194287a3592e26acde55cdd8447be541261dafe0e692f74b4fcae4b", + "0x0891a030ad7a045459c0b1699204c3515323bb6a75632b5743b74c5bbb870092", + "0x087e8d45b7b8dfe388606fb31d88fbc0dc4ced5165f2690f43fe2eed57f05466", + "0xe83962cd84a651ad4edcc1e1054937f65b6fabdc8fcb258cf73bd1f5903b6faf", + "0x859a8d02b4cfb48583d8f6dcff1c66c392bd5f228fd7babd98c0f7e6b092dc92", + "0x7bf45599d498d4d6d000d71f254be679697e3146aa7e28ce7ba2108887a2be15", + "0x086c05f0d00c703ec5c675c44292eb23c6690291f661659490a25c56ea7a3a73", + "0x8a8e9e95fdf9b0ba0ccf827f290153c5663bfccf0d2bb945d805f068630d5004", + "0x8cf62dc9faee8c247081458e7052731edcfd5f57010d69055b2ec357e450ee75", + "0xd43370a9593707b72c7dfe56354275bc857688adb71ed4c9d91c8066d5d90663", + "0x74f04fd959c29ba0e2f300251f8e861c65c929ab039f98338cdbe9e6c88f2c73", + "0xc9976a19fadc4427f2a74e650edb642fad40e1e4c7103682d73483c694c5ec81", + "0x17b243d3f8d19224b6db230c092553775ce0aa07560bf8a6c117b68b1db7dde7", + "0x9a0e7e2b16d5606714b741fbe2a2c71bd2ca613783d48fc83b094634ea29a418", + "0x556985f756295621bb13f18965f5760bdd6e7d0bb049e43ae25583d678502a2f", + "0xa61cb3c4dccaed706a0f34d0c2988b360ee9af8191f12ca89b3a99d13aaccfd2", + "0x32b625c248c60c94cc275545b5697fb08d81074a36185570b716246359d82b86", + "0x5cb88865d1bfea3b28afe549fa813255018cf025962cf71ad67476eea2bc63fd", + "0xe5e8f6c9e1c6bdb89a8e618a386a0848eb612ebba6b33a135e72dcc2f03ebdc4", + "0xb80e8f2ef542d4bf4e42e58f79d7f43c69aacda42eb3ff5a0740593911b50ced", + "0x6980b3edfa3cc9b676aaaf96ef0466796d65cfc1c284e87533ce174454197038", + "0x9a665d2363572b822ef959b93ff3c3d3de810b91906a13e8133ad5527d228833", + "0x0a94203dc4e1eb634641fa2fd26eaf768a2e4a94dc2da2448939f916cf86b8c4", + "0x7ee1b9d2c4db5f1ad581cc79cedcdaa45053b2c43fce354a28a8566b479c9bae", + "0xa73b4dde849c3f7f6826b7508e133b836a38c369629c5279ffe4276bfddd2dda", + "0x3d4f7325c6c29b993b6499e6cb878c2c474c017fe957dae50360d2daff75cc37", + "0x8375caba3c2a74d97fa56c06793344efeeb625c7f9389504a47c6ca1da06c823", + "0xcb3c99ece7b047b66df7c235f222e54d66236e52a8e8cbbfa1d6d997996a0910", + "0xafe76ed7cd71890eca89136a0374182f5b281f95d7df4164afc05452e8e672bc", + "0x3a9963132d5cd55354385a378c7d6195b2af2d82711be3b57d8b7837b9f52f45", + "0x11ad3c5f5e8f1f39113ff798a071c0a3c7a9238356904d221de5ea1f60af4be8", + "0x1f714ef2add9bf55f336051bb602595d3241c29eb1caeda722051d7a990fde58", + "0xed706f831ecd5fa9a63fb5b30202e4a56cc709af19f373ac5b678306db12c57e", + "0x0afc268def8531da0596cf4481b631c4ac0116e5832ce4d48e8c7c643165cfaa", + "0x6f761afad3b50da8f4d9a61708aba9a3c8d4dd4cd53a95903075039613a7e75d", + "0xdbe9205a2dd2845184074671b55b7d8e031d7684e67ef0d3fe0392463600f525", + "0x5546707e9424adfa3bd0dca75b298e58a4d942c846333bcba5765507bba5d731", + "0x527dbd7c201b06ff8378655af5c72a07fe136b39772185b67c8deedb63e1f379", + "0xf068c4433273f3f4c98ff13b7f1b052155786ef43695a8dd1147891bfc55934a", + "0x036fdec6618960896451053b0f2cb8793fbd3e26ef358d8baac71edfcc0da213", + "0x5ed631e36c8ad52d25ad3cbc40c5eb2db452dc9db962509731111fe1a52a08af", + "0xb54a9af9a872b4c5a65e1fe56c6f2f1595f346ffffe1ff063febbe9b0752d515", + "0x57b184f94d894e82789e5939d5e1c7bb268400d8a5b5f4700d2bb6a8431a5557", + "0x8cd7b549719e7f3ba77521327b547e25d0650248fe53cda8f686bbe3903120e7", + "0x1db027c57e64aa7b563daaba00585ed5000810bd7e7411418eb588a963f7fdb1", + "0x38bca857c3822e9efeebe5e4f96f81948c2a90a8488636aa01b0ce5682af2928", + "0x136ed40ed8835f960da251cfe26b7f3c24468c4af0ce0eb657358452329441ba", + "0xee2d0d47030a9a14b5f59f4606b61d5d612382fd39d280ccc48b8a089c3eb510", + "0xca54472d602a0aa44c4aafd681d49fa0c604dbb31ab490824f8811c0ce393ebc", + "0x6904577370c7551df0073a90e1bb93b3ef3507148085bd0a5188b058647c8e3e", + "0xe968e61835e335851ca1b22ab32c6a3f9c7745f282b9e1104b11395c9fa56811", + "0x3e1be7761b008d6c177f44a2349877d2eb4ad8a2634b3634224db49acecc7eb0", + "0x89153b1ce6636340ef2c43c3cc35a06507765bef3e1b18366b93bb976badcf80", + "0xdde668d5d0908e29296934d0f5e31ab143f28d59ceeb9782daff636fd8516cd1", + "0xa3d20bf5601f796b4726fa07e4f82429a77cf50fcbe3f99dff682024374a2ef1", + "0x7d58770971c35e0e194d1960453b38e17bb2c57593f2409ee4ccb39597b789a8", + "0x5477a9229b708c90079eec8e8363af7102394cc0aa67dc25071d25031ab36cd7", + "0xa8d3491449565dea94c24c4ff9bc07187b21c124aec5f63fcc6350258b2f9c8e", + "0x8e60fe8f278adc5ac6b7728f63ee4d7fb436a3a9935739b5245c50fe3720fe2f", + "0x0ba6024fa69b38ee3c513d66067b6154387622eff71e81b44524cd407c68fe73", + "0x9005c9d1043686f2f7292de3e02bd98d70ec0bada3395c28fc7b4f6e2b0d454d", + "0x9cc7c8f33af4fb313b525eba91660087a9604c4f88df634a28ead9af43d46e80", + "0xa7a791fcb85cf8dbed772993af9e6eea2be6b7ae76f382158fd9d5dcbdbd2402", + "0x76db5292d258b479ab2f110b0819e520117d80ca41b5d5ad6bc4b48972712474", + "0xabde0a098d3f0678b3ef61b8551e1cefccc0fd0b5052937ad1d636042063957c", + "0x9202d485a5da388d71b9a31b70cbff31c18411122424f4645ffb7ded487ac2a9", + "0x8c1678956a159b4214de11b83eb59d36dd6f2c97a2442e116baffe3f83fb36a1", + "0x3e8bbcf2a73d075a36570a6e160088c909bd928fbcb6f3f5a938be1efef05c77", + "0xd0aba2a599c8db2d82a90371b54b629bc2d10f10e2882aa45e72bcee3306c329", + "0x82b76a2a4667587558bb53354709873cbef50b31d7f1a5c7b46e29d3e3155817", + "0x9fb8be199bf0d9ac112b89c82663b7b40de88a1b51b50813b031abd9f0266c61", + "0xaacb8fb9f6085bcb04c87f3bf1e9edb640d556bc857eb88dceafd512a8da4cee", + "0x1a61ce438fbc1ea4bfaa736bb8caf40d6881634360e51ad180de11f61eeec0b4", + "0x0d15e774f7493f57d66ead1faf73b808f0b1649ec7a0973d417206076e0eaca6", + "0xd346f01969d7b468e7902418ae8af147aa9f318ffa0153fac75a15729e679064", + "0x208543e69f0746dc2262848dd26d42036eae8b17c2438582810d0387e53f8673", + "0xaaa93a439e56726586b427ab1405ba176c5f8716aa9dc3e49c9ac691be5aed68", + "0x1240c030aee11a78e395ce16dc7bfcddd8606341d13d12d26e1a7c6506e04a7c", + "0x549574bc1555309df269673db8e9a735eea72e0a7062764d9e565d0ae6221b5c", + "0x9571476a35c0ff16f7e67ab51789c8fe9712e1e36d3822b8506ab68e7ea51ffa", + "0x3c89aefa956e7d5a9a11c2964c9bfe75bf80dd6861d34e90d86677776ca53489", + "0xa40b5481203d276f7ed9ff08ef861d8f32b6561c493b78c9d3a985fd2a9e8ec2", + "0x5e6befe42edc8539e8052b66c9d872b730b168b65050fb92d29b6f74ccdd9a07", + "0x7d0dedcfd7c99088705705ff51e8a3ac5ce68a7f19058a9c96967c80e4d671bf", + "0x498751025f00cb41a130540708e13e6e29de1fbf5b59ec4fc01f6fdc2206fe0d", + "0xe6d10e1dbc1e00c5accf69b897d80b7ccad9df9aa6a761616a654720cd13b4f8", + "0x9e46ee932e1ba6a836fae57ea6f7826ffb1c3c88d0e06ccb34c00256820d81d4", + "0xcd77fbfceda276f629d8f13b027392b70248baad442f43aa08c2d25a6d0982d0", + "0xa37a4104a05ac528624931744e8999ccf1ac48ab9caae952f8b177d554cee1b1", + "0x499ad82088bb1e6202027f91f884321ad740d08939e1fdf339f677a0cfddf75b", + "0x37f8f1df896d8e3b70c22b09d71aefde48885240bc3afe5c02b91090620afcc5", + "0xdfd6adbeade977218afa35e28605911362f143e1e06de64d2f02cd32759e4eb9", + "0x4b4cf1e596a709cef6df86b61e76b4439344b918e46199ab3ec18de5b7328819", + "0x237c247d64c4c8b680cbeb5d8818b28394eec5b852cef030af4b4cf3e9c2cbaa", + "0xdae079d732c3daa8301349e23795883c63cb013c822fa1f22d9647008e45fe5b", + "0xc03e9f61790c1ff2c9cc3b9dec6b2ecdeb5daf35ee71692ff6b5d218f6e41350", + "0xcd21d18ca07e5bc7e86fc8f92be6d5aa00c473c42e8c2abe2ff5e0375238b78c", + "0xe362ad7f0d425b8f0c229ebb30ca8e81580302f958df4c8c9277ddeeba1f9700", + "0x4a5b1552a270b94ad50179f2f37677974b70530f39fd5324b9a3e49e86c7ec49", + "0x3ff44161157235326a1c267191d81b1e2c47da707e43b905492988db2d81f5c5", + "0x3d3fbbaeabbae89351d2044609f8e1d89ff548ba9ad979c87faa64f7b405eae0", + "0x74f8b109c405aac4168c8ed10f18e3e8e253bbe481fd2e998d53333226e63795", + "0x982e1ad6877b72fa1b8a75fc062296b5cf5a886d0b50850c85132706fc2fdd24", + "0x2ba0fd8fdcab39007b8103eea8af00500e79b0a3368f173519b71b317dc93516", + "0x9fda742c4478f10a767c144217fad15a98eda80d0519638956bf6222cf0c2d55", + "0x826b0e43d3e91c9ac42a28e69591084a2ebfba2d60464567357954540c891494", + "0xc103aae906b92cc8d934bcabdb4043d8c8fc3c0e49ed6efe57292321e255b861", + "0xc388d46f79a3154552e2d7dd296feceb0c13e6ce42f20e8abf7d2ce04f5cd370", + "0x79175320382e78512b8188aae504bfb77741af84479c0f020e9b16e0b9653cdc", + "0xc8603304742b1bba1ed148fad24f463f7a2c225de840f165e1750f22d474dade", + "0xa7f4e5e1d33ee2709e9cbf0c45acef17efa6fcc7fbfdd7d8482592c0312c9787", + "0x24cb67d164ddeb37787f72b2339b5d46dd524b34b71a29fd0be30ba5270b97da", + "0x54a18b5471fd7df2fbd748f3cc92551100dbb2f9b26b51318dd8f04864cce396", + "0xdd1f2fe3d17afab0195128ded465395769177b3418d1b33ecc2225b5bc97f5be", + "0xc67c9f8be3dd28ce7b87ce19bf0afb6e12e235cacacae0ac10f3d7841e3dd190", + "0x1c3a755e4b3c7070b5b182c12744f0c76b6ab035621bca25c4536523799f2235", + "0x29055090819fd2b38c6fc9dc05f172f17d8c15e8354ff722e0e024a52f770cbf", + "0xf70003b282489cef78035e5f18f9a3d4d1b6c02ca6eea41955a3a12debff06e5", + "0x2d344a810f9303ab645aa4aecb69dee399ab63d790b6620b479b724b96f2fe5b", + "0x8d96456119d7381741ed450ef9b842193a42f5c96ad976b655110d279b39de6a", + "0x1f01cae6cb4388008221d1e0421d6ed44a3eed477e00b5277251c07901412913", + "0x658c4c414a90e7c2409992b1db1c1fc324e2c88061d0db5072ffe4722deb0de2", + "0xea8b36d1fdcd45bf802a3f6ce26ab8e616630e4803239f15392afe346b39080d", + "0x8c5f727fb54a6f2cea86f9419313c0fa4a2199632cea72ecfa5ae5c00cee9cfe", + "0xeab38eba363465e7bae51c0f285b2ee14ec64d7496325614eef687a3f233572a", + "0xc8ffbf275118b1a54bd1579daceff9aa1307cabd60c3b44425e84dc41b7e356e", + "0x63ccc4cd81ad0aa962810fe1f9e6256dc682effa5a3e045012e511840c369f26", + "0x85c560e131fec92ce7a3a8881ffb9f4074f3d03da2bbf760a188340b2549936f", + "0x920f3f9d3365db81c4128e3e241d84564b7eb978a981f10fec1489e43412bef4", + "0xdc237539d58784ae17a12b53668f2fa6b7702eee7bab4b78938a3ef9203d395a", + "0x9471f06aaaa0520e74e662e5d622cbcbd033a23d604c546fe1329233f8f51130", + "0xe77d055fbbe9187fb44d7582a58417ae7802d29344847d86bc7523e7a4ee411a", + "0x507d10430b1d05e99f82ce2cef5e822fda9a7e3f299c8d990faf39ff22914374", + "0xd01d9d1ba2f368154055b41df3a5d377f9eb919d3e2b9ee534f39da7da78c192", + "0xdb55213f129b06153c60d02d68a0ed53f04d3c33ec69dae965d1f7c75c5ec0d0", + "0x7459d73d4fc5f16e388f32cbfe19ec1b6f48205e5ad8120fa795321b85ffdbb0", + "0x777d90bb25ee206da8de8bc9ae20080da1c4843ef5b1534e9951f1e0e9cbc4bf", + "0xb747109d40c3ffbde1201f588464f4d071a62dc55f91c34edf8a1fb3bb878552", + "0xb9c610049a7886a9e41ab6edb51316d6acf5f40a1c9129dabc114c56e59a4437", + "0xfed2235b9e5b38ab1347d8b2584187a218f7977dad71582ceb5733d9dc1a47ab", + "0xc76ea84da80d2f1fe6d4a46fa47c18ef118f4d33643d81f1054bcd1c9d4758a2", + "0x81cfc27a976a19899f289ac3e4fc13be4ff579fdb9a84f1e612fc83e2125bfbd", + "0xacf4161e0c53ffc325bd48449bd04795535d76daed330f4b1fd819a1a2e02d06", + "0x62acac43eea1306c74c1d62f5375351c4323d90ad03f0ab95cc1c50e658fb30a", + "0x48aa4f3f5d0970a700da2a20ba423b63f32b7adf33b9e86a9e4722ff64cd144c", + "0x01d114a63e51e5aa80cf8dc7ad199cfd0cf14cf287b2e32ecf253943fde364ef", + "0x1efcd47488f594352dcb5f89e9df03c3d6ffeded909ecdd171b60203a4e54ed1", + "0x4ded59a384104c2ac23d303d3dbdac44d8c3ec545e0b9aa87435a6b5d89836ae", + "0x7c59b154d1f2010009beae7662ac8191b0f423cdfd1227f195fe5a141db77bd7", + "0x4c6dbfa7cbe440167b2d3404bab8a405e847976bbf3b22db07224e40f1929ab3", + "0x2db4308cf0cc391b4fbf8ed491af97fa78a5fa7b779c5e3a897e8f2a763e4883", + "0xc2d1122b00a41296090cc90435f092e50faeb7ffc6b141e6a5110289b3428fbb", + "0xdecec944fc7b06bbaf8aa9d5def7b73b44dd11670e1dc1e8cab715e2cc46becb", + "0xf53037f0f7a7fdda32fd1b42e2f3e1d568d349fbfe763f586ca67d901419b04b", + "0x0c5929a16e26f8eb1a23b1a0b178ddde1f981131b807d8cbd9f7720417ad995e", + "0xf07993002ec5b4ac7f6675888e56d7dab6b371e3424cf3a1d7a80ee18031dd44", + "0x54108d33b815ee1f6f132a1f4fa145331174da60f5993b009c9863c170800921", + "0x889edd7a783809aa067248b26940c14a5d049fa64bbb795e08cda8d0a211ae9e", + "0x07f40dd31cf0125a6743397542fa0f3bd228f52081b37f80a65474b610c05f45", + "0x5617cebf76252b916d6a9157e990d3129e2cdee129d458dc2af829aec97d51ce", + "0x224446148a8e6e5c52ddfff2f70c4405dcd8281dc500ee4e2d215041a5b9eb63", + "0x8301601e343015dbdd59c12e2012e97b4606ae260818e3dc5dfbd0b355d68214", + "0xdda7bd5086b42306ee0751af7f481de9b3d421636e30aaad7ec17448f407eb5d", + "0x705fd3b064e5b48dd22c8be2ee27f7fedf091b77ac14bcdeba08ef0ef4fd6125", + "0xc14bb234364a11d84a3798e8521df13380049c4a79dab8c02b39d2ef07a86b1d", + "0x3d09c790b0d0c000a26c7972a6926f5deb169b6854af5d10628f77b79229cab6", + "0x1828bcec298ca4fceeda13e81e4f4c40c5fd1325d8ade5607fd8ffea47cee66f", + "0x5055f55a426584571e870b5b7c74e63f41825f7f326b0febb26702142262b2fa", + "0x5e370d8fdf2985addad8efef7a705b6e42e426777726cdbc232a3e96f4d8072f", + "0x0754d467a4eab81964fdb91485d71491ca5cbb369e74267e0e83eb77aee242fd", + "0xdece519690f92a05092f380a5c7efe638ecf21c0334593fe703483ff33ac6810", + "0x014397ad2a2a3c1eb352b1225ccda7c56ae5a17f6a29edd8ceb1d9608c089899", + "0x883b977cbe03bd5903b91521cce4451847713c6994d6340c22888d5ef1e9fe60", + "0x6a6242f06a86a9c7a0763d41085c093c25df879f76845af6002572f2fce2aec5", + "0x0e0db4bd875ebda249da38b39e3b6fddb856fd253a06f055baed8cb67cc3209e", + "0xc83c0befedbc4f706292e5fdb87a76f06aa1f482072696a51ddc3be48d7a4da7", + "0xf0e7ac21e97792148f0f08cb0f1cfaf8231c30692cef8f6626a3d64d84c4986d", + "0x7da5bdc9e770a5ff14d602c3c65ff12d3a9d86c5eefb1b706befdc9c5598d92c", + "0xa1076b96f749dbf1bbc6589e207fda2b512deb8aaae9012c174270acf1a69b8d", + "0xd2f2f7549ec9b51b727f2eb62fc5cb6300787eb77acbc8f1a187036b7097587e", + "0x9680912c5f37de20ad48a397eb0422a8609240078c448486ec4a50ed372519a7", + "0xaf0d49aa9c3e7945b8825251b4c7229d2970595659fb2081bbe80b834f4d4d6e", + "0xd28a28b5bf9fad059e93cb968b3fe6a0cca1093b287133a734820937310db757", + "0x979d0e67942c13b00e5734b0b05228b617718f8755bca78234b56385c98ddce2", + "0x56bf6a5b77be42628cdef10d24cd3db7f8e4e0f343a3fae1ff2eb688bd78a001", + "0x1f2fe3becaeac6d81331aa6507fa864450eb322bad72242639e39f192f74bb11", + "0x44ed23b7916c1cfa471d7e52344163692ac4a3f4060c660b97196036f7972282", + "0x84b25637b40dad06253b35c1608d5d203e0fbb3e0a68f8eb4dacafcdd9f39ff7", + "0x9d7283f8f0ffdd3582272a86ae98022b854819a26d09ce2fc79948c08e00f768", + "0x933e3a6a43a73734ff36082a35627f39962c78b145dceb11756145a9d8c27ff2", + "0x518491b4c1dde394d35b97dbdb5d8ced97cefaefb7c375f564bb899ffc5b3fec", + "0xc1f4158a783457531748fc7c87d732f39f7057e19f0ab692999fd8bef20f9814", + "0x2f9f8bcd0ba9e37fcdb31151386ce1c5d19e68db979fa627d593df79b044ae84", + "0x22128bc17ebc5dc9d35754e776c9711a25612a908a6357c63b9656523955c2bc", + "0x55b41ac2329111dd9739a190e3c1d0ab4be453f3ae51a2f99e361c6e9e7a0409", + "0xad4d7fe202366e1486ff5041daa78e7b306571bcbd9dc40bbd94a5510744f1d9", + "0xa2d5668736309e8b213de44a4d4e2c2f46ad8293a55f5e35b8fef80e760ac525", + "0x7961a8872702533e22961ef2fe6f0a0bb950450655bd24714818647790cf0260", + "0xbcd61366a0206b5b102ecbbac699ab01e54af1123747c420a9ebe26967abf92a", + "0x7b7104cc03dac5045f68c1964528cc4ac18be0d5ca5c4422fb2a9aa3fc6f2eb9", + "0x66095b052c2af69bc938bb1b563726a374a1d3150d237d40d21ade875dca2d7e", + "0x175d02a53eeb37df9573001e9c6cda4888d0237aecb0a0734e4d013bdea88df2", + "0x5913e4af24a7fca82a1b96a3b06504cdb8385996a08fd909d248e5882530aa40", + "0x17746c96d1ef0b373836ad6cc8eb0b95dfc4803a650d1b4e5424916b2e4803c1", + "0xf10e70bcdaf395a13170f86383a08c37174f0929c4a28f3702c3e1c62cdfe993", + "0xb8ff9bda9450b029191fb718168ada1d8a7a0a64b78e585ad38e32e5f55668b0", + "0x3de86ccc8da88f0aeed46026a2096e3d0d0b49d0826a530a18a99bb17dd6661f", + "0xa91dd2b27e2eb5bd20ba5966f115d3396393d58208072e23d6bc6acab63637b3", + "0xbcb94aff327134d0068f91a14bf1305c336b70567372fbe9484cd919a27841f6", + "0x5493507591606359410cdea0314d98b5f1e3de853924a8bdc09e7d540878c15d", + "0x16b5f72af984be27533e6ae2f2509e09aac5a4d09b791d20a1494f4bdb3ffa69", + "0x10ae30f1375c596cc82129e73afd727b7e83f5e13fac097ea32c1f58ce225d84", + "0x598bb4e84bfa5c19f630126dc4d09e898bc8dc955392a6f13715bdcb5b1dabe5", + "0xae20f54d79013c018da2f5b1e8f02f9344430870e63cd6119d1454a584be6813", + "0x7b6f59eb6b5df27f9bd5ff955c163855dc728f01a57e06a03acefb07b4dfa086", + "0x3cc7114ee1bd0da28eadcba30e1bde9f52aa34b72a642a1f15f36e5a83a7ee3f", + "0xb77635e45bbcd91213805ef7b190b25f30ae7953ad354caa38920bc75915dfa5", + "0x784a451be66598f93fb7cc66de0f09c2089b2ca04da65055b09481dd539dc257", + "0x8547ca34594628e0cca6761fc2c2a2a39697c8ee462b09df08b9ecae1b288192", + "0xf625300d5f490805668ebab398e8379a1b539d08d4c1d050bed9f4eed45e9dc0", + "0x375cfba62d3b34ead13aaabf8a473d9c8934af535dd52d1dfdcc34ed1c32b7ac", + "0x82f193812131a205f7f7fa54ab57d834ec143d7655fbbfb171d9a9e7b27b07c5", + "0x775fdee0f80e286b9d3848898bbdaeb0292dad49d35f388f4011266727b30b62", + "0xa4d08ef5d7c9b46a911eae711beeb62b65b61e104047bf90434baef9c5b77f7d", + "0x770527bbc1bec8c43ea1e8b9006f039db3fedd80d31ea2468e34157ce3b2b897", + "0x6835281dd5aacd354785dcd385c8436ccc35313f8cf91c533e06e02845a51a95", + "0x3cd07569058493bb5fd053895ca30ce58f311498429948bb789e13670002c6ba", + "0x751b16295147e71adffed0a31aa1513ce2d726bf06ea0f4c7ae8e87ec05d3cd1", + "0xe62602b56a500168fa91b167172904404b18a6eacd5b4a46daeb509d3b133dda", + "0x61300b8b4925ad706499cf9156ab21b652e5baab841a18e89e1895d5aa5f2362", + "0x4db47a38bac93e9517b47ee6032fc3aaa450673e4cce937976576eb4bbdc421a", + "0xd5b31cc02a7282fbb95ce20722926b0c5ce6bba1429e690582665bea31053830", + "0x7ab0f9901bbf700345225911a416c3b4a0005e3acff3040c4dbfb86a464fdacc", + "0x1c146b732cf728e0eaccf40a3dd8c53fdb1665faadcf87a2f36458c92b59eb46", + "0xa4a5d87f7d955b06900c398dbf28a8d93ad7952b46627f9a15b7a5e3626ae8b0", + "0xadab680ad4fa0b7c1b46db92a1ffff9ac45e99fced1e067bef52de5bf5cde3df", + "0x476ef491b9849c28ad1a0e15625657b90fdc3bca8909c66f97abe4108dc55370", + "0x8041dae2da7424e828ec047ff0c0aa29300271b1114665d3c6db85f7ddeafdd3", + "0x8fea91c70f26beffaeafe0501e9ee06db1e7f43ba09f272647db803d6a2981c4", + "0xf297a9374979053bccc5a03a30415b3e6cbc0d09c43849574a6182bc8da06fba", + "0xde26d6068e69d5d3ca60e40cac41676fb7a3fa39d3816a70ca5afb903ae5163b", + "0x18d798c71a96c3b4629e70c26b0ca180a2e82da82858daf249c46cfe29dfa842", + "0x023a78d031c88108723efa53889e6b589741ca2ebd3327baa813edd6760dda01", + "0x65d6925c20fa5a5545aeb2a633bcf35291706254cdee0f342f4e582a84e5c342", + "0xc057bf7a4c5c496cf2750faf20dad6261fc4673a529f5ae66c64504e56f8323c", + "0xdcd7a2c1f04dd745dc15e1ab0d460e666ac0256a811ebaa95e0ecf8adff0e994", + "0xf52f526985c15e24d60d3e9f37618bf421536a0155d1b5ddcb1e6565c1e5d45d", + "0x7a1029d511972d2869c7f11dac44ca1065764beac400ffebe4111e13201d64a5", + "0xe3643156f874d66ec4b6340dd30497a7711d2235bfebeb88314d9b86aa09d4e9", + "0xd6fdf69c1624897b4afaf7e63b3c8fcd3d84a4a97800fb0340e553b2ce929885", + "0x6735fda836b1cb755603046d0c3c7e87a39c17c3c61cd118a5352de13f7bd87c", + "0x560c28d63f900f72a87f796cbd00ff99d0aa9189439b6f994a32a8b29b077227", + "0xaff016ea51ec5cb25de8951778146dcb1e0d9a592a5b55e4ae4c9aba406f43f3", + "0xd9cf0c95dfdf1fc2ec17595e6948810cd8c515c3f8e7e9d6783f19257becc83d", + "0x433702e75f8cae12de6d3967d5d88c95338d13c174e8732be3adce1d8f2d12bf", + "0x073de52dca97a7d4f30ef742fd551d6481d682bba9ef4594d0632975d61509fb", + "0xc6e9ccd03aa0a04c33a7527a20ae4524bc95a56fcb9564be2a25fcbb19b34740", + "0x39bc0fa77938bfc6e0bfd53b30fe921fc012f6d5c2e65a76b002878efec8ffc9", + "0x3aac180abc694dea4f4479aeeb0fdc2b90356bc0abadf45171600f639d39a81f", + "0xa3f8ce4863cbbde983530c0f7795b9b99a5275ed22e670017c99e3eaba3bc99f", + "0x4953e1d2a5c86f5c1c195604c20fea966260e8bc750ae0f3114781d3322295b2", + "0xc6e6ac47bacb51700027ed74e011644dc590ed42ba4402073970dd860b72122a", + "0x1bd344a7634f7b27672421622145570df1bb14df58bf8b8d221672cb27a9b4e4", + "0x58ddef9eee56d267c81025c96c38a8f9bf25f735a0efc5e673109260eef4aef2", + "0x6bb0d7a956f40c38979bb6a26749fd16a5c6b8863ebc00cb025e8fe21ad4f908", + "0x97e098f6f0e2d41a0c078d65b7677dd92f0be54161636416e2f82d2c82a166a4", + "0xe6fb0a5f1a2fb7f54d1ae83c30fc555a10df04e1ce27fe37e41b460dde03659e", + "0xd516cb4b0d165e6388f3fef464e172ea59ace3c5f1b1e05ba756efd418f46663", + "0x038ad21377a1f98e214ec6d92723d4ea96e6ef46372449f39bddeebbe394b999", + "0xb3734c648b7ca75281316ca5d3fb94630a2b1cb2f6e68f559e46aa07c082202e", + "0x599d08b5e044379fe076c04990addde76f6d59a46aabd705cc066fa8ce94d24f", + "0xa0c68af4a92b75afcfed3a02b95246125adfe323e04c6ebde819832c3ec588d1", + "0x7e210be9a24277f74672c2c3f9ee29e440ca0472b2da409d2b306c68bda2b443", + "0x4a9a6804c056152ba3a5ba907feaddcd25153304cb0ca9061ae3bd75cfb4f2db", + "0x36cf31cf03cca50cd54aa8cf18fcd1f2d0fe058ee27feb1ff0d5c030ba22da34", + "0xd31731ba760606e171ec4ac4ee44286ab514ddd075d619218e24cbadc26431d3", + "0xc1b1d9e9589625db13329b4b8730755f575bb20ccd4be2167d365bbe47cd15fa", + "0x4ed09d26e3907fd63854518ae199066154a89f3144c55235acc831f855612edb", + "0x6e66073ffffe2ff0caa4e16977b4186bfc283727bf1ec519ff891b96fbda8cba", + "0x38129481c656c59307591cfdb2333e63b24bef0a2a02a7be9e17a4c41e36688e", + "0x1edce46573bad72e5bb3ee5b3e897e37f40c7f20a188859e78f2e497eaa110b1", + "0x5bc4335407b1dcc1fb5b5790037473ed02259327b0aaf93c55472dec72e80736", + "0xafbfc555312c53ec0df601baba81e0bf1342e6d0afe8e02894635ae7406e2177", + "0x4999cd89c28507c5c45cafdf0f80f0f96daf8a161b2c43f4c3369fe959d07317", + "0xe0a71d374e4d179d47bebc00c2f3f0f199c050b94b853582686ffad9ba8dfad5", + "0xe4ee6cb57c90b49e1c1df1125cc901c115d41cfd7b9947614a06f9f02fbd10c3", + "0x38d0902078528299400998a8adb568b15d1c41b2b279516374e8a501a461f2bb", + "0xce3119c0c8a011c3d947e681bf26a68be3abe7d81afc6a155bda75e527908c7b", + "0x2d9c84e21edd950c38e063d38c49251253af6883c9bb06f992e2b4c477959016", + "0x27f5098a7c964e310bd752a7ad7d333c25a24c19f4154326b3f8b10b9ea4a185", + "0xde140f7549e4e89ab5a604d200082b00ce92adc0198fed38b42c592ab274887b", + "0xcfed06a3d9e131b016de9f9ab2108005f2fb00582921b26f646bd1e93b884be6", + "0x71f658b69cdfa76148f43f80f633746c77406ba560014a3d8582eba565921f11", + "0xdf7bacfaa7ffa2b02a76573d840aef22d514d76f1f4edbbee854047599e0161f", + "0x93ca4673ae2c1a809d40e66b06ebb576999b1254af9638f0e4f55c7381dce428", + "0x895b28618538d30060a67aa2efc9fac26337e7d3915f3c8a4bc28fb30a429df2", + "0x0f4a761751fcb30506da5d58681f16ececa5789a2da356d736debd860fd52c0f", + "0x2c562ef81f7b8d1e6828e90a26cda7f109c2bd0adcd0ca3b0a4b3aa95c6300c7", + "0x6c47ff3730cbfe4ebbf66fee806f2980ce8555cfe971382711355a376bddfdf2", + "0x9df23bc8294f052c6d9126bb76480831c5b9875d0ce96178d9cef1293cec25cb", + "0xae42d54fd88f65a13d6636230b2090c481c42685b982c014c704c06cb1733081", + "0xc371229c995ccc1cb8ef57ec2b50ba062a55170a2adc77faca6f22d1f31429c5", + "0xa4f1fa1b123f6995c8abf31269c70deae3e040a9ae17dfcaa4c10b9801f8e0fb", + "0x88526ed25e0cfbd650299ea212f1e6a28d047849337ee971cc4d3aa5388cedcb", + "0xd383effdf5dbc31395689e22e1d40d347e6427b1562e0c38ed74d79f945eb11e", + "0x8cd560c385aa7471576f21b214f4805e14cd34202a2ebba2a6e5466901912df2", + "0x669ed4075d27b332b02c32f9d5131b79b145614a2c2b790643a28c0c2caea4ab", + "0xacedc75ad8a371863f7f801650d0a96d5b2ae8b7011bee7b9e4dce2abb4adba4", + "0x5f5853317cdd372460c9bbe282fe5c3068576d253c46128e6889ccf64df042d5", + "0xe1b3264219c5342e3bbc7ec53fcca1b600760d14727e5307bcb30d77cfa246f7", + "0x6a75b7229ba5475a5e24b90a55fdf0dd516849709f64ab0703e03cbc03ddae13", + "0x878b56fa00c776f84e52e5286540933efeda4b480ac0ae80a013c8b4561acc8b", + "0x82637e04ad53a6499ac1538686405e160123ba2a6606eceade6e66d9d52774a1", + "0x10c04e9f6e3d61a3c687b68f8b418163d2c9d0b7403bab32ac83b79340ebca58", + "0xc61277793a259f51989c33d9a3b4e86d2cb2b3bc03f32f3b9401dc31182de341", + "0xa8cc21ff4fa8a00e2ded9c5791462527694a34851ff1c9d4f74431c8fa2198b0", + "0x52083ca675f2d3f9bdbb2a32ee701c0a7d01aa3699eca8badc5e51cc7b81b8b2", + "0x2e0e3dc9588fd0f08f691be14e7d8c77af4b7a583a59145c7ea54a4a83803a24", + "0x8435ee4ab2f6842367226476a2099ed5a06adeb81aaa51d31a4fdf7118147f84", + "0x034392edb9dcf86ea008df61464f02bee747035324e9ff1fab6b0117b0f5904f", + "0x803259fe309f8761e5d1edf7b965bdf9bac66408fc397019d739b5f4a313fe01", + "0xfe83647653f5485af16ef7823e3d757d0266be25ef5da1fca2e5117a7221d66e", + "0xfc230fff12e5b397a09a3fad1eb4f401934a440020078ebc4349a0518a3f2d95", + "0x7dbdad94ecd3030ba929668f81c3688aee5f5ce460527f7b36db9bccb815387e", + "0xb3bbe0c352086bfdabf1b2bff9b27fce7e3cfa2759488e228040723d4e830fb4", + "0xd96bd1e0c00121e9361df7a3d5ca1e10533d57658b01a342bc7aa14ebb3b536e", + "0xcaa0b5881d33b38a1c78e901b2394fc3c47b4a38d3c42f215edfe247ecf74c20", + "0x5dcad3c00c1bab1c412c1b7da329fd9cf31262a69ec9238775e6af37bd6096da", + "0x138c1fedb41ba7cdcca293f8c6c9b9f7751d38971cab322a4efb109655218ed8", + "0x5b9319afb14c8afffcc7e33d7ef7a55690a93aa8b3263f7cd8136e4fca9e6f96", + "0x53d2a27301db2d09cf429ac7ec83e7fb8ecbfb3f2a43f0827bbe131c6ba5687f", + "0x32de7dd8d19f2fce27aef18c77677919550a2829c6217f268abb3aecc01cc6db", + "0x9a650402136b2027a6c47cecb915fd962a9eb0f3f3d563f4b323145ce954afb1", + "0x5413cbdedf46ba173b5d8eca8ec3e9304e3fd125906d129ea5798f2d97ab5eac", + "0xd093b20edf99e0e122c9946845d7667a80f323fdd17be1732dbcef4e1bbaf0c3", + "0xeebf6a9c91a98287212ce16c14331e0841d431ffb55847e1c9e33d6a41f7eaa3", + "0x7584249351fd2a7dcd31a92490fe9fe08d86870ef900359d6f41b542498f6e36", + "0xe5a551e3baed962609a0f8e733f6d52f843c41a26e6f999d4f31ddf563243bdc", + "0x4457527d222c9cf728abe12e225c1455b7ae008b3100258731a7f2d177c6545e", + "0x522ae2f78ee1406a56dfd70b897c1086d6ab05d681742ab09298e2aaa7769bc9", + "0x82363dadc3903f3109e11d3fa51b18ce12106b62fa57b8a2f6fe68fd93569ab1", + "0x8cbf13a7fbdd0a3f8747e9f07ce49797156a70e291c2c4a10100b717bc6419ad", + "0x488c938123c1b4662adc0778373758895ba3c2b3dbdc048682735d1adbbffc99", + "0x6aa86042af2bbca4306061d13e98f42d69de8ae83575da95767b855e5af8f185", + "0x9d98764d67a15f7d14b7015e7122142f1af60530214f7a9bdac3d4568d17a7ec", + "0x0bb5d66c698f28eea023356f47de996a9858b5beee030d72a6cbe8796ebd3750", + "0x280b2fc7159371bbe4883b4de700537b01c718093220b50525cf613a7096f93d", + "0x79d2b532f1700d89fc3e0aca5362325f2f7882dbe21dea3181344731659e8459", + "0xa7be2d68c23a4f1f6a154eeeb3f821eccef40ab37192dc50e99d7ae1fdfb1530", + "0x1ec63c5c805f293794c6f6b3bd71ba4fcab73b8e0fc0dbe7ffb95adae93ebc2b", + "0xe7a72ad15c68e906c3f32d17eb135b18466cfaa1da3044034e0c644707a99f66", + "0xa15c0b04e928fa931597ec064a64f26c135870c52c1f2c3ae5a887a3de0cd80c", + "0x43f006cb70c8e55ef7d21220fe26060053f280d2de661df8a74db6e4e1eedbdf", + "0x9330df071e980848e1d11930aff9e6e95bd1c8430fe2eb4bd23a2ef45480ef3a", + "0xfd06e0ae1cbec207235cd53ee0032edc65bb3b25737635f60bc6a3a47a971da0", + "0x644fd982b7521ffa025ba742090bbe4c63e0aeaea67a1f160acd91513269dbe7", + "0x4c248dc599a012901d6b6320c7de52455db1275b1bd3c03c0459db54ebe65e3c", + "0xba78fc753252b2fe678ae57ea6972583e23c9876382abb814cf4567a265ab8db", + "0x5d74fd301251f385944f10d21b51e55991db0b432b25075f84e10a511cc2a998", + "0x2918d536b0649b9980e74717c0c1dc6d83a161d96a171220aa2a5e5f601c71e9", + "0x1e299208d576b1cc76f321fd9398faea20b5bdfeebe5d5b9c5faa9b38fedbfb3", + "0x5dfd61c513187017a996df43c739e6a983470571b6b5e24deef4b4e84fd26262", + "0xe95ff889bc35d91ed9dd9647e6c161b3e2229bb5017f020521f40b8c74556026", + "0x477e05edbf22f98a2d202327b382ee90809abb2d128b754fe76b7217998da0b1", + "0x3796eef16692b81916a73ca76b2cf96786f8ddec554e2bbde28b0a8571d25879", + "0xf18f15b36e26c5d0cd173b5e4484ea0adc54bb4921aaf6fd4eed9e4e7ca5a544", + "0x8f8a94cba361c196775a3a019b6726e627d5cd127e60ec9d7c582bc79cb9efc2", + "0xb341aad0c9a79efc708d547b56f32735cc66860a81f1cfa145a5dd719ca8102e", + "0x8b29454026800a74bf9b2907579a1cb4b26d55185fdfc0aa75598315db767984", + "0x253b1ad14393829b5427c01b9b81f180f8e6d70ff0191d69490d898006ef4b26", + "0xdd8e9d146532eeb06cdc6194873001311e1d582302c9ee7a6ef56f94f3d3956a", + "0x799262fec28014ebdf9d7520fb05e98a3c7d99e6be679bbeb17cdde365d02e3e", + "0xff1f5b0b13ad47a428689780bd7205143fa0127eae7ea51e3fd3e03468c6eb0c", + "0xf3e8cb2e10bcae3ce854823f3aa980bddb0e6cc920b087ca9cca273db900f677", + "0x9e7671f78ed4b0b08f0457a1c768f173f3837a0daba25e7bf81cbb5fab0d0db8", + "0xe2d37744b0b927c3e4fd599fe9501ffc2867e23fe17872a48a11607ef916d7c9", + "0x88c5e70819962dd5e3f14116384facd5908906ef743e8769f54b61ad5aa5f6d1", + "0xfc28af52169cab765fe362a478f3daef0e9c96bf498d992a8b263b5d386a5253", + "0x4dd17c8d123bb3c233fa1278ea17c416a9e961d720401db3b1292f86c1f1d75c", + "0xc11e9f079cd46853d9b74e53318717a787d28d5f4ca62b50ce857e1d9da60904", + "0xbdd33dc93e2b822e5e6ea03992f92e5f239765b7845cd82868212ba883b01685", + "0xb9e216d20de56e65454b6c6b2a268e0535bcb023b6171b8247e3abbce5ca614a", + "0x32544f77fb33ca0e5c13871c2c0c09a18fd6d1604e1cf822346bd87f6e38e4e2", + "0x32dbde4259156b02ef6d348ab6ed7416670f19fa9f888b57ce5932c11f68f032", + "0x3a05fc2900df6acabd59fc62cd4a5d488e3416642bdc5e58175c6f1ef83330cb", + "0xf56c081f54274269c75f17695924ac76f403357049b081374d30005caa9d4b66", + "0x87f9dccf9cf2ef048befca151bed49073033f54469ce839b6d461ac8607a8f4c", + "0x0064bb5a2f70986f06942d6fe8c16504edce924b14049dd6b28d1315659dcfc9", + "0x05614fda6a5d4b94e34aa0e89f9aeb099e1ff8b4a551ba5da5626ac90fbdc6f9", + "0x1940b01c22d6d1a28880391a635df874494d0d6ebec0e6933f59d2a84c9c453d", + "0x7c059d26570c217ac75302b5a9074555d92465bcf0cdc2f936afdcdec0c3a17d", + "0x30f4272e4ce2aba90c6aa92e0746bd94e3b92d525b6495dba9f6ace910b22af6", + "0xe07103cdc025162be05d01db39d151b29fa88b800e0cfaceda44aec531df7eae", + "0xebc8498ea4b18c89633e68c55ded7eb346e2306048877a15161f7fb8bdb0314e", + "0xf2e025682c54f27b338538405a89e0b6feb1c56f8902e0b9e3edaeb975a95144", + "0x1ebb701220b9aaa1fd37036348cadb07962a90c43f0de8f8e4b2d58805e5cce6", + "0x6c7a4c6e3bc9c0e7c1260ca2a75db15afe0981c7f555f18d01f92b006dc16a97", + "0x47703b876316f6d0387b114714cd442adb9752905da0d3082154f0c5633a0ace", + "0x5dddda7d6f4c16216cdf69e5675966032668e31a4522b9c53e20390fa228ba9e", + "0xc0dadf4987cabce9c5914a85e4d1d6d45ab05048f3e313595a3a2a006911c5c7", + "0x3402a60d8db22d3ef998624607b48546530c7538840323022f466a942ed3c000", + "0x6634c90886be98d5821c1b575ca83c98dc39592eb5b11ff2602d79f4f43178f7", + "0x3dd9dff818298d4d54748db623338c412c382dd8316ea512267c12e2a7a92480", + "0x3a8333eebf32901763faa8c29fe3d957d5bf614fff917ef44769d1e310e12738", + "0x072edafda8cab26d4a873b16ee24b96868e9884e372996a113fb88dbe2e008a1", + "0xaac98b34fac2da7537f6d6ad22d3ad39f9a8975d1fd55452ac299f9e6c7fd5ae", + "0xa1b1662ad6c49e7923909ae9989eccfbb50562c2654704740c6a42b3b6186cf5", + "0xc6c19d0d67d678dd767c6cf2dead29d454cc73141454f0ccb102d0e052711da1", + "0xac546219d25ec0f4692ccaccaba72369508d5b300b65d9bf3e79e516f8b6e9fb", + "0x6356bfc67f64a69c732d37c61a7d8a6969769e522b3f570c80c6a5ce25f4396b", + "0xfac199084c510d6720439dde691dddff8d761e49a5698be64eaca7c51cd26674", + "0xeb69059b109517bad0b495b18b95ad2f9425cf100396b38bda87d3e702673487", + "0xb37952957ee9cbbef1dad42ac4b6c67370dab8e6e46d06ea13e99be9215fcb4a", + "0x145bec0669631188e19ce5e55370f72c0c1c55c9d9876321b16929eddd085b76", + "0xe2231fb342f22a433781e1bb87715e73d253917cd4288c2ecf12bffb16c2029b", + "0x879e938519d8b06f0187026e3bc8734700812d73829871b3b0388028107284cd", + "0x1ce177bcaad5377840461f36f568f9a396044479da5cb9fb70544eb9b0a74b79", + "0x76e21549b9bd8cb73bff20dff06b9187cbdbdaa57b49a89bfdf819305c6b37cc", + "0x9ced930d51a3a55d54a1e07360df21d31f100faad44c3ddfe55951bbafa612c7", + "0xa7d842cf12e8d4399ae0e5230f22a454721fc4331df1d8bbe674e6643f0670ea", + "0xf4427f12b76e2563a6a554b7570c56119db0c10d95bb300a62a0dce1e42be9d6", + "0xfa570b6c7a6b4f63e3436a0b76fb2568e780b411cbfe77ac601e0e0c594bde2a", + "0x7d4d1c4919d73d64ed458625302bb8dab147e19bdee8103f15b41fc7ae8d2857", + "0x5990781d6d10b56368e5f3b2fb972f69ad5398960f2cb08b1c3e0da1b252d0b5", + "0xe9aea245d4c2517bb776fe2b08f6257dd6dcca36fdd8fe981e272e2af55e75b8", + "0xa5bd37c728c2aa926d19ce1a735b1ab8c36b276473e7483925d08e3819bf2375", + "0xf213b474849a93070f12d1126cc73424137bab5da040557758e5e3f4e21cab2f", + "0x55ed942327ea15beb2253789440730c61db435fe245df905b013660e068fe083", + "0x501b7933ea947a6218b119e83763983c4c0d5044d094635d133d1399b63638aa", + "0xc9edb869824a4fdad86f3e24b66f1770170cea304471d844ac403763d326973c", + "0xe719ed07733b1f3b21a8fdceb7ed3a1e993a81121df42f1e9177f2a7b23c328f", + "0xb40d89569bdb238c49d676973633ad8b55955634ad4ab71733405b62493c98f0", + "0xaf78b9d40343bec0c1bcf9cdef234a35fe11ed849a6cab3f0260fdd93404dc88", + "0x9432a57b7c9548a22695999e5dfd33b736808ad46521bf6b4f93252f02c40185", + "0xea20f7be5cf3dd437f7bc6ed9737af856acab346d71091c0fbff8f1701e7346b", + "0xa055267d91c6e4c067f1c1e7d3cd2b8cbbd1e71d9ecde3dc54390f2d66e211d5", + "0x7741d89acb09be08b8f34039ac47f6e8a07516843b804a594404343992e83fa0", + "0xe82734adafb7420816edda8b72ab9ede820b7eea6317b7a512ff8b2a8562db66", + "0xc9814332f383fd907ce4e091a76ebbe2d39977b7c728218955326a292cbd299c", + "0xd614c7624193e3e39fd0044ad4e4ffe5af2e4b331010f1932680a6d3afdbdbb9", + "0xf38702a3a5926c689e5b77faa6d477a47d412e46bf7a3aa181479b1bcb5f3f2d", + "0x06d09126d26853f83ea4d9f9b181112820d441e8a89edbb92b378a081120c203", + "0xd16bad2fb6e6628d82558525c4f5bb565f64685f68e7e11b94dca48c6317d289", + "0xfcfc277a73f759593f7fdd2b5063ad67b2e1d164ffefe0c2496c4e5eec761fbc", + "0x372661c90f9921a0dc006cc3f3e81436fa58f4a22e74ac830d5ea02e0a2ad780", + "0xa1cfaa11aff72e479a82efc11ea7c8888826141592ba1ba70d6bac65a81b5f41", + "0xd9ee5c97933ed5f07cf5d19f09fd0779fa6df95ce1bda890d4bd8d0ad1299909", + "0x604dd6804ae4a250ba18106c6a3f3d6a235ac52efed4f06143ea54607115e011", + "0xbbf906fbd4e12b9ba0b47abcd57c98e6c29b14bbf86b027d2f742cd07b5a8893", + "0xe4d53b0d7603601a26295ce7e941327ccb67787b17c8ef7cb6c65a0b71ad24cc", + "0x64f3683e8b155bcb9e095ec932b5e7ddfbb7eaf3768152d7a2c6fae02c1e47f9", + "0xf47850f75aa4cdf90c5ba2aed5bb0047462d5f1842638cada9341a70d038784a", + "0xb06f6923e7a5481e7de292b93960a6f9eb2efb6d43ed1cfef3ba622453b0a486", + "0x7df89c5d46bc2821176ea73638d9feeb118dc98abee6563fb53c14591483347e", + "0x957597ab855a25647bb0f341a70b6f5b1077c34ffb33b1667023441ce8a7ea06", + "0x4c8a9e80127afe687656c7fcf4343dfe16138fedaa5bb1f48fa797fdfc6b68a5", + "0x5ab1e037d3b1c205bf8311ac64e9819e7b3c3379ab79466437a251908fe380f7", + "0x0efddef79ffd04072c09b5ee709af094cac46dd38b4d62dc803acee8181f5973", + "0xe08b53d1c643ce5c195c6c89a40159d26ddbbe3b9ccd0b2247d71e29ded7e062", + "0xc7cd92a894d598938362466553b52424baabeab6f994bfbccc2b6877463c9393", + "0x91a814a84ebc652316c027aab58f6b589c305f2fd97137b997ec8f92a6f03b6f", + "0x6afb1d9c138f0c415e377fafe6a5dbcbf2943196923296e2efe6d601b164f3fe", + "0xa8b7d394a01d0b25ed4302c266ea3ae24378b975109b8f5cd781a2eb9f1249ab", + "0x07199ea288fc36cdef136533cef5453d1801d485d61c309923d244033b636977", + "0x5d56287d5250e8ce58944bac90aa327d5338dc0d8c7291a73bc285f1dbf9220e", + "0xc77b1a39164b8e87647e2a1877594420db67da784f791b35333d58f5bc181b20", + "0x37ac281356ba2c9b7c2532159f8f0211c6bed87faeb283225bac6131f2a9fd05", + "0xde294b05c5713e7a6b83b18c54e64691204a8c6c3d508a3c9e14bb18ba3f38f6", + "0x866757d995cb9a880452808e114c4d978178488821ad998429643b83591a9c9e", + "0xe4885d4fc9413a378495105bcca7b82ac4596fc12f57c55f848d353cd0a3ae7a", + "0x80640a8439efcf872cf4a4263c255017368cc71e3edcdd5eeb03337f81538cc4", + "0x38de41202bd565ba440ca3acffd83e8dd905e594ddfdc2dd25cb30a153112c56", + "0x61740d77f1413d9654ae2c33a64328eba03bd727ae2f9d62474f993f38b5c332", + "0xc61988a6a2285f27149b38fdcf3e2ef09cea98c8676dcc4023dcda08c7147930", + "0xbdf1ec4f340b2a2bdd8c6f4d17bedd2ee69c6d8c33ba49c0c776d48330e62f05", + "0xe4497732cb6b7608714826d79e4bdf32edb42554616d4b4ab2f6830e4fbe20bf", + "0xc26bf2b424e629a58510add3068042956640d9e4ab75fc6e076392a2e8104340", + "0xa426ef3d72db9953e1786e645b2d25080d19e4036c91d0730fb4868344069eeb", + "0xd68314c896d877b7eb92b9975a326df63432a05d4ff8ed8b2415e15c866c1f78", + "0x726b8be9c02fb8350bf56d6bcb0478c115a6c9862b99f4830afa03a71274efa8", + "0x368ef3be60591de2072ab75962d4d8c384c7b177f62ec195e2e7c1088ce40607", + "0xb03327da3f2bb7c0da354eb1d0c1ce4bd9a1dc236743bafbd0896ffa1ee987e7", + "0x636db9875af13a389a2dc6b3a4d172eadf8e2201f5ae89cbf1cbbc1716e22462", + "0xf85d35509492155fa8a882023a35e9f1ea220f4ca14a32317768544ef700faa4", + "0x83535cf300dbc9a8a4d010e683911fbdf8dbe2e8cc0518025758eab7365c2520", + "0x45b81e37f42a27f6dfa2f5dfef6d63b8adacce344d3f4118c42c1ffec3ca172a", + "0x03b97a36ce4d9e0d32e7bef842143d40b8db2c99412d610f8d9c2fac50c62911", + "0xf02f0685f8f2c6589b192bb6df1fd224c0a14cfca52b30e3eaf9b7113c621691", + "0x1bd419dddef9cd683ed2de450e13312b8de86f8193efe8ceb2beb18a5c7d50be", + "0x70eaad1a4d5d5b00eb72aa494ecc8d5b67d811f23bb5a9833b01d2f724e2c0c5", + "0xa76d58a3819ac56ffcf93f3ca3a7c5e514c973049f868bf21bbd660bbe60697c", + "0x5e7cc645932581c572cbee714150c9e038be76cea883526311796581319515d0", + "0xc058e026449de4bae7661cf78271d9d2d84e04211dc97bdf38c47dc518685a5f", + "0x30ffa5283c2111d131832bee968eac1c15c83c9c89715046ccf2431f1efc10a3", + "0xff7d8095f4b58a1102d6dfc30f7296884b33f548723149206c9513d1624924a5", + "0x1981d04ed23b6602b975225c4cafb6f3a5dc722157ca4553d0acc4b8884970b2", + "0xa889239afb3535f2af00e0a707593a6284b74dfdaea480027cde672c8e7ec1b6", + "0xc32d5384c2b198f80d4ef1521de2ac6845d6866ab8cb48eb45e6f06b62b18197", + "0x097edd63d238c92b679cf657ed446e210d01914e892db5be9845a21e3ead71a7", + "0x2d11769b662204c2a7aa0a3fd8b948ac1ab1a0150d90c01ef0d73071e068412a", + "0xa7ea97f34eff1763bc1f69888c2f55619042b26a5f2d21dfd3b564c21633d0a6", + "0x6d9ebc71e39288f97471074fba077ca8ef227c913c20dad059cfcd145ad01527", + "0x78054f34eba00a2702cf6f05c890c740cdd2246b87dbd9f89bb8f50180aa35e0", + "0x87945a6971bfcfd96bcb4a2ab33429687e29b90ff0e4af21504a68ac14fc8949", + "0xfe2dc18d8cd5cd9fec302f06fb2b0a493d6ee2559002782053cc1f7a3a1117cd", + "0xe36e1dcc4d557f7b6c2aff4c76e010e8589c5563c154c939f21261a1fab58528", + "0xba03c57c504dff70a9432a690c40e011b6e6edde59fa9864f50bc92a584dca57", + "0xdcb24a0cb9682508d153364caa6e69f94c5e12e2a487e50d223af631b25f601e", + "0x3fd13070a46219e59c8d0503bdb8a2e8660e78c3d080575c69ec11306120ceeb", + "0xd49d4300d7f1841a270395069b3e7a3b8747a3b905b75d0508f72933a37bbbba", + "0x0ed2813772650e379e6cd4db4c4c5dd88d1530e8be6ffa2fdbc0583ee15cf746", + "0xfe11b034f29a2359abf1d8b147279a051691b2cd0b16ee427365dc2dc8092058", + "0xa3d00c51a97a882fb8caad02de5573431e9d3b3d103673f9e9ff627f24cdd5a9", + "0xb3107b03240469262ab8fe94578200ae161e7600e2815c034f121090386e593c", + "0x835203fc9cee614a87acb9e7a28dd25b7875ae35bb8382d79da01bfbf5558926", + "0xb315339554b90fced311da3d8db6d1ce95561069dec63fc3182ac9b1e35f4e8d", + "0xa1c90c7c53c87ce520e31b369eb7715d1d58f3220162c035415f11c2cedbd8ca", + "0xb8a6af22faa68b0634b1a5d2d37f6d1de104a28b4e5a4c108b438e518871faad", + "0xe3d678d3cf2277b9918ec6666143e0ad196b19620e27dfb68336c6ff4e004787", + "0xc653329dbdc785728344750f8b7b4194be0622ab55e40c21f1381ec7c3748bef", + "0xe7fdabcbd7fda6bf3b8a2839e6cb6da0aec955dffe1989bc2e1f2aecb2e9dcea", + "0xc4f4a365fc1bdc3f2c3194374948096d6cdd8da73149626939f5a4e7a63eb4b5", + "0xbff6151fbfd1a863dc163cc03ee4a22b52f02162f89cb79e3e6d04637796cf45", + "0x2a9294dd93ed77a707655acd1e38b1c578415291ea4c8b90915ea80b11e101de", + "0xdc3fff13391030c7ed6649067da36c1107df5ed194909e03e6586cdfa58b8607", + "0x88394ac20fb65efe3dc39091017c941974eff9a9ba07187bb0f83aed61775c59", + "0x91d5f916945239db79a3bf64ecf940925bc7ee9e436e3542b6a9729150173ae6", + "0x35e81d885561ccec352965a428db5eca2c8deff04707d1467449fe23226e445d", + "0x92580891de872b4d8261bbd4ae27b44f53dcc0fd40bd7d0640747bd9fc96a946", + "0x7b49416c34f14eb8feae03cd733e7fef74f8a425acbf95f59d065558a17ddbaf", + "0x219485ba5fe7853dbb14c7cda9157e0099817763b6928b5de4325d6330467317", + "0xe3f03aee6457d4ed8297bc3e22d6a058555b84d9428f79d7fdcc92fe3be12b50", + "0xb82f025329d70fe7b0ee06987fa198d590bfe2fa243d541f267ef23a780b9d02", + "0xee7a4b5dc531a191ef24888bb49e10dc85fd3fa66023509f860aa413ae6e109f", + "0x689e43655e6d4cb4c0f51017d07c78dad9b12f790ec085678b422ba271aa9e58", + "0xd6b8f5d697319691f7306b56ad669f77a681783677b8d2e238564f20e669dcb4", + "0x2deda5e0f1ae99483a12253014a874fbffc17b34bb949b29301836138876c31f", + "0x361e301732fc12a4b3cd4007b75ce55d9ee89e628d1deb5e2b1a239e3ddeb00e", + "0xa226fb0ee77751c6327443f31c166a2a38c7374eb6c92c0030fc2fbcaa1d6448", + "0xfa41da47ab85fca12b19c653779573abc98574d6a76967f8cd2c86f950a2c790", + "0xedc2d28dad585c72e423ae6ab6f18ec2614728b3be78aa22c66aab396f93ec4f", + "0x3039d19588f93c3b78a1dcd2a74972543598aa4c59364d4272563260e114232d", + "0xf743c6e4d312a443444ed7da4cc0107e45abcf6c71450be0abc9d037c9a67cff", + "0xe1dd8d3239a04b96d0d62193069b7e26fb9b9215e21f6bbdc174770bda48f75f", + "0xde233eef3c437bee543a931a92804e4ccbb6c6c5c21956c6b698549b6635196a", + "0xa2f8346985eeb70225fbc55a23cd7e5fe4c8f49febf1fb31f4202771517faaab", + "0x89da24460641dd840f2c5e92c2ee81f731a6b8806e48f74349c83d730593112d", + "0xde9f1b996e581f8c18b5ad06b071852dc1e2414571260b8cd3ab1eb75d47468a", + "0x789596990969170cfb7d282508c63eb014a808352059241a2be928bd9996d8eb", + "0x0ce3f9256dd680d9b4fafd270c1ec94ec2f21b92d97a4c9dfd3b9b6fb262c695", + "0xc4f3e33cd6121e490d4db12e84d443862d325c86bb8fcf3438e9284a3ac39659", + "0x20990f494909774c9c79d7865bead5036732a5b9a55afad0de0ddafd63a08b0e", + "0x61f68bf9c5998ea5360cba99eb7c0cc056ef2338662c4d78e2b2321a32a3b5ed", + "0xc898b91290a836a60a43584b354d319cd384ec9f462d235dcf4624455f16eac5", + "0x2fa7c65fbcc296c0d559fcdc9e3cf7448a7702ce46c2df49f57a757284733cb1", + "0xe6917cb9e6c40ab6cd93854f9487c12c94694e1da447c632f71af84c94ed920a", + "0xb76eaf2b663958061280b19950115fb945355285b56e4d18d9f06cf5e2000e58", + "0xcb8fa85018c7d4adece549fb62e8033e79fab9c5d3740419654c7892cc889e84", + "0x85a41ae1a223e6c199c895b9ad1d38db82ac0c17ad1148c9c785f3f2a5904b6a", + "0x5e022eb91bde9aecdacf15767f5fdade5173fa1b839af56d4df160569c7d0be4", + "0x62df61825afca20808e9ce45685fd1f0a7165eac925757622318995216283085", + "0x68efbbb225fa12a64fabec2c16cfc5ab3bf31bec1661a13f6f002c91c2478cbf", + "0x13a9d5f36b9383f99f094510c8fb3c3737c42b4a7a69f9aefd398300692af39e", + "0x4c854bf140d857d510e2d7dd79eb2d7afa2e1d12bf7c57d58203443135e773f5", + "0x22a45da28d99487379a82a425694851f2e84924384f53a6bc367e5c736d8b128", + "0x1fc3fb2a1d943ca2355504d540d45aa64a526fb8b6a820e52c6f614717ca8864", + "0x05e4678c9959522672cfc9326de9caba2f63a5e89c0b6f05cfd8fe235f641c5d", + "0x4bbed23c42c3d512d12cd1ec6be0bdc5bbc56e9208fe2cc9f19d3ac6ed6a4f83", + "0x56c63af336358e087b83d2d3f421d0436bca8766613977fcd65042abb20724dc", + "0x723ac0d5823690deaff9806347f28dfa41545b603733e298753f436d63589793", + "0x7406f5b166806fce64c17c303d959a5134a9f692edeed51c28a608c67e5d1c28", + "0xf8c4d99270d6a77bd8301c2234c3b8be5ecaf1f2e53267161ea2e18f3c5a2e0a", + "0x398acbcd79034444968f3b7fc17f808fdf6af58d94b54fa3a8f1cb52035d9b9e", + "0x9007b00b1d75b3880e807f65e8ff62346efd00ea6fd49b8fd2458ecc10b10608", + "0xf8590e07cf854f80ae18fa0a1970ea92b748db528b093d68690474b7eecf7a3a", + "0x1a38050a98c779a4b2f9e8019beaf785d2d1ab863219609f57c8dba30e9b3443", + "0x6c9f25b88ee6da67dc4ea097bd0aa970536374813ecf8b37da6abd911c833780", + "0x66918c1ceedd0777f9355f550b5e015576c612f4f7015badfcc161783f841cd8", + "0xefe2becd8da88631348c09b687060ba7bba417726d3e9394a35882704feda59a", + "0x9fb36c4702e09fe3d3584b20fc2508a9a560e7d752bd616ce75f8bf5f40aa55a", + "0x15c2dd4e594f7a78af213ac0db3023d149d9916640492862c236a942a5634b88", + "0x8d781704a16244d0fa41c5b043104f78fb6a1d75f02fc05329d5bffaa96e2dd5", + "0x618551536200f2e7e95eacb921332856d2476e223224bc28d483ed80f97cef88", + "0x5bc12c188904f94004e293c1d0b649ee3ba3cf11c6ce11738cae0f39ef0ade08", + "0x3ea99df8c6bda46c338a2517fa3133b076ac924cb40df8e9894eb104f420fa33", + "0x5151b83a58ecadd60507111b81355a321f45d20afc2469b030124d2f5aedac0f", + "0x6f6f8519b0616848dc830a0de05db910366cdc430a3ea913dd598d62a5f4c3ee", + "0x27e8083704d13a2d6bb206c5a7aba4f9787f22a78f4b0a8aa8bc15cfb9476bf2", + "0x2af3fef6212b1e04984ad9cf144f2c613e19204730ea3d1c41cf74dd005f2a79", + "0x2cda40606d0522190e6a2d3b79ef0c0db3ba5ff4c02746dd1730982d92a08fd4", + "0x2daae5909d9907d39b836088b40105c0a30e2e6107f14a46a524b4ca9dc7c36b", + "0xc036c4b59d20696f5239b974efa057db9a763e3c807a7fbc78647a640f4fa7de", + "0x437d2a459ac6a4cfec814cee34046879a3355db27fc7002777f1ad0bfeadff3f", + "0xfe249dc4ae2044268295099aade7116c454a40839583c596e5c8c4bf3b5f5441", + "0xf0aed32daad485a1df7a2e459dc4e8d3803a1337d6d5f83cce85727d86604e5d", + "0xd499d1c07968251a5dabf4c45bff452e975ee56b4aac0064a07330072276f951", + "0xfea73b06dc4ed554c7b4204d4b0772cd2bd4b96d64a011628df1700858ab75e4", + "0xfae9bd74fb7cd56a3bfe8dad0c5833d4d5afdaade2985e6acf5a02ee30f508b8", + "0x4354d630091564227aaa3c0f6df3a1682acb08c760fab136bfcb4c59d7369ea5", + "0x3c57304a955f935eb67bc742026c2ade797380f577fa7c9441211c2d38bc2198", + "0x6663773ca94e58377c5ff9fb40aff7ade26e37ebbf70816906805c962d83ec9a", + "0x1a7ae02c247a2bdb6638ce07e9e8f13203b6abc33af322dc5397fe8c80792743", + "0xb0cdf126e6e30f45dd4929e036bcfd767fa0081a2767f022a5819ff037a1f914", + "0xd8accc6d96079089b4be824aa3d2fdfa614f837135e4ccb94df0d2ded3456b6d", + "0x0f6c5aed86442381a02b2cdcfa74e777c1f9d344628582a4636219a3b9edeba0", + "0xb20449b9eb3031496b11e771f413cd8694a9bdd1a297a4ea0ecc4662e12df684", + "0xf4ef99c5962179691b8404809a5460b7ea4ac9e995f774cbec009cf49d84db1b", + "0x9a089b809ade8b9f07484905bbe9297ce47d5f4dfffd8b6e3ba69b0920dfe0cb", + "0x4472861e5e426ba32a8bd858a22d1411185cc6a9b12f836c6ba5a0f972b2c171", + "0x5fc1cd83af35ff124ff28eb69e8183a81318cc403b141717ca78b171c3cda89b", + "0xaca2527f6279c211292757583788fb3cbc37a4c455a0d1241c1bf07b51e234d4", + "0x803b5e142368be67fa9c57d2268285a16e0928f618dc93d4066a54e6cf11ddc8", + "0x45c7feb6d7a793c0d5367a305779221260ab03fd552ab3068fb0c59e0a32659b", + "0x9839c20e9a94d71a9fbe7cc92fe88521eba271a165f19e62b2305f6826fdaa32", + "0xce4fea145cfde9965a726676f2cce586fdbc9552fec0fda0087b732519ffe389", + "0xa2a77b518c7f9cf8a43a2ff3dbbe12d95904fcd285fa3e93cabe5a383da95488", + "0xa2e63e220be1ed5d47d9954fe30149429dc58a4bf08bc772c93a3772dc93bda1", + "0x40ab05b95d02fc0f064dbad87055d01d5397befb79009efe8ed1f3681dfb28ec", + "0x68e7cb55a101ce295dc6188406f326ce6250245979bcc769dbe206f169ef9ba2", + "0xcd86fd1fc31d99e40faeb692e29208870a148a7589a5e5695e259f5a2fb82879", + "0xf33fa5ab48007fd42626b9518b8217277b3c4b60d10ed80f179130e0fb8bce7e", + "0xaa78489f7d7a2a13ee9a2974659a06fee066d186ae602d0e2da8c4ae551031dd", + "0x12e5d701a8eae09da2eb9550446baff6af4421af4286f70f6563961498b98e12", + "0x5509645568da0cf2191beca31b3dadb1e2bc4dec124de188b97e8da72c87407c", + "0xce5a029602fd0c444d089ab1510db72f94dc75e1c9d4bff9421e4681e2ffd4e9", + "0x32d3078339d7cc45ca59904d53fa9c8fba08c61590e8cdc89f6a5e220899e21a", + "0xce213468350f47b3d15eda0c0c99ad406c7aab466b8fe2e65535f7ee13c44dd3", + "0x035cc08e116302c7fae60b4c73ff8536bde0313f18876929972d71faa84cae9e", + "0xec036a6af020961eb947ce6d0f44e726231bc7fd5bba04fcf1017762533cbd0f", + "0xd2c0149e965f1cdff3500d9bf8992a43817d1b76b3cb776f964f1b52888924d5", + "0x5bcee4992041b5060d40a1b75b1ec8d11de6406e21fba8fa653b782f2970d197", + "0xc7666e5b9733872267999fc22a50bebc4d912c20a81cf98ff7f2b81a4075c1c9", + "0xaf3c4a75e5d8103d5a8f35b7d96a4f5df8bd3ec75a5a03cab575b2d06599835d", + "0x67788f8827eff568a35325c8f05392767ed5aa37dd1f0f1e5a17b95f199f7b80", + "0x6d23f67ce128353c7cc25aa66a500e3a3c335a038615fe346d163cf78bbf4dd3", + "0xf54ea3dbf7df8359f63d3a079ee149eb6940de72f2880ec28129fbe6660a30aa", + "0x590f8dee02cc9f2456a609cca0e8fadd584ef86ee664fa544f5daebe07821d31", + "0x060189477476bd79693331aaeb8b0730e010c673e9c6e3333337817745c57119", + "0x5403d1aed53b6a93e0c7a8c6c565ace749c4c9c71e7c1d8fcaab72913bcf24b9", + "0x5d5425475ec3f9eb0f2befeecb8e7788cd98f828e23742471574d91bd6b9c0c2", + "0xb0f575b846c60fe811df13d3afda032acbc05f6bcbc1a282480de24a82378bf2", + "0xf8da0dbaf91d8733631c4b68219d6da8e44aa1b17689cea7101990a1928f87d8", + "0xf05d2f5939723cb18aaf3329cce5131de42b1345007e33ffc3819f187c743cd9", + "0xf70d2778785d7808dff92cac185a17ae33077c42343fb38fdb8cbdfd68207493", + "0x5cc36d7c8a4e6329156ec7dfb455bc46d6229da0022b1061756f6b5252376841", + "0x59f2d2ffcbeb01fd6ed2f4d5edf0e9a636375f829a8273f8e6492275f855a976", + "0x7071b3fc06014e84810b3ec9f4a55044ada77a9d08b1e7603d6bfe699695c21b", + "0xd3a8b241e3d3ae2db1880ceb241a7d9d9744e940db4ad225cc8674bb8f1ba43b", + "0x561b4be3b67e618fe08ecc5b4065b5b855092b49de98f810aad43c9e0a79dc6f", + "0xc9a575f519848f8fbb503afab862ca30c9d1eab6eb18228c43c22539e1d5a421", + "0x7adfd2321c791ed97954d6776d99837e96fa365a83c385f2157a97ab97c44e14", + "0xf649806d5e2a6c0ffa0e1077b11cf4844e1b792b86fff44091a4b4f1ae72b10a", + "0x29c9bb8fa7ba3412b40e2748f1875578c3cba5efb374bab45e7d21395163e829", + "0x5879172d409140d8d1839c5c2a5ac3aab0808e00a7912e8f59f84c9c01005e48", + "0x412e38124054cdefe4252329c4385a2222b3eef4cbcd64f55cac13eb32a47ac4", + "0x93ffd8dadb72be3bccfc3990495cae8a1d117da7652b5483d9280ce87ee2070e", + "0x1c56ed92ace0974fdaa1aace55e3c92ee50b8ddf36ae1ae49518e2decfd8039d", + "0xdd3826dc173fa9bf019939d9f8092b3b5ff5069a5d7315c0231ef93930a140b1", + "0x1cfaab20aedf03d0eca13c6f4a52f25d62ce361db457035eac08c490c22fab15", + "0x8c2dfd338a2cf30312ec607c8d10e107134c0103d568b09be4241cb2ae8bb46e", + "0x20a5289b7b81c81847be053f8cfaef2bde06c17a1b275ee11c649365963e67d0", + "0x98a3d3525a3830d163091eb0ec4176e1b8e11cff75a00191f4d300e988f7a025", + "0xb746a3fdc1ebe4fd2f35fea0be38e69230abd0ca4580412f522cb16246860f0c", + "0xbca15359ea543e541d07d3642fa248e9c6c589114765fa1bf82b42ab0175f5cb", + "0xb394e7346bad95a96805d6171ee419a74aeadd9d5d78fd812994fd82cf7a8f0f", + "0xdc7306104ec7e081328a2696b6a047d858ddf548a45f1c94d413001abb98def0", + "0x5be2214a1572405df047a13dd641bcf846484a137bad5ee5cf082c367d6d74af", + "0x8277dc7d8e6dde77b27f8d2cad930b3b3afde0f948b712a447ca3a5ff2517854", + "0x936c56e835cca2ea5403f65d9c0e7154e6bd0cfe2169d3781c4b6f623b9da405", + "0xfebcab5345db51829c13658ef7264f868d64329221e89eed04ea92a7ea2c95d3", + "0x4b98fff426b692f67fde18e6c7652cb7850a0aa78cd22e0d75b9717bfd7fe3fe", + "0x29869731f4dc6ae844c2e3dbff8b33312792bdcfc2a78c9bba421de6ee1cb1d4", + "0x3785a45b79af57dbe71e9ff83286d3f90f35b75031107d48484a20a2b32062ca", + "0x2f537f3c3819bf90501dedf1acc3587dcfdbbfe4896ec94d057cdce4c0956e71", + "0xbf178cdc2ad59aa13ee82ca509c5da5a837cfb8e963092944b455f8b8a4fc60b", + "0x8f32e66ed0992e7d05cbcd58d4dc2344a1bbdeddd62a397ae8d9b48181c49496", + "0x8a2493a711e5cc6a13ae54ee1d378adb1e05db7389bfe4fd1c61d73e86758ddd", + "0xaf598db8923847eed5aa581847b224d332ab40d37744066c784281b5e4e5cf95", + "0xb8575ce4b2036b7c5ae7f0f78a2e8099d6b18c9e685213e95f53ad8eb0b56091", + "0x98f8c2663b58070f064f935598d1166245a8e56198b42ce5320a843975b82abd", + "0x360914f77f944705deaf9b541841d55f6256b7665be148d31c3464145b624166", + "0x93a47c6d4042fa1622e7ad065ad5acbe4f4d630109ca3ae81216c88cfa647158", + "0x8a5ab9354a6d737abd1120cd00d01c6b9936ec2122854ff6efc263071180a93a", + "0x9b53188cf04d0025a4a948235cb2a9270ab8fec009d81a1086708fc768f2e743", + "0x786f4437e9c7f013aa531ba75c5334a805f55738c1cf14fbeae90a491626c3cf", + "0x36990fc1c5b0bdb0685a7a909702deac475b78e1f9cb989b051c7c2c23a139af", + "0xb2030fcf651563b99277e74de9de55a6d6b74aa6624d98cadc6c3a7873c94c90", + "0x7b55b63542e6de59cf85f9f5c9f3f1c70abadf9a25d2e9b9b2a91e29f540804f", + "0xd32d3eee20416249d48a1b554e60312f15559652cd9d8b40b48cc76b89ea3574", + "0x897d0785482d2e98e5f72cf19708bfb11f815e182ad3fb2acec5714ad46533b0", + "0x27d5af0a3d4ca9cdfac3359467f52f087850e37d217709c749e358594a8653ed", + "0x64f67994be541df51e351eb9045322b04e0e6b79ad8ba57f28163942a05c951f", + "0x593384918020916dc51e8c65bde7c95016da601169dd8739a5cc681c1107cdfd", + "0x1ce9a7e0d909d642e1ffdde709201c360deda94d69575fa2c2dc3f199a408054", + "0xa8c86d8f388be36bdaa7f2346fb9e234813099beecc7a1b9cdb6bd118e19465f", + "0x7911180be071b894370b9eb79a6a99cbb5ee26e4f9fbbcb698c59109be2057a8", + "0xef6704588ba8e8eb2e00940e6a7c5908b9cc523d33bf22e3f9d2b7e2e2b6ef74", + "0xda4b1a37f215d506aadc11060a41c63d464e9286dd032c8914fd76570c23292d", + "0x76f47dac55f74c4f2e58bed6f4ef5462c54d430645a6e611f1cbbb921cf0a26b", + "0x3b8c621a4067a75918684a9ed8b9a0f7c8cc221cd5ab2c4bfcfebbcfa7186819", + "0xb01c485cdd63735541e9e27e17e8cb320c3c5ee9f205fd818814414dd3ef4e49", + "0x09d0af6f8b828e74c74ce5c6a364dfb396785788080181aa8a8f684f6ad511b3", + "0xabfb7fe37a6be7020df7a9a8b7ca8be10e009dc09d58d621e79da72c6b16646b", + "0x75ed14986fe5023b4e33e3dec4676beab6579bca146f04be9e7ea2a6d2513d68", + "0x51f90a75a5c4b80f7ed12b50249106d3bce2e974079194a99e3eb0f7ae53bf82", + "0x3ccec7c639c39ad8393987b363b783710d397654bd2ac0fa53885ec836e07153", + "0x5ddb2e6a65d1bd9cbc783724f24235f1e77c67b067157b0f218c985130a60c3f", + "0xbf86c55d179237d242c05d5477af5effd081db830bb2cd05dc3803965c5b9774", + "0x3604103dcc775230cf86e60678590e0729959e80e6210ba1a4bbbea5327586a7", + "0xf231b9964e9cf378f09bcff684d8985a091397c8bfc9449a177f9374ccf04e4e", + "0xc13961e4c64197269a501f48bfdf7a6d0ca0f480865060486929091af4f4146f", + "0x2ee0cabaff2b1a2e0677d8d7a45b5008fdb3cd3c9637350dc607881c5636fea0", + "0xe7223e6328c18f3fbe5e45ce2c1b28c36394ba4e944956d37eb268e0593368bb", + "0xfaf3dcb26fe3d4ea620131abed7bcdc703734da9834350cc164c046de7522709", + "0x1ad62693746fde5020ff2c7e1e923d5df8f53b8d5b8639fa5c92dd627aacafbb", + "0xc205d438ec25f973719df3991d5acb42cf3859920055ab92474f03f9c89e5214", + "0xfaa245570f114ec82c4b6e4b7f36cdeaffa695deba28469e65ae0855721836c5", + "0x8db842983fe5e3ebf0464e2632515693814f5fcfbd5e613a2d90f91b17a93ae4", + "0xebf875386a93681e55a9d3303a20f21a058c4a1278d6e9b80e9e493ad9e7a4ca", + "0x17c9f609600ff07d8fc48846958adda684a69735d9402a8f55625509e22f979f", + "0x03bfdbeec7cb25d6079f30cdd4adba342967c6dc9e1cb8fd6d93dbff5732b5bc", + "0xb453595043428072e914fb5e4ea50f669d070d9e709bfa8de7f086395971a92a", + "0x9e5344eb70b4d453ad7a595acd0483d033b0c5dce0f464d455e53624752defb4", + "0xa267944106f456641604f74022f4dd588789c41fa54b546b7db4fe1b37ad9d54", + "0x9faec2a8f2e97123e8a642d720c6534afdf1026ce5407a706ec36a855cd47fc1", + "0xd9768446b7316c9fd82703d5f3a6d19fe27bb5b40c7a18d9c1c259f60b184431", + "0x89b19c564b724f85b368d68b5b52c86acd2af0c396f1b60bc3a6c1e4fc776365", + "0x518415098e31603ddbf9031031718306f960574d63f83a67849c187b164981bf", + "0xa01950ec41430631d5e77374e9f4103ad4f085a8e649fb2b0480280a26146af0", + "0x5a79ddb427d80baa26592bfae51f93ba7c7e995e0492da6b01b1d187e004e3a7", + "0x77cc472c5bdc1811569fa7171d24204e8ea2908ccc8fe656d07fb048545100da", + "0xaa3edd72a826ee06cdd31cb7181c89d6e1e7a924e52ea0f1acbabf0a0bcd5722", + "0xba8d47b1176c35ab7d0c1ff43e49c8f28a261e9c441c84c1b46d3c8d16393d47", + "0x306cc04c6ba3b79a1355587bc4794cecc4815f823a4e913f01735ee506b1b809", + "0x74bc56f7df01eeca69368edefdd013466bbed30b1a18e8db35fe07debdb3ddbe", + "0xa9bd49bd6e6d522953e25227325d2f1c9d52d01a8bd77754081e85e052d7d339", + "0x024e22d771f28a994039abb002b2b730f8641182ba847493611eb9dca1c8d93d", + "0xcda82d1e716141190b6f94209ac29c63b17b85df0abd0ecdecc6a13243569b45", + "0x60012b36e77ea97cb46a7e5cc9aa7b56bb2ac3222f64d506e7f00f894ee07931", + "0x9840baa900602407e0b326d10efa33bda68f7d13a514af2ca347bc51ebdf7587", + "0xb9757d2e126486f20facc3028c94ae986a7e04570f6ead13388bbfa7ebb20e63", + "0x79365c873b5c0666a20250e7b60beddc9782826e5d6cd2d6b0a1ad2331a76e21", + "0x1f037bcf5c2e52c82b7e839f5f81f6782008043292839ba9b583696f3a9fca4d", + "0xed58c110d7ea8e85906da969a86432322d890a8dad2b335a1d581eb917691e92", + "0xde21497cdd832554d5300c1f962b64499d37cae64830bd72364af207b9d456c0", + "0x771673e047d21dc2aa1ea324f71151a4c1b980d096ca4bee90d6c8c7818803e2", + "0x7b707feaff903950bfff3ea9cb8d6b08ea3f627848123293e1d02aacdb52c9bf", + "0xbbcacf6c02688bd7e1639d84b04bd084cb524e18b50df57aaa0df95266383061", + "0x99f382dbc0a030906d78954f94f5020ab47060819223c134280350085d7a9ffb", + "0xb384e3d699025faf5b41a28a38282312fff92f2e0dadf23a8ab17bc55601b254", + "0x8f1412f390ab05dc5d1b9af37d8a4e6ef2b9bb399e97271f55b0aea1770b4aef", + "0x7141097440bd28f8c983fdecb5cdcf1cbcd0ec689b5db3e658bc0403e77fbbbd", + "0xee66f3f97c8388f0b9fe01504c7ea79c49d63f1ce8efd17038f475671455e5a4", + "0x5c5ccb868ece1ce723d4d9c71aefaa580b7ff4d64f97e621102f5e815201041c", + "0x14ef699854a2c7fa3283089ef1a8eb691ee16703500e4ee4f30eefe5dd3734c7", + "0xd262a5ef406719784edc1c273c3805e7f958f909ebf2040aca6f350d4c89618f", + "0xf7ed0524e9ede2b9fb0c2643f8d8cd5211d3f664580be3ca5d6047ae35e7e524", + "0x4ebe2a6ae9dcb214ce8fe48688041a206ec0465c2dd7fe27dd25544006a53bc4", + "0xf722fb5eecc27b1b94afc85d47b0c38b6c02e8b16aaf0a3ad83a4a172ddf59ad", + "0xd78674dc58c8129913ceae30d39ce4eaa5c9f2f780e68446fa48979137c605b2", + "0x74c6670266d3a196abbc2d85a06add2508b727a4c8702f920325cc5ed774862a", + "0xc777a5821a70b1b1b3fb41b55d490fb67ca1c8ba1d8c3492f98283d6fdf85a1a", + "0xbbc93d394cc292b3a7a96d2fe4ed034e4df17e274e9065b450303f748c493d14", + "0x0004c12a79e2e72d519cb9673113cd8dc30cecc193f7ef1fecfc1da644585ac0", + "0x9cd6c52f785805eb4707265226a1f5327a94453b9cec5709cf12bfde5b90fa4f", + "0xd12d59bdf957366259e2f62baaf94ff4a09a79dd7e5011dbe389115f883d51ac", + "0x49771d530d0168c9f79316d429af668606f2698da4341048feab91c5b7f8f01b", + "0xd97ebeb4b0b07a59477987c9b1aa535791e54ff123779a7c4e0a8baf4153a54e", + "0xdaa20dba6040e8d266be32364f2838ebe3b7383d39557764a21e2962dbcbeed3", + "0x1b937f8ca652b579f6f7fc637c8c59d5d2fc6d9d19e413eaef3b01f114554fc6", + "0x076a56cde59a19462a4bcd72c2d26e358766e73a8d73dd29d2e6f6bed81add19", + "0x2a040005f935fe1842fdda6d81629ec7564b9a392563dc66805f719635d57daa", + "0xfd450c2c58eed707d958be41aa97d3fab590b37524d8a6e9d74f4fc3622a7fa4", + "0xeb52b4cd030727bc0d96fe2bc8b27e17287e8d267b2f86c8c86cd5213549b073", + "0xb97c0f3b9321dd793fdd5075f1a2842f22d7815bf0c0190bcac293750d92db65", + "0xdcfa5abfc7052f9c9a703a75c980a3c85ba95347cee2787692e90390f6f59360", + "0x40891c97d5fa2218692931e3f755e575fa1e19316ed9ccc95655c3fb23347272", + "0x68a42e26b266fed496b623f692d6bbf8a05641c14191c6da8e6495adf98941ee", + "0xdd1f7bc5169ed6a54398bfae81410209bc709b6d64eb8cc1b9a4110d2e00013c", + "0xd6870dfe727a93eb32f5ca9c6b5d558db5676fab7c32935564852662f5f6f314", + "0xca9e634fb5677c7d861a21b7d0d8e012dc22c202a0c84c231e59f73d30284135", + "0x3f00c59ec6f94cc9e53bcfc5d6c89612980a40aadf07d4576f7fd1152e03077c", + "0x2457b2bcd12779acb444caa5d81c36bf2672aeef86f5e381f0e649adeefaea5c", + "0x0e8bff4c805729c6ca9c88ac39c4e72b37ae4ee1e03a803e747fc6a4d2b4654f", + "0xb122f64bb06adacb3e733086c77dc7033ecc239cf176d578ca9cdeb55f4af7f6", + "0x6e7764034864bbe52b5856558999738b8686bc1883819e32eb94d384777d81e4", + "0xd300db461d4b782cf34c6f1a39069c43d1fc8dd21ee06b1b33c98cb33597e163", + "0x23d4b692665e85927656963a29377e9d6eabe5ffcbd9b642a4cf63e8c7eeb865", + "0x6bdc675748efc2e9b11a30c1965ace24ccbc4828bab558fd0b852ed2e38bad48", + "0xddbe45061a15bd3e1b7497bd2c7be459b130a315d752d46e89756fb3b4d93e59", + "0x16413a4ccd6b4bd8b2386793e80de87bec9f35bd5ef98eea0fb3c3839e57eef5", + "0x2641fd17a180a4caeb7ad1c96322ca3b9d0cc14a4d5edaeb0f2a1802d409344c", + "0x4dad46c48d760c95264f987340782352cc43c299a1604894de8011a2edcfcef0", + "0x912dfcbbb4f27aa2a6208c4a252d294b21e66a1e24281b4f8ba69c5f814e9c3a", + "0xefd44e242d083dfa218c86ca58a5537d9a012e3ac5e5696a5129d0e291383edb", + "0xc477cafa8ba365ddd90c558f4c4abbc5da2bc95bd72f64478cd6ee631cb392a8", + "0x25ff9af7a0ad2bc4c90981901644fb2cb0448146827190c4f2d22467e8a21953", + "0x3bb36e3fb55d08fa02f5cdb6647f47114db11979264ab67a715cb1ece8fc889d", + "0x05fe267f353a2bc0c6ef9d60f9757e42925d439f49487a3725f0c94e20fca106", + "0x0ff7a4052d7bdf60cd6c538fe7c45620331a3e0e565087d585cbe7c7960ec1c4", + "0x6d84867347a63039dc25215aafee14b4e412bed04aa2bb1f804653e57fce79b9", + "0x74af24c4aa49072a25929c87b73fc7a89d96982d523bb7864127d2093008872b", + "0xfb0b558dd98f7a514ae65065ca0b6e11d7999b19dab718f80bd03015431ee276", + "0x35178836b00b6265b75dcde2e0dd5fc29cb2135f1de94c71f865e18bb557e53d", + "0x2094521829cd56c534c3bf2bf19eb4f469df9ce7300abdaaec0032620123959e", + "0x1209aab9faaa34262eab5078317ecbb9054bbeb0e338f090ed6a52030e919e83", + "0xb4b1aa056b30fda390688be088d0d13b3864165e38c4d1dad6d6e143b88fc171", + "0xd0ea120205918ddc111512453a7d27812ac4e9d5516f1979e8cdbe1ddabb9fe7", + "0x0852660efbe3ed71d5ed43c038e5034867e5e8f2db3b76a2f880243057b0a87d", + "0xc92f88045a15ffa0f4d95258f7850d6fa06e6475d265b8615d8b29a0dc32b69e", + "0xbd75d93b059a6b1c882346449d02e29c324318384a37721c67840398741eed11", + "0xe4148b8c9530a16e2e89eb39f6d94c3fb693881270a5ce6241fdb4ef0993c2da", + "0x56c6e503f5b5221c4bbba4fb222944353c8f8deb42916a977d8de1b935b707a8", + "0xf81f6d5a24af1a6ae98fc0bf2ac9a330b8dea05fdb12a2f922bc29f3823db147", + "0xbb7857ea19ce2219da8b56d9f122dec12350954956637d22fbbf3dc6c0095035", + "0x101d81520e4930f19e1b34fa311fd6f450c7bd86e202cd697db3daf31d4d798c", + "0xbda35b34387c99aefb07824170340f74fcd30a1af772bdcb5a9e522463c27353", + "0xcf58c295fd14e3c3f20c09364ffe8c3795a6d22df619306d2b994b6fe2722f8d", + "0x4b7c9cec04309ba11eb5da86059f0e8938a75b0424e8092f497c109642ec814c", + "0xd511235ff72ad38988169620fe2c460243dd4c13d417b988e1041827be2bb12d", + "0x974cfc4a8544be338646025f4d39c713e014cc5be62dd0f3cfb9246b32c80a78", + "0x53babe7d7d5caffedcd0d0c21ff848e37727943ae2a65e64481373e2a6dd262c", + "0x7b665d8953b500b2333b4c38a487ea7062ba39da3172059a00f8e5363e5f31ba", + "0x7272e84ffd18192de05e48a8c70d59edb188ac20c6f8d15a39504ac3c97e5a6d", + "0x4acf9ab7279592e9ade2cf6c1a7056f4cc47e87590efb83e65a2bfb1e6e7eeb3", + "0xe561d381b81ebe807a5b6f6d69ebf9731714c23e3791d24b2499040d6c55f4a6", + "0xb16d0d24b292e3aa99f3099cfa0d901ec26ceabb053cdd9f3a117af51a49de19", + "0x2ae328cda46c345dd8f0b933088bf593ae8b1fac361aa99601d1762e2ac90617", + "0x44ddca8f9f9364cf1087a6247cb44fdeea0a61295776d963f3d23edd36e23594", + "0xcf915a7c9ef73efc8cbf91102bd52f00d3783e88e5ab0bf95db4b5a1e33b503d", + "0x250b3a636f4d598d20d56b0f46a11cbd075732256dff6230589d29c4364a6bee", + "0xb375f94b109b3d0da03e42345256ca237b5b1f546286acfdba509cd542b3a98d", + "0x5809f7f8ecaafd8e1d02c28ace57f8836f83124c326e96da9f1d2d1e601ed39a", + "0x53bca01a028f3c3a97d2f4a15709a432889b1b7a6c2f1662219300e66aeb8f28", + "0xb21e8fce04b1ae30a625c96e4c8b7abcd94689c4b6f6a056a6de6433c1b0bd58", + "0xa5caa4c55d2bc144ece32eb3c8398ffeb38394a99e35d8801c15d04aecb0bb21", + "0x09b632d469d3784edde7879da8114f69a919399707469a9297e0cc5c9cdc2951", + "0xb4b24f0c5e1c40a2ed10c396f0551c968bcb55ee6b4ac089cd9e718a480fbd6a", + "0x70127a93adecf3e61c0cb18fb6fd612f1ace3715736a226fc0f059159d796132", + "0x4a8bb9f8ee5f3204e3afc0257990c5700b982c8054ad10046b6cb170bc08ca71", + "0x9a659ee9bf96cbc072fb7b88ac9d073ddc05b0121dab43eb061d022309bf734a", + "0x6f631601518a96ea7930c13296a728dc045e9b47d05dd3f8d6250d702c9d6ff7", + "0x9c553dcd627179319fac749bb355017d42a10fc157afedef0a5ba77f4977e294", + "0x91b75a9eca5cfa944c8d244adda83f9a58c777f8a465fed744be5bf368350862", + "0x4d857fb47b85ba7694cae17aaea56c2819f7b021793ca4afc3b02ac8dd3f64bf", + "0x1bfe4fb6bc3db15709145ae903d692c8b01e0911599828dd6360483fda984710", + "0x73d40d1f351caffbf77211e94bfb2deb4f28cdf3829ca995891d2b76c365ccda", + "0x23d3485a381f46f3da581df0ac278651f0f2f656100b89ebeb7cf9d9167e0576", + "0x34f08f94fc71f01776d8bcaabedb2823f05ff0db0de14775795bcaf21f1f7f80", + "0xf5b44239bd0a54da99cb57ed52cbc025aeb513f2b911d7dcc75913684cc375b4", + "0x02ee790129695e578155fe87e25ca56e08345a57e192c3376bd4d41ffd5393a8", + "0x6010731f6a611655c814190cdb89dc6e9a7e2a9918a57e2a5e8fa2fbf73c9536", + "0xdf1170c6ba3a97249ae24758a8b8caa7dae37e3032ce0cf29cb9b56d2ce7b2a6", + "0x47ac6feb375e71a84e40cc5b2df74fd6916917595d11036d540e34d3a3af23e5", + "0x382701489b69ba69a176b69643f04658b8736723fae6c9d7c320ddfb80bc8056", + "0x594c4d10005ac5790b40fd1a2d9158e5a9057b50bb065259a0300ca58be31f37", + "0x0a5e7681273035d3f9aa6ed950dcb58f2e95434a5222dcd29767d1cbb43cfff5", + "0x595cd0862d762b0858ac90eaf7ce8cbdbe29bf61d7677450628b5c9c7d19fab0", + "0x6664ea6e3973f7391b56e5e36c8f00083178c98e7b10cd578e4705c3edf84ed8", + "0x64d112d5268c1692e46fd03a60c0b55ab50b2250ea44035d23087f46a99d5869", + "0xed4a8f7ca137045c70a4bb4d4f885c8c5d49a00e36ceea3dd122fd85273ca928", + "0x6fe677d495bea619b8ebc4c197e95422945d514ea2a2db13fb9af23cdc903f93", + "0x4199e0bdadce3c396a735542b28331caf53ffe1e0ae09e689b5a9de9cc00a78a", + "0xbf3fcc7b677b7e43fadf7eb0f0dd528b370aa4c59809db61f1d4c532b380fe74", + "0xdf5ddfec26facba7dd79bf66c7ee24658b8679c3c459bdb3915462a44582a32c", + "0x0299d577b8b7caad49ae95ed285d7d692e2311c63c2e32e1a5779e60f053fde3", + "0x3d8c7e7f526e4f2fbd62ca46eb58ac62c10773de914b78d16f7163e0f57e8c3b", + "0x978c639b15a3d3abbadbbf6b8c40ad801f43de62769f1dac1fc427685adcadef", + "0x3a3a896582381ebd0b04c1108130a64b2b74815787e36af7b2a3b3a1790db2d2", + "0xdd89abddd4251a9db97529f090b9deb1ba07a3199dfb8dc12060c05ecc25e0d1", + "0xb6409fe188499c3def5b918c74f32fb9691e6e413db4fefc743a02b9eb80ce16", + "0x813db7c51991174a300bb72374d6eaac9f80001db13ea6810e4a6b960099f2d2", + "0xf06d9a4ea4695e3c6f8a1bec6662e7b736e7d893f21f2d6183e8cece7dd73841", + "0xbb4ec84228177776029570622b20d4dd534e790d87ee09a6e18c6ab22580ba8a", + "0xf1b00edc4e0fe0a373cbd831d9320d8e13e739f7ae06a8c9d820ac92580da941", + "0x7a51421be07dd50869cc8afbb2dd66a7e7db45003e1d8eab8e91b679c1c05264", + "0xa9d071f6a43ef2226084c332043900bb62afed1218cd31185caa3e90ac2b9b7f", + "0x893d4380ce9848d1a4b844bd6cead6adf5913665c6a88a692d077e2832ded240", + "0xfe498015c26e225385fdd8bae7aa2a7f5efe10a7fc85120d79545ec831753329", + "0xbdfe3d57f426dac0fb3bb596e15fecae7e527f7b0262f18400e20bd2fc7ca993", + "0x232993df694443c847aaed0ee2567afc58df8eb488da9b3aa7da9e4b5ab20a85", + "0x649a15a28262938bf8dd453d2ff46edb8b6b6f7891114d14479d2663f9f4a8c6", + "0x7f7b4cbd8ddce3bedb556749f1651978e7d3582384ef2631457f478b1d3a5ed6", + "0x8aaf9b8941fe4650f9dfa277347af75a1f848b4916e48d68f8fc00e485ca68b0", + "0x1603df32f54a824278d755beaf4835035872c2de2bc53ca2d5e905f79fa958b4", + "0x36ad61764637baf1898f839eedeeb65664e18b7f6e4592eb5777163ef18b96c2", + "0x3579ba11bd46419a8159ee741b34d4b27c166d276965a6c30b18811d8f8143f8", + "0xb9214f621b83a11d3b69a9ddfc922eb90a04bd5aa292455840a4c402926e70b9", + "0x4f063c5fe8da941a121b8a44260ea447e29982994f72ae5ac04df67ede36a72a", + "0xe25dc9280170e10e8852ff98b6a2a897ee944ea747cf5d2b922dc74153f859e5", + "0xf71b50baf2a61ba1d696fb221ead68aa1acf2577429f991de1ec698e523827c4", + "0x2154c86c8172ca00c8db5942fe0ab40ce265d05ef337ec44370119283899a638", + "0xade222e223b78d30384f5bd1da262ad98c88020cc75137ca75c76d28101c0e26", + "0xe1e918fa7934af5df5d965bf4f86c9f91e2ce13be2290dae0869533caa36c565", + "0xc5b3f9314aac2562729bf2e34870146d596b2dab9c3c7aea8c7ec7f1af5a390d", + "0x9bbae7dbc4cac27a34d1ca98b5f977dfa30a93155dd64f112671be2cd80b6754", + "0x655a3fb005dc8b644fcec1fadbc39b1180090e3189c5c3e5ec98ee18bb536d66", + "0xd5932ef49c5ad2ce0458ad2d8df8ed8a87960878005146b208cb4244e1767b57", + "0x40bd814fa9af55dc05fb5fd12ca3988c0ee50e682ea345801973fa5ba9ca6bba", + "0x4763fa313716e803ad8cf74bd38601b4ea2fc1c4c2fbab39fe087f035ba080f8", + "0x21fb5ab3aabe5cb7505a869dc36011828eca6d9463a516ffe69eb1bfedf59841", + "0x5bbac80a4a6c79734f44a7bd9378fb6987f15e6e6b9188fbb3c11f6683bc470d", + "0x9be4c45eed642949c4150fb6bd1efc0e882b59c62c775a674543dfab61c6fd42", + "0x9235afa965c17894cbc695b3f68b720f83d39510d77a0cd8c78d5245329a8779", + "0x857fb779176958df82499a479e6ae5902e7282654d328df03821af0ca8b59be8", + "0xca36e0ac8131e8f3bd87bc040531121bf11c631fc32c18cbefa8de6d01f298f3", + "0x6505aeddc7646475822ce3d769220438a777354021638ea566348dee0a165e45", + "0xbc33b2b858b7df7eaab1abc264592895c5136a1ad3aebfdae2f840234c66260e", + "0xee7292e7b49047a9ff4fa3e43f647c50be790d62390390592c86748ee8459871", + "0x1cda8852b96a643ff338cc8deed7c2ce19a7b8205cf2832dcce5038e923bbff9", + "0xaa241c0d4b857d1a4a91ba30e0a65f8e2b633304872cbff51faffacacc591df1", + "0xa6e936f3b973f5ed15d52db1da50ad88cf3dbe5b5439d7f82f4600033eab1efb", + "0xc95d857237fc769ca688ad6f7a6c4f9752b88220adf5a570ad47a554e4f8028f", + "0x5e3a4f9ab502afcc73559ddba1f67f15d86b1297b6cca5f5887b252e5e85061f", + "0x4e97cfd0de17261d55e11f87a93bc3b4f6d785846480186dd2d933e92d9328ed", + "0x57e63d324ed38ee8c495f552160dd2cfddaed2e437a654bbc043267293d4e640", + "0x5d181ed501be60ae35cbe35af47bf39c3cba9189891097929004c344e145c7b4", + "0x6f6f83dbdb81e0b2025bab157ef184a613de73b11e7152b12a428b5c0e4e82f5", + "0xdd7849d521a74d63b53686d8a1239fac83b560040da057d6b6089b05d7f6716e", + "0x6f1abea33b3c1443d9f23937f8e3772f62ea73aca230e231a179451d877e2fe9", + "0xf7bda70c1997b15cd0f6735528f6ed53f8cb2aa5c8967640397c62778c000397", + "0x9b74929e4becf748b0cb50f89137d01825eb0d6a78297e8c428c0ab978daf0b7", + "0x5c77416ba88a4db2aaa7a9d21af4c83a0e697b6faf8a221cea0258200a82c72a", + "0xa93635b3eaeb5d41212e7572872af405ae6b568428fb14ca4ca452223ae46fd4", + "0x666eb4f52a8692e0526ffe979d1629ad44ffb30eb20905ed3b6780e298f8098c", + "0xa79d0bf2c38898f9fe5fc8ccc6795fca02c2a8fc9afed24c8d03ba12010609b2", + "0xb71f455b2cdf5de44eff1989c6f75cc75c9b72172e3c442d1a57b8ed77229617", + "0x339be2274b9d982cd6b95a087261c72ef5efbdb07f30eb47e8f66c2181c90274", + "0x23f4138c911f9412c934ec34af63985c0480576006e543afed7a135e1a58fc83", + "0xf3f0cea61c47e6549f561cb7a6a652e93153f6755f936a9916eaee595dce9f01", + "0x7bae637b2828adb593a0149918a436a17aa566ec612e73959a548ec232c5eb80", + "0x3da9a0249ab96ae5bf598944c6035617af61c7e39a075db6e43d7fd844122883", + "0x6d810d2ec9d3c038df345a4c6684bad3c9965c96022233ff6d6a6026a3f4801a", + "0x1b41e73c92e8834e7890d0bd44f9cad9404d66f8612aec03c9e1e11174f9a820", + "0xf223afeccb0d734a5ec3ca3b67736d3953ecdd7e6efe7d798c305d2188c8bb83", + "0x9203c957f9b9fd589c1e5ab0d04603bacdff84e5303d05d4afe32ce3654dbbe9", + "0x5ca3d0c6d19e0cbbb70ff4f4edbf1da868f52ad83a79d4ea7b083f8f9d288954", + "0x7a8ddcbc6fe742e74bb1891d8b909d69104d4cf7f8636bfdf35d8e9aaeab311a", + "0x1c9216bbac9a4a36d2d832bf64291d1eb26b73a2b80515bf6bba5908c3fe369b", + "0xd61fd3bed060e4792c41d6c67dcf98982656b52e8c2d65cb57475af8c7f1e896", + "0x44570ee8a7f1f5d3b9a0454a9cc2f9475860d16b99408590f9d25e7774c1a0a3", + "0x957622f2012498e8d69a02c749e24a7e35317a69a681f058556a6a163ee11dfe", + "0x2af72c1cacbef7f08ad420b14c08ef4042f3a66007c909257bf367d28c66de60", + "0x07524626f722c5a5b4b456d7e47cfc6550a8df2097976dcc2420d1b2553187d8", + "0x1c98ff55c8c79cd16e84e6acc2d3416c086ddd0806710549a2f6b086e1e01a7c", + "0x815af035088d0885bf1b7f416bf72c5d1bb1de95f3ad6a6f9bab8639a52da1f2", + "0x2f5d59355dd667b869ee9531012c152e860e8113870205decd51aee492957e86", + "0x59c3514572c1eb6f6d6c03b47fb3540d3d89d7b2127870c25b52b8ef213e08d9", + "0xe961f7f29c3ac5c08c2e10de43627c97aa8bde9a0738ba6b660292eaa4e80002", + "0x152e38f972dbbf548af71d675273045f0a588fc985e66239265ec8098b254567", + "0x82cdef304e77aca4d42f8eed5bbf19261c45156584375f805a7ed8f877d4e141", + "0xc31ccc8d2356070e08c1437ffbf039b75771af80a104a917f3873a6ca3fc421e", + "0xd75cf4cb3655d2b1d1d3a705e1ae1dbacf9842d776e8dea8d8a030626cc2ca32", + "0x69abe9dfabf016c4b6b183abf8eb0ad735596b17294e23308625455460baecd8", + "0x2bc6bf129cf40f28dd92295246cf33a76c8fb55a8bc14e1e8b16c94a3c3c90f7", + "0x90d019c5b1dbb9021e2535b8a66eb5605bb9760afbbbf48d01ea3db61b53eefe", + "0x4d3a5f19f054b9fb9289ff9d35bb152d132b45f45ca6f179e953d11b9fd9a3c6", + "0x50e303e7f20b1c17c1e26636458e01469af17974dc5c60fd1a5051fea0946f37", + "0x1fde8d75925df1e74ce6be6847dd2c4b0a245d8a91c3028b20080610e0fe506d", + "0xbdb4ba38077a96090396f588f8d36a4e97489632a55ec651af74f5fa7648b273", + "0x74a02c652cb792fd3a0eba609d4a9f1e2a78058c15557a824c500db4199dc450", + "0x52115874b3cc0bf3e77d7f71a7212054e1238875c35807d613f6924ed2a849ad", + "0xefee113af1a8d66a1c5bf88ae26fa6407fc175051d18cc9ab913c20d7f74b506", + "0x839d2d8ed57f162713f59b051f921ba7b92b0592bd789cdc6003e1af79e554ca", + "0xaa2aedc18c460d63643ae59db6d61c1d6ae16cabf45c031d551db8f84981bbc4", + "0xdfc5bea64b76248d03fe7bb45f7400401ab0005e64b53f8aa95133e33f4bbc39", + "0x018d8ca1bbb5ab08496bce578274e8331ff4428209ec1477aa716b0773ce46fc", + "0x85981d6bad78a8a60af73cd73a51e36d8140f1c4f085bc0d72c634e80dac8863", + "0x34c98d639e313a0e57b44532562ab151281f585a2d89ecad426c4ae65c7b825e", + "0x45575aaa81d8f826ab3e5267da282b3fffedf78f78d83e5baa05de084c42db7d", + "0x1639fff5ed81d7210fd8caf36ea2fa8805a3a8def3aec3756287f3c9f0fd5885", + "0x83bb0d3111fd83b7976755fbe5c0c5cb22d60e3838965ed8f904290ed7fcb143", + "0x2d60ee867e4861e402d6c950a60546246cceae3222a5848bd654c80120223ba1", + "0x2548cf995dfbae9d1f7310c0388ecd0f32efe026788809011cd30f1c6e0b10da", + "0x66b9856b6383dab8aba984386c41b4f241a0d3e4afc316d506aa4c03a35fca97", + "0x1ff5b7ec05103b794d396decb23ba6af0a368376091f089ae5bd100e1746112c", + "0x2b647ebad2672bfe9db3ddaf01c3ada6fd6b0893a40208e82f001df481e84774", + "0x62125466719ce9d9a05b6bda17cf4101e700da5153de709ef344966efd7eb9b6", + "0x44c16aab32301dfbc38da7c39bafdaf81008f35870dea956ca3134eba20872e2", + "0x9a3d50dd6e004788bf4cf35f870d4bee43711023a98bf2a951dca3f498f8aefc", + "0xaa8fd147929a477eae4a8e95460377b71ded3ede4d67a9498dc7994a61059fa3", + "0x353ae62b36f853d998ec17882d0c696b2b144314ee600984da52ac46cc032293", + "0x322ca98f7b59c53bccef7a930813a9a3981a8f7c04a3d258a068cd78b826966e", + "0xa01b1e4e55700ffa2f7c18823efe5db8600150d1036428c66499fa466775e374", + "0x47294cf401bb495ce83cde719732ef03a4cc024bf0b798fb27206145102e8800", + "0xf4ba4a10093fd10c915505f83f1155c3d03fadf5a93d4e6ec43b81194ae63ee3", + "0xabba933e825e26e1c859aff44dbd52d56c94161b3eb0713a7ed8b08e8908e531", + "0x2866d4caf5feb0ef63bcbd2be2798f20e39061149148b21e29b27743d75ae862", + "0x4bbdee74c3eaa5199fbded296288c2b1da913a010020fb18abce81f437d9110b", + "0x1d6a41403431620603861a0b7aecb8673de244997941ecf820f733f848e3aa73", + "0x7dbc7aca299d9beed85e894748396df2ef89b11780ac46c010e2831060614700", + "0xfd5733ac7c11e6df5781c182ab96a81de26f1a525c9460cbc91f8913a74fcd89", + "0x16f053bbc75e667909e41932bf9bf88b7b1df2a57305b370a3aff82f88e0d170", + "0x65eb524b893a93be79ba0eacad03f99bc8e988bb653fc38a2120e0cbbc4f540e", + "0xfca016a8ab04b5a8159a1cea1fc24ee11a4603a4cf8b1d006c7bb2a5d0efa4e9", + "0x28ad700e8522e9c112b17fadc5c850f90bb6c570f5668f8ab5cba75f7c529dfd", + "0x942c957a94b3a79d293a810bcf846bf2d324d165853ddb76dd8b0e64c273a26c", + "0xc516ce6040542a26a0244f11adf2f67958744a182c6954c65a66ace5891f0f96", + "0x25d0562ea3b2e896a5638bea82dc563eee5761c266b46c1cf482457f9c8b3723", + "0x7ea8c0bb8d930608f0e567277d6fc113298f2bcebdf74bb000cb46db4e5ca2a3", + "0x378bbc463cc66c0e1f133646b0f8438971bc437b19a9d56ab19588b21f75783b", + "0xabaa8e63ed9c06dc4c9e47ebffc9f14b1aa5c17eb95c58286811a1e9985fc65b", + "0x49027c2c7563b5fed0456855330a145fa7a269639700217cd867647ab8581956", + "0x21987c41afe845f104b8e85c80e4a59b0b33595d527984cba135e4227d7e82f2", + "0x189916114d1dbcfb23a7d8cdf85dc51538c4c27c65ba2c460daca6f082e47172", + "0x3d129847ada55294a6bc53152e404d310a202c99b2152cff594eddd042d08180", + "0x0e1f365953f385a6c7caf50da99d30a98cfdb47f20b93a3dd709247025d2a1aa", + "0xe84afd14fb3f7dbf9b8939cc4f18f206a9ea5aad54635635c60c8c2ac4dd2c6b", + "0xe7fc09b90379dd24f801ab160f9c7f9749dbbeb46f931a11ea420cf8cebb7a2f", + "0x3ce6bef2daec36693e5c4a0c14251b3db593f3bf0ce283477ec6b30485d733cb", + "0xa4b708fc8a75b47025ddc19a77d5dcb8d8ac1454f7a9694f3aa01c4df00465b9", + "0x1ceca14bcb6d65d6fd5ae94e0de634e15e5d7f8aa50194501819b0ae0c068ea0", + "0xb55112f6afe3149b5f091778b5604937f7ee6b5fdf0720562c835d3bb45ced76", + "0xff184ffafe405d256a3f0546657f6501a1caea3b8606cd018ef117bd3c11e887", + "0xb72256c8a526eaa59149069fef825ed63c3c143af1970544403bdcecb3389df1", + "0x882876055c808f5bab7fa9e044f20d8f71eef2b7cfd4977b6ee662d9c92cbddf", + "0x820a8af89f3b94362f25af0becdc085c8fa33ae4e97d17b8b317fbea45f73a69", + "0x4ae5e4d960a7feb07011a4f1a3d777bbc44b76c4f8a19cbfd230774b8d50a9b6", + "0xa88d2272858ebab899605ddb0a2415f54165728933ab904ad3937886ae3fecf6", + "0x473c60b52c9fb8a992553610f9e6022d1f0529e7e934ef9834e5cf9964b6a2f9", + "0x20a980f0cb4fb934938e8d659d71f9c2967f4645aa9b26101701fc4badea99f8", + "0x4e031872723958ba3fce617e03155a8afe05e8f41ad340a083555980d3ed5def", + "0xd2b8cbe4d15aeb9a6afc0098dd1c0bca89ccfb3413260d7615f383673d306066", + "0xc51755c7da3092d363fb5630b7fe079111ca487d7c1c60105bdc7f3f15c31ed1", + "0xeb88cfdd76c72c37461b91a4ce33d40a5f3e588444e9fae4cdb70f156b4e35a8", + "0xaba33f1bf7f0f135f55f9308b3d93187616bf089edd194fa8940e815d8473c4c", + "0xc84f4380f2e0bf235fa1d9afca906d7546deb3bbf2c2b1dfa8dc553bb8eeee2e", + "0xd327109912e972b55616e8a61871084924f07ce3d62a7101c6b21a4808ef3bc0", + "0xdd505c7d03cc4b36f6abaf34487d6785c7b2fdd5010f80b44ca5a68da81087cb", + "0x2126457a95a56de5b77800742ad87d8b2bc72b52e9441811a227ece18e7342f2", + "0xbf54ea1810a8c3b967dc65fd1d9a2dedacbb29fbfea243f3bfc3c1547694f6b9", + "0xcb4aff6a90581986933558b6d7ac3bbf393c2bfb7226f6c46b73aaebe994aa56", + "0x0fd30c5b566c72f91bffa9d64dc35a852deb037fc18a3586a2ee8d37df0416d6", + "0x8a896430429e098e8da7cb3f1499e1998675a8f82949ebac5962d53a0b8ad9f7", + "0x86e3aedce88b0f030a837e29aa6a17641bc501906f9bb577802d2b6ed69ffde4", + "0x1d94cda5cac1742a034e64c6d0eaf6c3f582aad257edef4564926ade03e756d3", + "0xb7d763ea700250996ccc61eabaa682614de94be23c7cdf21cda919983254d0b8", + "0xf5ac15b420e8fb20a7b959842ce69038584f59d60c4d81fcbb6407b08edfcfe8", + "0x1da7cf7a56695c099107329248c468d0ba75f5115c98cad7f7f274b7408d96dc", + "0x56f61f8a4b2d24067633ce81978eaeb5976d962170ffe4346fe8720ace43475b", + "0x165ca7eaf7c66eae916458bd565ba5835d59b5cf9d0895219fe79d6043c23d8c", + "0xb4f2280c8a531385e380130fa8e2c2969d6d9110d2a0a7251ab52ea28a13b408", + "0xbf678ae913f40bcf562883aa903575a25552549ec97765d89d4f45ff31e4cf13", + "0xd33ae0ca86fc0d54321a5234bb375fd30fd8131c91b5def7c1f1fa1128e330b7", + "0x8d80091c981a02912b5bcd3a72e84fbad7622ae35db8cb7091ac94c68b649f04", + "0xd9ab135bb2a17463386de8901193d2f15563167998a1e7390ed827b358c01df3", + "0xfc1120bc2b2eb0b3dc6e295cff50e13cf578fffbc095b3df91b9d2b733c258aa", + "0x3c93c6b677eabca9c81b1dce8ea6d163514dbbb0841bbdd45e2caef77c3cf10c", + "0x00c8d33a922842b92c7d49f72cff2a4e85e767be38c0f7c08f6cd67822b13299", + "0x4fbeaccc9a60e09b4c16bfd17155b6e8c938b8838f4e716e710dcaa706bc82a6", + "0xdc4c7108825d895169a2c190a5559cfc3ebe51b4eb0a84156dfac16668010a0b", + "0xd651d79191662e5dcb33089c09bbbe49e7a11ed0dee0c339c1e39e3fba327fff", + "0x42951cf456293ceda862aff7acc02d10b5ff8747df5e4d3f421e23a15ca48a96", + "0xde1029800868c36e545bfe2f4ab814fc523ec190493f62b89ef9b83f2865b340", + "0x9fc9c32a3d02e9fcdb1ce2a85905d94b15c2a2b435b100e82269c39e2b64d6ab", + "0xd91602748488b19a88e28b5d8cfd3e93842b36c33f4ed46ebf71ce277300d906", + "0xeeea8028539a4b942f34673391551e2c1eff9942cfa21eb3208d60dc63211e32", + "0xf0452e727a58c295006a993a6a424d601e37e94fbe82fde1bf3864f87dc2dde6", + "0x94de9551cebc76ea41da619b57312f497bb7c7c8b170f9135763586cc2c6d998", + "0xf7d38756858dac0a0639d8847961530cdc14867bb5c73ff458fd05beeb2f1fcb", + "0xb06c2c849c9ede3b639c1232cc907b1402b6e471651ad230b8f87fff0e4a3659", + "0xe9f2263a3ca75e3a64d4d816542bdccc6c3ad2eed68d26dd0bbfb9a88b355437", + "0x190b8e0146442028d381377b32cf66656c723074ad5db8d23bd594461d31c61d", + "0x6ddf702a922ad7494977bb52b841c9b96bca810d7e0f98950d240b7fd6e0e5a3", + "0xa34f8154fe4c387522e929b0cba7bffd9977dcd2ba8558b7c84148984fdd0298", + "0xb29fc28a02ef605cbc300d95b9be4c8ba9da38e1753c87e05b7d2697eb92edc9", + "0x9b56a26b60c102a7ef82192806b5849cb2bbab50bd5cec2b730bf067a0526c39", + "0x496bf3f0cdc1c29d6fea7be82c7d3b29d3c04973249b38162b3046c7b16ddb72", + "0x104655ee05077581ccb31443ca8b37ad060f261f399ebc9eaad0c96c5850b401", + "0x129aa924c4daeba67c405e09330a2070b8c39f84230e7438d7598da3155e0488", + "0xfcf8734e860bec77c864b406c0f82d808bb273d6708f7d4a8a6a6f7d18df6c63", + "0x192369a8a65ead8802f114e88d18bb3ed807888d6c3dadd442824fc6bd6225f8", + "0x4433ce7b28e122ed62c93141beb6fb7699ea25d014744712eefd8d21e6a755a8", + "0x7305023763e475124a492c6dc3d398e7ad367f2408ca71fe6e4ee53b36c141cf", + "0x32ef5faedc1387f2817421370d04333f780afe22073fb33db2e4aa70fd412ab2", + "0xbe867abda61c69f7261b4ed6458510b5276065b86257f6485d8ffde641d35299", + "0x9853db4c991e416518f6ecbf61a1ef776018d3791b01dc6eac1cf3eb8622e7ec", + "0x856be0853990d085ce30dc0eb521fe8262dffce6e35dfe6fa3775a8bbfc3b25c", + "0xd376c55cc28169b157122f1aff22e2112905e565ea8307fc643fd2c47065af56", + "0xc2f6c43da754b56230cf60c2840071361ad56ad6fbd68a03a2fd1b0eb15ca2a0", + "0x37906e93157a450784e57bcc14be6b6d76f12743220da507eb833c56180025a8", + "0xb488d7b20d57d550e1375316b4942000a1d09c827be69f77155b246f3c5f97dc", + "0x2ecb40a1bb5857cdaa10b656d4c936fb977f23ad6bb8491041dc74598c823da9", + "0x4f0e607e432f4c57be98ee214918981260b97c4bc327e75d5511e4b4f5fcc78e", + "0xc29ca214f72997f8d899501f9d2280d4f02decb478ee7c9aa2ebc899c1324a44", + "0x0945f6abec3d0bed9306b548914e00ed396ea58b0f9d15ad0c114c63f77481d9", + "0xddc434f8b1cba2f117cf6cbe77a65ba9a1eed83d64bec37a2754afd06d092ba2", + "0x8faf71d5955fe6dfe177e32e4e5dc3f0ab24af2119c271336d36374246a1d2b0", + "0x2c67340a988962670df22c391cf1822a11058efe60a76c5c49120ae9cbd86b22", + "0xb8facb686bd29ed711149bae6443397ab9684dc06354144b7704f9538f9845b0", + "0x8abde10fbdfbeb3eaf0a5972c2c846ef2e51a48052c9b5a83e42d64800293187", + "0xf2e2fa68049aaec4592a160c21f61b64ad306eefc135e8d078fff2978bfbf7c4", + "0xfd627c79f4488199fd43ea544d9962bff8ad0526e1c1badd56d979b5a22c6c0e", + "0x99360ed6fbc280f420b58ba3b8bbf5a17198b2fefd276dcc9d7bcf6307926303", + "0x92d465f55ea16736d1c90fb6f4df6ae3a4a49f16f934ce0d78c34bec7803daf3", + "0x71ec9ce670989f496729f615fc4fdbe2d2fd8a8456622f355475e34bdd3d6d7d", + "0x266321c92359f7b1d055fc1cb192ef6baf0754e106eb8ea2ee8949f2fdf0dd39", + "0x49b996026e19597a96b7081476160b1456fb385dd9ddcbb2506179754d3f76c4", + "0x736e276b72048b6ee86a09517efa24055339b65b19ccce54d1f3c3cad8609ac8", + "0xde77118c74e281088080faa6bd3000cd9374a79be6546b904c68728be647633a", + "0x555da22fa51c916c6fd43b69700d7d89aeb615995f31d5fdf6052a080e825415", + "0xe89d12f061167c44233191b7efc52162304d0a29eeb00d8ec31c81e97fe89056", + "0xb993964525093810b3f778515578a82dab47335465dc93f7fda07522be00f437", + "0x0313adaa0d902f61cc7091277b0a017864a12d970cd8202595c4bd222bdfb84f", + "0x8cf1fc4851e26d059ff3f9ff069a4b614722b2eec552a0af24e724c8c63bbfbf", + "0x8bd510fb181f014948c0b05bbc603febcf73b500644168dcf93e02c147346446", + "0xd8db2156d178dc39f119614591b0de9ea13d218e7bfc96464a5891c35fc0bcb2", + "0x0683b8696ef15df99b5d405ed1e0d94a6d65d4090b1ced2b83151bc60ae3b5a5", + "0x011aa4025d891a607fe2eaebc5915a680a2602e44513d056c9ed58e75f637c15", + "0x0f01c17db5eef6ecf43daea976f4070e1d8e5b306685ebf3468266729ebbac56", + "0x782f326e5659295ae629c0621cb6adaf4ac0dc9fa1fa12e57925b9bc4b26f23c", + "0x3130a57cfe59273e375b9fd196bd88be41736aae05b4d03031118c5d305a3a41", + "0x5c62038847ee4b4c25d2be78170abddbf61ee2516cce6d344cf0f75bb2be1f06", + "0xf8f5b516c78f36ade585373b6c7da16814d8af35c9a70b4a6793fe17cd9fdc82", + "0x71eb6ef6460a56459c7908024861b9e6d8ffbc254b7c2241a6fab2c0f174157c", + "0x6e5f7c689c01d210bacad7e9a9b076f5ba6a6fc372f13954449ad77c24caf866", + "0x80dad7b4dbd0daf18150f2f3122be7a8e4d16f5a5027c0a1b61982005fb5d368", + "0xd73f2389c67cffd6db8aeb8e52e731e33f6dcf444ad11128f522bda034b2c398", + "0xd407a6f87f786e8ef9f889b3c2c3c8daeddc5d0a070e9bfb1cde440f6dcfaca9", + "0x9295b316f360dbd0e09a3454647976238d211a2289d1156bec2bf3f798f50cad", + "0x55df145cd0f83ba99f010ef26d7dada7a6ded5f1c29ecc153c391924f913452c", + "0x4a788c4706e136e47351c43e495af7717aa16631e39e79e043fe7090051f0446", + "0xbc4bf580275e633314e6dc6f4ddafe6d2a09863723165874c6b98ad44e94b013", + "0xc6ebd67d429f4b18e6c5e641c258d73778edfc72f8bacd474b0f16589c439dd4", + "0xccfc82517dc165dea88ddeab8a4d3511502091c3aecbce08a777a97bb8494a96", + "0x27be117f650da24a997187fe5cf5657b0f987ec2ff9d37cae9dbccb25e7c2ee2", + "0x6b50e061fb7444d605740653ba4adf680c0529a86c8fde2698a0fe1d4dff99f3", + "0x84cc3225a8631f7a392fdda8f40224e81c58dc94e6a9b0e9bf524e6958d4fe27", + "0x75446d5aa1e56f93be46c346aa0fd65eb245d4c51a69430e71511c98f93f2f07", + "0x0ca607c04854cdae7eeef5b98ccb328c7562805de376896c030a2249a1eb2f27", + "0x19d60083b08b88f032c48499b55fb174b85265ec0864a170adb7211da7ecf4a5", + "0x83fd2ff863ed3f07d79a122b3ba2ff409701d318a96ffc6837818bb39f225791", + "0xa905e0cf1ce1e96a3408ef597cc0035e52efe4f31b8c1f67e0417fc85cd90f0f", + "0x9e37d72a520fce59f9bb4205706eecec4aec6c3ab1e4fad12c524af6e03ab04d", + "0x8bdad187d485f01c4a21f9daab74619a03ca74098597483654ca4d12c14e8405", + "0x0237a5e72d632a55768953b00dc9570669dff115a609358bdb4beb30e982dcc2", + "0xc98568b98dc530fd9f9bd743471bbebd75ba864446f03b25b1bff690fa7be955", + "0x3467610bb99fb0e20e8c0814e163e93a11ec4cbd411e8e98028eed9ab0232e84", + "0x2e6dc674474f9a33b3584c340a11bc88b5aba7a8c9d3f043bd2e21e49eab2882", + "0x80febd89e3c7aeee890716bbf2c36e6d1d3544ba8ae2978b52076ef5240a324a", + "0x04b25c86fef57f82b52e2419087be1f831405b7f0ac9a26fadd60129c6fe3468", + "0xb0efe392fc4b602e184244f9e5abc07ee0732b373d5230d22e208d03579ed4ef", + "0x7ab3ee1c927c7df1d2beb81d1f42c6bca06a07682c783397cf13677871fc7121", + "0xc381f44585b4b9ec15c8df04d9274a11add8a1e7b78fcd1451fbbd475aff7e3b", + "0xf2367b8aa5b24d8fb522f62a579701886ea65c35e1cbf2c555fda9806d95f874", + "0x1ada7eb36bf2c84295ac6ea4706f226f6c1ec15bada1fc46383aef8cd1be6188", + "0x892c674ad52bf2f369f9f805bb43855727581f05e1269283f2e94207f17ba50d", + "0x219cb167b56de6873416b67196677eb6958f5d7870746c924278eff52236db71", + "0x1c32508cc1bc5778a62e5bf8946bd8e828c7a686034baa88ae2053fb8479c684", + "0x980b97d202dcfd4cdbb68b8c400aa01936ee07d3fa1d77657a36e2ffaaeed2e4", + "0x9ebb11463e071ee0fb021a2e5434f6a7573cbfa7c23d258ca213ec8f0aad0473", + "0x6382494bbea3be164236e73110ece3a8173e6367574683adfb65c9a6752767ac", + "0x75c5d16e853ec81b98e42c18091e5b37fac5a8c430c8a7d8085f32019b9218a3", + "0xe4e5c3e999d2d1d7fee1f3c5e8e4ba7947acc5822fd7ddeb33e0e1f72a10ad0a", + "0xc6a8365d451e63a8703dc1a18ce2145f0ec4d147989463cb833acb498d608716", + "0xf3295ed67aee5af9c563f8432bbe998d2e4a4b7478dc4b5ca3d6497b855071e8", + "0x19a963e9cf2c63e4eabd43bc9cb7b55fd1a39c48ec43cec57abd7c9c8a3cf7fb", + "0x3f2171b0b249886c04ed0a6c2a52a75ced16b762df269033dc42cc6cd9587784", + "0x49058c86c027174311bbc29c899de11f512c7c3cd381bd13ee0b67c16171a6ee", + "0x95bcfdc7b6a1aa7cc8117ec7c455e9ab0a5c6f6268716662286e05b8a4a266e7", + "0xdae56c3d74e96434cad041ebcc79ee65d5ff23f1ed42b8c5161a0aa0df56252d", + "0x66ad9e2ed518cdf607d04af1725bc9f9f65c483a714df3671fd2ec9f0550d1fc", + "0x5fd4803b46ab00ccc54c466d06bad25e667f3dc77d3b29becb159ae6e035eacf", + "0x64e6c02f461f404563530a4dc1e2889045ef01aa5d859dd0a0e227901ea4c3ea", + "0xa9d5641dadf7e914adf3c955be8102772bee92d7014630027356ef2db9517576", + "0xb9298e7fbf0d149c71822427445c8f21e9acf4f2b62ace8cbaa4fb25c457a69f", + "0x878a9782545d9a82d55345395fb679faf46eeab5ddf20c2fa900bba202799b75", + "0x1295525f04647b7ec499674f3f9a4a3b42ffc60e623dd0e5aca153522be1eff9", + "0xecaf4df15195a8b1704e7bed6522b1e31492c2a7ce3284ba51e9a0e4c6cdbe1f", + "0x08e2b95ecba646450a01f7498de28263707402f4913ee256a7052e69b048a206", + "0xa571fa5eb7c3772046e519245d767829000d9f6a8977efece2ec9df7c74a2062", + "0x18dca3e186d48efa7ce9fa91a57c4c57fde3837bf0f02bd5b146e736ab668064", + "0xf2ec89fb07fc6699c13ac5f4e719e9e1b605c3658fc7cd1700b89890bca9ece7", + "0xf97affa93056b828c8da240e0e321003d1c6ad0f4f54efcdfee83aad94ee9cc6", + "0xd8c52eb2fbd76aba1c7ff4efa42893ca14a9fd94773fd1a1ac738eb47d0201b2", + "0x454f5998a9a1cc89c7c37e98cfec06dc17b7efa41ede099da102a936d82c3261", + "0xea4a81286d4393650d4987548e3d4fcc9dc7625595f230e2be26b942b65caa9e", + "0xf36c6e3e1c5028da410e450510e875dc767086248614afe200652fb1b429e2f9", + "0x1bf82acd8752aea0982178ceb66f36c39195900d6fca5a894512022dbf0ba92a", + "0x4f86eb1d46e026a405ca0f052c90eb69eb94baa38999481cd708e506db9f39b1", + "0xcf257ec9830edb1f737fe1ffcdb55ab15940b89b64b3c8bbfaeb46980eb9ef2a", + "0xbc597fd2e39970986dcb4bcd9415469350b9a29455bc56b9aec100f76e5fdaa3", + "0xe4a31c71270c33c349b2178e6092dfa615c4d1f28afe8f7f01d813d62be68e15", + "0xd8d5970bf9d5279934cb75d165396d24d011390145d484367e5d8550ec1c4384", + "0xc9949547745726079f4f7f0de72638404779caf7a20d142f94b1760e6a92e419", + "0xe34911da13f55318ea21a079928ea8b656f8288963dc0a4916ba9dc7cced28d8", + "0xb4c634a53970b23abab94e4a58b964f56f3c7e5bbf5b19f4ab75030d78e9d9e4", + "0x5e8083d69b99672512ebda0313c466ad378812ab5f6c9cde18f14501a858d119", + "0x8cd0b9cefb3c7fe58f45433b0c4d10c520f621f3a43abf1f434c41fbab19a69a", + "0x43d4894915ef468d697b66db956d60c1c509acd38a54a446b5e6808926122b59", + "0x67376f396c8db678b1d3994b1ae093e674904655f9d0ec091b01e9ffaee67949", + "0xf07cbcfcb7e72ddb5a65af759de424c7f7aebe90a7a9b4428da7989e89109918", + "0x3d39cfa83faab404b2fad8e724c63122f18aa68dfd55f386074cca72df3e9e39", + "0xbd0c09ae57a61976347e2464330f924d96d8a994b853538e10e789d63f2e38a1", + "0x7d89c8909234ed989ad75987dbaf8bd9eaa32bf39439ba10fabd6fd85e34c81e", + "0x8dc52739b56039a34f43c5541a0ae3635e12c0e6131d158287d09ef65de162f3", + "0x17da92bb0eeb342489acf8bb5b6a3f2b58793a6ec23ffaf6e4d2221ff074a88d", + "0xd54c2385e019684a2c45ea58b363ef17f6444a776b8794c812293b6e1e6190b8", + "0x31a5e3f7b2b7ce9f2d4eaad772b0d1bc159dff257d6cb6acd812c79d3ced5fdc", + "0xe3b59161d5cfc5b83585742a38e49b95bea6be0643826d215b4033a031672435", + "0x7ac85e7f5a2d4da7a61fb57e7f4c47368aa588c2d1f7654dc5be2a0fb8af6450", + "0x528322398c05ec6eee0f552af35399bf0099e7e5121fd6aa0c8d7b7f3b54b700", + "0x97eba37196a56f5957e7cdd7f1442a064578856bc0fe66e5eb2b79333e3fa80c", + "0x1b5a1968fe6b703af2f6cad0efbd5a7acdc4de3bcce3942a728a0888119fee66", + "0x5b7320a39d065b46db1c967686462dfdf9836ad12fbb92b3f5ccaa21b77472bf", + "0x0b9786b7f77183137d53046d4f1e02cd7789e17ae384a1e5ca408b365f22a64e", + "0xd1e2464b841a4c83a7c6b3e5fcdd5abb5e9fceba2a70670e57d20cc21e86c60b", + "0x2e39843788886f26c6c0fb725b80edc7e49bd514a8bc4cc11a6d6c2154a0ada6", + "0x3bd3ebd07405c81be753d20a91ce5035dd42a7877b8a0bdb3e88b9cca9a6f03d", + "0x5e8f9679a586c4c1cf80994f57692306ef5d77261e8afc74c7fa700df145549a", + "0xdfb8a1fc1e9790e184ca9fb45ac767736cd00e9b48464a7ddc64e00b1d59cd8d", + "0x83683b89b8582f0d16cbeadeca746f72cf0c880e858f2dfd7da92f168ee82492", + "0xe700539698ba753de52451ddad5a13166f0f0e8daa03a14f33d921be212e92ed", + "0x3a2a298d2c735eae95e7abf5d085f40db6fac6d4723c7db6f21ab9f609e48aae", + "0x2f6688e8c673d5437cd5918c25748f304fd031c028ce64741635a20087b6c804", + "0x69bd6b1619fed819fc79a3885d0438860b6a1168562920cc7aaa69b5ca9f7c73", + "0x41b86c5a3f56afc55bc041407f9c57c3a31ce9ae4444907450d208a7cdf293c1", + "0x4013b6c861de411ead91dbaab74e01e1fe356ec918ca373babb0af3f08894621", + "0x0aa717284586e8c9a29a1bcc62b3201cb93ab3142ee1148c36ee03599cb93a4c", + "0x9995c3bcde59f21225f4ad25b755a0eaf1efc1ad7b1072a74daa95c0718c3cb6", + "0x5e4f913265437c3bcc168d0cf30d1dfa0c4d7089dea04fd342229064eb2aab72", + "0x329ad22298880acd243dea2a66c158981836eb7174fa0af0288cf8127a88def4", + "0xeb8487b148d09641434b44989e5ca2b78eaaf417c21e81924b930b719cb5835d", + "0x50e22495ea35fe31310e697d9411f046f39711af66c5feb2a247d8686b8bf42b", + "0x240e197c6dce292b3519ba051ec2086b8a65b2c6f64b45b8415f5d6bc9746b05", + "0xce2bc021ed9968289a6006ba43db273f4e48c707496bfd47b5e5873b34d83948", + "0x4328972b05ba99549e62143ec88155da2957ebc99c13a5b0e8875094816ef722", + "0x069694ea3564577c481c6d13ee708a6390c91ae0d7e5161c5a1d5df7c6411835", + "0x5ba0adcf28d08ae37ff332b6b3cf9936b3c3f186d5f6320b613a23d50ce60a33", + "0xd8ab29d63979397198fe848cf765c5597c2eb3c6041f1eedc468611ae8c5d3d5", + "0xf33e8a1949e6449212ef72e85a7966117c7ef9aa1dfe84773d7e6c140dbb04ee", + "0xa7acaf3eaebd68dd64e5260ad2b5f0331b0bb53cf04801e9e7156c0a10051758", + "0x422238a00660581cced8f22339f31a870a40aeda9dde7da9683d1a21dd2908dc", + "0x6bd677d662c3d70495b2220f1431e8f718d167af4a4a40db674d10087fa6ab55", + "0xb5529c2cc0cf8c7ef0b65a2892f54ed520f53786b4d8810cc266496c572c7b10", + "0xa32486e793343361fbfadfb518ea9d5665cafbe9ccfd74b5ef2cc7580e1a0fce", + "0x37dfa03e17a16b28f1476f1c919bfc80d5a1be7a407a482ecb48e6af076f7999", + "0x187508f2723ae932fb7d58467b6a911a5fc55691e3b17388dca731d9c6d44204", + "0x609244209cbb28c01db8ee91de0c9a1016c488308577ad0490fdc9307a4f5b4e", + "0xe9174dcab6cd2b2b9a3e76b6c53cb710c401042e7c5a6dac672b01ce8be28bd4", + "0x4f2514a74b4b2282813491052abce85faa2b9269093f4d97103c51a40dc76b05", + "0x2ed668bf7000e4678ae0bffd7dee0a1c6b48442d0fecafac499579c9a007824e", + "0x7fbdeab628e29ef68a6ab479c531c10e889aeaaf77a1fad71a3950f521bf6f62", + "0xe210703ac6917ea472b7948d012af8c9d38c1cb3adfe740a8153d5224a82f3b0", + "0xd964811c5059e36e6882205043d2abb42788353699b21737985c446cd6de0ffd", + "0xc443daafa54cd572cc8d5d73ed2fd50468ca03ab887a9c3d13623a8aa286779c", + "0x76fdbe8edd45f13473c2f66b47b097732bfc205670df6f76e82db4d11d619e30", + "0x1738680a5ebae85a7c0b2751607d12eebd79c8ec32bce83962d6a9a169ba1dd0", + "0x53a2c6ea539cc143483737cb25df343ee56f33e3522ff6c8dc7adb8e12a46a8d", + "0x8a941c9bc9764df6e85aa84ebfe71caae79222982753bf3f18b80bbb9bb1a38b", + "0x2ac680f1103cebb44a83306542af4cc6257b0e754a384a597cdf03a6dffe5c00", + "0xc09a1abaa624668a7ab4f133dc8f474a5a8768fa1db7c29cb6023ea31b4a8bf2", + "0x8ac174eeff1c4808fef286229ad378506dd8ddd5dbcacb1b0e5e84f3aeda250b", + "0x616b4382ce7ffa0becf8845dcf4910613e3ef6588b38b0fad247953fa97ea38e", + "0x88c79299dbf359fc4049b11e222c456465bccca4f508c72db2652a6e44ab55f3", + "0xdb0a112fc2fdcc9251fbf52ba11b2e83b1537f7b630f957d4cbd7dfa7c36a245", + "0x89b8ffa5f8a05e8ad8068838050615b9219713ab0948b6261ea9fdb0a5f38f98", + "0x38ea06257dee0b6ab02e4be1f6c3ee4293ad9c6a0b6f1bc69e5b1d407c55005a", + "0xe0f6ab26a96e2f22a59b08f74fe26bc10785402afcc8c94c6dedde479a431873", + "0x832ef874cde6db4712575d79bd668edc25deeb6c4f226cf7ccc3f912e87db13d", + "0x4aed293742469f00fb533db6362c1d2a5eed6bb7d7cfc1b53ac9be83e45e0f53", + "0x0da68825f03bde4c490f7f1ab67d1ed3236d6107dbe1b52b556e5d86b1be463a", + "0x98cfb08064c680b4ee8b2bfeaac148d8d6c9a0dd22a54843f1ae877777e30c99", + "0xf6fcb801d5377f9748bb731fe646d32bd109bc61a727d10b75d87832a07bd37c", + "0x4ddf1cbac58e896e322095d1b9708f265cbcfd6306b64f5f2f31932963bc8eb5", + "0x09dd2672506519f858bccbbe3c63a334cf363bb79a82479f0a4a17001ae250e6", + "0xd6bbe55021419cbb8d3849bb87a9e9dee5b3375a6402ac524829bc0091f8910f", + "0x49938744522cc307c4112bbfa78eead9cde39ed4130d8845541186f68942183c", + "0xb8600336562ea2da03b738ce0a2779c3e8259a125f087561c84e8363f751683b", + "0xc114b048bff7b60a69b85fdae32e56735aee7d0069164c4cf8fc2186759ff4be", + "0x4dccf6353a339c3e440b6335088e7187924f19ea84f330657c06a901ec5c238c", + "0x952ae4b4a40f64080be6a10b2bec0c61cf77de1cda3ee286eea869c7618b201c", + "0x1323be2cd3bc3c82e78f284838e1805980c647bea165dd2865d66913c1869497", + "0x200f73c6120945b7efbd3a1428e89e938eeb86ee5e56e8e6be322226258c46fa", + "0x9e31b65121ec0e17dea1505c5e369db7a5c52aa76b91e052de66e32758196d27", + "0x71fb9a9b00be44d17234a6a10e45b8fca37729a535a889e43ea7685298d29c14", + "0x25c02fbf9b6556a999d58d60cb7fedde54f55c4eb0e9200e5f592f5f3e43d626", + "0x6c63e364b76f36a3155173222c8abcee63e1b87da3a8cd23bb6d694c08a5da0d", + "0x38c12893f5f1edb9a2ede01283b35121deb16852aaef7e8b43265c8527c79654", + "0x519b12335c7e8c2687ace9ca60bb497a4bc7aad2293ee32739cf6b629f412dea", + "0xcf39e84d8290568ae6178b93799bd63fe6d6af02266b8fca2547be41d02f6620", + "0x4163363ebd8b88ce41f033d6eba5a16e69d8cb9604f58174ddde09e11c5aa064", + "0xdd5c3973422d53d0f598fe831a317787cd14d4b1263e7834092e1770d48c9032", + "0x7d95abda939e9d5a3a7e655fddcaecc35cef2d8479de377faa9f1d8a72889e95", + "0xf1e5795d60a5b1505bd1b566c166117f1e86e5eed9b897c25fe79a34d8e1e77b", + "0x8699e138e67bd3ea4e68fd753f3f18c89f0462e955d6de2b8f2b96e157348d8f", + "0xcd59ef26270898469966ab821dbceb1fd3319f6116dfd3d7449f628995992e55", + "0x23b8d4fd696be0fc8d0dd2666286075d2bb429da2c8722fcf658be7c1ab824ed", + "0x3a42d8aedcead663fbaaafd236527f5e19bcce2a372373d058d45e9ebc0c5f3d", + "0x0fee930a0215c59a4e18c29b276aeb6031b42e7f460d7578380e490dcfbfab19", + "0x6a0fb0b54c6e6f3e751537b11f31a374add228aa0c7986f0bf45012e57243859", + "0x6972c54616be1bf4a2b197c6711b92d195f8c3e62b7a65c92c16af81b3149165", + "0x377f90ae8ac2e9178be22fcfce9062b1fb72562f0d83626eaf7208dd1aea266d", + "0x83a57c84efe1d43a1a111c8c2dc4eaeaed50218645ca7cb93c9ab86d5390c753", + "0xe9bbc67796080e81b3a22b70c0cf5cc77222d931039146600cceb68d13b0be07", + "0xb193b4d9d156a216fa539bec05d563b668de5144835dd76f8df8280a741e07d9", + "0xd921f6f0a614dc74ac92f6a92618dae2e9f5db221a9e1db98d2a73f2b060e8c2", + "0xceeb9868f8420a462dd32de858ba1a8b5a89ce1504ccec5b3a13747cada00c14", + "0x9b3d130eaa0d944d29de26737f18a07d0394912457032ff9141fa3cad8dd7844", + "0x804d79666fa631e72b2e197d6cb31c335cf19eb6433afa549404e7e109b1db8a", + "0xdcf83600c2c01ee12928ddff55a0e459028a8f164a85ea8b4291aeca7ead680b", + "0xe35c02a3c506064a5bae4e5799a9b08ced0bbace38b380c2f23035a218279327", + "0xaea49248672e6d0fb0816dde405bebdcf6a02feb506e0a3acaa7cb1adcff542e", + "0x98be88108e6b34be67009a1cf9fedbe266bf9b3f15a4303cccdc454cf4247320", + "0x25532a8fec428d1144bf32a8ce394ace2ba7144c5220c6a20e8043684e9beeb8", + "0x75cce640489f646b55d4668d1df26dbcefe544fe645d68ec96dea1dbaecc592d", + "0xcabd124fd6e600ea135be94ac48df7f28ea930af1c4db59ae293508c80728361", + "0x01e774cf48bcde88ebc7ab4cfc3281929cbf121cf1f25103b9677f3fea86a229", + "0x1f8655715daadf4242c2570091d62c1384c7f549ae60eceb7b0182d2a0dd9d0d", + "0xb0de10978ed9c2fffbb29eb8cb5845a1b3172fb2d557c869b0027def24a0538b", + "0x9bbb2f8c326f06e3328f8ed6323d3d867cc01664328c9914aaf0cab064acf609", + "0xa01d2c068d69adddce7fdacdef0048eec546b6a2d15efc5833ed8b165defc006", + "0x1b8fe9a61489836e7e39451521a97dd082e1122abf8cc4572508a280f2b8c527", + "0x5bac8ac5a24c5d242efc9c98fb66c3e83bfbc717fe60bd35d7f8e7c40b7a0ba2", + "0x90c2504daacf35c200872e90a4094ec6ef051a07f98d0b16b9ef243b1a7749f5", + "0xffdf2db0220d565e7c61416cf99fbab61916494d4249a5a09d5189e2ac2bacf1", + "0x9f87cb76d4e93d7351206d1c22caabc825315f97813f8925b25a6c5bb3b29cb4", + "0x39db25477f3e74952063df35131bb545e9397848f4e40e4d522602c00b417b22", + "0xb088fc1aaab15ccba151534cf14fbb0a423fb9bbc79661e1a878b76c1ce05177", + "0x14762db2798e7a1f4c1cc81c5595d1ed5ed0cdf87318005a7766b6718421c993", + "0xdfd0a835ed3ba4e19f8588bb0317e110c428e0fa054d9eedd5e020e0171005d6", + "0x2245f16ebd09dd8e8f770785e445daa5c80f5d2afd6c7e819c160c0f87418dc2", + "0x7a368c0ee073f8ca28cfd0c708ec5e7889c145c28f78a9019f98b57965154a2e", + "0x87c58619dd716482fb6a0aae8b3fe99b1913e50f6c47e009c1f1eeefa444ce84", + "0xf1295e14dc26b236a40f4e3803a9b56184096a977561410b2d50906d853e7994", + "0xf15a50fac673cfa491ddc5b220b02a15625a8713304bebb555ec497164d91dd8", + "0xa89914bdb8ae3317f857543b76cc2f8df917ec87f15c706ce8b99f504065bd2f", + "0x1751aa642f52d3fc9e4c9aadffe0fcd818ea4b8f5669667b4683b962a3053b3b", + "0xdd604d4b3002ede331412e122aabb85e6665b698b9d9e1de8da2af8f4973174a", + "0x3f37d8590d5e521de1b8c2a3dd2a2803db2ffcfeeaa2b0eecd91a264cf14f693", + "0x1f0d6d60618b9eab9b14a5f742708632ab5e0d4f45f55311537766810a5be75b", + "0xa85fb8b01aee87b65d35ce798f495e781c09f21f8c53e2515e1fe7ec58309838", + "0x36f6d2ab0024a14bdc8099fc38fae732073ae129e236cf4cb1e3ec033e22bfd4", + "0xdbef3ff0141b3a67295300416d196ba22c0d092e99b44243b28f5b271db64530", + "0x79f8ba4c22c904f61c991d902a332800dbb4b0c7f7f8e1410ce912a157f888c4", + "0xed301e52f9b8ef68b4e101849181794defe5585da5b21babbe35fe55bbb1b5f6", + "0x13c85b835a1ad90af3f4edeba807160dae44ac13a512f05cae5862b32679437f", + "0xbd7e4f5737b8bba44b4dc89f17c1cf5b20cb59653e9dca09946f76d5074418cc", + "0xc6bf4ad7ddb1c8bf228ec022c49a4cac8879fd1141a584b7091446d547eb9340", + "0x9fa2fe809382f98b54df9a66d1a41f4a6a8354ddbf24f99154308bdf9712ad07", + "0x2908a3c2a4417756263213493f984f71aa6531f209d081e638c5127fe41a96aa", + "0x405d42d09e1c5631db651a2c2847db071f8560e5d2ed433b678f82d43a4a3ed5", + "0x0442150be1a86f3691c841c8fb90211c1bfea53147af53d5d06e38a96d717d67", + "0x6c0601f89e383e69bef7d563b1d57e28a107c18afa4ad72f0b17d3a1075aa95f", + "0x4a161cf921af3e9f14b109317a242d822780448e041eb5de04fe18c628e337cd", + "0xb9d066eb025b647a9d7d53513c10c2ce6b75f3a11c243448fa9cf232af56b10f", + "0xb0b9d8057c7daa089ef4125092a65ac9a6a801a2271edc0b9aaeb75d9ffaf0a6", + "0x15ef89065d0b10621cf4f538bdc9387dd7a1a82b2c96be1f286a136ee7f7052f", + "0xfa9245cd2964b037a6b85afe875043d816b64d67e57a9852adf4e90c4ac751a8", + "0xe5c6e7e16abd826d3a667c1f66e0bb831b2b9c8d4999e3e8c766c86d316d48f8", + "0x24042d505a64b9e495e688640b5da3e19f196305f79deb3f9f72383ec74d6e3e", + "0x786387a4034ba28efbab0f0580cce8351505a1ff2616ed2529b8ae590641436a", + "0x1e55d6e3388161706f9695ac69871d409dcb0fc591fa923c7d50e9ee5e2812bd", + "0xf2cd8e147c47384118c31ba38d79431830ca4186eb048c0b7b7d42251a269409", + "0xe5fe785be6311204c6afcfb4bd2f489b418763b63133a6c7dc7af6cc4da74806", + "0x00b3774b4e81fb044518249717c663395bf38203738fe57c8a78d41b15986710", + "0x87e5ff90e2d4d46384806018798a95ecb879fb11f179f754e8816bf8f5722a96", + "0xa72bf14443f27d68ede681ca9179ea61408d30cddc9ee14f80695ea7572562a5", + "0xef89b08ec631ba2a1270d7d7a9822e4333900275b8313a2725ca93856b3f80cf", + "0xdffbf2219dac6b9efdb457e03ed2da68ed77555aa342f8d28970853746177715", + "0x4916e6a5ea103be8ff3146c5418bdb248b270d2c6755b8de9d6db0716db45260", + "0x41a2aaed9ffb54edad9bc0a8bdb26622c77f48383b77be1130b3731f6ab5571e", + "0xd19e59a17ec0ff9b4abf3cbb200a2fbb5d630c4bec2b4ecf48adbd90a1bead6b", + "0x6f356548fc8fa6bd620701bad360bf6d0192e7b75d0cba19541680a3a9de6b97", + "0xe3a91f46e930ff104e93c12dfa12566d7ba1482089c6487813abb2c40a162e6c", + "0x3023de0b7de58d8052cc7b3d49e152a550a625de79413225954fb32ef3e307ba", + "0x3d0efbdb16d990074ca9bdba4a41e20cac4dcc08db3a18a8b1454949e53f1084", + "0x239ed8bf8b6bd366ac739b2033c0339a4eaf64bd20527e81339d94fd13fe8f21", + "0x8c225d5f61e2ea21f4b9242bd33be9851f6c1cf371bcbcfff4b09f5bf6b43874", + "0x8b5016f6b6704e685a4067f6f59e8bc6fa0609946247efd22f1fe09f06d56dad", + "0xba5df646a352d00365b20fa1f1b46e818e0ddbf25b6eb4a7e725fc0583dfe27a", + "0xca07c674a2c32fa3a05ce7de70178ab5298896dbb1e44dfd5957a73621ba9ca2", + "0x0cb2b1a57ae60a3541af3a7478dbace7c1b1d81e2f3dd7e9a1cd7344ae6dd3f9", + "0x06cb8943656e33f00614930fd786c1cb640cb151cba64e4707d636ec840ab916", + "0xb43c49dde1dfd6eff5694efd03b0bfacbff38ef065073e20d9ad63035e239b95", + "0x70d1b7d7d0bd93de21d352924b924cf43245693b1e794c52db21e03e4839ca6d", + "0x3f595df24979d5a2661c705b268ebae5420e787065d08c5c55ac71cb29c17c43", + "0x3a82b3a2aefe4466295062cd8fe1936835eb4907d9bda100397c82e0dc256ee2", + "0x406d561b7f9a9c7e60300f5e9271a65e02b1e3ce5c0097836e72f6f555f9e9ad", + "0x43037d22a4cd5106096dc5b2d41062946a384d22ffc336ce87fcc5ec6d6b544f", + "0x625ec267b8e5527d47f9830818f7e6254caa6a4063869c8af8b31fa43984e8b9", + "0x8e0ffc55100723a0811e530d10f4c4d3d6a11e177c4ca0a6f0ca0deee1ab8d06", + "0x1c137e45b2aabede4b116fc3be2563d410b894b01116700ee0dbe58e5e94e673", + "0x8394f8da512541f04839c6e56614e0c3a0da1c0da896c375baa8f48db23b9cf1", + "0x176a02dadc10f81ee6e8e2ead1f6c7296fc2f4a7131cee47f74ee1bcdbcb2d89", + "0xd664aa6c3463aa3efe8fbfa1aa0237052d325a3ef0f77b7eb4409ebb1f88658c", + "0xedbe52f31248a3f5b740ae2f994d7f8fb0c7971896b21990df070d9e92f3a5da", + "0x567d0fa624dc355defe21deac70309225612595c9bce5c4549f8d96f743f4100", + "0x95af25608534d63a20ebce3c57e41ebfdadee1d9732f415c5e568ba6b17bf385", + "0xe753630579a11cfaf3d2d23fdb06ec42e562ccbf420a89f7920416023f60209c", + "0x488546df01d515c475244f252df59f94af4cd9c41a1eb157f17b967796bce325", + "0x289863a1184ee5673299a760e05769c975ffb963c1266c531b626b0f4395f22a", + "0x1e6fc1f648b9c93c09e586d941a266825bccfdb6fcfe51bbf5f53abbb6ab0b7c", + "0x8e72416861c0077da18f8b391a82b7d498e8b170709fab3af600f2b15ac30f7c", + "0x03f19716823e80e58eca9751a0f44bfe790599868dea049716097929ef3474b9", + "0x46c13fbac5ec254c0f279405d6eebf596a024f70ff24bc3364117ab0d1ce7dc2", + "0xa2f0d86392aea59bd81cd54520c41780e72863370784fe0bc4ea3bc9ed1a9939", + "0x56f3639ea6526dacccb82947f75ab451af064fd87ce3356bb02fdcb7b495e6b1", + "0xc287c98843cb2f4f7e4c220940c188d53c127ee583edbeaf9bb52042a7e57521", + "0x123d8073c10434f31a59da9f4b675ae2f93fbe963428174d8bd26dfdd81055f2", + "0xeccc09093222e7b2a380d642cd3dc9861f04f8630eabee0826e02798edd1d930", + "0x13457b9e94cff147796be3a8c4bd336281eccef794caccd5960f2c513d89820e", + "0x7142a0500ea5d126d9c9beeb6c3afa8e056f940ea2ffdce553d1127a66ae160b", + "0xd97cbfc8e91b7a9e1a387efad78ce161faa48cbfbf045561d1c8d1ee548f0e93", + "0x74f653d52b8959613e83e5c80258fa87bea2e46ce8908c627a94cbf2c92f5a3c", + "0x9e8a9f3cbadb7e090c9c3aedce1bb4671dcf9f6e96fdf31e9ef489f0d6b8d7d8", + "0x684e004f214741d0cf3b402653953520d71bf829cf626bcf41e0d38f8fefda04", + "0x166e93c81e9d6481519c760ea36b30a93b405fa5dcc96038e5de4f4f54984e97", + "0x1399bd8d24bd954f15dfcc8731f695159413f413d1190af066e267e9219cd0ed", + "0xc56746ffc8f0ba9aca3a30c4d93ba998c585aca8ca70d1a6cfa4e9735a04b4c1", + "0xefb37d69ac765a8ace94055a8224ed9752317ed61d9d5272b3e5dee1fbf17cd3", + "0x8ad7de72226d69e663bb740eaeb9a56a7b553aa07db9989a773b66f5e01085f2", + "0x8a83a3617a021e6c43b6be18f56751809018df9c052656dcb5f4b0c604dfc305", + "0xe1f547527f0d0696a4e4563eade30ef06115e16b1d9f8febd664e55045d8866b", + "0x9f781a9615c4920d5aebd43cfdd0a57f81985c407946a450e1affcde3bdd940c", + "0x17d73fe6749140b4fac4dc982191e26acdb703591b48ba6199f97cf592b8f910", + "0xf54b3ff01c0e2e7c5657daa41d18ace12fd67b1f72f3c683bcef172dc14e4eca", + "0x9cb069317fc99ec5a0d0c5662f16ecf512a3872f4e4427d36f6830cd61bd2c59", + "0x0d2d8123b51d839e9951da9872ec3e6cda2945cd27c0eead8ab71d057de07dbd", + "0xb335a1cd342c8474772f456f0c1370424b86b7bfeb3e309a030a2dac5e1fa150", + "0x1287fb62135a4dafb979b20a123ac9bac64990f387dba9fbd3c1038bbb1abfe4", + "0x3778e2fa7a8567f2b4d7417e0ad8fc290477e9b3e419d904a8197af9b1831a69", + "0x6193d57a20fd8e4b268fa66734ebbf6dbd4629707ec3c7068e2cc9fb9b2f9925", + "0xd8bb5e08ab1de7d062a82d42f9749af034862e28164fa08e2bd8dba2c848d30c", + "0xe8887ef263553b7a1313eb0f4e867ce41ba654f1f9737885ec2cb490845c935d", + "0x7dd6fa7803fcc0c35e9bec5b75ff602fc5e42d97f620143ba2d541f877cf6b90", + "0xab1c6f5cd13450b3fd6c02b8007a228ef0280c07c82511c9d0327150a5e29ab1", + "0x87f55d5f18378436502208034e5df26c86120c43848bc703b6f56255be7eccb3", + "0x322349f40e7a6d3ec5e5a8747c37e4a942f885a1a36ec4e9bbb54b6fb8a126ef", + "0x99cd338c84eabdcfe825642431cb18a31a1ac92d8210e95a87eeb53214bcd9ea", + "0x745d15051fe509aa43991804f5f7465eb32d38f3f8ad37c16a22350eb7d9449b", + "0xbe49a4ed35e1cf205bd505fe8a24daa39a24d8dfa4bd1f8810ac9735ba91f6da", + "0x25d8d046f1abe70a5d4f69acfd4ac480ef332878eb50449bd1a58be1c285e839", + "0x43be1ee1bc3a2fb776588943898d14552a4d018eaecd084af9d4d2ea19cf578a", + "0x84c51566509101bd98e2613b631294c49ab66a226043863fad226f87b3b51e2b", + "0xea92be072141e7f5a6e975705d06261381f06627ea02014d1f139fa1dbfa110f", + "0x6289b9ef24a03796dedbdbfd34207716c867eccb540bd54dbef718b4e3c530f8", + "0x61b387fe71d2839eaf0fcdb61e01dcd86e800565752ed997433e7f80abad749d", + "0x5a4df748ba0bf18ef901099644bd93e83863f0a90c6f894e0c41af9914e322a9", + "0x67cbddab56675fd2fa5b7ded722f23a8dc61c93fda01c129e08f58fca47e9609", + "0x9ef3b712cc24fefb4b8b9df76342d3d4de7057e4ca726954c4c6068dbe7e3c17", + "0xcc53d35549ec012fc27507bf3cf0ed6eb1fb3e2b819146620d0491b01d32d4f4", + "0x854f3518e2ed066cd07ff4aabf7e3fefdf7432aec491db3a082e9bb07feebdd6", + "0xcfff6e6df5ca204fdf0c4aadaeb41b694382368e8a3f93762b0a91e9ecc6ca46", + "0x8d931f328695f9eba5e1f53c442c7986003963a354edc21e8eef3a15b92eab56", + "0xd2f4cf9b04fc26c81b90d0da3878635e5359e97fd660ad5fdb574101a7312869", + "0x7065a0e064e3ac1637672287c9ec867e417c039171b5ff18e7befc2c662e96e0", + "0x45b3f4edefbae0564334d4c91e6c578982f21ffcd361b89c0e48385108b24b9f", + "0x6cd3a39c81bb8c8c752091cffd931c989a235c279eb9e1bcc7daea2d7b11935b", + "0xe4a2321753133b285b1dfe18046b875cb7a2ac0d4747a4c56da8ce6c20bfa7bd", + "0x64ec16016476d0aa7bf30f19b1b1db7b44e1e2261636923d05d8f1e93eee48eb", + "0x4f7e300c850b8d7f4103cadb78fb7fad346aee58764f0673c9b1676383891f98", + "0xdc22f020ed412209df95748c158c7bf1c95dd26559268b2a7af74a30376a961f", + "0xb8b79aa3e06e7ceff3a3cae43d42a9a0324847c3abf616074438b315da07acd7", + "0xcb87c311fd76e377de62b226913a9f44fad9938bb2dcd45b9936eb0e72e66d42", + "0xa1d88f55c3dad26cb4ae9e6a7958495356f8f3947683e8e28964c9622959338b", + "0x02a08833ec4ab39aee44d48c72acd9865ebaee204cdb875ad2b63e8206f5918b", + "0x69df735c7f76c9ab354eea805d07ccbf2384834ade17b74fe11fa8a55c0b8c17", + "0xaf66675095fd7d4dcc0adb96fbe7e8637c7f631a2c04c93dc97fd824555d233c", + "0x5bc1b6d5574af1efba6bd5270267af1466e05d0b92aeebb3b379e54f81e0d0f6", + "0x8f9dd7bbe392a5d47389091c64165531f253b6b72085a8762d3a57ac9e923b77", + "0x5381ac0ae6481498cc876111c565f6da42ab9478567ad30fca98122c9d5ad5f3", + "0x72a7e59ac418710c54afb1bb9b3a68793aefb27f5c7987cf14b3af3e3f4c98b3", + "0x4bedec0fd84c6f99898d9c8fa18ea136d24aacc96c8ba5f74871abbb00358d94", + "0x7db9d5c69bef928182309c8e572d08f6d4c9b3ebf3d564adc33512fefe0d44ff", + "0x5f71614c2d515d34e7323d74f59415bd68f4e0cea6a4781b76992fd142fe2eea", + "0x0c5f7d1549a53267668ede125fe7ebfec66ab31cdf22b4f1d1256429e181f191", + "0x1934a3f9c8165c12d582e47e5d8eccd75472cac19c88fbb04fd54bf1308cab1a", + "0x6ba20bba201c45f0dd1a5da708df8659fc6464f624f4a03abbdfcf3600a343b3", + "0x6226aa3bbbf685cb1eea6bd6ae274c97a44eeb28a85f777ae6bebe0ace1ae771", + "0x9bddc51497f314929d8662d84fcfc7a818179bb8b229944041b38b57a1bef5bd", + "0x29b0c16dffd3404f5b04a3c80eb73e46d52fead0dd5b1893bc09add28ddd577e", + "0x4eb99344591298b1e5c30bf00ba8b9198ff9d5aaf29ee458eb6b3b6c4bbdf4b0", + "0x0d23949b843045d91bf15f942b4f0a7148d91184be57906a0d4dcc1c2e03e414", + "0xf40955d6e8d9fe52768596bd6381797820af37d20baf802c15a56feaa6004401", + "0xfeff4cf459035d432282be2fb3b03477ae32a2828cad4738c68ef768a9fbb7e7", + "0xe1b899ee2997a3c2015579dfab7085448059c527ffdab5acbf7a5697692624a2", + "0x6a9560866308f674fdb42ea6e79dacf95e766e05eed39822bf38a6dbc6834b40", + "0x4fd52b01b910bc0a4b668160d4e0ed2e2a23f711364b2bab20620bbc98c343fc", + "0x4e9107ff1b9c4db278d9208da76b85814e5e2754afc9b4a1ead0851b13e2b1e1", + "0xdc80a6ac968d98f9922142f5b278ea7d301c40e13e46bea2ce64f36c026b3476", + "0xa0f998d65a2c371bd012ddb479bb6dea08bfbf4d00ff6e58b0fa029c29c66071", + "0x0de6b5fa767d74974eb8971d37146f07616a14ea4eb3e8a797c41197151db401", + "0x7ca7cbb20623f95e21a2e16ae0fcb88d6086f4006a3ce60562de6dfaa3713941", + "0x625d42a9a9a610a81e911add91ae6fe557f9c87c0b5c6168c944cad9edfd3cf3", + "0x6619efdc12dac2b82feb0486f16060603797e0980d5e6504038fe85d745141f4", + "0xeee68c793831897a1956382a95e43d7d914a5a7e71867e166c75ec34dbd4bbe1", + "0x140f25bf61968b9d68e0362836462c3fc1ab7ff48cd5b3a7595ed423082a9eba", + "0x1f7df5a2650cad61c48f6533efc291426943840b7de37c79927a68bec692248a", + "0x57ce666cb138ef97e047d61014153f0859e718e84cfb5ab29adf5d44a03a5c56", + "0xdd28aceeb71418ecde23fa89869cd58378161d1aa2be7222d0d7aec03e11727c", + "0x2cabc29f1691cd651f7876ab0a993f3a82185c057c034aa3530235b2e04cb6ec", + "0xe01026034a3692d4cc371141b45ad6a8652d6335ee8a4e01ad291e6668a619a0", + "0x2d503b6ab894172c24fd1b175e58f89d066f5300387a67c0d0cf246de0d9e2e7", + "0xcca3ccd36ade048068650f3eecd711054cfd411e52037dbab107ae579bf0180e", + "0x42bf685fe1189cca63aa0bc33c8a1caf92a601c157426c989cdbe7781bd510a9", + "0xb099d39f7d30c751f3eaa8525511d2b88985f9be4f28063c897feea3bd2576b7", + "0x44f333fd407761569b90d92502e16c996de28290b4f56f44f316c77b0ec1415e", + "0x8eec65b7a3b292d4492668201e574b22959cd51e6da469700d66889d285952f8", + "0x8f9fa18eb5cd245361f3507d5a5697b083d36f03d5bb10ece414cb3dc13d1874", + "0xeccb2c8311dd0b18fb48b231f93913d4aa626decb11d876383e1aa563ae7624e", + "0x2a4fecf7677045373e3745d82e0e5519e3663e7f281070cd8593dc9a40605e00", + "0x49e8140af37a7b11f9772fb7098dd135da6e136050272a98da51c24165129320", + "0xca609e41bd8a8936f33f1a03b696102b69a838993bf52d9ad1a58a97324c578c", + "0xff9edbebed7db64e961b503628ce83b36dcf16257d8d16b8f0881487c7400fed", + "0x171c011f92b1c9488bf5dd14a4d2542e791a15a7aa448dfca2ea56c8575f0963", + "0x8ecdb81dd74fa880393a925918aae6cbd6a695b1dd41a1d7cceee89b71bcea3d", + "0x5980680a52bac0bc5935fff870d27e6f2994bf1d1bf4248c2e3b1bc433c8dfd4", + "0x14befa73b2b8f050c3750b0c630ac44cb8dd45aebb4aa35de2be999f27ae784e", + "0x7277dc1684b80c4e36104767f5f767bd9f62b8575577e8f26ff7423b1ddd2c1c", + "0xac7dc17ebb2ed463882ba79a5b5d39659b8b3dfada087ca5915d3f09f957e86b", + "0xa4cb7360055267e0a5e06bf7c7ed71ffec755997330519bb93f3368b03249ac0", + "0xfc0373ed2f3a32d1966c134c3efb913ef9052b2e4a7631897c2965a07bc53eb0", + "0xcf088dec3c1b5ddb982af5443a353112b370f80384e3b119f5aefa205202baab", + "0x9ce0259219238610b7948116a7ce3dd1ec6d7d97c3d4562dab998fa23311484a", + "0x16897b62ed5e8f8bf4fedab0095b2d4762962196dd5f568c70a491823bfe8cd3", + "0x23830e5169eed9f21132dc681fcc9535320320467e7aa1d526d28e24d4a79d00", + "0x8da7bbbbfc5cd1e2758b8f0f177676c64f9045a97ebdadf0327e1375bba5eed8", + "0xb7e924db146ee77743bb6a71f473074b26984e8325d5efb25bcd4923598777e8", + "0xf73adfeafe2e1ca141723c41e5bf15765bcc4e8c1c0bef86246300c1c559df44", + "0x5a2773c9f8def3daf17cae0ad294b05fdbce1da698a81e02a77910aa44eebd14", + "0x51c54864680cc69e7f5d60b190be6d4f04ac7bc336dd1fa6bf1b11f574f06bdc", + "0xc1bde6c790eeb54a85902cf23a3c7be7536e863c2108588be923eb0bb62ccfeb", + "0x52a4ee91eed2f1c021d6bed72c79c4247b033ddb0c190a0314e21b998fcd00cc", + "0x11860f1223feb0d685cb5ce4838df0287122a13fb057ff6d4dc1756429efae24", + "0x71c945fa22e0e040571158e608c538ece678deeeb82fd5b468d49b24daa43174", + "0xb278bedef9f18cec715130e812e26b17ed53878ed6ece6f6fbff4f23799232a7", + "0x10ab804c14d3da3f8df4e635c1a2fbf3a5e820224a0e11f9650146054592ad72", + "0xf53d888449ce03d8a1d32c1e6483c492faf3e31c98fa91a71316b72889697edb", + "0x9cda08cd2e2cb6dcd727d09b8e199dbb5ad9cdde95c005f3cdff1616f019d35c", + "0x9abe38c34a6c6a95dfde3e2dbdd711917356d7089906e4fedc24e0f1743c3310", + "0xc1e61e15894c4a1f541d9f9462ca903c7f9b0cbe17fd8e6fd814c15c574001ec", + "0x067707347be85f54b4f5e83a6ad434718fc6f841f46e32c1f0609312dcc2d5e7", + "0xda5bb307310d2aee92073aa7238ae5f395a2d4422d78bff044f2813a49feb5ff", + "0xe581530b765f410701551cf762708217211c233bb5a45db75168bb8dd7ba0f12", + "0xb9eb8a41fb05d28004caf51eb9a48328c1346d7b92dce03e43bd3d9454cc2610", + "0x0899ba9700f6f72d02440a89e778efabcc561a7421ab8597b55acd308a1fc027", + "0x3b722d42b1fd390360499c514f6c3743d787333579256426c4727930c8ad7973", + "0xccabc6f7dab5c4cbb2abe4cc885dd5d94e67e3c0c1ad6ab7c904f4a4e96ee47f", + "0xa0c95dd16c7f08631dab8845145ad10ced180f4811c20b80b8fe194bf3d69c7d", + "0x8fcb0955f89f6b566063002ac36c6931028a55262c7c8f2f53b330ad44e9fcdc", + "0xd96b7880bfa90f6b2e7db1538bb2e6af23e979a00197a56ef3d270e37d32aefe", + "0xdbb65a1449bc936cf5fc4221e799b2967df8e8b23ce9dbeb0c4da713ca450f70", + "0x461aef4daac7e2833672bf348a6dac2bc21a1be21a95492c862cd5b374105ea5", + "0x2ff17e5e79026d9f6926afb8711e93833019428f399e17a4f6ed3da25e8cb1fb", + "0xc6f8ce59472b2554a4cc164725c0ce90f42c04d9990aeedcef104794989c6d05", + "0x11b7b777c0d947d66e34fd1cc0218ecb265cdb4e0803124de3f25bf84c846f1c", + "0x3e7ae23238ef07a11b7880b0df05249eb0a9e514033ec5a5c07026777391562d", + "0xb7d3e0d03461e3395221abebd36c556fe974bad3963b950bea6398a8731ff62f", + "0xd5eb95e7faba570b3b771a117ebe24f7210b412880c82a0af9dc4aec6d327e05", + "0x70c87b127f28ce89603b3bc544bd72d26c6109b245528b58dc211f325d21cf71", + "0xef4e4e7d549a777f7378ac305a0262804aa0c4d354bd1aac0de7d6ace1518103", + "0xc0da9c260481b51005b8b40eababb4fd5860bb8773c9f544948ad6d461a37c7b", + "0xf1b9d71db1fbd78606d15b58aa376824014634cae2c3312627394dd1e242dab6", + "0x817d5747255d19ad26de064d02e98cf2d8575d0ee9b023c777db5acb9f4a2370", + "0x7c9e5a28cf44856d6c1efae5f2b0641cfaf5cde57ad1c43ffbd2a75c7a133900", + "0x97ec098ed82928502ac2c1f9bee081f8b9fff31708604ba938ac0878a13a7565", + "0x9f7f904822183f91f0630eede546ffdb47d7fb54398b8335da6d449bada6c715", + "0xfd4a2e44f68e27858e7b0849391eb14d1e9ec4969ece58e5731802fa09572a3a", + "0xf143bce01d8b9720679718578916c1b9682a7ff95966d814fd9007a4ca347056", + "0x6d8acc44dd25d65b3d53749229528c7fbbd9fe19dd4310862ff31183eeaefc22", + "0x7933d76c4441cd9fff8fda76a2c1aabd2150a2dcda35b28b0aecc9a80748e212", + "0xfb226b28cb201cf22d18f98b7dbcc4580789bf5e55cd2120ab9ca817309cdb37", + "0x98f0189f1c82169fd734a9992ddf60e50bef80419725198f424b2b01d95eb406", + "0x2ea616a8e6a6a058661d6c2b86d73bd5dc70a932c15f9a71c9206d8edbfbe1d0", + "0x36ca127e3511b675b1c8ebfee9c9739135e335acdf57c0c4a3a5b5d316468362", + "0xc8187c6399d4ce9fff7f7bba4ff9e062c0777d3e53abb705430063d565c77aae", + "0xaf703c53d1f0d8048bdd26eee8dba862783a3f3ad19d7c1509a3edb73dcf524a", + "0x6f2ae737803347a288b2985e30d3e73178883deeed296c914fc1ddb080ffbc92", + "0x625155cbaed1b87b32b7ceca474056b5d93c6b5c77c745a81a1ed56059e856ba", + "0x6b7daa25be6a7c8133690c9c333524a466f8b84f2d1118b899917f65725bdc45", + "0xb4740bce4372dc9c14d6399c61688321f4195a4c8fa535fb050aecf98e86de60", + "0xc2f3ded56bf1a6199dc8afe184566334dfa09ae5ac17184c12a3e7f0236a79fa", + "0x58bbc3ff71fb85ac640ed0c7deae0fa65c3b83976fc16c399fe0fb040db2d4e0", + "0xddd87e478924729f91d36bb791696dc39668086c94c230bcd397472cbed10a63", + "0xa7c68331d8487ee4cd54d624285fa83a5b1a644469f3011979a9f1a4ed4845e2", + "0xde246c8cc8521624569776e2adb88ab5cc9de14317e918737e3f62e8b7e98088", + "0x6bac8f622e91fbb261f6c2156d27afd862f6ebbf0c1884b4af544c3ec51bbe39", + "0xaef977944c4b4381512d3cd67cd9fd389edecd2b5af786d5f6f6f246fd0265b0", + "0xdd381781a294af7eef2e407108667696e03071357de6fe70d11f95a7bf05aa3b", + "0xf5bffd7fe37829f35ca9b592516041f84625ea367150964c5bbafeb5c89b4056", + "0x87169c6603fd5c82597522c94a1e7f23ae0c3fdf8577c65c27b81d64761d83e8", + "0xb8130f7fe44c86fb1222ce59b0789fc644249875d6084bcbc6ab129a2c8a2d9f", + "0x5f35f0fd8e475321c24b6defb55ad1205fb2e09490d870998a38b4311fd4ddaa", + "0xd3df8ae0c3801f4de41d3a3f6939ceb6fafe7b57d6cda8de25142e1276e2b32c", + "0xf73ee45d7d3ccc1a811840f20fc74a375f3734e09054366cc96ffef48ed2aa59", + "0x6410be59516ea18c76c69f9f0e64f11a9af5397d6bbe01e4683e4ed770cab030", + "0x928a2aafa1e3cf42ba941163f64a5ef4336500c776b3136d4e3bbfd63b56f82b", + "0x5691e36d6361414d4f6b8cb789978f6106bd0215b75cf7803e017a31533dda15", + "0x09c3b3cf191feed3fa7db662747ed68ed5a5c74c2785120d12c16f424647f054", + "0x77fd808c7638f68cb88fe6b6baf06f5a0c8a310a241b3be4f77c90aa65292f18", + "0x6f1241801d1a60163bf1a13c052ca4e3c2cb6faa8cdedff3591f5ce525ec3015", + "0xcadd8707bdd0f4dce7448f8e4c4159383c4c328c429aca6fac26daa3dd34826f", + "0x21ba2643614ab80062dd500208a008da245c9647850bc4b446d270330464263a", + "0xa07444a3d0421ba73b2c01443c4c873f1daedb31304af2d14cf70ec0f0edc520", + "0x64abf6329e683d6837700faf45e3092e21671004a33c141dc52e45a3a7c7d555", + "0x207ca4945e8ff04625014b09c0f4c9ccdb18ed28b2239f91b504a98b62681553", + "0x2cf715c9931fa2d6d70e34c68e161b563b76829dc58b20ddbec58e5bfe474a34", + "0xddc1f5a3fd6c0bdc7e5a80ed1cef74b3fa91e3717c759a745d6dd3cbc2aa2189", + "0xcd032ecb11a21beb66f0005bb87299bca6f875500d2c1832171adac4914262f7", + "0x15fdb98ab1fdca3c1c5103e0aab860fde04181c3a202c7e790efab30261bd7e7", + "0xca0fd113b30a78368b82c97d89740c71723af8a622a2469483c4a3d927220e81", + "0x6c4b2be680a4909638491985e8d36558e0c65ea474344b2dbb3eb758da6a42c3", + "0xbf0e4472a3e060e513d990065d904b8be255a91c637f5852fd4ecdfdf922f523", + "0xc6f16f65c4f543f14fc3d2223e2dc0aed0a4e4c4ca74bf50c2284e1ba3e72b49", + "0xdaea514d47a5d4f08feb250329d9d05d1ca6bfd05f092d89698d862447e23ea0", + "0x2d225aef3290da4e379c180e60f4dfcdf0cc690f1bb1954e93c6b2e23d5e18ea", + "0xa0f2827b7e3e5dd9b6388b37625e2a70c3e8c05ecf5c73ed9eea4ae35394cf00", + "0x84c6eb178ce3319d58242dd2f2397106d27b5fabf0205afaaa341e1954d921a3", + "0xdaddb722267c0bf3f8e226c206e8cc9fcbe246c6e32c6a6c7150fe9856aaaa94", + "0x70c20d0527c6bb3f31c9f4e5a2b4f7ab5f4c7859209b89d7c91a6b5b762decf0", + "0x20828baebd8e0214ab6f1ccec71f3f7cb71e9ad2efa947b329bd30826b2817c7", + "0x373b13ec4abe39fa560c4f9fe24869cc77fdf8d63a4716ee863c84d4dfc142c7", + "0x31ca93c46390e874a6e0c2043696de05b1e70e4c0a349f2e84183936cae26c93", + "0x988b12eadbfefe77775ddf69ba3709c9515e07245067e5baf1e804142b4bc338", + "0xa1b24bab7c6116fac887c9a0fbe1a1c95cfc61c1035289f37087224e27607da5", + "0x57ab70badd5fdebd841225856ca40219ba102d580c270a9dd1057c23bb6f0ab6", + "0xcf0153f6f226d4391e4b0bebe75b3fbab32a6638e256732e9f4e7873f1ec9fb5", + "0xd244c6bf25f55dcc12ca9ba8d51ec1f3fa3f3675c6bfc77c30bd2699addb4332", + "0x0c43d2ee0ecac11120446f717dc76ca6f3d58a996e1d5bc16f10d43847f6c32b", + "0x5d8f4e07788532ddf56f8d7fd92f59b429e50d2b9297b34073213ef330566cb0", + "0x0d2d5b80241b5e31f9a2322f4643725672630b535a96dd92e8adecc65f33982d", + "0xeac51509dc7d24bb0299a3be5edd7645c07d2cd7a091d4acacaf63d6f791c8b6", + "0xfd8e04640f8ed4ef48427f46fd7678f0d44fcb8c902e4fd3d963730d54405f2d", + "0x2c125db5202b38bd92d45df0450d56d281d8674ce5e154998f431917ce030fe7", + "0x95f3f8fbacc77fadd6ed0761b7664cbdcf3025cfea4e3848d53e05e0618b08d4", + "0x30c374b6260b690a0181da60210b9f69d43a88e5e887ffc711d258afa97540cd", + "0xc7715c3c9cae7b0d6d9406e6187319ab25443b63b485b93fa5b0953bc4e8a773", + "0xad3aa8aa32169df563496e581c463f32e5aedecc2a6b90047534f70e390b98ba", + "0xbc854695ca10d451e2b0eaf0c7008864eb0983bf471a91e76f0793aa40f182cd", + "0x39ee0f32024adf22bc261fcfdeaba03ace2fa02ab69e334f7e7fb461621062af", + "0xc5e03a50f6a60506421b42d7333d1abda82f18116b74b1e3901340703a66beed", + "0x6fac074fd476e78ee1c4597622c845198c88596f41fd484cb9ece3b28534f9b1", + "0xdb9cc901d87f5740a706c4f8950c7578ffe273fa59c7c400cc8de5cbad1f9352", + "0x1015161947faede37bfccfa4cc3e1e7c96deeb6b7582e44a4716e663426aba21", + "0xa5b87e3e4fed2e07507caa2e2c382a9cae09d7732902034a7df20c0133d04001", + "0x870625808aa2b1870c584ae718fefb14c59352600696872f44a74702f3f93ed7", + "0xa97605d99f0b35a3f39eee6050304e5ff074f606c204fe2647259a65e7927acb", + "0x7808b248edc27341f99e316e6ce59433f821184ab1d86103506b25d47ca55548", + "0x9f1105d4a4aef93875517461fb422b0db2363d9644f67084cb0defc7ef21751a", + "0x1eb5922aab31e672ee245b7609a6f7a78db16d7d7ee2af96de4bf2e117158894", + "0x412b3e9277eb03109ac7e2c2a8ecb78aca1e855963f1e732c94ce59d1657dc65", + "0xef345147668f1ab39edc6504173a5f021c5f523a7c95a676694fac06b71cd77f", + "0x8bccadb066ebe7c4d5675e764984b036a00922d21576df83312bcc8c58344c71", + "0x2381b408f8e970bd2c8e2be4fb2942567c83c2f9d302645e8e701024ad4d1585", + "0x1eaaee04b4c0caa758ab194756094579fafaa9dd410d28932fef2c44b4ebdfc3", + "0xa40a5666a5b81687f34b9500f501d9cee2d58ac11b5f48b450bb73994149d433", + "0xe720dc26162b0f93439ce0f8422c8e94abd43a145f9d8f06e27d4ebf2aab6a0d", + "0x9c72eeb33fba31b4ffc4d2463885fb94a4a74447c99ec7f09df34956f72985e1", + "0x917c28e8fbee274cd9594ddc36b4e2e36901cd2214a85d9d088ddaff26eb75d5", + "0xe14960bf73147c6d1021ce558ba717a457396dc576e29af1594d2f48349fbdaa", + "0xa5c7b711d7a85955a64ea1714acfdb11e68ba44bc08be07279254424775fad0f", + "0xc55ce83a0ff6a5d87666af865705b994ad72e21f44796cef93422658ccb87e01", + "0x73899917b8cb58ef61cc863350e1d22c9927b58496196cca7c4dce54e76a0d0d", + "0x8b2737ca27f2a04441d6a526a431368d4503f6792e3b44b8e8d861a1c1230a67", + "0x1547efbbe962bc7a45b85f5a991f734029db5fc18cfa76fc325d13c59f95baa6", + "0x726581206ea01ef33e024c1bfdae03cde2541cbe459e9b549bc994e3f37f72af", + "0x8341b5f7002cb7b6b2d7ec41206a92dc0dc1d999f14cb6675f23bb23d152612d", + "0xbcd33baba9333ee87135bf41deafcacb9739fd6b18ade27d5a8df7368b9f3ba0", + "0xc62f33d199cfe22a138a56614ff0402a07db617561d6990820ff5dc1ba6500dd", + "0x9f80d4ac78c3776bbe393b29b5d7751fb9ff451576100ce3f645e7c51e825cb9", + "0xc4cc4143a27c4a540a26c83ea4d9ac63d63ec2bd27dab91dbe4c18de6ed81ec5", + "0x2a0aa5a53237af41aad5110f83887db9b63fe3987b954c7fd63948975ae170e2", + "0xd3ba8d37d7b3ff625ce178c2a4da733f2d536f99a935be65ed9131c925982d44", + "0xebef74a09842fba8e32107eabcf72c9f1d90ee4d1b1df16e6a5fa7a6bcb1a99a", + "0x34f83dc59e459346d67c188533488f906f8c8daf6df0cc2f39433dfe97be34d0", + "0x6c3979104460fa95b3409814057409c9bbae7a6895b8430be6b95ff800fd22fe", + "0xbe18f8e37372620ee1ba6b11473f4bc8988edb410d2e6b569ca3719e59e79817", + "0x3a868dfbfed32797d6cb242b623f752bee17bc0a0720663dcb519e86f58212fb", + "0x156de95e4f36170af85c100c186efd9b689badab21819d239817a9a93b9ede47", + "0x346c1b579d46b5760f19ed57c6201d48e06b61165bab19aa8181abee9f3d0071", + "0xb68ec2b2d5a83be2754c1e4563d2142a0ed81c51366eebeeb37c60252e6b7341", + "0xc35f09f663badc9bd4eddfc00a148d0617429c60ec06696215d2d11afa33ec5f", + "0x51506f60c711f5b6c0db725cd9b66334b2a42f98515da09f4d7e7ac26209298a", + "0x820f047d02af09f382e0de070c080169e0e78efba87e57c3f324e437034bca8c", + "0xcca0288731d63228b8962546a1eef3ef2f0b35656ab3fd71044662451c862455", + "0xf5a08ccdefe6027178ef0d4b62a39adf51ae0d34f2f4e8c1a285bae85bc83a9d", + "0xcf4aa4178868c5435b1f9b6d32ce83c2d61c9a688049a1cd0d51ff742ffc3d03", + "0x649f115e94f18d09db180e1045439e9c269ff7a5a2a95ac02b52eb306aac89cf", + "0xb9d14649c61ba6bf5f7a316d26f4bded6a825b44a12a019a5696f8ea397dd40d", + "0x934d0225849a77810abd690e525f193d12e0bc7d1add94ba0155274dc2adfed1", + "0x0b6aa04807c11373e36af409fd8d2eff5f2968231c27ccc803d2b6afe14ce063", + "0x06824823cd9a211f7ac7197f2d21c4a3bdf2962f84a28a63e979c17314bf8c88", + "0x6c271afb49e08e935c1b58dc642f3045fb4d318727c59e71dc0164325d292ef4", + "0xa692a7c97e21421ed7caefad88552d49d8cbc85775938dc3b653e4a8f93eb031", + "0xea9e71beecd190843c3824cbc0c3eea60a32416fd7df87fae776aff796044344", + "0xd736638e895d243717d39fe005c906b97cfb3a6a35d8602cfe6ea859239bd6a3", + "0xa3f8a311dea2be26a1c7e72b0f7fe6364d7edcd4185079c7710f03b46da080cb", + "0x5894467409d99d3c4710ccbc44e6577aa47b551558da83c879f055601ea8452f", + "0x060a64693869308b611c89044b312b3186557729487b5646fd57624c27696bc4", + "0x7f06761759bd0bc55e5bb3270c6ad32e8cf312e37b65c5f453297845389b8825", + "0x7177473b94f066075c3a3cc8dbaa148561fdd92c1bbbce8b6b1535f19018e626", + "0x7437ba2e5d15445ee039281885c29128cc5bb953123be76582179f784c93363b", + "0xdfd5f483099d328ca0272cd7f6db1df1ea4e5ffea09baa9130fd5ce9048e67d0", + "0x76405e147c954d665f2900b7a45bcc3985526985ed44353c9bf970a8ba1e3d4d", + "0x025ce3855646d337f87243f405a518b78ab0f90d482a7ad81553c3a41c6db38f", + "0x2b10371d95dde869cfd115ca2495d9697a02852759f1ab908443d331837683eb", + "0xabbcd742ddfda075b4c142705c3fb814a23e62567f82ea4798dac7deed462cf5", + "0x216cbeb89ec2f88746f548ba722de1bcf7e24997ff74dc0d054c79091b3dfa91", + "0xbe82e74f8a0eefa124671b069db4fdfcf2c1d6870345c792e07f4adb6c7179c8", + "0xd1c96ef5dff9d20dc6ebbd0600f8bc897271c9dbdfc67a0642b0dcca3e570c34", + "0xc4b0c674b8e7b955394152857569b4e386be3a1ee9ea042fe487e837c648abb4", + "0x79606495a8d6fe4e2c6c8d372c65ffcf07a2e2cb4b679936c4d95fbf1a6f002d", + "0x6bf71ac583a6cc709d5c1b0578243d94daff2f60e961006d9af5d18feea6a1e7", + "0x4a28cf8e1f9817c1287e99b71c05c00fd578d9f9f9dd70c3437f46b4e57ae955", + "0x5d00c95935139416aa39dd912442173889235518e6030cd51e931b3fd67ceccd", + "0x3546bdb64c92a48db91209d968b51e7a430dfa807d21d279d2e6345258c133fc", + "0x4a445031b5832f8f17377f9719cfdb9330c4ef2010ddc136a5d1a76e50ecec79", + "0x92182dac689bda81052a4b3822492453430b766990f1ef4f4c130d03983a092f", + "0x0df917fbf91fa08ec823e07447f151a06aedb8a27269b43c9a71b234bce390d7", + "0x5f9978b01503fc566b721f01e02e95095749a4a731394b7cac7bf57ed56a635a", + "0xe52de94f9335251a5749f0b472190a87e984d6982de1cd0f16baf6dc18d87d40", + "0x389c9aa61f77c0d131f644c4e091ad2559ef7908f217c5019d63ba3b3863090b", + "0x319559d2267342b19bb2b2c33d36fa404c353bff4069e11e12e00e6f5269f9d1", + "0x664f7048a1402af13124a119e208fed5f4a2eaf1d617e5f22e29607962654088", + "0xef72047b85830a71d2d92ea54b132a67ea6ef7e8f74d8b690e4dd3f4e76d9003", + "0x6d3d2eca3bfe6be3840b64c6621484a1bb54c400921907bf8ae5584f98a6e69e", + "0xdfe9d3b89efc46e8d835eaa0d1e0e128826f8f610692f6f2f6f8aa4948741895", + "0x20339cd9907ba3e7298d14dd271f7818bb13f304c8b048294476f517d4114f14", + "0x509bff0185fc16887ff28fa06b44c39c4d4b9ce53113afe850e9e311a8ec5349", + "0x8c1814da7e7f3ef5118b589e7a473dadcdaee005a0f57edd64834ebde788210e", + "0x2e20966d3f48a7834c9a9f3c8919a5560bfe4c2925337072bf8ad9f4e4536090", + "0x12ad180aa71fee8d1410c9eb7a34f777f3d07dbd9e49af661ce168223827cb76", + "0x94c515671eb9bd8b525b7ebe12ff018ac3d5e99feeaacf3d94352eaddef9f061", + "0xaada4c8a2768bab0a122d15b5830912e9ed7bb70bd9beb63c877c938c075610c", + "0x59fd8332d0ade8f0a77ea60f5bcd34c66f943e130145a1a33efb4c3d14fbe9fc", + "0x93a81e0bbb9a4fe2ced60c59a01f690f925f01e858190c0d29d9b1530235a2a3", + "0x10e95aabc2502f70c490cbd7b3ea4a52ffcf21dc4705539cf3ed5a2382f51873", + "0xfccf2934b7c311f1ecaa40b5e01c3be7ca4afb7d36552b3625a1ea08e9130cdc", + "0x66b5f92406938f85ed5869ee8b725e02eb3a7beb225ed4f6989ba7a9410de347", + "0xde8ebc5ef4d1da90603a7e1b27be87a2443792a76cc643ee70357195d96e7c5b", + "0x079a58a70a149cdb59f57628a38639fc1aaca3c586cabe7b47753436d15be4bb", + "0xb69a29c31b23cb864a3bff9b8fce465523b5c71cbb81cc06b4feeee7edcf812f", + "0xc360e61d8fd4a3cfd0ac1db0b76cabf6be2bded1c334758ac5c3700652250191", + "0xfc2e491331a284fcda79b00b680f346fe4649fe15d447e60cf5c125d21ae2e15", + "0x6d78ba016bea5037199e0a3308f63e207ae5d526e8d4d8439c82216bc51fee2a", + "0xbda96e18689585c99db658bdd321d83f5196c5dde505bbf9abb5862c0f7fb152", + "0xa0d97bfcd8ec122065a92b702fe00c5813e18f2b3cf8baacad844cbca3a9c227", + "0xc12562d319b4e4c9b8b31d2354032ec02a58e17fa2d8bbb42736e452a732b13a", + "0x0186ebc60f9ba215a91bee6512a5fa56459e2aa0816f1b58c26698d23218e65d", + "0xa3cd14ce86a450f1597848e7429f8b901483df2988ba65fecbfe12f87e64f5d9", + "0xd6b1b29307bfb3653bd609fa220d1970aee307582a60dd2f5bc7fe2b10bd0b14", + "0xe4978eae9d05773d7010a5ba2c11384b4595dd9a4a23213b4223d5d8f929c3ad", + "0x958176e3fdd3be59524670815da14c1e8477783d567b898b5cb8dced534076b1", + "0x57e7a16450beacf0ef78be8b81f299d285ff810f8dcbebeb0f4aba05c5b67949", + "0x01698189e39a025ac882bfee0820852f6a9066e60ef6731192ced75835331096", + "0x5ae92487bce3b91020389ec0bb97bb020e6f7f2526bbd4fc4b44c8f837eb5989", + "0xe7522162b8f47c2b47b63fbaeda999b5b81649297893494f594d11780be89cc4", + "0x7445a9e724ee02bad02ac72d08e3406116fb2db751041f0d30c8c9c394e59f4b", + "0x0a7a3d396ccf759331d88fa40698de91975516f458be82c3996e4f6ce5b11859", + "0xe8547aa955f134f307c4dca40a3930bc19730b231348e3c2935b1aa85864e724", + "0x124be52db05a02d33fadf8639f6d89b74065c068739e7b94ac18828bb3b30aa0", + "0xb237904b878efb11df1327df8b5be4364bffb400c1ca66b6a94c3e56f85fff71", + "0xa76869c4bab08aa6a8ad926ccd0ba26b6998b5b66b57adf8d222929685a7c38b", + "0xc5d4ebde40f066d065719dd30f011694044caedbd860de663d370ab93d9cbddd", + "0x29573dd30e92d5c02e390c01322b588d34fb46cf4e483c874d2098643d017248", + "0x8506b4daeed4ffdfa58a71c2048f06c10d5e4bf666802b2f63f8278264fc019c", + "0xa5901b7d928862f9671fa4fdcd4c11ec1db38f9cb7a519acb76f628dc20dd609", + "0xebed8dd5d8f17860208c1ee4f894d7f12de88b0ce5f2b3e6c9541797c2d0606c", + "0x64102ca3c560b483f69dcae6db158014aa245a1f81c6a8307247dc24c66fcc40", + "0x0973eaf64afaa533e52af39d4d27a5709fc027a2352d807880502b66bdd10fe4", + "0xd7ded934ee1b98d04a37ac834c5dbee6b2aae2737f9d74c0afb3d5cd8a3d447a", + "0x1d4bd677648d90198b78e3f1fd23bba02dda835bd1aeee50331b694b81ccf94a", + "0x49f0b61d4ccfb53397f971fa8c3220a95d83d90a3b6ed5084b459a6d71f123a0", + "0x3531ce8de93f2f1b052649958b3808c4f39305dd6dbb6593d9a15b2d6883ca3e", + "0xaff4eace16e0f2779184684ee92cb898fcde8ff806bcb6db036968696c0d7dfc", + "0xb0361e0af0d9caa0e496173897f1bc3f40a278a45775f686d153820c9574c6e5", + "0x520fc1babcf9771f0a31d1cb3d3a9f8fc88df3c98190d2d0bf709a7824ae2f06", + "0x079999275f430fbc95367162ae0eddd0edf73e37b3211502d55d17e32203ef9d", + "0x03b1fba7c1e45757022558bad54792eba812e07cfdb6979d6cd6173f1749cd43", + "0x3249fff0fcd21a78a5144eaefa4f8e644c7e9adc8eaf99ae0fe4835add2d2c12", + "0xca3a1da09869bda3c2e3abcd1d52b66e394d581a0764b1361d05a9ea703c7a56", + "0x63fef729fb2c18d7d8d733ec93090d11c3d40deae23e67822b698b3a89f89479", + "0x18b2d89bcf049d91cd7c891bb21c415add05a2bef96b4c7aa56b82c733e153b0", + "0x05a34cc99136798b27da33021b25550575f1f4af572343b61d16fdeacaaf1f7d", + "0x125aa7fc5be74436a6870b2a97e74246833766ea6dcef38966b6378b0d7a7b78", + "0xfc48e2a71cf3033de75ffc21490fa2dd755394773e2764194212e88f8bd5ae1c", + "0xed8c0eff0531d8ceb8923f106234c9b37fcb380eb30110d5d9c10319a2304025", + "0x9556019280b186fba48578f0c1b33ced701f76dd538ded5b2d8fe55e76515dc4", + "0x3456afbb069708ac9e253388ceac409fe261798ab2e8d8a1520ee104a1d53e30", + "0xbcf6eaa8198072b19c0f73dab69f051ebdb049b7209ab1ce775f17851e4f844e", + "0x62797e7dad79f7f3c70833ad58b7e80abbb71ddbe9091fb1afc8c54582562245", + "0x96227bc772e9fad1852d45a3f03ef05b9e93302021be347939dda9e63b8d5821", + "0x348a82bb13b2dd363c741c8f3b59220480fd2157a46841ec29fa99e10291dc6d", + "0x410fa5c8b14b9bf9752d3083e751910f800bdc732f7584419b633209f1bc6029", + "0x72306560a481fa281004d14886ec9f3439f2e515f3b6b52d1bc2ad7d44b63cdd", + "0x1337fb2ec2f9306028d73b4a3aec1d1b5351e99468501398030f040c21ee5ed8", + "0xce8af3d7f264bdd6191104ed1a83ae61c368dc8d36868404737b77f95a78752a", + "0xabee7fae7008dc5ebab86102699290f0236ab262c90fbba7007723e4f5dde2b6", + "0x064ec9f423dbe994fe0fe60de43fdea160bc03132d6f0d4539f58d45568a80ea", + "0x3d629de28847ded7187acb6fbee64d7427be15dbd7b1608ed957aa43be9ba0bb", + "0xecfc798bb25651c80d9585984caf1c0459e075b1af8e38fa13866c4a6f57152d", + "0x827e4e1e9b2b346055046de89d78fbebc81664f91f33d9fb4e85417a283c0874", + "0x39916f0f66b8f67a239b07637d59a14d520c192d2cc87305475d1215f0f6daa3", + "0x2b69598f0d9541ee1ce76b14c934641824a3f5bf59e89ee96b9cde8a3d621d40", + "0x8359ab06310c7f4af3109b9c2b7582ff310a2e06eb59849a5f310bd11f40f6cb", + "0xf0526a2834dd0c714f08e265909077bb57ad6e79bb1caf989e101398ab2cd0fa", + "0x7ca8a7fb510acc7ad109b46422895b2d34b1e41fb23d90b4e77009cd8bc83836", + "0xf3296b81971c209bba6e486f72346af0f95f267236f79efd8a556630365756c8", + "0xe538e8d4ecf1d3fd1df939fd6fb964c8b5cf1ffd42c7c27ac5df86138b17156d", + "0x9f4c4c1e8ccd4ab646dbf5dc06db317ae4cde23b7cc052dc9b6ebdfa6aab58b1", + "0xf2834ac59b6a0be6fba8d76791daf0777a1243b81cc37fa4ebf32d01950087a9", + "0x3b8968669e198dbad12101a7ada37f5567fea8fa05d561037844ce2a2ab442b7", + "0x34ad3a1bb6dedb18c78cd5c9996bf757723d8642a081dc01a3865b425c645aac", + "0xc8bfb1086ec9550d24fe527eeea83f6ab6a7052b5926e19815596a1ce094916d", + "0xd2bdce96566be7ac853a8325ef4e4cce7c13b841c7f6e6de70156896a515a89e", + "0x0978305bc7daf1274b851b288f51a20bd2d06c0f36a1339da44008f88a2914bc", + "0x4ceb54c6a56ff907abc37fb80a83f732da8eb440edc59a6d698928ad1185b97b", + "0xce98d68a48c1a23f05019afc7c548fd512b3a959737ceeb322fc1d943fb870f3", + "0x7bddbee2548df9c4992fdc752c74674d413da15d9c58585abeb378663b445c15", + "0x7bf86667f5f8e2ea6b63aaa88c5dae45a04677cc0a985cbc5ebfd83f267a27e8", + "0x1017fd64f0ba3ea8ac4e2688506fe0489a246082ca3cb7862bf980dd3322c062", + "0x243acf73f5006d541019356f48402809133793370b09c736f2cfa231427b2c80", + "0x817692fc614b37c6c1dc5768d06449631665a189714107392b0bd5793a0b8658", + "0x9f40a6c1ab15f48c3ad4808c40a11f4e80bab810e3a2e5d4fe7327f21401eaea", + "0x390d4c4483fc9a43e5b0d958bb55d3b4fe43e0bd177f8f8764c0506b3c361b37", + "0x12bc8daf3c26cbaa139eb4f0fc28687f5735d489cb81baea4a196e308d0173d7", + "0xf9a7c872c157d7e87a5c2981aa38cf4acd854e668531161329cc295059094ae7", + "0xb9fccfd302def3ee33cdec01f81230ee7e9fda2f5600d35d0d21d3c35a971c39", + "0xcbdc7d2635eb9ec37678bffd881fd97842724db0708fed1f1eedff8eef0e001d", + "0xa30a1993d86f852c11bdebead080159517579295e552fadd3fbc416bb73668b4", + "0x03a49efba6486967dd49b85c7b57c76afec3ceb8bdf7b458b8b06485ab866ff4", + "0xf3fc49c93a3b1c513c9f9e90d25a9eaaeab5cf8e047781ddc9df8bafc165dd4a", + "0xab6e6831cc6192b253f38f2b98b600ee00800eeb292da3f3fa5fc999e2053d62", + "0xbb205480425b3b678e95363394b615825879c87228c9ccff0c2fcb79e266a217", + "0xd6e6a45322846a0f4e76a8b3b1f5fecd0fe59c4bd810b6998af3e437fe7c061a", + "0x2a81551f0aafd471915c427eba77340c79f34b5e4edeba506577e8e6cdd31bf8", + "0x3a23703915106f1bf047f31ed9cad7f2607e431f0e7eb5e5181a839d2d29b9f0", + "0x65e2c1d93e98d3b6b49d7c8da56bdd2bff2fe63737d0ba457ecc008b2807ba0a", + "0x967ab58d0b30477b4d5fffe8809c9b47eee100944008637c734f204fa3254107", + "0xb965a38f96afe9bb6de3c6ed2c613eba1368570aa6da0695214e6211e3cf4155", + "0x0b6cfea72599b03cb21811c282f0db3d4a6ee8d51bdc9824b4a492505b2a5f97", + "0xd74b53f7ccfc8ac564337c97dcf0e29923287597d392e6511495b1c6a4175c7f", + "0x66f41f8337bdadc5edb69f8de27240dcb302c41c692de4d625e693dcb4fdf91a", + "0x58b7afd60b379f2353ac98b32698491396b9e559bb42d70e1d70b60f092562d8", + "0xe5393a65e1a5f252d8fe5fbc51105999c6ec12699dfb1685d413a0033aefda16", + "0xf0a7e1c190d6db2e0278122573c8127d3b83003059d1e676a64618c193b571e6", + "0x80e16654e71518447047fad2576a4ed6eff5c5a9e2e99c9adbbf6818be2e2340", + "0x501b181eb33af2ce7bf8089229f30e545b6394e2ae00415ebbf532a72ed8c307", + "0x75acb74d4ed78c7b16c551ecc631cb0ba1cc41e4b9b8d15eb827d0a482db898c", + "0xaf273422632a0a2d9507768ec2e64279c17c9c15adc228fd9f9c6f9e1b2758dd", + "0x00dfcd50c0d40464a792e1d5ff5f57f1f86826291e1d76bfc0567b69c22c3229", + "0x852c857d8322e4935c7cc4eefe8991eed039f8a1caf6766ff7f935e5c995ceab", + "0x72854d85df80a4aa389280763fc2720b21eb7776feaa7fa6a7562bbb51c08f97", + "0x56c35bb293ce0e63fbbc61e7de396fe580faaf9db314ca15e0f8a6dffd2e1a6b", + "0xc2f2902f6220ae0a3f2d408ea68313d09a584b084c6a7dbaccbf9160fd9d74bf", + "0x49db5944cf1818d1ed780071a4ce2fdbc0be82b4a116f143ba102e4479fab8ed", + "0x7dbc79fa98e73a9a8b04f4e82febbb483a9e75e272d5474df3463b12996cb0f7", + "0xe2e0b3ae5b55d0e7ede1e873592fbc82645d5d9b355aee70029cc9433e18bf79", + "0x4b5f0cf95f68658dc4d7916b57c89ce22dc82516dc131881304cad218d0a18fd", + "0x7980c311968e0d79189a09a1281ea1707ef9e1985a5be9698fb80d022be6101d", + "0xe5a0418d18c19cb6777dccf99b2d10f210ed3158e90aafd20a4af19aa730f7b3", + "0xf099f3e3dd6eabe708c7c59b7ba1adaa7a33b1f0abd1425f7e5e398b5b9b924f", + "0x88f46dfd14e0de8e43d756422d660792903fded358481bb8d2414ee8e94d76a2", + "0x2ab7ee7a9fc04cfdcef40fa6e5c3c833465efc7b1ff90a879befd2d52be60486", + "0xc014362a60aac1a790faf04472c44fcf7ba789f673b5da0cd51f84613e7b022c", + "0x4be09725959a36c0e3a9dfd8dcc8ffeac083ad89344ab6fb603ce4cd704372fd", + "0x2fc69d3250e28d43bf523b1a953125196dcb9d45d72ec55983eb11319c334cac", + "0xeb17535db509d8e4d5f1be6e97306f45e0fe9ef3a091fb547d24cb3a0b3e968c", + "0x029358761c2286fa8a4d6cb5f293f93f3e1d84a43bdd19b5a4eb1be2e6a05a8e", + "0x755419406a59070897b5d4ab521255ca74c5f770d2da01732ad86fbee441dccd", + "0xa9b32f0b60533674cc7a255e7a70cc1406d9fc67f755fdf9e8206a1aff87aec4", + "0x08254c84fa90b7a1aa77a6494770098d10d3184907f31184dfa29b2d6af7d627", + "0xdd0f78891fdc7c3a554e84d6117f0e7ceded8ec4241e964651056a65e139e8e2", + "0x8464efb16763c2bfb5aa18beb48b40c96dba00c9207b8cc1a167dbee6f496b26", + "0x61543d1b7491e091d8e6b2cd07c54bc0867f24daa65a5d2d97e39f999e4a7a23", + "0x23177c9fc8a3a23c0b20ee66879612454499872b9f12351f80b36d50ce3ee46a", + "0x1ffab43af796e9cb3473ea35c84c0db3cb6ab6b88dc15544cf3195340fd76c54", + "0xd94e4e22c908e8e54c5716df978ecd75bd3888356474d81a786d7d07ad621745", + "0xabfe2f40cbdc9cf7b5eab58876f989b3f42eb764b277e71daa8c369b0653bdfd", + "0x0b58694df201717af8a94936f42d02db4713c3f12d25616bbbea071e4f1bc7ea", + "0x5007f203e6fbe118a88052dcedddd095de5a20a8b8b74bd4f2c264c77af38aa1", + "0x471d86c2d63353c3a9cc14ae55bdc4eb1430e101e800d584d248eef088c8f654", + "0xb8b69e5f7f242e49e1950dfe1904fec7dd079f64c26902e13629cfd0f3efea50", + "0xe70d4cc186786cfa7bef003e8a2baeb512178823b82b282fee4b4c8cac2b2167", + "0xcf3eab30db5dbc7d3f7682a43e2c717f94b5b88fe980e9533ef140464273eaaf", + "0x26c55dbe29b099957ae80fde63b5d6805ce8a8d669a85c94c45bb265f61eb8d7", + "0x6fb8d5689bdd280f5446df703ec98b5c65416311d9b5b3d99737d7778ae73931", + "0xc19a58698c2b3528fd3a021c1f9251b147f32f863bf467e24a62cdec0c3bf434", + "0xff4e564f7b250d1f6997236053e2721b0c8370e824b9cb2d0f9aab5760ab3703", + "0x424aef75b1f8f808e923002c55badef8f2ab1676cad8c516ee22bad480247c5d", + "0x112707b924205faef4ef5fe92f51c962cdef18a4e159f87309c86492796e0df0", + "0x3908962b3b510ebc16a3d7aeedd07b5903360275e3f490ba1b4c343ad049a422", + "0x22fd7af3b1d5169e5e26357c933a4871958ff8662548bb0a291724f7351e62c8", + "0xeac7d509b715273c21f37925b84c3f068937418333d5cc875f83454d3961fa03", + "0x62585a5e396a1e96b7f2a8ce9f79ad5d476be838b979ed61ca32f4885a49d611", + "0x567662fa9cd8d2eada96474eb057d1b0af8c00a061767d6b793f7eb11f6a455a", + "0xc463e78741be372fe49ebaf7c2da38782befe668d78edf0dffb806216e39b2fc", + "0x02c9e9bc240332b952cba9ae3c79e90ccc1504d5906b5b8bdb6ab860be804548", + "0x9a40d8d09f715c22f791a626c4f30aabd040b8b0613fc03e6aa893ce60be9eed", + "0xe28e41e7130e212413aaaa572e26ccd38c3cc735e97006791679165584294768", + "0xef7e5dd12edd143e2c2f646326fa7b90fa00b7d9d6c2cc233716a9960ad08561", + "0x443db78aa8159ce8488643a6fe2359a477db701e251e1bbc3564621b296e0ac7", + "0x559a6a27d0f0ed434a34b262f132796b8d9e5a66a041a56142c1741961f1b2a6", + "0x5af5b57c2835f09e57eacb3eff984b7d543ffbe3a9d4035ea816089172deea18", + "0xabbcb7c6258d681a8bcdfbd8fc8cc72fa355ddf311feb6b880821f0705ff0700", + "0xb363563c007184a2533cb12f177ea618dd926243426369788f31c37f1435f2e2", + "0xb6ea8c286ed50af493a96cce824bf144cfb05b1045bde2a4d3497fb727a9b128", + "0x3267f51e5948c98da41d0b8d48eac9dd0ed9c985df6e8734490866e12d02c15d", + "0x20d9c83b55b33a7de7b4e98680b7e8da43226984c0e4f3426c71a24c35131424", + "0x67868b72c8c82e071dbc24c596588558cb895b642ad62b665b50836c11ebbcf0", + "0xa062edf6b2f08eaf7e3a6eb20661a054c7f7627df410a4ffd6a6352b9a5e004d", + "0x90a036a7b5488685b0c8ebdcb7553ada4dc965b99cb7a822e67aec43150f805f", + "0x86afcba1703cb39d944c2d27ca3121ee749f45139f241173d6783bbee32a24b5", + "0xac66f496fd8b0babf0228fe29b5a86cc5eb175541b7ab2e76c29eaa5bdfd8b99", + "0x3294ea47eecb646546b33374abf4feb2588b571fb6161489f83138d29fd51673", + "0x0aef3d9ca7c6eb16f03bee23444c1962011b3b15d20d47d962f1a9ea2fab549c", + "0xb9a61e9f6265062e97f3b17f01ada88cf50911ebeb7c21e14d58bc4f572328b8", + "0x5efd95a0f589d3a4ed58c41e8de0248d4b6c7ca3d90430ba5a2275619ec8d600", + "0x07bb3ddd29cf9dd78046f136916034b0d91b73a82a1409c1e89523e35a83c994", + "0x4529fa4c0ae999e451cf6736a0d0c23bffbc921d28f11e9484057d790a721c00", + "0xa095d9e8b2c1777de4ef86121d37ed62f736b59cd4450dc311ae4aaaeea9759d", + "0xf1932356aabc45f0fbfbf8c37e5ca6e3b291a671654aa631fe7c83e7d551f0b8", + "0x235f43f46b21bb6e5d0f2c02bd14c39567ac3b283e6f6ef1e051ebcdd17b92e2", + "0xea009cbe68d32687e7905c7d7f047dd38f328289c907697cc2dbb342cc248719", + "0xfeb9cf755056669cf75f642ac3ef08e10dd9a1270d973de9aecbba07edd41041", + "0x591ce376e94b7ccedbd4d431c2b7064dd8d19a2c0fecc1207bca4833a11f43e0", + "0x458546ae8fdc1fb788859844502c7ebbf919b1b057d44b8d3d30d13b06652a58", + "0xcbc95537eac06d4041e4fef49686ccf29afcc61cb99861a1e78969563494afc3", + "0xfb99ddb5b0d5a1b3e99bcc2afb2dc3eb4369a77785a468fe5a1ac515adb92001", + "0xa006da82225e8442c5f946ff1c702dc98ea2f9256460723e5cc6fec712136845", + "0xfa9d4331c649ee2361438a1bf9380c52842d9b420812d608ba3e8217537d8848", + "0xc7dff710cdb7b9637588c9774afa0446e98fd1bcd15589b20b16f607e2defe18", + "0x736b50bb6f09709f76ad81feb4df9bcb11c5af1587d7186770624e11255ca249", + "0xb13066c1566583ea145b0ca86360beacdd17c96bbe48dde7c3c4fb105e1e80cf", + "0xa1560b9ef6f8c55e7b892fcaf4478f234aed52de7f5018122434da3eae1bfd0c", + "0xd676ebee3b7ad0bf139e9daa24fc57004c10f72df8505f35cb10481f9f3f1766", + "0xabb59db77afd292e896f65c482a8216f41a58585229038eaa5bdb9b2a1f25321", + "0xba75b9263ba16754cc72624f55afa814b649113c147531e545d0aa8f6e102bd6", + "0x8eb5922cb15d19867233ca4d84020235626105b6f13d79e0f40a2ef2120ed4ea", + "0xf6d1059334718202c7a0de06665b23b09e1398b03b2adc47a962bce526744e23", + "0x97395b21610bf666e74cf8742ebfac8b984a357df6632147ab3b5a4d32f8a46a", + "0x43f07678e00a692bb6bbb8bc6d4596ef91b7ef8bde5304f5b1c7217519056958", + "0xf512abd3df7c642d65086e9fc4de48272328dfaf78acd4484b7f88b9fe700d27", + "0x6048ef45d19410232b22f5cf47bed240de8d7ec3979174cf289cbdc791aea58d", + "0x14fc663c883739919f1da92f692219746b55fe49bf26fb0d129a360708f64645", + "0x39aceed977bdddc00cb2a7ee1cc796d8b179471cd5d76b7bf42bdf0d48a6b946", + "0xa1c01a66b7b5898df66444a8d68fc9ea9fd34ce9c986a43380379acc6e4cdea7", + "0x61a6537ae42a684ea4122a27333e4fa6f3a24a8d05b6f760e801849e45c0c4de", + "0xf7829186312ac01375b549dde3731a33db69a00233162801bc38ff7518e932a5", + "0xc6643a8074b66c471aef5de35f6319617d6cec87dfdeb485d1ada2276d6fb037", + "0x35f8202d6119356bd2c7a9638234c252a12b6b0ec97d0468998619b9e755a058", + "0x01569c88b2a7b64a3b2e7d04665045da2382f92f670f45a1a29968359652967b", + "0xc0a768c7d755baff53f98e442bb32f6cfda1aced18234ec1360c64afeeeab949", + "0xfd8528cfe4f1667123775990e351700b2a0f3c0b53e4e9a4a197a18a9cfc4292", + "0xb5689a758a1e3b44da61154aea734b2a643b6be30e4a57d3b7d326a3dd42d8de", + "0x37e3ad7f9abafafff2b84486adcb39c916b90042ad988743f69318a507ca0d70", + "0xb22b446a374b675bed59818fe05a81410f6973f749d7726d9ba8c810bfa43fb1", + "0x72da6656db47610d35a65ad7271417cfaad3d68c322485552e7feaa1fe59cd54", + "0xbe9f971f20cbdc3efac3ea62692e2258d65de35fe045bd76294d94ffbf648759", + "0xaafd0dbc0653af289253387303ef2b23e1405da8211c98f288ab2349df878f21", + "0xbdd5963e37533877cfb01e4f3502f1bc1e5ab2911dad670b70953333e8c0c4db", + "0x3a0af5353fd9f48a2840e067e715c568984ab762bcad4ffd5e068eb0fa56e154", + "0xcebb9a7a15e7a7548ee2669ae6498185a294b614a97f04916e8eaa5dee169b0e", + "0x32fef4ea43b1f21680d151e1a4e54e166e79b8bc406cae292c1443b7d261f4e3", + "0xbc3c0d3f8886f84e2c7443c5e3fa51049b4d7d2c5eab7309ccc42ebd4243343b", + "0x4f759ce94ee7cdfedb26cc2af4c8b72116c7d6c4f927808bc14d67268de80262", + "0xf582d41d1b02c30f6298c827bfae83f4156052ae80b90e1e18f2a9a73fe0b596", + "0x74125e6946d9a4d66dace3ad9b3423e7da31dc92e22af345df57c1660acbccba", + "0xade8ffa3bdb845e41d0f4ee30253cf889afa9d38ebae4fe51bff8d4606004413", + "0xb050b444701900454bfcb41bd752a1ef01d5635cbd8f75ea7e746e0beb15ca1e", + "0x355b995b8f89841f79a3b498147ad8365bf6840c5c52cbf926799f5447b2e9c9", + "0xf27da828a12c9562d4ec03e0d4dade1f3d9ce9adf3a98e165a5a700d71e7a35a", + "0x24af925b63be3f7da6563d2aa20bb0b42d5b70d70ed5ef3ee951bbbdf377b13a", + "0x53c7e199a2a616a5ffd538916bd910fb7afa0dc02d833f634172a4447752515f", + "0xa85ef76b3796b17e068305cce15825d9e26814e3b0ea277e963f44adf261a3c3", + "0xc4bc5106169a82377fd50fbc5029f30b64a40ad53971aeed37cd8f22c0732708", + "0xe9bd61d652de6b601e3f13de3ea7f26f71a30ae9cc14d3c8401cfd213f459bf8", + "0xb04d9b030ffcdac955c3d621ae0323f99f29461e1ebd7bbd79c7dc9f472c88e2", + "0x958e6501abced248e189e11dc348d5384705c86edc271440ea61e1c90a7c5c25", + "0x784c880e2a57475c23622be37b5287c6eda011400b375ec32a470c94706f68e4", + "0xe86eaef3ef02ca8a6b09360efe65ddab224b6c52579c9b82bae3bb2880da07fe", + "0x2c9788247d893e1e746b55687fbe5b05146bb1a44170641090f6dd169646f4c0", + "0xad3989375acc83b7e58e9805e38d69c06dc76eb0033740d163c44f0c23b022ee", + "0x819f9c81b8598c9346eb9e20beb4222d20381bcbff4ce90d6d3c8f745316f0d5", + "0x6e54ec42f3d42370dc9ed45d58beeb44f2bb8183c8ccb2bdaa48b6c924165f3f", + "0x523c35f70c833b554c4af4eecdfa6b0645b28eb8e75943690e01808cd8bbe5a5", + "0x4caf49051e1feffc53783db5ee9e558b6d6f743ddc724ad48bb497caca3537f1", + "0x4b15c64f21e9b766bc57eb27bf8328c48b06932d0bca815032cfd03e8ed17269", + "0xcc721c4f57a44584fb594c27221fd69d9c011bf0a59c116b78eef3af9c39e43f", + "0xdc0d6770a8830721418504292b1459dfa1149ec4ebc3a68b30693d99fdb77bf0", + "0xed2d59139943fe9513c051644079cc421595c0ce258daab0f1fa67faa550a5dd", + "0x2df8bcd4477cdd7bad1b2164c6658cb0b4bea3fcaf560fef539da14ca8af72dd", + "0xb9da0d58a804a30b5bf38c90d36ea7e5058f6c3e40ef92056f9bfd1e763e6067", + "0xad3fec2e08743d6651859da6c68541d68a958a4a4e8710d9c9a2eaf528c065c4", + "0x38a83932f4dac1a8bd4d9500a973c8e0a4da1720a91c7dbfe0c90e2874ce374d", + "0xfd4e5213aa29c2c9218b315f928b400da7c76ce6064e88b73569538d260fe6ca", + "0x7204424592fc96aa87c3228bef6d9b20d23ddc0f42aeedeab1eadc2111088595", + "0xd4fa9c0f555c426aff02f0a78a089212a4dfa49c185da59fc8a72a1f09694a85", + "0x048bc2ccc29b7b53bdcdcc9703abdc525414c72ddd37974ad3024ab333ade6b8", + "0x2bb19f1454c221f17d77bc01370e841d50d313dff45066087cf98805191cac81", + "0x6c6e7978163af3c55de12537f690284b6d1e1463bfa347f5f95e56a40b97d4f6", + "0x962941177ebfa8fd3b02ac0d259c6ac29658aa6e53341cb537df79f6761166ea", + "0xdb525afc59b327c0cd5274f6743bd13cc62b2a625c4417bb43ec610c0d01579a", + "0x331cc90c1863f0ae9b48ecd07b9477551da28b404b4df0499d7c512670800a1d", + "0x009603f384053f8e8a0607410aa9dff372d7e16f45f8b14c25b90f26f7c8b765", + "0xc21b29371d9b709820488cae6b15df831848629428f358e2ae978040acfe2514", + "0x7a04228fb19aacd5fcbc666c6b1a5988879a978fcd296b8fdc344222718cdbfe", + "0x61179cc0a9e6f47c20b4d3289126751537d4826543fd685191d0a9676380e4ca", + "0xd26c60275553647b6e431ca6f5e12e48557e0ca82ab76f75e9340289939f29cc", + "0xf075093992d9cfb65405e3fd74da733d95fe81d19636750b0842e03e15b6a720", + "0x19987f7b857c3281d63c333e48efcca5225a052298db2d776c6995a8947a2255", + "0x5968e3dfd4e142128422ddbd5cab00258bde828e3f82ec0ed968de59178b3caa", + "0x5401a35760bdc8eaf3a96cf97e70d8e8f6b7efaab64f1b5ad45cfa4aa430df93", + "0x9584dfedadc649160f57d922eab48b539ff44a61250d8973a01ca2d2cdb9ad2d", + "0x1e6fab7230a22c00dcb7a0d680909bc116827a1a816b67c2b49a087791467c73", + "0x7be23fafa2bdff189cee10058bfad5902a4975e36ea0c70464b28c3fc5ec08b0", + "0x5fa74fe32181d1ded11b59b336f2e1f6ee7aac7e553f21749224b7ac4d78f522", + "0x24b947607123edbeaaa496a7f180a354f6fb6de6e183f35634800c4105055c8a", + "0x943128f14208100acba220ed65954b642b51c39bcdb6a6a5ccf077443e3fd132", + "0x1e3d76efa73bad1c34d66cbb4b2110953b977a24806658bfe9976a9a42c1ff53", + "0x4b88498feab08560774c36532d85a8bb1fcc5308e0ffc2725d96ca736974e82b", + "0xe0e9ef3b7794d4182572cc64e059ced8e6477dff918c87212c3bb43b6f15fda7", + "0x9a2bec4ecf6921d4ec3d67c0a5ff62467ff815491abb349a016b5d3820f06548", + "0xff610e977fc56997f9ab0b4bfe13d187dfed82bb413a62543de1a39f0ad7df37", + "0xfd382aa51134f38a51bb23eb6ce04923ff38d585f450cbe37f2cff9262b52dd1", + "0xf87c51fc87fba75af8d4352a74f715844409c302f579dcf39ccb2be956e32fa4", + "0x447befb42b7d156985d5418bfad3e6b02742282706aa329d5c10b5b92bd6694c", + "0x0f26775661730380c986f006ae6abdaddaff68e600fe4d11bb92f48d10be1688", + "0xbb9d3d3866d7238b870d23df1224ca8fed77e514f12e4de2d72e11d03002b177", + "0x4f0c5a73e08b6b649d0643bfa3108d7a0d7f4528dbbe564ac309200a81b329cc", + "0xb34b90114645c5cf31136773e40f4a5d392405c8da7ba9e909d232935fcc9abe", + "0x8971738983a09b1e46e4ec3873b27755522276f0aef29b6f09346a866b28d1d0", + "0x98c966d02c29452956546d17d2ea6f3e0ddba18425c0595643ae046e2b7293c1", + "0x493d4d3096c2c46b841638296f21591cfa0e5079130dde96d0e0c14a89301763", + "0xdebbee2095e3cff1437e6e004c476d023b4e921450c7c1ab02b655521caf334f", + "0xc3d3686224c6535835a50dcdca1af520279b2f2aa85034fbff24706eec2342d7", + "0xe137c0ee8b6752ea5b5e2c41167a13e16723e0a0acc65801db2b1bebb23943e4", + "0x9fbdb7bafc299cccc4ee81495da9339200de66b45bb50f8e9ec62f4a4c27de70", + "0xb1ba06592a496297f80f4f8a615d9bffe12a9693f618a3e7193688b965a37084", + "0x2d62651aa4f6a88a3138cdaeaba477759790efcfade99ae211991237e3c04ee1", + "0x9d2eb483ffda4e46bbebaca12fbce6d66781a832f784fd149be03cd0579029da", + "0x1450746a0b3cc4234312b5dae884d383b6da946f081ebce20c79f46d3e00e7fb", + "0x7bb453148ce82a07cc230c5c007e6e3a2aeefea88b345023382abad3c57101ca", + "0xe591ae7350f6d341e34ca3a5bb81a7a51aceec971ec8f15d98f5869b8a759604", + "0xf4454862bc9b4697153dcbc0fd4588eeb30fb0a2fa2c6a9fcc83fe3820707777", + "0xc0680c8d59f248d50ea938abd6daccbfe92786ea71e4226673631ed1724b8841", + "0x999913693772e00f0f794760e3e045ad72c7d2253e35380f1df1a7c5de0a1c00", + "0xc54de39aeacb9f33c781a10dccd71f106753e89c69a1c09df8125cdd20a9c6ab", + "0xc94155057c1987735abc537fbd44d19f1cfe746bc0c9a108236868feb0ce1c48", + "0x787a58b5105fb5d510c851460262fd80064683edeaebc3754abba83b91d85c31", + "0x52ad4f7ab9517fb23d34e050ea715ac38d06d4a827047dae09ec2b09d58fe78e", + "0x6d2e50d02431434786fa5ccfc91d8b08a070bb951cb1fb6473f06e2b70fd91f7", + "0x50482bb38e06d8ad3975b0092e239f5170e58600425756208c15f67fe7c5228b", + "0x259c6ab82f10a0266aab7a7961e72b0b456d95f9f3c68596431eee4f942bada7", + "0x68c37cd1dc035ddc4f3e758c17057708386e21e38fcb2ffe314967b462375591", + "0xe82352461757b20c4fef7b051191510aa23ed445ce9a911ce0efcb6ce7348df4", + "0xed2db18109bdfe20a76a7c327d1ab61f02020b0d8fade4e1fe96b3fbd732f8e5", + "0x8daef1767e29658c7b178e23a09b0c5f0342812275093c7bcb64bb63c5fae834", + "0x46c09063d18357aaa589ced455d11ab78a68d37845fdba71cf3dbb33dd9544f9", + "0x1689d60e7d320272faa482a57dc82f45690348b5440d2c171706ae9a885458bc", + "0x108be0a54965ff2765b6e8f3d56f3d0f5d83e81a85114f9fdf3384597d58e618", + "0x316e9dea6e8756824dee7aed98c9853438eef10f0e4b71b1e3c5d18a5dbb2757", + "0x1dd208e8098078c2e7486e663791867b6f1ccab81d139adc64a46e39a15d82dc", + "0x7ec96b5784a8ffbbcb715ccdab480211781acd9a5b61a489bd83e2c3160e8039", + "0xfb2a2e6167bee52c957a862fdf8c454ce44e34d345ae147ab84aa0874510a993", + "0x3ce4de75aed583bf60432010181950d402ac7379652c7a04d6a8d0ae916a4b63", + "0xf0686c7f7034ad6eb7cfce4a2ba138a6be4647a4fb0d64e7f88aee4ba3b803c6", + "0x3d094a631b86d89e3b2bfd67522663b3d59b7cc9a9231e7741b6e7312ea385bb", + "0x2bb6fc1381b95ea0e6ba3214906ac812202c27989ed70b2cf5615c3d81159dd4", + "0x1afc634dd428f6d35f0323635caf514722a80e86109b24f6521b06628bc79a88", + "0xdcc0faace6591883b4eb18311cd333c742e1256725a1bfcb6e41cf5967b0b9c1", + "0x52a100337fad1888069c1bd89847b1b1c53ce1797614f16396bb4bc879620055", + "0xd6e12def270e08eb39c511926f6d9b77b94d97470e8396f59c0d76449ae02e25", + "0x1a9e24b606c8b3426ddfce256192a9fa24236456d952283f89e54a4cda665ac2", + "0xbc3e9125f5ffefd84a7b7961c4bbec293139d4f8433597c09b91b91a6c301d79", + "0x7dcee8b67533e9718870e075fdd5012744693dc1861a4468576e6c8d57692d45", + "0x77785e00a40618aa9db27c962a901d3d8e1d73669a71b89c996a0d14bda1ea82", + "0x3a05b4c263ebd343c13548f7ac679e3a008eac87700bcb61a89ea85add4d70a5", + "0x5e5a8133cbf70e191de072efc4ba13baaf648134263735fc9f36721648fa8d3f", + "0xb4707b18d14818b288b32cc14f52bf27d821f5336d3e895100e06ac153c19ea7", + "0x3d78523a275a05551c58cad20402d1f12e0e9488aad0472f9f4b55bcb7c835dc", + "0xac206cde4283ddda8b0e5745377f711eecd2ae8f8945b2e2907a4fe65b934108", + "0x15daaa3de4be577d00a6f9f997ab355783be1c7548d10df60bde739bf2b9898c", + "0x396f364ac03370c6f20fe792108427eed8a595315e078b17c30692f912b0a587", + "0x15af8c7f58cdfb4392d630d57f5a43457c0f7b4b0ec525106479d946f9681872", + "0x00404951b88d83984f36a87544d04a5562502281463ea2bb1b4f5d599a4b41db", + "0x5b48a66608e6877534953c0a2a4af19cbac53d9ebf85058a159fc81ebdc8f713", + "0x97b0d142d1cfb9590bb97d56913f6fe890f77943247a7898f6122b3acf5f9129", + "0x639636927bbf38356fe8f7663be9eec7ed8f1ea3903e91df3a9dfeac371eec13", + "0xc042cc39d6ad57879961ca7ea80a7f7d136629b04d645f8d55a96485ba5d3be6", + "0x883e3e3417b6f8c3b1eb961cbf0fe17d11c64f6e7c4b238b85f8b7f956d099b0", + "0x35d35726a4c89dc01b6144fe1903c26306a6e8666c241b276eb8d68934feb79a", + "0x2539261813559f740b56a67c6e640c1b3c58613f830bdafeb52df2c527e4ec41", + "0x08a83133bdb12b540618162fa0afd566f1e79d5af3defdc024395a106ec73843", + "0x68c3c756f456290f5d8078ee9ccf71c27a78cdb3ebd263e2f239d2a76bc17e15", + "0x44f1680e3f43e0478b3336dfb20db3675c0bfbf7c051273583dcb41a5b6004d7", + "0x86c8343792481ee8f2440eb5001c838bfb91dba7029233416ecb9912b18d0c22", + "0xca34f890999521a3d899bcb2f59fc484537736c81ee63a14f54a187c40aad7b7", + "0xcbccd23e9d50db5e832e5c1a8fc454a33d0edcd920895005a8f6361bf1ceefe5", + "0x1f6de8f5f6e8000d53166403169ba38c72332df3e00bb3d891bcfd7196a079df", + "0xa818e2986eb5c6ae5c51c89651497abdffd1649f286fc2aa5a2fcd24756fc5b6", + "0x31d8804ba7f05fcabb57aae75f5423dd10fff9a75fbccab6462c9e9c26851bf4", + "0x1099db30718318c9155ef5d39c64f6903de6e8900bfc94488d1b97d1ad0f8b28", + "0x8ee976b0a9852d3d28767aa77f8747f513d0f0f6d26e0b6aaebd70e26855e6ba", + "0x60be19697f29bc3f3f26acbe702345c809b7e18169432a2605fd1c84dec71eac", + "0x5bf05e398150e6719360604e13800a456cf932bbffbd142df910a2d8404cf914", + "0xb83d6123213ebe0c8a06e01fa6ba489f58e6b04b9efc3786b6e3bb036f74d9b4", + "0xb5302c1924808a1c6f31a09eb120d73e9a72d69eebfa813e460e4950d7018fc9", + "0x307228683a49c606a238caa3a6f276c720433896539b37b12ad204a6b9fcfaa2", + "0x8a12ed88dc5b261fa5d30e8cf82ffe74b010fa16780cc949b3fd936f0efe1901", + "0x794b40ff10cc50956efdf9286b9281906bb76c9d6988f0dc684ea26fcf8a6b67", + "0xb853ad96ce7311d90a093579f1e471a3c54cbeec49d1c8300973ed8ed1d88c71", + "0xe4a922c910ffffb83438875636cf14c42292317d623823eb65af414773f18597", + "0x072d05a3584dba1889d84ccb2ab82e6a1242a4f515fe81b004603e3c808b891f", + "0x1d20d71ea16d7c5c29202c9d995d03d65ecd570976b9d945cb940a53442ae9cd", + "0x2935b1408bf5dfce1f44c6c4dadec5f296cc73a62e3ca0d278fe6a4557d58a42", + "0xbc69c459b53f865b2b1f5062df59ad1057281b6e1098420e0c14857382162a92", + "0x6b91256e7b283d11f3e8ca4208a8140be0d0de99c4c4a3f8fbca941707cca428", + "0x82342722dcd42b9a62facefe424de9d8d413da7f28de95d9e3ddbd9c6abcfb1e", + "0x62daa2c35f7e30c227ce9e843e3738867afd8443504987eec998601483175217", + "0xa23acb9320cdf7248c5b91cfcbb1fa12e21a9dd0ebd7a98297535c9fbdfa0435", + "0xfd0f8bbdfd5d6bed843dfeb6267012423806499da94cfad17dff048b9685ef7a", + "0xde1bde38a693a9b3d61671c7a963c938e5524eb6ae1ac0dad738e993f63b2754", + "0x83ba400575ff9f9520abbe80963e6bcac51b27f28083454356824305587dc996", + "0xa753a6871ec9d9ead3deb0df8e96745cdb3a2633e3b1b31f58238609d881ce82", + "0x0d6daac4b175736c463a58dc23b78a967988c24293bc9de2696cf8cd3f63dd96", + "0xb64b8ff8a80b3932bce3fdbac8c9c653d60be1a201c37fdc5ef34bcd46d76da9", + "0x46208951475e8b7758dafa909abe56d349d00fa136ae13a9a3e681c440a235ef", + "0x25fc761aa54e2035c4c560fdbaf8598d64b55b268ced9f0c5fcbcf2d6a852e21", + "0xe2a0c3ca1fc3f29507b61b3178ba940cb17b65391a21d3689046bef47ef5f08e", + "0x36f3d91db9327896c51ae53db320f7e492da329b722dc838bfbc428c38216a40", + "0x9b3e3884e347f91211345b50e911fbf534e2632ba9064bc376ad14bc0131059c", + "0xe8c95fcee1357c3c8f1a539d79e14c948d56272b69ccd650e835808948134099", + "0x4e3d251e277eb58af3ed0bfea4d5c8ef3be671c907e8f1f23c161c2917b82c0d", + "0xff99ae2b1c5dcec0e3decedae20b1afa0b6d5cb8120b9b684e758e4d6c817a63", + "0x2f56720ca8dfce58f056580d8f64af14c04465abd7c5eda63cd31f21ca3c183f", + "0x95ca18c354ba473c7c24ebdbb9511db216cf074c6fdeb2677738864238d4b133", + "0xe8eba775684cba8fc3032da1928180eefc36ee4e6c0c631de31dde5119aa9277", + "0x7dd5ad82dfb09d20a5ec78f6c463e1923f60fa1be3a6cc837da6ba134ffcafa3", + "0x93fb694555344d16fc02786ca4814f57c03c95743f37437aadaad699f51dbc61", + "0x7258e9a767ef29625d2ccfefe5953dc2e11b76e9824a02d82998d5009531871f", + "0x27e9a65872d93a8165d3b8fe4ba062750ca4265ed72392c0156c14c803c6d5dc", + "0xc8b4d0cada2795cf34288479604f7daf0880cf70d8e897560ed91175b13e1b10", + "0x972ddc6f197cd3a8150aa560effb517427e563d41bb2a22ab47dceac0a034a9f", + "0x1c70c5d5a500b52651188a8587219c0f8b5d2ae06e45ab8dacb7ffd6b5324086", + "0x625b0aa804d2d38c076bfeb33b64f32f2c944e28d36122c62b8bdb20ebd3fba5", + "0xd1e372e1e762bab8bbbfbde7ea03059e26546a77a2ce068a8ea99dbed6e9ff11", + "0xb4a0684d59b9ff0231f7d0429730ec35d61fb19c2da4731eb51e21577c579b37", + "0x35445e4982637efc9168dcdd85fcee79fa84374bc46dc43807c527d3fcb4e56d", + "0xd30cfc740c59b0eb0b360a0e48421ef2a5dd848372b8f9a6a062e387b37db10c", + "0x2b006e209675c3cd82775ff14e1133ec9569d8631737f9717bbae294c3aec9bf", + "0x9ea0eabf308cadcdba6d920670cd4310fcd18365af081c3666562dc4c5d7c0a3", + "0xe62e4a4812aad345a3e13b579bf038a6d7a72744248af1f27c92faf98fde0b35", + "0x4c2cd1362e021c3fa8b29d5d9757d32a5450c65afd7c0d5684542430b265cd2d", + "0x11e0d536e837b467744a3207cf5599c3a4a24a95f5983fa26cce123efafe2ac4", + "0xcf7f737e25d8b4c4b62cb578b0630234d0c8b3866024fe08ec528e11fd89bd84", + "0x20b5bfcd9fa25cfd140af525840387432c03ceb31ea4c9a5061aee6e6b26d0b7", + "0x2c3fb155ef0555f43550e59b7a672893c3fda5014de0edb471030a7f4fa575b4", + "0x7345803ba4e095dd191c2cd1493a06a8b07c1f8e34f85ca513e9d750b7e8d8cd", + "0x38c29ac3938490ce60b28ad8417a6a1fc80e32b5121488d99e24bf85a1a8a599", + "0x1d67be1844308e13375c4e5086999fe4de08bb0dbccdf9355d00be27f4202b0b", + "0xe4493f2eaf951d59b313987e11a13907f90d806329a4c07570b95d6c84be2d34", + "0x690060935c794dd59faa05e0ac11c59d8e6356658924d64ec879a2844c861b86", + "0x119edd3e61c0514e7141e90981c18b06d44f86708ea5112230dc63bf55218199", + "0xf3fa29dfcaaad83f9c659688b12d9335761f20d6557ae8ce2f58a21e8e78d8a0", + "0x29343779ded8f667d17694c9e26c68573a673b2dc9095af72ad8b71a66d8a1d2", + "0xaec1a8430e476ad6bc2105966063d780bb5c8993d814257ad43460e80dc4e94e", + "0x8ec332c4d04e7f22e4f31945b9283cbabb26293c07f818281ccbdf9169c99758", + "0x44d0b6e62faa3d6f32110aac6c7e5da12f60ce77ced439d834e0f0c75b753b37", + "0x8a87059ac3dbc5d6c905b7ef0f81fa326895c3662b96d2ed833f73fef3ef1f4e", + "0xf409ea13933804f84146580f3c2db262b565b115a5e0c8ce61d9980eda132ace", + "0xd49d0b8403d53a3e784ff4486a89615fca7168755f97b4e977dff21862501994", + "0x60c0e637b2f9d7ce3d186c55ec2530df1b4bb52ee3dc2130393d39626a33ebc9", + "0xe7d6136527d403ca2b97ed01585245b1c268fec713e3f9a72eef0b1b89fbe27c", + "0x446eebc18c5d6c9a54c2aa01df0aed4419516d6d218436293cdccc88aba0923e", + "0xfd4ae68987e60126e256676024e8fe42162b369061de714978f7cc51dfa8ce13", + "0xd2f2db43dfd8d04800fc052edd8f212ec2d868401aad54f1ecfbd95e282d562e", + "0x06bc843a50f22195a4385f7a69545c8376de3a2eb79f9e42c23be2787783ea68", + "0x6c7ec7a3f66fce6d490d5a67d82c8c13b4a8fb34ba2054c8ffe2d0651b199366", + "0x317554f79cc49b23dddf21a284a6ba6ba57aeba72c055634bc5bad63ca8e2f44", + "0x1266b61a1f2b0fb3bcb0479ed5f48b4472ef960f0aca536414631200e5215d1b", + "0xda69469164c73c0c019fd64cb335db63b3340afaf7ce53af9a4f46fda30d1ce7", + "0x124750218008860683b98a36d536bfec847f2aeb4c42ce330dbe2c390fd411d7", + "0xda55dbab350fb6753c5b24c4938fa44e66cab79d5bcc06febedd6f9ad8e13ebf", + "0x768f3b1db427bb46cd8866867b403a7471e376588faf6028e06515fd62d3e2b2", + "0xe471af29ef91ba88b373802d2b6cade479009622d6071487140c89b32fdfa332", + "0xe639036fb23d7ffe2137223d64992ec19ea72e6ec0301b682d044d9caf1ac803", + "0xd8e9efd5c56430d951909485812afa07af1b93b63b81bb87e739c2bae30340ff", + "0xb516d7ca2f040783da4b77b5c66e98951c02798e81f8b018c5bd25f590ca3ac1", + "0x3579e4284b5cbb300af83e5546cbd35e8f8aa7fdc406df6d6d05a24ddb26d0a5", + "0xd0efeb042a5595f68a7a1e7d4610a94c04a8e8e066171d40f025274c4993e16d", + "0x10b1166d15ebe766fcd637191bb4d77c7cc9d55f0f469c22481cfd9c600f5f03", + "0xd0071a4232061fb9182c86eda2387be2d98ad7c32d44f5d67f588761b0ec5745", + "0x05e3a94a0727614749b63126030d671a36cf97367306ef29edfa8c790f31fe9a", + "0xfbb9a0029b1b848d9be0d4b423019ecae9b77d271eab1cd0350379f45d069350", + "0x780955941f0278c69c34c6ba8ee98387c5ab9d7d76a0de577afa32cef8d5b1af", + "0x701bb7bd4b9ae082722029f2cbc9bbd99919adbc8b56b64f8be52c35ba68d2f8", + "0x01e19044f15be114aeca78e241c53b79dd8d15471aedb3637f9aa121e72120d2", + "0x69fd515353166326ab8ced94e8573260c4c48b43e0c7091e6655322e60aab424", + "0x6679eb3590114dfa258bfa5efc69f1c2bc82e6378b7dd12935dd91c8567ed7ef", + "0x9862e730477a812797efc37fbcbf3bd44b33ad268e63376fc80eccbd7f572ccf", + "0x546ef94a53de0fa9dfa099f0cf252badc4181d8fc4990aa7b13eefb583ab0f70", + "0xf5cebf5f3521a1fdef7da43b80fd6b46a4de30f5b3318b9de8f5764f4fb3ecd8", + "0x40eb373ecdff5af485b8a8f32df2a5596862198879191a06e52538435deb2777", + "0x877c242966ecb2b4caeee088396ac8ce38dec85884b825cdd45b5786aa844151", + "0x6ef3a3248c031631eabcc286336c2e4e6881d41e615b86c1c9477aea62b12b3f", + "0x1ca8c7ce54ea632e1068d0e3e1a7a4991020dc238535b90a2f86eabbff15b706", + "0x390456312cf7a1b019746dbbeae46f0ec60692fa3be013d3fd77a4f8b88e6859", + "0x29d137ea6814d20e3c548229c07f90ddcac0ac411365eb834063e48ec243e892", + "0xca0b8f9da34db51d3e58af5d278e1134aa0c7160a508dbf1d34a87f00aae1b3f", + "0x3656386a4cd3d24519949610a8175384d46dba47479f14f27168b51e4b56d16b", + "0x78e09f291c1d9533afe242da4e8127315b0bb54dcef52d33b6cd31ba3d0c341d", + "0xac76e3ec38c606b8d5032b5c085dbb4457e66714fa187b7e1203507bda76c057", + "0x31ff42d8b015c3f519b4e1c5c61dfc2aff29346151f6c9718fe56aa8f4025461", + "0x508515a7dba1d0429b217b78532d854d5859998143bdec6dca7e62fa0fed016f", + "0x8ac47c5a6b52629f42c4214dab73b53b499dcc0b4138f457dd1d9d82947a79b2", + "0xae5e47458501256a6804415f814782d5e4738234e572262550f3c6fbbcfa4190", + "0x7ccd5d12bd6fb87e4e5fb00f7f42666f4ff92b85c71e2d18040925d20a074689", + "0x8576faa823583f25edb639f2422dd9c9a7c9bd453d6c837353e079ae0e8d43c8", + "0x1a961e901fe8eebf69d2245b9abbc7e055af240b7d163c432b9c42a515401d16", + "0x93ea0ec4715e3bd7cb2c5d73e4883453df2ed1f2d91cfb8013c82430ee91f51d", + "0xe2b123d65a2bbe78948bc5df6a01de60d1b4bc1c6dc09464d519a67940ec87bc", + "0xfed042c13624e7768e21613357f92794b1cf32c3b2e28b398da8aa12372a21a9", + "0x5a8111802320f44d62f8de6cddd68f9e6d246a706a783aa5fbb884c60308e085", + "0xf5b3ad3043cfb3995710ac87e41e4bf73637d19555ad54a5e311e15028e2b58e", + "0x32a3e57dd71f6a7a3c7e56160c487fbd9f98e8761ebfdaffdd6fe5c7811e6bdd", + "0xb051eb53dceed37789a374b03f0db0e5bcb800c16c6f670afa9e7a9eda8ba5ba", + "0x88852963820052bc5e954717f7bf45033ea89aa71f75896f3e8b194e273c31c7", + "0x69a8ec996575aaea822be11b109a122ea64080e6422d1fa03c96af262da62053", + "0x4c4c45d63c9bbdd155caf896d3eec46235fdfc95da4fb8a5c02854a35ec0176a", + "0xe2b104dd8c10e71c17e828a3606b4e759c704270b4bc42c6837bfd7ea70fd686", + "0x0a7a94e28d11c7e45f30027b40b50ea2772473f2ca464b73a2a0d745c9403b3c", + "0xccfdb4f30c39fb1c33bb2d0263bbbde461c4c50d4df7fc20a0256f37552e45af", + "0x0f532d41988aeb069a2f9e464d1f8075d8abcd8d6d78b65ad8e671f8e23fb6ea", + "0xb2a3c1820ef3201ca77fefb5e155a81319fa5607479195c2964318d489b5b99c", + "0x5400dd40fcea8a4da336ba2d3e6e856534f223543e41f8aaf1e06f2648bbc516", + "0xbce6cfa9c28d3fb0c8d5ff31c5a47d25893428b4669be09fade32891308a09d1", + "0x1a00bae84ab6d55e7db98e6a4416e1502debba1b8eaf9b274ed9c1a6b7504902", + "0x078abaa51374ae33e2cbc73d903b47cb12fc535bb7a4b689756408763814a59b", + "0x9ffbc711bec19ad5dcc5edb7a63a1354439023198d644326c8ed359870ea93eb", + "0x78029e550e87b4ba75797a2eb992d688bd89c77b210c1fee679ac4b1b96ec345", + "0x9bc56baa3cea124562651f972dcfb736caf7bb9451919fb223691d63af639a86", + "0x032f4246295ee11df937fa1caee42a93a172726ab8914e788eb1644f737f7f1e", + "0x031ebe3e05e090d5145de11da8c13bdb63117fe8e6985970e35b51602849e0e7", + "0x7dbd4bfda309b02f2a5aee6f86b79e34574b2a754aa18bfbca2381e3da577d08", + "0xb27fc58ef2c6b19601d899dde661409f73f51bfc122802c076bfa5ae3fda2676", + "0xf5845f00af7fc786c4114b4e7949eec24e01816ccdd25c43bf95c67a85010743", + "0x9694d0026b71060a44673ed47485bcb557c0dab5f02bbf37a86a51368bb7e4bf", + "0x1724905181e85bf91b18617109e1bbad9d5d73cac51fdf831d6044933c59604e", + "0x251a2c8bf71142b37527889d7ab65e028a21b32a6b9064ea72c6a7c31c762a07", + "0xbc4eb6a9a151fbd95a52b9aaf2b35f9b47bb039a17a925d731cca28967537e1c", + "0xc86eb8dbf613f53720d99013f7c6385eb137cdca2d2a922f770712114ec67df1", + "0x121d66f3b87ca8cd05074592dcd51b5325d659506fb975e9fe5bc4c2a3fcd36b", + "0x782c1f3117b0b4dd4c96af21591730ea85ad86b9b1a5d949df0a654c78912304", + "0x6d6bc3f6c040ef381198dfe3ede2864f59ab0ead77811dd7e461dc570d812927", + "0xd481bc816f788e1ca5ed9d4c41d4adcbcf9a531330fe398c552b7859fd3768f3", + "0x2840150bec174d3cf9203790ddd2a89a170876e08dae51f928643af820bd25dd", + "0x09874935cc80fe720849a171b96febfe5f0c0e0984233ef02888c4e3882c8cf5", + "0x92704f5dc848456ae152cbe1c5f154a7a54beb7a10ab47791d85ef243f4783d9", + "0xcbd6b19b75385418919833eb046efc5a873c2fb5d9298fd0c34f26460e9f2393", + "0xe8d770e9d9a35c3b6ae4e83165d4885f77c634099fd3ef325b03455bea83566e", + "0xa12a1bfde8068db9b16dc7169316f7d70483e34e90a4c1e80c8148c5ab7dac76", + "0x215122620f7b1da34c88527430fb8df16b9da2a0ed01259252dfe06e14089f8c", + "0x07c65b05101996cef384d0fc080d583d6851f0d89f169c4192b649389be1255a", + "0xb0941b58dfd8ddd682f1a9b6174b91f65b60fe36a0aa4bea685e7ed3812502a6", + "0xed2444684e25e89401e197d70867187e04d5d5ba4d2f4bd5f51809ce6d210428", + "0xac2cb9208e58bb7109beb75dc85bb94f32676d1a1211c68bc238ad93382f4d57", + "0xcb4309fdff5620f4493413cc2fb62163f7607d5d4c12b296cc2c5a6f5292cb10", + "0xb5f916296b0c22154b6ad5de313145153780222b735e093c1fff9208f224dc54", + "0xaf80d37ac24f43447692617d51a581a63cb387fbd8feae5104d62612142154ce", + "0xc51dd3d3d6bb0e6d1ecbab0a05215f70541dfdbd0407e7369083d875e7df32af", + "0x8981d2523f139778e641593684e4f19fdea5e245225a030c691b32bf7397b13a", + "0xbe9e9110ff83627e8a2c2f6901574fbf89a1271a9d5e07631f60c5710fc587d6", + "0x3e082ac87d814e009509a394bc7e4fed071255301bb305b0b4433afb68f39e52", + "0x2ee2a06d450e074862de3b5a4058c4d70fce1dd62d4dcc29226ca57c40198085", + "0xa9e255e4017d9b9ab671df0445ff72970ce7d3509a3eccf7fe9ddf4ee4dfe3e6", + "0x83f5d2044311368e5bbd74f714f17956dde882e15de72df53ccc94543cedf124", + "0x381f5506eef40c639a89378016a0bff368a787b88d894f21b1335a6ab952dc68", + "0x9dfe71a5670ef746e642effb29385143efec530a607ed32dc483ced2d416c63a", + "0x909f7aec71d74f8b030282fe5f21340270802dee781c541d5f14ef6ede5c3872", + "0x0709e5f149b7d3b7fd3d6288574283a3e64ec1f1d13faa4d0bd38b0b05e07fcd", + "0x5be18ffc571b4e147465f37e9b2c0e377facbfdeaa7798decf2cafbad18b0102", + "0xe10672dbb0f2f1645406f91560a69e8d983e91e824a3160b58b3d53cc6d5b262", + "0x0ab7b3880fc9eb13c67a06a1bdac16e441bc2428c3ac41bb38e0f63ab37b327d", + "0x98bbf11f7a1d9cd9da33b3e6924956ca385172570b7ed95922cd1d8151c1b1bd", + "0xa9f0537d990c7cdea9b590bd04ad9d97d1a4ea3f490a6451f399b330a0992ea1", + "0xcb8a5a7dd6a9fc99fab466d855defc60ad92de0555525a4206a69f1653cdc9d5", + "0x52b7a8a10f3155ccf343eb206c39aacd37556f2f8cf3fd37bb1e981e62b3899f", + "0xe8161533819357da922e48fbe0ec1e03c3050d622a85267d9a1ca5f3aee9cbd0", + "0x619eb8d9529dd517821a8a5b7bbe3a17ac7e56287142a9b894085b55ae13be31", + "0x8a6f9d8e1cd9504e6922934edc2818dc697a770dad147dc9f395b7fe0386f5a3", + "0x3fc0dd088414e33db7ce61db314b6185c84e77e7005167a0882d81f8f7b36dd4", + "0xede1961bcecbfbfb2e8215983bd520c5ca7e1668a13dc58f182b005cfb3af8e1", + "0xf07802e7b61820d2fb5bf07734c4018d96f24391b127f9d9005210cbf2327f9e", + "0xeb51908dc79d406e3ad530f740dbd2bd410f8255eec6ce00f0b17710f059da49", + "0xc18b575778bae973d61bf2dfc6c2b61f200334d4d9bc6b5f6c1ef6b6faee3962", + "0x36d70acfca09d042339e0f648a3c09790c2caf1e31b1b21ae1dc8f5fa3cd06b9", + "0x35712da313cad734e0aa41f04bccdc1be96f98c7e614c1bcf102462a1c2cef05", + "0x1753431e82bdcc7f9b741ef5ed1c2cfa3bd31b7f0e3d65696490878d50146a8b", + "0x8d1d3306c740b6af9ab47f8ef1ae25d841a19bbe53c6419b8094e2d964b2b475", + "0x3f4417210cef53bfe874745204cab0eb817741d14fe8de006ddb5da7bcceb062", + "0xa8754c378647616a756868ed230d0bdc89dfe5003875d74ece08b7501791d893", + "0x1b379e68965ccd9f8793eb05cdc376c16345f398b839dab9ce0e9d4fe1560007", + "0xe92617a30ca50ed96afd810d5515825b9b98d0052cbbd70dd5586ad1c5ba5fac", + "0xbc9e4d14d1ba8ac81218097f56f3c537f60e179ac03f76f470dedc76cdf55c08", + "0xd85cd8d0e02a414612ce551544e4b1c48eaa25f5f5fa3449c0305d1c3c992289", + "0xd861a40fb6e25bd90dec43eecf41810c130f2ccefb7979740169c8d2dee6c651", + "0x99632b5601101cc477fd0132b99a7abbdeeb2664060819f6cd0a780e53baf4fb", + "0x05d65a185accd289a16b4bab230ad9ff3a43bf1046ff4f88d1d94c689eb63e94", + "0x5d94098a9aaa79f4d35886bc1c19ea9dfed83888fcf923119295b35857fe458f", + "0xc16c8d2fe0df26906be2b40dd71215c0f2902c21312f90ee04f1bafe73887c8f", + "0xa9f6ee65e857203b85ef35c70755d26e7534982c5157b12ee91e8a894fbd4f96", + "0xe7743d71f88c178ceb827b5bd2a289bca2fd958bb4d0cab36a21e352c7880833", + "0x50bf4796ff384cd26e34a4b0f69a99ff0cdc55ed64bc7366f05eed763945e58b", + "0x5e67bdb0a34dce344fa6b6bdd7ec1ad0698bc4737245adbe2e9ecb6998373148", + "0x8d964862d12a0bf28a34b79916abf39d52e484816e7811166c0ea71b44c7bcaa", + "0x4ff2907ac34a8917d0fa68d2ab7ee573426365c45fc9c6e526719fee3598ace8", + "0x38deaa6bd1d6dbd7aecc0d9cdcc083d69196c9b796c16dbf79a9288904a6edfa", + "0x81195aea804705fee6d95ae4c49a73f02c30b1db862af74dce8a961a112db3e9", + "0x7833db594863fdc11492a8d6d247233c4b1b92040ad7f421c876b65aff587444", + "0xc42dbcf2bc8821fd335b030aef00f903cda7875b6965fa6f743b260a7326084c", + "0x0c57c652b9046a1a3c003ca4f0a1c349deb9e6655bff36b2c5446e49d42cbb19", + "0x9bc31d8e835294206229048e7be200024fec82d73d252c5bde4cf14bc443e73d", + "0x1a91774b36ca95410e041d05291ebffb991d6367f2382473af56a86e1f7d063b", + "0xad9c724dcbb85a08c4535b1eac8f9588afe3944d0e1fa13424062676900c5ef1", + "0x70fc8e95f09ba05d3923a21a62f5b8ff45eda521801ebbb5ec7121de3e32a863", + "0xa5e3e5cb8db4cee895e855d3080554524b09126374751c40cfb8090def6a9acb", + "0x3909d462c688b96c40d6ead9df72ca30a6d64f3fdbb84042bd4bd75c9204ad07", + "0xd1888429998773097a63318b181d6ffcb31e16bb5b43689cc4d5ba3a79bd4813", + "0x5a50ed771f35fb5c29b515b2ebe92e0214b0ccdbb14377561a3a4d281fafae0e", + "0x6e4408cb9dd32a76850b37f79e3b640e614b4974a84c2ec0e208a13d6854a001", + "0xf0dff34066a2214fdeac367b3f7b0674ff999120e83f15c3b426b8ca134665b3", + "0x63e4e2bddb1b1c81a82c27ede16bffb80be36c74aa80d91000cb3f2b18dd79a4", + "0x917dc4c7120cc70657008b486cc49fb11a474b5bcc1297f8a9565a376cee5562", + "0x0c2252fe7937ce6f31bccf7db2290acfd97278538eee1cc95484b05b7bf30e9f", + "0x4fad7830f8019306da69425c49bcfd0bb25e3812b518bfa211d03383df3b9627", + "0xa34be7cf40c9c9f3c85657cc4bea4a8c4723b8d8fd86847c94389eee0c347cac", + "0x91740b00f0d611b84963be611063a0ee6d12c89e7748ae4f2f0211ff65b725b1", + "0x5e559187fd7fcd8ac3d5714e704c113f219d619fd6ca58d72032c9dc51c384cb", + "0xd5756f21fe634be2841605c5cd7bbbe082a2fd9831064ff047da1ac0d79c0237", + "0x38abf67b72ac31e114e1bd5fdec01e548cf8686dab3ad180a22eabdaf75d76d2", + "0x457777d0a6a71361095054d9c253087d3a80a231e31dbea67fd6a6244fe9216d", + "0x6fa7d2ec5e81ad94d9788a4c564fd99e8af08e31985cf9aa86c440162259c973", + "0x6b01d1dd833d4fa6a548f2b4e17ffe04b72cd0561f0ba6bfb8ef5adf30546635", + "0xd6ae7528db33c5623a948bbc50cc50677816c3220e70bf1e052625181ce497be", + "0xb088f3a1c34fafc20b2093cf47d8e98f2edf347ff1f45133540dbea183cd4028", + "0xe11560f4f8913b16f83b6b0eb7206a5e5b9a971e0cab361d38c1dd01de2669f2", + "0x0c73e6fd870da29e01abdadeb19e1e154990e48df5bf7c055752f189c726dfca", + "0x99c98c67b2f3c695ae9ac3ca37995303b301fc619cdedbe7c6c9c2f9646bb735", + "0x06f334781e74e7614eb6e443e5ff63333528e73199522c32770a2183cca14414", + "0x0651f86ce18ec84f34e06be4914ae04c0ef8f8e0f0ff53d1f670271306cbe8ae", + "0xd75049a53d356117f669dbe0f94b5153a4a6ab50765a1667d8f6773cc92a053f", + "0x063b00439cb37dde93836ea4fbbdc2c977c1b9e092a384c51bec90be220393c8", + "0x3c76eacb5eb967a65ec47acf62b03424b3aa32a152c989b00bb8f4de4afcba37", + "0xf6bcdb31970f742b0ebdb7d1083ea42827ca11dcf8bab8c458d68988d0f48d65", + "0xdfd869c19e3f2e9f5aef957c3dce193c8ab516110e2760f61800a9b35da362ab", + "0x5631641e08a0b75fc12e700e635be542d163c98efd0c3f1bb03fbffec6858dd5", + "0xcc59262d1a872f6fd17f19f5fc8b33c7a676a18cefbc2418892c912f134597cb", + "0x0d0f888fb8d03b85efae7ac5dbba6eaf4dee01c3cb639be25b36b7b67476b694", + "0xa8b338fca35a0b2879cca331a31869178a297406b01473ece8f3ecbfd442dfb0", + "0x694cebb985336a1296962ada39d5eb7165ed008f1dc7a9e1b74a33ba49349cc2", + "0x3b72adc7e2d6aaeea49fc7f4c205f80a3cfc2fc4d442c8b18519ee33d759fc7f", + "0x70a8483a75f074fd7023f9d181bdcbaf85777210bc69991db9e21afb5e729efb", + "0xda072d6b870cba3db764c186660f312b7b3f6972b60a72e102a5de8d7e354696", + "0x1ff6e17c949be09cdca2a28efd7fd765cd9b5231c73e3f9b322391f8c4cc27b6", + "0xaf4f862d4504e6ef37fa39b090b224f409b69bf70ed8227d0cb922608b5ba553", + "0x27192fd2a98bc6d7938e7e1ba83d01fe03148d3b740c7a02b3c27cf92c88dcd0", + "0xd2d11f4b8bac427bdc0cb3ec84c1d6c0c5c72c6a5db852c6dcec5beb31aa4ce3", + "0xce129950eb40c6bd295fd2501116ab270ce94bef1c8a687531f19313e0c9f73a", + "0x3d1f88352a96b789651f57bd12de1561768983ebea12d4106dffd3809815eeb6", + "0xf760d5cc6af44ff80c54181eca16b5c3d2426bb05a1be7877e37d5d46afa0c9a", + "0xdec75c20c3d27665e9dafd8040ebceeee2a89af4bf0851c6cd1a42970597daaa", + "0x1a871c6807d47d0ccb4b54aa528aa28b12510a6c85823a5a4f94f9193e44131e", + "0x71536b0fa0ff1b2e0e161ad33f2115e1cdecda0c669c95db4ae287f61e04e3e4", + "0xd728b51e0b01167024197218ca8403d6ddbe9c83390cb24fe1540c73076ace05", + "0x6632b08ed04f9f578ac70a82fbb2027fb0828ef11329af35385b806e065b5f43", + "0x7ab2a561a49fa7b84dc4b39ae40ca1effa2b00c5d0738ac939aca93ebb61c2f9", + "0x07836ef22acbaef9ab361bf1cf24277450c3f05f66ff4d48e0c84aab30df4fe7", + "0xd14267ad2122e48f64a5e11aa3f4fdfef5ad1af1f9aebab1ce10c3994c9d21ed", + "0x10c29d84745ff4aca8f0ea8c86295b15a66d5de1794eec852a2ce8fa781c0c0f", + "0x8356a0147e00fcc716b1b80bf8a44339bf3b3b11234157ef7620fc9032a67524", + "0xe8e45fee4d42fdb7bbbab75838884b2767c05cea8415db6d073d885319b8eb67", + "0xd10bc863a8759ed3cda6246632e6ee1d2ef0e723412b73546bbf51b97e16aba4", + "0x37a6fa5f72c3957c542fd6a5f914a6037de71e60196abcda6fbaf9e4d23d725e", + "0xe746aea253a0f19e3992858f785c3d73388e2c71d777a35f984a67613271b19f", + "0x1268bfc221f37edfa94b25b413bbf27a8cde667aaa07aefc065176e67718613b", + "0x72079d971d172e0f79413c73a6e5bc423f56eb3ce173119506639247a1848318", + "0x1e5316b350130059ea14bb65ef7e948373e0cfc901eca0cbaf32128d7edc82f5", + "0xa968769254c1efe80eb7ce5916e216d69ac47be8ce90474989345a759deb608b", + "0x64a4892ff4a9e97f0aa58b25e551144df8e97857a00bc694104ba32ff1c7ed1f", + "0xd74e345757ffdaea584c54cfe53620d4f8076482322fdae5513578129cddf077", + "0x5d581e4e70ad7d1e59e0b12bba5bba495784c271d4e7a8d3e1f88379c093f999", + "0xac9af2824f74df59032cb9e2ab6793c43b4307504b138d396211ac0e2561d70d", + "0xac7497a2333a7f495cf8d867a2642f30b17d247b4e14e49c43e447f5b4b11aae", + "0x60def1121ceaccb5686560b72d04f7525e4159d0cb27a481306fc6a6f499e971", + "0x202cf701089c9d76e69404ae863a55e126a21459e313aefe266bd53cc50d325e", + "0x84224502606a66b1391c2f3c7a046798318cc7a5f3e059d79d960f7647002ca1", + "0x9362c5c6fe4de81b74f0d304841da4707f38193f4af9fb73e194f92efd696da4", + "0xc84774de770928df2d4cd68661c3d90848bddb9e4253b2f6ecf5279c230a138e", + "0x49a171cab688991aa7d3bd9b0c4d0f962e1bcb8471b18965c44c83450996b90e", + "0xffb8e2a03f702606668e0a711e1eb8018900858b555cb8da65043b14d3566029", + "0x27501f6fda9bb76b7e3cabeedc69ed1283ea49a05808e17699cdc90d1b169236", + "0xcbdcb87d83d68e9099f1f871507f574651bd7164d6584ec9a23199ae5eeb513d", + "0xd3201395950ad218eae7e89102e3664fd4c1b096e764dd32ee084693df1087e7", + "0x527b4f135a418f91e709c70c7631530e8d0711e6c9c17fbdfd6c674520713ec9", + "0x0bec6bf1d08d4aa57adacaec5307160f313624a3913c7fc836001e9e9e5cc3ed", + "0x2c812caf48228c80187951d2970788a099b4a89f01c44c1f68e5f67c57c29e43", + "0x5affa3424e6cc6ef6ff95bfdb38d6f438f341f7eda09c072707d0bc45fc71655", + "0xf1fa4a26dc62bdff342fd8a7850baa8d2ed5f30dc65f7e843635006947964244", + "0x7238b3f61f882a5146d17ed42c7e403b6f4a2a7bd0fc8867e9b8bf49efa30a2a", + "0xa673fef686b4c3e7974ab8a8a5d58aa9b67cef0c2389ba6cf5d6f605fa441ee3", + "0x1b2cb2e97e797ac7be0347715500d9c28483e209eba7e673e0c81603ca24d2f1", + "0xf35aeabe0abd88135c409d1231d6576be2af0fc6c795a553c557acb40202ff80", + "0xb13a1f9f947a39f1b1a9bdbddfda0b6c6faecd8dedfaadbf29a0bc38eee8cfce", + "0xd536f53734681d9681a92ae861bd0f6c43d1fb65e0038ec99c38747af80bac52", + "0x7bb199258a254d1ef897c9157682a5c9e4f957a3243617a9c94c81d5acd804a4", + "0xddd3e6d97e7abc41a02a68b2d8b2f1ade77bd63a7e515dc12b5ede19a4ff1c69", + "0xcbbedd68bd3d24ba5ec4349e6e93cc84ce4427d0aa9c8ad38bdf017d9fdce1d6", + "0x325f2c3847674cb1a22154a1e7fdff81b6632fd8b46792eeef5375782057e98e", + "0x4248a1ecb542a28903b4bb9067e30c682b79065a9aa372d5937c679fecb34979", + "0xf908ca3ac0d6011322fc04aa1a58473a6ebaa4dd98a7f44a7b3cc726c4fc5d46", + "0x4141188df7d13ba44ab6729d8641250a9ddc366ada78fc18b4556956734cd370", + "0xd20ee6e387f70396b5a30e99032b1c4615dd47f1a18e9bf747479b852b623398", + "0x4715c37267d36a342b30224fa237859d731eebcc9d86b2299a60936f1f8cbb0b", + "0x7ea6603b96cd333199eb930b53122dabba22a9940b5d27ad4d8b74a1d727e1bd", + "0xec9788791acd42bf9581f89e3729890bb08bf0d42c175dc00bc21d99db36abfb", + "0xdbf7cc27e3babbe35d3c22178b78d23472833e30c0768250233e57d88872e8e6", + "0x11c199a4603f64610eb028f79e638235d02dbbefd168f62cf9f540d82c5b83d9", + "0x77f522d3433d80e3b1d961080178aeed90ccb6021a9466f5c755abc18d1dae49", + "0xb12e329cc99b4079b4c9530d623e29aac5a142ff75b9c6943e340d62aa393477", + "0xa079816bdd16a3e718b212d0cc09c02106e6093fc280a481f48ae96d54320a0a", + "0xf9b17c8bbb1eff109f0570dc99222e73316fee0359912f1b94376ce6971e8de7", + "0x33eb5f45798ff45489b7bd1513dfe53c8a412d6b10cfadc51be3e9ff8d3bd4c6", + "0x12e85780e78e76f07e25bf975a97b7c9f7d10c46055f3828c38db629a19f8f51", + "0xc618d077729543b2e7b53613427ecb65560d1f5605f0e81ad6d27b2d45c29d74", + "0x6a522cbd9d5dac3c274863d003d57d017162af7f6df124e8c17b60faf0ebc03a", + "0x2578e7e249936f10acbbfa299ac1dc1f2fd8f634cf3cd4d9dfb829f617ac7238", + "0x4389ed4b41f18041122ac38f2aa6a32412c7cf872f708fa669e5cb8deb7c250c", + "0xf1f1e342391491acc9eb2f621df18b90e183e76ce05196988bd9515d6284d16e", + "0xd6cc0dceeb49ee70e7190b47d223f8ea4e124b90a1073f5b8a72ced7d9ce8a28", + "0xf00bd2c2075041dcf42fc891b57aa6ae8a9f160defbf0d1fff42a335ea648673", + "0xf0038541c8ece44ca25e8aaf287c097f7bde0ecbea5568feb55b4789219a5ffe", + "0x3d9641605359ba30a5263b32886b35fe8ecfadf80c684756c86f769139d2dc9e", + "0x28d1583404d003a80830a4afb66f1669938c2951e51c9c31d50d19351f5d86a1", + "0x4f60340f839b7ce1666df709be8b35aaa21f9fa739f6789fa1fb5cf32adfd502", + "0x3881149612f6e647b7de2e0d8f015771a1998252b9e0bf461b89761f7357c906", + "0x669094eea8ec586c438c5c64bffaeaabc25b97f3ccf5c7b44ba51bf0256e7d5c", + "0x3e55210c9f389dfcf0098106452e4a706c71b92862bbc704f7a69adc52e0a4e3", + "0xf6608b778087501997fe69588712088ea0e180583450f40d6d7c2be9d34e9334", + "0x64ab9c748dbf65074ceb2231751425d08b4e96c7fb31f7cd85ae0aeb663df62d", + "0x842e4343206fb20c5d7e38926e7f798c8a71f0699f253b3bbb3d704c7e3f8d0e", + "0xfeacb04bc2e523e370fe229e94b1810dc2d3843c237cd30a952bf7918b0919a5", + "0xae28af049dfca1978bf79c1e3f5ff87caa205a9fb4007ac329e43208168da33f", + "0xa1b498544292d8becb63c051953f4a67825bdffcc1e125deeac7cf1b24a004af", + "0x5516fc9eae194f93855955ef55a741aaf58564eaa2f5142c66d69420f3221f68", + "0x4e6d952fe1bd90a07914a391445bcfba3f9db022d97adb9bcd2aa7aa725d18f6", + "0xb7a97088fe6f8eddad9f6a2f47c700a55a87833d2c73f3364fc08982ff58dec4", + "0x61530ac67f2103cc3e88ed5c424a14e57c75f6980ea973f9a3aa087a8313613c", + "0x3c3508187b7685d86ad2b286c282f99eeeca90fcf286101054cc28d4baa1d22e", + "0x2f73299813de8641cb73f7e5cc184290646b0be7eebb5c7c3957a0fc5342d5dd", + "0xc9351f12a9d8f2fd07ab7a6334f57c031a6a8fab5fe326f3424a8d8993e4ffb3", + "0xf29b9476003225a8873c94e097a91fcaf8410838f4f8e5d58743bb7b3e5d479a", + "0x0b5628614bcddea8d83f6cfc25b9cc801231e341c9f50ebfa4725a537cb46065", + "0xf3a985352364c06fb32bf7e026a943c78021fdbc4e172585c17554380078e2a9", + "0x0d20782ca7bc5036bb4e641d99840e536180b0def7e30534c97ea65bb7eda556", + "0xbf2ab4abddccc869048c0fe8c06321008414dbed74c2c627de8f6142cb73b470", + "0x1cadca29c2485177d728f170b55a17663b13b00de48e93deba7faee35483f738", + "0x255428024bacf855655149047e4f4d3f56357859c640859b4209935eb922df05", + "0x0824fb731453026b24065449cb9644e38e979c7db7eed6bd97f319b170074a5e", + "0x637af3d9199b13bac18e9b03cf28fe8a7fad10e1a999d0994cabb5612033a051", + "0xe21bd45659cde11db79a4688cc237bd1b54aab4a04a8625e367e786dc5fe53e0", + "0x64074927b3b14c8c90669513aab73eecbb0ef7ecb14b718f7c3e31acfef34c0a", + "0x26b9fad207580c8715ca306cd0b77f6f12367828913d48e7bf177bbe439e4ded", + "0x416d4ddf8cd529e7afcfbaa9a4e81ac12a379a54ea5fc81ac4c1e0e2016c7ff1", + "0xba02e3b139aa93f2546d0a8587d68f61721a11a3a86d93c66fc64ca7b9a89293", + "0x859861a331c0609f3b8608d29479429fbda6435175a799a27f7464fb9b742083", + "0x2f17a744c21aa589afbc7c63acb81bf1a1116dc9701b8e479df322a1962a9caa", + "0xdb6fc24698d270d818c46059beddb38ce408609cd49e9bff5d053daeb0c668fb", + "0x0078923977a4893286a75dc823dce473fe6effbd7e54b8cfa5bca6620cee1daa", + "0xa04d6bfa3e36c1e60b6975fad5c0c0a8d40219c6d877acc917044aea61782b9c", + "0xe3ad0b2453694bc89769652c330b1f991723f4ea1e44dfd23acf27b0aceea76e", + "0xaba30eab372ec1d5da30d4a436468cdba7e1d650f2395360ff85fbf29ad680e9", + "0xf68c371145f7d6e578a8930bed4113899e73e5c0f82c76ddc50686eab8f9eacf", + "0x8b4f8223d398a7ffba59f753e4ecae92b694141983d6bc0cf785182876447540", + "0x853d04d16906aeb3519dc0577491ac469fafeb55d269c3d06bc8c8d42504f9d1", + "0xe3ab6076bdfb7f6acc1e23bd3b09bf5045e0d189d130ea9bcb9c140d2c8f23e1", + "0x2406a4147f8405e0d26b607dae632bbe18453809fdcd3c8cac6a6e2724f5c1d9", + "0x5f96cdc8e157d1c38ff1add60a3fc7a1c154a7f3183e47ed989f2b528c94cc54", + "0x95e129dd4880db9a5fc4661d982a14f7f14d76c3ac681e141e00334fbcef8744", + "0x80416aeaff3f5a570e176e01c39c7f7f9708da93abdc0c61cd3c0162fc31413b", + "0x1535417bdc941bb4c5c3b185d067931bf8ce525f94ad201072181c53d0fa5f69", + "0x58b3a9ce060b8a07793cb40f6cb1b1561602dcf84d6a4300eaa42930a0c4e5bc", + "0x6b8e5f9c35a71507ef2251db9e43bf983114e280f8ea463ca43e9617e6ee1ddf", + "0x910201775f61fce62f395aef7495b1b27ce3e746b2b5f7a9136f0b450ff3dc9b", + "0x2441d412e45bac758e7fce7fd6aa32586228d0dcf9c1eeb26966d47cc4293d62", + "0x0716ffc27d0d1494a1e42ce1f2bed3ff6d3a39282060cc0aad838c1bebde5201", + "0x42e823bca0fec2051d8e950f5f6ebd87cdd4eb0207f7932b93dca4754bf2bcd2", + "0xecf23f7a95f6cddc6976e4c93486ff1b928a508f6da7e199b19278812b0d544d", + "0xff078fbc1e137dab548b8c3a8a96858dabc2e9cd56bafe99657f7f51b032aeee", + "0xea866695abdc3f8761283ded6f0624cf5c5ea4f8c91181523ecbf3a9752c6244", + "0x488825f4d5a9239b5dc30b2ef4eb45748d8286d07de699d3852eb25bfd65e8c5", + "0x967dce4e18cd42405b537550a62360110dffba197ed864703ff64b6c9466a22c", + "0x078c5124a0a054badaed2001a6c7f5ed2200982a5a8492de937f69eade3d0761", + "0x45c5f50fa884f951bcd4292e83807718b214f76994898be1674c7752fa6126c7", + "0x25984f153fc0b1c5aee9b4ac2f7cafb05de534359e424711d1ee1f319d44b2f6", + "0x0ce5b49d22acf733d43b2c1a8b1a0602c74f7d1e38e8b3ac0696377d0ae45c0d", + "0xff7832f02dccb171a463d93de258debc2bba8e675f8d2b92169dc3d2b1130274", + "0x6baf8d682f84499dc94e2c3ed35c977341e640fe854e182d7bbc96a4df43d131", + "0x140637be50bc38a6d82d1cc15ca5fbc518da2c6d1e509b514313d9fabd3e7aaa", + "0x7e292310c089b66ef68f687d6cd9ed3c3f080b199999ee4b7b9d82c55ddd97f0", + "0x655d69f493a2176a7933f09148b504072d1ddf8047772b8d389466d9c66e080a", + "0x0249e131a8a3d85a2d491defccd9b2de6292a6a060c025a457f785fc481acc7b", + "0x544214fead5b526d0ab836b749fc24654dad39ba22fafed6c1dce4336b7fdae9", + "0x60868584221a54cd9699fa769bc8aab54698903aa8fbd4b8ba7f0c2ea33f6aaf", + "0xcc4344dc4c13997b809cd56a913954689599956a9a9f9b5037dc4c143cc0330e", + "0xd28b36a43b855b59cc3e5100099306fcd4cdf5d7ddf2fb44032c1ec7c5a9072a", + "0x4dcc978638f5e0cf73a3a584c4b0747aaeb611f7630e5b24eb17091d877d39b4", + "0xc42e55e81df2e1a580661d7e5b735c7e30d6a20511cbb20c62d987345ca8a356", + "0xd47827776074e70c28cdbc4a7f2ed1af81318b180b7c23ce02d472de50e5c465", + "0x3cbf740e6026f76d770892e79ea7957a84ebf262feb816123e51917715460e8c", + "0x2767983d4338871a6698b7a480d7d454729ef5b6b052f5ea7a9282716bde2bac", + "0xd31296369cb111cd3fab4d797c54cce2404116c08ec843d2fd8fd7896254c6ed", + "0x592b21feeb23f9365a0ed0342bfcc2f254c7908bed8a8b89e96cf7ba84fae810", + "0x0ce7f0f6ce2e04605d5cc8637f621020c2279427ea1d376f7606ad8c51045070", + "0xddd00acf513bf28af2ff734f8295884cba9118e94e5efd6ef111b2a1fe03a26f", + "0x54e09170145e775e51c88141e3f11b72c20f465cbf555bad0faa0257700b0ce5", + "0x587524eb45a116592842bd51c900ea26af904d3058787e510f46e592cf6a4b0a", + "0x405c110017319524f69a92410d1b0b04c0fa4dd02a4c1d36a23d753583fe7543", + "0x5108967dc2b7238df785f91f51b5357a6912ec88021979b818008b04477d435e", + "0x2d95145d58ad02bd2048eef7d7b932f6be3a54b2081e4a2c2fb13b138f31e83f", + "0xb24ada713e58186f0dfbf08361a39bb5760bb022641c5192aed554e970008451", + "0x97643d9ba2c81cbd6bb51a5b741448839a210fb65220b602ad1b6e052efd63ee", + "0x2f1cebbaccfc9336996538e8622101e92e3366580cbbbbcdf7352002490f7c3c", + "0xcf6c940669e592203c171dcd168ab39485ad15dd18b8a5b506ac3ba07585d769", + "0x407a31765ea9e641842d4c64c31e0bdd449d6c6a9ca1aae236508a6fab0f9700", + "0xddc6e343be5a0694415c60a670235680b711323bdd57db3ecfbcb212d9d8eee4", + "0x32223980873ff5e353f2b74427fb4ee3da623239fe82283a9e1fd27a3b4dc259", + "0xf1142c655680228b00cca6c79c4f5f2e241582bc85863367d2ae5cbbccd5f257", + "0x1f448edd5c1cf273885f8bdd3edd641e64913c7b8de6b8f49b6ca5bd60eef326", + "0xae77786b0edddcf16dc708ad72290923065570b19ac37713e29f7b1849c54774", + "0xdc48b633ee2b1cbfa35170f0dc1696d16cf21931a4181d2eda06ac450c6b2ffe", + "0x0a968db3e7cdf0fd4b68d9f3a010c72504f986d9333ca5344f35324b1d890bf8", + "0x3250a6f33f6926e0740343944f2e3e939ba1f5e3ba130c5ade21d6b561aa84ac", + "0x958f03089b1153c8a400577c4971f70f1a296635779c2b43cd8fe56da6052255", + "0x0ab530e036d329ec0c13bfe8a02e4275204c975d308b4f711e7cef20f60a199d", + "0x0314ae57962652493253089cab8edfa9f4edc1990ee5fadf92dab9c3389ae2ca", + "0xc3b6aa37f2f049fc617d9a72333638c19e3c0e1febfdffd5baaf4efd978ca81b", + "0x763535d10ef16de47f5f5bee9fad8b7eaf80bf269ea0f95352ebf1e9fd6bddd4", + "0x62158aabfb44fd057f5cd82028ca4d385b4450adb5040691257e5ce7b5a522da", + "0x1d434d41d0fba5c13574a8aee1c52451a481cd61079ee150b34669738ab82377", + "0xc6bf20b0074f8b4c94d1499754533617e36e0145a160b2f7c047a98a274f7b15", + "0x64aee3f6fb089bfa2ae328fbcd0ed7e816f53441ffcc92995e905a435093ca4e", + "0x35638dea124795863a907b27f4a4a8acf7fd56d5bd21f2d4694344ac7dc98741", + "0xb49e290f7189b35b269c10743c006d7830e3e92c9aa2bb687014c05bb9953ec6", + "0xf519394cef50eca4d133ba056df7504f5ab52c24f4af0d3341546700a10ab294", + "0x2aac52a8af45d094f51cd635a2588748e07ef53c7f53ac24d7188c4dfa68673e", + "0x976a2d52dfae19fca1c21283bd4de1d1d62ffd10cd806aa829a3b46ed96508c8", + "0x965dfe25ff0feb8f1e034ca90926fbe6e33009aba217ca3dee67775c491ccabd", + "0xd179d215bed426bfabef8cf18d72d99ef4bc738d24606c952c36cb20c1beb302", + "0x4f7c4e04ee8865bab7efc7f0350179748d01bdd4558fc2f2c7e0722f50c6802a", + "0x95f435137d837becd299ea6ace3fdf85edee25e02e03453e43de0653c18efb4c", + "0x82f1aec530d22d94338bd01784232e317592bd531c88949a15a4d4616511f1d7", + "0xdb1a7e54961389f4bc61c61ac2cb2b209d480282c9b47ed4312c4f27997a4ca2", + "0x5f6aefa3a1bcc7d23723396883e8ea3840f801f5135eafdcfe38fd6ee97b00c7", + "0x7fddc37ea92f7a21719a914f41784e67afee063f74f7b7200e1f56bdc61e865d", + "0x34c3ce0adb94d27a9f1f7961be2fe1f0f5033d05caa8e29e3ce1fee006ba6205", + "0xa760f3eab6d5c9f193127cc4b4a7db3869c9702cf4a2efb1658a84ced0cd14c7", + "0x7af15fa3bb56bb4c5079abe84e72a75db38ffdfc3b43ea85ed143be59a322739", + "0x224da6eb0d0d23c1694da1834e607fdb38d11cc9591b73adc773013482931325", + "0xfd91493c00abf686ab506c2283804b1fc1291d94b67543a4541da9cbae73fe9d", + "0xbf62bf11ed4088f4b32541321fdf50006b7e0ff87799093dd0787a88b0273c86", + "0xb2887a53a475176f2afaf595e5314f35eba6de8932b380bf4a903c13eafb1d57", + "0xdb130e3a5fd980883ffe7058c687c2ebfb0b6e74714d6704fb5deb1968e730b1", + "0x22eda08f56ce4cd58495791b40a7b30b2eaa48b0a152a232d5d4d16458b4793c", + "0x980487d75ba597b10c85a8f040f11f4d51299b02050d01c6f5ce8ba7a047a72a", + "0xb45e15e5cf4ec41c556563b7e3f76d38968d1d96cd2563e0f0fdece3b9e4b4f8", + "0x8de4193fe785f1f6d02a264356eba50bc9f3ec1420313429e0b4b23c1f0b5ea6", + "0xc10d66d48fbf9f636637df897ffd773f3f6236394a1deae4d76349bbd1e220b7", + "0x6317aa73d40c01fe026cdbe6ed06182370f86b4a077983fc62783113cd0da30b", + "0x14c1c7d026a5af911192a15b6b5ed1215ada34bdda952ccef9a5d2be18d42c04", + "0x27d6b2990ec011db440e0d1144342501fc2943c49782cee6bef19e97226c5448", + "0x1d487d1b495d0f3a389e5e0e733ac07b9a9871614891c1cc19462b6cdeda5add", + "0x2cc9fcac2aeee390ed0e8f8c616e34af3475d8a4fe9bdcfed22a7f8f9de0257a", + "0xb09b67bcd45321e43eb5005cd903f4447c052213e85b39e48be17ea6afef9bdf", + "0xe8efc998dd46837daf1bc77f0efce387d199d99238195a3602589130fbd4fc75", + "0x42061e6860edf3bece1284136e8a46c16c64c9883ee0c57ff61d3477c2857e33", + "0x911948d704f5285e3b5bf0f9c8ebcf85a7502ed54225e6248b8e6e221301cc91", + "0xf449829f4662c049bac0dac3bb2654e06bcebdb86ff219b81808195f24695f7f", + "0xf06f30cd5c003ceb48135fd95b0e9fb3590049bc4b741edde3fcfb4c111b6b4e", + "0x4faca62e3d8173f474d4183607e26bd5cd721d544bf0f680c768637a6827d2b9", + "0xbc88004ed1dc5af42e837e6924933d8c285c9108f39173ffc49116c557c6fdb5", + "0xe47d46b46ce5ef4efa64546bc8a36575065caac0d913b6a8ceef47dd6b418051", + "0x26f33a3255339675e052cb2fe338648fd931421c7b148c6ac3f3b638ea734de4", + "0xa6cbc23d052e3624aa32fa9854c5b4dbdbd0a5d6ff2414cf85faacc1c7d985eb", + "0x15d2d2c1f062d4b628558fbe4baabc9e9d8c44e0496e180f3a8ed1b1e943cee1", + "0xc7dcecc82fa57b26328295249479dc6f1e8e1339d28d3956ee430ad258c46370", + "0x2fcef6a79bad7065105c49c913d71d49e871ef4a84377fa4ca8647aa6db0093a", + "0x3f2108a29d01c472c45b156fbe1fd93eaa39aa8c412d5ec5c10f4c7e90dd780b", + "0x93ff4da222710e690328115f65f3ee64636060b13eccbe396b6dd4dee534effb", + "0xaed603d35304874c50c3f98171141521bc6696fe850c9f3918f7f1417c21d947", + "0x7c64ad33fe8f6c1ec528e9fd79ab6a0661b27e8638970383e5fc7aff4d75bb78", + "0xdcdb018faaa494e28f8e9a9c98daceae15d882c62be29f39e59747a94f39331f", + "0x07c6f2674dd748ddb1a26ab30be01b1e923ed1d166ae806da986952b32cd18c7", + "0x089b421f2972a14d56343fb6c40c0b20f31d1bb3d335ccbc29435480bfd55572", + "0xa31576df32e44d133cb7dec7a00e7f2dd8993c2f0ddbd16e4c01844fe27fe717", + "0xda56c0535738a818fb7d80901b99502a9a7e46b7b2d8d1e65bd51c3a552fc246", + "0xa035c7d75895a97bbf0f37392ebb8fa3fdc2cf5a547aa29fb5e4b01fe7532074", + "0xcbe331313e041ddb6373a9842281e97923613f88951e75f77d6d6df111546fc9", + "0x78a596dd1999e9b94240b034e3e02e14d3a2a5dacf00cc0041c124271b78da18", + "0x34b5771ffa02ac6f42b009f4d2b0bf068f1897945d9db37ba86412ead45bcad8", + "0xb4461cd48b6221d45148b1294a7a5617709f1eec50794aa9f7f5a3d296ccfa3a", + "0x7eeea91f0eb54f459a30275f9622fa08297e772dab2c774fc7a01505c6258079", + "0x4e18c9d391f5b4cfb0534b7c30aadd68e92ac228bc72880e554fe21e9101b2b2", + "0xa9f222305702704cc4a4f9f1141bef34d72cadbfd61ae154e58422fc409320d3", + "0x11e9fe0e174145a763be58f17eb1f0ecf53e68a69df2daea4315b0d375fb0e31", + "0xc055f4555b1f0256cd08b23a3d1bd7fa300c5612a78eb06f5fba2da78bea7003", + "0xd65f7596aa4416da008490d1f9ab4e26f5100b081da9c0ea1d352941df6d3cbd", + "0xdb63077f4f2c8321782c04ef4f0e223c70d119c798c384c7b279031bdcbe92e7", + "0xa70c8e2c103d1526b1944cd663045269b733d7b960bf511d2a70a20edd64d352", + "0x773167a5dc9c38686e0e75723d33aeb6f066ea27bdfa119faed1902267948b0b", + "0x61497632629ed6f176e234b869131f7fa6ef7e57320753c01abba247b8f4fda1", + "0x2ed251f537a18eaf791b8ce8f6c7bfe642eacfa24cf4ec62ce622f8377440af0", + "0x146efe2bbbb9a79e088fa67036b04f7b248222251f5dcf9ba3101a45cedf4436", + "0x706e893371bec66933723976935891c1d39795d33c7b15186a5e2effcaf936e9", + "0x1ff06a503cbc6ea1ca1395506f46fbf05114abf394adf4f2d9b2bce3ae87372d", + "0xa30d5b70a341ad82d45e4d8e1ed6b176f3258f0636c6b2178cd463999e4e691d", + "0x1d13d9c03e42fc2422b93a4276e9c6c9867e588c5a2a26557c665f1f1b5c27c5", + "0x56a85cdacc2e50ac14ff2e734d28e1875c2fcaff93479638a4d78a31076128db", + "0x9f106c9e1baf1b3d190039bed510d3400d19261aa6b4962ea314367d76016d2e", + "0x1ebf3e19a150c3fdc97ec18d180d7e9c9274bfaa109945cba629d63a46f7cff9", + "0xa4dce361d09ec2fae4632785729bfe2d592213f7c499e30fcc2167d0b98c6c65", + "0xbb904722d58e6c03551d0e4b812a2e5589db9f6041c99f3a3c3e87bbad1617a5", + "0x223daf2bb706feb35998eab8ba7ba447192374ff54e95ce8b6c51cf77ab5b832", + "0x873bca605c47dd4309a65673c12671aa423208ad843bf0fc66d5b9e10c8b578d", + "0x0ccc5b50f2de2f56931143ac23664df4d9dcf8706da7b0229b4ce94be9b3ad5d", + "0x6a36a535cbf600cd0959a0f2407159a296a3ac831c1a47fbd5aa4dd91013c7dc", + "0x7fd58adb0ef546e5887c4bfa810e3e1f8702af95494784ddfb133f4f2cba5e44", + "0x4b03b38e974683ca15f7d79afdeaa0c3f8989685f133750b6f387b13a1de92ab", + "0x962491e27f3eab2ec25f68c05693ab9f3ae9a74493c1eb2a7d12930a49cfa671", + "0x362389b654b68aa17a6879fa3687125d7ac69566b0a1de7cce64fa107532d621", + "0xff889833c02350da951d235b757726b0ebcb8cc7115167cc722f505425bcf953", + "0x54be5b9ba8cb821e2899cdf2eeddb62c87a5a6237471f2a1cb2b0eed23b75157", + "0x99f0f5fc3bda4bfbc4a42c4c307e943c8989675955fa4753c30dc56c8e3a7417", + "0xe31631df964f7687afc153f70c064ee59fec5ae481131ac8f20ea3fe2f100c0a", + "0xd16e56a44d721fbda6d3f060ec87c044cf8b97614e44896da9e2bf4fbe21afcb", + "0x6880a06252679da54a31381954af8ea131537e28df0092bc2e23a85327579d11", + "0x07e28a0da0c043b32bc2cf59a53f461464c6d9b98d6aa6e2a942d55240be394a", + "0x81135d506092e89fe07ef9035f20cc80c79da46eb2575abc3eb1467b8d6b54a9", + "0x2628d3da8a41496662fcf347c51a1b6f908cdc058ac26749cbf049f81b6bdf9d", + "0xba49ed64c1f6e8e5da56193c8c9e3fcc4f07cbcf0e5954ec0e24dec05f6567bf", + "0xc5c2997134949d2b62988964f8cafb50f4b7de14327b5b648b97e0888455e3ce", + "0x338059e2e82462bf83d5368dee76829c190334c3995408080bb5148c6f7590ec", + "0x07d62c732e33a3d804eec6e6972b545623ae09f492aea6e5ec85b1a161c47a5a", + "0xc5ed78005e22aa5c2965a5da6030d6358b32b77512e48e941d3f4c2b64df7baa", + "0x29f1435b8264bb67c268b2bc24d49d3eba4a25fb372a4f28f290f8962327b19e", + "0x7c3bc36b1b4a74dbb71e7e8f2f0c583e7d465f7e4d5e8f5dd1ff23ff8d4b5696", + "0x0cf6d9e1b74c582bec773553a799e22b45d54a8f42ca1e017658715ed2ff445c", + "0x49638266fb7ed13c385b4677fa1af132dcceaf07e0ad21213c3dd2c008d72dce", + "0xa5222f00d5658ba7edd137444c93c3f603e0c319b2a0698f0f6c0ef26dbf61ef", + "0xafad1e3992f7913981ed735f86a941c99a8cecf0aeb4b5bde5db2cba820ea0f9", + "0x93e06a9140d8ca04f9db8b87c69412c548c85d74679061869f86c56f048a0cbd", + "0x5908af06ce14e92ff3bcee4e9cfe9c29f1fa184060fd4d9ec81ba742bcb9d6e0", + "0x12260dfcc49a8930ee3abcb13e21d161ee6170f14aff1d9c1e2570ab40a4169f", + "0xe6ab0a0ac940080e95fd2186264a6f14c5b91eb4285613cae5e4c88cca88750e", + "0xcc7b9e6975fa4cb48fc380fbf944c1ee3630c5baf4555377d3d6b0186d496d07", + "0x432a67e21bef773512bebba4ea3293723dc95c948628dfe1f1622525e94ae680", + "0xf4a2010ed1487fcaf1fffc2fb0bc73c2f39603ad7fda4b4a96b15939ed7c4078", + "0xeb2b18af01554df137b5d3205323753181ef8ba3938c047ff21d8d9bdd8a52f1", + "0x06466c627b44364686c0d4f2bdee6d512a416966c8aadd7d47678591798610e2", + "0x6dd6807eaf28c21c7e81e78addac2aed523752116be5465cbfee8c71b2d609dd", + "0xaca0323e7ce85e2b0e54448269fc1adb94dba12b4081e9981715512968eaae11", + "0x5559159487df0fb41da03af21f1c2fad6f654903d1abce844ce05f1ebf227c0e", + "0x7299e8ba3e65e189ab20cc0e6bf45f64c4661e9252535f2ba3645f694c8f1c52", + "0x5d4aeff916d1804b208b93f2146a40a61e8aa393c449ddbe3cf107aaa170fa46", + "0x072a1cdd3e0583bb907aa5a0290d255889847512ad77dc6b601d63171938464f", + "0xb4483c56ea0077d1421797f8a130ecb55a714ca9e3ee7dfebd489af7ed765087", + "0x75d332150daf0b1a3789df798833cd13d5efda680f8842f6d816ab133fdbfa4f", + "0x2fff1a8e308cb4c62e4c2ad7ffdcc12da9bfb7a07d75f68519c46399f1ab6504", + "0xbe9481c36239d3530810f5683226d4ce8ee0275b741205669da54a62e1378d97", + "0x7a2fb544029edcaac21c2c6b53ee79d8926c28b3421806052407b524f5104c7a", + "0xf5971898f1020fff5b16ee3fd6c084903d0f418c23d9df5bc0c7f1fea2bd2972", + "0x6107cfcb4a5d1be452c91123fd434ced4bc79be17f566b9d95c95d0f230e015d", + "0xb1608cec5da31360816e97897c757e1a804cc13093f720c9ce0cf6c2422a7194", + "0x19f1610d237c1b50dea30ffe3d168c80ca6f4d0d08a07076fc28f223aa78d40e", + "0x77d48ed4db293d73bcb4bfeab0e47801dc6d1131f95366915fae272ff999b287", + "0x9f449c093d98f4a67696e8b5d691d30ffed952f8d3c4dd508d3cb939aefbff43", + "0xac585a4dcc893aedd1e163f5a1d38b6115986013bff771760ef67951fff1654e", + "0x26ddfbb4fba28d19b92f0b4efd4558fb61d0ff5229d9e8e37ea5b9428e2d83e0", + "0x6e9093346fa7e22f0d21b147a4bee574b1db5605792ade35a6160d85010ebc01", + "0xaa2ec31c403230c894772d1068bc56a247f497b621b294c609554142e88ac4be", + "0xe673bee1bdadc52e58becaaa359f1dad0790295e093dbb7c16865df7e550285c", + "0xaee34d0d646bd99b184d885a5af5c64618faa357d9a6896c8e4ec05d6d22ffb2", + "0xb2a657c4c16015819778237e4c7b58022da3a40f3052ce6ba768dd2589c3ba6a", + "0xada9a13f6f5b7ec9ae0db3cf191a45e6943787d6330751a6229b269f051da73d", + "0xfa8daf595c6abbf32896d0334fbef219cf3e07317d211306a115f098c9e86c31", + "0xb01a839de17f7508652d3c6c13997863294d0404151035dc8ce11b7e789d955f", + "0x92477ada3e0e98011ff6147aff9e12b179c636239c984c1b1eabd11882c3ce6d", + "0x0846e0692cf159c30cc9dfbad15f3d7b15b044d0bd3802063c307f2ba752b7a4", + "0xa28eaf9d94b6085fdabc625292f116444ec8019310093aa4d053d9fe184c28ce", + "0x6a59fef5f224adbdfeff3a9de772ac3792a489405eb3d407466f97c07cdcb689", + "0x3cc706af7fa368d5ee96f59e8c94e4f0bf8170e1f84b59f17c96adaba623278d", + "0xd32b0db9d3749f4d8fda85d7ca3e569932b560a32637209fb4a407221ce2043e", + "0x3ca2e9fba8d9194e9905fd7c98976428689258cca95a5e77a384c0a4faecb804", + "0x6c5da708d33d75d1c422bc858142a05c46462f90569cf41b46c8724a3d02b14d", + "0x857ddc12443b0fdaca45b790dd9700d868c139ef6f6a8991f98efb8e8995dffa", + "0x6b0596ab39f2698ca666fc0945b9a940b24ccb5e6b79d71b6cbfebf8fe6c8c12", + "0xd485093141cd3d8d80c93033ba673d85f49d58d9361307dce216dbaa1335f576", + "0xdc7610134db02fc6b5bc944cd6701215cf6ab1afb902a023aa90c87c99425a47", + "0x8f073b0490b3225165ff2f52b777e7c22f949929ebefc81fd35743306c4e7ccc", + "0xbfdcdd225cc0e44d3b286bac4cb17967de9136d3b1b712e2e8123eb96d97ffec", + "0x9cf2ff5ce260983638f6bb7b9755256e48eb1b14562eba83318c8e7a122e15d8", + "0x02b0f56cd2f55c60bf1b25bac99ba35ac637fe99e9b2c8a34dd6f96e96786482", + "0x1446d79743a79401802bb1862631600103fb1f928c41415aee7d8f9c39724cea", + "0xcaf2bbd132570a68e3fa49020df7fdad3151d52ae62ad28bde89b4ca749bddc5", + "0x872e07375b6c9d7ae0bbc2721df314e5f900c5abbd0872a8992a63ca80056aa8", + "0x8cd5526b5edfc9b22e4ba8923cfbf27884e2d2f80f87fa889774591ebb0e9888", + "0x389bf4ddf3792d6592284f6785e37e2d71fee2fb4bdd5f95de4e3eb18e628d86", + "0x17c89b1d51e9cb67c760b3945847938ea403a7069c03d1372f947aba870d6728", + "0x37f75f8accd0a69060f466101e8e619e2f301851cad3f8e3a4788d3d54fd9903", + "0x99733df15018746e885c980691f0a78dad81107bb5ebcdb28b46abd256cb3484", + "0x62b48fcdb2642905dea8307fbab17aef203350d1163987ef4580d8f089f70f91", + "0xfffa5e2c4e736339b8a6a15922df8ab5b7f4bacdafd722a3fbdbb595c57eb94e", + "0x3d4c75df576d6d3bb8751d4a3da7e08a62499b5e264b04d4269ab16eac92d211", + "0xa693676a5df5c0d74008b6c555015a1b518fc1fbfa2129b989f81da3689c5541", + "0x2c06b0cd2711ef297061e9a5f714015902b45e20a0d4933b42a41cc61d8403e0", + "0x2244c05f9230adfe62c4653e69b276965fbb4d2d308d953aba781a0cf7e61d92", + "0x1faf8025d0c329aa544b89769b549f7af4bb6235cd96a6eb39977f87e9aaf9d4", + "0x39743430fbaa260bc97c3f780b7113ef8334e2459e85f1f05d12bbdd68efd37f", + "0xb30ad3157e918b5bc1f7135406b2e0cdd4dec043bc7eabe03e2cbe68dc4e6030", + "0x164c7e5bdbf26de3e3b2ed2999a6ecd0e7623d5fd4c285a97cb3b2935196e4f7", + "0xcaecac7695a193125a1fecedf801d3b87811ce23cacb76a547844237f401bff2", + "0xedfbef6dd5dce3bd5c6f42d307bca15e09676eb297b27aa19de5c462be743870", + "0x0a3be90144a31abe005b27d56695d7c49d0d286ff0c266b863dd67ae54265226", + "0x10b984b13c0aed0c753e4921036ade71129cdfb1c394c1adcd0bbd1146afa827", + "0x6e095870cc392e6891219f7fba3bc00b91d039dd61fd34b2dd08c9f6895d6d59", + "0xddc5fea0e6e021186da664ee2a8dacb3052c7d731ac0c4c4ac77b27eb43b8c4d", + "0x685c324c583c84be6d106b275e49ed1405ff0468e114d73609498971452448fc", + "0x2717514615795c2f95f2a34f0f3da582652682f7432cf54a6160936b811181bb", + "0x5d7535b3eb8978085c9a9851c76e6ba0a3dc27cc643ce4d12347f1e913d03786", + "0x85fcb9ec7b92004c398a43a061ac72db01dead9c51ca80bf09968aa347a59ed3", + "0xd80c2082451157590c254ffbef81b843e0f48b5fd11e41989d5a2f3f0a9f6699", + "0xfbb6e5826e0ece7b2ffd3b1e5d4f6a38e0098a8000f0d8af9e3cd00f247760d2", + "0xf3f21089dccaa5b067052d63716d0aadbb1d40fa711a46ad26c40d766ce4aa07", + "0x7a2b769ac6d50aca4deac782cbe1ecc2e7936f82bdd422d719c89fea7825c74e", + "0x5f674f00d93a2468e8979b83a46545f88d65c858f0da0a0358d89398b0bbb1a9", + "0xf21d3dc279c5b471461abafe1f59cba9e96af63a6b7e099446c17c8518cb4d8f", + "0x6f835371733397ec89794b0ba95b510dfbb6ddedb10d9dd711f9cc4662d313b9", + "0xf9fa9c7df8678c2a0619cd821558d4fd71a129962cc1acda169867d680cbcff9", + "0xe70721364c3e9e3ecae7f3e26d6634484004731e3ee4e226b26e63783111943d", + "0x65e3d7082a7fab87e24e27e2ab92b4b2413a37632c71dd9c8ed666beaeee31a6", + "0xc435b6bdd93ba907fd0ef938dab1d7da737213cccda2765601fa9f3ed1ede193", + "0xc9917cf45efe29cf36103561696761ea76883ab9debfb39d1aa2903e6b22d90b", + "0xd666062f948b08574d7bec3f8f9b42ed3bdbb8be65d2266b6a1ecb137e560426", + "0x92e4264ae4a2279f6f300bb2ee1ff1e70b6a65cd7067f46900bbb960bde9eea1", + "0xf43801420ca30ae142fe98a25066e59b32c24e5f4442cb6589b713f3a53e7af0", + "0xf4ce3b52c05eee569205715a6ec39dc7619de6d182302a9360fdc379e7587106", + "0xe48a703e7613fce392ab3194a9eba2f1f860e0caf02aae18ea65bcbc45cf7dc8", + "0x3cb2781dd02b803906c6045116c5afafc9b94c0b7b456440b736f437fd1248aa", + "0x6745bc2ed210ff046f83607874608ad2b82f35cd627916a06db8cf294b354fa7", + "0x3b42fccefd1f3fd97f5f6be1cbe0f9a7a33fb3f8801fb560d34e16e5b0116b2a", + "0x260fcd1a5f1ef15622b009f4f31630d6b29235cef7f2269eed55bcf47e74dd15", + "0x2a78c19876b27e1a73deebcaa24c52cd0945773544c4a320c081cf41099d8bba", + "0xadc6040a3b2c77d41cdeba41c7f238d075fdcb06ccf032e5b97a4f8c62858c62", + "0xc6aa92dd7b294788ca774c9d1fdd366c172bd9b5ac0935ee6f3ca12dbcc774c4", + "0xe392c90065e54a07306bc4e71b4eeb49447fc21dea760c15ef04f1f4fa261567", + "0x16f2f69d7b4f7b213addef0af6ed3f39b322f7de3d0a187adb1652e312a4428a", + "0x011027be150fd1221965d9fd2efe889c4ae44e1f7048a6865c65d97883e68147", + "0x6e9d67daf6d78cdae6ccf9fd0fd29199ea191e1c240d60b572e29faaf7b55586", + "0xf3fe75dfa44c30522eb99c8ca0abed342c74e8e4f4baad1e772dbfd4ef0e977a", + "0xc3cd13f1cf3e531878cc535b9e82961ac8e2361f65fb123a5d9b8843cf86cd5e", + "0x82633353446b333909699cbba59c2502ba714b6e991b4ac2c82014f283bc4280", + "0x888a288171ea09056abbab2d6193abcaa9f722ca4874b63c656707787f489f01", + "0xb2f4e24acdbe2c20dbcc78e872f0b6edab11804f61cc3da4105402ba60276726", + "0xa9a67ca57c8c2dc3b0a099448f8b7530febf0c513df964f3241f2e2db8f5bd05", + "0x2f137841adde99bd3a795b3528b23ea82140efd508d159f4742cb72d2284b25a", + "0xaef54cf8ce39d4c8e818fcd6e82b24714c79f0d80fd6d0b1a5c995397b448362", + "0xb657f41609bb87fae892f0bb169e5b3561fcb3bbba9c025657726d4d9039b6ee", + "0x7ebd9300b2338cd76b1cd43f6becda8fae6a5de28d750391a931190b5582042a", + "0x8394f1c606c22684fc8db7347d29aea20f6444491d04977ef31dbf54b71fa5a1", + "0x79d41dc202c4b28353e2b8f2b9384775b427d2107eb06d13507f0d493f967128", + "0x4d4b02c4f84b0824e24566d01c6f820968bfd3687c325015ba2f28adbbcda76c", + "0x9d22716e6fee8da88719e252e99839bd3ce7ca963f3238826909f66865d4930e", + "0x4ce338f925ec42af0a55406136a9d218d100d2b4744ec44b45c790011a1e1c66", + "0x5726b199e0d291afe2697d54b5b03f0bdeda2be861d0fb65cea58b8129a7f148", + "0x0dbfeb2392a6332609154b7ab4b86b51343355009cca3743ec09faa0d3e98a8a", + "0x3faa957381203fc95edae5deebcfe93ee1e1a94b8e0c7feed0a70c92ff6fc2ef", + "0x1e4738524f522d3c69616b29c16e7ca9c8e3b24791946d12e93c51b0935b874b", + "0xe50cd8d7bd22b3a5a0d291b60c13992a7e4777d771e591851f18408814e4d3b0", + "0xee6431408c50c0ca843de0e10b12d8582269313edba11731887d00babad5e590", + "0xcdbcaaaa27e3beb4c4a60a16dd61ba682c84d88dbf3cda23335a12136c5d3f13", + "0xbc629d4a38a26e02a3e9cab766ed7e94d48a648f2ce4b88e32857b8b83accd47", + "0xcdc8698c91e2066a9bb3ea748f2c34a975852c349e09dd5a0b81f81491afcf15", + "0x608c81b105b5695c54c2086705cac30e307508622be00540e054543053c7f56c", + "0x538a940c81dd4806f5124abc4b995a87af739f1e34d63281a464fccb8a9bb255", + "0xb5a814acbeae3cab961f7714cc9461e9180b3df94b98840265421ad6616c0313", + "0x7c4e37dcb28d7cde7870a6cecb7ed09966aa0a503dc80844c3b5db3663507257", + "0xf37de8542693400f3ddd678560e595b22e2259d0a145d3d7c54c7da97296aa24", + "0x1098bff3277701c3d7aaf5551bca4db5808a3c9c48fe361d05350cb8e86bfdce", + "0x015814ca90ca647eb27414b8000235ea588c2ecefbaec86329940c701c4f00c4", + "0x696d8910a5b0e94892136920a86dd2082ff934332ac861f35d6c681ad109c69a", + "0x5f7ae78716d507102afdedfe1805281bc6fc1466b8c8516ddb05ecd4d4e9b7ea", + "0x5dffd770264a1a123cf515abe16804a6c6f3184a751a75d7caffbfe28df37c42", + "0x05b630cf10095235312c65a1cc1d3de909016946e4cab8d589df3710ff2f9bba", + "0x7f93f7470b98c34656fd5dd30f16772295f1c1dd23ee36ce54d3c72240ae44b6", + "0xbf7c5de182d8c2df6e14b226015e7ffeaf87ba3066605d98ea896accb5ad8f48", + "0xd770a387e51537c3e941e758e5815aa8c6fa92fc3b3a49f0029a6221ee10fadf", + "0x075396d9c2eddaf0240bec609aa27fa1e81a32206547b3b267ca35cc810cf2c2", + "0x292fb20462e8d60a49b091a4a48512d60bd2696f793b4398df46783ad5c8f08e", + "0xc60b034953029bcb97e7162cf105774540d2b91940c093ca07804d6721302ce4", + "0xc1b735911cbf4d1aa6d74c293e723069ea60ae1d2891ffc0a9b5768dbad80b82", + "0x4f809bd85fc89ad269fe79f9319e1c8303664a6aba6afa1a32a68bcdaf218061", + "0x4ff0bfd9dd801a47ede53ffbd1636a0c51c02a43a82de1e20788928bd7b77765", + "0x47921fa07d7310233846303c0b3c4e08e3c780f455cd750d945ca72ed54f5be6", + "0x1961dfdfec2f56b2d4107296df231a5908d41ddf878e68d7c35b8703927fabd3", + "0xc7e04e950e6a80fba6d47b11fc00dbd6d1875c3376e51df31b011b1aec24837c", + "0xccd59db1d999c8d1663a42f477fef24cd2868899ae6799339deef8df09ef62a9", + "0x81241e5fac7299735984cb0bd0a19788821d6a1bf41bc5e84a018615ded7c8cb", + "0xde8e7c0ba2a45e05a211a54a79bad716b4aca6530dc3d06fea3d4ceed815d2f1", + "0x55c59215777291f1df8eb6f6af28b2426444c6a6742acb71769be19c5f80d4cf", + "0x4b88a39b9c026c6ba201d80ca5205b653ad40282bd88a9c40d2e42f6fa6286bb", + "0xf2eeaf761b756303e620148d9aab1055eb13816c0b5c9526ced7ca99ac0bcc5b", + "0x1f338d9b15d26aba4d3b99b3ac4d9f745dd641be71a45d892d79332a2b588bd4", + "0xd0e668f0cd91af8ec4ce3125892d6367c11113f2d6e0bc3912ed02182824e28a", + "0x49fd8ccb7f34e496ebef0721f5b2b2a9800ed0995a4dffdce8d4d9353de6cc43", + "0x67a2405fad93f416001bdd4e400028b871c736d224ddcebd519afb86fabc12c3", + "0xfdf20252f023a3de57c8da58588e6b63eba355212719088f6a314cceb50a54fd", + "0xfa243c4fdc583219bb6d6d656667d37626be12f0cbdaa4a1d3b54eff28067f84", + "0xc04d4e95985af7b8d4606b6b3890181c45ee4d711d491f27fe98c322569bb188", + "0x992ced7db4c8a0adf95115548edc2c0f24e8a6dd9c0f6101835b379ee4092632", + "0xa977d9de1df7d8995acdf6856b40b919ef18105ee251358cb036ba7df01cffde", + "0x2fb83cc40fdaf1bbfee48f94403edbd38d7c584519b97ba34407062e65f6e101", + "0xea7e4136e38ef912e1e42ed2c7590a189895faf0317cddea5067867f7ecbb508", + "0x80dda1751a06e86c0ce4894e427f291b3cf5fb06d393f8d801305b7e400aaa64", + "0xd99542d510916ba83ab00c041aba6d5631d374a2cc1ad6f4ef07fbca8b84e830", + "0x48fda805a9b44ad42d41caf6f253a7d51176c8ab9f4480fbca29489415756c18", + "0x3588f469e29a4958023f66f5e0f181cd099f43db5b3fc146f44d796927c2e930", + "0x066decb99e26dd1757e5a0f57ceede7f4bd780dbf7037d1c8b73597dee3695ab", + "0xbe61809164a5793787b02d6775b521bad97bbda6e9a84f3472e0472e89e69bad", + "0x27447990ee001610dfc80e430f0c8eca5f6d565aebc196626cac0d2ec345704d", + "0x94de79093541b7a9e2183475ecfb5cf264fc77beac5bb8a9343bc7ada6a4df60", + "0x09913ba0067e206110adc67526772bfbee09ed01e19ba518b3bce2482a9a9603", + "0x617cc9c90c39c87875645b00d25c5b01c7abe7da57638750d31f007a7193ae69", + "0xf3cebff8b59ddf24fe100cb9131e812f6bdd9e4c2b967ea50fd9993c662c3fcc", + "0xd6dec3b18bc3c01b2e032102b22fdd2120252ae86aeb37bdcc4092351d95b2ba", + "0x53d8f0a0e2137038017037163b5606e22a4a9dfd10d00bec551cd3090dc5e9d3", + "0xcfe90587a913a1e86987006496745245e45e2713d1bff78a0cd2ea1e17ee8589", + "0x5f54a237b2f8bc290655b6cf5250bbce07513f6b40ba183b6e07ded4094f5140", + "0xa50b0ce0a228e302d78a6b421843342a1d60bcdf5ae02e2eadd46a2707fac7e9", + "0x2fab8f841869d52337f811b92b068d1ca11b6503e730ee470120c18afefc5d15", + "0x5b9c754a6712ec8e5f682e873496c8a72598e3fc2e7533521fc2ee3d178d8f39", + "0xee46aa6957559482e9d19522ea79af524276548af1406b6d0d9fa951b1383f86", + "0xb1fb24b431a9a0d08b02b2146fda4aed857c0659bfe261a51d1682e1f139a088", + "0xe9bce096e2b9443f47d47af81137e0fb300cb56f7e78ae1f24a28ebbed9b300c", + "0x250f25daa8911c06fa4c723aed6b0a26cc1bab6d1f2ae8d70f4bcaaffd01f2b8", + "0x6201939f73c84fc3ccae9eeabcd40973075ece2183c788c59f9263fe00b44135", + "0xa8216fd91c08867e3ebdf764e61d6239f1a244995e4ecb0729c478e408f34875", + "0xb4e7ace5567299288ce94b5859386f419ea6955bd25fbea31bef7a11c35add3c", + "0xe725fd62b10475e5544ffd3b5400a8036c9783e69993c8f46d3fa2a9ac08fb8c", + "0x397dd7e8f248a299c0b24f93682c31f17963519c1b08c96b431d9cf842659dda", + "0xb9a1b0001fb2957b97596cd67258d505a180aaa0b38cda9f28633e423383d7b6", + "0x47ffa261fe3b636c6c10a931f531e935d7bc5cde0ae581327c38cfe8fad418d0", + "0xb4ae05f0623e3d69b8a6967ebe4538a848e59a9d7c2918ab36dfd487ca795f15", + "0xa171a3a10ad8cd0a5b9b3695413517291860ec03613a5e2db408cda588291f73", + "0x6ab38e7f583ace5672dd112e4578cebf64046365e1ccab96647e531fee529000", + "0xb422e1a679e8aa4c0f65167ade13faf9d6f2aae6abae657c623b5a5f84f407ed", + "0x184e340bbdded5decaea7d0087fbb8e431293fed3d099b6dd2b8cea7b5ecb538", + "0xe042e02ca12476794320f83efed98dc556cdbbd86c55e480eec0fabd8cf9f13b", + "0x76a1fc047c8fa6fd1202f26ebce8852463a7f98061133795d0966998505cd7e8", + "0x4bc8ebf5290234dd0ea2452ec6284d509229d9121fac032c57d0f2dd38ae823a", + "0xffb0a6d8722ed694e61577d0e0d3f220e5e060f3ce7cc3a5548121d5e15d323e", + "0xe92a1091aaddfb53f10849357299609ec068602e68627e5d94dae468a15f2df9", + "0x36459ee5297dafa3abd29ea21049b6cf46bd713f185c6abdedb28a47a7b4160b", + "0x837c9e38395742939942e4a37c08ca5991f5bfda39a1768a4246461d255bc19a", + "0x351d1b17ff771fc23e14611a34eba32bce00db879cd851cdd21966e10d208a97", + "0xe4176f13f6faff8d1d592f627391ea3b771b017b7bf9a80d365fd03be41b5ab8", + "0x05522930790c094be98f56511d97d68e9c01954dc1b762bc2e1d16446e4bb460", + "0xaf940a43828528050f9d7849219afad69e0f300fce940347c25880e246fc89bd", + "0xb4fc391025cc568db7621e0036d786a2559deb700cc794ed5e5e7064f9301c33", + "0x1ab5c15ef3b19278723162de0ecdba13be7bf390d64e4994e851ba7af175caf9", + "0x91f63782a4c872254492b3a4af65f0929107bc57f0916e09ec778049e998ab53", + "0xf78db31634934c7fd87a0ad272d0709936a592fc40b6d723c459c7cfbe603403", + "0x122a7d7a080d88647b2e5d5c1308b050e555892b94af952d3158d29445e3fa75", + "0x6680854e869884a4124ae5f6314b3a53b44e591ddc50d40971cc1dee947d7a5e", + "0xa88417d25a8f4158e4a55bcfe228b9c747585149e5639e3b588b1a270cf20a72", + "0xcc8924e0b3dc592e9a01c7c9b76a5fb311f482357ce3d0184b3fd842a2723107", + "0x73a53e6c1bd02a7590851c08a42427f569bde4461e84cacea2190c827a6967c8", + "0xa1c2ec0e6dcb85c916b09364a74fd0f07d71ceccc336358b29a3b609eb99510e", + "0xe5c040e204ca1c23b5ef53223a6c77ae36f3540ef4929eadec8a3cf139b1403a", + "0x2dc513ead3ef30a664a73bf52a16aa1800359ccfabde3a61fdd8b9fc5d75a82b", + "0x1efe3c2f9b30baca8ac4199cbde5b9fc6d73e185ceebcf3b85a9ab8e561247ac", + "0xe9770bc9baa3f48f09e8f193f068d5f67aa490f7c8ab1f5c19f71c558f0249e6", + "0xa713822fafad5961c28d491e716aae71315312f5580e588747da265cd91421b4", + "0xf640008de38f298bbd37672777dc1e44b8b49c3090b2664c8b1087e34f714e07", + "0xe72edd6bdc3dcf057fd3c0f992aa52e57f0771c8d387aa1d8c6769416e2fbec3", + "0x5dceb2f5bba5ece29d2676641edd5766ad0d98267a559ecda1427582cc3bd37a", + "0xf1fb5332100498f500d61a04f39107b52e2d4ed4fddb8577e70581e9cfbda07e", + "0x337ed24ceaa0e176d109a2b9cbda5b72e024cece939d9570fb3537cad33784e7", + "0x680cf079a492ddcd88037b491f7537f5e8dfed9567d4ebd1b2c58c846f413db4", + "0xbad57a3b483370a81ead660561812b20c8bd76af95fa5ec56497e886a654a5cd", + "0xfa222f457dac1524927387d720f3cac5ca04f0919d5eb371e66bb222c86e4031", + "0xad506f3f78e38b6b3c786f9c77b49d5b5160c71b74fe61786b9d1d991cf6b756", + "0x88a4e0be48a98dbb1955c8dad8691a7ae2097e0a70deb0a2359ef1862101e316", + "0x8fa57bbe64dd26e2d9e12ffdbd7423833053ed9b9b92531f206d631150b0a382", + "0xc8234440ebe7ce840945a3066c3fd5856851035f1f11bb342043481ce2a4defc", + "0xb551eda033e2b4f4b7c0b3e5f6f16f2a4f9044720ed3b85b0d936817bc29de89", + "0xc40913431ba68e8069a487665d1a987e09e789a1c79a2ff7122e480e388711de", + "0xbb65446a2a1729d6a0fd649b1ff2a4ede3d6749de191dcf026cac144b8097b05", + "0x3f0a16fa113982745afed9607d6e73b55f83723c74cf6f33a449487d1760d468", + "0x5a2dfd7239365c8f502eaa6200b6038ad8a8ce57f1dc55eacbb04c6845ef798e", + "0x7d4e9ffaf7f6bc05c5e82556fb08cb531bd77474e93a81102d27eb2a601ce60c", + "0xb8b65a4665abbfa50b3dd1394ec1e4514188d5035b1992cba1a5e1b6f13a51a3", + "0x3ee9587e0e94feb76f1addb277c790d0cefa58db231520a53ae043fa4408a7d0", + "0x532f431d3811f22e5b60b3b8113ece0d90daa00aeb5952ed57c4f91e8307e8f1", + "0xa490a98c4795a5e728776a17cfb479233c098c37fa0a2dddb75dd8fc111d121d", + "0x2382ecd66d5fd76dff5bed087d612506ea5df43d240bb98f5342ac2bf0d64102", + "0xd4715bd6c347aaa8723930aef08bfcf1ab9830b074abdfd9008f9ed3c32e3ef1", + "0x009c01a95041e2d8d0eeccae2ddd0aa872204acd7ba79db688f8280e3cd0cda7", + "0xe2828fd95ca6990044b1c96f9c9132c2fae16a2379f9888e7c29efbf4dbe56c3", + "0xa8caf7b0ada3a19ec1587eeef9a608bf21bbce43a189d0fff66b77d2abed9ea5", + "0x58c34b4e1031f2c7704c5e32092fd77a305af92fe85552dd36da99ee22735502", + "0x48c7ee9e817baf9f513e86dd209b14b9793c9aef618d9ed3bd344c1067250a2d", + "0xb38d29fa1ec20e085ec8a6685ac74fa2f17d5b53fdede92d080386e3db107d80", + "0x51a3fdd603da738ea0d3f74d17908930343972c80b79a823ab63e0e75414d945", + "0xc697485fa8ae92abbb8e49df4aed93a60c9b2447846c82f210174685a813132c", + "0xe249de2dcf7185def0fa59f9005b7e9571dca5c10f8a8b5f100ffd395822084d", + "0x19535e06570e56afd726799ae4286e094122d09388b0d63f4eaadec8388c566c", + "0x1c3fc264846763dbbd9bd430d3c01f496e3c602cf5157f1f4cb78727b05e6938", + "0x82a28f16c8434062c329a98c31dd1c4a746e2cdfb928b201bc64dcad713183a1", + "0x45709a28e630b04f4bff4463f4dd29bebaf9bde6b60c2dec27f11173cd57b6ef", + "0x1a9540170a2161d81cff7640008c9dd58ee4b031e9742e5403432ebd65dd0a5c", + "0x0bda03a95d656ceb35a56b1ba4e4a0972ffb681b760fdfd58e5b7e72ffa7b821", + "0x794c7fd5db6d385777002ae47a29560efd6d4aa055c0be25deec3265347ff555", + "0x8183c1dba061d48fa73da926fd66ec43d464774b9a899843824254ef54b6ca1a", + "0xec29ca5b9e3e773b787e7505f098a09c505adc1b1a24e6d32fcfe383b1a5407e", + "0x17e92f9253713c712261e6f307b13805b163757eab5cef492745a9ada9759910", + "0x75ca5cfab50b76e68568bcfaa4feb434dd32bb35628c0e86c840b72202ddccf9", + "0xcac91be5af9bbc1f301a9d4bae8ceb4df0b759654e3503cb8e592a82b2db279d", + "0x7f5161a327f7a481c2c327c8a65ca5427cb69498ab993144608bab62423cfd9e", + "0xa9ef93bc60d671ad24a964be755021137318293fd1cb7d649b14f3d5228d68b7", + "0x485eb56b0c41a92718a31b2a8f8a76b5f1ebe62ba450c211dcc596c6a91616fc", + "0xbf221818933cb8426ef79da9b25f9e30d72bc558eb0b6368f549004c4f16c3e7", + "0x4922e6b6b127a05f8b89e47094aaf8d71a741747b31a5fce6116738e017d0fba", + "0x066e85750be64adadf9bb98553a0c512da5ec07ab584c637e65064592ebef562", + "0xb94423c51b7a75a606fe3f7516936e36fa37c769d0dc47fa3a06ebdc65f73fc7", + "0x866adc206d50e8b71158c3207334dbd20645277b193da4db624803bed0e6bb8a", + "0xed2e0ab5c3b2d84beaa4566f497c77c2c7d552daa735935550e442811251cb3f", + "0xf4e9abc704d615fbbee72efe59618fc104ed78f2c52d49ef5eb2dc45c779b1ac", + "0x2a3925d14b90e083960fe4d7ff59250401357c8f63e03d4163fc7baa49973535", + "0x870df32323e316c4777a41900b7b3152f55888bf1d89d7f4e6a9318abcc1d0d8", + "0xbc43cd5dfe2ce44b6f4c1fa9f0a1733592d466a69a453c27325327d58304a7db", + "0x1f5a1ce2239392beb78cf8b642d65fa17aafe823a094405832472d5ece5beee1", + "0x0ac0ada1ad2ebc92504e1c2f5f43249208388e3198ddb6579b07d3abb82ef838", + "0xff299f8a7535db07bba490491aad1cbb33f771fb7a6b950ac2a707052d6d5e46", + "0x29c405db67a66b060fc9d5899a91709e49bce8ce29423e63710783b77bc28df3", + "0xbc91684ffafae0cc71b994fac762d7431f4e6f764057d58fcaaf2593becc2e4b", + "0x01e4fb337f818a1a4c65826b2d35356c0a524ef6438f7f2f5205e3ca123604fd", + "0x7de9b2a4ac68ac6d48612e61bc538706da9d04e5b3c52ff8bc635fdbc4eaec80", + "0x070047819c4e283464e617d9e5c876d36a1beaf650f7c08836f1eeec117f795d", + "0xf361ad6ff45a1e50adb827800a9c40be07608ab021538e6251ade11af79aaf12", + "0x9b4ccf6b9771f90f9a1637b07bc6f441d9f560332ff95eabc61f55e7b00f89e0", + "0xf459e314706d83b6f9fe4fd680c01cff402072231a3b2b7f26a520c878bd4048", + "0x6f673b0d7731c88dfb9b3e2b6dbbfd7449f9469362f63aea41201956c7e72256", + "0xde5da49c27839e33000e87279e0db7970d489372cba7cc38498a3073844f5264", + "0x274bd8e6cbfeaa07023c974250fca61de7178cf393298eae90aa5e7d91eda933", + "0x34c95093c99204204bfc4726f3187b6c1849d39d6b3ec5a63f37a8c45583d93f", + "0xdf7ed80e5b45ffff0a68a41b754a1c352edfbbca41a2718ffef048b75cd3d9d2", + "0x311271778357020981f98f6510b44e8800e62e4d85f0a1b86de07e660fd9c497", + "0x60640c655e966a7c7017a33d81ff0e80a03d74d0d70644bfa823393a1d81c7b1", + "0x32360162ddb910740608867fa8859b1386895aec6dafa3a430a54c73377852db", + "0xab6e14d0cb9cb960d35c24e3ce2420fcc5d29965b0c35914125a676530802634", + "0x20b866c2fc52c03c20c6258bf7055a5f6a1ec589d17670e72cc316d425a12b89", + "0x1d6a29de64ff6b5a6fcb2215967ed851f6cf589860a1fae3d639123f06520198", + "0x8f7c347f69813c7d6d78102610e81fa8ef600dcbced1362ef5827fd85f8213b0", + "0x6102c10a219423a4a856a2c8c096fcfe67330d077599d4cd03751d43882afc98", + "0x66e4ae3b3445ca61af12173814a02d338572ba51597d8ac03adf9cf82f9db024", + "0x4aad7c6e7e7a0a981e0f7ea30e06937ec1c90bd2bdac87c1c54d507bfc7afa36", + "0xd1ad56bf26c66cf2b9e4028f98306282a9a3a936af13ac9d50aff1feeab517eb", + "0xe1279f9da6924697786e540c1e2c6cce76b61b704a8591bb457d18ecac1ead33", + "0xa1b3e1db72c8ae671826dfd132127f8e2f1c43da3c4fa328af6a6805e10a8eb7", + "0x16d019edda55d3c4be6fec457df347ddada7b3a1951613caee6daedf6dee08ce", + "0x2507b13fbb0d37fa3d610b943547f5f3aeccde93fdee5eca0fc91b8b1f4fdae2", + "0x79c3cdbba4a775f2dc8e97ce202b852498192d434e156167a255133a9bcc7d5f", + "0x40d499f661df154302aebbef4d677ac1675355257230e1612fc97030ce52829b", + "0x4d05c68eb964eb6188e1eeaa411be9d0e0709653aa0fbd803d0d8a1fcd98c01d", + "0xa96e23b155d04be4a88eebff4a6bc12d281f38246a0b5a1fac8278e58ad3d3de", + "0x3736d1684ac421415a0c649920bf5842c255800353cad92b402d55c61db88895", + "0x9837d716f738ebc413cf8c17b67485690d850173c3c72285df47dcf988ea15a2", + "0x50b3bbfe04ce985a2cb48b4c5793bdcb8df8a5ab9bf803fb117e750ac65acc64", + "0x6b2dacdc201b30175e158a101827072ff8bda787009a9f13f01af4c6979eb187", + "0x497b2d2912cfc8bb1271ad0c744ed443773d67938f5070430aeaaa0ca57a9252", + "0xe61d5747df5bcdbdab66e8499216e6b1fda2c36873f16e261bf767ac30db0149", + "0x045da368227b0ff2fc672ffde5025aa35677bb663e20e93c5ef80913277dab6c", + "0xd9103c7409c1878ff8dfc0ed8a5cb27972e1f5c55dc8637e60dd46c1f52771ee", + "0x29477f67814412f8158a28d6fd1b1d4f6bb2891d12f9e6dacd4c1193fdcc6e01", + "0x52c76b2becfa289b9059eaabe103b87dacd121fae08f06097cf32a0b220b200d", + "0x727bbfd965ad685f6b658865627ddfe76a08bac915d150f8159c9e5d58e9a0bf", + "0x70b34382efb75fe6abeda2f36b06078201d97752b8dda913136849e66d3622ee", + "0xcc40b20c0d56c0d764088f4ffe03580fbaf99cab20e586f52a4dd9486f6b8c6e", + "0x5e273e907458ec55344b5246f859b45240d4038717eada1860c8295a683eff71", + "0xfb78b9f0090c37b46d4e685d27776edc206f1026cce8d5827dd14cfb34133e5d", + "0x2f1188f7e3cdde165d196c133bf4b046583a39af6c2ff5b99389462d059303cc", + "0x4cc0892600fd0d5c9b96dc3c9bf7e79b4a1e0b911fbbb66c6d94c0aa90670345", + "0xe4841166d993dc005673e8d6d2988caca69af65bddaa3949cf661fb224e4c8b3", + "0x2475ca20c0930111a0fcabc26fbd847923a1a1a5349d291845971e44cfd2262a", + "0x0087c1ba017aa4d0d38e1150ff1c580f9ac160afc2cd25f1a8b1fc9fcbdb726d", + "0xe4947786d9eb17ba7fc6452856dff9076189ffc4bfc165843fa458e838af2caa", + "0x663c5a0d69551a03dd456f6bdc702c3e46fc3b7acd5f2e7b06547605f840dfba", + "0xbbdf3b9d35094bf526a9c0e68a1c453566013935abe7d2b0a33827cf5939f2d5", + "0xde73225ea25c0f8f2aa220d743c6809b802ae09aa211562b8a13dec67332f3b6", + "0xf6d8678b8b082186a8450012ecf369d5874f183390018a7b1b104c3a6eda2188", + "0x360c9b58e619864a4d94da02d7c2f39341e4af3029bb023c2c208fd87c58cac1", + "0xf4fd5fa3baaabe7217d5560d2b11c0188c3b89ac6a0e8d7442c32dada3214011", + "0xca48437464946ce4080e0db4ded959b5bd8e0c97326d739f9f7db6e44e562487", + "0xf03ff39bd6732e670d07873677480780c6bbc1246f6702f8257aaa9e421344a7", + "0x88dd10b0fb3536ea8d530097d1eed23ebbf68f4d208020700662aebdc23d3c9d", + "0x915d1ff9c61f2dc6690de86c8e885fb1bcbf5e94b1bf3751f606576a7ad4a1af", + "0x4623aec585a582447a295c285b3df362bdaf00ca57f2cf5b78841f09c26b90e8", + "0x1bf2a999040c1f1f2c821e3bd9287085a92072db042c3ed1a24e829cfd99e6cf", + "0x1a29d98aaf8ee4bd82a8607c65d6fcaeb5e1eb9439efcfdce2f248d30cc188fd", + "0x0957a0e51934089da8149e4d2c6b4564dce3457ed008bfbaa59333898166c18c", + "0x4d43d63b4cc8c777213967ce0d359530e6f5d5b5e518ce377b719e7eb5c0aa34", + "0xf198ba6218e4b70b1b3fd30da18e0b38d6cc9b7896cbc4f7a8c282540d8e0087", + "0x043d78c7c723c330ca59658d872842016c09d6e5739f6a11cad5cf5a1bc7a880", + "0x02adcdd273817ac89caf0825ce4cdcaeb657a3c3519ccc3e9627103354cb5e2a", + "0x277017675568b2d9475db17cbd5e5e35d50521d0386df756b63cfcbbaad0788d", + "0x5adcc98f3f0794c5e9f97571cf530e9f1493c33cf9892568bca442bbf1cd4c0b", + "0xe670d51da3ba73593dc1a7afdcf1603f4104c60d7387d2d386d2de627d7931d5", + "0x256fb4be2c19179346fcfae71b7eec0003bf1b6f1cd16068410d5857d80854b3", + "0x85537e64a772b4cc6b8c0972425bf6a8e2d966d9ed3006793299131a6cef6c75", + "0xc2ba444378b8508d259403f36e01c23968c37001c9b71fe3ad855979a039016d", + "0x2a68c95cbdf085aad117f534d021e51afae327a64d8f1fa44b7052f5ee729dec", + "0x2af100b9d1ce04039ed23125af12b252dbdb72a0a233206c8f55585c7fa4bc0b", + "0xb985c4e9c6d37ac2ea59c59a65c526fe9bef18b95880df9656622f7b4cb5b9e2", + "0x8697aa8cd18048ed11a95aa09f6d1ee805bc1f7b26a1e6e986f594fd5473c26b", + "0x25a2bdf93b5550a4e521727728f3ac7fb695c36b8038ac7d038eb9a97fbefcde", + "0x107bad5fee25f5582679ddd9a60f2e4c931c922a8a784a247f278f83faaf62ab", + "0x3c9ac7438ddf65d0e7c19fd6abe5e3d56f493c157df1838dcd28374b115027b9", + "0x1bb985d85ea99c52b77f7ab1a67ff5d12508cef8b42d94410f48a4bb84e7a3d8", + "0x68b42518d701dfe1e8a35c8a5519b6ba88b443f4b3a36f1e1e76dbf4f76e42ec", + "0x6c48962dff8c6cd07f6d6fe7463f8d255ef0762a76dee29d2ace06f25b59f571", + "0xc04442c2d93ded0abfeeee83100b9d7696df583183f4f517cd633f28e493dd31", + "0xcc4746ab2c3cf15c5dc66b0e56b5477ddfb2f9713427ebdbe0909a75c169adfd", + "0x13004ad442b36fae686f363ad12720560e14cf46ddbc7536ca90a3d518b88139", + "0x2fd9987ba68975fcec9574f2c346e64279cdce25c9f9522b2a4e171cf4be4b47", + "0xc903318a8eca5a321fcc305feb675f1dd8ce5ca7345a7f3a1d474deb8b922480", + "0xc8853e97b884251321e3da6ce9ce925f7e176758071de17cbf5a0dfd8a8de841", + "0x29ab33fdfb3022cf5806e984ed1aebd156d39f81d3b66e183493d8103c158a42", + "0xbfc011b8a1616f063f7f3035e47dde47009e7cfb90187005c4c8fbaf52281dbd", + "0x082e587f31e9f060b7f16e3dcf1521cb8a2c27f333df3c3c7bea5074dccf76f5", + "0x27cc81751db4c9296004eaca86e0a50680130b2dc450ba2e5ae955e7d2eb7375", + "0xb69e88e21acf0548499b6f4e6b977891cf79b77a0045472a02ee4e3d6df3f8bb", + "0x1c86ab2051b69e2eff57e6df27d1fd9dd1ac5ef08f775555453ebfc5755fec3d", + "0x03cffec191ae3cb18bb0dc0b13918e9b3928601ee7f09bcb0a169fee88b2b87b", + "0x3397b4995ed07312a25503db95ee74e817d9232005047e5b1e0445baa13c2232", + "0x75ce557d60fd36e1bcdb69d044016beab765d64b506c2b68e31dd86d3bf4ac59", + "0x1404c8f9eb67c4790443fadc538b4b83cd92116427b5cfc3a3bb2243d94e2860", + "0xf0f8ac1f2ed5ee7d476f9f9fbdcdbe6453d3e485a2e785a9e7f6d347c126fb4d", + "0xe52f72c641ffcc2f69ccd56c14eaf1ef9fbe41704af2e35fa5796660fc69f3e3", + "0xe752597c01d0c60629c31b684979022f416eae835b2b5a2daf111a5edd90daca", + "0x50446d94fe1f8118b78b77cc148b80b98730597048046885f283a858420c5554", + "0x4aa082412ff7d60b78606d3268da4f19cc26e7d0a764881c5f0090fbd231aa7a", + "0x0a3d6bba60ccece5391150e84d0bcb3141aec53454c8767c7d52fe2431c09a2f", + "0xd65ba9637cedc5682c0e759d8de944f87429265d0105ce7f85a2348485231371", + "0x8dc9ab9dc175879462daa52e2ecd4f0f73254a419db8ea95b63bf199c655f93c", + "0x6eca853cfa64bfbef66e3a7a30252f364c7a72b6081e4fc901244af2e551b57e", + "0x314eaaee2faa7a9f64e32f4e772d46eee9a26be9d93ce47030b25e60714e297c", + "0x98617475fb328c23d9fa100d357ab19811e78ad789b42600dc0bb5c5be574420", + "0x075283b8dd9e9dbe3beaf7bfb21108f520d77ba8d03003b0ae59573ecd5dfae5", + "0xf42fee849a8c0b64669aa8b69bb01d2aafdf35f5817d1d21f52b9120449b75c6", + "0x2157c67776616c678ce0703030b6d9ba5a5379c894cf32c6e2ef7ebf208d58eb", + "0x3738de374863b46ea8aa45f71eeba068aeadd5a70be48e68a5d4b579fe8f643a", + "0x455d4259ab0679678d043f890d25a241944572e4dddb84b38d3821b3e5f884fe", + "0x282a24cc340c7056b953b38e45b64c9d46b71bc4a391fbe84c4de730fec9619e", + "0xb006220d04c7862317a8f7788a24e66a0b9735d0a34bc98cd68e163c0d4b570d", + "0xbb5eebccf8c4d1267b0da4da427259576b83cd6cbb5bc5eb4fda5795d17a03e3", + "0x2c06e2c12dcb59e82d9e7b8cbb9d4a2325684942be3b44ddf79bcfde65f54c0c", + "0xa2aef212f7bb8d9be0d34c5a1e79eb8dda175a7aae2a14995037efda5d9966e5", + "0x557424300cfd2e5fc23ec41bd655802737f5d1832efb2087ee9a456682af79cc", + "0xc79b7e99fb313431d2acf7a8ecf2413a00e351afc7114ae81cb10cefd172c11b", + "0xee39bf8261b695243b0ee54580457ec4cdd344376285e6574fa6a857a0412473", + "0x4c1ab647ad8bcdbf6ae9b7f8956bafb2d8bfab342be40bf9940e36502162e4a6", + "0x6d0aab7d0d329d041191b6fc4daad371561b19e1aba0cd7504c1590061071133", + "0x19f6107aa1fc24985b1ddefab6b80bb232c85b709324805c95baf5383ee5c9d1", + "0x5a8d0a477a71ff90931e70a0fb71c621439b803e0b4d98f1deabe8a07f896e4b", + "0xb7531c109e849312bd891667427fbb0045465074cfcecb5a5040759043435d3d", + "0xcc99357573b21600090951166dd1b2a4d7c5c92303dfc67f74f8f1463ef3621f", + "0xd818f1e3270a12607dd9162741ed1d4e6e9acb1cc52700dccc8eeb90af684f34", + "0xca14edfd11ae78f71e9c49cc8501cc4fd486c8aa1dbe8a6bfa6bd15b690b90e7", + "0x528b2f227ff53bc4f035196acf9f6998e4cdf11a1b60eae510ae6f0c69c1c02e", + "0x90ad7e25d5eb5ef9f04f697cebd995aab75c46456d44df80efb7f7c6d7dc77f9", + "0x82bf8587bea159bc2065a26fbaa101c3d97d3a72920dfcbd9df18970cf232a7e", + "0x86ea6629334ecf314ce52294040ad3bbdf5bc3faa3a49f5075baef3bbf3ba72b", + "0x86eea09155016917521db41b5b87d6585f4697c2bd2947ec354654773be0c1be", + "0xcf287de494c9f3d2ffd0af34adfb31ab462596880895b26576ea1d1d980438f8", + "0x91115f1ead59988814de88921a491e343f1302c73b82f2c40ba0cf610b765fb3", + "0xfda83473be019c0855c449ca47f70cf367738d8f8eadf972d2e0c635c044e51f", + "0xf1e0c2dfc9c2eb1bb4711db7b7d4c391493ba3f5f6a1bbd1e183ab66b19c2bb5", + "0x2e5d8df6bb9a5866e6ed58adf1d14128612f40ea759b5921e5369b36b998a6ad", + "0x445b4bff535145bf0e4dc6525fe72548ef86d7a683ad1dbd7411bda9fcc1e4c8", + "0xbe831b4a1d7c7cfb99feef72e02bba2ae46103648290031b50fe08903753c058", + "0xb3555e1a84c741af286fb55ba50083d0931c07aa52d469366f9fcb8a6b3327eb", + "0xce37c568264991192b942feeb0090afbb83b83a9a3905f55f922d5f35ad23867", + "0xc650ea418582ac1427c72db06b87137981e42ad483fe5fa37f9fff132558dbcf", + "0x89ac5b79845f956736e3c1db0897ff60c9365ad09b3956f420c745fb20513c2a", + "0xfbc340e737171c464794c184f5c00a7dd82eda3467adf9a13a0ad675daaf0fcb", + "0xadc4fce76c36308c2a048243eeb05ad857ad832be4a5f53c49c496c5ee854281", + "0xb50b92b66c671c1d5cb85f62cf7e17919d8e5112b3e94b63e67fb7abd24df387", + "0xcb8cc302219ccf14dfe62e57e392c7f892ad8b7d1f6c2f680a9aab9defa9254a", + "0x8870f0f8b8f4f0fd6b0f99cdd5533d84cde73c002c6fd1069619e14c247fafdd", + "0x5712a9ca1f7bebe580c3cedf497f82fdd7d1f228ac20ebcf540b6a2b9fb04930", + "0x69ce73010ae43f6fee72f6aecffd01a1f2e6c35bbb1d06866d2040260b8243ca", + "0xfb7974402702d45a69a7be74741c337f5d7f56f00e1ea4910eecb073a1095600", + "0x185be97b8b6ae85470a882091b36a5a028de8580166c5ef701714535e65bd378", + "0x36dc59b856707c616abec17f17e1b3a6f23df30b0b3155934ca116597ef20e16", + "0x87b0a09d3f74d473e1d85fcfcc32a04f962c529a4c8f34cdedfd4a9aee8531e2", + "0xe4f73725e3547757840a146a4d9a936ad87bacf2767e1d3735d4c3dd7ee5492c", + "0x0e7dcbf7d607e3269e263ef07f145b50d69d008e44232766a2653264b4443dc8", + "0x4a0444e42147420d77044094c36523d3b9ed9e2a7761d7b509e78178e2d2558c", + "0x89e47814c2720aded75acc4191a6e3aecfcbb8b938a65f2064eb8e5e2d655666", + "0x5e090bc08df3b71fcbebb46a6442b4c54836b169a02c04db82840807218a1a57", + "0xde325f2ca7b5b602c5bec4272da2c5bbed602151effad6e88a41f2ef276028f9", + "0x936b0746e97781a2a50c7c445a8e7adaeada0828d19dd1ed0998a94a31d4db7a", + "0xce75e666926cc075763f4cac400dbe6fbf05871c382261758981a31482e3d9c3", + "0xfa15bda7e97c9556c72e83c799272e9eb70a014093234ec77fef89e478c11860", + "0xee9196e5326f54718891c710c58f4b42b7382cd517fb78fc72941b8e00ce750f", + "0xd5c301f7ecc16f5330046af9efedabc08e75042da98afaa2da20815bd3d40e81", + "0x864747b4d71ac398e8d3897b4f3ffaceb1c71f5f088f339ff7cfd9bc92de2a26", + "0xb7dc2fe92c2b77f28df752fd0e5346aec63bf5d33856406e1c770f4e4b3e2262", + "0xd00a02e19cd7c671190978d000d7d82c0a2ef5581ee4994fd667dc8698e4a306", + "0x9e023349fe729def5d22884104b49e900be262849c4af2ccb8c535b366ea31a5", + "0xd47096bbe5466b8003ae378bb7ca47e3fa7655718a9e83740e9cf2d2c2e4de61", + "0x2f6f8982ea33c118ed7e50a802d20ace2301ec87d1df9733d5489ac2bddaa56f", + "0x0cdc61fe5dd65cb462c9e251bf12d57145bc67a9d41645b630e3b531c41594fc", + "0x2d738515e8acdd1435ae04229db64f595d1b128c8fb5b11eb444d3f589768cad", + "0xeef048f7cc3b6310c2dcea685d28346be13a3ec298a105bae27488f97d25004f", + "0x9f35f8e3179870ae736ba3a6407d1d372bdeff33907b8c426f91b129907ebb7e", + "0x34abb90caae5285c98bfa052182786f7d02d6db98c664974cba2bc12e0d3bbb5", + "0x3ef3ae2cd077a3dbeb47da4a441235c853705745445ea52efb1581d8c487e375", + "0x740baa7646b92d2437dd2cc506050fe52b7c72a828c3ac3aac9885826e28b241", + "0x28f0774d086e6f27834a4537f8cee45fb53dec41ac86ee4e2db8e52db52aaf4b", + "0xc77617780d016f9bb6a38e39bad997c3bed97ca4929f165cf9f1a2e479236b64", + "0xf07c58e713a05152c69e332d5a7166e7608ecfaed654127d1f2c36df25663e92", + "0xe8394357d86a8f43460886cfba4f3015da6990775a1b8d6293dffaaf5c02563b", + "0x1a87c75f2ceb216c1101b965ba77737ff99e239dbdf91ca6ed6ce16447dc2176", + "0x28f6fe4588176aac39e568c331bff62ecd2498cedc495bdd77d8936f3c63efb0", + "0xfbe2a4f3e566ff822dd271f92639dd8b77d1a511950a47f0f0d0e956db50ab66", + "0xaa6f78fdbe72f30f9bdec4c355cbe81ff5d1f9c55b7634ca93ad64f78dd778f7", + "0x5c92d485e1a607c2d28f6dd3536a4f20f4eb7956b133f04bef66b6b70212770a", + "0xd4782aca49e4be5624f6e99c78723d14a187ffe51731472e4a18705736e84507", + "0xbd99dc257e3e9a72f4638e5c0260ca41d193be59bdf8e24fa29d30e7e7f72196", + "0x8f9151c96e0532b813fc6b0b55a9a775f10edb2a9cceda099d6f18755d3688eb", + "0x52f9170cf716a6e9a01980734581a9d3358994064d469f4f6cc283c7b359135c", + "0x8bac725c839e93e3fc5b00ac349c6759b60200cb4fc878bacdb3fafdeddc24e9", + "0xc6a1f50323dc2b944d6acdd2a7c827562665ba21f57e688c8a100ba59776d9d0", + "0xbbb4aa20b3d19ba61b9749a1260bc5a08ad979e26f53835647fb765e06efb307", + "0x3cffcccbb8735e8802bf8b2a2b9df51f792695d6722b29914cd3ee3ea69feff2", + "0xa1fb895de47177e6842e4532eec095b6dc9d581e9d67d5fdffca3df054b3dd84", + "0x8ee4c90c9abd9d0d8b393d1c2df9480bd3a529c6876fbbeba5639dea756ca55c", + "0xf0398a7d93c7c48084f124d628f93864f937a3cb00779b7024e83f07a1d10e61", + "0xa16ccdd5a9199d23cde04a6b92b116798f56708e1a60f3745a6bc1a298a70f30", + "0x73d0bc384c9c0045bf1d781d6e8654f3585bbdccaddf3b8f94ccfa4bacf82024", + "0x9e63e08d20d9a569effba774a7c236f191bc957a8adfc559863fd2a075e1462e", + "0xc37265b88250dc5ac0929ae37c351793ffbec765ae1f0b211040c60dd176ce94", + "0xbf59cefa43a77205af4e2db2650f58d5e794a32d7b8b658a0db0bf9676240bb7", + "0x5dd0f31e43d685d4ce6ac8ddf6bd5ebc21584dff28385182a7a3bf1085a76648", + "0x38b4ffecf21502b7ccbc900cc3e4f241d7598b43def07a8931c583cc166a9fb6", + "0x42b27699f0886fd4c97e3471aa70b142015291e3e6e621141f63a0cb4eae6bba", + "0x9fc939cbe877fcac5560d1e564cad3375e6011d3a9eec0f5a19ca36746b30c5b", + "0xba0325304528d598b798d2234476b68273962d99cb45fb54a96deb21c6a8130e", + "0x9d6354b6d379dac3ad3f80f17796a0e9a0521afd368d4e7b8c1199600055627b", + "0x43fd9f60b878cf6ef73b10fe0ca3316a0c26225638d910e70e23d47066f449da", + "0x278e6398f59217d57036a6cfa975707ae635eddd5ae2e4af6ee3c607c865e1e1", + "0x820fe7b52e815e8f329ba451f77c4336301868746f6076e02b2c51e1fad19a39", + "0xaf1638f325ee8d2122bee300b11c38ebf116f40f34e061603707de63b9f46bcb", + "0xe99e51287f8fcffe9228f1c0d3ef633b1522ac5a9a73f24ad422502ef5302ea5", + "0x07eee987723fb574dd7d4d12e7f0e9bb025f424f27a04687c9351827abf6800e", + "0x28ee0d14390f77eac234fad79044a80fbc75e717afb66fb494b4a101ecbc18bb", + "0x78384e5c21137b0711bf3a8d956cc5fb9f8d13a1a025eb0132622c671e6146b4", + "0x5eb2f31a180acef343cbd40c6bacaba3071223813364cbc4595c64f784627d79", + "0xb2c15ef092e80561e6b493d038229f96adc189ee20f85a6df5b2f66929172f62", + "0x799249d65599087643bae9ad072b5773744fe89dfa54c60ee2921f113614bfbb", + "0x84573c91f42d65aecea6d53df3d3ab9382d67a9482fba1034ac2c92c24e50dc2", + "0xae23b4c531f8a78cd945f4a6743a6953a19eb0fd1ba02a77d709723874da5b2c", + "0xa8fb72ea723da1687c34e428854201ddc38346bfb0094d49b37c06aa67174c73", + "0x7b6cf1cc875caa46110dde58599af4a01f4ebfa7fa70c662ce900ab5699e3b0b", + "0xf80dc64cb1dfde3c2b7c58710898b331639ea7832b52d3893e98e88191334092", + "0x87a3fbb76219417cb2be90f34fea6fe959080a725796e17c9e9064af22dc4f10", + "0x97537e9c7ff783944bc0c7c7f7e633cb9eb897f67747af5ec8f671fdd96a7179", + "0x809105216c62e45c62b08d620925092e93188c1ec9bc6adfcc5c7d56c89a233c", + "0x77dbd71e583bcd7f7f470e168b78e81f5e79c29ddd7bc377cd77acc36207c0f3", + "0x321ddd5224debd9c854153499040fc5fbe9f1191a9bf8b9b3655c94119f7a262", + "0x47648b8fd565e64b16a93d66d06af5c7a5377211d31fb61cd933c31b58e51684", + "0xca9965f503e59040e6d6111f8d77babcbc905ad66a8b153c47fa6d59561f230e", + "0x6029850431fa3e98f70755076aa907c36c15af8b943a652a158e3772ff20d3dd", + "0xfc7882b43c73fc65c54cd22373e4241409bcda59531d4f9acf8fac9cd07107a8", + "0x9fbce07e726eba063fc48a55b1cda2a8a51048ff53bd5840900f92d8695b9419", + "0x28baadf956d827b96f9495ef7bde353904fd67c2b61bfdb2074fc50c90d4358f", + "0xb2a5026deff064094af0ad164937fa5eb890a4eca4f478310d2570d42f7cfec9", + "0x4cc791b366a3b37403942ece356fa3445adfef327e296e7f41e7b9ddc707fe11", + "0x70cbc2bc2cb45ddc0fc6200b3599ae2a06260ddf5246134efa9becb5d317dcab", + "0x3a446f50bff80ea31aeb753fd7e6ca27bfad9694cda140cf1536e628ce3e8998", + "0x67af8182f32bd8932bcd74187552e82bc77b5c0666ae5fbda871c9f862dc5b34", + "0x3e2a50d2a90cca3c4de150ce1a24f6dae760428a10f8a67911f9a3305a690532", + "0x701cff54b3f6ce1ff3552ef1a324eb815bcb9d986c5752db7e6e330f91243516", + "0x6a106c969c3064af106deca0783e8367bf4b07bd6c2f3fb77a48e7a08d1085c8", + "0x6fd0c2a0e9f4a9e0741b24f3deeb58c52b9d4ad4b2987e7ccf29dd8437033f8e", + "0xd325740246f29f8a419fe7d0f85a6f58534761141072f50e038a9996da92190e", + "0xd7231dc5898a866bfa8e54cbe41f9f4c2b9ac5a75a66617752f1e3b3a7de44e9", + "0xfe7cee6c7c1fbd442d2328a358b5e9dcbfa237087d4a78b5ae15e91b5c22904e", + "0x1bd61ed8e2d16dffd37ae86d854459d0fc61c674e89c1410a7ed5540f164d86f", + "0xfac8d4f21bd94cce0fc85b1e2c011ba80f8492503631444a0330bca395fe5f19", + "0x6d83ca48bdb2b20b940fa5a84a12e18cf73fecd8380edab5a15f9d6822181c3b", + "0xc25d554f65f874554867645e9460f3c45b25e2a519df986df7f2aa33484922f5", + "0x93a0c74f5d749101956bb2c14abb58c37d8e478cbc768bc7a3ba0d76ccd8ceaf", + "0xbfb7772b18056a844be208c5b8aee0b69897916ebb231b3982e6e3c1ea8bfe1c", + "0xc3e636051916f70739edddb6eb3bde258c56e571e9994e70b46f9056f977a207", + "0x9b20e59c5422be338ab6c7a27c7eeea1ffa1e0bfa3ee2cef10a2ea54060a0018", + "0xe6e8c0cb4a3edc1cce08bcb6866655a31434dff6f8c959417f401e468f9d1b1d", + "0x4fa9d5485898f2c2a5d520cc8b91e475ff8296c8b6bd4ae96f9362b3afb5b00d", + "0xefe1759f05ead11e6bd6064f4bc6784534676b4262f5c397d15b836d221acbb2", + "0xcea6249adf15c8f509536a4b54798326856b5d0d206af7396b0d70b0ef7e66ca", + "0xa66363eef4b66866e5328df8096141465e46d204ce77f359f07f53800a8c82d3", + "0x028911be339cf2b4ee7ba9b5d6239d32cbe0acefed936a29283e16f6581db211", + "0x188ffd2e33a7e5abf2a604bb38c6ed0098bef7e4ecd639bddacf331f78f34b84", + "0x7d322bfe46e9960bc6a2e672e5ac2ec7e33dc580dc2a4749ee94a3f24d2216a3", + "0x0fa05a516a6d91e7950fec8a2209703e04e0fd145067885c5ead5f8319990451", + "0xd86ddb7cd003014a31fb33cd6245e499ec2c85d90a8b461d6437e4aa06a62253", + "0xbed18e798a3645494678fd89a2b25d28fd916df3115198e29bf5900e3b76195a", + "0x83227ffa417468435473f0bea79e49192a35509f57b1790533c79d36ed75dfc3", + "0x7d1486f0035edd3203ec8613744bddfa2269cd2e871965e509a29a724db95e3b", + "0x271b10d280fd7384787d56898260ed3dd17aa7db67df636e4facd6616dcdea5e", + "0xa927c7d995cbc37ad34eda0cb71ecebe6fc3da07eecda92145a4dbd3cf5ce39e", + "0x7b95ec5ae5672b2f779b860272719059a81ac9c0c34aacc5d2b4bf12573d89df", + "0x6d402a683afa2c596c4cc675bfbc4879fe80a322e0f925fbfe6c7d59dc416154", + "0xd6dca466a6b561d6f1d81bcb5e703f9b71214fdc8afe670483d77ea0e985d9bb", + "0x13b557c124622104a4b8a48ce25d5dbec0306321715d339539bbbfcb98a7c274", + "0x63ce5c1d43c9312221fbb07f63823f29d2cbcfaa9b7dd971be4639044f76cbc2", + "0x9d1d5aae239a895bd7aec82d129126fb584720ed6dc3f6fb00eb8df9758532a3", + "0x07205c35e8c1e27d61d4afccc23bcd6d5786ca3a560c778ba2c82b61b9ce9f91", + "0x12afa2617831c51c1a698694a54173023f2242e47fb0203dc18125140171f760", + "0x949ab19fb6f276ef44709eb9f8d873b96328226c881fa0017d0d434386e30d85", + "0xf1e1f00d08c6d505ca621184628f993d933ffbff394fa6ef9a3c2a6ef4ad150c", + "0x5449ce79065784aa4e3cc0a32cba7875ef22d29f869c0160aa53cf8cd1133102", + "0x1b496c29a67d6c177d8793dfc3795807bd00204d60c4c7537a3ea9f62660329c", + "0x4697496b52e639751f873e10fbf103e42794c5a97cfb051079a7f3354005fa57", + "0x2edb4136fc923dab15a88868fda3f38b8afb79db7023b6bde679d400c064d9e6", + "0x64650fe3025087b070f4c3669093285a642d460b18a8c19a306a693b49886583", + "0x8c4cc20a2fbe5fd383e6e0408350c8f8b8d8790ce62e479016880fd43ca6fb96", + "0xb0a91133979ccff106bff1fcde10de4e54a6e428e1412f9236fe5eee07be9658", + "0x5b61853c8c4118b3ffd4f61216710d293fbed3f5736a8388c18e765176bbfaef", + "0x9313de5ec776f76c1777e509dd15e66e6fc413967426fcff5781967a876f6188", + "0x70614573c228a663fac999896c3079a29a2b48c728e1ce54f1a48bf549a5d033", + "0x4112941f347387f51c57158c0a5b4530e2afa2cd1577513baa4ae0b74854171f", + "0x34409456e2fba9babb7065c9518327fc00c1d10f6024e98045133489f6952f7c", + "0xba6895e9a5e397667c4a861b0f289ea61817aa0f312be7a7ac5cd6fdc394240a", + "0xf25f12ec277500ae0124067ffbb4c7dd520cdf48869db56e3a1a82a9d51f8d6d", + "0xf49c0bab019bda83ca2ddb7802dd8ff3ced702fb1c8c46ccb44804b5f12e3377", + "0xd0b63b315358aed04701e42549b5055e6539fb25614189f449e21914c281360f", + "0x75d4e1da3f086ca9860c218a86d6adcc2be4cffc08fbfcd78f137c68e7ddea83", + "0x8f5285af1fef919fe9099a5a118791520a253ff6ae9b3eff628a2519ae5e1283", + "0x6cd4909638fe92221b1b6b11697bd63dc7132f23b069c5de8274372d6ec69495", + "0xf8e40cc20fc8ee05f52e02735c691765a6d1964d218394c89c8a618635465f8c", + "0x87d98acf1eaac161dc1be1f51ff0714d75a0536e183e4c61139ba4bb574d2a32", + "0x062cabfe29edf6f17427f94c356ef39687699038188be61c5ec1df9fde77b92d", + "0xd1ea596692d44aa7cea68e31efb7b86afbdfb53bfa9f425ce27e8ce5d1a8dcf5", + "0xc53a1be4a3774eb9fa1507d7c75c25799ee0588ff6f85eb8c8cec09e17e92215", + "0xc82cb6e3cc40880e07695e4521c22806955358a6f98d81f0857c29075d5c2118", + "0xf6d4b6a848c4ec8985fa007ff445219ab31464c2152bec5b63f851cc0b07ec61", + "0xfc82c81e5619784129316f2d88eccc2602f033cd0343c9f278dbee3bbba8e138", + "0x8c311facaed62a05b26358f00bc59841e784ff03fe42df622e4d82604de0d372", + "0x258939f810b941fa296de6bb54d9c825d28a12a9b3173ef4048f63913a9f08ac", + "0x5e8d7deaa931208d6fe8001cab18413918b961df60e57c08c923a869c106b8c5", + "0x05d7105a46b3c3d0f2a3a77db465cfe2cf1bd426bd6a8b0b16780f9de5bc93e8", + "0x46d9c91db84cee5dccec93efd70d8fd91e4edaf5fedcd6099f51c7cadfd1b2da", + "0x598c6f01b6d83b3e856d8176c541646f227558d6712fed2bc5941f4194a5d5cf", + "0x6b5e74b4ae824dd5d2c6dc2b9155186f213fddb26738fb396e14445e75789a36", + "0x60d4e10b17b346c67f51941d9e3b860e571f17cd5f36caaa8e2c42c3ff8bf752", + "0x6966dfb0f2453f92fa80645245545d5a86cd2fb743e3a90bca66eef71f47de17", + "0xe9394cc20346c8225e54ab17d26c78895ca12a2f4fddd3af0903e840aed84594", + "0x009224595f8896d4bf7cad44613193b6bff91d008cc74d282b78dcb3c698b506", + "0xe993cf4d235dbcf657a2332e32fc48349106a9339b835bad6c6eceb6df7bd3b3", + "0xbdc78981829f03d6a0820dba0927952aa210a499f29f623a919cf4106b4ff948", + "0x94a793e75f2ddaa158eeee9d24dbbe29b312c67cc3871fe6a72d4698d0a27dea", + "0x657db426923cb8d31626f7b99f66db995df1274d1bba1139362f67acd8c22e6d", + "0x9bb97f97656abaf674b73668734958267f4ed9e03638f860c6af3deec9aa70db", + "0xde2251b267a61d2bf54e656f117831c2bdf5c5f42bf93fa9c604153d6cbe36e4", + "0xce4a9ec1a730802834923b5d511709da7dbcb9691844a677737d11ae232c86c0", + "0xbb31c5ae0aa37ac39b75b3c34cf0429ac67e2b1b6b50713605a07c5af7fa1074", + "0xd085460a4841acd3d72a41d3598b3e235d00e6f1e9ca88bf55d84963c5e962de", + "0x10ea6d9fc0b30c6d2bb0846582238abe181f6ac05e2fb420df9678fa634f93a8", + "0x517c87cdaaa93ea70dae8ea051ca01e15bc49fe90ad79b560572689f9f6ab4e7", + "0xb35109747e09eeefe73a4f5814e048c8a3e89b65d51185ac144a8fb588db772e", + "0xfee2c74ee59d1282d9a3629117054c631d09fbce08744e218383e158105a8575", + "0xbf29e1cd49bf0bf1e1931a2a8efd75cfe5dd9f0a14b96c7e52205006a8fa621c", + "0xe70f8683fd2cd421c4e788db96e7ee95f4abef9ab8f3a18261207413834ecadd", + "0xa708a972087a893e027dd951e28a1b57126c97e14d36e37e869ac2dc702b6dc9", + "0x75b4c9863894757febf09e1cbc38cc8da4f762ce481581c27419b84dde361127", + "0x14e28020d3783b87e9170ae5e0b8e85e27d56b88cd93065972bf7a8e5096b343", + "0x47df7f0a712141411702efbf72fb52670413de94dede439dc7ea2d66ba1bc83d", + "0x36b4e07fdc9a8837e020c4ae14a8fcee4fed858b7732c34bc26a5e9a34624f17", + "0x2ad674c7f1adc8c72848169ed0ec595cc2a4ffd7d10cd6b800d6596cf89f3dc2", + "0x72073ed218dd1bf04472a889c0d98bb002f0b221ee2fff215e2be12f3b806d45", + "0x645feb9303e680bcc20b606316665eabc6c444418341b643e54c3f955e86791c", + "0x088f6cb441ee7d4078f7e67177f49e88d08926f1519b85400fafa37670d4fcd2", + "0xbc67086e77ae1584704bdfaa78c18937d812a9aad4349b40747f1c4596b8974b", + "0x540d549a711552c657178e1b2fd540cbc225c8b3fd5473c11f4345d7da820aa4", + "0x5a473b34c7acc72aa9b6c90ddf18a1d1d8e940aa8c1f24b2c7330fc7787e804e", + "0x78da82e0f8ad89a8a26a8bc7462967474c8368f73c0ac7e5a2b99b8f62860838", + "0x13ad29e57ac3d40b8d5d484baecc085198aaada5279e64b66ba4865125983187", + "0x92b974405b71bd61416fd5f344243c6812fe2db94b75deb1a33e5c1dfbd4ffd5", + "0x7ccb62dd5f0e3fc20bf1fe472cb193d3e86811d14c0fa2699489973295afb2fb", + "0x61d89d34ad1b42fadf9e3da73af1693a8845b2460dc4efc97d6475d2fe3a2489", + "0x9e5345bb6bca947d39d526e360504afe072967c2b020bf70cd15391b6c440a6a", + "0xf06c6b4ef88c3af3f7c684d36167330107b89caa48a10b198e179a4d9c8d98f0", + "0xf9dc3d030d0aebfa692b1fea7ffdb2f22071f7dc1bd82bc57ae1db8e5e90ec46", + "0x5af585d1c5b1287870944e1bcaf31b7226dacab29fc30f45ac4313ee3321becb", + "0x5657a4e48472e4a4954758d3a61754d94efac6b570da00c7c3287244b7727b53", + "0x7b044a31225c0c1d2354dece71be215817134e744acabf92f07957a8eb3c2893", + "0x4fc4de886344b3af489c486d82ae1913515cf771d7b0c1ddb5f7899fa3e5be6a", + "0x3f65d2eaccd463cff99b9ad172ae517319eacc57086f16c5a551702b744c52fa", + "0xe01d419aeee95e2736d9baab825e54eb57671fdce565a27a4d72b0d38f5d7a8a", + "0xc5d37e94a6df3fbfb049e6ccb509aae3c7325cd514823f18436c2bd16eee9553", + "0xb5667a33f4f572ecc2b300adb89b7f5d47ac65b48710a02147b83cf31741fa38", + "0xf7258234481781f96822ffb492e621a172e3582c0133b8ede43cf9d8932c6fc9", + "0xc90496391cc6eb25dfb90fba5f279e45fa996c4f2b8ca3318233342668ee5c86", + "0xeebaac62317c34794b3fd32572c862b3b9c282c5734eef3e09a55914bfc974a1", + "0xd4f191901e572ba2a7480f6d717e4370cab2c4582b62ec8e29e3ed1a1d7bedc0", + "0xb7d88146d212c3c9ac3d402b39dcb04544ffc2ee60bf62855b4a11179ee8e59a", + "0x427b451b14ab4ccfde78792b1b69041de7b3d55038e3461b7dae05e66e3d0329", + "0x0768b26a779caada3212a7a8bd664e818091b35427551c46ced73661ad910ea3", + "0x93ff858252ac8f0856f3ad1b1a4fbe6ac6786cb69bf84246e66c45a424190a3e", + "0xd05ee3fc0deddb440da6dfa90abec85a74618467b48a25f7bfc53dc9414d8643", + "0x94b8621ea8d4be73b8f5482b90f9b5cd7e275d15b109c105ec52721a362a0ca7", + "0xf168b6d4324fb870008c4e572f2d47893d75f32012cd1e1ee3c5198e556fa5cc", + "0x234a363d1183f9a324d476d0e7edac0defae953afb6ea17183e999e1bf73ee7e", + "0x3abc506bd1fda1911dc0f7ab19d2323de18b93268b4038bfd8c6e6a0096ec3bd", + "0x1f4462f69af994174a26e7f0343d402f9cc8beaff3e9ab8be20370073e50026c", + "0x648e61c5bef14d573da05685ab46289e9c76978ef6bd4ce489926de98c73088e", + "0x12867d65de9cc45b587c0aac3f345122cadb37838b5772dfd0cd9d4e6d105a8f", + "0x976f62f6babcbf1cab85c37306cc2de28c6b400a5c64fc328095c62bbde0519e", + "0x4536cd98f8f16a8f92ca2b553b79362df066df47e7e821705d767177dc3466fc", + "0x022bae9a428781e3cd82d3de9bd814903e1b8be8a42922e9c3dd33e5cdbe514d", + "0xaa037a2abebeca44ffd3ba55ade9f459563cc74d86abc4ceed3e02c5b1603408", + "0xe314fc1842bf3fa54fca47fc0b5bbdcc6351673b62e869bb29c82274fdc7aba2", + "0x2dc847e811bdc4fb528e76523247d3454382e372e2407fab61a77961170e8d19", + "0xe89bd05c0fa5ad213ac8ab07382258655f99c88255f3a70f187d12161cbfd0ab", + "0x8070a025218229edb139f5b5652a1e9049ce9e642d2e5ed247099b74d9784e6f", + "0x077229382ff78334e806755bda0b08a8570756790dc6aaf5fa7e085f220cb751", + "0x8de5050f5c9b1ba942f9d85401bb0f5ef7286e084f6eec23ac1d5c1315558c98", + "0xe16f190937b41c689f4e5e4a813df2df1473906588b82fac24c084d14a26666e", + "0xfae492fed16c4f803481bd3bc58d9b27c3b648de2e6b8c4464ea71173f3d4833", + "0x024e7a6be79fce101b37f1150ad07bd76da2db7b309415747a63876873e85f8f", + "0x1f9d1955885655e509a800bce861fb9767038f4c11cb30c68bf83b226a4bdaf1", + "0x70c4a1015c8355878e66972702e326ffbfab5204471481fd7a68b12094f6fcc8", + "0x1030ed95e635022b8a2e4ebc03efb370a7b78a918e1487b686fc056e07d5a080", + "0xe8f0cb64f6f0efc000ce42c0809474d760fd6bb962718afac734eeb5d42693b1", + "0x144273df4fcb58a472cfee3a45a7ccbc9a458efc50e10a130a1311e9b0ff99d6", + "0x33d2eba1379cd19b6c14e5a8651629d8b34879e9f43f2ecf1a5934d42d4605c9", + "0xb682c80ba8f758c8706f7d2a13a0bf28b5620b765d0f33dc37e24117c31c23dd", + "0x8970aecaf6a65e05961c39eeeed7cdab0bbb81068914acf37c6f5ce61d0e1b51", + "0xd0837647865e56bdfb5e80cab83fefdeaa0d4ced82d605f6462b6cda68d853aa", + "0x6899870cb66348582505c664cb770a1f44e6e8e5065efb74816c2cc36ba52c94", + "0xe6962f84af749e2d8a3007656f3aefabe34afaf82beb02387c89a3105de19ff8", + "0xf045e562f29181fd07b8ec6775968bebfab488e18c4b57f5a04c20dec6a71faf", + "0xbb3ad94e4cca84c06dc35628df96bf77ab582a2fa43f8492e15c9dd1dc9481b8", + "0xfeafc79fc75ad1e1f82bceedccd96ede93d4722de76d36327ab088bf277c61eb", + "0xbdd920d4a8c73c51f89e209c1c2b7e9daa4ae765e371c621b55fefaa4523d2fc", + "0xe8bc763d40b854576374231703f004695c2312caf5b64074f46b0e4d01f86e29", + "0x3ca15502ab3b3aa72c8ff76f064c5d186dfb1922381c2bc78f81919e338f7105", + "0x2054fbb5ad3a654b530c7192fd1380caf863a7221c14cf7245518df6f8313fe5", + "0x21b74ba4faf241b9d95bcffb2e55f418dd7981d9918dc6e2f41a834c97fcbd2b", + "0xe0df3e4e8c1244cf0f69acfd0c3e2e9bee002b13fa800ec01cda684464ad52ff", + "0x62a5d611466cd9d341d4a1072722dd0fb8c48764e0b2fb33bcec4e5b133d456a", + "0xbcfce2f6b39dbcd4fc82ac957b4ca875a40a256dfab77529617625ecbeed9520", + "0x5055d6614f7dbcd3daaf00f029c586a8648722051b5eb4d33f7468ff736b60a1", + "0x479575e276ece7923c73a5faddf0574e2f57694acad9e9856fbbfc7cdee5e9ef", + "0xc521d7efa993312132e3fe4f709d0b85e2dc81ada5739ee09096924d5d7ff202", + "0x7a4d852c71de72cc377f217e444413a199c51fa47ec9feffde68ac91236bd47f", + "0x8882568452e918e41b2a2c3dfd1291f1903d4592cd8c76a1fc37df810811cc0b", + "0xbd35ae4f8f5556734f7dad262968ab7d52d54b0fe0369494b67f0590ba4905b5", + "0x0846b6bb261952f1069437712c956ec428b6391027c2649f2b3b3dd04abb7c7f", + "0x1cdf966c4b20947b80e2983f8f5da2169a0553a71e7bea962500ec93053d0e96", + "0x204b9c1013adcb0b7efedb1b4f04474d7aa958b63f2c20793d748798215a2de6", + "0xfbefa8466ff5b628440559d97594305162c538d56bca669985f61328b2a767dd", + "0x3b7e7a0a7a1a54d3685fc4122f8d402d054ec7aeb89895ade93d0d57de79e39b", + "0x5706656679df8a294703c5f50fdd0467972a3dff30b40069a353c0cce6eb4929", + "0xb631981eb5ae9f4c1d235392025317249e02a26630ce2c37c7d6b267f9b044fb", + "0x445c14bdefbf7f2698158bd2b19c2d694af7c5205b16d35f585f3a76fc5b14b7", + "0x48f314188d04b354657abd25fea1d0a7b3aa7b2596a49dceda7c6320aa4080fc", + "0x95844d29f20f9d9dbae08b6c079e8672628284310801fd984bc7c36dc2ec98a5", + "0xaef8c53eb0abd0a3fee6132128b9d5a583b597de602b6e40465d33044cd2320a", + "0x933ea29d046d4511ac9916a2676297ae7e5fa7e07139fc615e1a40e7ec1b5ce8", + "0x0ea78b53eec3d14925dcca06c349b9625a6d0269a28ef49cfe70d37dae6e80b0", + "0xdbc4f1aeac567f6140dccabe8743c709c11854f8091a93663b52a1ca6a688f9c", + "0x7ecff054fb153832e2bcc0098b1f19921b5ccaf065ee59426a38c2492f0da68f", + "0xa39f92b64f2f47e1f3314c673a00151292475d4e2e25f9262b50faef93050806", + "0x111a17f88f25ab14f434bcb452d12bbc8e96941b812a817d0f64d436a1cbda11", + "0xa911a4d46554fd57701299ec9c26352516d855a8b3606b961ef37ab5b0c8cee3", + "0x40e3ccea110d8773839dfd4545358438b1d025352a8ba4e6533b1b4d5940b9ec", + "0xbba5490e81f41e8614c1a47e2fc60ecb77e1eb25fa55ceaffb591c12dd33c3fb", + "0x5e123e18021d4ecc7235cf4252eb8197ac2751424316e808e8de7ed589b924d5", + "0xd5ebe862a333576a194275375bce6e2423eb7e836f0add7b32421f4c080c4ef6", + "0xba1b1e438fe040e6f8e550d16a2589a1530fdfc4a82cdc29c18710933559924f", + "0x71ae1e36cd5754bf7bd021c9f570362167ae61fee9f7041c25f802fdfcb5bcc5", + "0xc765351f18efd9c1a5b4dc1fe6d5be2f69257c8d5752f3709ac897494db5fa0a", + "0xad552837c8d8cab6865676c228d8f43cfae21676314618d0b06d3360ba6a8150", + "0xc00c71aba0799ba626dc6c43e4abaf9721810a4db916a0cf4384c558d0d0ee18", + "0xf8786b8c5d7afa707fba0ac716e0a07ef9f1fcfb71fb768398b2f21964d1a7e6", + "0x012ed505beab30dad9c555f5962704d6d222c12f6f2fb5cff9086b0c1502d29d", + "0x2a07b6836a89a3c4ba65f024e7cbd67b7bf707dcbb2978e951d998acbc136f40", + "0x93d244cfc5093945a72d09bca5dce235047461e8563876126d826ed21983cd96", + "0x27967d402d0b7b9b319931b544f9c2f3959bde8e33dc376db2e0528ccb3aa9f2", + "0xd607470e11cc18218d3ae51761d521c76826cc39cabeec799bbb0b650c0b1093", + "0xa73ae252f1d6c1c94eb0fba24aaf8852639d9fc6813f71ff8741de1bd8a7850b", + "0x5d760bc2278f6569fe69b9308d3ce809db539926335fcbac83480e7d52ce5489", + "0x0c1886c77a88ae3b5aa2301477cf5875489b7be413ca48903b348867835d4fd3", + "0x4488fb9a84e158320f5b5a22c81a5e4236029d8978342e70941929bc608742d9", + "0x65219544e873e709279f4f0c20d891f03b49ac2c2eaebf2bcabd25f4c8f2c834", + "0x9f72e7bf24b8c3cff6366af9e594a90cbd0903e6743027db3bcdd3b613dcea44", + "0x056bae9e337475691342a63c72c8920d4be3fd5aebc2340d8255fd57e8c3a51a", + "0x650839d0b4fff0ddc4c250574876e63398b0a462066f09cc77a7f9bc512063fe", + "0x37a92a9c09b903cb0de256a4666de26072ce309dfa0cdc4156e61480535e0018", + "0x8dee341d341b57c3dec015fa4f31ca0e31f8aef6a3fb9ea9fc0fc1c3199f1a84", + "0x7db0dfeb997c94398eae48a3bdd7be698232e4b6604bae0986d415a933e0211e", + "0x9a120e184f1a02089997e16f8e374356d34fbc017db3918640753670eca8a3c4", + "0x456eba8d2bb3d8a6b2c0d9e7a1959f5fac5ba013f270be2be3e16a261c9824cf", + "0x405957163c5357c2c1486606c8c9a5f4756ee4fca35b25a93bde8d098f149015", + "0x22e72c8f5bdb97cd89612e0f91839826262352a2435920740a3d741a544ebc2d", + "0x710c2dc403ca9d93ecf1fbdea45892c055970df5c95ecdc70badf3f4a9f343af", + "0xb351228173ec2aba09913ffd1c431d88b1aab39654489090a8fe3c4748f3b3d1", + "0x10aefb50b072baa1593f532f1842aa690430a31d4988de914ca27bee4a4bd1f6", + "0xc522a26860d734c7387a931cb0dd56816f4ae63ca7d966d9ad701630c22fb318", + "0xd47e86c791154035551339337bd722d017e08bdeed1f2a5ebfc62c0d9a02b692", + "0x3e439474c97f09d94e330c0652835a1ea2b1654df172177322a2b3d0dc8f8af9", + "0x790977c179b02c2f0666827ed55231b6d581c592eaedfd8500f1a86f8ad71d82", + "0xc97452cc3d9147bc99825113b9e2758c30e4ceefbc26d18652c9f6a6e0b41e02", + "0xd5430bcdef47ed32f0b1d3a270defbfb549f3895409b7ebe69556c9dadccfb18", + "0x7847ef3e9829b163b1fdeb27854046eb4c4e36a744db4ccd1e0aac1f974d5a93", + "0x267ebfe15f94445a88f6560f32b4d626ee2dee5d21e239727ba5b3e4e3047d3f", + "0x7b17f7f8449a7296108b68e606159a6c21da755718c07d3b9be73553661a5687", + "0x17407acabece921c3e0eae13b9cb1814398127615bd76a43e4542ec7c29acadd", + "0x8562b220a2d4104e37eb5713cfea3efbf5d62c0c2e1e1fc4ec95a82caf661371", + "0x34957f90e341fe2839e05c2c784e63c4155ee59524b2f72f07a30f4f6fc4d620", + "0xcb041c4c78bf42e4c27ed4443266a1c7edff03bc05b84eb0c9178d1b8ec570e5", + "0x921b8c8b670788ca847fdd53ebceb7d4668a174ac1a69351ca9135718528ebe2", + "0xbdee1c5dd0d31b946cb689b3502487ed189b000255b290b90de31229a0304bd3", + "0x8c46606d2cca8d421e6495e74e568a17cac64b008a8f7509351561ba7c2b6b12", + "0xd9b76d8e96a695c8429bb925410e33520947e51580cd7a8d7ad84560f7a60d0c", + "0x732999f1be313080c13c9683e76e4082eb7af3ab42b690f994838f7b87a17900", + "0xc0500a2a5ad8525558b269256c453624086f68df6ae813e59aa37e5395e54a6c", + "0x643e528793cf11e7d8555b8e16bf68912c2e867e9d6fa17e5d945152c99c1388", + "0x4bf327078e66324c1b7ace011b535076eac160667af01f6eef41757ed480c355", + "0x96e5f5cb3a77ca3c26430935e8f474d595cb31d4d52e2bef85336cab5dccb3db", + "0xa55c997b3047b818142fe555ccfa778b2c58883ceb371b288c5f1bada61f4a68", + "0x9815b3b0818b5082185b3fe0358e5a1def6a4403d96759771ad857d713294049", + "0xa4638dcb763cc35e7f16863dd715ba0b1b9f01b65014d108df6e62d3d05d4a39", + "0xae401f9800daad4cc6ea37a52ab7787be32c4c697a2331069475d2f5b723ff99", + "0x42ddd8112aea6a8d33e784f41e6adbd5021a9d91f1fbe1e0ba0409ed9adca37c", + "0x0359f459d319aa31e9d60de5db1fa5d94b7c0f0e28374ed637117bac1925dc77", + "0x2cb1db0facc4fee840f38b030b4037bf862753719f21abe270321542ca0864a7", + "0x2d7c6211c6ad52e1585068b6d3940a7d570f1ce376b123dc373a1d26af0405df", + "0xb95465176f9c0669828ba37525f714894c5354b1f21d760319cde840ccfeaad3", + "0xe08c12680f7053e738d18b83b8d26a395f22a586294765e492e371943af49119", + "0x7b1c57a0b461b6b4ceff4a6b66bfafa95acd467da340ef6a88b07c58b5512254", + "0x3081ca2080acbc4db0e3884ffbf36b4d437970ae17d31c372945e075b194080f", + "0x50b9fceb8f1d48a1954cd830ecf630086fe11ea5c4a20c2db72b5b47ab60d5e6", + "0x16b17dc3ed590471c7c7d3b2fefdac7a8484449331b1c7a96903a07a4c517268", + "0xa7ba5d7aa2d821608bbc9cb44633dcb72f6a714bf897d774bdc728dc79eb6ff4", + "0x87e98a3221044c7d3977cf4062e209a8ed6175fe1cd03c64b1fbdca34953c4ae", + "0x4d78042e708b83cb048deb2b44d71f1e636b85118912448b04279f0d88af049a", + "0x630ef03eaad63134b53e07a4c375656a9daf500a8b7dcccbcf9c26dd5f38494c", + "0xe7ecd57e9b96ff945fdf97eb38df6636c5453a17781a082835676a7a26778a49", + "0x4b0d553d5353dadda3d5e427aeb1cd2f130903568ec5dd82cc63fe22d20598eb", + "0xa6274869b3ad052cf562b4c15cda78d826e16d701f6884ac159f999907925a7e", + "0xc348941b078350082713e423b920dc4b6e3d99ca01b71c8fa45ecfe703d0b6dc", + "0xc8467b78c4432b16e2dcb2868758f88f1f3028818e109e340a0aebf2020670ba", + "0x449a662bf3d7b09905ece9e036fa7c6c01fabd0cc4f70e5ba53549fd5d93a2f2", + "0x42edd92cfde80d9ee6480a2dba1c9fb5d61944d61ba4514b905e95bd58048066", + "0x58c9b2fb9b5c3abae87bea4f28b71cca49c7b5fda94d10752701945c01f776d0", + "0x62d44111c985c15f28083fb89bf714e84785f52af0a08325095fcd9c07bed3db", + "0x9cd7eca716a2c9d2590fc908b4c09791d773bc57adc1ba8d37b3c1b36958e5ff", + "0xe85900e438869c9b99bb7c014207f0929e2ab078b252e70ab4104a613dbbc461", + "0xb2925b74b2764fe9cbff8ccf9902b1db0b824e55ec822e4e1fd2df4b2d479657", + "0x2e42da568de9c78ea3484a674ec5a27fa56d7bf1ee4f61cdb4412790fad11290", + "0xe4150a2b58d122c3f0c17c2fbc0d1d28f15f9e642369f936406d96cd0fa81ea2", + "0x618c58c125d0000f2496f170497af027e29de095867539ad146c764299c66188", + "0xf9b552eb22c6800e11b883c6ff15ed75fcb1bb2f56e6c2346910e1c72ff5914b", + "0x76a99d572d73c8ed82ca21e1346b1708fd558d3d5da3ff23c33f194c2ef1f358", + "0x476cdfbd8703cc2591c56f4c853e710908c22b59f023a22b7cbfd7d5f5cb4255", + "0xdefc350def5ee2fea9e3e1564c1de22abc530958b8edab3dd0ad92fb3d465ee2", + "0xeaec47616ca6ae1b143561655fcfb4f5091d2e300b13a4f06844efc3146c90a4", + "0x4cbb6ff82ca334da5c0d499df7aaff7d1e157d2c6b2ecc864b9c25f77308f27c", + "0x732cbf15d1cd2545cb4dc6393df264f87b1b1e88a3b89192dfff437e8966a8a7", + "0xc1831f76fc838578601b9f84aef6b4ba102b2feaad4fbd24daf37f4300db0f8a", + "0x1099c2ab57b9dc1b5a351384f7253b0f6764ee12a426c61f6a1f69953b3a7115", + "0x8969ad018aceb166ece064cf7efd15c1d8d3eaca83c92dea4d96aa6d9e446ad0", + "0xa1ed0c09b0010454debfe03275bc2478188c6c13033b90165c73106bb4d81abb", + "0xabfa1bbe7fdd6451f39f017fd95145b86fa3fadbe40668cb1284360be3ecdbc2", + "0x38c8d6191d73a6419906cb7ab355f369c97936aa5ae6101966e5627b75299965", + "0x34222fd3e477075c281a2abb57c38601ad4d71761d09a204f9749b283a640b6d", + "0x534fd5a93954e04eca2a653d48869bad9d2944a068536710dfcb7ecc3602ca94", + "0x0f49c78401ed0ced46435d1ee1da4eb66135d8806dd9705227daa14e3f4ee3ec", + "0xa75c22f239bbbd32481e81527e2ba0262e764bdec35620f01b42296669a71d63", + "0x0968b9ec257f5d5e19190af467a57421f9ce35ef46c68daf04be061a8c2d7b47", + "0xca0e0e4620ce1fc1c41b1875f9de623748255dcb3a79acbc174d188d80d94559", + "0x06a3c97785f375cd9090e0f6508cfacb6558858085bd0f8cc0f13cd2b07dcb36", + "0xa863baf6c5344f9c96b34fd583e935c3fca896c7f08099c281a557b3968891c6", + "0xcfc3f1e7cb7b538b291571df652dc30158177d2f0c787dfb9ffd81198e5c925f", + "0xc8fa2b69942751ed58a0c23bf1c11ce250de9814bb2d904e386ee10f1792a98a", + "0x60d309c5e7f61f33c4b0d748a049c28e695dab71e705dbbaaf67047ba1822904", + "0xc3a36b61da4a617a3708e203ea3469fa8500b1d94fedb8e7916d86cc939eda36", + "0x33907e788d9d8cbb1010ff32b3182f20d55355ad5e042256dae2d8190ac04e19", + "0xc636d21b91a5f480ad1f03aa2259c82c1c1802b4ce1e8726ab1efa1eeede7c5f", + "0x73bb36be281ef1484d06e2a9ddb27254313f523d1237c39e10e28750f647eee8", + "0xcd6735f081eb6821bc415a72d9db709ccaf1856f199c39f2f0c23e5e0238ed1d", + "0xf1525613fa1e085fd6c625dc6dd73f877be5665e230511111b885fdd916848e4", + "0x32dd3b2c2210975f98c8cd3a012ee1ff756e06a3a3a75d7719686411653d6311", + "0x771464aef59b23fff8899502d448544a43a1428e2dc7363d91af22b7da1ef4ef", + "0xda36c8557ff4c56bdacb3902256a43b425044c5a9407901098e8941eda4a0650", + "0x87cde1996eb4ae1cf5448bf677b189f7c23554be38673165ac710c1cd721f392", + "0xff58acc909dee24f351a211e0ee81e30731de9223f0206ca9c8e6d182d6f2519", + "0x3f1470c42827959eb0caaf01dff804b5679afd71af496d09c046b4c9dfd326cb", + "0xf445cbad2295df4adfd89ba117a17748f7c75b6880cb43f1f3721e8d4b1b67f8", + "0x6f456dd22815c7391842ce9f44c8ec0a89dec72fc914f171eae2ed6e2cdf98ef", + "0x1ad7c50914541a9df315887681657256c276b59a4dc78dfa5600843285be899b", + "0x1be188dd7806adeeddde79860516d814e4bcff613b4cb66848c29e1574f0b3c8", + "0x202c9d435dbce03d37844f8cd59dc756541366467f20a4343b814dae2c46e3d5", + "0x1de45c9a458b0d14113b0b6521036a52ec80b65892b65476b211d344ee7abaa8", + "0x78d7d7add3d3ea8ec6db2d8c6f320e5ce29d68cd282efa4d57073325ef9026ca", + "0x88f429cc4e2d833b7497248ab3dcb67e7c0d194384827c7aa3c85212211ecef1", + "0x573e95818388b539b58152fd8b0d376e7201e46d609e66b1a47ba2a4ed84f872", + "0x6feedb9d1d65f33ddae318d93ea4bd5b823c50cae8911914eb6f00a1bb53ced6", + "0x9bcc70c3463cb4976e720e9b62de299f745060c133d3cc5dd72a3b2629b95837", + "0xe9da34c7b28205179dbb00e48ca7d3fc0bf2c7995f789604df397ab1d30dce2a", + "0xe831911a6f32ab19773b327340ce03acbc48fad6e61c3e0c22356bd15ef786bc", + "0xc5e946c01f6b8e180bf72c1ba7511407e9967fdfbf11ca505124bcd775d1c60d", + "0xc5be8c00c06b3805476d15afd10b6d05d5867d373480c31f97cd90452680bdc5", + "0xcfad1deb67d2968c607c6329038a61beebb7bdaf7498c9321746149380c5df15", + "0x9f09d431ff210dc2a25e673be34dea73730d90295ee1d9a05d7eea0c46cd9f7c", + "0x76cafd510ebc963ff0e9fc17e641574f22b87833bf9d250e6eda0153a2f9ca7b", + "0xb4d94b1ed4153fcd0ef5ee5669c5c4ebdf340303da819f1cbf11a398ff862f98", + "0xd286c201f33a0dc7420aeb619ed6588c4eb93f07805c6407887e4cb927ae3cfc", + "0x52979c38d54c4df57f1cdd1b2553de674087037af22ab4da6c934bccfb5deb33", + "0x7b208040ab2b7c505dd00cc7e0633588547f04c674a6d11941b9205f1c28b6b1", + "0x4c95d226e34082d547bedbd8eceb35c0cb83ad90d7cab3d956c544f5302ef714", + "0xefe36545bd6639dae2aa5ab353f168d1d508a537894ce123629a92922ec58dee", + "0x956738fe3f510046c775906415e401fd86519984d6adf7172e9bfaa06ccae349", + "0xbcbd6d20e7f77249f48cab6cfb1bfafea3f65c98d932201cae48a2fdd7fa5929", + "0xac5a83b542b2dd57fc1eaa8d4553728baaa0d06a01258d0c19c7eedabac60baa", + "0x9979253a9587191aea603e807428b10cbe0784d2fb53974bfb4e6222f4c6fb44", + "0x1b159d69251aaaaf4ba6ef8c5a968fee2619d1a2902e22fc495105f1c9eaa876", + "0x58514bb43b91a0199e51fd91fc030c55e4fc556df87ace6ccdf9b9ba4b4f5722", + "0xf9447ee786b64028b80cad103ffb27b7fb88b4ac7944689b7a695458e8ee326f", + "0xb950dcd2fbd46985095e92f4eb62a11b15f3e38c522703d971bd068f48989c73", + "0x4f265eb2b08ca0f739c604ddbaef11d89b57e2d62e84093ca85e9e1ee0bcca11", + "0xb34f9dae01c8a67d417ea6307907dcb962899c88626142742ee7a5374664231c", + "0xac5131f9f66970d506ecc86e72119d08c68598c842a447c22f0dd75da8bf6497", + "0x2183fabcbf38ca558378a26e8e5257800707f0f7fa931b33d643af66cc611c7e", + "0xc47f89441809d06329d1cddf009e78ec2f9fdbbfcf167ffd7628676ecde3db32", + "0xe048471219608cc3f9cb973fe12d2041b0cc090d6ff670baed8910ab4e1239d3", + "0x85111ceb8e0db07e448281266f7b86c23e65488571a51fa8f4102c43f9a1c1e0", + "0xe6770767df490c1b6c8eea734132a08304b480dd3139310a5e0bfdbf6458735a", + "0x6e422de2187c096038d9b8f58d556aac3238c3e914d985d57a6b24a7edf15cb6", + "0xf96dc8bb2fb0c343dfdd65569c50ed66ddb2951885ebfaa1360f299872837b4b", + "0x896eb68dcee7533f94185bb3e7adace1ef695de89a6430e84b3d907831e11924", + "0x9a6a028da49d48cceb74e55dfa5cf8423c4034d23cd16775dc7fc05ddaebeb37", + "0xcbcd05fbea591d41a75b711bda3e55bbb72a3876bd42c40548cb7a285867a2ff", + "0x2cbe0e9055a776be8981d735f4d889de8f241cd7ab985c2b890239334c1da220", + "0x24a36abae45836322cfb6067113812edcde87ca94540fc4698992d537bf61e21", + "0xbcf5da427b8b2876ef18773430a7ad691faca805108a90b504efebb6fa99a2af", + "0xa5c8f2aa431c138b9baca0445e790e495c82706e731039ace7088e058e5160c7", + "0x1802d69d1c25deec06c94d2fb6f8fe3fdb8c5dec7b4a8cc7679c768cdb1c15af", + "0x83bdf11a844239d109e4449340789c7f925cd2b7adfdfb7c967b3b3e1e8783d9", + "0x22bb25c43999877f1b389aa2453a998893d2b6d552f4213b066b8331a3d44960", + "0x580e98fb49a113c16e28682e9358fd9117323c9be1f3ab9c7e533a34f80ecb3a", + "0xb82da388845a0c74d9faffdc641854ff480e883e16a48b5123ce0dfd8e698810", + "0xb304397d155a82af43d190d376a084cf6e688fe9c515d8bb88eebb4f7f0c404b", + "0xd9218eb9453464615c94d47acd5105e1a28836312b38dd83bae03259ee2700ff", + "0xd1c93361a8d89e32988c334b2fb29d5ac6d1941add2ee9043212eebbac9ee837", + "0x8ba25e096bc56a551ab5fe5e7a60dd47c2d544d7da6ceca5be5193de68a69f51", + "0xe523515790f8df0a6cd6024d8d00bf7713611cf44df4ec20ef63577d0abec358", + "0x46c2336e09fd3595354facd2af302e10ce5deaa9be0d7407c5af2857e52d6957", + "0xc9e89d2c038937cc4001015eed5b3e51a2addce52b1c931d9ce0e2552dba34fe", + "0x36727638821a473cfb0a7f5882ee0ba5c7600505a98428dca6dd8ea6aa82bc7c", + "0x4d27e44292c6b66c509d359cbc1a36cd2f024362a2137acdbfbc103f6424cbf0", + "0xe587c5bf74c118d4c702857100ca13e3e8199667e2c1a2653abff26871d184bf", + "0x1b5cca44ffe88c41fa2e4bea12c233790ff0b99b2382c1bb5d1fc2216aba7daf", + "0x5a708fe70607c35c0a78f86961c46942c84cddc1a0887a9ef117d4104ff06a80", + "0x3d6576a93e9f5390bb416bf2161440ab276d343ac3da90ad497069b97012bf50", + "0xcbede413263cfe6fb1ccb1319d320a4f85728692d77779e3de57e3e0cac43373", + "0x20b95f75a59e1d2a8b58ac1a35116f4ef993299c4858eed9a3e4d928e422b69c", + "0x1eb9781d4540333fc4be05594a9daac38147c195a099b93607c27eb823b84f50", + "0x945c93387402dfddcadebcd2ae16c37e1bb70bdd85a7ca766b3f45b7a967f219", + "0x8e682bad072782eb145e8afee108fea5860f426807c23521686cfbcc56534b3c", + "0x1dd1209bee1dd92769771282dd5aa78b403892d61f1af2b55771f70ae89912c5", + "0x6a5f8da4533873c8ffe1adc7df57faf6a3483c55151961f1ad1209ea6acabbc3", + "0xaca21601c9a26af334d821d63f8bbad1ba9e59df04ab5fb623a2f0950faba8ee", + "0x3587fbb5aa0e5e3db1c7efe536807ccbc805dcbcd8fe57a5c661652ff62ba70b", + "0xadd2e5674950891f19d5bfe512079030418bc62016351e16b3ff7b5f0ba3ade4", + "0x62a76896e48e5fa91b38dc78a08ee7769cb2965b505994def78c41c13d5a84d0", + "0x9fe4eb33b244fc69dc8dcd671c08a135c5a4b11f3c7a68ffd105b8c8ad323f65", + "0x2075c3616f67051eb16cb8e1349740da3d38c6df2d9d67916164eaa8f046a0f6", + "0xc33e8afe359885afd86b8a34a55332155aacf7e893764aa55f8f6fbfe00e07a2", + "0x70971e1f0aa6f0b869da6fafa987a5068940151cd8125dab12541910854c966e", + "0x669213a6d9f0fd0fe284c253b1592eb9590ad94e76877c8c0caded873326fe4c", + "0x08180583c0f9cb5117504d7abc59850df4685000fa7431ce8ac317b20b95afcc", + "0xf4151388f449e7529fa189f053eed44f27d552822dbc82b5f4b05b36fe1dc856", + "0xbd51a111bcdf945e0abf43ee24cb39809daf23d4f29d911d3168da02d3b57c09", + "0xa39efcbdbb9443939565ff34144c9b16fa511f57203d7391a5a4749f048507c1", + "0x199b30434b43a696c3fc514188391fc0cfad3e2d690d8464babe4478998a8bd7", + "0xda484438c44102a88a9fbc8d1193c06c2d15c30754b273059d646a40282848cc", + "0x62d5604c944e25b631607f70383549f3f33c125f861be49fc0e3212f03bff289", + "0x821f479d71b266cb3af469b5e90f74f5995834dc38193446084aba63d234f670", + "0x95d0d75f1786792d270525b8602b235688b3dd4d67e9bd7e515ba946a5b25a5a", + "0xf6cbc6d4c3fed87bf1d32cb54fc55ed709bdea33ad2246643d1d6468810d91fe", + "0xa6797f2a63f996438d02bd8d5d697de60e2f7b22352b302e357067a83582ba44", + "0x75c76ec8b14250d18bc5360b0edfe2a2964e3e603c2650cb5dbfc51e7f9a994f", + "0x2f71b23bf182f1da5c3f9d8b121dcbb774db047385d8a90ddb8638f7efc3bf89", + "0x63d692582691dfbac88e02db1c7774ca4facb9c056442b1aa127190d1e65095f", + "0x8f9efc274da5900478cd7549dd084243dd6e3b22aef7a34462f8cb2b3be8d501", + "0x570855355e86edd59ddcff2e5a1e9175e50a950ef719ee0cd9bea8d0bec384f1", + "0x6e0a48aef786d43bbe5371152b9fb4677ec33d2bd6b6420421c6c72ae21804af", + "0x11ad65f613a391cc9e5ddc0aef45b1431ec18caa5c4dcbae0e458b879501ae58", + "0x74d6c4f62a7a898f6e950ee706e6dd802338982bb5c33442c84514bc21205ecd", + "0x206fa7a662dd86178034c32b31ee7b741fb4e0d0f6165756bf324cc187f0931d", + "0xbe992b5f3b91b36736335cb5a7a45499fe64077e0366a641d08e89c256fb2dd9", + "0xb6c3c845b38c51944d5b594b4057d3816fbc2e19e65132934993d23e2cf80750", + "0x57c0650c0a11e429c1462178bc1a7c4b2cee751e760d4e69ff518da5cdae0e2d", + "0x9019e15d2c1d20b164a5f29ba3e4a877e061628d970e3786e63a7ede330d2f13", + "0x91e8d962b0862cf728bbabd6b0e1711095d36a00e49209373550187a40cf3a27", + "0xc233c63b11a9a768b7be3e76e137684d1a099400b2a5a40c79df800161925513", + "0x58d52e1f1de37dc7c0a5e6fe6a298f4da1647f069063485a7a56d5ad64a881bc", + "0x0f418081dafe26091cf93c5ca16677d9325eab729aaf69afe879656d57fa651f", + "0x59ac3592e783519379f760c02a875342de3df468d2d4a2be8ea6bb017e48dc20", + "0xb83d0bced2e4e5779ca6fcb84324ea9c43b9e6b72bb69a3b9c354ae9ddb2ee29", + "0xc55d0e8c61aa47e9265a438ea0dd9bb22b97301196af98adcad15baba20ab30e", + "0x319acb4a3c4b842ca67a1286a3163c1638c30fab59352e94b1c6f392a075dc2e", + "0x2e3fb17aac21b13195fe01e06c9321dd3374c9a9d810a00307f6259f3becaf3d", + "0xbbb95eef5fcff3e5b4c669bae6c459d9f0b4a245bff28cdd18154f8bd8a8477e", + "0xf55b2c536a25421302f999fe32f01d8a70913ea657f07ea410e9ba62072aecbe", + "0x48dcea41db0c2321c5d2b67ba2e9570213eed1b623192123ba9958817030b9dc", + "0xcba1bd9f25f8c01314fcfaee2b4ca88e1e650bd7919525ccce7827a50e7b0621", + "0x01ff0f8c8d7f8eb3f77b10a55d5494856e41777033dcb42424e27c330e135809", + "0xb17a5aefdb29a69d61f1e13c93a5aaab3eac923e0b4087519f6dd22cf1195346", + "0x9dd5d0230817899bba343eccc1261647d7a25dc062b0b629d8128eb459245a66", + "0xb5e0dc8a81ffe8bea905a22c8532db5ba48ce6d6c10a4a50088f7901b1aef0d8", + "0x7901d2574d93e22407cbbd8cdc5123200f3477ef3f9e50ff558515c8e388af5f", + "0x0473823ecb85dfc5edcf41e6f3628ffe2f7186d2d3f6095ead8934d1e9c023c0", + "0x67ea5aa7c363fca5f22edc45f372719293df24a3d9df95288f815dd81e04f6fa", + "0x84324dabe1125f226873f772d38d6203b2f42cf4aebf29950d16fab4da00b085", + "0x2c2c7532782dfec5752f596f76813e0a2ce57ac9d2d80620d11c7f9ddc450503", + "0x306e85d2b37054dc10d093edc1c095b0c4a4d0a0933e52f60b033aff26fe0225", + "0x15c35fbf3b0a7b39fa7aca314f69247fbd81099e5cf6fcd1eacdb2277b3c0a69", + "0x4e4085d0903f939c63059231215a6f4c8e95a5ae9be6be0e1ca3c7577540bd24", + "0x6936975a509bdfbaa9c4fae6e3d1f33b176777814263052c1ece31c1fd997433", + "0x31bbf21a4dbf99991bbe140f2acb221140dca751be7a62df153a5f406ff76dcc", + "0xc0ec54f4d57bc80eb3bd148772d8a55c17e240af4d0fae52a2875fb61699ccca", + "0x84cc4991d9a39ad3f5f4c51863216b49a366aa30c1ea5b1694a4fadc18675af4", + "0xe876a4920cf0ac0da71635e1b96a6ed1fab9679cf127b5da61321d2072545686", + "0xfb7ee6618125de5e3ad0c9775bea6c42d74a6aa8f729e93f981935a9a0df30fa", + "0x0cdf8e6f47cf55d0831332295158a771b47b6bb3d2460100da46fa3798e3ed37", + "0x411baddcf69884fa73202c25c31aeae2a0d92457d8d81b6d9a557d50a4af3a18", + "0x796727e2dc14535865d665dec6baa741f6dae420c632180ce964fa49f81a55bc", + "0xd99ce51a2d668f236b522496a6b8fa92b9e9bb28d105fe3117dad837c37e8c29", + "0x450f67032b19bb490ad244ef2b97466e4cc20c278cb2802f9a6dbab1dd45a947", + "0xd3746934873fcb29b3d53eb5fcfe871f15dfb2d1da0123c461695182725497f5", + "0x61486b9ada227a6912c4e98ebd34ef063018fb2217eeb204ca306b06024cc1dc", + "0xe107948da20aa7be669979efff94c6c205614aaae1c358f7e2af002384d5ad8a", + "0x8149ee6563f2c136af7f0a644f0bce1fbca0a745f453f7d4ea3a315ad736cabb", + "0x35511c40055525beb1fed9c229a934c470baea556688b83c2fafff48b3e1fcf6", + "0x87df09f402c970c8148dfc77645172b4cff69e08af1411b692ea10135024c98f", + "0x4e7a4f1e8ccce338b8ce55ddc465a70db2eb16138904137a0fe719b5be1d568f", + "0xf83d7e21e6d3a707cabd0d9584fa9ecf97e9cfc3b3fa26de99b4ca121965a87a", + "0x99e89412da98a019db4a01ac8e680e575b7b1fc540db541463f4770aa3b7245c", + "0x4aa08366bd87759ad19130309e48426e4616fe38daace20b7b2797fb41fe8e12", + "0x42dfa5deb45f8598a9dd11e2bc2372c0f2492bc8b3d9a41f0e7af07c535b62d0", + "0xcb129575a7c2475be53b1b985e598f8571898afbb76287eab8e86bc52f9fe808", + "0xaded5f1536c9e79b6d1050c8561ebe5d2189302455fcf2c5a17f02ec7ab8f14d", + "0xf0bbc8bc879adb55fbf5e8fa2d2a306a09cd60020421eb06be06992b41993244", + "0x97857ffd21a47f6d7a5b41c73db5a9bc8f52b6856c0660a4319d40217a3dbd31", + "0xb5f9df222810e27d4c5360f57002cb214446abbb7c436092c3ef2127c86ded5a", + "0x73d263db8c30fa5ba03c25ad80dad460bbfb723082ba62ad475ed38782241da8", + "0xc87518923536a3d33a167f2b3f3ca5376a0f9582f73ead405928d2056f211808", + "0xe69de4fdb29f8fcb4067d74b19b4130e5c97fb0b13c8f5a0517dfbc7f22046b0", + "0xb992a0b4cfcea9be695860e7c26b9cb80da28876a56e30c478f8e918525feda7", + "0x3763f8d712fd0ca3f0bc4995f6259f37507ada106a2267672f23ff224535deb8", + "0x713a6274a183252d96b13f6170f5d5b19ba4f066578ed01cf1ba35e547b2d542", + "0xc8ed76c8682075964624b99389c539243480e0ba90e0ca6c709d961353b2fa98", + "0xa380dd530e490d91abfc2fed45c30506be0c9e12fce2074cf6997aa02920744b", + "0xd26aeb79e8974684d3e6987e09fb7f79f7be2b42aa481794f13f090f4de3d7c3", + "0xa3ccacefe510a762d7300cdc305a3248d429950088d73aad86e0d76a4b682913", + "0xf2fdec1176d78ead3961ef971635c41c7980baba7517dcec4c96b57358888c69", + "0xcd43f1a818a5b7571e00ec95b93c975ef1f55cf6d980b1bbf804c5eccddf58af", + "0xe32f706472c1b4107d22b74733b0db12be0a8e66fd959e3013294b3252825601", + "0x23fd6dab4a8ad90c343f506ca24197099531f31d71f90ff377a01cae8fd623c5", + "0xd7a8ad3f5cc0188d4f7e0f8df75ba0455cfd2692e2e6700d16e0d7b344062105", + "0x35823a4fed0c9b56ee5c5c68dfe8fa83265b842a34e2d072b4384a3e52fb5a26", + "0xa2a996caa28edff7280e5b5092ce60e3f18ea2c400a09398c0391223c1e49fa9", + "0xe697fdaf0d928156aa61f8fac5d349a6489bb1f616be0394e6494d681fb3e7f0", + "0x4ef44274ce47fa84690df8c6ed3f297ede7c2e12a92a1131e0d5089a08baaba7", + "0xa504227982ebcc4e7f0010287b8eecbf5d13fdca8903f133aafaf51ba568986f", + "0xdb5daf2bf033ca2cd50dbf0b18e9e9fee997a93d699168e77345532d0cd9bfcf", + "0x256f537de4abe25b3ede4c47f4c4f9fb7ae59477ac6f5a3b64401ecadbc8a2d1", + "0x05204f56032d05a67d2f28083ca2c5d49c7068abd7203af12310e16355bcb691", + "0xb199549c2ed99daa63f7c09ee81ca62de053411a5f62a3c46797d0bd2e1a327e", + "0xf3012930bd4283f6feb5de01c6af7526e94b388f301ca4bf067c26bd7b4d0f8d", + "0xfa023007d987ae8036efbb8e28fbecd784cf02c5d9d388a1d0dee444abc114ce", + "0x5e2738bd2ea707c5c60591f343a4bc45df466795fb0d2671bb3022dfb7f82075", + "0x01d43224cc26ad8a47d4c1d06ac4ad961527ae7b4c0937382f1e0658fc58465d", + "0x43386ec01a213669c6fe463a61920aad1879508c31bfae5f6175b5d023c31fb2", + "0x6614dce84fda38a0e628797078145e2fbfc2d514913ac33d0927408833cd7656", + "0xb2f95319d272892fe948bce890b41f6c2cc7281e850ce004965abc3d501015c1", + "0x5e8e64fd53a87330c9b0d5144a5c38863f58bfeb72c1c15b2df4ebb853ff6c54", + "0xb1906d863cdb5b0eb6f4fa7f8a566f2a76adb40f4b6b8f9803aae680547d149a", + "0x00b09abe3f9a2a0867b7c2b89afe9d6942021f6422f77f3f6394e6e22dfb3a7b", + "0xedf97c2cc44013f7ef87c9376d4f4364ee582acb15cf887ea3cf558febd3391f", + "0xc647c8604da2bc0103767472cf68f1d1675543428194a25e4a2a6ce093e1e241", + "0x932ae227bb0e993ec47b735c71a5ff7030af5237a9bdbb5a4a28706e0e08d2e4", + "0xd44b49e32934f3bf1e5beba9658ae3d8c213e838d88d421c180db4a73e4b1046", + "0x18faec51f6a2aa5a81dc08ec38ca123c5ab46a91113a216038419aa8b41ea291", + "0xd5edb19772ddbc7e83c5faa2283692cdbda0f254ebb14593d90027a235fe54ea", + "0x9ba8e183039702af49c6027d77634f75e9d4b684878cfd46f6751541afe8fbde", + "0xfaae1f8535af27360dccbd9479fb891db1fdfcfc5ef8ed59c6fa69e1263d5f8e", + "0x6388fe171f7aa24d9a62b13a7dd8e8c30dd35b12db3da540dbae333f6d686355", + "0x4f13859b2e0d4de66bdde57a95df9dc85e36750d182a98fa6c68b145b66c66ad", + "0xedd3abf90a37cc95af68fad7603fc0d4f6856606abfc3f06f49950b22325db65", + "0x3e586df070ee0f953db0b7f94c33fe7d7cc4a66329f7bbddd6debbfa875ed1f4", + "0x53c09af2a3971a010122693f6ac3734114a04ab4edd8632aac4fd0fdf689782f", + "0xad75338dcb5143d18a94cfa596733c253b7941b42f671550e6fc2dfd585febc8", + "0x42020ded9891fdcf92085b6a723ae587673c67299b6586d47077733bf793adea", + "0x853d0ee40787dcfae425f74551460a4513c70c1827c0626dc695232830d8555a", + "0x03c4dcfb4326ebdacf17637ac3c75212a7c26d9e781627993c05abdcf8306ab1", + "0xc8e811fa55eed99e2826bc6edccbd0c1bb67f6ec0c34b3e198dffc65a5a9bbc2", + "0xcfba84d4fd6cd723494eb3f046399cef988ede2beceb883f4a6ff398ce26175f", + "0x2b98c377b6c583611cba5ea6b6d8cc855c41f9a20834ac444d2fe45e0274808a", + "0x59a71597c2c2e33e4b447ead0063bbdf0521c736cc367c2e78595eb5d9ef640e", + "0x419f06a5b8dbc9f893cf66f42417d8904288eb131d3b859fc7ced0c3249c5282", + "0xc4bdeb557d397a54e00e5274c79a8389f1c40bfa3c83ec2e0625e3f0f3e27b29", + "0xe81764b24372a6a0d7a4c02a266d559d1043d2f2704bb74c635181fc21f8e567", + "0xef5383720b4ff4026babd093eefb2fb4062aed49075f0c3bb6e452d39e2c09bd", + "0x6766af430fbe8603e8103a3eddae5c8f9c64063575973821befc08b52983c08a", + "0xc82eb7ed96f34fc1bab5a2b25296334b20347f3099f5e85c858e01513f7a277b", + "0x89dd8e441cf69c4d46ce5816ee382a0f1ec038d7050afdd4193a1a7d3741ae10", + "0x6401e1e70f8e8f00e2128162c1fecd03551d311506a61fdb1bcf1897debb3773", + "0x1dfa3aa8961b56f585e317b21faa1c712027c130f970923df36039a24b3f917e", + "0x7dee46858f9d265c271248d1dfe95ceae9f449ec6e28d0904536db6504a87dc2", + "0x7a75afa4c8656b836441a8cc4c63161b8fd59713377be083b9967e43be7d8354", + "0x8932807be04e0736b2882fa0a5e492bd3b639826ea1c2a0d164d90caf0c24303", + "0x665292a071456307facf5b0ca8c2d17f811d11e3c19c2b054d748048095af462", + "0x7768973a9f321f9c8c8111de7d33a42bbf6ff9310c9ff17e49360dc83b798e04", + "0xd06f1ed5d6c51058e8387b9051953b0c2f83157712781a2d347f09a02ca37f41", + "0x2f5749f72ed8f758345ff190cbd0b8646507fb02db3962440ce8c99bc1f8a3d6", + "0xd374a08da2d4ac87b999815088a586030af391e4b25265a600ea6f98a4924050", + "0xeb6b4006f204113575b07e910544ef8cae4e04623da06445cefe232331cd1627", + "0x4d4b40dbd5bcfd7c77ee418817d043a3caca3c61639bbaf4c5362a14765b96c2", + "0xaacb373f417052854b78c61db3ad93215977a9eb4ba723b0a7408ace89e79cf4", + "0x4e7776f6afc7e018a502c21e5402f4ee850f1915ccedddb3ed142ea12463fbd3", + "0x189874a382d828dac5d155d8bfba0460675d6ac3a64f3762f6196b4bc43364c6", + "0x8a37ec7110fd6d5a96a518b0d8180fc27acee66d2a85f77b21db0006d927ad65", + "0xc2080ddd6e00d589bbb5cfa2ff043a26d49675d4519ff7516fe16f88a6c6931d", + "0x067c48c81aafc8824145ec5fd2a25d4425b807627cd957640d7daf0e116f453a", + "0x95ec6b2dbe64e76e28b20d9d54e90d299b9129e6837d9bf5a3101c781c283aa4", + "0xcc6f73b226d96e545b8bda41e6320c60dbec5351c856e5dd2deceeca2c421a30", + "0x473de7bbfada0f062cec2fffdd6d454ccc8ead5d0e49f9557bfe2e788f890108", + "0x80a4bbbf8eac4083164474be8c85c72efc149c09578dcfe9cec0f77f7331d053", + "0xc68019f3e2096ed99df53dfdacad840b53c884aca0a8b3dd02cd3d55412e70e4", + "0xb572370c6f3f6083ae81b151a20ae4eae4de8a408da14c9e4ebf13116ab15377", + "0x5c614e1e2e1a49c0bdd796d1cd3530b3e8886849eb28ae136a6759e4362795d8", + "0xac53cda650419056d25c2c9702eb7b632c85d366ba5339d813d5712568de1034", + "0xe07fdfc28c4df7f7f8bc4394a0555dc39a3da42eae3a7c68f0a8250eafd2c859", + "0xe8899d18c7560dc92c1bcf2767b94c34140ff88901e4a270d088b425b3bfbf9e", + "0x08081786cef6e8af9df5dd427c05734ffe2e2af91a4ccde16439c3a9fae18c40", + "0x903a6651d733147385d18520f293d63b1376d5e7d6672a63c0d23fdf4a447dc0", + "0x61519db9df8808a4195a631b0c30df705a749ee12350904e6a407ad67f56f2b5", + "0x4b4c24847820efa3762a875c8e5a7ffb10324d99b3bd792b6f6ab8cbe08ac401", + "0x2021e3150007cbb5addd4799c27604b16309b13f8e7d25ee5a8dd6034a5f0994", + "0x379bd050a6829385d1a0d7f2ab36b6ca16eb65cbcd4e8a0dc0f86695c841b048", + "0xc14d85176163ad904711c52e8a00d232e51e203bf2732254ca2c95828e0e8c82", + "0x53cd2c2bdd83650632a4a639a4b5803b7ec081f3ba56221237619969dd206a43", + "0xb446370579d3ff001370cf5ae3bab3b0ad41346fe0713e16c291e2a08670c38e", + "0x332a0df9fcf8ddfc5f5c91a48caef6dd65a1e8c6c251df850940d5b31f25e9db", + "0x9d7fd4b7b982fce102353cd0ae948118286d3ea801a99a7df45dafc0301ffce7", + "0x4a95ade4fa69c3f82dd5643398ee458d0421d2373394b3bc88a28e933ce2659f", + "0xde641ac12bbe9ad98134498dd641339ce6679e1d4b9e9315008eabb1eececde8", + "0x416b2530ba44157137404dbfc7a06bc3f5f3aeb67ad643931e4177e26738c33d", + "0x16de3eb43fdfd3081e5db02cdae7a661dbd88ecc4ebf44bd3359f65ca622c2a5", + "0x17120788f87ffaf3b4b3f05bd11a69593c26a7fd01c89b43aa86947be7f54121", + "0x69667968c664fb0775a4ff7970f1ca841a1ef8ee9bd2e160282b5a2c3acabc2f", + "0x222c2908e314140b69fafeddd5f8edcd3f50f2684e5fb79646fd66839a76c02d", + "0x3bce7ca8742dcf45271a95ade92400aafa8afbaae1373c690ed465982e39bee7", + "0xf0f72528968070ae479752b55c0b2e6bfff3262f1b107e5db67601f07cf32280", + "0x50716c5501806450da59a8b87a5bbdf2218b31e173b70b84a349e93eaf43d2da", + "0x008a39bab6e090894282653215726116dbc8a249d1605130b431e6117a0e1ab5", + "0x8c535dd00616e687281b8e6d8e9cfe1252a1c0b84a419c714eb89eb390f2fa1e", + "0x8662a70e7610afe2e7d72211d42ff555ff636178bfb8ff393afef2266700e505", + "0xf122f2b8e8e69f436f83b2810204a41cd62d8abe76db2b8c2c672bf285844881", + "0xb485e98517aa5649063ff23d300cdc18340982e8ea1a4aab1fa533efaf99034a", + "0x4ed9d680b137417456f26c76487cc760ff08941c9af10882a277023f0addd9f7", + "0x533162f34817809a804179afd7835275a1e0b8978627e52c756473e0bf6a1968", + "0x1343a85f6bba502f29e44ba5ce91d7f3dc115ae8e7337a8c5389803d400af920", + "0x04d1a3c060cc8247384f74841ffd34c2592c53f78b9fd3954479a210829dba7d", + "0x6abf1e50a37ed0ba713f75ffa768664fc4cad0a53a45692fbd65bd85f09355cf", + "0x96a6f71201bccf0f0d176d4c18ea2a6f1347aae08c28b4a8b8a2ff2817f2c5ec", + "0x0f172d0b822747d1874a12c13dd6a0082b5b1ca4619816e9007d56ccadc74f5e", + "0x67d604f098041164e95eca5518e94bf7ded40102094eb4e35db72a343905d343", + "0x081ee079074391480f975a7624e27a7e135d27fc9943bec3c691248d9facd583", + "0xebfe4893d9f33ab843eecd7ea782f6e1edeee5ebf32486fbb928f45d9921b0f0", + "0x39402cf7822539d659dcd6c4f3cceae6063986273c8a01a6465f3ba85d149ff5", + "0x67dfbbdb151d9e26bc15472b2c4707839eca2dafe8ecd4ce01b43f996b4876ce", + "0x7e0e81955ae555ad2e5781585845f69c75f23340d9c11a0965f032a8b525a316", + "0xf7b0f1661ce62d8f1edfdee9e2fca8a217fcd5bad9c8dd78c24d10355b6a079c", + "0xdcbbd1fc8dab39027bb4f25d2232bdf6c7e6e2f1cafd23c626c05df4092e99c4", + "0xaeae16e803ccd2992905f230e050fc394b31b5a8714ba48a6ed2e20fc091fc90", + "0xa708ad692b6b528b60936e5447638829a31eef0b56a3225552fa9ec5163fb616", + "0x1e906aa6b813adddd3939b1dc7cdf68183e0a8c5bfe5d19a86527ebb813f4f56", + "0x13c17939e3e12740369194ee1ebfa61f8831ace81960226cf65053528d339658", + "0xf085aceb8b6d588df387ea14f57fb75465d15b7e6227dd8c1a4b6376b1550339", + "0x62b04006767b09568d281eddb88967650c9ebdb2710afc5558d32e61bec727f4", + "0x0f0fbe8df05337f32b755dac461e2098f7c7d99f6d20ad208d831352c990e4af", + "0x1bae743622fbf612e5a9f01a71310b152e59e5f3403b703317ea479aff507263", + "0xbdd3948a83ad6591fded2f4686d00732b58a4a652bac8db1dc1f0ca2c3fc0566", + "0x5af28cd2e944d300f5a7cee0c89dc6643ca8db3e6bc1891ccd54e1921c172a8d", + "0x5974be93a5535469466cb915420de05afe6d1286bb6deade734cd78a052bf9fa", + "0x51ec9904495a9d3b86a9ae4364cb5706d334f66741a5e01401d584aa9b7455d3", + "0x4c1a7e14fddaf628b9fdb59e1a9b2e5eb290952e126a34802d74bdd73c0050b8", + "0x9e1ac3394c05b9156276e7d033c8dac054e5de2d11c0970e755f3dee94af06ca", + "0xc4e98b09678e87c3de3eae7ed29466349feddfa70bcdfa38c707a87d8acdb54b", + "0xfd7a87e4fbf61b1cca175944aa163ba65c226a148f67da624b9ce7e6b01e4dd6", + "0x2910059171feb575e449a49c2c7d64d597d277ac6275295423b8266d1d0c11ae", + "0xccd536b647f09349dc487ab4c3477cc266a2afff43554aa138af4910f4ed35ee", + "0xbb6d570854d1f67bfbb8fab2a055663d747cf3c732331bc714f43f6a0a2d77b7", + "0x5bbd117843faaa2f0921d37d81e04762c2084a0985b7cb5506a0be16afe3020e", + "0x45d789e691612242c8b453a39864d2c76a34b96fcf92751cb945e1c82fadb541", + "0x6ebf53a914d41df41b3aa830eef795379b7dbc3ed9ce34e0bd74356d55669d97", + "0x8bae181d15e047db0e5332657f2c66070cca120f94eb8efdb6bfbf241b60db6e", + "0x721f9e5a24034b467e88c3f36e0b9d45e91e91123160347ad09f009151ab24e7", + "0xf2626bd9c41840d4a988a57a66048f324979428637c21e836737b780f542213d", + "0x1c96d760ea3d49ddb0b7fabc6b81888385950e0f950222a9f24669ea842973ea", + "0xfff91738028211d38bfee372eec2b15a26c729af1455ad9b39aedd0f1c982081", + "0x600df4954ad7540cdb0209c37cec5a50ab3df6a7cc1923435b773399229e4005", + "0x7ed0d1d83295a9b92ddaa8f4f5ae6be3ac2b8791fa22c564ab5de05d82dd24a0", + "0x5cb2afe4777c05e480b859976289f0b6649edca01073056d85f1d2362ef681f8", + "0xb563852f6f63b6a26bd9b70a68ac56ce64729bd87643bc81932eac267f24f11f", + "0x63605e71a952c8665cb9a1d3de10e07a5a3332344aea756605b05d5155d6b545", + "0xddd0ab567c3434a49681984a315a4b63eaa8ef51c82e21983941e75d412ccfa4", + "0x88518dc9304a816f9dfd74e9dd7ab07db54535e0891a0210787720268ac809f9", + "0x94377d43bb1a3648c7b38e8fbe4abd77b05d7f06e620841cc94b5c2a71708405", + "0x8eb98105ecf4dd9092618dcd1e15cdec5ec21da344860cf887de1b8befd50087", + "0x0286c2325db49e26cc7c03bd79125d10feef0e4ec00e4398306a21277472de0d", + "0x977ee4dbbf8f728e8adb6487b3ec7fe53192afae65107083ffadf89e1090d24f", + "0x868f1addd23b29d1473455cc92a60281698818ce33d079e2517ca02a91d5e72f", + "0xb58af84049a3aacab8b117f72eccdb2c866b9a7d1d216575eebeb4912601276c", + "0x989eb6275d0ce09d027b2c479b74a59339d4bd03e0d7e6f84da84504682c4434", + "0xc8e8a1afd5b2604d0c775ac9d1d314b8c5d5fda77918ee6f99b46218cab4a37b", + "0xeb02a9815b37173ae09bcbb0502376d5beb230ccc01848e8cd60e9cc258538af", + "0xa3fe3d2e8a56c46b17b1b0c40deeefb3d89a7189eea5a4731711c00d0e87e621", + "0x6e00669a23f2c38252f03ba0762401fad3481cf7e3b4ef1faf2276c6b378c34e", + "0x2ae3349778fba796da50a23fb8ff64aca9bd4b62948e688c3a72f152dc0e3dae", + "0xaa9ea59446127b30e498f26d9d7bb7c8ddba14a9775646893214fa72771222d9", + "0x1ac5e9867e763b0b9907006c50732827a686f19cb63ea4396a4dce571d66b142", + "0xe0803244c36ea1a15be949c706a7b550a3ef2dac009022ac6e762fc6ab51c7f2", + "0x339da9b6ff559184fdb9a118ce5c893c35926ab452a1c45b1f374e37b4242a71", + "0xb27f3b661b14dc283b8475014046d79ad1070e22b91d6366d16f7f9a40c317a0", + "0x858cce00c3125201875c168d812628d6f59b0f3bcd8f2d75cf8cdaadcf447e6f", + "0xf619456f623d508d838a7dcc8ff3e271c1eba5ee43583f047d73308841c6976a", + "0xdabd39e43b27d3900d7a87c9956fa452ac0d86ba1c8f0f68bb53d0fdc01202d6", + "0x850e6a972ebc3b052b961e3c4d5b9c0594674e04206bc0e5402bc4642dc4fc20", + "0x52195df456d3333605d5dd5cdb5db759df098fb7a3dfb752f1e124052c372a85", + "0x8e7d3774c326faa8d965c8fb35766cc256666cd018466512461d56c606416704", + "0x863cb78391d46a604e515045f3af191d57580154d718aa0fc2dd9185b5a0ec95", + "0x6c33da910a7113db73eae464d94a66d4b6fa8716556fe6e18861c54a1b4c1875", + "0xcea4b0720883b4d372155a8c2e5c3886a33014f24cebcb2fc2fcca62a3a9fd2a", + "0x3895887a20296734972d78b52cd6e8eee4e15c62af3316276a79e54f779360b3", + "0x71200289e5e9bfd25113d6f845d7a47cc79865585ed9dc1067a95f2133e28434", + "0x7b413ea7ba195fd80d061dd717e5881dd486ac0204d0aa33c5ebf7e65587eecd", + "0x7619eaebdd10e29a3340a895fa80599ee9bbd817aad7f151cbe2c92366e7dd60", + "0x6f6e154b0dac910b575e47b0e303dcfa9ddfc0ca24cf1abccb1f4e1f9a2eb13a", + "0xec913d95cef6d054c9affeba2d9a1467b44f457925c4cc737a934697ae2abfc6", + "0x2d03ee401ca5dce6b279025eb67634891420551175407f8d14b7757c52be4e01", + "0xfd5c3b0ea478c75debd2f80c2ffc4a44fe1a0e8b494d43d0d8963cb12c151946", + "0x14d7082b33df6e98d27e898d0c737f4639506b3547c19a1f9f36bc50fbf18d74", + "0xe5567f8bd016b726ba69353a32dd6bd2f91699250f4385e02b01340eec7fd4e3", + "0xe0c12d8df9ecab17271b3483157a5e95f14143ce4a6978caab36c64a485e0e2a", + "0x611fbee0124abef9b64689157842cd3a22699e726939435fb8511bc1d3d8f7f2", + "0xd5b4c82b84c4290fda44c8292c67bc53890846fab039bff059a3d637714f2171", + "0x532e9b0e0f7014d3f5db774adb7bf0febe3e3e28f12fc74d8d8c79225701044a", + "0x5d5187cb7b3d6174dcebe936afd5593fadbe3b4dd5c3d039db263e0c7a04812a", + "0xe551b7790fb8270dfcba179eca9737b79f2139cedcd2db7f25fcf847ddc5f4bf", + "0xa0c849b7e9b8d414155dd4c3f9fc45b3b8080582f620eb550edff35cd4d41c56", + "0x218e1a8f9d1925334213c8d96df51361eb452310b36afd75c3fb4bcad6375c64", + "0x965359288dbef2c7b0dafeae70ce8d77dc033355536dd025b0925e05d0177456", + "0x66dc61ae5f86aa9475a2b236ea8dd09de3f5459f5e4f875eefb224f817fb03cf", + "0x45f90c180780c96e8b8b4fc33826913fb0758b8c932c8493ab9e5338a524dcf2", + "0xb541a404fdad3aa199b6668fa2183bee7f97ad286321755363e120ea247fae9f", + "0x74c9dda5dd3b0518f3606d667ee1315a247b9cb2cb5046b5b09e9f3ae8304c21", + "0xa83b1ca65db76888ec401e462a9b51cc6b8d5b15c54c4a1352de08464b08a658", + "0x86a51cf68baf7e8b32c50b22fcd87eea9b4228d301b9b2919dff14f5abd7b0db", + "0x917e6851523f3f4814cac80601fd83431717e5786327d2ef01b5a93733fb09c8", + "0x70aca4792c8b5ca218d0928c223cad0818fa4d93e36b56841aec4f09f61a65d3", + "0x7f309a05e6deb97ba6cfab406c2b0bd28ca438697fad9b6edd7b4177c68b8009", + "0xc0b7d8e53ad5c910c0f3d1c844de04ce1bdca159fd462224209eeda424d53f95", + "0xe622a4cc0d682690cd9ab7072c368af871d53e71c1412a0943d3f1f04a95e478", + "0xf28c0bfe963c852e461c9a7ce3cc89fbe7950ef5dd11a4306d7996ee43ddbc5c", + "0xf2f0e15be6514b3a4fdb065272ebb740184bc6e8e18b08634d8655e288e675b1", + "0x5827d070cf67703ef7d92ed9d4b28c8186d152e575a653223131f4ce1c3a59d6", + "0xdc61a7a22bd93fc9f87f78c3f85752c568cb19ba166f67041cce888609eb169a", + "0xab12c3b24765e92f8a2e629ec9f3461f31be47b8418424e2cd94602389afd09c", + "0x5b0475e94f401e6c4e24f095aba3da28a494c09294a16e77ac31151258fbbd02", + "0x7a4a5efa80a16c49fc521352ca2c0bb2951afb31e76989cebc810957cfdfe248", + "0x926d8fa246d7478d62eee47070375e95be2bb39da4a64de46625bf2b40509d6d", + "0xa505cbede11b86dd3abd2ffe897d976a4926c163480cfb82fe0a4fb3ea9c85fc", + "0xc2d75bc7402dc01c7aa498659b2c9b5aae0c4778ff565d3f88d9fdfa01641a07", + "0xaa632aa88b9ab3fd5591eedb786f8ef96f2650d797732226b31c99a02cc76be9", + "0x28c0b327fbb2a3bfe1ff472d86844a47fdc0b7ce7cc22254412eff42c1a40664", + "0xf9a046bfc15e1b6bd23ca52de625b2bd8a9b7b74f13942ac3d4f58964e0d3961", + "0xf9c7f6a94b99f4881f14bdbf859f4e0f702c18d49c0891d93ece517c663dd031", + "0xa929274ffaf753240804fdcf969a60ab5a611caf129fede731696fb90c9be982", + "0xbe9e77f99bc88acc2eab1c9154240002e09f2302a46e5056047c64cff08ba018", + "0xdaa09f0e34dcf32dde3cac712295eab9422f6e1ec6845f87ae907983166ef6b9", + "0x516d0c9a37aa26b383232b77039dba863ac6c3c1f5657a1e63115a6f60589b05", + "0x590b274952fecd04645058831be45dae065d472b63e12bb43f62c33aa2fcc463", + "0x9b31ac3138b587f28e105ab4e5f96893c097102fb917137b34db60dbee96b960", + "0x0861ffe65aa4f9f2a5f7b0d7951c94c42cad748f4d457be071206855463f4095", + "0x06ad0a8e0a374cd0c7a14ebaa13ae79e7f8efe961698b031199ab969f95dfb25", + "0x6417790a2452ee0de6c995d721f802735962d217674ac315c159f1c10367d0fa", + "0x5723990e47c2e3ca5e814a2e01d49a02f0ec71cbb7cee8648783fd983e056ac2", + "0x44cb719a6f8b0e252c6d6fe3419abc59969a76d61f278685481d27862a4ca8ef", + "0xfb8eb9a17b06652281798bdca82eaac263b8fcca5a88bc67e680567d08f8169e", + "0x93c856ce8c5f6ec8ea9ba513b70b19263218110ec95d9fd00299c3c8d936d06c", + "0xa99212cc398f626ca7c5c198c8466d332b7802c681e2f2a5af699cbcb114e561", + "0x4be2f0543846933751d3bbd0512e589b24573cd8a842e59b3a57188aef8f370e", + "0xeb407ee70617f710e4aa79c8c110506b4bf09c8cd1922a0fa0a478f1a5fe6b40", + "0xdc7834182e7fd885dbc8f1709c8b3c259bfe3744879742d0c0f88849e83f45ad", + "0x8c34ba36e79a3400aa55a4b0d0884b34e44b954426a3bbbbf220c471ef72d21d", + "0xf8b0c7160b08ccb0eea3eb7272af98044a90855e41719a536afbfd86375113b1", + "0x842c2e8a2102509ea5a44a3f7cd1addf26f445c50b57c9ebaadd32182a86fd8b", + "0xdf5fa5e0bb2fb0016f319ca678d944a1e3caeaf2997e46254596717b34d715d3", + "0xee6dea55686d28b1a2e35b9023c466ecbeea5642ba6c6e7902dcaff64a8fb82b", + "0xcffcb83c4beed8ed2293df83241c965df78a60bd618d473b16f58b33e93ec854", + "0x8c3959b9ddaddcbef906bf509f57878dfbb665ee10b8a34b1d6ca7f923150084", + "0xc93372e7909c6852ed122019841cca228b94bb1f3ea4e7449cb0da618e320433", + "0x7c1473e137e62387e357990dbe718085516a2a76bf35ea50e9945eedd90c72a2", + "0xa8810bc6b8f4f4c4e92ca22441dc5d98605ed94c85e9f6019fa22081046a4df0", + "0xfb0dc228eaf5df90c68583d6b602999a5ca63c9e9384cd2cade64799f1285970", + "0x4c04a395f41810a7928291c2e5a5411b104199a5cf870b1a17a6695497f91497", + "0xd066b3dc7d793d72d01042c4f8227b3168250ffe5bd5182798ad9742ac2fcd38", + "0xa6af823e531eb7709e4b9d76057cecc6c86dab21361a94a21ca97c00c4410729", + "0xaeb58a508f54ca4b0443c67327ecc73a578a4745de3b16de0a0a8a869b1b93ce", + "0x1c259a4fcac9305def14d2ee80ab7310d95f244e5abba5ad2e0a86b9ebe6a31b", + "0x9db2ab93d83bbef546fe084f541ee6e13c9506bc62d19547b5c5bd0e7bf8f8fe", + "0x7faa561b3883371e3b74e56664530962fb08cd32a73eca10a1a0a966e9d32465", + "0xa6ef05275645193bf1ff6edbea24ba5037cfac994d53b88ca7fbab16f7c36dcf", + "0x06c118de9a5b7a54c7a0f9f03a6d824483daaf5b1473674ff421d66139f03e27", + "0x9213fd3cb02adc28ce998e91993d98dcf0684c628e69108249a319d4d4d2da82", + "0x754ad98b0206f1caf96890a755660c0c1ed0f2b5b84f4b73cbd0244081d09ead", + "0x97eab5b9db2d4f46fdb498df43c9f13e1f0b4c66e938e37c8295d374997141d5", + "0x635b94cf574549e7d207d54223cf8f1283ae74fddc4d720cc3dc28cc49907c57", + "0xa88f84fd665a5441f3ac147517bb97704ee7f7bddaa85e25cde106735023e47b", + "0x7713a011f74e564aa0fc7614092fda15212a0bd5abdacd4316f26139da16d78d", + "0xae7ffc35061650643718a0a6d07318099939e368597ffadd9226f0f7be28dd14", + "0x3ca019dcb8169327329ebb18a39c24be57412d16e925e311a46b373b5fba31c0", + "0x1f1c8fd1d8dd213fdd5a6498979e750ea0db327a3a455f5a4028d1bf7b32fc57", + "0x6fe4a62083dbfe9bf033cdf93aa75e60dffc5e0e9c926de51deb77a10eb32e91", + "0x3b2b001851a5d2eb8c79854173f0f55592a2bfcb03793dff44c01ffd20e7d51b", + "0x0e277a8914be5ae4b8972ea930066671ee8f220c3f54a4714a75aaa4f9313edc", + "0x6436412aec0a8eccb98420e8a198767fa8e5b7e01a9ca3d11839134d7a59199b", + "0xa554951188dfc9db461d67dca0ef0db482fcef8e8939b2a3d15c7907ae8ec5ae", + "0x2cd17fe58be9d624a0d28a19febcd9a508484c2fee70cbab5a7da8c1c31ed0a8", + "0xbf453dee8f708bba311d221747748ba681af7f0c833ba2c3a8df14ebe067b880", + "0x3ba1ee792325d1afac66d33037d88892de839ac0dbd205958dbbbf37ca08fe0e", + "0xbd664db8a31a0022a22ae79e7721acca50dc5a3e80493fcae2413356f338a587", + "0x6432ef83b4b0a262d77433dce7269bf680eda60342df544a79fd5420227a0dd5", + "0x9c3ef2a74cc025f2c37fcfdae4875c7a718c387edf6e3783a392d24a8047f697", + "0x9361812b117dfb5e69ea8db7390d29cbdf7d5e7f07ad0b1c60d817d8c768ea56", + "0xc168478b56b443e89af7de05fc717b88381c68bda2cac3b1b4b6d8569ed14f88", + "0x17cc8f40e9c699e3391aa11e9ed760109f9e3952ebb4ddb39cd7bc19e4ef80c1", + "0xcff8db9cacfb3afcca81b5952313e77a14ae222983299312217798a7040b9fe3", + "0x812374893beadf3b34d5ea52d9e6507ee386bd1cfcb877dc54a5cbfa7c8636ee", + "0x99836c4cdd3eb8ea4692a793e24bf319429932e39646bee037de341939860391", + "0xff0120c5e50f9d01a1f2094e31bef5b17e335c40a58b4c3066eb3eb9bb232a2b", + "0x9981beb659d296ccd24a2ad6d04845d9c09537f810cadee0cdca48385d270bb7", + "0xee7766fa033c887bdea4ec1d935114657b5c684e71b4c213d55a8d98782f0753", + "0x4a45b0e2484f544d7c6db9c18b0ccf2ffb986401ce998d8f4247f0653e36573b", + "0xce0082dde888d21787e6388483350fd3c8066831254d8d5f1c9d095ddd1050a5", + "0xef10e26fa1c285021e731cac359603d40ba7a020986ee95911b976a6117c02f6", + "0x030c3bb2a6006d24ab4986338760d2d06c589434bd04304f203ed6a033e383ae", + "0x275f8f7f187f37e525d25858988e58940f25367d07ce714978cc3acb5c4e4197", + "0xdb3368380f807619ba4ad39f149823f44ff9ccb6c39deef236ec524db73913d6", + "0x8f20fcaa61efea00c07f84a39954c5c0ea7172c232abac1595c7c4f0a73369e4", + "0x1e16c0ebf9bb9761d60db3cb6b68ed73b44821ccfc24b059523613294080cb35", + "0x36561468ad9695a54411632e0efaed9c03c09b8e7c01097acabd6c6531ac9ea0", + "0xe49ce7aa7c23c72854fbbdccee9a7f9d4a504ed7d37a6f76a49363afe6822e4e", + "0x8a5593947794b27729f984e52ed17fd898fabca4c1b84a19897e91da76243fc8", + "0xce18c9a3dd92f7cc88b3faae7b38b5796c1e21e3db4875fd20a7d602e270832d", + "0xb177e39e8fd05fbe2f96f8bd1e139574fcccbb87a05fe5d7bdc135ce2dd11028", + "0x47007d8cfb22094ce2b7fe4f4631e1a1bc718c7fa1b845a4b3742337ffe09845", + "0x2655e222c6089c817a7b57077616eb4c677ea0e5758a4a9c01037c0d95051b55", + "0x1578853e5bff1eed42a713332df141eb185654afe3552da21abf42d093c19c1d", + "0x8a80cbe9bc329430de25fd45ecc08f9a6468e5fc24e5aca1797d7ac579111f1f", + "0xa99c81b8a985a225c07c9fc745315222d1d5ea0fc854123e0f498a67982fe05b", + "0xe4517665763904ffac7b0a6ccec3ce1cad852087b1666f9fba97515e7d10dc20", + "0xaa6b9e203cd9299e3d7368a030ef47cf6cbd833e5532f9216925dea714b6d8fd", + "0x9f71aa3f18a659217cf0a972d0ce18ff19e5667b9ef06e3b1e2a71ec2b2f7f27", + "0x81b48064103aec61a0524e7ab738c157c70d31107afd7917cdaab44e21bc7b56", + "0x7a45638918ba2912b67a4ff54fd969213f1e4f08434cd344965716ffd130337c", + "0x054bddf689a334b094bae1f21ea2543c389fbb92511d00ad855cb3468120c28c", + "0x6f5c5a3b9fb6d91662a5037d316efbcb1a00a1135022988d62351368e1d70c1b", + "0x6fa21990fffd1a7bb6cfb52bf09a1bb3f543e28f591ff426ba178536b519c75b", + "0x062ff40941db628c3653b093dc6c23b5c8a13a84fbee81041ced292a17aaeaf8", + "0xbee2f72346c503bab5039f09366458b7752ab5411d58bb136fea0d04a011eba2", + "0x5f5d9c039791917fcbab872fdd593d49cb674e117948712a1ba71e71e37cbe37", + "0xe0e5ab07f23b54575f9f398f1867fc979f77424e9f20faf73a6bb2d585749f44", + "0x6632f3cf17596f4aba65d7c10b4427745234db40b96311727ba669267c260525", + "0x2d57f0579e137fa4ab98a363eed62d5fd17c591df64c70d23aac12b506a80a33", + "0xca7aaac99aa1f0300c9bbb23831148a395298917465f27817f5252721a26588f", + "0x805fa2aa587dcd54a218e78d886a28d6f2350e02787bbf06d1f78d91d9e7255e", + "0xe112fa3321fadbc5b10072ea13e195895be9c4c9c6915330b95fb735e2d7302a", + "0xa9ca7ae83df586b2844e08ea406a82fd1c5870e959aec7acfcf8853b0fcac03a", + "0x775aa0958c21cdff2cacccd677396edf335a7db37956225ca4b8b011336fd598", + "0x55171961c6b8098ffd64af65139bbade58b118ad6dd2553e03eb40340b35eead", + "0x1f5ea5961953b80cd1ee1f8af93c48604bd4ff5505b9b07303ccde986a8053ed", + "0xa2c59aee95c17e1fbdc1813183bf55d08db5577383f03cf83b358478c2730e6e", + "0x92a57a81ba5eb02f16a6529c378041a7d213ce06ad61055224e75fcf8640fae9", + "0x75e86e793bd572111c69ff820ea0f7b08239879a1024f58c9f822cce81aac372", + "0xcfa20819b32a38b1fe0807a3efccc98f849d330009ad73f2c91cdf70bd651be5", + "0x539ce6f2a4eed7c1c34ec1e77b608b4275e11ab8a529131889b91881f292312f", + "0x76c13da06b6e61246f7eebd13987ecbc87aa7362b708adfeb46f94667ac10573", + "0xe8d0e91c6a84aecc5c805cc67a14932e80d63cce7e23263c14f6fe0201334d9a", + "0x6a1d4133383125c8f768362fc85e6d5d844d40bc8fba3ce72139595d749ba5fd", + "0xad0be29887f75eebe4024294ae0fb39739937f326a26dc08b894dfa61fd17090", + "0xc3ab3c1efe5d76af20ed7f20c5fc7c3f886ff312cae153ac54a4fbbb0ea12ffc", + "0x33500cd46890a13d0a6ff2e2a24cc9104d00f78117968b1bbbf119599c839463", + "0x684abb06ab282091b303f8494c7ede4a0f0fe46c81b97f0f75c0d09372a90b96", + "0x985946479f9393a1b71db6109902c900f1bb98d34847dfe45ae54895c007b103", + "0x5cd3afceb4e1672b38a3496949509d2e648ff72f74c7060e371c9548e8a58670", + "0x31ed9c3468d831d1bc8df5dc0e84fbd12c71b54801969b06b19040e7ae5bc7fb", + "0x088678778bfdd7b3a44c2b4f0dbdf0fb45f24c1d7269db08d613cf2c3922b0a6", + "0x1f55c8e1e575e304e44db1b73f91ed489a5323e553cf6e46ac22a26f244b4aad", + "0x5229989a6f308dd94d4340de33efcb8cbf0ee0a73e486e2480d021c81847774b", + "0x8bb0e12fc43cd385da87c59510faef5ffc0afd6d916358b28095740a9d077c21", + "0x9cc01445a4ba1fc74463a957b33ede45d0229c208a7cd5c45c1324660628e8be", + "0x4aecbcf1ca1098f994d4268146880751b56d49834a2b9bb223bee38d50360c0e", + "0xd6e0248ff2e4450f626e16d28fb1fb459cf275c7c85afad16b88367102fa9dfb", + "0x18d2449eb8c6358da44e09ffa7c5de3bcf778a0ca8ef23bb47495437c7a53237", + "0x9ebafd308408657d15f0c8d5b7ffae683ab97782cf25d866467109e2def261f6", + "0x89b8b1b52d4b1eedd7dbced52983d16e9e55325b1a96934f89d2af56f59e93df", + "0x0127642c914ee08c640635f8ea2ea5e9b719574b9f8070837ffba9fb8a4cf4b3", + "0x780bcd21e9a54a17352599ed091fcdfd44ce19e9d3b66fb415de9b5a32fc7131", + "0xfc300348b7b611f55e6a39ffce9fa105dca8e8337b4c8ce93015654ee88c664c", + "0x412b19d469cae09a3358f8e94013bee5d48f7f39ca2f07d2545d5bf6cff5d494", + "0x8122c002aa1a769df918c8f7a88b0a4edfd91d60ec143e85139e3c111bceaffa", + "0x27b2274761cd49add3dffa7c0b6a4af084923f6f75e071091e3f6bdb709782f8", + "0x1791208eb4cd58e5c28ea2610578959dc8c6865e470ebf79b6578b3371453820", + "0x5fefd4fef398c3c3576daa485a545c9e2bc2a820b27068a85adc6f80e0dc8147", + "0xbcdf0ac150cfb7d49472b827c85f2c67e3401739514a5e5f1852f4df01db683f", + "0x7a9b9b5cbd0e4b5aa68ab839519d7fb1ea608e4377222083df6b816afcb17172", + "0xcdfb51bd8f835e7865d3b0d22ce1caa84fd50c8bbb2c7449143fa7bc7cfb9ca9", + "0xba2af76db7b82d6b2c58bf17745b2bd26500946a014f4bb71fb62a9ac7064838", + "0x5b5544c9713cc5c2ee0dafacb55e81f7f547654f862fb5f700ec250643833288", + "0x7b693e1436bf87a056e9351ab64194c840ee3d98595ee0ee6ddb8b9945a6a830", + "0xecf0efb48557a07c993694956544cfde6102bc9e82aafd3128bf5d8bba2854f0", + "0xd1eca2c285dbf279cf05c26fc24fa2f2389eac140ec820e6973963f750e262e0", + "0x89d379237c8d0b0ed2fe41c1825f87d877a02d430f4347a3819b3ccdf58fbe34", + "0x331b66199abe54c1724b8368af4b2ea22b6110e9da097d1d5d94f9b94c76ee3f", + "0xb70e28a6338b3c20ee27ac4d2f31a7063e314c49e5d267dc5d6fed93c6ee5d86", + "0x0aa0321ead741b1db064501b9ee39b95fd2ed9383ace799b74acf7c9fb17e6d7", + "0x1d61c77fc3eb8b8ec9cde561667700c8c61dddeb9c71eeacf70d42802d1872e5", + "0x2d45d46c9dee8c047f5c8ec85cdc6e6b6404c3fb9d8532f3e06dc70b819927b4", + "0xedba005c50bcd4eff8d2f5c430f004ea439db65321636eabdf6edaed31131e1c", + "0x80ebb3425c828cb1615c5505305e9e1416f8647adf2644d2f8cbe27156486009", + "0x816596f489ca68a99f03e38090b35315cd4d33bbe4a54d3e81a8b374f1ddab02", + "0xffe272fb007cf814281802d49fb30d8e84711acf709a8d8edb350329248cf99c", + "0xfed190cc7746b4e85e19f657d79fffa108f10f7115508ab9c52bc1d1ba920318", + "0x23a6ff18bdad27e262bb016724cb7fa3f164523eba1e16191b9da2d65bd65acc", + "0x28ace5f3832eda5848d7c21fc4997ee814165ed45f34f2e46c8e2a4ef301f239", + "0x8cce0915831c2ec3019ced1030b414f033513afa88029d2d55570e2b0b83650f", + "0x35756d833c3a67144c6c7292f28264198c5a6632eb744c77f6ed4d21342975ee", + "0x8ac99b1c518be7e0d3c4da6393257efc4eac41dac41273903c9495b7c4d71a10", + "0xb46728b2495786ee58765258e71afe2db320732df5a60b21b77bdad3c5a0c706", + "0x98c62e6b66763c922cc4045673a2d67bc72b481b606cf6fa895f8ee2cd353ab8", + "0x8e8461eedb33b9070fbff30314ed2a2ac3aa765535a5cbac470bf5064ef17933", + "0x9fe2a0ecc7fbf6269a46a96c7c8f0796d12e5a4cd072d3e1ab70cfca785e5ca1", + "0x860b1a04a9f9eff2bee94f93597947844f8ceeec9b19d8039308ede70f327696", + "0x9d47b15afdf4cb7b7d9838645c6c0d5478738463ba4eebdbd19ea3d9b1d53d6c", + "0x56486096b7b8c5c01f105f695c04e347af713c7cefa9319d722cb0ce42c37052", + "0xea440ea41f71b11f1548ed85ef28c08ae1a9843faa2edb13fe421bacf8ab2f5c", + "0xfeb0d625bcb46abb76b410fa12cf10ef86b92b3dc9c48be5ab6512e0126efdb3", + "0x9bf4e08cfd8a7a6f6cc45ccf88c0001ca222a9590b0acbbfbdd93f0f7f40057b", + "0x674c33ad62b3e4c2a474c3e3743eaca2034f3a16b64d60fb05b2af842e56be67", + "0xcc2a6a0dd3a20ae99f22f4775ce87067ef38f47cbfc7e705d333a441f5255a45", + "0x30fa202ff1c9514fc22a16b264a0dae4e41d51a8d35bda2ea3355c4edd17dc0a", + "0x03d0d6e4c713dc4de6bebed290a3ee271e0d6bc6be084842ea4fcc20d8c36905", + "0x8aea14250c7c3838a32120a6ae166679a4b36385c056ad05a41e6a0f22dd1389", + "0x5453ecbe40548415f453f6a7dcff402767f2821d3e75949b89e2c9be0ee272e7", + "0xd3b3169589c2c9fa5bd965e619739440968257285fdc01378ee392b204985911", + "0x7dc25179540b2148d19dab749a65e1b3c55c9712da3b2b940f8a3681152262d6", + "0x125b29f15f1127506d7e68f997d932a9794a7cacc6ecb5400a80e7206104cbd0", + "0x20c89a0cc8402f8baa00280ed7e02326466a7df9ee997e36d3bf2326ffdea22a", + "0xaeb7820eec6269703044f131f12e20218a52a536e5b8329f11b27457b6ac00bd", + "0x25923d0cf3b0497228fbca55fb1b1b4ac28ea169fb41f718539b437e8fde0a8e", + "0x5c25d8caadad41fe789941971a1c64809060650796ab529f7b0a289856d6ae67", + "0xaa5e84048dd364bb18a7a5d6fec157a87783847b9552e7dcb2243fedf89c5714", + "0x4d3faec479f473762d668c9fa6b1704315c5d89eb48a016379c02ef1451fa903", + "0x8412ebd671ecd367eb6cf45b2a2abe5e10096c6b96c72b7479c3392580b2941d", + "0x3e8baae2f9889fe0bb0c7547fd5ed3f81a47417c93124f63edd66629a7f17392", + "0x472ea5435c12559b19d6f61ef2df40198884ca7eae724c82620319bd3d8cc81d", + "0x907a87b8fa390732d53e2fb02421a78a3043880f0f0d0146c5b9a3d042712055", + "0xd9d3e39439d8245be1062ea98a0501d3907bdcba9b3eab50bfb2a997ddaac300", + "0xb12b8f464c9b087e5879c99cb59f63be792816997e3171b5d37a38491145fe9a", + "0x1c3452c0f718261a287ac05b0236961dfb0d1a207db5a4b3788b0c2be714cf59", + "0xc605fcca5afa24601d617f9f411c6d1a6ec946e6dbadde90f86a8c6dd9162b48", + "0x8f410e35bae12b9df515a8541d3ab367361d27290da23a91407caba5e71862a6", + "0x3173976e2beeef47f92e4f2db7b880324dbfa74f941eb26d57ef7f33c8faa506", + "0xbbf8b7f6bd7361b01210271d204688e456298255dc993878e3b99537d8071ac2", + "0xb00806dae41ebdd2e9d8755692bef1ee78b35ccc1485ec0126738cd7d3b3f181", + "0xac7c088e85c3398748882f50c46b6371e3bdfe188cb5be1d142de91730c69d16", + "0x10786112c74a2535a8d600bdf3e9eb13dbe2c04b1046a5dbfae71abbc4773d95", + "0xbc688b1080f52022de56bb821df73636410c1ad58204e48d3d5b6c999f1cdf88", + "0x99950aafedfea8c08a0d99f9a5800dc14c7d96240e4971f2db42282c887ddf86", + "0x8bb3acc1d164f9e324a7f0f6306ce47f1622059ac04a55a6f7dbdc4cedafe50b", + "0x91c7cb89aedf750ed00663d2dcbe67d5eca7bdc32dc71104e5861d4c1ef1ed23", + "0x79da916b1f18df0cab751103bb5c6d69c32e35365b98ada01223925b056f45dc", + "0x7b1f4f8548549ef54a201cd28673a3f076585eb7adfd726da59f458d06c489c6", + "0x30572d9b32b42d3156d1bb5e3248d5846b11b9a832977e15f3e6671271c46f06", + "0x362dafc5fd19b79069205bb6953012303e031e5c65625606716163c47c9cec79", + "0x1e299aea7e87a8788479a2d8960bf7ba0972855dad26ba56d9e78ff9384523b1", + "0xf9e2e3e3dfaf18362c504a7e58d99a5bd87991224540107bddfdc4de433217ef", + "0xd03977ccff88fd4cec50c30e5aa412789a55d67eb672a3fd29fd6d1dbad6580c", + "0x6889ed98bc721aa1984f63a8ef156b57dc4cbabdf29bc81f583965048f711a0b", + "0x3fd2e5d953fc753e6c11560137cdb89ce966c9d73ca744df3e2c36a1708b2ab0", + "0x98626674be81f0901d03aca37b7cbd662cccae0b1a5fab59a057fe098ed3eab9", + "0x36afc2241a3c00823cea4cb7a384df24e565dee3c00697fd3646e3a30f7c4e46", + "0x95801ff6eb6a43aa8336715eae4a0a06f0c908c484350eef36201a791149cde9", + "0xd0268e76f84b33e354ad5de193995f0d9098209232cb031eeee262cb7a697a19", + "0x4bdf69ca782dfda3d88e9fc601033a7fd214cf0d78ae61963f566e9695929f0b", + "0xab8ca4625fc7c9770101ddec2e5edcdf331d4d356c146e1f71cb45f62db5aa5e", + "0x055db14c4e028755e2406d5510304d2d9dfc6cb8f3e229b58f44943bc57f574d", + "0x7321bd951ef793e9f5f81fce72c3ca78035ca59aaa5147b03058c40508757033", + "0xe9ea5e0399e5ca5f07978aca241cf938af56cc90eb3c66db8e9a16c70fc7994d", + "0xdaded305209447473200d784e50ecd42710a3dc8aeb78e6aab0951ef9bc6b670", + "0x02aade19bafb80787c86e7605737a889805d838d4695b96114532f8c1b733970", + "0xc2a88372120acd3f690d39978bc9710b52cf6fe9f40deffc0c7a0a4e72525f97", + "0x0420fd3f6ff5a4df834f6fdad99c2887f80f938030005e990762bea15dc8e1bf", + "0xda3da92c30e6429071b6231a394d2a744d2c7c4c63bc6800afc5197692d7a5da", + "0xa1dc61e36f1d6c09fa45433bfbc7f2bf77fe883eddd249c32cc907798beafc3d", + "0xc9609a072acebda03749cbb17fdb6b5fb49f41c078699abc4b8ebdc897b88d51", + "0xb4fd4219279a01174526d6fffdc353724d5d552252e484ad6e5cd87c679ea07d", + "0x9b48fa126014fd94ea3cfbc6ddb500c2468af5b54436c7217d2983699e523a0c", + "0x62cd2c186e211f98eeae6ad911c8bb3127efb0106b21b434b00ccef501e0f720", + "0xc4fa6342aaced6040c0d3fd79de167dfbddfcf2704d4354e42e8f825c7c05333", + "0x56705d46db424e6dd907bcd07f91d564178247761c4550475f24a4891099678c", + "0x4c0bec4521c482f49203e761c10d1d51db624b511816f97400ad3e1a3fd990da", + "0x7f4cd9a2f9f4d41f67c4cb53e7835222875cb96111f7db65354fe3e8f247d0a8", + "0xabe555d8cb951972130b4eb1f94c15bd79312da434be0bd2da1b5a3ff567becf", + "0x27120d9ac5fa90db2b93757726608a791089a7cfd1fbc71472f234a1c4f86cdc", + "0x45c3270ace69e133ca044833a3d409af675ba62487e459b92311f9b80d85a6ab", + "0xdf11e4ad87bafdc4b05d8823171a163dd313bda35ac993a7b34c3b8f0ac86ea1", + "0xe72c28edb8b969ad476e1789a8705a31fbdab6a1e55e203e55f4d05d2ef88b34", + "0x78fcbff7f14b860a113ec3cdb6a9b91458c098fa4a99e7c7f29b0f577a85e43c", + "0x241fd4a382f6e64cf54d0528947f18c7f6698f99ac235fa06f90b5ebc0e4e76e", + "0xbb009b20425e3ce974b560db440b1a7f6385d57555ec5741ece7e4f7bce6ccb1", + "0x771754716d82e49ce4944f3607753a9c43e30656b42406b27e820abab73e17ac", + "0xac16e14640fccead156e51f2b26ac66337d58749d9bb533524ec78ad8a40a293", + "0xd20a064a32afed8cfa0e85a4d697a9669ab6f4bec93d54d548f92d3a8f77dd56", + "0x0418d48b1de05b46b88c84ff6a3eacc10b704a69fd0c25a7d82daa68c0ca3bfa", + "0x4edb9aa5d744da9a0cc6d363c46ef3850371ebf78d0450aea6c0c9cd39506230", + "0x63ad4a2589339da2cd33b6b06a1a5c2b90cef6a54c228e5cdb56ffc2b964c043", + "0x1d8d676b22084a5de0ded6884759b22680b6b76618dcc50f8d5f2f779dfaa3a7", + "0xb2b40fa255a2597028aeafeb2fd660096eb3df7355aa40bbb3bd35d0f46d0c36", + "0x408fa61fb46409762efe023039365e499729fad1ad034ea2eb5403e932ed91fb", + "0xa52583f16a18333ce51921137d7798333512d169083c6f1ce0d19d2cdf987f9e", + "0x3cee0b4f077956c27c8c1f58b84ee913b0cc9cbe8adfd07adfeeb9e8caf26266", + "0xa66ccd61c4533737baf39f8fec137d8fe80210835ad0540879271dbd3af53e3b", + "0x19cf8514cfc7cde0bc46447c8cf958d84dedb209a61e5bd3044534bcf97cb2ed", + "0x0c8a0674457c13dd055e444a17bad6b4807c227d8081d671ded810bd503beffc", + "0xc8a8b09d147bd7893a9083550d0d39d05512c1b1c446f72f7bf79ff5b741ddfd", + "0xc569e06d421cec0ea079ce612086a80e7bc82adcca3ec898d1c7bff4aa2afdd9", + "0x9b4765413fc66d454f2c2a7a1d4907276f3bdf5154fc73dedcb5687ce01504bd", + "0x87541692abfb6b1f430afee2b8fcca7e331e0edb7fee0f4dc42e2ef6433efab3", + "0x92e45ae97892849f10078b4b1569face9291839e56ec190cd99616fe770f46d3", + "0x9369c81d52cf56b7561dc456f6477192776fc4555536f8969342d41e74be70ba", + "0x4f23780b65d5641af919b766b9dc570e5a5d13e966f771a1e6883156b2b3686c", + "0x53a8fa1c6460d0f5354079161763b94178b389cc2b1330d97a56ce5e13f00825", + "0xc2f79aa9e1f7595a45e15911036ab2b7bbe6dddf994fed217c30721d730a02b9", + "0x07a57bfb97354c9a728f6bc93380f6c5e7057dcd107f89f4a1858f06f6138846", + "0x214f9ae5a6e41176d32f1c71fe437a63dfae185267483c7d5a89513e3f2c7baa", + "0x6197419c9ccee1ce58958dc2dda0c750f912ad18bad0dc9bd1bcd643ae529d73", + "0x7632d894957f18c41da9e735faf3a10fabaf0a0235353c311cd32713449dcba9", + "0x25d2db2ee0744cc1ac1afa856e9d13f55f90f9e2af01eeb1c9675bbdf72b4434", + "0x21157888df9f6eb468701bf2005991b02daa35575e67fa99e60ac3d511066eec", + "0xaa553126f5a778921264cb1c2d3f0d2326d9a87cfef2af24267c4bc3a1ae8c54", + "0xc9d1610b76424eb5832cb8985b3c8715e3ca304409a5332d785163ec6efa576e", + "0x58d78e7fa2f5d7b9af9b6d53b27450a0a9f564893d53b6fec000c75df661c34e", + "0x7dec6b777abc7b74e01d089817141d9a0f45a98e7d6da78ad2691225c167027c", + "0x84c7f8abe7f114ff004a9d1d6bfa4284be1caef351a0a3bffbb740fd62d304ba", + "0x8be841a1c98dd92005fb62a5194da915db550cfa77240ecb6fa8b375c525a6ef", + "0xd3876b275c360a3d76a18469864d40855db3ecc566881719dc536726d1338e24", + "0xd47522c440078e88798303401b916ab4cdf5210145325c54257308530efb451a", + "0xc08a583aac20332f7f0b8e2d322eac11379196054070a925d96710cc5cd3b865", + "0x83e56bb22351bfa12cf53fc7adbb04fafe3be4074e6f751487a73e70604b97b3", + "0xb25a850c7595f5097df4d99df2b0a1e42ecae64a28fff37c09253affd85e62dd", + "0xfc1c4b6927217908fee9946b766a15fdffa4a615fa0205e67099ea22a72ea5b7", + "0x50794bd6bbefb3e2cf9b84f559065bee47c33a55123cae09379f2e7a4cf7a752", + "0xfa1f5761514686f5a7ca204329176625de0d247142be6986dcbcd71a4464c2c4", + "0xb93b6b62638ba50741490e299d5c1949814d83fbfb904c115addc76b4a09d75e", + "0x41ad8d55077bd8c1f44bc0844c57492e0ae1fd873ffde72b6775df10912f8ff1", + "0x046677414e5bba9f4aecf988241404e6558c58b1811c253febdcef58eedcd2f7", + "0xcd0e814b52fb172e98dcca5c9d640cfaed2d50ec5f12b9d15f0a94504dd764be", + "0xdbcfeffaf1e836561d87f020fd06c3458c30afb8ef083603bf48934a5115a5ea", + "0x9312cc08939e8610cce5289bec83c26ef052d5747fbaa749af918f1b373853d9", + "0x54755f8ac987c10d6da0e1f6eac9682467c7a96d775739b1810b376d6d67b6d3", + "0x6d0f20b160213c4be8270c971c422c4aff4e5810559bbdab83ee4391542a3e6b", + "0x259c861dc36fcf491304b2dc2273a7215ab8f4da5e392ebbf238732cf61b4f2a", + "0x93067f249979fdc97a16a170234c5c1aed3f7a464bb9d9a46057fa1550a64dfd", + "0x18fdd3b593a89617e66d7060613e87dc816727b1c1c6afecc470f6605cafe0c1", + "0xb9978824326a108c462b054e7da40ace8e0ef45c3c42c575a77a6fc910ee483b", + "0xd5adb9428b45bbfca489d7b30ecc63c48dc2787514b3ad827560a67061549991", + "0x0a28c34a5a37f4fe902c79aac154ab32daff0e3e9e7bd3c64bf83b8e954d8524", + "0x3fdd8e2d9a73672721574ab3644867c43b26a9aa2f061a1426626e59c9df2abb", + "0xb0d89c7b583a79bd372d7c1e17c3ce7e3e3ee911bb722d202ab08a34c5d6569d", + "0x6ea715a3cded2597ea542a3c69a3783090e597f3532aa6ac3f9637c53a83fb32", + "0x576708018cfdd666d2b315338b0b62097332f8021328bf9e9f6c81351839de50", + "0x5ac778eddc582036fb7f73131d15b6b0a22a51bb3e61bc47beee25c8f2f46aff", + "0x905f95bb61d4839c37d216e2bf525927e033ce0894618da53f6633e461c6b9ab", + "0x80e5952a7ad4e9ed143a6015b10d836b215d4991b7492b6e3bba093876a285fe", + "0xf0c503c1849807af58b853e0c38a74514993c67e816e39e56bc1f2aabf79ad23", + "0xecb0dd14b8793d4b7c100ce1bf1843a4a9c398deeefed1c470af37bb04dd130a", + "0x53f7fe2440deee9b495164db0393a0c5731054507169ef74ed45c1768a5829a7", + "0x0b16eba97c99e89099d43a814b7fce56e52faefd7cac0adef4347cbd41b05d0b", + "0xce5d4c8538f18dfc3986f9b70eada9b06d2b0fb1727f4e8dc0cf9a5723f92618", + "0x6cd0e8fcdb6de9552f691a5a766030513e1a6400b96b880b777d5ef998448893", + "0xecfd2580df0001b59fb4925567ac9136d569f9f716aecfcff595a974896b844b", + "0xbd32de518d8888409a81392a8e5d911df0a78c03bd406858e4dafa15f3ae569e", + "0x4c1696b4c59c512675795bfa8565b746e7e14bac6d4bfcf2211bf3654778aad0", + "0xd5b8afaedf12c19f140ebb7f14f297bbc2892442685c0d2f2fb7ae486366642c", + "0xa51a14f5bda1f34dd665920eeeb75ba76ac58485bf80bcbeead97db2355cc421", + "0x45776aec22f3ed359ee513450d2d83200309a27e7a7c41105df551ce2a89f430", + "0xe6fdf995d27d4341de395abfb86cc31e6e585bb4388f55faacf1cb611cb619a3", + "0xf04b83006f046009ce0f6933786a10148ef22a3826526507ab7ba5170d70a43c", + "0xe69442e3f1b4d1884263c1df352cd4e495f4eed59fb4aff504f23f9797e767c4", + "0x90b05b57b3a883e2a3580dbe74d46c5d509c1c49c6e0c36c5164dc152b0e467e", + "0x1b42872eb1de196ce99fcb7ed7bc689e4be264d8b2a0ff739293a44418b0c6cb", + "0xcf07be269131a7c6a3d0b5124c509036d13bb5430abea5d7da6f1f42f7e9dd32", + "0xa9ecc0074a389cb728911370701b39df4d8d54895e82bdd3a1d86a83d35a9cc1", + "0x3be8217901178219b0c0abd8b8119860ed3eca9246b67d9c7197b0145db578ef", + "0xc72a730be24952de0e69e0e2d3f1127e2852420b72e8644d4d64ffc8cd77c556", + "0xd91469aa490eec2d9ad0ae6ea7f97241d4270eda58b180c4baf46cdf9def65d9", + "0xa4ed0744c46097b71cc299d50fd242e973de0eb7e50c4cdf668a2c3fe60ef120", + "0xfa49fc12ffbe3a2d8df96681944f668943ebff0d5eaace4f9594680f176eb7dd", + "0x688f661cd877c1b8f2367ed239d19013439005869d3f80a0e42f4a4393cf06ac", + "0x4261e3c37a5015c86b5a4d882d30313b386c7f30add0655a92de959b339a5af6", + "0x065f0a441963b6c8c47b1e293776e61a61cf5957321200f8925989673d7bcf51", + "0xeb756a0ad46affc64174d36f9a4534451c0c330607784c7b319f2553fb45dd68", + "0x5978dde042efc64b3a4b50113073485522e9a858e9e7c4539c29e4250d48baa7", + "0x0ef4f00ad8ed3e6a18011ddc4b06259b1a12bf2f0658d8a87b69fc883738a6fb", + "0xb0d9a5ba173c2f9f2077ec6c583b9f5af22e9f05efa7b26932fe3c00a54f1e2e", + "0x9a4554b141aa2d16b260c1e9c2ceacaac3b6d34126a44de64cffdd4aa1c40863", + "0x6822285cc8d3df9430876f4cdab09c97e1d7a1cad3a614daadb52e677c7284e6", + "0x15e8f3d710afc5e3d9d56f66514f14582025589d6d00f14947922f616e37c21c", + "0xda4d1e4778e85e540b4807540f14f637a146ecd7e9af50e454ae013350f54147", + "0x6025cbf0be47271fcbd5fb0ea0c2cc5d2aea6a2e1f3aa481fcdbf97fead925c8", + "0x049f3e19111c244e690eae9941069890429b795adc3f26fca4c0ebeb689c75a1", + "0x2248195457159bd74cda864c0761c24484339dca2aa64e4ead3f8e2dd29d1a9b", + "0x9523ad20e1ba4ae99af08bbd132e28fec6b19af8227f607cd4e1760a8b55429d", + "0x18f299bec19554143910ce8a2b03ff132cae2efbe0371ce21a0fd886f1626c1a", + "0xc7ffbd264143d3845af5323cd357573b354af68247cb48ea5398f888391d3006", + "0x5df7de2a3ed545d9b7831879ae936db0efb7c9646d2122059692f19a7da38867", + "0x950bf5f5fe8422731ffe61b0bf2d24e054332f7c248675b6577fd4cbabd9eb0d", + "0x0515bee11e44f98da2e6cb92144a00a3cc7452b2d3f5dde7a8175996bcd803b9", + "0xc79c7e4778b21452b953d4b36001185ffbe0803495618f13d871ae5586b8d5a1", + "0x81fffc5df9063ac7b13ed71d43069390a03f2561e28924f7ade0036ab436ccdd", + "0xb8e43dbbc4d5d02b983e369d010228334de82205e2241c0b045a1b90b69919a9", + "0x16ad6c174d9bac2376e7685ad8e74dd0b53cd3d9de280f460bd9636903c9eaf5", + "0x42509578a8e8be16c15a4b61e3e8049792f16720b2d20513051c7a0217a4010f", + "0xac44333356533c7b57bd5ea97d03779b1de70540b265abad27ce45e86d682866", + "0x3d9e810fa8816a733d6c94d7166c51e8b8ffb10bcf064579e5e3478d44fa0b0e", + "0xdac55572aba3c680cee7c38da108891b4221b82756d5e3b0e818335399c18d87", + "0xf9da7e1e81275d5f7b252af906b44e56680a837ff3bd4706cf2d7fd78149f0f7", + "0xc9e71d866d1b96668d83addb33d0de8e1747b1ae6dc374f328883a97401bfcc0", + "0xa57ff0e121c5e6518f4bda70c89142bfa9f78a8707fe7d5059798170cb6d1bdf", + "0x5dd5dfc9822536757d38fe697dc7a8e8a6210df695fc7382c6598306974f91f4", + "0xa3aac9ae7aa79113f51890081768ec916c9cfab4d4f198983c445a84746a528c", + "0xf9c9d8c2586972c50a98cc9af3233b56b5c43fcb4d205f5795e0967fa82ede1a", + "0xea135bd9c04c655198e7b9977296bffdcd33035bb2de125df262bd682093f4d5", + "0xc32711cc3520d65b474b853b7dc2fa78ed8daea69f661597eff9fd60f38f06fa", + "0xf7c72afe89f66e6f0d434ed19358423a5b9674c00c7d249ec9d8dc5644a646de", + "0x14b59f2c914e44fc8b9bffa3bf18fd7d0432eeb8958c80191d3bff9b28fd2873", + "0x1b2064ae4eb38ccb47183f5d938772d3e3c1c86e436889afc4fe9623fe08cc2c", + "0xc75aa98988b6d871c062457c2a73e74add763b40ecad02853f9652a32cdac97f", + "0xb015d9bb84a5b159cbdbe73e1205427e92b99b229b200f7a557669042eb04644", + "0xef1f0e59808a41024a409d71fd05f4510791200e87868c0896b5b48f2286f6b1", + "0x95b7cdf0443db39d5e35d8f667592658ca70a739a5843311a663db404b826cd8", + "0x4d62bce2ee71ec8bb3953c5911859813ec019c107e8b63fd7c49f36e62bb600d", + "0x79b330ec7e8d795d16365cc11bf23d5e6de263d896887461f5fbad74468ad8f2", + "0x137fe92900b3b08ce34f4b22d6b39ade16971b0961cb040d9e1efe13c13c93e3", + "0x56570773f1a39fc7bee4047d79937ac40944f19c4fc743ea75090ad571fd07d6", + "0x45d894af6ab595ab3c4ca8a0fbd77e1f6c411cebf035f82554037225879f72ab", + "0x9f8a47bc9e746d39945c846b3523c3fe417951d8570efed8541ff676b185c622", + "0x402d7d71c36549dd46ff4e56ce788ebca10499f6272299bab8849b0780bb2a19", + "0xeb674d0cc0f44847abc94fe8f908a24b47ec653ec9ca4339847c47c34931a62a", + "0x2bf92ea24bbee57321c3f6cffd59a21988d4642a03b886cbeff1d9961db47ce8", + "0x0821ff27907f3fbf75a44053fe44caa21da32cb897f80260acf18f1df7532f69", + "0xf56e52533cabc54189915c820587eb9a146620ccc61c146bc44e51328abd75bd", + "0xf30922e1ab58f7f7ea0a23a78f3859bd4dad131d73451f0f0afc8336d0c71da3", + "0x59c7501eb3f360fbbcd202bf418e4da1c780dc30ab2a63aabc76ee7b796bb18e", + "0x5150655561b362b0b297cf34f60a4e0ebc53b5a682143f79e0fea7887948af69", + "0x74ce917ee76de4ec5e0ca91de339872d8a89524b7e6954737a91332264e51f57", + "0xa6b2e446141af69cc7979909e30f0999717d103bfef7af10b0071d5dddb4487f", + "0xba050240ca2e47d235be673ce32f8c193d5d2156c44ca90305a489028f6d9248", + "0x32ee57cd87d4bba9e9410501d554ff4811202dc87dd2bc36e5151365c02761fa", + "0xe17d772c0afa7db15d5adc07d94d3b630f68281885f27cd59027500b507d7e8c", + "0x80a301efe9d947b3ab0bca95906a53384b8c2280d833ac8cac2d783841b20fb3", + "0x48623ef027d095bce3a7bffa693efb659ced1b6e4f2f1da87b6467f7c9525f76", + "0x7cebe5fd068e30677c782929be24e61c2a289d39d9310d42572cecc36e424f22", + "0x99ba5f5d554376c287152a0dee6c169c970c576a43161ba84d8c13c36e1dd731", + "0xb5bb3c578651a786f3ed707f34efdd28180ef3c6eca9093588a4468959017945", + "0x7c87c941ec1b7da99ef4755851f93d4adbe342a8329bb3d6a513083aabb14f28", + "0x1ca2369cb4c5b88c9d313614f53773721fe006fb886ebd921b858cf523cb996e", + "0x7b3a8b8d73940ac7372d4f5136d5bc8ee4c0eae8838d8048a23561ef21d2e171", + "0x9111664b840b372e1a2910ea5fa67ea5532c196783a48e8abddc717fd036ab46", + "0x6225a2962d33abfcdbfe544b02f353af744cbcfd6117aba2833aafd83a41f3f9", + "0x1e68328931821dc9a1eb9caf4735c49c386f02df732defea451f8a90a3e320f1", + "0xedc8a7e3062559549e84e95cecc63e3f202232a1f253120039cffd5adfc387c1", + "0xab7a73a42309ce85bbecd282e8311530729f49a1847555aad48bb121ae707cc3", + "0xc2bbf72f3fc333f9256f2d6554bf6126ce50fcffcbb99b4a1a55b5a0c0f58d76", + "0xc3fb4796f448973c747a6906a9f52292d448074472b4a6a9d9b51cc8571ce789", + "0x7a7f46512add26bbffd3906291909bb50e29146435192f8ddd925f4e60a1c436", + "0x5c664835a8b4ee9be47160f5710be7e79933a46e89548e9a4b49902af4b119fc", + "0x6c640519fc5b531cd20f7708a161dbed4c4a0c3417c2cd2214308f65091964a5", + "0x7f9ef61acf838fc5a26d8561a9f835c5f381d8dd65aa9fc21e3218ed96602b66", + "0x177e34991ebb700207130c34636d0662e24bec81791690f7bc716bc1a4248b6e", + "0x092f424195ff9f4207171af28280f7548a3b44e968d9d7214ef7f1a7c2fda09b", + "0x1c62e661f124c02084441f2b3802533a0158449ac70bacd2e3f2681ce1647e82", + "0x1b72adf9674c7e42df8e3c380fc5a69f0cc8fd82830ba86ff80b96bd67b1a4bc", + "0xb0f3abf78e4b8054cbec6db6af20fd64a3a0913e6c97bc31ecfc8c02eaed3e43", + "0x25a7db4c61648685d170d7c33f9a76e67ac5fc91636627070da0a7087fa27cf1", + "0xce7ce517e38e2631ad7bfbe9c75602dac40cddc506fccf765299b739fcfcf21c", + "0x1ef40bec567565f7a429c16259de4feec8c2fd89db416399a0578af6e15066a8", + "0xff3a087e3194f0e71526506b267990a64882877ac58816b3c000504f20469ec2", + "0xe77220a1b7793940f1455f370dc64d2422d9daed93981edc0e07ed9c1cbcd47e", + "0x73d9176461cdd3e2464d640e15fe466a38be3655ee9ac0197a293997350d70cf", + "0x5ec8a2c5e41cf0372cae74b7ee93c0cf53453d02c9988f07b84d0e650b760a3a", + "0x6b81a03d22544c807eeb3959b62e3c9d1db33a5a17c17ec9a2aafe34c1606b4c", + "0x19f1e32d9790eabc47a204a42684395f2c1cceaf92d391b76b37bc36e101c6c3", + "0xc8e3d07aa7539a7510657f577975630f7e56632d14b8b8fe8ebe0c2dd0c76473", + "0x5912bc3e2cc802e627779ca60264588a4759a8c73372229dbdea797cc8bbaf64", + "0x8aa8a091df8eda18ee43c0f86a20861edcd976036e0affb4a0e8a06bda005d4e", + "0xbb171d7b079dd7018d851a9b135bf68cc7e36d3b1f0d9d1b83c9dffcf4e0c8a3", + "0x71992470a938624f3c36eed0f29fab6d32a16697a7789a550571bc3de728ab4c", + "0x89e16bffcb1fe97fdbbb5dcc59ee021d34b2320c60a136529f6a50249a72d7df", + "0x5015cdf8900deace4b73854486ede880c53cc90a565d9a53dd418e65358b9c2f", + "0x2b7212ea7195af6bc70995651e18cc17cb84fbeecf89fcc5b38dc6951f638423", + "0xa424d92d60d80fe869168a5723f7175873b0e7be78f59f1f1d8a5322ea38bb6f", + "0x3e80f3b9af7c5b9c3703e7222d99cab000671f4c7f13fa5f52e97cbf1e57a841", + "0x2dca444269a828b3e9d9fbf3b23064be1df079d2692477f1052519c8e57b7dc9", + "0x1c589282a97e0fdf4d5e7ac47c4ff94c6c99908a57bf167760ffa08a679d38ca", + "0x5229fc9f3d3f1d918c8153cc0494e9f64f7fcf7e0d90611eea96e79abec4dc4e", + "0xb961eb2a4f5b79f2a50f86b6e778d677ae66b097b66e64b523ef4c1728edc6a4", + "0x887008f5b363a7f96575a2f9b484c5c538188ea7d73d59f4094019813b315d8b", + "0xc7bbf858aeca10daff9994e6ceb4e669f3967a1be669e7da16e75a17201007fd", + "0x341780ad829fd7a4487fe0a08bb8aac53b6e398b222e7a6fc4838134ce5db693", + "0x2dd07f6f165cbb9cc1b9c6f862dfc4bda0f08af1f9ff73db0b52dccd0245de96", + "0x02fce474d055cf1d31e3699b8346e9ad0622f5496e82d8d2c19238efaed31133", + "0xeb0a6c4fa09ade82553d9654eda25df17156818d9c80af7952bf31a7ba7100ee", + "0x69e26f5f53c37411307601228b20291a1f85529dcdd12eed709e1b19655c5210", + "0x5ac00b8a672bc0b030371962a598a85239549e056492db36c1f6fd994624b083", + "0xb8d4c07988c47ab9310803042cd84aff609969b3052995727267b02f26966414", + "0x82ecd2d893c7197dff80d3786680fb57289913d45932ef70b28658e1ca4bcf2b", + "0xdaac19260154173ed10857a23c0e37bdf1eb3512d44ac329f4e8fd79a4939535", + "0xd19e5bd6d96161bbdb7c1c45da6140d74a1fcfc6e4e505f2102095b0679cf950", + "0xaba41da43b4d257499c8689ada49328c48a87774f95f137b2481fdd9dfe0d17f", + "0x611eb2b3cd163d1a88dae6cff559ad4557eea3b4a9a91b25bb974d050881dcb8", + "0xb9aa401866b1f2764c7d78da275ca7651181e03f38e094a65a96a24ccfdb5f38", + "0x596f31ccf8307c2fbaebdc22380ba8b6e082db9c140813f3ba836ec3396dd488", + "0xc0651609da5f914b61b0a51ef8f7a98b8eacb1c9c731f599bdfae2ac5ba9f37d", + "0x8197380cc8d9596d69427343668fbd758bc63837407b6dad5620b8bc12cda71f", + "0x041ab30bb100e4eb8f8f607b36dfc4eaabcac2b7bde741c0d1aa223d52f14f4f", + "0xc1beb77df789f7cd8250a55f1e13b7561f4580e3b42721ea3afdc81d836a1cc0", + "0x123cfbc9a05428554bb669e7530bd929498d358b45e89b19a0a1e48a9f5970d8", + "0x529c9c157be0e1057fb5983a9d00b3aef79c61a26b7632a1026aacf34dd49972", + "0x84df0ce45fea2c31c49d9989a45305716ba3e23ae2b855a26751bebad3b08838", + "0x6a65d7af6124e087369ed933a8490ad14e40b529d7b52323876b1cb5a9b54e18", + "0x2647086ca14ee16a02e1fe089de3e9fcaa5947142d468d37764b47e7e8933cda", + "0x303ed30b9b1f53a511ab4ee724779934697f055d977c7bb36c2dab63d527b06f", + "0x997b7bee5759e4c8697b4e8a38797bd95a52d2ab5e1f1a8901d217f033ab87f5", + "0x2c8ec63154ca217d908152cc4e4053b2fd32da386029d98903e0d10c44da90a4", + "0x72cdaa2f82ef10c47ca52a1a97c49d914c27a8a01b7450436de5aa29e86cc64b", + "0x3dd2f105416b34e33b6ecdc77260145822afda988d450ebae96062de1d9acd2a", + "0xe4482e603f0ebf78ca27ef9221a2a530163840c314c3020ce5bb53918cae541e", + "0x3878513e46aafe752bf8ccd8c1acd4da91193d590db01d569809a2e9a93c587c", + "0xdfd49d926ff4336aab65ad3279d53adc9f480ec7013a5bc9c7256414d898c5de", + "0x4ff0b8272e0526e46492cb0425e1ae6b5934535bec52669785527233c4769330", + "0x88970fab0e18cb8c9e5d000356aaf36f3f70988c9561e864abc2137364a10925", + "0x1d5e7764fe9bbf5c6476f103285e2f4d6b8a8e5a1f7c23dc9fcc59c8f6ea4a11", + "0xff59550a542be505c23cd4dd0de6f6a87b8838183bd9e03a51c618092853bfcd", + "0xac13a94e20006d7e06644e28a7b0615c375f6d7c4f2659563e031de248909f87", + "0x81472440d3ad35a2422bece1aeb655dd47e63b0f24f7794e918597708e6b01c7", + "0xcb9c81b6915d15c3ac7eac0b65e95a29c1b934b069fcf7b8a6941a5ee502af6f", + "0x30c94b467bad4e952879289cb48efd9612089117ab8dbd28a87c25f80ce68c90", + "0x84bc7389c6a218446a98e16432225addbba261192b599dd1d151a39fd02d38cf", + "0x6ff5878e4b8de1fd42233ed835e5fd349ad365a74cbaf6cf8418bf5574914b9f", + "0x61caac3891c3c388298c68f8e2496ed08d06bcb0e95faa2eedd7233dd5333479", + "0x792cbc810b04f63603fd266e0a85e515157ea9f8ee243f5fa0c627d8ff1830f2", + "0x4d2d32d295665fa2f41a98e4eec29a7184de1c12701bfd545e83c647a77539b1", + "0x674c354247c7593f97023491cf466ad3330324a30ffaf3cd6c1c13a5ae541778", + "0x69a03e896d1f183fb6f420b586bbb162e87a2668de9e0859955db7b35ee5da1b", + "0x8adbaa9a4bed08d112f53ad5d66a914663fa29f6cc309dba174d4187dd9f4dd2", + "0xead5efe56d229a803476e9b5211374bb8ba532b678c07602bb9beb911f92bd2e", + "0x2169fd555cfe7291d49cfec439f4879cba09b36009526ed50cbebf0c68518cf3", + "0xa9f3908e1b46551ed53044a98a076f3278b989cfd973fbf7bf30e65eb5e09bd0", + "0x2cc190f4e57966ac8a59c45b31d0ffbcfbc2c4f6e7b4da3ca02b7a78d363b625", + "0x66b180be3a766483850011fa659440fd065cc7b984530a48b1051f21db344c3f", + "0xf654ea2bb26040c738cead2fe6df453730d516a544808e056bacb4cc8aee5b61", + "0x14febcd6329bde0de7c39a6e4b27061684c5a0f1e0a75c74295d791cf5815631", + "0x39303b5c2788cce4c2a47f296c37a95c4dad9b5704f13627e96bfcf5fc211f7a", + "0x02ea211efc9e3bd7b416582ba7cf8b8ccd9b01fd11dfab2fcebc4102a3e76eb7", + "0x7418b8743a1fba58dd798c12637522e68e268cd9e71287ea2abab9fe70b6f181", + "0x627b4c521bd47a98d984771bbf9497b8abbebc5df470b5e60c3d9be8e9f01061", + "0xa2575284050aef5dba386242cee7dbc4de8e334053f2f87a0bd6cf51e510f40f", + "0xe8b00bddf340b8fcc4f176f9526d9be0e7153bb0abd92594ce6536d3eabcca02", + "0x509a211d2bc585c6d83499a494f48e83e1a256ade7b90c84cc5fa06592e9dff7", + "0xb2c97fc95eb44e2a5ecee8bf4e92413f47bc18969f9e8791c4e3fdff7ffd06d4", + "0x79cdb380ea040edb482ee38bf45fc5c211f62638dd7e646d0f8f41f9f0e816a2", + "0x13f31ba56364aad618e3d585cf84002fbe58a134a628c6d73980a0fb4d8fe672", + "0x31080bb5e6b367cbaab4fa009a396393fbd379e19919d6cb79e4e1ab6fa2f363", + "0xf5ca7d4bd7f24bdfb824e18d39363b56cc0eba3ebabae60b7c488527645ee6a6", + "0x0bb9ee237407c8d41b3b54bef14e81fa1e91f246d320bf59fb5eeb9763adcd75", + "0xe7ece4e32717b1bb8045fa7baa860ffa5d912a93893b18f369cd2695a521bfb5", + "0xdd172e1a93aa60817b8a712279a2ed10e0c3b45b6087647590b05126cc0e2ad0", + "0x70d8ef8d50501cc6203bd7b49a381293a6a304718562f7f1d11a48e5a266ccd2", + "0xc9c10a6403b38241a3e08541fb9d2ddce1f9628aae117d0c6914e556742a6e0e", + "0xe3f72751f7cc176df77b06ab07377d88ae9264af68f06ce37b3501ceb3065964", + "0x75cc24caafc9fcc41219ee47a8c541695549a87af67e1cb59c982f151852e6a3", + "0xfce4f9cc40e7be5c644441c381da8d0d437b5d6001ac4f8c62d46e09a5261412", + "0x1b2dc0b9bcc43fdf236bd079aaadbae2cc4e283a41e7f022f45706c23d5e5343", + "0x105cc7fa40b1bcc23040c985ef2a7491436a6eb8caf74f0a6c4db2b05f41f7e7", + "0x93dab55e5d19afadf0b459b1d36bd25938defc582756c46a76b7233a5b920a66", + "0xf0a4ff2c3abc05ac173f8f6f1b17bbe1814ac5c8a58a033d2f6fd0835d75b458", + "0x822ffdcb86e5ab449862f1dc3a7206d20055bec4c03485816c5000c8d009d2c2", + "0x9e5f44227664fb9e0cea4070f4bdbef213b2de16ac65f7d1c4fe045320b42218", + "0xe3f40fe777cfbcb484f762e2129be96ceb0e61b304dbd7a34d5e669c2fc6a043", + "0xc3db34884bffb7dc842ae1d7c9ef7f7c9af987145f0e52a9d1e4714b217cc989", + "0x4f0f574a46e176b718283f69e387c84df348685acd8b2b095179da5cd329d6ef", + "0x7d28be94c6de4341675da1ccb695da5591ef8439a54089422301bf17c8e01ee4", + "0xd026903b687c67675a1f87ac776fbe7d8162fbef1edf373deafc0b1a421069e0", + "0x0cdca8eed28676ed6e01412ce538e87c07424949f24c7e2570ddb204f9e8d86b", + "0xddcbfc563e7c00cba93e1536c97457db12701c76109c4b79dc6ee24e73d235ec", + "0xe9f15e76f41a3254465b084c938d98fb82aed1d5b32c36a32ef7e370615e444d", + "0x3b459d0be495798e0d32adea641b171afaab1d5b58d03486c2db39952c20ee42", + "0x710ea6be28b6595a4a5dfeb1c7f3faa69261d12225f603068d6fca65f531ab71", + "0xf5351727731791c227f05f4e53074edc5f05f649b3b03881122bab534c675473", + "0x63c053af0cb4b38553081e42792efd83afe8169fc2f4518d02a609c993e260b6", + "0x5d2da6e0a3775de05b95c13ed96bf812192e05148d658e089e48777b07731274", + "0x9619f9f126893c9f0813c491c53f62de09633462c7a67a1a36fa62811866ae8c", + "0x1795b8342f0dcc924b7c9193c03add9ff3ce7e76c4814d5a341b25c4548ac225", + "0x26ab4cd41019de5266246c8e3cc4dc548c3aea75362886e1dba05a991c99a3d5", + "0x6655c44c8732fdca6812a02085557067c134ec1156209e63460752706bfb3545", + "0xc0b341e5e1b87beb36a807a1b05fa60e9f3c26900af2ed806dbe0886b90c8495", + "0x7dbb637fc940b050b316a68b922093abbc556045e771d5e451b89afd782c898a", + "0xd6ec0bcfbfa0543edee7e1daf14816ea23b9ec9147550a429ae21fbb4c3cce15", + "0xdbf3e217b36210c9ad9a00e77a2279be099ce6d2539cbc1eb548bd7718d7292b", + "0x28b40a120f6f4a8954073fcdf6126970420c3b07d11066d980916bc748336e1d", + "0x2ef4434c9264d3f5020b0603332d8f2978c48931bb9ffff13f0a5511dc445b02", + "0xcef87d21a726a2175db8fc8feceaf74bc14c03da8a7d393898bbdbf7b9a52fae", + "0xfdc9159f251cfdc965eeb90d4126b6729a67b8a6cad7ef579e998b8d1ee0199c", + "0x8b9ab87096eb068fa8e60c22a9cf2c7dba8f3aaf3dc50b35076211f06cdaf831", + "0xaffcfdfbae220ed97611ffd8cec59c3d8893525afc4fa80f1e9e5e9059d6229c", + "0x3eabc4e5bee585620f90fc3fe99a4ccb61f1436a163204e6213974ad0190cd6d", + "0xa63add3c89914e5e45b7fb423dab187bcc4f45800964dcdbba86ec35fa26138d", + "0x3ef004844959c292a4920ac304c6a9ae9a69489de71695e0a94a4c678aebe9d7", + "0x686d86c07f1bdfd25de539741311e4d44d32008203a921bdf256c5af36797de0", + "0x794124283fb7bb9efaf5490a8bf7ecc06f71dddc114434c0b41bbb74d2c5cca9", + "0xaf549fca8fe67ac61d162b33e666e3d35b6060c06be63128a835f3e4f78a80ed", + "0xa807dbd0ec3ccc52b84d25279fc079b43c0d5a5e23a1527824c4b0536d1744e2", + "0x9ac01567cf0dc7ad4f4827f24ca3c1401f84f52cf7a79065b40f9896cf13c392", + "0x75f2ed3f6a0408ee86df855b1c6afb8ad9a3d825c866a69531cb804f42337808", + "0x635e5e4e76f56881860cdcc04da93dd39c0e77292fa4287c712bba280b9f5258", + "0xea58270e891ccd40ee9fe92aa176f909af2de878e6d5a13487220b7c4e6a3285", + "0x66a4a7407f905c75fa8a370787616fc4caccdb3194b2a7f1441c7ac8e84cbd5e", + "0xb3194269fea09abc3625fe81f6a604013bb4eb779756151dde66eac4f00fdbdc", + "0xc10a2ecac60bf4b55648c03ab9d583ab2c46182480df247bae62cfc46612ead9", + "0x4458e9e0e996152c8b26b376f979c9fd376e81a740796ba8d48e4fefd709be6b", + "0xc1ad256aa5ffda406f005edffb146c498d0ee9025145990d24b5a03b82c251a7", + "0x7557980fdc140ec3c671e34fe4c00176628a6223218810a04fcfc51cd43178b3", + "0x4ea66c924b5be9dd23a46a737c438ed2414eee83e4f8ef1e0ca748323dcc3b43", + "0xae38cf8c94c15e82cf588c0a1b5d1f2f44f3b4975a375f985486b998bf88be2e", + "0xd0dd1bbcab053146a2e68fba129331ad2c5af08d843abe1c075870ca650cac5a", + "0xd131e9eac0275cd36979ad227d82988dbdf028eea76be18ec2d6550e2fbdf831", + "0x398818300e02fe77c5a75994c2da3298abb2b74f182b747834a668713f134b4d", + "0xd3417b8fefac9b0192c60424e868ef3d5e55a5984f2c13e6e0f1abe1a6ce120d", + "0xe04a777eceeefcdfcd0b5fd673c5f81e9fca371c0bcd05a0937b4c7c399171dc", + "0x7d87a1c5fee6cb6c0921b9be83edc4fd8fc48604c6f2e57b366e2cd2e3a849db", + "0x515318ab58b8125721a9d7157c9bd6dd6cd82d957b470852e8eb8b97b6fb10b2", + "0x8e611c78afffc8dc4067dc3acc15fbd7db74b74ab6558f03a0dd3b23c0e4fbca", + "0x5a814314ccf7452b0f4fbd9f270bfcac23fc74201c2c2d7fcc501d71f25d6309", + "0x2df6e5f5639c25696c2f1eb18a861056fb71c4cf650595fdd1abe8287b4264b7", + "0xb9197973e11123c34797127ac86b89662964bca7f9cef31c23191cdcc38721c6", + "0x7c8f228104eef64965613d5e0d56eccbdb8cc473c8f35ad8b43968384d0eed65", + "0x741aecdbed7795bb12c90ccc0946fe22aae917b2093df93b62867f81f921ad83", + "0x4b806f0324c3db6ced7aa32d8665038b0742141f646eb637fc4474b4ec06da2e", + "0xc5fce25147aaa9095cf91d9e372326a47584ee63412b607ad90a1affc46b14e8", + "0xa98bb24caa809296cdcc6588b1b001c35e63531137215ed8889ac38c631ac0fe", + "0x339a76d2fcf6dc7fcb8be289b9f264b18b919fac89bc3be3357a56e07f18eca7", + "0x5492d57c30a87705a54ba006b702497676794ceaee27a67903a3c73a611f46d3", + "0x2dffea05b5b408009d9826e4113cc26b1273a490e2daaec815034a3f069bc98d", + "0xd869bbe88f3414c82b07619f7f819dd9e9800c9b6a1d81f800fd04bd3a7b3d8d", + "0xc6e5a1fd0fbf1bacf07a91795813ca231dfe647234e0986ea4203c317bba68bd", + "0x71c0ea26fc46f202f2f5a081569ba7c0a06a295ab7348b855fe2d45d87343220", + "0x69ae4b23215771d511a4e8d74fb92d3fc6ef2ca1d6e28507f386ad5ed1c6bfb6", + "0x8962d91b704cc497cb4896edd48c17000dc4706048b99a763e1020cc23383b7b", + "0xbaf50248fae76c1ac3a6fc6b65ba1b69bbaeee1424a60d836931c6d06c1b08e7", + "0xd099eaa9129121b5de27e236b14f7a7c44dda64388da484c0bae0448011c5ccf", + "0x67e0931ce85c3e7f7a895112ea45c1ee92f4003b0a909b29b90a306fadb87ca7", + "0x9eee4eda89d4f684c1fc0ca587ab15c12afc0ae3366f6a42cae742687c3e3ad2", + "0x10dc8a9e34648eb8057df038645d813b17200a809b383059d4387d43c8d0990f", + "0xaa7e736507504a053b2d8b5bc7531e05428193229f1224647796ea4a7d7104bb", + "0xc57c14b0a52f8c49791062d48ec7ffeede79d4cc3d349eec13986f547d4c6613", + "0xbc3a0bdbaca6d1c4eb94c5c814b9c31a5e1bcb632c6ac494835dd75fe84b2c8c", + "0x3c791dbd2d682ed1ab36406e9a8b2c6e9035fcaba2cd4890220df7c848998cb6", + "0x6fd06c7e264a138b1744856fbeb8a20065ae9775ff64e2bfac0b015325249048", + "0x83a71bff34186120dc8d41dcecef981299b6c07de3c95b20121b72c9b1bee83d", + "0x0ad3b1a2411733bbb52117755ae6f700b0ca2f124de23a5431d1a18c62fa2755", + "0xf111942d4f6ef8957873fb027aff08c5b30e8899b7e744ace81b8523d985fd9f", + "0xa67e37e95e10048460a96e8f44bc5f78decd7fbbd617592eb173f5535549d444", + "0x18c38e6ff1aa6758cb1046d3e739c20fa2c02e74e1b73a3df697f59de19ac9c9", + "0xeec8d98b95fbc1e4582e7205769929e08e286f90962edbcd8422a1f424aec5d2", + "0x11cb4e03db65891b8c851ce627a5bfec3bf018f02eccbe9334bcc2ea7b4635e7", + "0x753d93dc59af9af632c9091b7e70537a82b5afcff7d53bf7b98e93380d4fd0a6", + "0xac5a3dd8871c4f6cce47375a756e3381268fec67a11fa045b7f7bf0d16d1d900", + "0x5fb4b84056c39e66d69bb036813f01deba63553a2cb114dccb2f3a52ff85425f", + "0x1c148fdc76374605039954c6115d1168fe2e1c031c83cb8806d9aed056a524c0", + "0xfc988092a892168dd959a55a0ec1efcb24628af5fdb13b91b45143718ddd7fa5", + "0x9125b055395484759c3519c07be06dde96e5f289e196fb96ba09ba2d6beaadb8", + "0xa557f7b7e8e9b90fe3f3289be95ddd5db8586874e147d342ff9469d60bf92c70", + "0xdb9bcb48b2a3883698db1a05debb5df2ad89b0d91ace9f7624ce2cf60a3a7d59", + "0xe9ab3a90076cbbd5e9db55b46d81ef00b77f5d9355d0bb132b66616857c96db4", + "0x967264f34277fa1d16cfa8f09d8f8c6f100ca3a57876a2b3b41e3fc09050994f", + "0xa47f97ead5a2ff71ea8988c2b571f5c9942e1fe8d733421c21f171549530d7c3", + "0x02be96c42a76b34edb27b5ccee2b74c658b8b9acafe8e4b7770cee54d7889d8b", + "0x24bc458a3669e1932867708e6f0f0de011277265e28ada05460d10b654398f4a", + "0x6b443a0ee4e4c72faad8f0dabcb9eaa6ddd434c9bfb44b5b6a02ef35f33d0147", + "0x602c412fc252f5c8d87d014ca9b022dc4eeb0df236373cbd5aa7a8512a382f4d", + "0x3dbf64d7cdf29ed9fc1ab3e4731994986f89c3fbe180d1237018e18a75ce5198", + "0x6c1129134013ac490d9ffde640ac791006c0f620741d3248b211acd49fdd2291", + "0x2943bdcaf685390e65aa6996069e6c51b807edf63dd598f42ea30e6ca9ab5d42", + "0x567708ea5f22d7736551f223ff408cb0e648cd762f88e3dfebac3eedd298b98c", + "0xa0a9f0af9badd83c1a6206b2de5327e2812b733a3c08a92ef82c6c9f2e56ee17", + "0x6ee004d377e62e4f06073d4160f6f9a72a758bd9e17ec96f062eaba24c96ba8f", + "0x218a0614fe1b03d71a518651d1c21fe6a2907caca0637119f6077cdd0b78ceda", + "0x135121d5266fdc5c51744045ce40fd20414908211fc5d8af1aacdd1c08f55646", + "0x9420cb907f7678889689e2fd9b5c9a389f2f92e1ad5b33c7b8aea0016fc75216", + "0x1e6582b223ae179129ae4658e3c81c85753b5ff205ad05893d4213169f3cab9b", + "0xa9e6016040d659c815e9ef809bd23a47e27795a9d692b9ea40371c174af4413b", + "0x34edf7d33e8ac5c344f9ad32da2a58bc0eb7091652ad0e36d1ce5b353262fc99", + "0x57f3fa6122e6f53e58a582659cd1efa106b560c0099d7deac06e009e0cf51cf0", + "0x0cb9d16c6b3a1a84633688a2e08354c8a08cb34f46ab1bde112ce3e201babf2e", + "0x8c1b5d2c3b278ec62c5f894b3a0eaa2c5a330b54af158776bd66e429942a69d5", + "0xbfce7f9819a3d09e4234ab1df1d2df6b41c2df55a7be02dc464ede01f282ab39", + "0x069a96aacd367b41c290280897d9ee53b6b09596c7834546837f17e52bf532f5", + "0xa618be207659c0499ed4816d7b9f5f49a65802c6770ce0eef090c83a826032cc", + "0x51cdaa25b5e6aaf1a390ae892a8f665e6a2757bd36e85c9b189174962c998bd9", + "0x0a6d2cbc0a4efcc5a40e487f6049a73f7864272608e39f4b8766e47202377188", + "0x1d842a2889e2ef5e1c53b0ffcbb257a7cdba4c4fe900e105326f662a605e6e96", + "0xb4bd9e6c414f474dd3cd9571179401dae6190995a6a61ba06e954fd8a3874c7c", + "0x5a811b6ba2d64743af114ebfa31794c1cd57c4daa7aa7eeef2098fe3bc229150", + "0x359b0321c920c751de4763c0fff6a14cb20cb0e3ec55fc665734d23d372784f3", + "0x05bbd2173bedfa58d2a548d2ad5a33d3bc178c598fca2fcfda8d58708690e4ef", + "0xa43fa9d5c03141cc2c1410da1cf8db44f84a390beaa5089e19277ca36b276c44", + "0xe94c12c3223602de89988904b9cc7093fb67d7cc6ab811d0bfec15f6c7ffbfcb", + "0x1322bc7489899a60c29346cd5651cf3f7440e1c81a634d69b27f0e6ddf18972d", + "0xfa2e2855181267e108d540b5ee35c8e410ed98478f6862a7c5c3fb500e5cccb4", + "0x333b0f6e5656c03b0d0f614601a5684ffeb5bc63e8a5efeb87bd239bc5015c9d", + "0xc798d89e4a2e3ffac30801d2faa3062b07e9c9fbed5a708107f945182b096e98", + "0x2d866d0689a0d200b6e825a01ce5bd274d9e926dffbb1bcd4f8a2e491764e84f", + "0x6f800b671ee9097aee5f52a5de1d36f592fef470dcd4f271926a5f2b475cf6ac", + "0xbe537b819ca7aa10171896dc8d3d5f1d6a9f9a1d02b681a391441843443cb6e6", + "0xed8eeba241bce29fb71f943501481130df20acec4637f59eb8533c6ff0ff0d55", + "0x1b2ebf8c11db66d232b75e9c8d6c688b7951da4c245194a3a216c58553c67ad3", + "0xeddd6c7c1becfa6e530794deb317c8a9a7e86e7110112f2014376be972cfe6e5", + "0xd8f4c6f75bc43cb3fc529210b98771223889a279c5c4fc29b614e360927075d7", + "0xed44a3d9cc4becd1ba2c907287e33f44a20ad10d2a4efd590afd7dea322d2a78", + "0xc77581100b5ce69831fa90a0b9bd475fc840690a9a92cdcd8bed119301ae985c", + "0xf161f9cbd4ca5e3a44e26f97ea08616326e8e2c648dc7040c8e7e6e70a82109e", + "0x57a449c94d981e19c45d0fbe54c70bf7e786df69f75b2ed6943990a72e63cff9", + "0xcf6804abef05cb15e6191ef27c607fa4c6cfe85ed5a16dd7386e7203746e99eb", + "0x7281bda87ccb9af7c33b09b4cf21856d7ad27439a114fa69ae2235575c3d146f", + "0x4ef18b81cab5f81abd12c699768606990ed041929ec5036fb932a54f97012008", + "0x81414aac9b1ddb9722752b2b2c2e01c3d25e917d1a8c4e94e64865a71abb4dd5", + "0x2fe01eb6cb02712b7b229e6119e396cd68dd99cd16274277b8308d4ecec116c7", + "0x2b983b7a99569ef183b7f1a15dea9a9db27a14c301efb75674f7ac53c23c9252", + "0xf1144f35222b0747974a9638fee5d4ffc8fb24b08f20548ef0da3aad6c1c587c", + "0x02ebc5f7cf5a9486cf822daad1581ad6ce638e4da5fb3d0d1b0df349ba6b21a4", + "0x7865183ab07edfb75553e44987fa641811cc145b1cdaed5a3ffc3cb9bbb57a5f", + "0xdbfe6cc8e2baf9b7e4cd845a72d98f41e40550bd3b98050a6b90f934be514399", + "0x411e4f6a58991609f9030f6fec9c106b863d8b45f6e6a7ec16e762075bb1c66d", + "0xc27a29112208dc57d91b4df761142bea50baa3efca482bd2aedf1ae9e5ac9be7", + "0x15f8664b954484664203318fc331cdb3da9c53d49c32299fd28d7381debb47e8", + "0xfc3d3d9789bbf827fa392202ee401bacc827cbb39098c13cbedc38cc3f2d8633", + "0x16bcbea8611828a9e162d0d046fc66c48bf567e78f75dcd41074c2c8d82408bf", + "0xfe586001bda7ff5ef6d2f6368971faab6f949bac2085cebe65b9db009fc1739c", + "0xd470b9f8d75e90a691e4ed5ef0060c9aef881ab2401a5b79c657d543e74225d1", + "0x72acb276d6bfb04d4cdbf786c8dcc5b045b13dda35e095ae505797ade1dc7877", + "0x3930ea0e5179903922a7f0cc7d3c67a534d583675d70a57375c6af3c8f1e846d", + "0xee4b0d24a8cba5f786d2ac6f873b7fa9685ec02fc08ced7d8723a94d8e2852ab", + "0xff2dbea4f19541de016c0196b34ff7642bdaa89aa9671b5be30873b16b6142a7", + "0x2953e4791a22d77f6c141ccb73313205c262b526f8ec5aa9995258badcfbbdb9", + "0xeb66a05ee5faf9797635ae10b00db95de030adfdfa6b37e822fe28f190eeb4bf", + "0x3942b74543a9a69c8851152ecf4d9e3901f71044e9f975e88379509b33d81caa", + "0x63c88e91e9c30998ad0049efa3303055b07c3f36c110a092f9d7dab80277f675", + "0x17f47a8b09afd0e5f3d75f87cee935fd37d99d8977b1ba15d7ae603a825b936d", + "0xb095cb9fac809e1694a0343df7fa39cbc93f680a4ce8270854e87c2c5d57128f", + "0xecb64462070efa84ead87dd2e9a7f334d400b79a66e8a35266bbd4b757bf7420", + "0x11b5b0cbe6046b77b5bd73447b204ffe2840db7ffa75c9f83501bef27d5058bf", + "0x53e41beabf20e27725e3149ed1a4ed5a4a044d6fb905285071a4f34359099e53", + "0x1138b17d9d3d4d67cf178b53f32db452dcd533a4bf5300e6a3f46dfa9fb14e55", + "0x3e994e6ec704cf36fb11003bb8bd05d6b2b70818f73be07eb1dd43ec8702a529", + "0x7736fd11f9d98aa012a06aba83ce182d820d82a97c6ec0abe3dcbc8d8752ede4", + "0x27afbc493cbc2de77c7530513390efb5b7877e316f51d1bc86d70de80ba3d08c", + "0x228c7f4bb2a99d348bbadd7eeb6b1533d0b1d452c24bdd4f0b2ef9b4b597b394", + "0xb69e757644cc90358fc9c98a9e22010f0d70002856fbe747b70b9edad3c5afdd", + "0x5525cb53828da51ecff5cb163349111ab814df5d5345e33633a68819c6b0c2ee", + "0xd7e36a07c0c6513c5e84d857c2defb793391dd74645334a3c587eec285cfc580", + "0x81bc4c2731700d643add54f3e2a9983250f2ab89b7af7585e9d99d3be8c30716", + "0x60e34f628d93616c87926a985b600c482727e1ce446ff1ec89bdf255ca12c405", + "0x267f71673c4b3222a632d2018fb79f95949c6767b77b40362232d8e918fc0ac7", + "0x9619727d92705207fa31e5c7c696645e3c33b1810539ade08f7e7f6e5efce733", + "0xe1fccf6ceee4c2929821d97ed6b09c0f43b969d7ac51a084e5ce64a3961aa310", + "0x96b31d0e7cbb6f6dab798853e961b70b555d75b80af177ea8e9e0e9e0fecbb56", + "0x5c345578f27e57219f03d5be0ac069ae49c8a4c87b6a3dbabf028133e03f22b2", + "0xfb525ab0be3cc291603f8a386044551a8efc73a6f292dfb9816373674c4d2d94", + "0x2a3cf49b8185aea4efcb4893848a004cbd7d3d6c90fafcfaa810421f44772ca8", + "0xf8073e367961107bd70ccad156e464ad9b6ae67c5feaf34fc39495ee154071ac", + "0x3fa3d7e0a0f14492e6ba8d9fa570730e2ebea109d6464f1223086a8d278c4bd5", + "0x489d4522e0c7d2fa6243df5c9c26f8e5b9a7519ec58d302787e861439ce92c53", + "0xd72c8f1e2d3e073a0c74908aa43ca72c58077b692db9ddbaad9d9013cf576c25", + "0x417e59ba9b6ec114f27e199f09639ba784f6470aa8757405270605805365533e", + "0x3a693cbe20f5c627ff5fa75abd042e04747263af716998137f240b559fbf94e7", + "0x34e638841f82f4ea10e14dd9d3e1d3bc8efe4a673b245816c73c237125d5aca4", + "0x0c5eb5a63d7b9cf8e477e6594f247598d18f01a100e6f87bbf77f3434b35038e", + "0x5f67676eed6df62ff5d23495f703ca19346241534879f60a8a7651e8c01990c2", + "0x03fffdacccff8a3147e3404e695028f3b9403e5a48fbae68bf3d069b562ce449", + "0xd4a0fc04e072011e5f2693edaf2f407721b90155bfa83c5546a21ccdd8a9463e", + "0x166cd6de7ec4b234aaab0ce477486c738a3865bc0b3a002e0439d1534f9b997f", + "0xd162e039c4d369121794e923a8c754d1710047bebcdad102377741e14acc3d8f", + "0x50b5086b93044cb0d37f9a022164e7e9ca3398f093523a98ec14cd67f86aba16", + "0x73a224ddd279d6f49e45c1c4d32418d5b19defba15d26c7bf3d64edef2dbb8e8", + "0x3565f8b410de370bae434aa73ce425e70c1601b594eff64373f9899544724ada", + "0xbeed5d466d2e62e45c4cc866dcedab02be29260463a8762f08e40ce7a04744d3", + "0xc1d4b0d029c2a8f1c0f089c5508096859deb60de6a17b674599850a0e0222032", + "0xb0da8c58a9235e064d1fe4fedf0ed6c356158b2b51aaf19269b6b1ce78ee3aca", + "0xad11d400604d3bbf8da8a3ce65d029132308ec46bb4962b2aa4ef38067de4a82", + "0x0af21743bbe2868050a8fd27a26bff42c22510c2e9780a450f05721d8f48dac0", + "0x207386e305729b1ef3a21b75e806565a0695dcae9e37c08aeab045b4e755ad31", + "0xd2085535258f968cbdbed39b457e1834b8fdb945605cdd3d3028a5b1476761df", + "0xf991a7c61feb8bb8680ba2cb7220dd425e4d6e83e4901e188f7c03c7d4bb135c", + "0x689fa3c998a2f6510d597a4a0221cd270a800d825729808e0f524e38f68d1e43", + "0xdff1fcd4c63257c0b16f4cf0740b2990392518dab74070b59616f53c9c12628d", + "0x3cb35b69ff0ec47b11d14fa5f26863c783f112fe0a1f55a8363991ae4504f56c", + "0x97327ce8fd28fd8a95a45f8e3405a559dccab62fd4c544ce4b6871c56f921559", + "0xc8956b8b21757ee7413d9b89c71cef7ac43155e0b0322725443ac5c8df3a3d22", + "0x3a09d3ca3ead9bf9903f3eed67b526a303945ca67ffdc5d39bc4b8f9e754f462", + "0x9f11ed08db534afff4989c56d2abe7c52bd231da8da3f3105851a8db72a655e3", + "0x3d3eca744d1814f26d24393c11d93f0558f6b1a16dcad8c19064323b52cb10e8", + "0x89ecda53bbe62010f0531a45c714ebe4965b3d03ed950386fe3706b980670f3a", + "0x68af12aeac02801093bb07239c2585936c5262c9f1adf8afff917b869a09a181", + "0x48723100f808fad74dd1b36d0359e72faeb2ba436c648b92667c3eeeb431a834", + "0x4489387041b4edeffb331a51b72aad9a126d1a62fa6a87f8dc45758b92319ba4", + "0x12d7dfafada5f7a584bc0d1a6ffc7f6aba020e286fa2d008b9e89583f26781bc", + "0xe9b95b3531ef4e5f02a0a58c25eb0e4d9aa4942df9a4cc8113d3243e372603d5", + "0x4542974e7396f60fbb206b12e0667aba30675983ef896e4be7c17d17f3302d5e", + "0xb39510f2662d06eb51a61e5bb4cb9a8a0c77b6bddb2e167ce31a87c400156468", + "0x62f6fa12bb030b8855e58750bc8d67de07eca3308d70ecabd59ce85be15a1f70", + "0xc621f1b9fd5e805682eb9165c0df65500b7555e6abeb43d97e89e0d2b2ea816b", + "0x943e31122e91bde60e31acc6c12f2ebcd767bbc48b13a279e3d2e4323cd29ca8", + "0xc301cc4381f2e58676c7b20bb0236b7fd1105465e08cd3eda57d3e5f58d1e2f0", + "0xa40c6be5774b66623885f83e9ca3d6dd5ae56db974899341bd7184f1cbe2ea8e", + "0x681bce05b7d0b4d3a680e2cb05800b5d11f6abe7b70603ad2859d9594869ad58", + "0x3413291fa96d6a0e8a2a043021047c37d006ea15317191b0b636104be1d9840c", + "0x259ae305a09f983144b34f7c2140914ca9d46303c867bd25969e7097a521dd7b", + "0xbdd13bf5d0f49d732e2c78a064eb22c4ad7d2be8e81f262fec1467c07fb5235f", + "0x04bfd9216c894e5cd005c607d4814ad483710a8c2a2f02551dda550415828594", + "0x8f7af2b5acac34c8b6ea4a1f25045a79f64f357868539f6f1e6b9ee3a9c48e20", + "0x55203a80772c00284ea0011e4133dbdc1fde71f18fa66899a0b49a69b8e400e1", + "0xb37d2f7ac974c41d07d144e8dd8493dbcfb9a5eb4ff577db853bea0a7bc667bb", + "0xf18c3144587e4ec53eb4fc9a99a19da4fea2ada1f9dacdf36a24d097b800f7cd", + "0xc6fa227b2335f3f40e47766b8dcf47f231aa1926f3f71238f1ec35f6d9ae1562", + "0x3e9df64388849950cb92801afa7e58ea31a448e519f61202d203ec731be62ba6", + "0x90f153ae45199417ce02b7b2aa4963d3197502705eac92e9ce21b60499a139dc", + "0x88300f24fd4bf8194ed957ba5173bc7a9c76a3904024d4e935ea68aeffc73d31", + "0x38d3201a04a193c169386c2799a9a96d4228aad3d4406ee375fcd924127f2851", + "0x1c3a9b665515bdd0a7f92f80f7a8991902563225751076a12e5e5aeb9d1ed01c", + "0x5e11f2b3578dea7427da7795e4eb9bcea11ce64331cc882a67172c963d37408c", + "0x745ebcd128e3bc7ae383f7d678b7200b15a67b5d755f926b3112929448f78827", + "0xd2c15af91fb9cc8d4d02db0674ae0590f2fcfef967b8d57450e446da9358224d", + "0xa97155f2fb6a33d31c4ffe436d91fde7058b6c035d961e8db3fd332d0b6b261c", + "0x967ae2eebbe1f1855a1fdd2b09aebb0b32af445766c726ed37ac3a041fd65038", + "0xc46455a4377bbfddddfc2989a49dd9cd460345df847279d1d80e9bb8a10f6580", + "0xfa713dcc3871918c991ca733ee21bcef3bdd922f7dba75c41e33bd7da7404858", + "0x5e7c38813b60a7812ef0179883be14e801b61a33f412aa7bebd7a4e4ec19d8df", + "0xbce46f291c2a08eb1b83400611824c99b71bf9f474d4d335a0c66eeb549446ef", + "0xe0006b8816a691dfbb65648ea9f6a006d836e1a36930db84becb062c54124b74", + "0x20f310a149fe7581017639b539c6e24296c393d83bd6aebfd1baacc2272ab4bd", + "0x63022f2bca0f8a6fdf5d52c39a52c19c0d1776d85fe95b54f6bf11922f157ce2", + "0x474428369e9a0dcf1fef8bf341540cfd0d11f964c22bb43732fc7d8549bcced3", + "0xcacb2db182553413224c57b81144c26c3d8520dc3da720a0adb8bae6e786ba8a", + "0x6a192001fa5e3197f7df1b264bbe0db6d3f35774203acc35a7c8302b222f113a", + "0x3cdc460326a23b5f15effcde0e381fc10f40abcf399246958f65bebc0a8b03ea", + "0x5565a68a0ad2f91fd84342fac31310702284b44f78dd504021bc477ea6ad1053", + "0xda85880adebae770ba9a669f4a6d2aaf83abea9d6e7f6621fea5d6c47a6416ca", + "0xf69eb4ca9c83b9ee6a0ea1c67dc6895837958d606817d10dc18c944f1eb2ed0a", + "0x6b6e4586b078bd49ca8bc0be7e50ea6fc11d07a083015c784ba70cd342192fb8", + "0xd34f81dd1eba004603bd5faa8c1f220c81f401f75e87e192b90595bda6d3465c", + "0x7c122686f261ca2b615843292d78d5b2606598d85a5445ebd6779d2153f47161", + "0x73f11091464efeb4ae12bee98c7b1e70c81b26b5828609dc2d6dc10863ca4f55", + "0x3782503e6ab688d1389818ec16053187234b443e93659180793f24584933fa16", + "0x42b4ab02e7888707f290558c22c290d30e369f9b9638c6098b87f66b9b24d70d", + "0x6029adf0a8b56cc6f152582be0f8cc83985f56f0db0a2d219fab972c90e411ef", + "0x09ef19ec1a5e0a1938a39ac9e7c67f1aee970176db4a67e7c4f1c3511caae633", + "0xbc42496a16016a068ab60d7c1608394796f8d19f1f37a47d2e179c8fc5bc4bc1", + "0xe9b3a6417ead6f8dd8641762668ec9ba8d7a0f54043bc885f07ae9d457af76c3", + "0xad6896508a0465b52368f0460108a43aaac5aadf1d6754314e9c060fdf3bda7f", + "0x844fdb7b62413696d078cd7c7b81cdc187b02aac59401f1329715f99d0647a12", + "0xf398251dcdbdea095f2425c263e6f6a3e5e7b18c6c0b496ce771cacf5f277247", + "0xd86a200f7be6b3c0f1ed0115b86817245d679b8a5a773b07c050a6c552cab56f", + "0x455ff39bb9ca905214522dea68d44c165fd7bc821f6f19ca48f4aea6ee9bc2f0", + "0xbba742fb75f385b6b27082c6fb23226366b4ab79b0e3ea0a44f26ac532acfdb0", + "0x20806e8c87322bb0bd7c48b308c55b66c9038d1899e9616085a66d109d380b96", + "0x5ee8616e41a2c23ca39edabefd600a7d1b37e77d5d71c0d7dd8e63f4653d2082", + "0xbb6c09d43c11cdacb546de87269f79761a3e05a7f8a387732c87479a39734e0f", + "0x889ee94932c8bf6c50845322ea4d37a4bd577b2e0fd4c7a02017ac5bec4a3325", + "0x6523fa0391fe79ab8f26fdd0eb6e617bd2a31cbd598dddd82438811dc8971983", + "0xa5606fa0f4b3ddca2be34f93efb43cdc3f7732cb572781f0cd16b9a71fec88e5", + "0x2ab52c860573fb139c9f0dcf0fee6a71001a187d2ada5b3b7c8dbb8b282652f0", + "0x57eab9eb73f445a7ac5548c60d370b5119c76b9d23e79115e2de311e336aaf49", + "0x255343ba8d7f85593f35db685936f6195b0053ad6dade6ee2fe8846a02f0a813", + "0xe48517542bbab755b3a36286191f57d868193d15c989a537925421a38d837ee4", + "0x7d693b54454dfabf31a6e5673834f4719467ca5e35219a263f9bbc3587361cc7", + "0x643fc781cb2529444ae8e218e2b74dba4069d84af4a5800a7a4005769f7c9df4", + "0x4dd46ae4155d9de526404c6edf890eaed2610f7b7234a8eee97effc3f54234af", + "0x597424d912c2f744a558057c7dbe5af01a99abee3acb1905491c4311adafb2a5", + "0xc69345dc5cf5d31c270e5c95265d8403ffc584bd7ede0640923e5ca811ae6fda", + "0x5ba5cfc312b0ebd8f5dfe32976c6d1e7797969358b7e30e57776479842202283", + "0xfc7c748e9e770ec8bff9ed640d251fdb746dea44504cb4414d07a41b2fe2ef76", + "0xeec414e64d6ea75ce5accb171df01c326dc32be5c977711b0d5eaaf8f7ad5ece", + "0xcc43d302e7920c699dcd1782130826dde790f78c41f509cf40de78bf149a7c84", + "0xd04f5de1e1de11991314c3ce0a0af018c816607075c4063373b6c98cd465cd3b", + "0xcdb1bab6ab9266d10fb6b7f402061dbf00b512c4fec963af2d736584ae1323f2", + "0xd7729196ab39722218130ec67aae3309682c0bea98ed30fc7e794e0c2bdd8fff", + "0x26baf8b14c588af4c933858044d283c80dfd12760af5fffe4613c820067cca58", + "0x44aeddc060bb203f614216d829107bc6b9b5f002b209f2f0e28bdb2d18b933ed", + "0x0d38e88e8a2c888dce57dbe9fcf8b0975b8b79c9fe2e4d80e6ffdab77d6f9bf5", + "0xc36e9f27de54282f03581b27b5501f60314076424bfcabe6ab28d26c5bacfb01", + "0x2beee1f6db596329c3eb714bd793d8afe0b717a35c3e685f10cb95057002f222", + "0x597566e3388a3c37c04a1091ff3907e04c44c2abc1ec81aabbc83e32925e92a4", + "0xe601957fb6e4ab19af0bd98c51f71e860e22f06f9cb8798c8c8a782b493ce50f", + "0xd1cdc87d460a62c0449722db9f7c62dd7198c61f6aa9b35a0095d5d8a9cb7874", + "0xdff4dcd195ef71493cce3a852949c5d561a0c4b69801051d1c41bd9e6683149d", + "0xa5fb46879738cb7fbe60d67cb0af4595160d1289046f282ee3f125d2302eef72", + "0xc6d3833a6de1f949a2ed1cec2081beaf96641ea9c12c3cf6e652dc854068bfc1", + "0x232ac425b32046c47146d947fb188cef1080e379b802e6e6aacf94d5717a2d1c", + "0x34bad98ab07ca6084ee345bf0fc27723a907e7ffaed97a1554b12d6c032371ff", + "0xe2fb08db6a4ba8e2e1f8d7ec947486424cec7032433bc2e6121801f1d9bb4507", + "0xeb6524883dfbf3c8da51c18fc726a732cf01b61d627fc7ce65fe183d0da122fb", + "0x4625aa8eaf4dcdba1962a3287969f40430c3f1d61058c1b114c3d0fdbe0b0e00", + "0x8800b254538b504f5ab5416c05c7e997d1ab9b2b8220cb12b0c1b118b869dc67", + "0x47af13bbe6ab5b8b5d7cf679ff18746361be2122db9c5e3e422d2fdfc47c64e6", + "0xe997571a4974908c4c42ccd41d3a4add55565e500b0921a2e316bcc16d9d2f6f", + "0x42c645792592168ca4ba7fa0c2c2ac8dcba82e9919018d53140d85a9ea9a8c59", + "0x25685323799366b8bddad0ac59ac6055c6a749e0cef23863354418e991fe2f48", + "0x08ddc1050057af8965f0d7ba4c45c49bf7d4c050405ef4c9c455bda558353798", + "0x0a8f846318cdac7fcb75f31beca992ed9affa1d0cbd77d88f858bc8e3b748ded", + "0x13a1fb1e6334698adb444ba2ac9f8dbea69d44fe6a68f04ef1ca9f1bf18112cc", + "0x3e94b6e698f7cbba803738ba9d0e3c0958b8120cfbff5d96e7c1d208cfb38d14", + "0x0afeaa1dfab810d38861c528a60024f950c37031ef57d94a5951eea21be1302f", + "0x79c5979635d472843a0b3b4d4913e52251481014bdb64ce03eacea8ce55977ce", + "0x5b076cda2c6d6319111d5944a08806359c5b45d6b337ce45ed1bcaff026653f6", + "0xd7f20cf85cd0ba54a1728d71bc83e9dbd9ed06d2830d7d13f184ca13959a2345", + "0x90d0a32003d5b7454bfa1b54ff0378ae17b68102febdaa40308406aa496b7482", + "0x49fcae18f9df200f078914498b99f3ca4bc2f65c7a38432df87a08fc86e0447c", + "0x02f97613eb2fa7c4ddd185781dbf9878f4c9653ca3532603d228144ef8d173a6", + "0xd9de9f7d972c5d0716c49c3d6e5219304122592846866e4b9d8ee9b791f38f29", + "0x4de18efc5cbe5b9fde40481a8452a56f5e6c4fc59eca7c1240b77ee8dbdad3c8", + "0x2f87d4e0619f99c2d40b0f800157e0beebc0dd83453632e0565283ed1d978e6b", + "0x3a9569a941485d75d65311a932eee3668daf576494ceaec790e25c0a98a8e986", + "0xf5e36efacad44894e210b1665ad5fdc3ed208a1f95466c22a4006ef8f32fba40", + "0x0d6bd8869bd7c328662dd954ee60eeb6ad90bb913b663ef8d9bae0f169127972", + "0xb360aa4d0554e13c5cae3a92bb0a44d5e961f99f865b81bc1452101132f62d99", + "0xf74ea8a58a605a2f8dc554fddfa4257eb40b23f0636c057fade21054dd8721ac", + "0x28034193bf4171c1a3bdedb91eed0690c0f1d4441ea4a0debc7853fbee08a79a", + "0x9c7c7632f3db963a081899b4a77b67821c0cccdf5b6a53702af55c5a72f7a0ab", + "0x1516feb40e824325bd86e01e5e6ac953f7d9d91cd25ef77061897212ced1b534", + "0x2ec944734117cf3346df18a7a16562e6701e1ed18810c85a94505fb85f9a86ee", + "0x8dd13bd7ec1c58ff981b1d8f781fce171eeddcc43ae9bbb009d1a597364b7cea", + "0xd691ba5479ba1b259c5b1d0fb82515f1b52f5b70f30902b7b731d5333bc2792a", + "0x5514056ee0d0e9774c614e9d217cfcfd2f3c331196e1a84338d1105ca142b8d8", + "0x0d43ae230e28b4676c322238b65310ec3e6ec5e8bc495c7966116bb37f0c5807", + "0xfbae1f5ff1940eb0364f03f9d6a9b5b378897a5d0774f59681e706acd4b211c0", + "0xfc8ff208442374dc5b4667d6418397c45d668ba99c950063a58d2676407f0a4d", + "0xe65e91ab48534e1c452c9b9e4f5c967c7f062ff1a2369ba5e9dc8334032b654b", + "0x083c8c988615c9cab932e27ad100bf6bd990822ea334e3de3feed40230550f1c", + "0xd82e223affc9c675f0cfaec9e6f5123f8fcdc4f3d53264ade12169c004f9a68e", + "0x1d4e5731a6a06476bdd05d59d08014eba223097a2e66efd9d252a1bbad19a5cc", + "0x40c8b41e47e719a606799f947d96302d81bdcc52562bb40494bc6c8e8f6d1d65", + "0x478967e9c9795d56f727a680b907a524e77d8fe2b6d3687f167cc4a02d8a4abe", + "0x255e9bb8ec6ccab51b8b5b0b87ffcae810cc19073e454695a66c0935ebd7c0d0", + "0x51c800c8269bdf530fc53c20cd761be996268794980d7a7c0b2961b0f3355092", + "0x54f08e676fa84827ee028913324767541fbf56b03962e9a5b190ae2d62fa247e", + "0x5e5070530bcaf17db7b1299418353ca72edb07e03c14174116c05de8c8cd75f5", + "0xe0c9a5912ee0a31645b95983d0cb4cc32366fcb9ef509b3201051b2bcbfea1da", + "0x062309ae9fe9b1d50df367523830a46ac99329c6a53a5564020f2a3efda9913a", + "0xe097b28b5627c583f4332b13525b254ab43a364e399efada78c76d199fe5bb89", + "0x53a3a09f4959d5cc6774b6b49646ede2485f4c52f672a4148b032e5f01cb23cc", + "0xd1a60793060e8bbd6b5b8c9b3a08b3dbb46c53b24fd47699a8ef97a560472dd2", + "0x19820c85774c74ff419a49bbcfc73bb4508042f92863b8e2d707d8511767e1ba", + "0xf6a0c9e3910871c897865ae4ec868ec56a6d6457df92352888a64f3e1dbe4478", + "0x706405e622cabaa57202930b65a6c2e217ca79dc49957086033a29189685ad96", + "0x7017693f88927328bb02e46b0c3c4ca5e9bd7e269518dcebcb22c7d2f066a30b", + "0xd3950483f78524972bda2c2490fe02fb7c5210afbe6f7506b3a47ef11783b2b7", + "0xb28050cd56edc14b6ad5de151e8e7f2917f4289ea5d120c9a5be9ff22b29b0c9", + "0xc2ccb5f531148117c518583af7a09d84a2c0f31a069df26564be6f937eaea5cb", + "0xe130b81f641251666a7e95b23c614aaf005d0b8436699f6570e1b43222ebf2b8", + "0xda95b32bc4645c7bcda7ca3ed3e83738cb9fe83933450393f3fec5e59bb34b28", + "0xa64062a2dc833242989b6dd3bd3965f58124280632e91370ee7c2ba5d0745ce0", + "0x731bd7e1c0f5c7bb99361f81493972f4f8fbc558997eb3ea2be885971c30c31a", + "0xaa539ab0aa75a096891cc9650c9b669e6be4c187265da8f60a7df96c1e22c372", + "0x1032f9a5efe811f056c6edb3375c4f75705df9936c95eeb3ad479b4bd7650b0c", + "0xf7554a596ccf046b4e79a5aad4d8f1ebfd5387afa53fff16f38535d341083e40", + "0xf028d70bcf97b7cbc06fb501ba64fa99953e663e3f0b91d956a3a9d9027562c1", + "0xe89ccfc6150c4819e703ed1a6c282641e602fa9694e1985f06d84ab512f05cbc", + "0x4cab108fbe3460542b2e03dec402746dc0573f283cbf5168124a426c05f8b38f", + "0x9025589d08d16103d65296d7e3f7a174b6b3bb26ea036281ec981f70e88e3743", + "0x94cb9c248eb6b7098b26f897529e7a7ce97390e6dd44b50c2a4e985ae6a8ecfc", + "0x239259aa1ddef00b63c1745fe8fda8302fc8a8627fa08dad63bd5c547187c1e9", + "0x9f5793ebd6d74d34693da4124e638ba3ad2d95bf09d0782773e3f62eadabf151", + "0xa4c987d4ee3f068745fcf629f3ee561e0092e97efa8252e712ac9826d43d73f4", + "0xb9b151343f9eab24566187649166351a0428861a5b6fa94f71de2fb8fe0905a5", + "0x277a429c7c6c7a03c862fa2ffb34ad85288041ec8c9f27b2127683fded10baed", + "0xbea15e7ed26d69b3f23d8fd7d94e81fe1bfc1da1eaf6ca46acb472e5caad7ae8", + "0x457198a611ea95481e96a35a4cb353162a250e31a5eccfb2f28c129d03dc667b", + "0x5153da64a1a7805ebc43bee618732dc81fb9a959684b3fafc4e1596508bc43f2", + "0x060af34f8fadc9b35cd37f9f04246aafc34ee1aa4c37a859d2dbd367e1b0964e", + "0x1362648cb36596b169ceafc076ddf95e5883e7d95b6d678d7a789cebdc115d70", + "0xa4ce31c89d0a1a00239948c62d1062899263c7172ca8e026b3ef451ec18cc945", + "0x67641f488ab06e82b0fb17a28cb29b124cb58881fbb020e4530058618c1a3486", + "0xb4986100155a8f264719b50712c88aa2a8710270c4bf040eddc312c188815238", + "0x759fbc8b3113afcb30fe2065ea600c132bf9f88887aa9d83ba0560a69bd38672", + "0xab5dd32d5b48b15797e5ab8b8024fb50add99fc560c2be623c9bf2a21e703b9f", + "0x287ba2f64e0483a82abe17aff70dafe829fd25cba7b49d8d1ba92564b7403ae3", + "0x08cb94a5ce66ed9fd7ec5ed52959315098fc790559815bfedd6f3f5df35c94be", + "0x82217a94c944e75515dbfc513907b008385ca5436f3bb060a167d8a7b9d493ee", + "0xbf0aa0293b8a473dd3592a91d8ded3c52a2154e72fbb6f88db9b45f74765077a", + "0x29661c6d5bb5329ce562c7a0db25d2183f4b1d625eb03669da674799615ae648", + "0xc87274027035f0e9227ba7dc03cbc7aefa22f65a26eb0effd0fca2f3d01a5b6a", + "0xd0af9404a235f1710a9f948e6e2558b54fb5c42fda4d9bf1a16d6c742b78cc5d", + "0xba51c5c99db20baa17b49b7e7b0a636d1fd07528c047208953549241619224fd", + "0x4bd37bca9c9896cd9b694531612506d105826648619f3d14fd16a5c318dc9ee5", + "0x532ac66103ac03154458b08d4a133437c8c7fd6961795254ce123efa1b1baca3", + "0xc54db4fc30cd5f8dfb48cec0767b9b2aafb3b93ff1aa2827a3897a7ee1cc8ff4", + "0xffb2fe80da24eca795b92e83bdecfd785c4c031a0683422d0f62355e78fc451c", + "0xe16f79aed9e6c3e61fe729ada031524542e182e5b3ec950f9c1fce1a2971da73", + "0x0932e1c58f3048d6fd73221a3a41214f04e7ec45fdf4c11b90e49345562f6969", + "0xba80e5d6015708e56ca3a60705db8fb5a2abbb96e92ebb8111174b0e3ffd4a1e", + "0xcb89e5bd81b293f9356c6138ab64b945b672cc5f2dd1fd970ee2b396bf37aab2", + "0xbdc0500a49d4f6b26f871a34e1b4926dfdbbf575a51e54b681a56612d6637d1c", + "0x3e6816b92395e5fc7a38dcf2d855fcb053c354c2a5f7e89978d96b71f2959d03", + "0x168307fec7679c611dfc64d5fbe1a5072016ee7e8fbded740280f1265b0e32ef", + "0x9e71b13276c01e50fef8a93565d11bdee0be25ccd0f725abbae7bf0b4eaaf4ed", + "0x2f003224fbe10a76b7b4786297486a7efa70b5a8e3f81255148026b2eb96a573", + "0x762f0931c74069217bfd58d7f78f7e33ce0b78c3f64f6f5fd362fa3ec4aac62d", + "0xc1ff4a91300c6b64b14352f8203beab6ca0a5415a6d9d315ebcbb46b7dab4c65", + "0x60df60ce081be4375ab1d9ad95972e67c57befc0b55f8f97f28cee02a05e88be", + "0xcf9d72421a9fcf76f07a70e72793372c20990b6caa5625aa571fbdb1801bfd0c", + "0x7b378dd7b22612746730a17ba2f50119b20cb906e7426151017ef5e812ad5c60", + "0xa3ad512f38d9dbf29f6ec719c320c16da1839e791e448974dc5532e3c00296a6", + "0xc78ce2bdc258aa520008f65595e788a8b34a24872adf17c8b8075ef1a798e36e", + "0x4aa0a112ed144c6c7a615b85521c8a74917ab03d6269c901715406c704c3371a", + "0xd0212b034bf0e2a0b55850aa05b6de4d42a498102bcb1b24e8eb2db884a110c4", + "0x61e0bd9d632cfd69a3323009e04baca7603a7f06869810cf7db8be5f898f5dce", + "0x754c8639035e3cdc62655086648cdc16a4384313dcbd666c1e7ec09f96dfc068", + "0x8927509a2c42c2b0a29dcbc9b6698763c9b681fb7ad7fbf7e836e32480dae196", + "0xf0fd4f4155f92cc7ca309650341a76027a492897e3a684f138da180caebb0c69", + "0xd97bbe183c1349e700241d98d9cfcfd597db4e4e1835ebc5489061dc939e3fe8", + "0x7cc05d415afac7af0e47c35107b6bce6ea9685518c41d7827cecaa3fc0875bac", + "0x7e5248fc0a6301007722fb1ead0c6e1d0d9eb51ac4a7d7167bf8532b76017aba", + "0x895eaf42e19c1ef3f909e569cd3c2bb85b1e1235046cc1ac312665e8e4adc3a1", + "0x3ee118e7a9c0feea2709d3760d8ca68db9767f64596670fb14613d8afd7f09d0", + "0xe6782016135574b669e5eecba54eb29d957128f625914e2fef893f40376a1e90", + "0xd366b65ad480f81147d5cc046772956efe1449cbe032625c06e092bfc38319bf", + "0xdfc168d88df3fe688e693827d41ac4aac4592520e0b0819a12e5e1469eeb166a", + "0x9767ffd2fed46399473f0489ba7c00c249262548196dd31fd790a7c55a41e701", + "0xeb4d14561447da7ed341073975cda57d6b5e6d3e87cef704d525eafc08e73bf3", + "0x67962e657291b66bc32984ae7624fad4b520a22e62af2209a924afca6a89b8d3", + "0x6e69094c3b6b4ca36c9098c22374a9bc32722ec61cacd9e9420beec6d8ff5fd7", + "0xf7c8c1d37f04d6e760756f5e6820a6e46fbdd0b19c3df7fd05a7cad562999778", + "0x9d35551f2766ce0642e928badeeba7a5b4ac85ad4b8b3399b170f3df039a8e5e", + "0x8433f07572adb32c667b55af322d43200d64053b51b9e962af4ac748a2f95108", + "0xb78e4954f6764ad0b0032fc129b02227c9b1e533ffa4d19f348cd74010b95392", + "0xb3aa691481063c8afad184ddde20aead11af212f61d70840c3a34a321f33efd1", + "0xba7a844a34f9fb9d62b324c47af5899483559c1a08b144afc09769f78f1c3ece", + "0x222a385048bdbf06b1ef8dbfeb21dce0f9fe38cf1923c320069a13a735205d7c", + "0x0a28dc91352ff566b2daca40fc3840f2e07c4f94fa83f75cb582caa50e20dd66", + "0x4a23fbbdbcf4df551223bc5e2a36dadc4472a51cfdb7e0fd78f0041df5e62e48", + "0x06929e150aad9771213cc389866d016fe3c499b4a84768250db6a6b7a66b6968", + "0x4208a7ada6c20115a5307f3ee57ba28c76e276671bfd494a63bcf412e91769e5", + "0x64adba4255d372a7cadc87a187b2c7a61fb19a872dd7ec9c0b5c5dfbb93a228b", + "0x6aa0d3f1f127c8353b247a3808297271fd1d02dddbea6a80b417c91d9010a537", + "0xd7bc85658a736ade56ad3d5c04ae0b5f66a5a81cfac5cc877922f34954552906", + "0x34b196bd9512efdd5228b63967dac804a8bc07be3efea9468a411b11d7a3e718", + "0xdc40d57b306cc3da6a5fd545f7f186385e1610af7e7ae7bab1bf77e73fb73f2d", + "0xdfc80d01345e6259eaed3d3f88c13d2b2c4ac682cdb09da1be8d088c3c093086", + "0x4246adee5ca03203f0b6103c313c7017332cafe7f3f2fd16ee25254fbda4181f", + "0xf7eb092b344ec05eab8f0cae56518bfab43497fca1d2bca3089a2042581a4435", + "0x3fa755aab2762c57f9430e972867fd51b56df2aa5bd0464ff0dea012c50ad5db", + "0xb98142dfdb74c4359730040e91a52301af2ea7f2658280f89849a6140f46106b", + "0x79413cb874c1260e7075bc4c35be37dd6ac277e372e544c3be02432f39d5748a", + "0x7c81f00c759fcf2e540e974d67643181813e99ef94b98e2e1fb0bd573eaaf27e", + "0x0805ebe6f46e8df34a785b0bd649b7122122014bdcfc9994b2bdf48627b00dbd", + "0x5a497ac4ffe7e148d833bdbd2c7d2e6852c68c83f5e4a53f62d3e5569f070b6c", + "0xfcea007bf7d23e61402fa5c7bb558c53ff379dcee7b77a796bd56d2df0a3f1fb", + "0x8e41f881c89ef5b07a2baef6aa308903ee20b373f911163c2644afbdca976f70", + "0xc2d11721d34c76b7ac8fddd66a4edc106c28724350a58294a2f3e946a6b3bb78", + "0xceeea18c32fc171dfd9b98d673ba78f0039f6cf5bc6ce98ab13895c4cdba3d53", + "0xac6e5611f6a2993c265230fd70b391d2c1956135d56ea2a789d6aebc442e445d", + "0xea38a69ed6143e01d6dc7fa44a387435ea4b697a6be76bafa68bf5f66a6f8af8", + "0xdf2c229b3ab136bed94f9de388db91fa29e1ffb48701d566718fd1a0453a44b2", + "0x0ab063fcda7aa821c0b3eafcb7cd9713bc2d13814302986c6c230d140e4e1b7b", + "0xcf6663269f2b054e6152372deef6cce00f188be83ac8c204cef399d3b51d68b5", + "0xee28bd5b8bbfe0d2b13d704fea9adafad370253636faaceb98a0c7ac710e6006", + "0xb5371cf012b748a640b79f3be0a97923670ff297b8c7495d907c38449f118d82", + "0x1a922465b4b62e53dc7a7db598f2461910e9afd750c7a20720950db422dbebb9", + "0x69a5465707b56cd0d6191c016ee86b96532b1171a0978129995ae3770a5ad7de", + "0x2b42f028d9b432caff4d6a8617165415d7b448d936cd19975b237c8ba7edef42", + "0x7bd14c72f87f244911ea9d629a09990a324d0cdea2b65c189f942c343b577fc2", + "0xc54a4ffc6944187cd54cf6b687303bd35fe30a5e3c65d09fb7fdfea78ca93667", + "0x3ac6afff7d5955b6c6a0f298a40af8d0a6329f2b209e2f3753f34d1589c42299", + "0xe672cf11a898f61147fa4058fb9ec2e3e9e471f80962bd46f8b66566b1859fe5", + "0xb78f2bcf240824c73190eda1849874f2e3adfff3592728ceb3daf61d7797490f", + "0x9303cbc9ee5e2893a70237c7383f2f724f234684ca9cb8d7ed2d8ca66b567238", + "0x183a739a52fd14e5d4f62360342ddc00317ef3e61c64b4d2d5f30a5dbf09ab58", + "0x55e946c31fdf1c763ab8e884411c82ac08dc2839fe2ebbb664b8f56853144e00", + "0x39c9db0af3777400ef37d8100fc04353f62c9a94055e4976e0fec9e035053676", + "0xcf94908dea8ea9c193f7aa92a36615cf758e663eac5ca7abcdb8602141b7850c", + "0xd58d20ac4fc3dd0d8f55b141791cbf3f7c7de86c0893b6b992eb29e63a5fa3f9", + "0x0302c15edb5bee85ffb3b501cca9fe809350843742afd6a89684f8dc00f66dca", + "0x2a81a50745eed1e80b94554ffe7cf298cb5d51daaa9a56c1105c215194797949", + "0x768f093eb683e86aa990d64e893655feda6fde09bacc2dd1f3eb7008d492073f", + "0x00bb9ab90f27ec4b5534b44bc55295797562e6814155762ebabe9f067681e600", + "0xf8d858b7a27dfd19d315914f7b73b63c5cbf16cad562d43f4f0b6327cdcd5f31", + "0x561cd806916172c53c471a175fe01fa65934051cdc77e2853cb834331d2d8135", + "0x79a265d9728028b4d851fa2c45c904edfea349fc584e21ea015c9a8ac02bf225", + "0x1888ae276270d59e8401dc3e9ea464799913fc0555270a447178c240abdb7d47", + "0x431217f3e9351913a4f55ba9a0c0f785ff701c66fca7e5678566973c7027243c", + "0x832c21c6a0c09a54a942cce4ad4a8b8c8046e807537614b3e165fe9362185de0", + "0x32f804f27ac60df6ed09cb84c9f1ccf37ad14a391b592ccba40d23e60d8f124e", + "0xaed7b4bfae70386569cee1af7f8b433d8c916297b2879f8e3bda66ae259173ee", + "0x0deba13d069b0f9a91adf99c53b428c5e10ab0a71b2dd34e96b8b9475c659527", + "0xaa3311b8080a6c8e1089472ec23ebc4945b2bb410f18a56a647d59c685f297fb", + "0x8d29d39f187c3828168dc081c8a0e4395d5ea4a9e34cb015c76edc4da3d161af", + "0x4006f47f3c51100694fdd52f96d69181be7650ebe2895bb89f0331c3475e57dd", + "0x7fda0e063804839840251c20afba5989a0fb9131e603a6799ad0c8993328f900", + "0xbb2f9552ce8d78108ab3e2ff7ef26a5df4d36ac01f97b4f47b2417b6ca7442f9", + "0x1ada8e2c92a37bd210147c63933e2f88ed672b475fa4534671bc5e4c2b167bbf", + "0x1b8f836548bfd99754aae2c13a288efa56af187d1007930c2ada6d58d5d4cfc0", + "0x1d484e716cad984226a3ebb734a8f7a3833706fc115565c7c30f6e28374bdd31", + "0x7cc728fe6e75e0d0be59d52a54bb4edfa1d91970973dbd15c96f1bbed4a9bf4c", + "0x2cd0fc8fa60d8e684b6ba598e647af105f7b8da2c565528545f51a8cdadaed76", + "0x3929e4bf85da9774c362d50a3d6dd9e87de8b22242a84d919a36aa09e05ce6ae", + "0xb03b4180862c1afda1c3db0c26901ef6ea6fb507cda5f9d30ae9b5cabdaf69dc", + "0xa6b225c5caac14ed58ca0178bb481feecc76e991640761483f8b07bc76cbe7d9", + "0x78397bffe39befbe82debe4462230f66163d0acd113dcba32763b98ee296001b", + "0x3b837874aad1b100491b96d43bdecae4f25f79b934972682ee1a51e0a4e4fccb", + "0x147ec7966a4ed0ca4c228bccd9dfbd614b214d422ee8c664bd8b795ceee8497f", + "0x4c1a38d60b8b213f3f8da933029812c9c25ec467694ef0e7f9d5ee7143f69f3b", + "0x5dbbaff7665f79c9e9c94a7c93ed5ba7fafd2e165c50d7545a17e6629e1f8617", + "0x570fc6b00e32ecc5b2985ee1fd96710c591948dfcd1d28874300eb2586840277", + "0x9b40fb65b9cad6759688de11f855332506f5066f9ec1973b8f18febe7d94e7ea", + "0x5ee26d41bc9254bc0f3e6a712d1bdd4994fc57994f471808a16976b0cd37c158", + "0x95a1d4cdb25e5e6bf1488ab08e192bb78a3a94bc3a3a040aa5350a712f075524", + "0xa2cf1db93fd5bf42776aa1f3da15093d08e050a6e8893b80ca3a25824cf7f6b0", + "0x99fac6ae886a52bd92f0582fa0c4f0b7323fcc4b4ab750a536a0e5115f3ebcf5", + "0xa01b585823b96b66ba752985d92e07ebfc9f1cdb946dc8c8f0c60a68c00f07e0", + "0x3d6d51d5240cbb9cd9c4f56401bed3aca004ba7116b059b2dde114b8c856ba07", + "0x62bd6d59f13046156f353cffb8b5ac634bed4891dcddf7507f84247c81c2677d", + "0x49a17bd5c142201cc03bfafd4b877fcb446c034e5bf9a5d71fa181e59edccdea", + "0x2b53941f71bf2a552a8e86bf4f7cd802fad8fb4beefc54d66135053a92554f99", + "0xa078680d48034878539558173528d2e1e7b38d5ea4b05279f10b6ad275746a85", + "0x6b20c3f94f7adfa865a638c7cfd7214aeddb4218e57258302b426fdacb900905", + "0xb23b7f5acf597143933cc730803905c504e3f7e7ce82e73341198228137dc3c9", + "0xdc42856b19b2fca32e470416b87357db96d2faf829093d1d78d0b372301ed85b", + "0xa1b08334f940b123472f46fa9fb2d37b849473a8aa3605dc97fdaf5a8a9fb457", + "0xe0381cfe7aeec9edfc2297cd904669de69975a2755015f96bf82a54b0a31f9d5", + "0x23f09b271a3f0c9d5a07e988329f5a3cdb130643b3bcf61eae918922a7859363", + "0x26450113c5d2c4521933d2e50f52bc45b57a9dc9e692caa3d9840704c3d780b5", + "0x855f88b88a09874715e309025e43a53d31d247f3c15d605f0ea3760dab72c2bf", + "0x73639aae032f60efdc666e052f626e6d0fde68229c9f85f6ac46521675826053", + "0x9c3217b0071344851e0103e7220878063a2982325a10af72d511ec317946a7ce", + "0x4fe13a26fc556b654318b51e8dba5dc5cb7869cd2f3a141184e7f43cc8f442b0", + "0x82ce11f4d3ca4a60b1efe21f176dc03f29d130b101494c8a195b37bfd4d4fb6b", + "0x3f49c98abcdef0f30c41dd3c1fa30ab2f696ac37768ecf84241d35044760dec2", + "0x1f0d20cd3f9032793ef68a7d56a87986bfda5bd94ec94347da7ddfefd41af2da", + "0xcbd45c574641337fd79abd9dcd1297ddfb3ca10ba960e8226cd09bdbd6150257", + "0x63a23ebd8da3bf25e6fb7c054e6c5e0cea807498e0fba189f4802b179144a7ff", + "0x03e97b39aedb3df5bd58b23009902d5da1987bfa5f8ded7d9e3233f5cf7c3d96", + "0x76f5c0a33923821423ee1e253a06b817a7d82e70129cfeaa1779422fd0a23a48", + "0x0ab002b1d146c3d131beb72254bc436471bba14b5aa298a0a141d0f2be38f263", + "0x430926f5d1dc2ca84441deea38007b3a2c4b13042d847c154a3c3263d64dc4b9", + "0x02bf0d5edaecfc484977faed3c522b19019762f15f924aebd46e6e5d30cd54af", + "0x5429e14df0f2c43b05507e1d2a2ad77f6215565b9df2715f90c310dce2e919cb", + "0xdefcb1ecd23fc69dace378b87a8f4e0e44c51d6e7a90e5b439570d79821a7129", + "0x50df815ff892f181081c38ff53379ea00281f63cdea2dd34859344f5d4b34bf5", + "0xf1521f073a91ae65ef7ea39e8bad2c5a6f3dfa542b80ad35f1c6ec7d564067b1", + "0xca8b3c4834fbe5315bd7d574952f39ee5238b6ff7ddb1140819cdd799319953a", + "0x2a34098eb037959f1cd197fa394edde96db16d096adcebd4d4ac999dc6750154", + "0xc00f3f9862c226b77a7d4330b0f4fbbe159e47ea334b3f9d495c0fa1153cfd5c", + "0xfd823931c922231f7e0131bc8fdb6126ad5969f1d9b8457dc6fd1ca3cd88e696", + "0xa0f652640d347886691f20a20b0fb00b6a709e8ab42c60ccad3a1edeb0d8bbb5", + "0xdc00bd3012da013e181cd2ab7400a10d78a00a184bed114d758e40d5dc765993", + "0x1bb6db5026e01c4d998fbbe4964eee8ef311a664eceeceaf346f572c9f0db721", + "0x4a2f8a7a5d41f1682072d15705af449a889ea14e8d8d88df16ae3959cb09a68c", + "0xb5343eef17fff800d3d0b0e2b9f3c746725f579641a00ca4f0dbf337ec68ff6c", + "0x9ed9b290fc695816413650bcf875a16fc5e75fedc681960130bb9f89ccc79efe", + "0x749ff51afc8f2d9aff77c31784f64b93a00810336397ec4dae635eea8a1bc811", + "0x148b7138697499b467b018c27227f42bd4a5f9d9963ffec10aeeba9dc7ffc741", + "0x4339db41bdbf43f4a5085b34cf3abd74d97b9f2634b319c0c2764ad9d2780d59", + "0xeb59896322096620ef432e7a73b7ff0a6406bdd73311a813a8b9fe932bdd2c67", + "0x112860efef6cab1410c169a826219c9986f3eab4ed3ceb9d62e7c1b779510558", + "0xcdb2218450e17f241c89238ec2fd4b06b0a459213fa87812d2357106c40b44f5", + "0x89f05911a397ca61fc6739374078725f91b808f6617816e2b4859c7f0ce98e01", + "0xd576925df394d204b2ce15f68f51bab731a164f596a366add936a28472bbbe8b", + "0xc0910dd56c248c42235ab2264b195d1e101db1a06501fab838a6cd23fa6e9c11", + "0x1956908523fdf07e567a40174b03285f6b87c96bef17b7dd7e74f6cbe1285dbe", + "0x7aa8a288c52f5455ac56f410308af37f4ccb758aa17b608e023f0100e4c66913", + "0x97ccad51a795579e203aad258bc2ff38acfe7cecfb857ca35b07bccb89ff1e4f", + "0x6a829f1e5c77b4a0ae6de125061dcec0968b30718797896c6ce3892e664c2f05", + "0x814bc2cbba689612b10f5c6dce1197636dc6f57221d30d77d2b957a53fe240cb", + "0x062f1dabc0682ce52e55b23aa31eeac0df9ae497ef8424e3e3f32bbaf21f07e0", + "0x4aead6f97e63823ccfe61859353a43fa35308e22663767a52faedfbb9d2da29a", + "0xaaef6ca1dab6a26b716cd9708612bd2f052c565a2f5d8fe2e95227d5cd4d1ebc", + "0xcbf1d0f6dcc96cb68e76ec77df363bf6e63cb3d5627bd7985d445072c5bb9762", + "0x2c9c725e10b43fc1a65e942ab85c6681877f129becc5a4dc91f47c35c056f661", + "0x87577d78a4df8725a2688e1acaaefcb568d431fd02425e459fdd11c7a1854762", + "0x3d8ff882212e24e01816fc87f3764ef436a6b1f8758b105295248fe715c8273e", + "0x2c7c7a32370bbd14785f21e9703bb340ac99f91d3af875faa244b03da2ec9529", + "0x15a038192281d5c22002f4367407b6ec525da0b1d2b870602a46f2237f4550e4", + "0x52bd195f75ddbf6857b37f3382f2ce797948267c479a5b0b518a5efedc3ca789", + "0x524852e22929a52704b4ae09ff10c8f1a0131832d55bec6c8a1be33d7643953f", + "0x4d369f908dcfe5c48ac0d8a80428fc230c3bd7f28caaa5325aeb382c2ebec44d", + "0xa4d2a17e3c183a6f3f31e6ddbae18fe7bcc0da6e724826f32be3d6b970d829be", + "0xb648cce8f4de40fa55a6586f1a2711eb6264a49ec9de15e967974494d8a5cc82", + "0x6413b9ae9ef9d7e2386277f5a81f0fce238e0e7f1431abe009173006abab3cdd", + "0xfc44fcdb16648354e9f7fca2acce20705d66a5aa2ec8cd71d33b66aa3f87c3e6", + "0x5bff1025a6b40c5d67dbc504b415bc0c7af01a1f29f4ad4e0459e1faf8013bba", + "0x2760d1d80e1154e20d27e864af83ea61070a62baaeb249a31566faa4ccfafd4b", + "0x2e47ce46f6d5608e17a123d9876035cde79d86f9ab20822f7f4b129b5ef0df6d", + "0xd3a1aeb5243e6cfc72d10bc8af865ccbb74cf507eefb1a52a04f24f7a4e74181", + "0x219ec9276c31ba271fdf04f8f2317f87f6c4c5130e2b81d659e1bd96984da067", + "0xa5874b5bd4c1df4cdeb754d2305d52375520edbb4be74ad4f0222342100f89ba", + "0xef09ca1d3cda71ab498b1db29817cf9e0f71dacd0592dc6fe4bbdd3bae625f95", + "0x8799c70d42df9901b382d811775fda3350f25b6dbcaceaa727c11e9375b01b2d", + "0x2ea2159de18196ce4354558818bc6d652b22b6985dadee7ccdcd30659cc51528", + "0xc502f0dadade454f1a0e3db48f4ff285ef3e85d7e63eae150f3254345b0bd9a8", + "0x3cef9af4de9426145be948375695c44f8eee38c7515162422ea8b2cf49200f48", + "0x948749785e822b026a8307470b6d8a5c26999f511a82157875186c1d34eb2d05", + "0x405d5826c2b5f718084dec5c12f7b395bc109e3d0cd075ec8ccd5987a651321b", + "0x87ca1064ec679697de3e2720002eb137f018d2d4a9848d2fbf6f802553edff06", + "0x757569e527c077e1fffeff50d391cc3687d23b87c98b0112ee1de75485baa13a", + "0x7c34a33073b5140f356f266508c76967a47646dd621b575018e2715eacb6b206", + "0x50bd4dbb58c3db3a37c7316d201dd0d88f861d1f295410fbfe82a365e7b1f786", + "0xe87f1c183ea8f891c8323563e92d443392376feb460a10bda59e583c46e919df", + "0x5ab8dff5260706b3960f676261572555f33669f2c8a800803db1fbc8af78fcb9", + "0x2fa6731b4bfadf9fe7700011ec4f8385e99df170a43e0f71623cd2d0f5a2dc09", + "0xb246f28d0f52df5f5aa2ee0da5dc890249384de0380c16df9911e6bc191f6fe8", + "0xfc51f5d4af426418ae9ce32d71c25bf09979fa61cae1bbff207677bf1cd33ed3", + "0xa6ba1232c0c10a2fd222e40bdb84065eb263e778a5b7a38f3ff4a5eb41f1a507", + "0x0fa92ece71224d96e481a9fa0b71b8068e213654bc9bca22fdaa701ceca611d5", + "0xc4bd1741555c0d3724b1322d5364246f976019cc64cb65b6fb3a26b5c4f377dc", + "0x41002ef7d2a30ddc59ed3423fa45edc41314d6297f6334ab4c3312472255692f", + "0xa7c8a9b3cc61de38aad1716145b69f3e423f562ae7f7669b93e27fa5952630a7", + "0x79c4dc489b9c278df041016b6a7fea0794a1c338cb40c3942344c3f5d8dae49d", + "0x6e2f45b734b421bc169d43195bfd260590a3a00757d6ef39660a8c18438339d3", + "0xa96e23cbe6dfd278fcaa69170859ea974aa1bba05730e0c420fc14d6c6b51b88", + "0xd31d1f4702501659a540cf349c9b9cb2f3e30f75af42045dea3f9e810a2e1aea", + "0xdb47c372b6cdd57352f98b2c104cdfc39e2fdf548eb91dbc9f8487a9a8b82a5e", + "0x0b8940a9a803308fb4341c3486be689df5625cad4d4bf11d3ff484de0c758fc7", + "0x33fb847132e218f36a3ef7b627f831fe3951285b488239a22fd79ce4f5a57d1f", + "0x88c144c779492612d28753dbe64e16433312f2d7f2c52d16bc2f14ba1479b050", + "0xbe583bf85449038174419092f5d3d95b426d85a0b5dfa99ccd691584bbc7ab70", + "0xd79baa235876c92b2531ee0ed2ed5fe322c2692120412f88f2bf4dcd6fe97016", + "0x0f5fe784f328480e1e2ee1cdf509b65c6db0873eb36637808cedf99ad9fe91ff", + "0xb262fb29f06d93d50c9dfa9b16ab39513b5374704a5276d537f1bfd30f07734f", + "0x01c7860f85280075397c24de4277797638be0972d7a2b48e57521929ce9acf3f", + "0xa311e1138a451cd116c2d9be6a4b48fa75b97d3fee938dbe2b17551844f6862e", + "0x916209353ecde0c204d4b2bebd6f5a53a781da3e5ac48665c97c70c5159f174d", + "0xf8804e1c23b463551a1adba8dfb55c452b2648eb11c88e0cbf726ba28071e9ba", + "0xf7c0422736271b9e6371e575be362a4cb3ac728cb8d5f5b382022c8f96f928ae", + "0xb0003269022cd711dba1547099775b0a5bd015b722467c982ecf2cbbe4936a19", + "0x05ee18ca0f5e25e311373c9c81b8d681528a1b1ac8b6f61e5f0e202993403c9b", + "0x4c40117cbf975370e0abc3e6b1b67268aa22c6762df87c62e0b843a42216dd7c", + "0xb37e12892dcc206e17e5a15827217e4833c36f957445185e853073d2240df7cf", + "0xb18cb1b126be0434a2056246043b46585ff041e592eee7ee7edac8c83a46448a", + "0xb3cef28c5ffbe02606246812b1234d56bf9f56098d6e1331bdcd5703c61c8847", + "0x1d82651fb90992646896e27230b88958201359c22dbbc787ac1cd181de2c5c8a", + "0x18077b887a078fb523a7b0a89b546638c12b9d7b9a0cc2ba37d1510fba84bf97", + "0x769409764e23bdaf553ddcb5c5b8ba6eab12146a102a18504b9f6adee9babaed", + "0xc90d1e9a7a75f2cbeb6ca208f5436a1642f72cd941c4fb4482af14bb4c693388", + "0xd55a46ddc102995061f073190081df8870e1713d2d1a394b8bb6905e33280b53", + "0x9a6a705cd50bbdc2305bbccdc146009fa932aae3db5f0c42aa2d2dbade932a5f", + "0x39faa0aff74f8028b32d94d796179ad14a2c2e0ff8730326b365f0ebed45a2d3", + "0x2e8644aaf909108b1264e6fd5a64f7940630ec3c1fb84f62e7fd77097a9afc87", + "0xe3f2f5b45357546316fbaccbbe84e4ed64f9e5e9a73d48f04b5cd0d0631f37e0", + "0x8a091da6449266dae676248b30b63b76180068d4152102ce6419504b8f5d5b29", + "0x37f42c3773fa5067d30ec73d51704f60aa713268be3f9dde078674c6d870cbe4", + "0x4b39fc8ce8cfe4898ba777b4ae5050e177db792b99759ef89156b78d4aa4b84b", + "0xc6af41d645608f3d2307ef857eadd9aad8367fb39a211b417c1361cda631f59e", + "0x5c0d621b72551b5d5c88f38e8264b97312a65fa1d4ebf15f010d27c95272cde9", + "0x9954c22807fe27078b955281e04ec4f3f0461a17e5c11e8ff627ac05d51182aa", + "0x7eb44e49a9187d446c5db3da9443636d9e34f64ec2378c6464da1b7a6d1d2e3a", + "0x6c83f6b91bee8f3586527ace752969ba354f558b9c745df251cde22e3795796d", + "0x8673fbf61d9288166d58cb9f849a8e3edac524d235890104c91487b5de0213b5", + "0xb3d1098c96c6772f40a684464a9e5c21bba4d6949088435168584aac4415c7b7", + "0xece69c0a80f06a2c627ffab0b4eafe87287fc7c584453eea8ff0728cbcb4a8f9", + "0x7f193d0d1665b5e830c7b07f039fd15829add82c60708b1efda9b23768f64612", + "0x3989f1019fb11a638985e81f2a89bbc98fded50879dc0341e35a961aa2f51734", + "0xc47c48e306cb41bfb94e3e4a83a4234688eab898681a37f659a9ad10b24c4314", + "0x720ab3fec5998a64b547b3460709665ed5ea54028811d488184f96d06f95f734", + "0x79db4cae00ae7679a4d73ba01f8dc84769dbc688434a5949476863330e37ba83", + "0xf0c758033000ca69ab2689725ab664ec73e3ad0dc279ef91c7a64183363713ef", + "0x103fc6c1568d9fb8db26407181ed35507488961ab71c6d6de222c87e6176d248", + "0x1f0b0e325e195ff3cefc415657712d1b301f35ee374970d3bf0f3cc1db9d6193", + "0x563ee0fe59bacb32667dbfd7f0d7c309edda5b8d6fd1f2be716c287c7c25bfd0", + "0x7f6e448526aefe0d11d7e88a97b86ab216effe2d6a02e481e70eb250302f8910", + "0x3be5ff510cfcafc05bdd61f36cc47cdf8ec9c0c8164626c2e04c065160de09da", + "0x5e909d5efe73784d947d488722de3f850e43f627ccee08dcc5fcd5223734790d", + "0xe9d4a5b0bfa793f50c15ef2206c844181dc650434546015694723687be3b4c0c", + "0x1943188dd33c6f4751cf97acbbf9962772518fb389eca49ece2e7f4235ef0749", + "0xd3c5adc0edef1bbc9f89f61b9627f64e0379a53a7ae9346eb665d5a14c3894c5", + "0x9462a8437b3bd275f2a78f6c1a6b54b75f2bd9bd6fcd64c32e93f148ce4ec35c", + "0x32cebfd699b3e90d35345d595b2253ed31e78922aa88ac8b393870d6962ec3e7", + "0xbff7d4e1a3b55becd819970b6c4463210dcbebf3575d2f343cc079bdeb978a16", + "0x3f0f88926d138ff06effc70d894874880167d456b291ac0b2fb34f645f0494e7", + "0x302349458a9c7dca8f6f1802edf143a3e05fd2092d56dad9a131425a3896d7d8", + "0x974483b0592f9d983f2799f7d04eb725bf56bccd584768ba3dd4d03b2201cf76", + "0xc60b915d6d2f5a02974e880910941df40e9430068a6c8b2e0a6f0f9590727e8b", + "0xb5530a092f4334ef5d58fa1213c37f7845be9608565f760b332470578fd138d0", + "0x04c01b19079b0eef549e759c601985884b59090ac0c9d99fe628c9fd47ba830b", + "0x4d62f6a3d97950fe1770b5b5a22c77dc3c86386f77736cbbf6d32237aea98d8c", + "0x25144b5349dc04bcca7ec34273fc37713ffb014c4b36ab7ebf3053ef8aeca684", + "0x99c7402838ad7381764d33a2f53aa8da5b4ea0245e60e8a529e4fceaba050677", + "0x1a512d0948a13d1a9f13aeb3f1f30f7beee77cfb29ae70878884831629e8e6e3", + "0xdc0baddca08bb5d176609151af121e2d048ca60eb0540e50f86286b7716bbcca", + "0x220d54fbf0aa40ead8f22a3e651ed1d49dd3cc25a09552a47b3f93863ab510f7", + "0x2ebbc72c02c3afc86a51cc3452d3f8af98d701a1eaa70712caca18e102e92ec1", + "0x272804895c0502aede1565ea0b8538a8d1db5886ade0b2cef894ab8501fcf578", + "0x4f65cff8b33226926fde7ec7879cfa57ad1dfb7173ee1c6b982fa3d7c984cae8", + "0x3efaa8b1add2027ee0c749be9e0621120bb881c742c38581ac45ea8e32b66e1e", + "0xbe618e718279db4d8775e47cc0f09c7034a49afd87a19cde07bed59528476172", + "0x5a05262a5003fc56e51b77b707e691d0b25fe928f224be7bfd1a5af79ac45bf4", + "0x95fc732298493a4947dc16f621e04f236742e42e4b160d20ff807e6c1fd71a70", + "0xe8518da4a66f738e4179ce1eb4b2a5cdc7e73ada8fcdcdc154819272cb03347e", + "0x44cea9dd80b28522391ddc33fd825ee18bef229042b0149ce4c10b25e2ecb05b", + "0x1eec8f74629cfc8cff988ad3aca52345c3ec51fbac7631a1d0a61f95097e791d", + "0x0b55f6707047b3f7a6d265ad23e0db8e33b35bb5bf51fc1fcc8a8ba865484a95", + "0xbe4c079a51d39678ee0a2b3c9d23cd2dfe9dc002a08a3d23326ca0a06b38118f", + "0x109487448c3e4010ee194663fca52d6ec6e7f699ea111cfb4fb75d863dc217ac", + "0xd17645ca0b867bdd5ff8b1c82b012e53affbb9f6131b74c30df71eebe72c7520", + "0x922c7cad82b240841c80ac78721f8c730ec39afea385c6c79f0f6d64eaf8ea96", + "0xdfac264827a2d2b7437b9d0d65d990f128226f981cd5484ac30eca0f06425e3f", + "0x1c8e1e4712f0a6fcc99075ab01b305f20ccf6a10f364b411ad59168e2bff642d", + "0xf58a81506037409e816dd155717db5665a5a82ad1bce239b4df7d91d2ce68701", + "0xf7e9d65ef4f9b84177dd2e8468058807a99c35fc33b63ab1e81ae49f35db9886", + "0x0b0030f72d1c9aff3b81d7997c6afb355f03963f5f65a30425efbdd0c60e1cb9", + "0xcd08ea9f9159a5563dc5f7806865b602938f5468661a7ffd772327e620985f28", + "0x2925bbc184933a0a4db51c38c9fbec6e400cf04eef442513aa1319b27cf13875", + "0x15969b543b6b53c1caf7e748c8f526d33a3a2a9bd40196601ad71ba28c00267b", + "0xaaf42037ce419946e62e5eba74ed36501554ea4c287df7374f85b078ece8ac42", + "0xdb91d47792990e942663939f4343f665639a94fa077dc9bf6d2355058c704072", + "0x0218a472b59c04e78796dd455c17540cc777826782b90af1349f8bef10ced6af", + "0xcdfeabedfb9d0a68902a2f10225b41e6e5364ba8eb7590b05c9cb6a1df8b061b", + "0x7f589e511d1fda2fabf5962a9d1014dd59524afee6c5c301ff29f232e687e891", + "0x6398cb494d78d6c7d0c96862145b31d122c83fd91c98ec559d775b772496d355", + "0x01c4602d36999f7807730e782e0e1ef227eae49c620b69d5892c221cea6e9a17", + "0xea981ca0e88bdd53249c953fa14e5a390db7bfa406011cc2da4ce9c710378f9b", + "0x2f8bb00f3ad0f680ebdaa83bda223ed60d6053b339be0a0c9258ec1923e23e77", + "0xc2c93bbb8c5919eb57afc5f305b773e37b9bd7dd545ee52aaaff712bc263781e", + "0x6cc44798e685ef999ea3520e23cf30e56c14dbcc9dedea3f78a187fad32af09d", + "0xb60911e24d88435ba960d49618a91b0c7a6dd2b0c23b9ce3d779dfae2830c51f", + "0xff2dca159f6625be7ac2d3d245dc49fdc4ecdffcc3026307b159d21362d2fdee", + "0x423ad3cbccf8209243bfc7bc70ba0aaba6b5505a341235813fe072bc8c2cd5af", + "0x34c6c5eaed97df66e84ad4defd5b866e40a9e91698b90dcb566bc0b59f67acd3", + "0x1ed84df4c4254ef0251701afca0e8331cdfdf2f2ee9cfd640546f47cbb3e6c74", + "0x4b1952dcad6489573904fd44eac445bbceeb565d6d9a13848352ac57493d994a", + "0x5b05417eee99ddefe3867b1539fb06035dff3975431cba8098c9a1fb58df1b53", + "0xa90f63f20a1c3e977f8b878352e37b3d8cbf6e5710b5ba3d53b404ac51b13cc2", + "0xb105b11741bc02f6fc4ea9830737d87a0a7a44544fdb87fe856041fb20c50e20", + "0xb90a68bbec00ab48091cc8fd89a3b523bd1b477d7459577de9e709af4118d6ad", + "0x1bddc2dfca241693cf8382c09e0c7f1ce36d84c2671e9cf2217ec5afc386b4a1", + "0x5f179176a174fa122a71d857b30f29a7e310edbe01c14866c32543b804db963c", + "0x038f4146ecdc0dba9e96a57b187847ed45c379353f2195dbd79a69e7722f765b", + "0x70ca6e40b0855cd1f3236be46b5876fabcfb52ce71f16648dc162dec116956af", + "0x4cd74bb46ef1955a6cc8d49578b74c239d706e204af5ada62fa6517d09475d76", + "0xfd736b1289a3b6f12b5556583def0846812a27e216dd4c9304b803f65f21462c", + "0x6a9dea7db8ddc644cefd9341d70b45daf2cc7557bb918ffa5535af151498c63e", + "0xe07c7137f53c45ca8d6bdccae7f88663c4bb308791e31a227e3f251d3ccea030", + "0xa937c1e942766e404eb59b6bf7bd5c0e2fa261feef9189b428a243666211ae41", + "0x3bfcb8f537978aa27b671e3d288d82fb4a582a36aa5610d400e4cf7036e44580", + "0xd767e82382f0d3d3a83df20bd2bfd1830ab26196aa70bbe8f19da285fe305dfb", + "0xe98e058f1a0173bce8aafd4fdf4ace7733a3f55c675b1cd75a2b3564ba3295ed", + "0x51308e986912a1ed3f823ce061c419d4b675e65ab05a5f380d1a27a7889980a5", + "0xe1d8c98839b71cdb0e93803fb3a30c7fe0544180cdc9cba41ec011d1f13429ab", + "0x329af42391d2776c17241c0b929b1034194f20435f6567ba9ed778992c020b32", + "0x9a5d21c93de0c8feecdb4dff8695c1cf3823ccb7d85c1e07e2282c63bd089580", + "0xc06b257e844e3405cd6b0919b4fe61ad2ac82eff52d933a80d0babaff8a20365", + "0x17dee8de1443fe2962bd5a61928e78cdabbd9f844453bdaf7c601e1a03adf4f0", + "0xc88b38441b10ab30d6a6a1b9e26d2be4ab4ed8079f4c921fa80a1160b4685ec1", + "0xa390a1f42033f2c49294b2d18ff371bb881309bed2f84647b9b1a7f66921c5b5", + "0x8468526c5bc953a9258a8a9ec1eeea65d18f806748a9579ba270caeefb6c31f1", + "0x880bff2bd530df8ca336373d4487d62b9d0701653b5690f41cbd658ae14afc55", + "0x3d6e600ae291f1560d8b1bbc7c3da193f68da161cb9bbed60ba9c723ae48a527", + "0x4a530ef2dcfa89fb70e604742db324a44dd28ac2a138685c782d2d647d93e433", + "0x7ab8ece70dcbbd2512f476468ee88e79b0b0c5b3e02ea4931acbd31b15ac26f0", + "0xacd93a632498706080114f2b014fed81f54203fd4e25a3c7563a946cc098c4f2", + "0x2b044d1483cd603b3d7251f68f9eda808dee27522aef6e1c14cb6c69b503c360", + "0xceb469c932adaa06d7fcbf899ed9fa32f870234f8a3945cc113c869d6c9e40f7", + "0x427853bca16ad6e54ea3df13b783624fde6dd8d032deb0d05a11a36431743077", + "0x4d3e2b107450762524dc0c35217b4713816e2edbe6dcf096fc5fa2ea661ac65c", + "0x323bd56a928f9ad4e9c6a6d34245a99d368b23a30e3f3ff3b331438ab42e676f", + "0x7395d897a8e270291dd8d6701f054f20eef80a1328710bfb39ee81b97f8886d3", + "0x161f013c5f6630964f5a14d5f4e763627cda28b8ab8fac5cf031396d10c3059a", + "0x3e738d981c1dce443d7a54b5badb780535c7a3bfa0e7a24633da35dc5b4a264d", + "0xcf3095e10977eb4c7a8714388832ed8f48a4dd987b824d4a6bf1fd42ea32aac3", + "0x3b074d359a2b5dc40b535b291d2d7f0f80b5bb5c4c039e65bd15b42a8588b6ce", + "0x69d573dc61807038961dfb21351ba32d447498eedd8d1d4214a18209b17d2bac", + "0x91380d2f0eed0b77ca4a4a531e53d4b1e80ced34dd7b465335cc35c185c6a8bd", + "0xeb078e076a1174e6bf749d4720b99d7c81e215caf081c146d3aaefc5568f4b1c", + "0xb2bd8abdc745316caa335a689c96157bcd4002dfc0b536edf824f7d601f5e295", + "0xd37d7a8579c45c481d2f70dbeb14132bd8b45240b4c6118b1617b7241471c95b", + "0x734675429fc6fcc3bfa0b4b77681524dc4aab8fda32d9ff01e1aa26955d406a8", + "0xf471f0aba12658fbd89f7c3045be027221f84db38bf9f05d9759e53ddd0d756b", + "0x49a9cb62eea7f044a6089c8355d5e1525d8daf9cb2a52232a7dffc7e08694bcb", + "0xe9040da9a0aa789aa64fe27559edd7917e31bdc7e4ffa395994acce759b48245", + "0xcfb59b67f16a1e4a66458ebbc09c340eac4a35b49c495a4810143f323bb1b82d", + "0xf9352c6830acef4d70b8629781257fb85adcb523d40e1d597b771d6323c09612", + "0x8eac808b0844f3f8587fbdb0a01e0fa021787fd92d2491597140cd82fa2d2488", + "0xab23589f3cef85a93e4e547978cb1f140094dae80f5df47371fe497ca4b21189", + "0x6b02eafd4a8ba1fd95e0673e5fb80633fd59101864e00c6e4dc96b1dc21256d9", + "0x05354a71e519f0d9f17a94227f73befee3e78eee9edbb36f776691569eee59d4", + "0x327cd0502e3b500265a62fea8a5674606d1a680cc1b8d8a1ebce8925787f6178", + "0xae654aea97d747ef6f333f5cf86ca98c3afe937209f7df09ab7619463004641d", + "0x6cb0f53326711ce6195adb88ec5e5aaf7c951cba76497620c7afd2b8b2ce4a5f", + "0xacb80b714d32588d8aba5924a8744641ca5eb6906bac7ed5509b36d54ab73e5a", + "0xb846261d0f0ecf998ffd6760a36b5a07f04965f3f5af761c673e94f20470ff10", + "0xd17d54c051fe4bbdfb9d58b6c9b1a3b29557c03b67c9a04e4b257d447c29ecea", + "0x057556f19f9b4738ce06ee0169ec4ced1351065d01e5ad217bd7efdd2cc8c2be", + "0xbc644892d76f2131947e3d2c13cf32b70ca1f4349644404b1bd95a39af5adb79", + "0xa8b92200767263861a8e466f66774e3a6de204a2f1d6103eb7290fd3856f972f", + "0x0391d26061b3234a444e62be59f5ccb9035e0aada847f3e70009a45ed541ff09", + "0x8672fe13dcdcaaabc47d6d03b420dec7ced9f3e69d4731253b4afaa3054d51d6", + "0x040759867f3392da0c2f9d3b734f0efcd3692a74a2979170a157c51619464c6f", + "0x885f3efa75d91ffe3fc5f91817def7c6b7c87b71c9f31b38860762590c517298", + "0x61d14897d5242afd72c0730b5e3974b44681ca2547670073f0362b6c34973f79", + "0x672af5353dba08c6e7485add68459c236d2ac901952f4a7b766d660275c365fc", + "0xbfd33778b91cc203309671263478c6c2832540c6cc6c154da23f9c20032cc350", + "0x6c1d4b85f1edf68790862fc02127c2ae152d197363f63559ddb2a29848ca0354", + "0xd495d3dc9aa963019dc537f05e4930767dda76aaa16a639406b6928ad979b9f1", + "0x48ea28c3ac716fc9f0f52572c2c40f568e6627b147601cad0996796f5a949e41", + "0xbedffa343feb39119b59e1cca66f049881de4539b877303dfa56ca8d2760ca4f", + "0xd32c2515d7020d158bb07c4d8e58075c1316439f2d4d50290479b23325d444fc", + "0xbfd5dd1c61c624f2cd74c8d23b5908fe162e5052145855005d95e69052dc1b80", + "0xf8985c28cd6b27201c59861e888f85b373e5ea83c9960affc8ce1b142865bc37", + "0x3e1cb1eb7f1815e7eab3258d3b5af1655cbdb99e786bb6d1a67f3e023c0bff04", + "0x98adc0bc6513aa815221dc3cfb93ddfd5514f8a5fc54ac68b50068dde8c7a626", + "0x6e14f28be20ae649799b98a55efaaeba5cb3ab4ea337e70b6edce3e5cf78972f", + "0x36a0d324541ec7d20136a4c56cdaa416fd8fc6b2b61119c05646eeb6eea3ac30", + "0x56a199ee436ae11be8d570e894940c2ecb0e39f16466e422adac2ab241e7bdc1", + "0x63a230d1353f49d3869e2e6417854112a778c9e26394d04b51ef7e6710b3eaf0", + "0x63a9630aa9998da69854c8ea499eeac6267e3d7ac94a82eaefb1c9335e8e88e6", + "0x62db7a521aeddab8ead707aa53128c96509578b8ee0376d0bf5fa42aef92d53f", + "0x5db5ce7a168a4f5326fe1f9841f8c5f3451650f54213189171df2674f649e108", + "0x6844bde7d8a85474e4d5073bc186078cacd957164a96a1a1841d526b1d37ea4f", + "0x8961ec490b9cfcb4afdaa99423297b27446b4d6e28b1f0c52127f77792985350", + "0x8fe00ecde3364348f03dfac1563cd63d51fafe50ce41d9475c67ee22110e84df", + "0xd7b164a1b36473eb54026c92ed8eddb7c53619d2211c90d07586131df22edfbc", + "0x782c2967a19f2e7f9b389abf93fd4eca27ef00ea3e389fa282bfdab175a71c8f", + "0xdae9a94a63057827b6e927213516d649a380ef93fc40db922301d98d17053960", + "0x966c9d5c2c2bf3282ee0bb132f101fe1f73fc1542c2b2fecea61ea9d166db792", + "0xfe19b1455a8b82f704ebf6ac2b179c6a3b7248744f92240eacbb8053b2c13a46", + "0x37f922416529bfa867e382f0d799fa03591a3905db0d677b3c91f4e5f323dc8d", + "0xc6fa85e6eadc19ffefc0ca824ae87daf81a4314afc0714cded6084b696209946", + "0x624fe68fc87dbaace6eee938c8ca60489c0b7ecbd1018120fba7f01c994d80fd", + "0xcfa3832d2d029c8105cc9cd5e4dc0e0eb464ee9dfac5a63f83613da1ae2817e8", + "0xe56a4b763fc962ae685b50e7a555ac5801e88c89aced7fed9a50f14adc375be7", + "0x095891834970256010ee4f6883a59c85bc2fb941031541c44d9f9d44a9dfd01a", + "0x0e35885387aa0d1ece9d300ef3b132fb921ef4b11c1bda73ad37334aaad5a79b", + "0xaa9ac9f74325cd64cd365613913c86b938ad1320209fd14896d44fe7604a25d9", + "0xccd06caedd6f2607609ade7b7074b70ea691b316bfdfe86734ff278a6efd3290", + "0x8be01494f20dc16e86c23b8d1e05c8045acf9bd7b70f32a61be21e78a04e2339", + "0x5fa101978b71908d0007c3bea5244346fc56e943f20c056d49fae464fdcf3085", + "0x3f3822ea80a082e0088b3c48a28c689a334ee50e60ffab07cfb987da24e38975", + "0xb278455ae321fe7af58b4380cbd0032dda456bf74a3ca67624c90136021bfa89", + "0xb851de5dc81e50d57a6618530d48a0410226372be383e41cd2050049fdaa6880", + "0xce297b2159fc29e7ebaa71e7912cdedd67432caa29eb3a01a2cc5140f006e657", + "0xb53835fc987b6493c562db1ac3a633333373f2824ddddd4ea041ef66535ca13f", + "0x1a88f303f50b1e888f3b66dfb265b4ff0f9d4495e6843e269eec4c1ae0794139", + "0xe1f0d26c27f627b492c7e47f5c295c6dfcd4939da2b5a8ede792c0bb96f3eb73", + "0x3dc7f49b1fd47033c37b1e63feca1c4751b0547d5277cbda1031ec988eea1e2f", + "0x4c028128207ea7060a8ae2f44fb5f72e0e151f3eb6af00102106a4059406df41", + "0x81acafe77a72fe409bf201de03494028e6c2490f420f5dfa4835bbade3b470bd", + "0x332ad5c876b110e2363663cf58c452d937665c93bd83136810f261763acee0d0", + "0x6ce747809dd710324400fbc61b182e2e0e4072ec7fc8551e49c6d4a94d8236e6", + "0xa584b040ac71881d65433dc9f495f575b27127bcbd35e67ea61d1e376cc65ef9", + "0x33979abd1d0ca1ae4a870de321a5e1768cfe2ebebe30c4ffebbe374b55264082", + "0x856023f95f821020c6b630cb9c0959df35f39cdedd974fe2e72245acd88328dc", + "0xc962c85f113d41b43e9960f6e3b23e6c45c69f7aa9d4bc347534f5046f302dfb", + "0xf03ecab61d82cfec853c8586ea6811bf52951ca5ce7ea6db4bbe47b717faf728", + "0xb3564972a1e9d1e7df0f6ea268be44341047a6e6afe529c5b3c3e33b4069fe3f", + "0x7aa4597797b2635e91de7cb6e58da60b9a246fad868e75bb3c9be2d3d430d524", + "0x8bbb07658ca8e3d3f99a0b98a1cb1f04ace001f8c3499b2fe5000d538d6d2868", + "0x1324bb753c49922288af4bfaba3659cea790eb733d3fd6e8a7295e360464b48a", + "0x875bfcde6d3fbe0d0ef8c2c405703dc1b1c80a86b3999eb20ad4a7a4a184be6a", + "0x639942960b2d3c07a6a52ff4c7e89facd58572d0d53c5d33e617fecfe5392bc5", + "0x041701f9339307863e2a23685a391eea93357a6cb71dfcf7a82d95c1a5f58845", + "0x29cc62073c285050028ad4a7dc09b95c5a414aaa3bab218a2cd1b1cbe144b1cc", + "0xb7192904bf291e9a9650768f02929d5162c143c36807ebf480b1eb757973a28c", + "0xa0a5d40d886a372cd791bb1c97832ed539ab7da1a9c016c7c0c085da9fefbf40", + "0xe110c70c537275ac43820aad3d7751cf91879c87b0b48f6188d0b9afb586ee62", + "0x295c40869014dd1d25a5d4f7338d98efa8c087c691535b99f7f608fd099690f2", + "0x533b54f3cfe01b2b2fbe84717a060f030af005d87854b2ba5f1f1bc10112e42f", + "0x729b39717a08fc95fb07a19826008d1b02927617d03bb93472ef60ad88cf0bd4", + "0x59d234efada8b46430ddd5b49f9ed6596b2091cd84d424fbdd9ad58061864232", + "0xb98a7d8801582630f03a76aa34c1b2c6cc31acff5b416a41203bf36abe74b58c", + "0x5ac6e2a67bd9a001f373f86c2b9e090223f0923b941f4eab127fb9bad2142608" + ] + }, "nodes": [ "enode://6e3d1b39cbd2a9c4f053a27e68fd90d0bac83691dfdc4a13c59f2555078a71e63c5daaee5a82aa6db500512760a5456f86076bf8bbe8011c27c82ed7d6f5fb26@45.77.140.210:30303", "enode://31dffed97f8fed1f34fe66453280a89cbeeda60cf28f6fbb212ebbefd7c7566a02c1c7d5c00bbbb49b9fa8a49f157e0f786f379ca9bcbf2fea24de70d70a22b6@206.156.242.61:30303", - "enode://6bdc7553ab2e4914cb47774c1e6d8c8f47ac7c3981891f85f65d06f208ea1bc4d3bf982b330950e0a0cd127efd7145c4df7113159a1d4a06ed722e6c16d0ac6c@45.32.215.190:30303", - "enode://872d82a24144bc007658fb6fac0dcdfb9b63aeb05ef563a06d0186f2d1e5ffbfc5c4f1244891a8a86ef70682b9d24382e654b305224883698862e2df647a4d23@45.76.236.247:30303", - "enode://b11fbc6cde81c80be69508aca8ffea8460680a25a9c151b683293f8617282062b8e8e139bf91e88cedf60068a3cf927b0d48832fda5169b58a8f7ce442de6fb4@206.189.76.132:30303", - "enode://96678da10ac83769ab3f63114a41b57b700476c5ac02719b878fa89909a936551bb7609aa09b068bf89903206fa03f23e1b5b9117ca278de304c2570b87dcb27@35.175.15.164:30303" + "enode://35e0e5c8a40e7ebcf2931c6896a40df39fb07b737f2a6169eb34427a3d788b232f7482322dd45aba38178343c1f3b634968b570cc98673ebffbffa1061592f20@104.248.36.106:30303", + "enode://e18fcde9f130f95f52f520cb0851f578790ffca53f03baae2dd2dba7ad2fb0933ed95f9549fa5b6409a37af52ac2964532b070b3487e552d27511b69b3be6843@95.179.220.216:30303", + "enode://b39c9c438c533ff7aed6aaefb1c116a350ee3acc3f37c2d6cb85d2a949296238174fe1cb9a04d6a1204b38ca12f63fe4827eaa860fd889a898735fbaba4cca58@178.128.175.244:30303", + "enode://1e2ad63bd5127ddcadb111689b364dbc973f54cfb1203901ea47eb867a849c88cf818a579e0b5971aaf705eb28bd66a1bb3ff3452c47ae8ec6176400120aed87@45.32.153.135:30303" ], "accounts": { - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } }, - "0000000000000000000000000000000000000006": { + "0x0000000000000000000000000000000000000005": { + "builtin": { + "name": "modexp", + "pricing": { + "0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "12598600": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } }, - "0000000000000000000000000000000000000007": { + "0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "12598600": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } }, - "0000000000000000000000000000000000000008": { + "0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "12598600": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "pricing": { + "12598600": { + "info": "EIP 1108 transition", + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } } } } @@ -105,9 +5487,13 @@ "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -117,9 +5503,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0": { + "price": { + "linear": { + "base": 60, + "word": 12 + } + } } } } @@ -129,9 +5519,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0": { + "price": { + "linear": { + "base": 600, + "word": 120 + } + } } } } @@ -141,9 +5535,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } diff --git a/ethcore/res/ethereum/poasokol.json b/ethcore/res/ethereum/poasokol.json index 9caef0bc94..db0e3d9468 100644 --- a/ethcore/res/ethereum/poasokol.json +++ b/ethcore/res/ethereum/poasokol.json @@ -42,7 +42,12 @@ "eip1014Transition": 6464300, "eip1052Transition": 6464300, "eip1283Transition": 6464300, - "eip1283DisableTransition": 7026400 + "eip1283DisableTransition": 7026400, + "eip1283ReenableTransition": 12095200, + "eip1344Transition": 12095200, + "eip1706Transition": 12095200, + "eip1884Transition": 12095200, + "eip2028Transition": 12095200 }, "genesis": { "seal": { @@ -54,26 +59,5288 @@ "difficulty": "0x20000", "gasLimit": "0x663BE0" }, + "hardcodedSync":{ + "header": "f90244a007e815a1a849a68b04aaa7fdac4bb5ba85d9d274ba5f80d5a1342b062e59d28ba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347947bd556b838a9e330a815a53ef39e04ff2fdf2392a0ea427a5d2cc079e4e49172310732d1b0df486ea52ffce66dafd8263e7923ffeba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090fffffffffffffffffffffffffffffffe83a3d001837a120080845d7b8c819fde830206028f5061726974792d457468657265756d86312e33362e30826c698412b24f4db841391a4dc31a16115e459ceb60c56adc84faa50c8368ab8436442892a9e30fb6b45e38e8885f98f931553e49f4b7293df5dd2ec3bb1892529f5d927d05adc8c97300", + "totalDifficulty": "3653141163116664624311283312912817150642348210", + "CHTs": [ + "0xe4d58491f073855d2af7e30a5281c1d0520171b8f89865e3c4d46a82ac411646", + "0xd6e91aa689f2dfef5d40c072a7c97a67262f9964bfbdc46ab0b3a3cd4a12dac9", + "0x4f1b8cd6877c0f2392b5f3b816b5fcb2c63ab20926c630eb1f9cdf1599ef6eba", + "0xe6c336eea0394e8acafcdae4f979ec0432a0bfc359e0c1668dbf8d9186ffd36e", + "0x9a218be2fda1387e16eafef3f0fc946c6fddb336e4c5674bf0c486ba0077ca84", + "0x65b4c98c12afc0e9e690bb5eaa448fc593c05955ab81b259567b5f5b3f46ecef", + "0x6097f8c594016f138e529a8938294bec88ae65c4b97738ab5bf7b240971e2b18", + "0x6318ce1d20f01b4f6f3744e367902ad341c3328ac340b2223c6a1c58f73746fc", + "0xc7f3abd09316d66ed9876ec573440b1b34a6d3d0800db3c43d38a7a5cd8d1e29", + "0x0aa9374d9a8d9fff6bb0ddde631bf2f2cf38d6ba9c0718ae6191a7a817140ab1", + "0x3f10442d7b1df92878f1dba8e45bc4b962341da11c1a6f77645b2264dc7c35e3", + "0x23e58abf925360db7ed05357496cbe48ef8309c33638dd09c81c1f5382fdc850", + "0x09200e0365f8b44fc38ec4ec02e4307afa8d0c588924df5a30f819db8bbd080a", + "0x708de71455bda7b8a75a1f7cbe830849557c70b6630e05a3871c081009b6db44", + "0x3f1eb0a3790c55e9ad763317f8bb8c593af0b2c626ae9a5fbe87bf418deb4c88", + "0xb986c685e038bbb45fb3d8cfd63cab280187b20f7661b0e4b54ad46a2469cf21", + "0x7e89061ed140e76d2ba378fbb7930a5ae7121622a723cbcbf2f3a6cbacf27354", + "0xbab48e622c52635ea3613a56d2e578b2dee5678323aa324de37d5e2090bc5e5d", + "0x9ffddd2913f77e34e7333830d55a55bb98bb6f5a98344b36bf3fee5c3f1ba556", + "0xb72d75c7576638be3b1daf2f94e7614e7eeb0b98eee9f0406f5444368ddc1024", + "0x7fa2f617d712e4757a90ac1d9cd1bdb9ee3b24a88e3e26462f5211592d31533a", + "0x1c823b1e86e412b20d392c75e39a85eb0bb379d885d339bda714a98ae5ab51bd", + "0x7dc9e3f0cc65745665cbf70bc2475f4ff901b43200347ac109a038e9138c8e31", + "0xf0c3390881e9c613bcc222c703b30d3747ebc8137cdd461d28f169b2a7d0b96a", + "0xc503039cee22dac519f669f05dd17ad10c199103aeeba25a1f5d4b252a216d1f", + "0xacc594ac6d193c04f13ec141a40e7691a578fc688be0b6331729257b603c148c", + "0xdce787bfd843da71ff2b42c9c5859d157193b7a297b6ea3b47ae8fe949d334be", + "0xab4dfb79d216098da16382d64c8c2d67cb370c89311d3bf5a0bc8f2860562137", + "0x4a908f0af58df38e787861b499f788c03f7e5258226d694114ff69ce2c735492", + "0xeae101ae43c3302e1905c6d0bdc57a4609a9212e0f00152d65738b5d64149e0f", + "0x00d7329f5d255b51a1ac5220c659a4f3526bf4526716cbd43c361cad86c65e05", + "0xc6b46b6a917eb3fd443a29d231e4b08825e2ba99d5b3d4cf204607ce49696787", + "0x78bc653d7b39467e8d82bffddd436d0392085aaaf42afbb418919ea9ac4c17d1", + "0xffd788eaf3d3e80aca2e72314b434b5e2bd94f78c353c0180213ee23a4557a78", + "0xb639302e2eebc2b948b745df789a16acc09f4a7faa0726333b4e3f4e43733319", + "0x811883062f01de86605dfd986bccacd3600e7a413e906d4492db8eb885e9902b", + "0x42cd52729c8c1904cdbba8ed7eedaca7a296e779c8ab18a65824f12535021dcb", + "0x426e891e497970cc0c119db69453e36db57958ac60c9f37ebadc58de8771a539", + "0x7c289a3e30a7dfeaf9e6014d17ead86d7773f453f63c676fa2deeec304e5030e", + "0xcef2c3a5971a52ab3008edb964e1bb648ebef5cbd0047052c56f7201d46b6464", + "0xcf679a76785ce4990eb8605c7b50d7a13003b392c51758288905d9e6de26dd5d", + "0xeb9126584d387019bb200d5456c26468b8960b85a95e66790b29d392bdac8e6d", + "0x0f8e93d7dbda7e62482ac1dba5748ad8aefdafe018788fe07776898cd7fe511f", + "0xcd868642b9fa05082bfb551236af04d3e297f639b08402206f33547b6abd7f38", + "0x9ffc0811fe04eb40d540b557b65093034cd1f651612adbf09e408db71895f18a", + "0xfb75a3a596300cbfea58042aa2522bf87a32dd545be340cb340609d9d2603efd", + "0x48465c6b8d2837c7b725e15a8dff9af44209408aeed99dbfd531687a01d203aa", + "0x2ef7babe6c6a53bc7a1c5a6167bd2bb1bfd77b80afa9e3c5eeb7efe54730fb33", + "0xb0b913743f5ffb5955fea5800e43b7b8d3578020aa3953cd45dc35d6d2eb9158", + "0xe68bc531547141573e4e87b5acd81b817fc8d7a79b2e2d6b73c9079151fdcff6", + "0xd0ab82832e006efef5073614f86412fba1cd70d9d90d590aeba2c23b7ad6cbe6", + "0x91930d79798f82127bee5cb72be851c0eaea69d00a1d69e51c76f1e78e6c6106", + "0x7c916074d263878a37384b8a58cf73dda15723e58eb747064b18086d0790dd95", + "0xee6f555684e662c192d78b796d6abc15b2eb46433c6dcd1ae868e5abe97bb83c", + "0xdbc976efbf3eaf7453ca24ade29cc03a9b3b99c0c72e9e6a43a5f6a1e630022a", + "0x223a3a4a30341c8ed1e429b8ee1f1f7201fc92a287adf297ab0e7e0f3ca25849", + "0xb0917b1ea519dc2a7dbe5ff62130456dd03fce14eafaf3d60311ba895a6a0810", + "0x2328f6b2d05d7367e5ddcf63cd72d9c22d5f3f26c2f29139ecf3e8ded509e751", + "0x4dc93b1377053e38e97ae7826261ba344fb524f2d4ce4535def8ab8ed98d4cf2", + "0x790accfb98ad71524cc2122871d6f0f2fae7867f2c7d4dd73f456fc278938a1e", + "0xb6b3d284cad897cf2bfd2149d62c167a6312fa98484b15f8e4a809ba34f1e565", + "0x240cdc34143842974dd79b85c0fbca4726e59867a182e18fd39384e881bd28d9", + "0x70853f829acef1a0b35a7a2df9f4230a6279532690d60011d905932cdcfac8d5", + "0x8fdb7d0ec82ba983a15b1b368d4e4f329150b68fca4b4d7667120f4a238bc85f", + "0x712c4a8d309abb8c23d9ad2fd26ace5981a540694995e9fba5adeaf6b0c22c6d", + "0x8343fc8d01b172f6046451e8242b8031cd1a7b326c74b7d375af8f749fb8770b", + "0xcd9865bf5e7c97838a2390ccf3a28ed4f333b3c4690990422decc82a45fa77b2", + "0xa8d1df12dea2c90d0987b6a8f81899fdc1eadefd7fce2de4a133976d5ec84ffb", + "0x9bccb687c728af6ef5239803b73b6dfbac0d0ce7e38636e636e9d233af3ad7fb", + "0x6004ccb758fdd6431a6f4ca2cc4b92aaf2d5f0df94f64953bf84d3da055c90a5", + "0xc325ee9db54c359ab7c3f66ffc34bfd2110bf887cb45512f1542ffcafd36e73a", + "0xaa5a7dc7adb1db811ef798968da05a265ef6437885e20bb48af1538f6fe35c17", + "0xcf09a84c8f96ab5a84362a75136a570738427653f44cf7f9c8cf5c021cb5deb7", + "0x2f21951811d2b6b6373b02ffec0d45ba6803c9cf29cea9765ccb376fad1a8c29", + "0x78c9177bfadb53dc06d1a8ffb4721d2b84aaaf193766df81cdea1b6343ecd95c", + "0x6627a97c799d7ba00f88ffa7f89fd99568f01107bc65c4d7f937ff90b6c846a4", + "0x47c2342451d31eca3a4ba090949715aa375c6300ad136cbbc29ed5eabb5666d0", + "0xace2ae33a4c60c74bd73b464c289888842f6d4230762968a5841368e097f8a9d", + "0xb49902580e550ef5988ba8145aa9eb1349d23a656c1ceeb0002944d43f93c217", + "0x9fbcb633fa06a6e5123e8da24869d927d2b17c4102f0f16864d0c2ee63125976", + "0x2cd3c6b1919061ec1c37eb5ccf1857ce289984ea57f19b1dd84712d090b9fac8", + "0xda2a3f6ee3f16ff8e0cb8c01891118438782ed4c751180db1eaaa2097634aa2f", + "0x48fe7937f7ae622516e79976a4b0b5e8d77f919994c63ac1b6a99bdfe8ee52d4", + "0x5c2209d99c75ad57f922603813e91f2ad72f51c198b48c2e3372176b03df558a", + "0x99b732e7b66c49e908956f5266b18d0af543ec147a6f4a959a589b79482cf456", + "0x40318536c0d00c78216d986d2d8924f0ee642253b115a3cb3230fada0db1a80e", + "0x2becf0d96a85eeeb8c2f92869ec00ab6b6d6f29391245f4979e1fbc69c5e4176", + "0xae233d47bf46a15c7adf411937e3ef15b360df8476f8908b95968d3a9fe3ad2c", + "0x126252723235ac4ebde13ab59587be40f40428b70ab6722d99d113753f783aba", + "0x1240ea41ef0c5dcc9401726d8706aa136b5b39f9a6a5fdc3743dc66c95a6acf7", + "0xfed54503cc0759a61c37789a5feaf59c22db9888549dc60949fc46d1f04a13cb", + "0x5c10f217bdaaae3e71451c8d294f99efa81c7ac29ca26b752d399643c2562e1f", + "0x76b5b667b1dd603e580a32e273aeee7b7d369abcfe2cdf1ce17d03d60047c701", + "0x908d59f6f5bdafe3a87ccafda4fa607c4cd818c3bdb1745cd201e92afb46662f", + "0x0ec1b38153f165b1e2a9bf6e2d7ea2da4f55868734e83689f90cffb908d5732f", + "0x1ed240e1a664f73d6b6ef4726cdf8f6b79afd86fbe7f31e35e98a565165d82fe", + "0x864f0d21ae73432b7de33a3c039aa87fef9c7e8283a53313309d403a633e9775", + "0x8785efaa7f23021e6118d8703e495c08671fdcd6b9119033ca87da8ee2a714f2", + "0x4368419766465ed4e94e61fe6533c11f3fb365b6632818cc7be4543bbc30a6a1", + "0xa7329927ce5cd65ef14401b8a3f5e5d72081da98bcb1961111eb8c0db01e5978", + "0x5cb311db5addbe28df619a995938565f002240e17a1547c1aeaa45fdab510da0", + "0xf72a0111c23074c3fd88cf950615c293934b0e556c654f61b67d72f6912e5da0", + "0x4e20de7510de36481f9613176297d05afe61cf19d6091d2d91c2b4b304e8d517", + "0x2dd1e4528066f3dcdb8af7b349a1fd1d91af7dab55f0259b9dab50818887d106", + "0xcd6d48100033a814448d9dda16d386b92444172531c6d4685081834e5901d3a9", + "0x4976f64422c56dd5eab300e22b9bc0ae89240539f3a1b7aff52a06a89427f55b", + "0x2f4e97e2043c49ba2b03d578e68c5d0e5206a14db519c7c33d1001508275edc8", + "0x2d39332014e0b1726d8639d962f6d0ff3e9ebf0f36d49f86a57ba74c164a3b2e", + "0x516252d86f15a686038fd33aa2df19d9edca7d5c8b94ab5260ac32411d76cb44", + "0xd7446363450cf1dd0c64474af1d0bed8d84084279001565122a7818f2c490c38", + "0xc3034fa62d4cde0f6cd5fd234fc5605c3156193890aabccf5eea5ca61e1cff6c", + "0xe49ac8d5db08a90f793d5fb2de20e95b5798a3792d4c072a7d7d031ef784203f", + "0x8b67ae0ff780c15843d51622719843ff5fccdb67e4c819a22ab50cea5df7eded", + "0xd2f80b3191abea540f5bb44bcfeb0a7e0c07b6e128a2dff87d4ffa3616ed511b", + "0x230f0eb84418d1b934c772b29e1a89d92a6e90acfbc49866cc0ab5845883c43d", + "0x6857a654f5c4f50c557a6be7fdf009d0fe02d8fa40c25ec805a573546da79121", + "0x1c0a2066248fbf5d4200bd72fadb0ee890c78518954ab87bdcd6eeb71c53906d", + "0xad72b196c2a20882ea2885e9f0fa9d0a96768c0912d0d680596366eed972f94b", + "0x04832321de0d1cbfc403326ae4854049dcf616110270a01fde2a74a7c682e0d8", + "0x0d32b18307f94724d2c4ed982cfb86b4d96115419941ec68979bc79cba0e2162", + "0x8fc7077cc1443be014bbd71c2d8332da0163c67c937b4886d017c0c2efd8e0e0", + "0xb50f12c267fdf6477853b3f44f642ee6c1e5951dbd9c8af4554e355fdf5f76b4", + "0x3f86425027d610a5cc801aa451a5e729b5feea08ad3727d8f64b883af0d6fb88", + "0x12a9c32a7369a341a7a00b6b579d4752db06e43f1cb70fef5d646acd7b9e8b49", + "0x04fc75ac0a83cbc523ad7f82cf7565a28b46b719a50558e0a60167a3c9afb7d5", + "0xbfc9c17c84c9c455a79626a61ca25769ad9f5b19dc5b741e98b0cb12a5c022c5", + "0x5db7e099fc1b65f5f9cf427d559af6ca0f91c3bb72f56b4a346291038731946f", + "0x87f4f93e0660743705a5616fb21e31b814eb442767ea84248d307b0da73b70d9", + "0x8e88437d7b9ac6a00d54c0b0fbb8be5127c89ca7e5f68dcc68b296bb64bb9760", + "0x55063cffd488401844cffc22519a5cff81a073351dffb192e8d9a1717a5016a5", + "0x478559c1c28a30e595e0d5cd5113033950c813dea81afe38aac7d97fdce45878", + "0x3856bf7c4077ab3f1b3586cbe367fff1f36a5dc820e2e285feb7b40428a2e447", + "0x8992444c0c48d241bf9dc6fe279548cc4afb9307a3a3bc85f63e1e40f22fb734", + "0x49e2eddaff7a36d91c4bf0bd1110c82210ca984d06ed813ae5f3417d84425f64", + "0xd9482ae177237665b00773c4d8aed5c5741c6f3870016efcc7117115a57f2339", + "0x3a8d7c3217745b5afc4b78e85b17b3321ac3fb13acd0609f6446a02b034be6f0", + "0x474a43267bfa25a435346bd7ec0f0e300064c365517025cf8b91dc86765f3dae", + "0x8f12f8a33bca84771f5987a83361b9e4d3ceb15ea51fab1500d928520eacd4e6", + "0xe4474dddbe9fb185cc4209d223a726010972f83cb7391d2824ae229fdad1f6ee", + "0x586ae56df016450ff588fc030d62036402db90b03a852d599404cfe2bec4869f", + "0x7d5fba59f6b02c6ab6b384b605db955be5a150a734c90b108e5a93af8d25adc9", + "0x3f48f5f7e31acd0cb620c9ea3775a305262c6b81b4891314014cf2836aaee4c0", + "0x7327cce6a3997e791e6a38b9d26ebbf86615f06dec1a6d64d5f2b44334a86a3a", + "0x82635d6a26974709b23ea8d9fac675b0c920133534c1958aac26ec52b4c63cbb", + "0x34e511375cda790b8a87cabb98bc25b57eb2f31053e1e56e8cb578830ddebc47", + "0x3ca25a940ba858c92fd08f29f4acbcfde803860608db150fc7ffa1b69a6d73aa", + "0x0aa24ef1d1b01b351652d62ceaca78457af811021ccfcdcd12b1e885c92b35de", + "0x4198e80ca378c02986fdc895a9c6cb30e938df2702d2095d6b3f97973ca04ba3", + "0x8a465842138798c3dd1d1cd2ddd75b6f4f2bc1c371f211460679c260df6d95bd", + "0x6ad1bc3b21bd682d1739c1f0ed060cd8340d044cf53cff7bf79dc15ecd8130e9", + "0xf47920dacfc9b15b15a8ff2acbdf9fa27c4b2e90d6812abbf837f476191512b5", + "0x4b7491cc9534d1aa379ae1e9fbb1372cf475aad270086b492137f0e8761f3b18", + "0x30d4950071e45feece77d2442043e000f30258e413caf9027c169b132b767b24", + "0x815e16c3aa8bdc81140311b883eab9414a8c3014fafc29acd9d3d7296cddec48", + "0xd1632b1c80a22d859838b8efe821484fb3708758d7c6cbe65d63d18012bbabc5", + "0x6ffdb3308599f88bd00bdbe3e9cd31bb290dab8002bf6564d9b7ce69d4bf4ff5", + "0xae14b5d1a134e92d87888c5d08188a4b5e294aa554c752b5fe6ea6b4c36781ed", + "0xb550ac68159f76efd69d6a37f211bced6dd22d895f86a72b44a992e9433066a9", + "0x2dba0221d4ff22939d44984251cf25ccb0e6f38276de2af79fcfdd35cb2fb14e", + "0xed6d312c1ca2116fd0b17271cbca59af40138731b03b749a1ed8fe49925d2208", + "0x4e7a1325e4b24a64f502bcbe786803a025b90c7a9b37dce8023e30fbbc3f6e49", + "0xb197158712ccc89bd223c0f416e7204a76b4d2cf04513aa3c849eb315ee336ca", + "0xd8ddb4e3d2ac9bf2487df7e340ece14668b4779d513bae25b841b50f43970101", + "0x05bb2f744ef62b06e43f1ddc75c54d58ed9b631e4646ffdf0d0f2cd2518c2ee3", + "0x5e35d3597c4a7af52deee062a57999d3bb2397d9872dd6e397cd0ab6c6f43dfe", + "0x4a8144b5a362f95fade0ecbd11171fd8bb4021aa674e368e71efcebf173d33ca", + "0xaeb8a990d8f46a0606f1070da7f4591277a1b6428937c70efe4d5a1d7203eeb7", + "0x341f30fe9b9924d965765ea2efad80df094265e4fa701e8b3012803bda108500", + "0x8af60f82260b5541ab8fe4983c338bc3e709c674e17099decf2fadd72345250e", + "0xa211fa437beaafe423e891b494232dcb0050cabefb81099f9125a1311babac74", + "0x41dee213bcdd56c08d2d0accbab67eba0abf3e96aaf0497f501f582d651ef341", + "0x0dd1b0b1450b8453ba104eafd97bf98bfcacac9afb8c0c925c463e1080d40978", + "0x406ff07fd75aec960e52d9770dfb3c2edc661e68554e8ab40e267344a92ab246", + "0xc16ca56a026b8b8a316efcfddc1b2bf4297f2108db99ca1e9274481a3f6ab5ec", + "0x902ccb64299d82648cf565728e51c0c3bf2bbdda5323db91ec385b9de995e690", + "0xb7eee36018cb0047abf24f8a2169cb24ef9aba1b9b6ff0aa3d4fc73edeb189b1", + "0xf4b0d0e83385fb608d69b07a491077065fed060cfb53ab1bb53310e843bb57d7", + "0x366faff87239812cfca5828288e8d26475a9b4aebc6d9a05c16539fa9fda11c1", + "0x81c686ad782f981f81b0a38e1818778737c6d9c0340c92ad0fc8a277db823c9c", + "0x78328d995ab11e8d99aa87b02ef77db27cff2d211dcf68be342ba3a8bd47bca3", + "0xdf77ae4eb450724643eb7493724258bdbd8ef0bc0994d68b8d90776c5165b322", + "0xa09b3398236b6195b733119436d5939a4aea1f8ca410c68a188fa148646e3d0e", + "0xc9e3ff97dfbd5674488f41c8fa45d10e7e1500819cc9b814035fd23d7e428ff0", + "0x0ee8f68e5333a76419e426b1cb0aa4d8156e4feeb38452a3380858acbe335b30", + "0x5c4908dd5c09ee0a744d9871193142b07435ba286b1c44a89486a83911d94fa1", + "0x5c37cf2e1a71203af7424c7b9b8b212612ef1110168dbeae34fc0b57577af907", + "0xa9df805ba163337c052be4cf080eb3458d53363be6757341b16e1b307bf5ebfa", + "0xd9efc033e2fd67d385633ea7f142537dee929d2b0c2ec620e2aa80f1736eb255", + "0x028ded5ad630205965c91568af9aa4847307036355aa517daa3f287670e77af7", + "0x95eaea74afc7ef2cf8974280529346d4f5daa5e1a478e4edbee01a9db3c6a01a", + "0x7a7602c0dfa88c0db73b3f0ce4e763d29285ba97b29c51c6248ef65bf94fd511", + "0x84ae23d27ee9745f987aef427053beeefa06115eb8b0cfc7b94f6a784bbfc891", + "0x57c959da3e67e079e016e64d6a877a9646c22f84f7b71b4d0b4c8cfe14598fb1", + "0x49dc68e3597f5b4386f426c424c286a6d1e359e01c7e7c3360b581192f8d86df", + "0x290511b85276db2ce08d75e5ff21c06920de74927e380728b390731461ed45e1", + "0xf3aa7571770b9bcafb026025ecdbbb5735b127a36e7a241820a814616babb7ad", + "0x1a6908168f492b5620c583824d8097f8d59fd512ffae5cf3535069713037c0d0", + "0x1ea5b88d83d89e41a0119a088422da524cdeaa6a8f3470ab4a7ce585e34d566c", + "0x633e140218709064f5613dc53dbf35294566ec9b9cc30960bdbc5e55611a2681", + "0x31fc6806f2943d91aab07c6e6e3cd29f40ee9bb8ba1674731710e2ad7dac5394", + "0x65bb9a15b6aa8873be245123a6d7164b4ee6ffc0fa82fc862d08a4e322a4581c", + "0xe094f190d793c117892f239a80b1ba3b7c5d39f9f14ae6f26a908a13b44eb264", + "0x0098984715ad7eb8d337e9559c403d81249702fd7cb60d64168f5c6b94c02acc", + "0x38532c4552f58fcb939459db33934fa55c3ab2058b6d01d9f6211c510ad88c1c", + "0x746f4dea70f5f793ec1926f7b8b991c9f5bdd9a8af23d6fbbe08e1854df5e0d3", + "0x55f80c0c589f30a4988f1243428fb66ceb2d4a7395ac324c31086e8e178d0b84", + "0x02723ad10bccd6d12b1e880595843546daa94de5fb6ca5848e63a789e9c8069f", + "0x501e18fa7e69f6cb5cc72a6c0b2b5fd74e1195f1612581c8240f42b62d53d0b4", + "0x99940cf705165bc5ebaa122597f967f18fc50c388db8bdc815d8f8f9e9d6423e", + "0xe945144a2eaaf5ccb8b2f697fce5a565f93c8e9f593fed682814979167742121", + "0x9e63d6b949d005e0b6d1c55656af4161d1ddd073213705fd721289215ac21500", + "0xfeba3cb6bd7576369bfa6602690f637394b52c3ff56bcc13668af5e2ebb086e0", + "0x74c327070bd83d14829a804e0001ee578ef1753a81c82f6d41d00c56a63ee5fd", + "0x578a60de655f60128e71dd7beabb42a8da38ff97aaed1411cc1f8fbd1247dec5", + "0xf6744ef1e83ceefa44ffe472cc1cf1baf927f5370107700fd0cd77775376020b", + "0xa9f04ba6178133eb16f949f4bf6d739ef2ae49606f3dc28d0a40642af86e7a8b", + "0x2a7c784292299caf04db9bcac686a41e50ef473548d3af594b3ad6b205da7109", + "0x9c5a2a7e8df300fd493768f460183196839d69c0d940b411703e6b87fefa668b", + "0xe6f1468f438a32a893f73967ff86f55b7fdb058102bf0b98e27e3d6e1bcee708", + "0x3e67ad9e2cac2bc19901ca1b7c769f637e796ff2b4093b1769fdb5bd68c31d01", + "0xd535c0dd68e7dd44a30c64ec1880ed8b96bde788b7e91cec2ce78584cbf4c7fb", + "0x14e732f5d1545233007eab61796d43c1b0ee297b7cb609f85c2f1d0ce3ee5cf9", + "0xc2264c5fa18944faa6383fe163ddc37fb40da1278de1b41ad82108380f2cedb3", + "0x8cd67b242d9a361f1344046ab6eedd59d5dc84573d4f68d3bee5083f1bf5e545", + "0x9efc79129240b3f851470a68005459f42f68fea491ddd1a0ec52bfd0cc79e254", + "0x7deaa945e27f4563cb101f57b90850517fb5b80c7052f5e9a88eeab35f2c1d38", + "0x28379066b73178eefb9b3eef228ecdb11f373c88d694e12fc5f3fd5f75696d34", + "0xca1bb898e60cdae8854bfbf4478c67d9a736188b90eccf10ad718be56f94ebd6", + "0x126b9519258f37dd5ff7dbfeb67443be32724b3d69cbd5ef48eede257a2b2e36", + "0xa36f4ceecf8df307447c4ca6817b77aaff37b6585b59b230ba0877c0ef85f38b", + "0x0219c1fb5165c5f51cd41a26bbacb1754b3b3a08dd662dcd0ce59c98a591612d", + "0x327973e35325e8ae0b9a383fb3649d7171a6754c5432a89da2c06083bb2d785e", + "0xa20f092921b92e04d7ee23b526d86acd4631135891bc61003bebfc3146e8ff53", + "0xc0867ef3111ceee6aa96261a02c820c43ebf5b2fa49c853794674eb48cca517b", + "0x24ed06ebcdb98c1d87562c685a85bc89b473113a814f721fadd87952b627f8fa", + "0xa9ce012ea11c25352dff7966013395b127e70a9b5ab6844a6983620173f13376", + "0xf0ee9e5fd27a6b432960b26172c6be0e3df8fe70a748124ea1e8bd47a30568e4", + "0x52111519b8bd9a41fee1b05cebc99684b0054fd7c780edbd7dd66a52d4efe4f4", + "0xb9f4003c97be7d16a2f61206ddedcfb0cb79cf0b986bc3655e3ea29418fb8931", + "0x9d56bb10be4e4a55064c256429ae876104ca5b6f126b9a45a7cc936fc2cc8ce6", + "0x0afe9046842fa53b9f4e6392ac533ff4552ae90d7840e3a195e8e443dd969586", + "0xfbf06e08dfb191c9e89f18ee34ad055bb50d8be9f792dd0c97ff5628ced5e12d", + "0x2a6c3a24dfc5dad197f0fe0d2cba20949205292c96f7f2b2c54c358d5efac8bb", + "0xfbba9f993607403f5c4e66fd83c1123d8c71e431e4d4ac497172d3b04cb94d26", + "0x5d180984d6148973f30586ec33ce539dd068a072787824a54ccbdd5feb41c071", + "0x7c4439fec2a142dae03bbeea5dad07c51d1822497ae75db72421763461dca0f0", + "0x4db61c976ff0f68232739f3fef60234fb52997b8141cfe94730a9e3f1c00656f", + "0xc91e1eb7d44b3dcb8c00eb38c85b6df36689f0c2f119b5c70aeac912c8ca6c3d", + "0x2a120e254ec840dc3862aff20d8bd488a06a399f926ad214bd85705db0a96284", + "0x40ef22c2cf6e8a970b2b347095d2a6456325162473798767861cfad600b3baa6", + "0x00de522f3cc4a37cec73843bc4aa0d145f41aabd1961a47b0034f81bca781abf", + "0x39e87fd217831aaac98160017155d307001b6ec7502bfc883f366447131d9e65", + "0x1166a651cd9d48dac9523aa2bf7bb1080cb887f2a9b2fdec5946c3878809595d", + "0x1e8d0bb5f1a126510599e2a66fb9641bdbccaf95c352a128631caed3bfb437ac", + "0xb94859f5460e017a521dcff196d3ef9854042dd2ea389152780f8a35ac09af38", + "0x3a2766c7f605a5f475965403903a75abdedd23987e594c7f830ee65761fec519", + "0xb996c57672c9e445583e3380d3b9f57836598acd915e94240b18fb48b7831939", + "0xcf220ea223b8ea436bbe597dc2879b48ade27e20f0a5e7ed67d02316297b88eb", + "0x73c6c8231d7e518ea6631b0d9300bbe48b82a07b3b3c12c49130d5a7383d8043", + "0x0899f8f75c249d374b8e1f0d7a9429237173728617d53d0855184d970125e858", + "0xa93fda89d3027832f72aef2bd0a8684905ce9d688bbeb69a13b0b8d590fdc1bd", + "0x6ede80867e11a73557d63e414cc81281faac70e8bc05c8eb85ceae1387c885fc", + "0xd3b5ef8a10b04435857d2361e5f4985bd22232a9fc7a8d64b563725b3bbaefce", + "0x287bbd18df5e3888a0537e95a1dc031c90ecc4a848686626f420cf9000117b8e", + "0x6a54940baa4233d18ffd8de942909fd71c7d77d261670c9329aca00b45160079", + "0x0d8cd57a4c84485449727259f3fef62c25927bfb907fedcb8fede8788aed5e70", + "0x5736a9d8a2a013ce0ef27c8336d80e910396a2eca76354c092501346b700cf06", + "0xc2fa19630cc1c84f76720c20396198bb05bd7daef24a55c5a15953d35f7f4a4e", + "0xab86b92f28e4f01c9789b4b526e09e654f84a9bc8a0a0395b7b57b361bf51d8a", + "0x607cad7d78c65ce30ebea0e5a529adf7e9334795c82d5ca0745007a8e46bdb0b", + "0xc1108e8eb3707a510356e0c46c31ebeedcc774fd6370fb31f98055d51b0921e6", + "0xc12e33987651af239a545c3d1296441c6bb304f4bc56f47ad3ce10b47c9f08de", + "0x93d4aef4692ae1cbf27d959b42c8b66f565f18c964f7da6bd9b45707f0970b4a", + "0x727754c01ee5b8846484df810d0c6ae0e065d8feacd4dcdebbb9dbcd284dfc17", + "0x93511fb934c1e23f0a9018a77028834b52871c8b1afdc837c0be86d97ca0cf53", + "0x495e6e52b73d9f43b67b908c1410eb0126312b87278fed9fedf7d2b1cbd4a3c1", + "0x4d66fda9f19537742754550aff72ae91d0bcef465225d3dad3b7abb4f3fe1c35", + "0xd7effe6d7ccd8c935d4b5d4198bfbdd7d38979c6231e1e51b8123c881c90c110", + "0x7017a0129173ba3bc5a5b060fbb1da96ddd3d1b1c82a28602b1f79ebff2c6020", + "0x3ff24f8c42112cad12ead94367e2321d3e5a346c92a4c21bf9dcfcc19b1ea1cc", + "0xf661f0b0668229cba48e114bdd5fc39ace72a847a446103c95a68dcb049bbd2c", + "0xb93be307e430ac1f00b3cf731be087e5766b384940be5db8ef0938309e8fd8aa", + "0x3caf894a05bbdf2af9a11de29f905d19f5b0d5c7c24bebaadc413cd2ea783109", + "0x2ab7cd0b2428492648d9edc4bac4cf885e23cf15ac7ba13c2a26f100c2980b24", + "0x190a0a5756084e500d78b84e69b7bdb922f86e260024828ab224fce42e7897cc", + "0xdf024abab264034b85622282fe72a5382cea538a68b2a8c6cf05277961ce8664", + "0x839fba37df7196ebaae8aa35dc2ebd1c8c0837b9ffd455dbdff8be4b186f01d1", + "0x249a9d815aa099964c0c7c9086508b6a7c2531b1d7f62d20dab2479ec7a36949", + "0x34d9e8f206c61f1e4f7940969cce2bd12ba2c8659d09e3c0da3f148768675471", + "0xbe49855e301e00dfc30893dd90773baa276c371ea4383e5b583f82f7a52d2582", + "0xc589a18bbe230bb078943400d200eb999cf39e37be693a565f42fd5e4b127e7e", + "0xac523d256ba792a6bfa3f0139c279c0c95eca2dd4a562b567d27986064cbe7dd", + "0x0dda51b177a089103277377f00473aa86dd4df0681b89d91c6b521e5f6045d84", + "0x1133fb1d177f9c372f9216238e201ddd0feec288bf9ed2e05b7379a4e18810b4", + "0x9f59391861b26f14b4aec1526c77c68b3b7b4d146edf86ab30d0bb633fb591ea", + "0x9cffc0f4d149593d5b12c42b5f25479b428ed4ad1a9e90062d869b2430783f09", + "0x8faf55f86fd129cd8e969d6b388be862dade273dce1df1f2403d0e7b6428dfe1", + "0x56aa7fcc8c6f7f84a4b3c51554ab6d0f8e360036db26aa9035698c7e16c3141d", + "0x1580c9aa2af54dec437e9928cb7d1de110fc23c2a8444f7ac406e17cf1fed36c", + "0x74bd2aa3fae421ac653a1d320392d3da218d428d2ead3714a13f43e505ef9362", + "0x80647356cfa589f23f17913ff38252fd3ef0fbde8648e73071883df97c901fb2", + "0x1daa1d3b4d835fa103e9dd93ee0baa477d74cf003bc58fe55d535b1c8e9eb729", + "0x8a5b0c3d7c5f599812f06aeb090d219efede03bb15ef194c1bdf7344e33808db", + "0x2096491c1e9c4c00edf923f2f4fa42a594ae9c14f9d44eb3d2595b56e5cbbec9", + "0xebff9bbf9bd1a8479a9e54c6926db339286f971a1d3194d3abdf8f6b6054a236", + "0x011552bc653340bf80a72e977802788baf1d3d4ba8109dd4c3ed1b979e20b2cc", + "0xe83333db1754d8a1699ceb719cbb41e666cafbea3b9d644a9308eeaacf9b8060", + "0xfef3849b9b616e60fc66983d68891bd6df50d2a39e24cdf9ed2ef3cf5e0f9a82", + "0x3071f9a963f10c274445d69956cac5e1ca7c3f00ba67b5771535674b45c00e6d", + "0xc0678c40fc0f71738d6afeb85420c596d44fc1811840d3dfad2b6f23bf1de077", + "0x2d141f60f5023a35f2df6976d79e45935037baffcc58bd2685b3b56381e20294", + "0x81e34d0fffc019e8f6cc4eccd5621886a509b3d396e4db1b950085c7a050531f", + "0x6fc5b47f17e188c59cc56590f3802406c97a1b948149f8a093b665bf69e3aa69", + "0x6bd3212239486dbe001f8aac28feade38e29a97995170ba468f4355b95782eef", + "0x9a05ecd2fdab4382cc087c591b0939c9cb2bdf2acbcdb5e600b0fc6aa602b18a", + "0xbd4963e3fecd3a695d98304a32e530fb62309eb3ec5228de76e35ff71377d3c9", + "0x938903571a2a70ecae99553fd57dc823bb94e9bc1b300b1537a90dfcf5950340", + "0x4322d491547ad767885d6e91e95704af3386fdd1b2c5321c75621d80468a92f2", + "0xf213b33ae4cab1ab2e1fd2fd5fa7e541b79d627b3dea2abfbb62fd955cd41d44", + "0x2c4bf0e8c8f436d40a1d51609a9af6f7a901f4cedd1674a91441def0300125a2", + "0x93b9f82dc602c819423b651a761f209aa54d5e17ea4f8c38f672a2430328981c", + "0x0c7d533871520f0cc05f4a589a57e13f6df93e9a496764243c7089408d0d6b66", + "0x903553dfa524f36283e4d38927a37f14be4d03e6024e33ec02a13faf0f516c34", + "0x06e600df83caa2f08338bb260627588fb5dc2c1259ed0fae8b90e2a78cc5b8c1", + "0xc1e10c8e8cffacef717a171f333dd2bd658aa86a73dbf1994208c6ebe9aafcf8", + "0x0a6186f70c24c470e476a802404305e512385295c512ae1aa2896abffe27278e", + "0x7e823e36c2ff2c1337bdef8f451c4db67bdd78b813e1797650bffe5412f83851", + "0xab4a163211254adbefc47b5b99e40be39ff7fc5835ea9f9b61471d4b168ce19d", + "0xa1be37832e378a1e02a0c1ac214ccb1567205615b45ad74defb87f3192592793", + "0x0be317e3cc5284ac88a6ba2d61b9221fd6d070b4fcb3e60c047f82f7c2c3abd8", + "0xe5a39ce3c95b24c9bf38b1be313d123842bd95430b93063421cab2798aaaac41", + "0xbef8c7e07475295b596212ca93c24eabcc075bc654d354852ef9e2a2f742c5c6", + "0x22528bd08b551151e37e4561cc3bac705398ffd2e14e3435d405fb43eee3d9b2", + "0x31957e460dbf1c80c5cb392736b6c77a43fc68852c878b55ee18eb6876eb0861", + "0x66920036fb8dfb0c4ed1c2c69b69a4be059dc2e9cc149b2dc68d29df55d20ba2", + "0x46d5402d28377a95ecfb650c6c24227dc44ece826f5130e9c2bf0673834593ac", + "0x9f67639152650d9c384fb25d0cac5d581882e8266efb681e566c0257eafabe12", + "0x01498df5ebfc8ee30e527d675881f58fd82c8b008710556059c3f487fdef5103", + "0x5b35678eec6d1eebb1f9761ad959d3b5502ea4e62a71d0f664cf8ff2fd487515", + "0x96c21ff7fa865aced35fe65aac92f9200b0359642a49e73dacfb46f99b0c3b66", + "0x02489dbe227257093024b57d8391f22cfe3d299e77bbb085c568ab8fb109039f", + "0x9e1fc1b0864454c5e30e70662a013c3ad4d04e8499ff2d4c5521bec951c28ae8", + "0x7ee9a57144606ef98a22ee0ca478d586ff94d06c896b6617a62bcb6a8d815a1a", + "0x0ce9c66cddf5c8a904fba5b6c803589eb39788620aecc31746580bc32edb39f0", + "0x086d22a7fbd74c51024e770ce75d51aab7fe08db6c69eac4b5e458be2524552b", + "0x61e0d1b63ac241464361267258c1abc76576b2355bfa9c39099ccb985e617ce7", + "0x451da7b88c81dbbf45cbd81110fbd9329932621d24483e0d7bd351aa5dd87ee7", + "0xf2c65fe7d92b8f387ec834d97b197085e54b5665f8be0fb6d0fe1a01754e4b63", + "0x08e7ae17595a016ef8f01ebeee821b56f4952f59f29108b9f54a77442e4ae18a", + "0x8dd9eb6c74bb8ff4562b58aa0a9c58eb6c0693d1f417f90c6c9933441cf163c8", + "0x26772dc099e7ad32284ec1ee10add1a0f11d709d8761ee740281a47ec4c282d5", + "0x85bfeb06df6180a8efe0fc90a995dc3b1f22ee2db1ccdd37bd5f9e1e7758f44b", + "0x556c05cb7367c866cc842e9dcc58d882f5d7b98e394d9b978f0816e52ddc21f0", + "0x9409d0e27678af35c905ebe3a71dec1f9f2b36e96a756da29da5f5b0b70aae0d", + "0x38fcc147719204622c9eb9cfb3a0578434b4f32e330ba0385bf94748931fa57d", + "0xa022301382e0214f43ff18357f6b6ae2a2405feb901b5651305148f37568a66b", + "0x895e100f31fd79d265269100464167fb25d1aec45af1ba9e754f25863c50312d", + "0x75763f341dc03c010e5a8607d68a90cab3234bec22163ccbf2a30e680a644ae1", + "0x0780e5373c8bed754dbdf5f243f2130818e641973579818803aa4df92537ceb4", + "0xf4475a83da0124fbf5d8169ad847abc6dcfbbe8f1c1f5cfcef13aff781c8f143", + "0xe0776928f523dc292ee07275dd7d6d948811d2049e90e2367b55c8a6a914ac6f", + "0x81ef7cf02b3ea376b2a8450d2b5c86916151c97b14e164474252c81540f2ce60", + "0x704bd271b1f8b3848b51c2c1780d23b426f09d30b5e01521463ca795612f321e", + "0xd6b2989e74e3119b9321f032ed61cfbc69f35b1edd39d899467b7bf1f64d4472", + "0xb15048b06ee3eaebf60a65811e3cbf871f807c4d77eac4e439004678a3a37a2b", + "0xbe9010daaee43550ca45f002aaef992012c960d9e54fe36be27f50dcabb68213", + "0x48dd50d87e0d6cc5081a99f2e303b24dd2de3edf76232d3520703920713847c7", + "0x5e5b1e017e65e1b71290bac437f0db595178c8a4d60a8c859171dc0d26e53f63", + "0x6c49a45f9dced4f442f67f8d1abf1164008c5495aba26a9917553831ce003426", + "0xd330334cc4a4b0cac7c17462ecd72064e39863a083a6dd45237e8a4a781ef3cb", + "0x1a834e7694e08b64651322b208e3dea0f9391772225107679c31532a74c68442", + "0xaee7269d62bcaa120cd8e74c0484b277669db30fca39787d92973d761e2029b5", + "0xf1a0baed25bee68ec8f0750a8648f4941f0c69f1d4742077be7abbaf3f42da2a", + "0xf6a0f5e2e4c1acdf592fcb90f3ee7e0fe912331a7320365c9f1b7c36d3936d1b", + "0x011213506261ea7f91a2a8f6beefcd750104e0956705759cc06ef535435d23b6", + "0x559419ca04113bcb518a0284978dcc8b62d2a956e30c6efcf0b444d685fd607e", + "0xea88ed968f0a9586d699c213fe9812f2ec32bc99ba5025ad0c2b0cb704f10fd6", + "0x05b41cfc5d29dffa1c1cbcef9c3c4644f6e7c9ae8eda494d20f87ba48d253ed7", + "0xb4145d9dcc93fe5271098665b3374abe149b555a30d6f9964c697c724c5de662", + "0xb21c8d1858d4af16e679403867cd412e73fc7d8c75247098ed8b2bed36690ca1", + "0x5050a254f642fcba0274cf1ad3753cbe8edf61fd776be6b2bf9d696f0ed7d394", + "0xfb5bfc6608d9a6f131edb0e2020f6465f20496e2a1abe86388e27ca2d9758888", + "0x49e3ebb231b80a7ee039937bf91632a9fea789f8fa2ad37e680e3bb802116c7c", + "0x35e46a14c0c9d926c0b91669f0ea3a3e14d9044e065db5c96eaee47d9c8d2a1c", + "0x0a2eea221affa0baefa8ba8c0cf7436377f26787de30c4d55804a29a15e2c4a2", + "0xe964fd50a8651ffa5a69fd8fab80d2e16593e919eba60d09fdcbdafc45687baf", + "0xed2b120d56c02c50b202d5b127e972193905427b311bee90e8a6c6f01ce7663b", + "0xc08361ebee3077ccbfcf4c7cb158623f4ba06f9d64364e41766bbaebda5f483c", + "0x9a0ef217496d430f55711db4409c7f6fc95458b155a14e1905fa5509c556e6c1", + "0x1c4fc2090d6175f35848ec3117139f83f3e5ee3ea632821c5d1974e74a419b31", + "0xf9c17e3ed32d910e445db6158ecf46e7c19bc4917285842b79898f66fda630a2", + "0x788c35b1f1bb9a0284b82d3040c8f9aba5177a3448190c3a235c8601ca0ee7ff", + "0xfbb001bfbf6d87e461898c35a75f3adb8deccae8f9d24121e16411c5d3c3d582", + "0x0895a29d8ec8f9eee9ac9d63905746fb8b2f74d18acca94c6fc7bd3b541643b1", + "0x8b4a9d7cbb83a6e1b2ce78b1005de400315137e513e275930f72b56542c9d80f", + "0x4c38fc0b2ddd680248d602c9a6c6635407d433c3e1ed9f27d127c85d048be937", + "0x77adc758de36d87b1a56636d2bbbb039ce25a82f6dc49b3dc7ed33b4634cc4af", + "0x186998959617c823890b90dfc5ea4e02bda16f6b075525b591fa975a054642c9", + "0xb666093d25a5d13f21b2afd6775525ddeb51a6e3c17028643864d1362b919b9b", + "0x0439e9da1da6eaa5e440716aa8d4f85bdbd2985442c41665791d888c46049e97", + "0x43d2cb5600ee3712bfcde630f7644c923dd76fefc00dc75b6a92b10bd889180f", + "0xdc599a8e3744c16864f89d6354514a3cddb4d7b15cdb504231805de28cb8eb78", + "0xf2e2a43cd02b64c1cafbaf313637414e538d465869939ca8a21de7b00a152566", + "0x58755fa95ff90fe6465a0b03edde00586144a1f9731b57615185c3a4c48564cb", + "0x4a72f99ff20d0cfaec51628370bf905f4780fabc3769d22a02bf33ba9f9469e1", + "0xff65f78acec3f6031af2ab63816a3455560aa29e90f87f88cec0cd647f733270", + "0xff0b2ff801c10bde68cac736ca3d76685e486549507044155fc28b07fd2323b0", + "0x240213e77e5150bd3aeb63d62cb6a130637623c2f4bf6c443fd5db4d2e7d585f", + "0xbb2dc0e49511d0c03ce9a5b45eeef8b637f84083d05c9fcd3e1ac5d9c8c978b0", + "0x30914c80f2a7d412616576695227ecba1bf82ba333052484ce10aeaa1f9b5bec", + "0x42d4fad66e4410e610ccabef8a457da74690d5b85b1fd88f368b38c33eae140d", + "0xd3858728030d3fcdffdff4fc454125418761c5fe71d641dee3ad0cf8977c7a10", + "0xb1d03e7de2164ee9cda0d201292378785c43248b988a56867e94fb3275cae543", + "0xb180bc491921b74c11919d1418660b5ae1153bc1b53cef0d7a7f325b34fb9479", + "0x8a0c96b875a2cc9f64cdf8cc9039a86d98d348ca2b441ec48a12b4899a7ef489", + "0x247e01eede88a9f8ee489665c60e97171760fb2ac25cc1910fa1892643f45788", + "0x461c06f12a7344ada6ca1d98e36bdac67fca29d09ea599d2804fd23dd9dcae22", + "0xd001d565025e9ef1b5a059184fa3ab7931c3e8b071f1651251d5e6f0d3e495f0", + "0x9eaee0be4da6331efb289003ff65d256198940b4009430589ddb93706ce102c2", + "0xbf81d0be70113e68ae66c3803cfb564e402423ca4ffd264b99db6517d3096b9a", + "0xdabc8e6389339211343fd27a32927a22524e219a4b21f8dcdc7722355c19e001", + "0x90ae6acf8733134e206cd8ecb89362b60ca63ce5f4a2126da7a445908fb3f265", + "0x9647541ea8679d59afbafde1d0e13d554c063954829630cf4d23dcbb92ba1ec3", + "0x6b07afb83878a700312c830fdb1a658da37d845b682d4ceccede39638c4e103a", + "0x8d5b9a415fdb13d27854d2f449a5116132562962ed89231ddbb6c1b8bf60efe2", + "0xcef004eebb09f873df07b27bbf34ffa0f96b5fc10b09b1932e4a55d2f83890b5", + "0x11113a1a298c2978b08d6a53bdfc8d0f75531fdc2494ba82efcae918e468b8c0", + "0x60f8a456a675433eaa2aa9eefda8ac8827ac3471f3304664fdaafdebca7f177c", + "0x597a2e57a7f37988e53ca63c03c8422e92badb308e216d13020cb68e7b5b723b", + "0x384b42bbdf463bfc69cff7707c9473006b7ff38dc43359f2335a832dcbaa9895", + "0x6263b395aa0082a06af2b1428646a46f52dcb4bc557d52b95369356f4fcc4557", + "0xcf06afa1522bf58108db30e5cb0932d9d103cc6be73a2f4b2011c260ce4610d3", + "0x316786efb8e6cd7a2d964964c230e2f267e06b7459eaee0ce6b9cce29dfed846", + "0xcf0e58bb7f3d02e87a2bfa3c8a3cccba12ce9533a4c3b9860af46276335b8424", + "0x2f0added26a6bb63de7d166aa3875b9c11b30275384bb1d704175681ccaabe82", + "0xfc9bee7bd8b4979b077355432d6f75e97658c68469b63b9989db96cc2880f3dd", + "0xfe02b3dd0cf8e3f9610bfeffb3c3061b0d18e0e98cf227e217054e77a1b81c7a", + "0x7500021ab63f7e39b215597cf0ccb16b7293e5f3c4af1363cc0c227f6a2e9724", + "0x252b99ddca8f0db13af88346a9bf9e1e11b0741c8e06f00379f55b9fcfe0a998", + "0x5d1e4bc209b89c4eadaba397e51e77fd2f5596c4e8935d5a0a15d0477535b78d", + "0x8dd3ad3a08e43fe1dcfd4dde3904161af1c22fb3ba130240931586c53b979c42", + "0x3e3ecc195c9269281f8cedb030a1a92883334a1aa91002ea6ca4231c4dd0c429", + "0x8f39d55737717808d718a252767dea09c8b625d93a404c9ec2f4f8dee85cbc9f", + "0x2140ac1f3b18eedf251ff5895eaf739233e614233031f34c411fb8e697018029", + "0x1eae7bed91bdbca1c86af961dc7afac3a3998b3df3fd461fa7e12a69495adde4", + "0x5f5396584061e8dc3837318ed336f0c6842743059385e4b0ab39f06f24e20a37", + "0xa6194663688f3d9a371c140794e29813c2e10ec532feac678ae77742862a561a", + "0x840ffe1a19d9e484db6f8c71055d932c26952e2c67b53cc9c744a7299e5e616e", + "0xd5993cec80869c5181576c195c5c0b2e9ee54eccfe39e8101c61a9c2ca79c942", + "0xf3896a34a6721ef9907b125640687807c90058b1d99db5bdae1424b6102ddc40", + "0xb1b859c0807d2ce0059dc52db6652a26fc3f482f586fa30b180e3958f63c3b6e", + "0x32dfec30a1260d98be59e9e5327933def088f7c8940f8f198d44e0d2dada73b9", + "0xad3c4aefef30f47f935e8272797d87570b69fe95ca0cdaaadf181427860023a2", + "0xcb4bf24a0ff15760abd26115d47797a49b25238b66c75bbf4cad09b780d5f120", + "0xe0239c1584d4659c12bead3fe4afef4ae3466c06e5e17cc262ef609c5630afdc", + "0xdb8f3c6943da2fedc4d9edc676a9e5f33f3c55c1efeb156c0d30eb2dd2302bb5", + "0x2be7245d4ba858a3263907effe3dd3176013cd778f117fc897ae5b8138c75da7", + "0xc139a9a894e6a5e13b9cad2a74c10f4b0fb59c030623b446713ec52d8175d91f", + "0x4c928f77e829432fe1b17ef6f36da2ec8b212df4a2fefeb89de7f45c2080966c", + "0x5bf20d51ee3601015f0f0e603e885ecde627e200105e7448ea3a68249edc0681", + "0x7deb4da4842e32ae4f7af55a8bcb41f8a77c11f5753ba26540f4e6e2fac7216a", + "0x42d5d3eb67063a04bd45d3d4ac7ae0cd3e0e3e7e4cb7273449cea5c7ed78830a", + "0x21abc2befacd8849400f503a338288ab6e77172724aa09a2a23f2f5bafad9308", + "0x77eb81a7230c5f66697705e4c9fd357b55ff1e9c8a0a87dd5f39444c2c621355", + "0x62b2a3e71f9878fda627a574f6348709f723f65e9d0ed454ca158115678ab19b", + "0x41dda8cf95d2623cef87dac5e144e39fb06e8a9f189079fa47c20df1165c4138", + "0x6eb621279735fafecca7640e327b398315dcbef56a09d5d2ee23f9e02ede8333", + "0x41f816a1e7be4e0c410d4171ce770a2651771bb04bed476bcff7c880e9545089", + "0x9c60365b6e860ac0514061dec9f838d0ffc8251030cdba08ea1132bced3bc93e", + "0xb335a10000de44fb02a78a5231568326c4790718ab6429671122321395f67ac9", + "0x32322ff92b08cc1178a1e04ac812c8fb777a9028252a5fd676781ffc5f314b08", + "0x5f4dba80af3d35280df731799bda9470ca3c50008d330bba0efc07cb5d320cf0", + "0xf85b00a5ffb6b8fde48f8c53c6e7facb5cbae0e6396dfebcce47edf5f34e9986", + "0xf8c17cc5f55e0b682cccd830ed22e9fffa4b60310993fe81f6477dbc883640ee", + "0xcfd2733d9d1c0f6046d5fc188b1bcc33992953970ba621b9f4a1ed20076ad7c1", + "0x4f931895363a594e2811e88b2f3318f68d75a4f752856d6e8e6f48019c1d5ed4", + "0x7d4ea4a84c4407ce4310fc2267d1b8cea035b36794ad02e1fa9dad23e76e4979", + "0xfa7880d815d007b2a0c8d4476792e8ffc6e43ad7988c251c5c3cd877542c7ec5", + "0x28aa3aa21314a1d0756e88ef649bf95e6691145387e1788d60d146c67f9ebf58", + "0xc7217dd077b6fca89f2d4bd5fe1a48861e8696218181037b584cdd59bd0d0040", + "0x899376d0e74785e72edaab9582f202427d729d60ca8f4649ee61593df5b003d5", + "0x6f5bd36a5d1751ddf8fe883d29acde87807cb4309dfc70c936b3c21b39b5f30a", + "0x335dd3067aa4bc41dad1ba352f2414f55fdff86906519f6b84a292202732d129", + "0x995257cd993155cdc8c3403d9b9005bf2e0945523b55c96ae4d0e7c27ea180f5", + "0x5ddff80b3bb9ef600a61e9d3b0d067e3e288292778471bf0c744df37884fe1c9", + "0x9f04d617744bf64cfba13e37c82155cbf4f383dedc61ce572e9e37433ce5e6ac", + "0xa5b63e6f153633ed425061825dac9c3568e9ba73ca3ac3564526457d8f86707e", + "0x92f73e829aecdc8dc5e42a22d882fb04fe8b4ac677870abdc0b503fd89e7ccd2", + "0xa4e0cc5c62f4f2b426da5ab86869b4121feb98122b9d4de5c39c943e1282a56d", + "0x3bf8cdb559b5e8c415ccf83fdb2a978e46b1e630209cc3190af8bb0de24772e3", + "0x3504c9d292167b0a082323477e464146cd87aee8d51749462aa43bda0c2728c8", + "0x789a976bf0a523d049107df426e65b328b1d94aa51666b88a80d6405a17d3f3d", + "0xe2d03a9184b68ddaaaf8d7eaed92cf2897776d30189c489b01535ea423677403", + "0xc80f574ee5cd6cab589a19de64e157596ad22a806033c7a321241985f64329c0", + "0x95950ff82aaec8beb97753d338d84baef38eb9c2d968bb4efaaab147f186a3c5", + "0xaa550f91fdec7364c919e112c19db493435e7c27366412b583a94580f022cacf", + "0x310681e8ce88444cecefe60e50552e83fb2370f04b8a2f344ace18b65aad8d96", + "0x8fb90ac0438e04597072b7b388b255e33d9713a22945e37f1ada8b90daad0c22", + "0xbc2e8c348366dfad128aed59997f1f65b8b5c97d120b2b7116043820aa8f3e9c", + "0xbfe573758887d296ae8f761080db0fafad31e76b669d1f2d7c9eaa9c0eeacd63", + "0x8330e900b124f4124520850c738d80bda397d646847d9f4646979b55f7ca4df6", + "0x45e9197370ace3ea1cfd5914ad5be72c5a781357af2043b8e3ad80d2b30336c7", + "0xf19d79a849d78ac305b704f3bce411d65dc04f3a29d2443e091719e535808072", + "0x160160fb0af1453d99822ecc802ce9a891282e90e83248ba5ab08eae34ee1fc8", + "0x20af4de5662f420012f594bf931b3e99f4376db16bd453ab5533a3c0c044cfec", + "0x1dff337dd9339fee4b0dbc3d1b08c19086f5f6518a17409ed8c4be6e4fddc0df", + "0x3d39e57bc53f0606ac1580e8cd89e7a4a6a775a9b477c5b498598f1e2595e91c", + "0x05efbdc477070f59ec1aa02bb288836e7067d12b2766e9a6cf251b35142f03ef", + "0x06ac00f66da777b028725c902620ab8b6e553f11e6dee6840e1e96411ca1a0cf", + "0xb50cfb61e2f52e3bfdcdbcbde912a71327f07fac24e2d18e5e3ba3aef594000f", + "0xbe7937ae231432d166efb57f1e607896dbd2504acd8905572d0459b086d43d03", + "0xbc87298b5b7c27af6c38fa38d352ce9f553ecee433c044b613b9113eedf1db67", + "0x503be847bd825d8c0e933b970052a847eab6724197d231926fb6e1869b642976", + "0xaa82370b0fdec97ebf9c86874e484959d07f9dd2bfad76faf6e6ba0fb857ff70", + "0x3c3f4f63e505441a3ed44306f09e0406e9e42978bb25596627d7039d2ffc6ed3", + "0xde37a859e529f0600b73ba20254bbd597d953428606810252a3a327f6f2a9648", + "0x88dff172c1fed3f92d66d693cf3dc3f71067c6ee2b98bd2cb6e15dd04c002a0d", + "0xa18782da47c01b11310e1da84cf9fb00666fea68f7b12017cd4acbd840b43919", + "0x3da809db79638326f2f63c9a68220c5912f589c1567db039293aca907c31f1c7", + "0x4f40a610727c20cf39e4142b0684e59572d1a60b95c221dad5463457614f9289", + "0x0a9c0e658e5cead62ebfaedd150fbdeeed4f1b27c6d315e060af4f15160afe21", + "0xe45f0bed93b5f26292bbfac940c229d0bbe9774a66436f53f1e2df8dacc4ed8c", + "0xe6e30c3941cce6fa9eefb449889414030a5ce29872192e4076a8bd061f6d5d34", + "0xcbf900644d25992ba6269b58dc6e1affa70106c47a5bb84fd997cd94015502ab", + "0x21fb381d3252776daa98d91430d9bbb4592e18e42d7c62f92e54c8d1410646e9", + "0xd5f72421b4b244acaa7d9f3e3bf47df1e86c696720ac88c3ee3a02b6afad1520", + "0x86a6445fb773a02ffab8df8320383ba0c2b46e0ff70898b97e9544745127eabd", + "0x602f3e6d442225f78e8aae55b127f8f366c1b3422705135c4d8eb86a51fec92e", + "0xc57eeeedbf82be9e1734e318db1a87fc1fe85db61239d3faa2417ac1efcc2f68", + "0xc947b9766372bac7f3b80ed3162a20b0fed1de984e5d0d7e6d80939af153ef93", + "0xf364cfaa1d03f1efbda30ba5384f761618d1d4a2f4ed752e3552fe5980367734", + "0xa83b8a368ca52ea5ee29990b154ced0bdca3069455c3718f6d85a5583c61d34c", + "0xf49f280002a91e303ec43446e0364e5f3fdb07b024af764c97a61fcea633b817", + "0xf87fc83ae823b76738af11e90beee384a7e14c5cfd6337dc28d6612e0bf13cbf", + "0xb56c53306c67293888b742645c1e4fa48d327803ccb850918167f2fa8cbd86ca", + "0x1ccca4e2b547e9199c74f661e211a2f679de719ea15deb9c53bf51cfb0e22d3f", + "0xfe13102bd82bdfd11615ee021a5bb0fcbbacc0889a71408bf5ef17c9501f2778", + "0x7a490644ab4dc49c671a6f4b39bf60e26091894ec2741ac0bd3fe521a5b7990f", + "0x80cc1ad8da93b12bd62cf6b8de20b5ecc5a1808042e49291f5220a59c3e8b45f", + "0xfad62cc4f30faca387e34689ddb210e60239b991363b581ae8db0b9a1df92d24", + "0xcb0b22086cc6b5fbdaf2d76d18cae5a9b6a2154a5ffa3daf64d200f7bbbd9474", + "0xcc1a63e689d2d7a992eef36fb0d9c97f282818f03bd5eb1e6093699b93c27609", + "0x05a50114fd094fe9c55a399e10a3deaa3fd991f5b62638cb19b9106d8a84b1c7", + "0xd0a0fcad27293ae857f490c3bf0b0d24a389e5acaf77465b945f118577d531de", + "0x4de2d535b48a20c6c0288581a4cfbb2e533559e1fd90963377a505ab794de39c", + "0xf75dcc46c5d5a0f89884b0668b44a41e712fcde375b6876111ebf08015015c33", + "0xcbe119f3d93370ec63fea018a72bd4f890f7ed87c9840a0e7aec8ed30c17fbb1", + "0x9b48daf2f687a15768b4f0805ba47d9b1801935f67a723a4dd0d145f77fc18ad", + "0x4c291f07294218a01f5c3b81dd252f6010c8ada735f5b59a75a9eb33335e56b7", + "0x4cf16af6a70ee9ad34a22c74cb259b698be96790207e00b807d54b46f6d25b2c", + "0xcecc8d0d12bad1e7f343542a18f0f7810d325f417481d3f85fc75d509db078d5", + "0xa50ec29d7194f0943c0ed29db04afaabd46a6c6f377a6f02beb2d28b5a68d890", + "0xb206429c50d6b36630e9bd0963d38b11115f0f62f759c57862fe148cf37c69ff", + "0x2e87b9c24614dd62d7f676efcc96e15a87768de2aa808e1a5df65af76bca757d", + "0x2aeec51608ffe04131738dc348a2398f4b50fc82e546687a25ded510049f3ca3", + "0x65a3a72a10e3b7fe3c71eb2ba58b2fde6be4f4876b342236d5edf8c105377d78", + "0x34fabb471d182ab390ca1c6cf19a6f30dcc87e707adf35077413013b2330533c", + "0x3478fc96e68d8331883ef0b6893dd17f7f9f28a1ea16115665107910b1cff5e9", + "0x8eddbe6f8a4dccbb11982d8bdbbf3fb62333cd8995c0a0d5dac44baeeca7d036", + "0x2b9570742941a95f3599c3d82e1c0c32f7a0814fad635ad99c51b2a1c6bafb12", + "0xf3ab66733673f0ac4dfb8131bfdf55ae655d3e9acf536bc8ab2fc51f56f769bf", + "0xec2ddb0e496bbf7e017cc61bbd195c0d30f5decdf172f0d7ed1f54a118000580", + "0xcacbd66a8f696869e41480fa7247581f95f57cf93c471b92f66b6e74dd69c623", + "0xd4826f0e122f817eb8239aec967ee50892a11e9f8f4e232abab0a7a72d0644b6", + "0x82ea5898bdad5cdf735a5357eb30ef547675cba48b451f3a5d16fab53d08f5c1", + "0x6ef2989d39cbbf90f52801c840fd169fbe776e76fefb095c46db0cc3ef166ceb", + "0xb988b6e718e236e87f0b3e7ba6ed62e47db8b18561c625966c27564e064a6d76", + "0x9b028f3a1345963ae9baaf632ef3f2d187d14aa67cdb289aa609be5bb96e88f5", + "0x023fbd1a3ca670319c8eb46d81349540d86dd89d220f0d3b0056769fc7fa59b7", + "0x1e246013369ef680a2b9ccb06c553fba22989c590753e33c35002201718ace31", + "0x280526fe0c1dcc62b640ccc74d36a0dc69264e148fbc76d486ebde5bdaf59ab0", + "0xc923987e3d0c79c65e10d2f3479cc750d2dfd4631d85336df479f4bc80395ea0", + "0xb9770bb76e8847fb7f828accb8449962fb80fd9417e38ba3e3be44f3772451dc", + "0x071aebf17e1a0f3168de03e72a33b2e8a9ddc604e1cbe7c63a80efad5029047e", + "0x50ae74a48f4b25e441e3a3d11812d38dfbd997324ac5a56e39fb3f05146f9a97", + "0xb1815dbdb1aecdb49da02af5aabddcd628f07a864d383b652b7301a7dbb0c9e2", + "0xb71face97b9b7b5db76c1599868448938ed5bf2c14b22a2abc415af0e30d958e", + "0x4f5bbccff737a59a778b628e48216c78f19fd2019f1d04df1c49f7c88d73d883", + "0x88ef9d776497be2fdfbeb6765f5da02942e9706b0adf2af7c2c6e0062ad20f23", + "0xef183a21bb1fa31ebbdeed3f291bf720ed027ca2d3cd45a6b869eeafd3fcdd78", + "0xb800d5f8a46b599515af39209a17af1b4d00a551d6b080147cb0b53a32ca813c", + "0x37cac43a863c00d137d00a13e342afd74a11eb7fa60875048f42bc08874ad84d", + "0xa3109612bbce6f56a2fbc18e4dde01ce8d94bccdbc2a4f56a59a5f53bc7f7601", + "0x9cd8e888ecd6aefe8a47e5b9115c9f3696181b74a34ae0dd3318f4aadda405be", + "0xf82510544a9fcce965499a3365fc82b364ca593917dde551f4462bf98b05ece4", + "0x19f5e8e0002f9312dc8b20c7d2b526e2768e4a4728fa49161315b6d3b4a82600", + "0x82c9dcb90e9e6a043d72d17bbb553d953578f1213510aaf4acf8f52fe10b217e", + "0xc9fdee4644fec4d295a89a67cc2779f79d5acdf406886d230421924b6e4edb7a", + "0x5607bf1d350bcf9a3ae1262d661d09a9d6a69049f2c0944feba429c99e35d035", + "0x9027831421b4dcb4870ee791b56404a88153a7e945598a1e8138eaa07e338c79", + "0x9baf250ed0c7d294a4c687aeffde8affdb519507db666308f61be8a4bc9b84fc", + "0xc2ca20e6196502d6ddc2642593d67635c3b88148abfd9b8df5f64e39c682c40f", + "0x4b34db85e83b2b2ca4ec25c8928606a2d4ac6ce8272d8fe41029aa50f9727e5b", + "0x57ddc8706761775cddae76a4294b48d285d76f21727f6b06f0a12d61d695ecbc", + "0xda286eb0051809ea28e1ecac275591e90583b5978015cb7ec11587d9e3420d86", + "0xde8e66058aca34862144cc1fecb20be2382910f1579213032209d13ebc87e558", + "0xa54445155ceca813ff56830475c952b24230bdd27f9fdb1dbfcf5a898a883d61", + "0x5ee6007d5dae65fd5b340e018f884b5fc57839bd46d630df7915bbe33c75759e", + "0x40d1d601830d2176042d56cc0175d47801d32f45c1abeb4f16121821cce1d56c", + "0x9afc64486128dbf28d615623a73ab608276615d7f5e3c13e5df625aa0c58a468", + "0xa842d267fb7cc4ac9447b4d7f0f83fec63c5f5eff68842219417cb797f55c5a0", + "0x53fa429ac535ae7649d602f185d9edad29121555063607c467a99334aa35b8fb", + "0x8aa140be5d7fe22f9b402ceb10bc84953e8dca6bb8499498ed5cc7160f501a31", + "0x4e3ab0eb2d005dab1926e974e80cb944c3054221ddc3f5520a62674da18baab6", + "0x5f084e62f0813e718271ff171ddb3b792fb1b86c5ca3d956632c6f4e4c1649fb", + "0xe8e9d85ff288eea682dafb54ab98d5e6dcb97f0a5df3ffbbb41c332f69dc7f5d", + "0x067d6b5741835af2a933aa03110a6ad360e4edb27cf59dbcb21c0c839a1f69e2", + "0x5c3160b5c7cd69d40b7bdc5e6ce05855e02df4cf8a1283800309c98743d579b6", + "0x333c24cc533da5e54b44ab54315e56a217fe32356d48aeb22ba712e1b8459e9c", + "0x8ed45252d97f4154b0d0f70a648cdd295d847f64c4bf65859a2e49e441702b44", + "0x7038b0afa42c34a12c90aee65146b7ca860d9868948feaee2dc85efd398fe9f4", + "0x4bbd89048bd27a0856952705691e1d9cc4cc16773ab32b5ec54e0e61c97a5ce9", + "0x91741f803f63374ee7f98b3795460b33eb672641a2501204d08f5ca440bbbc85", + "0x7662a54951e6e8ee328d1e4edb7f30cf8385adb7529cee6f511ac66d669455ef", + "0xd1170a01d6ca659043edf46f96066eb2e1e95f20ac2e849761f05a54ce625489", + "0xf5224f718f2885861338dc1259d2c77b7ed17c79ac0885da337539b54ef2dbd9", + "0x04a00eb698a1c08470018ee022b55897dd820042609304fcbaa5e3ced26800f4", + "0x9abad725558f21d4ca0e2b77d91659e94a020a9476de03c2f4acd4bcc5045725", + "0x23e5b719988f75e13cf1a235b2c9c178eb5637316d2c70f9138f84969551ebff", + "0x94bbe4059781b27107154eb8dedb5310303d0658d746fabae325dc1d81c36e49", + "0x85e3cedd3416862748ae9fb88cd9f382baa639e3ca7b958333123cecb671ab65", + "0x6ac3390bc7e7694a9e59dd9385687746017de69a07985ee1369525cbe4be5bb0", + "0x6ca43e3084d3a96fe65e9f0b8e8d7123143d3365b34cb2f6a4e97ba73334941f", + "0x3c96ef4edfe0969d0631127e356d914d3733a8bccf1d114904bcb7040a627f25", + "0x0a28fe495018cd887f584328aedfaa0fc374afa7a96fa0ea74066b92e4950370", + "0xfaa1fa0e126411f8de89185a3549db86acfcb1428437841e4a22b6e2550ee627", + "0x951953267acdbbb7a37562d6a4783af5329154d35cd596c0e96300a64687d99e", + "0x1e37a6435912dd128f2ad1af26c2b67734e17b6e03cd7b7b8185f57719349134", + "0x03260ac2b2ef8b2f76bee1b3a878832fd9cfb472838da8c3fcbb192eac2d889e", + "0xd8ca5e45af5d69999c54c67ba5ced738df4c11a4ea57f05a5cf072d8a84b0d77", + "0xe4448fe464415db242e01c3d862db662e8cf5589e37ec527ee79baef65fa5991", + "0x83c93073aca9867420b144eaf0632599468caba0ea99963f3bd866468f059013", + "0x521cfa9fd14a24681798fdddc2014c32cd85efcc4a0d151e84ec04c9fc5ace19", + "0x157b1221e02913af01525bf26b57e648dbea903f5faa41122e1ddc4ae87a183b", + "0xe521e315aa2f86bf85546b92807f291c7e761258c444fc9d6e78a610dc952090", + "0x28303d1c5111d387d5830146eeff8d3f537901eeea53390fa4da87c9a1a1a9e0", + "0xc1e4ffa377d0ea7cba9986d2216fb84cb5339dd370333d8a5b0b370efcf0c718", + "0x55ed8505b5c895cf00fabe852995456d492abc1e8e9cd2b0558b5ca1f1cb5d55", + "0xbeac94a53a66846dad7c2114db046a25aae71456ec23e016eb32582df6e17e73", + "0x5c1a7634c94dd72d5258f1782fbd8492a988926fa5a954eaf50da3365a9959ad", + "0x0079cd7814c678e44d6f4f95ed00368184a1df6e6bb323136d887de887742ca0", + "0xfe6df0b78193409292fb82307d81aa2070f9b4374d65fd7b358befc56e23ef3c", + "0x4ffc947115eb5afad96af4b63eda265579501417cd2922645562b7808785e41a", + "0x72fa31a392e3228f0a038d6009a45a33555c9501f7245edbff9f5d0042793c63", + "0x948d1c511886fcf00edc5b39d7d7c1ada6682b2590ff16785ad8bd6fcbab0553", + "0xed267f0685efc33f037de15e7385af6780761b5891a3324e7679f6154c1cc0b9", + "0x3c43575c50bc34cb61f097a2af285216fd18474a377e6f9dd6c2c23d746d8273", + "0x2e08456113b282c428cb0cef4aba24b3f89cbd43668b620289f5111549382cf5", + "0x2c56615ec411c707d850999cf4c08cb60e2239f230cd77087ebb7a6f2b00ac72", + "0x2a5c8be7e2e089b10a32cef1dafa7ea98f67724b2e1b3f12804e9cd67af145d9", + "0x6ce139fbc717a71b8728804790a8fe3649397e235cda6c3a019d54279e9c4c5f", + "0x55a1c0b71f93064551aa0efa6094225d2a235cfe40ef6d6b60ffb34d5ca735e6", + "0x529f374526cb37738c7f43cfb35ba520dfd9af3a5f89d08c1d6f607d45955547", + "0xa6305f84f6ff28dbb0249bb6e6af300fea7deffc39c9e398f69c657d5789466a", + "0x9fc60c89957af4a8907e9949e2fba7fd22084afcd2e9933b4f4dd119086f9928", + "0x23c7680d60fdbe01989d4e355eba752606410c8ab4949b6cc419ec78c1d0f4c5", + "0xaecfab11f2a20c033f2c5551c829d69be902c3ca320803e5a34723309040a808", + "0x6b75c777440d4caf63165998231ecb4b2f10035e17135948b70376ad27c693dc", + "0x6692bc7a38486463d712c76e71a964f4383da9dbc426b5279afe753c57ddd480", + "0xea883865d197bd5f809db22fea2dd09915afb3587fa157d237e8fe5900bc77ea", + "0xbde58d2cff1fb2d428570905b7e9a509246403513b740a0dd5b131dcf6ddb5f7", + "0x2a143c021fe378e612eb69ff4afbfd08221040c704dbb3aa0286897c327f86d4", + "0xd7771a8c3faf3daa5a722483c691f8130a633f5af677df7ed5119eab5bcab0a0", + "0xf9485b786627dfc31ed6e84187cfc73187a354353af671dcdea687a16aeb537a", + "0x4be77c528b6017e49f5caca836def67cabbe7ab077b006d255d6c4026c3f972e", + "0xedfb5e6271c502c383a99877dfc36cf262111667bb25f6418a9b17924486e6f4", + "0x2c1b463c56e8a2e362d802983f75e6b82da3a71df2851564414c0214e93def55", + "0xeb7984b9d970fed59db78adb51df50b286d60b544c8ffbd43bbd319d793e12e0", + "0x735eecadb8b034a4f656f33db154b15c7c2f0df08315fb40782ed81063c67220", + "0x5b24bab8004b323a26c4d49742108440cb1b94a6aa31f914e24b7d027e6301fe", + "0x8edbce461f8f9f684122dc5bd546b9e0c1284b3c599ed06b0e5948db4445fd66", + "0x1883dec9810eea64dec66426466a26a47dea10a98a0052e9e422428d92078f37", + "0x01f7b235c537135ae2b38a2cb3010b699c284667cf95cff007006989f1e6ed27", + "0x161e6ceebcd35f6c2877cca901301c95456b0118be007925b84e4ff77683f3f7", + "0x0244cb9e9428be3ec5e6c5f1f63f7af97c849c3e86869982aaa34b51bec79cb8", + "0x2d89a541957b100a6b0f8528de2ac6ecbd1269b5a7800a3b96e09260d26d4dbf", + "0x8903f48d9c498a5f38a8e0c8da65dae37d14e958975a7d0c2b5028c0868cb674", + "0xb31b476bf0716c343fc6556389009639d2ea76c2db3c275650721b848d99e74d", + "0x6e4f19623a1154e9c2bddd3581a4d8b131f75e8f648fbaae88d4a2f3a9e94a42", + "0x73935025c6223de184c2dbadbdab57fca1ec979e57fef51484a33d112771bb7b", + "0x56a732542142a261eeb80fb37372c617e1416a1e1f0a6592e5581500830688f9", + "0xdbf50d900050527ab636a4a8b3c67da6ba9d57a4c83df8a6d2b251ecce34b608", + "0xe50a8d3fb06dc0764ece1294bc6e16a02d70ea9fec1c02f93770119cb0733329", + "0x131f8d8d0d2747a89e91558a5673da736b91c9f24974ad1a175618cbf1bf8ece", + "0x814fe549d0be8e512172cd29acf580157f2890828e61d16a5ed2c0a9e2acd56c", + "0x11a5fe999b1d3e65850954e35c208a60d39a114107233371c7fa619ae2f81e12", + "0x481f550d0cf4f04fd931ae2a542e8d24d9356519000578318ecdbf2a5c44bf06", + "0xe7a69db393c8407a7036bad6a34c120490556302908a84250a85d5e39670bf63", + "0xd0a6096a09545c30c708938df80a1b5fa5e58f48471becba1611695d866ff24e", + "0x3a907fdddfe84750043b69bfb94f20672071589dbd23986689d4c51cdcdb599e", + "0xdb2637d2c1c867e1a1a40d2d284b12a7585465e4abf24e8d1589bc7f69b3d3e1", + "0x07a99fedc5092ffebccd76c3d4cc49c51ab261a5e5f842408c48310d6e2198a4", + "0xda62575f90d592ec63057ece7cc4778352a1f4c1a13cb7995201a726e00b113d", + "0x0dc930a4cba4057756b55c4ac52888a928c4f45763692ae5581ac2d1946d0c17", + "0x6b676783c0fdb5d561e982b80c54d7071e873bfffc25e64a4d94e20b4eba20f8", + "0x1953dc100dc3bac950f475227496085cf71f7399ae4c620e69f1a53c66b8f6ac", + "0x8931ec9a0561f54a300386e3f09ea0ff28215dc07b5984428f89112bdab2c0b8", + "0x767060c3af55de8c164cffabbfd0c757ddc7b5e9cb8e2e478c782916df5d2d40", + "0xb0b64f467d3f8fef1861775021624829a91ea3b6d7c65f5bafbf9f30ed32f4f1", + "0x917fb64586534870e1d50d5c643205a5c51dfa0604a41da818314187341de8e5", + "0xf93ffe250c94baccc6d177927eca5ed94d8d3431248f9a6050bd1ad105872139", + "0x04a2cb68e33cbc1547a5494faa61c5b4ad7651126b637eef06b43158826dc40a", + "0x08d9f651aa81caed6775a6b7599af654130abc44ada9056eccccecc87dafc9ad", + "0x2b250c3335de2b656155e6d38c56a1fb2adb539485cd19c334f81da679d1afff", + "0xc49e3b76734a04ce80be690eda761160da6e2166511b4141602545619154f0b2", + "0x85cb7acb3fa8c9291ed4ee8aee92b01062a5579859e39d05f5ea64f3a92ed2c5", + "0x257e7bf1196eb28fe491104e45d5a259db0fd0f3c6c10b2a10acc80575e68220", + "0x07fdcb19de830661c1283b3cab6a0cc9fc81f344c862a5ad5cc36a692d6fd813", + "0x9ac05ea1277a6fb4fed3a180e4871789093df4784c6cb744d84fbb91b860d320", + "0x30a4551a59481f532c38d8e4a092d4b87565cd3a45324c3848ffee534e06e768", + "0x77e93c712c5449669b6cb358f64eef58168be4aa54bdcf09493d2c3f6ebb957b", + "0x236d8e21c7c97c4d41ce279f14ab26ccdce3cd9d1572082913d42963c61f0b7a", + "0x8ebe738001bdd45351d1964fe57e00483c2f8d12f0626ccaaa62608e670b3e7c", + "0x5df45c918d55049d55fac74e9cfe63cde88ac269daa3198acf3c4975c7ed473a", + "0x381bcb92ffd97f2787def89c18cd2ecd40ca4f4881af268344332028673a5fed", + "0x829836e57c1754f264acdc78255922b46a732158d73e8a6ed3a8a43036d9c1d4", + "0x06e920dcbfcd6c33a721ab6f698445ccded876b5ff44bdbfcb3657fc7c0648bd", + "0x05fce937b100338d87f21ec77f790da5c24fc6b7182201a8aa9e82bd55a36142", + "0x4b756fb71bc1e3cfe2c437ac7d2584c212dcfff939e1b4c56cedc5fcbdfdf2e2", + "0x72520c566500782271bbb9a5c731fd1e79ffb71a271c8c2a9ff552f9cbaf34d0", + "0x2884b2432d280aca6d94426668d022e0ea2398f1f33b21554dfa12ef4c2339c4", + "0x29a049d56155ef0cbe251b80ba3c31402c5db15f9cf269dbfd6e79c0281c059b", + "0xcfbf77646529b05e480b8d8ad7faf24c2432b83c751bd92ddef47da508ac5fe6", + "0x5e962e077ad531e8f6bfd81f45a5f99b716502fd0400b5848bef2841e395b8b1", + "0x2864100a937fc793e49aeac55784321b8448e1fd2a6603df3994dfb968f3c3d5", + "0x5c5e43d16933bc89bae3803f0736e467dd88396e2edad64d0864ec24d5956566", + "0x307b8bb531783f4fddc851df543843e65cd5cccf0d886eae27ed09a22aff592f", + "0xf98c556b2e4880bba041fdd6e5a21ec38c73ae4e22b0bb7ff98d8401553454da", + "0x21e4401ca5a567aa66276996fbf1fc9bd6163ccbb9092e7a1ff4dc333c550db3", + "0xa0147ab3dcc1913bdc9131ee36f5e23f9427359a251dbdd123f63312f5d0e764", + "0x87b24e5d3ff07791097bd096095e41c99d74348489a2a5c54b1c959cfd70422e", + "0x4fc872227a643a4396393e737e26a5d5b2af695747b83af6166a00b8cdacc7f5", + "0x5253b3081d04552176cd7929b9c71d1cb8e7608d27453e86c0299a695f88cf06", + "0x6534686b2a645dd46301ca1919820d7d208b4971f126eb13b61b9ba14238548a", + "0x1dd534523e146c1fb0eea3d398145c5b7ddaad8a0ba77d6e9ace166806e9950c", + "0xde86105dd04c66f64dfb5aa8c53c309200d43089e8de4a66e334b7a5a24a87fd", + "0x06963c56c0bced1e469e5259a652821850a6308aec30722ff38d8d6b58a6d144", + "0x7e9417e21ff81a5957b430bfec81b647f052938b92f43087066f770763e9290f", + "0x4226eb1d384570f54f1e876ddba7c7e8a6057e2da82b8bc51870bcd000a9e71a", + "0x993199a9c23a3b4fdc8cb4e0d417a90fe0ba5c7517aea4701596e50955a8bb73", + "0x8e4c71e04dec21a113385782663f54b73ce79d64695fc118ee3b8432ab19fc17", + "0x016518194f110b7e0c73a36a425990525abd58161c63ae5e7edad9c34726957c", + "0x366c9fe8d852f133f4577de8f03add7a57e7575b6cd730c50d02b4797d77a038", + "0x01c88aa7b801774df734cb9e4c7fe34a689f79aeb07efa392689e31c8b3ff8f3", + "0xc713bdeb3ae806aae7267d0618d14640c3b61b5ec650e180d45dcf1b6f9a521a", + "0xc6c489973aa0e4b462f41cfb3b9f2a53f51551712c916a63af2d7492c3945fd2", + "0xa9807566d4b7172105da27182958445f1f7f859cae4d60b07bfa7db0d8b1441d", + "0x836ec237e6563992d529ad993146ff32b8d4ed9704317dcca261bd7865911497", + "0x79be4246cc1f72e53031e8aa748df2457660a00dae74e7760600ce6f36717c7d", + "0x4467aefd98ab7ceaec6f8035738df4d45f5d25f5f6ef26d397f389b844442533", + "0x6b1d6ecadb362d3276d9ba6e4a8b1e04aa07e8c51cf4a7ecbef1eb1cc86089a2", + "0x845606b7291486dc81f3386715d920c237ab1daef4f5e1068b224e43bcdbcd43", + "0x71c0b3f50cb0ecee793dc92ed8d1730f9b65a2d95ac14ddb186d75fc1a7db5a1", + "0x80c10bae451799062b97253fad42e57a5d521a1da2ade858e291564615b17b5a", + "0x09f5c54a81a2ec76da24b3c803b9d70f0bc6e7ce9898ba7dfaf559de9db7c18b", + "0xed80439e78da27b17f22123a84d7128bc82fbe3da3d72129d1aef2d09b14946e", + "0x708896861d08aba94c42da0fe9993b81ef05985974f52dd1d981c70b04074515", + "0x69a6a15585834c0200f39d01f8e6c0a20a0ffda34bd712e6be172c8d80ddf536", + "0x938f6d7f18805925f278c682b5ba7c024359b7ca8a99f2447e117ccc0bc088da", + "0x7406b90045252ac5135e4bb30f7d9349eb5f67bcaf0ddb0e117dee25b956edc0", + "0xa11056a3195e43371ca10c1d4729b4e460c4f52f8f00e263b022ebd813832161", + "0xcecaf942389d7d0ba5e09dd506a022287351ddee6783b5ba641564f6fa8d53c8", + "0x231974abe7d75bca4a6e037abdcdcf0e9599483468501927537b05e7127e89be", + "0x25b2513069e5b5a00dcef995c845c78443df830809a752a5518473525af2a89e", + "0x71e8e14865a47841eb5d5c38a42048a45a03f377bcfb95faf62c985d45198da9", + "0x4b75a5a18cdad2848b7c626a12e5e097321c1654eec053d527077af1ca335da3", + "0xd4df98d90e32b8b30cac2720bb3691277d46509fa78620deed2e69a9eb889fc0", + "0xfbeb7bebff8d30e28520517e891ba10ee287b114aaca58125f2fa7486a65d77e", + "0x1d693f106f212198d2273ecd740adef3ad2c251c86bbad1be692dc9aa1772d30", + "0x2e101752714b61e2b7d2bf7819757f47b4347e11935333353b1aca124b001df3", + "0xb625351a4ff6b29dc017b5a3ea001b7a72501eb47a47f94f1394291e96a21fd5", + "0x24b7fd0786efebce4fcced9163f212dbd846881e6fed8d27a51a713dbf038213", + "0x326af5544dce51c65ba08273a88bbb2ab1e02812f44e9524e5b0784cf03e8e05", + "0xfa274af2b7c6af03679c88273b7deac01ad3895863d681eeb87cb85fa1cf5896", + "0x7bcba32a49a5539be79242ef81d9d86d72a7abde9c5e4d6904f914e4e114cad6", + "0x5d832c6ab56171569db941e96f9ccf856096f11111149094f5e72129a10e1f60", + "0xd5176a01e429a00b96d9cd4cfe48baa0d681e23d60406808f531c22318764f22", + "0xf360983ee2f750e168536fbb86b7c92422c6312318495096804d31986bcf80b8", + "0x33cc65ff724c1e0b43e5e19b11c87a8641bdf8562d7d46456cb63b3cacce7adc", + "0xc66fa97d55a600bcaac3290b2955a928384c96117d2d2355cb74afb042da91c8", + "0x67cedc68ad83905ffc213528ea4d8251db44a1850a4f25ec7ae8a93711d7f9ed", + "0x7fd2d13c74413ccee3e4c4b28851e15bd2bc49d709dd34a589b757a9e8272a72", + "0x97c3df2e5ec73f0c0666fa520dd9b72ef4290627573d7abed177c313467f254a", + "0x7a700d3ef9dd3a27fd23544f84704ba9fa69f5716bf0a102c566cabc2d42d7e9", + "0x3e0a74b4b417f2366b4b45036f5f0a6c1bc4adcad82496684f1a736134d0e3c3", + "0xd7b006db8273914f2f44838aab00a1d57e6836125fb5d645ad542f56dc5e3909", + "0x559aeae166642e3c461125ca39657d13f5a4b7c8b46e3853539b5ef63eb226cd", + "0x0e038c56f5caed2a3f9b88f00b12ee3431be080b88eabf84c615b29d66a79e64", + "0xee2a2a565d4b1cb256e9d51132f75be0f3f9e699cd8c8c70d5aeece091397ba4", + "0x42cfaca209cf319aa801c05a86e72712267da74990ffd47cc5611bd04bca43ff", + "0x9eefd1f4f3266c30819ca10b3ddb1375bf58c8df9438da081f570012cf36f811", + "0x1d5205734a4e99c842df78dd9e187126f1aa6aa3a43ecde626473d94a23c9f34", + "0xa8a22bb37fc400dbf269480a713ef0f1417255f8e2cad2e4671e8f6a16d3b3ee", + "0xb2f78ae2b363f6278c488c11e1bbc7c34b7e0a3a6c1b8dd259014c15b4e0a25e", + "0x144ceb5acf7c0d63e263f5285cda3dc259ee334037312a127f201d0d295f0641", + "0xd4ac5cee49fcf35a71d8750431f350f742d9abbfdf33329ea934c6dd0b89f50f", + "0x21720ad5f6efb96e35c367c704bdc66ef753a95438879ebab7af5266785bdf2c", + "0x8c130594514378da910edd16a2f5f88b01d874098eef8161e394096f926169c6", + "0x5d237942f57601e9f0804acd9e55da3fd62a914fd47e5ddb4a721aa12fe8427b", + "0x6faea40fd543910d52869646e377dced5be2429e2315b833e9859d3ff6fedccb", + "0xe52e1c192a4cf07431d0df9a5c377d48f5d70c704ffcf382e129796aa8d65aa4", + "0x9ef9be68dd5c02ec5460e672888236872a3ba5e712d41d8896cadbca9d6a4ee5", + "0x348a3273421a18ad7f9fb6414c11561353e70a52dffa4be59a7b63357bfe1a5f", + "0xb62ba19b0639bf458a02d0a6ee046c8ac5a5aff78e2baf7ac3cb3db575114cd5", + "0x61a0e322ab7862d115cdd5948ea36e2580e3bf85ef4614b851b2af1272bb6b49", + "0x929328cc98eba5b37d7868bce5694914c722a315b4ad2daa81241498a72ab022", + "0xbcccba9c3cc8b9fc5c4203a648dc7b8e883b1c9a13ecad1b30b2d462659b887a", + "0x99a913ce0df9766c2a35b92c810cee2c9885b7bcadfeee9d9f9bc515ab4c0b52", + "0x93de8260ae4d0743a1238fe325be53e0e1f7e799f7bdf0d40f0dd252f91f62fe", + "0x97bd18cf3e5aceb0df90ee58f47bc74bc3c50c325fc95a500a917e4833689c1a", + "0x1ef00a834af727207dee98ee2d844664762d433880370da09abf66c05c8d9317", + "0x55873835bbdc3fd71bebe75dec01d1bf8146460e9b8ec2f7ce43c838f40c29ae", + "0xf69ae35e750de62f3d37daf31c262e8a374b6e2886b8b050776d695fc12c3a54", + "0x2c2e798e7fecb6bc745ca2cdfe4eb56584578a0df9a2afb4311849d83f2a8d8a", + "0x7ff1ee42f125f771f8bbd88e0577f6bc74dca88b370d7522346f10f4aee4a539", + "0xa2709b4e8c1ec9dba2e54dd458b1cc97824837cdfd5216d0ba854565bacf4d24", + "0xb209748347474f51f4daf5b7536ce984cbd63dd1be37a7dc8e3f445547313783", + "0xbfb7e0380186abe5785112fcc95792c0508b6c8360b15f0edd93f84f357c8f26", + "0x81938406b9f7eb7017be06afe00afd78c2ac4329ef3529169a097af67250a4ad", + "0xe92c64fff1a99c982f22d64daa3958aba4a27800e3d1eef00e643906a2846dcc", + "0xf0da67b9e5001445051ec8a4bd4fdfafd53554cfe3c9494f6e490f4a820aa465", + "0x242ebda99f649d327520a99e68ddf6ce81ae661d7914938dd2a5bf962b55f0d6", + "0x164198964537e74abcfaec3fa0495080c7da60ceb50b26ffcbd1c641cc73ed28", + "0x9bd9d870d314c1bf1a94a89de4da0fda9e5be37d718139a0522c22d607dd1d46", + "0x41e53ee744e82dcd7a99a2f7f8ae1a03ae4347962446bcc9ae256cca505df8fa", + "0x612792d79924b4c48f963f7218d182eb508fceae0e739916518eb6f7697e15f2", + "0x2f66b1f0544952c0558fea46da15e38bbf8da6de8d5bbf51bea9eacee9971b64", + "0x470c05b727d27124b1cfc976dfacb77e369ad655c606920946904de4c42f5ee1", + "0x1551e7803357d334f53c9673af12197d57df56db80cabc2e8716b37bdec14335", + "0x69e471b7fa6ec6aa510fd3c3851fd4e65c17412c5f5a5455a31c9f36a6d6d867", + "0xdea57a49a7d28b7407c0259b6a33ac3647748a94ad7b50876f3d03be269bf34c", + "0x10881882eeeac57c0aca0a919c392b8b21edda595a1d59dc96438563f95f0879", + "0xe17e1c42a7de23e8e892d7e5159e5a5dbd3a63fec188769446d8f4040f7e2d4e", + "0xfb07f5ce81d30572a6e5ff8b6c6ecdca2e9b1749ca92fe5f22ba5ad133c6171d", + "0x37bcf5e520cb683c98b22c183fe59fcb4fe30f8f7baed04cf29417330c3c81e1", + "0x46227b05b93ab890430e3fd92c6591b38c30718757e5884e8ecdbb53cd769e37", + "0x6b57a86ce312275a66efd43619e605344185c92f08c442d442a7f9c66323e5a9", + "0x291af7cf2f5da4b7858406a66d2b76ea5006f77257f4ced45947a7650f51696c", + "0x07b5bf5efcd4c12852e80a702353369dfe9a52aa58e6518fac91bc592bd4fa85", + "0x9264a7865d8238961238a98009ce4636c1a64878ce75106bd0c0ece90a838dc6", + "0x817c42352972b3c017fdcda69c5094e8d57bb5518b5fd8ca8644a54687bf4571", + "0x7c898aa92b1c88d63bedbc2ac337dd2ac7fcba6279b05d4aac5e5d8eba04ae51", + "0xa7aaa415615f1b610021ea78558c6042f502132487b576b85315be3bf150c55a", + "0x7c0c79751553b5cf9b01544348dd51cfa0acd2d0bc03d4addf8838a02173ea02", + "0x2e90226f7cf04fca4d40c53f7f612135559c001f82df42853ef560b2263433b5", + "0xdfcfc154e6d1f5c775a5f44406fb72b6fb8ac6a61505e14d3c5ab84701a3624a", + "0xf885c446ab5d72944ffb73c27dfe592b479cdd71582db87291389d4922b19950", + "0xa421b9a8ab3579864c51925adeaf8fd268c3533f242dfbcd141755fb2886749d", + "0x181b0dd96c96bb73af7a33a047f837db920deeb057d94933be0fa757546be5ad", + "0x41551049d41ee3d367672376c22a642efa3e0cb3a283d9e8b8a99390bccc33f3", + "0x9863385b53222a64aeeab06d403911faf866c95e6f2d3344ce91d5515f97ccc4", + "0x5c32b2a6a6888eb735ed8c4c9ea074185309aa1b20eeeb668a406434491e45fe", + "0x8195a2b9dbaa6e38a8c7dccd1969f8e88830d224256fd14be575203983ad20b9", + "0x1e177075fe5e80bc958e64b3494d9409b88c039c84c7d84513b096f13dca1980", + "0xc67af23f9b6e42f11bafe52a4a6a52a67ee0cb28b09c7dd721a4d0a7168c11f3", + "0xf840f03d55aa3530f047ca619d1d09b3b91bab75f3d26ea94f8efdad58dbec28", + "0x6b117850396fdce86a615aba07bbc079f42b7a12a3b0f235ebfd59f84a5df992", + "0x696b2e0837d6055bea1f4a365cd239432284c0baa81a79eff0e9e92c437481b3", + "0x9bb2d36d293345e82995fe1cba58718e291da4eec4ff812b0efc4408b614d3b1", + "0xbf3849a020156d224a34bfa6e4de1d80a5be74b603bb50b852a67da4708f4117", + "0x256f7ae1fb9823503ef9d2286278b581e3d88a78da79bd0eb1646f19acf98cf4", + "0xf03476ecd9edb7e0e4f9aba5712528ed9a340415075e77c6e24c06e9b9b4ef13", + "0xd7c0e503b824499baab9a2a4a1688f46d7fdd87a8cc3d59a0734d07adfbaf6e1", + "0xde9c83ec0aa32363430b97e1819c51dc0a0732fbf62a495d1b37a70e4a7454bb", + "0x2091c1cdb8c4563401070aa24cc1f2a8b8b11e260897d98453d5446cdeeff2c9", + "0x69ea4dc9e58bcb783192c2ce41f62ef0fa47a75f8b08a3fd09764e2e03634e4e", + "0x28885b6bf3cc1e5c4e236cfbce552549aa0eed46c43b50d9bd09c44953ee4604", + "0x4d034ec74f69e51dcec03ab78c498a552d38e8ac98aa14cfcb020f06a0700ebd", + "0x78da5046f5285b0b43e3cd64d2dbf55e93c4ef5a4830f095e092ecd7d0b39ed8", + "0x5295cf6a42eb2cfe0c14528b10de6f445ed7e6ee54a4af4220595533128882ae", + "0x39e7ec95a031630291f175bb54da80662929686e17a1598e74c9132e8fcb72f5", + "0x47e87b96e6b96030bf96d8e9f76d54d42294e321e81777d229bdf2b0415d2138", + "0xfe79be3b453b3db487a9fa7d39d28433591dcf1f7e3f6563c58f394467b7be23", + "0xe5d536b815d5924e83323781f0d656e07f489e6d461e0c1e2261da9577557e59", + "0xc74ae9748d15173c0710bfb2f1887287ba70515f9d0b2f72bcf1bebca6e7f0aa", + "0x81c193c6a4604bebc412741502d27e530ab16a9fbecd74a3187060f25266f725", + "0x0a993ae60e2a54a31c7d385f357ba294258ed85a8e0ba7e6f9db80f1a8aeb874", + "0x1b3093699343f60cae9d3465ebd953695e21b64d92e7205ee04e2d088bc8ce78", + "0xdc5a76eb087544b90b9dbb791ca0a509723c5ec7924c7ce9285313f59887fe79", + "0x227cbf03086442b1b7629c1296c64f3dadb4094f98998d84fa5851e32103023c", + "0x4b8ba2948a14492c7f04828732e9ad950ed2ba30bc6867973eb545fbf345dd4e", + "0x1bdfd1602b16889a89e03d1eb6dc36be8d8c6f467d2f22e09a7e88449871b506", + "0x0926b282744dbf9788437c88a57361e18cbe127ed6b6f963d4c2b9f2f7f139be", + "0xd3fdc189e79ab19775d3c07237bf4f06a85114ec8fd67f50acd19027f0c443ba", + "0xcd3b005100b1b09cd22a423efb9b6549148eff4c1006d06964d70da377a2857f", + "0x3bbe56a210d4f8ddc95deda7af016b1ce51a1f693cc3c4da029e04f4434cf76e", + "0xcc194b299d3a6993f288fd92c20c0f62841e68bd361dbd0a3442272c21e359f1", + "0x05b0f868d3ce816fc3df090f71c346918594bf8050e03036a26882a6235e3413", + "0x1477c1ba3db15ee093a2323bb238d68cb4f20a47be10848840859837ae59be84", + "0x5d53056a10ea29e6c08713006a6b64f9a2421f5bf18a8d76460758108beb4dc0", + "0xb705e59d2a12645b7791e3125433bae7758a7ccbc0acb7327675e6e8f7d1b7da", + "0x784195aae0b808bec8e63bf0016b371202f0ed87937fce6ef1d2ea021e12e728", + "0xa3a02a122844f4d513bb4a902799bfb102e996a7657411061927833405adfd65", + "0x60913560ba8f3387f910a66a4065d29aade7682589fd98832fbe4589f578448f", + "0x663e2bc9693c90ee0c9656605c6abe5a452f17b2da7d183903dd82ff9367a67a", + "0xf9dcfcc48253154faf875e8d18d793ff33d44bf9747fa724aa217ab59ca96d60", + "0x5122b29b441a1cd8eefad31e51865d6143eee0138a5d76d559301088b44919fd", + "0x77137b17274dfc87a978652804a0eb4ef3b1f56f5a2e25f8dec655d5f98febf0", + "0x275476cfa5eeecaf7b10e9623f37a2ff760939c0a98ba6feb86eb3fd2cf689ae", + "0x6f936cbde6df77c0f0a1baee957d9fc807c5c02fa65284022e8d08c9944126d3", + "0x1bb22f557b189ad988e49854295bd5b21a518f8132b218ae36cb272b34cccffc", + "0xf46d1f046bc321d74b34b40c8ac68dd9861275a1dd58700e5c40b5c4677c0993", + "0x796793e4ace4134ec013f3c987d094614d12fff5d4943495d3be0be3abaaba04", + "0x1c59cece32d2238006f84ba3220d4ee6ace3d01344ff83f37176decccdb44030", + "0x65c86175473cc49c65791848a03f4847d4dee97e81b5d8ae8c7cb4470b65e5ca", + "0xcb8d13c3779b27d32c3df66817e91144c5673c52f0c8df8f0af62cd11fa01aaa", + "0x364b61318bd8754621d3b5885772c499ff3f1adde6a00d10394aea6da6a2d544", + "0xc62d02b5fb047d504c638521f4163e443a6772bf38fab9dfe7ce4d8666cb0f91", + "0xaa04b25b9c29c97baf5027d00543da91fdeed81ca4d9509a68c5005c3b8dccb7", + "0xf604a169bbf56e3787a8f63961ce62714d4e76cc6fa2dd880ab9f452a095240a", + "0xf28334a2911ad6fc6933360c03fda17b2fad5b162fd33d0071a162b65b0a7602", + "0xe689196ce77f1e35bae024dcc29bdd21d534e3262a932ac5c11a72a710f8a9ca", + "0xb7f7cc7238e71dddeef079c92bcd3adf1f3728572ffd2d7f6733168a6a6a806f", + "0x9382eb89d4a5e50f8b57e70e56fc11d76cf282df18bd6ebafbe440cf19814250", + "0xf7796781a046dc447f200bc52931b652bec729e864e3e32f9be889b1e6044df4", + "0x944da3059f489e5cd4e68c7e9d726953d9395bfe7fafffb7337c482274910d1f", + "0x860ffa374e6250d098c0b99f88e6f8345392da6cefb3fe4f0bff2b6061964158", + "0x4d813c9fa80f89645011d6a85de49b2cbc45211ad810dbe5850327c7c41d453e", + "0xa4b4b8f07b313bc75301d972bdd726c8bd7786f5461af400f0d02fa928ac786a", + "0x8271c217605b4862a79b4be3fac7514c83dca48aee8a64a7590acc039f25c89b", + "0xba4924f28f2e7ddc6a8fcb9c645b2395745bec7a7b981d5dfa9c4b05781e9533", + "0xacaf21a45256ed22bb799edfdb8d048d3ecc396cf4c97538f37aff548caa7373", + "0xf7c47f2e184f8589fd2e9e16040296087db2f5da51fb49f573311665a5c3db90", + "0x42fe94879b173b00a8a671c2bbc96ec94d36e87d84cb8367ef7028d270226825", + "0x5e2753cb8c6cfc6de11b7b262394800a0e2f4f20c576428a08ba773b34efd917", + "0x664474519eda585b852d3371f0871c37825765c258e695c6a2858b4bbadb44a7", + "0xce5f9930d10b70574a19b5887f6799440bebe4f8e47c2caf465481c87a715981", + "0xdbdc59a1b7ec8c2252d49bb377bffecc83a5f2b83ee2205b5a4421f54b7ab447", + "0x4a108fbd6cf1a0f30999aa14dac3436a5138c833fec039e0aa02b83d4541d74a", + "0xdbc321bc8e1e201c1e78e272076097c95bc683ef6acb3160c922ea9f61068665", + "0xe68d2de17d49b33cb4e62ca4f8863aad8ad5c615d24bf86a0b1914616ce3eb92", + "0xfd554e6ee6d17467330b979b3d237aba39234ea0906f1ed7f1bfde4933d8ce8b", + "0x5c02af7cdb2961535e62b690ef0d0ee2fc48a15ab638091e67b2dbc4261a602a", + "0xd92b141cbaffb3a4722062c4858cb3d111d96755d1236cf9626c5ac25fbd1822", + "0xe33bcf942f4fd4bffd91751c94a1c51f442d3ceca8304a91b593de9cc733c035", + "0x74771806931eb18b05427c23ec15e90c4724ae212e7d00b1332377c5f06bbd69", + "0x1a1734ddb2f42b75a286197d32a462c6a6b14310deadd821fb47c8f0763e946d", + "0xe17d231ec588021f7f9a674181f7c0bf95f10cd724405f6b2b408d6ef9638dfd", + "0x0fc2c188c130df8aa5284d337f024d9e780d614cf64ac0c8aa0dd207f2410183", + "0x608dbd94987c1302e03e3cb76355d3616d897ac206951f7e10ce588dbe3252bf", + "0xa15cf75c95ead12c7779417390356728759125520edf391ed86199c61c830467", + "0x251deec1fb1fcc1cfd8a8bd72edb7f34a5b083daf3253f65e45a2bd05c6163fb", + "0xb52b118d1ff98688b6d9c2e0d7d5751cee96afcdbe39acdddbb1918d0e51d8f2", + "0x5542b753801a098808cdf4a1223757bbbb0a6590f9892efc805364d0ce6bdc84", + "0xc81029b6588945b5ddca6cc5883be0db764685edd3b833356e6ba501cb1c7ebd", + "0x3e188d2da80fe46ffdfb44aa38b0297c4e20e0593e6f79f99b92345bd801b6a1", + "0x3bd4bf44586f4d6845a80019d001e6e954875035c0ec4034dbf8151216f67760", + "0x520f433c699cd87340915cea348f79454c96f3fe1b363fc8abb1ea1f659e0b03", + "0xc794878dad4847f5cb1a226c7e7b12746923e83e0764fb9382e46c5ed2689997", + "0xf7fcae40691689251cd09298ee634ed5ed549f6bf80e9f878d6a01589b92d206", + "0x1d9ad73e3e4cdf6d01597209a90e9d7b61a8917f920725ae318168950623819d", + "0xea813406d76d2779ca6ab91252fc4682f032dbcc09ef8d6eed0bbfd6e0cb43e7", + "0x6d38c49864be15f462a0040d84f021a25b55f181dc899f0a16109fc9934081e7", + "0xa3efe91aedd723d9921daa75320a5b41910da8480b6effa24032e34b6f19dddc", + "0x7e5389189498ced97390fe44e59c40e814ea8a854a9817e617797a1b11f68e83", + "0xd2bdb8fffc4f2c1c4989abeb83aff0d3182a3e2128ed8fb4834a54b5045fc89d", + "0xa1c7a0695d9ba9d1878789a3e922c0ccda6675ffef3008998041a3a3c8ff34a0", + "0xc845b3689faef8626bb4a625e3486f79d6f2ebf969998ba3d7bd402cc8c17036", + "0x1d9836c535b61444bf56a7617cddb3781e927883a59e69458fe15a71d12eb75a", + "0x03c902c5789e3798a52cfd4e5f946d545596de8712a97169cf309a72409c9aec", + "0x8b0e1e30a1992e420748b3ccd6108deb38eb1b194aa08ca1cf81e841fba39c69", + "0x3e096684310d774f4c30c0df7a76ccf9c7156d5f8ae82bd7327e2e0deace981f", + "0x3272f187d2dfba31ad8ecd2f9138ac9f9b5cd84572e038d3c19cd45eaa64318a", + "0x9b77deb93d0958d7dfadc91d334a2cbd103e563dead60c363d1a6637186c8314", + "0x4bf581295d105f56704befd38e54b5fac61480cc4cfbd7dfed35ac4e00ae68a8", + "0x958da1898f50939f1ae93f23fe0d03a3802d42dad94fb9240213424522ee5bac", + "0x9055f49568fc7e352d9a2760f864400ed28804520b3d0e5055f3d5c5ef6fdc5b", + "0x478503e8b8856732fc14ce66ba70c4fe617b72e5ab8ffd43ff807c43e206ade1", + "0x94cf78097b2926696ead9d104ae6da4a33d0d2feec86a9451e768e9256e65771", + "0x9cd6da9d76430abd28df4e72d67d7d565c537968cb7d2dfb5c818b539e2409ab", + "0x3380f6a2fe96759f2bef8df1d035d99634f6cc0ce45433fc116a7aeb6c5f27ec", + "0x6d0625d4131497cca0810fb55308f092161a4729a79ab3596db27852992fb757", + "0x0e8284a4b6123ae7506d832c39bf65eee2fb4910bb761d6c7a53a05126bc7185", + "0x259ee6b08c1346c426c270eb9b27f2024c729ca08cb8654f6a687c19ca94bd22", + "0xd96e141216f10b8591c50aff63970f6eddce780031b083867e66582a66de9db4", + "0x1cc83c2eb434c7c4536ec26a27e1f03b885d5a4e9cb6f2de19ab5a702240a5ef", + "0xd194d43ccd04540e0cc37b1774b33dfd45ca521ea4bc9594c8664f7a10c10826", + "0x287d7e06d1128e5755fafc07687fb089a96b30f48a07783160818b8557bcd520", + "0x9d5509f5fdccf84807d969750640eda20f59780cc70c23e0d4bfbdf27ab47778", + "0xd8c0c18ea17c2eec884d5ef810ebb79cd9433c3db8a289399f551e609a4beec9", + "0xfd071d9973e15872dfd10876ee69917483308dce8c9b0b34eaaa3eb7e1d92dd7", + "0x4573d4fbb8cd70d159ee18e6cf7eeb09fea60148bdf1f3f59e1a730495ed8297", + "0x0b6499d4779c1c3ac4d70ad1cac8edd603284f824029b468f3fa24e6f8552f9a", + "0xd0cce3a6f01ee90411ed6a232935c87e56fa6c99b6f89944184e7e5002f4b3ed", + "0x01bcd63a2f4809a89b620037873f3e3a8d24a53c4a2d3e76439dc88fb300c672", + "0xc88047347a37dc6076bb843502e10d7f92de40c0bdbcef5457022736953d978d", + "0x681a6f28343e8539b852e59c7e7e562cbeb7bb68096dc82acc82da5e8f378e04", + "0xf89f2307490fe8d9e8c7967d4e9572bace6dea0ebd281218c95d0567299f7c46", + "0xffe3efa2769af0e512bdc6a52da3823c880efe99baf554c0e1cb572d2db247d7", + "0x01f24de5c8350f0ca540b047100e177f9fbac782904396e480f4617b62663e13", + "0x4dd8f069cfcbdd9b5046956d4759e52ec832e46ce403289c79490f7b055a83c1", + "0x58b53dfde471bcfa1d04449768d4f508fca1412ea567818d732cda6dce65a365", + "0xc1d48ad8c129f4ce78bcf3de1405a3addc98eccf322ccf00bebe73dd3c77eb89", + "0x6f5fe5ce38d07bd6fb85aad5137d7ce08568d6a12cabf17b2266320741925d29", + "0xbdb76ba8126f77bdce5f0ce9d3473b1b207e8aea1848d4069684eee10c36a347", + "0x8dbc2724ee37b8235453ec181362bbe340a0a19d4da17e697f1b2f49a5d98085", + "0xdf6633170c07242e8408c0f744aba52d578b629745758a602a9605e2c86ea42f", + "0xd43e63d56effc9dd376c4bf8b417c936fb4e025e95d9c8e0c156248b9158a7ae", + "0x302b03d760be56a6b6cd084b5d21d40ff752cc04f5b35baa1f63b351cdf6cb0c", + "0x5693a69a5617b59edc21535596848bb79ccddc3bd28d5b14cc17ef6411d8b7c3", + "0xe288498c51bcc64acf1c36d0f49000960f412c53ac0d7a11ede7b1518243fe76", + "0xb999942eec18a41af4fa05d81accd183a4e73545ec4fab5894e1b22c1cd11084", + "0xf86560824f1392b227bb83c5af852ee2f9c4e068ef148c7bfac9777f1ccee7fe", + "0xc8870a82f9d95156944cd281c474222337ec535980477076e7fbcac6ad269e1d", + "0x734ab4d6534b937df79cbb5f1fe9e99cfd2e3883bace3a915619fc60876786fd", + "0x51e4f3d654bbd1c6b5906c09bc31828286f57307cfc2a28d06890f6b39544374", + "0x57d30c51432db53373b89f8fd9a0633c1123cc5513595fd778788724a148159d", + "0xbadb72735109b15b82b4583210f78aec7b29f770a04095fd2fcaf4d79840b569", + "0x72affda03825e35bd0f1a82b93a004cdfed03a0d947fa0b153291e559eb6da16", + "0x21b4c612f8198eeec583418d1febd3d77d551ae05060995abf50ca24dfc3b22a", + "0xb43927cc4f50ed47566ae499a04eda3f689f72d1c4555f44eef44c5a65601fac", + "0xf928795f27deea8832c57b7bea9d4372a69e4ab356f13318fe2f988934f2d8b5", + "0x01ad0d542f7747cb041448cbb2916d42bb867405a2d60a9fc5611992fa8fccff", + "0x32abddcda204b63b4ef88c2299eeaf40146a84a04ce97fee988e37e5f8847ddd", + "0xcc07f58ce5c143fc0d55b3512bf569908b32f826cc8b28c9e3fffca694eaa518", + "0x537876e085467432ad244540f429b65fc9e2598b1e7d5bbeadee037b286e2ae2", + "0x2362e41f22e3482f0976ad4c050f4aab021b367a02f81e1a5362c2d3f454d070", + "0x1271c2381ecd5476cf4c38dd8556e855a670067fc10ca40f549b91058a503169", + "0x9f67a064fbdb054815e0eb67840784f175fc4747520811b720c7516db2755c6e", + "0xcd970ebaa2d5a492f0f7805f0611316d5dcbed38b239de74e90af770ed6ed58f", + "0x7940ae991191e4ad0c361da185749f742eb9a69797a0d171741998c9b0b6db74", + "0x2aa811a0820cf08a8c91d99ddb782332d2caf96162f365be20c7a170b511da53", + "0xfc41ba4dd50fd31f87fe5c787711633a40ceee1ed908b616dfec06757f15ade0", + "0x012200f7ddba9e66b17291e5607ba03228f12a2796d2b9aeb2d2606e5e8efd25", + "0xd6ded2c9133e4eddf5dd1a1759f3a66aee938bbd93126a53282164cc0ac4492f", + "0x85c2bd81ca0061ac85cbd1284e327bc3cdab8e4e2a24f73fe9215039f07e7690", + "0xf3c7a13cd1a6b55ffefb3b6f5f7e4f4db5f85060962c78ca78af11102dccd267", + "0x60ff57880891f85a94c6b99a62b6b392d67b4b10b1b4d963170ce69324f79d8a", + "0x6008dec8855bdb77e21c2b9de46a5f2413cdeff5b610e705360484069a90cad5", + "0x7d54577580547576d50cbeba0bbd649908e533b32d8cc175131ec001276e7919", + "0xdc69ebf1e6b18b18c64487deaf52ec6c0ced324b353a88d99909e718d7737abd", + "0x6e28bfbe8634037891d3f730c2dd2e06ef37dfccff2964d5301010e970145b7b", + "0x0c51725a352e4fc70b418d09180f3a6f0c90aa45cd15916df24ca9032a134e8d", + "0xd6cd643bce46fb2ff315555650a52fd672b887429a46f655daa12b72ec0431e6", + "0xb73a7919d8e318666b14cba40937672c7771424a2257f51be427881746cd4221", + "0x75bcd2d2670f22dbcf8c3ad2d1dc1fa08ebc7af4d79654f8e7df66f32fab95d4", + "0xb8bd110ba789483ef3bb2c03a651e29df592e911a551d4752b87901e2cbc2c8d", + "0xa8acbdede4dd24e5720803a6f7ae08a7a0e9c579529ae33525d42d200ac3c581", + "0x30427f3c62a25786273c00cfd2e54c8084563f0d4850b0f50b644c1305c57082", + "0x543cc4f9e2e1a674ca4a2fed9a919dcb688ca6d7eb0b1497acf969e79128d651", + "0x2e0b1fa4314ef1682c4af160c1fb9cc86f236316091a91f5974c82018a7b6482", + "0x7eccc73e3dba8cde203fe24bb78cf20cb1d510b54cf95720f910545ebc7c4351", + "0xcf9958407a6c306791fe163b0caa019663004e9dbcd8ae2083f10694c50c1f13", + "0xdba8e98065bbe7bd77ace099760000ad5a67e315e0f10778a1a9e598d5018682", + "0xc1d037ef272ad37e67c8db32fadd43122628b3f3ef3808800d9ab0d3ae4c2013", + "0xa584463d1f23b1cf6fb1c8493750a75acfd0354f17b170a203ec2261c4f2eea5", + "0x5527e54ce439c36e22c225eaef6a73a2dc64d4c179f74b7edc4e1ed62bbccf3d", + "0xd74dfa02d3bf008cca8364e5b41991e9a1ad835b20393f411703b188b2861da4", + "0x2232568a2c007aeb71432e65402d5feaee61c4d0396c902fe6b7a778003e046e", + "0xbce17edc71433ecf73f9fd27179074f53dc81635fdbcfe763fbdd2a947acea2f", + "0x3f94172c3b379825bcf48016cc682060cf13d423d286c5d49bc1a7a48b2252c2", + "0x7c4e3ebf32f126871417762b56c0f123069e04d1f656ef2be9d2b51efb2a073a", + "0x8feb0059b25abe602e4c6c1802220758bac6ed4d4e6e8febd2ada4c772efc8d4", + "0xe3e487cb869f9540e08a1522bf2ed4cc8e1223f215486a7055822b24ead79fd1", + "0x00349435919f4a82179646b638467ad6c5b2eb68f73a838702fe369f45e589e9", + "0x3db306e8bcafb58a4482e07793f1218fb8aaa676027b70d0aa59efddfa3d40d1", + "0xc05964948603febf47e77999ca6e07e629451be495c59f086e3558beadeddbb3", + "0xa5ae31f85854cea66bff281a7821ee7cb15a7408b23799aba49f2ece7f5c8116", + "0x8d121047138eb9ce8305755b088810e1644201fb0bed84df61350805172d1be7", + "0xe7e3a088f6b4bf1b7197600f52162ee664328429d9096ec1e16c8aaeadf47393", + "0xb9fc4f35ca55715ae4d3a72a03b63e2a0728a43cdb60b10eef27458575c7d876", + "0x58875004d7efd2d10d3cbe80d6913b820121c690962c0a6acaa03626eca6d606", + "0xfa16fff9e7700ffe818d97ef5f95c663f6b9e78b3cfd87f456e71210fa32e4ef", + "0xea88649a30c56ababd8acf5acd6b0ca89eaf2b08b553f11dc3ed683fdb7c25e9", + "0x300a905aba77a64f11f7c630728752360b16c77bd677e91cbc0c76cca6bfa832", + "0x9aa544dbc7e92802901a6487401356a7c8575ec52851891cc343ca66946c3048", + "0xaf27e1eb929654dfd1377ab4731f07bbc5bde3f282efce0e9780c7cb110c6730", + "0x1ae758f463fd2e7066ec1efb6a30d9f7b1f4ab7745d6c7c3866a4da3382871cf", + "0xb93e1061c6bf1232a08651defba0f6a74adacc4dfc06d7a5617832f2fb063c49", + "0xbf42cf787bfe0945ee536efdc3ee79e280adc52f435d6bf73ad734d049bdd1d4", + "0x33aed555f0392b32ebe731466159a1da2245658d34fef2e91de5494067687185", + "0x0df534e3a4e26120161f0bdbe6bdbaf2578e15ef9afcb8f0bd2c77d1aaed382c", + "0xb719160f1fa8aaad46c1f8ba59cbc5608d26eb2a967aa6fd1b8dba1c5cbff6b0", + "0x566ed709b2878ce5b21b76f0432bbd51592e87b2c03c9694759fdedb5e54a383", + "0x64869b29a9835355cc11784cf2a5a36073d2ca9ecae380a3a98c0a1c375b3fbe", + "0xb84e34e59ea91734d761880514a2465552cd22c0cf609a0be2f6e9a59ea1d608", + "0xef0758ef8e5099071365bc302f2f2c3cc2512127bffaf61a9e14ce5d3ebd87d2", + "0xb9b07fc24a734c06f34e4b59977a1555011e5de62725550845700b71daeb1764", + "0xbde03bd964854d6cbeab1685351b5e7761f3b8ca00d72b1b882b966bc412b189", + "0x87c3e218c1e32425393449ae6e905d395326f7fd732d4896cdd96067c393b2d8", + "0x4d2a0bb1b24930a577a2043e832fd8bcf41bff4f4e3dfadd914b0f1d47ec5d5c", + "0x7233808d6a770c21eb88ec5b8f23dc7943f953baed7fbfd848f9d44fa6fd9473", + "0x3bdad90d7f6690ef5a2b1cb82d49efc942db1e0973ca010762e928060fef8c21", + "0xa73bf0bab12a4b2396f3e1aa6ea4eb4f5c91896640d0f7003dae7074db4f546f", + "0x85caf536ac23683cbcdb62b7a8afdf27003b3bcef9c87f34ac5083c8c93517cc", + "0x055a1b8d0a1683b74764537fa7aabf6c38737f1c7565cccb62eb327886b24007", + "0x1c342d1380d21a28d51f7432bec68af2e748ab4923e192c5944ce0f4deb15a98", + "0xd89c85cb7593e6a683f8462f71f6d67dd1a096ecd6df94982e401471ce7216d6", + "0xb283996db559b1d1a412b6433caf15b5b2da89c59022c9693fdf00c5b14c1ce9", + "0x3df5ffd65e0e48c91cedac7f4540ddf36812ac9452c99a1ee6903ad437bcd51e", + "0x706b815ec7b676595883935015146cf89d76fd8eb345d48caf62b2f73d29c259", + "0x0698c123dffe17333cabeb724c64c208c59730edd95e0e7bddf9d21f36182ce0", + "0x7a860a9a44ae8f311a2853329718541cd0449f35a159cf6ad0d08f0e0c6208ad", + "0x009881e8f5ab776343e56a395fdb57615329a50f1f33837abe7a7d3218b84f7f", + "0x9f65f12d90a2c6c733d7787dbbc062babe01ac56387e31b3470b87cf8707e441", + "0x24b36b85bb2034f9e3484f63430fa4c93568a42246dd66346d25609aece2fd68", + "0xa88b060d816f778156e58d7e4026395e9f6ec4a123b637ceb2ebb3b1a705e8e1", + "0x48f1f5e1ac14d754fe265a838e6d93f9fe12339d075d34c50d90d36fbc9e2896", + "0xd5c4b8cd33d5d8291c5ff41089b17d1e0f4d8fceba902916e170be11b166dbc2", + "0x7f21bad67700d4c97a23438c4e936d5eabc5ec2f7d07954f5ce21fe92dcd1beb", + "0x4dc67280c6079c6502e32f4577a453fde92ee057cc0b62cb8ac8fb73d8fb271a", + "0x61d4375e6eafb5584dce53c1e548e38edacb839a529aa3dbf1090b2bb405d8bc", + "0xdf91b2eef9b866a405a2f7394ebfe2ee2f00cdcde72e6a170945864c1a214b8f", + "0x5164ee819e3b2fd1759b70615e8809a5d810b88e57834e8090d7d829d3bb53c9", + "0xa4cbb9690b2a5b567334377f6f6a68b0cf873f32cb444136529283830591e882", + "0xdecf1ec1edb48f5bf5626db7eb11c9cca3893d89e233b9fed9e729b2b57901a2", + "0x1c473d270bd51ad9a5d0fec82f027e6c2b025e87908c0aa573b7c39ffcc25932", + "0x26ec2347ae3ec1b94088f1b229d9d8cc1ddcd959a7ddf8cf00172f1188fd6cfe", + "0xe06ae8d5d99f0d455050fb05b63798377b4917cb44edd21681ee3d5fc00b8466", + "0x7babacf3bbdd78ba3cb5e5e0f097df3b04c7db9b0367cc0d86e8b0da68d39a59", + "0x6cf61c7ad2f7f651f398c7a112ade338cab05e5632bb7e2a3c71b2013359cbe3", + "0x367e7096a56d1af593d4df31bbffb8a8e638d7346a1cb1104e5eb33f5f4c61ab", + "0x24658514d9615de0081f73e6c031e09d6910f6a40c767ce1d716b8a349099393", + "0xb8497d57d57fffa8c652f1a86266cf6177a86c7728d5c345a4ecfd3982e8bdbd", + "0x91aea709b5a8d078e05223fb86b2bc47cacd08c7c3b380f6d1f952e053f20295", + "0x91975cbb195c7b225e9d2b15b7d66256f09f2f7a93b1ff548e684c449215e07e", + "0xca49497fc3882b3de4ed7ecb1c2fcc9e78c9f7620b216f15bbf181fb69177e55", + "0xf70076c5a7c4b7ccfebbb7c1ab37e69534f57ec03e1b58492acc0f308f8ee6ed", + "0xa393b19365ad3a75c553b1d0d34b55ca10dc27a4b5f9e8f6e1cfd9ffec2fceb8", + "0x44367b97a2912afc9dd53c78d0bbe25253c58573c85534b686e453a9333b6552", + "0x102b865186280699352d1806914a3d8c1a5263f0abef2f081d792ee349d2adf1", + "0x5528fbbffc25fb00600b4f349bec9bd1d07962744e53f49079798e6411343ca8", + "0x4752564e3e41b454364523b0d85576138f4b23c1c680906baa26fba3bdd660c0", + "0x476a4bdb91d7fc2a2e506df485bc795337712e85687aef1728001ab689d71104", + "0x9045f7f2dbc6c72d50e7bc960724428ff59ed9613c45263420ce36a716a2f573", + "0x98b386c2a23e27ee0760961f909e1183af8ad372a8c6616a388137b2b9d89322", + "0xc741f96b66b7b9a3f41445499d9b891eb3e0abde8f40a94a83a5513fa387ff15", + "0xcce443eec5928b6b6c735d33c6dd051e2a0b866170c80d26596e566fede040fd", + "0x2c9e2709005d13cc14caebd99395dfc4ab7b42fd04a118d66ec8830855d6289c", + "0x4bc751548d254a78d7c18e2a6cb7e75e4d81eecbfd54d5b80c3e6f1879e8d5ca", + "0xe4fdbc51ec442d722acb2abc7dbfd1a862aa0b4ae6a7bbffa95718939b037b19", + "0x794afc9cf8581b051d711645c238ba995a3ea40b92860437b931b835a8287348", + "0x124624e93b7286b59b67eef8f0bc1dc9b8501dc1adf438bc17a8cfd377dd4a76", + "0x4aa22642556fde4ef776eb936ad3ad37f714532564ea3d6a4195fba7e713b900", + "0xc8bdc790ccdeb88982a396739ff63469a906e53f6d9af644dc65891446692ca8", + "0xc0bbc7e1a6ce7a214150b30e14f601df2a89949643440fda8b87e0265b7c0d65", + "0x99a99d87d6acad518bff0a84f6c41c1e12e272c79755c039d9530923aa497206", + "0xfbf0a65f031a548370578dceaaf1948e8b25abb60ecffb0f76490bbe37cf8663", + "0x8dd21f365c29d2e030cfe55a20058b92d1d9753dfb52c9c5050bcb3f27b8fadf", + "0x977be929cc7ce9cb29e3556c5f7faf67f36a2d59e675d0384140e86aa2c8a9a0", + "0x5463e23c5c4fdcf711c1287fa7802453002cafe8316c320f907c85681d9665c2", + "0x955876f735be04db06445964d45bf1befe5074850fb5f3daf41a1136a6838779", + "0xa7c2d3ffff0d0b53a05e3eeb86c5e7dd4d2a70e0e2166ef39033970b2887013c", + "0x1a48d519fb92aafe8790b481833c06fae6da07face7961e83aaa668bdaaa98d7", + "0x2d8df0ac094f8c54a418f2f1e815360d3b716d63b59e27d6f09ea3b0ba000d79", + "0xdc4af12dc3cf7ea709634af5a7adb002f75c21cf9493be1b9494133b802da774", + "0x85ce83e6149928abef1b1ce06800dd08df3941e453e5c8e872ff9219a0655fde", + "0xe930628752553cf7078cedce46b8a2b1aa9fa7d20350a1a421cf1bdde9b9ea65", + "0xc976508201d6152a9003ab663a68d6ca2c44a55adc01d0beb5ae464374d63663", + "0x8b506222542efbdc08293cfd9e287395453c780934f486b47c049485d50ec158", + "0xe5178a6f8fb7dc223d642b276a9a3f8587e29299345f352b739561b7ad4b7a43", + "0x208f52d678bdec2c030570e84a184bba9a473dbea3d905c7d57e1fc06760b3db", + "0x384369116a2c8176d299c510b3dababf2b7dd99917ccee03c02f54765384627c", + "0x8172da0cbdcbfbda4f7283abbc34c09268bd77d0d6e767327250acbf8ec6448b", + "0x39c641c03316d418c8f05950ea294e41815c02c42a36629e5567f7586be86a94", + "0x108091bff4fba77fb8e80f4c6c57aafa3e1ae44047f7a894d5c279d5d96502cc", + "0x8ed77a8dcf8dcdc08b905a8bc6a8046694a54b55c97a17617356081bea2ffccc", + "0xa69f45faa925632e2fba91729cee22322026dce8c80dba79f3b031555f2540c4", + "0x601d39dda4c600f4d21d285c92f7672fa02cc64ee553ab8cf8125f2bacc9fc92", + "0x7b8fcdde09ff19af22ffbfe173a34f1ea2b5b2852ee4d1aa6735ebf200b907c0", + "0xfe53a6aab2bfb6dc6ae096fa7f463314e2c76bedb466196243df534c690bdacd", + "0x3b16cbbdd2cad46003d48b1b77338e280e9ff6563f470da7a1d9d75419420abe", + "0x8b32b92156afcf2b68a75cfb720abd3a888543a7c5b388f94c1a4c8b8eacca99", + "0x1f4fd9a76d3bdce5b0bee9522f97785b7cfa090d543a647a3f3471b52b3aa414", + "0x2f989c327ac6cbe81720856de50dadebfccf56e39c8290b13826e5e43b3a3581", + "0x30ae668cbfa3d88011f7c3562ff2212a22280066a0d94eb1a146265049a69a66", + "0xf46469a65d8f3ea268a303e4db8bb5878da8e8569b3360dd8a1ac0a3e44229b0", + "0x08b79e09523393b54c461e3db6b16c9c277e4ea09aa09f6a7d1087fc5ae2281d", + "0xa55b9e489132907dfd9938b0b0762526b49e74861e7fe6cc111d5753926622eb", + "0xc46d9494c64282b845dfee62e9d334eb3927f1e709f917b8a359b86bfdf13fde", + "0x202480ac69ca8ecba1e7734d3385676d3f6c44b1e6b9c5cf3259f781abc19e45", + "0xb4e7545f8da492edbbc03042b24d6e7a2f156b9f93d53917c879809a33415d71", + "0x3991e8b53c5598e5a44cfe7be93812fa0683ab4a3b04946830a95ff0c1e4b51f", + "0x60fc6d9eedc8384a8999c5bc7d4a62a3016a3947ce87a7902a5e68faee432a86", + "0xf76bbe5bb1fdbf0d2f63e65399aecdacc5bc487d0a464c61f295cd75806db179", + "0x275a8cccb855247cb71b4e62075603269c248ab960591419e1701ebc76929224", + "0xfac4272d15e901b5b5df676cb52ba67146f065d69176b17a621062f739b9aa59", + "0x2d8d551e44ac83f2b1a8bc84ea2e62829f672915f261a9479cac543201191588", + "0x5f62923f54f1db8bf759ce66450a263d814ad8b8f1a7e86ad6f23329f194f792", + "0xc596cdc35708837b3f63c98905478b7c66e1ef56b9f70e751f3a34898f219743", + "0x923981636605c1ef182c48e67ce7b1fc0ea0cbeb60e91a7af6ab31e1d7ce1c08", + "0xb238d2ecc9e5c91a732068157eae07bb8ba4cb44008dc8c124338a91b2745ccf", + "0xd6b83b054ffbca2bf3065a8095e28b96083979424fa8e52fca12cb1800e17978", + "0x6e63ca54b9c0d852059b4758547048e967af90fd4fca975205475a99b2fad0dd", + "0x122da2675acd2a6dd0d0b0d7a3a49805ad24ee8f99f18fbcaad3bab32e84cae5", + "0x734bff35f49f254a1c2d77f4dbb926b504818a272b6a297d6d6da9c9abb67f4c", + "0x6a6af00eded0492cfe37bedd80b66a013b903bfce8cb4b8e3d3c3c9bcf3c198f", + "0x25f287fe333715da42e0390429085abd39ba4508b293f172e2a880c7f1bb715b", + "0x08a43370609f46164b201068e837858437d12812f584d6e2aee89cd551ff3ed3", + "0x5437ea058728fdd00c55eae4b1389548a2f0f76e3fc3542382533fcbd6b08d2c", + "0xc2c5250051bc06af9aba2aaae9f15d969bd5eca5ee69ed554f1fe2acfcf43901", + "0xa379a0a5eb424c8e6fe9de43b7f90b5148a42f7dd6e4da78537e0680357819d7", + "0x618b1ec9f9d49c97c3fd58a98244532ed0672a8f7a64df9abb54877932cb9fb8", + "0x4a1aeadb8ec53d1c62d971c8bdc6c614d66871bf80f87e7fb567fa5c43eb206e", + "0xfaa6691d8952cf2365999dc19e9b0855fc0212aca9d139e9ffd5c4f59daa5ad1", + "0x13e1280b96c450d1a7d3c08ab55376676cb62b753dcc88a9387468a4ff84209d", + "0x4cda7eb953db15fdb57bc333acec960ff4b46a4c07e8e9018cc61de8665052af", + "0x3cf7e44c3c2832cc72770e7e9181356a1d937cb1a7bd3169bd641028acc8d624", + "0x55ceb1d515ad4a4732a948938951c734b54e9eb6b5585a9033a667d91762088b", + "0x238b3170e14e7a1df4fc969a01b8a21e4f573b4339face9b8dc92649a454dbe8", + "0xf294ba864fe5c80c70b35a37edfca5c2b7727aff40a35b405a73dd2d8d4d3e82", + "0x2ae039206ee114f5faa0fcbae7d56eedadc1c46cf1ab11094b1ccef13c85c974", + "0x09e2e7bd519bb16f80ff47705278e8198542f57a1133f0ccfc0cee9eaffdf4f7", + "0x1798bcae0432d185cd2f083f77f0ffb08fe1a7542db9c37855e574e0a7a4feb9", + "0x77332d8ffcb5663e220830aea06e790cd387903802294d7c54e7126f7d4826cf", + "0xe5bfc1117e758e8bd3bcb4b43ddaca70c8cc8556035b6673ce99afcc398274c1", + "0xebeb4b91b2161792f191c9aca8053d5e667c8e01cc3125c6fe0535bcc5117d80", + "0x9d120d3576f85cac4d99d41ba308fb6f0b2010bafd01e91636d11292269fca4e", + "0xc1698c4adc9496f2ead2708f5660af48a31d14f6ae95021eec77fac95e616605", + "0x2322bccb24a12afdaf6478a2ab9278682fe3d08d2a480ec3708ff3f7d2033316", + "0x783a9c0a1f5283e04a1e0f59361d9a7333cb25e3884d091200d1323c593e1c96", + "0x62298867ef52decbb96bb82c50d6af0c9fe34f48cab244b5b5a58b3c7a4a4941", + "0xb590ab9f0d91923d3bb1fb897fe9f2de845fc98654c7695b7bc83200f02e30f1", + "0xc8965f6902b2102e0ac68f95ad593208b539fbc13dd6e7dae9a23a3d9c7deeb3", + "0x8580348f408bc2c2d5d09878e003e9faf20b23c4b18ab85d027a8037e38ad0a6", + "0x76d56159a675e1ae1a21146c08481e68fd1b19dd2b21d8c371497fecb12ae201", + "0x3373f7e9ea34e126b325e6f59ff34fca1c5cc01a9e840352a88193604e5783cf", + "0x2205bf77f9bae6869e08cbcb8467e52d4a529dec14223a494098b34e8c28d87e", + "0x839fffc4786df07fbee0191b42b368f2d8f9c2d1449e68e4036748b1718ad2ca", + "0xd9ccd596be3fb8d02db2dec5e94616602234783a9b1afacfa02903495be048d3", + "0xa6a43aa8b53e0d4d7f256b35ae88b79ae43e77c3a7ae6025b291cbf3baebfcc7", + "0x153bce65ccda07eee105af92ad599f8470920a4c0fcfc0501120a40afeb9f22e", + "0x125a97fc5bf347fddae9413c167a7e1dcacfb1546847f9a1f53185c70dc36fd4", + "0xebe66516bf583f5f93141ec247b12db3c072284d2de7755a30802d0cffa15bde", + "0x14ede71983397bab9bd1005ef705ed08333fdcec6909a40f097daab907fce57c", + "0x9d3cfbc5422b026e97f9ab4d4b10ecd357e913d61e4094858cb39f9c62dd1f15", + "0x05e73efe9aa6752ca131e74a3155728403e8a207d662c414680704f206d099c0", + "0x876929d34bd3acc0c1045157f497ddb39890acc59dd0bdb091b1174102f6a59e", + "0x145cf855716dbba8e0a63d554a9a8dde44c509a894499c11c13b77ed7ae9d4d5", + "0x02e5551f0bcab45b16675efb08534cb96ea5a4e1984e6a6eab84fea577e368d8", + "0x2dd2f3daf7d57384ba1f87926c36a7b77bb9b5b3cb33dda61c5dfe4649abaa37", + "0xae74859adac24862e25ce32145f9de02b3defc8d55163841c06dfa41456a08cd", + "0x48dffaa8922b6f2e018d30665bca3f335b04ff3999c3d6007185eb7b8dcbade0", + "0x99218df1a3b73ba12d93a3678f9bf8adf2602a72f5e86b2e96ebd6cba1c53578", + "0x2f20c6921517f5958b92a8cd2dcce0607b25899f24c32db0a8c5889771ad1f80", + "0x4267b3ac24b8a761163bf06ab57ed0c6bca33f91f78b83c7d48b41b574652817", + "0x1a7b21320ed08374ca8c9d15c99b1a8cfc2150b4330b3e72556edf3574b3346c", + "0x58398f4ee79267a484424fe33e4a9f9882b0ba18d5dd759ee3df6cce8127cabd", + "0x1f90f79851458ddbf4fa5678a40acff17f07ab24da8af86d74959fc8ef846729", + "0xcc5e703bbbcbce4b9161bc0096b144350a777610e0f4b47ec06373358edd8f21", + "0x4b66910a9e09125851f74197f92adb2d3289e7e58c512779b37893ce8ea4764f", + "0x7e742c3feb57d491f1b2180a7573d20d4d8bc8273071e58de65d79484fcb5526", + "0x9b0b620588260d8441317b006f5ad43ae59fdd93b5598e4575fe9e8d91c5b8b9", + "0x447469cf7e38fa4b70287a153a30878ffa1aa645dcc985ddfd255c57994cde06", + "0xbafbbfbcc7f8decb90c856722a0d55b302dd76c7b213736db18d510bacfceb09", + "0xc07f4659adc053d55041ff40e4b60d156398f02cc0e2667e85bc85d2434829d9", + "0xe4edc136227da6b8618c3c4c58643e768eac91d0be40467c00790550e85779d8", + "0x48529e1178a6b5b87bec83f1e996a803caefbc4e77f06139eb597af51f74ca5f", + "0xa0596b75f6800d0449676532cd2cb9d72b67a3c1d256960d680ea73fb59620ff", + "0x55795a39c82c94929731b058f55a378a4d34ff44a3836d1a41381891a391b3c9", + "0x9bcd36f946807de999864db0f0b3f6f65e3ba2ab377b45b871dbe196a1e846f7", + "0xdd2490f4322679c08f96270846c4ccb0193ee198b342342d098793aee0d43e2b", + "0x8a1ad2e1c18680c35c3fce3457c7cf5a3d4ce02bfdb0ff22815463cb6320b1f4", + "0x2f024851f854a8284a79f0d7087fd35fa52bbc10c1321564e7c9d65b1cc86425", + "0x99ad5ba7d0e3ba85ded75748ac406c3cef8a3c3931897ab97c620479e476c2f2", + "0xe3788f3f50ceb33ee27331fdd02534c3cb79ae89fe9470bb6d84fbf41f6c2bb6", + "0x7522fb91f8e3d581fe297828a5a56307345eb59d5efc79b5ca9d01f60de8ec54", + "0xc3fc3b2dd90bf4de6013e6c9fe0d3cff69b4ea414845dae37f94ef137f25dbe6", + "0x8c5199c294cbc6ba26cf68ee0f638b7a6d316d2a22634607071e2519b438baa0", + "0xb19c3d83c0a81407a94a403a9e56b6fc8c9c861b21ba8fe8321ea1c22717d234", + "0xac6e4bac9e09b209ee8c4fc2c1dbceb1bb7b29d41a8e637adae26b54cf6cf461", + "0x3530b7736cb227a07180a35f9540ba39f90750c84c4e9a2ff806dc20587e099a", + "0xc3eff93d57e49e5ae83f15f7d8c43ca9306888915b9aac09d43410cd35195bba", + "0x57d8eb4cb8f0a56ef07702577c7e04faad83e7e29826c98b45e0d477e204b321", + "0xc9cbe2953a8802907e22b6f7a93950c8c378db5e40e429a24d7c95ae7cca8952", + "0x8eb8bb1be4fcf2d0a0d26a8a07d71d3ad1b8d18d8686abf228fe647d69f6bf41", + "0xb2e1529815ea4ac961db30ced949470449f14a6674abd61fbf4037c9e12f9162", + "0x911b7cefaabb9e4dae91e08f48c8ea762b6d2765220ba124161202a0e1b0f549", + "0x36d3e0eeb1d8c3ba33ee8a3f5911214c994522dfe697aec8e162793207bb9b70", + "0x4e9cf2a2112668eb729061c0e7d7357a9c9e30ef6356e4bf2f05131243eff8d7", + "0x7ff62609b05cd0528b11fc805a172192a0a161a9c7100602dc3ebbe4aade567e", + "0xa1ca4bcbda6c2cda20c624b191315612b6b682cd6e834e03230a239a91a41b8e", + "0xffa416dee32ad401fa279a90c331ec8710723285fcd1e2d55781f2663beac511", + "0x64652d5b8035649aba59624ed5089180dc0eee9d20e265cebe25a39fc79a3376", + "0x48205ef59931b2affc994592b06b0dc151a2654b8001554fa4f46db06e57e0ab", + "0xd3b121338ffdcef382d442c4ae5d939eb6f264c7bf3a91281c053237e9030087", + "0x8cdd9fcdd050803100f4380f697c04e03435f270a04683ba3fe9e67323a74f35", + "0x82a1010ea47d5078bc88e6b0c625a494f07de6c065be3e88980b40adf6fecf85", + "0xc08ddf3d1c95d2ffbe33c5ccf1eff45b1949d598dc11b512fcdcbce76142d1fb", + "0xa6aeaf8570220b1f16c9e490b76857699716870abd2a567e27f4fcf45db46197", + "0x921d502f84e3c4b5133e75ed6ca3e56129b42262159435b8648f16858bd17753", + "0x6afc5564170d40b256d9eafddac23792f9091d27150d5760574f5bc9313770b2", + "0x04bed21b7a71d6f4a30fa14f1720a3968d7e631092ec4f45d3db55d1db15e16a", + "0xf0049625fe9664a4da4a851bc93d5118cc83bc9a196a7e25953e3285ab6d7f26", + "0x440600825cd9a873a150dff8d404a602739819a2372ce84a4683c14f07260f3f", + "0x5c1d1c206eb47271bad14ef48376abc481837e7612873ef090b0ab297817cb46", + "0x07fd121c26f5df1d12ab17237297e96f70dd1ad0c01fd85df9c73a75878068cd", + "0x322e4b280b0338e3acd72895d508b19b6fe5117201cb3a680fb78e2374bec569", + "0x214a12df153b673e118712ade35355523d64c8835e201c3b7f30308191640c27", + "0xfac10a15163104cedd44ca6d27c47a750fa77907accff2f152e9d240604996d6", + "0x5025f0214a85859ad783a23d9199c4fed9106b352fb0a8a61557712be83076ee", + "0x82e35bf914e6d56745e7a8bbbbb98aa1d8eaa269c7dbb82093eb05eedb9f826b", + "0x0dc4ddb5e1b28c795698bedf03d90fb1f3a81569a00149283e92690f2246bf4a", + "0x3185f9d6aabd0aa344c53ac14b757dd95503262d96ad4277a27abcc590f1afc2", + "0x308bfcaf8fd088dc7ddc982a66b6fab625087fb8751c3fccca4f98cd7484fb01", + "0x4de2e5997085cd0712bf116734a01955ee0729453093bfc03efef472cd466b17", + "0x4ecab34696c93c2e0af96f18997e627ec389b09ec8ae0fd07ce090b39a086cfc", + "0x431fd9bb0d252ddda802ef98c4169ec97cab540b662cd83be703ff7c981dcfd2", + "0x7d9288793644ede3a84625dc03f26977c414e7870d4974f7bca2a9d1264aef7a", + "0x03bd1206999d3aaeacb89f3b22263b59f884a8cacd8d73558b4b9e9303b87f86", + "0x6f6beb624e7bcd0b65ba21ecc6987c42e308156d68ae443a6fcf8ab9c2690fd9", + "0xc8e5a3184c23700dc177af24cb349167eaa50657855c20fa70c381f0aecef59a", + "0x742542bcba7cf84847380fa297fad6def27c29564309ae299be2ca2a9a7c8d21", + "0x9227312d3bd2e3975460af0517be71f08357e1a075dff50b09fac196d0684026", + "0xcfd57214219581dcc82cd64ab6ead138f0c724df4d9fdf7bf431cb0b4f3fd6ee", + "0x46614aad7b27dfba6ee046558a3e2d113065ef44d9749b09105b873fe0503adb", + "0x8b53fcba980162eff878b5d494abdbcb10b322e1c61960051e936a84eb759c02", + "0x2a0842bb846985cd7894961bdaa768a61bb451cf6b788053af77feee2d94447f", + "0xb2c1c7ccf59fd8b099aa2c8281dc656da6a32e42036d51dcc7c58c720b59c03c", + "0x8e44d36041d923307ff105fdb23338d4f885d76e2a16e15185f865a10f5bb497", + "0x9b300225992407d914cbc9e3b09ba2fd3b1a2646242e2af0c3c4744435b619ac", + "0x77f2e2aea7c0ff9464a1193f7e34d4744053c22761d763c4763b33211d3fe74c", + "0x6f620d1fa720c7e96e9d901f09bbecfd7cab1aa4103b91e4b69bb9af5eff9a11", + "0x4857fcca9f8124874b6aaef47705f289d6bd546e08ff03e6dbfe951398d23aba", + "0xf5c59825ac82789b9d18addc6c55ceb793067478b015d87ddd125e6d8ecf49d8", + "0xef4976a51e12f1070d85ff29793e4d2d006b995f9e9bfee4b158bf6ce3a652f6", + "0x52e9c828a1ac65cde2ae495a7ef977ad8073281b556ea3e194e49f13138502ea", + "0x1b558464966232015761d705d00cea8f757d6488857de892fc3a5ac1ef056002", + "0x86bea583d4506fbac64a9315a460ff863f51afac00f0c663f9b88bbd2e6a146d", + "0x28219b20afa70928b78c7dda450406d8a27d3a851237fc9f18ce9f12f8d31cd3", + "0xc9cd39e3e44e6d95d68112dc4c23cc44dc99a70559f916f3df2e88be6dd76b37", + "0x63e0a6b411979c56475251c7d8fcf63aba09ed7da5b60af29b066236fabb9239", + "0x9d046795eff6c32fdf09523eed13ee76cd94e839540b557e285e28b18b577248", + "0x83f48ee0ecd00e54a2eab7142d00f908da9a45f56567aefcf098c5178b5b3518", + "0xdd2120b7b8200adc3c8dd9e8b9ebcefc163de43fd331b130f68daf3c3e519021", + "0x91bb911968dfea7d5486b93dec88cac8922b1223a92da77270a1b720f26317c7", + "0x5c3df3e41dc2c3a3c6d053113ec689f731a321b86efd4fa20c068522e16ec133", + "0x72dbb6ce52f866d5ba82f0213142898ac6c23877b4f0c8f7a07b011777dd095b", + "0x7c550da138c85192e44a6fde55610453ad4812fee4ac74f848a6ee51c2081fa6", + "0xb5fb71c5a4df1facae2422b8b57284c9f3d94acbbd066d45da32d5cc5e84ed33", + "0x40e093b923016d8db74cf5ae1d436a17d367651af7cfc63638f8fa87b7a74f3e", + "0x80e6f0ae744796d60d7a72a10314d6d0434f9b98872c7b3ccd1cfa704a3bfaa7", + "0x2bc680d9e262c1f66df6c04e0840604f7392d89516177cee458686b5255df5dd", + "0xfe8a1505e3b6c761ae869681daccc78d26dd0f53bf0e4ad49f8074346052aa12", + "0xc8513effb54de5112063ad412813a98601127eb60ca655422c2ee082978f62f4", + "0xd5dfa0653c81eb65e800164bd6be40d52083f6411b2080e208148b5ea7c1dc1e", + "0xcf90fcdf11ab1ae55783fc62079f67966bb37451f532c7b7ed8c1127222cfbf1", + "0x27d8d78a3d140f883ffcb6e00fc241a4095af43a9834a239454c7fd27a8ed32d", + "0x4d00a7cdc7367b0c317824e370a77ca464c981531951fabe52d9d8969fd29f5c", + "0x6a2c9eba3bda0016a88cfccaf9a3bf4d8e4cc22945fa3617c39e5d20b14982ac", + "0xe244613a1b188245f9b654fa3499b4c4307da8d85dfc4f08773f4e6245ff05d5", + "0xd185d732bbf848874c478ab0ed0c6fbdd6da9204b9b1f549a712b5cc6b8efab2", + "0x378b353b52dc556a600fd56a4265e255beea6920c9a54b6bd9ce7074fe68a7dd", + "0xd25d088f38e71f23cc4f67fcc09669a1abcde4a2a8f237d410a2fa9ceab64e68", + "0x11bdce4918a0f4cf7fb24f29e03b01167fcc71daf17195fa2e5487a717b34d28", + "0x9a56d85dbaa1fb86db4614b4209b29c612616073395242c1dc1b3bf81448693b", + "0x3ff53fc61997fd331b8a736a19ddf25adfba5bc147132c03f770870337561cfa", + "0x327951d5a3bbc0cbbf26fc50a8efccdefdf742b9a6dd28bb939d2cae829754e2", + "0xd9dacee96f7536f9698b9bf7f33dbfb764110bfa97f4fb680142ce81905ac908", + "0xd74553b00da5bd9b928052d015d91eacb9e2d7b31404c9491032ad96ba9cda86", + "0x701f0f98166081ae0a2323e5402594c71903d2fcb5faffb7cc13141d687bcd17", + "0x66f81365508c91c00c4db2e6056f2bc4c1d3252f6d543010846b21c2c7fae145", + "0x968a11345b61b7065858430bcbc2e0f57d102d828781b6642932bdcd389df73e", + "0xa59617623493928cb6efaed020eebae0b99f70d07b687d156095e682d1770b07", + "0x3be563fbf89bccd8f6e5d10e2dbd90eff00192b4ab38af9b53a9d028c13721d0", + "0x36117691d4d3782051ab2e7f748ea489c0b8c89d9474b94053574c556add2bc6", + "0xb81aee54f20ddfa8cd3eda81c1d3f505be2136cf5d2f869bf61082552cde4c0f", + "0xb7f893979fd3f357fce2c4618e9d94e18ad4261edd4ab052ff90d180101f1a73", + "0xd282315a7d976f0bb396098730d064f9e4ee5cf0e4bc61f4c3b4a85424406fa4", + "0x540320b8028d8f89a6fd854f145d01d9d43709360377debeab78bbf83d3ed3a6", + "0x39ba849af862c44188b8c67d7ac6f8bd7fecead8f0ac53c10d077bb3958d8a06", + "0x3dab83bc87a90747169a70da42f63a4284ad234dd50a025e4b14fc26f359e737", + "0x0d32d849c8da0b6d73f4e0b884ebc9250e5a9af43aff5ae04c98f6fe912a093e", + "0x6d748e58d0d469ceed627c288c006e003a938b332f6caa270dd1d23b3b00386f", + "0x4f116e836a2b1224f3876127a3866ba3e324846b3f1eb7e749ce153f6e1026fa", + "0x70ee99989fa4380f52d1866387e577fbb5228f0dc8006307643dfc349f21d43e", + "0x7982dcfc54ec2bf35d108b6fee6a7c7e418f3964aadf2583cbb3e5a994f39fd6", + "0x5570aa9dfc1527aef3c611e954767f0f72dd67ae852eb15bddfa2fadd4237c93", + "0x2635c84fa0a811e14b3d7294056aa86956aa800ff917f6dd1f5273f127096241", + "0xc6e7b8b157dfb516434d99e77b7567aacd7c4faaaa0a3de61c399cb5362e5bce", + "0x89693f8145f9d1a7c884b4c2085513806568bbe7a7e2af2c5cbb2d18526e0ab1", + "0x1d0639e0b3971948d382c88e4d213c765db6068c11b5cc905585b12f2d7da5f1", + "0x94f185f554409116f966b891341aeb8009258bcb288b6501f61622268a6fd2fe", + "0x819c1f10ee0aae7663fa0eee8d78047d92af649b3065453d37d8dc71c6f70651", + "0xb9204a9c71c4792f3c2a0627cb73f34096401ce3d1cb8bbad4038fc01b9e09a3", + "0x224f256695c36969eca15dcdd8e58c702b26f1e35a367e0bef7eecea65150e42", + "0x0f86b0894bc9f5d29b4892efb76a4cba068e5e7a110b934f3a23f5c45c3beeb6", + "0x278f1dd3f6762b68db6f28f142e0e410e329f653b30351176924a8871e085cb4", + "0x528c93aaa3e3043123ba6939df541395a9586255f9e393874abe7dbd2f175bd0", + "0xe6e6f237a83aaa504b4baf76b25747c56a5af9bf7e42cec03c19ced3c400b0b7", + "0x4f118a1e9eeb4f474d29aa17dd6313c89253cfe5c8cc368ebfac2be1bcfadc55", + "0x9a9f72b6b677017d18dcc182b2e1ea92b3f7128d8c59089a29f14b5838a6dfb7", + "0x437620f9e180109b4f05ab4427fb9ca670398203325e1bab4084bd5c6a83d21d", + "0x6dd3ba0e077a35f484cc2a66c775c628bd085aa4c692d2e7460fb26123209664", + "0xe119bfb6e40891ea06f6d0f76b6ee6d498498088a9eefa2cdf4ae5b2e09d54dc", + "0x31d2a0694b28272ee129bbfd127cf7d4ff6f7ee4318accad4052176844c12dcc", + "0x09d221aaa454a654592ba9fef940cc9c2fa81bb06ab767ecdde647c9dea05577", + "0xddd98fc2e42ea0cca3b5ca24adfb1481e7db954bcbc676d69d8cdb3b74eae86e", + "0xf293a2c6c1a58071050ad7e5b260c679ed89c9cec1c758a633555d2f22402c19", + "0x3e020f033ed15b652919dc5b5eda5ee1c83e9b4d9c0d641bfe2ffaec97e9cad2", + "0x8939a8c37e396528a0d0be6c5e3549b59f4b8add2e68785c4df698616146040d", + "0xda7d7ee1ccd444b65d840985bf2f7b1e2fae00438cbf4e25499ba878c766e06f", + "0x26abe5bc61ba118089052e089013f82420ce183b18c94d0303562a4da4069af4", + "0x7800aa28f1050a259e0d751b8f832f501d9c0d2bade9f078264d8176da807571", + "0x0716b85cafbd3f40f56b93b85b3be3d3bdd8e6ee899ef79bf06292810a7284ef", + "0x7c444d04a9b6e62314eb30dfead43c13129c1e0d5bd4cdd0f5c0bbca3c98eed7", + "0x47f27974b72882ee7b2b03696c65604ae302c88d07284af6815d7ee080ac5083", + "0xcd96979d9b4c83bd8a38f7a2815f40f5d818d1cb9a0a2d8379e6a65461db799a", + "0x43869b5c5b83d93aee1b8503d81ca3e1387fb23e0f8d9dcf2b118a28a37dd855", + "0x1ccc653e94edeb039d0ff0ee35f749529e48bbf624976849e2f8d66ae7534fdf", + "0x3d92ca2bf4aa4d779b214f71adcfd9c65a2012153c678f8d1ae877c3c8f59a2a", + "0xd8ff214ee2c2b294f135cacb9a31801a234f1469f8203c0dc7587d79d49b05f5", + "0x115ceeb2761b823a5385f4da78d0bc3854fd261fcc25eaa57402389f98f9c478", + "0xfe7c26264a2c8c2264c209afcfa20b0239bdf68a506adb2c177548b09ed92350", + "0xb4f43d330b18c516ba4d33dd59468ae206eeec1a7fd2124fc8a9f3eb2e36104e", + "0x33972d9c5fb30577e22aa3228ad50f646af140712fe86d2e711f8f55940636d1", + "0x25a39ea25c88e432604cd304886bd22c9a80a6ca224917cb3b2e5f788b95ac5c", + "0xe54d8b45f958552f31fb4eb64e6a4ca4e11b226d26dfabf979b881e2304e29d7", + "0x3b80bf9dac6cf94bda7e44177daa50a3b621365c9d067d0a05a7c9dbaf8ba582", + "0xf2e912f023256123c48c07c40bb644bfff531dd4c5ee74348533c8b7554d74a6", + "0x907e0f9c58b18a47b50bae7e446e14ec7ec3285ae1e8a959c650433a01c0604b", + "0x086b286070ab2027c97fe3a89323b65c553bee6a2071d4c3e300b5f62b745c5d", + "0x191f5fce7f12dd3960723b28893ace37b30d5ee9806572d749879d6ed2fc058e", + "0x5ec2261ce89328481596e196450885ecb64edae42976337b4162cb554a824079", + "0xef4758b88454ee440b5c8fe0808c3a71ed290a1c233d6984a4209858b28db79f", + "0xf8926f91037c5880821d69e78c7d9af683a9ac3369f2d7497b489e44ff55c91b", + "0xb20d90a9a32a823d6934f0238f37939877610404fd55c741351204f6d7d82e1b", + "0xb30befb20d28260198f0f9a7ba0aeff8f6f358485c433b5b6faa96e7e7fab1cd", + "0x48249b42ae8afea6b33956f496c958894d1527efd6a4d430f669ff88c4a0b0bb", + "0x7314d77c8ed1ca5bad906626b1ce19f1f56e02b4133d4b385a0cab7036adeeae", + "0x0e5a185d2620c099380766891f717a0c8162b141fb1aa0c7cfbf45c3a320941d", + "0x2c580a9372e1e7ae59566d7e3b5d43b5a64bc266c286f811883760b713766a10", + "0x1e71cbe9280314c0cfaaa41aead8c7596ab224d89d746de042f3e4aa0fb3cd26", + "0x60edcfacae18bfe191c20772184dabdcc941029ee3cf9671b7ffc4bd356f0128", + "0xb1c943b0980d4d0066ef5c39c18241aab091e2771e11858fa8ce4384994887ea", + "0x11decd89cf7a4efc6ff3c33e63192c0be19eef9e08d19aa02f6ddf30ce21bc57", + "0x637d86589f0f7d30cd2da78605893ff3bd1608e2ffc29153622a1bf3b58b5dbd", + "0x92e5fa56600c06a945973a01a1a858b3f0a734b29238f209e8e4befee74a2efa", + "0x613d387b3eb2c3dbe5d53863602c26c8fe505b20eec5e80a563007118d362ef4", + "0xb7ae60963965f68247d817f45d2046ef30b426764492f16a5a705b751752f777", + "0x4e5c82467290cebab50ce45a3840456a5e72a9e65acc22e999ef1fa8b4ce5636", + "0x817245255ab08278598677b5a1836cf3d52c3cce5d14279e105db317e35601b3", + "0x76a615a05c325e79b341723fa976789993d54f5dfd1d91289d9f3cc38e532011", + "0x05165af7583f69f4cd8bea37221d7fd457ca09d31e84e57e090ba8695d107b6e", + "0x7f8c8e6068945c66fd323218c622eb979a3be17904312108907893c53918d198", + "0xe952ce23ae57c503a90324cd621224f52f5077a54954767b886d7ea800d038c9", + "0x7306f6546e707834ca1c8a85de761ff9d2850e1ae466c152a61fa08b97451042", + "0x024cd1e0b1454edc2966ab5bb218869a48892e215b345f672bcc7cf809e534bd", + "0xdc1d1a60a17ca5b1cfdaa3a53a06f4496e38ab20b0ec959be607806407a448bb", + "0x255f5530fef72596842f48d5ae6fd8187c6aba265b8c3be007a94898c9813583", + "0xd6f50559c1a95777af1d7b521282d25cff490c35b07134897647168b468f4dad", + "0xe6b5a9b6fe20bd1d2464dc7dc670aa1684ea765ac9936725bdc94570bb4e6e8e", + "0xfea9eaaeef0f3c1ac8face8e29f7c7d1ed0c33679c28c8e1f2dff56e0c57d01a", + "0x1cd75232b5158dc107252645a4fde684a0e2cd5859a0c15f675f11bde61d3b51", + "0x2657aa7b4c9ff6d6ee86e0103b41f49972e89468014414236a331a57c710c4e5", + "0x00a6eab6d45159e3f8fcfc3569d90692bb10b55a227c1d703b5ff7d6b6e8e435", + "0x285f05e10143c552a9522eed3a432f5c2bf485b67397c4663e4a2e7c1dd45f7f", + "0x83e93050bde5ae22d21265e4d20eb2fb666fd7c477db7591ea3e7085deea804c", + "0x4ae58f3db94c30bb2dc29d6ce067f85ce49adf0585556ec85ac4c197603d679c", + "0xfc125ee61c3071d3d2659d74bef6c12460811194ab81e336ef55e638455c28a9", + "0x7026648fe9fdc07bcadf0bf89fc886c99d0a50264301ef88c84d7a93aa21ee05", + "0xf71304f2f5a4a459cf40ac723a66216b1c94e2734413be2a78d5daf66acbc813", + "0x8e7b9be9f3a78c07465c5c309f32a56380e06c3890f64c5e71d3743797e375c0", + "0x22a4d5deddbab0fd5f2f10d57dfa98af94c9593ffa79eb18d40a9fdede80b5c9", + "0xe1fdfa63cc6f03a5f6250e47fc0a9b0e162698865f7819f004d31a6507ed1584", + "0xff986bea7e1f567a7b41bfe3595e07543ef32ce7ddfdf2af5b6dcb371a3d90a7", + "0x6446fe52806010831b90a12a915436c622865f96743ffb5450ea62167f4e4b12", + "0xf52b97052417c66520dcec3d278c240661a29b0b2b44ff94cee73701f0c8eeb4", + "0x3f6b44897ec4d667779f0893265a82370433644c6a4895c8c2c3dde9dfe41259", + "0x72eb89f49ff8c83d0a956721210ce1d1770f7a4eead03c141dbc1f99df4618f6", + "0x54b35b85b6b105af912024c518fa3a4c55441ec3c20c27a60e1835b8c8ee8309", + "0x39eadf17e790022f80e7023c125d9f96beb2296ac6f2c5f039c833efae4ce0a9", + "0xd6c871681c9dd29cee8d165c3ec61a009301e2defdebf65dd3fca0fb0208a334", + "0xb9a55841d12505ff3e44f26f93a6dc9a48fe3fff32853a89cfdf9e64f64a9cd2", + "0xd834ad64d86e8010572fd078d2f0097c9ad20f4304ea20de53cb97ffc2002683", + "0x7996d24d0b96a4576934f19ec2c920167cfcdc122a056d2a617f5038e435d963", + "0x6dd969d988fdddeee462ec0bdaf3aaab7e6fa03db0c33687fcb238d8463712eb", + "0xe0893779e8b9644ea2d59b7f5a7851d507b58f627b7f2fc68d091d2f62d599c1", + "0x7bac1a141b29eb0bb812a9a226162c174e66f451a5bacc0db87467fd484c029d", + "0xef23fc3dc1ee06adf4300bb8ee0b7260aec8c39d2ff8d8e2148d41322b837d55", + "0x9f8c8d1dce1c57a6b37429eb2144b0572fed6d86bbb7e08b8a73723c428d60a3", + "0x4e66c7a59b2102fc627a3da51743daef77ff1b396863a2695b071c48f1eff73e", + "0x4c80ef9b6bbe8bcd25bd69cf33e40dd749e0a52ed9e4302d59d7d47e7a018eeb", + "0x58a683da1d9ff415faf835faee3b3eaf6c7bea2b15cc826a1c5ac24c88c3ea9a", + "0x26eee398b4423ca9d66ebd2db26dd3f1f2626c05d7458c900680e63ca65f16e0", + "0xbeeae718af41fd5d89136340524310099b5400834221448eb0573c2d6f467780", + "0xb98d71bd92881d23125086848a39f68e83395b82b536705d05eab94fa2b63b7d", + "0xe0de5788398019e80ca8ec3bf2501bbe7d2e9abd4d98aba1d02401b7738bd65b", + "0x65282fa3b41ddf8c8b903e390f97ae0d1a193f7250aefd5c1703841c31e8338b", + "0x5e2fcd56f825fe901ea431454f41d927af687d43d4f36a403aa6fc48a2f11b13", + "0xe3779867e6d181eb5288f56b95ee300ffc7e917102c382cfb43c04ea725403fe", + "0x48c9dec9c00f9cdc5280507a69b3dfb23b1a8335aec357b22d75b19b83689580", + "0x1543e6e262b4198061eb3c5d225db0e0b82b310d112e35b04d5db55d6a804e61", + "0x9725d938df4d12160d82a3971cf9374ee15e4da31ca2c4ed231c4efc1f6881a4", + "0x97aab0402b0bd44f4cc6d7bb5ecb241aa72d656992b9059d8106de3f44c409ac", + "0x7dd6d308a12666a29abba9c2737a757a0cd3e8330f1aed9073f58ec36c05ed49", + "0x9fe92cf398cc87ccbc485da9ba13e93313e38f4e37048efcb81ce8a88f628643", + "0xf3b402881d27346547aab25a3a939b6d032382268620d80857e67ce709e36c92", + "0xb17aeb68efdcfa9b5ce91c1560515bf7da636a4f04bb969ab22114d090b64e13", + "0x306df394c67f79405d8a8e08f65a82e9fd90c5b91f60deb7c6699258eb9b12a2", + "0x348f26540cc6b142ed78089be569cf14960e2cd1bb74ab7852cc93b25c0de7af", + "0x7e1d5bd7cb4d5018da968be5fd1072766919687b7e68a6623b6209ab0e9137bd", + "0x41f0b72de4e8e82afb6ae8073667ed175dee4f732f66e0eca422051b6d259186", + "0x4f543dd8aa23ce083705629d13cbc011fcec54a32e3e8c717f33d1f578571155", + "0x0ae537c16c50d3cef77ae76799bbc4f5f9db14ea365271d9d00cc9fc03acff47", + "0xe996eacd2961c589cf328ddb0ef23f343a5effa4b35bec717a2069931fc237aa", + "0x69793d8809d6c162b36114ffe8a5df9ef6a87845de7639a7b1b6def6fe454ef9", + "0x1092599b2ddbfb362145346a81da7c78449a7531593cb6181dcf75e47e53f2c6", + "0x15cc227f7043b460d43796f4445fcf0e966b3eb69af3409d9d1c030bb19e369a", + "0x2fc923e6674547812c8844ceb32ee70c9a1f91218199e87808169b6ef8e3859e", + "0x6b30f1a1f48667c970988cde0997b69cae28d5dc1af85bdc1311dea94959b1a2", + "0xb7536f280fa63b9c5a304fa12802bfd2f61c09cdccf1c98c0462c920b56521fb", + "0x1981cdb14f723db0e92d0a3df37f432f3ca705ad8ed85953569b92377453f0aa", + "0xe50b6e73e2c5dfe658ed3530276778f80b454caf618afddafb52a23cf966aaee", + "0x6eaac005aed668b1bc931e3b88466698ad389a013737ecf08ddaaf5c8346c19d", + "0xb0765e5252cced99624427bbb2cd6f8a4fec4ede5f5b38a88e5950fcbf5d888b", + "0x15c21d0238312e2e1ea45f744e418caae542cad422965f65861c6b181aa90e43", + "0x12237f4acbbb7daee2e3b7c2f225c3edbd5c9af83b3ff686dc6a5f19025daed8", + "0x845d91c94ee9ddcf16246314bfbbbe6286947c361b0976b21788422d2a60c445", + "0xb1792fa09f8beae74f84469d4478e910e45ed54724cccaff5fe2496d157981bf", + "0x3c92d6ab027387231b8d27fd36441688cf7812f71cbcb9d4378d9a94859ea6bc", + "0x7b5c7d9c1db7de9605b1aa24f2ae78635655f25411d4b579c550c0543deb6e11", + "0x49980e9c7c896927738e066dcf60f555d511b1afcf4b6824c2a83da17dd4341a", + "0x6eeb8fce3e27344d9d12b92806337d68e8a214524c818a495f63b672bb385d45", + "0xcccb1092af25754d50c35aae32a20baefec09c53efd874434302575d5c6c7cf3", + "0x39068973da63c2d13db5ce073af73b712b37e15dfefc04f3a57a4991a5df7edb", + "0xc1b876e17965e5a3e001b663ea56de870e3acaf2cf6ce29f6071166561916315", + "0x6f53adbf23a29cda211c3a8f10260250f91d22bdf0e38f594e5b66c5ccaf2901", + "0x00dde3ed08859b0ce7ec4733fab082a36719c1719c9aeb098f65fa0d9bdb1429", + "0x3de709a293af34b7694af066c0d8a1fe6fa7011bf9c4e697573f4530850b2f37", + "0xfb59ef65eedaf3791990b01f644e517176dedc97f4c4767f561c1d0cea50eeae", + "0x46b3b2fd5a10132986d71286de3134b165b50674cc5c0da884cdbfb9a02e8bb0", + "0x51337dc97490c559759fccd30dc04a21426371f8b14c824f319c5224755881af", + "0x574c20f222015a319cfc1ccd374277344b2876d7bb74de46a24839c49474b6fa", + "0x4aabcee355bfa40d806935f7c031e717f8f2b9e9aea8abd4825445cadbc53684", + "0xe4d283152ba75b3250bb8d4287a265586a3d8bfc64afbdc5cd468612b4cfdab4", + "0xe806f881dbf54dc2f2fd0f0ed199d6c94f279eac28b51f0c069300d72f7df2af", + "0xc88832a41e9b59f93287256089e1bdb2a1509dc0667db2ab285480eb17cc555e", + "0x3eb4fbb1a82bc62eaf09ea37159bed2e06f3bf9fe62652e771289703dba4500f", + "0x416fc053a0abfd1a848567f1c6f4a63bd630240887a8201b9fcc6a80cc9c360c", + "0xef3ab3317338540d2f28c8b6efd3471df0c8ee563de38f51c90252b23d113804", + "0xd293c978e0289b7cca95b7b9e658c1530cf792cbcf10e456c8458c5b11509a00", + "0x3ede6da605ab4d83acbbe5ec861f6294c4a8546ba30abd1020e101f4637e9f31", + "0x8eef9c96a170f395d79c423ed85e15d35d229b2de5966d52a6ed2c63d4e17ca8", + "0xa9c067befaf210b1d393ee4b7f23945cb199206591accb49f7ded2151580366a", + "0xf4fda3c04be7c5abf47e66a90e240f68fef8369310cf0a0c84d5bd50a01acfc0", + "0x288543d583b654f390e2ac1dcd1f52ff4aa34653b031230d1b4dbb12732f1fb4", + "0xc43d614493dfbae0813514e25756556c534e353a535f84744ce5f621b5bde402", + "0x47fccf2121ee0a2ae963655c767567d713049d37b8aa7e38c4d4dd38807c5c53", + "0x076a4606992cb3906101a5c7d2f107c78628c1f27d1dd5bda21c58bdc122b355", + "0xc907d4790f07f664829dc0c284fcfa8d318c782fc53fa6cd34ac4869813facc7", + "0x7b8d487460ee20ac76a9313a7d905c44f40e8c9553b3d0094d449326dec0c417", + "0xeb9298c871610b9887e3fd737ec4e2b63dbe5b118e4471567b8f9ac7d963419b", + "0x2b42920f560710a80c13e960e9084b4481293f289f3f2aae30b664fe5d2264fa", + "0x4b41fdcc6458da6fa72075a40c472213dd2c14b05a59d814464810e0555ad8f6", + "0xc49126d8df9cfe6fb7a008eff37246e4ef944eb7ba2e1defca8eecb74b957cf5", + "0xf6f174aa7ced3845a13cd6007974c9c312c7b2af8403f708f32aac12c40fe85d", + "0x6b403fdc7893205ba507d245510c60c0b719137b549f8ec13c3b57dd82acc9db", + "0xdcf3f5da9c0765b6cac8dcd64b5ec44d470ac947ee7894138595e065dad232f7", + "0xae8707d773cdb5766a1f38b98fac32d007fd23a511a9a2b6da1409595d6b573c", + "0x80f2207c478cc43a4b28a45cca49b14557a7173ef6655c419a65207ff06a2ebe", + "0x1e94c221ff206d34a7fa72fe8cd0ea9e86f2362d5d6df00a23c7913f70769fa8", + "0x7483ae417ab071a145333dd59801fff74d5e463b6733559a884a8461ff477180", + "0x2e53e7596534342edab3b7082e480db0fa5ff949c2a594029d8618d6599a11be", + "0x3e4eb73f7b28d6aea246308b9b5d5e82a1717fd1a792ecf45e9819460ea040da", + "0x62773314b3e8ac2119d06cd57384ee10ea3814fe2cb61ba9cadccd897188d6aa", + "0x5829914690fec9ce442710947e13dc5e37f6baa0a4619801488128d1e6ceedf9", + "0x8f11d2aca664fbdad41bca968158e020f85cd66606c57fd0155968b2e8eb3e04", + "0xfddc68a787385055d2ed6e156569ad5a59349036fff07611442d1dd580dc4edc", + "0xb4e94a6b13dd7e8b9ed9cabf5a90512a35874fd42b5d8500ef4b68f2d11694a1", + "0x124be49a0182ec4f2f3d2a7451ccf1ae86249716bc654ab731405ac2b83ff86b", + "0xae8903821bb8865d0fbc9dc30205abd38efd2153890aae7ef6142627b6ed87b2", + "0xb6e1aafaa0a73627f146d0379ac73745da50655505c3454bcd412e9dd0b182e5", + "0x0226db64654930fc1f7f00668f9fcdf01eeb8905e27fe3c0064e2dde7c9d040c", + "0xaede6d90b74e10a67037cff68178f398bdf055fe1b4ec03f1b7d09a70aaa6acf", + "0x33f53f5973acc4e7f5eb6b706f433472438e1febdbe1adb7ac3476af4364c679", + "0xe8ef7e0bb462abe2b6bb4c209ae5b74712e5ca1f603fd3d9c5e8b0d1ebca3788", + "0xf730f4af6f3f7864e06adea1bbcea08726fb306800ca0ce371be1292483b524e", + "0x499b64880ee90be5a5e54d04224befd811f1cd721d6d3af7eb9b9975b31b6e5e", + "0xbce41c8176ed984e84e8e607ae5bdd832d753fb3eb7af27a0ff96ac88f6f8724", + "0xa79d7aaf76e7067014dc5c99253c51ca2c7da07f2937c20e0b913bd8a89dcce9", + "0xe052c89ccda4cfb19722d8a18f7e4d711c06631bf20a6160bf2c37e12404192e", + "0x460d88461dbec4e928d86755b12d6d9849d3e9e38a3643774e2477e6176c6e5d", + "0x3405efa61ec84b0445ae08e97d1718d307adad0cbbe4649fbac15e3df2e5e604", + "0xbfe196dec613a5f05015ca0e779459978d4c2355d1f31e42ab1bb42592a4e01c", + "0x691e979967595d197c6d2cd4e57905ba9ba18d97be6c225045a78994a6728cb1", + "0x7c8ac5e6911ba9db3728158c3aa82d37067c62a0e3188299d166aeb3e015fab2", + "0x1fda6aef22d7685a8cc9f80b8fcd05e84b60952b937994f2152aa1f4cb9d5dc8", + "0x0601700111075375198ebadaf2eb5f322e57eaa16f91d83e8d93a823639921a6", + "0xc3ba9a347649cca9fc606601805b81b326b7150b420a8ca18ec1832fe9475273", + "0x940a3ea74da168aeca7c04866fca18262db6722bcb5d952cef86bb082da04b19", + "0xded34ec3efa8763a275be8d2e47f2564ebe7e81493983dffce23a70af7d2e495", + "0x55f3268e4fca563215baf8abed5d6ddfd96919dc63f920da3d60f459473e35c1", + "0x7fb03205c42194aee03cc059db0afa064f77b813b1a2d5ea9ff58d162677855f", + "0xf1f4460c1940566daeb569edc6460a17cb9a163e70581ad8c4f3dd8e61f75308", + "0x56ad9f13a552b1a7a70bbeb27f83ecb398fbb3740843b0ae09273f93dfdb7aa0", + "0xe161b44ce32e3041ebed801192c79bbd8a13079ac9427cfa840d46aa9588cbc8", + "0xa9196ebf68703e9711fd75b0d656d4951a83866ebcd526c014fa2ccb9c01f089", + "0xe6c23e60f04fdf886fefa3209223aa91ab6025b68c4595bc9b3c62daa44e5ff9", + "0x598eb8244bd278d87bb4a040861d3f3830f98be8f318a8d95158be1bcf81aed9", + "0xc256bdc3f962bb79ee1959be259f1e8569c61fbd7cda079f32b83f1bec0f38ab", + "0x074b10bbf04dba282cd5da3176abb3e7b6186bb8dd36189d15e05d2c6358a7ca", + "0x84315ce6a94da202ee15940edfede35df949fba57d5c91ae8dbaf24e07f432ae", + "0x11f322c016e6b33b94946dc58d1ff78517b8418ec7d094034728b11acd05604a", + "0x1ec99e8f71515b9f641dbb8f8b09e39be25c9d2fdeb2cd01c0d97735f19e025d", + "0x571f45f587b5c3ea199e89c3a20932ed1e1ac4a4436602401eb4e4b73961f259", + "0xd9b1277738633012b4e70068fd995407bd76f70633654256b2bc21b1088b2d3c", + "0xfce93aaa97814da77c32a4e4d50f5962cfb713f242fa8ae30e6cb648cf089be7", + "0xfce2dd0779ea7a7b9afa34fc42a67152f038426a030504c5d2f68dc2227dcfdb", + "0x47842f0aed46a17a9ea4e482a3d028643d5a6fb96b7b2a1b25662b5757c323c3", + "0x7f027c183f424c5d0cd39e560aa6700817c26da759c60d8fd27cd0f797c16165", + "0x66932997977b69859817246f9c5a8cf985040d7ae542500e37f93ea9bbb3d8b4", + "0xf20b0801416a97c77f3db562258512b9909d3dd4095d0f4bc95b2569a5c16e73", + "0xbe72318670c103645499b571cf83d9dbc90c346edf7398d280ddf03af1c3c026", + "0xe8d01c9f03ced7540a2b9ed8c1d42465ac81330a00d54c7c0df34412fdd56f07", + "0xe04c0df759450c5c638e5486f15f679b06b7e41631537b2664d2313cff97b576", + "0x0d8f737d2fbad89c64c42a4a3ba6befe6eaf557789121b0f067e5489fb96415d", + "0x3e6b9aa74c5f2497cfa80edffb868af819b3ae9efc747b4484c06d4361536261", + "0xfa6e473ee3e0844655f5e054371cabef3db76f63622c5e13dbc1047da4caebe9", + "0x649c6afef17e1b41a34a50aa894e5446349e6e0eb1a17389ba8103177d668315", + "0x6091717036f7580a2a7b9783c802622c13bcaf15958ec1ea8897cc5db7c82de0", + "0x140375898490fe80a2786b74769a3344a0e7c8992d697667721d47a66611d527", + "0x39e7c28f24947453a2c89cb73a3b69fe736504c57bf7c0528cadd49716b13446", + "0xeec257b61fc85de8898021f3b8ad5105126c7e243f2d62a0478fd52b9a46d891", + "0xea5527b5c882fb52493896c8b95fc09cdc17f8a4a57959b773cc37403d9c17c7", + "0x1e4e65d9944dbc53f541a891527fe2de3a5f8434859115ba0b8054d2eaac3738", + "0x97c33f5d6b5401f4341a7f3610fce60f40dc48c5cf3fddf37009657d096835f3", + "0x73663e86f67959a25081b5224b55d3183f83bb8c85bc285e8258f45423a7c88b", + "0x545d3c2a139e10cad7df63956de23b7f9ed5a91d9bfa3316a4c86d337787d49a", + "0xb00861356841fae7207f6e763e3f5de4658c4787fcbc95ce06abdd0825360530", + "0xa1f020c914aeb3d733583618847facf89935e495ffaa452ebeb0b6a41296cf02", + "0xb242ba2ac6a7ad4f6e8557ffa42526a822cf32734d6195742204a90032e377fc", + "0xe121d2632852a24723c8fa3bbf4a8fae94a9cf06385baac0f009cb8552baa4ff", + "0x2a7a80b8f5a91ef228ffbdfb2071fed8188802c667780f30268ee2b455a9fa5f", + "0xdecc49700c125043867d628e3ec3bfb58a89e9afe0dbce0647b06a69cec6173c", + "0xae371fa4b5cc6996fab650e3b3fe3c1fd1efd4eb624c35afe31ed30dfb10869f", + "0x79aa8d019b06a980ebb4c2aff7d738012a6887a8d9dbfdbde74ccf9e19f73b97", + "0x9709143341d633a523e238c98748d8d9a748182d3299b154e0d7ee60aecc70e8", + "0x633561920a22682693fa9a7aac9b8ba8b525d7ef8bc7365711856619f6eaeee5", + "0x49c26805d35cc3944d72dbe8d178ca613c5be6393d410c57a4a5dba19240f6c0", + "0xb43e5c91c87376178fece063d5b698250f06cf84691496a1a673c4bd65aa909d", + "0x9682465a41d91dc778ac82651ab6f3d9cc9b8d35afaaa79921b2d8dee7e72be2", + "0xe26372087bbbd57d5088b71e3e34ce256d5f63a713684e37d25f361e0c3e28a3", + "0xe649dc71a6cdf03108d4b4a2fc9fddec4c177685acc036833e5288a7dc6d8f92", + "0xeb2f39335a80cd4cd255ece575d30b3af3f8ddd0ce0b08abd637e4f8701aec3f", + "0xb4cb6cf250ba57de9423a4e1a250215708124722f05cde66089ce7501404494f", + "0x7b8e52e4da278c47bc09eedd03d4dc8ac23d8e030b465e5717c2c2c94f9f4334", + "0x1412cc1b9184fb1e3d1affcb6a2eb68368c77478e55b6aa29da22eeeee4022ce", + "0x72646121dc5f99be24d30f440721612d476e32a0d3a3923c8b58a20c30cf4017", + "0xe0ec45e149e1914c82559edf56fdc45bb299b1dde86a9088e07d1139a3186480", + "0x9d6b6be456aab981a682d190a392a4f3daf24f9906bd2eb6c38cdcbbe807f8ac", + "0x3372d1b19926c0fee89faa5e70cc44f0ec3d6ac8205adf09babbce95c904635e", + "0x4d812c538510f897fcc370df3b115b9fe567d63a91dafd17e76e30b4fbe5a99f", + "0xf580d16d230656b9ba26a8a03f41b3456e1fc8c0ee04b70014d8699a8d00c498", + "0xe4da1f028fb657f2a13cd439b42935b23bda7e66b9b1ae4336b6dae3937ebd10", + "0x8ccdee4da4f274a34d4885dcd95d4211a0a9848ccb78a0ae3b3a1e593f2e6466", + "0xf4bbc2d2207995cb2c8b0c8bb21679d2bb955374fad5a080f0af1e4d430e4f59", + "0x970f9cf3ad65714ec27b79fd55129a323f858a242e167f8c85ad9192f24a8730", + "0xbf1c03dcaa76ea8d7df4b02a8e72ec5cc71e70d8a2c6c98b47f259fdad50d8d3", + "0xdb9d1444cb4bd1e74ab3067ef1ddac27a55badcc0fee95e96a5a7a4cc2c27967", + "0x3ea29600df37347fe6368c787242ed52c42e8a951e8a99db18b146598a102785", + "0x48710541e3a59a9bc3b5f0732000041874fd3add9060c3efc2726f0483126ec4", + "0xc61ce600792ed6c3ba09a859fc75378419155a99d327e13b322831129294a9f9", + "0x7bb9a26d12325b312d71daf57ac180212988092be6fdb678778ab257859bdd23", + "0xc894c0d04acf92d5f5647a4bb7c254afe92b0826d144d23f20da326aab3914d8", + "0xce071a26127274bffbed112741a09077c38c6c7ecb57d3ada04cab5aa6bd9f81", + "0xad8b0bbf01e050d494809ccf85f75823416b587c1d4565de4a4b202bc741d508", + "0x272710bb06cb673ce2fb7ce43020ae419c4901a37be6d7648e8c56cfc08a624d", + "0xe6e8bfd948748df6a1babdad1d2a473618bd867451e3b36e0a150033351b40e1", + "0xded1523f1f44243eff2327b12260f157652e92b451f5b525af7a00395179e803", + "0x515e190fc10e6c73e80e976ab21b8ad6c56400076b7bcf6bd360c1732fa9bb6f", + "0x7011f84ba760ef6ea9dcd7e24793bdf8cb150676f91810895377cf3781edf06e", + "0xc63bdccf708dc297993a2189071ced5a7d1469957c4a6e142eb70f2c899ec599", + "0xf3e705adb744e0380577e1fda1866fcd47ffb2fa6054d251aee008c79e93dc59", + "0x269c6c647a5d366cbbf2ec4b0fe04bc1b78ca0fde7049d357cfcd35c79bd70bb", + "0x869f01c7e3b73531b7498b1c5b176f9dd60f15eb25440a24bb7df9e05ab3f65f", + "0x85e881b0a05ff4f1fe23a80c9b661789874702bcee60698dd6584bb3da3a927d", + "0xf62a67693d37a4ec97143634658af0e3d8d3971ff06378f58de07390d755421b", + "0x35fb02bb44876959dcf335453ae5674973d09ff158a8da0da7d8869d94098b01", + "0x4dcd47faaff3a91f43c9088c268e46d372f15c79eb0c2b95cc1b049f9d288344", + "0xe5e5755ccaa949c2e626b821d96de9ce4d6742df1fd3ea0ea6b8c27f6525d4eb", + "0x57209f0f1e7812b50a4f61b05c7d8edd4a4e292772ab893fb640445cfaa7ba16", + "0x8cce99ccbe1b613d446789d6eeaff644e238f6b6724fddfbbb39f809cee701a3", + "0x8249cd360823bfa1c7cde356db18c76554ae1afad2b49dfdb34d2d16990ef585", + "0x7fffb4d5c17d677c83cfcb8c9192750b0012fec50b1e11005feafad2e41ac147", + "0x83d7853348624add4cc9931b5b2ec8e468ff2b2bc31851f31cdd02e34526579a", + "0x960005d3f934d9a5957f35f82b1423d3e59717825427f57712e7689e792819b8", + "0xaf7fb91c1b81767a539006b5417567d557aa47eae76497a0260307a04accc2ab", + "0x43a2ea7666c229d61a75d75816e48d3e9a1d348e02964e40460aecaffce805d7", + "0x6f723f6dba9947a1fc81ab1b12f6f104f30ecb8c2374d421190f9e6f10bb76e3", + "0xd1fca647b5acbc4a9da12b0e48e7595e10eda8d234e6d6e007818121fc9e946b", + "0xde4f7eef0ae691b889d5067675b56db4dada1589bdbd4bbc4a011f32fd29d06f", + "0x4149a0e805483833cdbd752cebcea56e573731368e685d158789300f1b72b9a9", + "0x4251a3c77e28abd21a987f3b1f2272195558e1d96f34dd85f3afe45fd3ac42fe", + "0x3d195fea006d72cd8434878e40cf0ad48c0c6bde784dc6d60e95fe9ccf02e748", + "0xb76e3e6af2bd79404c3219b91749ec5ca826d614effa3bbab2aed39aa53875f9", + "0x2bb3bfbe5f6301542f29a06529c119a4364df93dadd22e4736dd4e51101c9a43", + "0x9607ba71148543e358dc1e63e2bcfa5160900ff05f10bbc7ad0f9cf1d7b29d57", + "0xd533de2bf2fbd6ba674fd42f53aa4d309106c761811c7c8f98af8fa19eec7c03", + "0xda1f15d2d6b1d507fc2d80b5145bffea86b72cde5207357ac9681bb9a8ba53db", + "0xc00d2680ce50d0badfcb903856ee6c4b4bd36b83a6a6f11fd50e4bfc6faab29b", + "0xd47fc8c5908e2fc33346f0e607690b8fb328ac9e4fccc569e962a16253e202ab", + "0x177ae6d13b9e3fd713645a5da7865207ace363e677c36ed9a57e58a46dd5206f", + "0xd5fecf1f8fb6255bcbcdf6e94a997d6b81149c85a792d8ad1fb6b6d353981b74", + "0xc30d865d5c805d149523b1dffa192486b47467b4ddac8d9366e845cd911e8fb3", + "0xd3e91ac0ee3793182db53147127b83d82f2e9b4e2114d70ac7e2f1aefbfb8316", + "0x502c5b0e6301928bf2aac0f7f888994b305732faae4972b5e466deb3ab4ac29f", + "0x379e82dc0fe5413b8ee3f5fd6fb88be81d648186514eb3e647eef22ba3900fd4", + "0x540532e0cc5d06c8a8d3f20a572d901ee0befb9ecb7eb649902cb45f9569be1a", + "0xa446869605c70efa255f13b104ceb0928bb2a1d54946d0ac735cac82ee89f85e", + "0x8a50a5905c8df611825be97baa0697d8c76b41ad8a6c29930b0a1daacd0d19f8", + "0xe37b67fa74b94e0141361d861f6ddb2980b5f394cf4d6ab4773d181a5c75f170", + "0xfb2282c7c6901bc59337f1bd7fe15c3878771da5b44006740c82faaca2ec2b8a", + "0x4e4c50c203691849a26dfbd33132a809d6e1efcbe387f2eef7b552f75736d7c1", + "0xbb86152821722bd9c0e332e38f101c44435ca4cb480f7263558ce6eb3ab0ec23", + "0x07fcfe28bda49b1438fecec025a99aa23c661031b15edf787e67c5e66a9d6db5", + "0x876ba5f20dd3de7e508d12ee259d491fda96ff7974f497b3d1e77cc7fd2e0a2d", + "0x442d0ecb07745fbd483e1628a32eb8118fc4334612eb52e9155d543257be5ed3", + "0x5c010d67a0a752504514151fabb969eec36cf5f969228de02d8c3dfb24ee62c9", + "0xa49894c684fc54984d95be5415401c52e454bff15f232c587cb33957f8d37841", + "0x24cbdc376c64e8a6e2cc86c43f9aa6cba835c5fc038a4713323bdf6c110400d6", + "0xb02cbdaa19eac557f9ba871978f2cedacd33e8e58f791ef4fd572eb9c4784eea", + "0x0451348172d719186abaca659bdb20092a1a62e7fbc876dedc8e6abfda2313eb", + "0xabef9c4352e98417355f400f8ca358f6a0e5bcf503cc4e93137473f11d301dfe", + "0x048d9aa94b0bd17779a09125816e4e035ece0949ddd62c68a4c5402bacb7de00", + "0x5c5cfa65e75183e5c3aa4dd5f263eb6f38e25bbb2d82b6de51e7cb37006b9f26", + "0x84c59741e1a3135b297050afbf5b2e61c71bab2770193432afe953810f5939f4", + "0xa330dc7172839cdfe11b5dfeb36ce8ca28c243c81b578707d06fb2e42023e20d", + "0x799736b78b0e5b9b39ee6603e06b76299245e56d466066e952a3b4fd7ed90cb2", + "0x67a0cf2cdb4cbf461416437078825869b7b82a2016f0128d324914cc82b316b9", + "0x5df9af0870c83773627901f4e3a81d4807cc8fea6ee7ea49b83ea48a5cb77a97", + "0x5a54e9025d0c6eddbbaa641263a5ac8f9501f3a64f48cba58a4c939763359e25", + "0xf2b3ad511327d70600b459dbf7f6a7731190a95cb93786a6d846b4b5a356975c", + "0x2ef1d3e69040acbb27106eff818b8bf156b0df87ab59c91e00dfc7d34a9f9eb6", + "0xf8502cdaedb57b503365f99ef1047586a091de7aaac5200e2e910e924a79f9f1", + "0x768d0a18e0735b602a0a59a7c11565457539a2304c81e5ddcef2d02ace850eea", + "0xad155dd610442860bda517ab9857b12a5db0fc818fc8d01152502850ac554d1a", + "0xd9d44a2eccc5b575700262f29ce0a695f68284835e2fa0bdef058a5d52ca2b27", + "0x9121fc19cc1f55f9d54323db077a1541e8c8d9c4d6a1201daf116f4e7bf10d49", + "0x5323e89a875344c1cc47086e37305cd74cf8b5e658a2fa0fa8c30e4666140347", + "0x7a1451b790baed513fd86b6f443b2ddf0613e810d6a60f19c777c0b81f57fc1f", + "0x1e9de6f4b22a32a76f1dae3e6c383d4a66aa507875f7d7a9b5ec0b196fd5bafb", + "0x55ba41cc07ee3c936285e9b704006329a45777f49ed423080c13b6486854bb9f", + "0x4fef8a27d1134853a473214a2b9664485596f006feaa7a54cb6d9e3e7a067b1b", + "0x10d660bd73c2fd2e234d73a991d28e89a1fed75a51301d3dafe6890014c89841", + "0x65eec23fea9ee6421fb11f1f38180a1e93bc483336ec457e67d56ee14a3769f8", + "0x87337a3a8f6d0d528bfb06208a7d550a76d1f3cec25fb63388577c578f875729", + "0x152bfd4514aaa0db2d743450c9523aab70bd4adc773a8cd9f6107fe4d809f97f", + "0x1976a63ac30c39434491f0d2195b42282b2d8d1a610ff420958f2cd038cc7852", + "0xa9bf32c1a2c90e1b0f98f87cfa534682eb6990cc3fde8e67089ec4b56b552de7", + "0x47ed28af817ffb31aadc44ec006ad2babf69956912fa62f00259770ad4c9e359", + "0xbf60181c56138e936347ae895545c2626cdbceb0f2970ac7153ba74f43624d1c", + "0x2ce5f4032430d79040a40c4ac1f5280cc4a564a18ee02ae69f8fba2d7a1dd84e", + "0x3b186a9ed7723ebc9e1de997c3dc08093966c6b6e341c6e675154aab7320422c", + "0x6be66120ae2934816936cc1e6662058b2c71450607b3d91c7f6799276774dd9d", + "0x28d26c1c5412f4f814a6a0e2ae0847860d7de1ec6c4fe59fdc25ef3b83adcb81", + "0x85041d1fc1cbc997bc4aa5bf0dec92b203ff3d6c66d68155dc9722479f980d9f", + "0x9174ae1448235c6be61ae6dbf1dbc5150f9f305bf2abfeeeb64b0bb7d6d5e4dc", + "0x8072b40b276953d8f2515eaf951392380c0da8212a28ad5117c15271514dead6", + "0x7ba68b25873cf38634dc55159379a90db61030da041a6d2cd412b889d278d1ec", + "0x3e66890bf559189f284b6ab7e7635f050a2c67d4cc9ac9f22b6a8fca81a7d419", + "0x0620e9ca04a0490025e888b186e9126e72aab334e2e8cf300450efcfbc91036d", + "0x673791301584ba0931a074fa39b526feb4838830ded83c97f0d5b9f96389197f", + "0xaa9fe60c2f5ca0bf4f4bdf16e7bf8703f79d5791fb7de0cf135d20f6a1fe5955", + "0x5b43416d7f408d2e43f596c25c547c1fdcd22981270d74c2f47aed099f9ba065", + "0xead52e0da4163a438c0d02a5db34d8f2225f8058dd64f56571642fa1651dc5fb", + "0x4a3a794b660d78395d8a7c645ecdae8b7b4fdee233046360f85c5b1775f51a1c", + "0x44c3015b8c98b87fcada6d8f06ff2eb7e89f334ba3124df9c7bfc0775012bbab", + "0x062d4412690b3be82c7aa46cb4e9a1c59be65a1b084a67c503dea523f281204a", + "0xa81ab5c03eaf70626f7b5ab4ff19499b6494779e61970a16d91026c69fd171e3", + "0xcb69aa6780c9f07671022bd6c1f84c6f36fd6064df89e8948937ffc3c3f42c60", + "0x93a33d93e1eaa910f23527f4bf552cb1a46ca2c4c7cf457eb55adf6cb207b932", + "0x65f462c1af0a49dab5abb45c9553fe99d60a7f8aa8b54056146dd0561ee52a18", + "0x9d86cdab46fab02e3a831b87949c5160d7d50bb4018e4ae3f66840e2494ecf63", + "0xba3eca930660919158136a005333f8f1e5bbb1afa1c344584929e1d0577369d4", + "0xbe5b7bbf0601386abd5fa2cb16d50a2db3f2443d718f72048fc3ce4635c03c65", + "0x36bef3fe051dd1aafc30dd0e81b7058b496d6794d16395e25049bb130768cde1", + "0x365d92a38688d7f62ed60deffdda45fbe694f0aa5bd14bd69ffad8967ebf499d", + "0xdd993839f30dd0f0cb1c9cda24e0dcb934e551713a8633cead3c9616396a244b", + "0x246dc75e178e675a4bf4720eb49673afa9a050c59735fad54f3a07a493fdcca7", + "0x6fa4f5f0455fb4e520af09890239c2e1591a0b5ffa4890f7414bdb2f1217c6c3", + "0x0914ff64c9bfd22ac6cdd55998321dc464303cbcdf327fa9afa71a268f4d14d3", + "0x4ef074c3e8ccb6d2988f7256e0de94d056797a1c613824d03603600a5ce4afdf", + "0x0a2035967923f838f19d610ffc3bc78c13522462b0ed186c34d8076c52059600", + "0x4b03218c9af700462f7c55e1e8aede38b71186afaea3c47a9eceb5ee022c0f2a", + "0x7cb5ca7e11a2217fad7ba510bee6983861d74e5fa537b87fc23ad99e9e7fdba8", + "0xf08e2c915b7917a441cb6544e8f22f1a0bc648ce80fa4110aef36621d6b256f0", + "0xaf1f7bab512c9d01e2a0fe2c342a6bd3775755c645626eb99a8ab025c0ea4f9f", + "0x43c7e82671356e548c424c792b271bf394a61e7cd7f7193ebdf4dbef6ee5ef80", + "0xa2bcc8d4619fbb17a2feb73db8af5d665a5d5d501ddb337e92d91aea9f6bb2ce", + "0x78c9fba344cd5e9c1572f8fab00fa3712ded5d8217166d39bc236dd498912288", + "0x12537bd2ef3d6fc95484b8c31182c2e198366101c86ee1b00ee2fc504177f941", + "0x3bc1aa1bb81ec1ede5ff9b2e86525ef9eb9732684458f3d68e0855f8654c6f2f", + "0x43ca5c9bd45f1d1958db9deea348fb4a12c54f1e858cd2ddc23d761fa8c6c777", + "0x4046fa720fb130a4473ab1117957b96d6541486d643bb20a5462e5bf24805c94", + "0xa6e74a30224f5e1323de881bf132e068ca5f3f23af10c1ffc354f484b9c1ba6e", + "0x1293d04d31d8d76616d9fc5d76dff18401fddff7c4083114b34e901ea3fa09e5", + "0x35e2beb4db29e81884817b497d8516b60ecb490c1a4d3f5ef67219dde50598b1", + "0x794432d833f15aedf8ea2ded5eff58b28a231b9e38287d65b1e9927188410348", + "0x9f2f927ff959e992e9f4ba8d2d5edadb87d177fb2c4746999f255e2b25bad93f", + "0x07502b040f49276b6c103895179012e4b79d39eb1e639cc8ceb9d978f7995820", + "0x01b5e732a294d30aeae011ef800f5b9d1292563e74e3910bebe54bdd5626fc09", + "0x8b4e4c3555efdd63945cc4e8679c75725c7d4256770a00865604743d994a688f", + "0xb8a418f6e837c230d86abf2281d32ef28a80961d70c3614b9dd2958c1b73fe2d", + "0x5f874e6052dd1fc995ff27b836a173b2db4ac80f946ff199054c0a4d751a28f2", + "0xd02924c77deffa34a5319327938b492486c6b8fd83683da0afc4d4b0009a9d17", + "0x7c8a990c05ecf4f90890c0b678c90cbf9bfa16c4d31c846b47163a1f04c46ad2", + "0xa49127c17cdbf566c48d766675f8d9c9de475b2e14f7c25cef939a081d9244e1", + "0xe5535f9ce13db0db2034245a36d14984584fcbff14775a19859e5bcba6e8136d", + "0x294054db1c0f7528330f6052cdfa85d612deaff82acb7bc6caf6a9af48503d89", + "0x21f3eb373a57535418c2172c8adc85f682d9a9ec9b6453924bd2c99509260704", + "0x512d490a65ab17993ada8dc27d4abb66a859f51833f2ce5ab89d3aa404835830", + "0x8289902ea5bcf9d7407b43781bbe0ce60723e27b89d79600e5b97ed920a947f8", + "0x4e0bb25ae5f4c92a522eddab5970d1347dc458b1013874b7370f31b1b35ea840", + "0x531254fe2d2cfd2b006aa5894719c67179474fd236803df6878ea67b5b450f8e", + "0x0ea7425b50d583eb6d23ce99a1f43c30c6fe238bcd5a9a6fcb5d1caf2142a609", + "0xf07786516b23b6f73ef663a2209e673bbb5b3f2f1ed01783aded8f5261ad0d7a", + "0xdd3730035c331a54abda3969a564f3c3d3f1cad477e97601a65679f782205777", + "0x06336ec03b2d6199f63dcfdd4ec8026626b43fe5ce2918323dcb9cf110ecf74b", + "0x90a7946ad1d302666ae570ee88e905c2e761de3742a2c3c6bcc43b1c27b85966", + "0xdf992b4be0c57d4cef564bfa7bb2bc1bf0bea287f0fd8a240ab84536de163a4c", + "0x0789d0e14901630dcf3b6a3ca05961cc54bfc5dcd029e58b0e6c5b3b85985e40", + "0x4183597fb691b58ec24c47a073ccd5bd3b4065b0e9fb353c7043b25415dfbd11", + "0xd7a6bb8ae0f989180ac11da97b2be33a743cc988cc718a938f65a8420ff4f290", + "0x8970c8c0dc286deb337819c1583f241ed7273fa4ae26ba98300197ef752cc567", + "0x76dc750487ff0b0d4735756c94937fcdff49ecfb31b1b142cf6e1072f7367cc9", + "0x682c30685ee2a8ffa5a40f66fed906808147cd112756c520f7c2820a6e337be7", + "0x11a8f6243f84d855e02a5f6922be3857607c6ac07fd1b16d4ba58f224c7d64b7", + "0xe60ee14c8fb0e38e3abe7330a69f9f7ba1001a56c50c4c8fc4fd0bc1754eaeb4", + "0x16c690f33fa675759dbcb41a8656215f149c2d2077a57f841d2cbfc9b67fb662", + "0x8134cdd6638505a4b9e53841affe0ae196742a286ed8bf5603bb71ca36f5782c", + "0x120af892fc3f2f12b56fd65d4d4e9d4d79862272b5cd6d002206c11d6267a3b3", + "0x0cd2942f03a9f0a643939fcaa3de20c8b2040e2d448483516e74f6adc3a19ea7", + "0x604eea9d7aae54fe172ac79ac26e620bda5f23dc15d23db7519fcb721d142c18", + "0x614530e11b54eaf326e660e8247764bd9df01a2f81681e030e4d8a25b029e1cb", + "0xccf2d0ee1aad6f7426eaafa127cad718eedbbe852717b39f1f94e0ad99f13efc", + "0x9fded55ec0484b15e1c85219f34e9b2e5b18de3f53f9cb8a10bc528f1db105a2", + "0xb3aa92d37cfbb016cba6d4191784479a4b49a8c1cab069a33e83a17ab55b7d84", + "0x2b44f36420f4a13778c30d84c1857ebdcfbfd87bef10e2b0274b6d2f7a71059c", + "0x378195e5071964a3d59a33b8f8dcfbe4b83eab1d3ea230ddec2564a9999cf6c9", + "0x9d31d15277d07d77cfaf61c80dfb25b938def70291db77fd5ea5ed428404af13", + "0xd3381be82415c340b5999c2a630fe7191292ce4809517579c62fca8d4c7fe106", + "0x2a443bc3a676ebe90e7424f483b4412d1c8f6b9795d55cb10f6c9fae3d8b5050", + "0xe3f1a1795cdd75084951ce64eb4c521408b5fe7f4b69226799c3d49bb96bd2c5", + "0xe960a3135788a351f918efdea55b2d63a0e003b871cb28900d3e01fccede21ac", + "0x875c491c48cac1119b9287ffdeaa2d559bad059a128b190aef9e72a7210db33b", + "0x194cfb6be363db73e4d7ff5ba7b5ecb6a9889763c11906f9fb0d4b18dba8469c", + "0x51c451a73ef33baa73686f7384587afca1b797e4f53d5832576a5f612e93d661", + "0x5dabc47c03cddf732c5e63098211bf76f408226b523ce7f4e7c0c52210db185c", + "0xab72875ecc4bd79d2b4983a5acfb8b18071354020b219051b3373dcf4c9755ed", + "0x0e25ae5149bac2a2bac4c38a5f2b63335cc45975d2467b9160c0321ef56c31e3", + "0x5c0783c20ecb97931d1d8d92f08937b67abe6e07785ec04f49582967f4de5e62", + "0xd5bf43bf3a667e341741764c5979b22a593e92de7bceb01ca252072c82ffce81", + "0x5f53437fbf387fe8364e404cb6783eef2dc99eaaf27d2fd7b536489a89dcd483", + "0x438f4918027cbc339758a029ad983040e042bff33c0e81d35cc774bb0a87faf4", + "0xb619a72abc6e25a617dd97d9e9700b450ba497e477c00e790a68bc4ec2c6f582", + "0xc854236dc7129e9e0a62c90ddbb088dc70ef803895be2a47fe557bc5cba7da0f", + "0xb5befe9d70da5116fcf6c40e2f71c9dd79f7fe259f1a1ca3a8a09abde3ae78aa", + "0xc86ca1c5e0209755c116c8151364cbd99ea017e6403c44e5092cb9d52c9dfc92", + "0x30913323f33d48fd0330e93979902cf2dec0d3fa97d0810b50ff0e163eeae286", + "0xbc5a0904ab3722be59fcd70b79734414f8d4b291e0d91645c9399f610af5d6b2", + "0xfdb6582db56a89eb17be7b5426b6a3d2300670eb5d088f084fa0f426bcbd16d9", + "0x6324c73bd1876a805397e1c90c3413c9a17e2257436db3428aea105d40bee58b", + "0x5bb5fa7a12fe57929b493968907f0bdd1721df6b9cca2346ae8e0a7fd982dd08", + "0x965627aa3e4bccc654ec32221d4dbf3368f618ae69ecb38cd99a71c4e34fc2c4", + "0xa6fa0e4f61d64e47a7bde5264d42a6309d72b59f990988aaf86fc1ff8f90d6cc", + "0xf33ccb26d8972f6bfaea7f7abb887d8c53be47b18690e7ce2b4a6d63bc6a10cd", + "0x75e65876b6d736d2f9f1f5480a1c6b2cb39a724466ba3e9fd87056d32f08e19d", + "0x08164452af50672efaf9d9d7f822cf8789bdda2e54f4cc00c46f3282597c5149", + "0xaa24484f402faa2e793d184a9f4b6b74d8eeef1a75b9185f16b7bf794a68fefc", + "0x494f9f85d6539d20216b7f9f7e50d6fe9cc9521fd970117591d0ac9dcd66ce42", + "0xaedd4cff2a96cfed35c31d13fc70b25cda48c1dd6c97815bf3ac45dc3d73ed2d", + "0x9a8f21a23d35fe5c5399b00df1f914111572172459d0c682ad9f49da39851b44", + "0x53030a3402bfed34428a7de919a855d79db183af7fe4c62af6e1b21aeeac7f43", + "0x6603b9c68664151de494e8fb50d62cc5dd1b26b3bddb9bcd790408ef487233ea", + "0xccb0103e7ee0ed03d0393e13f0752f70301de56e776e676a1d3d9898a344bfce", + "0xd9f57f1e4bda5df60d0f1d9485ca892e7fc55b7683bb8b16abf68a5612b2b62a", + "0xb65b25efe151aa3b77f2695dae588b6ab89667791b5903fe6503c2e7f1bc21e7", + "0x11b01794108299e9127a96e81121b54b77a8551cfd8538c014bb9d5173ef56df", + "0xfa28e93082a209341635f4f65f328ee779ed0c9d5e188715a8884e557e660cf1", + "0xdf982e2030ebf55d6eaa9c9af5802d9e5b1471ded48a2eb0b7d65c4ffe8462ca", + "0x624e9517b9d073b7eb9f90074400772f879684e47cc6f4e2c75becf0ddec5b29", + "0x550e40e43b7d210f2185e45f8935bf85297a5fca881c518c5baa00910cb35cf3", + "0x00076da0443cd85105e6fac73a7950946b1ffc58aebc2a0de93c1b0eb8b8981a", + "0x0242cb897ca6658cfb75db3993dee5316a34e9ba1b20702e47da7735b0521b8d", + "0x6632c73b28f1c989f22de628291938f6b5d2d424f08955d7384b286d7d2134df", + "0x0f58ec36e8e6ba7535c45d5dd41a8a7caa7a6f15d5b25fa1d345b1f9255aedc8", + "0x9269ae246cc81010515beb39b92304813391b961f55df4f00677fc530839f0d2", + "0x2d187833b59a2d3c797dabdda8ca9543d0854fd15cb4d9bafcae53dba464c73e", + "0x1020cdb55ffcd3505c20d7daf1bfb6234a5aabfdcaabbb16e62cc1806e4e0c73", + "0x540677a4d7eeeeb52a285c5788e88503bd2fc5cb19ab3fa7c684f66dc8cb491a", + "0x482dbe5acd511ac733d248f32db567792e64d8820fb909baeb8c3e08f501b2a4", + "0x3ea24b68616c432e297cbe9697eac74992dfbcd4ca7de09adbae20b81055a827", + "0x0979854861beff9b81498599f8fa15d96278ba51d3842931ebc1adc63436a33b", + "0xfeb8190635b5ae1d79be0ec18291dc88810097cc34c2fd5e7c4e466968968703", + "0x0f7e052e5f83c7ef360318acaa6a977cba4ec3f4e786739596a344a7e3c9fda0", + "0x4666d9ea52c9cfc332009f9e033dc6fca3ff7c5ba62091cb0a217a27a89a7d00", + "0x596347ea6288f7455c1d7981863497862339c6fdfde6eb8b29da315a095d8b8b", + "0xc32b5b12f1236dd6f307233852ca6284a442b1989f32b1ea27cf2d6c0c885d4c", + "0x1fd84d3aaf9e6259ad0976232fa44c7e098e5f4db53b71f803960cfdec3993b2", + "0x019035ed7182946065396813cf176b947d4ad4cff8b1c85b6cdc2908f8f7bddf", + "0x0cc98e84696ef932cc2bf3632b7a7579ed14ff9580f2087c936fbefec0232947", + "0xfe5af584e2d02903c9dacf40eb21161bffb0f3ca24a95b27103289343f3ba40a", + "0xe1f1e771e90bed90b894078582028d20b70e1d9a038a3ba22139ac4c37cec5f1", + "0x8fe3d85170838f0c1eb2ff83e1709d4d5457bb59fd6619acb93d5c04f3aaadec", + "0xda16b9a373008c6a674e5f3a99c2718ae6db4aed61802c0a0b07fdd5f8cb63f8", + "0x1255d62498764e70a83b8e1f9a37f7a72cb1b4bfe8403bb5c7fadf574ba3ec27", + "0xc5ee396ac2c65bcd93ee9df5a444f55402e8c3d051ae1ad037c3a7ee5f72c3f8", + "0xcb5e627d2ab342abd9feed599f1f91e305e9462a2275b1bc59e18ed85c16a16b", + "0xd1126ab90eb3ffc004f5255368f36768dc04df59a850499c38df3899b479a092", + "0xfcea1058ff16d016d9c7175fd7dba076b42e70ae49ba96cc676a6a729e5f54d0", + "0x287bb86603300093eb9957e607188d2a94a131bcd77ba07fbc9f0e24544df4ba", + "0x6352e52d5f7d759de316b9176a4a5f4c2c4b91ec2fdd8607177d529675e50cbc", + "0xd2396c2a763fa0807dfe25319936888eae8c27036d5f098d2c40b0e7f32968d3", + "0x2f371d88b0b3b259131d92cd310a519a7e617e0f9d131e9ae9db96f575754e85", + "0x44ab8938b3298a9676ea6f9ca0b3167e4b270418b4dc6a1e4e5050ff8c848213", + "0x716b15227ee01378a02d87d035de06ccc5c2e7135d0327103b550f5e73461cd6", + "0x8c45111920b1e8a450dd6b1ad2bb079fe709a0879ab3b4a40db910965bd771b9", + "0x69c11fe9fb5e7b582be762397560bd6ce162b8c83538568329127e0bc0accabe", + "0x6857f524edb6f98e263eeb21bdaa0bc4afd4f3d57846c4331a32a5737c4661a1", + "0x2be84e8b2ae07ea9452d1c2f21f4f4a218f4b12a4d140d6a5c9a1c1af1f8f484", + "0x40ce85aa7432b71719249f207faeffacc3d6a97e4589a3cb8d21ab4e7eb20ee5", + "0x7991bb7793ea3adb3f63b1d72f50421022e8218d1e32abd412e8f683b9ea7a70", + "0x68551f2f0b7c8c8cb60fe1e8ed474f29234bbfb4d053efb7cc56525b8d270c9c", + "0x57dfce67f1f1b5144f407afe3dc23068cdac7c600ac1cf6c06536ac5e727540f", + "0x745ba411c3ec5367926b1dbd4bb6a102fc11570fe733b99683846509306a6a1f", + "0xbdf6422192ba5fc16cc8d29c14369154afb45e48fcc57df8a2319a91c2cc50d2", + "0x1588a168a5a536935f779f1171d3fa5bed6dde1ecb134276f0aad707105c3d8d", + "0x0a40bb6f93ac8fd6b14c2dc6aff5eea4fa75055d27ae65bf32e58dcd0eee9a78", + "0x19231781eb0b0d7fb3cec9e9126491ca8b3e5c3c423d61f54e1fd4d331a24d3a", + "0xb2f45422c2abb93ab9ca083a263e896e0186a1a1610836ed4ef3a787109d176c", + "0x59e2451dd1e00e143552470058a1871497bc3e34cb82eb6b1b3e27705f017973", + "0x468699ec50f43ab3114a968db07193c4b0c4d7b3dd03f55e7e44d6cfa9136fe7", + "0x05887dac5c1fbf18f6cb26e075e0104a42e8f54f4f4303b43acf89387ce812db", + "0x3d829ed5471cd44b39bd0b6b687814872192448e5ba42c60283aff2e7a4af5f2", + "0x6bfe60a236a8b5a1af52b2d98c216f3400c4d70e59def81083ca15f90446558e", + "0xf2c99873b9a1beca2b3edb7a5472b1d1fdb543e36ad610d161660565e11371dc", + "0x49ad0169acb83eae00a68ee1727690f967720dd1911426d49d1fe3e81256ed4e", + "0x5d8e9e8f8714619a44fdc6a8e4b79cf24872fd66718798abfa98cd81518c80b9", + "0x91fc05bdf2e915d36c0761c360084c95dc8b8c44fa95e8a3fc47634e1600fe7b", + "0xf5b5a4b834ecbe4dd8fd45db4edd0a4e5ca22479847732de05b38e9a2015a230", + "0xace561838f75051b296aba4101f10e515435c29bea5b79f60cb8733d6219a9d8", + "0x9bc96b460b0c128d8b687f2565db223224a0cb6351d937471ecc39eed2127b7b", + "0xd3b44b50d87375839b78f712e79481debe6d7a1d265bd9de396462fbf9ed9541", + "0x9949f0db1c54534e4c1085d60e797a9306b27fb6068496d77c54d2d4399a2d04", + "0x5c239300afe71a2fa666870a103b319afe44abac14742f2d410275e3877aa2f8", + "0x9ddd07a341360e68eb2f57d74ec82e8296df12ad597d34627a127c676865d169", + "0x64e8b36f5ab8138adbbc47cdaede874035c2cae91f6f94d84c38c50ae17237b4", + "0x9c463c13d6ffab12522709e8ac6c74efa30cb8e2f00bd7a1fdb7f87b7e831140", + "0xc7ad803c9a2d8187ef3d1af2cd9d9852143b6b34ebbd8ca06e4ea07e29324332", + "0xfba9e7d041ed93af99b3418a8a5131e67d2c50ed2b332c270a1ce623774f804b", + "0xf4f4db3496e45676615cafdc402a8a728fc60ff29c617fa1792ff03f05a43532", + "0xbcd4eb113a4181733ac12846f170e9aacac63306b88c3fb7d8194c212043de9e", + "0x01c77c2bc852098cc514d854e83855347aff75b8f910733922a55408cdc23be3", + "0xc2f3f0e8b971d4620a057aba11370341ba542f5026222e8ec95b1b7d377053eb", + "0xa537dd4d73cbbb4a90ad44ed6c6305048d2c368dab53d3773d2ae8aa7c7d9144", + "0x579680bba6b8878c8ab5ffee969dae14fc6659e09d4c8b92b619f3766ffdc379", + "0xd4eba039e0fa0ab2792537f0ab4a309f4e28d62bf2be7ec1e2f05b77a7fa083d", + "0xd2b9db3d63c259885dd63de0360dc303ad2ab24ef6335ccbaba7fb6cafe4544e", + "0x28e6d5a1ae112f6d2b766eb3d113bc68381ead1d8ba9c1e93f9b73c8eb6eb766", + "0xce1197a0510212bd6bb167602aed9d6b99486adbc1111ef0d9d5a9429989a982", + "0xaa8251a654bb8b9e777cb38c5c16f75e680e35aa1485d87581b07dd3f028f6ad", + "0x5154f54272941cb1a70b69c0d91b73dfff258e9ab4be6f3803a8c729be38cc79", + "0x6de87ac981feadd8a2a0318183797b09ad31c079acf5d887e6ff6508710511f5", + "0x70ab21e1677e1915c473911b8bfcae0963d8fb0586f8ae527d83d6304fe190ac", + "0xf4a46b8e416d8f4f67f8637cb6f8960c9e1bd2c3fbbe516939c3fb41071cec39", + "0x24788d6298159e8cd2d145a373e9f85a8dc9aaacd5f99d79e7d36140f9e7d06e", + "0xdd9cd994fe3fd070fa1ae346156e666daf62d0bcaafbe35900a88d7894ce689c", + "0x5732ceb193a94971b46add4c4a9eea110615e01f072f15d8f243c9000e4226ce", + "0x5dc0ca3ee9382e1bd6cf0b6ea88abc0f917b0cff35a300c17eb51088358feba2", + "0x028067ab9588b1916ec8160b4a0f0751a18b090b3f13beab10c3a802a55e078f", + "0x0805e60c5955290fe6469c99bdd2094886bbd5f1f99b4b32d4c450d63596ef81", + "0x8e77486c24371cc5dd7b74a102e0ecd815b0b659e1b3b7d8d6e18165ab36a5cc", + "0x81c09184f6c1cacfcbf0153e9affe976cb1f663999b9a9469349f33fe31590a2", + "0x005387be863f5a4680e16cf5c005974002413deef0557f1eb105cb36b2d4fb6b", + "0xae32a2a56cd222bb39aafad5d51b37d46ac67fa86c7736e00adbd89c50898d70", + "0x59ab323953929e0d9f7a1ba6998c792e246c8c129bb60e708acc89c849dccea4", + "0x4a336a374fae09674a15bbd1b09e85c9b457891607d6204b42df7f68f9e8d38a", + "0x597408bed8197af108987a98607472a1b1aba51e1847c9c48005c7fe0d645a5a", + "0xd4cd17d1c13ce422316e0d8bf94148d3f6672b3afa737361f0bb7bc4081df0af", + "0x4b0a30d2b27dc988e6ee378bee5917e7882bccb0028f7714044e432a12112329", + "0x7f15ef86a18001afba59967e017ad11d24851a85de507b95a007ad181c260508", + "0x70ef7dd777186358890ad5dfc3931ac999fd9ad3b32b244e20561f2b3cceb4df", + "0x63eb1a53ed7cee3401f53712c7702aaa9178c54f5eaa764e85f31f2d5e7f3ea7", + "0x176bc9f5afccb9be07934c9eeb749ee7ea35bf0124a0f608208cf4436371f5f1", + "0xaa6b2160dbe7c1b10fba58ef792fcc0f5ab373c66ef0f7a75d8d413ddfb4dfc2", + "0xd8cb5d9f8595fe449e93c3ab71f312ca140d79d5d0dabe2bae287b1805fe0c08", + "0x7f6a8a4b85174d0657fff4911981481b88a8d97ca031a4dd8cd7a15241fdd646", + "0xd9df0ebda1e646151a1cfe55b79b94cf4ba10d58648af959be092abb29bd6b9a", + "0xef12e76ddee42dbb9f000fdca050a4c02403309c5c51802daf5c1c3b54cf0b4f", + "0x99a90800468a25bac197e1caa5c9dd37bf8f896a6896db70b81fbe707b65a097", + "0xcb99f310dc173b2afa984f94310e34cc990af3f6d61ae533b252e0679e9a7ce5", + "0x15d2aae2b893d3ca1bed1439404472b70b85e405b9b651155ee6ce57456790ab", + "0x1550393adde1c73e0c37a3eacc9956167b3a87c31b9bc83c71dfb44dc9ff4fd6", + "0x08772deefc2bc2d75176bd43dbf15c77a27dccb4caa66336b9fb93037f072fa8", + "0xd728707269a6740adc978e097566b4f819a550619d7357228e9a015fc2c45b6e", + "0x5760064bf7dc9f99564fb32b6889e7179ff3748a2bec127f7dacdef4378af595", + "0x636e64cfd1f4269c9df4e20c75b0a11b544c65975a9f35ed2724aa3450a9b70c", + "0xcad3fb65498c69a9cfc3e14ca771dc49c58f0954c916a0c4a1fd6670b8a03750", + "0xfb44b798f24d390a3cc5d59c7a8c1b79ff22ff1cb92f103008a9392e308a1b80", + "0xfc276366e9d89eb36a35e28ab257ab2afc18bc74a7e8428443e8fcf56c1d232f", + "0xd97454fe1cfecbb139c86035cf5a385b308c66ba1ae1934f5e29f22baecd23f7", + "0x9f34f47f170c0ad51fc22c6920696e74badcda54b6b7e5804df4d04ad9fa5358", + "0xceeb11e573bf62a46328666ed67838249d6ac793056ccdc88e5747804669e48c", + "0x7c93e70289ea55b871cd87dce7551e7b262866e18434dc090c74490b4a639715", + "0x18dec9305e4187794844a92dd420e68f90b87fb00461d83073186193cc547744", + "0x45d16086e79454050dce01d07b6720f3f5fbd6fb143bf50c7ea907d4b3ed5bc4", + "0x27243cc124627b1ba925269df915d3e3a2d6a4f0c319ffed926d962b63934ee0", + "0xd620a8a12049276f9b9600d69d9649f3edc06f6169de4b89e86f83c8826567a6", + "0x541a39e6694bf8e38f195e415a52c6ede1a0e387f64938a1d52aec525e9e11b6", + "0xbf4cc030543bd54838debef7dc248d61722a7a74a8dd4935033b8d4ff7ab8c96", + "0xc13b235f2e8deb69ab74a47ffe52b2d38cf078cecae90073c8b51d1e104c50ff", + "0x307409a36b1cbf493057871037c2c88424a0d6f3ac08d93b7c70bc46ad210757", + "0x973b205fe2e3f987153b2d843d19c375d6748188525edb18c23773c630980041", + "0x29d38d02a99e1d142451b0da9c472c74576aa728ab62b1019c307363cbe42a4c", + "0xe8fae3f266e40e98e1d4603544695b6cf68ada415621c9d27112bf9cfd7d8727", + "0xb7c8b2ec5d9d33019ba4b76671a826a13ada032c8ccebf8f193f0442cba3bf4f", + "0xf4c0a7f12c7772e4c2a9f19fa97f5efb9f679f3dd9ebc3c3c54ee8414e5ee748", + "0x1e5d9de5c625125b04e0fea2973437fb22364a1f63ee31df7ad01af471e57a94", + "0xc010a03572fa1fb211e46b5a0bee2bf14e539a8c6099c6acdd0bc39c5cb32916", + "0x509dd5350a26f2b3f842f5645ae13809b0b64a567a878ed04feef3167a38d45d", + "0x63f59aa522708a8e626a180619663b2704731fe1f25ba55ff7f671cd190d34f9", + "0xdf4f0b1170a3e6d3513fbf6d1251de1bc18461137e1e4e7139a42bc7e814b0cb", + "0x6d32985f49139529f704a79f04e3fc514a69e4fd83d6bb4604e879bf0e51ea33", + "0xa1ed8f97264ea4431d245a1121253264bedfb16ca1de2ded854eddff427d2030", + "0x2115ccf2143ec31501918d0efd1f8dd9abc5e7e390b65b286caaac6881a5f9f9", + "0x97474d0227890013500b638abebcbd81858f804970e037678967d5942aed32d2", + "0xcce0aa419d566b0c374a1fab1dc263a7de77bf389748374f33a9b2cba465db7c", + "0x636fa53922adbe0f8bdb8238b57acdc159f97fb99891fa5055ad2909c627440e", + "0x181fac6ebeaa3765c4af2b121a1902cc06c6e288cc9136e9ae8739840e0625a1", + "0x79dadcead31a65ac2c4a06c09a18ca86e2a648a41da4da8b4a8e8a51f6adf6fc", + "0x0f491f9378dfe41a595efdcd0ff4ed1d93c897c49e6b0634769c845aa8f4977e", + "0x8cec1bc0af3d7466d3d198ec0f9fdd62f38e65888a8bb6b96ef29d0540797e13", + "0x1c970e2d330c964eef17607e978950b31d4aced41cabc0f13b29e5ba13e71840", + "0x1d8cb153b28fdc0349041e544eb10da5fc51d99d504f4eda1de5ba6fa82fc6a9", + "0x338bc30c01771792de8a24de0a68cfb7072ec2294bcec444837b6a76edb77a92", + "0xa05732d3db39b6a23a50a8b315b67c4c8d2b46700cd62d9736c07c477a8c7022", + "0x40ebade27a79264d122866d5dca2c596fca96e260afa7dd645d22c41f90278a6", + "0x09b98b981a1c668972e33caab6addfc24b697f49840d6a9922f2baf405bcf2b3", + "0xa913c90d4a859942e9cde848375780f23f33eb8782a83e999884cca6d3424e42", + "0x9e6aaef57b4e117462d5f50716a1501b342afe03ee112b462cf40f4d967941ff", + "0xce933d7d35b033c3cae204e82cd484d2c466363fd81b47247e5bff4767240915", + "0xb03497f0cff1960d5cad9dab4ba91e79253dd06e7a0e6526881ee9632c9a031e", + "0xbe8e75a8483f713856d3c5f6b14130123cc32a2482fed27de31c3d1ae14cf347", + "0xea4a21b066b8c83fdeb998eac2c8686249a60938dc01c5da685de0d904c47fae", + "0x89d6609e30aa17be26ee9bb1d1689c2d7daea18ce5bb1b3273ab08b1e6450e54", + "0xea525d3ea89f8151c3bfc0087951ee570f1f2aacd4f46e73f0bd9e4b72512a3b", + "0x1040007acc2b213ee450898ad40639a1741a583ccecd162cfa101b46ac85b86c", + "0x992fbc21b3611b17d7d3c76ba825f7817f8da8f85a339aeeddb8c6aeec60819e", + "0x4de4cbd55023e8d73671b1062fb147ce4f957a84c0c442e3e10fcc435a760037", + "0xb452b9f9579f435b1674e65a0be0504c268b3c2937572d637e746df2a9e780b2", + "0x31ffd2c54dbaadf062eb4646dc755c79500570c5c9fd90109567bfcfeed2094c", + "0xfc4c512b915edea71489a5dd04ce8f9b116a805efb1344521442bd35b3a6b00b", + "0x0c25ebd257e0934bb80288ec4ee308760015e672f4a075f1f977a91ad3e027a1", + "0x97088fe464d2b8d3b6077f29b0c8afb564dea76e69bdccc2eed306d588d3e0e8", + "0xfd3f4556a984d497cea58eec358291c0fe18f422556482bfee48dbb782455ff2", + "0x1e311066eefce1aa123c49c038ee0345917fce8d4215551b9d78d12f61964e4d", + "0x75ac9ed434a5321fe663b663b375653271667d990ef41774a4fb75d32f1f25bc", + "0x9bb6397efeedc2930d264ec116e376194591a46cedb9e794b7b9609765eae59d", + "0x57fb4ccfae7cd9bf19c00b8fc52adcb8369cdc0d9309a29ecf776b83d4a33b4d", + "0x203561bb10cfcfc671d9d8bf73b38b3b783b635654c2cc6db7a8b72e3dbe8729", + "0x19c17a4d7813cc459381965e3ebc65768daf07b3d07b1f9b9722a634879dc80a", + "0x087244e325bdcf659f40c6ab41b348d269f17995000347ae630c91582b377678", + "0x80f3d86fddca48fec1f3464665f58a1133e45c0e72cf79c86a138bc394448163", + "0xb6d77534978c98cc018cf4158276a94b919bc7719ce342b2cf30ae88a5b9d249", + "0x2afeb6d60e7afff216a8a6b609e6561b88b011ec53831c37469cc1bf38c08249", + "0xd76d694d8cb92ce1a684dfd835e0cd64f49ba50c2544777eaf2bc0203d4d67d6", + "0x066d44af2872fda884cd69a12b79320898036c6d4892025b7f911736deb53b06", + "0x194422d1c8d5a3c11627152b88e6fd0f42e3128aca419a8a6e6ab8f0da17c1f2", + "0x53b53e174c762b046fcdf574020a56ffcc3b060b1ecc6b8c1447800419aa6381", + "0xfcbc05032c35830622e15451e89140b362a2aacb443c6b44b5e11587ea5e1bb3", + "0xa1d18ecb5808c03419485427ef39765c8839fba8a9a8d791c3612a0b48e70f0d", + "0xa3b8c19e7a73c31cdc8386bd728414f4a44a5d9a0907f22d3b2bf7c661111f7c", + "0xb85b6350de179109c8444307cb21108713e9ac39ee8a2357fdac4531b9353987", + "0x59f83f43780b899e3da10b174325e6f64d161b9b0ed76eb0e3d1069c77e0c6bd", + "0xff468a74ba2a29bdeb737589649676b8f078ae8e8a5ff33597a6a76b14e9fac9", + "0x4e7745523150f189d94b4938d65234f8c4251914951e55849311c9ed3f25ba04", + "0xa42d92a6fd102efce5fd4e0cf2dd3ea50bc1d22128d4a5c76ac9abb2c516f93a", + "0x32a90895a0d7b61fd2efca9be45157b5527f65a0ade5e05943fbac7223e8f07a", + "0xcb06502adc1da0a6bf91eddff8b12e1f0e71c0dad2c82bc08a5b1b0c55c7a83c", + "0x22a8fc66eba5de7fbcf62dc48df07aec6467883d949a9a8d38951c8239d307b9", + "0x4b69cac847af7b1a56d6c8573606274a8cca93c41d58f24742386e7ba703c2c6", + "0x28c0ee13ae0c0e759db0a39d1414785c424cc466b61b5261a694ce8be12cc1d8", + "0xbbb6fe4b549f6af2d2919048aa910b10ec114d3bbb4b91c03191af08c33652ed", + "0x2590c26b67d68f6d35b208059faa3e51ac9475fdc225fe2773b2f6915130eacb", + "0x89774940d0525e70875ad9fb5d0afb2e276d10c8a7f7caaa3019f80933bc8014", + "0xa31b93f070dec436cd484684bd4f5874a1b722d3e462f74888e624375b2f95e6", + "0x719920060d168a9f289180660be2a1c9bf88e2b71e7803bb31970f341d248756", + "0x78051355db04f572b89b1b10424a27592b44e7249b8416f09deff689a37dbe2e", + "0x83c69c89146717532b881df6f1ff8bb31b76242dc1560a658c2202d766b15b65", + "0xcf10dfdba378d02477b205ceb118f1c2870b1b8ca411341ba75e9fce408ace8c", + "0x9bc2aa58db9d8c29cc28d67f5350b3d5a511898b647982b815484c2edbbeb0f7", + "0x515c3e63c2e31d416e6c77cb1d5119e186e1690ef94e82a13db86f9f31241f44", + "0xdcad9f21b186f0381833da18e978880264de87e73a0763fdb1b86ffbb55dbc88", + "0x86301e5c98627a2c4b90ffe9decb70f5bc2bddc2ed0aa07ad8708a496daf4762", + "0x10ba211709db38ed1dc8d7d53f1ab6d1dade603bed544b4a4bd25a4b1643a6b3", + "0xc6653b8adb2b8c1044eeb7bccd3c73c70a12a410d8a7a01ff2e51bd258ec162c", + "0x0263e50945d9c01d2ce0854ab2d43bf3bddbda1d981f67550acb3a674a827641", + "0x6f472e35d60261732d4abad0f7a515f617993b6c78b41a14f3e83243b90d3919", + "0xe025592872cc605d32fc6000927fb2e7a0c8220b5a7b2b6cc242bfe64d2ee46a", + "0x2709a03c710f8532f2df0e5a2295da7c4e60cc38ce14c1312c809cf218d04023", + "0x7dc4b57f105652dd3ceaa4b4cd9d3a15154ec61217be65e3b81b86cbc054e883", + "0x4db0c2ca574ac648ecdfb8b55c96af6280d570b586d51969c250cde05bea0867", + "0x8eabf266e6f9b825da3e5bf851a75b19368145c214a96a6ccecddaf99642857b", + "0xce321cc1edb8bced06e6fe9b36c93e1555d2fa1eb75d6acf38376ab9fc5d6091", + "0x76178e50316a507aaf9b7e5d4ff655b158304d9562b8150dbdb6222c6f29a291", + "0x292fd8caebc4729dc2f08d6f68aaaca82ab8cb30e35703943ec19f7141fbb12e", + "0x4eb1b62eb4725251e5a8e87f9013a8643828676b72b377b28939702621ff1139", + "0x2f2c7903a58d99f919146609076ca6066b472f5bac4e430f0d61260d5e4ac632", + "0x8433ea28677b7c04f8393ffb60d795a44c40b30160f48809482eae18c463212e", + "0x5957eacee298fd3c46c08a6a3610640b61a7ce6b2930f5245f91b4be1c5737ce", + "0x03c75a721845514252032b9361a151ecf18337a136403648000e55079ed38f30", + "0xb05b291ca4a934c17e5db521b9eefc5185ad09bd2f4c50ec12e33a92065feb08", + "0x39771db9ffda63867ea3914bfc19a66d260ca130d1a96b120eca2942f5ef29d7", + "0x854e6c88124e219798c37922de6a883016c8a396ae6548d064f21d52e6b7eb67", + "0x403bbc3cbfe483376e75c1119de6f40f060702094919b658e84308432cfd0a13", + "0x31dc5c03f80b17b8a82071d72c81a385de9ccf95632f484d34ce0c87fc286f3f", + "0x337d04555a5e89e7b3bf7f198845e6c2ab3f8c482add83cd1dda076960a7ded0", + "0xa0ac8d511e500d022b2a2c85c99d305de7fc0d41b02db141272e789a71c7befe", + "0x3d92b891d54d2cdc6632a89f66c52f979cfda5dc7c57f34511d0f1ecf34b4bab", + "0xc0dbb9c04f97c681f25ee12f2b9e971b5ddd27f3348941b260beb10c32f8475a", + "0xfce6e536b30efff4ff98750923945d7196022eb898b0e4a671e65f5d060068ad", + "0xf3242e69f1997f8389e148463a480a948b999b9fcbb1f0e6693e17519346d786", + "0x52111bbac54d6d05c592dcd207aa80e9296571c18fcc5856826871bb1d520ebb", + "0x0623d2617b78ff5c14c9666b465febfba0a095e01eb453dd2466223ce6085db7", + "0x37e7d30e6634376777f0005fc392058c99dd0d4d79791bd46725619985829823", + "0x0f0581da83e56ee7e20e6fb5b4a74d1cc8dbc4626f9af8f49f7d3c97bd656063", + "0x012aab8ebdd0167a908fc812feb33eacacacce07cc5c4385d567153065c01ae2", + "0xadb9e8c1fa13b17437755bd5680b72f578a94c4ff61266e6eaf0164e2d86eccb", + "0x960ed72a59764f1a033ae27b6fa8f45ddcd254f91d202bfbd732c3df2be6b458", + "0x2930d919b90910180dc762468ecf855c58bee2d7a991f6b70024c93d3432baa7", + "0x133da2778d1fb4e1a04e2a9c1cffe8c18204963f49e2321450f09fc84f9f2d9d", + "0x84739c0c2de1f6d62bbcd57596bb79d5e104769afd41f22a0e71ac326d4c2e0e", + "0x8aab244a36aefee35227757c1984a74495eface32023c1c7e77681fad7b44c2e", + "0x975b2b4ec7867bf654826a942f586f1dacb693759023c423596905438057bdfa", + "0xeb1d82899cd0a285a185ba2ee3f65690e27c10ea09da8e64296befcfc02606f5", + "0xa5c5975b7dba18a7976991df629da4211a7a1f276a3a5ed33c5135c6c2ef0401", + "0xe4bda234de1bebc63165c19f5f8bc5a269c7927f91efeb9ae603d791354ec566", + "0x2a7d8dc6bef12f270277c3ae1ac2a6b742917dac82eb1892784e29100cfe84c3", + "0xf34292bb380affe46c289aa26acd45416656188aad290c3cfda8af773eb9f31f", + "0x9b4bb39c44962a6f6440035c0fac1e2461fa2a9c07d0c01deb4e64bf904d29e6", + "0x5a31c1bfb9e5571507ffce2f3233d8e0aff2a6554db3ff6c42a188dc4381be93", + "0x0ea98ddd5e50ef0e7e93b19df4f614d392f92aee98c6fcf18805c7b848ce0d69", + "0x63459403ae1eed70534e83b62ba6c4c359f98dfa7c3d029f9aea7ceb1c88c9ee", + "0x2a8b1f395008d8e35a702027c3ccf8b1b67a8ab430f6b0cd90b69c3f563c0204", + "0xb821a01badc3290cc677346a8d16e941c21ad1bc80894549b7423fc98b173558", + "0xffa93ecdb3e4157ecbb952ac922aa9056a9287012e91b3bd31cc53e48fb0d3e6", + "0xeefbd5dcc7e51faae93c09eb0c7fa7fba6debe7cfe7d46d4fe01c6b2087711f2", + "0x1f349e5a91aabcf925ac1964451d346809f721f3546ba83888e7bf1a55ab1fbd", + "0x1a91899152809f6aec035937d1ecfe766212915c5c3287add267a75a2b628ec3", + "0xdf121eb356bd2b6e7c719410d47e32aa3ac8c5c0dd99fc6da9c2fec24ca4a594", + "0xae27af772fe492ef06b5a12d30dbbfbcc9add4870904baae5f1383b116dbd360", + "0x8124a0ca4fa6563e4493a217b49d9f60cb8124657254b27cc1bd0ab0c857faa7", + "0x6ee502ba34bbb9d4259baefe68ac73c39a32c350f3c8372e7bb23f05695f9204", + "0x0fb3f8c02e456072a8faa1c51b9a45fd335eb3af8739353d3c9c8db08927675c", + "0xdd00f61329e58b39bd5a2e0ab2afe7bc740ac91767e1c6069b68e163ab3788d2", + "0x95b208a23eaecd31debd9cef2ed809d70bc6e0880aebe76a7638b4f67b8eb1b9", + "0x6aa67927ab0536c26b40fbd714f8426f83534bc4bcc7c830ef2a8ebad50948d8", + "0x12957a98e236c3b7e870882187b9b116c0ee15c43fd88e997cff7ee9b0b69ec6", + "0x2e9137986b2b25d1d4a43517bdc37f8765d41c0b4b6b4c825a30899b4482076a", + "0xfb5bb9fe0ac30951d712c6755e2a84967a3aa55278033e61720847e2751d8e01", + "0x396a4d450d3f4493edc9269d3c358b7499216e5fa3f741338f50d32f078f4cf0", + "0x98c1c491d4c81ac32623a09cc21b5a0ac0b24170f237fafa11ec6063b5e4129d", + "0x98434ad3ff46038ba218b6c34f3286dad7f99b52998e923fe11ec387802ce93d", + "0xbc4060446529a821ee503a23bc8d6eea8145a167c4c436ab0d0ff6460a322f86", + "0xfa04a48fc7afa54a7dfb0a9c3836d731f0efaec1473f1872dbe48d84a36252d1", + "0x9ce391bcbf289311f515dab1f1bb07bce554d0a1c47bc412ec89636d187ccc21", + "0x740803042dfbaa553abb03fe7fa367b8c37e9dbfa9e939b23606b2bd0ce9ae1a", + "0x9e6b80ce1a0ed996bebf8f95908c9f1ce22fc09a8c7a20b07d2cee6233459c33", + "0xe6bc453e3bd82c1a883a45bdae4bcd0d9abec3a09991900d9b8a6f1ca40c61c8", + "0xfbdf268678f414ffe263c8a400429ab4c6b839d91618623276789f10816de891", + "0xa1ab99115ee4818de5ced9d32a7da94437eb7b53788cb2382e04fa7abce94b27", + "0xd313fde42b5c3c369c3275f86eba160e411a5adb79e23cdbcf7cb0fa6e3e5cd2", + "0x092a32a510a516c2f49fef5fd417a3c380ac5738b3eaeccd2244d39050e6e365", + "0x623bd7718bacd027bf04c4bc3cb666a9b019f90473436c1abc1504767e3b3987", + "0xd563ed36f4c6bbc2c1d5766d0d272e3006c96cdc951c3a0e005e72a33e9909ae", + "0x4f3f67e71d4f88762510e6f7a6b720b7f908fbaf0f3618193bff4394d36592c9", + "0x3a4a5c413f8715337316814df09a30f508f9d462f3020efe1940886b86a7a6e8", + "0x0eed9c700fb00ea3e48bef83b45795f701efb5c43b5acac1c88f4586f27dc665", + "0x77a0ee01a38975ab85efa5ebfe5d30c3e55dfc8fa4dca797541b3d9b34896ba6", + "0xe4f34e3f06d3cf2486ea4cb14c5f88d07def48c076fde6b8ce6dc959394e2f6a", + "0xea47d8a1fa366b39926c30239a4947b5efc4de10da762e71b31f93f4bc643578", + "0x5e0fa4aa5b5822fc5a2783f871ac1f7ca5f80621119836415281b234e6d9c24e", + "0x5fffba4f6160a208c65860c3c9b086252c91406a7166cb715b7ae9b0fd733d12", + "0xf1555ffd24d1277252f2fd36f1977c3eb48ddd682c1db95b2faadbb7b6c6b4bf", + "0x5f4f9cd1498d6bbae2b85ad5c4737424fa55e3ab92355561087140def15ed2ab", + "0x9d5fdde748f518f6600c789b9237ac99017e7811cd5ab5c10925c38569f5b307", + "0x422d0e3e4f99d9a43b1f5bf066bd222a74143a3b0885faa529a3b9e343448211", + "0x94dd1296d282d1de8911951275909c352a7b28ec6d5962299624ad65483952d5", + "0xe78f457473a5fb13da32a16c57119e761d320e555df0f6a94ed7950fd5d70304", + "0x565d0d78d59256f5fd9be5969a339c0bad1534426857e49414cdc598b44598bf", + "0x72cab5e4f2ebfb2e3782fa35cd10436cc5556bf0cfae156ddfc313306f933802", + "0x02cefdfe27bd4b3aae29cf732d09941d2a7da297144b06cdb2205ad1f501322c", + "0x4b8fc889bdc73c7388b985e94e97cdd1e940d42719348d43fae2fbcdc97b3dfe", + "0xfd0e5c746adf2f92f872183fc9b2116ecb06b2f72a5a4a7aea32120a4bac23f8", + "0xc96a1b297b0b2afb5ab2bab42f0a604866179589b8f18e43cd90b0006c973938", + "0xfb347cad7baefdf2be5123bfa52dbee5f4aee4e5dacdf9c3fb0b5be185f054f2", + "0x622be9ac7b458488dd4b3ab5bc097c1fceb3971fb9daa0f77bbd9d48b880f5ae", + "0x01c76aacfedb64e87c56594f1a83008d10cac96ee20caa3e68c3414c17647f03", + "0xc1b1e903822ea93ae114d7bbc23c1a849ae671f727bcb43d38f2338f8de0b731", + "0x8bf8def5479c21ce1102aa7bfd79e692ff96cdbe95d622454b6fef80bf6dcb42", + "0x50d171fbe0eadd5fc149b9ed10418b10c4ba4e60dc94f335df5b2296278161f4", + "0xff6dde0ddfabd453ff7f2d713c2f44efd512d9ca74fecd7d27a5fc835c7cc6b5", + "0xa53ffeb5b4cb2080697dbf6daffcb4963dffe62cc2b997e343105a2da9ee8a94", + "0x7f1e55391f420b505772b75af5a11fa6b609b430636da6faef4aaddce6577a6e", + "0x865732741659f31861ea136f9407a2cb104d692d7017ea88dd363aa51a69c0e2", + "0xb6ae2c340b1129da229c25c2416000c9a296a05e3719f4618101dcc3bed3795e", + "0x73dc729f9d135d0ecadf207837a437bdd07688f387aa2ee2fc586946e16d1ca6", + "0x15e1e73ff70a30e9f5ca65d45a1c24cf94524b7e798686c1b81b1590b12dbe0e", + "0x6f8f72e531113019d29c29b08b90de25fdb3f1e0699e679a6787f0eef7a04a42", + "0xaea782960b69ffc6432e2ec09cc65e687abd2cf8471b1ee1514cb3c12dadcbac", + "0xe701938fb2d4589d114d5f100a8cc5c75ef3b09c7097d5fea99f4e9c6b8b2e7e", + "0xc2ca10f326427f5710cb0339b75171057d88246c6523979dbc30db8760fbad19", + "0xb5f653be3e6be9d648da76fb8172148a1b1f68b8b17c10b0f1380e2f0117ba91", + "0x3fa309f0e03232682a6dd6d0096e9b7147db64e77313eb19389685896b92cffa", + "0x9f9cd8852a8e94d160d50d05716ee7a4a611d83f99a3d83ee476561ef4aaedfe", + "0xd1506b0e83a32941192c388a141157ea23f2b9ae2c7e8c0e372b039b1710c824", + "0xc20a305d5a24c9893ad35eb5d2ca844b451ad53719630b8c55c5d46f9a364114", + "0x8b0a7bd1c3dbb2f600d41afa2b4ed2fa6eb319fea0b3a88be6b2ebdddcaa94c3", + "0x7c6f30bf2d0afdcf9971c4c1675a94a48f0f837ad12e4822ab08a5ecd39ec818", + "0x13513cf7f6c0547b4339ae1d975309696dad9280511734cd07af79dce725378e", + "0xe2ffd4fade0f07e57e232c8b6f9091a7353f43ab1fb464cdb4d5d362a9a2b175", + "0xd7a8547a15f4987b7a0dbebaa5eaa8097cbd64c580dde747bfdb2ec0f7fcbfac", + "0x755bc892fd61a81ea5633e7c010647c0b28f76a52fa39351d5d3fdaa2d4a5b25", + "0x3585496554a5462fdab45cb29674f04bd27647fae4996d62f1002afc6a1edc22", + "0x9b5eabacdec2a5f042e49a36dac30ba4d0a156640728108eea656094c152bdc2", + "0x5eba14bf097aee48140b0a3967226f788cc691ae7cc523c7ea1604de7c90d394", + "0x8918e62659a0b624cbbac591b4868ca5e0e708c0635594fc1153fc89003e910c", + "0x3220e167e5f98f71c18ee6824d7f726070eb1c4386a74e8e42ccb6370111a0ff", + "0x52574b6391783d833b7e172c0a9cbfce3137a613bf20477424dea12895e8d204", + "0xbb5541c56f46b82fc0788431171e8c539b2fe612f7d0fb4c811da5f855b30d56", + "0x256749c8374eafea80647704d36b6053a05fcc3980a29ed4547678162ac97d53", + "0xde6833997d9ef119d0c055431cbc8d4dfbe9a8d4073b6afb703758d74828100a", + "0xddca660133f1b5b9faf55a5d1248a1d21fc3525a6f7c6952bcc35ac21c1e942d", + "0x0df2a3e7cf40267bf81fc83e9717d63ad8c94afbe5f4644a98b644491305de93", + "0xa9781a6c469cbf7470f5b7c0f30b9ea42c96afb469a3d2cd00f2aafac248b574", + "0x559a5132459c7e50f0e7b1d19c47eb693f016467980079d4be15975ee2b09ae8", + "0xddfaf9742fe2cfdb133ec53026af77d3ee083243503e37bb890f27acbe956392", + "0x37d4a9b0eec9837be2ca7070abc9930a07be1d1c6333f7b76c34331474436e39", + "0x3273be57e854c00091b589c6b14a834703dce847402da4ff1e985eb5f3f29c0b", + "0x5d8d2a5c845d20fd2151c048c247cd923bb8f95928d6d1e801b13b86d91ce28a", + "0x0da56bf7d3763b8c54f0d69ad35b24bc0154a4bb27ea700b3bd49cca3ebe3b0b", + "0xd4799435e1c13c4cd7f1fba776aefcd0dd5518ad59fc42256d598417c47496b5", + "0xea7da4eec740649c043c8e04bae23f06e5928c897a1f67ef5f25ba96998cb2ed", + "0xdb610da291f60b38b51e6ccf513467161b26e18157e37e6231cc647d6367db58", + "0xecc392bfde54534fbd5dc6ab640e8cc731fb6e399fc6f7d8fd64425ccd66f522", + "0xbfb7fdc86f1d0feded6539d5f4f7135d029ee72be37705c288f9d4001c87255b", + "0x6f2a50c163fff9068da5f7b672ac05b0accabc5621a5d4d84ee7da1dda1b56ba", + "0xbb2bdf6e14a1cb65616605b64aacfca00eae0c61dbb4f71b41e61ab7564f0e0e", + "0x4040e06fe99c365ccac811fc186a177c3dc62f32a0b31825a77a2ad36e1bb795", + "0x9ff3e88aa889c1f0082c1f2a130ad62067f9284650d73104d51cf261f39a89b4", + "0xc59b0b5d1428ba19593b587ad84ea57b3eab276c21320145bebefa5515e1de3a", + "0x736640ad3679478e62ecfee712bed663089366c136eeec3ca83381ef8fe87f49", + "0x73f55324a894a169d71ef5164eb39cbf57469ebd6b0ec157bf660d6bf046bcd9", + "0xc7c7fd8c7439c3d8d6d2855aff3ef55eeb63184c3d9e5c099d787a9cab20ce36", + "0x2d293722c0a9d3f69a0b6f54b335af828190f3c6251f31d731525eddb4a9a275", + "0xcc919ec926bac3047a209a25c45acc91a2995f37eef5e4d63172172632236bfe", + "0x58235b90b60f7ed9e6e91a71916bdea526afa1973aecc32b8db0fda41e581e9b", + "0x0d94ef6bd5e675ce655576cd2e34a34954c47be3b43ac7b6c56bc8a0e9bdcde7", + "0x0a90d07641e226c6364a6aaf96062bf1b69d7d2702e20c75f8da06b434f5a9d1", + "0xd8ab41d455e11ae4d4e2af614cc026f973a11d1a390beeabbcbd58b963045d98", + "0xcc365425a789705a9431a1a5bc18cec191ee637829bd5ce29d07118fb7f219e3", + "0xc2b5bc0ab27d6b6775403adb4e95f2887f9e4457499798bfa54ab0e9f9654e1c", + "0xacdb45822cb21fbd31e7fbb5d5d69b5cd358a8c7e98ebfb3017c53a41143e92a", + "0x212eb99917db2628360244289450321e0ff6762633f530cac36cc8fbbabd3eb0", + "0xe2bf8a188a086c231ff464da0d8ee4055ff173d834320c52f6274333407ff135", + "0xc4d2caad9ceffac6e24d8aa0c3b56b11768ff998708bb62ea1c4541dc4f99d50", + "0x655899c445c917dde0f55ed6a94d190a795ed7cb1acd38376c40e41832813a08", + "0xc5804e73df42998766c091974358348757bce149eddfb30e43ad75218d2356b0", + "0x024cba16cb44c95485735279db598dd44a5871e5351e6ebc9ab018cc09aad0f8", + "0xe841954793a463c9a8bd6aa9308e6a7e9e0999ce27b94ba31d25dc7fbe3417db", + "0x9e03c74eceaf456e30d89bb327301e98497f4684d90a99d6b3123488c588ddd2", + "0xa9560b96c8dc404dcf81a38c498a24ad1e17b63def5dc6e6f13acbe80db92758", + "0xc98d48a8d161cc49b5d5c4a54b986da9b779af4ac81a9df25f2768f2adcc6456", + "0xc96a1c0eafa701c2728ad796f7070ed88ac887bb5026eff516cdc3c97962d428", + "0xa315925440bdf4cc85f077d55c4ff81585a7145b281f3418ca2f369f2eacf65d", + "0x5225c6465315f47869eafe949126811ae1775f1918c00c2960b0c306a015f619", + "0x14e7a5a8472fdd7564f4ed476491398fa453479e7bb672d719446ddf2b33e8c8", + "0x99c6d0b9d9c5ab7282ce43c55ddf7ac6636f4dff2b72517ba5da6fc93955a8ad", + "0xec83cbe6eb48e720a12e15588b8ec80d8b592d6ed9c8e39b9680f911773b44d4", + "0xdd6e0c2ea24434ab82ea33b3febec5c9784bb7b9aae93866fcac759ffc5b8dbc", + "0xde1f00b82a9889fcd8c4e320f40d9c97d5e01bcc8212d7f999e2b1adba06a30d", + "0x2557305e9a6128d41924aa3e25c82e803fbfd4f606a2ba574a5b6f9340cb2e91", + "0x8c2bcb2459d477da54bdbdef5d1f95c3bdf1910e6ea3cfefc89a06cc4a7565de", + "0x5355a5b331fae97e1be5e07a476706d78ef57a8028b0c8a0d3fdb9b9e910f41f", + "0x4f1045ebc359cffda66b3b7cb297cf70f5ad731f41b9e5cc5974fea63c0ee1ca", + "0x13e9bfceec855a8489b538eda0b51f36bebfa96455c9140a274ce1ad1baba840", + "0x8f4d94d811b5cf65471c100f3b225a39120a66111189f374b4d3938f113c2106", + "0xfe2ec6cb4da687d31db6c4e895bf67f98a0100074611bdc472068f9c8b42d2b7", + "0x379979606a42dadb224888a2a7f5250aad0b5bb52cb5213053a1547dc6058dfb", + "0x1685a8c2a963caf5699912f7a2bc776dc047c44cbd62bcee8e9c068b5d331ee7", + "0xb7ce480a8184693ef907c770c14d1431fb7f863f7338616c0f93fb95b58f633d", + "0x883e21234b94ec54265fed246f980d18b99d2e0744b893597b2a7a8a3b0a7393", + "0x31598acfb3d670023fb0ec19cf4e1df821e254de217b296fb63bada901ed5cd4", + "0x95316727e6e3c1a72823043cfb7b75dfd8bde6aa509b65635405f31af68ac216", + "0x5a08d56049871112b44d63fbfef4ec0a1440dcd6c28947aa83333bd2343f4048", + "0xd61563a61c2d9af326b508d1d06f2819a06b587e3c8806602ad3bd70bb8c506c", + "0xb32d24e0d3673f462c3eee7ce5d5d3a9d6fa73d97a67ebd066432b931ec2e1e2", + "0x38234ce4e162d6d6a5da25c90afe583e442ca9bde3f33199e4544a00a1a1bc24", + "0x261277308f4a87e65c2c5f62f1e52f95916b423da58281b90141c86f3c58e89f", + "0x99449b134b8ad93347b943aa19a1f5cb259ed8e6961fb3c63d7394bfeef5c819", + "0x6a3a3fd67cde4a3e43c16629a9291c46d6569c0799e76c971f430c924b86fa6a", + "0x8260857c946894ff9818c7cdf7631edfc19381384899ef748a94b02ab679c520", + "0xbfe96b5adf9a33a9817089eca95928f7c820791bc00c1cc6514a951d17bde67e", + "0xa0d666cd4c56997f71f259950c49e644c9267ecc29b45a239ba1aec1e9f53bec", + "0x53ab602c291cf62f7e5bdf70546f066fa1374f33d23dc5ae2a7050441e9abba8", + "0xafc55ba81a8527a2dee3f4b19fb71d5450dcba4397d66c0a1a541ef886a4ea49", + "0x08260456f1a4df2332e79f8ebe50ceb3f4f081036e39f17657062e4504dd5cee", + "0x30ee1725620b2af7d80f2c14e0e0ef60c29f848b7015583f6c773db42165c445", + "0x49315854ba95d05cef5b6588de81bcf3904fc12d5850414c2fd0f5e63a7d8440", + "0xae06882e9e544761ad1bd498057f9e6c879a5803a63d508e339d638f4d7f6b8c", + "0xfaf4b0321e7cc5d56365ccc45c5d6860989f22fd1dd873f3bcfccaa0d39a3f08", + "0xd678e296a1029c11281a2867f670d9728b2de0f5395a5694a72181217bfa26b7", + "0x918cdbb3566ef2b475b55da97eee8c54cdff6872b3ba9138ce782e0454c37511", + "0x82a970ba66032669958b418e3f2d6097a5f44b5005d9f9cc3de53f588f30cbc0", + "0xb793d9fdccd7417b802dff55b83301d3a9182a93462b04be676adbc06883940c", + "0xd860dbe741134d4dd2ecad7cfa5959b3e8a8b6d2190b4a1140ceaca96d387520", + "0x7cc5adf8bab86395f4e6691ee7bada5f5ec2f3163ea1aa1195e452cdfdca9aa6", + "0x880e95509d5e50ef23b6f336b36eb8313c3a5885834b74fb7d7b8ace7ce26349", + "0x2dbb9a3cbbedf06d65fe18d05e4deb8a16f7ef191017e3cce7717489a71246cc", + "0xc62c35a20a2676d2b764b3ab0d4f68c5f3b17d064519b160d9e78f1338b6ae64", + "0x73305bad02d13686564bcf15b84c591b95ebd03f7c5106db9dabdf5ebcd08b9d", + "0x542fc992345fc4da5746669145ae0310c36d51aabdec9cdafa1affda25816041", + "0x340a957927ee28bf0b1b004c6c7374039e2c76d608b760af0e6df5690e082a72", + "0xff0493ebeeffbe9dc3eb93a16015dbe4e40a2c6dcd3cb5511c651e555eef2362", + "0xcd6823644edaf1f24cd82f7d86ba16b1970bfe5e4558d80a9aa8588bf13eb7b2", + "0xd973126becf5971b54aab7d2f1c0c8493a1fd2ec5a3152c8782219ae3ce4ecf4", + "0x7629fab823b96986822b44c3fb5354415144d1f1b6f338e911038107cd9515b9", + "0xfcd6e41c66fee8cd2921e7a6d117dd82a843bcec0221a428bd91b8ddb93adc66", + "0x446aa3e823c043ba16515dfd4b11f45b697deb37df13701496303a5871b0f599", + "0x6458250dc7d34540d73c233dc66c689f875da6bf6a5e7ab5e6fc7b0a0697c661", + "0x5250d6e47b9419d4e22efb8a0868119e8fe4448e8c96a3f2898550857c1ac579", + "0x7a07de48f5632f133f0cd968b706a5124ae2ef70c1554f2918bdc6bb23d61c41", + "0xe99208bb46ba8b4d1e9049c9a44d8826187212f7f622b31360422aeeddafb1d6", + "0x3679a06e8d00e1ba322564588ab0208bcd7e962dcb58f34e673f5b07be94b718", + "0x67f2ac701c6b4fec9acf27e05b03a467564f94edc5212a5f0365cddce4118c7a", + "0x820f4c91967a88f4f9253a98030bacb81bd88dea94af765c828de1f4d89fe244", + "0x1f22dc1ef0d1eb491a0593b1b968307a6bcc35d5a0718bfe9ef78c7b94170011", + "0x9129b8bee009edac29a2592792a5e15a716feed30eaa23f14ca6a358b74215f5", + "0xe63a3b8b101218092649bbfd966645a2c14594bccc7bc5cbcb1527239e2bd246", + "0xaf3245a61e89f03ba44fda00a85f25bd773848d006bf79805fbe5a9a15034644", + "0xa033eecb33091172ae6a16088fc63b630467ae54ee5ebd24101ccd05ce72ea64", + "0x7e04f2b8e0806cbb95b7f56fe71672d1f74fc5e0280d2cd7c38c4fbf376e9c5b", + "0x327a27efc0e1158bb2c3c3da880de91785975aa7912fcfdae2182b02eee1bcf0", + "0x116f0de1d7919db54859ada928b323ff48deaf95ff397676f892cd464356b43c", + "0xeda23048ce492b23ad7ed2282857749a05e514d960ce3071f361f30488b488a1", + "0xdc7254724ee9297d5c9d94899680593dad40e36b0d91d4215914b163904d481b", + "0xcb65b41ac0116505d87e1373425586b0b67faf5d6b0d222255d07dd3cb543827", + "0x0992b373fff071220485090a1af83eb8a5bcd8a03116fbbfa88068ab40884572", + "0x256264ebcda2cbf6fe0ee338b0d768b7954850d70f26f7fd4bfbe47f0bd3a542", + "0x079c500f7c18e9f106de141d015b11c599cd27a7db9ad2735ef87014109172cd", + "0xc996bad78a8749850e78f7a99874c3fdf4a93e0fe75e10d4065fcb6ff2b21bea", + "0xa70ed5a99cd0ea8427650cbf9587b886d76a489344e80d91878d050e3cba981a", + "0xfb36b5824cd88d3ff8ce62148b48053df8d9ef16246ff1898ba9a89921a1feee", + "0xce223e13f0d80145f56d51e45c2051b14c1b63b67367fd0099931ee39ff60794", + "0xaeef8bb60baa482393ca0602f84c664f7de9edfbabcc31db4020d6534130b7e8", + "0x8bcffdc5185b10f624d4bfb69525f02fb5445654a712668b0ee6274ffaaaeddb", + "0xdbd2b0d0367ff3c6588eff03b4a106409f1d4a986c71d7fdbd78a13a928d6bd5", + "0x2d0a9cbfc7334da867767d5cdd1db4a75742c07240df77caecf56117cbb4ff8e", + "0x43a1e87e4fe2be8391bed4c51bf929f27e3bcd4b1c3a4fbc15a273ae0f96dc98", + "0xe057f2a7def50461fe9d9e607c17cfbd70f089bae1157bd226cc58db711a1f01", + "0xc24180db189a3ad3b54a593a86b2ec3f89ca9a71bdca95236b9b445a7d4170ae", + "0x07f61421854004b8c92a9a5f23a8d28f7ceb6d870e7cf4c576ed3c1430567607", + "0x169bbcd706bb84a958336aaef88dcc1737dd65b8179682cb8fa1c48c55ec70f0", + "0x06695d56f8f999189db90dedeb3b474761fa1d9f41ce449e5c0cf5fb7578ec4b", + "0x504dc424a0ee4123b700607025b1a70c0b7d3d002874e40fd97cbf613ec78425", + "0x60de119df7a9fcfd76681233b7a20159349d91693bf6adc4c0a2eb85fb9514e9", + "0x123ae38635256df053051ad59f4514807a68d19ffe674b161886898e648c1feb", + "0x9eddb682ec06177677caa3d2d89cded5940685119e6ff8ec32af34601c37be7c", + "0x79dd5cd115eaabb5c12cfc1f5c04a2ebd0677f8e79460a22be16a09c1b84edff", + "0xabb099713837ace62656b148dc4b7932db5c2fa7ba160304d2647b8b1dde28e5", + "0x39b684bfc96f59d0801647955e34065e6e54e24331f938dc35d0dfc1b6404af1", + "0x8879a6ea09e0dff49c2e705833b859199d08ba0616df29cb4ba518a2fac6821a", + "0x255e8d2e23f0c2b2153f12706d0fd067a85fad75d4c11ae01e37f9e8eb3a7e39", + "0x750911dbdc0e12cca11cdb01a26eb8d1b0dacd2add311f10c1995bc897b012a9", + "0xf49cece36a7dcd017a332e4baabdc9b1e9a85cb6a08860e3b3119cdde29fddac", + "0x2c020e0f91485af52ee15c65ab1fa5b79d7ffd7fe04efa4e1b988646f815765e", + "0x5e4823dc6e7e749e8f05d0ff73f0bd93528179b397b1683be7bc062991023b4b", + "0x482dfd214a22baf5bfd8e970044d69d1698988c005c9ecb817ca5f315b121952", + "0x16cfaee03df2ae2afd253a11357a332b8c26c80b24640568910d06a781404550", + "0x5f63b6711f1d2697bd2354d7491585d01aa669c6ca08ece86eeaeec05c7377c5", + "0x6891769c55527412a34c91aff8ce879a98525445725b748c69d872884728731c", + "0x6e90f1d928a4b4b82dc64630268b7dd6ebc1b64f575e8eab9a565db3be2eeba3", + "0xabc9810c356f193286a7cf77db01b240b0edd658fd0026c9537b0aa11cb92ef7", + "0x005ab5f9fd76b47d9e06c9ee3d38a6b3cfddd6ecfdbe83cfa2031b415a09f8e2", + "0x23f5a1a226bf17c8ef37ae80879f9b9a39308e7b3b81f2de50a8990c81ab4952", + "0x56c7c0fe23478c3af390986b4aea507c4c84e10a38750ec189094167eeb5e60a", + "0x477eebb5e00ec1872fa2c3cd89227af4c3f396ad76adf7b93f17be3140901927", + "0xfeb53cbbd09e63cc95afa117420fd98872589ba7def99673bec9c9f78fa979f7", + "0x788783017b838e123c385a527ceb9ec200fa02e3389d1140efdbcdfb19c66bb9", + "0x18acab08df580d2fd089003dc704760cc2d077a016346bbbbf695d79e641348b", + "0x99c8cc2c0942b75d012cb9eb7fb152b267c1490b50adf5e96dbf64bda3994fdb", + "0x1006b1e67708e0c0ae1a5b9a55e17dffd51379b9e5c72a62e7dd39e2b0d3795b", + "0xa30315bfe875348d61809844c285e16ad83e86df61d7c5d30fb122d89b86ee45", + "0x237fa041dd2ef12c0aa27ddab888936193591d3e95a62b59a6a669adbe6a9d26", + "0x8b14d2889c2084aa81609d7cd5d7af440520657983c2539117b55c892c569fe2", + "0xb83d55719e788b8f82ad03647a52e7772781a714f901acb3a547b621d59deb09", + "0x3c943ebac140db1360dc0c0d2a9b66713f6c57d394dc8c58f5faef83e45c86ca", + "0xb4a940fa7cc2e72bd3b6182a172c634378c2658ad36811f7738db36069ec20b7", + "0x9642379196bca32af7f3d4a7ea5169924aeb0956035e148f995b87f9deea6d24", + "0xa202b6bfd4cdba90b3a6cfbded5ea1a4f3db29157de7eca0391644a7dd50a428", + "0xe8c8da2d63f3a1010f096832abb0d18833dc7adbc4ef3048f221e56a07a7c70e", + "0x4e3e8c528c16fabf593c9724606e07e63eb188d283bb818a997e86df8e8ab36c", + "0xc1e12ce3756acb45b31096d4716f5eba2d7ca99933d2d7d47ba5c62115556984", + "0xc3aa16cfd6ebb092cb56945696b80b5ac0ec649d4586e330553cbc5af455db4d", + "0xec8b745de79e0b208c089e8b8d976049d56df379d5a864112e1c2bc77a27cfc2", + "0x5325be47e67cf88dd06254173aad7234b05f7466f8c08c81672c7f5d85320540", + "0x4fb8261792159453dabe895abdcccbbc11f43b923230fd65f8594a55a9672d14", + "0x8e63bf6410f12a159a5a30ca08cf7823b15827d863d9821a55723340985208e8", + "0x533c1282c1ec87ceec3b3b648ddda3c7c9575697740dfb825756d6f228399a02", + "0xc1030f1fd43f900b7b87545874a571b016a2f5d437fda2e2c8a9c959ccc259af", + "0xc03500df56ba7852060fd60865935bd0d9e4c0cce7e636ad3a6dcf015b66257d", + "0xa92e8e8e2b58be613694716e529ad96c652a30cfab50bc332970406bfa18c9ab", + "0x02d780f631594466116fe4279857b1f707eb78429783c41ffeaa44ba36639bab", + "0xe3c99597b7fd83454b2a812bcd650a0991abaef60193bf819846d388ff2e819f", + "0x5d49259ae36c416919cf59d1f4018e6d23ac72dae0d3809ff0b4fe7cced6eb4d", + "0x64da6590b3ca78503224a9211a4576f889543dfa941215bb5a5e2799913c9a73", + "0xa32ffede92e6abe428e62ee084353921dc41c91a45dbc84edc58fecf1aaf1efa", + "0x3d00b879f74ec8d4303ac685d2d36377554221b591b91665ce3d597f87a3a194", + "0x580f890d11907d857145add0d0aea8478a67492df4a4e6266f12d480723627fb", + "0xd0d395c6e8b80d34cf88ffd7f65adf788f1b7a612aabe8588c71a7dc83c280e9", + "0xccb13fa9da8adab2b200b8a530bc979a03426f4b889d824413d0b168be45673b", + "0x8252f094d6eae3cf8dd2e923a9039ba9e4ef7f031b7de3f2c40dff1c9e6a0fe1", + "0x183212a23fb3c21602a04cd6353a952c415b0af5bc61ef31d16ce7ab799e000b", + "0xa07b27c933f0c1818838c7287a6fa1e302f06f212723602cb4bf31e7b9b0c0d3", + "0x367dad9e70f6f9f342cab5b21dd331c41f4a7476f6d09c9bb349b18248da0ece", + "0x898a9c76e7ce0d96eb1ea7571bd474d1d45fd780ac4db799d3832045c1d3618c", + "0xa09d308d6b29903621a906fcc42edf45f14680459d9de4c1c5001a8e2f82d4dc", + "0xd92d77944718c99776b89a7906cfed8e85523339e2b8e04deecb71291be3f8cf", + "0x23213f0d8aa46acabe4c415cfc251b734d82137b252e3ef0e836b0654ad7c9da", + "0x3425864b4427456f7e381c78cd5aa796cf737f85974e63349bcdcc677b6aef6a", + "0x64daa4349e56082fedbd956724b43492ed2b762569e40f278ac6e9739333efc0", + "0x627336edde06b04afe049295c30b941fe01d7d22a5e7d6ebc63c80bf03f6d6b7", + "0x137f49893e334be8c9e4c06518385914590a7ab7cb376b3ce9b911914564a491", + "0x22e0bcf9f000fe431247a2b17ec20c52d1652bdef601f56a42508841648e1efc", + "0x1531adbdd3f62f96816a940436df01e57276495f56460406214061c7475a7cd4", + "0xf3b736effa4e2c9382336f13d2d5b3396cb86a78a8c25bbdb5b5b77c840f5fa3", + "0x598f2f48cb42a270507c5f7fa26e803dc2ab84a208d5dc1e37b13ab01025f6e8", + "0xe93f3759d3cb246aaeaa8883814706899ab09b6072f1886d008715dfdb8ad5b4", + "0xf6c3de3924ba5bce90f797906a8b3de37a9e9a0f9e32cb9ba100551c896abbc3", + "0xe0abfb65559b0acdef7a030e5866cf7e1730321cdbf7d8c7da366dd91165a361", + "0xda3841dbc8685c70718ed62c78a0742a03174d579796556787a850814e4fae03", + "0xe6ca3bbf8f46bb87e87d97289e3184e86c0cd86641f86316243d8960ba2fcfea", + "0x4a10cef55932a2f7b2f45db4a57cd77f50b58d4c0f24e27d2a35b2f634ed2b1d", + "0x8538825d4710ce710898cd8ef1c8b68223ff4fd92117c6a391749636c280db7d", + "0x44f6a21edbc0a826fc27485627edc13eef1ad09de5d0092ead3b55729485b27d", + "0x0170451a1c7f0fa7c42e846b6f3ca97f758414f21a86208657e809ba466061f0", + "0x199b0163dcee5e9416bfaeefcc6be97712a71996a87b90e09eca0411e0acf368", + "0x98fb368348fbcade40f334e5430d0311448a55ed779cd275ea93566c2fa3715b", + "0xb14a759ce8777014e4a2b8b1edf2b4130b1b4bcc5959e1fc87af7985d6277bea", + "0x05d47e48cbbfb47ea6530d605567209d005e96ffa8f63989f1eb6ee49b825c30", + "0xd523c3fe657096b4f9f3f8320d7b6e888912b593c20d69e6d112eb339a9e1b74", + "0xade14a55f0a84ad50542919860795aeb1e9be43cfc051e71383d13a8c680f784", + "0x64ce0a9b9dfd849c5f4e369a14352e27d7c2b15da9fa0b0586d679da4b1302fd", + "0xcac58c0e8f55a18ee85c3d9d227c2ec2355e65775834e2df6afd2db3a2a9f03d", + "0x489bd085e57332e7fc725254328a77547ddb959fdb9ef46428903762c22d5935", + "0xc8c09a8da7c0f09135bcf61a5b816d3e484b0f1da47452845a0e9824fb467ab4", + "0x18439a7efeb9f6da217dbe59222d02d358ae27b39e404e681fa86e59b1e027f8", + "0x9764d225155200395478d742d420b098ef2c20b42a2ecce4f6f50c1ba7d12ff4", + "0x23759a2a525272b134190a52c72ed39721af82e79ceac8dc22036efed6dc522d", + "0x77f1b1ac13a00260b21ea53620d15f2c5051e9371f46fdc70b9f42a15d801b3c", + "0x3965c7f59281867b1235b374b39c88f396fbdc1ebd42d790cfd6ba54d6cc22bf", + "0x33c593594ff6efe450aa88a9136d6810f4c2d501191e537364e33f415fd5fb05", + "0xa16799f5ce2aa3eaa0afdced1be23bb7ec0bdb9c8127973d5c1332fbeb060339", + "0xd590643091d7c86fd6b5722c81e8e35e3bdaecabfd93aed4e5c8ec0ffb9525ad", + "0x0ea1d0012ee7679cf4897ba7d20108373b58774d61c9a45ffb5b58793ee7caab", + "0xb623554e1226e533fd81e37142e51858b3979691a4f01db8c92e4151b9cf4762", + "0x1beee3130c1d32e6760b570671ea81c34932f81249d15d953672bb343bbbc506", + "0x127f50564c242100d6eb3bead6148911359d3ad46aafacf9f22761537986364a", + "0xd05dfedcf6d68dd095c8fbb6d194f2166a97b4c9bb6466b23d606529f0945215", + "0xe265db01f6a9338e13ba1c78c3feb1015bcb93d48b7bd8ae2e44ca354d6004b8", + "0x3cdd29e514b2f8d2e279d009676cf8e858924d07ee8f9002890093c84ea00bbd", + "0xc0307fb9e6e5c3db461c1b4404aad8e8c76e0bdf830d1967d890c5eba8babfe7", + "0x24d67b48bf4167e7de9a46f876fe77fa61bd84667c7f9e3f527a1bfd1b8814bc", + "0xb7a86729b1489eb38e654f212a06ba2bfd71e7a02823e62bd7425d4ee3013143", + "0xbf34cb840705a49260618e6344033c75e26fa7b5dba25400309087d144cf77f5", + "0x0fde2bf47370fcc707cc68ec722ce1ebd955d0c7267ff53a1ab1ddfba28ad12c", + "0x31e98b123e3d153e1b8b48feabd460e15b50c40d554bb5a25be787100df588da", + "0x1527a6a3292b477fa9206703185d7d941316d3a75434a67b07a2d2dcae7d084e", + "0x6b73975672cc5f44de995aa5ca13c11d0dc8506f1b56cc4e606a49a5aefbfa38", + "0xfe67334080ee75b3686dbf4bbc5b5343f5ea5a8079100d79d27e73714172e27b", + "0x9b11c169abcaebe20312deda8e465148f89c710e0b5345174b2ed4e57b4f551a", + "0x49f11c6ae94800c126c85a7d19ad5bfac01c1fb8e7c69c4c0d42a19b50b0c89a", + "0xf11e104f7092422a695276592a99b7b084395360521b98d5e45cbe13a5ea1d1a", + "0xf63e56ecc238362ff1ca6477ce7ec241da6f88c66261687eedc457728b73e4a5", + "0xf2f4aff3ba13d7f4c3754f967b36149217b2674cb32c4c86ab7af67a14e57400", + "0x91343e35083c7d9d1eb361427c621bad0017870004bce1168a4dc45864cc6f65", + "0x23b8a3199a215b77cca2e66b226311ff8079b40459991fafc1a2d5dd5ac203e6", + "0xbcdfca0dd673a3379fcc87ce19de6710fa89a6e8f9b1ff882b7e6da506837420", + "0x17d722dc43d08e36af7da731599f14c5af03aad2bfafb19de1596a51168caf2d", + "0x88cfb6a57a0e5b5a0b25533aa72f103e1163e6b245a7c9c6f4c3975e6c6a477e", + "0x18ebaaefd7ea7982c03b070356a46aa1fc9f3165667ae965f9e9914d7dab97f4", + "0x3d62dcb9f259ef429269eb549ddba4686c61f81036af2b5d15ff58fb7f1bacdb", + "0x6bba2ee873d99cc666f4c400e2ee83560ba071f204d976c840bf6ae1abab3daa", + "0x2570636b0e7554af2bdb8768460767a9ff7ef6cd4c77d6e91906548f98635536", + "0xc93c109f1bcdfe3c426b82c0933e4210aa7e8763967ed8f76c1fd646bbd86c55", + "0x9bbbebd878a37d8e499090e87573bee9dc7dde890b207669725a690b8ccebd49", + "0x469b9e388ff42193ab55122050e0b54e266b4cca917c08684f8bfb13103d632d", + "0x3c96e20e17bf8b1942a8baa482cfeed5ac29311a7fd73d5f670a1bb78615a3d8", + "0xdddfc424bb63196ef7053f5bb6741e4dd7bf21518c4e8e854d6af92e29a369e2", + "0xaca5677f07a0965846dd194bc8d67433b55c7719fcb796d27576ddda5bed1398", + "0x9dfd22c000de0dca9dd4497f11f4da618ddabbb749dca41b6b1f21c08d1586fc", + "0x1ddf735cdc6b07bf8dae602087e6fc15328bab0c802b452999e89e7ae1928443", + "0x06af862af4aaeacd2fdd62a3cb5bf6ac9ab0642ca531addf3f891a3eb8ae728f", + "0x39abb4a02d6bb2b461a97c96bcf18fecc714f959e66fbb434eeb1d549a097cc5", + "0x211ee3d710a8dadf9096a763f07bd50af8802e3f82354cf58a6cd6cc3332f428", + "0xac14feb974e74c92b52087e39552be29c8fa5e0117ce2024ff0e5bc03bda68df", + "0xf6643200f92290ca7cce54261d387e87e04da9118d0a5a2a27f1f93da0f5177b", + "0x65f0836b623555499785b5e153cd81688c34a7955c7788ec29f9f98a22262d16", + "0xf03b788ede14e6d5689aa54c06d3729f0c163c68cdc4b5bc56c9ab82bb0b5952", + "0x3322ff7bbada29f12e66401132f0c944f30784f4b7f51e39aa5f83edf67fbca3", + "0x2d1512ee7f7ee80c21b58168f226d7877d7737d145f9daef7da577eba35a5de0", + "0xf831df4615cea77bb8cdfb32315c4a85bf7dbf69a764980436aa971643a0c87b", + "0x77ab889c6caa595c7691401afff9e29a7979a25af9397e3b65e21b7cf30a2b98", + "0xc3b649f894dce7172ffb8c0f74801ebedb3859e8862d8dcf21bedbe1a549fa84", + "0x427e10d25a7bf59cd3ffafad41ca9cc6f8dfce71faa91547f33b62112f72a2c6", + "0x87f271d17d610f3fb0294c6b0aeca4769c742a94ca9547776d9ffc3d73e8618e", + "0xc947628c4e0980436d7f8b8cd45ae216404066a680fa7731ce77bb0759cbf672", + "0x35f82358041b198595b65bbaf4b94d61a08b258ec2c01d708888a07e0ea71162", + "0xb52775c32787ef7e559a33124e3646cb7877b46c3e93c2645b195ad7d96f6f15", + "0x9312fc8b7b174a3f1eb3df18668b4b5149c947a9d715201219244385e7423e2b", + "0x83c7de078bde715232e8bbe7573b29b938d16255e1fa014060eb47d25d370fe7", + "0xd35709ebb02bd1c81c4752f01136c8c692befa9b3eda65acfd65d371766789ad", + "0x1d5b2b02ec85c3dd5c5af6e95018c7eee647829c78c7bbf11f435b03c1132480", + "0x8dcfa632bbdd05f019aa180f0f18eff1ddd152191083777425a0712d7e7ce6ec", + "0x389da23f51a7f0513933e954980534f7072e5b2a23d40c5d408b572bf8f7befe", + "0xf9a1ec1e26f67e7bf281635309b75456a6c60f9da62684cb9d0cc802364700be", + "0x91a9fd34a2fe395ee5e863a73793fab9768eface74ebbb990dc353be5803ddb7", + "0x4fa5d039963092afb03a5b14446c037078410ae251067ebaf5cad37aab83061e", + "0x4c2ff424c2176cbe6719d04348bfbb085ed249cddd706c444a16998bf9daa1a5", + "0x374c0fa250d54cc5716c7a78e526e164948427842286e6e49e8c6e705d96d3eb", + "0x860fabdc3ddc5819bf5241b3add27e5bfd3ad2092b6938ef3cf21167d6c01b2c", + "0x14680f3f47d3312e0c0f951a388758b2ef0d67d4339af48a1f8f9e1bdf2062e7", + "0xb2e10944435c0c07225ec7f946a0b3bd1441333cc2b33eb8b56c3f8d227af5bc", + "0xd0619af16811983fca09ab0e8380d9ae03c81a4ea1537ab6d3b06ac728849d21", + "0xd83b003074eddc121e2840a99bcb1090345aba381123efe77146195aab522703", + "0x22ec2816335fefb53b994a40c614e1b69a3dababb4d28b00b87de4fdc6bbbf5f", + "0xeedbc58d570531d462d5c43c8a4cfe3c73a5547a65ebdc86b19633c94510fcd3", + "0xa1bf33b79558f781ab97cb53bc3eac534d466276a71b6a69b8e7c9905bdbd5f8", + "0x5392f083cc3184947cf40543be692c0f488a156d55c266275047c04131534f20", + "0x91ff7cab0e7a363b95285d44c24b7ddd8b5af04ad28af78ab7dcae8985d4fba2", + "0xc96ce9c919a7a41c26193587a51de4735523d851d74430b1d416fd21925ac0e1", + "0x9db9ef94900f8ededc07d34bedea3872cd05d41bb4b28c85aad59bf4fb482344", + "0x9553b3afe56aeafd2dae1c940c3212cd78c2d86ac6a2b3f4ffbfa24538eb352d", + "0x20ac5a4d7b594612159c409ae91ba58518638781d71025431a7220d0af4cc8ac", + "0xb614c200fc581787a40555150e560a1596838064a24533d9433f68cc25116630", + "0xbef79c86e558c9824f66fa47070a462932d64037561127d99f1ab8ca1b651c4c", + "0x17a05ff846d0d22c2c6fe0253e1ab257587373b883b113064e6464e1260fbcc6", + "0xdc799f63c3d7ad7a1f910346e0a5893a39eb6a48c7e3d944d2dc4fc7244a0cac", + "0xec41b1e1c46bb78d1cfb57c165a06f7fabe45169aab753e91e9a66fd704e8163", + "0x30264833f587e3a50c4302410ec7f721ae83d50224fc5b9c6f5887bc3cd1be73", + "0x225e3f9ca671961218bf7def18406912c30b5b318f6de0d3f1e9f29ea9cad997", + "0xa31c63ef12589242c2d1329f8fd178970f7a77de4ab1451766e653131ef016f7", + "0xb5fb727bf12201ac445f467297f9e05b1af9184147e45bebb08123a73e5467a5", + "0xd193fa5636c7db8341b73159163f18317c3578b29f9bfe39703e9e8e9ec6d2e1", + "0x517d465e91d53b91bed69e299dd8bafe29d4e5a89c776a27952a80553c90328d", + "0x92e297b6b635f90297f15616c438268c48db257e03a810bb1d148845a2a209bc", + "0xc282627a0501a15299a264153d62ea4686300b9539f5f6441111ff801a684709", + "0x170ea59c1ca40bb240b23fcbd685269a8405d474c1743ac97aceb2fd096902dd", + "0x2c9e288b99ded4a47c1c16a03ab9e57f629555b8ab364938f55ba4f9f0fb7877", + "0x4d9fca90df64ee9a6d43ba6551bd846bff01e56c2d714ad8c2dd1c50ec5de74c", + "0xd9eb9ce44af9c017f1cfac3bea5639489c5d12774694b85ab7ff6f18aa6a14d6", + "0xfbcbecfee326a780e9572faac0ca1e6f22066e69d32205ac63c0e73169fcedd1", + "0x8f7ac9a739977e19cd1e35aaa9d4b63ca384b69477a8a46dc843f35aa508456b", + "0x0ec68bd3d35230e924fe3f31cf3c414a444cd0df9357877ef87dd5b3aabdbf37", + "0xc579d68023d7c1fd4d480fc0e9c71f2fbda5a71f12fc5681433c34a6817c8ffe", + "0x8d906b2f511f1ef977c3315133f6a68c07fa25bbfb63c8b2bdeb8182cd6be912", + "0xd79b310b4320313a6cd65255039822f9486564930f39cc699aafed5f61116b52", + "0x23bb2ac91e996ea5cf304fddaec700359015fc489a95ef05949021de08cd8960", + "0x7fda543f539f90f1045b50cba5e5f76f8cff386308ce79e3d6583d6eeaff936e", + "0x35b1634cdf096cce8385eea6ec1d619024cc44029eb4ded3024773100919a9a2", + "0x9158d8f9f94972a621bb7189d90d9e197fc11cc5c4f22ef44e0d66dcb6625509", + "0xb5f9c2f893cee8f0fb08288a6d2e2b6564bffdf39f64e4fd932fde5975228ff7", + "0xfbe9baeb4e0620f4ec6323cbc7ad8aa589c133f7469a5ca39095539876534ad3", + "0x33cfa89534e765d2ddebafeab05213079715f2ae6ac24ff14f06f56545901850", + "0xca1bc2a654c87117de339f1219f507fcde84a1743a616562f5ae3be08d6b627c", + "0x349f56ef0ae3d68ba74c811d582171868a4dc0985a29bd4b1f4fa6e8972ac7e8", + "0xa2d7d3874eeaf658116c5e3aa15f1eaece6d09142207dbcb324adc5f07af3a40", + "0xd74d2cbb78c6d0a2d476e5fc7836678a17e79f71acbb3d569ebe8dcf6f9625b1", + "0xcb5b24d0d25f52fa92099c030914d6be8111607c5d96afaf80366a7aae11e438", + "0x376181f510e429df1897f129419284b0fdcb986319a903cba1c6e1a5cefc76e0", + "0x82a19f9109bd4f1e9ee5e86c151d5efd92b0021ddeda0fdff7fc7e7376adda82", + "0xbfd294713aba0ea2fda0c8e05bf0dc3607d7ef56551e93f02bfa7f5945a613b1", + "0xbd9447e3e056b4f5abffab6cd96fae839863e12ff54bff8c233c6e3ce9956a68", + "0x3ed05e21d92ac0a7305a660a160a7167897c8dc8013ed407a06b9c9ab6569e99", + "0x7d83d4f0585b0c728dc6db96ebf38804d9125a71124f4899294a0d5bb0a0dfe9", + "0xf43a3000e6d3c0d2ce929f7e3c6dc284228c2ec3df30a3f8e510b418207921ff", + "0xa8686f71c1208d848c67e00f71089ca64097107471c0bf60f272d2dc05cf97ae", + "0x35b5f6dbc3a076720c67890dbdd8901fe62ceb972e882f2b4ab2528b266bc227", + "0x01637926d7503a14b5a80aef17b108a580f089601c161041a941a79ffb092718", + "0xae24740919abed5d0611b39fe1498f9d741fab46fec56e0b4e66a62535330818", + "0xfbc82f411a5d5cd7f2f14e53c23785ed00bff0ac9f61fd0cdbb82480876a4c23", + "0x3f1a258af805df5199b9cd1b82024a0213fbee796e1416c9fbee742c42c87375", + "0xa443f1e8593799e7cdd29eed1b3d488ad823e4812e1dd3ab630636c16180995e", + "0xdb0ea3505cc5f6c87c598c104144ba6ba5c3f92c3d24b6642608fe8fc6f89dee", + "0xd012f2aa9a3ee3f019aeafeb7f8657e098ae07e79f5d06a2a43312a4681d294f", + "0x0eeb8157df2a23d84d78cd53e9ab9280082703ec5dfb36e3df67a9f623357d10", + "0xa62c41df2ccb2930c9e2d4d9252afdcc73785d43fa92788fe8737ee627a590cb", + "0x1c600c0ffa50cdc4ae4fb35aea542f70630e958ee205a5d56c03fea744eb63f0", + "0xb676040b492509afae818a16b9375df42a531ef51620b60f29fa091afb43a292", + "0x065ceb2fefd8570cd3c6a9c1a5704323d83413eeb43990de3fec835a380446c2", + "0xd182cee277216f8184c90aa5be2d24911654d6d380c1bac01e27e95eda07f9d3", + "0xc44a0e42b5d5f13a3d974d67a34bdc66fb7f608327161e3810661bacb6eb3f52", + "0x69d77ac1a105ce059ebee6c3438c6304946cdbfd9ecc32dde086175a17d8bcc5", + "0xcdcb7e929491c9f738d405e7442cb9635adb5b60bba9a74a915bf9e8a3d03855", + "0xbf8f84b0a7d1bb2e05541c34a09b2c29fe85fdbdc96a769243afb0e7697b66c9", + "0xf6585465a2eb851e4cf0d7c9648e4455e08fde57c8445a7702d8a0c739acf547", + "0x69aa242f78a9dd6c9937d46084baba0cbab215bdc50aff17cd7e5acaa91c1e42", + "0xee67f6b113134eed4e7895a94aecf66291308ba6df2d8c6f694ae7a107f23c41", + "0x4faf43d4a966c6f415c164eedf5c29d326d24397ddb463f2290a30ce07d6b5c9", + "0x1696980ba7552e11bb281b743e7dfe448b0550d1f125bbcf3eb685b9fce4b745", + "0x0c246cdee666b9ecd1543884b452e32ba7776b7338c653f7ff9822ebacdd0992", + "0x36a8a294ca9212404977e1c27f17be3d7a2d7e35e69dd1b3fa85a67c853fdd25", + "0xa2c393f539e7baf03a39dfe5d29b27ac745f8b66a306bce76c70c9c6f46e1df8", + "0x46dc545b75fbf2383558ba53761d79746ee7b493c0ded566310e9011eea3b96a", + "0x845a3d924dcb64747dd0824187242496cdd875acc4246507e4808e72e105128e", + "0x0bb6f2b8a76416ce2be487e7d2748a15adc0e310e3f8cab97c455b2627c30a4a", + "0x4dac916d2ff9f2f9ab1c9e258100d07dff6740a32aae3d624ec23f400bade8fd", + "0x100206635e8372b6c6f33259c07d41a9a4d671528f73d93cbcb1b1cc2bfad64a", + "0x1143030a8f5dbe85db029eb182e50f4c417a06623db74c5eb94d330f5edf0c56", + "0x88c050b118cca781b01da172a1483b182b51474a7e27b6a125677f14dd82c884", + "0x3065cbd5cfce358093e70750c5c643861dafa7897a10fe1464692e48e913dfb2", + "0x0e36d181faf0243ca7296bac1a9e10b2d7b32a15de5e5b1878994a09d6caa555", + "0xece3c7a54a215eca74c436f15f3b7808325be63740b1a6df3770e115d78bddd8", + "0xdb85a0f2a7534fa69fa23bbff73ad0eda07badb8c8b8db933eadd705b6829f4f", + "0x4d2a85a648cbe0c0f3982d634c236de5f2079fcdfb01839053accbcd1000b9ee", + "0x9710dc72710d26d6766c3846cf73e02417cce93194a51c0c06a703ab64bd96e3", + "0x1969a1b68e222d9e84a4207d0e27cc5176154909c0ed625bf5543460ffced166", + "0xf03fe1e999728c40c63c433db3770eb3785b7dc7b0002255fa8ec980e9b3dc2a", + "0xa05681f3f3a37d24473c4f37d77f597a2a507765c9481211dc9b8d783134c3e3", + "0xfcb2f7f74869afab1da5fba794c0aa16ddd72b71941543afef3ad4ecc05d9490", + "0x062f6cb9c2489a54c54f6f2332804593abc9d3dbcc6c24d7d2fd2b8753e28c2f", + "0x743bdfd46faa731456307dfe6d3a6bc16c9cecea6c974f89768bc197d19400ac", + "0x1df32ae0f8e4b29e1a42099cd4718eef70a2a1666635002cf2f869b95d4c2f99", + "0xb627fea4f076776158857a74bc7140eadce6b3ce025147e6b7c763f348c984dc", + "0x4716267853c13815406a4310ee3df841b97806e556e4390a5081cb504d42829c", + "0x5430cf1bf0dcd68cb167d32a6638ef7fbf93415405c239e2ddc3e063563935cc", + "0x79e7cbf4ae19c92cc8090b6c994f313cae3ec733b64b0759454d4f65acc8852a", + "0x3002d5e0cec56f70007913a90eb0acb7f170d477fb89854555b38c372fa03ff8", + "0xa220f65cb133fe2570817ab22b9fdd5e68a8123b71c420d50d2b0c6e9a230318", + "0x915c8c52b70751872ca5ada8bdbcbac8c18e304344aaf725327e754fc036c7be", + "0x1afdb8747bfa7baeba1d095084f66b7fc1de9e54673390037b6aa1374a945710", + "0x7f8eced2beb9942fa882021e9da6907088cf4a2c00d99905f24cc10c67e3ec8f", + "0xe844112b2157615d01ce0287fb4e66c271738017bcb7bef151916e58be8316ca", + "0x7102d82f4824c2f2dd66d2ec2b80b6ffa4e9d0b6356154942531ee386c8d10a6", + "0xad023afe83f56c45b4b5f5d7ad5dbc89a224aba514e102ff90e502e060e21152", + "0x17c0d59af21f8dca69b356ddbd56c6af8e452ae98776b445b21e1a5c2cc2a1ca", + "0x62fb0d711f718b8921d16715fe307fc4353c0431ce4f785f3fe6e97ac312ae87", + "0x4a2db7db4388de5e5c3cee089d0a9006f991daac1ed37aeab5b2a37426a0a7ef", + "0x978843368a9ead926c47df34a4b5ff8f12a0bb70fd97f12e426b2b2418a5ae9e", + "0xd06f1d857822496c4382fd33a98d53f043f888b1efa801aa1833c1a5132ae9c0", + "0xbf902b8b20cb9dbb223134d58c140ea5a51390c1fd2b050890186d21585942d4", + "0xa3df2a1f0908728461f185d38bf3174c71c2a6b1211931d863a7174e960ec629", + "0xc9bfd17e4ce37cf70b4b258ba8562c50a7a8c01764536fa0cef9d468244a4e0b", + "0x302743601c1a90045c4199d192d2ca93c5888e0def86060d9ba909af84728f98", + "0x724e7c609fc7ddd2fbd03e86c4b776bb25f3533cb9c8400d6496649e1ea468fb", + "0x814e96c79a3492e72f17b106b83900329b379f521a31a69e3a75526813a9211c", + "0xdbbbf62e3e30381a45d576f1bb75f6abf21915c552367214cfddfbe8754ff948", + "0x101a0acc815f2aad9441343f8fc33f667838cf4f0d6efbf008af3892cf82b89e", + "0xac3dc0a5582ab6fddc48b04b07aa62e765e2b5b428b01b2e9fd1863f2cab45b7", + "0x304bd2af2cb9818cfaa093a254d509d245287c74b7452feef939dede14b4c843", + "0x07087566bbe6547dc0a032fa3120f80fa80d5017823ddfd2d8c9e3275bb7d0ae", + "0x8aa0c5f14066087871e23234e1fda2b110b6f586c35cbf4b67508a0592545de6", + "0xbb4b58d0b76c3bda478cc8d8b8bcd1b0cfa645d082f09d7b2be91c672a6158dd", + "0xfb6e1e89a6cba734e65b374d0739ecef2c9d117eb067e5633b439a039b8685ae", + "0x7ad322c6817d95882b84f4c19b403d848f0db922984f51cc4e32f090947ac95f", + "0x18be0ba65346b09e61ddcac2ffddada86443fe738d3e9c291e87a082b6b9793c", + "0x32b103c1daf39780aff04024624d04d1a8b5c77ec0160ee06ff8525a37f2386a", + "0x835c419e89728ff10b5d54b443a7d6a876edf92365780bbc3fdb77629874d8ec", + "0x8038344e9706986e134a9d78071f02ead20bb72588662f518d89c64e6ea27585", + "0xaba844bb2694971b4282aba84d8f4acfc82e324e75f1cff03d347a3b4e896668", + "0xb1fd642e40254ab34f44c0f9cfaf2d2a4aad0c2f41f83bdb612c82d97c05fff7", + "0x0165ff5b3e493b02cb182cf11c1a0d34b558a591954ccde2e0e369aa7b7441cd", + "0x795b44d4e6c9f437535f8c1355bb4c0a84d22eb597c1e41885381c9ec437dc53", + "0xa50ae212ccda16b5d337e595cf96ce72a7361487a99d40319e7a40ec14f63edd", + "0x78eb96149dd476a570b346acd1b605e30e3914cadb456086225122e4a47c8322", + "0x7c4ebdbba11a181c8cc152351ea2928d49b44e59548e5d895aab7fff3aa5821c", + "0x0d1a687e0d0a40823713d543111b0be2e6004830ece66831cb408cfe6c6c2d61", + "0x5d6af31891b8602b295ae45451d24848235f974adac6e08e1da86fd1b3cf6659", + "0x7d1387ad1b2fe918d4dcb9280488cac983055c4aa3216b68fdb47300319347c1", + "0x6d7fb920873ca6c47e7f4f6e58ca20eae96429409009504beac92bb41f1ec4f8", + "0xa13681d22f2f68c4b1c97b8601243bb77529d8305b2d0dc2aed0d4acac5dd649", + "0xdb8b4aa812a6ef528d2daa242dba52679f2ac36a3b6c76b2afc849f482f0e0e8", + "0x2307b0975a5e2c407a15d62a9f60694ed0a7612c8f1c951c457c976f82664b95", + "0x91cfa4e75fe236e5b528af74fb2e19732a1ea65572aecf76cbdbc2cb7c271b15", + "0xe6ebc910429c730153d0e69838dd321e69410f3351eaada6fa4aba377030d5e3", + "0xe5c80424e9283eeffba1fceca10939d80b547a70e018b01d52a063d356abdbe7", + "0x8a0302ae20063098fb5a865f964b9d728184567c74a2cd58cfaa045c8f7cd443", + "0x30e2af02324fafbbc0964358369f74424a6bc9023609c7bb6f85f8df5f7500d6", + "0xea3312253cfee3a3a52ca5da425149a157a2a5bf3f787cdffd6c22384760804f", + "0x01c3df4f576f6129ff84709db40ee8033d62b617b77715a2a51fc5ae28c5ef9f", + "0xbc086b1509aec81dca9f1f33437926ed6888e1d104ce9bdc3df06765bd34c56d", + "0xfc69ede3b8caf174fc57b0fde2db76fbf6df4d9b105d7996e0a79e63f1605673", + "0x8e5a293668c5d610ff7324b695909530d7d0f9314977b01a5ce2d10a2157b1ab", + "0xca6eb065f9a08ba7dabcf4901b6c91f242f928f483878c54c07644bb673c9b54", + "0x601d0d2f7d4753bf3787f95c3c6bed3bbb3ddb4af2769a440208380e6fc7404b", + "0xfb858d4fb2b0fe34e45921d6a45d683015bf624d0349e19bf06c8099701a8a59", + "0x8d39f16cc1a2dbd64f8e5efa67a2d4a6d2503568174d692090aacf4ebe135a15", + "0x1322ce095367f8288cf0f5ea31d7baf94f43de6bd5f7bb6e09732812c913c51c", + "0xd7c444b28d3570b56bf962cd6507a3e9cb0ec6b0336161705daa89d14731ca7c", + "0x80e30a7cb537a534d26804388f0d212e6119186ba2d57b397d77d65dd63b0f5a", + "0x2bed1ed29b4f6e5c53aac9af116858650e5b3860799c1587848e878fa2d22b98", + "0xeea6cc06197ef5b2856c2f8a4c75aec07c074de3fb9b287be59381fa7e9a3d74", + "0x354ac819fdc0d4436506fb2191d2129b87b29a1bba9e184bbbdc899189c81a24", + "0xcae66195e1f3e61908b60abfec4ebfd3e3c9c325b5f1e8f33b6b64d2488f81ac", + "0x86f351219805912623f8c0f557e20f1e5ce51db372a6ea331006004fc8cbd402", + "0xa489a037af1594ebd3e4de11b29c4f202edf30375e36855cbce27afae1afacf8", + "0x72accbf762f09dee77e64acb4da4a0fe98436ca687f9c8f370a814babb681c34", + "0x6a5f77b8125ec717e20d56acd70b0d6396b6e9119f07564766f8028b7ef246df", + "0x6a904dc242f6c95a2479668f1bd2f913a7aee55de5f178f9b954a998a87bbfab", + "0x060a22d7f217e58a4a09ad9d7b6abb91df24552ddcde863ecf4f1e1462e371e6", + "0xbc59924f6538c59102d13bb4d6119bdd0efcdd55f99879814b4b424097c31f6b", + "0x6eba9e845eba62be156ab5b39f9a10f1889910848be1d57f7d1661ae69873d13", + "0x8b479066cb2012d974dd2cc36ad15147d5b52ad17901f2d987165248750c8aac", + "0xf12b4faa8c7a04ee90a6f1a166f48792ccf39622f414e2ea4cfc57104f8a31f9", + "0xeb433cc67d17257965e28f9f3504a3af1e2131d432f65d55307ffa909d2e3ec6", + "0xadbe4060badda14e4620d60c18e5d44947526ff6e3ccd137db48ee6861d80319", + "0x96f8f852a74eac00f0af56da76c0398a2a41d70fc35cb347e24bed59a59b82d7", + "0xc9289657e680eb7f7a38ff081f31cbbc0ccf17d46b639f540964f9974fbb28f5", + "0x54ae013babf66a3738df29edcd97146725e748680e5ab13646cb37a2c8f5209e", + "0x46fdb10f512f8916f5874037138dbb6675d203e288c14973d2f8889473f4c39b", + "0x3a9ddfb4848d833685c3282e4b22c24ed20f6ffbb54d1c6cc20da4f110d32cf9", + "0x830141c7ee97d981a8cebac1a5702e9d763658cb0affbbb34671e106e41425f7", + "0x1a9d17e7760f8b81cb6701714f132b6d9cb55aaa4c2e1615d3a033e9a1620720", + "0xe6d88ad9823bf1a0ad550bf5b28701958d126499334236df3254467a3f3543c8", + "0xfb0583754649a9a6ee0d72faec6ca2e04b33882ce13e37364de55ac02ea8a50e", + "0x4b512db4f139a5942d6d43a3481c0d8a316f113e2c303a3cedd987cc37f0cd47", + "0x7e2bef471b343f363d034598602ac3a5ada0a861545de8b337f21a12f862e967", + "0x5d1fd56feb47548db8a9861f99aac894a1c88257a3cdc54d7e7c141a0bbe75b2", + "0xbe5faba5a6b5b89330c99f765b1ec23690397eaed4eb0bdca0a3bbe0d9f43865", + "0x962cbd5189be994174bffefe57bf73764c21dbef7717471d459053e94f7b1bd0", + "0xc12a5859a397e8f94de391a370832cdc638b267364cfa78affd6b6059daddc38", + "0x1c2f5181ebb0dc2d31e491b45df5ad262086b0983c5895e46d20a068c683fdda", + "0x8c09c4a94573bf0672e926fab6aa58adfb7b1e053c347d90128f19a9de8e296e", + "0xf987c85796ae4afb6b91e16af33a77f3d84bcbcf4c2c0024ed0e4dc22d9b35b1", + "0xaba6473c13c6c46662e721f8b3f19bd09243285f48312bed73685805d02b60d4", + "0xac7b6689932db4afba1c7f3f4563018c0cefd9232dc507b0f05dba71351fd4a5", + "0x3fe5a32d214ba40669ef82a847f95d4bdf4693a6317d683b5479e8eac0fb2094", + "0xcba2297b4e17735b9cd8adf32de0e21ef86e78184cc8652fb474d01571b8d34f", + "0x59951da0629a303a5090a91982f485c63fd42d13266d741274258c80351f3946", + "0x8302e1898f121fa5ea2418ecebea0d2087f3cb882cb0f2dd0f41816424101a5e", + "0xca020b1108f9fc9e202e0061dc59def487f94367bf02116cf826714569232829", + "0x9ce7f975185572e7af45c4a53482e819bfad2ea540dfb60e9f8b9853c6d0d6b3", + "0x45e16a68d246afd1a8b7b9bc71ca8946a9a8e489fb0c87cae0ee403df88ca410", + "0x2d304a1760e8fa0fa4552ce05f3b17f770ebcb6b3c1c11ed5d809cbb62d4bce6", + "0xf60b3da2c64687d73c01a094c3c539b650bdd7e5bf0d7e4962728d8dd0009588", + "0xd5ddb896e15de43761606bd465a76fefd0d72ce15043ef826362acc33e849e7d", + "0x9e60baad4ff4fa151ccf0129163be9194ccbb57375cc165b4c11e25262d5b527", + "0x5a0cba4169a8e8ce5dfc4a97d0707b359a3b19638722a032b7cae4d57f6e655e", + "0x53f1dfddd5fdd601dea5e0bbc6ac452c18be75639e5f4caa987848215176211f", + "0x8941cfafac7b344346a8adb16316cae21db95ebbea6b63cf593ec9cb8cc8b113", + "0x91595f782f44d2cd8baa0017d49ed3a902169814ef56c492b74ee6c6aef8da35", + "0x4bff54911f5e2d7f89a3980d9fb0a89034902a625cdacbe7abb3112542aa1d69", + "0x2cddd970fdd2dc86f65d15c369dc29488d6ad25e5101dc9a602127e670fb70f3", + "0x16fffbcfd9d77035b707ea66afa90699f2703697be167ff904463e7993ad84c8", + "0xb75cbaf69cf7e27faa9c8a9ff2fc4749283d63bbc5f925d853b25c51a89fde55", + "0x1f3a9b24aed9d528b6b488e57b8f71949811444c339e6cad24e0aa505f6e4f38", + "0x5ac28cd52380f954e2bc9f933409527037c24a03faf350b733b16ccb797a749e", + "0xc88bc6678f360dfcd718fe0cc9d57c4923bdb7d65a76cba8330063f13a73ff78", + "0x2461ce41dfa87d660c75db5d4b5d4535a53a1db7572af5b456c89c84d8ae92f6", + "0xb2a49b6732a15e2e13cb8ef98d0e4cfe33c0ef52828a82d556c8e3b4667e64f9", + "0x590d32b00930a5ec5a82c46584717d8517cef8e39ffd701afca21bbd76f25a92", + "0xcb37222228a821033edc73facf1c6e77a0710e3797ea0dfac0a60b4bbb1f3964", + "0x52041a984e4805c6b9db94275f99cd87a38055454f56579bd446ad99886e71da", + "0xa00f757339df8aee4a37bb594abf1a10f78f8342520e992e4302cc19113a3a09", + "0x3057737d78f335af9497c89441e0d25754b94ca735e1c073416058a94e2b1831", + "0xbd454f522634e1f48e4c45cc7e9c7b07075e8e823e31df06cc6c0a2bca5df6b5", + "0x5f07d353878b6a69219d160c9f13e7cfbdb3716a1ceb70786cb0bcbe59c547f9", + "0x975209fd2a0a0fbca6cd815f2b373c7126850b32413186d85954dcf46c107125", + "0xf2699bc28baf4032d46e3482612d00bb4c0c0d3734cee615f721bc145d6c9737", + "0x3b98ab1afc6ae0a0b573b2d019852ee0ef601c8373ba0791b65af1f86882950f", + "0x34210f2300919adc205aa3365aa0bb0238ee9ea5aa8f9db1d4af86b5e451c376", + "0x32feaf0415355c857526056b23a329e322eba6adeebc99207c35d9c1616ee474", + "0xa469ad69bacf03eb64bb6f9f6e8c20efa87047582b50a1fa5c28b2cad1785c55", + "0xfac2fc405214e53dc94351558d883e5c1c28868c980bfaca277b9ba409656bea", + "0x088d9b1a809d5b3b30c572daf2762c058af2d0f08d03154436d1c78d6ea91c54", + "0x4be7db64dda47f6878ac95c92eb46e6e7c752c180e3bc62df914eb20785be3dc", + "0xecbefc0cfa4fd41666ce8d22b939871d224833c830f7bec557bb07b6614eacb7", + "0xd569b7cc37e41c0c8548e4cba3d98e81a3ea093281f06759bcc498c271e2a3f8", + "0x3dd3d409a9d51ee9a385bb38ecd2a8f9624497fd74d093b90c23b589432e3b79", + "0xec0922b00c29f4c26b0f09191d4579ce4f392482479a810e9e95542f24bd3f37", + "0xd7fd81d291b0428156cdf4762927018aebbec9e5a4172cfe3cc3423540ed3bed", + "0xf5af2fc431d5b83dc4f2a370d79fa351648da46856c90df068c19187d314b91c", + "0x6145fc57b918d3b9778f0f90c2fcf05790e92d843db40a51ad1543eb463e102c", + "0xc444aff61b87b1c66ecfeac5cc44e61ceac33aa4ab407c72b02d8ac160967ab9", + "0xdc539cefee7d94424f74a7e33857af76fe204fac71fa7130b304d0f441365c94", + "0xca918ad18330ffd246a916d8fac51d6e4c085962349dec4c73fbcd396ffbdf9c", + "0x7e48052c9990ba0f784bceabafe070fb1a0617cccae29887352b43064f95039c", + "0xc3afee6d93aeb9dc83ac4341477ab69dbd29559e6406d31a1b6ba382d0e4fb7e", + "0x5de38b80de4b20b1fa1b2fa64af06c1c6e9f410f0512ed33bf29d3898373a27a", + "0x1bfc2827d9e45e6ad4eeb92f18ae64059b4ee14bc407793561a4273d4241f955", + "0x2b501771a0fb438134ca3bbc0f77806ab7516dbb7b0d87b7cbc8fb655455abb5", + "0xb39824d16c7d288614ae5776d64dfade88755a8e9f0e38b3ced5b1e452a3fdbe", + "0x2e45217c4e78a50f4462afb4abd97cc29fe505e94cc8162b469f974e55ed7040", + "0xc3f97faf20b6bf9449cbe37886543948e2143abef1c9258d72223923d1591e96", + "0x55cc4d2b4422d71dd100428d885b766bddf1fa2c6671fa2af2ea9503507a7b71", + "0x2bcb0b79647f85fa26ca6303366d35426b235b5f0d4c33e7b2cce90a6d5acd31", + "0x4747b29f548fb9f841eaa7de26efe900476f9715353f9dbf9a16a8d4e1d46d97", + "0xafcc0ee7effb1da2509431365c63a30567e97677b62ad5e21c542cfa636611c8", + "0xa49dc14f84e97f5e55e62669dd69af0c7dd2bfa1ed0142937ac0db26d2ca4048", + "0x9a75bb314e8d7bd21f2e88b0dde507a528d0ebbc16f83dfcbc7b9a30bf118117", + "0x7c8cba2212c7969c4636fa6c3a0e19b094bba2a1a95151f209f8d7a97d501e36", + "0xa8bfdbfef8d94dba50dbce55d9a085b456864aafd4f1ace9571c4e104df7bc91", + "0xfc2e8151d235fc05ede9fb38260d3098a6433d2950142a668e88491ee39a0942", + "0xa3f0967013e957d2af52a9c60b98cd9cb2874e69df3a66b14563ab77d6e9eb4c", + "0xf0c15585dc777e5799359d13fdd4fb6b8f6a748a9009fa0c2f0f21a3d3d985cb", + "0x7ac5b17a77ed0eafb249a48b7da6491cb2f18ea8744593b00e64eb76d95a0e60", + "0x2994ad155b824ae9fb11066308b91acd93a24156ae44eff726379422073bca65", + "0xa9ee668cc0663e6743c8186cf3eb34bb2d7ac63286b6602a6a37295f995e7c0d", + "0xd4756a6343db0d98fd48739497d9dea1a462aad9bb735f9165089b472e673e4e", + "0xc60ec5977100d99252e5ad656bbf88a583792dccb2b66fae2449f1d1f5e61a6d", + "0xb08c9166330a3a5c8d80dd1630787f0fd07e592a0a2b005fc8a6e0df5e93152f", + "0xb3132a6469de4fd4d8180e17aecf5c12248f751658fa2601e16ebc3632d4c526", + "0x033f439a3acdb6bfd46aa7f6a89eed53e53e850ff8c9619945255039efcbda56", + "0xbea6405856625117fd679a54825c566c6addf31dea2a8fe0d539177ea87a8b9d", + "0x312d3cda099aa08588089892eac13506b891d4e8763e213fa9e718410d1ccba7", + "0xf08befc4cd058c7ee4d9b635cf505e87540c8237719854723daa854f2cda4dfa", + "0x7258f043f963fc74bfa06b903413a389429e4e852a8745f242a54508f2d4a54d", + "0xf0af4ba40f06f6abbe6c4f85276dd65e81bb8dd2a94dd2baf33c9835230c54da", + "0x53b0ce536024edfa16847c7968b0f44a12f061642cf51eb23e27e4dc8f37c3a1", + "0x69ba8a25e936ad9fb39f0af6ee87415b090402a93cce83bf08b6792b947e28b1", + "0x6eb0ff589254d68a5920272b8deb870d3cce4196191de386bf357147c746987e", + "0x5fea4717b54953555ace73394bc3647c661cd997ed39c0de3be5445a7a91b6ae", + "0x45bb4496fdb5c32f2fb2af44ad80886e45a80a57a3b74919e1eb28f4827603af", + "0xf695fb7879831a86d23a79e6af7b956c84de9c9827cf2734d96d67e4339365cb", + "0xc640919b196406384899aa231afd2cd66f581ff1c43f434d01954e30680a76d2", + "0xec693180966986284e6e37fa5885459f81ddb7738709213ad466f3fc00cafd18", + "0x8aa95d614faca0f4745f6cb79c7fe2162ba146c2fe6f8dd9b9d663128a80cee9", + "0x35283bf621de028a94fa33eb10b501ed981decac14bc7a826df98c5fc9554ff5", + "0xf44c39fdf4d3496628bce02bed885422ef1f8c1f8c78a812ae780301d7344135", + "0xda11bfa2f87411b326b3588aa570984a848987084eeecb445f2bd84642fd04ec", + "0xed3547548b55d2971a85e23a0a2d5bf0b297cbd1d8325bf7881bdb9b3f9e9b8a", + "0xd6727001d67017df7a536dd2295c231a42ed0ef4863995ba782aec46385ee24d", + "0xe88ede32d73d6224f9bbefaef00f745ac5137a876909a74f69ebdf3255ee827a", + "0xf16e8c96e8cc189708e8e2c8fa1bcc81b0afda77f6088715f776e9c032d18cef", + "0x1a5a555013e5947463e7b1a94a62f599e85ff2602e19588e24e9e99c8039d280", + "0xb8c4f551fa41e7c587ab33ef078bf24537c359fbdde07f6003b80454d2838105", + "0x798f8f4faa6e73290e5e7adeb35f3ed8ea6dc151c45276a27f23cc70092c3a14", + "0x3a1ef5c0cf76a3f44715a714ef6e53a1d03fe0393b7c1e8668c0c1c0aa7be8cb", + "0xb784a35ee21c25a50f4f1d0b8ea54385991960e8fb77665bcacb4b7954b46492", + "0x250ca4d5c44dbd5436d02064cd9d62800f75ee121c606fc00470a94c68bbab0c", + "0x56293390012c56e7c2544aaa761abc6e17399498482673cf3f5c4471a7f12341", + "0xf78b884651f3da2c8176c150d05b80f046c69a2ea660f657e60ddaf45256302c", + "0x57c61097aa9f4f39ffe146a205f8e4e2f08629fc67d5c8b1194ac10215e8e956", + "0x0a9cc30399ac7cda111a079268cfeccca922c74d2b3eea582cb00e025c5cb5d1", + "0x7e79b0d9291c3fedd40354957aed5c56bbd36837b4c5a461f03ab85ec32a1b86", + "0x53bd2f652d785f2696a3aa9874d4233c694ced8b0f3633a8a85ab1bac5c5534e", + "0x42693e6db1e7ebbda1bc8c3d90c423062a305a19f036156be34deaf066ca9926", + "0xf3e47e972a6bf0928fcf3c8abd45cb4ca925fc1d4a554ea5860f06be91ff0ded", + "0x0b3b31e6205af904c42a72c99690efc0498c0ac3c04cf7d2fb3b183d84b4980a", + "0xee83788bda6e36c9caa11251b2b145e63d165abde945c761653fc11d1200b468", + "0x3e5afa51e6797da967a191d6776f1b10411680b9d35b53cb71b04caf9546946c", + "0x28da5f9812e7f9ff777bcefc50794c6577be99e33dddadb84c7d4774874148ed", + "0xd48ca3bbde710119ced0241fa81fddc6ec418ebffca712620433d8dc236c6691", + "0x90608f47bca7d34f3d0b261c8092aa3b0e9ffe27851949dcae69091e7ffedcb5", + "0x196351e1c19ee96ce018ea3c5e403037d303df2692ba0e0f1701bfddbb850961", + "0xa263e2576cca77a28739620ae507dd38f8566f8db4a2b5b3d5d6979b428a3311", + "0x1da1dc8eb91d9b78d68bb6d31fdf07da419a5f2f2a5c3db16380b8b6fd6e6a50", + "0x15bec64f799a033b26ee727685702408a9b2c1a331113c262bc6189fa33fd9df", + "0x45104dea87fc58c7d4a30f5148b5b880dc5f95f484a1d90648fe98e9844022b9", + "0x83ad948ccfd5e15b5945ea99e893b4a60130efcf59a654d8281f7794c9af8c01", + "0x8b69fa0883dc764709ce9043cfcdbe6aae5192cab534c23df289656a6f2f194e", + "0xc3dea784cca000e12c4233b576ef4b5a92c2e353a903a1cd2bd8e841ad0d8627", + "0xc39b535dd0aab7febe4b6c2219baf97d51f11eeb1941332fdabdef85ae9800d7", + "0x8cf91a51f22967fed2d59ae3ea08b14e60e81f455ee94b31437976cb4d3f5c7c", + "0xa39f471cf4310a5b173b327b062830cc8a7576250a4e1dbf527900488c5be207", + "0x2e8ba046cdc34a2dfb3d463a7db23345ad7aa8d2f5332b587990eeb9ca87959d", + "0xbc445fd943498b05b9f7a5b556702f35fff3a6abecf1c40b92b94f6de9d590f9", + "0x8107dba8ff62f79dc12cb63809264d753eca412ec56f4f360c56c84de48b0ccc", + "0xbec43c883aed8c93d67903829986b7bc43bfdbfccfc1a15d92bb5f01b84f08f0", + "0x2129a3a66bf89d68bf8f01647eb262f7c18c62656cb3343cb8311f0e62102203", + "0x03548f69171c9f493fa1444d56336ac7be929ec357e0272676443c18c0a3c5d1", + "0x359cb74ef2df989670d008e9f8f3c8414301eb3d98ca934e3e2912bccba1d80b", + "0xf1dd6517f47b957627fa8abbe1eb08ceb8689f57606e5c431c118eed966d66be", + "0x97fc7b7d41c8bb936715cedec87edeb5c2e9c783855e96727d4e600515094b31", + "0x1d8d8b93ba9b330665133feaee616b778a6d4839a60ba439233d98c5b1487674", + "0x6310aaf3e5f754405a271484cedb5f7bc70e6d557cfcc17801eb7fe5cf0aa2ef", + "0x340a8449316bd6432a92df5aa23e991a58d697705a99ae46330c62504922db81", + "0x2bf4256c05ad10a40cd305017417ad390eede449c17cc4f74e078e4fca226521", + "0xdb786071913271893532736089d771b523038afd5edf7d66b03cd10f987a9ea5", + "0x06de3ef382ac4543f3db711c29fefa66ae34c917bcc92a3dd72f2301c157b63a", + "0xec521d5af1354ba9eaf44c8b3273ce85c7f9a286a5eb901147bee8911f809084", + "0xa81544fc8bc7115e97ab237840fba6167f99e5729295df2e5ccf92d7dd20a322", + "0xfe06911ee1c255b737b8a95efe2f8be0000fab18ea9466aa424cead9f58e8068", + "0xa086f26d04eb066ad386c300b1745db61ac6016575a0081ee6d06d7043e5a200", + "0xa22472aa46531463d23db4b6206a210a84320c3bfce3f556eb95e21dc301a573", + "0x295d066dd165c34e6004971354835682d41880da3b2439ce1100b9d9c8d9a975", + "0x599bcb302caa2680337cac99140346d7d7a31bf6a0bbf67123716a62c93031b3", + "0x693f4a5706473afe965fa4632339fe418b47897d210c6141979b5d1b7b542bb6", + "0x8c66500c6e271e0034031c1854091089f1a4f3cd45849901cdc925c78dd73cb2", + "0xce29a847195c6af19265163c2a6e0107d3666f6b441d8cdd9f109d8a0a14fb7d", + "0x30b52436ecbbfcec9206cf6654fa62b769a31e49bfa9a64ca24d0b5a5237dfa6", + "0xae7204bbf982f98923dc3dfddfdbb23f9b6da477c7b77af1f56804afce515101", + "0xa56b70628bd133ad4d0c34ce0e09a53619b027bf08b34e1a30a5f67882870768", + "0xb337129dcbb79f00917d7fb1535984cc667addaa0e5360de2ea4122abd7fcb58", + "0x5fe7f05ec7b26795be83e3733b235c70d7a715fb905da5b72838a5324f80cb10", + "0x1f46c31b7c4eda0439b2af7d898bb4bbb181f4e3523552d39ff9d49860b5a3b3", + "0x7005495bdeeece1ca32e4c718317131f4070e3bc56774942474a587a5b7f3827", + "0x1fa1cc289ab0fbded5ec5a11dc0f1ff5a0c03190f1fbd39a87adb1d5c52b8df8", + "0x9aa35a2b079059425aa5bc83fbf1f93623481861006ab23fa862a9081959cfe3", + "0x0b7c74a86f846581a4fda0e904b6cf06b24642ed1017077fbd6c8a490e0f1040", + "0x48f0fde14473ae53a22156f2f6177a079dc103156b7ad4006c36a5a3adf75cec", + "0xb50a9661b04e44aa59c293d7f7b05125f953f92cba8e9fa3c1b2b4d3b206b21f", + "0x35f673a5dc93856be7ee680a4ad84ff1922709826325c261b399dca9d60def41", + "0x00bd584c857596de840259a072afb914d2b3e775d380b4b4e4b92e1b42a846cb", + "0x14da78adf8ace2a08a3159524a7c816f3ab7e229273bd5239cedbe6e28baf46d", + "0xdc9322c57d7eee45a5ecbd4af4489e848e3860f949378e0428106661cbb6bd8b", + "0xd2a63c87f3ab64953233969c7a76d8e3442efec4f0d29da9b62db1a394ed51a9", + "0x06e6fa970dea0468db5c6010ed7b2b49bd9ede7068d837beb5c13a69d8286fd9", + "0xdb41dbc9244b57773276dd3471567be720a33a5d70d416a102cd575b6fd9aacd", + "0x9ed964285c8a9f695d60304f51d293ceb4d6556700e2ef6710d54b95d485ef1e", + "0x683dceffe58698350dd4f3a34ee266f47f7d17938befe9b17751aaee103175c9", + "0xac755d94c90d95041ff26fb73510bf5c459e3f129fa2cb0c08b60c2002015ec5", + "0xbc2dc0050e8d9c777b90e897d863e1573f2b4ad625e8527a17e6101d2190e2d4", + "0x66c3e9e2a8daaca26d2b5ebdd3c81a54aea408fa29e34c92a478d78bdf2735d8", + "0x15ce7a3323572ff5e69f7a6230b408e03e66a8ce149cb900fe34da574a4d55b0", + "0x38dc2603b062bc287b9f8e27d00f1a091de3b9fcc5d80b879efe992449bfd7cc", + "0xa9fedb103c45761bb7a67235381b6a9febb0de203412fa62c0cddfa608c55649", + "0x3240a7f4463d42b99b4bf9e9ed377fe5291855b3f44ed384f8764767dffb73d0", + "0x18f7d69b9597375332529ab1d1ecda1a0873cf81d30ff49109ac5d19668691c2", + "0x101ab89bb017f6d7dae086379a92d37eb72c53514e3ab335ee28e4cf0493208f", + "0xbdd84cf2cf5af3eedb257d378dce860d82c6777fc26a7757a9345be8dff743d2", + "0x576b3319d191fbccc9e5a6214f5d926627b2e555c331c100b7a5098190a981dc", + "0xa6c7ce83f61a8fffcdaea40b5068dc5ad85ca28f74c92df6934d038ea1df541d", + "0x7d993bd8495d90c343b4c08bac2e893c5e490fbcda5671cd1c5be04b204ae60e", + "0xfe8fa085ba6b7a6a98aa515a1f4258f02acdc52a272995acff5a7a34f80c8ef2", + "0x60ee9695206835a6314e48a777bb9242a4f336fa36c6cbf356308439c8a491ca", + "0xc8f94ba06efab17a74d694626c54e552019291aaa191f1facfb3c3ac5a072af2", + "0xc09331a766fc2b6ed3f07f98e05475dcc506946f4f75bd133b46535f6714b19c", + "0xbc7b37c2f589a91b096b02bc68f78f2120f365f9cae4ee9c218615da58490fa9", + "0x7bc60c4df149cec5f58dc2e9da0d48210546c812aa87f0c7d7e33976dc01be29", + "0x7b759ea228bc6d9123414cf2820366da0d2dc6d38d3ce09a5ad2843a29b60cf9", + "0xb0d75ad8247bd4ab03d5ccd896007984998750d193b677df7ee420bc6eff7ee2", + "0xbaf46831a0defecbfacc389742fe95711c7abb4e73d25173eb2e08d268b14c6a", + "0x3334f49d0f7f6388dac53fde960dbc8accf64a893363cc06ca26a0ca49913806", + "0x3826a7851decab5c5a003e9e6c377d16ba01bf7d832b7f6c37110e18ec625de2", + "0x88756efa00f8ece3b49ca46483d2299ab2059e4c7b88378d80f3a0b4495a933f", + "0xba681ec93ebb76cdac6cdf6c46d4fe134a36ad08987b2fc071c01ed05013b9ff", + "0x3435b5321749d67dd1829b48189572682c8869eccc9ec65a7ed56b99dd92437c", + "0x74da80338a9c00040914c56cbfe757181166b982fba6350e5477e54abfd7cce9", + "0x6cb366c9c0aa3045a301870f7b0d406c3888344c833dff7a200ade9362f69da1", + "0x1170e1e535658e4cd69add560433ad46d2c988c41c53c90de8e8f9a6eadd13a1", + "0x84485544a1d96c7dfb02f7b8d3d6b863db6babdee7a406360649c8ed8eb2501f", + "0x6cbc50fb9cdd4721d9b3229a7e14760b6806eda79d6eb1c7a8114d3021a71de3", + "0xb1671efd63d4b94df34fb785cbc08b4250159c56abbcf98d2d1fc74841dbe5fd", + "0x66a2b558fce45959d9914fb156d1a44b37afe7f27aa7d562fe5e1cd01c90930d", + "0x3de162c7a8bb59e5226277115fb4a4ce560d24ee7946f631d260d04d6dcfe9c9", + "0xbb59353c41f1466777ade82c766b6336c6715fb726f64c43b8fb65aa1cfc33ab", + "0xd4455e36b1ad0eed6a6da9262964b95f6abcd38ac19c5030b06aed849f1909b9", + "0x46fc56887acb4d1752e345a7645372877f78c6a398cb8931e889b2bf76790aab", + "0x59ef471f797abff959cb58116a099def006edcea2b9d5ac209b998b8f5846461", + "0xb190ccd8b12e893b2eff103d055341dd4cbf1fce49eb6f117bc763a2c226ea1a", + "0xafaf8625f1f65fc78bb5bd58cc9e0ae94d8bd9c1e8c432c205b09467edf12715", + "0xdba5c0495c49cfa3d6b9429af4306bf88f82ce6a2a74195be2b7e38c996cf194", + "0x52446992f99fad91a6c22aa55f3b833769194fca3a7e3fb0f6e4d9d62553d06a", + "0x44dbb772df713669ef340bea82c325e01a9a4f747422cd5f49fd801b359c810f", + "0x1eab480eaa875d622edf80a7d9083d27576e5496486c52182591fee98aa42ca9", + "0xdbb16b4fe07f88036579ab299f0ecf0fa0b0705c5e39996322f8f958f9898d8a", + "0x652e55146bdfbed2a3e819ea34f855790e018677cdd39e7db963d95552725c7a", + "0x578b9bb81d9dc6640f4ef5aa98445a9c73a7cc806273b7026804fe4ac4dcb4bd", + "0x0725a59263abc3d7f0b2bb383b468bd715adec714db727a8aec440054846afd6", + "0x53fc5c2a9fc5d66c56a9bb8ecefa285207f73b4a2ce3168ca5d56618dc7da3a9", + "0xde278f7bd882fff735376033029167f488b1d7b7f4460a2dc71fd1db56318c56", + "0x908c2d9178f01dc47a36186395046e31f725bb024d7a1cb460a9b3faf79bb057", + "0xdc91f9cd8868afae7505ed6be861203234814ac2120ad739a253934cb234f0f4", + "0xda35f6f248805e9040ed5f44ce1c1c61e2f9912c01ae4612d94c5786f411a106", + "0xb3192ec583be8d615847d057e34338c76e34b3041365ebcf02b6b2efc340cc67", + "0xc4bc7ea9f28140d72afbacdc0e28ec88269c3f8fe6b8931683c74eb1938241e0", + "0xb45915e87d2c9a566d719e93d3dd8c3d98a0bf1b9a6beffa0fe82d99f48b696e", + "0x2d3c42cae5709bbee2dae3afa92afd5e7361029932d08edb23a604107edd9a3f", + "0xeb01cd8bca0fde56cbb35763a092902a4bb522b0c3680d3a96578c01344b79c3", + "0x867acdf55ae228d3ea0b9f526b605c651f50de3675f7fb218d9883105f3498dd", + "0x67c93e38ff45798b37663572ace2037b410249a24dc1f86a5ecbc6051cc48355", + "0xa2d0878065c7879ec59ec75bf8ff7ef8a6fa2ebe4e31d3f2e939b0b113f4c3bd", + "0xe546d86241de677c0d46b8ba487964d325f91dc309b67c7dc46295c40af9fa79", + "0x9407e798e7ff766890079998fcff3143837d0123cc75e869cf994a859ff43a96", + "0xebf83be3b1046d2d3b9281c480cdf80d6cc50a3813e68471d4ba51ff6c7b10af", + "0x776bdcfb3e185b2e706444fbec6c10cca03b536672881cb5a73af2a7ba672d88", + "0x0c0d978abb31002761a127dc96a41de7a94552874fc3d265d75b776e09e23805", + "0xee2bf84758bfe60ba4dace065186b10af359e7b821e27a92af574371002b4217", + "0xd3292995f666163da0bdbc2bc4da30d11ff63ede9e8697f686f4dcc1ae7f88f9", + "0x891bd27b9b3666aed884388b36baf05458bf2ad6987b101fa10028dab6d0776c", + "0x06b99e96f402e168d2c73dbb519dc6b26ca0cf4a9b8e023bd39a7caa98f7fd15", + "0x1eeff67b19bf77cc480e4c48c5806aa1523b865bcff13e50d53651544512c422", + "0xf29b57c94a4bf33f5a59baabb2e98768d1002e6e6f9816d9f9eba2b4c8ec0949", + "0x4dd2a5628a216c76d247bf676b5e3beda63f496fb05b8f770e8c1d2f839db639", + "0x30775007ba4d14d0031fc59543bd035e079f86a36e0185fe14f0186b56d9ec8e", + "0x87b07ca283e2cbd42dd89b5c60088bd66c6382a681385ab956455ae0db0e512a", + "0xbaee8d4341b482f269fde76a10c5867edf5eab3719df8f56c519e246c1231b26", + "0x1187b6c0b5a3f5670fd29763a3a5dedda19b8a14be1cb19efbeb130de2e43784", + "0x5e20a921b9c92cfbeb54ef1ded8089b46164ff613c8b2eeab4919af832e374b0", + "0xdf324d076cfc8a0bcc84477bdbe0ede0f184f1eb7bfcac20ecd1469ea18607b3", + "0xc748db2709380a99a13cb17eee7ef0764e31ac959f802351da6ee57361c9f680", + "0x2f0a904fbf1eb66b6e7c73bb3533e5db577409dc5bdb80a2b385ae29dcdc72f0", + "0x8b15305de926a031856215823e7adba5132e16a52224c642271bc2f79507d5a3", + "0x04f0722746d106da115097643179b29067df0e93c8b854e1efcd1ae5ee6afeaf", + "0xa74291c06daa725decda4a949a01354336514748770f6a51a3ae6af1d91f9cdc", + "0x8d1b7a3e82c7c8bcc19a919e5634fe9d372a4f9480652f4ab8d6fbe40dc33e98", + "0xe2fe0daeed4ce439d189d9555e797b2f52a9d8532e384de6e10be1f661cb524a", + "0x99a5412777c0860fb60605193cadda9f52cec9819a99cc5062143c8ba197c12d", + "0x7835f6b3070a821dd2ca1265bfb826a23a4f00f7a5e72a2bf01a62d28f3f6095", + "0x52e054717ab39776992ee3f387cb9db33cf39f5d4c6b278fc8ff6ea901a436a9", + "0x11f47672ffd3e799df935a4d2e122910b4ac244f4c8fe8c87ab6be8da648e4c0", + "0xe4d128339133ba453726ebf4524084abe9a2b74391ec1c959f2fc7895995699c", + "0xdb490b8e3aa85ab6adb55c327a180c75bd5654546d28096ca3451f73b50109f0", + "0x20ad43546369f1cf148b42b8db048deaf6900f0f6ff400d681e3dfd3c1c98d80", + "0x87d194834f90619a6b363da8dfed170881bc55d907793611bcb8afb5989edfbf", + "0x2bfc40ac4fc7f01a978b741af1264618137db4ca31f9530c0bcff6dadde36033", + "0x124d337bdfbe024d4002672c95ca754a91cbf32ffbc9328f055246e1e9e2619d", + "0x6787e4af0b98cd86d6208bac2e97f706dea54e3f3cd4df40f31b5a959edf93c7", + "0x2da17d235274481471abc2e575f8666f069e0e707fc8ee2dc54e2d3393c36056", + "0x2985e1a9a702a234eef5ccf0238bb7f3d5e378223b62ffaf09d25513c9c72912", + "0xaed79b76b5435cae1fac7b8f14568177c9c30602df5dace0e323ae757b665d61", + "0xd17fbdfe7740416a83f1ac4207a0007c0badc9beb47cb6d63da094eb5014a5bc", + "0x616141b1d9e67db0caa871056f0d69ccddf78a386bc4bcfa8db79257931d4bf8", + "0xd09320d9ac41ee1bdeb0324416bd8a8e70b397a955b2094e2bcc6d682968ab27", + "0x7a3a10cfd5335bc568d2d00b4a13c52a8abdcf588e02bd31211e8d6f2495d90b", + "0xbed77c73a1b2c55bd72bf5690f6d81f33e97bc5ea6a49633caaeaf6c372cb3c0", + "0x0e86bdb891aa5ebcadd13532170853833a271915eb86118e2b4d4b5f01936516", + "0x4a8eb0fd8ad375515717896b5d7b01340ef4efa7303265bfd46ddba6e2a90f12", + "0x953123900c085a14ec8190027519ae6e86a500d127078c26019652822f7be785", + "0x6c68233833d4a95aaec89a38bc251c04dc96966c7111eccc733b39e61905bc14", + "0x6106b415f290d6fd35ff9aea309fbf07287159f92a8f6d8bca87107e41145312", + "0x7812d331db0c368915ac788907153e0ddf5b71acabd1958610641a1508ecfbb4", + "0x9082615646c2aa4e03a2660a07be297103606efa0770c6334df09e0052c3829c", + "0xd7b50306dfbb8b2e22a3b929d134762a8bd9b7df8a9c4e12d3aba337bd637754", + "0xae434c888f687f4e9ba72fbc8708cde09d24a34f40e6a34414913f1c00e0e93b", + "0x3a85b99dd54e11cc743b311ccce43e7f03f422da3840bbd60298836780b11d3a", + "0xd0029c31e23aac20efd1f6537629ffef5284802ad3c9dbd05027648a0f91f279", + "0xc29cd419f86855f8c919fa49c5c23e7543af6bcc4403805feab31bcef58367f4", + "0x085d0b5c994c3198e1f629c43e084c69a44faf983e25e72de6382dbc7f6a16f5", + "0x44717237e4703cddeca6cfbf8188106dcea150729a0df32d190cab81a0bb6520", + "0x9963f3f827dc9dfcad3f9bce1b36acd842ab94ee04743be801865a94f70083e5", + "0xb3dd5e89babed14ec6618cf8609590d5ac92f306e9c4919e67fbfc33a199610e", + "0x7b1042bdeff46cf597d76d55428de994ef0c2e7af6ac114fb7a0ed12f21d5c74", + "0xfef18a73ba8b2ddcb4e6cf218804c13469b0f8d60f92227d366eef6ef6c50083", + "0x882c1919e6964ba6e24d7f1a383a62055fec329849c867dbc12b61fa3aa1d03e", + "0xf422e622479a4d1d6c11a06d80cec6e3d22b5649c2d800b46112bcb8abf1a84a", + "0xb2e94145b846e66b75886c14c3d41a20775b1dd995704ead2a9d673a0856b83a", + "0x2bc6060b499979e500b5da7da3d5855e04a78f909e04ec23c3b0ce08af599fb5", + "0x7d4d6f89f3315a6bd78e09f34d3ec816c35240136bc8db0afde12c5539f5672f", + "0xe713536efe03b8af63c159fe30341e9fc8b5088bf2cf8e0acf014a32c277a854", + "0x437821182ffe6192406a1b65cd59d1d81008718d11f5e057d3e4f97c5e17f817", + "0xc28f046a711887c4420016776a13b7f7b442eca761c03ae6afe0b6a3993646bd", + "0x9be296816c751834ad8acbd3eea9647914c3a3aef597e2872c41f63a2c5a748d", + "0x540d15337ad6f5313e4500c143f3b84475cc5e0f24f56b7133f52829c9a913a2", + "0x452cd403ac6315c7c1ff6402463289e7ea586f0c7b934f8af1a0ae44355b5aca", + "0x3a6d288174f3a9f1102f78a584b754bae998b3f6e36f873b6b7082fcb9ad0c68", + "0x0e853298528150f465f7d712f8751eb4846b78f847e15c05cdd11695ced74510", + "0xfb65e0bd18c0451980861032b6fe0b15f1eae9368638bad718f8c2762dae86d4", + "0xd24898f65603c054519ac1f5da6c500fab079d6e09ac24813fa362c0e3db16ab", + "0x6c86f369b29073ff72c59c2f5a13f147650dddaf50fa02b23dc93d48e860bb69", + "0x8da85e2a4eceac6feb3b7dcbef664af61f0feb4bfea8c12ddeed8a3b29fc3999", + "0x559637bfbe78526373b49fa19baf877637863c7a5081c522621a49e47cbd1bb5", + "0xf91132729c57faea4a54ea5cc93c465913e7a8f7de6b20a84568b94c3de84ac1", + "0x4fbce40cd53e98d75408b2c8e5267f0d81149d5bea9afb45a4debfccc524a997", + "0xfa4b9ba9b27bd0144fb435bd9deb13786e1cb129e937bfcde3dcf0198b417421", + "0x3b7f3739bfd2b91442565a067d9dbdbebe49b2b865499951cb8a46bfc4e70f18", + "0x01d98986a01c5b14b429134e617f6df705fdc8eaefb041f061739b7cbf9be0dc", + "0x87ccc53dc84e3be602dc2c6093c70d23fa6b76a3255323888715a2dea105d91d", + "0x001223d180f04feade86ef032b583fa14c2ae5ca09a8403f1a6d911290a96353", + "0x9b5ff85d5d1be5f5f364a8fa0b3137a6d8f98fa86065d1ae960378ea9f884bf8", + "0x1ce3c0e8693a78277d4d63b069d9b53a02154efc0ef4a5c44b3e3a77021f7457", + "0xb84ce5918a8e8f15cc5dcebdd26814c8cb8216e487c4082f985cbfb4ac4d2989", + "0xa1f7ec34a0bb0e4b93f87aa12572dafbb07d96d0ab75384d86d21c7934b9a259", + "0x0b3dd331b7e4bfe0909b456b1460761adad44c29ec595e745cbc9dbe3ccd283c", + "0xb2bf1c25e0eb405621fe4a9dfcb015337ec20a38bbccba01af1290e785289179", + "0x53837208e38a3c2603ce0a7d1149bab301f6bb48014b88852a9a24774e82979a", + "0xb17cf894de66eb3690b11058ff624096a2a2d04db38cceaeeabeb926bf56e635", + "0xd2c717e752d4ed28153cc87ff37a28458409372cfd3eaad6e788b60262c6b96a", + "0x353096521328dc43b3e2a18795b1e4c5ce69a80848620757ae44aa2fc48e9287", + "0xb1d40971dbc3537aa54d842c84dc0fb0550dfa00594b7088e008b046b9e2debc", + "0xdacdf2ebd22329ccae7a1911649b8c41659fd4d6a397d0162eb19c06619df280", + "0x5b7acd7d2d5aa49158572ff03f1c1ba966a96fb29cc34dd1e492a5966a3e1c1d", + "0xf5f22b4c413a642484bf09fb211997f5ab1cb212165b0fa66f782808f1cc4723", + "0x17491a4b5abce30cc617baddd7ffd32c3c8f46787f715b99b232f53bd4c43453", + "0x0593f3ab2fa4c8e1a6f905c2caec2038509fc3f737dca28968b9d90c6c5cdcff", + "0xf7632ef546b434d9083d50d7815dcb318e07ff0ec813d27f63f76abe89e05ceb", + "0xfabda96b44af3a19fd29004f2c21289d4463fb95df7a702efe1d0dcb4e6c6c42", + "0x6339ac26e6c62eb918849546cdea166218ce760297715af7b38b81078cc67216", + "0x4d1b0026336e9178d0e5a8bfef517fd76010437d193f8aaf712eb2a6d3255718", + "0xca300590d05e4cf984124d52d920e3575209334c1262abeb1774e64f6ab8b8f6", + "0xa9628f3028b2c4eb610d9664242ee6f6a4c112fd76f9bca4e03cb87969b6fab9", + "0x14a45690cce7300f7e046ef49e1411163288ff35c2a5fc8f89d5b4e4933d348d", + "0x610cce30bc7e819ed681fadcec359ac59bdb18ac163e6dc4bf3409b8dcc30f89", + "0x754385f7efe06740fbebde099fca765736d6c741b60d0b4ef39669f914e6c3db", + "0x90abf445e48791894554ddab766f5a86d2366d669ab40fb35b2954b99efcf225", + "0x51c655a2745169fd22a0b7a6c724845691bf001e2882924d9e54a867b55b58b9", + "0xff0fdff31566a9aa928f619587918a79f4f90e02a20c381ecf4917cc6dc3662b", + "0x3f8e78b711dca878780ef72b02063579fa7fa9527a34e43b08eed6ce118b4bed", + "0x97b2e8b23431059d96460f1690abe5d134cbabdb37b518e2f3690abdb2a75bcf", + "0xbfe16912ea3a3497a98918136cfa77359bce5bb86a97b88fea55e544eed6620a", + "0x7a6807eba3fe97a4ffedf909666b4a5605632b4ce543ce968d2d09c9d6c8be25", + "0xfeb794bf57196b9b0c497ef7bf95068e8b6f4ace4dbe98322b13cae02dcf8297", + "0x87f86bcd4f814a4d672d59e232c9380071afc43c65e1add931758b428ef72db1", + "0xd7aa81b5d9c2831608d20e0de553b4272395a71244f87ae46291ae361b7845b6", + "0x0101cf9b48e95a1d4d5b0435c4dfd98f4253f8fc645750bf372914f9faec0d81", + "0xcb818634477c12b16d5ee4da7f5bd7878da87902b1517297b6f74abd18010d29", + "0x37838fcbd302be2fba8692f2d356f82d1dfa5eec660fd12b98adedfa69599b43", + "0x7ffb359a0d75356382d631eeabc0425b4fcaa7f9f9887c0ed61323b2e6377a3a", + "0x5c093ff4049e8d9cde63948a848e8f22540bdc38c30c8422e802bcd48f942486", + "0x217aef7ba99bc436160f2a42dcad9084b2806019b05d05098fe99c3c319c2a58", + "0x6fb7445e4141123b8b3a5c9d39c17e43b9d9a9dd2a964b5e1cc8654ab8d67937", + "0xaf97ba5af4cb5b5f77591330ddc6173f9871418874fcaa19f791a0dc05469e6a", + "0x958399da2908dd67a1b404d25451d44907d1cbef97f9249eb9da73b92e0ee03a", + "0xfeb7de92cec1900fd7ea5f3d7370fa416f799e0c1b0b5761e20c6f0d1f7b2474", + "0x110c8b45b8a46a8491ae2eb7540daa0cc6352855d2d40b29d55b41e658fd8cf1", + "0x379c06946fc8469725bf9ff61b1e0f2214f46d0192996071e7c65168dffeacc9", + "0x0eb4672b13e15cc883d24d6f173ae76a57dc74f62f9993bc5f63e95562d0c870", + "0xd9fa46edd71fb99a804d4ae288eb0b4565d9d1905a5bb5c3c0e5a42862e8d98c", + "0x67ae69ef03976eef98e5dd2836dbeaa26da162f6daa0802bed383363f97a470c", + "0x42bdbfcc5c11a00918995d349c0b536915820377a334ead51f43185bbadc4484", + "0x0d3b7a6d17f63096cef8e7646d45568652ba3ca5d48b1f6b4aa88e1cce353cb8", + "0xfeb8456c1baaf674206fad7c06060eba5217ce68f75bbb61d1f91b98679d0d8f", + "0xd12b52936db47aa9a062f8de2da410b46d98e5f8fb3651b89b9a569e49e76c03", + "0x86ecc1efbe31d40f27e2c7d66e694f6d357acba446e74246c83ed190b5c5e7d6", + "0x97a9f9417ec40071dbddf96a9492bea1637ceb0b8b3021d85468dd14087e1872", + "0xdda1f0abbe9aeadb438cb484b1162fe7c768972562b694c128b42f105109ac5b", + "0x6bdeca3224887926f7f490e735dd0213fa6a676904ee5d84a2aacabb4596db8a", + "0x8015a7c3f73a7c47c23a1d5055607bd40423bc86980cf683d96eda185e391604", + "0xb033badf3c42a3a744dc11a6d71732c935e838c16929b6d9778df488902d992f", + "0xd3ed09c83f0bcf3b1a3849b818afb0c985094a2e3a81070abfd72a35c654d49b", + "0x312d075a65637eca5ad36858bebf467bdbbfea4a446f8083dd87f44e9065786a", + "0x7c99b8102ce1432f800c410c25aa81bbef25fca48ce36597e161782fb91c34e3", + "0xff8149a25fa6a12007aad1e03e9a589016cbcabb7096cef82832d9dad9d313bb", + "0x83c9a57711e6c78b1bfa756204f8a87003b7e9bcb8b200682403626de2192679", + "0x851cd96a630ac9bd7cabfaba3b676f368d54c6b1e9428bb2d636559c7ebcceb8", + "0xe175e9b2aff18cc6d7e395917e90d3e51bbf5de64bff8ab6e2b6f0d0b0fdc922", + "0x0bb261fbd40511dbb1c2f2d6fb299a8348329c51c1dbeb646e8a6f837f06b49a", + "0xaf4b10a5df2084408ddaea0c2f41ee69b699802a4bb1287a515fde5ade44edc4", + "0x06309b2fed284649d38e25ac4b851bc8072cfbdca05f7c45cf325e9c13e3f774", + "0x970dbfbfa81237a5a623c2ccdc4731e1852e20e91848f9b8ad470058f6386ff2", + "0x43cb1667a3a79b11daa1c86dab33eba7411f7b3786fba86df5db99ec09f073ea", + "0x3463503b87b2b8d8e40b1f102d57d0cca47c5cb9084d6e709dfe8c6959a1afda", + "0x27af61b8bf6cc189c9bd8d947bbeac2232611e3ce1e1a446adeded5be5dd1622", + "0x0b79198bb0504d00cbe262bd764a37e6e80a4a3eac9ba9abb08c8d6a5a7447d6", + "0x075928b873033a4121ba0f576fbb10d0f5ea623491e5ea12ad0a1617287f2682", + "0x82b0f5ad468c44c7e99ba549540fb03ff31f47ee111a4bc6164258b8409c7e63", + "0xc71152579e43f3b09908ed544a33b18d246ca1ca9585fc15becc062743f872b3", + "0x446bc918be4ba2ce42192aa647d42b9c0d0df862b574350fe376b222c6ca861a", + "0x51225849ee1316cd62a625d20c89896bf6e9dcca2075eef7c921c564a27ba49b", + "0xceddaaf5e4a8c8e40b625c940a9cfc3d1d7cb126802f4a9e152cf8b40f771a93", + "0x4210c43ef83152357a2e0111458c174669df97169f0314a7a213cb5d5e939fb0", + "0xe9c98428f0a10ffc7591f2825fb2c8599a14a70727b894d408982c6959cde115", + "0xc93508ee589b8ef1372bc6705348e959bf04c255982e7e9c680048ad377fa816", + "0xb5fa03df9bd81a7bfdc4dc08bbc797862e8166bef55db058db95193c6abc275b", + "0x9cb51318940efe755e7f8147d816d40ae0fed64e08e9b8c447fbb4143a885a02", + "0x4b43c595760d658d1a14670dc5866a9f5380084e4a032398d984e1b3ca733057", + "0xafc7cd4919912ea0c7cc42626c122a88c6a63220fd916cc44a66b82fda99e469", + "0xd3ac254eb7b3d979bf40ca115f4cdc6c08b533a98f6a8a7782ed52fe2b15b5ac", + "0x7e8211501955f12d9ea875006e4079ed309a4459009a63995ff18d6afcb6e637", + "0xc1b012c47aa10e0d8b85da900f64eb74638568f32ddce4364c39b026c37fdb4d", + "0x44e7ca67049aa9355c5c7569ab5a6445000b7fa2fdfb98a2faa8c3a4e8507be7", + "0x91691809c6d66afbf037a37f6756de779fbae3e52a1364557990ce6111ecf4b2", + "0x8e9651d0afb3845607181a93a1c713fc105eed60ec0013bd682ae55b2ecc531f", + "0xf68160583fdba40eb96fe069d2fde8f592e749909f2743727257c094d5ad49ba", + "0xfa058600daf5e3a19b8335cd6e09ac8a5a4abeea414c07ff17912ca5cd533df1", + "0xdb87b1345d96066954c84d04370d5dba192581051ac7094aa738a720d32a1de8", + "0xa7b59831b687137fd46c5fc0f7df0bf4850ba8ba3d75211d785ad907048f2ce6", + "0xd1f4731f7d817bfdf8efb6e841b2ac50507b1c6f6f48f95dba4fb29abaf789d3", + "0x05d88b9dcff4105a1bbdac6ccc059c0648ee676508849fc02328f1ad9bd39246", + "0x5e41d6f7646c80279a942b2bfa911250e94da7dc54d7de9f447ba4a49249fc2c", + "0xe1742db722b52db1ab0f0334e099decffa228dd8e0d6295e12fe8a933d4cc85a", + "0xbb68adee25d9d0ff833fc3a5d8b72e56f03e4e0365312c922cd55a8b0cae0177", + "0xfc5f1269d25ec05e90247540a80abc6fd8a3fcb4a363a43c59375954081f2898", + "0x450cd972ee9f6357646d3f2e7a1101ffcd75f86b8f8dac70df464fdb6657ade7", + "0x40f4562bb4b468a6c2d1240a0a08d77cc19616df73ba2b29e0c014a7d755c736", + "0x47569aa70f02c01f39570f5a4364745111ecd36329a0c352ba3d9152be2cc7ca", + "0x960a552cd437c852aba674a77628bd339bd5d4f0a273e104eeba77739f69fc88", + "0x931d513d5b0b7a025c14eb74ecffa4f7dd12bf7fd822e25fbdfa3a211e55e021", + "0xecfc6ca0a9c7abfb2a9a02d674adb7a249eeddbee292d67182476ff9b824ae11", + "0x0efbc9bff0255c6f38150850862a03540c938c5200c74b63adb5e3631e7532b4", + "0x1b8565ae07ac740344253fd6d55872b4a0a6ef8e6ae253915704c969b6ea3636", + "0x4040f8f5ae17537d9a7c12162b7614e470c7e407483416c0b5c6575a76f33e92", + "0x1d3cb0fdeb08d827e05e23d6675aff7c6a5e75e02a809fe3a6383e176c4d8ffb", + "0x66ab0ead69cf168d25e98500ada44dad2dccc02df2da00a68d9ef61ee1dc911a", + "0xb89ae3036db9c2897e82433bdef9a5097157fe50141a71e891a0c00847e0c090", + "0x7143eab795d3b1978d5d5338e5cf0baa96460d7cdd91aa85faa0739b97f984a3", + "0x2218d3e737c6339f5007935135234d2ba8d8d2af11bb5a2191167cd1bab131a8", + "0xc70cc44fe06903ed81249ab2b4e7175a9fcaf23b5c179bf4a7fe83bd6ca8abe8", + "0x875a19e4c3f72fb73edf813f7715ad9da89438cb7dde26a54ecd1ac206ce63e5", + "0xec10a5d21879b6cf51c3b4b6b6f6f0c49bc04bddc11329885e58ff038a29a414", + "0xa1f528995cf848c85120f663efba605a9514b23b1d2078cb499b456179f35b1b", + "0x55a2dc98a327bfd569e19f93a674aecd634151c04290e519e04f152ceb0ab601", + "0xf8dfa29259ee6d850b48b6f6e5988ecd551913277fd1f50434fa5869e4f8ce64", + "0x4295b6107d61716b669ce76b35f27e37dc546c5d7d8c509e0b68f62104f52d3b", + "0xdd8e517a7112fc8a6c1be5467e1d12403d8528074d9fae78d237c7332ee86ea9", + "0x173b46f8e796392c979c89b0437dbf4922d29fded4541e5ca02b710758462773", + "0x32094d28e48d082b33d1b7322739a2af1795b38f216f26d430479df4ae3d9eb3", + "0xd8ed9accae84568d2cfa316702d42034e4be519fca19278bb9370923aae3ae22", + "0xc576447e0c92a7a163b649a996b9bb6bfd83d46d4353be24a1c8c5ce5ed8e70d", + "0xe4211c0eaa23ec8d38f169d4f092bf02a78433ddf13a862e27979f05434ca15d", + "0x6dabe15513e126c09aaf0cca9d2d2464936900083bbf5f71e1c79e45e8e8396f", + "0xd6c783fdcd0922665920e20ec3dc5b35d49f4e8652c1c713b0ccd2c60ffd508e", + "0xd371b9bd497e26c6b533c896eb623d52225bbfa90504c6343409d66ffae84c55", + "0x94ef13b63a45cca8c854a61a50cb453214a2c4d1b52f83375b2cce6655b07ecc", + "0x660a89f1504ddf64505a1f89bf26404c03f6ae6ac7a3375b30af9f4ad058a688", + "0xa490708e0eb1fa76c2cc8e94203558eca8501ef31d0d2b5130868f64a9987e67", + "0xff31d7f33bafc7360c9f5b5011b3a471f9c1f9b8f7c2616671845fc901fb0371", + "0xd42df1f759bb2cdc7724147c2dabdc2be9eefcb8f54d599f1c06d1d89d5dffea", + "0x2217e3e18bc7a594f2719a8068f9d4e40f4169be27288c2e427f5a49999339cc", + "0x6914e598f09c85dc53c9d68621e2c8d226578fc1220f2b031ae2ad04ade943d9", + "0x20283d03f4c5c475eaf9ea184a966b8e41e2c8aeee1f1fd9bc3bbbb6e9b2c1f8", + "0xd94bdd106988ac681eda1cabc2c2ceab7eb60dce6fd1da394443f7bac0347787", + "0x0a3b0e99a76bad2889c0fb59546fc2764df528b2a0df43d376118575eb954b13", + "0x4beb9b697db77b2981872e7dfde26e19140d83acd168b20d6589bb9f3329c0b5", + "0x3e0031b84fc5a163ebb864712096f0d05245106a5dcd2ccf7122025e68099631", + "0xf4a2b90a1385a7352eff070bd6f08e7bccfdf026c1887677138fbf79f04ea2fc", + "0x237d1121bc0de6a5efc7d1d9b69beae85a26681ce2a4dd29772d28232a6ff5bd", + "0xb59790aa93851cc8c68a54f879d2fd92cdae2d32061dc9b0c201aa3ea68522b9", + "0xe04c8e454a03d4833d39cf2abf58fdb07e9335042fcbcdfd2ab3f57d2334ff7a", + "0x0f6c6d3cf53d85d6fa795d280017ee006b2ac9e10806a98f8faf704693cfb83a", + "0xc2591d60c63abaef4bdcc19c363c4f4570443b12f910f1e97113fa972d3068cb", + "0x3ac23c9aa4fc1f6b102a2f97da397ca6fc1c5baee1be5590404ae31a1957a4cc", + "0xc61ae9c03f24789788eee2126a4b568db5fc907791a924351e77589a652046e5", + "0x9723b561e1cc74709bad2062bdc99c6e76cbbf4afee2dc9db83f64d38a391d00", + "0x6ac570a1d3196ed9c6e1916ac5808de431bc3b63c414369940f7dbd596038563", + "0x0aa794556696c1cc7490328e69cb6f19fbd3345631a96b58974a96766092cc86", + "0x78e35ab9c27a6f3089c924f769751ce61ea27593df7c9d6777e3fa24203944f4", + "0x12b2d848571ad30937b360ffecd8d039cf4c63a3eaddefd8559cee0bed73e785", + "0xba6f249cfec20c584e4a82193959f766bebda1a3bf5f0ac42ec833bb47e577dd", + "0xdb61d7d2cfe1ac5f9daed9dd577d313e2535bbdca57c86021848a682be4f2237", + "0xc789c0269d56d503582b961ece29d19d00a23959221eb846e04f538ace1f14d6", + "0xe64430a7366d316ac60934866f069f4e05a9edbb87ef3c5c5de8ec6ed8a586ba", + "0x5ae72cc23dc33b72c1627511a41d7efcd2ff966e6f00919f5239ccf49a732569", + "0x362bab9a1843561fc65e1f5a7eec9be3fbd831ce6704c255ad3cea93294494c4", + "0x111b2d4a1bc9a35900fc443489b748190a74bfb6eafb3245709efc69df03ee3b", + "0xb2e4330fc037eada2ce12e48b057882523314aec93a892c71e8777a8adb4af20", + "0x0effa841434a22a3a682e29426e1cd79df28d425a3f2131023c15be3be1f7676", + "0x809a03ca4856df454ca3a927f5ef31ea028142b761a0e21f6c351d8c00667ea3", + "0x68016de38b6f95321dfdb3ca548d1558efb992fc24fd9cccb157f63c6ae41b44", + "0x1f7cc038cb6997059bea7f246ee02d841267eed28866dbb7225cd25df799787e", + "0x049b9ceaabe82a7cccf11d0c3a247d33e23cd08586d94a4c69a1b3cdb69b7f0e", + "0x6cdec2a762ff54966017e350c70a8099c81a287d662132d1957f23f612c2901a", + "0xa282e7e3d7e513665351995f84494d02b4f020f34a674e2dc543e3454a61af8b", + "0xbc541f313241c0159e7b0edabb5ec441b3dba032ab25d14882bcffb007dcc8b0", + "0x42a0d6b73297fe5409da2654829af620c6067a6acba33f7b93b4c09ddc1193d4", + "0x164a75dd31d8dedf55336579185d678bbe2f30a655cfc30c474f438fe8a9a4f2", + "0xbe2d6c2993bd029e1c4d3005be75574e85ff1751d95f679b14c6e7a11fd4eee1", + "0x11b2f6e5fba50409feb26fc3e8c07e0efa04b619346b3053c866d6e204d7accd", + "0x560244fe8523f46bde10a3b4b57e691a2a5e7c82c8a39a272b270de500fbd747", + "0x2784e18c0a31bb828095121b65205bd4f3171cea4e1160f5a0f0e3798f88e910", + "0xea9e057c0a593e94e0e19d682680d3c89de66f363f1ca1d4773a0de34693a150", + "0xb67c37a6e80f74c8b903f52b9e161110846ccb449a1573d7b6f82a064cbbeeef", + "0x895b6d8cb277372a1f35973222892e17f5ad5933c0f4fd4f1302fa1b1429f1da", + "0x198a8a05ebe5e2749d809c9f76b7b259dfe31f0f4d13962336c4a2a78f934c56", + "0x3fc561cdc6b8ae9cc9a621ebe61a20428d1ee712a89511075d4ff9a788e26c01", + "0xfb8a9f9a28d93c0acfc781606627f28324727d80576f03502651ed6716c3c18f", + "0xa88a0d653926b51c79fc9c4b5271e630d463418cdee41b185481aba5dab76f7f", + "0x554bac9cc9280651465488adef242743aea09d6f95580636b0dc526bf151f80a", + "0x545a4c627e78876840ba1cbe3aad8718d7b9a1063a90fad448c7bdda5dc1a1d7", + "0x76862b5cb0b82af76d03db47e925b3b2347c6ce441734a204f4be8ea1e5243c1", + "0xe41e70fcae5aee865ed08b0b6a60a1f2ad908eb3152a8ce89afbccfa4837c210", + "0x7f7accad76a905854d330c6e05c80ff95ba8aef88480b9c0ed566648b41528c5", + "0x268bfc1dd368506f6138a62b7d8773f2ea2322984eb6497fd4e7140f7fb318ae", + "0x9246a5fa1a2c0ae422ce89b8164b0c9d786fc6e74a29ec6b80a55762746050a3", + "0xf4a9203a4cd0ebdaa2e8d39cafa89fae8a00379b060457cbd9ac502752efa9d0", + "0x04a59fe6122b7ccd807c01697d740841ddba1089ea27bf93d49bf2b8273300ff", + "0xc6896c29f447455780376c0967ec1cae523c534379ecd6921eb98e5a642f0a9e", + "0xe2e106de54fb3b7c7f93f20bae543b9b54cac624afc138a20052b5a0f9f8dbc1", + "0x60e91048b79af36001858c7a361d1ea7882dc9ce70a9f0b1be09e5fd0711c3ce", + "0xba5f73b1c3fc0c8dc800c488348617333dd60f162b5b8f7a5e093a1aa1a8f99e", + "0xb6959fe8dcc9df211f07ddaf663f401bea20690c14f94a4a9da1a5aa60d04934", + "0x140dd7ff90b65e245ec7316154c9547dd7e17789a4a53fa298d2c1f30cf3ed0c", + "0x8c5acc45ce627ce596b021d5a9944a5a094df6c992f521aee636e2f8b051a4a0", + "0xe64c0a59733fbbc2a979540e1e6bd542dd83ed7da6b8e355f52b69eb915b9bd4", + "0x3ea9a4ee34f74b7332b1bf4fef6050afe6d638f391c1affd5958e82e8a90e6f8", + "0x2e84002d19b8871b8d5def1378da6f4462b1cfc5104373025602d67e10b7d443", + "0x76ddd84839dd6d4f1ccb9471980a70f20a63c0f73aa8f4dc48c56c4aebb97779", + "0x46df143e765ca61214a3cc19f06b617b0775e17b52b8196dbdb90aae47ffcd69", + "0xabe4c220a084f14289648c006af9d39ea2b32eb5fa1c1f79ff07ec555b7275d8", + "0x2e51d01e64cccfa2f9fc874cb4c432a86f26f1dcee475a95f4220a3acac91e03", + "0x4f87a36ee459eff74a753eb2aa95bbc7c1dfe552b587d9b09c20bcbbdc269999", + "0x18f0b4c1e6fd0321a985814ec4b56b5fa70fcd695502a48577c765e0d90d67f7", + "0xd96b111dbabc8829f375ac47cb1a337cc52b2f0b1da7a0a16a4496ca50c0eff7", + "0xb2219ee7daa522bf92e148c7072677de88381ee00bbb592889469f031a6b1d18", + "0xb0dc3d1333ffd6812d911b3f0ab7db3a38b2a95c7b6ddcdf3e51f380ed0641c1", + "0x01d554d2d4d511c9104b2cc8ec0e539940c04c8f0432aec6773887de1a40562b", + "0x0b5cc8b52e01dd845d84d878ed7200790190fc5849730038c3d9585f48313144", + "0x3b5ab52a6aae96ec47f8c84eae31200a4d69fc408292a441245b9ae77f9d99e6", + "0x30a940c6d93097ee3c24d3862112a20cb70bdedcae982b51effa216b3f60fe36", + "0xca5a0a82d17d2e731ef0af8fff982dd27a2765f7bb04d83b2b385b26bc401660", + "0x2cf9e389387b1bdf013163949e6b9bc455b1fead394c9d0112a77e48087bedfe", + "0xe779280b318edcc3dc7567a268cc779ee2b66ec0ed082c4367f0271a14131181", + "0x309d9fe7b170df52ce05c17966d6f0554c97f77ccc1c39bc69403e7d9ff4b993", + "0x21e083361eb54dfe77a5ce1865daa9665a9263461a27fdd16123c25c8f602e6d", + "0xb41227a4a013114f720dc5c1a4afd959b4cfa65cbb2d2c0ce171dd0b67c2a571", + "0x102c337b228df5d610cfc7480156ca71b2e44621bd529279c173e9f568f841c4", + "0xfae182482524d29ef38d4027143b54ee07f8e7734dc4cfaa793723bf19d7ce5d", + "0x02213622a7d3cfabac97e3635119896adaebd5ff781d52b2deda66769fcb1805", + "0xf9a6d0f360743260b9c27b196ce8eb5afab8eeaa38044743eea5e61a1b2356c8", + "0x95c304adc8a3cb8475f622a30fece981b5ce4d5a08ce9e3537a7e724e2e3b4a8", + "0xae104efb2a4f1a13c6e3aea0906bc7777c09992a392b578cfa10b007892d5839", + "0xbda99c0e14564455af52d8c4f381a7c66782ab8975b7cfd3a1a00912fda11de8", + "0xfdccac6336fb22139dae3f985525b1a3fc5cc8f2526c3c946def3e76342f41ec", + "0xae587c3a355746d7bf04ad117c302d1d47609a2dc27383f99814b51538f6e643", + "0x14b599f03959d1f3933fd0ed85064075d3e3a2d88234fc07263ee72f7875fc15", + "0xfa73cda6fca734cd82864fc23b15b6c3ce07817a5a6feb7472c92bc63e15f787", + "0x516278ec8340db7afab4c81ca53389f47a269606d994a0fe62437a4dc3f77275", + "0xab44e9eee946bfe3f24ce6069f6cf27d4bc5448fcd542ce7655ce14236672c52", + "0x3dae35702a2b164140942d40f1b9b7e15939e2e8fab3e844dfe88206470e9275", + "0x3e55ea92869d539dd26998d7c5c1181154976fc6f0c9ccd62fd8c4d6cfd088fe", + "0xe12412b52a32c8d2cf373e4366200299694de21230fdf7868fab8ab918f4f038", + "0xc7f4b5516991db11b356f189cce02bdfc56c932398baa474d43daf5e2e7e85d0", + "0xf692a3830cd1d05973b71385c9c0621b417a93b5243c03e07f7356140114ef6e", + "0x460e6433cc5d8034576aebd6e3c51e2c3c1616a3cdf66ab237279648b16fafa6", + "0x528554c594557bc14e4ffd8935db15c917fe15a59282d402c7a822862106d779", + "0x59cff70236eb97f2dfcb1eb9e5fccd9306770e4886c8b0d501cde49b7cb96caa", + "0x1fa9410197b5457cad8464a6c8ad329e39cd59df59a4e29d5125184cf6af9176", + "0x013793dfda9892c63c21bf113f048a6dad2ed2be574005063ce366dfeaa0f8ec", + "0x6dc39d8388bb671271a50d9a009e05a1af8bfec7858706e5dd8a32a65ac567c8", + "0xc04cf03cc168e341e6d118cd770ebe9d41f172dfbe084e118707935e6b6046c3", + "0xbad49c614576bdb4a97f9300a6a311bcca04e3c8a4852f96d3edbb931aa775cc", + "0xd01e50a12bc4f765d5e3f4022daa10f45c1cb965884d9cf0bbdcc9eccc2a7aad", + "0x368ed59b9a0410f81646a148a14e969839f9422ba810abdff5e71e9aa07d9f96", + "0x78c6d60974926f90d9542b5ea34f5b9f2a030a49d498d583315048ed71489330", + "0x8f894db7d0dd8f1523641ca6f0ccfd7b0e57f666dde4cd52ceb3f36760db5b03", + "0x913edf4771faf929082bae461e0e04df9587254bce387defe5f11cf62ec9b28a", + "0xd5306416801019b0e8f904a8fbc7c1a71d3303673929a0c03406b32f5cd3b33f", + "0x7634b96df9209fd9fa87466ac8e5fe6ba201abf79029afe9966b8ec34427d974", + "0x10a508ce51d8cd5d7e20775951bcdc3f23a9af4fc442e9442fab8e66967fdc3f", + "0x61b05d772e4d4f0cd5c7b3ce5dbdf92bbfcd335e96c1b0a4538bef0029d96a19", + "0x6c5e290d5b6012e281bf746a92937c2a2395a3bbde8710c52f07d08f1f29647b", + "0x6b1e04241a3dae319997d9f2f0e251f39f9e6d5c0408c92c80771a9564ec7884", + "0x950b17d5bfc365e0d17fc46cbc27deb3215d86c02a2729f2e14d7f0b9734ba79", + "0xb4c4cc7e089f0bacafa5868246664f156d785c43166b9d311b65236808b6ca32", + "0x1712c6c1402a043422832e342cd1b862516faa71b9bc86a812840ec6c53b0b94", + "0xfd9060e51e87f375cdfc187a22503be455eeb293c3cec44a76b0fbba960e4eb0", + "0x89b0e667715dfbc8a1e0c2585169b36e4869e51af454ec4c439ef4199ec35aa9", + "0x7990b90e67a029d18c758937a4a4aa22463988245c25eea2145d30f987ee6cd5", + "0x521649048ad8f62f02c4b073214b66e187c90adcf0e15edf992ed6f067dc39e2", + "0xe07accf02d53c49bf33c647d46182833f24f0548199301a50fc872d618b8c384", + "0xddf40fca2018dae0179887045fc27d1417933eba81e83a7a9d2e3faeaa31cb55", + "0x22d69a7342110f90635b6bdadc2fa65b63874da33b486a453e2458e1f2f050bd", + "0xe8bb3eb99ccb44e7bfcec3839a9f5c2fb2385418aa1ff53960919869017c7673", + "0x15b7d028ec9a5e53296bd3299a16cc1553ede68c43145ac2cf3e87380fce7977", + "0xd11eb79f38901ff52c79c0e04964e956d5d764d90c69f98c6f8e1936cca45a89", + "0xf86b6722b6e1b0efa16ba17a636d9791f7bc29cc3738725154a90a9f5a90cb9b", + "0x787a5d7514ffeae3fd91dd2e4f42fbf84919bcd332b39bde3c1f2478b1f17db0", + "0x9b6418df98856158f2f252d9a7136df01aa459b0daad78e8a55c222f8c476c53", + "0x191e12092e86ed42774cfa79ffc9b80e63587abe1d7cf080c9ee17492e722a14", + "0xd3ba154065b307e064cd24f6b19e903e9275a5a5a6c2e9aaae0c4645bed2b0a4", + "0x2e32afdb62ac244a8902e69728f7abddbd76cdd1710c27f49a80453586d2b0b8", + "0x6eb74fd937dee7525584b347f96047ef260eb9a818d47ea6dd621033baf969dc", + "0x169d81dc1dcc0359019c5ec8df33b1a632b76dc5069816d1f763b70a2c88f0d9", + "0x5a5872402695b2c53b477c9a85fa3429da38ade4f974be00c8ff529bdd8b741e", + "0x297c6ef9a8ef89a2a4a8f7a46f6eb5e7c7f77f70caf2fe919eadd54ca6e3f067", + "0x6c44948fe14bf4d00b8cf18386b578f7a090ba85ac23a99e5d917461cdda43fa", + "0x88e3b8293a99b0a1df9be918d06b4dd254c479d30819f5bb795d022af1b0044e", + "0xf922ea16e9bf0d927c801abecadb950f6618121317f6377921b58b5b4ca0afe0", + "0x621708086ed7e6e6b6aaba7f48a753a997b00477d8484b7256d7d8e8cbc77faa", + "0xa1fc50aa5a8a39f0042c48191b4fd648e15c81fa9ad31e5d6f363830e43c2248", + "0x97faa052b324b68e5b9099ac26385296063becc0f624fde90a32d8df5aac9cc8", + "0x4363c10d37e9360b78c4390b5a3cf46ae221eeec96d68179e19504814011b202", + "0xdc166ac26be52e3166527982b962b8399015e6b64375ef1a054d4230c5738ddc", + "0x09c12304606b14424730b9363c07b8f1c4a5fc25c4bf1e216888b78fe1aa69df", + "0x82349893f360a5ca332a210c1b176279417c40d16e157a973006c1ee754b6562", + "0x9232eaa7ffcede5d23443f2c16f714a274e391769d17a3cbae3d6d80d05f1c77", + "0x02f674ca2f03d0ba8557ba825ccda668ae4426f5dfe5f4a1e23fd7be22026c29", + "0x7a77c54698534d7dcf8c14219eefa1c68c819e2da638e26f40649674c949ba7a", + "0x3b0e5a3b80e07ddc612921c9d26d941b5d5fadbfc8b2aa4e642e713564ba9ae4", + "0xb42dc39ae5624d5be89907a27cdc5b5ea1b228848c53b73fb4883d72538786bf", + "0xe2a8e56f36d50f34ddeafa35cd2445696e18cc8e07a6d6af856b91e7c660e6ac", + "0x2f7c73b795172f1c5030dce29b4b432fb4e8bcb62848397464466831a640d6d1", + "0xd5dafa2bb73ad992fd59dfe9568a119df47d4559efab5da56782bef291d0c0d0", + "0xc3687ef2739660ecaf28cfaf6df4a149956508cdf9547ed7c848671b4a63b570", + "0x8e2990bbdd6dd6c60d62404d2b8e1778e06330f6903abb44aeb14ea483b240a4", + "0xe9f97bbc0a7d4a66043b7bc6a6cb03452e63c352961f30e1c16ea037d5195965", + "0x0400091e5897cf953a179768cfcc299b1dccbb839cd13d68b8b15237098bbf89", + "0x5f06081062b61fd90d81eba3bc2931605314f8c748487437a680bda5a532bcd7", + "0xa44f5f3f9675279ac4828e3723210f9fd84ba7cdb0083f5bb09766650ee31212", + "0x654db73aff8aedad6b684649e9feb23416738226f1331da84c02f7d1fdc20316", + "0xfe9848eefe1cadc8cc92cb7bc788bbcea2577bf7d41e96779b25140e456917fd", + "0xff8e7a744112aa81457bcf988c0ae508e981fcf23a057f0b8f7c318ae52c9d54", + "0xb3676c6044e1471e626999374f5ad7d57d739766c27810ee2ce73e3ae36fa0d1", + "0x4af0a639e8c914324b8b0fc910ee127c5e13d59664e212efcfb7c83825fd8d35", + "0x873a3648b786928877edce02b5d1c39a1769948c5145830196866aa065b0c29b", + "0x10a86da819c5ffe1bc1053f04b324fa44024b5d7eb3a57f1b807c67f16db29a3", + "0x455feab532f1677e499fb5442032145761fa02b2cfefb40ac0a21fcc23d3c96e", + "0x70854cdb1bd869f6846d4ed582bb060bd86551e7032867fa04a39694ab0ba0b1", + "0xeac15d26de61148ffb165658a80b9c63e72186d563c51a32e3b584094a3f4c64", + "0x04e8cb9edcb2a60fbb03f333d550653a4571e575804e6b86fc39d893e5f84bf2", + "0x07122f217854c774b8d3357217948370ead47cbb57b1415136de1b5867662f0a", + "0xd044e38d57c743368e2bb53cfcad3f68d97752d718b87aec2442b3a9a276004e", + "0x21e891c5a1a06d8012547d35a3a12e74aaa4d4f2aa63f005c5a2afb6b807c56e", + "0x13e8fad9b47cda41f4e0e059921130d61a41a7ffe56045c7af5ac92e4d130d5a", + "0xb859ef93e001aae5a3b0f6162e2998d88d1d89049330f3185c36289f6a635d65", + "0xacf85e7ecaeff23a5a2dab09943016b8a78972f5f65b68d02c2376b31f7119f4", + "0xb02a680f65a8670c2c168b78e9cec7a8b426225206345d83418285b8c935a4ef", + "0x71aab095ea5849502f280b9b4135b53a35d058012ed80f859bdeed9642c2a063", + "0x3e1de20dd065bb59cd15b6a4e7afe601d6178d48cb318eb724ee55214d7bcb32", + "0xeb15bcb10bac0508055e9b728f19bfa93f9b764b75a7a14a65cceea1624f35fb", + "0x55ffe1849829a53d160fb99453ef3cabcd652d4396f2257e1328f86490818eeb", + "0xc7412be4f309517abf031faf07f73f6ba56d82af57fc382f72df9650225813bc", + "0x28658aef756dc1b26bf023741e1948dd68d6b1cb68797e8e41978dfdd8840cef", + "0xb83521cae7c65bb8ea945ed7da1f2c46d2d330a5ae86b81843f04d483b9f1d7d", + "0xdb80f7258cef1769b0a639eb0a3d52176cee6781c703828a748f912d36d8a583", + "0x43f9f9ed3ea2efdef2075484e7c89c556f88b1d410fe668058820765d046bb45", + "0xec6623a73da6a96936af7c3e86ee84cd25f345567c80f82a212b6b71eb2f5112", + "0x7ebf3d8f2fc54fe75b588ef86f3688066f264e446056c1123920af7d81bfb7ac", + "0xe3cb9c458fc1ae91d30cc9aa65ddb99edc444ec4a51c52eb8989ae573c4bf30d", + "0x128fc74f48ac7f5372395e96f4b2f917a15abb726147fb223f66863de3c5b261", + "0xbe468219987a052de3ad63e78e422dd48744fc1a8cf3142778f379933cfa1bc8", + "0x3d4e26c1f7a71de0f37839a77d8f1ef1aec7b2296dc1f928e2af1311a10e8270", + "0xaf5c2ba987d1a1ef8d221f23b6cd9c36c48c9527a320d6836a5f8cbd185fa26e", + "0x1365c384303512603820a7e6f1671bb397347cb5c84d28d84713bfd2db76100e", + "0xf832b86c9fbbaf3084199cf1f1ee3d05fa7de611332646f4bbdc96c35f0bc85e", + "0x18fc4d1a23ce9f633f30618368895db6b2c0f689f4ab184f15dd36c60295d194", + "0x1ca562024f21df2dc68b4daf76ce233a4ac55b2a690fa9696570fb3a6a6665df", + "0x2e0b366eea420c15699576b24980c5e09e29051a247b15a1dbdd5d2e9b897a95", + "0x71673dcf2f96a624c8bbd9fbbb81430e8d7506d3f12525174b5610cfcf1ccf83", + "0xc452716a4a5c163da3c8273687f0c495dca8fe3b36267192a4c76d34f89e9c94", + "0x357d1a5250d16e298f7bb396b053bf2265cf6df23357434c18bff253748fca41", + "0xbf7441a7ce2ccf07fd97e13ec39c1dc238b41dec86e6acf34d36d5a0b7a3f7d4", + "0xc5dbc31517d83b302fc0d8ff1afb5085ee7511335ac7df376fa05a65beb1d313", + "0x521fd52506fc3bc5b585d61695281b8d527de37bea0bf820e45cd59e2e6764bc", + "0xc4871d1abf954b999b7e0cf1248048abaf1b2a4bcede8a59aad3736b04040cf0", + "0xb9a049b3d46e327c55be65b6781b0c5e8aa63cf158e84f3b97976773fda5c8c7", + "0x5c691f6eb101d5b297243fcfd99d5e5b50dc7c6585fe9857f23f8760e5110573", + "0xf9a3cc6a0611dc3dc6c644c7b5d46cbdf04d71f2238f12bb156f7da13eb2eb50", + "0xbdf2d19d8eafd9cd7daa0e6210e92d12947b1b8c5d66258183f0d28779b2b618", + "0xb5dd601a8743cc89b5b69509c676f75b38534d57d297b982c1c9df07fa8ff681", + "0xca661cb47fba8c60bbd22b90ed398891ab0227fb7c70290cad98edffe972ea53", + "0x6e44e59fc0faff79a8b35e86182e8fa1a9a83dafd99cf527cc013bf07c41010a", + "0x83973cbd336d736925dac59ba60f7301d7ed4306636cd723cc639334c0cf528f", + "0xff6912672b3270cd72317073d9a9aa0bca7ca289cc6cf1cff49e41995bb447f4", + "0x2486413d108abc8992bcd75af5c88810f99d5e0f2e591c5a6a1c3d68699376b8", + "0x7e6e3eeb0cbb3a9916acc72f3177a635deee5538e82df1b578ffc0fb82ce0d7f", + "0x24baae12d5c1f5fa7a57e7821c551a311fdbe23cb8c957410e311df2273a2313", + "0x610100dc042d366d17979be2d9f12abe4fbd7409d0f4a4dc472e1f00a56c500a", + "0x3a4567ffd2524dc83e9b5ca755dffdc246a0f6745b6394512f1b4a1173f1802a", + "0x8ab69d10b443cc75f82a837c90fa50504d48a6ee271c1f8933dea22d35284b8d", + "0xba2e5bc0645d475387201c35201ba8cd9277ee3916dd401b0a84e17a62a9e9a8", + "0x1bb82360ea0cf85bfe19b5c70227e76851607888662455943d1f8127dd1f8700", + "0xfee69c6298195110ec0f286adbd82ee26a47129759a4b0e0c48012b6fdbda0e4", + "0xf6c5016c2c36217a66898ce388329c33af8948d3a7ba1aa31e62b4714baa4dac", + "0x213f87f986a4d858cb68f9865bf5d6f71f09877afa009d80d3af03ddc6051431", + "0x57d90fdc575729ebdc24b8ac8808c431cd4ccd6b3b6cde6847cc638e2655214b", + "0xaa9ea941a6a3a51a0adafe1e1583c595bf6859617867444e5203e6b193d9c331", + "0x8a7dcb53f7cb43065677e35e1a0117a0749fbcb6ab7f5a76e4ecfc7e898ae3e7", + "0xa5efbde76efb3343695b48746a6566e47117bc127ca315940e98dc1feae5a220", + "0x4fd076fd325fd3d0ca61146406ea61162979ea0e49e9979f0bbacf65ff795272", + "0xf04246d7ce3c01e031f7372176b895b6370a640d6a422a9e04829208b9685749", + "0xe91ecd8fe4363f59b22fac2ff0ea9b60fe2d825e2f76b185a3fa531435922181", + "0x8581e874dcea5c2f2bbab9dba8b66a90c4624e58d8705711c1646a7b048457ce", + "0x676f0f0796a39242310d0d7eb5f3062eb9e0f4950d567c8eecb573214ffa3bbd", + "0xe2390e97ea7555074a06b2f61f77bb9bb3228ebd13dadb3708cd8d1c1474f17a", + "0x1a0857cf60e0770d46a59036af913cfd6cd2894e6872636faf318d7cc6c78d18", + "0x7cbd4bd3b4cac529120f80588f55535867c307aa0daf6d0fcde46629d8b30136", + "0xf22100faf8baa066105414ebe7d8720a7241705144d6c9b2e346dd22d5cb470c", + "0xa87a1f346e5b1734c0e3272264d8a03c3040f7b03a1ae4f1f834474080d47fab", + "0x14f8dc6253e7e5879df73530b4275906bbfe58707426e33f25b488ae5b62c207", + "0x6c5759d111827ac35c971dd395c88639f31755567345777ba7cdc59bf44744c5", + "0x9007f7fbf2d01df6850b18fcf3ba4d100a033d25d613d6a4d6a285ca14f0cd43", + "0xd59e61f5a307c733db346c70609b7b4254ee99ae355ac4e26b58010c57aeb4a7", + "0x500e4767d559e63514c36911de65d8b23cb2e7bdecf32c92a23f3f1379dd94e4", + "0xfeda7f0ec81633f7c571be341c381167783efdd7f1e3c8d84311ddc31da8fb37", + "0x06351d32ca8dc5bfbcb67e5d792e9758a67c93b889fb488c3c7dad0406d1e2ca", + "0x6c0fa78099b53ca4b2ea78781eec07fd56a1806458c1d9db9e85942399175ffb", + "0xf8592417f793f640a4729493220556c3664847984b875bdc02e3593b1d7daf63", + "0xf98e61c96405e86cea00349c149b688ebfb6711ffb50bbb5c7c10c9649eb3531", + "0x5a18a55ca1c9bb134c38f63152cfa89b81c0c07ec3e679047e42a912e1c430ee", + "0xf68b52aa357bdb1d661146a89bacae2b90817e764535abe555ac8f18a5bdf7cf", + "0xb050a4ab781f1a95e7ed6ce6b5184db15f2dab9d4a420ccd4a1591782b34ed36", + "0x0173b0f943c59abe721115e20c456d7a73352509e5d0196aacd93a97d039f195", + "0x3d9f9a57dc3e714918ac305adcc31ffa8d1d1ae63eae105440f33c8317278391", + "0x686ff3ddaaa9ba2b9ee071b4d0efa4e27f92f25660cfdcefe81109bb005d199e", + "0xf57bd8dbc95c21bc23481f393f7699ad73249a56edb8928c68fb153a457d3075", + "0xc69511d380b9f3b33f732d385193ee5e9d2cd71bf0197bebe396351120e9968c", + "0x1c17b186d4d93889cf1af3019bd56a9ef1a25f5988c21d7dce0a19e34b154163", + "0x6535b8204aedf9e64903910503674e8a130d502acadf6ac1c5da867ee2ff80df", + "0xef6baadf806dad8fa064f32bd85df6712ddbdca05bec66c4bfed649b38941f9d", + "0x25748037bd2fbd399fd51131912d6ae1b0c65233f7a6edf79313fcdc61515dcd", + "0xbb1d6d0afdebc8ac0a80e1fe85d1106ba14d607331f3f940d2cb01c4434c9470", + "0x01d4bc70e15c94a8dea3591fe05b4d3104fb7fbbdaf183fe8d7d72e5cd22baa4", + "0xbab832143bc83d0218ae8a6fde83a208b7c97113ddd743cdbb3a3f51bb64f39a", + "0xddef45983e933733d3d0892edd3707131977bc97f3265401c5ac83af103d68ab", + "0xbacee7cdaefc2dbb05883637bd0a9bb1716429f3e0f1f9f5049add81e3c70b89", + "0x8116439f409e3256c9bfbc42e2caba4d9d159ab72f6572ad99af5f5a597296f5", + "0xf3c789c7be4858ae930caf5a2c7196037eb6261abe39a845f9d5010818216b2f", + "0xdcb3407deafe4de69beba8ae65dd8c91f24a1704b48a0b2f26cef13aa8567efc", + "0x251c90fc770d4a43f088ca20d554bd22b1a809e9a08aca28a02282cb1a74f306", + "0x64cdc0d5762e775f75c34917cf1904b81a705676d212c8cd9d24908a6e73d9c8", + "0xe9dda92a39e5e67a7d48cfcd5bef831d28fe61082fdcda250b6ae4167f1a8dd6", + "0xe0b29cd2cf8d39d4b7e8a04f12787201ff92b1d136baefe3c3399d8519c67b35", + "0xdd68b2aeeee147d0a2bb3cad53dbca69a6ef391a1bc9506a88f5c625c01e1011", + "0x53519a16d13fc94342c8bd6fa93ac2311dd7b774cc23a601b5d4228d986392e7", + "0x41a22c284130797bb96944a7d80321a8087550ec053c4563520d23b3d5ee6b25", + "0xf62b5e5985f058adb6418bc3f5806d0ea99543573347d3444eedf704f7120692", + "0x87cb46a7a0bd2b7d7397d960460794771afae0b94703dc91019a01db5552bf56", + "0x04986ab6e438fa81f8089d15e46910bfde04b9dab6a8a4f7b28eae49b051857d", + "0x6a066a09d89f33d887ec85ac49e2a16ce1148acc01e415ed5ae9f17121b8064a", + "0xbb456a44feb15c7d81c211e49b8c53805eaade020395fc5731ff312edf9ea6fe", + "0x2c9f32736f0b5ce571096f0c6d4705f552661f0891dbee1ea990d81a1bd59685", + "0x39745f605e8b649b1a2408ad72ab042de2a919f4ce9d70d97faa47663ecef700", + "0x60cf9b266c93f0d9c0499d900f8040d86f823ef72998d0aa30b5068227996006", + "0x9e97998faa147364fe6878366a6952e3a68422f8efb1d92e1e3b5d53a724ceb7", + "0xd4dabe08cf80bef415fc40dcf6d207c2bd1988f840067a43aa47ad9811b683fd", + "0x307824c0450e344c084a6ed91620aaf763a9e2f823e94e3270ad14bd95e9e0a2", + "0xfeb0962f36ae2a91b8f7b772ffa0c92da635c0316d057a698af819b8640e7819", + "0xf1fa4f53c861a8d297eee3a8c0cdaabc8bd20bf400dd6e13bf2affd564c41876", + "0x1bcc84b09d64810e4b077a77d840c44a1b900a033f74295a694d13c29d535460", + "0xafed26d2a2e1f5c5092dde3f0c1644e2005a2a317218ce38f7b135d7d7968d98", + "0x99ac2608813907f19e8e083fe93703d1c90ac17d1c5192c9c4834d46aaa1479b", + "0xf0bc4bbb09d7e83ac9e0a8090835ad63153ea3c1d964ef5730f6b70a6c5ce523", + "0xd6218951f8a6f2d2d12b30861f104dd2709fecfb58ec16edbb9e72820734ee08", + "0xcf7dfa7e1c2f659f52f77f4f6f6f6bb70bc1aaa5d06c3806848e4759ef7bad33", + "0x168fc638a985f1373eaf9c45d42fa15b0a0ed64e7bfcfb108df5aed50faa6ab2", + "0x1c59062c11c7a28fce1e9464b9ee93fef92f150a7239a1a75f0ccf90b2011016", + "0x142726de957b381a51c690c2c2aa0c0022b68d9c1e73ab0fd62335cdbdb1dedf", + "0x60be2c87e9c7e178231ee617ffa309723a5f2f9fb9bc774962b30bd0d1a0d227", + "0xfb2c2b55113758e0ef71171f26948e264ee60a0ce53f389fb72b3e978f4a7ee0", + "0xf560f8ff2cd621c93118ddab18adc29a8b33d73e35117042290c5378330c2374", + "0x624485e37a79a029709791b2d25c7119aa428b810fa0f074db84e30930ecf5e7", + "0xa6c0e1100e8084efecc427b69809b32b8f7c91d0db04f4c3668e6681fba4a103", + "0xc471bc8bc76ed0c9fdc8e43848cef5da5a37d9c1acdb588d68a6e9504d17217c", + "0xe667a547d1e8658c7b3936e54fc9f7a297739523c875a2d47cd349c84d162dad", + "0xfc3c754f152ffdd8c6c44839dc53bd702527eca16723901fdda230b413029702", + "0x7e771fb13105e19f18b957b290423effc485da8780d79c39b3e7031030c2eb38", + "0xfa63241bebd1534cc5b048abafd5eb71ba2d0a35dceb63926ae11b4d027b1eef", + "0xc0ad28386bc79e3298f92a71632d891422196d3bbbe0af3cc784df27402e6587", + "0x42967818ddd9b6c7286662868dea47ce65f54dc0283eb523d14527c64c8e54bf", + "0xec7aa51f45afaccdcd5fe6ffcca120368421c992d90282014153d36d5d966fe3", + "0x91d140a3db3246954d87df2d74bc4976ebafc85f0f2770c4f237d114e5da7d2a", + "0x982b29d4e37422c2dbaa03c7b104a090c61d4d0e18f7eed8301ea93e557d6908", + "0x09ad2505199df8852c1f6cb775f44e97fe53214b0bf3fdf7820bd1b2a7e5c2c5", + "0xac3e25aa84ccc1e47e68f6ae0e632e36312d12e85a7cf09b05e845112258b9fe", + "0x023712d71312510c04b0f4987b3da76f56c04fa5fe15f348f646fbce88afee96", + "0x8cd988d5270b27a1055a8dc2eadca3044cd9bd9cd69f03f0edb75fa0f55c4f5b", + "0x60ebcc7b3e947ca291c4c5d02ad6dd8acdaa1cd9d051c4feeca8272e2fa27f2b", + "0x4e81414371da274b78bc6f372eff7b7fda54c300f3936f1d85038d0149c651b0", + "0x351e6c89b810c6eaa1094423e135efd6fc8aa11da8ca5f26f173d7f2255ecb5e", + "0x5b70166cf6eec57e9355c8581167438a59a898cf99e3e8e16fff2aa5de9fd7aa", + "0xf63817a6b59e91f3593169e7f5deee3d36bff85e42254982e1c5b0acb0adea81", + "0x7c943930dc010c37fca47d518c905c8dffebbf89cf4956c3f8526247fb7dd52e", + "0x61e3671d4b76f44b0d93dc49411e3b1d9641c7e574e0669f7a3056207f12feff", + "0x98d9d72c9402187e2f702a5826ed299f6a2118ed1a075ce66114ff51bae157d1", + "0xe6d51d3e0fc3efc28cb3aaf37f202d486208cc656ba23dcd51f4fc8bfd7b5911", + "0x6f83650cb9ce77e4e7f2a4c1404350733e3f2005c37343e9076b4c269138898e", + "0x89b896461b265457d48928b12d1da3db9b03d57d121c8ae2012d3781c36377cd", + "0x75f80f75c8c2762ce799882d1f7dd284ae8af11ec5136aca45f37b53b4e82474", + "0x075e2d5efd480e92bdfc31ebdbe4a0e17c60335d16af6d1f9e6107e9521f55c7", + "0xc5f8b6e08249510743ffe9493f4527771852e9a914781bd6ffb1ff0ec9ce04a4", + "0xc56b8ea9f445b5c9639984c48e3e4098071e428a668d042329ca41e6a049a4ee", + "0x5d4baeb6ac832521227169db24052be286ede2f3c39092b0dc9046bb0549512e", + "0x0ef1ecdbc9a2263f88b0c4b7a992fbb043d42c2b97d01c02d0a526cd4e3a8ee5", + "0x0394a80081074483a808469326a56cb37e6e2c134e9d5e4fdf3503d6a76c1dfb", + "0xe8550afae6029a44d6e21bc51578e8677e4c265d285e8b782b103b5e329cadb3", + "0x817de60bd46cc2e844d7df702251a15841ff3edf1d0a42be0c436d4bb92c1ffa", + "0x2fbdd8eb428a3aa87b09238e629bf47fad9425d13a10f8d8ef1a0e8f0ba231f0", + "0x27765c61cbed4c8cff7a8fd0cb19be479d2dd45fcbd54ac4fc049c94141f432f", + "0x6c52db3e631a0d06bfb11a1ae00e1cba9dabe1a5b8e6140ce6a9b671cbd27de6", + "0xe449b9ac0037564fd0c37bf922666eed98d479a96b4698d6616756b27f87ce59", + "0x3823988585a238d9084e01f55d97652cdcb67b36215aed6b7c9c276e1537ce23", + "0x0b9d33895650ee729c303148fb5f426b88b4865ff0b9d43d4d591d704a6802c8", + "0x84ba43b1023b062db40e054f7a78efe60225b98b2c78d3d58a6e121b784da562", + "0x97121594eaafca267572a86e40823ab885ec183b004a103dadf76483c91396b1", + "0x1716413c2ac59aae323aa25edb686fc9cbfaf0445fc0da956c174f43ac785461", + "0xb4a4ced069974f98a0e44e39b8e6077a9cc1d31e0de30b7b7efcb06de48352fa", + "0x289c96a7f19955033cb058e4845a2f40d621f223fb5ac82c7adc52cd2b9362cd", + "0xee11ce0a54837dee919fda6c9136ff5744e5254eaf2025a4753b0dd26675c087", + "0x2bbd45d3cc0315bde561cbc9e1785c02ca5f2ab6cdc7facdafe3d6495e2ad3d4", + "0x590462a261de5cfe0f9ea51d74a69a33a6e34483c6d0028da1b444dbb857a1d5", + "0xd3f6a1263536fb15f7fbd59dfa467894e1aed8746625bdf801d7a3eed89e004d", + "0x01c8c6de80c910d426e899c49632a31c1640f18be07f9723a3cc257a86f8b922", + "0xb1a7f2d84a6a1e6905c19b8996bf6d1abf42be1dcef81191d4506f468f766649", + "0x6f185b64bf3c72552d6e344d3dd5d66a2fd0af1c54636452964c3cc5d1d1c5bb", + "0xbb5a4c11efdca82a0c7c250195216f237b7c311757c54d999fe0eb412f60b714", + "0x3c75fe4f682782db273e1f4a0ca841149bd7e53e1afb473ae1720b93deb3b0db", + "0xe83db9b4d309dde9a53eabe4f83d7b0ece05015b68b56c472c701d2def7078e4", + "0xe13e2e696e89e5faa515fd717aceaa705342b77e4390f21578771ac0065189cc", + "0x9208bb5141a8f4fdfd9bca473ba83619ebad44c367500b64d25e136a8d8787bc", + "0x3856271138ad927b63b8c1d97e9c36162bbd57f9263ca35af5fe47ed0fa24863", + "0x2ac64bd4e2a57c98e41b531e75d0c6f7c11a8ecadddcad0c63239a2279ec37ee", + "0x8b1db55e16f6b5df9b144abcf69e88416c6f959c246dc6a94daa97d9f169613c", + "0x2dd6f109e8db7ed56599a244ec7749e643e4ec867fa46c7615e95a576776d323", + "0x449d062c7645dd22cec7854dfa3809d33432095448776655902fcd6788a043df", + "0x8f0a9addca05bbaf07b5a6b4da3fc9e3e8863a2c558351a3315218d705d0fddd", + "0xd72b50989256a4022a2c43a5edacb258b1dc961ad8682fbed238292dc7ebc53f", + "0x2c39a50a3ca7a521e5553d7297231f975a252282cfe587009284152283140a5a", + "0xb80c1480620da9283f9ebf34ec3d5b1734d49f7ed78e0cdba1a5483e2feaca18", + "0x6ea84ecadaebe301687403554f8ba930cd49a8ae0f3e378c5a00242eeec1cc35", + "0x2891063b542fa840de862a720134caf2df044172fbaafda9d47e18b3c0bb3c30", + "0xdc33f5c382dbaae5b7359ffae13325a363fcaa92282908dc1123454d37bef9a4", + "0x29b9ea7c9c020062f18c36aeaeb51360f7efe72e56c135a1983eb25b3252fb8b", + "0xa6897105371f934f148f23b55649561ee488838ca2ad23bf63aa2d9521e302c5", + "0xfc6b0067c7f4dede47bacce63e0d4d39c093841da45fa8c4dc6fd4c38c2730f6", + "0x653de20815daf5113d6f1aa1d48ebd9e77c26392bedd51d3028c98f4c9d1bf35", + "0x1cd776e94683fd815d3aec896ae1a1499b22f9becbf46a8573df43542a6907d6", + "0x466f75a84183295bef8aec03e76256517fa6fbf593f48549fe7fab13e8c03c8d", + "0xe442158ef8a24df53d4cd9d4da754d34568be66ed32c421ebc2ef2f6f3a5d10a", + "0xacc4a75fec055f8afc535c9e8fc0ed7b408ee9f0334c019df32718c37bf13f00", + "0x8b41e897c4d032adfdbd3942ad08d4580cee2dee2902e898d37f4a06c95a41d6", + "0x285bbc0970cc8fd32efcb51d1b6cb9ce7debb89262f95d3813a73e7267b1acda", + "0xab103b9fb7e9ea11b609d801ed4876013954404aded8f3f9e12214388288bebe", + "0xe2e0355a5aa5cea898e053ac2691831863329aa26e3e10e0ebc39b0cef913471", + "0x863d72987b6bcfb9b4762b2a0fbf6b2e53ba219445a4045874b5b1335f2435b3", + "0x4919f401025ff03c0f5b1e9bfe4b7de3aa8cee083d59b83a3d89807a94c489e5", + "0x075c4d3c695d398abe1c37d92a1fc5b15952ebc84c020926a92ccb2075405e77", + "0x5ce22d874abd4b1154bb2c7ae72f75fae9f09879ac9409b6241007bf7827dfb5", + "0x591f7aaa31062df334bd904d6f12b0f000ea50866994ad31d6b4a0bfbfa4feb8", + "0x376b16ab378c6809d6e10e5f3864b13a0803f32460725698cf3398c58a582df7", + "0x8979823f6db99d09ca99a665d27a62af0d79fe05077a90f5102990b16d14b92b", + "0xbe6bb2b147f4bbf8c6e05da4e9c453f274f89e6599123ca1034e6c0d696451c0", + "0xbed3de7dee65357ae24d8a14840a88391b01ec8d17812f645e6b0e7afc063ccd", + "0x44eb84be90b90c6b886cdafc7b414f2155acff20b92f56da392e9be9ddd8b52c", + "0x94a62d3934ff1d0908df1b37ec31c966b373a75954d95ad3e169cd37177ccf47", + "0x82d8b7433ece8b45c2b1d93da6d0354acc10e103a7e418deb3fbd18f0fd001f6", + "0x5f25566e94bd06a57e75a00339bbbbd5c59deb6dd6590fb3494b8ed1185e2033", + "0xd40d8a52c26712736f73d9d069ac10e21cb59803c3efc087495f80287ed64970", + "0xd15e71c6412128b15d91d8cbdb95d31b72ceb7a4f4e9e5bf1a88eab668d96c09", + "0x9472e0e13831a119743367d915c83f4810a702e739dd8ac42cb3ed99d32ec408", + "0xefdcdc95448df1d0b17609ad5e3fe87eeb0a7d9219d021cb537f5f8409c6f62a", + "0xb69d34b14fa27e56520028d772cade83db2c94dfbc649b0384c673c65c36db7f", + "0x5d8d0651e838c1e809b95cbaaf710be0fdf77e8db5aa0bfee1a5875633ef3ec3", + "0xdd943ac1086b218ba3e15a5099c7397187b8d9def7ed4ed3e05a625fd093356b", + "0xdab6b650fb26d26a14eb9fccbadd9d727e4e4564c053f2dd6e41a8336cb12b2b", + "0x234b4fdebe97e798456fc97da251aa4ca3483b5ba61e5f25d3816d88d79ec4b5", + "0x0d3059cda7df5cbad6cb4e8a3201b745cfb163ab4ffb1a07f8e90f44c3ceb5d1", + "0x57f225e458e43a496e0ffc2b0403d9b3d850e0992052667bd0fc1dde3c4e3eb8", + "0x90faa103cd73b87d884bb4ccad5255a1bdc6d050a1829b19446007a992cf1f40", + "0x7f47b47ac38611c950e7829233084b9a2cf20f269e8ebcf3158ec3cb34569650", + "0xb4a27df5f071f97a5a00fe31837d2ac9e1b4ac3ad4faaadaa294d241b05d79de", + "0xe4e3495a5a68da53f6236a5c2cf4a9e81ff45fe93b61d0ac10ae72a5a66d7aa2", + "0x323c7db2aa31c1798f40ca7b3dbd1d3fcbe170d4e7ff6ae6fc04a6f20ab789ce", + "0x5f54231f72e2938562c836b4f63716e32abfef6c82f78dfc7161eafbfb87b10a", + "0x7ad190cc311c20ac29b8a7908224fa5460cb4b848c57fee12055505f6bc4f272", + "0xa5808cb9d41cba6c68bede26aa8bd0f021c379b6c0c5b7a2f1b0acb710434cc3", + "0x8a64381cb39ac4ea2a4908e1683758f8da66ea04f6b37f58585288182b5ab1e9", + "0xd2969eade6d845e9fa7ddca7e4a2baa42955c487ddc7845ee6c1d262f3e0755e", + "0x525d7d105a4e6833bed8713769572e20ce1c8de5978b862b429fe54eaae24725", + "0xe563d70e5ae650ba90cee5c2aa4ae9c322ec73f700da3d2309c170992586cd0a", + "0xef79abe35c55ceee50fb60e4e2c49f04414988c3a0da146f0e16178a1e951681", + "0x6da724d7041eb0aa72024e56f0da4993619e6e76549d47a8985cadf80bff1e0b", + "0x6b156ae0aea6777a0b52e0d4e46ed285da23d19544c5f25e7493e9699bcd29b7", + "0x28527442e1a4f0206b45d6fcbe5072c20f16d3fa0683859dbcc03f56eadb7569", + "0x8eb68b487dec0850aaabe9b340de65dfa7a85ace89aec563f4365561e9234879", + "0xab93b8048a14c1da45684ababef0a60d7876024b2b4f064168bfb2fd31b206fd", + "0x9cdcc7b77448612dece41882c270d5aac9273babf6eb0b609906007862c92f72", + "0xd323bf14e69a3a935ceceb4e22879c7f579e133212b7ef1fd057e9900a917fd5", + "0x68aa35f07127a56107ebb2ed856a93a77e694afbbfb6372edc0f52c41d3bb3af", + "0xedbe359a413ec0466c82571cf7290e89c4cd6940574a11656c9a7cc9eb8072cc", + "0xa8f5d2c2f3c25dfeb29e1f4d5b4c6d11fd508a4f14fe8096962906c854e69a6e", + "0xa3c1764d5b5ff48380c3e61d78bf05be074f1c99a29494036bc0e3872a783759", + "0x2d6da59e1029325012dbaeaca45bab1839c57fe6ace993cc0819020eeaa96c50", + "0xa2a71fe6f364a0fda70a5547590e2fe4e103db1081fc311a6ec682c1fbd52c75", + "0x19f06954bbe9d8b3de6f7e014a14e177e21d2acec3414e400e057592a153ca85", + "0xbe4f443cb912bcfffefb60efd623d4ff578daa67b41d5e0fdd9f5060ecad3bad", + "0xfe6dedcaeae82d3e120f0cf572dbc055c17bae07c6d10a3497ea2a653cbaceaa", + "0xb7447a08bc05a1241a90bb65308d12474eccfa8443ee344379578a89420eab3a", + "0xf087086dcfc5ac2e8d98447cd7e577185dd91df0bb61e8ffd1732a6e705454ca", + "0xeb16b9a95ce0edbbe80c8a510352a678a5b5e197818997164a7317229b441e01", + "0x3cdca082ebcbf147b16c76c278f6ad4f53497ed13bbde3323e6c17a4b415cb89", + "0x6a0101ce388b9166c8e8d1137bd838ab05bf2228533a579de106997bfe7e4e3b", + "0x86f2b754d67f9bee14d675815749d8567069d753cb47b868aecfbe5d98d2a139", + "0x367b6cebbf26eabe34ccf1dc430234ec08874678b66d5fdbf8a1739ec42db657", + "0x1436c128b112e789dbcf7698750e474a2c92290d62ac99dd2e5346a0efae965d", + "0xc9d2caa53c1845698c9c78151ce8bc411632e90e1d5103e96c4d256a98cae9a3", + "0xf36462b1515df13ceac033bad72a8d204691b36a2a6b8ee5a4118ab39506f555", + "0xedd9d8c372c86e7176b1f8af868d2763fbcf2c6ead720d2f66616022edec0a4b", + "0x9adf00206824e052b36c26cbee545ff1451733a7bb7328107fe74689d49dae80", + "0xf9037df49b414a9bafbdee27bccf0bb999e8824d4922929b40ba9a28488dfa2d", + "0xc5733818c586882d87e115ec1d6ebe808e4df13ec8b6155663e70f576db914c0", + "0xb7f4a6036e217932714fa473efc75ffb3b5834323545214b155a6414708ff2fc", + "0x547bcf2a44f9bedebf277e6a87ca9128aee11b1ce090dd730c961113e66e7704", + "0xfa93fdeb04dea25d7ac4a2470aa00a39f5bea98e02ecf07f03b62ff1e9d42215", + "0xbce0d4c63a99446925a7b359aeea4fb47d058955c4a76f59d90a6f1f9239efdf", + "0x7371828ed5c5dcad5abdf1ea23ef26b1a99f8b22eada485976035a1ea394c6a0", + "0x8a91b1e719592af9216e8934b70fbf15e7b3d6cccdfbb444e21294c8770839f1", + "0x64abbb19dada2985f065cc8c9ae37805733bf893b9925ef2598cdead5081cdfa", + "0xda21d86e3fc87f875df6b3abf09e0e47ac5e9e69a8ec671f0144f4114025f698", + "0xee348fd0a840c1138d84330e353fb7d17747f416ea7f93afe87dcf445f90fe38", + "0xea67bd713e3c1857e9bd71b4473abb8e86efabd313f1c121842c2b5a97ce0a53", + "0xdb7fc6b6ecd81528cff5afc73ae0d91b7ab961fa3bc6f67d1e5c686260901aa3", + "0x6d495e4e028f65d40fc83e5b9847c1306a0f41374abf2ad6ca274bf7aa47c1cc", + "0xd8dc8a3e168725ec6e35d59db6be98779ad8b600b5ac9059a2e26f19bc663b6d", + "0x659190ceb8a6ae4cd222f2abbe9347aa97f11933459005188dbbd9be8043ef9c", + "0xe2f2066830438727111dcde6b122b7e96b6c560fa913b43fdc499277a62b6980", + "0x0ab14bf60b621fe896cd39fbccdc0fc28704a9b6fca97076731158ab895ed6a4", + "0x7e61c09dadfebc6c9e4f144b50144f4b95876fb30ac548a8708a658d0b473b0d", + "0xdfd24a0ec15fbfd4d186a0b271485579b335655565433d02cb09528417ba0d1b", + "0x2f4422c37f5e1367ee43478cbd0f91df4dc698b790e13d50de93198c7d39307a", + "0xfcfbaa24b96d6c1eaed8392d0bb632f2b12d7f99feb4c23f3e4c7c40cb02302d", + "0x2ad0e89ee4780a3600b39ad2a6d7f7f5b8bcf3db499e3e420537e9a8e7956e5b", + "0x3a04063bce478131c4b37c542d616c649a3969705ef277fc1ac396dbc1d6a3e5", + "0x38a8a482a61a6e384f3258d8760bfd93f24a4cac132931c05b901870195da472", + "0x0fcb73dc552315138f00277ca155fcefb172d5875135300a7d0797f4ef563cb3", + "0xf7017c67c1eb1aec2511dca6cb249560c8cfa8f73e86950aeef7bf07607524cc", + "0xb257e0feb3b93c6e27e7f3688b30c132663b3bfb103bb7c52a4ebc7f26224752", + "0x887671bcf6a3c3e0ea91498dab09228d687cca4707a5629d421acc61b644178f", + "0x88025fc84feac526a9ee67b90adb10d6def83f7476b31fce207e25c323cbbc9f", + "0x0ff6d20cfc21654b5b183c7f959c011e658a17d655b1c292798b44a89319a49c", + "0xa650c5bef59579a9c55f4ae77a8cb4b85b02ecdf9e33fb1dbe62a79907b01024", + "0xa4c732931d630f97fe053956ff3e5993128e62aa5544fafba490b7495a3c2b96", + "0x01edc45beba35b20f68e622731078016e5837eea4a3bd3c4cd8416a79a92206f", + "0x3a3b356c9bb5fe4251a9d3c1e9a3512ca53be56239ad0bbb092836c6d849883f", + "0xbc2019383e67a55d2701b00857b39343d0b4cc9ed9249258785d1b925c00f038", + "0x2af5508bb9812ca0aff982328b92bee5d76b7d62e650a32cbd13d4baf0a4870b", + "0xc72f178a40119aaa301e3e6bc6dfa8c37c922388788b9573428084a7d65068fd", + "0x5ff261419f4c95f284972ded52ebff903b3eb9d781cdc7252b0723d196b0b116", + "0x76864f2fd92deacbc26da715e96db9985a455c4103f5d15f907262ff3d25de2f", + "0x38018b4cf9574d483f8ed019bb7fe1138238796c75efd1095d0134ae96a5a3a3", + "0xf702a6e48341da06488b72e74a01c53692506c8d95b124f4a27dd4567443506d", + "0x9ad2585cc7650a1b7b0b5f392a783f327caf17864b5091fb2ab93c20339524fc", + "0x7cfb383fade30929adf65b2c8b2afb5fa94b9772fb6d82151f39597e1ac80407", + "0x39aa1d9ff32e6313943b39fa7682b315b187c7217ad02f65432bc9b46216279a", + "0x267c4207cdda274d3548e38af19ec371396e7e7491c34071f565da6afedf2392", + "0xe8007e4c7d97fbd953406861f73696c495ceacfa0229cce24b59ace294dab3a1", + "0xbb8a48af100ec9bb31dd20843fb1e68b1ec93c0592b8a4ea33d9d1f41da9ac8c", + "0xf0d44929e376b1a8a3f38febfe23660205d8363eb4cd9a2e007651d843053331", + "0x4144a6626ca3a8b990017176710e3366671faf57fee0922beb3e8ca915bda35e", + "0xc5b09c41a4b28c1ffe7f00aa22975077077eac972c72f4facc9f35aa0202a5d8", + "0x6bb535262928c262c7b9a5e93b3d5ab74c672a3967770a3a45371f1d56b42411", + "0x9842c9b54d8d849a55719b7920caddf04b66732b88cf765e9f2a9070341dac85", + "0xecdd44a0250af4e8a785c947eb5307c797a9b180819a54e6a4576c30ab44f250", + "0x06f94075bb8f54dd7686dfd203104c1386d520628f98113bd4f03d5cd243b4c7", + "0x800cca78583b021f44336e54a54010119e443860de88f823f8fe5fe3e2dffb97", + "0x7b5fae9e0f5c80ec5d4ca551d0239a45f774b5ea34a142ebc8bc8f0eecd07f1b", + "0x1a2e2ce683507e8d0db1d23804d60680eda9145ad4a4138ec860571a10a6b147", + "0xf5e80e6f4641f731da271f2afc05025ab3627b59b7f49929bc195a4a155ff517", + "0xa266074776b772265d0319d67a3e0bb862b4e0848d8676bff40ab8824a8ecf3b", + "0x96a22f9edcc79688fbc21778660924585c5d0ec6c657d6613d5176ca4d135e8f", + "0x3a7dbe0f7efbf8cf726ba964282c9a487254e5d01a5301db7e1a1d79dcbad53d", + "0x1ceaad8e4790880087b467508d76e3d8a237942676aea1edfbe63899ba6df401", + "0xc1b8024a876e060aa5964af3f1257ca3c7ec389773ac036876af261c9c4686af", + "0xf59637ca6cd3e773e4956df1b6d18df88b120a2b65cb4def177422683a0ff22a", + "0xcd7a7a3c4ac3057d960e7a73a6be18f7a7a1ee6da7b34327de9ce866d6e0c31f", + "0xdb60e683dc250ec2cee0b4244a00b7ca2aaa5dae39bd32d207762313a99d61d3", + "0x741e00aa3252019b08b1796e3ad0c8fac64d049ba98ad05301e0d201c925e951", + "0x5b33e6e181773d800ed7386de152176bbd47e01d7d6b4db4789a08e6ab7582bc", + "0x8e039b890c754156b7d653ada682df17b9ce68e629ab714cb33cc3f12484a2a6", + "0xba45383376b68123196dd5e17bc59df10330a3be3af5ab15ca2ff1ae14aab02d", + "0x8a62ab3d81462a618d6815f2cfd670e2c3d589dd426bb24c64d5a82eab79e65f", + "0x1ccd93feafa5b680baf7573771d4dda307288672f7dd64cac275c574a27a94eb", + "0x97effeb5e09a506ad27d7f51ca08788404fbdbd65b596e349b6140e6623b5234", + "0xf269ba81f90579d2ba3c3f6a16243551996125d434aeffb0e8fb35424b9e5f68", + "0x1111e965f9a59b2591a8f2c8567f122b3a1b9fd117d4b452ca1945f71cbc515d", + "0xf5d2cf90128ea370584dae13398ebe56bbd5b98f44bcf37df45712740286975d", + "0xa523d68a2dec9998bd77a986b53b10c144b11412856c40dfcb5073a6ee52ada7", + "0xbf11e9c11fc93c54043e833a765ca39476e02f8f5bcafbe421e91e691d88e107", + "0x6ca095d83dc61034bc9ad507d0cf9beeef441b71e46ef127945398f44f0e05c6", + "0x56cb43878b240d8a81e5571a41a6179847cf6a5ba9aaffb5c957e9b26071aed4", + "0xce9205d34c4155834a45da56f68d547bafb7e9e58ef08b2e5c4f66693916d52f", + "0xff90e36b74a73550a24070f8ce4f7c9ea967196fa2d0c6b8e11996bd04ad5ab9", + "0x469979c7e59d44c4e37afe560d2046e0a23f417ae7857db4e2cbe594ceeb02da", + "0x5e51248403c541889ff46e5ea0fcab0a27cf012644c15061ead42e74883eaf13", + "0xdd14c8b5ad72cbce7fb2bb311e208f9bbc6674a4ed03182ef2ae58d0cf63f81e", + "0xec618288250a6b5f63611709eb6062e41200dbafee86d597bae0bd50213a5a07", + "0xef2f66869f2547a0a52b82267f7c9ca7f3af21288d27a38b044c61f5edd55c2d", + "0x058a0175a4a0486f32e4a6444234b4475e14eb2080b610d07fbe1dcd5c58d053", + "0x5e17ddff2b6f6ccc2e01836c99f479f8a3c825bfe8aa52f1da38c35478ef7b84", + "0x94507b9a33973c0f628d25e573b93d9d81635933caba0a58e2dd349f74a7cff7", + "0x19c280126a5b912683742496ce8c364b3b2db91b840a56939178c1aa232a867d", + "0x734cf4d8b4b06850c528bef95f3d6cc178cc2884d7ec4710c126cec33ce2484e", + "0x881d5d7fe052ff7e6da3f4176b41a8508d7843474fd33dace8d09659a8c3ed3f", + "0x8dd917bba4ebdbb4027a7bc12de231593f7a100f1f1d393ca459393d1b591919", + "0x1a3a79018018ce42c77d7dfa6bde87b8bac8884ccdcfa678af8b262c85e8a18d", + "0xabd5681eb3db1d51355ed192762e8f84bb6f5b695f13e13b1b4da7eab832992c", + "0x7046848c6c0988f397d5cd9261cefe6ddbb360ce32cd62782bbe8542b128e96a", + "0x44cf08bf33aad644c7b328d3dbae53325f1ffa1e0ecb37197bd95274101039b4", + "0xcc6f19ea9cb9f0465d9d3a0348b4011912c94eb9854f382a81beddbb35815ad6", + "0x777600929aabd471c70415eac78f7155aa3e786e1424bcc2c7e46987d72916ce", + "0xa3ed48b54060a1f4a41ef16aadf70ac99723758e9bc3da30775c7a7af8d67938", + "0x45454d0208d838cb9b730606d7bf2230e302a760774bbaacf3a8ae6d3aad336c", + "0xa8a936725389af09fe922f1d9e9c2f76ce42a9326c270166ff2eb80f036ce8b7", + "0x873ca4346300dda250c536abda3f311f5df0726bee03a5bcc9d7cdad2bc616af", + "0xa5fa0c51ff6f36bbd3a736a6d1803e58dc9cbea3326571242b902919da706b0e", + "0xe6bb12a6555c847333fa9d1b9affa0b2b04221fbbc65f69bad3d6bc8271741db", + "0x4a3a74cd5f935ef209fb9d0ef18299b60a09d66384dce73f78d24843a7151a17", + "0x70f74c85ed90fe2e154823a45a7cafbd2948ca94bf1eed8c8401411b61913df6", + "0x85636dfb3d7695494b451120232c14ee1bbdae6b69d83c80446551473ecdd9d2", + "0x9adc39708c11c2f538b199bc221054200f1bbec79b4263bb03588cb36951fa82", + "0x3ef382092645f2d2fb3acea45a919f77aee3316eefb06225cafc09dab983f089", + "0x1aa968a676fc366b84e63be2905b6ab9a9cc0e820e0c9db004b200bb60a95ad7", + "0x43d185c27757a25d706edb1b106d89a4c49ee55a30546b33e723a0c77564a01b", + "0xf96c017806c4a0d65f83cc43fbbc405062bcd6cb97e961e367b1fa14bb63096d", + "0x32f1244cfa0a70b674d3ab77d5b80af3d900257d7493a3d15f5cd067b22dc584", + "0x880dca84672812f0a0d5f90783c4dce9d30c0ab6d2fc65b6baaff717212e34c2", + "0x4575a60403d2e61213a9b0123e5739e2648820961b6c0e0cfdba3c7ebaf3e4c6", + "0xb349f7ace181912df4ca53bd929d8a1bf5ffe3081b41813bbc3bdfd67db593dd", + "0xcd611f40e35c657d4ce71b026ee25fce8142518bfd76e1729a981616e79ff211", + "0xdb8866eadfb2d7171932f3a4cdcea9a386351c15a7f37b0a3023951010aea37e", + "0x1b9c0254f4d2d3bc4629304744999620432c2c51f91c3820160fafdf2b0c5c65", + "0x1382172f9648640b566181e6c1dc58e8ee7819c2eb5abd0a7c1808105173f92a", + "0x60d733a1717293d70f570e015178088adfaa1d35ba4ce0eb7fe3df996fae83a7", + "0x0b8185b1aaf59cae3a345f7333ac58e1ef1f6acc25892a0dd94bf3e0bdc39910", + "0xc6422965ad01fb10e9919ee6474f6e1e789e0e49ae21e41382784bf9c4e25ecf", + "0x62c4ae1c7f7627322605e3b3bfd0989682b443ec0c3b75f95afce9e298102da0", + "0xa3a9f3ff248a4ec678d795749d9ae9e96b03632f0e57ea0afae3f9336d51ce9e", + "0x17d486911421ecd52fdd4092e3a72a36f20858b300137cfd740fd0048bf14685", + "0x8bbfebdce393807b310e1ee99f61cc067ff5b3c96c6695f3de1fa6169af6d371", + "0x3b1903ddb0b926e8f45b63fb3f0377827b72bd11233ed8d55087c97cbee64243", + "0x59ec30f97e26cddcde0a2821f0a503f84fef2d34b05d55c5191ad2f69844188f", + "0x5886161142e20e9752a84b8366c40f1bde43926a863c2c3ba6d1fe9d49cf8879", + "0xb87650a1a773601a9679abc5d6f46cad21aecac495290708c115cfd7032df6f1", + "0xb9181f36d8ef45c3a496fdb4ec157a7c2765c4075528e6b27c72608565f9558c", + "0xfb758a26acce18772174c0f042644f23e21d73aaa0136d64488c4918127f90c4", + "0x53d175ecc0f29fa28f825fc704dc875bb4889620c389a1ad5e1b807135531d93", + "0xadf34056bd29158b4510ef643e0cd1efff5d7b101795db06461addf60627b8ea", + "0xa1cf04a596f8091472a11a7a26ee3cf23d7c5ca61de9d5403e6dfb59282cb964", + "0x9e4ec46730fec459bcaf6acb43edbe9d95205b8bb274e0f01d5d8def8c32ccb8", + "0xcf4dd4b290701fb6cb99f8d405d7509ebde3991687650ea7b4b831f98a964809", + "0x12d2e05cef09189ae225283f694182f67588c060797d63a2c2b616ac2824946d", + "0xb5fcf017c8502e9ed8e5ef8f9cd257398a324342ae8ce35e33896f2e1c842256", + "0xbfc15d88176ee1bf9006e128af715ee6fa54d8e8a25e89749218622ed53ca14f", + "0x322833cc41638082b2c999efaeb0e06829146dc3e00aa08f0b1e4dcb5b834319", + "0xf583fa947b7929b67545376620aa475fe6659d755408a3e559e1689948b3fbeb", + "0x5b21fba1cc6bbffbfebdd78d577bb671d2c517283c965f30f5678f44422765b2", + "0x0e5f0054fb33afe37337ec21cb92ca184a9f788624eade55505778a80ce089b0", + "0xbc875c93cb92a61aaeaaf544109984b283c7019bc39320566813910a1c9b1eb3", + "0xf50b772e891f89a70c6a2b1c8e99005de3d7e58b531d7add69a425da8e6d1935", + "0x00713d413707617257dbcdad87ed02a92e045885326f6b955238b593783747f4", + "0x2046792a70af88e21d435f75f48ba1330924bbbfa349a671e0fd2d0d3b1e8df8", + "0x62de5efc4514406fc910f2382d7f00030b69e5b094271b892238aa66ccf5f658", + "0xf9f729d39f12f0b680b7ff663128e42a3561166dcd5d1136c92f12179faaf464", + "0xe67a953ac35f05b9b641d745717f721e6226d4174fe02c799476510272822a93", + "0xdb57623348427f886afac1c9dcb75d14a1b41c602b6c14eddbc79b650aa8b3e1", + "0x70f51bd2d262bf2f8e469fcb2755eb1d618c5ff732587b358160a95d941842af", + "0x45b9c1a2f2616adcd997ea5166612f302251110bffe652613f18bc341373a643", + "0x845714f10dc89f6e98269bc667e3ca110508596adcfde2c5316264dd22b6ba8f", + "0xadef2f1d6a8d0067b1f7968107a63e7c66886fcfdbd34705520f951b433aab86", + "0x9587194ab4ceeccf75eccf264a981f38f8f3ab551a03247dc4bdb07927602a6b", + "0xa65bb233a7e27dbd691e6e89cdeb798db315c980e5a1386e87ab76733bd6368c", + "0x537bcbf94bbc75cd061eeed11a3a083bb1e0e92d6baa1e0ce7944fdb44e02f47", + "0xd802d4b6f352abe01acd50f7da757b4c69120b73f4402f0c0ff86cd53fc59ff0", + "0x22763b9d48cd66bd1c20cc9e33e21223f094b4f02623e1493c93f3a0862e7d95", + "0xf1bbde585835b4a881938f158fcf331384c5332943f0703a484c4a3b28ac5b94", + "0xfeb8baedb0718ecdfee84d3349e9b7f8365dd938aff690a1c6a2943e4c1acc84", + "0xbf0567e668c8ed0837a47591299fd170821df5bd9842c14402397896c2ff1963", + "0x6ba2813259c8400ad6d32ea4b11fd3cc0aed329754f321f4e176e9025166709c", + "0xce22f2b54643810fd6be08d61d3d70f955ac2fc29f4fbafe12bf918f6b63fd25", + "0xfaa7e6b93fb00d76bba7fe7108516097bd8e8e4b6c815a9c9e1fcdccc7dd3606", + "0x707bafd52a08d76a2ad00e312f42371f88297a7f719f47fd330670e2d53d8b64", + "0x3510cfc92a3cdeab884b4c762957545a6071bfe263d754acdda2608bb69266a1", + "0x13b075671787f7f640dcc90bdfcf2d71d8e7e8770749bcce64f2cb88ae527654", + "0x7fe74409d1d94598cb69fbd7f8ac81b99084b2711e57a01290c32ad371c239b1", + "0x2d5e5646defb53b7de115428ed3e84556848c620ed102b26a0f13cf4c60994e1", + "0xc025bdc15e1fda9ba245e9b71f288a8a84eea64208cc9966f2c3f49686847c55", + "0x2a65ddb0724d6b23b930196d646624bdd9368d1d148590b9b0a407b1f5e57e6d", + "0x3ef810d30b9b0ad7ad7ccddc29f8305ecee5d360b70798f50a426dee244e1b93", + "0xa346f711aec048168dd2bbac567dd0e7fb6a7259d552a91220bb53a28ba1a5a4", + "0x3cd8161d8d67bbcaf5c3e276803a91c938b601046053ef6f70080979a63382e9", + "0xd0284a215d5d9a923b24905aa6ca33fad1f46f4cb55aa5947cea31b1df99b1d8", + "0x829ec002c7793a45420d54fd2310d100cbd0bc3b1b9374bfff16ea3a7414686b", + "0x1834ebd9cdd71cc6dd72c819a6ff8f31723c1d3c97f3afb110aa51fddb7cced6", + "0xa164392aea12a31ef777fbd5955aefa405e3ad94b47494a8dcfcdaecc8a91117", + "0x39a9c21bfd31cb989c82e3f1b2325e8a922e34ee3d791a52c270af7f8a52a1ca", + "0x74cacbda69bb65d696704bfc689ae5259b429f813ab624e90cceb4c7d407104a", + "0x45bfc98ccedd14f3af4c3e636e440a64f7ea4e5618aa627fb2ceb303c50c5065", + "0x90681b11855d1ddd5418be56183420916279ce87b4eaa402a6f38a47b5185ae9", + "0x224ac56d0c36ce8e03d65fdc036b28a3936d885719a575b7d335ae62b217392b", + "0x7cf618d53f55dcfd73270e1d02383239422e7575a7b1915331f0340f179091db", + "0x367441bf6b659dd7387a8cd436a512c499bed75378e531fc7f6146c1ef23cb91", + "0x4168d01a6852f13b5b37fda03c04c0f2449b96142f97fc26a351f27efc0461b2", + "0x5febdc0c26f86d01108ca0c49c8abe6556b3904a5af071d95cd2bac958585fc8", + "0xba49d3066edcf70378389164d902606974e2ff2f1f373a9f64f3d2eb49ca911c", + "0xeb074c7e2bddfe11c495165b24dfbcc37fbc321ce521bf8bfbb8887f56304340", + "0x41cf36dabe9343ac4f88e20367658f7b1b8272e36204b742d865e81d2460e539", + "0xc22aa0427507e35f71ced5139027fd2420ad471c79786d52a8d05bf64daf0315", + "0x43f72e8a2f815b4776c8659b3144feda6a128f1066be4d00eb07f522c6bc48cd", + "0xeebcc6f9c2ce0ac46de64ec97a339d4ec4d79b8c262756b6d42a20cb31d4a71c", + "0x423b763cc51ac721219d3d53d32400a294288051f706a8893dc93dff62849beb", + "0xc59557e9f39080cb8b56fee3308ec5975c78e961b77699e734b2f666d6045f85", + "0xe5a0d2bfa544db59d7a112e8a1d504f1e76c0868749c7ba05e85288d87a80cd7", + "0x9721307ace75ca73fe3120f5d81d4cf4f88df6281bccff33288c61e6f020e5a1", + "0xcb5c27cc9209a89fd08aaa74d8c387e3bbcb5362444b2cf55c094729ffa8beb9", + "0x8ffa7df4893fa361f15d68b4dde1aff65d6b0ad47c617ea71c16177699e87453", + "0xdb54b8a73a8c9a76a33c534e89adce277a717e896bdc238ec5ddb9bc6ae87049", + "0x58ad5cb72f8853d793122cfdda46d8cfa801c23952c5892b923eb9f754c3f6ce", + "0x9f1e95428049c6dfe0a2abe59a4d5a3d3d1a7fdbe491df1d8b73c633a0c3a703", + "0x696893aba5944167a759b229d38f4199c6291b63e4eb22dfaf7959d4d691f0ce", + "0xd2b935e334facef3a12b8345c3786db1601e693ae14ba685b1e77d6c24b9dd8f", + "0x57641f8a26dace75ffab6ce38231d0e606d8b848c470a8388115ff11477b837b", + "0x5c2d3b7736ac9fc9913d0af617d5657a905a873a275f6048573dc84d59c17584", + "0x7a1049d0718effa5a440be8f92ec896f788f143b96d8ffc7af035a349cef1ac9", + "0x76f1df9598d829ed9fac1498fe98aeafa3b0ba2f7d46baf3bdc21cc9120ff9d3", + "0xee2cb4f18b52e5191e4baafff839e7703d194fec8d71667da29052b29a98df03", + "0xf7d1bbfd6cab2a42cf564f389b231b34c9b317c91cf59243e94b93e8ec4c69e2", + "0xcb88c2f1bee341331c1b646d4d7f2f0e430edff7c17c256db935fd238e973182", + "0x526245e9309caae6d4adf9670677a5c0b61ba156642313fed9b6d7ae94fca23c", + "0xa805fc51f30b5ee4dbee42e919aca1a65e0cf91c520409c0f8626a017f943196", + "0x802965e763b3c6bab630317ee3e8b73217f4d20ecc3876936a226a791830cf12", + "0x4e48ad3536d7df7a972055924121db475d2bfaa6ae95495fe77186f1b4f2fa24", + "0x8992d2d67ae9d0290bbf5bbe46159e1e2a7be64ffa7a1818b6f82580a9cd20d2", + "0x63bf8968e3f064f3830db1e230c60f91692e5f2d42fb1af58f79996e517bf6c7", + "0x947db0ed13c35d1ec11efd312a7d82819052a723eca374d8dac7b1dc0ff6305b", + "0x93bd14c3b7a02655448d0704c0edd2f920e6e27cb09f13da53d005bc865b5a0c", + "0x46d43c93d7c3a98957ef39521f33f93e17a9916cc1b56b98f3844af77f5fc412", + "0xdf3f8fe8ff710225bf59352614e1e634ace3d4fe49cfe4591a00d11d733e5960", + "0xc2e4bbffa5946fe339a8799cab7dd3219595ed0e9973e1dc2931b3859173484c", + "0x97cc04785a86dc80efbcd4b9781187ae6715d6fe149fddfabf14febe1a322677", + "0x744fbb7bc934a190520b80d89384dc414ae4e8150cb71d38e62ef1d9a18b366f", + "0x344d872db74c1bc271c21f5b22d5d7a7babd4d07ac2c5f2b137883a0306bc251", + "0x85ed396f66e8f0ea0eab73ca1363de2badc4c1f2bbd5bbbe169309f5dc63d0da", + "0x84d0d72b6b12ad40e7ec54da10620e6593324cb9df2e35d50cb7cc13761615f3", + "0xe0a3bd63ff7139c3518245999e12581decfa8759ff0b32c5c059373bc8db1e7f", + "0x0b44ee1b898ff7f5cbc4ce31ce2f936a5641bc490493b5064279bb455aa326e0", + "0xde9c7563ad2e1eebf6b1acdd24b44a199efede7a5ec8cc22ff3425d0ac2ec0f7", + "0x53aaada9fa553159ab91a957dc2b67938d402440c0d99220ee573a1f8ff14d07", + "0x291dc732dd59da461f23597915a6d603faec4d446dd3fdbde69496da13852fb2", + "0xd75a663669c78d755a37737dbf8e6c346bfbf4d88ea7ef76579d29262ccce1cb", + "0x77e1a24053a1ac2fd856ca31fff2b118fbbb65042d5a84cf658ff3a39386e051", + "0x97ab84c92e98b46519bd8457b83949212bb28430517402a81d6a6dbe3f48163f", + "0xc0b5361e52ab290d1e83f0115733b4441944c5bfb008f8db2ca7c522c977539b", + "0x01ceb7f526419e76e4e5e3ae2d5fd6c94d68e4ac450bd408cd12bb3f1d0b56aa", + "0x4582ce9328c9e92f375e817d97e657ad29953df6bf09b11511e7a67b0511fd20", + "0x4c551724e6db35a6d6f699978a7836150999fa47a78147190f69bc18eaee7f2d", + "0xa250261e3724451591670e7167084e17bc493932cb6210ed410cc0f04082a13a", + "0xd335e76e95603e38eda5d0f14d848c3deac6a9d65a7a560c3713184ec6fb9190", + "0xb576c2e9e81c8a5c70473b51cd9e0864e474a7b33eb1e9b664ea47ada98215c6", + "0x58375b53923449b31b57e47b04eb6b3727ff9f5221ca368c229f560928049d92", + "0x38ccde5d7ef10f9aa524a46d4b733750c139cab85b7d669b6916a5e1625a4bec", + "0x7434a7ae0243f47eb06d86b593bc540f18c9073509478b8697901c32881be74e", + "0xb253adf44750d5f504f7697e406ecbfe6169ff469e37d71b5aa955672a967450", + "0x8fd44c435fafc953708ad0fcaec7249cf2e6084fd9cdbff8dc17454ef2e690bf", + "0x876fffe5053f45ebd2d6360778b04de26e29be56e0d2b40e1be5221d47a3d7e0", + "0xc40e51703b184f9796c176f67fefd52e12556c22a3f9ff8ae8846b201f369812", + "0x13e964e0d0f9f351f7bdbe30a5f725752f3e98b301c884499a240ebb7258c907", + "0x34de7aac0667026e8cef54eed38989d5bd998dbc2c167fe8f99dc8d61838833a", + "0x6272cce984fc826ceae3e3ce181aac5bda115be12340dc383ffa1d0a26f26e87", + "0x89fd3e76195c9aea0898cba76bcaa3732806f953f6967697d3ba69b3361d96b4", + "0x9350b1323eaffcc8f21bc790e9e40a1d21498ac70729f6ab4819dd0b8ffc4aab", + "0x08d16fad4178676992784a8d9e8b7287952e9ebb0bbd792d49086c59acb49820", + "0x342304ec2817ec81b79c4795d4fee3b72be76e3e55ee5b1715dead3829e2f1ea", + "0xcf8691bcf550aea72a3376b918ccd7d3c740ac597499a198329f9e5b73642a9b", + "0xf181d5657fc8c0ecefa57c179e3f5924a77dfe104b5b258006e1d1bbf6058507", + "0x47e6cfce8261f072baaf22be30995a41fa869e171a8226dd73dd2ed685b90490", + "0xd6ac79b9a2c60bd3d5ecaf2156efbc9908010ef9de9a7714df36a49a4643e18e", + "0xb1b51f74801e41590943d1d5daffa32fa58886c4bdf17e094b3ebd21c8e3f5c4", + "0x53d8e4297000f287ed067e1a9354b57fc6d264a122ca965ec4d0818a0c039cf8", + "0x10e9210c35a078ff40e5194a0d6da507711fa68d6716c54a48cae84e91d5f849", + "0x68d4c6e30a85d97fdcd4d958bfe02ed31ce546f5cc0d7671ac3c716dfdd95c82", + "0x02ee777d273b0b32e3af34cff268a5936d6306992acaec9e754f0b37eb8a8f9c", + "0xa470a76f1bf46645832b92e3667fdfc92a9a4d1710d1cbb4ae828f5c74459184", + "0x4ffbed6b6df0f68872dd09c8ac52f5a0fac0eb93ce3f817978793c0eb40a1551", + "0xcf3b51f75ce832ab6971256dd532d319f4bc0feaeace79fee317e5c00d3b4628", + "0x42a2bf79a226b8b79ace2c61795e365185f85d7c4e4f96cca21599de5d1b7c9b", + "0x2004f29563bdb23d9e3a3da8702db3a42537ae18d916f1131e668b5c345e4998", + "0x4356d58338c4ec0f1be5c1118d5bc814e0311321bd3ce26a94161aecd157bd86", + "0x3cae09bcb0cf17fe362fe7bf5031091d67c17b0c2d3ef1c04ef25eac5a438e08", + "0x6a94d740f8687d5447d4304056e70bed1762ccf711574411f92eaa03b9046674", + "0x121c5257d4e0ac96147bb20305373d2ad1e75c77f685fca07cc467f0fabbfe86", + "0x459b6695d373c4434b2ed96feeccca3c414340627459d7674db3db4566d02e3d", + "0x5ca3667aa707afed3ee2f562e90f84896c443e1b13faa81772378aec9a4a5ebd", + "0xeb5c72ea3642dcd4a23cd8ac1f164c8b5934e4de632d32b248bf62b8aa854a69", + "0x63a3c3c29f5505fe413dd2730f669321debde513b62f9375179dedf3a58d895b", + "0x677149d1dc41656168353976346f3c5e59fd7bab1045891c56c406aff4a5dca8", + "0xc2089ad27692accb611d13ea94884f20ec0d42f8ec7648b000a5a7459be44eab", + "0x33dec3727bd2933279b65b4375c03fb6739d1030415575b9a75a024cb2eca99f", + "0x6ab646c55bec7718b1191ccdfd1d7c6837c68263681b05332ab772deff46acff", + "0x3a1c1e4ef4d20c7b5ec905a985ca4defa793a5d9a6c5bcc8aaec2ef130c8ccaf", + "0xdd647a68bc4fe029a5b7014cb4ea5e56f50a47436e2dae87ac82b1d2cb26b8e4", + "0xcf82706f177f774f95bb719114a5dac1535823cf9c68f0dc4317f80a1f9b877a", + "0xfd4fbba4b0d20a54a2544e3cf8e2cb6e0ec69f079972c5a73cc2d073b33d1e45", + "0x0d684343cf711e7c90213fbec37c248971cabfc811946c5061ab119271b4cd8b", + "0xa0d98f95e42007efe6d146df0abb63cd4e8667ffd846ca1efb7eba4481ef500d", + "0xf85df1c37420d7b17a420f8fd8667c7f3b76157d488822d0fa5e53cfb1640a76", + "0x5324f5d0b4c6a8ea95fb43fca267aadddf8457371e6a2e2ceafe01a87e118dc1", + "0xc4fa52181640a22cb42175ef76ada99eba97bd549b910d606f0f2fc295396cef", + "0x9a79bcb1873c4355f433f23f3bbae26c286eb4d98019fc29f9ac736d2663b43f", + "0x5178ba05829b1181f0ed612b947ee9eee1ea740011b088b4f128994e680458a4", + "0xfadb85cbedab827d671c0c492a4a24137c4ab98e4477a18ca7935bd523dabfcd", + "0x0d7938bc9293a720aaddef50964795f0eb82c94918f7e5b84101fd465e172cbc", + "0x70523f17233bd32a37a8a0ebdf4c308cab4e2f1647e001f2c06ab2da149c9940", + "0xdbb4bb48570f6f70e4b08935a847ac85831b1d95aedfec5a65c7b7563e7b10de", + "0x767f03ea0280e13455f50347d58b100c6e6a3c9a06a4d9c5300950aa5ff0c855", + "0x50b83c5610cacd19c65e5f334bc399c509ff69d04c33f10b8321e4d1db69274c", + "0xf3d1693e62753998beb772041afeccce73f03cb0d0fef33e4e4a9e758fe8db21", + "0x7c1893385819942575d5fcab4ac11c54126c96ee1332c59620aa4db22dcb1a08", + "0xf2a4d04342dcca59c16db4c14af346627422df15636c273fdebb95baccb3e6d8", + "0x214dab0a8c2506c577a0441814a945d8d3a72c15be5896819ea61de2514b76aa", + "0x5a042d78dfb5badf7dfa20d3fe34431f47aa2530dfe88e8cc8273ce4b3b54b36", + "0xf9c46aa328e05a6338e6ff018c700dc154df94539bf1a82ffd0c40075bbd6e4c", + "0x7f0b6ad8aee3bd47f27afee990340f9494973f7b8b79fe040eb89f3730cf18f1", + "0x77355e1cd431dce18928e067c6dd46532303b85fef8744bf9b33b7ea5ae0cb3f", + "0x8dec625f9aabbc884550642c3ded9cef22e53426924c4b03743eaa451c79dd06", + "0x72a1094fd1ec3c2d66aba2cf3a059d64bee5dd6a650c5c88991ad55a7039b666", + "0x460e77238bf21177e6e7ed1b07e13fffae571ca42fc9d7bba67d6dd492093566", + "0xd9046c225272bd9eaf0edf5294b4b20842c0cf31f1f9edd422db34d2633873a9", + "0x64476d496d3badcc61e3cf8b02c2978b38e69b30fe755b59531635b30905ddbc", + "0xc6a04fa2d028efa1816c7a8f35d12f3790907117b0f7e7deffd04142ccca4a8f", + "0x0982fddd27ac916abb684ea93345926178df408393b35c1e3470c34bfe5d9b2b", + "0x26ea855339cba62a7e9b40225d4ab67bdf3430c28c0cacbfb8883af2230aac95", + "0x312025893f2d07dd59f6c813301fa8cb3b2cc8695c5e2b3d6211df762d2de516", + "0x71f18e4fbff199cf1bfb3b5cf993f4d8576338891af8d7a13a6acd6c58521e78", + "0x9f86808c0d77f22013fc8af148901cf6b83fb672942d58a908e1214c402eada6", + "0x8dbd247e5593ae21ed3b7be57d38604a91d55e3f1947ec72b802d79d5e97dbdc", + "0xe1f12adb449773b18acd7b0c1d4324733bd929ddb647754bc62c488b38fd380d", + "0x45e19bf4d7518696ead1421951603389e20357a45f40df3b76a7e67520f922dc", + "0x77c819320ff0e1e9f56e7461d32486a4f87b398c55a53ba72b514d70318e9b11", + "0xcb377305801c811e27eadc90038fdab06d3026f1256aeac779bdffcbe9502014", + "0xe963f24b04c150c84fc4e45069240e7db52e4a40bb1c858b994b8ba8e9525b3c", + "0x88a3400dd20c41d1a9eaea3f97b325b1c730141355e980dfacc5d58725765ae2", + "0xad099fd7e3291a63f485a14d9f62d065bd5d32a39afac2498e80bac6a60cab6e", + "0x7b9529a2bce7b65c83fc2bebbc6c2946375c6186590680d209441beb6f5508b2", + "0x7a19e8d878dcdddfcb86aed07440b7fa5c3f753d8a17a2ec9c2a6a5cd1b977bc", + "0xb799ba05099b593531471efbfd6781f25690f54ededb091f54ff9f7ecc0a299a", + "0xe339cbacfd17aead7f7474e7f01c447752daa9351533f233740411a3504aa5c9", + "0xe58ae0ceae908cc5d62ab0550eb6ad6cea0b54639f09fd0985ba49a9f68a88a8", + "0x07add224b825f03b9fb0c63f9fbe991fe01ce6460370b0ff216c501d01bcdf7f", + "0x000a2e90d689263ccd21ec40f2fc2608bb0b0589f7d3f28802eba638723612c7", + "0x5bc126aeb9da92411dd1adf0b77c4d7f5f36f2002ef1f385d622ae10a2215255", + "0x8cf4b232058d153dae15877c3b315a92b066d802a5ef2b34faaaf594bd8756da", + "0xd2810a332e85f560076ff5283e6df10a701c04606c5f9b2ce33cf0721251c9df", + "0x458331a1fd83f5a899321796642dc6fe806e8540fe5609b9a9fc0c63e4da82a5", + "0x1ef151b554eced4bff8ac3d10e673958b71935db041d67f0c71b1fdd47c019f9", + "0x33548fd48071cf0f109d4317767493b01be76a150a0b9df320218c2127c21a34", + "0xe0bcd4cd431e52fc3f7daef86bebdc0db5cb8d54ee7a4d8054493ce6d648a0e3", + "0x83acda59e15175aa0470f1bbcc0452a20bf83a59fd636a41299965e4082a814d", + "0xec4b9baed78443448d1aaa80f830873c704c47222e4882add63c766439925cdc", + "0x62aa755c119df6f4eb5005bafff77fa1778c410c753e0700335dd3a54b7cf699", + "0x73607ea2e748ac306e4ebb0c3961701591f3e31419a36ba1501d58d1058ac0f5", + "0x53b8e20189d5ceb4348e2ac7885f9199276e7e7afa8ce2a134a83d6744bf5a1f", + "0xa04d46ffb7257a822848914503f87b02469f9f1e9059718ff7bfa00f3a1bd73f", + "0xf6e93a6216f1c145fd513665e50663cb10e7e70ba449ccccd95bd5e737c03805", + "0x80672869f8f4a251c591c0f89cc2ef4e679ab74da5b5d395d3deb71e4cc042ce", + "0xe5275ed7746fcf92bdc69afe83cb946ffd47332d46a900c37ddf4f69d9fd1f92", + "0xb2ba12877a701e86f3ae9e5ff572c317fff1181540cc8b34e60c5e6ae18d8439", + "0x741b59b531762b5afc36936507182ffcb9f6a9b1e73151aee7e346fe9988bfaf", + "0x1ecd05aca69a9fef013f0f996e69020b8b7b16c19c2a3f02be91e78d20d73786", + "0xff493ffe120de6e5147fc2a69a34a6ed9a80717f293656d3f27a329f98ce215c", + "0xd7bcc3fc371c4808dae030f054df8698f310046243b8458921b0c0cfc615ddf8", + "0xf7bfc951b580ecb0da60ec62d8a7bb5180cb04e1a01f459d9ef069a31f2ef6cc", + "0xef4c3a7f5b13156c288901cbf7802678f9e4e64f224176fad3e0e842c735a030", + "0xf83901cbbed7aff0b3e98904e9df1be2bb5bb0acff5d5f17f371589ce4f01300", + "0x089611177848276d8a9d14c4e55d854a0bc63db759a74aa0001b34604e5a4c7f", + "0x04b3803cc8b779e896827679d0630c80a5796686a7db3fbaf0eeeb2b04cddead", + "0x57975f7a805769fc6ab86cb1e8c87e4cc66ae04f0e31c6d9f656784119623443", + "0x50ef21c5ffc953fbffa75dcf80542932451e892f4c8c33eaf5bb00d8b0c1ffe1", + "0x9b33644b14269ac0984c4f61bc2ab46e2732b669945048147059b5b0e6d129cc", + "0x4567602a7952c1c20c1ca2b8bd412a3287de0e647f7a40152ccd94cce7fcb88e", + "0xa5069dae511a6c4d22a4bf8ad691da39fdf58593c7358ca720cf7d1adf3ae3c1", + "0x22abb862f2920d8d91bd7a9799adf77a248c790da9655b83274b03ab238eee5e", + "0xda16334c20fd9d579f2e901094eb02e7d2e532cc2c6e72fc1c2ea290a72778ba", + "0x5ebd45ca831b05f9a09f467d771af473613965505000407e25f6f4be8e91ffda", + "0x8faa53912305456c4b6ce598aa9c40671594b05f3496594583f4a4a0a93f813d", + "0xccbdc379f3dae9c110bd09abdb4cad4e9040417c7c5889a72313c8649b563eaa", + "0x57c0005867982dff0aa162fe11f561588b695caae2e86689f338852f9ac36dc1", + "0xb4f3fa31d2da427987db398de95d6f71485bb3d1eca58ebd274263b33a5e124b", + "0x1638ba96a8df9ae55d3d000fef5f71a5b0480c981a385ab1e58d54df299892f4", + "0x811986da54df539c485c01c7513b296b5e4118da3c2fbc6a2e652727da6e96b4", + "0x38a81d6d1de444f49915174a3c0a416f2c2f0e2ef247c8687836532ee1748249", + "0x88ff6647f68f36ad282e62b5c5f5075b78db1c7ed7f0969e01a06f4c30afb174", + "0x9f65ab9993ba982ab18e81937ac52978ca9ebd528c0fe5cf093f2d2fa6731145", + "0x4451ac80b3be6bf30b619e14d4671c07833fe8a05e0d5484e826d8261bc1461d", + "0x5914dce27963de3b0124aad4d090431558d0c1f570fbed81647a13c5c69b5210", + "0x946fca386008ab9575b5e2c3995995900eb0f88d49aec5bb8e3032894b3f0015", + "0x572123c118cc1a0d73fe12c73c6ec192ce1feb40eee2b63bd94e1d9e7a418e23", + "0x400f4a82c83637e2c71b79e9c4a8b2b32c514c3322aee624663bc429dd41f931", + "0x6d2a92efc8bc60ed52df568c15db8a688442acc2d87706b427fae31ac57a9836", + "0x29d6225dabc730bac9e9ca573c1f5b2af7af7c5392fdbeb032ac4f914fad1455", + "0x4956cdc73d845ccb27713d77b63c808203537659b112d6c6d29fd6dbabfa348e", + "0xf172da7036581cb5b02573ddfdcd8ecee495cededca18ab81d80f79ded14f4ee", + "0x3ed643c60e5705350494b860539e127c11d78f41f798b0e6f2870eea92de2310", + "0xe67ced4f31bf338dc64fe1a4697ae86e50af44d029b1b29bb011669dde4b6f3c", + "0x8a19faca24d477073986ae2388d9b783d8bb68229f8a41ba4b2d7056cf1dbc51", + "0x35573139540a7d60bf3c94fbade9bd5d396b0a514ac043efd8203578dbc766f7", + "0xd32ea6dd25b11a0fd0254723d0c0297af68423c34d91fe46b24cf30b3c609c4e", + "0x614d13b3576a23ef04b8fd1b343423ccb5e75fa67c0abd1e1ac9557ea0bdf3f3", + "0xce65d856acf3745b8ef557cb59ee4126b1d1ae8c57671f38f46558eb9268c064", + "0x9fca74984410119479093c26babdeb5d5f4559f50770eedb1faa7faf4f6ea6f0", + "0x0623cb07599346a3e34e0aed8c53b155c5c074a6bf8234b0175c9f3b69cf6cbe", + "0x3ede26229fe3a09dc4ab55c2c200de3f5692443f87fbc4acde1f66de08b4ab7e", + "0xb794cb3bc805892c062e151d7752178cd5779dd78946bed782d962c41b27c2a1", + "0x5f12f7c2322a36fcc0f65080f6b48d23c1e5ff8d04513bfd91360850677f6982", + "0x4a724b68217d6920a2c484eaf3d5502f43c5fa151af9018f16d4c9e1250ae950", + "0xf9252c210531c9a0019d95a0a685ced29b1d36487f78fb85859cb2aa27255c80", + "0x7c232939fa537483f4cef282a906a38103b31f28647915cafe11eee2a43b8479", + "0x6b6fe8190580eb0255e4ee62d0f6839e82175b107b092d59f9620b969edb3ebd", + "0x17089e56b7804855ffdcaebb8e4473d59ab1b7d8a7c9d47769373ae2dcb41216", + "0xcce3de9e5a9a92473a2f90898f6d2ae78de2ed78a9d68c3be46dc499cedc9018", + "0xdf60e7de16157cb3f69c55a9bfb3d04af18b0835e3e800bc032c765e0f53734d", + "0x7db47d1e91220227f6a445d3cf0a6ae8c975c79780f3789971a54fb594dafbc4", + "0x9dd6c6ad9e6170eaa354180ed491cab6e0771b6d51872d45bdfbba36753ddb83", + "0x21b428bc55a5cbd1ea5950025616c4b5318f23ee32655663f8e97c07f2f768b3", + "0xb6e6e2f3f4a3727653e033bb91a3ee8a926ee5b2289002cfdf0a667216308433", + "0x56755bff10831d6a6e2254c1548d4b265e909c87ecd799f23c869ee45acfab56", + "0xdc1a83572799703f88d6c547cb566dd86f92c6c3af76720ef00800b67f58146f", + "0xffe4db48cd4788ef14538023a385e3091451cb294f98a033b0dca638c5664e7c", + "0xd20f8295385bd8f41d5f51d5653eb98dc3feb4a33b702a28b11f4ec31f250fd2", + "0x8e76b58acfc9aabf1f3fd7a78965798c13d7f8d04259dba5ea44d34fd30e9ca1", + "0xdc6d58e453d450f135250429b1230a9ef1ac91174da7616b972715fdbc7594b9", + "0x548f67bc4c6c79b3ea1ec147da40883005a8123daf63de7e9621a9fc670bd8a5", + "0x9f05da4a5bd3acfeb80a364d495bd75e492a88ca4690012fafd33a15bf491326", + "0x0f3e8b6eb6729701a99de5732d490fca107070013007be60e58ccf1f905e02df", + "0xd7770978546ca6ad67c4e9cb57ae8228c36ec5053c47ea79ab0508fd99f6d7c1", + "0x4cf30346833a8d19cc60b9eba732af0f2e28cfba9a9efbd30b309b7248cf5349", + "0xce0badab903502a1195500cbdf032a6e366f2afede90640b96a5e8c15fc03e2d", + "0x9e5dcfd3fe399a2c7f082e9e998c335cfe1203acf410f16c04db9be97d51ae01", + "0xc5fc97ab39a578a194c744e49a5ce9aa87df3955192babbec99473756a0c7114", + "0x08ab74cf0b32c5c43213a621dec1725f7150785295be91a114e7a24e1d0cb991", + "0x84c34310eb2de712e3b0bce9acccef272d5ad82bfce0ab211a3964a951ffaef0", + "0xcfd39592e20c61ae0e9b940c1145100e92d0b06c4f8f92e40c1a17fa4bf5b3fb", + "0xedf7f9fa87d9896ac83c5ad1ab2cbf4a7cb658d56697e05779e0b93caf987879", + "0x9191a42d75b56e90aff22c08f1af311871451f73a5a04e48bd5c132136c2d613", + "0x7c94fefae30eb9c1c85fa709e96dc2fdbe25be9fb7509b528b6fc6b3f4775696", + "0x256773ee6f50689e679949492cc7d49a348853398d1b2a7f5c60acefe595e619", + "0xff42d5ae630ebd3bf539d5315096548da112b4104885c7ee3c34cd0111661c8f", + "0x6d0259502f86ecc4834f4b20fe1df111e9a068017ca7115ba1a16396e47d1352", + "0xca57038b41736c66dcdac25c37d8bfed2db05cc84dc3d74b4edd09c93bf19167", + "0xed0c9c21710b39d291988194dd55a3526b59e074fa0620f09e8e566df957d998", + "0x35f8fde25ca651d3ba083528bf3e02fbc65a33bf79e1ba275fccfa146f01977a", + "0xe852610f2d4e63eac587756b5b28d0534350d16093a5a1e12bccf3c738b3fd18", + "0x52103a1f07514cf2cf4e08cc70eea869d96fa9da08115ccc301debc0a88f0041", + "0x483dc5eab33952a0329f499467f9834db3ccf067a5070430ec09e0f95ccd9318", + "0x1f38300c216573306ec66f0e147e062293c74fb64b8d62e904aca45cbb8c48ad", + "0x6a3d5353daf62a1f9238c343415c5e303673c32500279ff59d294186c153c5d8", + "0xd3bde163f7863abf3c5d5fb9d6c733a23e8553da7cc2f311f8fa5607d1e4fa28", + "0x42cdd3469d2d483db1f0f6f25ba84a5e0bdab69ac5c8ac5e90e00a4f84c4937e", + "0xbe28cc7e0df66d1406a046e3c51d7f293256036eb481fb9086e737b3e18b2598", + "0x60ad4e61521b2860f98208a67fc4892774ca4731c36cace957e42f117c74b42a", + "0x9b26fb73d215f8220896ae22f9de141d88e55beaae8c85ecea6ef7b2d416f637", + "0x866890f5d3f7cad25c3891675e0afd8390f8f06563f53251c4e007db8adc5ead", + "0xf4c44e0891cc61d8276c3ca73200b5cb687ea06933f8c83d225497e1ea66ac89", + "0x50ed2befce8599772d6b44408809eb80d2f220657b0a237ef3a5e675da936f0a", + "0xde3ff1bde0593139d88dc1f8023d4fd48c012e1e3795617d9b155deb9192ffa5", + "0x9710d28a5b28a48c19bb6b34d9270eede5e47c8ae0f45e75818cde97a66151dc", + "0x4e21862412b47b01dd91c3895c4f8fea480ef3680de39be9bbf712aba17ecf5f", + "0xac405c2eb8207c9b573ed18a491679d4322dc66e162a44986fb521937754afef", + "0x76e4ed769b889c9d446f46acf31d15124b56109385aba5d5f5300e359ef4c83e", + "0x34d8eadf74fbe15a6fbb2017df121aabc2ababa538a93ea306ab885e04c2f7ba", + "0x921c70cf5dccc382b6b87eaed43b9c53f82fd6925ed07b239b4178637dbd421c", + "0x5c950a5f3140358dc121a714244376a57c37af112355223b590a4ecbd727615c", + "0x21e603ea50aecd9e933b840f642828968156ad74eed413f511f7cfea8ff9e7ae", + "0x3b07e790c96e6cf392cb00fec65ef54d886f297540012d8e44a94468d9f6eb9b", + "0xa414c92cb6061733b7366c173c55c0c698f96d81b049c33a19ec79247a644080", + "0xa09a1261ab8858f3b228cc64dabdf4f39648310ce961c5c3a6083c4066b4fd4c", + "0x62ff4799fae79c5f884f1811b5c5b9daaad2817511f809fae5ddd2555581f6a9", + "0xc1f9dcac35e45d816033c8aaba6a474eba6cb4b9149f92d4937045f8969817fb", + "0x18174fc9b586ce700bd19cf9e035c6a7ba940a217cecfe8c8dcaf434501948b4", + "0x61a3bcaf875998380a96de65f03dc3484c08b4636e0d8933d8976c2aff89b8c6", + "0x0d9652826409a8e1436a6c885e24385232718e3a2e539612070961fd65130ff2", + "0xf31dcef261b13ac99fdc31bbd42ecef2fecf70929cbd1596b64a6812e8bd4d0f", + "0x4b5e16ab129a0b890d33dbac5629c907ad11cdedae5d8b6e279fea3e96e629cb", + "0x46c0e6ab302bc4cb78562129353893517bf3ae98296848467590a55dd6c34477", + "0xfc8d1cb1b86c87c8d0f5e616c2dd7a1ac11a45a46edec67696296c65985066ac", + "0xcdd9809bda78a5e4184203d756db67614063f8c0266c0062676167fbcbbb7394", + "0xbd15ddb0cb0fdaad3529d114c5ac833868d85d2b0e8f2d945e37869f6739e10b", + "0xb92d847db1bb8c87d6eb7018e1e6eef8e1a33e1d080d1ddba17a9b49b46596a5", + "0x7a25b19fe740057667cf821ac4604c03276a7648b9476fe7836efe5b3de64920", + "0xe388a01399c3a6b50dd227c7451deb478570fa7785666e86f97290a8141b3a07", + "0x28f1fe2c524d1f49e4b34839f599c0faad8c3dfc851834b4525e386ee901c528", + "0x46efd7bb2f18a804a821e30ec019b307659cb7315d06db31eacb44335b4dc083", + "0x2857bb4e75fc8cb8168b75eb65679a1a2f0f8debee534588d92f5103bda64319", + "0x6d06b680460a29d9a85097854dd230e9f505236cc507482fcbabf372709cd6bd", + "0x6e8e9c5657f7e3683ec38910ce0a3025cebb9ac591373d88de8f1d7af854f3ad", + "0xdec4b1cb35187a34168f452c120552ddf3b8841bb57b461315b5961abce01ba8", + "0xa9867b4c14296efaa6650e4693fa9670915a8710d54154efef403c274f9e5817", + "0x78a61ba3c868db99fa23f262e33c2e0801c9a37dc83c423c41597132a9ae4919", + "0x391d561534a9455cc4344d0a37ab3693a8eb81b3cc6826af7cadb72ce34fc7f5", + "0xfc4a66ebde64a893ba24389962d09dceb27af84bfe5a9634aa9686da55bb337b", + "0x133c4a1af9e3521fc8a8715244c674be637b61b901ce6f24290488d4b3a6da27", + "0xfea8760cddc55cf479dbf414bbec7252b1fb4a1a9aeef949e2cb0976c5977573", + "0x477e5cfdce57fc88b4a318aa5272b849f0205b5c49c18945807a76760e69852f", + "0xe940a11d11a9a59d967d336d5357584b8ed9fb5be281cf401025547140ce6089", + "0x83982cf49e2373968a7b855be6854ed0c946ea7225c082e9ed89bd5ecee1663e", + "0xe2cc6a4bab2958e2126a294502687ee0e9c28f8f718c085f87dfd5756d5fa4a9", + "0x5e927c5ceb3db565067932534bfd440ad837fa4b68a9f7e3497f37619432835e", + "0x4a8a2b1dfa8a1fc62e9bb7b25cfc4ffcc2a48cb88333f43356d4be832613effe", + "0x44dfc09ba39f092edf3260d8af05fd6dc3e6f2f4d121e33a8003877f1dde2573", + "0x8e46773badfd6d666ddd6c81fc111336eeff1e98da480870f0f01a71da06ae5f", + "0x5b260a3cbcd76ea5060c886a273743fdd0312bbf00a7134d1c118c96d6e8257f", + "0xa382d8822e3714b6d95b54792215e6ed6c05945492329ec16dd221fce79aee98", + "0xff25777fbc55f7f70762a759d8546978d0a1f05d96383dce91341209d74b7f92", + "0xe60cf3c0b1f67c68f838a66b85e9863d5ca4760192c4e39da8782572e7b5c75d", + "0xed984db1ffa677751595c129a7af5eafe1c885c31f591f8d090e6086f5dfd9f0", + "0xa30239c03dd4f414fea283c542a8e154805c4b3d9a4c2293defb2412e82d76dd", + "0xd51adcd7eca67311d0dc99b39b8f9764fb840837e94da7d13949edab15bd4f2f", + "0xd12683bc0ef63d23454de1d6ac93ec0a8f3223da1d719bc1bf68c8a76ebb609d", + "0x4a2ce715136df993540e69035686efb952122edaeb6c46ce6270c17450fbc9e9", + "0x690f84732302fc0fc33ee1f9f36dc581656975321bf8cf156ff855509d175e43", + "0x23f3217f5568fb8ca10fdaa7f742d50670072c67671b25b05fd91dfbe76cc923", + "0x4ee12c6ad2c926e986d84c733d9ba9590b4d60b934ded7c62ecadd1e331b1dc5", + "0xb4c12f10feca6b38059c911171ccf697738e6818b3441934b7a678bc384615d8", + "0x76155e6f1eb92763b629c21bc84fc82e84d4ab047963a8996614d4412cde0459", + "0x65eba8e37ec18a8e5459450b27d44aec00444d222c5e7ea646899d19474181d2", + "0x12a6fe34367f47557d269f53ad2e33af43a17c6e2293edac3ac063120600a7e8", + "0x4cefbd99e379d44bee24495a8c57f0ba15984e678b5b641fce92c9f4d164f6eb", + "0x334ac919cc955f751b7d4687f2a66e7fe6b0a5bac8e13eeeb0ebb862083e9ecb", + "0x049de36fd5338a4395f448646c3b66947a7672ff329f4241902a0ff9bcb54c3d", + "0x5bfc73d27c4f6eaff431ae7eebd8196d63b0e200e92f0ee55e3d8d22cfe7476b", + "0x9dc96b680024d5031a917f4f0e471abe0a7736f4038dbdaced69ba2cb37d6b46", + "0x5cb2f291b8c3d8272d0d90b1bcae860fac78ddeca77b932d03dde91f69350f0c", + "0x6c7f681d88deffef83c3ed92e6249d71c7ed81a600ffe813f0408c23ae3d6829", + "0xb613d568709e38ab85c23cc07db8c5a6c1c9eb96ef86cf9c9347c49814257001", + "0x4c63f51e2276635f3ab0a24ba174fd5530911ede5ee35ac817ed52725eaf9225", + "0xcfc4cfaa0491c1c29aacffe17f45720b5e73baec7ad3718942c00db86b335d8d", + "0x1269d8063437fc2d270a0aae0e239c18e80b8431e10dd9a8204633026dcacc43", + "0x0cba319cce8ba1e32ddf02ad3c40db27edcfc44eba925fc860702ff1bb0f9cd0", + "0x97b933323df0f872b836c546e68ef9ed77a40a3ea6052c7e1c044610fcd63039", + "0x3399958dc8a5050e4b5fec917c3a369eae07ab7f3e3e8562b1f9e60f89294c10", + "0x8f41a9af145be2877e8b4db9197b22a975e4a60303fac5623dd8e44b93ba2fab", + "0x48dc5adf6f1a057715a125f5e1e8f1da25290ecf30346604502ced90b210cdcb", + "0x24ddf56267945f23a74e6abd2adc4555bf892b98a28eb30b9ea12b2c598eea6b", + "0xa8dd30c8b8ef266eaefdb79fa743cafc9e459a67f938ef3822b27c3a2bfddbb5", + "0xb287c752ab14c3c74cdc432d072a66c21365bd58f5a7ab4994676137b17f68d9", + "0x4527b990de4c727da84ad06c905a8baf3918a32183919dfc3b0c6ff48dd2b812", + "0xa42aff55bb8d3bc98c03e3b74f3ff596c862fba5675ca6b359878526d7060ca2", + "0xfb6c69bc6a313cca449a85f74a51c644407cff15cd3ed9232c2535663b5a01d0", + "0x8d3a22b91b228480f502b97494bea0d5236b653b429530fc3493458af64debd4", + "0x7cbbada6e5c49a8b92efa595419bd36c1085809048af864d30767d23cc527a82", + "0xa6a52b0368d406639f13316f7ae3090c7c07c5a908e365faef2e95ea75f754da", + "0xfbb6723a9a39564b36abed86f4281262c085f433045ee8ff62007e60078787f6", + "0x663877223b14689ff867c8ccaa35dd81d11adaa7396b48efc66416331b0bd4bd", + "0xb357fdd6babb6e445b60578930656ae33c3631b1f6af266d0ccbecc3939f1ad0", + "0xb81a631968209df011499295d0de5c022aa774e1e27120f9d66f80e0a900adbc", + "0x1f38976ef0d9bb908a6bc383fabf9e6fb6fc13612d9d4b08f2bfff468247b196", + "0xb1e028906b53165dea90b579b5edddab9dd4dc463dafb0935d9a6375900b8c43", + "0x635eba74ce9c409d020619aaa12341da91ea557994f8c619c2a86827adc76f87", + "0x1170bbec44f5e8fe777b4756c5026dfa4ee0e612c5f33a517d404e75ac9c33af", + "0xc8b4fb39bfe53aaea332923d9efc28dc6616f0b6706ee451a7a83f7800f2fd8c", + "0xe3b0e40232ca151c3f9f23a24c437666f1171cd8bd5b8c58e491f0b2f3e04e00", + "0x85acd74fc1ea6a67a815ba1b823a21c387cd685d6dc6a32d40edf5386689470b", + "0x5e8f03e275ed270a16f82d737531660aaa38ac3493bb43c3d7cde1b38884763c", + "0xebdc27490ef623e713855922f08a7150b8a2779e0dce3108f6cd3f3e75e953cf", + "0x519dee84a0883b1531ea9c553ac85d87b89afabe340a624cc944b355d5740ea8", + "0x9e61c69e60d1cd7c766f979759ca029246c4a88fd42013c10c45b18adc4b59e6", + "0x9412dcd938094644eba902dd996f0855737affa22b4fc9b8182a26dbe431a415", + "0xc9480908609953919ae41876be09e20ec15e2f3089726b1e631f3b4dd3ef24af", + "0xd524fb18c49fa1efe7c5ffcfce12d69ed1bab42ed0db12ed86fcdeb6961fd860", + "0x307ae5438858392039fa78a3bd206f32f7bfdd39dc6460fd513eb7b80028a42d", + "0x88c7474cfe1ba856b66c5e47578d96b3eb58a5b54d5a27fc7c4598526d8d6dad", + "0xd6fb30f29aba9c267078b431d872e5f3eb07932b8decbdd6dd06887d7dfda5ad", + "0x2a731ca8db7b3c746ac5b24ea12b2de19909012e64a40b1b4be3d3e01eb88630", + "0x2eb661ccc0344e792c57526128aaa919fbf2035ce31cc2695f0233eba95a7366", + "0xcbf27da356004f598124d460615f566022c5f6f407c1a4d73f44ce2417d0562d", + "0xb75130c04f8c5e95d45f9ecf81cde02e00b38d2b4430424b1e8072c3695d4dcb", + "0xaec7b0d004a48cc96386085e1be6619b90a238e3d4bc124dca20b750adf8ace7", + "0x8240b6b18aacbd38c8d1af5ac8c1209d210453154e157068e743994df021406e", + "0xecf2c68fdb46d8d997b8381b1d0cd539487f0121bced497e00c42d0526a01f66", + "0x5150469a54e39723fc1198e4ddbe7499e6e62a7810de195c067db244bd91cd4b", + "0x821128df59c10185ca16f74b4c3fc560e955f98a932981f638ece3bbcebb4a0d", + "0x97234945028ee8f765d97ca853eb91cbc8123521c47230104b00ae20c651c188", + "0x02e98afe699ba267aa5980eb385617c000d4054bb75c5f6efe95ccc63d56b0a8", + "0x0c658ea6a2524dc98e04290e4df45960135c197feb1be0d7f8e020a158bb838f", + "0xf73e6025587bc3eeab271408b3976e4b49c31a72f4084ccd0a355227a3d0235f", + "0x23dd261961af4dd5e2d374f488900a2db748af1e74d6efd5ea06ac884949767c", + "0x6e410e543ff34b01d93370222f7215fc2a03c452bfd54b1b0b6ebc43e69ebdfc", + "0xdf4b1edf3c67416688849b391c49b56e321a0b57d1b56970459f1bfab6e1085a", + "0xfcbe544849222f5380dfc156d89a2f4d0b0b1667c2e69bf42ccf295e5ec463dc", + "0x2734c8565e13bb4e230fff7d57ca0eb1f6df6e14f613af46c414811652055697", + "0xcc900f16042ebee39b746ff2dc1560a83877c0afd489ee96507fc25e73dcdde5", + "0x2129e2052cabc777706df88ad8f2ac6e7305e5b48b750277e99f17af433fb4ae", + "0xcb4db874c882a358573f24e677cd4aa7f32dd3d168ba0f3a7f52d6ddedcb9fa2", + "0x6fa2f25f90097b8e9a40568ed47b722887aa55148bdacc2ac5a47127d56b5130", + "0x5bc66e068a60908d40db18e896fc3858d61ec9aad2fc7263751b13db201e5529", + "0xc44eecc17a4a111760455f9b9b9821a9869c52b2e3efb4d61525d23dad76cfd2", + "0xba633dc8d8d26d47406ac75dfaa06e1c0b1792ec7514a065c4a40a49cdb553c5", + "0x0cb6ce3d61e49c869f032b48d203f34bd44c1994988bd5bbd17617682604b867", + "0x43c9df22c68ef18040209ee61fe39bc2914c9d8c6e4cfbeadc37fc5e3d5987d1", + "0x635a1c76666370c1d41411cf74dcacccafc338f7040b25602d8a978459fed70e", + "0xe0c30a36337431ccc08c205c94ee5e1099cb87570c7c557c4ad400280311dfb1", + "0x3b6081e2cb1bb1c1cd02030fefac65bed8cdaae6b4441df3bae0a16998fa999e", + "0xf9deeda00032675ed253806b773ebd90cadbd5e2062d827f185ceb18534e0bd6", + "0xd02fce8a49ce420139f64c08c15d49e98b7aebaedafb26ca44456290672bf279", + "0x4a05045dd7bc5a57c19c716b2ee24b9c1b50b7926000b3a02af35f070372e121", + "0xea6f350a86e486b76af25ff646fe065bcd9d5f217bf9cb1b66a8eb8c95044dc0", + "0x1b2256b55dabbdde8f8e84d3d07fb6209f10dd712435cff476004077d9f586f4", + "0x59c2e587c83d66649699943837f2e0e31c2b76886b8f54083ee0598157396e47", + "0x97fc62801c51f43d42e79753891ae68f21cdaba87c7d54996b146d85fff170d1", + "0xd8405e4cb770b393542a55237fc0753bdb1268a0867f890cb9d9a0884843a56a", + "0x506b9b35ae50640e85da45f50e030c7c8156d818d3bdb68df22c5314457007d8", + "0x0d10e47ac27579828379f4fe9ba7e6cd6e10be5b38db96a64d0b4f30a673e078", + "0x858081f0edeb11a110198c94839c1af6364656dac2282f7b10f3b8c06c378998", + "0x49fca733528c068f70eacf952907402276c41754e9caff82633a7ed56730bcee", + "0x09bc57c007180fbc25c399db4371b50035c4a6780bdf29ca153ce0a4d5b9c4cc", + "0x310794ba7dbf07892d7978b575dd8dea685c5a231baf07aa31461628e7a579e8", + "0x03177c98acabf844e6fc36b41398a305a779829e773502bed90f79da0e2b4b15", + "0x29ecd02967426cba0dc9f2c0dc025c0d1013f9799b4cfb415e74a8ea5a40205e", + "0x670832721f46d257922e9d22d4867defd573a5580c696798f25602094a5e4dad", + "0x090c484824040b1425f2f243c7cef1c1a9572631ac8ac710c1cc84346c2a79be", + "0xcf12a43e718cd9bd0dcc86857b2bd0ec1d5f5367f806db90d9fa4a6a853af227", + "0x330ccac91ed27576d8d6530740b37f9c5a201c79e011e2f119992899fb80431e", + "0x27ecb6709c6e36545af38a06bce082c2d22f495843b614dcd1bdfe28aa58d7d6", + "0x110aa3ef2dd8159b09f9009e56191659e75fd9dd1903c9a32e6f5074cd2c9099", + "0xaff268fba6a8e12a8976cd4aac77c90df1cf3049f62b8bc1fe95d25c08fcf489", + "0xe7e0290717d06f1e77bf37abb2142141a80597cc1180a12bba1b28d148b52d11", + "0xad5a70e3d52431dcb8fd3a1c233321e6dc8bf746717434743fe3f33105e19ed3", + "0x0053cc7fc6ee917a6de3bc392b454aeab3b699ae412e26e7d1ef1af792d6e8e0", + "0x799982f75fc67375fdc16e2edf5748d5b55d203b33464521a2bf0d56a2d6b272", + "0xf283ace8d86bb2fa52d47090faef3921a310fc406f17fe981aeb3ff4b6ab16ce", + "0xcf5d7715ae3140f9dc9183e03b8b1ec8b6f0e5d9554ec434433e45234f565fd6", + "0x6507ddef45768c6984ebff5bc2b6d4e9f77fda454a8b5ef5ee5a606c12daf83a", + "0x234f7672133f60e13e77b4a80f98ba11ec96a597b662fab8d74364c2b26d3d64", + "0x8a647a4943f31142c30d91a297f8a541ee38b05ba11d693b67982f9d030ffcab", + "0xd268e0115d42e9dfb667c8705925ba6a796f1eaca02fce0f413d0dfae4955fd7", + "0xdd8512277f6320da12ee5f02b6a9f59483e35ec3d84381c1f2a5f776338b6d83", + "0x73a8b7baab1027c37144100cb76b7b6e6942ba87be902d6444965ad95d092305", + "0xd38cad9f345a06428a061638735f9555d98522e3e78db4f11a76ce1ac4ebe743", + "0x97464684fcd351fac1be2c668b60d09ea1b312f7a7e8d97117de2d9722ec5ac2", + "0x8b5774c194465f75386c19d91e99f0c66ece95f13805e99664ae960d0273d278", + "0xad9f49dcc5baa135fe439894c5fffa791cf6bed2dde986b0ecd23cda3e923d71", + "0x4c6e68776cdecc80ebc93ec7d1a6836c206611b8b5269422d197b02ab0d32248", + "0xa759c23d095fe7e29e9120bf3ff964890f9e2a71eb6d6227c61dc5a9c1f44b2d", + "0x4fa2483f74f6c7d1f3d608b4b437b5064dc700ee567dfe48a923ffaa57d8b9a9", + "0xbb81181d4f72995858d6f91877ec36bca26a2365a3ec56102570f0ac174793cd", + "0x0e56a43507cf2b4e6c8627170a4cec4e65337a4176e2001ef8b1dfa731ec1ade", + "0x6803e0f1b506cd11863da6644bb0db7920e68c11a24c1f8602dded1fddd8961b", + "0x042e5263e779471be7e42c2831f09ffa898134e060ec5b342542efcca895e7b7", + "0x9acdc8d36e25d07c5cb5813257398bfaebf816e20beab5c08269d6a86507be64", + "0xc03f57245edd556fe0d4c87739dfb6838c69cdecc2bc158bf316be084e35725d", + "0x7be1dce8946ff284f78efacbf816e4789b29233a83c0000e3eb37c7fd3fc052a", + "0xabe7b9ab883d45af186a4a0bbce226cc6de1d8f35dd8f81a4c4197c692020a2c", + "0x9ef43a89fc890b9ce8fd0b3ef16edfcd78bc3d20295ab7195a1f2671a3389ed0", + "0x7bdb58f8f0874845839c088537d927000d7cf6ea33fb96ef48ff01c2e0d8ead9", + "0xbbc9cbec3f3cbf495aaef4b1335c3dd5fb50e239bfc47c4062985e8ac2b8c882", + "0x0eadc3198c102bd684c6d90ab37b5d9179bb1921d7a96a4192916eb40655f55c", + "0xafed8e30ed9d2e8efb3d9c6d877c016f29b9926fd8faae1c29aef8ddbbde1df2", + "0xaa11586fa46907e70ef9df932e9b378cb6c794c234eb7c9da60e7912eadaddc1", + "0xd787e22878330cee80d3dbf2626b5deb7ff63b974404ce0ab991b1b8f663b2c8", + "0x3b94d76633ab32735ea9bcc5c95a9508e13fbc900e35e4dbf5e326bf71963688", + "0xf984d48c8fa2d2129d028b49c4c92b2a6c7541a724a8816e4b1f9a0284407538", + "0x42c62045d8816074be04e7845cf9c538b1c164559f360a7285c954d92ce76893", + "0x94a4e12a0dd045969bca2403917359086d045a0109ec26a0db5fca42a74ed6af", + "0xee584dffee8308b8d2e19696c6513dba4b30a98cb400883226eea76ecbc6b2d6", + "0x48a2752362ef0e744e3ac80648ea6438a9ed0ae1a18db99508120c8a07266974", + "0xef02e996573ceb88f20bfd7eb0a4a1cddbf95c21f2481d3f3e8f4d0f613beaf1", + "0x33c35a5cccf132f20d12b426a14f72ef0f8aa2d28154e8fd213e2d0f192ca3bf", + "0x3c65f8eb57824ac611c8cd8e1525b6e352c86442b6f2ce70ab77bdfefa393c67", + "0x2055f6bc8d7baeaef07a65613fdd2cd3d78abe2e701b208f4bbc69b241aa7abd", + "0x159ff6911bdee0857f3d2eec2b2090c825a1acd4607283a3caafb4277d3e22ff", + "0xb512739abc7e79212924bb585138d6b91638deba35cc7435d992d029540e563d", + "0x22b66028e725b6ae4e8f781c3611c65925ad4d70abfbca1f9f7f5bf1e7a1ea1e", + "0x716907c42a619a03f61b886762d395b6a66429bb41f0ebdb7800b4e7b08c574d", + "0x29a9c317fadfed889d9fc38febc60a296c8dd909e987b18ca5dda42967efa314", + "0xb816d4de81b3868b22f5638e8b29981ecedece325a80e96d8f7fd48a5bbd4c26", + "0xbd15ee1e739e0037f67b4bd6421d5009567da8b9f0c764775b2d05ea5463809b", + "0x72770f225115c4877166637a4ac88524c406a4fe558ab37eb3580c039374b1b7", + "0xc81488b073bdad30bdfe459555d4f05e045154b972b694c282ffac90acafd80d", + "0xfe753463eb82e69628738d1b3f06a227d9e5ffa4e177668706573f360ced6456", + "0x93b0ab08646bf8fd2c661aa31271048146d8731c163822f7d9cf1e9c436456af", + "0x405db327a76767bf7b5348bcfe82f7661c7a4d2a67f1578c8c7c50af7dc91a87", + "0x65444414d201e9320158ad5e6c99ceb127470e7a5e693e68cbaac3963501c862", + "0x8983b2e8d70db57a4abf6a5c8ceafbc189752733311a941198a6b9493a7e8adb", + "0x1c33b6d5c4d6aab553e6433e066dd7555cd0acccb58d9d5b3709a2dea61df862", + "0x32054bac3f7292bbd965653252af59a575bda212139f800be3e5ba812c5cdec8", + "0xab3f353b67f93cbb7f79ae824186ce30ab7bbd9344baa165e63684012458b274", + "0x45f29742f3e60c4ba33c605fc5607e079ecbbc4a978831900fd2ee9f7f5938dc", + "0x976dbc3a6f4308045442a7c586c0b6b5afbc8ca09d43d78f0b8c0ec0106d0548", + "0x17932710c0ca23b270d4fac213939d72caab32758637b58f7e7d59e12633666b", + "0x051fe05277fe2f44ed7806a68ee73e06df34a52a96c0b8a15061413b3931f809", + "0x36609b1fb7038940d8026fcce09a2594edea68638d7fe1fabdca94f5b65947ad", + "0x6fd9bdee796f3115ed498daf0d8e6962892ee469f7901cc0f52d5bbc78cc0e17", + "0xcc02f4ea502719c922672cc7219e844ccdc0cd9c96952c112618f5662aa4cacb", + "0x7afd69383289c94e757cda2c4c520651008c03b794c2ae5b9b313fd847de8748", + "0xb169e08f101a17535e02571dc17f3b579390924f1f3434d7691ac838bd87933a", + "0x6eede5cf20372605833b3e1442ba4a3bfdaf9feec5ab5b071e56bf5d3bd6458a", + "0x1e4cc661040c432b0125b6a0c1632afc807d8b683a4c970090f507522d858dd6", + "0x62b82a26fff0e5767ebe3cb94f0ce8dd99944be29071e4df8363e692ffece31a", + "0x28d445c7f0dbe8df258672bc14b78fde18291fa0ff8748e124da5ca60ccfee38", + "0xaedf1a0d0eef65f9c13433ca1c3d281fc75df26305071dc234635b47ce0b22a6", + "0xc7a2a1b4af21d151fb8356d966d12d44f610b47553b3c48f1dd2af634feefeb3", + "0x5dcd10870a95b7273ecf649af76cbc81bc82395bd236cfe6f88428b84ea513de", + "0x9feb6d054b1f508336376f0ae7d5110f680d0a734d5578007ccfd73131e5d081", + "0x404c45ee68ac22785479aef363dfe68008d45020ee5c77e711a6f641173611a3", + "0x6ff01f259c3b81f51366cc3088b17c3900527142b32cc3de4236ca520db3db7f", + "0xaf529ce52f6733d4ffc074fcd597318ab9acf5f6cf453848df0eda652bad0cca", + "0xa5324652c456a525ba38a8cc2be328ded38d5efa27ff9d1fa3771327d17eaa4f", + "0x09006a63cc9bccf67e07a07c8b9e95b71470fd7ce943bbbdbe43cfb611f27e46", + "0x603d73816e32b9a27593055d78be2f2746861d9e0f03fcd9e514a8c249cd99ab", + "0xa0c03ff97b02d36483ef2f9a67e336f468cf105b22f7dd0df494aa66cf90a41f", + "0x69702314a82ba0ec96fb33c26f8651dd1768359b97f33a45cf9e19c6e5f99726", + "0x48422b327b7a68b067e2ae7650ca4c83f6a390eab3f8147b357b3c3c56c5be00", + "0x3d3850e911d702eaffe0bb992f31df0a4bfd80920ab7ddb72090dfa669e77f97", + "0x35e52e64ed1a7cd71a7fd2f163fc38d6d6f74d06512215c1b4b024764716f7a1", + "0x3a907f58b411d30946fc0b902fee38126aa6b9b996cb4034aaf74ccdd6814814", + "0x60ca39b448367f47093d6098052f2460e0cca62e4ed27001caefe20f809519da", + "0x8ad387926d0067f3957934cf74c3d9d35c1f6dc55bb29ff7e6800357c86c34d4", + "0x27ce70b0b28cf8916d4f7b71dae198cbf3475b47cce8ce9498c8859df68a6ced", + "0x47a573424bef323a7b0861e6af21af57213459761868ec9ce33002b2b5b41eab", + "0x9c84fe80a54566c3e306c7ab45707b9a94b9f8b81ce36980ec086985a064db6d", + "0x1750b2b0ff8b53d613806a0888368e91966885e4dc09621b4b86e8831c9189f3", + "0x24343e6174ca7ee5d94701ac0bc752583ac13e653c1a46c03bfd6e7abc1b647f", + "0x7fa92c0801923e1734c76b316310f837ff8ea030e5b5c33b26123da0c88d720a", + "0x597b56409a0e80facc14699af3e4b434e3e479f6ec275b60be3a937106648e6c", + "0x82cd5b84940aedb06bca8f284809f0b75f2d13768f329da97382959143c7df65", + "0x2241f773963d2179c62ce788716f76d7307d87939c9225944927eb953912b68e", + "0xaadde38f5d093bc5997ae841053a5b60dc60b4321b8fe7283303feb4148c9a31", + "0x3b0c5681ac8289928b215f952442a0464f5540b86ba172031a8f043914ea3024", + "0x36fbc7bbd224fc95eb6267192ce5ca407c996b45896ed3f5c5d5a3e1cc887228", + "0xce901b98c35b3d29f2add053f19978da9e0468cb41eca01e2421321941fdf378", + "0xe561acde67ae4eb2989e10b9733e7f3c5ab23412118d9191d45f4687bc8552be", + "0xdfdfa9828f5e8ae28bd567b8b1fac98e393a9dd8c01dd92147ff50028f0d1efe", + "0x79c983ce43ac043b2ec3e188c4664d30bbfde106d84acfc65bd3f65e0d4bef00", + "0x8635f2a2bf9bd77ec84f512142b98b441cfd7715e8c67a17aa65d4fccc984090", + "0x459497c82129d461614744b216f68ecd59cf7193e91ed5db625807887c15f543", + "0x5124299dd49cb43145431257bfad3c884365f885f7e394716ab1e81d9e294dd1", + "0x2620e102ddf324ed10ae7744c700d9789366a2d404e8afd86050db6c3658e116", + "0xff15e971a934969efe8c60fd2e14e10ea6400ee75cb6cd212ce0ae29d1507905", + "0xe539fe4782d13fb817c447f96656a4878cf5a2ba141adf05d67ecb6e4dfacd73", + "0x1aa07ede2fee94761f2e16458713f6a4b34ef0fc9f41abbce75f53a80cf76cb4", + "0x744b6648af6886f566d48cd188351ef3d482a7ba7716e12d6709173ef6446d2a", + "0x54fc2ccfa6041ffb85a78b614ef52623efbaee9d559d85ef649b148e7f98d7fb", + "0xd880aea6814b569fcb69fce136f6c23d6b4b30fe4048ff2fb63b31ea437529a0", + "0x33468313a38b2f619b75142190fb6abd0e2825b8404aec4d4b9f079c03ef5353", + "0xbccc230787f26e61e5f1b8caa393738eb32be12a2ec1cb5a1386f75dac1f927c", + "0x4e2d1b72f2019e8e8e2e63a0397103eb61c13b732051843041f2f6d5e11a8587", + "0x14378c664f843b7aff4e3cb15d61248a46d9bb8d17f4b7b4dd81b948ab948fee", + "0xe9dd1b7df3617cb3d7ad66dfdf24504beb802c90adcd9467c92cafde2db2c5f2", + "0xac44878899afdc2406efbe437ac021ae7bccc4c52cc068a1484fd42d64e4a3ce", + "0x3c33c4afd11c460c6a25b30684c4b3986869c45a82237e0a02b591955fabc5bc", + "0x94ed4dfc3f6650c8cb1e90a16904461abeb5670892df95be32d978e419408251", + "0xd309787e62a4047966a5ee4ad96797b23d7e080796a8877281eb27411f43dde8", + "0xd1f8449383449df90f8dcb97f5b210f60577cc66bf7a2014b2272e43ba0c33e9", + "0xe57c9dc56df84e068c07ca3fa29b49fd686b4b0f5140d63f8c3b5f2a1fa8c854", + "0x8920814c75073ac20a35e32e254dc73ccb248dc8c47c64b34433061da3f229c5", + "0xae62a93e9a02d5f0cf2e7f446c70e7670af110a03f4be7fe2d72d206321e230c", + "0x932eae3d52edc0a1905ff9c1b7e25c3fa252c9abd65fbf85cf097c20968f513f", + "0xb98f44186626c6b4f8ecca2c53659432a262ff1c0e2955c0730c3c9f62e9c633", + "0xa7d11938579ff3dcc50e47663b390e3ebc7e0e211076eefc00134e49dd7789e3", + "0xaf2ffd2b1a1b7061715d5a179d602a5b97c48d2815f5c58eae9a587a0cb2c537", + "0xa881567841eae85a14a1102f9ec798737f8a5b3d276a86332321e6dd61167ae6", + "0xafb721daf253d210eb6ee588a06219d64bae887e719d3833615adfca49dc2aea", + "0x6ec88afe2396f9c51d06543ef8ab017dcb178d0efecdf7903e48f4899b0a06e6", + "0xa3192215782d844cbe0cbd6b39afa06c8234a81e964287d28f9dc13670005a01", + "0x28efdd7a713d7d77ebd1eb7fb8f834258bae682a432c2306033a066d00379a3d", + "0x2fe14677b9992882e3cf4586f4e1c070870ae0e031380f4c5b60ce33715c6cbe", + "0xb10c9c525c0dec4e9ad14543ffdb6f0e7c538f6e66036b63a1a4229da2361f1e", + "0xcd0911978f714489d6622d022829bc87465de408aaaa3e911c6a65ebba630a94", + "0x4078d71328d3d5dabd81525cf7ac893702f57fcd2feaebbbc824c2acd6977add", + "0xe712921c5607d99f7df552c877bd90de36bc6af45ad2a9d2b46b5699e86c8fed", + "0x04784986a2599138ff9039fa7d8655473931cd4e4fe7a3abc193ce4837e44b6a", + "0x35aebcd21cf1337c05829989b7378fb788ac816083ed3b7d550e65be1d2b2fbf", + "0x030fbbb4c0ceb680069715393bcf5c8b5b2ad752918af57eb843fe0562455743", + "0x8a12af1ff23e0c12a0a2b1943b2e1000f08fc3557f86af76dda684b1fee7fd3f", + "0xa83062881c7b226dadbb547239cfa564541cc918a11072b45af561f0e95b03dd", + "0x54fb9b23fb121e79f8ff6ff786a941a4a55a93b98bc77773452d49bcb4bcde53", + "0x62c8b14678ef1aee35bf4893e95d462df26de892130a04ceacd7f32bcbc1ade0", + "0x841325a946a635a37156317ce7f5ee13f62201b9b8ce1d52afc3b61fc2062889", + "0x9e5fbbaaeada3579fbb0f159af38d6dbcbe561ce4fb47e5a15d279a90d795635", + "0x71c6f72cb480ad80d99d1f7314e01a260f38d948a855223cd45f4eea8477e4ce", + "0x26bc991e57630e361f2508e112afd1f86043223f70f454a645e650f30fe1ba26", + "0x5126718c323cbef1ee4af1173af85fbcf337ecbd071f3196e20ef930b076c7e8", + "0x9dba422573bc92b8db51cec39fe1d9b2dd34a059123ef3716ffe45a01eba870d", + "0x00bdae21bcfadfc5fe0d0cd963f2a00cd3db441f31c44a7ee95f834948589652", + "0x6a60d0e6c68abfd2bf110c6f06cd2ede06031fa7b0f37357266cc184698df4ef", + "0x6fa1598f74ae0493b186220fe0a91ef30cc7cd88436e57fc58b2f4fb7e854f8e", + "0x4d28238da6dfba979b9d8dd663fa3df82bb68eb75aba65ac36d1513a32d1b814", + "0x764881512a4af3df5f64102cf2f4308462ace94a9e0ac3f3cfb69eac5d43f120", + "0xb188f58673d9d70477c14d67b927ea5d124e1c120d2dd13f217966cc6fdb8379", + "0xce77ed0eea9fcafa5cb3d6166249c9f136c5fb0864f6289780f00e6bc0ccc5d7", + "0xa3bf5e7f25d426a3cfb2221b5473b2e0b353e4caa44352deaf13dcdb221ccff4", + "0x78726058029709e51b7a02c28bf84a77f9897e2dd70307295f8e2cd23577df58", + "0x2e4f9a0f2c0ec6e46cd6d09bf39219e2cf79f0eaf061d298d2e6a3597fedf0cb", + "0x2a68bff95d0f22d7026ab054bea96a7e5c3a8586eedb00e8c90f9dcab0d7d414", + "0xb5c63eb6a1f734bf0ab8764a29b9136eab610c126788d087b8340766af829d07", + "0x1d0f47a1bfd05aa0aeb7e998018716bc1a2331bba182dbda297c423d2ba03601", + "0x5749290ac3dd0b49d6ecb4a5efc179b7742734a345133fe373b501b038cb5c87", + "0x2c0b1dec2cb66c565a779cca178c73b690aa9988b1ab3313e9870d4b81063bbb", + "0xe3fd85b7e9303c0af2a1554cfe3007578cea631581dfc11c75ed0259c06eb341", + "0x7d276413ece1afc3f8b894be866da93fb7877bfe134fd38d634daadd82259230", + "0xce7b9353268143569a9ed031e6bdd72629227b7bee714e89e2a0b506f966da13", + "0x1c95f51e08a4e03c86dd4a62fe2e6f482170a1d904f17e7834b06afc1e870fc8", + "0xc173dc3408de3a5d5f9038311141ac5881311a2a6c0b8dc415e204f9f0ed4a94", + "0x9fab83f36ff814e979d63b7bc319e88b99d0a11b398a654e2aa164e67428b9b3", + "0x4c82be85dd2cd996931f163103dc4091b6a203bec574ec358aa91974c933a2b9", + "0x158197010563e8fe53c6d011772a78b6fb3683996388b29f4e9b2d5b88800bc6", + "0x195ad9ae63fef198225f42e459b0fa54d30b7220899889e5d948d4be0a507282", + "0x1e8f08e4375c31c724ef4b473bb49c08c2d33936a3da159c6309437f271dfa0d", + "0x2084170a0fc38623d0d281dc4d7eb259323f1f47db697d25bed4a28ffe812051", + "0x94632aed4dfb576ff783985e2224ba010ab423d8715aafce2c20a4025b0025a4", + "0x9fe4d702a48a6e974157b423fe06180bb133ca3087c96c34991b499424872d2e", + "0xc728a4761a80237ed68d58108a010919fcf76b7c27ae007546e65c873ea34ef6", + "0x0087f8c4c5d0ab3fd2e0a9ff65fe27468a34607967c5810504de149ba1016591", + "0xb89a144686b45b49df0d9b6af30cb04a2ba9f775b109ce7a58b1cbf23f8a0131", + "0x171869d9044420c0c706479a6f8e6a8ca112cd8144c36413a98cb1c44fa81e85", + "0xd2e6b321ae587fdfd662d08a50bc4c6983803e1b4ec409115feeb90ab3c9fccd", + "0x6ba4bb55f70a8483c1e16f6efbd94ae02b0d631d5a62fc042a9c341c2f591ca2", + "0xe1674c8e8b0993ef75a674d78f198e848790686ed001da7ef43e022401749bd9", + "0xff39081bf069f474e37d97c8af045de28f95864efa76a8c225b5bc49fcb17a7e", + "0xaeb69a5572e78cf0dc72207206050898829d436487f6b875a7678ffbb534888a", + "0x4df020986c02e8f91277126fcbb7e2670ded7bf6f944a51a574b37887059fe31", + "0x443b6e4ee2362e07836b8794a27e3a8a7dcdf25773c08abb0a57a588a1b43755", + "0x90daf8ed2c411c0fdaa29c7a353fe914129f11d123bdd28a21ea18bd06becdfc", + "0x63c6b9358b9ceb73e63b3c6c99f67f7aa567820a45ac07bc749be1e2418d2356", + "0x1039f2b7eb026329ca6f99898797ffc57c393d2960421e97aa1515139d37d2f7", + "0x0f19f8fd9cb4d98ef15b0912e2bf238ea2d68d19db805bf5fa388cfe4645856c", + "0x21c408b4af3cca1537d78ceed8c69989558e848f2da9019c270e3e221a6bf6dc", + "0x48df5b4c393e3767e1b4ce908f1ac29e04c695fb29c89004c12e002b0bb8b94a", + "0x7056eed66c367c4a4fe48f5eec135a73426ea3f658462a6ea152174c68a08720", + "0x9bfc0f793f8da441e93fcffdd565de2a4e20f8dae711f6d05ec3ddb42d953dca", + "0x6387cf844f732e251ade3ea4927fab49805942001a83d35e8aed8cb7c763a8a5", + "0xc3a1dcede67d3675e4e0ba92dba1c2bdd93d8f7f86cbf0991fe16d1e49d25805", + "0x7d2fe02493898c499d0b688d14b19ea0bc086747f8dc02a188c933f29995e015", + "0x1d67d5336a9f87232b3ab384ebf5e4dd5b09afcfdc53f85807de90832a48d435", + "0xd767ef29389676480439e42c35635051450b2a8312a30c6d71c192581cb72be0", + "0x8248d1d38d8954fe7ce01c42535c1d93945096bf6e8b388b9249ab8210f52c3b", + "0xfb49d3ad9cca016dfbbc42f2e91eba268e8f55c09d642917282763d84b620ea0", + "0xf2857fa2a46b0fe277f38422940297a9f844e62e9bc987ae6fe2cc1e458a2e11", + "0x5e347f33fac567dbb752c886cdb7b5714382a544120191852394f617b510eb79", + "0x9983a4dd1d2c5d1538050359d6e4413f63dc25d66722358372bac701df599a07", + "0xf35245a3632345f3f0cbcec730f3b7ed8545c2221c255271370d3e4168a213e9", + "0x06f163a97f76351c3ed59defff036da7c3fd6a49263a470f072dd159d1b8d125", + "0xa1bc8fa031e186d0f09c5e6d90aa1eba227ee89ce029dc46e482eb4a6ee84ce0", + "0xf86867c12f2b5a2555a43a5896e53176e4c9e71cc33734a35610b6b48b9e8f2d", + "0xfeefac5d78000278688017797c9f6f05a8b9b5abd3ce7bf45293b08c3955251a", + "0xea1b007d50741f16db0d404ce1215a099ed84527577f8a4aa94476a6e1e2b05c", + "0x7658cb768449f8992f3fa3d0e4b031d291d29032d140437d48473bf11cc5aa02", + "0x7809fe6317d6b008746061ff994c975478ba5f7dc42cd1a60d237d7e7fc20286", + "0x8d1753114a7cb061717b9d6983ec8dbc6c19f38f20443bf2d42d90d920ffa96c", + "0x5d86111292e4181bd15265b3a3cd606f42a3586f9fa4daf6275cd82273f02990", + "0x70d268b0b7e46befb84cacb79029a62f98386777dbe788dcd7edfb96e7eb5344", + "0x0ccfeed2a966547be0f39e84eee4e5dd965a514d205c2686fc76d8e0953e4cbd", + "0x4389cec26c881cccd92a717a5860aab7c4cbab264314235a27669e1a61f63df3", + "0x89ed91685b5ec5c16f9f793b7e888d2878efb756289878dcfcbaa82837843dc0", + "0xee5f1a957d9557a5e0490442aeaa4da6f8a8618edbae777acda20512dd82e5af", + "0x9227406ce85a16e5dec42b2db4dc2789dc8a9a6e9ac3ec5f99326c38122db0c9", + "0x1d6cb4967450b84f6355ec0d6208cbbca35bee4c6854cb167f1dd9667d9f710b", + "0xf07b5124976e06a6ff9e1c4821611672c1cd74329c1eba2fb0cdb3f2ddcbc2f6", + "0xfd121be547903b996650dba869c07c049c422ef3c884e98c578212b6e9a89710", + "0xb14dfce14e7d59922f461c17b4ce6c4f849738a41044d3a532b34cf08743ba68", + "0xc6b12b7b8bca677e20dbc5da9ff374ee58bb7e4d247ce0d95b8955baa6f706d9", + "0xa51867d9362423554879d9e24a21be78e324d4fcaca9910e2303dd9ddb49871c", + "0xd28ca382965749532ff012e64724777dabd3db6e66480bd080dc9371c88219f5", + "0x76dcbccdaf381aa5cefd784515e2b60a565c8852b6645aa58eb9b2da1504fbfc", + "0x14a8bedeafb02d7441ecd6d54510d70b0c43f4d69256c64640c7f369c5000611", + "0x7eed422f753fa1263f1c7c270c66b7ae8aab8a0356c71d8658cc2025f9479999", + "0xee2947f8edb3136012ecf89cdce52bc9fef01ecbd34cf90fc93a16689ac721d1", + "0x5062760d574dc959fa73b0712eaf5e9a046251d5b9c860672ffe202d8e89e051", + "0xf865bcc33c85801f7e87ac3a60220f2847cd985176c5d2fc49088fa8e573b3d4", + "0x781d040e71751995e815ab4d88cca7c0ac11bb84fdcb906668fd622ba4f47bfd", + "0x7aafaeb24b2c27aac6b57d0f794ebe32363efacdcaba691fb2ae4ddd9f739db5", + "0x2129a9c9a22d4dc60043044625b39c7ae6bf9ebbdaeb97066335937f17a71425", + "0x1fcf5059ac1bc3c22f14fff0a12bb9ae427ea5d41b32d663ceb5f487c96a0230", + "0x717f61cd8a3305d1d7a19f133ecc76b8f94d87e8bdf121274c6e51832ec1b624", + "0x00cb642d27cc665006c73deead949bc4c8f1be3106af13b6c1c80fa49166a8fc", + "0x3514973b5c965a74b29c4e2561fed8ca49ac489417a94c4474ca5d1583c812a0", + "0x6d4547743715bb5b327c30340396c54d772f6bb91abbf91a94607214651ed070", + "0xb1d9c9f410f8ccd5229ac1414d38b0fa596c73d90d0285629391eac0216005ff", + "0xd774dd97051d04f3e36a62740a882092c3a25910c07cc035c236fca530e3828f", + "0x0d864f69d45663153ea72e98751b0b6085feacdf28dbf570a6ca73780b8527b3", + "0x9fb70fc8952dd95148c7d2ea628b221cda2ac4e8ad38a2bab79933000914fa94", + "0x4668812f54681dadc1ac6b8385c8564dce333d7e3d4acca9dc0e573bbfc55f75", + "0xc1c00b0530a049385d15d3a0f00a3ff495f6b01704ab6ab3d81e51804530ac72", + "0xe25ed19c64d3247e1c4bdab1c5eda84b857c374c3cf81cb7274c2fab14783ab1", + "0xb98c0291a8144c10804653c2f48794377533f6c470782d3b888057a4b6ee74c3", + "0xa341c084116f13e4de785ecd013e00e31c8120e476ac5b8727f86a2ca20eeb86", + "0x9fafc1b9a6fc6c5f6559c615e25df6579bac3b4dc282ff1b92a53295bed150dc", + "0x65cf1fd49802996031acfd6b64b75fac8fe8b68e26da4a676db3dab3fe173640", + "0x925b2b6be9b411e0accb302a267d6b762436a436522d2bef7be1bf11c1dbe110", + "0x27512d2b38c7ed66354c4685d870cb8d22eb1c131644f469813de651b84c1f54", + "0x8dfa7a75d9792ade0aef47ebfd88edac1033445a92d59548bc1180e48a991f74", + "0xa769bfc2b91f44e86b5326d61b389dcfb067cd5ceef8a3618b772ed257ff154b", + "0x55dc7e154885d99666bfe7fbefd8960b8d3a576fd47ad7e171a15c2f07a944dd", + "0x53ecdf30f3accf5bb7b4404f3567937e451257eef7d965f854da5deace38f239", + "0x16b4650562bb762d83e1f9c18f14407d9f9921fb1f6d905dae6e86dd2f18cea3", + "0x2052d6542ff4d1acb0380c13ad0f470f7a1ca495603ffdd5cf70ee978f53989a", + "0xcc09c6d4ed7979083e16e533ad4fa91ed99a4f889ddc16373762d56f0cec2b2c", + "0x3c46f12f188fc5e5a78f54c7d34cb4170cbb77cf148caf4d88cf8a336666b2d6", + "0x19b98dd52461a43a2a8618744c9ae543bc47eff054db2f7c181c3a2af917b4c6", + "0x3554c331a821c53aa6a095ca00e288051e2c1c686c347d29ecd9b9146620c17e", + "0x14fb93675e3c4d08cd866aa9be7ef754b77600da16a0979b1138c55dbd625bb2", + "0xa928b25f3c244023377f3b4856ff4dae439310558265c6ddb87c4a18d8905a16", + "0xfc0b056293fd12e6d45cee31fbbae44630aabef00119d6c1e8f094ef6edd4aea", + "0x7f4d301512c6fecccde2f992ef57f2d1734cbc8fb8e30faa3d1957b7a3ce16dd", + "0x8d60657368b33d39c38696f86e614ac4d00e684a6acdf762e9607a0b94cc2b1f", + "0xea7d259c34dacccc65069fccdcaf33e7d285d116a7c6f17ba1081851d40066e8", + "0xbd713e797b0cabc0146dee1c0eac2813b72a7c595feb4c30526670c725fc3f5a", + "0xf6735523765be437804b60eda300fdc2e0099deb3208a718c6a635c2190b1d97", + "0x27ea6e4d46e02869bed91ffc3634921b5c1e2cb0d0a77e1477bec4c4b8307689", + "0xe5138818b6f1d1a3da6e9a2278c619aa93e648acbd9db599df8c5fcd4b7f8cc3", + "0xa614b957816fe7e9aa9ef12de2cd9629003baadb6d3a0b1831032f630749019c", + "0xfeb21f7175a43d20560ba4b60f9d9032aaba07e432a0d9a506433ce58b3880e8", + "0x287476d06b354b1cf10268bb8c461296cb6f1123f962879f2a92491aafa600ce", + "0x76f62d6cc8836807ffb09722717be8bc02f4f81f3271c1a6d14fceaf4e288ec7", + "0xda8ac209b478b873f15839d1a71a6db09b60b42774843395c17dde15b138cc75", + "0xa05e160873dad4771d0b6c4a8a699f66d14304f6ac1a2f93ee3dfecdc399f957", + "0x62b6f8fbc6869562090fcd64aa7ad2215e535c929d9f90b095f05dfd00caa030", + "0x7439ee593cf329d514443ad990ff150a8abd33416ebde3fa29159693939f34c4", + "0x18ad06080b32721ae2c955904ab9a469179c29e147235cf5bb92f4bdb9b08e47", + "0x037989096ffe47559364fc84eea0a1bb97d58c0af240a418569341b46e42dc24", + "0x76fdada8bb4f7feb14523e625ce4fd87bf6be6508a20176fabcbed02a8f66023", + "0x1d78fcbd65bdbed57ebd17dbf17cca06a7d193448bc76622a2475ffb03dc8fef", + "0xdb4d29bb5f6c3f273190287627de90e3280772a1fdb64fc54eec7d020e0b7f17", + "0x88acb6da99963d76f70758aaa54a10dcb49b497fd9e1b1e9bf32ef3659c25b20", + "0x36241ff4e21577d046ff206178d1186da76b04e8b3875924b8fb75f15075656d", + "0x775180f69420189ab124c89d6b28f1e60afdc483f6a044d252f5fdb3baff6c69", + "0xc29f67a8dc1bf0378a6b402ec9900cef9c7162c92c51a8454cd6ecf3c76227cb", + "0xcd456ea9011dc102697b9779f78cdf17b300104b1e1e2f8a45d5075642c473ca", + "0xdb417face278eb0ac754bafd17f9969c1364d69c5ba2c6f431199968e536fd4b", + "0xa6e839704d4ff9028f8978a4ff3713529c97556d01fbc5dc8cb91208e8e78bd1", + "0xbb8716d035e772c14a8750c4737cbcd900bb0f1a2bbd01b34f778f225646d01c", + "0xad0da26232c35b63dec6ce38188215452c296cb6518372ff4b3ed36695b9d94a", + "0xc3ab261446771cde5738d71956d71c709ac930a90cdcc05af20a4562c9454c58", + "0x900d800b64a6dedf8dcc534f786ceff922a7365d5e32ef331d3ed912cc3194bf", + "0x6a2bf00d06ba393c6ca6c66867768833f4212deac1d08bdb3cb0ca42912f8338", + "0x2fbdd822a5a62166b7db8030dadea5020e9133d365f67624dec8b51e90d4bcee", + "0x69928612fac45c272b3398ea73478db93755973bb9224c7347cb0b3b643c658c", + "0xdef3edaa407bec38a86e207caf4e7fe04e65d5cdc91b558169a300de52f192d4", + "0x185b906bc7d7eb48d51f4c202723514933bfbb56959ce16251a450ddd85e45bc", + "0x9cf8f9190c529a7ca871022995d49e91aef2f18052d26b5cc188e62362964192", + "0xe3ac2fd597b77d169ad4e2fcb796528c4da2082a7ca4da66b614dbaeda2e7bc7", + "0xdb16377c312ecd2591db2f6b31605876c2b7b3627db168db0b854b465dec7449", + "0xd1959c9800ae2af3af2ec96c07a66a2779554a8063449c2c42b77e9919dd860d", + "0x39cff7290d4f0c43c66ca7bbe9e5b25e67fa1eea26fffc23bcba461cde82ad76", + "0xd87f5e7f60d72fc56f0d4e384e05f0034dd3edb6cd7b79fc26c928f7e79e60a7", + "0x317a184a35b74d062e199fadfba4309dd82b0c1e16ce834fa241a97504cedd5f", + "0x32d1a8693071249ca27729cdc968f0fe0634a9a6920475763dd47d21274d4014", + "0x449757d99994ae3a03427e8d9d7cc3b52f09af5c63d0969266250deaa3d92846", + "0x88c81609eca68427e20ba292fa25ba46c98bce20701bf1010beaf653cb723295", + "0x4f321eafdcc5d51da512f53e5cbda19c380f3c349ba080a525843bfff7da06d1", + "0xf35abbeb3f83c1d8b5e8ae3c9fb1c2ffd1d8b51e3057a042832598c3e1a0883f", + "0xb43d9653984f4d98afaca30ef7859741b9ced2cdca35451b8231923eb22017a3", + "0x05bf7dbdc49c3a2421f02c9a93378e983a80c493327614c22a6d3aefc33322c6", + "0x1b3018f63a4afae68671800970fe20f83a576fa598966167d89cf72086201d07", + "0xdf46f21965bca40db321def12a2c9483861aed808d8bff8f712f4a530e17a559", + "0xdfd8719a330b1ca47cac83ed035b0521586cb783698a7459e342c1c049e7d919", + "0x1c60f72a364d4948c4a6b305d6aabc08c24353cd7dd2ee80e660a44dfe588d38", + "0xae541ea95f0cc4e0239b43a1ad1d150a551fa52c92f5083190f45af356b4b690", + "0x5df6cf097051446d161afc42fba6ba947a1d12e0ccece5dff96121b9ce914360", + "0x39ec7fcc04e3b89343b8c235ab90afaa0b989f1adbb84695040e6e3e0442bbbc", + "0x4654e345b472f3be255044c4a8af77d1772a385160e5a6a337075f50b729974e", + "0x9a1120d294144ec365224a540e2fb3af733044b40155ea75ddbf72645f4f3342", + "0x1b3cf22cf7cb86922a6a04a6cce7e5bc7b5a35fdbf1de03e3cdae932159e559b", + "0x459d1e8f07a00676deaad6e76b0cab3cbca460d16cff24d6a7218242b3899c27", + "0x79e8e6ef7eb8787c2b2212c430d31a97d56b6155adc078d3fbf400cd8d6b68bf", + "0x69f051c58554109e3cd65d2220f8c5c5887ab23d8887160247fefded84ee1a54", + "0x265105a16bea2baa98d679be009496c6bf8163cb338712bb69d41dcd8e2d100a", + "0xab21ed64615fd48a2064087a6d2c02ceaf2937724462b181d0125614c1bb4cbe", + "0xc08cbfb79dcf5f6c95ce7faa57b32169c2e01a60a4b98dba5bd108533047b1d2", + "0xf79bb256a8f55150dbc201db449854a78e2414d322cd2ed4a521d9597e78b132", + "0x1aded18eca6ac4dc18420bf89357a2bb9a0543c648855e8001cf31b352484c84", + "0xf4b97d94d5650f20fb53aecaac073a03cd3d91be92c580d2f1fd3f33eeca27bb", + "0x11bf3283a475e7eea272843735b319a7b97d2c7665be8c74aa220a06501f9083", + "0x0c3a8b9b2a55506ab3137617f125e257aa601860e27a9d7049226bafb9bd6b33", + "0x8be182384dc155b192a8928bef77d86b0a60e63f69da382e9794b09155bc8e3c", + "0x78b8a964e04bde13683e72823dd1e64e307261f5021afecdc7e1f9b916f03f8d", + "0x43615d9122bb93b75e8290f804474c97b0f5a686ed355c1d52da648f62cd69f8", + "0xe9f249d598e4116f60f4877852befb7ebcac917989e53e5011fb7c9d3ffc3484", + "0xfe609c90c0e7610c8385ac75fd29bf9fca9197b8cdaeda35369e3b9b085c107d", + "0xbf8a61038614c0b5d9025bf0410813cc35184da0655f9992a6311db95dfae70b", + "0x5e8bc7ad504c8d9864459d856cc7a98aeffa580ab4659e843434edfaaf7340bb", + "0x37946c5bb0e830d36bc64990f2b56715e54b7f25a3904a2fb46e675b03ccda09", + "0x32465be451f0b5a62758b4e82e385c3855012cc4a9a4ed7e8c7d91267b66abaf", + "0x60c610c0139d0ae30d0cb4ebc13139a54f860c1e3ff776aaa9ace510542583d0", + "0xfe00701b294770ceacb95b84a9f128a4dd4931feb5b805b8dadce6a114c437c5", + "0x1d3341160a596a49ef928ba0d92653ef3064be23e3e9001aba3fca4c86116c91", + "0xd19139beba11b1240189936fc0dd43d9de80b8c7886cccb53ffb9e386315bcc0", + "0xd7bf50e2417c9488b76318b036e4faecb5fc753a2a2d1e2effe32f100683bec2", + "0x135bbacbb12aab84285a6c0c92b7a46f5401ac41674a89acc898f9aad43c7e9e", + "0xf2a68594b515701cccfd0a85414a2c7f4c2031cb9d17b94684545400cbeaaf54", + "0x4963c5c48a902fb9030128c7b3948d1946f769bd528124c86430211b915be872", + "0x05a29dfdba9be9b9e81168208528d52255a9c16f29de900bff9e7a622d985c6e", + "0x7e185f628ab0814a21600480e074929470e9bc058ce8bb616fcd9ee239455ec3", + "0x0a85bf4f5bfa1c4df5c68f906f784aa2fb77316b8782ca91664bb2579c5f1af9", + "0xe91752b9b0b8b9090bf01926629060d1aa678cf26ba5ca58fe8b2abc4b3e5f02", + "0x493116d179262108e5f2bd8ab8f5d1f9f9137da91eb180dac10435013d998860", + "0xb22f5ec904569faa841ba1d4ffcd06ae99662e240abe01baf3ec78af66d243aa", + "0x500e1459abe1353c17ab8fd60d9a07b8fdebbf784b0d3d1674eaf428cdd404cb", + "0x9f40a57ed40a9e16b5386132c7239c3f897e9884a6992e775e33b64d80aa2bf2", + "0x9f1e63339d7c3ed837f91d9f94e5473b7256c3c93ad31f27587db9631ce8c103", + "0x8fd9bd934cc963d472222cb2256729df01456ae0a54cd9d5ae450ab52d8f29b5", + "0x43b0a48c5f740abf926c35130fa63d716111d6754f32bc4165e03027664c8993", + "0x37b641188d378946eaa619ced8c47b5b9fbab0cf5562ab0fd29b4d727b99e086", + "0x2bb5af29cbdbf86345ab8b2f81b554863ec3654566a92f8c4d3fe6968a0f5587", + "0x0fa6d4171d891edc4af066ad3284c72cfdba559f59ab323170fc47cb2ba48355", + "0xc21a9569a742027739aa3e64d00500adf1e060842e4828b01761bd3bbf8a7373", + "0x8f94d930d5ce64bc955da63852c9aef4d3cce61f2955f2c999088274f026edee", + "0x8b5adb43d3e38b2ef24e08b7432635fde99663b301a31a734210a747b97c5b8c", + "0x864609f27fcd2fdde74f481276bc90be8d44ff7be6a729c54fbd30d2a84d2be8", + "0x652d92e897c9522608d5edce1cea42a67d5d21847e181378fdda2d12c02bd6ff", + "0x27b1e29c669db25b873bd875515a38f664f3918c61df1f80de8e5f7cae5e0cc5", + "0xf1ca15385d888342ee1bbb537f73f3c5d6b656921c68012689c4e2e5e6f15652", + "0xfcc260744360ba3a50c54e6c42289303da53055138e0fe8a9792d95d6292bc9a", + "0x476dc656f68d607dc317b666a851c4bcfd0610fc1fe52a7a98e58500a864c047", + "0x3f71c16e97213026f29fd0906b601bc5940a1e2f55281b29b51352f21b015e37", + "0x745f66328b5b7a0162bc784c8629d3c298885090e39c8131ad25a243091b2aae", + "0x6671a19a5f0552c18300b8ea3c6dd8f95d53e3545f001e02f73a709219424e83", + "0x05314fefd3e6a12514ea481cd7a59a8bf4934548a81951d8484a09590922011d", + "0x3238977b312b111d17199c50e5a2c7d3b6604a34a1f1ece6349a163b6af41667", + "0xe662941bff39b1baa8cf68dd2458364aca710fe37a7c89fc2ec808a078f11529", + "0x07e096841a046d5d268f8e041fca0168a68f982315f72caae3c112da88fac956", + "0x17277d431929a943061f8a2729c80085ff69c6f07d2ec3eccb31959bff6a7a0b", + "0xc245f7228abdc38d701ef9e686b00fee98803005a805a8a04e7074994bf6ce3e", + "0xcb19461a4779d5050bef49874d8ca18ecd6f9088a745ad59b042121698f63b41", + "0xe1a0fcf0a0a20eef8c012e90f8a629fe6cbd4ac9b1d274dc78b283e04842a067", + "0xc3f4b4f46f3a5adcbe7013f6a035c2af914524c246508871c855dfd3b80d5b49", + "0x7311b67ec489780a18b5a51978654eda95c097ff900182625f5b84efe7d2361d", + "0x266a64ad90e33d57428db7847fa5d454bd590d07255898de4f44dbe46aa51edc", + "0x3a8366104598fd877785ecd7de52b570cbdf9c02d1c8a4e94bc606be6666570c", + "0x646729b9608e9d09ddca2596380adfd5e1dec9a10f1c557c9b4dc8e57f07d736", + "0x6fbaf0eed64fea777ecfe2469e094d64b271bf30104d3d64f29251510c5c1b02", + "0xfd36fc641de4e2aa4f82ee60b113004b15ce0119e3b750d8c9e41194536daa77", + "0x98f3f36bf7e1280e9fe4c3c9a45c5beea156ea8966ca4e0bc3ffb8aa6142c9b7", + "0xe86a467cc1d5cb648bfbb882bb43978ba58c4419747a150ccf1dc68c26a59908", + "0xedbdd250e9ab2bfd19c275e316a77645be16b85abf97095b44db85272df25476", + "0x1fab20311869b9f754410f6fd51c45aeda366a166287bb148e6bdce4e6ed9d1c", + "0x920479c66644323848f5ecec51adbcd0809d124a29871c970d0ae063d490386b", + "0x4acf98ab749b52208936537191e9f07c1060f22746e0a14cf322a36e48cd030f", + "0x854f600266d1b107bda38aa99fc714eae91ff8bd479aeca6aaa56a47f9553454", + "0x58bf4b92b85dd6b0299e421996806080ad336143a3c22184a0fb724f8a64578e", + "0x7f336298a92d4614b2a371dee6f0b38127f443e99f28a86f5ebc55619e0bbb2d", + "0x1d469e4db9a42399096ba514c36076248a4bbde0700606ce48f348c35f89cd42", + "0x389b697a336b5afd1ee8a8b81a7dadc677dbb0667c49a18b04a755c47a9a2852", + "0xcef5f706d07c9dc8bdbfa844ce53476f4a0bee27b8a681e9544ca64265b5317c", + "0xd4a6f33b656fb48ebaede2da28c1e1eeb066c037ce799e4f3ecf38a05231bd57", + "0x9cb632981f181af187932c9568d61770ad763383c44b80b5244a908060d58164", + "0xa0aadbd5d3739178d9a31afae481a68f0795af439de68cb7077e1143a1490cb4", + "0xcdcdd40fd3fea655af78585a2ea3600e923a12eb41d9811cd272c58ab80c9a01", + "0xf7b6178049a83ee7047287b59d736eccd3bdd2bfeed2b807db158edd32053e92", + "0x00ec08c4e503267a722e99754691f25e56a7f38bffdaac81073207ac6e4628c3", + "0x36c63e764e0b2c716dad7e5296b687b26d5633b1abf3855dba686e1221f5213c", + "0x378d758bc2c4d7e66a6771cf234540df04c857a05a193625b8abd5bffcb0b599", + "0xba02fd4532cdbeff5795048da23201e9c0aecc0571d17b5c647aaa982eec1010", + "0xf1c38557ed2a9789b9f1f0a56c2d79cde7947e6cf7a5f62e7c00a4249d7de0c8", + "0xe9a4900f057378ea046b508b47ce110625df3d66a45d6a62ee3a643d47fcd348", + "0x22d997628bfaf1600bb670b83e8153aa718d4af7ea7e34339906217e78f20dbe", + "0xb176bd52ce070e88a976ec85f42140783000779527e09311bdc87174e5e17eed", + "0x34fa71a1e17a0630214dca4f0231a80541ad626cea1c80d5593c8159c0f015ea", + "0x8407da1481ba95b877849bec5414bde923ffbb9924e147e921ad27809edbc9f8", + "0xc577b64e7c560bac86ac9b610725bb06f3be5100708d37e720b3aeb11fc1d6e5", + "0xc554fe14f94f2673867bb459bbdd0466b084f371ac30520253d5b55ce70653a8", + "0x2ec7f94ec6390aaaad7cba4cd918dbf9ccaa29d67fe6944c230e9d1c7c6b82e8", + "0xa0bc133d47695a4c981fee01ccaf68268bba85df44de36a8ba620956a717de0b", + "0xdfead9f6428baf51049dc1d5b94524cd3656e6c346ad913f7f2f007abe210d69", + "0x3c1d2de76e16ba07da80358d43cff0a3a22e7f65ffc89a9a5a44240a1b64ff65", + "0xe2726966f8486e5db83b69d7a1e8494df1f8f335d2a033c8aad1b2caa69bcca3", + "0xd65e3e8646c2476673b77d29b620088ded4c41862324c15abdac23817e87b4d0", + "0x279ca5aa88cd91035f539b6d4c38b738e1308320af2f9b25e0f66ea79054ebd7", + "0x52698876caad91a7298dea1ab30df842f3b6c05b9aebeb938c4ada41d6db36ae", + "0x7528add2e38a618277ec28c6b15b7a6ef83cb9c7d3eeaef8679be4755fc0af30", + "0x2a28297a3666c0808994a4bbcdf4f159d751f2835c38d34ab4345027621c4d9a", + "0xdde1ebb4484f6d5aab69bde440708a82439cc9f60462b4fdabe3cf6414121937", + "0xed17938d00891b3d4fe43e595a8f55c961870c5e0b6b908fffd4cd79b9afae59", + "0xfef1fbb5ae6d55ab9e25d6b59f865de6497087f420401dac19e6d88262b1a831", + "0x2622e9a96fa50cdd579ae52d3f2bb8ea5dc63f794f29c3ddf7838513164f5828", + "0x3b04e8867acc99fa7bc69818bf7ab5a8121a521d712d1c6714478d10cbd4b6b3", + "0x1aec94262a9c608426b62f0f52ed5c304c9f92eb76a038c1e38edced9107ce77", + "0x61153700aae4e92055868c0ed64a5bfafd442091c38798a88e9e7a02e7b99b82", + "0x0f155a1d9a511c943d8c2384ecc942b202bd3642e563e02d16c4bdcdd6289087", + "0xd778930dddd29368e7c9b0bf21373844251d3411ecff171e2f89778eae37dfce", + "0x698b57b3185831bd9f6c9dc4d65cbace73a509fe5b8a38b2995b5c7ff56d976a", + "0x594c78abced577f69004f0c8a9e6f2cac7436348dbf375e0bf858b655571bfe8", + "0x15ccff546e33f2208b77d0a62c17192fa37215b1917c76bf280e4fa90c1500dc", + "0x758075d50d81520e35db4f03130820ef5295f2adeef6362b31c2d32555fbd6b4", + "0xbccc0e141a718dabd09ba7a1f8175c5fa82295cabae57be1e02f2271f6007fd7", + "0x7f448eb7d4f4f6bbc941786caad99a432de58946d91baf976c41d6f3b5caa625", + "0x992d12c307a69e58735aaaf91b572fcc15be5c002ba3c87c9b6f1f1c902d4873", + "0x515e56a235ec5369fb07722468d87876ada36331ad4df8c38e5d1602bb1c451b", + "0x13a69de5a59eec58789d1703a97627e7d2c4ce49d766ec932b5b784e3b408790", + "0xd5c2413464f7d3f06176961e2966914a402c2398107a58f848055705b7c2305a", + "0x9f4254624049c22542afc681c2499a931ad2f8fa8b7d332b4f4c70ac429c9904", + "0xc64587d31e47b70ce57f76de0a83894f53738ce5773a135f27afb925eafbecd4", + "0x6ba80130f7731c2babf72ce150d40d6ee54f6d08fbacac5f8828baa8cbd73ded", + "0x5b1116f6c1fd87cc28e3cac852b37f3ea2478b0fcb247c522465e0ba0499b511", + "0xcef5a665073b606a037d571a0e31c98d47d2c3ff6c99fedba0e1142b5044673a", + "0x8cb38f1d0ff05d4f396c091a6db32e3308cbdaedf5e0adf02df113daecbc89cd", + "0x334e8bcfbfb3a226fcebd45c36fce392fedde019ebc79a5391b1621e31a1ad26", + "0x35e2a0dd01aa4606b34efda7b92946c85eeb7f11af4f718dbcc3e21c4ff4f369", + "0xe8b7209843a7c8398835c726be4bf7dbdabe341aed431e6eecc8b3c3f0662233", + "0xbe6bf775c6670ada38071a3b94cd41adb8c5eddd32fa4b2bf205c794a11eb240", + "0xd99d2b1247e9128d8c83e49517bb773946f7324a0b57fc407215f833976df38a", + "0xe4188c5dfff81e2e5826301a1bf46987e3bcecb78d4f0e4a34238c73841f5b89", + "0xb02b8dbe81d332a87bfa91a34e56bb4429377f0d1b381ad33a42891e04f938c5", + "0xe213689182dca2d1aadc8e2f0f3828480eca216b49d11ca170b36517ea42f903", + "0xdd8bd319f25b64dfb0635af9eac3251e8f4eaa55843b9811136b701db2d6ad62", + "0x38e60f804f315e42c25491318ac465f2e05cd6308f4260a5d7a2bdb356f73848", + "0xb6339033749888025e577837d292fb072135cf31b43ab221bb8cde651ea9cbaa", + "0x960f881773a84eab99783da40001d1068fc8abaa5a05260e53b6c650bc88c297", + "0x526791aadfd3f566592c5de0f2760a3ebb5a7ab7e371471318373e81ac56c7f9", + "0x51fb7e4fae3aed14f6d3ce85394a16f85c876944ad047f7bfcd304324252953e", + "0x452db3280b077f39732440f17397e41a1188359b1f6219f2ce06ba58414f5f80", + "0x6ef70f95722b84942ef7bab00e6ac12284176d47b58de7f39d5e2ef2f4e127a1", + "0xabeedc9046a3dd0066fb74e166357b3bda6a02af862befcedda8bea34d6a4286", + "0xe92d61afc9c802878587754f8339a9d068e0551ecc54a68b7da516c2c1e234f3", + "0xe4f52b127585e7b7e7fd4b51802168da046399c3c1b1951813f3b8cf75d98565", + "0x771ff229260f3616e98c07852ca404dfa5cf086667f5768e4d1b7d3da70382ae", + "0x6be296b7e68e90ed51b8802a6791fdaf79bb00b69862fed85017e4c951294517", + "0x1ae46ee152037ab4a5d2a6cea1287538cfd6e6ef295ff5d823f570100625ee2e", + "0x1eb96c6545421f53bafa4d322662ca7cf901763deae9094c635d68031bc11082", + "0x2c48f3d4c51e4964c2e2b32000335e9462cee07eb12eb4211ad807a98d99bd09", + "0x02ba6a5c5c3d8a377c0c70c705891d063e1ebd160b4a58afea541cd83746bdff", + "0x80e88e57806b585f15270a5b7351b80deed4fbf627b3692c2dfeb570168fba48", + "0xb3e0c2987eb82730156d13d85cfc2fdefbe89f8aac9a5c99834a114657c60cb3", + "0xb2bdeb7e0bcef1fb1f873fe58bfeb683aaa84f8e2928b1a954b6f9a18228f469", + "0xcc42798315b8b3601a096d3018916587cd1af65fa440f8467fe878833a4b4535", + "0x01644ea5caaad64462079788c65460559579c72f53403da2e6e0fa59a539fc3e", + "0x04504999fd035f85cb4288611e7999c0628d39352507210e7ed8f6701ddbd41c", + "0xd3428cdb461c185891b80203d2be8234695e734bc6a2f7b4ee2a6cd1f96ed0db", + "0x4e7acb6fbd094acd1b46feac2f71ef49498c28203d16f01380d586b9286cc0ce", + "0xc9f1642cb0bb3c82f17cc7e5cbeca23c8e1659e4e7263b2febf2c62183d57361", + "0xbea7a9e356e102699698700aeee94ef74b3f7d36cf6d4d9ba0c24ef4ef07aa62", + "0xd7c63ef0a707ba6353e4c4d9175e67d40c8da077b6ccb00d95d02f59725b3de5", + "0x245b9b7f392b274f2e3344212786dd0156779a9ea2d05ef7c9692e2b40577db3", + "0xc6a56e275e68cec58b3c1080567b462a450b005adf282b6509b95b5843b7337c", + "0x77a43c584138e6b0d6e8237ae94b1cc54c1fee971b7dd0fdf452df1c4f2f9e6c", + "0x71620ccc770484a4d94391f4576948f8387868a0caa02ac203f3d26f98d39c8b", + "0x04b8cbd032731ea3ab05a3e988ae19ad13890c1b33d94009bc0ab5b738e08750", + "0xecf8a793b02156c072108c9284b43b8962754b845e554bd34e462fbd939fd4bf", + "0xe05e494c3f435889d57889c9ffb27f52abf1fb3e2f3e9595a3629a5cae7bde2e", + "0x89cad0f99f7e885fd2be1dbc2baa2ba44f59f8d2b5032c5c6588224b24d3bceb", + "0xb4e5e8775f08dbe1c651a086a2abf5393d29c376246697f3ccd477e17cf4eb26", + "0xc2ff46e6d28623afe6cb72e01d68c1a2fbe119d331d36f531ccd59d0a2415841", + "0x4404d717cc2969a48216b3257e228a43af44cb1fece5f6fb60cc92e3e9790d32", + "0x8172ac62c85adfcb921bec5fabded6ff6173d76362fdc2ccfe398f6f8739c3cf", + "0x5c58679605e40ca9ef3a45230c260c0634a10131b400aeea36d81db52dcdc084", + "0xf63ea400aece542890acd1b7bbe4380dbd9cf4051e806a338f776034a4327224", + "0xa683df1979d8ef514c7bc47579baf47131f505b8bff782e249b17509241c44be", + "0x3f3bc3e4eb3c7ca681072135e6866f60d22cdd8bc977e3fe082d16e82b436504", + "0x5f19e9bd19aa70ce66e24d8d9a0bd850893c7610efeb327a099bed66243b1558", + "0x1a45c3df1d7e41242818c13bd46fa4b983e2c93ad6949de50511766546e66f56", + "0x5ea16f78adc0e76c7754aba5a16c01469686138500f506c2ac87eb76ffa5870f", + "0x98580897f3673f2049985e18ad4de87766babea4d9f283a90636cba68d91edc3", + "0x3b4be7dd666eeff82c1805249d38984b97a24c837d3a32982b2edae6f93a247a", + "0xaa10090dd234513a96449a3f57865f705f2eb3cfb497b6a4fbb7980515826333", + "0x41243c386626e567bf4bd13e9b535d04c25b60fd6ea06832c3714e1b09b72c9a", + "0xc53924dc891d326fa20e9ebeeebffc50186739d3e739497f4cc702851735f90b", + "0x57289b8fcff029187aa94093898a18dabd23f44a469137fc458b1313611c4d33", + "0xcfae90a482e8e4f04cf857b762d6a6d67b68d7a3145c694b105483231b42a4ed", + "0xd51d7d307b0f551b7aaf96733162df636accd4f7e6a898874366a13a6eeb4ee9", + "0x1b2f947628a7f57054c67a61edab39ca6006e00c38b2c0c8abdbfdf22cab11e3", + "0x64ecf1eb993b8945d1f1f6c31968db5e9fb7eaffbbf3bc7c8898a63584b36d45", + "0x758fdc5571cba7568617025ba48465b4ab6d2823e8ac783af879e4084fd074de", + "0x38e27e8e92c318c0cfcd702459b8bf6ecc45f0664fc79796586256d3c67a2631", + "0x2dba67ecde6ee02b4e65971a978b6b798fcd1f259865075a2fad48e7ee4014f4", + "0xc70e3f266d73063a650c7d69c76a3e4b5491de140ba3466b7ef83d9d7eb6efd4", + "0x85217e058be57ad636e932158903406718b65bce775099747c7e53b29dd9cc36", + "0xf0336555c94c7b0f3248e61915675bf245840fd9bcc0c67f7553ed970dfe3b45", + "0x3760374ce6fa17e3e905cd4205b5594ec5d0d51fdf6d561c3338a991e41a543e", + "0x0aac6785b8b06ad5526040ba3c507dc2345c625bfd37a7261aaba553e00531bd", + "0x9f2b6270ab9a96995e717abaeb4302e08d52d120630b9b0d183ab78ea801de88", + "0x57281142aa4105bb8bdcd64704b458cb1d958302620398d69c25692488fb8f0d", + "0xe2912ede8a4931826d49246e82dd7489b7b8d788e182c827bb15606078989ddb", + "0x3c97732893d4ccb6edd7fdacb53ee31e7ce6322496e8460f81d6cf20a1869bae", + "0x78709d21965ab467af975695765e65a7861b46a4cfcb53bf5fa09bc7086197c8", + "0x8faa5a99e93cfbb1116f2e801a6673269f3fe05ab16d56788a3d5b1f1961628b", + "0x261beb6b4dba770b7a71044472e006811032e381a16ffc307835aa8286bdbda8", + "0xeb5464fb9e85ddd2161c8514d00db49bdfa0c1354b2bafb785ddd196deaf0b4d", + "0xd5762744d5f61a9cd22a8fd1dde29a88ecc36946c7a8035029358dc6d32890a9", + "0xa5a2cc780f6b52fb9240f4c3fec582b6f53536b7d0ba9aca789e836a7a401388", + "0x4fd0b051883875f2cabe8908a9f1169372ff3c8fe06c0e7009326d2af93466d1", + "0x2bd4722eb0795f86ea68a3abdd99b294ab27abdd830a2a5b73478ca49ef40d9e", + "0x4a7440bfc7aaa088ee7bc2678c380caef78de109342355823770c0f2ea28c9fb", + "0x885299db07b230685ca78cbb1371a225ddc9abd9600f246db29331ebbdf6548e", + "0x1b94bd3c94dff0f065663a313494cb84bcc8fbce60f21e86111c5c7b2477e10e", + "0x3ba0cbde60ed4843b0e78869ab375455b55bcc65f5dcbc0181c1763460913eef", + "0xeb877650de25f9fe38c277b345aa1ed9c1f3a352f1c666a65cc720fae4528733", + "0x090db64c50b5b3fa639e2436bba69b494b2d1ce4589b14187f3b6d459d199d52", + "0xca6a0a8422ca450d9263ad625c96c079e719386c292900c20c0bda4f4ef59df2", + "0x2c5afa151c56ab21aac1349d783eb80c8893a4042a16f7716d15739c563298fa", + "0xd53c8c842191b7ce23a77abd042f70764097102217158c03d316d005a38c9d21", + "0xf09ad9d85fc73aebe24e678721d0e2afe6dfb7dd84eeffc3f37d8445c638cfec", + "0x0107a21f2ab7dd4a45fb2d8bea40f08d755316f3dc684328b1ceb3634f310cc5", + "0xfcb36ff40e2f3588c97a20ed59117ccaba17ca8ff6a500de48cf513563be034f", + "0xda00badfdea8756139fb7172848bfa82a6c51fe72a2147af2bad565564d1407f", + "0x805799da2bd5407a07a6c5bbaaea08b7e6f174cda035a1ac37ff088ae7f26df9", + "0x89fefce5157d9f7f3c302fa60fe98ed1be23c5e4eb165d2cd6b24db6c1565b07", + "0xb7b612d5f167043002100dd42509c9e1e5c0e8f9ec64fdc2a4adef10124130e0", + "0xa260ec18dcc7c1a77aad5618e791d5ce0347826b1bfb04a8670aa64be844d3fc", + "0xec245f9c2a2303d1b6381da92bda44b0a9d52c4e92a8266b30061ba1e226d5aa", + "0x2064c86c3fc530fce89244cc5beea1b82e2b363e0d76737b5a30e40b61098e90", + "0xc47747604007e3a2425d6aa3f7ee8859b38211b2cee4ec86f60d321ed3f4e1f0", + "0x5f8e4701b78feef13f7f41a4ec83ecc83b7c29b07cb13dc78bb867eda37d03df", + "0x2b59d24a432df9436ec28d9d78cacdce122d491590365454a35c93a9aee16b4c", + "0x3e9fda960307bc09c0293dc385026b765f69aab1f2be73b364d3bb02e18bf3f8", + "0x258f4dbecc52a5f9ddd23bcbddc0bfd050470d7f4c4196c592bcc1d998946ad8", + "0x8b95a6f4f151545ea4189b373aacd24a2d2794114c08f7a5125277a9cb584963", + "0xd6c61b899f4b90bd0a3e2cb481ae97e229cb6d543c12413650cbaf95dd87c252", + "0x61d2e035a73ca7f099959adf403148a875b1e23e1563b934c64225645d50f646", + "0xd3b017fe4fcf7d0174aae3dc92254cc9b3964b554b7795e69ef3deb7899f826d", + "0xfee4ab89869482dc3ed86307a60b0b0e9383f23ffe241a39c08c20166c85db60", + "0x9d36f01e75488004652e1291e023d9ccd5869fc408fbd2c61f90ac1224a75e13", + "0x4fe24f829842912e575b03ecc6afc916c11ca63baac9401b60e8c9968f0070d6", + "0xe55ea4978e7c480c12888ca100614fee33f3012933055700e26cae03562b0df9", + "0xc68a514bce803dcf946d1bacf75a257a3121e7a09a2ff633f57fe6868a2b9a08", + "0x2d68f9bde62a6ad5d4f7993242d7b4d9920573eb98dbe79e1c5851e72b6cb2d4", + "0xcf4b633b84b22cfd1234f649605afc9f5c4da82f650c94333db18a312b00e648", + "0x507a5399cecd103d71e4e42934a4e90fc50bb79bee30cc743078260fb4f763a7", + "0x54552edeb27cc6ef8441bb66ae8476531fafeebe180749ac91dccf42ca631ae5", + "0xa21bdd2e1f10dfa6fd33c84e897139db0a6919290abe0b5c8963fc71e94070aa", + "0x8ce09f4862add5238e7ba89f2a676009cc77fb2c257dc5a4cc451b8d0ccaeb09", + "0x3197b1d3d549b6d2205ac5eb9bc2b439172bafc7e531d46488c9cbc2ccd2a59d", + "0xec5f915e5c4a86964b88b0a7d6b8f51360f32aa24118d8383ca80abfdbcd23d6", + "0x138aaa559941e82c9be308097acca3211216bb71e299b040f73789b4d5d8fa89", + "0x2d80cbb50b95d826b14f8fa88aca1ae97ef98637b15e85a27b9354bfa0339386", + "0x238ea6e8a90e16acd115add908bd82a68f72f58db91f1588787978b60d97ae8d", + "0x1c81a2c2eced2c2091f6e7fbecb9edffda884212f8b7f63546fed0cab58f052d", + "0x422e6ecfd5199a34854a5c4e3e7a6e683e12b0d5afb6c38335959127d52ec416", + "0x9508ce0537dd36e052f2496e482bc27d3c778939036cbb487e8a1b0b9613b33d", + "0x415a8c65dca7b324bc078d1803e6e2e438d2a35e6618bda7b9b355d1ad0dc310", + "0x387b0ed50dbd8ae5d002cc8ad3dd169de1e8cf3a7296022d370fc35dc09e77a7", + "0x815629ec3ec4880696691d4639e364ab20672e8f184342c1b510eca44ef7098f", + "0xe98ec36db6c3c3e8d9f72ac9c9f3487b8b8c756c7c61a30c81b8d76766438e8a", + "0xf66652f12d2f951e3a342968b610b4f5c30c203e3b38403236a179c7162bd2f5", + "0x27f1be485c87bf3c03857de67a804cfcc7b69525a1306ef46073479682e46f21", + "0x206626fef3d298428a6211b11c8afb950800660061432c5669dc92799d34ca5c", + "0x825ce7ca0fc8508eca577400907c1f4b1a7c22a09bdf1e5074fa3dcfe792bdef", + "0xc9720c0597df60b39ab3cd1a33e7971cbe5b9a2fbe4707e5c2f804a36de32127", + "0x025bf431c95c89d43df112de4f6de44d95cb336ad220f917a34b12e31e6d2c09", + "0x3050bc1691573f84c33014728323ab96f7e2b0cd800c5b632f161dc823730a15", + "0xcacaf6c3f589afc76962b716d755a1e6a1bcc98e7da024abd8b1df7854467096", + "0xbe108ee74b95e55d5a963974a78127565b79247ebd8d6db3e877b85613efecf4", + "0xedf5b0b7d3c569f62d0713d85be6fcd9816d726c368d0daf895d75791665b548", + "0xae1033b3d7108176c582e06784d4739658481a760344629ad7b9d25e5ed2f26a", + "0x2f5a2225c5a6eb04a29d3168f2c218c667180171804f0d24adf5581f5f7c63b8", + "0x16194493e68c59024edc9d6f49a857f699c21533b97266e7acf96f9aa7a4fe8c", + "0xc0384e45cbb0dbe255c99ec94c6c9148e8bcb5bf541113e9f6ac5f6bc21b5edc", + "0xbee0ec2f74e0b58021c17077f6a556b3d298d05f977d6ecc455e1afd29e0a12c", + "0xd03da23517152d777e2e1f0df2d01784f95d7c8be914d61649c8bff2038e03e5", + "0x47ea97f13896e6577d565607cfa66fd3a39a03bba7fd4d05a0c145d128f15e06", + "0xe35c78236d985c969edc791987759f7db951fd629ccad10cd3258650797d8788", + "0x8980c90090a8e837ec7d1e6610de2d2c2878f298cc6786f8dfd77acd00acbfc8", + "0xf87afe338f1716f2f6820550d5291725fc2068831846de4de6c9265b960d6af0", + "0x14aed16b17d49e30334d323890372cda1cf2b2afe4ba2ad2d2a76d3eab5210f9", + "0x99edd5beb20c57ea29d9df1fd5e501db14e8a6138b63eb56205e8836513ff8c4", + "0xba494680e041158764b5b51be463aae0de25110b3489aba0ceb2a79facd87c85", + "0x7215107d34354a2450a120060f8fa63528359a8f25b31e5a4c6ea8e9f5b6aab9", + "0x847ba946d684dafd48e43eddab89f7119577f1a5c85026715101937300c6969e", + "0xd1ca4ef6fe834cd1441e0d263af2954a1a35f6d0501198129146fef43f939d63", + "0x2f038cd0dd998fe4636c385c7bb11565fc5071a2b63a7dd636d04e138f2ffcbe", + "0x75f37318f63e7ccd605b7655f41687ec82b4fcd3e430dbca5dfb11c93f3c9b8a", + "0xb0a6b542382c203ace929e8b986c85b5852841bd38342004aedbd57ea5bcf8cf", + "0x11cde52a02f554152398e5e1ad38d1b4ec136b7c3b176aba31fba71a83ffa747", + "0x1464c97939c8cd63669bb18ef34717e8c13316feb29fe46b92961a753a5b5ad3", + "0x361e18a44a64645b3b2843bf486ba4cccc1a8cea0a2634af015383c7e88bede1", + "0x84c2d9e35e56cd343798c7ab115fe139a0e444f92c2ef3b1a4a838a15d9e7d26", + "0x788d136625ef52a141bd3068c6553bc4b479dcb8f1b58d22a94c0d47438ed142", + "0xca476adde147cc1b22a2a0068ae0ec7f89f5191f34ad88662f76a8c1f9e6decb", + "0xc026acb6479b1ad087ee371217c30b68d929f5db93bdc723dddd79d5fd01db18", + "0xa342b28e047218018ce169f7f3174956e91db22320386b72ff6bedefa95e787a", + "0x2c15c73e9e2eb9eccccc9e8aff56d2a3e52897e2bbddda378805a67663c6b946", + "0xe50756dede56b44df8cf7fd4eec7574189cdc2572d4ea7d9d3c269c3e044b69f", + "0x1bea2bf6990a1852d98e724f3ba4fd4a9b8392bc215b37de6dcbb77dc8ce09d1", + "0xb8123bcd4b923a876180af799154057760b2a6b27a3e945c44d8939be098c27c", + "0x5527f1ef2b1126db567e9a104d61f3d9ce2a18b6244864b31a68c0c0d8db6df4", + "0x080399798dd5c7ce8cd83c3bc43fac2bc6dad332f1987b3f50f07cfd6b4f129c", + "0x55d9a2eb729b17f7a11d7814573c766df09057b1e39f395062a0f1c22eb5dcea", + "0x42271ab5acdf64ab63521844f0198bd8fa78cc1e98d8400a62f70d6cb601b6b3", + "0x62cc069e816e05401ad9e540986e2795aeceb990e79f0a648b04db6ac0379f56", + "0xd1d78c5ca336d5e2c2e32d7a0c7db9e921963ac668263703624ded149b53f91b", + "0xb257af84b59eec6ec497473f9d349e5d657400832994985b7d232501aa7dd8cf", + "0xa140e021549016816878ca28e7e680fac57de6275740dc4ce211f5c107092fbe", + "0x3068a537cf41408e0077c40b808c456556d922f520468311ce53a95e0c805aec", + "0x8e793dfc8d08298e71a49fdea42fdf6d7ae2b4bb2572a63e0c946687b138da26", + "0x4f058deab404ea73cd6a2fb0cddd584746183ce007026f4d9acc8db9d36b5f57", + "0xe6e01373f35eccedd38cc747ac4aa56648027ba350c1df33e3e9f81482062baf", + "0x767097e68414967f6f50bbbac37ae4143ce4ae3af945c194602eac2df56d5071", + "0x7333b8e970b91e6d9b78efc8a84497378a91b99a82449aaf6407563dbdac0620", + "0xd6b08b413721e69c2b1b47a274346699080b3714e47089914962157b0d4abb78", + "0x59b688e2431b369561c1333e9611bd72af5868e3280e75a319de617341217bb4", + "0xa384d2ae7117da29614028b92126320eab4cf622a82e47db75b1d3ed611c059f", + "0x782023bd495be817c6ac1eb4cd60391706309f24d2302b6ad0c920c667472af2", + "0x5649c8b08a013100ae311014a8a4c45f6917438d6f90453b4669b29080eb4091", + "0x5744c117fbb36d4e81f9005338fc6da3a4af4bde621fa2bddb9e65e3ca189b93", + "0x37c7e5e54879b0180b41b90a06997adf23e7a1899c4ea1636f75e101f713d34b", + "0x416aebab5373ff873ae30b3dcfd0af37ca7d961d75cb53ee63278f7206914752", + "0x974d8d9bbbf68973d2e23a526fbff11791a260904ae8cdf8cad5a4c66d755c6e", + "0x85f06eacc9b57ab19476441917d48e4e37c84e5cb1d26544bdb8077216fe99c4", + "0x35c3b0bfadb7effca376dfe13040808f547f71abb18e9bef1bc9a302ea669f22", + "0xdf1ce7fb121ebe5495b9ed234942d5a261b63b25cc7fad2b2da4c6d8b9ac3269", + "0x9ac9fbf57b71dbda20a5994504da13f6068e7f0de69e43c188044cac1f485add", + "0x6b5162e37a2c84b1d7ddff1e527b00244a521158e76fdc120af9d7776c01c6b6", + "0xb6dce7cf3de93904c2e391fdb0de459ed159a3aff7d922b95704f1320b4fbfac", + "0x4c841cdc034604bda8c25faa32804e91c474083c28ad8d4fbab62dfe067bd611", + "0x2105bbce9445d3a08225b0cacef105e6d67fabc7905055b136942bde232be48b", + "0xf01b7a74df8e232e88c22e1d4306b7fddb6f5c55f8162fe542a0c898e2ad2cc5", + "0x0a3761abd2d40a9096b9d6962f47fd9e0e5b364463164c5326525dfc253e9a19", + "0x1e18a351827366db988a6ccbbfa24ca8a8cef51fba1f3fd17faab53abd2d0837", + "0x01637d0991cbfdb68ac4ee262cbdcc069b15d4c53bb8f64bb6859ed66b13caa0", + "0xaefa71d07ae4cb68d488d1969cae479bc983868d286334ce4c21df1ffaca5749", + "0x7f7d0cfbb3906f46695f64497dff8e61004872007cc92dec162e348c0b2f5745", + "0xf8225dc1ec093102acdc5d9cf623b77e150da6739eaff021e6dff1dd969ae93d", + "0x9029620221066f5f6f45b4c1b5b6662030bad888b909240fa186cc1217f51d53", + "0x179db498710d927f1158476c973179bcbe254c80051f02f5b19f9cc944e8aa78", + "0x73cf63cf9a5d32e3bb6ab768f76a6393e972cbd8f532d743247c64c21ce3a8f4", + "0xfa4c456a864bd1aadea034eeeb6cbc0e1f663173da0ef6bd2858ed3698c72e8f", + "0xad870466344058f1a8963c92ba3e38bb7298b51b928ffd0aace0bc59dd85e1db", + "0xf8dd5456b10d62e20c21ef12ce0ebdbabb162511648bda4e76c912d8ec64ff00", + "0xcd7687043d9dffaaf205ee6ca0b05abc2774fd63b4fdde1514f7d7519930a194", + "0x17a3a2ba425f70822d0d48bad5d2b47f03eec5ea681391f705290583e2961eb9", + "0xd5fa1d01404094eb3d01db43e71e196fbd2afbe6d86830b57e388ea07cc82ea5", + "0x666a70247b3c5e17bbbe5533135b22e0c1d82c684ecd2ec4efac5bbb6c9e70c6", + "0x94c19af2b4e61ae4be99d386c1e10cdbc8cb379f95c4e93082a0c46ea90cfbc2", + "0x984168eb809e2778a5e913838092411af54ff5178f7dc32b1a2e954c59ab51f0", + "0x3006503314ec45138c09f083a1e6ca4b2ed42a3b4880741ae7607e3ee47571a6", + "0xbe98637892c23cfbfbf9d09ffcc9d2d5fe26b03144fc0272ee939b9ccc826d77", + "0xa528676ddda0e5b59900adb7365ab29eb723c5af53ce358b3193b4510460a108", + "0xd7868febe82e54e722b7a60da3d9b36f62f482e4da168c0d72cd63ce26290078", + "0xb5f0fb1a4ca4f0f58534908b62d51ae76b7a5ac5b1f71e1d892575eb88ab57a9", + "0xcbc2863c06a35e225c340b7af3d085e264c08f5f26303f67da344ae5e7eaf200", + "0xf8af7586911089d7b09fb83b6400217c08b794defb1c73e8dfae7c4073bd6a0e", + "0x51bd9ac8b88be471bdf1027a309938fce0fe7c5942b735800c9f2740ff92ac82", + "0xf093f94edd7378dadba76bc9c61dda4fb31203d26758e869a36578f9f94cb013", + "0x8a242f33f6236871e79dbe908c0467e19ea1ccba7b9a88265402ebc0b02893d1", + "0xca102479c4cea3e74f60d7f8e288d35b0e7da58831d34e73ab27873eabd29548", + "0xf85cbab28e86f3b12c45da77f889f8d62649c0e4791b1e632cd7c5e68e29287b", + "0x13a8712a17bf0ddef2cfb1bef7d6acd02807dd0e85508bd429fe984b200499b9", + "0x38ab04274ae423668a690eded31d2d01c28679860733701488ffcf03d8ac0079", + "0xce13c8d5ee23cd96b67828d78f2b7317be386f7d1c304859b9349245bb0e4480", + "0xfa6d7e5f5c162da5baf91184693bfd9246317bb64883fcf40f9ed14a8e167f93", + "0x1c385e0b9d4787553c53c827e287e491e56ec51bfe3cfda355dd48301607cc41", + "0x5156e52ffdd2dc1631f8d2f9468591955c53435bf07545c99036c4d3eb4e62c9", + "0x313e473451bf9596efbf5b471c01c39ac9ed27c056a2da7a88c072f9d7e21519", + "0x933b437fa9b65638800905c7afc77cc9fd7b861713ee9481ea840b514c51b379", + "0x89e1f8295be0b435073fdfb479e3f0c65e0d1153075d692c409bad70bc6a1986", + "0x9c647ef7ad7944775d0f6c290afdfe8ed7cd7872b985c6503baff0c671fdc949", + "0x6b35df44215acec6073bec10638186af28a0cab619ecf458adbdd7884da71713", + "0xce118b8b2471240f1a8222006ef4721e717c71a910748146ff1cf1f7181ce52b", + "0x069a7b791a1ae11b5a378c5804ff3cfbd42054b028c7214329a19f4612b1890f", + "0x6fdb58e8dd876920d5b52f5c205172ad5cccd64b813f8a1d4d6988f3039168a3", + "0x1a5eb3c56f63ec02bfd2fa3b5970ac2e345e071933f75890221bab44ec9e01cf", + "0x3d26085ffdb4cf12787437712f749d952081c37b27073c4c4ab32ac5d6a4216e", + "0xd53f2044fcf543598c8e92546018748ef17372235690c055bcb62872c1dd663d", + "0x3ec25802a2b1d1ffff456af29438e62658d74868daf5221664a935cfc023c4a3", + "0xa3908f1f2ad87119e0b0e4de57f36014c95e489b7cd85c14f77be7ffba5e0062", + "0xc832cbe558739f37bb55ba827b4be0c94a5e758c08b0653cd868631540e11b48", + "0xfa525753b5ed3fb2c6d23c06a5b1650506d2e9d2c64e12180071592bf46ba5c0", + "0xaf4d51f3a36164fb70a8b749cc04b6dafc71420eaa8c81b8d87cc5c23c232737", + "0x1bbab743ed5051c58ccea9bb4f375d069e68d6f1e64aa7391a8056521bae37d7", + "0xcc68460b90fa5317de5cacfae34198ab0550c946a1ad347a6093d44358cd4529", + "0xe08b600f3b618075f23316122fb91b4620228aba60abcae7a9ca2838a0948cd7", + "0x39cb4cbc5c3159e0a7db57f7bb6cbdae367dfde871873b7b24ce85858557f1c7", + "0xbd55ad42fc2b9c9828356cab8ab1dcbc143325527a2c5cf293445cd30be7285e", + "0x448e9fa548a626ff72d89a41ee5fc657a01895e1ae774153ca7114fd20b30a23", + "0xf1637ed3b5bcc25421b114cb93e24243e2164ab83aadc09e468db85a93b892e6", + "0xa653f5b6117934dd45ceb6cb5f60ff9b2329b76197ce563583ecf92b1700bd55", + "0xdcb40ebc70c8e339321ac0efb43446d9bc1e23abe4543c6a70af159fa0cdeb12", + "0xac01eda4d9a3a460b9b2a91e014c4692f1fa61dea0e3e1ecd356a083f574758a", + "0x21f62d924f001589bc54e9742031820743dd34c030f7427c29c80acac390f914", + "0x506fa7b5d9f3794f31b6492a1186f5b5c9e11b35d6d323b00e0e2b9154acf671", + "0x40b177d761d63666cdf5324edd84d0eb7adc51b4add9f574825e2fff7cf3c415", + "0xf7b55cd6d82853139310fae41ec2f02bc29d694052c285b1d7c43d359bfdcc90", + "0x6933788ed77b8a2807712132483b3516a6d4078761770af179b00b889b58795c", + "0x35ad326214129fb772f3bfe65a406c982d8bfd7837997185db3070ff7e67981e", + "0x8efdd97d0686a9d5d94223c6226c974683b8c419669a886487a0e7ce00157c0e", + "0xb69da37ad5048a959e6b68e0f7bc5d74d1b72dbb4d2880e89f0afd0c915e4642", + "0xd35bf278fb84f867e9c6caa5c865ed34fe2a4f84784124c09c0a39432f615bc4", + "0x6f732f5be668d976b2d6e98e5740ad77e5844cf40875126e22df8f089a377f62", + "0xca25fffd51e289a6acf2b74e7144d22b693a6b52973def33f7262302bfa6edb4", + "0xe0a587421d8bdd81a20dad53805d13b0796d695ac2aa5dadcfbc2aea5a5f5251", + "0x6bd13da48ad45b4588dbdbaed0e27c3851419b53277ea90485a9fcdbfdf72b43", + "0xa2b539042f0372fa38946e31b10c6a44d84c8f00023f135af6dce99ce7115973", + "0x5fbcb6a22ad3b370a12712041998a289e22f8f463f6814065c25dfe0916d8d41", + "0xcdf495ec88ee4401a15d479b6588fb8f7bf1f679c4aa19aecc29e053a958e553", + "0x0beaf0f84e16d5e85274ace1a9d7cdbc5be60ece2f97e276d542693fa6ccaf5b", + "0xb7bfaf1a09e4178e264a99993f4f781ebd54f03f4f3f55c510e71914cb73fde9", + "0xf910ba1af6f52f7a5d3761024978723a934ffb7a986d1f5d64ae23d1bb834462", + "0xebd190ea0f300bd4fedb94cf3cba6074901d24e9c1bfa00954472973947c24aa", + "0x83b765686d4e26a18c02943816a5dd181917ae54b03a69f05232cc5e08efde49", + "0x197eec2637f5098dacaa7320792d47e6c48c89b2c8fada59c2a59067c55de04a", + "0x8e553e1f9b85a15eff450f383ed25515dd8f3df7e9a861e2a7ddadac6689424d", + "0xf81cd94a29a5652f5ca767ffee316bbaee33633e29b734b51b2d8e14613a6826", + "0xa5293faab0e9a4aea3272edf7d3416e3da62ce643cb7ae0c96cbedfe3908521d", + "0x1b0e9880b547beebb2d73c5fae63802bd41e7a6965e6b321647673e4f481d6f8", + "0xe10c201d9981412d518721fa652ca455145bfd13540067ade8bcf69896748312", + "0x83bc6a457b5a279936e162f50c9340fbb5380dea8bd5a25dc9d31b00772bacda", + "0xe009b1b3633bb3bee1b7ebd90ed664cd74fe627660f73def5fc97bc79928ccf8", + "0x4492d3ed20c2c11c524053e8ad5603c9ca4b9dd1ce1f77459dcbfe1be61ad2d2", + "0x46524e549aec008b4353164bd0a04f9f165628e3082ad0d94465cb31ea372e05", + "0x4949391e06e28ff4239211056f3894bf43c5dbcdf9a6c6059944dc7bd8c72e2f", + "0x1700a04dc9d41deca7947e6d4bda5cfded4dfec7dfb8d533e96be0fadefabdc4", + "0x36383e0ae9994547c270599598386a2d98e54e2e9f55b6ef9ae546f103c9fa34", + "0x58c132e0b783a1982086ef43e961c22002afb30474f0078bbe4bd1b85296da8c", + "0x23bafd7e2d43fbf10f9cc666ff238e8d070313fd70ff4bf9cc52b5884b450835", + "0xf3e4dcb632c51891f7808a36d7018fec35694c50f09a348b225899471a1fd112", + "0xcd985de4dcab5557d52d5c1cb2d983b5e9ee007670b8f8b3f18373402733d490", + "0xc8901b69b6f7982592ec54784d3578f7f77b60366dae809e43a5f8ad412fea4b", + "0x9f6d322fbf34dcbb5e478416609e9bfe03dc8e1202409c873519bf323436d7ee", + "0x5e30e32480e629ab2132ef41477a9b125583a96f9340a505d9962e518ab946fc", + "0xfab5a13f91e72b34cfd2c5369f803c48c806e45bd9041a80ac279af0a78a8642", + "0x2d38cdcd6e11b9b23037b77984ac158e779ba00f801d3278756233fd46ddd7d0", + "0xcdbdde9fba6d5b98aa16451c27f0cecae08059fcc1ef4575d015f37c61c594df", + "0xdf1c8cc64f8d0b06afc5e3af8d173b4572913b2b3b2976436d2b4d976a3c8603", + "0x10e4a86d9e1d132df3beccc22a0bc46939d19f50903cfe915b159069c0344d83", + "0xfde9aa31faa1e099abad666b9dc5fe88080f1ad542e52bf78163400d39d8d79f", + "0xcd701a656f6b2bbbdad489f620d0f04a44043dfb087b1bdf5f59d0bd8ae41e77", + "0x22eacfe31734908ed7778ea5b4208a1d5e86daff45cef6b4a8fce574d8fa1865", + "0xec4df11664534e9a34448714cc99de0e311855c5352665eb86d95c9d24d218f1", + "0x9dc7fb464a340fe6b7295c7b92698d9c7d78c5efec770bbae62938d1a9b9636a", + "0x8d57866bf49124907bcea808c4c30f8908dae3983f6219a0a3aeda3e88ab876d", + "0xff8c85fcfddef646064a277434390aebb0c67ce78834cffff5eb15e05236f671", + "0x79b555572d9bbc169cd6120b2a67f6020ffff0a78d13c2b5ee1d6a52b20f2449", + "0x371ebe36f08963b7e8c23e6efa8bfa98a85e38f4f282f92ed4b1b6dcacb242b5", + "0xbbb3d913eb48e11f0ae7ce2d6551a30688f094ceb9c48862ac7ab45ccc503c17", + "0xa2a5987b11ab05bfdcdc67c0f2115b4967df4104592ead590de4e39396bd67e2", + "0xf63d0d400b734e1b7e315e91c00cc6b27a68c2fcbfd08fb81f93f31d927e8bbf", + "0xdd12c1f59dcb2545d14e15bc034c6e50306bc84d192a393a56bfb8e18153c698", + "0xbf61a72fe55ba1fd4ddabc719946a943f51cd1bc363890ac0ce3bc0dbad1cf6f", + "0x121b758473edf91f1bf2d1a33d050e2c6e76e587fc60150157d9d9fb74d5ed2e", + "0xbff15a709474c5dd56ad734d37f54bb04548f07aefd7fff6b9491f920d8e8b67", + "0x054ff17cd16aaee60bb702195a670791cdd751234a2e31a1c91427e33b08728b", + "0xda0648f8b0eb69613c66d884ce19be52e5c13e3945a09931d594baa54f05a6eb", + "0x93ed8cc6986f45da840185f82e01a7b7ee3be3dfcb257107d675ef5fdf36a0fe", + "0xdd1b8b798b1e301f44db9ad7768ff5d505fc9478aad22dc73c0c0226439d1ce2", + "0x782c6f6329e7324cca7c7d4a16b76f7b65660a59254229d57f426d1714db3e73", + "0x3762cfa2b41b530fc82444bbb2dc02600ce4618038e0b82fc9d1d7ce37f577ad", + "0x4da34792215b2070349a007d0330877abc3ac7873f0e26b38181bd02bf38c1b8", + "0x7cd8252f6c05a03efff0c3efcb98e8d3fced527ddd404c319797ab471ace3939", + "0xf78aada512bbd87796c12fdd5201b4c0be8501874d2b7c684d89449ae30e6b97", + "0x95f2fc9b6641e9fda5aa8037f11bc8dfece8fc5293db4b4061959af93d94e9c6", + "0x42776dd70e14c16181f5d0164db3d6e3babb8a245e2e74ecf8015ff86d673e9c", + "0x63f16036a88894dd3db29cafcd157594a00c83537d37bd7a3e51059245d0f47d", + "0x82e78b42af946093f8c2447642914882f14d77aa4cea1553ee31ca4d8aa28dc3", + "0xda488edc004daf517bc77c3518dcbd5b2a9fd17d76a2fcede1c61d5aa97d3a92", + "0xa37e59761d290f64f81fc4b62c05dddb66c82c464482894ff99d24dbb49882f2", + "0xdc1362230347c188d3174b74662e0046f5e356a09c22290c6807b8b750d5882b", + "0x7b9e0600f423506e7dd282a9bd0b445e453cf1fbd84de96eb86a5966373d56ac", + "0xc854b45178c5c37773cae6002511b952bb093f96fccb35505746c32ec9c54b0c", + "0x6bebb4e396eb4591b52d54efc827ac1936be5513db2fff8f08337c1827d17f99", + "0x330b3722868948b585eb6703037ab0ac21e26fb6695e5fcfb7cfe1d5410a5b04", + "0xcc58d8949bfd89832b6017c2e7ecac45b8f6a6e6a4c59260cdda32aaeaac39bf", + "0x0fdec82c9a779c2311fd6d28d25a910761f37ad3d35b317a33c3446781b08dc0", + "0x12de2abd4dbddabf61a9931ea4ba1d835ce3bcfce29df8a6d612cde7db32dbdb", + "0x8ea209fbc0797a168c5e5e9f6dfc173e9ef04cdfa42046fce2ec7b6fd4042e9f", + "0x5a2548ed6838ae493d1b52da6f412dc3d3d9713e1ab0a82a97774af2fcf43c6c", + "0xd6d01eba96bfcb1a6f9eab447436ff11b253bc5ee0750c1e1ea3dad302aae16b", + "0x8c4e45222225d26b4e2e977957642ba82fd3642d56b5c0549ed650bd3d9cebdf", + "0x685850ebe6eee5ef278cf1150f6bd0d0362c98f0d5f20a8464d563cd83f803bf", + "0x4e06b978ac5a5887fb7fc5ff7e54d93c38b5b7cd3abd4dac68bb0d776439a712", + "0x0326ccddd62be1207d9fe7cda9f568f4a5cd98ea0c8cf7a527513aa637181ede", + "0x064313857144712806e02a80ab7987efb25b98d7a9f463333a830708e403769e", + "0x7b17a7be786f43560f79bc90e8bc88b6f1dbb70e6a710e6d0a985da112a57bc6", + "0x31d56030a179d4e47902be65aaff878063574e20dd23f4afe396230f411f0f3d", + "0x6b4dd1db444c7e568a1cdfa905f0fd3014b894ba92c4fb613282fdee2b8fc4ce", + "0xb7f34e02e4843011da12123a0b3a646529bb50e2edff046907db5b9b4aa06740", + "0x397bfb827be30bebb91d2cd19d887a61ab09756bb8cf4e07d834ef1a6bdd79ce", + "0xd078ee41ff580c78766f94d0fd02ef8bb5ecbe91ae2a917ef2b18a1e78476003", + "0xe52f541e5a331a6446662af29345d99002e9109d6ca5d8dbce928ad15c8743cc", + "0x854d161455113e736b74375d60a7c7576f8944326fe101c9a40fc4b92f74df2c", + "0x9929711fdf57b1d76b29ae444ba8505fd88ab2ce1eb39ed70e7c1cfaae2b2dee", + "0x8f239b48ad72b484d36ad1226a9371220fd075be25ebe1fc441199fb0e519b7d", + "0x500650843b962b5087fc846db7662c54c70dac1e25fa5dcc01477f813c0fe870", + "0x0f6e240291d00eed0042280981dd2041d5ca7526ca84fd0966f2443226decded", + "0xf4a71e8f8cd3220cf5472f3dd344b2de4f944f56537de16e54a571b3a57a570f", + "0xea71fa2c56ab78dcc9289c925e478811282500e9f8187e8b48e88abcf6eee67d", + "0x9944847afd6f70edc6e8c5506d5dbcda196cab25cec9fc7454ff44172cb19d7e", + "0xd1886dfdbc01bac9248ff0fd24e45c256d8c28c3e16f2e42888ae5a2662ef457", + "0xb5921403d9f3d7ab96ad31479f27ca7f10560ed7901b15cfd6eec64931cc54f7", + "0xe2c68c0091b8997deeb3476acb82a4bb2c36fc651212d28a1bbc47dfb73e484b", + "0xdbc5a5897adea6c117316a1efc273a4cc69ca46c97fafaca61476bf66e4c5efc", + "0x4fbc2592a8d8d9a49785e11e472d292dde0177e0f22add44f7620c651e058011", + "0x3cfed5fb2f2790af08b8878dcbbe31b63d96ee68addd46b8dac75933ff4451d1", + "0x4ae22fa05263a75c3e801177d83f83b79d501972c3e3c3f5e4909a3705c3a313", + "0x8668b5212ca04d4ba0eb5264e02c4183cb4bc0ad91721782bcbbfe771a06b80c", + "0x608cd2e0c509f51963d49a9cb3620fca7e55c42f74f1af4ff7a38d95e40e2651", + "0x745121305e9e04b15c62d981884742ebd54aa13c67333836a2bf5c554f478a65", + "0x5a43203b5b34d86a8729f25d59d1bf42e03290ea866df151f6651ebc966b8481", + "0x36ab1e9dd52058aeb37c83798d8b168f584e64bff866409604c77140f691f58b", + "0x9ee3a8e3a777b94472089ebe1b0243465d4080e55f0303ac824ade8b0d143aba", + "0x9577c5715e585a485b2a56fe7e2e9ab4ff6056ad37ad92e6ebfe81919efb428e", + "0xdcc4d470f7ebe15a384d1c59ab8b37940c8d82693159ec01ea248f76e13af162", + "0x3d10d140d601e378e27cbb674db0d74749376eb80aafeddfce96502905a007c9", + "0xedfebcbb9b5a61b5a28fbbb7f898461cc085881d2768f86d5ae4e51c5aa6b5e1", + "0x224210b2a001e220033e1f064e4915d9b6e62ef43fa31ed2aa6a371d3b742ab5", + "0x833f691baf7e22514857d1a7f559e11a0afbb91ed252f6634e3ed50388da7c4c", + "0x84117b611402efe1380a651ea74e5b739ecf1bfe389ea03314f269b4aee88713", + "0x8be7bb172e40fc20ad8bcafe38ea9201588f7e7447974c040808f1ad8ef317e9", + "0xa7e540bc068875f6c4346a0b182cea4b7b8d33bb358335be4b189753a7c2286e", + "0x2bcddd775c19cd1ab12ad15cc8f68d911e9352de40b475ab78c22993b3b17530", + "0x30a3aa473f27f44d412a4f00aefc19e2c99ce7374dc4909bfafefdcb5b5d180d", + "0xc357e9afa2d16421e9f642e4816f1f2d1bd9284b0c454a60525762397a924e19", + "0x6e15c1d0bf73b111b75884ce5b2109e70c627241a7fb51346a8253d9ca526266", + "0x70f5436b3374249f77f6e711d11dbc351b83c07a2e16606f8f74925c9b300bb0", + "0xf7f14602368199babea17f94176a35170dae0f40abf7356b72f0b91742aaa193", + "0xb7ad05b91a8d3fe25a39d7140bfc3f1b2f881322a67e6758d0009dd8af7017b4", + "0x6b64e9a1deb685d9f6ac3a697fd9d13b470fb6051380cf93384155a60f6ec5eb", + "0x885965c739f5817dea98cd8349801cf931f3f71bb30b3ca65b5464a7a5fb42ee", + "0x7c3e719140f138494e73ee01b2c6246c4eaafb6b85bb3517de2e237a3eeb8e20", + "0x5ede17bd931ffb7624a893d1b8a1ad5d4a9ab6730e079e61e52bdf55c6e0d146", + "0xc7782f944d9cb7059c6cbc22b4bf0635595af22aaad8ce0d4c6a19631e81a47a", + "0x693af62bb61ddbdceaa7a89a851bd17dd5bf7fcfe31aab629069b27bdefd2f36", + "0x0585838cabda1731694b31c6ac7937b6264745662b1a6cebd447412f7d1466da", + "0x2f7e33991a6bfc76f38af325f7f9310e41de9ed14f197b0eb5fb6d387a9c0078", + "0xf1fe98b7f2fcd995f24dd5121d3a3e85e20577f0ab5f30c9d0abf6970d91068a", + "0xa0256dc1826391775db680bedaf361091cb3f5a05bb061b2445eef8bda102e6c", + "0x0716d7a5921f47a57be8321f939f72fccbad26fe910a0f20f3c7a4820081f6e9", + "0x21e006e4749fc467e4e1c4c7bdb0fe6f0238d0249a9a1a9582032959d74fe45b", + "0x1596293ec108524ab2fa118b75a4c9675c03b3cc0b47267b95f2bc4b608b8a07", + "0x5c6b2018b16097ca5e77cb20d53e8c20babf47b771eff71a35bfaae8b9946ea3", + "0xab96bc75a4b4fa6a92378d9a4cbd61aa8f12416f1fc3946bb07c06e428125e6c", + "0xbd12624b76864c04130d42be13b20c9a957233082b742ca219e154242ea5e512", + "0xe3c72b1c48ef5e20ed8ee470cf46b0723cd0fd7a06f386fa0b91e0c3dcfe77e4", + "0xaa93e6cb685a56695aea672ab93851137efcc6e17a8558c77b36509310f006c0", + "0x5e4ee6339c7a40b0b1953bdb61aebdff64e51249e01bc900920b0574aee46a7f", + "0x7afa49da63d2a0d9e6db0b2af186540112f2744348053109122d2e6246339077", + "0x1c00c8c57f0fa5a76e553a9d7b04a5909c1d22179fabc6ec2a21f6dabfa19532", + "0xb591e1d2f5996d132fd87f8efd88350c9938ab295ba615c70224f6febd95e3d1", + "0x6ebfc42e3e1ec0571640c3cf6a4d192447d7290b3dde0e10794aae04b29b5884", + "0xf81481b91706369a76957f669e0bd529953ced3ccfb23611a63a0d0d03feeb42", + "0x9e3a7e812e393cbbc233778e767e7533c5d06b7307412275c6d8185a277d683d", + "0x3c6c025a33ebaad0684a513706537d4e3829b50a5ef01dfed28f57001d52068c", + "0x8872f5eb4513d7397cf8c399657496916a12b0fc23c9e9bced9e9a60cfb7cdd8", + "0x29c31d98b5134f5db4075decc730531af88c343f69a7ad4e66d702fae5df7188", + "0x2b895014232363f0e0d5cc5c7d6a613f3344acd01c70ea7037b1a209f2408764", + "0x43840c32c4fa761b1f3683c8ab3b4cf8da580ea7dc44d7d2ab82d6f1e2342d89", + "0x5ea22d0a2ec4191cc6ee6050920aad7931e98946e405c2e4c89117f6293d5533", + "0x445693f78e45831dfe79410cf50701cb33e6acbc21bf5b36ee26027057e3662c", + "0xec8be72212450e4c47b071dc0fc7b69a204e90377551b048fdcd125475436e47", + "0x3a83f132865c728257a5e936ea7045cc1eb3a389a10e6ba794a4cc84293d38cf", + "0x6ef8d76d82ec6bf4d949247a313dd5bdc6cef711eef5c567bacff1227e25b0f6", + "0x105f04f7d4f493284f3246b2f4ba9a4cfb993476b30aef35c5a58c693fdd7034", + "0x86f30b5f0c073ebebbaea56102a7423620d01e1b24f7a2304bfd384294def396", + "0xd752ba39ef8a20e554c275b6862bbb4ca0816ba96d53fe99ffd6fc18868d2c8d", + "0x34e84b6e3b77f40004327475d849660c8bdde82b3ca850db2d14c0671e759363", + "0x79f74a6186f89de2fbc1b9d7bc6d1760a7b125109e8f771f6e016e1c2a7f6083", + "0x1ae0223e6bdda1e4260180f6326d9e117164d06f7dec7c8d485d472789bf5ca1", + "0x29e23baa5db053d9a2cc1fb5902c0108d189c1d18c607d6a44ed903146e30f84", + "0x981b14b24d615026c1543d34de44789037f581161bb4b81976100423b93e5966", + "0x95d8d0644925790affe95e169ffc8566586f155f7a9549c458697b924a65ecd3", + "0x86c64adc751e822f3225af88397efa39e48dbc09ec55cb9ddc66b9280820fac9", + "0x15dafca73ce6b3c143b2cef34f7f2767b0efa9f6c9688c0c4de4a3ea6b97099d", + "0x2b52dc8e0dd5eeadacbd388977785695139acaaeb9b7f661a5ea61ee2da8b931", + "0x52204de8cc07cfe2e0c226f30f4bcc4c22853dd0a302cab173d02cfed1e5d7a4", + "0xb780df584d2ada47a7338e32b1abae188d80255754879d1cf0eaaa574bfc4153", + "0x8da209a66188b13423165735a78ab0cae61f495b394c6e12de1477bab29df83a", + "0xeb90fa18a576e19fa745269aba355be3a93aeaf7cb6ccbc4caf26d5be2e6beb8", + "0xb800573ce28ac17fe8b47204281607679d3568f6d674b17259667289aef5e721", + "0xf941fa2be81f98914a61e1f5d0e7b7b05fb0f548826b308e766141e2f95f1c50", + "0x43dcfd54a209bcd1bcb4487134f64a797714ca6627b0b15f08cc66321943970a", + "0x4fc431f5e5e6176c126fc7434cc40300ddd810aa4180b98d46f57ae80126dd4c", + "0x24bfca5f241f7f3c712d176f80ed58e1bd12344958464e31de41abaa312913ca", + "0x0cb817a835c4f244b427b3a33dbbcd8d0c75d396f8372a793cffbd18203d4787", + "0x371e3c1432b9e9a1c0525da982a0cd2fea86a1b54994cf2f82a0c30f5c43a0df", + "0x144c37ac8c3e1c215608dd88ed614bcc179493f421c7f0a49f83d1547c8aa4d7", + "0x9c59d25233e6371cb3e918a85fd94329e003ef85230886f9b03ab3c0a8d39b50", + "0x9c890cce4174e427e3970c2d4d5c8b2e12e331437a168457a6311727a3f401eb", + "0x6b8c59d9985a308d42071df3f5ec94bb1990bdfdc2c7bd9c5796a3219b61348b", + "0x97d6afabfad3086575825fb2c99f28ba0dcf7adf8a5a099eb4aabad317e595ae", + "0x97c1230325784c9794a1ed41c228f0f0a31bd0c596c7aecf77d4bcb9acf29d6e", + "0xcea0845eb1d3fe4fdcc52fb58064f1f63059f027283064bd41ff72f4dd249a7f", + "0xe3992852147a0eda9fb4229ddfabc6399a20c2f9c1f68e7d77048561d4e01e4b", + "0xa6c304a23cf0f0149bd8ecf232d7d1529acd66eaf967b2ea2ec814a39121b308", + "0x0d17850ff91b1ea5f7d107254fff280de5f1f5931bae870833a4c3afd31371c1", + "0x37e01c8fde23f9674d901736d457ac3178bdf26cc5e38c1e2e26f2f5d3c93cb9", + "0xaf2b8424d4a34cb4e431a916a6df973be36edaf0f5e3d095f50011ff59b0ce44", + "0x99b9269be01fd021af9e2699e8d61a3b8aae6f9e65d80f86bf5d0c4b02ec3553", + "0x26272407bc8be9c395065c883b60bece74a87195c4c76a14fb1c144ff231d9c9", + "0x095ffe2710d1b91ce947ff31b9e5c88dd9d23a003013c6d936ee0c600e18bc3f", + "0x1948914922ca9f2e9913f83df522e5120838a7312aad6dcbc10b721d6bd6617e", + "0x001670411d10499880e4efa9db47bb55f23f4ad0ce981ddf460a9dfe7dc69edf", + "0x21f07d091660a7a03e6eea42c1b0f387e1fc59afb9c5e481801054039cadd195", + "0x086900640ad32a3d6478d6dcdaf342113254affc89c8201270fefda3d122af38", + "0xc30be7e2371a77334fa1aaed8a63bed475e14760b71fc11e6d629efbf468acb4", + "0xb37917e90415072c254bac4c533de853cce466a76695585f2566a2c0c6c0be94", + "0x1f7f961bf65dcbc6494be2755df16c0ee37f5692861008ac8ea4a0dba37e57d2", + "0x1368864524f47f016e7f0040c9d22f90b19fa042dc2e23aa7ef0a39960299658", + "0x6d038d783fb068708918705a1539cc857805bfc8e246a1087df58816b723a07f", + "0xacbb158ff0b6e3f2fc538a91757513b123e6b47ec85e98537b0b29a07adbb9aa", + "0x4e4320c4734503ee5f5b09a7725288d169242ef92e9282605969d35908358199", + "0x31d30ed824125e2d0b038665ab18129eb725537bd8450fca950030443418d022", + "0xce2e2101a1804066aedc8d8c001fea1e9e35895a3216fac6b037e743c2779c0d", + "0xff75821ee4ba3c534111f7f7d540d03a48cc92db0d1ca2b64f0d7b4cc9acc84d", + "0x13d21e9fce1742492600cc3e8922ee19d1fb7aad1edf0e6fe4aca4547a1120f9", + "0xe08973be313c8b68827c93862a6f21021f0cc14f1abcd8772914c6c1857e1722", + "0x35cf5ee46a157383742583b065583f14143bd2691dfd5a9b9180aecd01d141ba", + "0xff78536ce34905bdc859ee5985d86cb305113c43e7be398d8e2b22d74124c6b6", + "0x57df378e4ab32bfcac89e40aacd6fbb2756e3312cf2c7501ea948d225b12f223", + "0x99fc258847d4f9313da8b52fd5e7444493d145448128512ca5737499ec8acd06", + "0xb3b4c84999829f78b59db8c92f537d8e0614f061163ba5ff3b2da505b8a5be11", + "0xc6209eaba48d12e4096e10b3ed52f7055b5efb12f7949c71fce732412c4c2b12", + "0xccf3fb0e240992b9cdc34961a59507b9b89ce7a893a5979db95e2b2dd0e341e9", + "0x73cf6b088dd8d881a345ab2c9ce1a632ee33738e13bf1d2a51f6b4b7827411e7" + ] + }, "nodes": [ - "enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303", "enode://f1a5100a81cb73163ae450c584d06b1f644aa4fad4486c6aeb4c384b343c54bb66c744aa5f133af66ea1b25f0f4a454f04878f3e96ee4cd2390c047396d6357b@209.97.158.4:30303", - "enode://0d1e0372f63a3f0b82d66635ea101ecc0f6797788a078805cc933dd93e6a22f7c9fa51ab4e2d21da02d04480ef19f3bbb9a2b41dd1c262085d295a354bb8b0f9@18.217.47.209:30303", "enode://875e1bd1b98019a5d6d588c23f68534b75462dd6ecbb3dd058221dbf7aa923f0ab782ab93bb82d42edc9996f7f0816a318bdc761e55c02b95e1169cef66f7edc@159.203.24.35:30303", - "enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303", "enode://182ee200ca134dc4d6390f3d5aadbcd80df0f7f24335830335d142573eacce4eeb919d30e82c5df588034e167e6ba6dd11187502ac9264a71005127f6b146a99@159.203.95.241:30303", - "enode://b022ff70b5fcaf9596ae5efed99a8198b4ae0578ee9d17b733609d803a75cef95d3a2a18e50dca9a7c3b26139f158c59eaf8b5fb8d1d331c9a46934a78acabe8@206.189.76.128:30303" + "enode://f11a0f80939b49a28bf99581da9b351a592ec1504b9d32a7dfda79b36510a891e96631239c4166e5c73368c21e9bb3241e7fd6929b899772e5a8fe9a7b7c3af6@45.77.52.149:30303", + "enode://e5d196ad4ceada719d9e592f7166d0c75700f6eab2e3c3de34ba751ea786527cb3f6eb96ad9fdfdb9989ff572df50f1c42ef800af9c5207a38b929aff969b5c9@199.247.18.10:30303", + "enode://e08adce358fc26dfbe1f24ee578dceaa29575ca44a39d9041203131db5135aceba6241840a9b57b1540eeaf7b4eff1aead28a74641be43342c35af454abb31b3@199.247.18.10:30303", + "enode://b3b8094b705f630155673e6acbb8377fd6d8c422f363cf908c4618e2d4730c5a0f7380fcc707cb958d10377e25762a44e1a9f1518d3ea9a3fb97bea4373214e3@167.172.154.174:30303" ], "accounts": { - "0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } }, + "0000000000000000000000000000000000000005": { + "builtin": { + "name": "modexp", + "pricing": { + "0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "12095200": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -81,12 +5348,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "12095200": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -94,27 +5362,44 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "12095200": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "pricing": { + "12095200": { + "info": "EIP 1108 transition", + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } } } } }, - "0x0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { - "linear": { - "base": 3000, - "word": 0 + "0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } } } } @@ -124,9 +5409,13 @@ "builtin": { "name": "sha256", "pricing": { - "linear": { - "base": 60, - "word": 12 + "0": { + "price": { + "linear": { + "base": 60, + "word": 12 + } + } } } } @@ -136,9 +5425,13 @@ "builtin": { "name": "ripemd160", "pricing": { - "linear": { - "base": 600, - "word": 120 + "0": { + "price": { + "linear": { + "base": 600, + "word": 120 + } + } } } } @@ -148,9 +5441,13 @@ "builtin": { "name": "identity", "pricing": { - "linear": { - "base": 15, - "word": 3 + "0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } } } } diff --git a/ethcore/res/ethereum/rinkeby.json b/ethcore/res/ethereum/rinkeby.json index 3945b88118..ee02408f6c 100644 --- a/ethcore/res/ethereum/rinkeby.json +++ b/ethcore/res/ethereum/rinkeby.json @@ -122,42 +122,46 @@ } }, "0x0000000000000000000000000000000000000006": { + "balance": "0x1", "builtin": { "name": "alt_bn128_add", - "activate_at": "0xfcc25", - "eip1108_transition": "0x52efd1", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0xfcc25": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x52efd1": { + "info": "EIP 1108 transition at block 5_435_345 (0x52efd1)", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } }, "0x0000000000000000000000000000000000000007": { + "balance": "0x1", "builtin": { "name": "alt_bn128_mul", - "activate_at": "0xfcc25", - "eip1108_transition": "0x52efd1", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0xfcc25": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x52efd1": { + "info": "EIP 1108 transition at block 5_435_345 (0x52efd1)", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } }, "0x0000000000000000000000000000000000000008": { + "balance": "0x1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0xfcc25", - "eip1108_transition": "0x52efd1", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0xfcc25": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x52efd1": { + "info": "EIP 1108 transition at block 5_435_345 (0x52efd1)", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/ropsten.json b/ethcore/res/ethereum/ropsten.json index aefca8682c..8d37b294a9 100644 --- a/ethcore/res/ethereum/ropsten.json +++ b/ethcore/res/ethereum/ropsten.json @@ -16,7 +16,8 @@ "eip100bTransition": "0x19f0a0", "difficultyBombDelays": { "0x19f0a0": "0x2dc6c0", - "0x408b70": "0x1e8480" + "0x408b70": "0x1e8480", + "0x6c993d": "0x3d0900" } } } @@ -28,8 +29,8 @@ "maximumExtraDataSize": "0x20", "minGasLimit": "0x1388", "networkID": "0x3", - "forkBlock": "0x40E80F", - "forkCanonHash": "0x3e12d5c0f8d63fbc5831cc7f7273bd824fa4d0a9a4102d65d99a7ea5604abc00", + "forkBlock": "0x62f757", + "forkCanonHash": "0x3a024a13310ec9b4805f681b17c3ae6c94167d1c6494e83d70a887ebc27df5ea", "maxCodeSize": "0x6000", "maxCodeSizeTransition": "0xa", "eip150Transition": "0x0", @@ -67,8 +68,8 @@ "gasLimit": "0x1000000" }, "hardcodedSync": { - "header": "f9021aa0a8da98b6ef1e12b6c49e85b0e965f1ed1688f5e3605f06bb3c6ce4f857aa0bc6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794635b4764d1939dfacd3a8014726159abc277becca0d03c319fe68a91e22fb3b945a8dfc73b817976e29cf57e6c8425e6a02e9bf034a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000849fe1546f8350d001837a121d80845c9d55f29fde8302020b8f5061726974792d457468657265756d86312e33322e30826c69a04bf72e97bcf64717bfd655e2bca9ed1a5253cce5373268729161b1786ca4710488db3c50627f9321c4", - "totalDifficulty": "18787961645682286", + "header": "f9021da0577de775e23825f45fbd357613d34c851ffe0cf6c9543861cc0fd46c00b924e0a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794635b4764d1939dfacd3a8014726159abc277becca01f26f20e5dcff4e9f3623fe1a4c0a68dbf75f5bdc2b137335bb8b8e5d309ef64a0c64d2a10516072685918be6ae77724ceef5c145c6a49588759c4e7ede228501ca0577f4a1be250fa0e1030467a4ade401b17f714f4cb4fdd9bf0e4c09562d35a04b9010000000000000000000000000000000000000000000000000000000000000000000000000000004000000000080021000000000000000000000000000000000000000000000000000000000080020000000000000040000000000002000000000100000000000000000000000080400000000020000000000000000000010000001000000000000000000000000000000000000000000000000000000000000000000001000000000020008100000000000000800000000000000000000004000000000000000000004000000000000000000400000000100000000020800400000000000000800000000040000000000000000000000000000000001000040000840ec353d1836de801837a121d830529c0845e2e239a9fde8302050d8f5061726974792d457468657265756d86312e33382e30826c69a0009a6e85c34c1d6b68966f2f5ff171be59b829451f1bc360e18e0e79a821403888855aa9691c346467", + "totalDifficulty": "29896789803435294", "CHTs": [ "0x614648fc0a459451850bdfe353a932b5ff824e1b568478394f78b3ed5427e37a", "0x1eae561c582dbb7f4e041998e084e165d0332c915d3a6da367638a8d24f3fafc", @@ -2655,7 +2656,938 @@ "0xebb3e8f76f3b6a95285154dc11d4bd94ac4c3a150383ed69f5373499b1983dc3", "0xb0919ed300acac5f912f01611a428861db27ffb8129a80495f735f0ac608ab35", "0x2ee321d9d805b78a97210df2977ab62b352705e308773b90e0f4e923adec377c", - "0xee00cb02e9b86978ae10b119924bbe6c38f730c1d1b621d32c9d697e11105871" + "0xee00cb02e9b86978ae10b119924bbe6c38f730c1d1b621d32c9d697e11105871", + "0x3138c1f196e8b3de9369c3c8ee1d556de1bae00896cac87b4083bdd7a691e738", + "0x0a777c53b3be3f3b136b24585dca1b7c85f7b0edd72f2173ddc70b0a1bc8bd93", + "0xbcb300ee517ac8356c36c002da6f2ad562c31c81a3353d63925e044b10d0412a", + "0x503c3b1da233bf2f9f9fa13b0488aa5384a3cc95dc21e6f2e73577dd3ddc1974", + "0x0d07a00e18f3db6896d0879fe87cf7aec13f30a957806f74cdb0c47ccac035db", + "0xb6e92e6a32c5434a79448503a269e6e5ccbf70bc59dc58957915621eb5951e26", + "0xc2381981c2a38689e6ca1121fd33eac2881b1aac64a850b7f5751d587a5a79f4", + "0x83c0f655616e7565d63333d8a5f99baf2f4e0a7dc1520d6c4142b1be6931cdf4", + "0xb17c34ad30548ca1889d7aa11aa12748f7c02431d64d7f53013a10b979ec7aa1", + "0x34ab63d2aea17cf800fc2aa3c8589fcded1a732bbfa2102315adfa03596a4221", + "0x6a7bae77e28fe9ea18b59e4bd4b81780daf520309808560d533604c0bdf76f84", + "0x64df132fc5319adc69249961df9830e5d63945564d912fe901d80e7859a87057", + "0x2323352a8e2f13d442fcebc760ece470dfe5542188f6fc2d38f4db6af48bc959", + "0xe9944f4b2d9e3fd7aad0446281d4971463c2e53029d7e7f40f8b0daad1a29d67", + "0xe012a3af1ddc10881cbc1c62c1e0e7205db5edebe74bd0c8d1c12bb9a3f47e2f", + "0x26672ff6f0f02089b7a67837aedea5e54acc3120ad4f2b936948d7053d74ccfe", + "0x70b1eb24d9c2544e26af43212c0cf70e3e2ba58316f5372db5daed02c45907bf", + "0x7156e65df46bc1d62e8f8ab4c6c2115a5129d28ae9e96ba779b4615fde08f267", + "0x15212b5fcc31d204cee80a4ba392e731a2fbe40e1a23d8fdcbc6ecdd665ac9ff", + "0x1508823c086bb5269f60b452fcaf3d58b6dece236704ad479205e1b6b870b7a2", + "0x73a9ba93b3a5444294b466acb8c560c0a62a16dcdb5c90eddeb19f9eb75dfd31", + "0x9658cab50473a5fdc06f3bf07b34981212f215bdd773439cda3a5a37dd96b7ed", + "0x1b76828d177851a98f2a4e4a5a28e2e50dc876878b4a44b7654c409ad6a58a4c", + "0x7345477276953426e16dc819818ba09988d1e2b552adfa47c5f7cf6c1bfdb3da", + "0x2419a94e778ed5aeff0552a884b87b12e3b4f9b87c2e5df66ebe9260f38f19fa", + "0x4c14ee271bcb543d5395324a6fae046e133bd3e34f6a3d1328c9d3c77593756f", + "0xcbd9935f32d758ec070e06c393f5bfb925515058237471ccc89a6c7126df2fbc", + "0xf945621e7c926f2739488d96c4e5fd42fa91a5be31e9a45876fe982166bd4cfd", + "0x427ae8d29c63525078a803c1e40554f77685866a46f2ad9d0f772ad0c10b43ea", + "0xe4a6753800fc0d20c0ddedd6c716f8fafbbe636256548170a871d2913c885c9a", + "0x4da6c6246e60c496e059dd80bcae5ff30dc285e2a2db142eb4da3f15b00c0df3", + "0x4d1ff252edc762f25066a466e26154162942ef71ad13c159c102d94e427cc926", + "0x663a98199d967f55acdb313e901cfa23768ae21e7b5efa6b6de88b93e96b08b2", + "0xf025c69018317e95880e27a96910c6a4fac0b5d7a52abaab7c616af08eab1090", + "0xa5dc8339e0b823dc3821867cc84bebff11fb0bb8fe2f54ca5748cd2d8066f68b", + "0x9b3835afa0d0f1aac1a603d1f322940f4b84da6c6352654fb10aea202b3b84b1", + "0x41cea6f1e1de2697e58cefefbd4ea1118acb43aaf9ec7a9c116d14a5310e925f", + "0x5f1e42931b16c26642f351b24bb063f6ae7dbceca760961fe4d07dee0286082a", + "0x9ab19e309d5a4fd3596f0979b268473eeccd04af6674968e1e340a47c6652d9f", + "0xd6dfe22679bc3590594bfa1ead582bd4639f1b3869915e802e8973666d29588d", + "0x92f5589f356fbf8e4c5b12bf11d5f148d94f0e9f9297bbb32a73ed8fc409ad8d", + "0xecdeb77e2b35647c59aeb935e6fa4404ddecf9287ffd84d02c090d6772f1a68b", + "0x18abbe4adfb8d2365a14201ceb3966e561817ae497754b65cb5441f64bfec184", + "0x4d57180d082ec586a97573b383a12a59664d9877d86140281af0eef88f8dc8fc", + "0xfd52e379ec75f3645ccd83653370e107970c5c61573ec688986895d5e825303d", + "0x9327212a8ca5d0139e6728f8b39caa571a7821782b49a2edb96371daf6320571", + "0x9d9b1cbc011a31b99943d9dc7a2a8ff2da0992a5e3d22bf24685723b85545452", + "0x8661975f8565dd506ee26d265ed78a06d0c60dc150df594b6a1cd09c5c383e5c", + "0x98574ee8064f0b276433b8d98f7db55b7387775c03d543f2a8d1f77cc9d9bd20", + "0x6fd37be595359e52e5482f53ab66c9e76ef393085485a4bcb344272607cf2c0d", + "0x1d2a15c07ac3b45c024b4c14c976ed18d0f2e4ddfc84fa153414e301115b70fb", + "0x1ebb4adc19ea75acf90cd66ded504674367b70506f4813e22cc856f10c36973a", + "0xcb0198d5b6883b582c6b389e6f98ed77494f3f6ed232056dff48a368bbc2b9c2", + "0x2bb53b3a72fd65a4a090b49301c448fe44fbe0c7773e938af9c511070b442957", + "0xbf8289e45461485d9ae0df36052adcc1c43089e173a54663330a4f1ccd6ff7ea", + "0x059c82830dd4ffb040cc75a4b354483d81bc6f16ffd0545029112845f8db7cbd", + "0x68fa73438a9093f1e2e5a88a244287cd1d068bc28ac0c7c88cecc70f2b7cfe74", + "0xf935992c7eea357445e47fceb2894d4c04c618fb238f6a84251c919cd2e5b97c", + "0xc2b6a5bf4ab7f6f43fa9f59269116929116e5f136faf8acc7a565eaf4f6f5a0e", + "0x0f06da2edc35f959f8f3c9f9b16b13df3d1ebc5de146d7467f342116648bcbb7", + "0x4cccb59e57aedae4c1aa83a19126e846b14701be2971f95868c9055b77461db0", + "0xb2a64c59c4a3878472f5fdc4365e61f6cb5a67a1b453dba34c2d257f97ac44cd", + "0x21d74f1e667bce1163c8da46d522bb39a6ce777b618c6d2551c01427a68a2316", + "0xe1d84da4146c25e3d89bece4bcfd19ebaf684086604c53e702c07b938057d88d", + "0x6ff86b85371f732a285b19e9372a2567529bae071a439e80be782c5e3c462c4b", + "0x86fce707891c52a94cb30d50c4bd08fdd5567b88f6676f35228523847c966032", + "0x59aeb5682b2a3443e6c8bcc4d425e3a59872090bc71dfbc6cacda9f02d9fb7c8", + "0x7bf9a30fae3402b8d0aa4f7b18842bbe082c1e11469f635792225c8e8e536f9a", + "0x3f13e2ce31229a9079f04f77131a170212bc64c1701c2ddd54713c0e59f03991", + "0x2daaafebbc63db1ea0a8fa6e5037c4da16a92fceda2dbcb9576fc2fa28b0ee74", + "0xb14610c614c0c0c59e295fde6c4ab0e4b4c174385fa734cc8a2095dd6a95358c", + "0x14ab25c7f34bb626f581d38faef3beee599300a51bb62d3e1e1dded92ddc95d3", + "0x409ca4196a8db10326ca032cd14b7295981f0a779a42c7f0233d71ebfec3b19f", + "0xa283859dca46f41b5ca2f403aaa9e2356bf41e06697b5722b245c68ea3f81b2c", + "0x1c917355509fab582d5556e340b71c6962577da9e189b703456dbb7fb55784c7", + "0xb928315f7ce4508f4014962fc95a5942a5c0ee66af2e125101ab02d04b6cccab", + "0x00c6a46b1b4eaf7f62e2133d454f850ef1dae798d5aff512f607dd8037e53bbc", + "0xc042c1ce9cc355d4e054e5880f7c09fa3bcd58047159fe6b08a02c80059acaea", + "0xbf7b1e204a760ae2782e926e202428312955404786b85ab00688da616682b085", + "0x36d29378eacf4d587bc5a569e8ed572cbcb71be0a016d69909a05457915962b0", + "0x27674967b0e43a5f70ec168cb00df61a1227da4ac61660769ac2a7a2cdcb2598", + "0x9bea8f7596f72268a51be1b993cde5adbbe175ad48334276295687a76f801fc1", + "0xd9e17222c824c8293b05702e3170a059543bfa3e4bdd19828203c7072f013eb4", + "0x170685db98bae956224b3a57e4cf50ae4a8874ddc988f70d5ea31357c2a83ed8", + "0xb8e7344df221731d25fc88dfd31f938005328202dffd0be8d28e9d0392a75c81", + "0x47abee9a0802d59dec86fbe5d9a13a7332d326b900cae4e3841c69e9b25ac788", + "0xc09452238c2890433e14e0fcf826051c752580f147750ca745d1b779f303ef9b", + "0x05bbb06eb270618437a1300c02964c8f854d4a6daaab2d59ff6cc69f46c7234b", + "0x89d752b94f59ad98d59ee317c3ebcd840dcc997efa7fb88797c35477831c695a", + "0x2dc622984dd44a2cbb4313c7efccba85b497e33214eb314dfd7f2be40fee6cd3", + "0xe23e51675985c6a4f887c843048487721da6ae6a6676adc87a805fdd46149f9c", + "0xa452ef2f6e358a534dfc4914a2c7ad3ff9faf2d16fc0b7cab96659cef5e4eea0", + "0xec9f04f090f443596129248d8675aab78e5275c56691444604751a0bb5da443e", + "0x52d8fe33aab98f680b3b7d56f501621a5516d3999bf4b1804b4ec0e845f87ee4", + "0x22dce4da35077c2efdb94c41c6ec7e17c1a3604c3c7b70041728a96c3d533851", + "0x706284df96499696a8c20c7a685493663b149af04afb5635fb831bdb20de256c", + "0xcfd7fa6c9a27e3b9c1f124522c87841a22105c561224de3c1d7a5e441f980c94", + "0x562458d2c5b55b9fe6116ef5cf165aebcc40d86863efb5036d42f1f86a804e73", + "0x05fffdd643f1eefb8083d7c2acdbbd13e076134e05c0ca6bed8049bc747403ec", + "0x1f1d7cb9e496d998ea6f8bdeb31e977dffc942987157f571cfb38037b8585bba", + "0xaac332309b32eb92fd347871dc513987c06b984ac00feecbc686029cd548ed56", + "0x64c449079ba124feb488948f3b17f6e68207992d71cc4a383563510503414932", + "0x0aa5215822bdc1f3151756079fb5435df2abbeeecaae1050d33af6fa1ab37a87", + "0xceddb86f1882bd3e196856e9a43d95a37ef115a4a2d2a86166ae827c7b0e8f0e", + "0x3f368b92f5217f872d9a38c14e5791b72b3b25730962514677e2c069bc04de94", + "0x9ef75df3e9b2e97208b2c4d0917ba6de03b437ccba8e580b9f4023a1c753a354", + "0xaa662f12ed9e399bdfa0e81b7f52c217029166b483a1ee492328d596d95e55c8", + "0xbaee3ebcc9a9de22b3d4067d905beb6c75ec7715d06a426dfba366da9d9b12dc", + "0x4fd3b9baa9f53e6edaa14c9d69f0cf2f0010d9ead83963d96b87b7505b97e83b", + "0x808d62434ef0cbd193a3ec3cdc60f79ffb797ea07d53b98ba1afaa8497cdf7e6", + "0x1da441a4ef8e75eab531c5fadee15bd1a60e93043e6f5b5264912d760ccfd79f", + "0xea233791cf501a1b6d172ecea7ed1e2fcc39555b597fa3ef86f8b63ceffef6cc", + "0x57e04235e51c579d0a7fbf74d469c6a74403cbd09ee56f168a74a3c1fc5940f5", + "0x46d051393b825130670a2d56872467b02033a8eb479d466c6522fc466020e76b", + "0xd9c313e7ac54bf497a315c8af6dc1f3751e10a97dab570b04f80811208f0c2d0", + "0x7219321803c2a129ef429886035bfbc27dd64fd50515c38a7c43bcd184a68380", + "0x140c4deb5543c31c49296e68bbca9d7c10799ff293e05ee130982a6d9f3d4638", + "0x2cb5aac917f6c1519a7cb3094953d9851d5498b954b62b05045ef0fb93924419", + "0x1aaaf2699d5ac7bd2124f3509fb2148dc1c93b940c517eb9d4417b6a6a911a21", + "0xb454286ea98d62faba2a2e531f6e2a42a61e23ea3274605f8e2c776697085dea", + "0x100a6c25a0221361db912b4a491a001032a3cac6909f8d805ef82251f635aaf3", + "0x08a834de8f3ca2dc5f7075f0ef5246f6d824ea4577a3d238b2458676896a846c", + "0x6c0878c40a466520a5b86a06670ff0e1ce97512205ae8c22995d0bc48d9ad222", + "0x6835b4bb12bae675af54580a1fc57f266c050748a583f8b6145306fd3bf86cb9", + "0xa66940592c705e99bccb61c82e6112cc75aad73b605aa09280edeb065718a043", + "0xc565e42a728c1a0211ebce7b5ce9b8088e74ba9b254ff339611806c239dc72f6", + "0xc691d8ad39fab8e168266f1d502f02f269c7f44ddd055dcee529136cce2b835e", + "0x34bd34e5e476acfdef9f3c260a8d6f9d4236e58a9fe60aae99c38f8fbd465b38", + "0x9bf9269591e17668d01181d6a0d71ac17b494340e47034ec5f40f91f0bde9f58", + "0x3f3b051eea4259b90aa1063772ebf6e05563c7de791707bc8e35cc5ae561d110", + "0x7021c2db5c4df7a546d99750bc905b1a9f846afc688b7a033653feb8a9afc12c", + "0xfc7b212e599b58ff35eaa80b49ead63215ea826dcb3edc4df7500334cb929935", + "0x6cf11be5e7c9b94e24ab241b7552fb5bb089ef9b3dcd62bdba020b0332d4cc05", + "0x9353b5f84c414f0274732975cd3449a7ba55aba6665b5cdca14650e6387ffb3c", + "0xa4717f0fecb6a35c60e9669c8a754c1c2d41f6144cefac52531f9870fe0351ef", + "0xae685fc22226cfb05373350506f1f2072406b3d391a4b57ea5c467a9286ddc3c", + "0xd91482bd0e080649387aa580e255d77c197c1734a692296b27470d8f20d971cc", + "0xbe97061686e83466182be54cfdc721637ef26e99b247ea68b814aeef0123f076", + "0x8bc0768fa4670eb28beaea6ebe20a7acc0f23832154b61515bdebc9e6e5aae61", + "0x9570703dace2ff11a3e8d61552baaf3e5e812eca124f42210f1300ccb2116158", + "0x21a68948d1014f8e541adead6c9d0e73f003dbb3cdc145cbfcdaadb1ab0449c9", + "0xad51740caef4a8c01223fe11d7346852ed0873b38a65fe3ab1948e5946e6a8c4", + "0xf5b3e50890db272ac29b8fed050b89a3c1260563466cbde8aa6d50ab66c28a54", + "0x635e873d51779460b6b4dc2683ca229826e5a40ba0f5963a0a27d02eb0370cb9", + "0x13302c3e93380ebbf6b54be15f792f0878396dfd9e8da75689e4dec5f30ca85c", + "0xffb0746ad72d53240895bbdb07d1cc5a94bcf4e6422c0dbe7b2b3fff57587271", + "0x1cef16d47f2975e4517a9bead7df47d7e0db677c68d80e50edca3a81f493cdb9", + "0x50824f81d7ccbdadfdf5fc4b082d920d1416d6f3e6dae3a347e71993a2565ecd", + "0x10707c02efc13e50e5b1a9217ba2602ec77dee3817385d145674eceaa21d2892", + "0xa281eb9159884095b5d3584b67bb7db899464fa176cc90800ed8ccf23eccb4c7", + "0x2f63567e75786960025334d3772571f6fe1ff411b6bf867c84c1455f3954ef13", + "0xe1d9eb8d78fb1365f28d4112b14f3d5575a68f1160813f0d0a2a1faae3266d1c", + "0x7a7dd4036031fe92e19c41af52f5c039fa3c12430d5b79111c568681b640a2a0", + "0x509e93ce350453b61b502de0f87fed794b0d77bb6878225122eee67a1414320f", + "0x679778b1f6732382d834000d24a94223b69a0a88b56d0c070b659fb2daed668c", + "0x49963e1807e7f45e132a7e3d2cf7c10fa0e1c36aae866a9641cc801ee8d261aa", + "0xa1124f9bf0d2b0ef671ddcfca3a66a86545cac8ca9ecc82631480ca091ccea94", + "0x9ec43a9de16fc83ddf5022891c6f5593796122b9f9c72803373c62888c5f9198", + "0x5af071f137c5584bc1766ef4ef092fdd8f4a3a3a64ebaca50b169a22e32d2542", + "0x5b3ada30dc686e15bac396821debd08d8a96fe1450e5180170241cd6527b4bbd", + "0xe56f8e15e3f74d77427424811c6d03b2bc98f010750c6bc83c34b8eb94bee0d1", + "0x5dbe293dc9f09c20091e29ec20249a9b1e52e28ec863afce106f03a75b88b111", + "0xd61adfaf9d822c3d8e981725a42113a13abeb8f887f16e0ae021cb23d7255a2f", + "0x1efdd0a60ad05c11faecde65b02f3ba3fb85bce685a9b89ee4e6a94ddde4b7d5", + "0x60613e04232a203fe161398acf4346652a9b6b9a07837fcc06f53a950136d911", + "0x1b9f0c43d87ba4efaf7781da46f52a6eff60b8c9ca6d81dbe02bf6774fa80047", + "0x41aab71c01968d0cb73fad35b9839f6ba2e8d807852ac9c6f1220d5aef866f74", + "0x67ca5da2f269bfea45e0ec5037bcb1e467d5938eff17760f6b3c5ef74448af69", + "0xc16c2d3a36275d3123d5d95b6ace3bf131af1e6e07de185a8f4e335c5a3cfc1d", + "0xa70dd20159272a4d8e5efe456500a1f710a271b690334e98862a24d31d4427f4", + "0xeb932d05a4e57bd9cbe363a96cc616d0aed481298cb38183bafbaed9384ed27e", + "0xad772cc01c6537fddff8eacd182680e1e8b0d203bbbbc317a4ad9c1e7c1e4b71", + "0x81d7acf3f3760b06a68257654e4b0c466883e3198c61ad71decae53963435817", + "0x33cbfe14923923214b2047d445696b34251f70d53005e84f43e383fa1dd791fe", + "0x8a74d3f3c775f619f9225388fb5172b187141a8ed35ad794c40b2d278e6a388c", + "0x9db16af2961834b804e6330a9f587512c92a137b8656bc5baffb227686fea9db", + "0xa23c0d4f859ad30a4b7d3863ad16926faf41b4623e6adf9c7f2a59815890f4ee", + "0x97da87b047a43970a8e537ed5cb15c0f28885812262e8119c659a3d1a92f8aab", + "0xe0a2d92c83b8ab1bfaad4bd1e717fb5dda967c001ec7662815042a3544bef441", + "0xc70983fb6257a34bd43862c405023db2112f0b1c4a11aebc23ba17eee3ecd1e5", + "0x05798069ba546f61fec26ddc4b260fcadad3a84d612900ee2afc84e10dd13358", + "0x1d12ca65d3a55ecdc19fa4135e47bf470bc6ca85fa6639eeeabe951d9d0ddfa2", + "0xd47f4b22fc0326df733607783934b1be3818fe057afe802216178a450f507c17", + "0x4d71d9619222c0c4ebd0fb25da7e53358ddf4ab2cbd962ad88b37fbb331442ce", + "0x2be4b69114e307d117df01cb568d35f79588ea70e9016f865f641614691cf7c8", + "0xa87d1304e2462cabcf90207b81a058ae766273007821ac98b718467792eff05c", + "0xd94b7d41761d34a27c5cf565f0e92fbb65ccc3e8aeb5b1c1d9d233b032f071ba", + "0x6079df72e53c3f1793bcf5a4d22ed2035e31662d45bfd210c4bdee475335039f", + "0x85506dcffd6342df61e673ac01219c58198558bcaf7759eb2e4b19925ddae8e6", + "0x6a19a7212075395bba890eeaf4709c7025af6ef3087ca86671da478c7e49ed83", + "0x62c52d8ad2b6c000462358d31c652b280bce3676f103d7182fd3646a7f007604", + "0xa600ffa0e6d161287d9b9bfb13ee9dba74699e542aede1278d5344e6688f0382", + "0x7404c9ba05ab64a8e88847dc725b132b627686f4531248ebdd76dc76d42a8b36", + "0xff0198b39d39660cb3c74d455b4019ae0f23fb181378c207bbcb3b79587b8558", + "0xf47db7ef987a8cc32dd3f61b44e6812c940592337c056570d265e1604ef49f4b", + "0xfbf7eeb0951ed5c4704fff2e5d121b831637a86472e32c24a7b85e9daed5e1e5", + "0xdc560bf4b2d849ecb777a4d2d8a3b3bfd12c1a4a295531573fd1d493af34042c", + "0xce32751e3958436d8aa727ab6f89d1354cd0373f288b40395e9105aa905a1713", + "0xdfb4595b9c8bce6108ed0b550f920fd098f6757a3e64734fa97fd5a640c962d8", + "0xfc557009bbfc0b2a47eb486002568e963a32eb664f7a55d81335ef6efa79c804", + "0x9dd310e9b00e69b7d0b0d91db5095065c78bc91f2321df8b5622a490ef940626", + "0xd4ca936ec7acd0d54c6efd71cc6c808573482f2510175ac6251f1cffd7a12ade", + "0x465a05fb5c4c66d0bfa64097c705eccb50bafe9d2ba223ee69111b16c2014c02", + "0x634d4372ed9478b3afaaeb35e316f71cc5d371548260eefe273c236501d5616e", + "0x16ebd07c2e0032fb1c3722df3647e74650b7c1c81f4e7084149a30524846ccf3", + "0x32ee5ef340cf87e26642d11d619eff9ab9e5a1518815b9897b6a95007e2b7572", + "0x47ed528e27c42d8a4100a84957ef81a4784c0272f5e3cb6a68212af492a844fd", + "0xcc376aa2c1b0b49f96bde6390d5ec296fe7d5671d6bb5b5df31cdff2eb5f08f6", + "0xdde8938b019a5e587a28eaf579b5e21892bfc3475ce75112c4f1d9a5ef65dad7", + "0xd0b4c6d8ca3d95851c34e729d9228cfb0f4c4efa7fdcac3d96ea4e4d3e0c0189", + "0xb441266ed512646050c73207c3d88869ccceb7584f3325125a5511d5b05bf5e2", + "0xf05a1a620f1179a6ebe9281fac034638620155bf9260bba7df47d59b54789bd6", + "0xdba9596e7229cee4c339fbd182b0ebd076309e3c8d3653060a94dff252ceca19", + "0x30142242a511348d48171bd65aa191fea095446cefc49967fc7ac88964efb4c9", + "0xed1b743e8baa487193935a6d974a7c417cd42fb761007e5ef168f35486cdc2d2", + "0xfe54e8161aa159b9873dfd4f2d3b7bfd5cc70c4625855b70348684c7a19acdb6", + "0x7993784b62dec4dbdc595450f114290152676cb6938fca76e956968367e34519", + "0x2efca2f1900c2ae1f572342a077afd97fccde2b2305ee0b60fbce8240d6779eb", + "0x99ec617f5f447a4608d5bd96ca375a33a15894270ce22091b60f308298ac51e3", + "0xa38427232204d3f8e58dacb6495799a6c21e60746d26a438d8504dadab3a9bbc", + "0x8c39c623cd06d598ef78e2c46f7506523ab9ae5fba5a7273366ce9a3d9bb977f", + "0x9e15df3655d1a3acc064309382a41e0f7db24925ebf1df51e23e0693d9b119b1", + "0x69b4a6a1cdae2b0f84a18b712286ee304995f6073b6c81fe7ee544ab1d17539f", + "0xe395574bbbc54fbc2e3dc7f363abb34dc760bc2f6fa92c980d26d5c9df5681f5", + "0x1f863b9112535a87fa2e28cecbe0af5c22dbeb41558b93cac0dead527affb06c", + "0x78229983620efa77087391d04057e1971b2ecde2c9f9d959f769e18c1e07a8c5", + "0xcb72a2403046e54d6babc6cb9e2db5f79f09a9c45f983dcf595dae3fece77570", + "0x5267de76947667220d218ec4d1c3db31213d991e59cd0f8e4e547197e4f99205", + "0x55b03de02f92b3210a614f8d0eed98e056a348dce7dc03dc46dec5f7ffa942df", + "0xc2e6b807b1baf078a962a9bf2d92f91c0b326cce0ba6ff59c834b812e55278cb", + "0x94073fbeb062a2fc6f1a0450776b598b1a26aaabb1c1b07e79ba0a816f63ea26", + "0x2521e9b0d16b0129ae594dbea0a2fdc115c379c6c754933ae4dbd20091e8d148", + "0xd2e288fcb68b22fb80e5591bca2524d7a21eef199c3c333a80218a4eb698550c", + "0xc92539e422408c2bbf932556b096d90cea0caeb81e29c2c92552d156c2bf8399", + "0x5ffcc95004131253c4b32e34f7e1db552938b092f7343f501e15554cebe914e4", + "0xea70a44b30ed6d5a7e8e9f85eb8b70aaa05320489054faa40e15989e01ba789e", + "0xcccead6845a28d610af1674a474bd77a5042ce93f682f0c4e879cb0c3e836320", + "0x3ee01f8d3f6948da18f0212ee7c990bdc2ba2eedd020d29461d9d558ac9155c8", + "0xc5aec3935dcbf93026bcd8e24632e405b56ba1ab003cddc169b41372ea6d3a59", + "0x1a9e0622cb0658017749353e3167f38bb1961c4088442f124c1ceee2dab52945", + "0x2628f184beb5baff725a1918ad222900d41a1c3911eca1f272b484fe103ed4d8", + "0x2a03c227d307eb99185eab66ad4fe4b77ac1a0245ec28bf4d87702092461c275", + "0x4664e5b9b20927813f129a23414f319628243cf2ec6d93b0779f0bab5c602803", + "0xdb8978d3303d1c40d51e1608a24102af93c15ef7a82bb3fa8f9a1ffcccf87bf9", + "0xbe63631e76bae0c7eb4ab34abae21f9c827638954578ab93e823d9db8212c08a", + "0xfcb959859a8eed19f6cfabce408e5207206e8e0f96924328355a279f9fe9779a", + "0xc1f2e02c014167d9456a985c669121d543e39a8957aaf328ec1779de2803ee99", + "0x3261a5631dc254f335cfe69cb835f61e11caaf9b0299f0701ebbfd0470755313", + "0x02d30a73d3a7fec6c2567e1f2861113428fae47abcf76167ec432da20515134b", + "0x312e3cc2897aa8eb766bb4d0a91e160884153c90c266d03bb6efed08c4f48cc1", + "0x00f196dae3153faa0b4ce1bb9b3122f76cf1c1398fb43f8aba95037b8a6d797d", + "0x8e4bb4443854630bf0835c3644270462d9b7e8f3fa409e2c8c420010c8dcf0f6", + "0x3ad0e8f5abb1238702bbd95556ac9a9018f4a4ce107bb2d87b7ebc76b0b723ea", + "0x5c78e87df5d32db813c9e89d4934a18490f9ec5e4f2e5e66941db9e6c8dd1129", + "0xac6749f8a149d465b9d98dda8283425ae7e27eacd803baf2babd9254d2da0e97", + "0xf68f70077a1f7d69e5afbc259adbd51fd97cb78e45a38bcd698922ca0f2c17bc", + "0xc6b1279ce5d96648642dfff31d3bf3d989de50143a69dced098f883f2d58ad36", + "0xc4655ffd7482e52531cef2edc1f259f384d69a3920f699f6443e92386a0b63f0", + "0x28ea7302f04d4ff4b2c40fc150908ffc34403d7e5d756be4e980983e08a72db4", + "0x87d6de7a3cfb49ba01d006bfc051c5d22c2671eb67bf3af87291d34bf84dd328", + "0xd156e6fefece7a5613122c24733683863b9a20f7fae1cd022b093c9dfb53b366", + "0x5501015e22d6086647d60e44df262d70d4d8921648bcf366e8442cfd8421e83c", + "0xfe520098dd0cb9422d83d4a562c099bf8445c25e93b3b9a410319f0300b710dd", + "0xbac6ee3d851c6d574004c6645647b2ffa6a3519f13f3ff9529e5924288815a5e", + "0x3fbbb9d0516549cb952f1be5ab6e69d60ef1a8369cfa4d22152d006d886eb604", + "0xbc1843d922c2e0ccd1403a0da02b4f758fad4f02bb09ab823560308826149cf9", + "0x9ec0d3d14623babe08dbba627c2d478ab34a0a0f46984e528499b86755bab02e", + "0x98715273786d911f75085819b301792d241d005934e2d93e8e689c8a5cebc5f0", + "0x0c25dafe9a23ad5d17e97f23782e11152f548c73a0ff58fba9b3a64aa26cfacb", + "0x7a8fc37c03b6ce7d275c2a4160e883787df5010892fd6ffad54dfdd70a97168f", + "0x342816670945437b66e0a12d5eae2d284176a54cee727a5cff6f5fac83845864", + "0x40f472f8737a813a879eb762f085ea5966934e1a4562b212b11612b5fac059f4", + "0x865b00c472ef94d6d9b038f328aeb2eab1e81cbd2da3c4720257c8b4b8b41e76", + "0xe94ee97f38d8989ab6bbd137ed9bad8529c543910e5a328b01fd5571ed63199f", + "0x57ac21373529bdcb22c053797736b9b600737d91ccaf296ea761bdb9d425ed74", + "0x4c56a968ab57aa36196bca9841e308e4ecf366b21891cfeeeb4a7741caf00bdd", + "0x79e087e68abf35a76d839a0792932730441d2f57506fe7db72b9b91b3fbb50d9", + "0xad2df80333074a637b0afdf1dca51759ebb10d7d443adf507f41a99863b2c7ef", + "0xbdc6c77a8ee0b979cfa93d42e4b87c9aee8b0c19e6f19206008a5d1083909478", + "0x369810909434600983003d17fd321e7e73dc4b8ad0e73d925caf499f2b836690", + "0x4d3749a7865675e8a3ecf2b1af4b7c3250a311ca8d20449e71d6d8ffa33a7536", + "0x7286924c10b2db63e61041976ca7c6bd3d6b1ddc69eac4dac03427e0460333fc", + "0x33edb799e39ee239790c7d3a6349218284a61847a235f9b6978accddbb57467c", + "0x0f131efecd5a1deeb5267f4f99e64ad2dc84532f7698802fe500daa8c306c1e6", + "0x96f48183924cca8948eeb08c3c5320aeceaba747c7f7ccc321cf17ec0704ffd6", + "0x87a8c9e79f17968814cc145bdb1325a2b5740cff68f1ef7fc7ae42f20729287a", + "0x3c9d17093214572bfe4e6438528163d7b6e24228dd15ec1986924f07e8b9e197", + "0xe9c033c63af4230d352ee370bcddbcf79a284e113ff5b8f559d5d958a6648f2a", + "0x331c86081b46499c6c0b23304f562afdfa467ae8b390e22718f2abd96dbb6c2f", + "0xf783b03698791d07c2ce40bcd9f69e6f04a4041cecdc85e17b96e6673f57d9d2", + "0xb886c566152b6f0fb43d8eaae97d8f6698caaca12845f63e6b98872d7be91429", + "0x70abce391cbeb52597c5cd3b3668e33aded768c797a1c27d22ef827c3c82e516", + "0x37b6b2d24a95cf8837b6345b7646e48942b1dfc26bbe8a0026715501e56d1d71", + "0x95d0f6f51d5efafcd395e79b83facd5886c4cd68fd725d68305a8bd3e73fa818", + "0x839db36c5e8d539cd87c2fad2221a95728d201e27aa0f21e48c61a4d50088e6e", + "0x85370ab2418ca50bd86694bebf26c9fd59f7b737282375f1a53c06d1aecdacdf", + "0x42270a3434074b79bf19b043bd5cb7a819d5639461edd5eddc24b336fdb39428", + "0x105f93a4dbaef09de92dcb0fde88051d7cc267ed11ee0433c2840ccea459fc81", + "0x2b97bb02b8447cab790dd30d312471385d233b89dd135c0fa59ed763f7f1a5dd", + "0x39d6aebdd41ddb3937963660a188b9e840166fb249c2859cb442588ddaeaa2f8", + "0x635d327dcea329de7bae55e025d4fee8470ffac2ecc8a87cb2a796b822cfbfdc", + "0x53b87772eb46bcb5dc54ee6f2fe0c8ec1384bf13e02057cf2fc551c657a961c9", + "0x9cf06b583c647f8c5e875c6d0cba5ffff00052620c39d49a04095cec4d4c8c51", + "0x917634c039976e153be529547de0799bfb23b733c5edc42e1e54b3e6ebe55d11", + "0x108e5ff2b56ae17fe3dc92203f454fda904dd4e15f4732397fba95f38b632f98", + "0x4bd32df511cab41fdcd3a502704c74f3c7b0eccd352b82b4c3c28b0a844fdff0", + "0xd8e04779971d970f01a77212d1ee2b8926d12194ecfb756f9f83b524c072abfd", + "0x1f0226a32fd46224ee3d9b7424df992847a711660fa1777ee8ec699a255a3046", + "0x9dbdd6b26f1fa61ae7fa8c31bcb259c754e2827d6c6f6e7a604ea84f54770255", + "0xaa59f69dc2ac48f6e2b06cbf95e68ab1d0971ef6ea4274ac4e24263d01a2d0de", + "0x217ab35702c7c959e994c09f9a8bbd939b9711c3a6c4a57d03b02d615561cd13", + "0x8016f4e0ab60e64c8ece505bc77fd8ae9a73e663200615679692057588706ae7", + "0x1ab438e4d7e7ac9e9157c2a43fcd730f7b3bc70676a302784d22dc6e9d7cc1e8", + "0x902a3b829ef54cd702094667296ff452eff0aabd04f76046cc799331da7296b1", + "0x80d14802bae3c50cda30a6830db5122e72f7dcc67544d33857e1153a49bdd710", + "0xaa73faa4c36e080065a0640acfa4cdf16de65fe6aa1b0ed74d20ab01ef675b32", + "0xf129f354afb67cda78b4f05e531e0553b512f8238278a7388a8ea2a26d4fd5fb", + "0x8ac7e8a496a2c3465ce049ed69c0f1a671714369856d705c1037cbd193106dab", + "0x0167c76291a52cd70b1302ef3bca9e8449d071761c1e5520219d70344e0b39c5", + "0x0796256c466d0f1a706462c642813620e7acb3ba0e17b606bd464edd52e0b491", + "0xf9e68a276fb7585b978ccd0a5d4cb07851fab909fe223e1534897574745ac585", + "0x215b53c58f116b3d45669d5257644b866639eede3eebdef04e171d9415247eb7", + "0x69425708be100d1e7402bea70be42a2dc981ff3a4d7c8c5c859283c06d1283c9", + "0xfb0b4c899f6081eb3e2b4bd4d32554c0c34199731d3f80976ac3885cd62ca5d5", + "0xe07304905487800ca8bc3357236b5b4971ccc884612d4c3a0fa903ca4ea399ae", + "0xfc0e4e84ccf10d44f338d76dfd0562cd47db6758dc7f3eb08006dbcd577c5dbd", + "0xd198d19f531de16e174f85a4cf6d3d2d0f326de398f027223f34534c9a6e8634", + "0xf5f6dd24e48fe4b37a5098700dea861fea66c835e81269ec20b911fa821abc8d", + "0x6930e75f5170de31a5cb2f899db422feb51f080f83f49be83951eecb5f913793", + "0x5a8d5ffc0944ebfa1273e0162e766fdb34fada198583876f0931e331ac7164c8", + "0xc6a5ae3002cccdc6bc302c68ce171dd83c385444ab5c04c66b19bf67255e298c", + "0x05c8c483696d2aaf79f985706ff9e84c9ad08defe8399531aa0fb3d32fea316b", + "0xc226d5d5d4df247a1e5cec4fa6fe43605e9ecd27bb07b9c220c4b6ad5a4e70ad", + "0x312cca0e8c4b55bb84176a54926d1366dd5928d9859f1d2ad2991e0fd9624b79", + "0xd0aac977184cfbdf53ee96ffb2e0bbcb85a202b5c6ea02f9989972c70120468c", + "0xdb3b8102d9a9a363174e138b292ca67020f31fc819cb15c5cb9ee34ccf2d2f8f", + "0x1a50ef2cef7ee171832dd9e9162125e51e6ae7c27d08753c30c4a03cc68cd473", + "0x292ef1d673e8441c3850d68ce27faeb572083864ec22f09220cb9f4fe32d560d", + "0x0cd5290d348d7462f747030cbd8a38636f1549023f8f6379c244b91420312ab5", + "0x2a35347668b072eb67d478dfe9146822e77ac78abb4b69cf57478a87c4f5b794", + "0x70d10e3336ea822d3385352737397cdc660ee71e07158fc1ffd711d249daa841", + "0x1f9d182b5e755e9c0ab2f77a9f302b01241c4d235b5b717237b96247a9157141", + "0x93ef750e15a936c2fd6f047e65affabcc2c0b5bdf3cfb160402dd0696329a654", + "0x17c7f10a273615b0f8df2044396e3f0aa33b46ad053bf6ec6dbc32273e66c4b7", + "0xc249c69c4c2d40dd9d0a7fde388824ff734f3b8c2868d1e824a52b32dc05e58c", + "0xc1c1f0f9ff1c472a7e84ae89f8da22575df60fa7f9479fc3638f2f0d02509010", + "0xb563144ab0b85113b0e30109cfbde500c28faafaf922c81939dcf9f1ea469752", + "0x80f88663065d058d2db7e55df1af8035c810e36a6589f4b1989794bb32e337c6", + "0x25905f6b1188bb66393a10ee044c6bc197627d83301f766f4900d36cbdbdf8e3", + "0xb73cbb759230184289722513df452814de312c90f8726e77b129b6e37e5e803f", + "0xff19c1a15bf598da49fc91d04c7401c88b4aab08fa926e467779fb14d8f8002b", + "0x71d46f2c6a4d7291a9906df0beb183d495248599e40c41da6ad30ca3fe37f43c", + "0x5a00726405d2db913571b141053892aa41faf26644c6788c16c8f417df49ede2", + "0xbb046094f514ccd722d224a2bc4b6b473c760224a7b36ba09a21f149cc244e1d", + "0x8b0d483f156f390376c526acc219070a9c393df731b7115753e934af20940621", + "0xead2c2100390e48bf720d19378964dfc1d0071e28c1d799b2d5d529b118290e8", + "0xa4169df4e2f8662b43845598c58a591e6fd17574c0d567d7b702d3f12d4b74e7", + "0xe14c4be058238b81d8025fa71abb580aeaf52d4cc3aa4f05bd5b24be77479fe8", + "0x81e58c707025a640a9b014046559ee7f827142474606ac24ccbd61557b0a7745", + "0xc1717626f785ffd170697d0bd148226d1916f16461796dee609f59ee5eed3423", + "0xdbe52fc3e71b136881136693f3a318d9e3257e6a0384d17768cba6c84b1e3ec3", + "0x7596e35148892b5831e2111a168ea56a59ede2f2222a08f8d82fd235f09cb267", + "0x130fdf1383873279e44ba21c7d1d6f962ab3d71031270a0e5f57f12f90eedcde", + "0x0d52be29476a3b4ec79a432554198255be617f50ddfa6d614ff5b73302c44067", + "0x3f1edff4b943e5fa15bddad5ab9e3248db0335359af255fd6efb66c8b63bce69", + "0xad9909d1fdd9e1de3e6cbca641e42a87e3fa254a0498085790b3e44e6ee42045", + "0xaa166286f4e1799f9ed1033861eb8b2117810d08dcb29cc3f7a3fdcb05989780", + "0x11401b423d204a1df2e918882c3f795e9cd1695ad0badd77532bd2512eb7d1a8", + "0xa738bfb7ad265db50854afc6d8988d5342ba77d7021ad5d5a45efd582b6b6bec", + "0x25051016399b94cef771f16cef661de81bca5f3803c96c602dac42d395679c15", + "0xeec18e2c9d76a98455ea4e50f744926a13b9ac8c6d0f575128787cd134ade901", + "0xaa4a38ed3e461643c1acc765ed8f27262eae8ed1dfd6659c4e4e1ea080f42363", + "0x42760a8066850088424917edca117faf1df6a05221216479aaead8ba11286400", + "0xb3503c3841ccf8bc2853eaa618435036b976c9e77d1e1081a5fceb036091f9d4", + "0xcf5979cabf525d5b6cc1199358688fe0bde640f6ed95dd7c30a6f42a38337cc2", + "0x713df6a565efbb13da4161b545908ac4255af1ce8ab229146ed00954b3c58e4b", + "0x9ff3a78a18a59687eb99e54ac79df1ac920a3361b3b5416dbe95126f8de3074b", + "0xbaa888556f1a519b1b1acd01b4ebcd9132dd55bfec1bef0ffae9408152333fce", + "0x5ced4fedd95a4742ee250948c233f59587eb1624e3adcfb6662f9d81b5517bad", + "0xac1d2706dd82b142b8a629b42ea5c4f13286af59d24f21b3bc36ebdb4eaaeeb6", + "0x1d99ca18dd1d872a4be8ed45385d0898215041f54e7c8e7f3252ca212b4eba3a", + "0x1dc60cc7789fb6b25c3d27868ac49136e09f6bfeb09a20061781ef14cb7c670e", + "0xaec01b8047a0693e78b6ed445396dfe6dea247997ad32747896ba0e30a20d2ea", + "0x977779b6e2da8fde03c1151537d2e03facfd217650e862c3fef07af71f34b507", + "0xa1634dc9c2bdb48762a7bf7b41b74a2e8d4d731b15792044cff1ac3b416fb28b", + "0xc36b6333c28344a360214708798c125adb5e2cf8a2145a216e303186b85a8f91", + "0x1196ee872385c8fea747eba6f5b4fc6f30817714e86c28b657c24593ded2e0ef", + "0x8dc3ccff642a29b46266eee8782b9a9ebcb171ba154c5dca0ca5f73fe24969b0", + "0xa9dbea651bb7a362e526744229b3e1807705ceb492a8e4bc54a9a1f36102a9c3", + "0x0bc82693277412688a6577a6bc95ce99504a1a50904f49b4b4588538f506fbf9", + "0x8df943b0847f1de48f76d6dcaec5cc7b0d7d839ec5e6c8c887f6e5cd785f53cd", + "0x92f4a5577743eb6c34134fa7867cc7d8836fdfd4000abec8cbf1460d136054cc", + "0x047f5f8332e788d1b6be8a3ecbebffa80ad6df670cf4eb4e76a6f22c5781ac14", + "0x392652a55a85300848f43879d9a45d8b7fe5897467743d51a9208b2ac0dc029d", + "0xfa2e6a2cdf4949028be0772f9513169815fa9af20c53ce188680f09f57efa671", + "0x0b86c1c1e683a9e6debddb8ef516b16fb15709b87dcd780ad6cd7f9b9080f4d8", + "0xc8e942935b64b294ff66d4fddc51fc83b0097772bca50140e738d4c034b09cb4", + "0x0b783f8b64650bad1753e4f86fd66b99d7ad03d2da0460bf44965a1f443b70d9", + "0x8a9aa339e78f09ad3d2ffe3999c22684d519219b787ac3cc64c772d1e6508392", + "0xb8a2303cb9d927badda947a962311c699a290545b597f0d242f669392076facf", + "0x34a1bd076dd55dc6eb2e675c5231b3cc8f2a197194c86242c9c5c052a6859d28", + "0x0623254a886ba8bb191d3340e9705ae52e71df2717c6e84bd07a759d1f415d91", + "0x37d451749a48d1cf4780f28360c5445d3b749d8b24f065db07b8df89facab6b9", + "0xd73f2404b935580681cd7f6060b17ff569f7419196e67596f101b6b1d1d913e3", + "0x45029ea3cd0d90900c46df72e6b182bba69ecac1a9289a4719b2f6008f11ffd0", + "0x23315337a4609580ca1835f370575bbe025d2ba5447b61e583119757debfe35a", + "0x581bb3ed8dac28e597d89e9a211bce6b011a4b6dabf001597b68e5e8c6ab7c8f", + "0xb71f91dc1a2eae9ff7e0aacbdcd3d8a77302437ba0e9d327790c5b92c7603ac2", + "0xde661510c4713c64961a0ce9db1eaaecfa509e66446998450178c0afb4f5a670", + "0x9070334bbe9fbda52e42939155c43daa239833ed2d952eaa5eb4d5fa8d56b708", + "0x7dc07b309fa5e188f2e417525e9228b80f84c6c36ac00cd3e49defa0e0146e15", + "0x2e9ecefa3e28f4b31b876e5d9214877ac00be6e7eb65a26990fdd5a2799dec54", + "0x6c4b8dc62fdb63d5da2e1387a50759e82f00f7b3cb9c4306d98fa1a4630e0136", + "0x1244fa807ba8ff8a27b88ea1146c3386e96995ab1d7b0f491efedb89a2400028", + "0x7b18bd023a26e920b03e517b3399304c2d277045d2bc62ea7ca0ff8dff1e453c", + "0xa71afca6a9475974486a698a897c34cd6b5a7dec8023c62bf252f60b3d63c306", + "0x4162c46dcf1961704d020fb0bbee7f9c0ea85f4dfc58d6db2c09f5ef0df543ec", + "0x651d2fdf296254f97528a2f7b2d2395438b1f58ed11f4d1cc076f1fd04912824", + "0xc2705774d530011afebc9430c39aaa28e27962427e46164822bfd6da9948887e", + "0x9c88eb61fae9cc442bb10503b32a439b771f8ed67d626c4980ccb3d53cd24fde", + "0x8f86c85d3df630b1f292217035ede6ab978d9388fb6bfff402419c3de0a8406e", + "0xf8946728a1deb862861627606bbfb887e66d743d47fe03048b9b1799e083c5b3", + "0x023cc1ee86a5dc550b934e28e668e3de2c0a925c9c0dc9fcabec34d33da07a75", + "0xa40e3c2eb6531a40239499165490be2d6a986f356c3e50c4449a81a9ee037df2", + "0xe85b21c59987f25bad5ae1ee09dadf22ab33530cd907ed72819ebade1e2b84f7", + "0x9acd8cc33e4e79adffd4fd50fab9ba6025822f2510b165d547c3bdc037cb7629", + "0xb07c7c23039d83efb37b63873d4c7dabcb4e9265bad35f9a1667114159cbefe9", + "0x3b470afc97f674222850a489a3c845a1c68384c88c03ee3946ceb90924c7554e", + "0x669492bc00a496fcfa5920c9617f74170242d2382c4cd231d22e48e991b12687", + "0x0e4a605fed37c87fe9bb0de8ee63f571785aa3da8767093cda45da51414a38ea", + "0x5a25cabd5470cf246164f60ca6c963a3960550e684ca051958c2f768af92e227", + "0xf6dc542a83245efdddccc4527ce125c5e4409045afb2e57e11941d4d5c21015d", + "0xca22cafbdd6ce48b7cd3700090a5b9ce0dcc267a16e5a6e55df4bcbf681f7e88", + "0x9d89784fcd3cd9a0fe85b82d2329a1561d46026abf132289126d001cd6507bdb", + "0x7fe02ef1e87c7b1294501dcf0fb97fcf5b8f2b131ea00b3559dba67feb7431e1", + "0x0c5870d20c5e086520c1a4fe39d09933da794f49b5e4db186b7fba5f55751334", + "0xa6664e2bafcc0b548a41c272568d2a3ffc0328f64c3f4ac80e3c0dca18253ebe", + "0x539fc6955f7ceb8b9b662534e98512be388ed59e3e3b2fc21226569c3a85f272", + "0x1f4a0c45d47e378f8c0cbe4603e6e8ef3131ca7cbd4a732a4c5a081bab3cb381", + "0x1be1bc472e060fbaeedee56fe03a42cd74afef7321904c64ca7d384ecdf39be2", + "0x3a74f2b96cd47864ee7b113651a176c328c23059e9b8f8bc2111a071895d2bb8", + "0xdc05d7314a2fe10122a1a4f037b96e960ce344202328932e8f069d3801881606", + "0xf5b2462912f2a53b8261d4f63e788289213ca566ca915f59969c79b6698aac80", + "0x8febf165b4673b4762bfc7d1c4f8a82cdc5649065f080bf00e843a0ae3f3dd53", + "0x1ed2f84e593f6356522b0bd4606209b1291518a7ff708d21ad8765572292f3ad", + "0xb7af3b32db199c91881f8b7d23f53376c310cc1cee191cd62a1878a229dbef3b", + "0xd5711bb07409bb27c5fb8164cb2860c0d6583e5f5f5882c6e6e74b0cfca540a0", + "0xa8d7cc512c42b6028121f6114b974163f687ac19a5c71b1c25789a7ba8f38236", + "0x72f3aa7c612ff18149497e90fdc03e766686e37209573a3e5bf144a9a010192e", + "0x6950075793f3170d754bc05fdb31856d6d15fa389281c7e4f73d8c597b1aa4da", + "0x34f1a8805f65b0a09f1e5523a53d35cfc12dfdecc22e47efc80ce209d40dd514", + "0xc348d949ce4bd0299853e6d616dc292a8d33485ad93924f7b9ce64666594a44a", + "0x0f828822e7a2250d0b8f2b819ff27631f5d50a3168874ba1c1922babce345fe5", + "0x92c5275a7bb840f9ea612340bca5275dee489cc7e2b13e6037ed49b3606ec01e", + "0x29e227f64c8446f49611f9d2dbe04b8328b8165193b5a0a2ad8c337e60d7ae74", + "0x4032647c4576c6e4f23dcf87377e7a403ccccb4785e276d404bc8e6fd66aebf4", + "0x971070530b371733bf83599364270fde641d64155807d36c8ec79e73e3c990ac", + "0x52d8cb01d3999e01141667df6f11143754e7cdc1b12f5b5bbe5e16f51af1acce", + "0xdd821e13bef48af8e79041e7a5e28db421502e98aa41382d2a89480dadd0e7e5", + "0xa785dea19433b90dac8703f0b075df9d3c91394826e6b8b89abd806f5cec6ce0", + "0xe82804babcdbf1af8f6fac1dd0076550928c372d0a9dda8c0560a6f378e2c970", + "0x1dd41ff3d620b1ae3725debcb26edec36cc219292bf651fe9305a0fd08523042", + "0x9e2d84e8a10b32fae0b29b150f747041b8eed743d2e3026991c31c607839ba09", + "0x79ca039f12ab21e7ecb6d6629a501515d94a56a3c24d669474624603a0ed0c90", + "0x7cd387f75eadccb5aea925124377d09be0e7b4a9af6463dd649213c5af521236", + "0x31fa2bb5eb784c0dcbdeedc7d439704bd44294ec2bb1b4f49e6bf508381ed521", + "0xd0772b6dccbe61b5b2cbec0a95a9542fb9b89e9a75b8324e05855039c2914dcc", + "0xddcb84f63b7469935bd4f6ec405ecd619a5539c89c2cd28250065ba9b4984838", + "0xd866486a928c2a69b054339b5e57bde41149c63e6e16c066046e3fe8018cba2b", + "0x7246af9a036f7b773bff1b2f9b388618402738e6bcd03c354816c507aa7fdebe", + "0xd16bd03e80088d51f007a7068697e19cc91a0daa96816ab336135b0e1bda7e1a", + "0x3571d1fe22f56864df7584f73958b5cf9336b6afe6702cc4ec9d1f097c4fc58d", + "0x74e19b61d2a511e2b067662bb61bf93241bf538d74face662561a5d170c8c2ad", + "0x01720ba8bf55fe1caac98a1cc5843f24a1376bbd08cf630c9a39fe219ec31b68", + "0xe2d0a0e01f6557e5ea96808c0cdb7618d36bb5bdbbd1d9e2fbffc07672a120cd", + "0x0ff2e856f17849a4349c70aa6fe2c04d7fffcc6e41860774210c2c2a0208f2c0", + "0xfbe09f759660388a823894be477df3491bb6c51ef7df038dbf12ab9c122b358f", + "0x9e610db00a109f0b4a4cbb094b88680f62dfe5d3526bc83377645cfcf6c03b18", + "0xe7460e14d9b5365c3b1306eaca86ffe4f996f07909c16bfbd731443d91df1931", + "0xe7c05451729b5eb5b61a3f159d522424fefa1b30f3929366289a89b714f59609", + "0x28055b2961079028d017758cd43b12e4046fca9a1ef53e09c3a4e0da3f8ff197", + "0xac65523c531d9ca6551cb6b9336d1e78671c7aeee7208f30548b3c010d79b15a", + "0x3c6939d2ef02c1c6e9aaf6aa1119fd4fa2682b71d5b010708a59144641b794ae", + "0x40cc42a031994649aa861a544d6fb7c02aabd574a5b14d26e7802d5cf2574e36", + "0x054cfeb8044d5dffd1577bf0e45dcdc53d15c308cadcc4d022d3c0dbc292efc2", + "0x337dd5519a0f33011f3d27da8e1d2cf43dc249597f199d5388cefa6200e9e3b6", + "0x3263b6e7d5c31f7873fe32542124bf2b11ce9520c629bc8b277e1dc9152717cf", + "0xce5e58922cf860beff17177cea7aa6c0e789233ba1b15cfaab6896c45dab7e3e", + "0x7b5bd2a6e19812524fc1c1c9c6ee8b970ddc3684b4a5bc3fe865dc28726032f3", + "0xbb76c2c12be46fb4a8d18e15817f9e77547acea67f71cd3bf0f13ebcf674080c", + "0x300838c866e4c8ec4e7d130a3191f3122795c26757ebeac2d51eb69f19319624", + "0x53b73eb4f155c5f126802db8f2a64ba08d93972743f3308454be1c0c68c7f4e5", + "0x4e0de18b8bb30adcd9770ee1cab2e0fb45225737e382b4376b08d4e64fab577e", + "0x3a6ca6bdeee957b6ed7297760bbcf53bc5b9945ef1fecfe8c9dbc077926d519a", + "0x014dea0c5dfe7db3b67349ebed21bae42f40f02857456827c72ace435b6b8632", + "0xa0b1b3f7f3883d34cb1ee9789c36b607ada517c526eea23193a786d72196ee1d", + "0x2bc8ee35c612ac2a9c9e5b97d6d3b9c458f7d8825c89c2d593faba38ca116357", + "0x6d61cf0212c61b9c608d7b0a5b2395e9ff41a815f429ed05abecfe350fa5c22b", + "0xcd284b589d6f3ea56a8eac1f7e17d4932c809ebe955300c3b1da554f7d0998cc", + "0x56b3afb245b853cb2cd2a9e6b938819db181b7541003fc298347b646cd0bd894", + "0xe24a047cf216f0837c52bfaccbcf4810026bc374618465d94480d5d222c37ec4", + "0x5e80313f3a14f41e63ae80192b79456d742e0a43d89e505d938e5d0b164a2b6c", + "0xcacdae3d23fd91db005624bd007f1f0c9bc48ee212e6ca2aab8b189b687b9f0d", + "0x2a0f603aeadf258feaf46805767fd42a61ccc3f031f42f131c4548875a887ba5", + "0xf90ac12554e9929db35620b33a5b61cef0cea7209a83f9ab44937dddb4c96bb3", + "0x1a4eb8628a48f763284900ffbd01107879f41c9479bc76c139db520d34295fc4", + "0xe0b600503c7f112ccc29a447ccd1c85285a304b403aaf0d227b3a7c7ffa4d9b8", + "0x1ecc791b207e1d79e10a9252fcb35591b0cabf71910eb50bde734ff7a364945c", + "0x7dc509f4b620ccc2de77ee7064b910a831a577008b8f099b76c2c9382c4ab31a", + "0xe94bf1b965b4a10ea548653cfccc63720c9945b3f02785b650c9ed26047855c6", + "0x19001b2e4a67c28e925ee2656aa6b8918c3b6d785101f4810ac9ba46cbd80d0a", + "0x717a34a0113fba177f11307a4b06f467fe4bff06f1bb38fea0c272ccd9256db6", + "0x1f8815eb7335b0a2b0b4d0cabc9f9fd97ec2fca78a69fe7c048ca540cdd303e3", + "0x56897445682734394c0e6923826b78222719c51dc288529d2ad2ffede4649f34", + "0x3c059f0c0e1809877da2fc06b1384e1ab651dee6b373980b0c5c64da57dc137e", + "0x7a0a2968b2b0458e653fda06a611c892ea1983ca852e31d916415244f06142a7", + "0x4711f167a6332b0dcd27e4530a498d2d5585dbad9b6a5d5e71d4dd07ad57adce", + "0xbdc064506c9f16e3a9607e7864ad1b4471c1fc9e0be5f98aefc5a330a33ba805", + "0x16d54ff57301af6239313e4b98e163d1a23ce6a0aa82d08e989edd31b24ba999", + "0x225a99bdb550da94a9a4717ae178f7def59f799ef36fe9d392a31a344ef6fe4a", + "0x3e502698ef85c6246dd7040f311f277f705bc028644a873df537b80b98d1fea1", + "0xe6969b55177b17b69d45c8e569fa83d00a75723ce5ba8a4b5ca67202ac5c353c", + "0x4358581c44a2caf58f64b86802c3729cf77f2b14df54c18ceac805b219440b0e", + "0xab33a0104b936b5e1be1e9d197619872eda67745ecd61f864149ff6932acfc3c", + "0x9da92be95e0f0d0e284bd8d2d3ee01b53b60482878b1434726df51e270fdf603", + "0x9543a4c4fcbe9d86084fc4b9431cfb8124919413c03f83e8666f2ec6376f0982", + "0x7ede3504ac4981e29634f703ac85e69ab389df3ee6f6ae62f6721b500fdddd20", + "0xe2ccb0b4c4114f266c9cbb387d255ee86971496aecfb57bd98f7ff1808384e06", + "0x971d50027dcde0cc2443d898438b181c772699bc7d4ed5d501258c4a8ba32de7", + "0x449ebd55963c7777e5d5523521810f69a5d4bcd6b20f558a2a5f904646dea0a0", + "0x5379acbafaa3c16ee514482e0497d4f3938399788f207adc9a77312c11d4aeb9", + "0x704ed53672d490277a0fe111fcd7790cd2fb53d94ef9d65196e315d788e38f60", + "0xfb0ff7818c21bd9883af9e04f5a2b9e5bb43173c8f791972d410d302b5dbf674", + "0x80a5bdb23c110c6acf90e6efc194eabf88d054cf1f27588ca77fbec650878544", + "0xdecf2911a31b325760b1df7fc9d9df80f28a6bca798f5503b2f73f93a4404a38", + "0x5bf29b9b96265b010a33a4bc05967edfac53c6fb3e8a8000d8c60fa869a8e8f0", + "0x24e0a4762cac65712ea029233970aded11c859dc4a5abf9c673664c35078ac1f", + "0x68327c02f690d8edd22330efefdb6233e2c8bbcb0c0ec4905694b1833adec387", + "0xbef7609a716a8d2623ead6eb31daebfbd9421816c467a4f80d027ba7dc1902cd", + "0xed29b3bc58c2dac4b2874e34dcbb83e32fff33b28437260d17edefa4ea1cbdbd", + "0x1c11469afb27568d93adc3d9af95075530067a6411b06431016efc05b021e6c3", + "0x1c1b432281eaea836c78fc78edc7ba02b3a5d6451a13c556fb46b682b02504c6", + "0xeebaab0904bfa1331f72dbc02e73b05303b4a71f2bea6b8e1b9855dd9b2d67d0", + "0xad36e72940ba9dfab81721c33d05bad5b412c774026a76ca0caaa8c6819e2f4d", + "0x5101be8446b66c40c7c375a663001070c6aaac7ccb559bc088e773f3370e8d97", + "0x3be827089ad34a73b9f6f8bbe1c7c414cdbcafc6573f6d987c3fe3e39f8fc043", + "0x16d5eca9c07ad9a133cf54c455d27030121482a1db142988d71770d083090dd5", + "0x4c2f8374663d94c4677d199b463f16916d307430ca39d36650be11e8dfe39c91", + "0xc26e2080ec735af4b67f9058e2289477ad970625d2e8cd7c4724d3844ee68936", + "0x49b9428a3cfb02fab5ecd3b603a68df495e36219f5c5a7335237137c9855c70f", + "0x55a4f76f8dc95a2a84666954727057845eb0ecf6ee456295739255c8fef23b81", + "0xe2efe0ae812195d68c6f8f846ad0fb90240984a444a2b2b5ba1096e8d299c96c", + "0x585d1f644a9802cb9a742d373a711616ff5ab0f3ba0a5f685d3dea11b041d289", + "0xaf87cda7cde3ff36e1a4a0a3a90e42052a72983f21331d78aba1e4c681eeb591", + "0x4f2cc01d0e07e9634505030847ed039ddbe4138ea4b2425e7b24188043b6fdfe", + "0x13a10286bc58803a4dbbd59c59dbf3c9ebb605d4ed0417a525fe30575331f4cd", + "0x9b54f33798b2d69512c527b1e92017351ea98f7edd956a1744d7ae00acdd096e", + "0xd5d636fd588c85c54d6f797ffa674e526caf5dab6e4e87f26c70c4462eef6c8c", + "0x47a182d7f196f6cd6f8b8818a9be457ce06fe8b93ef53c5669c756b23a07c5f1", + "0x2cebac7f3796b470e9c75693b1a8596bdbd2a89de862ab60ece1957d7b5383f3", + "0x7ce7e3078c683ec1573b2cd41b0b5fc62c28af48a23454b177699fec9ab322de", + "0x7d4f2be3a45bad9abc61edcbba9d86234f0460145e358dc6566ef623c50b91f2", + "0xfdaba3ce59b5e7ccd1f4a9e84bdd5feeb2b135603ffa49b86a7ce89eac635ce6", + "0x8c72f673640ed0564c3de1280414dabeb89cfe0ce6f49ebb81c3d7f0b1e6d55a", + "0xacacfcbe5ae3d3a9d1b3886bf8ec26bff6f84a23ae5dae8820ea138edb974c3b", + "0x3f2339582dc6834395622db187e6798edbcbe2f2e8458c2216779c1bb14d45dc", + "0x73a444fa697c736d392cd6d2b347d98734f5329d5910520419f391a443fdbd44", + "0xe03bb06673dd6d6e82f2d21c71053e62d3b1dd649d5e7a38150b83b0f085a473", + "0xc68461b94228db05a62a234ad0569aefbf31fa59fdba3d144c3a619d05fea871", + "0xb44575ebdc05026549326e837692dd60253155f001c7fa6f789568b7a136e8db", + "0xad0159409b99c1dead470cd92b3a9d4bfe25266d7400595c93f9276d8d580312", + "0x545c4abba819ad9a1d719991a5a0ca289932466fbc62471250e6c9148727a02e", + "0x5fa8b86faf4cec2e9de78f071822dc0af0198fcec23e1693e35ebb765d97d12c", + "0x2159886570715c7aa819fafea2166c045832707f76958fe456cf7af5ce8d73b6", + "0xff22db024b0e036d81383646db787cb0b1aeb713dc3387b788fd43a283a4a27c", + "0x7f229da8a815b51cdeb7b3662b7c902238b540ded6f224ce5568514032832ec3", + "0xa5e57b07a872a68d2cf2ee6fe73baeffdd4b5c3a423d2a819e67dd73dd36fb83", + "0xf0ad72ee27fb47af398972ea5ee5d07ff21091a0ee610a015da1f677ac04beeb", + "0xdbca73f967cd079e0c863e9be4194acfd63857fa71ab93c3d4c44dec0f88f9f8", + "0x29f4dafd87f712ebbe677193aa86ac807dc933cf0ab895c95feb8c19a46a78c8", + "0xef79cedefe4aab625ea24882c8e5493ac4540ddfa7ec8ac72d06d6c264a2a43f", + "0x377ad1711a2818e94eff19e349d34a5a7c0eaf3ad514b918efa58d48980f8dc7", + "0xca40524371fd62f82dfc73b74acd6f103bad8e81c22dcfe2c3b56d0941a2bf03", + "0x14f6e3daa8495585abf5030e856f742b63e2c3b8fab950fbcf61438d203b3481", + "0xfab710e7caecd295113ae04e2da94fd089d0cf2ed1bb804fc961bbda414fc78a", + "0x6a4ea10fee2a5029011fe25fc6788d1ffd6cdaeca4105b146745883ca457f7f2", + "0x3d16f801b58a8a0362178a2de3667967ac8e8e5600dec667098c56534e95c51e", + "0xe6b91400dbbe1279d8001c9ebbb951b85ae815e0812b3d706a257fd7cafc72a9", + "0x833f7223f1f9c216001b67fc3c5869c012ac7bdadf07a750bf5d504b8da9dac8", + "0x1dadfa642e018d46c90e4c9b45e7d22f964c713c829f28e7dd94456efb5ba769", + "0x9b0b0f0f9255c2ff2b0c0d9b70baea541997a2981b3e1cec904420c02c8e68f9", + "0x0ab920eab62b4a0fa9751d42b4656be510a8724337fe0adf54224cfea0b0e3b9", + "0x1669cc89308825fbcf62478c1d6d4a979d730bde70c1512afc0926e0d4e23929", + "0x66b011d44e8e971ad3063722cf5d6238c519d57c0a94c760e5c307a3158e4161", + "0x0a4ac475f2b54e24784b10b18df0086a92c407e2474479357baccd46086ea81c", + "0x988a166c64baeb21ff5ade5d2c563ef93ad0452c67015a1c472b2fc14944fcb7", + "0xac583c02f23326514085d3b816973c5907541f1efdb31373b399a67807ad1322", + "0x51d7e2b08d025386fb4b335b0d0e2d07f55b9208fff10d3a362832dc6a7547e0", + "0x6c4e553f3ce7b7c80cbce8d3c107cdcb8a7b4c89ebca8f1057e526552f1c979c", + "0x870d63c54b37873abfc8b0c8cc7bfa2b41261f77550251ab59a8f304417dbcd5", + "0x6db3c454f0583e94ba43aeca2c4ef413f80555eaef5829b1c4c1708d63d3b4e6", + "0x6cc1ddaf69d050d9b492d559de84c69232e43b7607cb6807933b1801cb4beea4", + "0xc7c0aa117b17ceef132579c731956b015ebaf3e802b85ee0a6c825f124a4451e", + "0x8bf5ba7ba1184ca6aa5f6915e418e455bb096403d43746ade9b5895fa8d0e69a", + "0x13bb12782984d634332842ce7b72a981a17bdd34ee6acd9adbf65844b96d7b48", + "0x88a59590e8e1366ec65ddf718dedbb5540a06317613a7dbe130c4a570f7f2963", + "0x8a89daffb49834fc7f2341d00151f952ef7d1a7299ab92b3fc926e442d9dad74", + "0x78afc35edc10009ad9fa1b51bb7774a48d7e53a960ebdd8818e51899fd8f5cc5", + "0x0e25764281bb700a43644ed3455c279969b80f5c537f4096934413a7904863a0", + "0xdad129d76c4fe2fc61c1af14ed7556321989efaef6824aee0436216504a36e1a", + "0x9e4fc1c9dea1e18cc4147cfc52cab7eaa90930a2b393a442238a8c5378cd4274", + "0x08c8c7dc91c967fdb900953d543b4975ec90b8def04eadcf7885c555c782ba5f", + "0x07d599a0c521f72463b80cbb534bf4dc605bf345dc1c5de1db13b55486d382d7", + "0x420c5f035c465ba9127bc832eb0f757814a6769dd9fce22b28f4a3a3e9cdfd33", + "0x2dc76172b77b50b249a5a5b9d5e23595b3310aff6199d7335eb94881d8d24906", + "0xd2fbcd5e757bd9eec8698e53088491d63b36854d315d278d1a2bd81988485077", + "0x3544b07abaf58fa8c4aa0dd1f0060c11e0de7cc1ed04b18f5f90cc196d3c99b3", + "0xe238c7374ba22d723699269c9c943eba814aa1afdb95d5e00707b3cac06a5f2a", + "0x1cfadf02760942304c47e07d07a7acbdfe7417c62d330b5802f598a7f550863f", + "0x65ad9de6d69c6e80c1cabd34f49c4c9d74b5986ecdf2e3bac30833a599cb792c", + "0x27f8b2a3a019abec3e4487fb353bd9ec0ac11a160c6c3a83df6ca959d404aa6d", + "0x6c814f0081795e7cdcf730b0807a3c91c7c656132d0d9ec2d9e39f1bb8f57526", + "0xec686e5d76bb74cc369ce26db62b742235bcf6ad7e7c5ef6872d62c45bdfd5d7", + "0x09941fefe99a62e910b1a0ab5751c36eef7d21b1740f2b9e25d814af27169649", + "0xc0cf4f22b4e05e192421baa0af9c888bd976239e70046a62cfb5b018d9205378", + "0x6c45ab7adedfa0a6a133d288a7b8ba2d213fce73124d78b0fa12e1360d930714", + "0x85872e50c368df9ce35b61d5363ce8db27f123661ae528334c6424a953197d00", + "0x6869696612636971deda05c67f663242977351aaaea43d9cdb718be5948f080b", + "0x291097a53461b3ec1740b0b61c129d827f0cf731e16defcfede2d7a544bf52f2", + "0x438259d8150e73fc0d2ec553a2143e1fa5d3e53e27b0533ca20ab37991050b4b", + "0xb1889369c5a7f3385258a7d63dcbe5b05ae2e2ff4ff1e5cf82e5b582a02e895b", + "0x3b03ee5f95e9588caaf54878d8b6c746dbeea3e695bf768651fd3c8e3255cade", + "0x60443bf8bbbec8f21d35a3d16c5cdafec1dd3bdf4b9c447bbeda74fd92655efd", + "0x61bbd3649fd7f9bdab23d6693135ec6dcf07adf1560d9bda68cbc97ae4917b42", + "0xc019e34634d76e06d2984f7201c445cf6fa3f72f3164c148de2b1827eed0b6b9", + "0x3031e3e27af9bcca0902254143e3a440d40dfed6bd69ee5095387347559fd231", + "0x6600ccda7d374610e0c8d0b7fb012bf5cb59966aa0d93f65850db3c6e4544bab", + "0x8e692bc730330a2f4bacebaae77983a1831372e64fb9578d42701a305f972ddf", + "0xb504ec0ea7de6f102f2b59f698d9f7a63893fa352fbe7d17bf05f9c0e0c7c0ea", + "0x8e8d3763f75dfb8c07fc5f609bd6becf34b6a19cadcf52a593cca1098bb2e56e", + "0xa30d9cca760ef7f64b647573f17adefe0da9694567c0948c677024388ff51617", + "0x61a0157d34d3474a167146c9dee2870b409cd9f96c9d2e530764a296b3b33d0d", + "0x625183a74036771889d7177b16cb7bc6982b1a68814c5140b4a31157ff953a2b", + "0x9c1fe65834f3733fef1fc14c47fbcbb270c01384975490ec7a483b5a29ab6a7d", + "0xf334351dae09bcd536fcf96d031457d0c1158d422adb8980b194da88670ffcbb", + "0xa9a92557684b04bfda34805c6588e1cb9fa44d7c73803c28a12a4cce0e46d222", + "0x9e5c9a5df1dfb47ea6cf46f0421492ed1613039789ab91d237ce65a1ef6826ec", + "0xc3fe546f606cee75b2c28ad343ae67da95b99b507742aa1814fcf472a8874bed", + "0x73a429e5ad90697799123575999fa28922436600934689457b0b5ae787e3aa20", + "0xbd8ce8cb98c7f263a6b8cb909b671782cb57ee111180f565c988bd97c9369ce1", + "0x45a0254dd0944f5419fa6a658a124de3a8fdf53dd799d2ff71fb1256b6648273", + "0x598cba26ab789de29b9043f53d02872d6fa2fc86df238d54786b14e4ad8bcbe9", + "0x402d9649e59f50016019d9ab761a04e1d8f25f2a97ccd4a2904d75c5dfb9ce81", + "0xbd95655f6839d84cf58cbc68dffd2bdabcac38c94075a57c5e7a0a0ecd869f74", + "0x4d77bfe9ac91859b67daaa6cb69774f5531da13688940b694f8f2091f7e491d1", + "0x96d64bb9ca88385fb4ba76ee2ddce7430e473cad90eb3670770836dc2896e8ce", + "0xe516b19438ddafc8da621c6a2bc9effdfb808fe0acfac5d393206ffc68588dc0", + "0x1e9de2002c4d53783fca77d298a6bc14d20a4009b5f4e2e75aef296f906cbc42", + "0xf48f040e7669e00ff657bdc8e9bfd3350febc9e12e79b4c00a2d4388a8a45ac8", + "0x31df46dcfffdd4888d9f30725330b7a329d84784bb8aa0d6d9849cc8f3473584", + "0x12e4748aef8d3531047f3fbc6565e75732e8233c056822e33b57998f9e9f9afc", + "0x06831c9310afc9c2c99089e3f438559bbd70bf9f2a3f19f70365e734b8cff44c", + "0x3030d21e8c767be27b23b88b8212838d5459f6249b589416a594938e06db0fd6", + "0xaf88280fe52dff5d696f848c215a44c620e17124b5bc8b8bae6b34dfddb37ba7", + "0xb847564c15894ec5ba87bf5dd837c188d4ec6b7db2b2ae6073a2bf77ffa17ed9", + "0x0a45ff3cff8f73efcb50a52dadfead10a33dfa96ffc448585f3a088a7c2e79b2", + "0xbceb78b5ff2b7273be272a091e1c98d38a2f483fd19e343c4db8121fc70b2f34", + "0x83c298d086b02024efac24441882a718beee901fa8a944cecd24aea6b9005449", + "0x5dfb5a3137ae200bf23dddd4b13ffae188c322e5c470d0f3495ff8f0d4ef6985", + "0x51895491df5733e5022b2303ade0bb8b8b6e6d439e43383eddd082002e16598e", + "0x22656f6e9c7214bc7d2c0f121ebc0897438975660b9b1e4fa9712cce2ce84493", + "0xf2f4e465480d1ef584ec70807388e54a0c7cce1a903c03ad0466f4b37181bb58", + "0x7167b3609994e94ac8223c7c323946fae88d73471fb95fc7fb717d8deaf5b3ae", + "0x74c36cd3ce1422c4143e491cf682206108ab7568693428ec4b483759d19ff7d9", + "0x03357b1a3eac59d49874ff8d9aba1e29d4535a316388dae0c316773be881c18e", + "0xbdb0f8fc10c26110a114ad1f9cf0cd54023b8ba474046bace64136d49a791ff7", + "0x27b57053f34bf545c813d46e566375aabaf99fc2827925fc23f6203d38612163", + "0x31aca1f70964ab87660bf7a63bf4964b7905f65b017af5d639c196971eec3125", + "0x9375fcc8ab004f2df64c8fe8e19dc20532c67e5c342837321a3609ce725934f9", + "0xbce051385a443a1ca558b7aba4e55946aed0a5755336ea38af3030abf60ee40d", + "0x14a76480b77bc2460de6b5419e1e318f6720e4586830b658f86a20d708c3af43", + "0x2873b2b0b97a6ec72a09b3ebf4b7923b17dd36898436a214f86eb619f4d58dfd", + "0x386877626e8664184e4103f320bf355f76e67a6f69d66b5fff74e84758428bd3", + "0xc0952bd50c3b54fc4ceff46a5f23b6a7763cd430e32bdeafe0775c220dd682c6", + "0xf91d80b63ff281bdd9b1290f4e8ed7f4cc9ff60a8047c074a8480a9f73e59000", + "0xd35b38b380b3c36482f2d97b256249d89bc15c850e1cc3f7e12c55e09b5fb852", + "0xd335bc4c5a57bf964886fce85d002a85a803ada83e0fc85f41f43f400e73b913", + "0x2b95c50486d089e62e5d450cfae356972974f0ab062d49bd268997dbaa760c8c", + "0x33d5aba3096ad51b23e8274d797f95ba283e5f549de906f5f973ddf34e312276", + "0x3a24b73d651d02bf0fb749a14bacda842841ea6781a8a44ea4c58a76e9058e93", + "0xbf10ba795be7c55d841721b96b93d3018b7b917bbcee810e189f98cd45045189", + "0x974d8b9f5f6178f475bb8e45b3c8e5af7df0e5636d3c60277b81a34530b45afc", + "0xb75cc207386b04e8c7dc2035bd0192026035318c0d86b7eaac06c93981084cde", + "0x4a39775926b7e19fe5f233eeec5c8b6ae010e47aca27f528479fcbf2a6b2b39d", + "0xffe519fe70c73b04432f3adcb64a9bdd26f7d73dbc16a1b887e1fca6d1f843c2", + "0x488f3ad9d1ef8b305cb8e0fab06495324220883c99406a4573c205a4c159d8f2", + "0x64cb40e66f46d07ff7fc28017111ec2ef4238ccb50e9c79f3e45647a88a0de20", + "0xf58afda836bf1e0f5d257c7af4d188898515a7a27d23b75cef17393c99375720", + "0x9202bcb7fd7c4c93c5b4f77ed6aab4512e49f259ae59100623bf30823fda44ee", + "0x6900bf2088111a96674b50745c7c52b27c786743a82a5bc9dfa7a35ae112c2fe", + "0xf40e163a6210107affac95864f2a5536b2a2353947dc5624e5505d21ac773d5b", + "0x4ec5967eff972330b55894a7fbc27dfd8d62ecd6f4c8c1088973d0ac0e355b67", + "0xdbabadb3c7910eb6f56a21083cd8e8ddac5247a2710c53db8704a4dd48fee1d4", + "0xf447e925a34d76a3dcc9194393fdec693f1923a61b6dfe2b9f371a6ac66bf705", + "0x6fdc2650f4f76f84c39d821b76d92fcf5ca9e3f0a27225f2d82ce773e1a36847", + "0x0b01c6bf8e5bfbbe69cf84e18b998a55dc6c0fbbed8c9ed5fde3b4d9c5f32930", + "0x0439e7c538572a8753984cfa155587b5468eee6dc5b69ad9d04b56256eb0b686", + "0xc3f1e2d19e8ca7e98decb496a2532029af8941c43bbe526c32acbabc87c5ff70", + "0xd0f2b6419d86efd3491a7e0902bf0712e9b4c1c976d5d3e986acf12e58f120fe", + "0x35686cb2d5dbecfcfa5f68c4635a372aadb503fef40fc75288153d0381c4a0d3", + "0x412ad577795a32ff0ed51c6909681d96f02e007eb85f13b1da851f4ed0392478", + "0x3a2a6eca545d620abb5fb64398853d21f84f02b58e4cd0c1db4e01f6c1662cfb", + "0x472a7c1d888f46701da9cb8cfb186da22931446ed34305fb7a9580308acbebe7", + "0x6007fe76d65e82a1ad4375e86cd3f7150b89cdf739d823764f428fd4c18237dd", + "0x8e1e96d6d992b0114cd49903ba8e62064c438832aac75f587171c73b876fa313", + "0xe807ef14c87f8a1682248c840db03281711db85f6fb5c84de6e18a430ff8f1cf", + "0xb2c308be90cbb5cc48e6adcf6543af2d96d20c6828ad74eecdffb716d0ed7e61", + "0x984fdd8065fd59189e6fb11fb8b34e38c8c900e0b7c912261cfbeecc64cee022", + "0x8ea7fb608f3adb04b7fdf3eff166b259873ea225b9913d8d38800eecacac6aae", + "0xa90083d78f8636c90bedd9feabf24c0fe0031ca0cba643ce716fbda2677bc327", + "0xe6129d534c82bb479fbe104b0d1ab8b17e920369239738c6bbeadfe596b4f629", + "0x5f203990e64be1811f3cd8ef33dd210b3dc65778b2a17cdbdb3d5643623a0bdc", + "0x94ee8c68115a973774649c7d00f91680c148ae6aef2db6c688d2fccf46b515b7", + "0xfe9d11e51811a48ad822ab5deaae4b6ce2697273c305b505f642dd7e02707e73", + "0x4b883b516a952a15c67f9df115e96cb3acbf2cc2c253d06bd083408ca52d2f53", + "0xab87cd63f88a6194b1d314ce49ba981a1b541f43e8f35d7ca1494f74e29cdbcd", + "0x690c47f9f7c085253d13dd000b373ab4a214bcc63d5640f8b184fb55039a2f2c", + "0x971e1d12f299a63bae5903636579f3c4f948ef186bde4d766a572cfdcb4abc4f", + "0x6435213d1c37f8f399df3eca19128dd4fef0f63000e75ef740acbc2db3e39380", + "0x0820c9c93592cafc0f0595b5eefc9fc991d32080f030705dfa0f20119a72bdbd", + "0xd176a8afd79388b661d30c118b242b058285cbe7defb4bd2ab06defacf614303", + "0x0b0a3e2a93119a5d55d8d250a04db691c52abd37273a972fe30b916a9c9539dd", + "0x6b2ffe1d034014223b8d7d8e10bf2412bd75309fa9f280805a1caa618115ca8d", + "0xb1dc29570539c5384763c9b1a004416cc856ab8434ade09a3a6faae9f0f10d93", + "0x69ff3251d53ed2fc33b0ef9fe07024c2026b3b3dc8e3bf1b4bf4605e6ad4ee7f", + "0x429a65ccd929996616d4af5a91e181e38658d576598811892af6d16cd5280985", + "0xe40d9d662fd95da0894608f9f9c1098417b038536fd6d1879a44d4a22b87ada6", + "0xc67ebc54ec6c2a4a3ddd02df55c0470f5400d449dab83538772d6703684dfb4a", + "0x1ecb6b483b4f994591e89fcee961437432f8823cf176e82e3de58d1ac79f8e97", + "0x7785e9511ad132bb5ebc3c17f21f49f2d01fff7ae99467b3952820132deaa794", + "0x1154a0e1dd5ad1b6670c31916c881eef8bb4d8324e6cb1b9658a8c99846729f9", + "0x2d97d42c991ae5356faa854a76fa2d7ef2cb645ca72017693fd144c0503d6ea8", + "0x75181621808a1bb3b46781ccd7262c108d8d5db5ad191f0fce708fbc68d5af66", + "0x5f1f8dffa3037df99c8889bb8fc02bea18285ae20b5fa705c74dbd2c76656822", + "0x4c4dbec0fe820624f394ec9bad961510fe3c6b6d1de987df5ebfb248e89c58f9", + "0x053d1b128b01a4737b67821aff54d98f2ef6e575c631304846e5dea41cadaae8", + "0xac6210bffb2ebca44c9e51aacf02f4da23ef4a7da3dec269beb267f7acba6394", + "0x3a42d687f6755a1cb654a50769bb9d9f8af1a4fb27efa052301a3a045df8b70b", + "0xf22dc6e623ee9daaabdea82f1326a7f531598d4fc7766c556b04f344b611fca1", + "0xf607a636868d864520df1058a53612467e2a07f2a146a612ed310cd54c6dd6d6", + "0x13c553d4882430464ac3911d9260b41d11868b02bc80a5ddf54eb55d287e6926", + "0x3381f7bf2a14546965edee3afc59fdb360bff30fe2a48f86b3d72d54a6600595", + "0x4b3447a72910943efcdf1e24ffc99887556fb51c7bd2781b12526b670b5d6968", + "0x71380b6ad662ee6f81cf2528e039b133974628fcc230cc69a78b124f062c64bd", + "0x456a1ec9226f5f5bcd0aca197dec49dc9397d085ba79e6c331ff6a96dcb74e46", + "0x0a07a451e7589fc8d5b738b18e604ba79469236717d665377b0b7e0c7ea2fbcb", + "0xffc268adf0be4940da16abf18ebe36e68b72f55b71fd0ecb322d0956a136b371", + "0x6d39486f314e2931641ea73032b4d3655da2f2335512506ceb415ac05bd226d1", + "0xc740bfd54320461d84c34420042d8178b762ba238dbc3716cca2297d7755d81b", + "0x0fb98c296b00e1498d32328d3960d0c182b7ebc4a2c1c6ae5bf95dcb30c5ca27", + "0x98ccbaa45310f121831f5c8d8c0e58b73a7071e47cae4c0e35cb9787c9e4743e", + "0x2b1752a28acce9dd03f0172d5a4074eaa3ab8634af1c99ef7b9df0b03e82090e", + "0x117e992077c6e9e355a1cc32796335a014e095b6d99011c089d6a3e9ef9aaea6", + "0x2b8412a57f62a0a775ab675b93fd55c64f909661352e9e0ad4fcc307c31063dc", + "0xca05606f592b5f9610144d5eedbe52ab10421be149c4388ddfbcd79a0f8b8c6d", + "0xb50a9f66b39d1d19725c6389c83cc077af3ddec9927667d4e51edbf5af2938b9", + "0xcb05c83d3f2505deee6df0c8a9b6e46c8b0d98b46f542cfb668a1f5f6f656e8a", + "0xfcd6d8a4dfd20aa57f1b66e064e209fb2336d5f714e208b54ac5ca5cd698ea18", + "0xd2aa33d6f1b8e5897c059f716f27e7187995c8cb9726ee17af5222acd6342f9e", + "0x4bda87bf0bfba5518eba8134f01e939c76d548f28f39be613f52d0b17cd53fbb", + "0x5d1d22e8448e18f9634ec28dfdcdb110ecb17f9d27715b3771082da99f748c8c", + "0x34dbd9f65931a65dc3e7d1603dec5abfdd9de589a123613ba1b07ac192669f44", + "0xa989a195cbb848467aa45a13b0a8c0a437dccbd886701a4b07547355fd57040c", + "0x9bd3587b90d1cdb7fd159ecf6d0889d6482c150d995a5798b519539b63cf44fa", + "0x4e7cfd8ceb7213c7ea538791cae86a3e5c800d7de8df599d624b4f2fea390012", + "0xf2ab78b0fe0459413a05de8a8a2544f077b6be840720daf90a60715f311ba6c2", + "0xa8a1a572ec81f39669887ffacaaa115429ccb99dd3a725a2b3ed4856dc36df56", + "0xe274b3030deff75cc3d9063b8cb24252f39d5c188e05f3d65527db75ef6aa435", + "0x8c93f8fc7dc4e6c0d17a36c0c14b310f2c0adc48d725fd150b2601d3be66423f", + "0xcd65fe957b60d518e635665b8eafd876f6ce0eeeaaca7a4d2b787646f9e72b08", + "0xf82069e70dff79405a5d9ec39bc160bf621b91d4a48714603c6df6666b1c8b15", + "0x8490979c2446d6bd26b202db6d9ab712b109d1e0d763b081461a4c6aef62f0bc", + "0x587df20e6cf69bf7d21c3e7632eff30a1559f694dbf3cc9e3f360cb11a4d24b1", + "0x15dc3f263d6374116bf050e34e18a7fbe750c081b42e0a482848d3986c343084", + "0xdbeaa4c05b4aa8a2363d7ccb946d400ffb6390c0d7899f6d10c75847296d2576", + "0x74a65f1fc92bfd183df876dc11d50006ee0a2fbe93ddf8d51093015071a4e479", + "0xfe1e1f1d6b9c3ce01e5b6d213974e66273ba147dfe2bbc34f0e8f7e67472fc34", + "0xbc9e385ae94f6e3fcffafb7725098a190fb11ee3834d39799fd5773aaf354421", + "0x288d3650c1655deac741f0662974a614ee97e307e37b71596acda844b6789c78", + "0xfd8e8cd4ae4d937f3ca43a55907edcaffcfb3d4328f701d8092bb3a7a991f6d1", + "0x8b286cc193853e8cfffe426dc68ae669f45ac5a9d08bb89c7f684f348135d081", + "0x083beb08d263eef340ef11b803fd49e49bb2ade058356eddd8eb5b2149e4ceea", + "0x6caaa98ca9fada0814f6a012c6bac5d7947a600567828165d4e0eab5f4a74ad3", + "0x21c451ccb7277881824c4bbd516e21d92026fd93226f02e466231264c8cf3af2", + "0x607e2478284adab902d491d8efd811f3841614cfc10d71fabe02711c24f4a5ac", + "0x8188dd0b131e8d920ff43f8e78fee7512cf625d50a0be04d50beca9f22efa0e0", + "0x800ed8b0e1d1907406e1289f3b3787f66a3b465775b15fb608d29a1804c1f0db", + "0xfbb52690d9b06528bdd1454e3d6d5e49468a8d95269719630d2343932d4bd498", + "0x93fa489b2deb7011847357f0485252176be062fe65b6ce5882d6676a3378bba4", + "0x70c6842b2109040d828e048b67cda527f31de398a0d51b4bb0911a5dca5fd051", + "0x9ade6b48cf5494476d036d01c8def811db6e1ac8721a28ceb64ac34a4a11d369", + "0x59afacfb3f1783b530d59c746eec1da981453cab6b74b3713f4c600d73623bf0", + "0xabc114556f9849ed57275d96ae8760a5bfd2a9c74933832072884c12fb6af01f", + "0x73d8104782272545f18aa07a637a9767a7e428039101d26961bc9ce8136c55bc", + "0x11210861270b25eb715f1e8022a780827ea31fbb5b9d8913e48e4f8acb0426b0", + "0x9881ac99685b97d794beccc3f9bbd03836113ee08c6b76880eec1dfa15165e4a", + "0xc24003c349b13ed5771b116138a3732c7ef649ec42c54f0ccfad6fea498e8516", + "0x50ccd39e0a76a96e40746153c34a00191a23926ca824749caf95669f5edee6d2", + "0x877a131b50f0e8ef570571c4ccb0f895569be149a89f8a752fa7228a91bd4c4c", + "0x644120da4340182e2031a0137fd51a6d276fbda2299e79932a3ecc703c97fea8", + "0x887993c447c48650ad3320b44aec87763c4e0d9559e6a22a14fcacc785c4145e", + "0xf52a7e79ad730f71a63f9e112e81ae9b97ed177356185d0de84aac21cab2def3", + "0x5e2ead192795ee22a2d3faea4c37d67c460942a495f4d6a4c6a028d52997d68c", + "0x18a358dbd4a57efa03ff78dcd0b81562c5b4fba381582487ad832e795603ec08", + "0x9a2cc94110833543e055ed08a75b8f4f4030129547bc70257066f30224261474", + "0xaac80fc22fae556df40f449ba1905fa918e443970dd56491275d2280a4850cc3", + "0x1b76448a3e9f9df24f042b21fab860e9294ee1ba03fec2f3b904173514533534", + "0xa38a6b67b744a929e05556b8688f2c65a45827b2eb01e67326e2d0367be77033", + "0xb52ae478e1e2170885927bb311d334f6b2ec3f0f8ae29d273739b17fba554a23", + "0xc6364af2c7c241ff0c4c7d4a43cf25d269251cceae74d7fe6655a127a62b9ced", + "0x00b4d33fd887a0c64acbd82d06536f9fdf501af0ea7d481fc620b00e4980b401", + "0x2dd1bf39f1ab1a5795b44386f1c1413cb4767697f451239a81972ff5d40d653c", + "0x10750f38b5d4951cb007d714aa38e9c46d83fa9a03af0f574918b04090b7b41b", + "0x2e6be521c05409a65a22b557b38e49bb7c9e3a10023650a3e2f5afd0401d0c17", + "0x21c34f19f622cc5519ce4d069a8cda030ef4c1af75eecc7a7bd40a06208e02a2", + "0x382d73a918434c518fea15bcd9bb8e78b4cff89160ddd86734263a33819992d3", + "0x7068fc3c5f04ce51f3364a600e8d37be998b2dd819e8f477f23cc5ba6ddff67d", + "0x7d9b9c6828712cb961edc141d90a3083f76fcafc31082637f8f85a1ccc73a896", + "0x6ab913dc0c8f2f2410d028cddacd32de9a5d03e37dbd7abf8b3ea1b744554d35", + "0xfb81c54dd01a1adf0095f71a2834b54fd4d1f962e1ced57b6f15bd34e94bd227", + "0xdf5a6dc5baf2b39011f0835a20db4f92ea3b79e055e7c65c04539867ea2b82bb", + "0x25fa7f1b3fd5baf7804d45dfbee5aa3d6ec074264a9555b1d75993257f915db6", + "0xac6f0bb4934afe54e8ce462656810234111ee65a8d11a4a6e581708a8733c869", + "0x6f9192bd4cba0a872015bb05db79bc2f8baffc4d754dceda2f1cb83b5b0557e0", + "0x63f2cc146bdbcb65a35a852d3074f9bc321764a9b944f35de112122dec0ec562", + "0x448cd384771816921ff26f645a17fcbe04623400f88926cad3618ea1453dc3b6", + "0xd0fa7c53af3ad58e0690cd5accef2634a9dd27a670edc211989f9196acecfdf0", + "0x18b5ac34846936635267a7e27ee655fb3d77795a7f12294e595cdc8f82e055a2", + "0x6a0ea2e502ab94dc4e3436d6e7f4963a466bf7132b3d2600e5ef5c4b1b4bbf0d", + "0xa9294a84ead1386e26e16f5a9a1811b9c3ae4c9fc6c37d139f841da6a5c9ac74", + "0xd66c058be40f09cb8ef4f52f74e00a0f3b0aca90099405aa31651ee50c639a91", + "0xa9ff022524f02a44ff030c5e71b946258edc0e8544bb3e226b1e23b65e32afe1", + "0x3a78ea5f511c4830cf54424320aa3bae00db7bddc2a79d1e5a57cf4827fcda34", + "0x3a496b86acfff6158b5c4b9d89e5560573baffa17f12a40700fabcd0cbe9565b", + "0x3196212a8c205160b7438afc34c977ef34d08a209bfde2d7169641f879218c22", + "0xa7d4fae94f12c1fde1a8cc7d8b21bd4008e44916c16167b6c2dc650d590f5957", + "0x3d56257eb95da31157995cb6c8548c48e2e50cfee06ab65d18286f8ceef6e8dc", + "0x3e1c7b25e5812b2ac830e2f6cb6c7bd338ed82bd15a9123bccab3a624b22e337", + "0x925a1b950868edad729ba3cf15625abfe37150bac88b36352f96a7361af74e9a", + "0xd38e76e2918ad0ea8b67bc6c8040b11ce300bde63385ffa553ead314371f590d", + "0x672cad7d3210673cedbdf7a39dab5ef74fb922a54d496ed25ebe3fa514f872a4", + "0xb381cf505fb0ae498bd6b9c2a7a5d588c12a68dad62cf1359754a06b4c3b4bdf", + "0x05f01c6c4f15fac486b87b306cc4cc3a6e6329441792c602e6d2213c4e61d5c2", + "0xdb0babbeab431b2760dae364df3e8f79405d7055febd34157a246c3b331014e1", + "0xd6de025c50ca4bb606a953122850e5a274fc90c9b394b941f992c229cc8d38bf", + "0x601ef3c9215b3a77fd9bfd0a8676e32e123f8fa525c53c0512cc13d90a9d414c", + "0x7577367e9cf40f5cf4e2111573e1d2028cc00c1188e0d4e032274775c60aa248", + "0xbdac6413f2f14332e373be6de4f740e5778f96ad60e97de20608658254c24bbe", + "0xfcc4762448f3ecc084565d5678b3adbdbc5cdbc09323f6ec18495e5cefa429ab", + "0x2cd294e3e07f34bbd078b0185a12334fb779ba1c83bb747199b2f887b53972a2", + "0x495408aeaa7bc93cdbb0e90521143d77e2699569da998ecacd421775de02f06e", + "0x20e1831384ee020db1266600a16e2e686bd0fcc77c5791b9e203cc05c6843985", + "0xaccc1935b709fd0a411e6b21ca833eb10d67856d04c874005f507be50a092b95", + "0x0826c91349eced65c79582091fed8cef5254f343bee83d2b14576117f83efb9c", + "0x395159715a71a93ed24d2422eeb44f83e26b82fa3144dc7a182838569b46d469", + "0x6ab595ba52ee74af48ac48e3bd268133753f2dc460fdf9de73a2156bd49fd3c8", + "0x688b687e354b26b67afb4a22631dfa6b8f21b9c09422e2e862b584da71f451fc", + "0x82e108c3ce0b2b6ba7533ca3a8bc8dbe9c9d5aae7f132c01926a45c8704835a4", + "0xf79eea9e4860c3100808cf4fe2e8ee0e7da6073f19f3e8e564a39131f1c25db8", + "0x83ab979e553f02d008e9ad60904b97001c0f3480478987a55871beb3521b5480", + "0x344048d97b5c946cfa6c6d12d4107ff56749051cf907fc27ec29d598b98ecc23", + "0xf34728fecf4333715bcefe32ba0d1598fc8413194baee50a5ccc27358f2866d0", + "0x670ea0c9e814f1d7fb206343028fde13c32a0a3608e9574dc6ee8bdd0fc49427", + "0xce5fa0eefea02b1447524039bc371af19a9e294163029f89503c233ee494aad1", + "0x7990d7175f36443527f738bdc6cc0300e9e5047e7f3a35b05a4f4022a3d5b09e", + "0x4db0e09abe135d2eb5ae9834b0a48d0276bff61ee4ff22a315323fed6918ba7c", + "0xdeaff3453683321e73edab2d9a37145a1a56b0f75e7400fb3070e22323c218c1", + "0x48c398c3c36bafbcb628b4dad0ce9a5294a2e22028c5b914b0cade6c6d198cd2", + "0xfbd34613d819bccc715fe1999b06951fc431c2543e40c57f9ea3418159f4f6e1", + "0x2f870eb2b1190d314ed8dcea0947b10c3c3f1ad6c6e40120f713dfac990a83ac", + "0x75507cfc088a260dcabcd0013173a30034e7e434aad7c8012cb7e68f5cc394cb", + "0x052adb2b8871c0bab5c7bdbb4b1a4287b2b5a61f1b9cb1a01bf200e407225973", + "0x2130e61d84200a34c75babddef99cadad85677dfbb1211afad3d4712dc6c5d68", + "0x37132e7024b7a1c17c815b64c2661ba69269835d08dacd9811c86662c7442fde", + "0xad2e338315984b05d05fa21d956cbb11179815e23140c81d1e12e6bc3eeda34d", + "0x2a2b902e90de031202629508f2fff8ecf866218b1ffc5e1a7322c670340517fe", + "0x2b7651357d9bd136b5276e7d751bbcebc9e6f200edffece606fbc70609875337", + "0xa03bd58a0cf560596c48f4dd4c09ade670b683d1b89cc5924c8f57ef586f1728", + "0x04510caa9afcd1d0f90c6babd318ba42cfe6513f989e688f24702e974201263f", + "0x9270fde521f0b7bbd9d8d1bbb31020d6aa1fc244d79cbfff334c9fb8700b3a8d", + "0x346673c787d04d3c31964540c05316c50ca99bc17abff2af68e72521480d600c", + "0x723d22aaac3fa2041ce5169daba17895b34d9f9252b8f8bb281f302de6d8f243", + "0x5628c90ed9953eae89a600e10d661b18b08ea11c13eeb4b756728ead67c3a0ce", + "0x734b90136d8bcb41377433f5af874c79869a6821ed6cdaf91b792bc4d833f09f", + "0x0ea69ffd1cace23f5aef9e9b09d46d2241d7585c7ee2fd411895b43ec4986622", + "0x08f471d572785017a9a83e1dc43c030c2273bc3aba17dae8b0468498e1e362ea", + "0x5c95d03d2ad1e7be48e49cbf8a09588a930591957f63edc1859366fbae21f708", + "0xa6fb7fafc02a5482c895f172879378e7cc19c189babd210fa7024be2a1727a19", + "0x9170cfa087283410de13f40af009a491b34476ea9ba95c06f84a5b6901ce4646", + "0x7f11e1bc20b482777a52e3f92cd5d9fdf3185aa5e3579ba2c9b92efcd8840f07", + "0x1be7a2120e0751a09707e020abfbc12076c764090e66725c4ad75291f53c9dec", + "0xb3c14f37ccf78bf154cf7a2fd295ce539d25d07f3e4a85888a1c1cb238454978", + "0x5d22e460386d9941fd9dd611e2d121144df9cd3e11eb2d0c5eba88f690cf3764", + "0x7fae31d251cab11c5391e10cb13a45f7eaa87b54f470cb510954b257a4d6fa7c", + "0xd90685e83167534d14e55bc402e2c2a912c1999539f9f1f30e6fd77c339a4dab", + "0xe9c1f612a774410d93382e7dc91107e1e2be4b2f06395f14ad6394fdbf0bea4d", + "0xb630632e675063f64001c11a409b99179650b324b729c0ffa4ec302273808b1c", + "0xc15b5bda10f8c882013f45acb858b98a4ce2aadce4d2489f0b132a267f0843b5", + "0x6467610c92703c56b832f61ae4c542184bfa2633b17e1347483e4c72f5411f24", + "0xd58e53548121a2867c6e89d4a58b4856e3759ab07d985cd2652bf3fa01f2ec8a", + "0x218658bb782fa314d6b3859775283acd4e5b98775482d1b525efe3649fbfc300", + "0x62453438f4323c5bd338a8bd5fa3f8c08c6586df793c51bde4f524151eb3a11c", + "0x30e21369fd052e399f673e1ac9fcc151bbe75a0e133c2796bee33f8fcac1b4d8", + "0x940c0ccb6a26a037d443d8ffae73d1df6c451fc5ac7ae1552b65073f97c2b7ec", + "0xa8b5f2bf75fd39a5d43df5ace0c7f30fd533e8a892d291de2b09b52acd88c78e", + "0xed2777fa73e4a2ff09bdd106dc2768f0aeaf5356e170b56b96b9034fa306aea1", + "0xc1a451609ee2ff699f91115d78a7818f27e0caf31b18637503826aec0ea4ae78", + "0x146635f940a1137d8a530513cf0e4911b86d8cfab01ec9520fc174587b1e2163", + "0x9ee377cc9b3d7521471a6a71bd946695c2503977ee7038f9d4cafdb47a57a143", + "0xf3b25cc9dfc0e50ae630d43aa1aa1278f4e3b0e1411b4fc381b0a27385a08b22", + "0xff7194b32e3935912122630712f161f032f38201ae123c6a857707bb3ed834b2", + "0x45b73edb2fc3c0739b2e2d78b1cb7ddbeeaefaf3bc402ee1dfa098104fe395d8", + "0x65ecd3902a35a48ef07c04a9fb62dec0ef7955cb881ed649ffca0d8fc88b3940", + "0xd8e6f7b5eb4519ce6525b2c00d2b4998e65bd735546d1764db916bb44837218e", + "0x108d77d199bb67534c09c3cbf9bc9a8017ab7066680ec44d5ce9dcd6cac3e7eb", + "0xcf9b4cb5cde1ab7f0be1b7aef27b856f0e3446a9f8bf7f9919a1629618f247bf", + "0x4431d5694c17fa2032178c339ee8ca3a657f00b301110ee287c58888bad1be8a", + "0x70d004e685b9973a76a066b5d6b02835bcc908a225ada85d1ffbbf65d29fcd06", + "0xfc13d0ff212c91220f3598839366a061cfb0d735a03e77850884518456eda595", + "0x32ebd9fc0079eb4433987f6d5463a7c40909077e1d6207429b747a8be0d9fc36", + "0xc0dbaba81422a5cb7fc43d3f7f7b5e18be0791f0dd83d4b44607ae334b8854e8" ] }, "nodes": [ @@ -2739,12 +3671,13 @@ "nonce": "0x0", "builtin": { "name": "alt_bn128_add", - "activate_at": "0x19f0a0", - "eip1108_transition": "0x62f756", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0x19f0a0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x62f756": { + "info": "EIP 1108 transition at block 6_485_846 (0x62f756)", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -2754,12 +3687,13 @@ "nonce": "0x0", "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x19f0a0", - "eip1108_transition": "0x62f756", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0x19f0a0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x62f756": { + "info": "EIP 1108 transition at block 6_485_846 (0x62f756)", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -2769,14 +3703,13 @@ "nonce": "0x0", "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x19f0a0", - "eip1108_transition": "0x62f756", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0x19f0a0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x62f756": { + "info": "EIP 1108 transition at block 6_485_846 (0x62f756)", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/st_peters_test.json b/ethcore/res/ethereum/st_peters_test.json index 768e6057fd..ac404c7dca 100644 --- a/ethcore/res/ethereum/st_peters_test.json +++ b/ethcore/res/ethereum/st_peters_test.json @@ -62,12 +62,13 @@ "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -75,12 +76,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -88,14 +90,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/tests b/ethcore/res/ethereum/tests deleted file mode 160000 index 29f8f4dd61..0000000000 --- a/ethcore/res/ethereum/tests +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 29f8f4dd61ad2672b1fb4268ab0c0de995889697 diff --git a/ethcore/res/ethereum/tests-issues/currents.json b/ethcore/res/ethereum/tests-issues/currents.json index d4d3f5e3aa..33759a066f 100644 --- a/ethcore/res/ethereum/tests-issues/currents.json +++ b/ethcore/res/ethereum/tests-issues/currents.json @@ -1,4 +1,57 @@ { - "block": [], - "state": [] + "block": [ + { + "reference": "Issue https://github.com/paritytech/parity-ethereum/issues/11073 (also see https://github.com/paritytech/parity-ethereum/pull/10923)", + "failing": "stRevertTest", + "subtests": [ + "RevertPrecompiledTouch_d0g0v0_Byzantium", + "RevertPrecompiledTouch_d0g0v0_Constantinople", + "RevertPrecompiledTouch_d0g0v0_ConstantinopleFix", + "RevertPrecompiledTouch_d0g0v0_EIP158", + "RevertPrecompiledTouch_d3g0v0_ConstantinopleFix", + "RevertPrecompiledTouchCC_d0g0v0_Byzantium", + "RevertPrecompiledTouchCC_d0g0v0_Constantinople", + "RevertPrecompiledTouchCC_d0g0v0_EIP158", + "RevertPrecompiledTouchDC_d0g0v0_Byzantium", + "RevertPrecompiledTouchDC_d0g0v0_Constantinople", + "RevertPrecompiledTouchDC_d0g0v0_EIP158", + "RevertPrecompiledTouchExactOOG_d7g1v0_ConstantinopleFix", + "RevertPrecompiledTouchExactOOG_d31g1v0_ConstantinopleFix", + "RevertPrecompiledTouch_storage_d3g0v0_ConstantinopleFix", + "RevertPrecompiledTouch_storage_d0g0v0_ConstantinopleFix" + ] + } + ], + "state": [ + { + "reference": "Issue https://github.com/paritytech/parity-ethereum/issues/11078 (also see https://github.com/paritytech/parity-ethereum/pull/10923)", + "failing": "stRevertTest", + "subtests": { + "RevertPrecompiledTouch_storage": { + "subnumbers": ["1", "2"], + "chain": "St. Peter's (test)" + } + } + }, + { + "reference": "Issue https://github.com/paritytech/parity-ethereum/issues/11079 (also see https://github.com/paritytech/parity-ethereum/pull/10923)", + "failing": "stRevertTest", + "subtests": { + "RevertPrecompiledTouchExactOOG": { + "subnumbers": ["61", "64"], + "chain": "St. Peter's (test)" + } + } + }, + { + "reference": "Issue https://github.com/paritytech/parity-ethereum/issues/11080 (also see https://github.com/paritytech/parity-ethereum/pull/10923)", + "failing": "stRevertTest", + "subtests": { + "RevertPrecompiledTouch": { + "subnumbers": ["1", "2"], + "chain": "St. Peter's (test)" + } + } + } + ] } diff --git a/ethcore/res/ethereum/transition_test.json b/ethcore/res/ethereum/transition_test.json index bc28ebd509..ea26ade2fb 100644 --- a/ethcore/res/ethereum/transition_test.json +++ b/ethcore/res/ethereum/transition_test.json @@ -28,6 +28,7 @@ "maxCodeSize": 24576, "maxCodeSizeTransition": "0", "eip150Transition": "0", + "eip155Transition": "0", "eip160Transition": "0", "eip161abcTransition": "0", "eip161dTransition": "0", @@ -35,7 +36,6 @@ "eip140Transition": "5", "eip211Transition": "5", "eip214Transition": "5", - "eip155Transition": "5", "eip658Transition": "5" }, "genesis": { @@ -57,15 +57,17 @@ "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": "0x5", "pricing": { "modexp": { "divisor": 20 } } } }, "0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", - "activate_at": "5", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "5": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -73,12 +75,13 @@ "0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", - "activate_at": "5", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "5": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -86,14 +89,13 @@ "0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", - "activate_at": "5", - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "5": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/ethereum/volta.json b/ethcore/res/ethereum/volta.json index 9ac6f9afa4..55cc39d999 100644 --- a/ethcore/res/ethereum/volta.json +++ b/ethcore/res/ethereum/volta.json @@ -1,207 +1,1307 @@ { - "name": "Volta", - "engine": { - "authorityRound": { - "params": { - "stepDuration": "5", - "validators": { - "contract": "0x1204700000000000000000000000000000000000" - }, - "maximumUncleCountTransition": "0", - "maximumUncleCount": "0", - "blockRewardContractAddress": "0x1204700000000000000000000000000000000002", - "blockRewardContractTransition": "0" - } - } - }, - "params": { - "networkID": "0x12047", - "maximumExtraDataSize": "0x20", - "gasLimitBoundDivisor": "0x400", - "minGasLimit": "0x1388", - "maxCodeSize": "0x6000", - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0", - "eip145Transition": "0x0", - "eip1014Transition": "0x0", - "eip1052Transition": "0x0", - "registrar": "0x1204700000000000000000000000000000000006" - }, - "genesis": { - "seal": { - "authorityRound": { - "step": "0x0", - "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x20000", - "gasLimit": "0x5B8D80" - }, - "accounts": { - "0x0000000000000000000000000000000000000001": { - "balance": "1", - "builtin": { - "name": "ecrecover", - "activate_at": "0", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000002": { - "balance": "1", - "builtin": { - "name": "sha256", - "activate_at": "0", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0x0000000000000000000000000000000000000003": { - "balance": "1", - "builtin": { - "name": "ripemd160", - "activate_at": "0", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0x0000000000000000000000000000000000000004": { - "balance": "1", - "builtin": { - "name": "identity", - "activate_at": "0", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - }, - "0x0000000000000000000000000000000000000005": { - "balance": "1", - "builtin": { - "name": "modexp", - "activate_at": "0", - "pricing": { - "modexp": { - "divisor": 20 - } - } - } - }, - "0x0000000000000000000000000000000000000006": { - "balance": "1", - "builtin": { - "name": "alt_bn128_add", - "activate_at": "0", - "eip1108_transition": "0x7fffffffffffff", - "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 - } - } - } - }, - "0x0000000000000000000000000000000000000007": { - "balance": "1", - "builtin": { - "name": "alt_bn128_mul", - "activate_at": "0", - "eip1108_transition": "0x7fffffffffffff", - "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 - } - } - } - }, - "0x0000000000000000000000000000000000000008": { - "balance": "1", - "builtin": { - "name": "alt_bn128_pairing", - "activate_at": "0", - "eip1108_transition": "0x7fffffffffffff", - "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 - } - } - } - }, - "0x1204700000000000000000000000000000000005": { - "constructor": "0x606060405234156200001057600080fd5b6040516200240138038062002401833981016040528080518201919060200180519060200190919050505b600082518260328211158015620000525750818111155b801562000060575060008114155b80156200006e575060008214155b15156200007a57600080fd5b600092505b8451831015620001b6576002600086858151811015156200009c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156200012b5750600085848151811015156200010857fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013757600080fd5b60016002600087868151811015156200014c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b82806001019350506200007f565b8460039080519060200190620001ce929190620001e3565b50836004819055505b5b5050505050620002b8565b8280548282559060005260206000209081019282156200025f579160200282015b828111156200025e5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000204565b5b5090506200026e919062000272565b5090565b620002b591905b80821115620002b157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000279565b5090565b90565b61213980620002c86000396000f3006060604052361561011b576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101da57806320ea8d86146102135780632f54bf6e146102365780633411c81c1461028757806354741525146102e15780637065cb4814610325578063784547a71461035e5780638b51d13f146103995780639ace38c2146103d0578063a0e67e2b146104ce578063a8abe69a14610539578063b5dc40c3146105d1578063b77bf6001461064a578063ba51a6df14610673578063c01a8c8414610696578063c6427474146106b9578063d74f8edd14610752578063dc8452cd1461077b578063e20056e6146107a4578063ee22610b146107fc575b5b6000341115610174573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b5b005b341561018257600080fd5b610198600480803590602001909190505061081f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101e557600080fd5b610211600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061085f565b005b341561021e57600080fd5b6102346004808035906020019091905050610b02565b005b341561024157600080fd5b61026d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cae565b604051808215151515815260200191505060405180910390f35b341561029257600080fd5b6102c7600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cce565b604051808215151515815260200191505060405180910390f35b34156102ec57600080fd5b61030f600480803515159060200190919080351515906020019091905050610cfd565b6040518082815260200191505060405180910390f35b341561033057600080fd5b61035c600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d91565b005b341561036957600080fd5b61037f6004808035906020019091905050610f99565b604051808215151515815260200191505060405180910390f35b34156103a457600080fd5b6103ba6004808035906020019091905050611081565b6040518082815260200191505060405180910390f35b34156103db57600080fd5b6103f16004808035906020019091905050611150565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001831515151581526020018281038252848181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156104bc5780601f10610491576101008083540402835291602001916104bc565b820191906000526020600020905b81548152906001019060200180831161049f57829003601f168201915b50509550505050505060405180910390f35b34156104d957600080fd5b6104e16111ac565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105255780820151818401525b602081019050610509565b505050509050019250505060405180910390f35b341561054457600080fd5b610579600480803590602001909190803590602001909190803515159060200190919080351515906020019091905050611241565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105bd5780820151818401525b6020810190506105a1565b505050509050019250505060405180910390f35b34156105dc57600080fd5b6105f260048080359060200190919050506113a2565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106365780820151818401525b60208101905061061a565b505050509050019250505060405180910390f35b341561065557600080fd5b61065d6115d3565b6040518082815260200191505060405180910390f35b341561067e57600080fd5b61069460048080359060200190919050506115d9565b005b34156106a157600080fd5b6106b76004808035906020019091905050611696565b005b34156106c457600080fd5b61073c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611877565b6040518082815260200191505060405180910390f35b341561075d57600080fd5b610765611897565b6040518082815260200191505060405180910390f35b341561078657600080fd5b61078e61189c565b6040518082815260200191505060405180910390f35b34156107af57600080fd5b6107fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506118a2565b005b341561080757600080fd5b61081d6004808035906020019091905050611bc0565b005b60038181548110151561082e57fe5b906000526020600020900160005b915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561089b57600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156108f457600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610a80578273ffffffffffffffffffffffffffffffffffffffff1660038381548110151561098757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a725760036001600380549050038154811015156109e757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2357fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610a80565b5b8180600101925050610951565b6001600381818054905003915081610a989190611fe8565b506003805490506004541115610ab757610ab66003805490506115d9565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25b5b505b5050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610b5b57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610bc657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610bf657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35b5b505b50505b5050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610d8957838015610d3c575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610d6f5750828015610d6e575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610d7b576001820191505b5b8080600101915050610d05565b5b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dcb57600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610e2557600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610e4c57600080fd5b60016003805490500160045460328211158015610e695750818111155b8015610e76575060008114155b8015610e83575060008214155b1515610e8e57600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038054806001018281610efa9190612014565b916000526020600020900160005b87909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b50505b505b505b50565b6000806000809150600090505b60038054905081101561107957600160008581526020019081526020016000206000600383815481101515610fd757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611058576001820191505b60045482141561106b576001925061107a565b5b8080600101915050610fa6565b5b5050919050565b600080600090505b600380549050811015611149576001600084815260200190815260200160002060006003838154811015156110ba57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561113b576001820191505b5b8080600101915050611089565b5b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169080600101549080600201908060030160009054906101000a900460ff16905084565b6111b4612040565b600380548060200260200160405190810160405280929190818152602001828054801561123657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116111ec575b505050505090505b90565b611249612054565b611251612054565b6000806005546040518059106112645750595b908082528060200260200182016040525b50925060009150600090505b600554811015611322578580156112b8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b806112eb57508480156112ea575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15611314578083838151811015156112ff57fe5b90602001906020020181815250506001820191505b5b8080600101915050611281565b8787036040518059106113325750595b908082528060200260200182016040525b5093508790505b8681101561139657828181518110151561136057fe5b906020019060200201518489830381518110151561137a57fe5b90602001906020020181815250505b808060010191505061134a565b5b505050949350505050565b6113aa612040565b6113b2612040565b6000806003805490506040518059106113c85750595b908082528060200260200182016040525b50925060009150600090505b60038054905081101561152b5760016000868152602001908152602001600020600060038381548110151561141657fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561151d5760038181548110151561149f57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156114da57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b5b80806001019150506113e5565b816040518059106115395750595b908082528060200260200182016040525b509350600090505b818110156115ca57828181518110151561156857fe5b90602001906020020151848281518110151561158057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b8080600101915050611552565b5b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561161357600080fd5b600380549050816032821115801561162b5750818111155b8015611638575060008114155b8015611645575060008214155b151561165057600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a15b5b50505b50565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156116ef57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561174b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156117b757600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361186c85611bc0565b5b5b50505b505b5050565b6000611884848484611e6c565b905061188f81611696565b5b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156118de57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561193757600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151561199157600080fd5b600092505b600380549050831015611a7f578473ffffffffffffffffffffffffffffffffffffffff166003848154811015156119c957fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611a715783600384815481101515611a2257fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611a7f565b5b8280600101935050611996565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b505b505b505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c1b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c8657600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611cb657600080fd5b611cbf86610f99565b15611e6057600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611ddd8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd35780601f10611da857610100808354040283529160200191611dd3565b820191906000526020600020905b815481529060010190602001808311611db657829003601f168201915b5050505050611fc0565b15611e1457857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611e5f565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b5b5b505b50505b505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611e9557600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190611f54929190612068565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a25b5b509392505050565b6000806040516020840160008287838a8c6187965a03f1925050508091505b50949350505050565b81548183558181151161200f5781836000526020600020918201910161200e91906120e8565b5b505050565b81548183558181151161203b5781836000526020600020918201910161203a91906120e8565b5b505050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106120a957805160ff19168380011785556120d7565b828001600101855582156120d7579182015b828111156120d65782518255916020019190600101906120bb565b5b5090506120e491906120e8565b5090565b61210a91905b808211156121065760008160009055506001016120ee565b5090565b905600a165627a7a72305820f1129b699b3017535535a920e15503cd06af1f5c77637c0637cc29355b1dad3400290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc19" - }, - "0x1204700000000000000000000000000000000003": { - "constructor": "0x606060405234156200001057600080fd5b6040516200240138038062002401833981016040528080518201919060200180519060200190919050505b600082518260328211158015620000525750818111155b801562000060575060008114155b80156200006e575060008214155b15156200007a57600080fd5b600092505b8451831015620001b6576002600086858151811015156200009c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156200012b5750600085848151811015156200010857fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013757600080fd5b60016002600087868151811015156200014c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b82806001019350506200007f565b8460039080519060200190620001ce929190620001e3565b50836004819055505b5b5050505050620002b8565b8280548282559060005260206000209081019282156200025f579160200282015b828111156200025e5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000204565b5b5090506200026e919062000272565b5090565b620002b591905b80821115620002b157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000279565b5090565b90565b61213980620002c86000396000f3006060604052361561011b576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101da57806320ea8d86146102135780632f54bf6e146102365780633411c81c1461028757806354741525146102e15780637065cb4814610325578063784547a71461035e5780638b51d13f146103995780639ace38c2146103d0578063a0e67e2b146104ce578063a8abe69a14610539578063b5dc40c3146105d1578063b77bf6001461064a578063ba51a6df14610673578063c01a8c8414610696578063c6427474146106b9578063d74f8edd14610752578063dc8452cd1461077b578063e20056e6146107a4578063ee22610b146107fc575b5b6000341115610174573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b5b005b341561018257600080fd5b610198600480803590602001909190505061081f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101e557600080fd5b610211600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061085f565b005b341561021e57600080fd5b6102346004808035906020019091905050610b02565b005b341561024157600080fd5b61026d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cae565b604051808215151515815260200191505060405180910390f35b341561029257600080fd5b6102c7600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cce565b604051808215151515815260200191505060405180910390f35b34156102ec57600080fd5b61030f600480803515159060200190919080351515906020019091905050610cfd565b6040518082815260200191505060405180910390f35b341561033057600080fd5b61035c600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d91565b005b341561036957600080fd5b61037f6004808035906020019091905050610f99565b604051808215151515815260200191505060405180910390f35b34156103a457600080fd5b6103ba6004808035906020019091905050611081565b6040518082815260200191505060405180910390f35b34156103db57600080fd5b6103f16004808035906020019091905050611150565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001831515151581526020018281038252848181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156104bc5780601f10610491576101008083540402835291602001916104bc565b820191906000526020600020905b81548152906001019060200180831161049f57829003601f168201915b50509550505050505060405180910390f35b34156104d957600080fd5b6104e16111ac565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105255780820151818401525b602081019050610509565b505050509050019250505060405180910390f35b341561054457600080fd5b610579600480803590602001909190803590602001909190803515159060200190919080351515906020019091905050611241565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105bd5780820151818401525b6020810190506105a1565b505050509050019250505060405180910390f35b34156105dc57600080fd5b6105f260048080359060200190919050506113a2565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106365780820151818401525b60208101905061061a565b505050509050019250505060405180910390f35b341561065557600080fd5b61065d6115d3565b6040518082815260200191505060405180910390f35b341561067e57600080fd5b61069460048080359060200190919050506115d9565b005b34156106a157600080fd5b6106b76004808035906020019091905050611696565b005b34156106c457600080fd5b61073c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611877565b6040518082815260200191505060405180910390f35b341561075d57600080fd5b610765611897565b6040518082815260200191505060405180910390f35b341561078657600080fd5b61078e61189c565b6040518082815260200191505060405180910390f35b34156107af57600080fd5b6107fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506118a2565b005b341561080757600080fd5b61081d6004808035906020019091905050611bc0565b005b60038181548110151561082e57fe5b906000526020600020900160005b915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561089b57600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156108f457600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610a80578273ffffffffffffffffffffffffffffffffffffffff1660038381548110151561098757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a725760036001600380549050038154811015156109e757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2357fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610a80565b5b8180600101925050610951565b6001600381818054905003915081610a989190611fe8565b506003805490506004541115610ab757610ab66003805490506115d9565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25b5b505b5050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610b5b57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610bc657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610bf657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35b5b505b50505b5050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610d8957838015610d3c575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610d6f5750828015610d6e575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610d7b576001820191505b5b8080600101915050610d05565b5b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dcb57600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610e2557600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610e4c57600080fd5b60016003805490500160045460328211158015610e695750818111155b8015610e76575060008114155b8015610e83575060008214155b1515610e8e57600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038054806001018281610efa9190612014565b916000526020600020900160005b87909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b50505b505b505b50565b6000806000809150600090505b60038054905081101561107957600160008581526020019081526020016000206000600383815481101515610fd757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611058576001820191505b60045482141561106b576001925061107a565b5b8080600101915050610fa6565b5b5050919050565b600080600090505b600380549050811015611149576001600084815260200190815260200160002060006003838154811015156110ba57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561113b576001820191505b5b8080600101915050611089565b5b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169080600101549080600201908060030160009054906101000a900460ff16905084565b6111b4612040565b600380548060200260200160405190810160405280929190818152602001828054801561123657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116111ec575b505050505090505b90565b611249612054565b611251612054565b6000806005546040518059106112645750595b908082528060200260200182016040525b50925060009150600090505b600554811015611322578580156112b8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b806112eb57508480156112ea575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15611314578083838151811015156112ff57fe5b90602001906020020181815250506001820191505b5b8080600101915050611281565b8787036040518059106113325750595b908082528060200260200182016040525b5093508790505b8681101561139657828181518110151561136057fe5b906020019060200201518489830381518110151561137a57fe5b90602001906020020181815250505b808060010191505061134a565b5b505050949350505050565b6113aa612040565b6113b2612040565b6000806003805490506040518059106113c85750595b908082528060200260200182016040525b50925060009150600090505b60038054905081101561152b5760016000868152602001908152602001600020600060038381548110151561141657fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561151d5760038181548110151561149f57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156114da57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b5b80806001019150506113e5565b816040518059106115395750595b908082528060200260200182016040525b509350600090505b818110156115ca57828181518110151561156857fe5b90602001906020020151848281518110151561158057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b8080600101915050611552565b5b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561161357600080fd5b600380549050816032821115801561162b5750818111155b8015611638575060008114155b8015611645575060008214155b151561165057600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a15b5b50505b50565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156116ef57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561174b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156117b757600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361186c85611bc0565b5b5b50505b505b5050565b6000611884848484611e6c565b905061188f81611696565b5b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156118de57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561193757600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151561199157600080fd5b600092505b600380549050831015611a7f578473ffffffffffffffffffffffffffffffffffffffff166003848154811015156119c957fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611a715783600384815481101515611a2257fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611a7f565b5b8280600101935050611996565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b505b505b505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c1b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c8657600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611cb657600080fd5b611cbf86610f99565b15611e6057600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611ddd8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd35780601f10611da857610100808354040283529160200191611dd3565b820191906000526020600020905b815481529060010190602001808311611db657829003601f168201915b5050505050611fc0565b15611e1457857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611e5f565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b5b5b505b50505b505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611e9557600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190611f54929190612068565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a25b5b509392505050565b6000806040516020840160008287838a8c6187965a03f1925050508091505b50949350505050565b81548183558181151161200f5781836000526020600020918201910161200e91906120e8565b5b505050565b81548183558181151161203b5781836000526020600020918201910161203a91906120e8565b5b505050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106120a957805160ff19168380011785556120d7565b828001600101855582156120d7579182015b828111156120d65782518255916020019190600101906120bb565b5b5090506120e491906120e8565b5090565b61210a91905b808211156121065760008160009055506001016120ee565b5090565b905600a165627a7a72305820f1129b699b3017535535a920e15503cd06af1f5c77637c0637cc29355b1dad3400290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc19" - }, - "0x120470000000000000000000000000000000000a": { - "balance": "11310499300000000000000000", - "constructor": "0x606060405234156200001057600080fd5b6040516200240138038062002401833981016040528080518201919060200180519060200190919050505b600082518260328211158015620000525750818111155b801562000060575060008114155b80156200006e575060008214155b15156200007a57600080fd5b600092505b8451831015620001b6576002600086858151811015156200009c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156200012b5750600085848151811015156200010857fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013757600080fd5b60016002600087868151811015156200014c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b82806001019350506200007f565b8460039080519060200190620001ce929190620001e3565b50836004819055505b5b5050505050620002b8565b8280548282559060005260206000209081019282156200025f579160200282015b828111156200025e5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000204565b5b5090506200026e919062000272565b5090565b620002b591905b80821115620002b157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000279565b5090565b90565b61213980620002c86000396000f3006060604052361561011b576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101da57806320ea8d86146102135780632f54bf6e146102365780633411c81c1461028757806354741525146102e15780637065cb4814610325578063784547a71461035e5780638b51d13f146103995780639ace38c2146103d0578063a0e67e2b146104ce578063a8abe69a14610539578063b5dc40c3146105d1578063b77bf6001461064a578063ba51a6df14610673578063c01a8c8414610696578063c6427474146106b9578063d74f8edd14610752578063dc8452cd1461077b578063e20056e6146107a4578063ee22610b146107fc575b5b6000341115610174573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b5b005b341561018257600080fd5b610198600480803590602001909190505061081f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101e557600080fd5b610211600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061085f565b005b341561021e57600080fd5b6102346004808035906020019091905050610b02565b005b341561024157600080fd5b61026d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cae565b604051808215151515815260200191505060405180910390f35b341561029257600080fd5b6102c7600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cce565b604051808215151515815260200191505060405180910390f35b34156102ec57600080fd5b61030f600480803515159060200190919080351515906020019091905050610cfd565b6040518082815260200191505060405180910390f35b341561033057600080fd5b61035c600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d91565b005b341561036957600080fd5b61037f6004808035906020019091905050610f99565b604051808215151515815260200191505060405180910390f35b34156103a457600080fd5b6103ba6004808035906020019091905050611081565b6040518082815260200191505060405180910390f35b34156103db57600080fd5b6103f16004808035906020019091905050611150565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001831515151581526020018281038252848181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156104bc5780601f10610491576101008083540402835291602001916104bc565b820191906000526020600020905b81548152906001019060200180831161049f57829003601f168201915b50509550505050505060405180910390f35b34156104d957600080fd5b6104e16111ac565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105255780820151818401525b602081019050610509565b505050509050019250505060405180910390f35b341561054457600080fd5b610579600480803590602001909190803590602001909190803515159060200190919080351515906020019091905050611241565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105bd5780820151818401525b6020810190506105a1565b505050509050019250505060405180910390f35b34156105dc57600080fd5b6105f260048080359060200190919050506113a2565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106365780820151818401525b60208101905061061a565b505050509050019250505060405180910390f35b341561065557600080fd5b61065d6115d3565b6040518082815260200191505060405180910390f35b341561067e57600080fd5b61069460048080359060200190919050506115d9565b005b34156106a157600080fd5b6106b76004808035906020019091905050611696565b005b34156106c457600080fd5b61073c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611877565b6040518082815260200191505060405180910390f35b341561075d57600080fd5b610765611897565b6040518082815260200191505060405180910390f35b341561078657600080fd5b61078e61189c565b6040518082815260200191505060405180910390f35b34156107af57600080fd5b6107fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506118a2565b005b341561080757600080fd5b61081d6004808035906020019091905050611bc0565b005b60038181548110151561082e57fe5b906000526020600020900160005b915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561089b57600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156108f457600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610a80578273ffffffffffffffffffffffffffffffffffffffff1660038381548110151561098757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a725760036001600380549050038154811015156109e757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2357fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610a80565b5b8180600101925050610951565b6001600381818054905003915081610a989190611fe8565b506003805490506004541115610ab757610ab66003805490506115d9565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25b5b505b5050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610b5b57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610bc657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610bf657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35b5b505b50505b5050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610d8957838015610d3c575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610d6f5750828015610d6e575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610d7b576001820191505b5b8080600101915050610d05565b5b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dcb57600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610e2557600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610e4c57600080fd5b60016003805490500160045460328211158015610e695750818111155b8015610e76575060008114155b8015610e83575060008214155b1515610e8e57600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038054806001018281610efa9190612014565b916000526020600020900160005b87909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b50505b505b505b50565b6000806000809150600090505b60038054905081101561107957600160008581526020019081526020016000206000600383815481101515610fd757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611058576001820191505b60045482141561106b576001925061107a565b5b8080600101915050610fa6565b5b5050919050565b600080600090505b600380549050811015611149576001600084815260200190815260200160002060006003838154811015156110ba57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561113b576001820191505b5b8080600101915050611089565b5b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169080600101549080600201908060030160009054906101000a900460ff16905084565b6111b4612040565b600380548060200260200160405190810160405280929190818152602001828054801561123657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116111ec575b505050505090505b90565b611249612054565b611251612054565b6000806005546040518059106112645750595b908082528060200260200182016040525b50925060009150600090505b600554811015611322578580156112b8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b806112eb57508480156112ea575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15611314578083838151811015156112ff57fe5b90602001906020020181815250506001820191505b5b8080600101915050611281565b8787036040518059106113325750595b908082528060200260200182016040525b5093508790505b8681101561139657828181518110151561136057fe5b906020019060200201518489830381518110151561137a57fe5b90602001906020020181815250505b808060010191505061134a565b5b505050949350505050565b6113aa612040565b6113b2612040565b6000806003805490506040518059106113c85750595b908082528060200260200182016040525b50925060009150600090505b60038054905081101561152b5760016000868152602001908152602001600020600060038381548110151561141657fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561151d5760038181548110151561149f57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156114da57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b5b80806001019150506113e5565b816040518059106115395750595b908082528060200260200182016040525b509350600090505b818110156115ca57828181518110151561156857fe5b90602001906020020151848281518110151561158057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b8080600101915050611552565b5b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561161357600080fd5b600380549050816032821115801561162b5750818111155b8015611638575060008114155b8015611645575060008214155b151561165057600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a15b5b50505b50565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156116ef57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561174b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156117b757600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361186c85611bc0565b5b5b50505b505b5050565b6000611884848484611e6c565b905061188f81611696565b5b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156118de57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561193757600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151561199157600080fd5b600092505b600380549050831015611a7f578473ffffffffffffffffffffffffffffffffffffffff166003848154811015156119c957fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611a715783600384815481101515611a2257fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611a7f565b5b8280600101935050611996565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b505b505b505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c1b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c8657600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611cb657600080fd5b611cbf86610f99565b15611e6057600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611ddd8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd35780601f10611da857610100808354040283529160200191611dd3565b820191906000526020600020905b815481529060010190602001808311611db657829003601f168201915b5050505050611fc0565b15611e1457857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611e5f565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b5b5b505b50505b505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611e9557600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190611f54929190612068565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a25b5b509392505050565b6000806040516020840160008287838a8c6187965a03f1925050508091505b50949350505050565b81548183558181151161200f5781836000526020600020918201910161200e91906120e8565b5b505050565b81548183558181151161203b5781836000526020600020918201910161203a91906120e8565b5b505050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106120a957805160ff19168380011785556120d7565b828001600101855582156120d7579182015b828111156120d65782518255916020019190600101906120bb565b5b5090506120e491906120e8565b5090565b61210a91905b808211156121065760008160009055506001016120ee565b5090565b905600a165627a7a72305820f1129b699b3017535535a920e15503cd06af1f5c77637c0637cc29355b1dad3400290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc19" - }, - "0x0cB1437200aea736788f1Fc56F327c0456c3598D": { - "balance": "250000000000000000" - }, - "0x74dd76E24B2CFB43C1b1a4498295d553D0843746": { - "balance": "250000000000000000" - }, - "0xeeB4CEEe443F9e0D17BdBD6Daa241681EE5E51c2": { - "balance": "250000000000000000" - }, - "0xA005caEa55375ae20e3aAEF746113535503ABC19": { - "balance": "250000000000000000" - }, - "0x1204700000000000000000000000000000000001": { - "constructor": "0x60806040523480156200001157600080fd5b5060405162002f1538038062002f15833981018060405260608110156200003757600080fd5b81019080805190602001909291908051906020019092919080516401000000008111156200006457600080fd5b828101905060208101848111156200007b57600080fd5b81518560208202830111640100000000821117156200009957600080fd5b5050929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620001e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602481526020018062002ec56024913960400191505060405180910390fd5b60018151101562000242576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018062002ee9602c913960400191505060405180910390fd5b6200025c8362000473640100000000026401000000009004565b6200027682620005d5640100000000026401000000009004565b60008090505b81518110156200042057600073ffffffffffffffffffffffffffffffffffffffff16828281518110620002ab57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156200033e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b6001600360008484815181106200035157fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115620003b257fe5b02179055508060036000848481518110620003c957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018190555080806001019150506200027c565b508060029080519060200190620004399291906200065c565b50600260019080546200044e929190620006eb565b506001600060146101000a81548160ff02191690831515021790555050505062000788565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000517576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b828054828255906000526020600020908101928215620006d8579160200282015b82811115620006d75782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906200067d565b5b509050620006e7919062000742565b5090565b8280548282559060005260206000209081019282156200072f5760005260206000209182015b828111156200072e57825482559160010191906001019062000711565b5b5090506200073e919062000742565b5090565b6200078591905b808211156200078157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000749565b5090565b90565b61272d80620007986000396000f3fe608060405234801561001057600080fd5b506004361061015f576000357c0100000000000000000000000000000000000000000000000000000000900480639f723637116100d5578063b980490911610099578063b980490914610594578063bd21442a146105f0578063c805f68b146106b3578063d826b7f1146106f7578063f2fde38b14610765578063f3aeac02146107a95761015f565b80639f7236371461040e578063a00745b61461046d578063a6940b07146104c9578063b3f05b9714610513578063b7ab4db5146105355761015f565b8063455701d611610127578063455701d6146102eb5780634d238c8e1461034a578063715018a61461038e57806375286211146103985780638da5cb5b146103a25780638f32d59b146103ec5761015f565b8063267fa41d1461016457806334ba3c1b146101c057806340550a1c146101de57806340a141ff1461023a578063418349551461027e575b600080fd5b6101a66004803603602081101561017a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610805565b604051808215151515815260200191505060405180910390f35b6101c8610877565b6040518082815260200191505060405180910390f35b610220600480360360208110156101f457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610884565b604051808215151515815260200191505060405180910390f35b61027c6004803603602081101561025057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610965565b005b6102c06004803603602081101561029457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab0565b604051808360038111156102d057fe5b60ff1681526020018281526020019250505060405180910390f35b6102f3610ae1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561033657808201518184015260208101905061031b565b505050509050019250505060405180910390f35b61038c6004803603602081101561036057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b6f565b005b610396610d79565b005b6103a0610eb2565b005b6103aa611328565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103f4611351565b604051808215151515815260200191505060405180910390f35b6104166113a8565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561045957808201518184015260208101905061043e565b505050509050019250505060405180910390f35b6104af6004803603602081101561048357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506114d7565b604051808215151515815260200191505060405180910390f35b6104d16115b8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61051b6115de565b604051808215151515815260200191505060405180910390f35b61053d6115f1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610580578082015181840152602081019050610565565b505050509050019250505060405180910390f35b6105d6600480360360208110156105aa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061167f565b604051808215151515815260200191505060405180910390f35b6106b16004803603608081101561060657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561066d57600080fd5b82018360208201111561067f57600080fd5b803590602001918460018302840111640100000000831117156106a157600080fd5b90919293919293905050506116f0565b005b6106f5600480360360208110156106c957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061194c565b005b6107636004803603606081101561070d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611aff565b005b6107a76004803603602081101561077b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611d59565b005b6107eb600480360360208110156107bf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611ddf565b604051808215151515815260200191505060405180910390f35b60006001600381111561081457fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561086f57fe5b149050919050565b6000600180549050905090565b60006001600381111561089357fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156108ee57fe5b148061095e575060038081111561090157fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561095c57fe5b145b9050919050565b61096d611351565b6109df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610a44576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126736022913960400191505060405180910390fd5b80610a4e81610884565b610aa3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b610aac82611e51565b5050565b60036020528060005260406000206000915090508060000160009054906101000a900460ff16908060010154905082565b60606002805480602002602001604051908101604052809291908181526020018280548015610b6557602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610b1b575b5050505050905090565b610b77611351565b610be9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610c4e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126736022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610cf1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b610cfa81610884565b15610d6d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f546869732076616c696461746f7220697320616c72656164792061637469766581525060200191505060405180910390fd5b610d76816120a4565b50565b610d81611351565b610df3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600060149054906101000a900460ff1615610f35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f56616c696461746f72207365742069732066696e616c697a656400000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ff8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b6001600060146101000a81548160ff02191690831515021790555060008090505b6002805490508110156110dd576000600360006002848154811061103957fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060018160000160006101000a81548160ff021916908360038111156110c157fe5b0217905550818160010181905550508080600101915050611019565b50600073ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461126757600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff021916908360038111156111b557fe5b0217905550600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506000600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b6002600190805461127992919061251f565b507f8564cd629b15f47dc310d45bcbfc9bcf5420b0d51bf0659a16c67f91d276325360016040518080602001828103825283818154815260200191508054801561131857602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ce575b50509250505060405180910390a1565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6060600180549050600280549050111561144a57600280548060200260200160405190810160405280929190818152602001828054801561143e57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116113f4575b505050505090506114d4565b60018054806020026020016040519081016040528092919081815260200182805480156114cc57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611482575b505050505090505b90565b6000600260038111156114e657fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561154157fe5b14806115b1575060038081111561155457fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156115af57fe5b145b9050919050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1681565b6060600180548060200260200160405190810160405280929190818152602001828054801561167557602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161162b575b5050505050905090565b600060038081111561168d57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156116e857fe5b149050919050565b846116fa81610884565b61174f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b8461175981610884565b6117ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b84438110611824576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146118e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b858773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff167f729a19138e072a5a8d3a56d74ae0b5c84530f09aacd6e12b24c5b2fdc3f8a3d060405160405180910390a45050505050505050565b611954611351565b6119c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611a4c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001806126066024913960400191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611af3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260408152602001806126956040913960400191505060405180910390fd5b611afc81612179565b50565b82611b0981610884565b611b5e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b82611b6881610884565b611bbd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b82438110611c33576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611cf6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b838573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fbc459bd9db54016b1966d0fe812bbe0a82cd627ae3eacd01727dc63a432ca41b60405160405180910390a4505050505050565b611d61611351565b611dd3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b611ddc81612200565b50565b600060026003811115611dee57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff166003811115611e4957fe5b149050919050565b600160028054905011611eaf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061262a6027913960400191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154905060006001600280549050039050600060028281548110611f1257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508060028481548110611f4d57fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506002805480919060019003611ff19190612571565b5060038060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff0219169083600381111561205057fe5b021790555083600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061209e612361565b50505050565b6002600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff0219169083600381111561210357fe5b021790555060028190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050612176612361565b50565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156122a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060146101000a81548160ff021916908315150217905550600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a084718a600143034060026040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200180602001828103825283818154815260200191508054801561246c57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311612422575b50509350505050602060405180830381600087803b15801561248d57600080fd5b505af11580156124a1573d6000803e3d6000fd5b505050506040513d60208110156124b757600080fd5b810190808051906020019092919050505061251d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d8152602001806126d5602d913960400191505060405180910390fd5b565b8280548282559060005260206000209081019282156125605760005260206000209182015b8281111561255f578254825591600101919060010190612544565b5b50905061256d919061259d565b5090565b8154818355818111156125985781836000526020600020918201910161259791906125e0565b5b505050565b6125dd91905b808211156125d957600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016125a3565b5090565b90565b61260291905b808211156125fe5760008160009055506001016125e6565b5090565b9056fe52656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f72206c65667441646472657373206973206e6f7420616e206163746976652076616c696461746f7256616c696461746f7220736574206973206e6f742066696e616c697a6564207965744e65772072656c617920636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6552656c617920636f6e747261637420496e6974696174654368616e67652063616c6c6261636b206661696c6564a165627a7a72305820c6b083565ee91eecaf8e5a6f14e1677206b36466bdfe0248a47919657b68255b002952656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f7220696e697469616c6c79000000000000000000000000120470000000000000000000000000000000000500000000000000000000000012047000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000300000000000000000000000036f67dd84e7327c10c7ead6c429a47189798fbdc00000000000000000000000020df7a4e8408add37c6a5c4afc1b1509924619fe00000000000000000000000077901f14183b1669c80e8c6137ff6721c9a26b25" - }, - "0x1204700000000000000000000000000000000000": { - "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561006557600080fd5b506040516040806116c18339810180604052604081101561008557600080fd5b810190808051906020019092919080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a361017482610193640100000000026401000000009004565b61018c816102f4640100000000026401000000009004565b5050610506565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610236576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610398576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561043f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252604281526020018061167f6042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b61116a806105156000396000f3fe608060405234801561001057600080fd5b50600436106100d1576000357c010000000000000000000000000000000000000000000000000000000090048063b7ab4db51161008e578063b7ab4db51461023b578063bd9656771461029a578063c476dd40146102de578063d3e848f114610381578063d69f13bb146103cb578063f2fde38b14610419576100d1565b8063715018a6146100d657806375286211146100e05780638da5cb5b146100ea5780638f32d59b14610134578063a084718a14610156578063ae3783d6146101f1575b600080fd5b6100de61045d565b005b6100e8610596565b005b6100f26106f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61013c610722565b604051808215151515815260200191505060405180910390f35b6101d76004803603604081101561016c57600080fd5b81019080803590602001909291908035906020019064010000000081111561019357600080fd5b8201836020820111156101a557600080fd5b803590602001918460208302840111640100000000831117156101c757600080fd5b9091929391929390505050610779565b604051808215151515815260200191505060405180910390f35b6101f9610893565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102436108b9565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561028657808201518184015260208101905061026b565b505050509050019250505060405180910390f35b6102dc600480360360208110156102b057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109d5565b005b61037f600480360360608110156102f457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561033b57600080fd5b82018360208201111561034d57600080fd5b8035906020019184600183028401116401000000008311171561036f57600080fd5b9091929391929390505050610a5b565b005b610389610ba6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610417600480360360408110156103e157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610bcc565b005b61045b6004803603602081101561042f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ce1565b005b610465610722565b6104d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610659576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f53656e646572206973206e6f742073797374656d00000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663752862116040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401600060405180830381600087803b1580156106df57600080fd5b505af11580156106f3573d6000803e3d6000fd5b50505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610821576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061111d6022913960400191505060405180910390fd5b837f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89848460405180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600190509392505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b7ab4db56040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b15801561093f57600080fd5b505afa158015610953573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250602081101561097d57600080fd5b81019080805164010000000081111561099557600080fd5b828101905060208101848111156109ab57600080fd5b81518560208202830111640100000000821117156109c857600080fd5b5050929190505050905090565b6109dd610722565b610a4f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b610a5881610d67565b50565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bd21442a33868686866040518663ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b158015610b8857600080fd5b505af1158015610b9c573d6000803e3d6000fd5b5050505050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d826b7f13384846040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050600060405180830381600087803b158015610cc557600080fd5b505af1158015610cd9573d6000803e3d6000fd5b505050505050565b610ce9610722565b610d5b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b610d6481610f79565b50565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610e0b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610eb2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260428152602001806110db6042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561101c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fe4e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6553656e646572206973206e6f74207468652052656c6179656420636f6e7472616374a165627a7a72305820df8e72037cf9237ba09d135b3962a00a0186ba17dcbbe421274543975b2c346100294e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6500000000000000000000000012047000000000000000000000000000000000050000000000000000000000001204700000000000000000000000000000000001" - }, - "0x1204700000000000000000000000000000000002": { - "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503480156200006657600080fd5b5060405160408062001b22833981018060405260408110156200008857600080fd5b810190808051906020019092919080519060200190929190505050620000bc6200010c640100000000026401000000009004565b81600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600a81905550505062000825565b60405180610f0001604052806704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704395680a6af46808152602001670438cfa9de4a0e808152602001670437eeee904c06808152602001670436b44ebcb52e8081526020016704351fca638586808152602001670433316184bd0e808152602001670430e914205bc680815260200167042e46e23661ae80815260200167042b4acbc6cec6808152602001670427f4d0d1a30e80815260200167042444f156de868081526020016704203b2d56812e80815260200167041bd784d08b0680815260200167041719f7c4fc0e808152602001670412028633d44680815260200167040c91301d13ae808152602001670406c5f580ba46808152602001670400a0d65ec80e8081526020016703fa21d2b73d068081526020016703f348ea8a192e8081526020016703ec161dd75c868081526020016703e4896c9f070e8081526020016703dca2d6e118c68081526020016703d4625c9d91ae8081526020016703cbc7fdd471c68081526020016703c2d3ba85b90e8081526020016703b98592b167868081526020016703afdd86577d2e8081526020016703a5db9577fa0680815260200167039b7fc012de0e808152602001670390ca06282946808152602001670385ba67b7dbae80815260200167037a50e4c1f54680815260200167036e8d7d46760e8081526020016703627031455e06808152602001670355f900bead2e80815260200167034927ebb2638680815260200167033bfcf220810e80815260200167032e78140905c680815260200167032099516bf1ae80815260200167031260aa4944c6808152602001670303ce1ea0ff0e8081526020016702f4e1ae7320868081526020016702e59b59bfa92e8081526020016702d5fb208699068081526020016702c60102c7f00e8081526020016702b5ad0083ae468081526020016702a4ff19b9d3ae808152602001670293f74e6a6046808152602001670282959e95540e808152602001670270da0a3aaf0680815260200167025ec4915a712e80815260200167024c5533f49a86808152602001670238da1d6b04400081526020016702260c5cdf4d240081526020016702138f83989f90008152602001670201639196fb840081526020016701ef8886da61000081526020016701ddfe6362d0040081526020016701ccc5273048900081526020016701bbdcd242caa40081526020016701ab45649a564000815260200167019afede36eb6400815260200167018b093f188a1000815260200167017b64873f324400815260200167016c10b6aae40000815260200167015d0dcd5b9f4400815260200167014e5bcb51641000815260200167013ffab08c3264008152602001670131ea7d0c0a400081526020016701242b30d0eba4008152602001670116bccbdad6900081526020016701099f4e29cb0400815260200166fcd2b7bdc90000815260200166f0570896d08400815260200166e42c40b4e19000815260200166d8526017fc2400815260200166ccc966c0204000815260200166c19154ad4de400815260200166b6aa29df851000815260200166ac13e656c5c400815260200166a1ce8a1310000081526020016697da151463c4008152602001668e36875ac1100081526020016684e3e0e627e4008152602001667be221b6984000815260200166733149cc1224008152602001666ad1592695900081526020016662c24fc62284008152602001665b042daab900008152602001665396f2d45904008152602001664c7a9f4302900081526020016645af32f6b5a4008152602001663f34adef724000815260200166390b102d386400815260200166333259b00810008152602001662daa8a77e144008152602001662873a284c40000815260200166238da1d6b044008152602001661ef8886da610008152602001661ab45649a5640081526020016616c10b6aae4000815260200166131ea7d0c0a4008152602001660fcd2b7bdc90008152602001660ccc966c0204008152602001660a1ce8a131000081526020016607be221b69840081526020016605b042daab900081526020016603f34adef7240081526020016602873a284c4000815260200166016c10b6aae400815260200165a1ce8a1310008152602001652873a284c40081525060009060786200078e929190620007ab565b506208052060018190555060008054905060015402600281905550565b828054828255906000526020600020908101928215620007ea579160200282015b82811115620007e9578251825591602001919060010190620007cc565b5b509050620007f99190620007fd565b5090565b6200082291905b808211156200081e57600081600090555060010162000804565b5090565b90565b6112ed80620008356000396000f3fe608060405234801561001057600080fd5b5060043610610132576000357c0100000000000000000000000000000000000000000000000000000000900480634476d66a116100bf57806394f7f62b1161008e57806394f7f62b14610451578063df6a503014610493578063e6e100db146104b1578063f91c289814610509578063fb1ac5251461067457610132565b80634476d66a14610363578063553a5c85146103a557806358ceb672146103c357806360d4b8be146103cd57610132565b80631a488047116101065780631a4880471461020357806330f6eb161461022157806333ea51a81461028357806337339a16146102c75780633d84b8c11461030b57610132565b8062f380f414610137578063078d8e7a146101815780630f411cdb146101a357806318129375146101c1575b600080fd5b61013f610692565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101896106b8565b604051808215151515815260200191505060405180910390f35b6101ab6106c8565b6040518082815260200191505060405180910390f35b6101ed600480360360208110156101d757600080fd5b81019080803590602001909291905050506106ce565b6040518082815260200191505060405180910390f35b61020b6106ef565b6040518082815260200191505060405180910390f35b61026d6004803603604081101561023757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106f5565b6040518082815260200191505060405180910390f35b6102c56004803603602081101561029957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061071a565b005b610309600480360360208110156102dd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061079b565b005b61034d6004803603602081101561032157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108a2565b6040518082815260200191505060405180910390f35b61038f6004803603602081101561037957600080fd5b81019080803590602001909291905050506108ba565b6040518082815260200191505060405180910390f35b6103ad6108d2565b6040518082815260200191505060405180910390f35b6103cb6108d8565b005b61040f600480360360208110156103e357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061093c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61047d6004803603602081101561046757600080fd5b810190808035906020019092919050505061096f565b6040518082815260200191505060405180910390f35b61049b6109b3565b6040518082815260200191505060405180910390f35b6104f3600480360360208110156104c757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109b9565b6040518082815260200191505060405180910390f35b6105d56004803603604081101561051f57600080fd5b810190808035906020019064010000000081111561053c57600080fd5b82018360208201111561054e57600080fd5b8035906020019184602083028401116401000000008311171561057057600080fd5b90919293919293908035906020019064010000000081111561059157600080fd5b8201836020820111156105a357600080fd5b803590602001918460208302840111640100000000831117156105c557600080fd5b90919293919293905050506109d1565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561061c578082015181840152602081019050610601565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561065e578082015181840152602081019050610643565b5050505090500194505050505060405180910390f35b61067c610eca565b6040518082815260200191505060405180910390f35b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006106c343610ed0565b905090565b60025481565b600081815481106106db57fe5b906000526020600020016000915090505481565b600a5481565b6008602052816000526040600020602052806000526040600020600091509150505481565b80600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461085e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f742074686520636f6d6d756e6974792066756e6481525060200191505060405180910390fd5b80600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60066020528060005260406000206000915090505481565b60076020528060005260406000206000915090505481565b60035481565b600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055565b600c6020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061097a82610ed0565b1561098857600090506109ae565b6000600154838161099557fe5b04815481106109a057fe5b906000526020600020015490505b919050565b60045481565b60056020528060005260406000206000915090505481565b606080600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a97576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f43616c6c6572206973206e6f74207468652073797374656d000000000000000081525060200191505060405180910390fd5b838390508686905014610af5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061129d6025913960400191505060405180910390fd5b60018686905014610b6e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f42656e65666163746f7273206c697374206c656e677468206973206e6f74203181525060200191505060405180910390fd5b600084846000818110610b7d57fe5b9050602002013561ffff1661ffff1614610be2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061127b6022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1686866000818110610c0757fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161480610c4b5750610c4a43610ed0565b5b15610cc1576000604051908082528060200260200182016040528015610c805781602001602082028038833980820191505090505b506000604051908082528060200260200182016040528015610cb15781602001602082028038833980820191505090505b5081915080905091509150610ec1565b60606002604051908082528060200260200182016040528015610cf35781602001602082028038833980820191505090505b50905060608151604051908082528060200260200182016040528015610d285781602001602082028038833980820191505090505b509050610d5d88886000818110610d3b57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff16610edf565b82600081518110610d6a57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050610dad4361096f565b81600081518110610dba57fe5b602002602001018181525050610df1600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16610edf565b82600181518110610dfe57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600a5481600181518110610e4857fe5b602002602001018181525050610e8682600081518110610e6457fe5b602002602001015182600081518110610e7957fe5b6020026020010151610f8c565b610eb882600181518110610e9657fe5b602002602001015182600181518110610eab57fe5b6020026020010151611134565b81819350935050505b94509492505050565b60015481565b60006002548210159050919050565b600080600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f825782915050610f87565b809150505b919050565b610fef81600860008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000438152602001908152602001600020546111f290919063ffffffff16565b600860008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060004381526020019081526020016000208190555061109581600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546111f290919063ffffffff16565b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506110fe8160076000438152602001908152602001600020546111f290919063ffffffff16565b600760004381526020019081526020016000208190555061112a816003546111f290919063ffffffff16565b6003819055505050565b611149816004546111f290919063ffffffff16565b6004819055506111a181600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546111f290919063ffffffff16565b600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506111ee8282610f8c565b5050565b600080828401905083811015611270576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4f766572666c6f77206572726f7200000000000000000000000000000000000081525060200191505060405180910390fd5b809150509291505056fe42656e65666163746f72206973206e6f742074686520626c6f636b20617574686f7242656e65666163746f72732f7479706573206c697374206c656e6774682064696666657273a165627a7a723058209c17c14dc9f6fcdd3ddef3913263be4e76376e948facfc131a975ceda79ba0d6002900000000000000000000000012047000000000000000000000000000000000030000000000000000000000000000000000000000000000000856d3dfb6e26d00" - }, - "0x1204700000000000000000000000000000000004": { - "balance": "40789499640000000000000000", - "constructor": "0x608060405260006001556a21bd8334ca74c3834c00003073ffffffffffffffffffffffffffffffffffffffff16311462000085576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018062001b5b6023913960400191505060405180910390fd5b6200009e6200010b640100000000026401000000009004565b6a21bd8334ca74c3834c00006001541462000105576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602881526020018062001b0a6028913960400191505060405180910390fd5b620017b4565b6200014a73120470000000000000000000000000000000000a6a084595161401484a000000635d408564620016da640100000000026401000000009004565b62000188735a3977e000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b620001c673aae0debd2a4c519364e6098537145369913b26ec6901c761ff4c24e9210000635db8d244620016da640100000000026401000000009004565b620002047336874a6000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b6200024273108dd64c9e0d603d56304e97656e011831ed07ff6901c761ff4c24e9210000635db8d244620016da640100000000026401000000009004565b6200028073294f3750000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620002be732fb294a0000000000000000000000000000000006902d2cd1fdffd73150000635db8d244620016da640100000000026401000000009004565b620002fc73102fb040000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200033a732b250010000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620003787335c468d000000000000000000000000000000000696abafbb89b2adcd80000635db8d244620016da640100000000026401000000009004565b620003b673c8be7a00000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b620003f47353f3170000000000000000000000000000000000692004e50f922be25a0000635db8d244620016da640100000000026401000000009004565b6200043373dc918900000000000000000000000000000000006a0215a6ea9b07d650380000635db8d244620016da640100000000026401000000009004565b6200047173b476ee7d610dae7b23b671ebc7bd6112e97729696902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620004af732908b900000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620004ed733b2ef740000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200052b731153818a2eb49f0a71b27313c32814fc02e4db50694220bb939da668600000635db8d244620016da640100000000026401000000009004565b6200056973330d6100000000000000000000000000000000006923466459b949a9950000635db8d244620016da640100000000026401000000009004565b620005a7733e5518c20876eab3a42969d032fa7c30599af912691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b620005e57357f33efad76d4b783cf42c9e6cb08f4425dfe96e6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200062373a87e88f0bbc43468cb657294f2479c6f35179b80692004e50f922be25a0000635db8d244620016da640100000000026401000000009004565b62000661734cebef90000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b6200069f73511909cef97475819de66b645f13464285c227ce6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620006dd7322fc2310000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200071b7347919fb8d4e7e360bef0d5a7a2411593dbcc0e776902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200075973949423db1bfee1ddec99c9d24a12a6ea27cb34896901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b620007977343ec5680000000000000000000000000000000006907f0e10af47c1c700000635db8d244620016da640100000000026401000000009004565b620007d57367cf1c40622f39fa067b614f13aad6da5dd95f326969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620008137310b9390000000000000000000000000000000000692ab13175efe0a8630000635db8d244620016da640100000000026401000000009004565b620008517376b707604cbd862050b938739637b7bde1b7ad486901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200088f73568075f0000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620008cd73d44fb8de580d34f44789408cc9335c9a9ce0ce4d691ad0235eb930a0540000635db8d244620016da640100000000026401000000009004565b6200090a727801b12345b6f10b69263cd6d4fc50b58c8d80696d688e69e3a9742b0000635db8d244620016da640100000000026401000000009004565b62000948735d66a150000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b62000987731d258680000000000000000000000000000000006a020057f2c74d5a91080000635db8d244620016da640100000000026401000000009004565b620009c57330e311c00000000000000000000000000000000069355d7ddc4d956e6c0000635db8d244620016da640100000000026401000000009004565b62000a0373428ab4b019ee3a9b9863b2b4bf1885ce6dff9a736902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62000a41738081f20000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b62000a7f735305c8071b604da7fb45059e7ebfa1d674ddfc3569038780827d32a3ab0000635db8d244620016da640100000000026401000000009004565b62000abd7335007170000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000afc733484c850000000000000000000000000000000006a0422ca8b0a00a42500000063608a9f84620016da640100000000026401000000009004565b62000b3a73ffd9b871df6e93803c0877e98fc1722b39c00d786902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62000b787396a5eb172efdf262ed6beaaf0e20c6af71831fc96934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000bb6732dd2e140000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000bf473656e5569bef7781bf0db199d32027766053501ff69438ec266600555e00000635db8d244620016da640100000000026401000000009004565b62000c32731d53db7000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b62000c7073b5b6d8885fbf28f843cc7886de242b811d6952056901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000cae734549ef8e287b94f1c0a8b88f55ed8707d74d843c6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000cec73dacd80d8e1d4f117515caa477ee7599cdfc766196902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62000d2a731f603e000000000000000000000000000000000069d3c21bcecceda1000000635db8d244620016da640100000000026401000000009004565b62000d68735548f000000000000000000000000000000000006902ab1310b5b095920000635db8d244620016da640100000000026401000000009004565b62000da6735bb9ba00000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000de4736dd10e41a7a84fe23ab35fefa2f46c9895f87a2d693c8c4bde2deef1680000635db8d244620016da640100000000026401000000009004565b62000e227316337db0000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000e6073b999004b49c6b907d4278067da5c85195dcd7fc769081e0dd1d85030b50000635db8d244620016da640100000000026401000000009004565b62000e9e733dbb2290000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000edc73559da540000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000f1a733a9d83766c03c465851a38daa364ef7deccd1ece6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000f5873b61c11b6e42d459efaee8995c44db08507e468e169477d5529f68a63000000635db8d244620016da640100000000026401000000009004565b62000f9673e803d7955a911106cb8fd79049a2374ff059000369038780827d32a3ab0000635db8d244620016da640100000000026401000000009004565b62000fd473509b057000000000000000000000000000000000691aaebeee26cab7360000635db8d244620016da640100000000026401000000009004565b620010127383980db85394d7c1610f37a90be744432368bad4692393a931b168245d0000635db8d244620016da640100000000026401000000009004565b6200105073db6cc57168c07b83a00f1f8871538446068824fc691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b6200108e735035fba0000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620010cc73d96c8300000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200110b7331178cd0000000000000000000000000000000006a042b4dd5360faca070000063608a9f84620016da640100000000026401000000009004565b62001149732f531158e2305ed4fb4144a2f4085e3d96e1982e6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62001188735d5da310000000000000000000000000000000006a017293b0a9e69fd9c00000635db8d244620016da640100000000026401000000009004565b620011c67318b9347000000000000000000000000000000000696abafbb89b2adcd80000635d408564620016da640100000000026401000000009004565b62001203722d4606b65c033769968bcdc63881b90b0853f5692648770b742bb90b0000635db8d244620016da640100000000026401000000009004565b62001241731faa8d10000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b6200127f7333445720000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620012bd730d1d4e623d10f9fba5db95830f7d3839406c6af26901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b620012fa72c2f65230815d30eaa1a4d057bcf0b72fe3cc4e6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62001338739c3a5ec7bd63ecac1b92abe4b01b12ffd50bf3f26969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620013767345635660000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620013b2739be61e41490f5227080fa1adbe3ec0a973d59732678ac7230489e80000635cc83884620016da640100000000026401000000009004565b620013f073572f91b0000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b6200142e7339350e4a75d1e50a5072950325328884c11c12326901c761ff4c24e9210000635db8d244620016da640100000000026401000000009004565b6200146c733c96a530000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620014aa730ad7ba4af33b485e6f2505c417554631a3e5643f6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620014e8735eea7250000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62001526733ab790e0000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62001564734cf84620000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620015a2735425f6f0000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620015e0736d516767e4068fc331bdb331fba7578bdb07a68c69234b04ae4f23156b0000635db8d244620016da640100000000026401000000009004565b6200161e73106a8ff0000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200165c73102fb9400000000000000000000000000000000069038780827d32a3ab0000635db8d244620016da640100000000026401000000009004565b6200169a737ed62cf71d519d3bf293ef90829508f92f4ccccb6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620016d873199a8c5000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816000015414801562001735575060008160010154145b6200178c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018062001b326029913960400191505060405180910390fd5b8260016000828254019250508190555082816000018190555081816001018190555050505050565b61034680620017c46000396000f3fe608060405234801561001057600080fd5b5060043610610069576000357c01000000000000000000000000000000000000000000000000000000009004806318a5bbdc1461006e578063192e7a7b146100cd57806330f0dbe0146101115780639976e12f1461012f575b600080fd5b6100b06004803603602081101561008457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061014d565b604051808381526020018281526020019250505060405180910390f35b61010f600480360360208110156100e357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610171565b005b610119610305565b6040518082815260200191505060405180910390f35b610137610314565b6040518082815260200191505060405180910390f35b60006020528060005260406000206000915090508060000154908060010154905082565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600081600001541161022d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f417661696c61626c6520616d6f756e742069732030000000000000000000000081525060200191505060405180910390fd5b806001015442116102a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f486f6c64696e6720706572696f64206973206e6f74206f76657200000000000081525060200191505060405180910390fd5b600081600001549050600082600001819055508273ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156102ff573d6000803e3d6000fd5b50505050565b6a21bd8334ca74c3834c000081565b6001548156fea165627a7a7230582078bf501dd1d053c49557b82491b940e47ebf94b95608375e22da53fcc7120a1a002954617267657420616d6f756e742073686f756c6420657175616c2061637475616c20616d6f756e74486f6c64696e6720666f72207468697320616464726573732077617320616c7265616479207365742e42616c616e63652073686f756c6420657175616c2074617267657420616d6f756e742e" - }, - "0x1204700000000000000000000000000000000006": { - "constructor": "0x6080604052670de0b6b3a76400006003553480156200001d57600080fd5b5060405160208062003e58833981018060405260208110156200003f57600080fd5b8101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a362000126816200012d640100000000026401000000009004565b506200028f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b613bb9806200029f6000396000f3fe6080604052600436106101d4576000357c0100000000000000000000000000000000000000000000000000000000900480639269881411610109578063df57b742116100a7578063ef5454d611610081578063ef5454d614610d59578063f25eb5c114610e17578063f2fde38b14610e2e578063f6d339e414610e7f576101d4565b8063df57b74214610b62578063e30bd74014610bdd578063eadf976014610ca7576101d4565b8063ac72c120116100e3578063ac72c120146109c5578063c3a3582514610a18578063ddca3f4314610abc578063deb931a214610ae7576101d4565b806392698814146108855780639890220b146108d8578063ac4e73f914610907576101d4565b806369fe0e2d1161017657806379ce9fac1161015057806379ce9fac146106e85780638da5cb5b1461075b5780638f32d59b146107b257806390b97fc1146107e1576101d4565b806369fe0e2d146105b45780636a1acc3f14610607578063715018a6146106d1576101d4565b80633f3935d1116101b25780633f3935d1146103ad578063432ced041461044b5780634f39ca59146104915780636795dbcd146104e4576101d4565b806306b2ff47146101d957806319362a2814610242578063267b6922146102f4575b600080fd5b3480156101e557600080fd5b50610228600480360360208110156101fc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610f47565b604051808215151515815260200191505060405180910390f35b34801561024e57600080fd5b506102da6004803603606081101561026557600080fd5b81019080803590602001909291908035906020019064010000000081111561028c57600080fd5b82018360208201111561029e57600080fd5b803590602001918460018302840111640100000000831117156102c057600080fd5b909192939192939080359060200190929190505050610fa7565b604051808215151515815260200191505060405180910390f35b34801561030057600080fd5b5061032d6004803603602081101561031757600080fd5b81019080803590602001909291905050506111ff565b604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182151515158152602001935050505060405180910390f35b3480156103b957600080fd5b50610431600480360360208110156103d057600080fd5b81019080803590602001906401000000008111156103ed57600080fd5b8201836020820111156103ff57600080fd5b8035906020019184600183028401116401000000008311171561042157600080fd5b9091929391929390505050611276565b604051808215151515815260200191505060405180910390f35b6104776004803603602081101561046157600080fd5b8101908080359060200190929190505050611553565b604051808215151515815260200191505060405180910390f35b34801561049d57600080fd5b506104ca600480360360208110156104b457600080fd5b810190808035906020019092919050505061185c565b604051808215151515815260200191505060405180910390f35b3480156104f057600080fd5b506105726004803603604081101561050757600080fd5b81019080803590602001909291908035906020019064010000000081111561052e57600080fd5b82018360208201111561054057600080fd5b8035906020019184600183028401116401000000008311171561056257600080fd5b9091929391929390505050611acc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156105c057600080fd5b506105ed600480360360208110156105d757600080fd5b8101908080359060200190929190505050611bb0565b604051808215151515815260200191505060405180910390f35b34801561061357600080fd5b506106566004803603602081101561062a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c73565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561069657808201518184015260208101905061067b565b50505050905090810190601f1680156106c35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156106dd57600080fd5b506106e6611d23565b005b3480156106f457600080fd5b506107416004803603604081101561070b57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611e5c565b604051808215151515815260200191505060405180910390f35b34801561076757600080fd5b5061077061208a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156107be57600080fd5b506107c76120b3565b604051808215151515815260200191505060405180910390f35b3480156107ed57600080fd5b5061086f6004803603604081101561080457600080fd5b81019080803590602001909291908035906020019064010000000081111561082b57600080fd5b82018360208201111561083d57600080fd5b8035906020019184600183028401116401000000008311171561085f57600080fd5b909192939192939050505061210a565b6040518082815260200191505060405180910390f35b34801561089157600080fd5b506108be600480360360208110156108a857600080fd5b81019080803590602001909291905050506121ea565b604051808215151515815260200191505060405180910390f35b3480156108e457600080fd5b506108ed6122f3565b604051808215151515815260200191505060405180910390f35b34801561091357600080fd5b506109ab6004803603604081101561092a57600080fd5b810190808035906020019064010000000081111561094757600080fd5b82018360208201111561095957600080fd5b8035906020019184600183028401116401000000008311171561097b57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612422565b604051808215151515815260200191505060405180910390f35b3480156109d157600080fd5b506109fe600480360360208110156109e857600080fd5b8101908080359060200190929190505050612982565b604051808215151515815260200191505060405180910390f35b348015610a2457600080fd5b50610aa660048036036040811015610a3b57600080fd5b810190808035906020019092919080359060200190640100000000811115610a6257600080fd5b820183602082011115610a7457600080fd5b80359060200191846001830284011164010000000083111715610a9657600080fd5b9091929391929390505050612a8b565b6040518082815260200191505060405180910390f35b348015610ac857600080fd5b50610ad1612b6f565b6040518082815260200191505060405180910390f35b348015610af357600080fd5b50610b2060048036036020811015610b0a57600080fd5b8101908080359060200190929190505050612b75565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b348015610b6e57600080fd5b50610b9b60048036036020811015610b8557600080fd5b8101908080359060200190929190505050612c4e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b348015610be957600080fd5b50610c2c60048036036020811015610c0057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612d27565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610c6c578082015181840152602081019050610c51565b50505050905090810190601f168015610c995780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b348015610cb357600080fd5b50610d3f60048036036060811015610cca57600080fd5b810190808035906020019092919080359060200190640100000000811115610cf157600080fd5b820183602082011115610d0357600080fd5b80359060200191846001830284011164010000000083111715610d2557600080fd5b909192939192939080359060200190929190505050612e08565b604051808215151515815260200191505060405180910390f35b348015610d6557600080fd5b50610dfd60048036036040811015610d7c57600080fd5b8101908080359060200190640100000000811115610d9957600080fd5b820183602082011115610dab57600080fd5b80359060200191846001830284011164010000000083111715610dcd57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613063565b604051808215151515815260200191505060405180910390f35b348015610e2357600080fd5b50610e2c613297565b005b348015610e3a57600080fd5b50610e7d60048036036020811015610e5157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613648565b005b348015610e8b57600080fd5b50610f2d60048036036060811015610ea257600080fd5b810190808035906020019092919080359060200190640100000000811115610ec957600080fd5b820183602082011115610edb57600080fd5b80359060200191846001830284011164010000000083111715610efd57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506136ce565b604051808215151515815260200191505060405180910390f35b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080546001816001161561010002031660029004905014159050919050565b6000846001600082815260200190815260200160002060010160149054906101000a900460ff1615611041576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611119576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b60016020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160149054906101000a900460ff16905083565b600082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff161561135b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b83838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050503373ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461147e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4572726f723a204f6e6c79207768656e2070726f706f7365640000000000000081525060200191505060405180910390fd5b8484600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002091906114cc929190613aa0565b503373ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019250505092915050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff16156115ed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b82600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146116c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f4572726f723a204f6e6c79207768656e20756e7265736572766564000000000081525060200191505060405180910390fd5b60035434101561173e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4572726f723a206f6e6c79207768656e2066656520706169640000000000000081525060200191505060405180910390fd5b6117466120b3565b6117b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b336001600086815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff16847f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405160405180910390a3600192505050919050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff16156118f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b823373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146119ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b600260006001600087815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000611a4f9190613b20565b600180600086815260200190815260200160002060010160146101000a81548160ff0219169083151502179055503373ffffffffffffffffffffffffffffffffffffffff16847fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405160405180910390a3600192505050919050565b6000836001600082815260200190815260200160002060010160149054906101000a900460ff1615611b66576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600160008681526020019081526020016000206002018484604051808383808284378083019250505092505050908152602001604051809103902054600190049150509392505050565b6000611bba6120b3565b611c2c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b816003819055507f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c3826040518082815260200191505060405180910390a160019050919050565b60026020528060005260406000206000915090508054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611d1b5780601f10611cf057610100808354040283529160200191611d1b565b820191906000526020600020905b815481529060010190602001808311611cfe57829003601f168201915b505050505081565b611d2b6120b3565b611d9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000826001600082815260200190815260200160002060010160149054906101000a900460ff1615611ef6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b833373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611fce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b836001600087815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16867f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405160405180910390a460019250505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000836001600082815260200190815260200160002060010160149054906101000a900460ff16156121a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600086815260200190815260200160002060020184846040518083838082843780830192505050925050509081526020016040518091039020549150509392505050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612284576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b60006122fd6120b3565b61236f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b7fdef931299fe61d176f949118058530c1f3f539dcb6950b4e372c9b835c33ca073073ffffffffffffffffffffffffffffffffffffffff16316040518082815260200191505060405180910390a13373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f1935050505015801561241a573d6000803e3d6000fd5b506001905090565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff1615612507576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b848460405180838380828437808301925050509250505060405180910390203373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146125fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b6000868660405180838380828437808301925050509250505060405180910390209050600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415801561276e575080600260006001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051808280546001816001161561010002031660029004801561275f5780601f1061273d57610100808354040283529182019161275f565b820191906000526020600020905b81548152906001019060200180831161274b575b50509150506040518091039020145b156128a557600260006001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006127f49190613b20565b6001600082815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a25b846001600083815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508473ffffffffffffffffffffffffffffffffffffffff167f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600193505050509392505050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612a1c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b6000836001600082815260200190815260200160002060010160149054906101000a900460ff1615612b25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600160008681526020019081526020016000206002018484604051808383808284378083019250505092505050908152602001604051809103902054600190049150509392505050565b60035481565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612c0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612ce8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612dfc5780601f10612dd157610100808354040283529160200191612dfc565b820191906000526020600020905b815481529060010190602001808311612ddf57829003601f168201915b50505050509050919050565b6000846001600082815260200190815260200160002060010160149054906101000a900460ff1615612ea2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612f7a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600102600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff1615613148576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b6131506120b3565b6131c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8484600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209190613210929190613aa0565b508273ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019150509392505050565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561336a5780601f1061333f5761010080835404028352916020019161336a565b820191906000526020600020905b81548152906001019060200180831161334d57829003601f168201915b5050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff161561340d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051808060200182810382528381815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561350d5780601f106134e25761010080835404028352916020019161350d565b820191906000526020600020905b8154815290600101906020018083116134f057829003601f168201915b50509250505060405180910390a260016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156135b95780601f106135975761010080835404028352918201916135b9565b820191906000526020600020905b8154815290600101906020018083116135a5575b50509150506040518091039020815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006136459190613b20565b50565b6136506120b3565b6136c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6136cb8161393f565b50565b6000846001600082815260200190815260200160002060010160149054906101000a900460ff1615613768576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613840576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff16600102600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156139e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10613ae157803560ff1916838001178555613b0f565b82800160010185558215613b0f579182015b82811115613b0e578235825591602001919060010190613af3565b5b509050613b1c9190613b68565b5090565b50805460018160011615610100020316600290046000825580601f10613b465750613b65565b601f016020900490600052602060002090810190613b649190613b68565b5b50565b613b8a91905b80821115613b86576000816000905550600101613b6e565b5090565b9056fea165627a7a723058201b0da92162b975c882f9c56423cbea6ec9afb46d8dccc92abe125e91c84d52de00290000000000000000000000001204700000000000000000000000000000000005" - }, - "0x1204700000000000000000000000000000000007": { - "constructor": "0x608060405234801561001057600080fd5b50604051604080610a5f833981018060405261002f919081019061028d565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101448161014b640100000000026401000000009004565b505061036c565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156101bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101b290610309565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000610285825161033a565b905092915050565b600080604083850312156102a057600080fd5b60006102ae85828601610279565b92505060206102bf85828601610279565b9150509250929050565b60006102d6601f83610329565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b60006020820190508181036000830152610322816102c9565b9050919050565b600082825260208201905092915050565b60006103458261034c565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6106e48061037b6000396000f3fe608060405234801561001057600080fd5b506004361061007f576000357c0100000000000000000000000000000000000000000000000000000000900480636ddc4a0714610084578063715018a6146100a25780638da5cb5b146100ac5780638f32d59b146100ca578063f2fde38b146100e8578063fe64d6ff14610104575b600080fd5b61008c610120565b60405161009991906105b3565b60405180910390f35b6100aa610146565b005b6100b461024c565b6040516100c191906105b3565b60405180910390f35b6100d2610275565b6040516100df91906105ce565b60405180910390f35b61010260048036036100fd91908101906104ec565b6102cc565b005b61011e600480360361011991908101906104ec565b61031f565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61014e610275565b61018d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161018490610609565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6102d4610275565b610313576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161030a90610609565b60405180910390fd5b61031c816103aa565b50565b610327610275565b610366576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035d90610609565b60405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561041a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610411906105e9565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006104e48235610678565b905092915050565b6000602082840312156104fe57600080fd5b600061050c848285016104d8565b91505092915050565b61051e8161063a565b82525050565b61052d8161064c565b82525050565b6000610540601f83610629565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b6000610580601383610629565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b60006020820190506105c86000830184610515565b92915050565b60006020820190506105e36000830184610524565b92915050565b6000602082019050818103600083015261060281610533565b9050919050565b6000602082019050818103600083015261062281610573565b9050919050565b600082825260208201905092915050565b600061064582610658565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006106838261068a565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff8216905091905056fea265627a7a723058207424d262effee3c4b2cfbe03927ef8931223cda85f89bf647b88b2f7e64cc5516c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000090000000000000000000000001204700000000000000000000000000000000005" - }, - "0x1204700000000000000000000000000000000008": { - "constructor": "0x60806040523480156200001157600080fd5b5060405160408062002b838339810180604052620000339190810190620002af565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506200014a8162000152640100000000026401000000009004565b5050620003ad565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001c5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001bc9062000332565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600062000291825162000365565b905092915050565b6000620002a7825162000379565b905092915050565b60008060408385031215620002c357600080fd5b6000620002d38582860162000299565b9250506020620002e68582860162000283565b9150509250929050565b6000620002ff601f8362000354565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034d81620002f0565b9050919050565b600082825260208201905092915050565b600062000372826200038d565b9050919050565b6000620003868262000365565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6127c680620003bd6000396000f3fe608060405234801561001057600080fd5b506004361061011d576000357c010000000000000000000000000000000000000000000000000000000090048063954029c9116100b4578063eab7ad9211610083578063eab7ad92146102e4578063ee7ee0fd14610300578063f2fde38b14610330578063fbd74f841461034c5761011d565b8063954029c91461024c578063a999809f14610268578063b71da0d114610298578063da5393d5146102c85761011d565b806370a05b18116100f057806370a05b18146101d6578063715018a6146102065780638da5cb5b146102105780638f32d59b1461022e5761011d565b80631bab58f5146101225780632c5653e21461015257806332d91c0e146101705780636a564261146101a0575b600080fd5b61013c60048036036101379190810190611f97565b61037c565b604051610149919061260c565b60405180910390f35b61015a6107b5565b604051610167919061254f565b60405180910390f35b61018a60048036036101859190810190611f97565b6107db565b604051610197919061256a565b60405180910390f35b6101ba60048036036101b59190810190611f97565b6109e8565b6040516101cd97969594939291906124c4565b60405180910390f35b6101f060048036036101eb9190810190611f97565b610c97565b6040516101fd91906124a2565b60405180910390f35b61020e610ea4565b005b610218610faa565b604051610225919061246c565b60405180910390f35b610236610fd3565b6040516102439190612487565b60405180910390f35b61026660048036036102619190810190611f97565b61102a565b005b610282600480360361027d9190810190611f97565b61119d565b60405161028f919061256a565b60405180910390f35b6102b260048036036102ad9190810190611f97565b6113aa565b6040516102bf9190612487565b60405180910390f35b6102e260048036036102dd9190810190611f97565b61143a565b005b6102fe60048036036102f99190810190611fe9565b611535565b005b61031a60048036036103159190810190611f97565b611853565b6040516103279190612487565b60405180910390f35b61034a60048036036103459190810190611f97565b6119d5565b005b61036660048036036103619190810190611f97565b611a28565b60405161037391906124a2565b60405180910390f35b610384611d63565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561040857600080fd5b505afa15801561041c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506104409190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104a49061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060e0016040529081600082018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105905780601f1061056557610100808354040283529160200191610590565b820191906000526020600020905b81548152906001019060200180831161057357829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106325780601f1061060757610100808354040283529160200191610632565b820191906000526020600020905b81548152906001019060200180831161061557829003601f168201915b50505050508152602001600282018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106d45780601f106106a9576101008083540402835291602001916106d4565b820191906000526020600020905b8154815290600101906020018083116106b757829003601f168201915b50505050508152602001600382018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107765780601f1061074b57610100808354040283529160200191610776565b820191906000526020600020905b81548152906001019060200180831161075957829003601f168201915b505050505081526020016004820160009054906101000a900460ff16151515158152602001600582015481526020016006820154815250509050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561086157600080fd5b505afa158015610875573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108999190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610906576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108fd9061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156109dc5780601f106109b1576101008083540402835291602001916109dc565b820191906000526020600020905b8154815290600101906020018083116109bf57829003601f168201915b50505050509050919050565b6001602052806000526040600020600091509050806000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a945780601f10610a6957610100808354040283529160200191610a94565b820191906000526020600020905b815481529060010190602001808311610a7757829003601f168201915b505050505090806001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b325780601f10610b0757610100808354040283529160200191610b32565b820191906000526020600020905b815481529060010190602001808311610b1557829003601f168201915b505050505090806002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bd05780601f10610ba557610100808354040283529160200191610bd0565b820191906000526020600020905b815481529060010190602001808311610bb357829003601f168201915b505050505090806003018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c6e5780601f10610c4357610100808354040283529160200191610c6e565b820191906000526020600020905b815481529060010190602001808311610c5157829003601f168201915b5050505050908060040160009054906101000a900460ff16908060050154908060060154905087565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b158015610d1d57600080fd5b505afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d559190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610dc2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db99061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610e985780601f10610e6d57610100808354040283529160200191610e98565b820191906000526020600020905b815481529060010190602001808311610e7b57829003601f168201915b50505050509050919050565b610eac610fd3565b610eeb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ee2906125ec565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156110ae57600080fd5b505afa1580156110c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110e69190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611153576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161114a9061258c565b60405180910390fd5b43600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206006018190555050565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561122357600080fd5b505afa158015611237573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061125b9190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146112c8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112bf9061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561139e5780601f106113735761010080835404028352916020019161139e565b820191906000526020600020905b81548152906001019060200180831161138157829003601f168201915b50505050509050919050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060060154600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060050154109050919050565b611442610fd3565b611481576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611478906125ec565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156114f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114e8906125ac565b60405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156115b957600080fd5b505afa1580156115cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115f19190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461165e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116559061258c565b60405180910390fd5b8888600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000191906116af929190611da2565b508686600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001019190611701929190611e22565b508484600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002019190611753929190611da2565b508282600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030191906117a5929190611e22565b5080600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160006101000a81548160ff02191690831515021790555043600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206005018190555050505050505050505050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156118d957600080fd5b505afa1580156118ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119119190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461197e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119759061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160009054906101000a900460ff169050919050565b6119dd610fd3565b611a1c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a13906125ec565b60405180910390fd5b611a2581611c35565b50565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b158015611aae57600080fd5b505afa158015611ac2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ae69190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b53576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b4a9061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611c295780601f10611bfe57610100808354040283529160200191611c29565b820191906000526020600020905b815481529060010190602001808311611c0c57829003601f168201915b50505050509050919050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611ca5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c9c906125cc565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611de357803560ff1916838001178555611e11565b82800160010185558215611e11579182015b82811115611e10578235825591602001919060010190611df5565b5b509050611e1e9190611ea2565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611e6357803560ff1916838001178555611e91565b82800160010185558215611e91579182015b82811115611e90578235825591602001919060010190611e75565b5b509050611e9e9190611ea2565b5090565b611ec491905b80821115611ec0576000816000905550600101611ea8565b5090565b90565b6000611ed382356126e6565b905092915050565b6000611ee782516126e6565b905092915050565b6000611efb82356126f8565b905092915050565b60008083601f840112611f1557600080fd5b8235905067ffffffffffffffff811115611f2e57600080fd5b602083019150836001820283011115611f4657600080fd5b9250929050565b60008083601f840112611f5f57600080fd5b8235905067ffffffffffffffff811115611f7857600080fd5b602083019150836001820283011115611f9057600080fd5b9250929050565b600060208284031215611fa957600080fd5b6000611fb784828501611ec7565b91505092915050565b600060208284031215611fd257600080fd5b6000611fe084828501611edb565b91505092915050565b60008060008060008060008060008060c08b8d03121561200857600080fd5b60006120168d828e01611ec7565b9a505060208b013567ffffffffffffffff81111561203357600080fd5b61203f8d828e01611f03565b995099505060408b013567ffffffffffffffff81111561205e57600080fd5b61206a8d828e01611f4d565b975097505060608b013567ffffffffffffffff81111561208957600080fd5b6120958d828e01611f03565b955095505060808b013567ffffffffffffffff8111156120b457600080fd5b6120c08d828e01611f4d565b935093505060a06120d38d828e01611eef565b9150509295989b9194979a5092959850565b6120ee8161269e565b82525050565b6120fd816126b0565b82525050565b61210c816126b0565b82525050565b600061211d82612639565b612127818561266b565b9350612137818560208601612748565b6121408161277b565b840191505092915050565b60006121568261262e565b612160818561265a565b9350612170818560208601612748565b6121798161277b565b840191505092915050565b600061218f8261262e565b612199818561266b565b93506121a9818560208601612748565b6121b28161277b565b840191505092915050565b6121c681612724565b82525050565b60006121d78261264f565b6121e1818561268d565b93506121f1818560208601612748565b6121fa8161277b565b840191505092915050565b600061221082612644565b61221a818561267c565b935061222a818560208601612748565b6122338161277b565b840191505092915050565b600061224982612644565b612253818561268d565b9350612263818560208601612748565b61226c8161277b565b840191505092915050565b600061228460138361268d565b91507f4572726f723a206f6e6c794c6f676963204462000000000000000000000000006000830152602082019050919050565b60006122c460298361268d565b91507f4572726f723a206e65774c6f6f6b5570206973206e6f7420616c6c6f7765642060008301527f746f2062652030783000000000000000000000000000000000000000000000006020830152604082019050919050565b600061232a601f8361268d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600061236a60138361268d565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600060e08301600083015184820360008601526123ba828261214b565b915050602083015184820360208601526123d48282612205565b915050604083015184820360408601526123ee828261214b565b915050606083015184820360608601526124088282612205565b915050608083015161241d60808601826120f4565b5060a083015161243060a086018261244e565b5060c083015161244360c086018261244e565b508091505092915050565b612457816126dc565b82525050565b612466816126dc565b82525050565b600060208201905061248160008301846120e5565b92915050565b600060208201905061249c6000830184612103565b92915050565b600060208201905081810360008301526124bc8184612112565b905092915050565b600060e08201905081810360008301526124de818a612184565b905081810360208301526124f2818961223e565b905081810360408301526125068188612184565b9050818103606083015261251a818761223e565b90506125296080830186612103565b61253660a083018561245d565b61254360c083018461245d565b98975050505050505050565b600060208201905061256460008301846121bd565b92915050565b6000602082019050818103600083015261258481846121cc565b905092915050565b600060208201905081810360008301526125a581612277565b9050919050565b600060208201905081810360008301526125c5816122b7565b9050919050565b600060208201905081810360008301526125e58161231d565b9050919050565b600060208201905081810360008301526126058161235d565b9050919050565b60006020820190508181036000830152612626818461239d565b905092915050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b60006126a9826126bc565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006126f182612704565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061272f82612736565b9050919050565b6000612741826126bc565b9050919050565b60005b8381101561276657808201518184015260208101905061274b565b83811115612775576000848401525b50505050565b6000601f19601f830116905091905056fea265627a7a723058207e60ec05ac38073f6d98c9cff2cce094d00db0238d6d4b0c183253539a43fe2b6c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000070000000000000000000000001204700000000000000000000000000000000005" - }, - "0x1204700000000000000000000000000000000009": { - "constructor": "0x60806040523480156200001157600080fd5b50604051604080620021cc8339810180604052620000339190810190620002af565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506200014a8162000152640100000000026401000000009004565b5050620003ad565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001c5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001bc9062000332565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600062000291825162000365565b905092915050565b6000620002a7825162000379565b905092915050565b60008060408385031215620002c357600080fd5b6000620002d38582860162000299565b9250506020620002e68582860162000283565b9150509250929050565b6000620002ff601f8362000354565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034d81620002f0565b9050919050565b600082825260208201905092915050565b600062000372826200038d565b9050919050565b6000620003868262000365565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b611e0f80620003bd6000396000f3fe608060405234801561001057600080fd5b50600436106100b0576000357c0100000000000000000000000000000000000000000000000000000000900480638da5cb5b116100835780638da5cb5b146101155780638f32d59b14610133578063b71da0d114610151578063ec26261114610181578063f2fde38b1461019f576100b0565b806312127ed7146100b55780633f67c333146100d1578063715018a6146100db57806389c3ce1e146100e5575b600080fd5b6100cf60048036036100ca9190810190611450565b6101bb565b005b6100d96109dd565b005b6100e3610b98565b005b6100ff60048036036100fa9190810190611427565b610c9e565b60405161010c9190611b14565b60405180910390f35b61011d610d85565b60405161012a91906119ab565b60405180910390f35b61013b610dae565b6040516101489190611a5e565b60405180910390f35b61016b60048036036101669190810190611427565b610e05565b6040516101789190611a5e565b60405180910390f35b610189610ed5565b6040516101969190611a79565b60405180910390f35b6101b960048036036101b49190810190611427565b610efb565b005b6101c3610dae565b610202576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f990611ab4565b60405180910390fd5b6002856040516102129190611994565b602060405180830381855afa15801561022f573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506102529190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84896040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016102cb91906119ab565b60006040518083038186803b1580156102e357600080fd5b505afa1580156102f7573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610320919081019061158b565b60405161032d9190611994565b602060405180830381855afa15801561034a573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525061036d9190810190611562565b1480156104e257506002846040516103859190611994565b602060405180830381855afa1580156103a2573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506103c59190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a999809f896040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161043e91906119ab565b60006040518083038186803b15801561045657600080fd5b505afa15801561046a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061049391908101906115cc565b6040516104a09190611994565b602060405180830381855afa1580156104bd573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506104e09190810190611562565b145b801561065657506002836040516104f99190611994565b602060405180830381855afa158015610516573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506105399190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a05b18896040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016105b291906119ab565b60006040518083038186803b1580156105ca57600080fd5b505afa1580156105de573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610607919081019061158b565b6040516106149190611994565b602060405180830381855afa158015610631573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506106549190810190611562565b145b80156107ca575060028260405161066d9190611994565b602060405180830381855afa15801561068a573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506106ad9190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166332d91c0e896040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161072691906119ab565b60006040518083038186803b15801561073e57600080fd5b505afa158015610752573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061077b91908101906115cc565b6040516107889190611994565b602060405180830381855afa1580156107a5573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506107c89190810190611562565b145b801561089f5750801515600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ee7ee0fd886040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161084b91906119ab565b60206040518083038186803b15801561086357600080fd5b505afa158015610877573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061089b9190810190611539565b1515145b156108df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d690611ad4565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663eab7ad928787878787876040518763ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610960969594939291906119e1565b600060405180830381600087803b15801561097a57600080fd5b505af115801561098e573d6000803e3d6000fd5b505050508573ffffffffffffffffffffffffffffffffffffffff167fc9e49a024d50440c73d2d12d0ae05064094dca76b46dc95e99ea6848eb8f27e960405160405180910390a2505050505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84336040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610a5691906119c6565b60006040518083038186803b158015610a6e57600080fd5b505afa158015610a82573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610aab919081019061158b565b511415610aed576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ae490611af4565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663954029c9336040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610b6491906119c6565b600060405180830381600087803b158015610b7e57600080fd5b505af1158015610b92573d6000803e3d6000fd5b50505050565b610ba0610dae565b610bdf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bd690611ab4565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b610ca661107c565b610cae61107c565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631bab58f5846040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610d2591906119ab565b60006040518083038186803b158015610d3d57600080fd5b505afa158015610d51573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610d7a919081019061160d565b905080915050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b71da0d1836040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610e7e91906119ab565b60206040518083038186803b158015610e9657600080fd5b505afa158015610eaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ece9190810190611539565b9050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610f03610dae565b610f42576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f3990611ab4565b60405180910390fd5b610f4b81610f4e565b50565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610fbe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fb590611a94565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b60006110c78235611cd6565b905092915050565b60006110db8235611ce8565b905092915050565b60006110ef8251611ce8565b905092915050565b60006111038251611cf4565b905092915050565b600082601f83011261111c57600080fd5b815161112f61112a82611b63565b611b36565b9150808252602083016020830185838301111561114b57600080fd5b611156838284611d91565b50505092915050565b600082601f83011261117057600080fd5b813561118361117e82611b8f565b611b36565b9150808252602083016020830185838301111561119f57600080fd5b6111aa838284611d82565b50505092915050565b600082601f8301126111c457600080fd5b81516111d76111d282611b8f565b611b36565b915080825260208301602083018583830111156111f357600080fd5b6111fe838284611d91565b50505092915050565b600082601f83011261121857600080fd5b815161122b61122682611bbb565b611b36565b9150808252602083016020830185838301111561124757600080fd5b611252838284611d91565b50505092915050565b600082601f83011261126c57600080fd5b813561127f61127a82611be7565b611b36565b9150808252602083016020830185838301111561129b57600080fd5b6112a6838284611d82565b50505092915050565b600082601f8301126112c057600080fd5b81516112d36112ce82611be7565b611b36565b915080825260208301602083018583830111156112ef57600080fd5b6112fa838284611d91565b50505092915050565b600060e0828403121561131557600080fd5b61131f60e0611b36565b9050600082015167ffffffffffffffff81111561133b57600080fd5b6113478482850161110b565b600083015250602082015167ffffffffffffffff81111561136757600080fd5b61137384828501611207565b602083015250604082015167ffffffffffffffff81111561139357600080fd5b61139f8482850161110b565b604083015250606082015167ffffffffffffffff8111156113bf57600080fd5b6113cb84828501611207565b60608301525060806113df848285016110e3565b60808301525060a06113f384828501611413565b60a08301525060c061140784828501611413565b60c08301525092915050565b600061141f8251611d1e565b905092915050565b60006020828403121561143957600080fd5b6000611447848285016110bb565b91505092915050565b60008060008060008060c0878903121561146957600080fd5b600061147789828a016110bb565b965050602087013567ffffffffffffffff81111561149457600080fd5b6114a089828a0161115f565b955050604087013567ffffffffffffffff8111156114bd57600080fd5b6114c989828a0161125b565b945050606087013567ffffffffffffffff8111156114e657600080fd5b6114f289828a0161115f565b935050608087013567ffffffffffffffff81111561150f57600080fd5b61151b89828a0161125b565b92505060a061152c89828a016110cf565b9150509295509295509295565b60006020828403121561154b57600080fd5b6000611559848285016110e3565b91505092915050565b60006020828403121561157457600080fd5b6000611582848285016110f7565b91505092915050565b60006020828403121561159d57600080fd5b600082015167ffffffffffffffff8111156115b757600080fd5b6115c3848285016111b3565b91505092915050565b6000602082840312156115de57600080fd5b600082015167ffffffffffffffff8111156115f857600080fd5b611604848285016112af565b91505092915050565b60006020828403121561161f57600080fd5b600082015167ffffffffffffffff81111561163957600080fd5b61164584828501611303565b91505092915050565b61165781611d28565b82525050565b61166681611c8e565b82525050565b61167581611ca0565b82525050565b61168481611ca0565b82525050565b600061169582611c1e565b61169f8185611c50565b93506116af818560208601611d91565b6116b881611dc4565b840191505092915050565b60006116ce82611c1e565b6116d88185611c61565b93506116e8818560208601611d91565b80840191505092915050565b60006116ff82611c13565b6117098185611c3f565b9350611719818560208601611d91565b61172281611dc4565b840191505092915050565b61173681611d3a565b82525050565b600061174782611c34565b6117518185611c7d565b9350611761818560208601611d91565b61176a81611dc4565b840191505092915050565b600061178082611c29565b61178a8185611c6c565b935061179a818560208601611d91565b6117a381611dc4565b840191505092915050565b60006117bb601f83611c7d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b60006117fb601383611c7d565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600061183b602583611c7d565b91507f4572726f723a204e6f206368616e67657320696e20746865207061737365642060008301527f53746174650000000000000000000000000000000000000000000000000000006020830152604082019050919050565b60006118a1601f83611c7d565b91507f4572726f723a20596f7520617265206e6f7420612076616c696461746f7221006000830152602082019050919050565b600060e08301600083015184820360008601526118f182826116f4565b9150506020830151848203602086015261190b8282611775565b9150506040830151848203604086015261192582826116f4565b9150506060830151848203606086015261193f8282611775565b9150506080830151611954608086018261166c565b5060a083015161196760a0860182611985565b5060c083015161197a60c0860182611985565b508091505092915050565b61198e81611ccc565b82525050565b60006119a082846116c3565b915081905092915050565b60006020820190506119c0600083018461165d565b92915050565b60006020820190506119db600083018461164e565b92915050565b600060c0820190506119f6600083018961165d565b8181036020830152611a08818861168a565b90508181036040830152611a1c818761173c565b90508181036060830152611a30818661168a565b90508181036080830152611a44818561173c565b9050611a5360a083018461167b565b979650505050505050565b6000602082019050611a73600083018461167b565b92915050565b6000602082019050611a8e600083018461172d565b92915050565b60006020820190508181036000830152611aad816117ae565b9050919050565b60006020820190508181036000830152611acd816117ee565b9050919050565b60006020820190508181036000830152611aed8161182e565b9050919050565b60006020820190508181036000830152611b0d81611894565b9050919050565b60006020820190508181036000830152611b2e81846118d4565b905092915050565b6000604051905081810181811067ffffffffffffffff82111715611b5957600080fd5b8060405250919050565b600067ffffffffffffffff821115611b7a57600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611ba657600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611bd257600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611bfe57600080fd5b601f19601f8301169050602081019050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b6000611c9982611cac565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000611ce182611cfe565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000611d3382611d5e565b9050919050565b6000611d4582611d4c565b9050919050565b6000611d5782611cac565b9050919050565b6000611d6982611d70565b9050919050565b6000611d7b82611cac565b9050919050565b82818337600083830152505050565b60005b83811015611daf578082015181840152602081019050611d94565b83811115611dbe576000848401525b50505050565b6000601f19601f830116905091905056fea265627a7a723058204ed2d756516ea8df8dbc4fbf2bd76bb43e93ab01c8c274fc9169dfd0be6254d06c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000080000000000000000000000001204700000000000000000000000000000000005" - } - }, - "nodes": [ - "enode://59c9250cb805409e84c9cd0038e97d8e5e4605b928663675869ebdfd4c251d80ccad76267a5eb2f4362ddceb5ec671f7595463adfc0a12e9f68dbf233072db41@54.70.158.106:30303", - "enode://e487ebacbdad3418905d2ed7f009fa5dbd17d73880854884acc604c0afc1a60a396aa90cb2741278c555a4e30ffc6ffc1c29e83840aa22009ec92fe53f81ec04@99.81.92.124:30303", - "enode://563f12602a117201b39ebeea108185abb15d9286830c074640c9fccbaaaabcc7fe2c95682cc43f95b95059f6d0dc4c9becbc1b2bd78e0c5ef5fddff07d85ba0e@54.201.62.74:30303", - "enode://5903b3acebdc4a34800f6923e5f3aec3ca7e5d1285bec4adb9f20ebb0f87a3bebdd748b1849ca1108a9f1e37ff9ced0b475292b8effc29e95d49ec438f244b02@3.121.165.10:30303", - "enode://8f8e35a6dcacfee946f46447b4703c84f4e485e478143997f86b1834e1b0bb78dab363d700dff3147442b9d3e2a1c521f79340c436eb7245a97c7fe385b89a5d@54.93.159.98:30303", - "enode://bd228aa03cf4a88491c81c5f3ab4a1437df3b463081cc93943c4d3ab37f1e4f8081c6995eca076f717d4fdf9a277c750bd0289477ac151f1e2b024747dcd1747@52.31.129.130:30303" - ] + "name": "Volta", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "5", + "validators": { + "contract": "0x1204700000000000000000000000000000000000" + }, + "maximumUncleCountTransition": "0", + "maximumUncleCount": "0", + "blockRewardContractAddress": "0x1204700000000000000000000000000000000002", + "blockRewardContractTransition": "0" + } + } + }, + "params": { + "networkID": "0x12047", + "maximumExtraDataSize": "0x20", + "gasLimitBoundDivisor": "0x400", + "minGasLimit": "0x1388", + "maxCodeSize": "0x6000", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "registrar": "0x1204700000000000000000000000000000000006" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x5B8D80" + }, + "hardcodedSync":{ + "header": "f9022aa0e6ea64a2c18d4e60134a9b80dea17c069127e5c09ebee58b42108ad5ca2d5590a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479420df7a4e8408add37c6a5c4afc1b1509924619fea07a7c1d3a7f2277bac57f1ec76773317841e6e4df6b403ebece7d3b10be71c2d5a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090fffffffffffffffffffffffffffffffe83222801837a120080845d7b87ef856577632d678412b24e63b841236604d05e940c6a1edac36df951e709a650df1969e5267ee6f42d5ba22638a21a79604fddac41b5c0a88aca5a393454781eacd9caf42f7ff0b3e87fe3d58d0601", + "totalDifficulty": "761710168469678517616542840624753029141072284", + "CHTs": [ + "0x41b3f780ecce0c3eaa620622be1ea2febc001b506c5420554376b99588e879bb", + "0x3f7357625521b6ee81a254983518ed97a1fd255339f0e491eed4ea2d8322cef2", + "0xcc77642475deb9fce7ce7fecef00f88033e417c066f1b4434e8b0f3a07eff7c7", + "0x75ddc483559ef7443fbc5cdc3d27f3e1dbb30652eea0d4535377de3c7892e08d", + "0x7dcb3d0ab710c314d05845170974cec53a1703d12d4808014894f7dd2dde70a3", + "0x2387995c0a39cc042ca30bccfeae1aff27b6e8c2f8e1ccb3b292ab0f8717b8ee", + "0x789d5915cca6bde2db2a6476bc7b7245f127f74e0eab6b7fb9b795e1e51fecf4", + "0xa25cf67523c8f4c34b7c6cdffbc46690a3c50998271955ca08b10fadfbd247c8", + "0x13699a3633823e28fbc46ab4c2f616831a13a6cb62f47a7ff573d8d198ff7e64", + "0x9ada8934b85fcd68b7c1b49f8f8986e6a3cb854b891ca17f5789412e132c2410", + "0x280caa3c4352d8bb476fbc8e0c28fd7c8fb5c29f408eeee4fda308d7edeb56cc", + "0x92137a64031b9980daec213cc6d68bfc87ec4726eef7d38a89bc18433eba9f57", + "0xa75db25830e0cbe8ee26d10a18773bbad9fbe11e097d29c664ea0856082d5b1b", + "0xdb020cc2ccc3e8b0ddb04ebb11a03354a15e40a8322197066cb130979dc25495", + "0xc463366e332e43c2471980496568f36342a3f4f7605b71e3ffa781020ede03bd", + "0x6fb25b998f4fbfe1373fb4d66380dfd938390704695541ad71fa8eba98cb74b4", + "0xf347fd85e47eb64db6e4f8b8b375122fe733e24da3de290528c9968ee54d2a2c", + "0x5a3c8a89497d57577aeebed419b5e11d81efd75b866d65979dc951bd1b97dad5", + "0xa6d48f29ced64c0c840ec8a265658e93b84c651d01ebffce33f10dadaf755e69", + "0xf54296197260da88327e3c80eba4d834dd03f20cd8c76bdbbc04d26281091b7d", + "0x6fb0aa7b79cd20b68519a1b30ee09cfb7c7df2c9b02722eaf0a2b1994f1d03a7", + "0xde3dab8a0b8cee49ac4d100dbf4863c9867202f774f7b2cf677c33b90936e7a0", + "0x6e96e646463aba29981593bb96614041175c0b6d6778bddb68935e1669cb483c", + "0x1d602efbe8bc065ac5e3008ca70f73921dd810efafa27f898d91be753431a903", + "0x3accb54475a399028cbddd96e77f914754f16c1be246629393713ad5cdd6a114", + "0x68e05b72aabd986254914e37f9c03304700e849cc8e10b7a97ba3842c92f8e2c", + "0xb16109323d95ccc44a651bf3202d827c2716d7041ad9e8558413ab8d2d7662e9", + "0x6741650eff050b1f0dc9d5256c3fec32b1601ba2413bbd75686f5d7928404cbb", + "0x8adf2952e8421b515b4425a5c1293e34e64e826a29d1c84be8059b87c072962f", + "0x0f23def280a4ac70820d01514506848740574ddc64539ed527140d8dca23469a", + "0x16b54677ea07856d7d69f032d9de2257bff506a11a8f9d5d0b63513b6afef3f9", + "0x429fae4397163ebb74482e43bf26877596fc436c5c87875bfe1ccd3fb73460b1", + "0x3d04731b7364885f37f476fe522d1a830a50f03e439be1844dcaae3f47928286", + "0x4aa8c62ac903d3bd4a789d332e8999d81054b1cbd35630e9c3e50d74810339e8", + "0xf165649e446d524766db95454e0e335fdb1f13687342be5ca2b5690495fb5a3a", + "0xfa3a865d026689e473ca3a82f14cacb0f53f7be04a7b528d2a976e336fc5d6b0", + "0xb066f97cfc19cae25576452cc7d0b7e3be69266e2655dc2708092f9b78d7e613", + "0x6fd390e9b661ed4074d7ea2234b7ae8ac90fe0e35d99ed4ef0e5d2ec17c646c6", + "0x51c6c122bd893f6370e8017f9394d3d8de9eb2187742fdd4cf5849bf4e80c8b6", + "0x85b10318b9fcddb09ec4846fe8905ceeac7ef1923cdaa5386eb2c91ad1121c19", + "0xd864bd5b7e65e7ce9f29162fa74d1903d8c898986abf8ad9c840bd14d4e6459a", + "0xfc5af2fb20535be25e5cadd2061491c308c556abf5b4ddd4b35e348eb1deec48", + "0xcf6d36922996b8b2fd98266fc1d21e702d87f3d5da5c24bce9ce12fa70b89142", + "0xba60ecebe0d75655e69e1cc9d225e44ae502b0147019629ec8951c5e02dd15c4", + "0xe9fba22b379f6b32316020ea1cd3af47049eb58cae10c7d43bb631fbea60c8c5", + "0x6dd578a373989ad3c8c701052bd120967b36713495d24c47467c801aca309545", + "0x6f8d05df4c09e7de8727eadf74caa91ccbe15c1f258fe19dd49fab01915652f7", + "0x0c3a833d2d63cb70481f4259477170745da87d724a7917575ff1617695d8f310", + "0x500f6b80d76f315a7c0dde48b7215c2aca55c2ddf4d09f803df8418c75b5c06d", + "0xc93861aae0c074b06bade783c9f58d3008b9545e4f189df2b482d9adddd48f33", + "0x78adeaf0a5423dda0c8775711535404ce8beb7f5bc4463c4c7a217d38aad9378", + "0x843e9ade9356c8bcfa8b90faeba38be759e55bf20780971535dab53ab2aa03cb", + "0xf4b47f5e0031d2042bba03bde9c1bef1279e53dcd3a6e67905af53c149c69952", + "0xd47ce7ba180e2d20ef494f668a9ef75f6a5b29827aba956ebe5045978ebabc92", + "0x280923c19c276cf299614c8b77f0a500cc2dfecf96e3c0c08b268f2a82e015c3", + "0xc10abd2d7d91530601d0e1d68381e063f38839c3c024824a17e501eaf5402c94", + "0x7675ccfbf244887d108afd102036dc42f84bfd1a8f2fea501456521f97536ef6", + "0xc56cecb2621e40724060ac11c9caea23845ecc90b794826e4eecd9ad9309da3d", + "0x205004c63736b7b904eea7b96b9656a293d1171e5bdbef962bdb30bfdb743bc7", + "0x4b53d96d36e99e83eac4b18fde812f8de3d96896d00d6007cac01fde18ce8737", + "0xd11c944eed2bcdf44fea0c7f3c8fc4823bf31ed5ee635dadb7969017f06f3e44", + "0x45b3d09ee88bf835a18446f787b768c7ad84e74a6944927b0cc266aa5f93da9a", + "0x135bf1bc55340ddc8473b34fa8c85e929ecfedab1fede7bbb6f98697d031a3fe", + "0xc510fd2e51dd7bfc42147486db35088140a3529f94c5076a4d1e7e619ab087fb", + "0x3047ece23f726c8cf82f59bd11f6b17cb39579e49405f28f6dad9f8a022e9548", + "0x3edcc019ed4012aa090ad08fb34ba3622dc1b30b75f10053d6e3d5b3a3e255bf", + "0xe180263ab8b372ce297cf0072f36a3e4c23734692e8f55eed22727be1de6eacd", + "0x93155c3aa8621b8a5999052659e60c226e854e93b9594587933ccabafe562d5f", + "0x33315d934d09be93fa523ee69ad5a44fce5c0940052f82cfe1b1a338e56939f0", + "0x586c36d4a2aa91aa933af4cb1336bcd8b484b27e8dc3f262eed314997bbf4506", + "0x4d7f22900814d9ba3365a4007e03581d60aa0639684a69ab5424d60a93d352e1", + "0xe12e4777e4ac20adb04a887064f3543b397bb6cf465cff0372703895eefcf1b7", + "0x0983ba3d5c9ede2f53bf71cf88c9300e1f83671a5d405b42ca711849e99e3a4e", + "0x35a8e5464d903099992e609d64b1a86c5309b8310a0cc7b488073d47cda73370", + "0x81d7c8b464fa3b424703643f332364536758639332b6582a582047d7db30f93c", + "0xd7a4680f8405e42871b522aa4df7b31906490a19904ebf1fdfb6e5bfe194e133", + "0xe5686d67673093eb22f277ce90aef0038691f37637469dc5cf32a29baac39d10", + "0xd972594aba86083be41b1b0f22ee20427b58a72d39d94e692a8cd27984e5d466", + "0x22d999b217f8d22276639f626d8f521dc05d96015ceca97e7b95024697000170", + "0x2e960190c87c8345bb9c0d01e512ae7e04fef2f1285d5c844754683dfc5e0332", + "0x84209c97130935bb1a50ec5ae0dc0ae6d2c2c0bca64aefd5942a3ec4b93c3a8e", + "0xd89e69e11b718a97f259c0bc80ca159c47840700f52fca79ed88fca5983d2892", + "0x4b24c823155a6bb126ec6985700cad976e274aeb0e317453c6cc8af68e5bbf6a", + "0x4c571c77f267bd7412f8ad74ba111fc66cfb4c9ec523777ca382f6004af55ddc", + "0xaced3e3f2c9e7756c1f333876be6a5e2eaaa3f0d2db1820750b11023551874fb", + "0xb768f2d34ebfb0cde65d20246660ed3201189bc908658b306407ac76ece650d6", + "0x72ee68d4c724c5e99c0963315487b366d6733b7f3e233507d8e49e2d7aac8398", + "0x234fd26c96b6105ced8bc2b4f6cdff99b745fb73798fa55f86d93507ec1270d5", + "0xde9a78fb6da543e095b5b21979a4aa0f92b188c2477a06e09d3bb8b557abda2e", + "0xf039f1703ee0e970a160658ec7be0528225b1536239de29d561e197d5689c3b2", + "0xbb7954ddf18678c94e9c5ac7a94b6dd8fe7c55fdc7ca6c33358ab3ab199a8865", + "0x8234b11e36c6cf91d6a51e49651b241fb8d870a3743f3639372ee1cab14dc028", + "0xa34bcdf488e3b8ecd15df8464003d28bbb3be2f6c3415f1ba5c813dd021a9c1d", + "0x1f6a57fd6001870a2e2ea3a2926468333c70c4d5b38419dc69877def6bac83d7", + "0x3a4cba6ff58ddab6bcf3773feca57a0550a4c3c56dda58ce4b73a308290fd7c5", + "0x2eca47820ad04951323dcb1ba4998d5085588717cf3813be673c1c10c17e2736", + "0x18760e32c529dc819ddef8fa4846da065c6275a8dadbb333d22de504e914a84f", + "0x8ef70eb412af3642a93d17a47a431981c8bfeec791ea8db3a3f43d40379ee8db", + "0xc58164a60cd4dbfee3f88c7944503f7f1784c57fbde51759993a512842b072c6", + "0xfc0f06f7ba1729704b1b93627b38847284ff2e8a172a9d6d47f9f08608fd1ad3", + "0x52cac99c64d0eb655660fe629e75cc8e66c8fd3e96517281b94c7a258c4592aa", + "0x2db1cbdc472651297d1494272ffd88846c28e2e8ebaf0e0e772ac35236452b80", + "0x66cf4ddd2d548597f74ea282181a4bfb11bed40a4809100ccce87276eba70ed6", + "0x44691b5cafab1d96b9ce6bb6005f6ef5ae0343c3873da9ec532e1734eee84f79", + "0xb4040911e710f7f0787bd04d4d9ef595cfdab78b9e00a65f3fbe240e07bc4d84", + "0xf42b2c5ebaf0cf42b6e0abc04af81b670488df57380448007804f8af2bf4ee1d", + "0x524a366182001a71599cfad9df99fca964a4caf132fa4af6c7784868eb926284", + "0x390c0feb6fbdfee1aed701cf26c3514e256f654bfcbc280effe38825c7e5423b", + "0x48a1863c854b163dabba8ffdfd4367657a5abff3443a591c13d4b160f4ef8822", + "0x4fc8f04d0cd89aaa43c435fb6aad02f4a6c45bacf6f57aeea29ddc4c552bed07", + "0x55ff12fe3bd59bdaf211ea6b74c87a58c57f8db280c86ca6952bd92351877274", + "0x7b22fcde7d64cd3f059ef5404b2ccdb82eff4300fba8fe25c7de20e43012795d", + "0x6b5bc10a0523747db88828ca35ddd9bf8a417b81442210d5b852c729e673fcca", + "0x70736b069938456836fd8fcd459f0e5e3ad66cb3570efa0d74c50f4a8f363af6", + "0x6dd2d30250675ce0de9f0ea8a806af237c7f285ead2054c3ec27408c36624b4e", + "0xcbafc9853c81de2a2ff637bbfaaddd95b7cf4be5708092f00db15c6f879fc0ed", + "0x4978ec88d1ce26a4f5287c9d76782ec4d86e724875b405fa85cb9a02ee030030", + "0xc801c340607e02750c8e56e608541e7ad4f4cf41462c1302eb8b8ba3a548f542", + "0xebdf8530bacd50755e38a28ef47acce13705f56b73d47c7642dcd94529d30dba", + "0x38f91c066f911e4194e979865fd0ea726e3973fec869ad535867753da287dae6", + "0x88c19e64f1ed5f6b2f9d4484e2af3a950930321777260b78dd6981a14037d2ba", + "0x0ac14f1f0fc2b2f1d60b5baeeabb0e345db69c8fd472c9dbe642595a7e42416a", + "0xb74b2c6e7fb58ce473438ff5c7bab0feafec9545de05291e2e832fdfbf136b6a", + "0x25add22bf0fc9bf19a9ae615dee83dce8cbe8dbb1c4b096f55bb8fb07f33fe02", + "0x3a1feba1f004d85a4c93d3c3fb860b777fd040644851fe5e4c956cdd6d857031", + "0xa553830b92170cbe84d143d873690be13d133aaa979193eaed0a0702bad4a39a", + "0x6b8f50607189c17b9ff6d841dc83044583fb967ad3367ce30c0976eccc47807e", + "0x5860e09a32b0e54b9d0765bf94e153727db8acce9aed30d2a0c0a2275f46aeca", + "0xb9e292fdafc7b5b20941203a86204862d811f118de212365c40cb533d657d470", + "0xa80d09ffe75a4d97c83e8518dd08aff8ec06088228f7bc41f658cee35b147619", + "0xc2cc23513896240766a9e60ee332c968a23228f103af0471f7895a49879513a8", + "0x858670cae905861ad87c120ee128ee6a976b0c6c8a49c4315dcbabb0c87e9a53", + "0xb55c00241029d1f7087f76af36d4a3081f75dc83f3ef5944d7464c215f095ec4", + "0x1ad8cf2111bf6ec6056306eb670d109f352b04fcc8fdae71ff0f9c72eb9adf00", + "0xa3c4ad1d4af73eaa27b6bab3fcd275113ccde3dc293912fafb03b15caa471f2d", + "0x4ce86f2bac3c8b621a032d066d6e427e194aeb7a39032d289311571639ecbeef", + "0x30e56f466f589d89ad7646167a82149b5a4fc17d401c183b228d5f3002377851", + "0x3489db0d7cf3f908699e2c311f51d68de31391a62e297ce9e3bdcef21b175c45", + "0x8d10d9c4fd3f75d8d5cd80095d255f50f638ac64488ed48e29897c43013c2f08", + "0xf14f1f0787357e36f98728e363be7d3062642b0318b49cb08f541f26782e8ccc", + "0x281a085b0112d332c18f3cdcf6aff0e452e8388182cb1bd20c5c46254aecbf0b", + "0x4f57d4301dfd9e6aee9d4d834b535ff6cbadf542a99c83282bd669d4df8e6152", + "0x5b930ef237fbd241eed87947cbd5a52f51df0f5308128c8bf1ffe50ece68111b", + "0xcb1830ad35fe0a9b66821ef8638dbd007259ba512ddc163d7ddb1b9ee2c3b704", + "0x9e998e22f94c0bcd32dca95776030e43ec36075083110192e74c8767c5c5d74b", + "0x05e03d936751d6bc887d90beffbe4ee593c6395c5dc666cdc4e3f614afd07bef", + "0x87c96a659976df9f0c87d27e7e9b7459023958488d88ec5a8cefb0138a336ce9", + "0x7475e882901797c14ca01226e0be42dea05a69b7c334f314dbd776376423c532", + "0x604f21fbe9d40fc2cce8e0924276503b40090ea857668af3386aaf94cd45b582", + "0x8c1c7440a30d3615052635001c4f3d97f5d01c9188b72f97b7b37fffc8405c28", + "0x20da627b4a619e401bb5e96e44bfa75239877e42963ab21f58116c2137479c8b", + "0x4349196f0fd0f857b8fc6a8f05e4243822de9b8627abdde7289f1dc5dce93dbe", + "0x6492274f5516fc78853a2c44bb2caca75b9d8003d9fbf1450ac81670dcd00952", + "0x70db7efb2ca923b56908b47ecf5f7321f4871f8d07045331cee49bc5a382c12d", + "0x41b9d10cf9871cd3e4948ca28d50fe70048daec6d3a3e91368a4304e29dd81a4", + "0x3f543d7a328238f74637396fe505cc37353108941cbe851b7c5a90b55a717f14", + "0xd5644d2b40ddaf47019081ea3c3f201d64175b716508b50d6356fffc78810aad", + "0xdba5f033a86bce361edc6d6cea97a2bf68871237a0bb9952bae4720fb4c68a1e", + "0xd6b3376cf36414d5144da72b312b3b2b2f6ba8dcb7a93a32c0ada619f076be55", + "0x6a5c4b324a7a3c301066ee55aea9dcfa776930b8642fd614664a5ba332a38918", + "0x999f9ec00c2b8d554dd491b93b1d25a09fa6e3910865fafe96a1e02845d11a46", + "0xeab66fa97529aabf17c50cab7f8dc7fa1e4d5af870f0d1ba8c6adbc7c592db50", + "0x38edf3cd86789d6b89b450a1510cf7868bec5a16b0983863a2b743f8fd399e58", + "0x0fd5fb68f9849626ed4b393909affc1ee113a8bed4b2991dfb98a584fa1703ee", + "0xbccfef39ae834f593741ef7898b27765c6e6661caaf020123a112b6368c9ca76", + "0x225fa981a54626352cf6dfe3298b39ae44847060ecd73d1f11c2231e4bbeee27", + "0x61d7079bad157a12aefb9b6c53e02575784833536d9a21971d401b553efd1c9d", + "0x6de24b1defb0c292ce6a90ad04c4fd795f9252adab3719e9aad70f3b13c8c6f6", + "0x4b8e435e443112f773dc913234ef945911af74093cc408ef558ba221592c6d15", + "0x73cf9f270d57128654d4142b3f09ff3fb4c0fd40c33aaf1f80d2db5b75348b22", + "0x8dac36d755951e95d1e5b1520207a9ee03e3cc2f41ebacdd0fc9c825ced0bb1c", + "0x8b32d81cb0fa801ca4b750857810faf6823399cef69c4ec38a748d9edf755f26", + "0x428e16bd34d5b3412e30c1907164e33a7de17620a154c1c2a9d0db1e064a8be3", + "0xcea1b574ac083a5b41da7d41290735c06630813ec44eeca231bf4c9512b2d614", + "0x707088e4e79039bd3f77c4b8f32bb03144edb11a543f059191529c3923f1c237", + "0x37f5ae7d2c2a0d9f61c12850b738c1ab3888d8dc1ec35bc6cdb857c7efa87b06", + "0x245e8e613249283566d6f436a4b2e8d37505270112f8b848d9150dd82d397266", + "0x2ed9aacc4224bd10996112dfd215be598270141f3cd9805d8a7b4e8492f430f7", + "0x4a18b388e58ba85c27245c38123b4aa9ea0a98153c3019b674d06e5ff98929e6", + "0x1043d401a0d62af95ab4cc5adf5050d4c7711d15d4636a230dd3adb47a15799e", + "0xa21fa3d7c91ccb27198c58e099fdf9619747f2c546e6c7530ec3d9fe22fc9047", + "0xd6c0b1b57bf582f0950efbb047b0567224851afc4939fee9e391c286b4a624bd", + "0x3af826ba6b78aac0ae170f191f23c6c13559c00040546706edfbc53bc3d336ff", + "0x1e1ac7cc499be3bc5661ec5a4b8b4b0161e6b3f532ae41fd4798ca7fc6d538da", + "0x6f0165b80c9aa08d72dde0643fb87ffeee9f61fed9d26939a901bf28d97347a6", + "0x4be21c67d18df789097d877d6c90271eb12319c88f65b04b756c3fd02012f427", + "0xcf8523a0e7256c2359db77b57bf22c30a30c0b7beb246166babf12f0a227d056", + "0xa90bccdf7c631ddcaaf1c0faa5f3927a750a0964f84cd3429e6d60581c612da8", + "0xb25181b7ff4d7203549bf20a8aaea8679d6fbe54ea214ee510ec502afbdb878c", + "0x1edf2675a587416317fe13b40ef1c7a35251604177070e6f110c5d0f50a54d71", + "0x0dda75f783981122aa46cb5c74f541636c7a05accfc95249b81de8514308ed12", + "0x015f6f0108de91e89b76c582390e23191c0d2d6d30985fb98574c0b53e3af6ff", + "0x1daaaf31b05dc0ef715861d06c50040bcffed39ceb95cd5d62ed2e8beda64336", + "0xda12f2341d85b3f70511782af0173948bda3b2680d41789b997aae0277ceaf6f", + "0x0acf2d7e658528aee304cabb9c89773d1be71525da49ce9ba2dd1d074bb3e02a", + "0x6bf7604e6b9528b47f9fca4f67b2556455d7cf182d01ebe0fd5e51fd9b3eb60c", + "0xebcc8534b8dd56355fb9c79b2146b89727fb6162d56750c10013616bdcd9a5a2", + "0xa2437a60b8a8420765843087e48417afac675b1ffe8f6f5edca95543ae9b8869", + "0x1d3a7639b96f49ad5a30bb5621bcfb1e7027f6e95a7ae70366406b1dda27d3b1", + "0x551fb4d5fbe49383f93935f8449c5924df2cf25b4ff74111e558339f7fb11ed1", + "0x9868fb8b81b8d58d502eb2ba65d65cef34f6127b15cc4a9eaddd4ca83cefa6f9", + "0xc874f00bf35db88bd32c54b318788ec1ffe1b6c06a7bac12e50c74bfa3a27218", + "0x85137adf206fd4e35715bbb512f7b715c2b0ac4b8972832ec0c5a11cc9398552", + "0x4ae1d4a1a3c870d22db44d647e9c5f53f1ef03f21ee2d47329121ecb84348be2", + "0x4714904bb039b609b517a91124646c037ac9f20ec0258dfc39925ed3cd9f0278", + "0xac60c857c4de03717cee2d96209ef32be8c0b10c4546fb03acee7b544b0c14b3", + "0xee1836df1c112ae89bcad172812909e88920308e707cbdf9afe01f725cc7718d", + "0xa0c86d0ff24b55d82348ba54087733b774dbf59178588c2975837d884dd4af86", + "0x088a7079084ee39a6848a50d3ace8acaa44b9ca257684174027bcb7e9f34af6f", + "0x7b4d4978b1ea3731339c4959483d691011ee74b49549109d56b21b036560b006", + "0x169bbdcc943db811fdcea9ccda6f29e75e9ab379144aed105d17a6a5eb023e6a", + "0xadb88d836adfa898efc591a100976c5f6aa09da42a5c80b913be1378ec1acd92", + "0xc80611c51be4c215d576aff2988cf48e3136e2710e214269709864562f5ee9ca", + "0x40c97f6b48b5a38996173f6498c4e239efe809e2512b74bd59c444609874ba21", + "0x3e52f02ed5d27ad47b7bab14211245f13dd361ac5a138b195a62f6748ac65fd9", + "0x505eb8bacb015678c179e28db862d612bcaf9022c060b9b4b51a0a9d2ff2c9f0", + "0x0e088c3d27ebfeee02748c04479ee5e63feba5ebe09b463ce4cd1106769eabd7", + "0xaf0f49fc79e5fb51960bc567076ceb3744e16357c774e8867d6f86fb9afd56e5", + "0x154ad5b477c2cb4887f64cf907304dd583fdd9435335e37ca87d4d5cc61835d0", + "0xc946833fa78b8d11a3ae7c69c546f20e2a20dbeef39b62c22447776c53ca0237", + "0x920108a359c507ea3c00f8d16ddaf1e30184a7d232f593e3a996ba47e621b51c", + "0xf837ec145ad844f928fb145c19a40076342a9dea88393074fc2d648a005e24a7", + "0x609e463455133feb00cbad4931cf3f201aa7ebe1e3c1af1cc8697cbfd299fe1e", + "0x008345051caf7253a1d6f4f331d84e53bf03e46ff00480f786baa905e97225e6", + "0xc771fccc08f942b0de47a2cd81b956815437c62ceeaf8178bf53f2c0f80f2be2", + "0xe5d361f6413f82983f7d16d6e74027d11edffbd6b8e5fbbed6121d2907154b15", + "0xaae9d7171d1485a92151423d29d8525230fa51b4ca0c07f37cdea261b30d30bc", + "0x28853430f42a2be4754e814655826dbbc1f52dae4a088c44089e2b2cb8123b86", + "0x129112b5f5e5a572d647d201fee35321d8201c8cca1252b9d8d01628910d7bd0", + "0x4f3f106f9de3437026d210d8dd51da8b3424f9198b0e94f9ddc0641be0c9d11d", + "0xe3b0c3be6851efe0965a961132c1888a5a2ba514c509fe227411f9ad97ba4988", + "0x2d33fdd62bcfb47253fece984a192647d590d9f23bbb6b3f3d11226b890f21d6", + "0x0a3d2f4cf5d893b2358a5d82df86611b9a553c99fe075887bc0540953701b47f", + "0x285a07e0a1ccf28b3864fffb1abd3380f7c00e6549a5b2fd6d54e64d1ab515af", + "0x56b08512e43065be1f0716a0432292a262ba63582c8bdf2a417a1f1734850405", + "0xc6da44dced1bb479611041ad17b588f638136da1417e62d21bd1f8dae3aa293c", + "0x6d177be98db07d363417767454f78860783466dea3814587444c1b6b3fb6331c", + "0x0355189dd2d3a39174d796e2aadfb9dfc01d2474793b7055160b99f623030d25", + "0xf95a7687eb64f922c3fdbf5b139281032d17504f6762c2e03749c08016205d14", + "0x95cf1f25fe50e8b8cd120803a3ed507f8f648fbc7910a8d1fb39b1af11cb5f04", + "0x7f79b0e7cfd858bd25db6cd7dcb839006f263dbeec880bd04bad2d928c5088e5", + "0xc696cc26e28404cb81b65a7ba61b891fe44de9d98c60fa34e56e59e6054462a0", + "0x7d1eb9cbfbff7c2bfdf5730ced7c10e506a50dffcc2226dfc2cac0fcc8d839b1", + "0x4b7a3716e7efd15cf68e71c1a18b406ca398bc06393710edded94c15335de317", + "0x5c39545d794c6f1c71717aec3980998f49de8b0e484e6a1cb1ca042a72443430", + "0xe10af689515cae040abd1fd629ae72733e681d94ac02dd0a97f7488e77a7bb1e", + "0xab0b294bfcf04f60573c1f96f99bb4613e4fea8a0e48d400454c7c8037fd30cf", + "0x87bc7473b3ddaa19ae3abb10be411e058d6b4d09b2d814db6b3d9f0ce6c6f566", + "0x2c5f6e71b55d68820aefec53204f30b0763975f815a941bca475d42513e9db48", + "0x20449d9c0986b7b1337996a2552925bcb1fee16be72494a63e4756733a0a8189", + "0x09fa3c9af750f387f8786fd6af7273307e4ae91121ee683d5106619881629013", + "0x664eb523dd54e6c85b34faefe100c61686bc00ecbc7c0780c837c6adc121c4e3", + "0x4b00b656534ca483cbb64d5c71f102f98f76456bc7d490de2babbc6fe554d9c2", + "0xb67d59ae8a4d5cc72743988f742e54763fcc920bf90c24613b2752c5b6b88732", + "0x53f5146410352968b052012b6aaa50467c71dbe46b482dfacab59f5afff9c438", + "0x7f5379ef8bce2e76300f85c80aa3bc75556a78315d9ec1b21396489957681fe9", + "0xdeb314296d9658b21a2ff1462d100d56f39cba861b4279dabdb7c62a24fcc952", + "0xf7512da6a322502a0d2e66e2db3cbc75a6499a5e9b773680ecdcec54ba5271bc", + "0x534715309910cbefd81eb85c0e32e7c00cc6ba134d0d758ed09879e6daa44d49", + "0x31ba6e3e1b63a5cf590795f40386068001059c8ec9f434e7cc8609b76f53e67a", + "0xc306d368230a642e153ee3d7651f102f8be7674a95abb4178214b02cb37e4094", + "0x4e722218d710fd712fdbfeea879346f6ff5ac684b8ec968d4a3e748efef7ae9d", + "0xdb6dedd39c8b44d5d3ee37e6394b25a039827be6b254ec3e74e63df83d1554fb", + "0x466ee9ef4087e4a6868c3714f7b217de47752ca27fbd971027906d5ab938d44f", + "0xe709e3327584f0061259133e307014883130250bcb6da0c0b7537007c49087f4", + "0xe4a16e63aed45118193da628e7639f30c5d92ef07d8173f171905ab636591af6", + "0x6a39375344768a72f3f25d638f870a1a2788a3a087f1427c975059fbe0279316", + "0xc4c5b7805c95e651c9a31f0dbe1f54b27913179c5499ef7821439a3ec90d427d", + "0xa441de2d9f3d3954ed4143147716ca5ed5537d1ac80f229065716befec55bf40", + "0xfe254394bbff420592de9694879d0b8695992bde730e41668651157dd0315b05", + "0xc15543485f35e2ae28c76afede06d359871b71c42fc8ff1235b3b648b42af2ee", + "0x4c189cb795fb07191e2ffc554de7cf71e23a9a0c31ae552b27ef63b81b81cf74", + "0x0012a33e2a754f6a0987fe9036a0816651481fc85e1cc33eae830fce28ae9deb", + "0x482b7e7abeee615a35c6e09ed3d57393e03d7d099bffc8a008571dbb239caba8", + "0xcbebd836de3c4d1d36c42bab17378f6a6e4d3fd56dfd0cecf2c1a12f2a2eda70", + "0xd466f0a6925a6fc25ea984d6d07c680028ff487962e71218b6e98c45db6e29e0", + "0x0a776d754b66554cc74f345d2041870f8893c49f8c3d312d2193b080cd0711cd", + "0x27075982ed5ec6b4bf6df75a9eee970f878c3b6c34b54af50f749af4514aefab", + "0xa6d385435f960dbeaf91abdffae677aee80005ec215bef93a17565c38e0dd28f", + "0x7edd4cc9f1e4b7f00441ec73de6ed8d3aa0d5d5a4fe60502a93e485cf7ee9df7", + "0x78b2b1e1d82d533c20101d17a6203fffc0d4a6431e4b11cefe9e616bc91c3d73", + "0x10ed93e3fc3d39347fad3f4ee9215e808e2f750bee4ecd26a6cf1ac2a609af3d", + "0x78fcf1d3fcf0e8fdae5f45b99f66ef5f909ed449385a34a4af4e0fc6ea9cebc8", + "0x76e7f70fd968df39d855e7fab7efcbf40997709ffcd9ebe4c1f94cfa0bd332f6", + "0x1c89d6e05ca2870b84df63272508fc51740b90f512509d540130c6b70aa0e4ee", + "0xa9271aec610d0dc64ecf170b744607b364f6a89e50137d870fecf9df69476489", + "0xa6bed34b3e626423541100033b4a9ebefdf9ec426a60196c99bd2b3cc4d1968f", + "0x5c3e3d643bdc643398d806187f2db958a5afa3b80813e654a23667b9fdaee979", + "0xccd5a3fea7c635c2e41f7a9bb9eb1dabaa149d7988fb34bece46ba6e420c5b90", + "0x8218d291553b38e6ac774c84f6a47a5bee8564bb9c53c6eccc55b26800c04f3d", + "0x9b42e29ff93389d8081db3ff65790a3844a199d76a680ba3b192d0d2d6e6b5a4", + "0x9bcaeae2b8459080cc22765d6e37e69488ad8ce2330abcfc2d4976780ab29a8c", + "0x8db4865d48ffba2f89232e1089c77da02d89e70402f2259868fde00d5bc0d8b6", + "0x21ba82862602aa263f94218d2c791dfb3c159d64771023b5d7e3afba239b0d1f", + "0x920f0f4b645536cb11d619d81a0836d6353ba4b951f6d2d646f258c5cd41f382", + "0xa5d5ac9a2a7c1ff2fd4c1b07df436d2f0dd1193a0b8decd645ae5fc67ee2a27f", + "0xae4cde591f67c28795d15b80efc20e516ff2542d7f4c1241ffd43ffbc625dd62", + "0xdc538641bc9862cb221dc964b3a363d4123d10013c123afe888aaa3aa93dba7b", + "0x83fd7a102239239c682948dd0444c22fbf6304b42be55dc6390e0170dd74b7b3", + "0x7d5d14e2f08dbc3185fdd5e291e116962f667368e40254ba37072f0743a94ce7", + "0x0930748b243b46787a06ae80e62ba84f8b6ab659326ad3c1996b568bf01a5047", + "0xb90356802cf8bc15e659b9d34fef588cd5d838bd9cb11a51559938b61cd92657", + "0x65a2bafd97b0f1886cadfacedee6632f8bdab7fbc30528b90da0fa142b493ce2", + "0xbf412a90e16c21a86e880dd62795442026f237210cd0a4635a6a1f06c421c806", + "0xf86b0c93cabce3538c2222dc41e1c2064d1dc64f5822e3978d5897808f2d92c6", + "0xf193cfef6f0c5037579917656293690bf90bf1da0b96cf66b72354c12a9adacf", + "0xcdb72eed57fe8634f9aebaed548b2a2d67995513229a68afb90b14376efd6726", + "0x36b096a5d8162ab305813690f066e1737165efa5cbef67b5ff7ec17ea3dfeae5", + "0x999d94a832fa7af1eed43484879bd15bbecb1326e39ca7815b64fdf626538a06", + "0x04d04ba4e96916e71ee06bb821c6d587f500ef05de0bfcac25a1e10573b07952", + "0xce9598c2a7ffed2a04e10930cd7546a99ebe04f16ad562d20ff6b182a53f2f42", + "0xa2f8112093cd50803de152baf66649499d7df5ea5cb5a21ad7da03c513f0626f", + "0xec3626a3b7e2b15b51739fc30aecf98b5c4f37af814f3e1a374fefe205fcea7b", + "0x180715b003cd0de98d1f4158b7ce24a437db531d145673e09f3c70a81b70f7dc", + "0xb843d1791a6f6115878d097af84804b1b8f61e7142f625dc6ac00ec91c3700a1", + "0x0262d2cd0de023e48c820675581f3c3a1fb9c5ff5b074246a3f13ed40156c7fe", + "0x66145883b036e74bad1547f18db7794f503801b5b0fe96fa1c1289b602eb9baf", + "0xe1566a31ce80ddf85494b6c1ee743728b1404467b0923ba3c3017d7431e114d3", + "0x49089ed792e0a76a78c78b2e959e0e4f8f6ed836a5fbc65741946392333b61b2", + "0x5752960eb3d3c38f62972d08e6a8c9fc3ee1a44bb067c711729df07f4ac032bc", + "0xf8fb145ba2fa9684e1954c197e5bc42c88c82f59e4aa1616d8f9345764631e87", + "0xc6e87900e03c656cd0dbcc5a013f7f0d4d5e27b5eaf074233dd1bc1309cfa89c", + "0x9f5c644d82ee74f034c2019d3650ad41c3cb79c8ce3ca8264363294b7b1a0bd7", + "0xea0607e49b125347edfc976d4736965a95cf892e17126ff72558c852616a3d8c", + "0x1018a047a5aa9a82d9b956f486be70ef856fc8c5126cff341a144ae3fa9702b0", + "0xdb655d919eb7347f8484adb7ef958946e0cd7b5ee408c96aa72f8c0b0e54be55", + "0x7f5986b674896dcba816c5008c44e6b09510add8d889ecd85778f1ca7265e35a", + "0x72d4a4d88ab367a072c4d9f9d0583312b60b2cb8c3718fea7c3ffd6e10bb7338", + "0xe58962cbab186e602da601ebbc2b807376472165fb5660610f095470b9a075c1", + "0x6e16dedd38955062c9cae9e0f9d986f98fa75da2f4dba47720e36fcc82ea002e", + "0x3f2777a1dbb9dc3dba63e34df2b2517c71c2e5741502d42c52d92d41bd6a19b8", + "0x4d74d322dfb4672af7368ddc312f0994dd8acc3d4e81d41301bc2cde24028ba5", + "0x0d1933a7f644b352400db5c7518142e97d02231240750590d1928b7a3b3a7d93", + "0x685ced7f0618cf29a5a93b3e444bfa8a6db35f8f87728cd5305184dd95c8f1aa", + "0x53000bb3e8022f80b43a4210a128868228b90576729887fcdb00dbd9a5a1ea1f", + "0x5c8bd1f5712e8cdad38d8ad2ce4889c820e056c2b941ccda795226e43d31adcd", + "0xc4ef659c2f742c74baf94c3d8c5756406c42ffe9511407c1bed50ab7627f35ac", + "0xe9a0f1d1c221c9852ed7d813c5a6df0561547f4dfd89820dee5d8c455bd796f8", + "0xb6213276df04617b51aa0271bd8ce7a154223e7078cf81a839a2293ac7cc247c", + "0x536449da324625ef81f92e6fd31e7265886039e500e35d3a1d69f516433d36f7", + "0x9aa041a9866dd97ef578b2bcb69b0b7283111cabc1e7eb75eedeee35a4e5b074", + "0x0123b2c16e8c9a8d1e3b7da4d218cc51ea674be14f03c6b4d73365fbfdd98ee7", + "0x0eb20387d1ec1ecd026a6d2ad699c54f6ffd8f30b783f8e0fe8e6f5509d15a85", + "0x563a70d42acc1246158fceda0f67b1fea0d2b296532bb2132ddae6198d435702", + "0x36f413dbe2721c85836b21263f0061848b35f1ffed43c016cd6e689a2ebd014a", + "0x3e0fc0db58d13922405b551fa1750bed62649c80f0a06d5086bedd44bc1beaf8", + "0x91adccae8ef501981199c5fa00153a70dc7e789afc129b38118c6da85660f8ae", + "0x1cc1fcd8c4557ab29a1bb097fee2cc8aa2b579ea85bdfdd8e1671a083598c68e", + "0xfe8fb70cae01d9583006f295cd30d07cf02f0dc18fbdce82edea7328c0d85898", + "0x0a046631b0a137d9c517e9f313aaec655e95e89c63e677b650947fd8064d0d39", + "0x888b0dfa9b325119a6277909e7268c6b8f51218e9cf1b99d090a067092361c88", + "0x01a56ea494615b4bb20c9a3734a9e529d31c4e79bad27b83b68070c94c892955", + "0x6f082b3f4d3c90f4c39f1add964c81073611503b1bca9566d681f257e8192055", + "0xc0886e0dfa26e1d505faa269d08105367deebd03ffffcc26c2d8554758fd0f1b", + "0xdc8ad68739d0d51c6600691234eda1de740a62fa110b5e1d66d49e23abcd5de1", + "0x7fb76390fa10105c55cb74719bd5a9ecc4c71c46c7d518eaf8cb5960e3bb4053", + "0xa27a4ff8b5b1643c2d5ec06af78778d915647730f8c1b6f888b9b220afee5140", + "0xf1fb33ca0c32478c068bff627034008fccf4b61e87e037c87220ab4c109598eb", + "0x0eb3d29c871ff80d186d7b5279ff61ffe17d2417727ff1c24a513f8c1733fce2", + "0x2f26a2428ccd2c920337e3ea82d1a9fe982f6e523fd6de951fd6ac7f6273899c", + "0x9627b8322b036daf334a5ee8fd88f26057fdd42df63a5201030517b4bb73d344", + "0x24ae0b0730c905e23cf3c0b144643a65821210de800628a0c7302450150c5493", + "0xd5c4255400a8a12deb48531dd5c3b8dc11d1192193cf3f5f40d8258b4c890663", + "0x0ea6700e745eb2c377d00f8590023aa61b0a99c4d7a18c1584bd41af953e5e6f", + "0x5378294c9b11a30195bc672e85ad2d950ffcdfd20f423fce8b3ee065764e174d", + "0x5d75de8a3fa97d7bee1bfa45421541ed41eac6ef5dd8298edb9d4cced75ace59", + "0xc0a50285022e8b9c3f25e9179155c9799b01dab71c64b00b36dfe0df58ad00a6", + "0x1add4e5cb42d8f4bb7ea4c2562360cfe97620b3d67fa5a36e27fdda8329f738a", + "0x1e0a529811e5abd7f932031c320be09f3a3fee757bd1824bd43a35588918f93c", + "0xf1578c2d5b99764ce92a1706d8f17a0dee00fcd0f65c2341e9c81f189ca42c3a", + "0xf2ed439b1a057602facbbc643c0095d6fc7250967eea08d7f263325330e59487", + "0x4285bc0479a9ae5f0558aa352cbd77c5adfb7d7f3845d4d128eb1fdbbc470482", + "0x2041915557c6d673dcca2ac6d660d6c09374c2f961ac89299b1197f2bfc83687", + "0xe31587cce449ff71c83810f2980435a4d6202e2345e3f501bb11100e82334e21", + "0x5e643dc7416c62cd0dadf7da1f4604936853f1903ac712026385fa845dc57498", + "0x8351d4ebe8322109274952a74ca7957d58eaea590b5495b753cf093d2cfa4992", + "0x29eac09319646f9340fb18aa2aff01d3fa2563809c7ab9ab64d5c2546ab21c26", + "0x5cfe455fc788cece20cd4d9d1c807fb3479f1acdf02613c0030b3c689263eaf4", + "0x8083e0d70b6afcd08cbfc48b228c9e50d85bba69fcffa71d3d2a418490b16729", + "0xb127fff7e08a52a7e1475e6cbfb884e3805419f9063a1ff07fcec2af040d76fa", + "0xf9a2084ab63b7ee7856b1e48478bc86afbecd61d0ecf42cc1f94b81467d73130", + "0x97194527e77fbc0f3939335311f484790bc188518932b498c6bf52d61b3f33ac", + "0x299dffb7cba8574c77e97f67fa108a4b2bf4db140917d5105bd22b0a9a1938ee", + "0xf161e0a7893d1f709c0001231cca9516f98a38f4ee25f5f71d85d2ca8d9a9d27", + "0x12e7644c623f229f1eb97f08a782e734c23876bf7ba289d086e98a3e90e355c1", + "0x2184f1fc43134e2d37169cac37264b4e4375e3a66365edd68652c384f31b5f05", + "0x565aef68dc59280c409112b4648a231e368b00c7adea2aa5d4a28850e5fecac0", + "0xb0ef3e2c4f25d05c004cdaed56d31b428818b75904e390a487a3682e21e40893", + "0x9b50f9cb4217b52cd5fea3aeb94889eff86b106bc951fd0b7e27db194cacd0c1", + "0x604bb1bf7ae4d6302fe754925d34387070bc3f029e6a145fd2e064e3d0f483f1", + "0xa5f933f0ace924f218cdb29dbe93764488121007d9254d150cc977927e903bac", + "0xe90f93265a9d7867c944b7192e12965bbc4c4e8cb0c7254c2db42532a7a2bb73", + "0xaae605c6ecc35897fa941c4a09a769c260c332a085e89aa8ad17f4d38fed0753", + "0x624e55c905ddd77cf4832683274645e10c48dd260ebad13e5ddcbf736fd2d9ab", + "0xae36858cc0dd2f35c7fe34127f77304138c19ecf500cb6629fc3793633146239", + "0x245885a09ddf383321845d293572aa059b5cd022271f2e3f9cd38e3ba3f6cb0c", + "0x04bc3498c84ff9fb5e01ab9c013a5e741df781e013ced7537de9ace0578630b9", + "0x55a526dd034e87063bf86d676f6ad97b83e4391df89f5f9367f3eda470a41f29", + "0x1a152b282e1d949b8803fef764e52f16302c8939aab62d53ca5099fcb192b00a", + "0xc42969585ee1cbb07816b2d569c69cd45435c02b952a81e2a6da1b3bb308a783", + "0x6855a97705f9db1dbce2479c8399829e5ba744e280b248c39112d554e8eb1e08", + "0xfdb86202cb249aff44b498ad1b8c298e68b00d18a78e4a8d79f81a7ce011fce2", + "0x4bb229a97da35dea56bc838e5360b3a8e487235431f54496fb3d82f390513019", + "0x7e16032f6ec38ba31ecb8e2ec27da77abf3e04862f0cb29272b25263fe893674", + "0x5b0b2013271895023d2128d54188312bedd7f94f508042626e9a5b6355620992", + "0x7f457f1688ccea4f900c4225568b0b53211ab9b9d4141e05b3f090d35baa6dc6", + "0xfd12d572cd9674283d57ad9ed1b09e6d23f6f709daa18a95fe87c56268ffda28", + "0x6e7e56c8a820fb2ef01b046f024c637cb246128f9508bbc58ab29b99d72c338d", + "0x53bbf016b4a55dcdd2924d30a62ca01e8f397cf31a8f54cbbdabfe349e64ffa0", + "0x3f4cf9d73818e2735c4b3cfed3365884e48afd69b2b0ccd89b55152baac1512e", + "0x71588b23a23ab7b35e955be65affc7a8e1b3f042e896f809bd72a2a54a5401db", + "0xab830958dcdc452dfc83ae2a01e67ba2f25196e55e546bd3b63e26b57938ca1f", + "0x178d5993c3a1af7952f763ddf798bc991a6a9fe89d63009c0c6a1f194599fa13", + "0x8e0447251ba26d017420d06694a6989fbb90feab5cee6fb21a756324c62219a4", + "0xa3f5a042b7fdd182188aac2042429bd813a373a6144e16e0b5b0cd4bd23aef10", + "0xe6853a6377cfd347ffa7083909a1fdc1c8470aa3f5a14c2297a897caf533d291", + "0x0f41c615e9cc322090efefdb92b16511cda0b42a6805023cf977d4662954997e", + "0x05e6976fa27d04393695aeedffe2dbbc350ba77009a9c214f4d2642d1392e6d8", + "0xd468865424ed556c89de8a27d1999bb9f140f20be787271f3afc4cd2f768675b", + "0x0737a857038a157a0e2d839abf3df94733fcefd9133ce96259228399c0cc834d", + "0x1ac2e20034e5bfc2fd2b2991ca7fcdf616bc42a44f772b70459fcb9bf7bf5770", + "0xc0685dd918de5c0573fa5cce7fe698b8f398b9926fd72b962b4c03ab068a22f7", + "0x283ca13b1ad22ba3813a5e58e91b99cb368dca8d729393e3b1bc09240d32d40b", + "0x4d8ceba5396f65c7944b1af5b0686631f0515b7bcc408743e0e4b806b2225421", + "0x3392667a27109c1e3b1481670537df4c3cf2c02a156b6489e352147c2b53258e", + "0x05c55b996e0835f2262100d2559dde635147b85688f3d0a40296a248145cfd28", + "0x8fb48e8a9b07c08dde30e580524da6dac7a1cc7173ca6deb7d899bc3682b6a39", + "0x425bd7e4683a77a54ce9971f415f988a07346cf333c2620cd9518490d3519cf8", + "0x2a085a4a242295ec378f3c3a71ca44fdb07cafb9a07dd4510e79d735cf444081", + "0x41a50d1a9c8cff6912e878e2d9d6cd9fddbfd518f90a7d30a7f77a8f672d28da", + "0xdcfa6c262c9cbb90248a26dfd99e0baf1cfd5ecbab9482d77222b54ad7916b33", + "0xbb66f9de9b13cda120dd1073e4dcc9b8a0c77e1cd7c90b253f90aab6474f67e8", + "0xb4d895d271f0825566c00ec548da16772da0dce0b67df0abf1f42f990c6446e4", + "0x26bae23affb763fd772b9c295e9fbf86e320bd229b610fea3f96267c47bfd488", + "0xe2796f3b29de84c1e6372d1563e249742b4653bdbc5bbf54fe817ff9e75c7497", + "0x5a8ffe11f7fb77d9eae8a3dfa9055f8b2f927e390ba580d7608c9e1f3440e687", + "0x8140d398ce888edd23d9c02d345ee27c0f8ca35e6061a359b9829877b41fa20d", + "0xef038b71e31cfa731cbf397d72898ad567da9e738a346e2e5a4299e0951898c3", + "0x8abe2e3bd91de4a41ac3cb54203877fffb2510169d0c4019de6ffd036ea615c6", + "0x58a6e8b582799560a4219946d52166b554ab9392825c776a375e747c848dc47d", + "0xce430b618269b1dfb846e434cacd8b0ececdb7f0f3f7e6cc29ab7da65d519680", + "0x547a8ca3474347927139520953affcf3d8e68cdf4770fc49da4950a93eb3d087", + "0x8939b6bf1da0c26aa4c5264a3241489e02cae7e0b9c53262fb621d5cb98034aa", + "0xf83f9c667a8fae29b6f0bc9138ed4331246437c3db77cc81e623b002a8cfa931", + "0xefafc4cc3d7ccc48d75ab8d3d4d153148ce7a9792fc1f38484dad6c5245407ff", + "0xe0c8e9b9cb5a4cdacd6516d44b072a2993e21a211e9b6292868ff50fc838ef99", + "0x880a9054165ac7327a17b6c0a8d3883b038e7578c8f7727bbb0ca477cb34b702", + "0xc4afc2bf9c1de5f8bc512495a8bb1b4ec606dc8f5ffa2bfcd0cebf5d99ac40c9", + "0x201422019952967ff99e271a7eea4c01d616edb9a6d972e926d9ae8a5d5addb0", + "0x4df1fb4c34fba4c8eb5ce44775ff2f5e7ef9094326a800c899c428136c57d4b0", + "0xd24164e0afcb4eff5dd05758eda40268c6085a8f8487af006d50a9c39c68df62", + "0x5a5fb603f2403171871f1c8dfe32b2fb2693fed701c0bb46800af9f6506e7745", + "0x0983b029c2baea9d44cb1ddea2087b4f0fa6a956ab6f0aa072052369e9462d9d", + "0x4de94a20443f2867f4a4da5a553933ef07a5d40409bf37e6d0b0e20a7edbabb5", + "0xedfeabc0fdc51114df2f008828f486e346e84e8440fa385b655ccacd57f36438", + "0xd81d3cca2273125c6ac5a5c9e246e5985fb30e3509c74ccc317e872448c48d39", + "0x67d5507adb24369c70a9e23ca120e449d30013ff36d54083a2ec5ce7e27544f7", + "0xc42788ea0a75f0e0f47f40cf614980ccb396059c3685c157e8b73ea44b4efe53", + "0x3f50107628150c8b7f6f102a39b295969bbfa7457cfdb007f83a478496846876", + "0x9fbd1d647de7d2d441be63244b79175ecd0f00efaabf50b620438ef418120186", + "0x9201463c17da85fb2aead6dc0977782fb9b0ec80489619b07712be1e713e2a53", + "0x50e601d9fe11879d099959f6fc7bcc52c3d28a7557f2174b6c80a4e69d72fc29", + "0x0833a425763fe63e4d3dd4b133744e207ebec5c16cd33781124d5a1312ec205b", + "0x997d3bf7df56c424d838c1ff1238a2f5e8438ad7c024bb86e1d536bec8d10813", + "0xa3d4ee43538381ce7731b0814030ab344b56188423a1fa23bfd683a70bc6a32a", + "0x008f63e3f38f9fcd757de776644f505df7e08c993c7cdf1742bf3ae85447f4ba", + "0x17c75fca2a3214510e39da02c949233faa1fa489bd6e395286cf22935fdf9edc", + "0x0a3dccf92be8568a8030d4e93c3cb8d005fb3dc149c2651a619dfef3e0e2f7b7", + "0x3869bb5d95b58060a16cc35c339fc074e84578abe915aa2b4b4c4a9779e98d23", + "0xcc208015c55e0e90ad04c6e10b573ebd4ca314d63e90744fbb047030149829a9", + "0xb03326ecabfa81fb4d57a701e0308d6851d46ddc7e0b932707772451400a0519", + "0x6366388019d9325046424bf9bcba7d283701692553a0e0d5202c10f23f16d112", + "0x57216573f60c991cf1be93dba6d8683092b765c6a4a62c45a97be0a2a038f8cb", + "0x3b28bba2c54dffc453d98bd95b0137345154b7546c9830c4e48751c50c7ddd4c", + "0x6016315c51e965418fb50031728f0baeaf6bb83784a7aa5482188ea4ba538594", + "0x93513ee2b22d317427232fba955e794040f23fc60c2ad7046b59ba6fa79880dc", + "0x58c2b131ce578a239bc6e344aed34e0b1907078468f52c6565389b687b443ee6", + "0x6fb649f410e24b59dd84eb3a3758211501923a2fc685ff504b5300b6ab9b29c1", + "0x85803e34062982a0ad9dccc2bfd21a25399f25402676175744e5dbb36c3ebc6d", + "0xac52dab7987e01703343eb3e5e3f889e18a39ee4e0afecd2efb0efe223763282", + "0x06332ef3f28285005258d375c523e95a3f5dd6c465ed810b053de9a27bba9a7a", + "0xdf14f0fbfbc16be422d42f66064709c6200f22873eb7f8e64457ed248764f1a2", + "0x1c2d219b87d6bb85ebb9e3f0dcd2f2626d37fd48ad9f9b6f18f56f8671ab5d7c", + "0x71d2e79a2295bbf8732ff802ec1243122e34a55b277c3e33a664a09dacb94841", + "0xf130f0ddb4367c005c3ec58ff3da025651b669643cbc1faf4fce43aac95b481b", + "0x8d5a3736c7ce7c603ff45c9b77e47b47a6c15d344e84535c37a38970d36bc2a9", + "0x06e852c89680a41d84971ba739ff9da471ee96ee28b19866f84eac48ba562554", + "0xd7daa76508f044548c5b2f3fb5a90ee0fd033d4404606cb4112cde594f67b116", + "0x44ab2f2645fa927a64d6f4b9393561c21c9167224092b993ba9703e050d590ca", + "0xf4e160bf5716e176ea248ea61b1d53852552d094390ac0c916abcf478db15030", + "0xc1269e7390ccacae733ac516ba58da6e4ad73b9321089420e5373f0e07d0dcb2", + "0xcd2e06b7eb82f903799c12b8bc2e8dc14a4c5be3f51146b552f6ba9cb7e4f91e", + "0xb718a2571ea271f470cf05af5c1a4be37f82dda304c1c84ab9d6add4dc8786e5", + "0xe713a00d8f6f2e794ce8da3427bd0e938a81abbba7fba6fbb053b43fae8034aa", + "0xa7b7aafa5fde6af9355e926c4e0deee07fd2a35f196eada57253d08405367bdc", + "0x1e97ed3fadd4d32660d4833091e6ced1b8eb3e99d41f42e9d4e874aef6be3217", + "0xbca4b08bf368d8cf7d666c4d7c5d8d10b034c3edc7142ba2bae857598e563e51", + "0xa83205e691d306cc69ca5967432fba70cccc68104e5dbd242035b7654c667815", + "0xcf5c446b8a20f874d5b8390d97e77dc192d65507b637c14181b8547daa10d9fd", + "0x02cac826b7dd1c08501284972a44154c474d3bd3ac5f4994d29a91e1f5d3058d", + "0x280c137e3b0a5314602f361ff8c8dbdda06ebc4f9dace21681439f44f421a3f4", + "0xff05091818cb32578323d8c60999b2d29715a043b94e694c76a50b94b63ec0c3", + "0xd8e53337fb619ae660281135ef1b324c7560b3d4cd497362aea57f0e4c2d18e4", + "0x83cd224e7c0432792f2d9f16eee09381ce52ab50c1dc5a57ed0c1659c7f4ee56", + "0x054993ca9efa566510ee58894ced8c69dac411424e6d1c4f1f6dcf2917e3517f", + "0x858749436fe2dbe91c57e97795965d4309a125ca4728871e080b14860bcf92ce", + "0x11b9d406a15f339c0a4d2a4592a14b59b96d2f912ebe246c2e61081461b03a66", + "0xfe1ac66bfbe527c51e41106ad46bd3febdd91a62628cf44c5647a1a23f5ee1a2", + "0x7b856712c95bff5c7bafe84a8a01f0a3c0bb0482366fa73acb89e0f6e07dabd3", + "0x284fc1319e573b1d3540a41885e03db2313e80960b06325881a1143fd86ce8bb", + "0xf0ce8af7a61b5d59e0daa08ea300ee992188ae898e518644e942efdf6303638e", + "0x1ea31970c2061dd8e60d439b799a8e00c47604f23d8eb7f9cf3b7ea685154be7", + "0xf3d30b8983d1848933674b22f54cbb54824dccea8c4ef7df81ddf6ecc0b4fecd", + "0x36ea20d41ce18f8d961992485210f7278721620d5535393c78245dd273950166", + "0x7245821c7b5b52d5bc677b0c597c8fa0bf11243b68087a763a3c4e37f9d2b515", + "0x3278f59c112998ffc5c089f17e240fccc312c29bde37735d4d30b6f5b2d3e450", + "0x44824db2abea486e28507b493224a26c20b668bd77b5b258ef040b219fe7890f", + "0x3062eec5cf8be0984a32ccc9123f43eb5fcc7a170969c90ce2fff582e80b3865", + "0xeca64b3fa1d26e8a0117b5f01f50ed0ec0d2c598f39dd6dc292bcf630fd08657", + "0x14b514569ecda34e0f9ad2a613a96acdd558bce3787d1ffd23bd62420fb6336d", + "0xec116d4cda3ba46940692fbcb84bbd513514948dce6a653502b78ddc3a7c2baa", + "0x9f174f3b7a20a6ddedf8de640492577e643363ab1e0034ad34f616139eb67a59", + "0xcaf0265214febdb4b6671ca6d05d5bcd4cb0292fa0944bbc80b039e5034ef323", + "0x4a05e3069a7a0f1763595f7e9d7c682eaa355ed39f2c8e4aa0614eeb34dffe83", + "0x9b953b7458aa76592c333e5cca276f5fee77ee1cc13394c227398eb28e628a42", + "0x727c5158d2deaa130fbb407fe5105217eb4484bb2e12cef2d850f134c51ed696", + "0xd27672dca52cbb440b8895d879d4d833976d8ad61f5f898c614cdfc69112d3b6", + "0x0d680ad40da25236980cc69040e4b03a84d275124502ef49b83ba8cde339dbb8", + "0xfde75d4d19e2d748579493175617948377e7e0b7af1a5839fe812b93977a83b5", + "0xbe2acedf4e754f646eeb4318853d0179e63860ccf4951536e52a2f6d4b50fdf6", + "0xe20b56949299c2cf65a6a0fbb5321f90c995677763b1e8537f674701135c6a6e", + "0x95d806e82d7d246fa1efc11bbc6f0892a2a450cb47d40b328c1467bfb0de78da", + "0x127ee2f6fa8265e89bcfe59775ac410b0af89372951a5865de2a7e8b5b555731", + "0x0826d22662a9b86c0fd049c9f0cd56c8bcfea96507ee9c822b7ce9a7011b6fba", + "0x315224d60866d2acebf8968290616ad61136a27bfa620f4bdba9aa0b948893af", + "0xa7b29cb77a7e65ed30277a564e86fe9dc40ab251ccd6dd7a6634e105dad9c59b", + "0x324fc898cb9e695c2a7673e5cac2cbb2d672fbb76ed6348bc4c43a2132ee1889", + "0xe7d0699df7d80900746669d39229c699c0630810ecb00b3afe780cb1abbd898d", + "0xf334f25a4c92cc2e79b3c633a49a00f4bdc4dcea13b6c9cd77bbc821a6ba38cc", + "0x307ad3315c4ac56ecc35e7fd6b77128f2755a5b11f525e63e0bd2f6f2ae69f8c", + "0xb5318892e137af40735f092374d65bb7c39b815056f9c567afaf0fb8ddf77502", + "0x819539ab72d20570db2d98e019fc017fc93b9c6606ebf59ba8e9e5b31be168c5", + "0x7e78c1f5e27c216395eb3d9763da1bcd8ef7c19008070132b4a6b83694d59b04", + "0x0374518118372a97f7c86bec9c79af686a9dcfcf66940c2abec27485049fd915", + "0xf860452500583a47aa37197b206175f0bcf13a90d50733227154bb41f0084b0f", + "0x9392dacaf6053fe8433a64b37af34dcacab720e1cf5060fbe80c7d2d3f73cae1", + "0x4c3bc17ee63d08ab63d03a1913ab4df5c6e24b8cf0709db7e381a5a92736259e", + "0xc3ba0ccd301b2b2b4b2d5cd4fbd23861e0d8436b23fc120c9dc86ff5659e2442", + "0x4cdd61ffa6969f9a618c95dd732f8b9be37d0153e6136e8524897bca156d4fd1", + "0xbd1f9bc5b7786cd205a42d6e2f586d73bc8321e06a4c74029636bd2230331309", + "0x4a91806e84eae34df335b9582b4018a2d1f38e9b823f5b780d1f27d81e8bd640", + "0x9d7b398bdfeb71a798392e941226de28a8abc1ae5b9ebaefd017e816e73ecd83", + "0x01f3cbacf791fc2f580d41e0ab0b31520db9833c388613bd44fa176324d9cd1c", + "0xa6dab4dc4aa066d1b940114ac033f61fc007ad754a10fc7a09a01f3293c2e3ea", + "0x553bdd4c68ca325fcb63b0fcb2f7789e1b15ee5ade70079c827000c8631badfc", + "0x546fda30b26590106475fc1093bb9540d2c4cd5782a18102938b6fcbd3632d24", + "0x05368f01715bb20d01b4ed7ab28947325c3d93fa36ba2cc13c714a46d4129f3f", + "0xc6de44fcc3ea1c4cb6dd81e819cacac726be9f818acb994d85a932beb7368cad", + "0xf7195116f2a5a18448193fd40f034af23538d5c6f49fd8f4f1979a5085bf05fe", + "0xaa74e99f384f5746e6d114be79750c317eb2bf9c42d39b1183805c7e3b5b078e", + "0xfcce4ed1448460fe5ec61a02c6f97d84d3217390c1948c602f01aa03118893b6", + "0x6c9bf42b44decb93eb07ded476b39ad9bd1312a221e6104d3bce72714b34008d", + "0xc85e29f3e0753fc756aa5c46268bbbb47f733902a0f363f03809cdedefa5d58b", + "0xce826332a6883e1e1445ff8da5dcd9af9fa80ee50cecdf663fbb74da7904eb0c", + "0x3ab951e50825f6f6301ffb1e328cfe93f9dc601d1b6d773257275658d0d130af", + "0x0eb5f77b4943002e532714424614d639bbc31ad2a8f00c9ce2368d35fa6a0039", + "0x7278f0d3971431659860d4570e3ba3f5f12a6d839ed6d7be41f9e02954a58dce", + "0xc389e2a42e1f3cdfa2fbf0aeabab5be64c068ecab99ccc073547180ad40e7dbf", + "0x855d175a6c22e578c1de3d0b87834724a56867e94d0bdd5eaf9f20fc411dda17", + "0x51e8b4e347a80f698bd9db854c0d6892a7d3225727ad896698c4c2d3023498b8", + "0x044ead2878d1a6f454c71881381388b207dd5e14550303a1ac9a74636fcd896b", + "0xfccfdfd9c60bb83d6b021884f569ec4576add6ae82c85dd1d19043316fea7895", + "0xc77ea57cdcf46d30fbf03e592fdc38f2a38fcf91ad8f90f322d228e42767793c", + "0xfd5e826c84a82c6fe76adadd8b79c5316fadb59cb3ef16c53f2b5001b61d210a", + "0x43bfe5f0e20fc578430b17895922c7eb37b84dcb9550c79cc92a3494133aab86", + "0x357f5eb1e6756b50ae39a80120ebb83ebc88bb30130ebea7a59da735de0b6d52", + "0xb0a6abe4a323c2cd24c21a036aaa5a893b5c1b7960d6334bded2da3b79595628", + "0x376a933f17ed8158688d3d21c4b911245828ebf3ee04bac36b2aeedec5e44dc1", + "0xb5c0ca679d7870c3640b9d7d928c041d60021601cdfa49ba5b280cd90241a7ab", + "0x37c6fd470099d01d579bc51dfd1bb7b9c87900ea8c63e0d283b44dcb18cf3740", + "0x897083a3699e55a241e524919ce1f483bfa8d66ef996c51d4d99361279fdcd51", + "0xd4ba50801efef0184b66e29b56d05d70ddf9deaddf0ae10b351a2f5a64e94ee5", + "0xf4daed74c17f344cb6de072fb42cdaff7736ca7f4b7f2d86fe8212257da736ca", + "0xcd60602cdfd58ac42f2d535fbb7d8fe009d98149640d72a2a2a03887cc804449", + "0xb0f2377efcd4ffd4ad7ca0943bcbe076e83a81cb0034ffa89d1ad28a43081cbf", + "0x62c44d9939642098240cebd4b82cb7d2312264652b22eb9c769c7d88ef18f070", + "0xb493b132e87ccddcd6dbd0b3a4259526ce1fd3ba427240df1ad81475c25ba5d2", + "0x360f58226f9dabc78ff2a532b819ee0996224aa389c5d5dbf67409a5065b9c0d", + "0xd9fe6d781deb8209b19db5704b40dd644adc2ac6fe5bcdd19a0f9c2f60898361", + "0x06ef9ae44d803ba59e359741314f660a22024262428089213a7f10af543d5321", + "0xee06fa53098291e32400909a5d18070d4c25ca449b217b3c361cb654706c2b1a", + "0x04ce2d5551735396c109123ab3cb1e41f4e0a7877dbcdb737410fea055437cb2", + "0x84d71efb344231bad4fc385fd69ac732861ac7af21b383e2521916c868c6593a", + "0x16310a24107da4fbc3ca801529c6e7c7f57816d606ba3171cdbb1314048a8b25", + "0xc9d2a29a96bd488b7774c34bcbedf541caa41767eaecaa74dc9ca463e1163d3a", + "0x81c6a09ae02edd0584124214a5df98b8520f9b63c19d1c82042724ae860ebe66", + "0xe588657d72617a6c54a93b628ea4249cc0268d7ec61f18e864c45042879cb8cf", + "0x9d26e5e113cbf9a05e924be913651057d828cd25eb9b53591400b018450aa229", + "0x53557c8a12b0c17510265dd1d01dc66aea588105b91d38cc4bdb15e4aaa861bf", + "0x76ea87476033b44c9276b0b2de0e882ed8ce5041adfdcb553e45c759029ef48d", + "0xacd97e5db37c7a3748ad833f4a1645c34cc3f0a611f11759622f5685c92848bc", + "0xa883cd47613b46e4323b14b64b337e8bc1d1226196a6a1bb0da3206204c97408", + "0x53efd395aef5b63c183022f98d52e7d389a96f12adc97085583b407b9f42d105", + "0x4bd2a41121286777ee3556879be8e2239d22f484917e816bc8e4ca843519cb88", + "0x89a8c2e32a4bd621e0f23164aca7c2e6a0125d1c110bbb5d7cea5efd4697a460", + "0x92f9d705c88c88cad2cadee358fcfb41b107a46a4afef58f128b4f8f835da7df", + "0x47e62e3ec453c3d9e57f078480788b9dd74dc987cfc2ab8decc325c6c9b0cbeb", + "0xf457b19f593bbd0452a965305994220379e19d96737d9731a1740b01149f51ff", + "0x62ee8ee67dba1e70b30df35227d76e7b386c78bcc0262d6a9e059e3f85e4a8f1", + "0xb0a130a355c47c406ccd1af450e39fffe9fc9e867d1c6939dcf3767d731c8993", + "0xbd9dd84ebdbb9418a1dfd2c57c9271ea024abd47f8656c71810f0f1cffe2283b", + "0x45ad6f082117859bb8b988a7c59c3e7e70cf743d6e6c9c0f263dc121d2bf9c48", + "0xc4a8ffb84279272d032464f70f443608ad5d26eda5837a255b1167b5d2166980", + "0x4fdda6cb1ad70527d021c1a9815a3fab0889088a022d23436957d370733482ef", + "0x6d3fe87816cf1e5a51f3959cbcfee2b3410ef61de9841cf7b0454f3c55db0835", + "0x7eb86ead4d2a08ea2bfcb8908156b40e48c9ced3239435f1111418d886818a89", + "0xaa245ead854f6114835d5078d74d74827935cb5eac24794a92cc2f1e75c881e1", + "0xf2faee74b4e91c8facd64897fff389211577154cd67a388380672b822e6d83e5", + "0xe7561ba52dfaf1197c84af04d3fe070288bb8a6bc494fc3e0281a833507b22ab", + "0x87ac3d861ad5c397cda1a3594b12aef878e6f89a2c7293b23dfd833e18492548", + "0x3d9b5218c3a7f3e7ab7704f5ee6ce7281cb8da82a33923441bd2f9210e117c2e", + "0x63fa6d289b4ca890001bdc1751fc3fa341e161f4c83efdad7f2c1e801acce49a", + "0xee01e88af73ef0bbefb93531d1c219ba9d93b635921db3953c128f4560acde24", + "0x91fe471a27189f30c5312ebd018322a41ce13f3ac5d9553d96dbe5c52d395200", + "0x99e45722381b9a8881d3be36b4ca2e48e0af1a4cd405f819aab5325adf1aa8fc", + "0x7bcf7a96539e9eee9e0fa802f8f7c6c196ddc5b5a6d661057db2fffa11996a91", + "0xde6b84fd4a401b99f54cd6a6e1664ce34369b23caae87f1a11958002284adbe2", + "0xfd17fcdfefab26e4284034f858fce86e184629e621cda48b529eb1c728b6a587", + "0x2e75427e33e0eeab27d2861a8dff35280156ff2d6f49ebc7c7d9783ed698ff23", + "0xa5a610b4d6ab1cf64995e579ad002413cb65991b38e870aded7c1d2c56db6d9b", + "0xc477485bcce1dc78fbada07358ce66488e1e07e69445834d8f0666607b31a604", + "0x3d2a49320af03fbc986e653cfa0f4748c0f0709ad061a028ccc1105223755dc8", + "0x39c22bd4aeadab10a0bc8014f5e6086c6be8563edcff73e9b2603ce579244636", + "0x509b2a544768870fd21ae2b1f4f4d1ce58b1e9759209818241e0d6d847f5340d", + "0xe2742910f6d34ba0eee958a11ff7980fe83ae61530f802eedf128af253d6f388", + "0xa96e61254a4bf2c365a61306b4097efcd2d92ba00f9cd1e0a4110186c2d90af4", + "0x3bcd2dbe645990dfd54761042bd63cbf633372276ae4e9929c1d82a835b456d3", + "0x2b113f84803ba0536e35381ca1d060674771264377fff821e057006bda97dd7c", + "0x3b662a34635ef80d54c6978e2b65f38757332abeb43e2c2f5420d554ab6e0aea", + "0x3e1496822a0da2153e5476c97c287dc8f8f5a89fec55a9abbdd979b47e205960", + "0x33920d13504b531bd39be9884fb2ee1183d56c1a8c6b458fce241e36a7330409", + "0x07a4cb0f62123b089f8e49b6ac5b4aaf15b75bbc5923c85a019b90a542ea115d", + "0x4cf0dad0eab7ac72265703277eae7c65782954482df634e214e8473c15eef072", + "0x876e4adf62cb1218758f74a7ff054b8a31d22df505ce385431f27e83e1ef667d", + "0xb41e0ea2779e527f524c529dfade91eaa1fefd68898be5df119ba5c5debc88ab", + "0x8a9aeaa2a6c111906cafacef446985002da828dbef1e6b17617670105b80085a", + "0x162f3e5ac3e0e4d97b6dcaaa6b426aafa80db5efe84944cfe616ebbc6ee11d3a", + "0xdcb9962a96557a6e1abee4705625e4bcbe47cfb30154ade63c677b6fb54cd312", + "0x9afa94f2afacb2224f62a0d0adda88c9f8617b8484e3833dd681b262af289215", + "0x835c77c064ca5c0ff520279986eb3b8c011d3f03795ec5ba53c2abd40f6d9be4", + "0x31f812523ec0705b8aea412a8f9d9039a84f10bd9ddfc9b4e05da75cc93a8871", + "0xe2b6a4c035dcde0dc6e8ddfa93be28b9cce4d8e981347b6d36e473b81812832c", + "0x321b80e522c8dd31db601b983a233cfcb2c74272c718a33e6ed70d496bd27b84", + "0x07a865114c148040c3ffbb24da9095b7e73a0349ba18fadc7bfe1da2e8279ecf", + "0x3d0c546a0a3a611facd37da1a4e6f46e557fda73122a2433682362cb16a5252f", + "0x02f3d6b72cd1a1d53ccb5d2f2ebbf40fea9d70b25f835b23795a92bebb2621a6", + "0x756f846220f348b778fc6b013dd019a365287ba3e1ad8f1cca780bd8d1853f0f", + "0x3f4e3bab6be10e0ea726813aed84e25bffe3c04d6583e22d954015414e9486b5", + "0xfd48468c58568697f564129337752b81aa38fec0e67eb0a78e4b65d3a980701b", + "0x15a24318e4de1ce927604e1f8f6ea99ddefe8eda4a99f2dac8351ec1a10b958e", + "0x28197f22fcebd5e79155f91b0b332d947b8fce84235067119ea95f113747be14", + "0x1004cbdbfc069f1084a05f59aa0810a016d1fdbf1daed935fb4bd81abbfdad61", + "0xe75796a23a4012b615e9c020decb50464e28ed3c25d3b36ead125f75e7640c48", + "0x60eee67b7e2676b4300b787b2df0f0be4f434e88cb50af66dd8531f2d6ad678c", + "0xdc30678aff2ae50378836333a4c3b6f9637c90c2642b9e1201c2349626475a57", + "0x9969844a438d5b040e972aeb87b6a0be03a45af71adadc1cd802659cf8272b37", + "0x1e4021e18e8b53598cd9fcddbd37419ff1b3aed7efa81a4da0a88fd11af146c6", + "0x4d70d0e076a11c72b7f1e92b56d4a0bb1d5f05d141481211e9e8b2faeb78f042", + "0x899bf0e3903971083800ff9e9e517ef897e785ef0869eca459b0a5e210562c07", + "0x25a4f8258c6e668c3cd54beaf10703b8d2d3630413670608c673ab390e5209d6", + "0xa5e7826501ddfbb5fc334c5f373d405ecd0cff9756ed29ae8fbc1a0c073ec280", + "0xcfa4ae8559503cd533dfcb2638cb625ee055129fa2be39b73e3814c17de72406", + "0x47288569efacfba5f5f9372a32d35321266ec4de71bdc8834529b1216b6ecfcf", + "0x4228ec6abd08230af654e15d94234cfb56fb53ae8a69c7a81cfd0b0bf0bc6184", + "0x7cbc69a239cdf89e1b1bd03dc4e9ac47073abc22218808690b21a29457ff5b20", + "0x54cab9ab46116fcb83ff9dfca6c42ad215d07c5085c841d9849b05c9610700f1", + "0x40a4d7c40aad2099501829d28803cfee9a00a3f407777c7d99020a7f6ebf71da", + "0xcf8b3d2f1d8034f97b6b6094d5fe6b2c867b5777507ddf8639830b2cedcc781f", + "0xf6c5e2ecdbdbc38bda835dffe62442349bcf4e2408229d4599231538337c8748", + "0xc28964529ac7f231c93edd8bd004709447508a6308bbe2360d0fdae3d227e15f", + "0x9c36be8c80f04269afb3d940cd18d6fa1572d27139002e70e94a4a88c511a2f8", + "0x84190b95d9cdbfe62d28c76c34af19357c8b57f87ee57a3f37d6852b2f7013f8", + "0x91ed9fd3946e2d5381bcf1d0cd819a6e7f41946532231f988bcce4bf303324c6", + "0x008efcb812b80e69f35ac5ded1e6f37b90293deb4d18e53d877af868a58a11ea", + "0xa75ada72e42d18922374c5081509273de09f35970d13f11da1a982dd2762fffe", + "0x804fc067668817bbc9251abcf16a54b9971f58323cfec124bd57db502969054a", + "0xa77a5349543f09b47ed3e38615dd62c94bc1e54399cfcd6a0bd3497a1d212e5e", + "0xf517f5d20929a6e1bc51c8b10323cd54903348add970b61509db8bf2a5cb0392", + "0xac637563891c2fd8953591643ce9f61cfa883364ac2dbc5af63fa0eeaa08de9d", + "0xf5c0756b591923f208b94b125c15dd3f7e13f2f45d7fbdc080faefa1823bd672", + "0xb83b3ab528b3dbf300f56d96d80a9b79f9182f31ff2af6c8b781b6bf46cddc2e", + "0x189b6703995828adbd51553773f2d8f238399e12290beb64d1de1d7d9c2f5327", + "0x5937bfacf2fcfafe6fc1df603f94f8c0075c646c9a802c49a040aac9f7ae082f", + "0x2f8bcb4aa577e0ae68c3baa326fb9dd5f039727559838faa8a28f34b7c103c1f", + "0x6312ce88a129b6dee2b07b68f15e77c8ee9fc1170e3ae2b05f4f2cadc3ec6a55", + "0xc44d39c1dc10ad6dae0fd0300abcdfef406b58f94a8e0111d2447e3bdc3aa9cd", + "0x179c33edfe39da01cdbbec36b4ce2f6775d0a7b9c570a4934cc60cfbd2940ad4", + "0xd6e94a0916e327c9eb0540408fae45e76e01830e597bfea82c6620e21ae631a6", + "0x06f274ad702ce8f5a584349b6aec3d826a1d248e7750be091aa07d7a441babf8", + "0xcf986fc90805cd16f76518fd37373469b588a27d7c21c68ba1d0d6a349dae1e8", + "0x036b973c1736a5967bfee6301d1a6e7af64e5cfbfec3066a67f3390ed5b87191", + "0xb221ddf6c6b142bddb69aefcf3ea899f6a22d6e3ebeb4e8e167a6b19b6a61b92", + "0x2e3ac0d4e5e25aea9dbb3f7a38a30582b2bbd29819553cd61fb2290cf56d481e", + "0xbe99a85ece700718bcb96a4527e1814ad4c338305e1f1f1af48a1bdf13886bee", + "0x87a2f12f58b0f326de885a8f081215bceb8b43ef8fb71a24ef93da4f2243bb93", + "0x84afcc3bbb52a5fc24537e700c55ec6b95ad959871f589253e6084e7e4237bc5", + "0xc671e485891a37d989c19b180a78b052f1c41bab4969febfff2da23b175494c3", + "0x9f285da613ca467ed9614ac5583c3118437aefd01a5034b305a5087fd19c406c", + "0x44aac47388129ef4b290983e58b70ac9ef523628c2130dac4af249fa64580d12", + "0x68b329a6e60e2919d3dcbea4c4beff28489d231ad973494bc2d70aae4b68892b", + "0x3d042a40cf48842ca8bce267ed38357cf52788f540af87d23ec3711d341c1867", + "0x37f134fc6d47270a969b951addaee057a9c382bece4834a56021cdcce6acee5a", + "0x997f424224232841c105af9b6ac92f99b17f487eba64ac8988b7822c401713d0", + "0x70c9215e11c0aab6c6895e54d33d500503ba138df8dd7e78f4bde2a17b93f8fb", + "0x63f3bfb4c54128f2949180842693cccc6563226ae52a073fdefa9da87b09732b", + "0x6a678a175d8ccc396355bfe2cb42c51a7b6d5181579aab2a645b6e5e45fdb22e", + "0x2e322919b50ace0d167b30da44ac8cb2d87d7ce1386ef6be2e42709491718f0f", + "0xa38ecd5580a67b838c49880ed9580aa1b4310dc66d6da21f4c610b08f93ef984", + "0x211e941e8972abf1cef0ff284abfabd551bb9dc7fa25a822f87225c5b5a0350e", + "0x8f7bb8570a07243910c67e43a94e4bc854bfff038f33b73d47fa7ebd114b811e", + "0xacbe9eb4a5c1c7397dca58158332e2ca6708d3c3f388f10d489eb9dd5c4e94eb", + "0x6a2979a3758824c6abfadd19baf1b186a384381761f93491357f54112fdeecf3", + "0x71b4356261682f9be9541fa695d56c3c2c4fd1a3cba3329f441d905024a253d6", + "0x17521c2ac53576ca6fe205495f910acf8a676f353c6038537eab45bb323f93e8", + "0x4466ccef41ce9f524ff1c60ceb56f026b24b58351a90f980637494d935c12195", + "0xf51beb0dbed6367d47dd6568664bb6523e3a4de0eebc7893a2b9f5965ac8d2f8", + "0x314b857d3f6e5d17514ab89d3dc8d7722cf6063c42ca23afa57be8aed30761f5", + "0x8bd3cb245c45060bf51581fa2dbf6e9ee41ade3080081b721134800e220bfc1d", + "0x1d2c1621d8257d8c5fbe717d046a7e43789bfdbb8e2d4a8ce36d1d99ae126c79", + "0x3d097d7cb7b8b08db4c531022effd5c3a334fa666a55ba6ef42e6761da3794f6", + "0x73edbc1940407880d112672fe9a9050d97794313174bc24034803255d446d1d5", + "0xb4f08ba9d5f7a537a3969ecfff0db4c7572d95695d96ccab2acc5c3ce15ca8a5", + "0x4fefaa4946e67942a2f99b938a576a698835b6aecf8d3b0cf3722f16265dc6cf", + "0x3922efb8e5cb8150ea9ed0aca2a38c9fb5dea6cfdbd91a28e1df114071ba171b", + "0x461906b8bf7de0db835ce4781976435454319de68a15250f95446fffa8b19624", + "0x5e07b47f0f5979469d2eef9c7164bdda51c282c2eda92867e79f1c8eda5d8d02", + "0xc314ae59d4cdfd61b544f6140baef007e641f1fe7262a0b1ae62090380219a56", + "0x01ec2c1100755b7ea2fa55af062d0613413948061f44154cac5b1a60102f730a", + "0xa59bbafd3b759530f957b6de2e6af5f2689f9f83bc3ccab36641079d3845361c", + "0x13269ef9a96d1172f44e1a2320e5223cd5a0687c484e298601701e2b140075a8", + "0x471c228cbb5154cc78385ced80432eec868d76a7718437fa95a6c22d2aa5b33d", + "0x5babd466acb34b53f51dd1e883f9dc5fdf0ff9428f1acc43bac527a29c19f478", + "0x07d3dbda2ef389783dff4865411e8f50d72a95cdf554f7cda1c127e1a4410d3f", + "0x8528111bb543d2a275320a613cb46f25623d40ad480495c2f5aa54605ce67fab", + "0x16fcc2ba08aacfbd9578a722dc2125f1eff8a246f122ba4ecaee21e90e188598", + "0x84e177242cb73e63b8b18b805ee8a9786e60e4098bbf05aba1e2e4825e80dcb1", + "0x3fbd51d54247533ee187251a5027b4dfe477dea4cb47aa3f54509fb0078864d0", + "0xe55a18b13b75084155ffb84c3a5bf7380eb8fdda4b4b441782661423625baea7", + "0x966f713f6410cef1c323ccc938d7c52e80d5019fdb5300841c3992c4c8dbb7a3", + "0x6c23d6c6cca9622c31154ced642e9572a66f1a699c29e77e559265339a2bba5e", + "0x554ae350171d09b93f40b460f034a2c146026a23b0dca9806bf2644bfd5dbc94", + "0xaa65276bd6585e45ba770fde229724a444c01b7c0ff42bb55ea6026a53a26d3c", + "0xdadf46090779bf9147d039fa530e0c36343862bde714678bef2c85e0c21164eb", + "0xb271935c2d29c5faf5447f26c15eda829f01f88127926ce4aa81b581753513db", + "0xf0e959d2f629321bdb028d8c9a5de714600b4736bf46c041b57d3b83a0705a94", + "0xdf1499dd19abaa95ac3bc2223354209eeb779a6a867bb81f2e916663a70e6876", + "0xaf66f05a6509489f0c501a13c183c4932779f95e1a04d18587f9c17433eb707f", + "0x9323d1fa9eb27be59d4f3b8b11fbb9c081a030f4f03f9f743328c97ecec30da6", + "0x97a3d74bb5ec2aae723eed8c8df524f9cbec191f7304e779601c2daf9c483ef5", + "0xd1f55a947c90db92000efd76a742c9bdc7f6056c9d0740b7409ddd19fc8c073f", + "0xfc47ed6abaf1736b16a262d7e9929eea7cee47d9ed1ef2a54818150820a8ae39", + "0xf90f93abd89bed263c5b4cb3b51c44a629b7bc07bb2e06feb3ba2afa86a6c076", + "0x82d1ffd3a901ad7752572e76f81763b9893993beb914e4eaec0ba3369334947c", + "0xdd7cade45b696fc5e52ca59098e43daedc37568a1ec25bc80868761b9bc151db", + "0x37c0da90a3fdf85e740da99c24c7b64f13b6d06854d1740c5d973121b9555590", + "0xfb17ed634b5aad830373ecf8b937351d3012ebbe258e8a87c82d5d7f3b36432b", + "0xa60e2aa09b1a296b0e4e63f4a6bbe5249677f5af33b72a15b8a3280bc2c81ffa", + "0x0e4b7ee7c2611c6099e4d3ec7c9a331e44e1492bc03e9fec27d2a29525987a44", + "0x2fec5182d56b93dd26e24694b3f26e9d40deecd76767ca5889ae590a360daf77", + "0x8e6da7f13e48d52ebedc8e2f81b76fd3a1f22a469cdbfbd4d5ca74669c3221da", + "0x8953dd2c95c7f5f721f11eef2122da0abaad384f2b6a4653de798f0db1c1b9a0", + "0x40b06d0ce4ed2317d5f559b0d9b4856c8f634369d8d24556811b6e9a633ad5b6", + "0x473e8fa4520c50623921abba89d23b5525b6e39810b5f702eba92950eb51ec12", + "0xa0cb368a75e1c8388d137d0fa240d502e589eddfddd85c8350b7f16fc89dba7b", + "0x1f9e094c3453de9fcb89e1b6119b1771d8fe2f83cfecead1a47f7e485a371f8c", + "0x46a105cf73a96a6d557b02a4df507f4383e8078c1a0e3b1909fd95c5217f6e75", + "0xea6cef6fac46652fa4ed3b9c01de0f515effce45009a442e5de644f8807f937d", + "0x73163cb9293f703fdb9f426c4bc0331bc71a00f9c5911ebe4dd3d4e6630eae22", + "0xdd6905ed33ed2704116316070a1925a45e7274071c793e7705f5bef74b8d9500", + "0x8f3a7230a2905d9e052f56e8e755af67bf7458a63197d8d4fc1a8e441977ec9a", + "0x309d075c26ce99710c18fa58cf42d538ac0653ebb501508a183249b11dd0cb79", + "0x2866b9e9ea1d2429a191dec597d236f41cc9ccc40c1a6027d5350a54aef4f986", + "0x60cec333cd2db98f6bf27283e44930fa72a00a71a17effb4ad9410af28461512", + "0xacf92d0251132afe0fa1a74c12200c7d84c1cc7e7290ae82796b81b8f3ae4a9c", + "0xb6cccfc1e4bd430f2701e4fbba009fcf3bc29f94d74498f9899c4023ce1f8d4b", + "0xe706c25537c6dd36b67ab45c7bf9807a2e290ef97084134bb2f8a6c55f2be064", + "0xdb52ad9bd3965f9bd61ea1b7c1dd6b9bb3e481b72e17093acf19d5f80d0b6ccf", + "0x027974ec8db0b1d6e5a1fa1115671451f236d71ca3147042d97b9b0b75d7f60f", + "0x9f9a15c62735a28e4d4d618c277b742cbb24763118042f3f9494dbaac11f171a", + "0xde1590619adecfe1e7a0831c5c180fa4ed70eafbe42e84bdd71b26039b4efae7", + "0xdbf11e73403244b0b449258d280aae92bd82ac6cddd4de566bb249febefce295", + "0x05552fd01548c1935c102dbf29521b2f186b6a18f7e93c67172b2a6608b5dcb3", + "0x35e084f3c927154923e8457cd12a9bb444d708de0d85462c52b344047cda7a12", + "0x8fdf7a3ad51c7cd97905386173c31fd073ffc5306bcd011aa42d60ada37f70f7", + "0x8697c2e4c0e6abf74a42bd0b116aaf0c7a1dcaf8320fd57d3245807cfc11ae79", + "0x3fdb80f2d9f02da7800ea73f99d49de276b70fa9d6398a7a08fe04be6b57f13f", + "0xf8ac9870c638840faded867d2bb1e08b52ce3017811279c0404eb77d139d2182", + "0x5c93cf6eeab0f4319e5ff8ccf94cad57d6ddaabc124f275d6b35380bcd584f60", + "0x1c0f79909bb9e88d8a57f351b2dee534c71b6781fa1481f7380a98af22be31a8", + "0x848caa171e0049ff653e6c11432bb89f248cf67666e636286d261fac07fe3372", + "0x60aced4443e04d8ca6631809a04eb520add999599e24963d00cb2b2b6a5832f0", + "0xe96e2e3832ba27f4607d540b9b850654b49a046b0506be8bd56e1ce1733359c8", + "0x90c617cc791e2fa12c42945d9cb41e943e45e05df6cdd51cc38cba745a63b281", + "0x4473dc1c6bac8cf833aed1d734ee0e60dfc5197bb5f70cb67f64c6a158770a49", + "0xaaba5aae8f36a866165e81f691e9a371be0c4dd167c755c3bc90d4f97673fc1a", + "0xb268b415e289be90d1f4b44ff98f1c34bd955fe79f1ff9733c1e4329a916d62b", + "0xa0608e5eeffca2e8ea8589763a1a3aea6627895b86b3ea4277adf03a063a51c4", + "0x5a1f5f357e8ee4862b0c0efc86e9230015d0774e4700832637d2fc65836e1f4c", + "0xe7492ad930a0e36db4609c81cd8d6e45151daebf8fa1ae461749334b5841d57d", + "0x99b3dd02ee9e107a684531c7b866f1e78f07226e7e2ea0dd8cce2e16a303d548", + "0x12ff9cf1135de89b912e4c091b9975caae36d472b3075dec45a4760845436291", + "0x044e4052b596365e25f8811b4f3a7a85b3ee14586c92b2aea18aa4dbbd211d3f", + "0x483926549c8ebf5afa904a4b432924d3a7fd137949bf116a5bd199f5b8ccb383", + "0xb0dcff0b137267663683d037bc909bdadc5b33ff6c0aa7783d0e27ff0b299278", + "0xf3d0a3758f278cb8bd3c5e0a178a2c87070d6b8b12747226c1af241fdd112edc", + "0xd336da9043a5109881cc071d78b83bbad2f6cfecf2a2d3e5da1263289e495141", + "0xba9f9c99a46e110998143f605a867a316d9b6bfa4ef84de81c8d1863fc88b77c", + "0x28e639302b0edf9e1e8d7ed5a8d583738f95f8e46fb272b4006adfc902a036e3", + "0x7e86f4fceeec4470a8ad0d683d26f37db34f439c425177fa84a7200ee50965c3", + "0xe688890b9bc0c5eb6cf0e55f0dda8afbc95bfc436f073ac6f09b594ac58b7568", + "0x9bad9a37c726cef3db9404bd1b70109f21307d1e6555ba31af99ab5784bb75da", + "0x96e4229af1a805c05e43a758b8be0bf6252d31657b7e069f2d9a56106b4ab95a", + "0x61ddc3176e913551edb3aa1ed873b173c15a2d37c754f52a23fd8a6aa8f22981", + "0x114a6528677a4d32525cce31204f4a274c5d51d4045f0cb28b8944a29342fc5b", + "0x4c936fc65dbb8ca9a1dcf0508a9bc659a4583394a53ead72b9622d019550baa0", + "0x31580da7ce9301c4226697ab1f8265136fc0317bcab22726bdc81ef10d579a2c", + "0xf8adc8c07663e31301b9f28d0e0bbd4316f8b70ee012af61de1dd4cc71e629bf", + "0x494405a0f5fa8c1452764d117f1655d107e940844b7a9b9af26e15b7520b3df5", + "0x98d8f1b6c829ab34f5560f9271bde2401f72e10db991c1e96ddccad248856cde", + "0xc8efc79a80e45ea0df723b9d394ee1c659d7ff980b1b23258515cbe89d94eb32", + "0x9fe59168e2199d180bb63b41dd5f8cd2a699d02b75eb6ea80669f72d4c6d55c9", + "0x8a8e9b799c93d8e24e71a11d7c2e29cb1e9e0eeb27a7febd13ff174d33c5439d", + "0x307335ef3abfbc5932e95d94cfd6eecf7475353eb94eb8995ceaf8021e090bc3", + "0x04c1998cf4039a44e8c690ba2e0c5953e2b73b5a5412cba203aecf26bc74a8d9", + "0xab9b1b7d40663498ca01334ecffeec3e1911650f735ba3212528c3457751527a", + "0x321cd0466beca7b80bd888fd4a2a69eedc0cf4c98a3f13f09702c88e11f01330", + "0xe07aabb02de13055d06d7d5529c8f259fa880fa3cbf9d77b23cb1c1920b7f749", + "0x4f2251e9c341f1e9d764b7a1de08af3b74cdfb642d72912b64df0befe7e94298", + "0x6e4d4056556929033f22e944229c5eeb141d1028b7689a242bb9494af1b21c53", + "0x73fd719af3b5ed20f23abf47e1af6e96876d3113b8d4cae1f14f69a187a8c633", + "0xea76498b8967047077db6440a2667b300a0713bfc58a51feb83197b859612d84", + "0x8eca2feff7710b254635bccec17af1b5683b0fe7b6b9bf03d172ee879e2c9f4d", + "0x5debbbc22f9a359b7a0ca382c0c30fcdc0d66589d123179d3b3d0b5f358eac1c", + "0xf1c417b3c8fce8220245f40e9d0f95ce770de1535dbc14242877e9d08a529d3f", + "0x9b308f5752a16ad3d7715b56a7df47057bed1b06a30a479f09d467066f1a697c", + "0xc3a720c56135630ca4af332c1fe005b7e7542b0fd5e8cc72157655ab588ddc75", + "0xdc61844a64c9c26caef5a9cfd59ea86f8a155067b9513970f4e4766e32e2873f", + "0x124ab0f305778905f30e3e3a0d3e736ed8aa667cf96bf8731bc8e93957da06ae", + "0x73d56dc8eda15410a1e7827ea1d6baf5b3d6b42f3522d702713771ac6b758cbb", + "0x00808b917a2bbab857c1273aa50b6d88a6398306242d8b29a158127d854a6730", + "0x627ee1f39983079a47dd68b95444931c1bd76a3075844f286af86f60237eee81", + "0x831de6f0f15f8bc023d4fddd344f0d4fb99f1875306b96e40d610970cd1e940d", + "0x4eb001b91a8a4ed11e252ec01956805f22a29c99315fa1062aa75599a052679c", + "0x2dd87817e45c7a5f8631b226ed9bd24b9ef98da2ea373d0ee5b69a253866e094", + "0x2b7d949e5e093d4b4e43f568f1a429e636207d55bc001862cfb4949019849c4c", + "0x41d88dcd720db475817ddff2a22c2ab75e9c29a8a00c87e140222c61489013b2", + "0x1892d37481d1de36c2946efda6781909057cb680e83bb468f10f5d86a30d964e", + "0x9eda987d93bbdc3de2da2a83fbe1580f26d95dae478d5d2b26843d43b344fed2", + "0xc6092cda6a690d9d86652460fa6f64cc325e69412566e0868670adf862bcd49a", + "0x1af54727923d411cca474d1d4dd67e97f34145a186c10a65bf3962f419a96406", + "0x7ffd73496a608682f7d7f2d8db21de1260c9509251d9c1a5de10583eabd79a87", + "0x5d49e2f163cdb7f6eeaf2a778b1fcb8bb3681912966de008483b7d997d5a1a4f", + "0xc09df8ec8124ac5e9b83cccf725b4c742c141b5458869ba394ed6764ab23f8e3", + "0x46f40a0524f9a7afb8d8d31622ab3468fac750f42aab74f2b2145a66c4dc2bc2", + "0x30738099d21cd9ed5f6dc9dd46858843583da0d3b29957b2187fb3971da7db4b", + "0x26d73f31256e9a5e1f1d2f5eee774fe09250b32d159c1ba1d7c0684473d96442", + "0xaf9b1ad6158b0b5dadeda93ddd89319a744b4b5b5632cdd92a14b1f9cf546e90", + "0xed1cab59aa51f23eb114141f5c129d5fc18727e026b2ef0680376d41bffde75c", + "0x3206bbdea1ba3ce188a4437e03d5d07d2269143c1498c1e05173bf7d05b36a46", + "0xce6ba1fa2d8f4e5a9f371a38d8c8e1aa94eafb329b45b902f9beef22d0775ada", + "0xd7831aaf526b07f38620d7b1090e519b61d3ad2c6bf7cb9230f6f58d446d4067", + "0xf12252798f26cdae6227edbce32b1fca897684a728726f221b88ef6b34614e4a", + "0x86abc41b3bebd7ba7df51d1ca3653f052af9f235f8633e11d3d0f12046007ee9", + "0x7a6c5d29005e6091a2e2fcc85576f9ba2116ce32367f9adaca592e99b69123f0", + "0xcb3dc5c24e3dc3008cbc92f580ab501c3240a64bfe3f62ab05f3c442d950f4e9", + "0xfb53f96159d1445b2ce967fa71d9cac18ea8461f8998b89b666e68a771c3add8", + "0x346da47460951b0f02188417373ecbddee70217e119d6f6ed0ba2ea6827b7b8f", + "0x029ac432992785e79259ae642acc3fc07bbec58287ba9a478937266290f648bd", + "0x49a23b35b18bcbe2ab2f40449754a78bcdf9ffef62ea8bd1a4d83ea528f3ae9c", + "0x61a527fa1c64e8d1ed6f8be598cb7df5a149ab23072477c6072f50e2b3e9435f", + "0x2faeef7d975d012b27669eeb18b8300d02ca51dc8c7bcc34b0fefd5b444cafd6", + "0x0f8e3208b8d34dbc5efc477f14cfebdeb420e16b400efd32cd6023c5507b1e09", + "0xd157d0daf47aac40d28afe1346b095c8b11f5aa93a1d480405b8b1c8afc0ba69", + "0x0da956cc77bbb010838d091b79e6376f6e1d67aa6c3b5d88adafbf284beea122", + "0x2b40edc2b8731cb69bcee295348c8d685737fc9b7b1eb18078d0687f8f2a8e06", + "0x06fa6e1a977b02303b7b2131746103b6227d40a63b91afbfc11fb29a6388c5e3", + "0x0cf089cbb7f09a5b40bccc9d039d2beed9f53b1ef68fd6a3da82182413a6a1b3", + "0x7dc5976168b3802651f1738d6ec07c8a5ce73eedac84c58e084e4aa22143f3b3", + "0xfaa8c1db0acd8c4484757e696fb8e081d83a5e3f2051472ec95afd2a2cf75c27", + "0x7b4b4c317d03bf74935bbce8d75bfd430a741312de511af2c03c15a8255c8fc8", + "0x73f1245184f4c1649491df1eafcc13ea954defb867fef32054f5b1e1f0d65264", + "0x4db585ed53af2b4c8d484233a78a8e028942a40d23e9d0922fb0a1cdb8c21398", + "0xa1e8481a017adc0e751da1c37c17a91cfd141454a28e96d20fb753c3e672e563", + "0x92f76cab19f99cb5afb1f6c86d39b91c977c2610b6205fe3edf095df268107d3", + "0x5ece345a8baba9afd82d71e0ca862145744bd7c8bf4b72af4fc3a6f2ed9aaac5", + "0xa5f9c2b29d06a900ff440f7c0563d694e753b56a2854dac4659de2d7f242d033", + "0x9b0208fc2f95aacb1cc40f859a1edfd4e0ea8e02f9d129f410aafabdf4db3232", + "0x9532f12aa0e91bc97bde54bbddc7a585bad5394671970d45345aeb493928553f", + "0xe54d2358ac3b50b48fd2eb84276dd395f3828baf11f97dc0d09336745cef6d5a", + "0x5d28abd1d31bd29109ab002acc898fc0bcfd52de08dcc50c5ca6cf757dbd99bf", + "0xd0c9991103969397500440a1dfc024ad2c381f065be895a5e2c2d54cb0c2fcd8", + "0xd2979ffe159ba77340470926d5b3fd124fd4ce3716ebc10abaed5cec5c2c9d8a", + "0x7c03d52aaa8ae16ea7afee8b4836a3867db30c8e5d2fae92c1ce68c2e6cb59f2", + "0x916fb8b790b3cd137923c338cb2d61f6825db62548e013854ce8ff4cf97d1659", + "0x8dd0237c7623fccf095ca4e46e23e29490acabcc8176ba12e56d0c141c8b97be", + "0x52bbc3148973dd3339c5fb429ce1345270e226c11e0c8a017cda38dec3824c6f", + "0xa20238f94ae9ea83852b45035a479f50fee9d649da05b0c0c98fd6952fe3f4a2", + "0x2745b0d174d075607e164831bfd681983d7ca309b5abf7914815a19d953ba4f0", + "0xdc95b4f2a390f77e684f7e47956e75dd66081f910ed274e579b175268e5ffcb7", + "0xc06e24e59d28b4f6fa09bad3dab9ffa1c07a564b395835b2e622d3d7026cbe72", + "0x7b497112636612b76d377a3321abe631a222b6b2d7f6992e463cf5606d4d33e0", + "0x8797215987eb5c9b0cd9f21eb8545e6fb491e255f2f5dc4d71bc5fc5cdc7ce53", + "0xb467f67e608e4a7050b516af1d55f0dd448322ef81df8c1f8114229904f33c63", + "0x79b68c87890a148ba03b6c0ca216b7d28065f8af1ebe23cf2c88db06ff6d5465", + "0x656c96e846e6de75f16b44d2e926c8085ee26a0965de0bf8c7bf0d2401a83e4c", + "0x978d9ee624d5ee3757b5a2148d20a126e64c751784c1d32940845a3054583d30", + "0xef90078ea4bb534b9ca254e8547653d7e52170194b6f8ceac83f374121dd0b12", + "0x6e0116cd60ca37900a3de52abec06f30f91f34d9a20e908431ac1af397a88c18", + "0x4888eb45c6380c280d47d342643f55271324e54e3228a8e870b0aae258495287", + "0x1f6278721c816577765c72082bc890195dd94b9d3c112ff3c2c6cba4ad45edbd", + "0x6487b511e60bb8cb53c3f78815ed24a396d2e8bed7575d1895c8039ef3797381", + "0x637687ebda7c801cf66a26dc3d41869dadd376100c83bf71d3a951662f20edf7", + "0xaffc2cbc626d48c70d3d0bcd1667d86e1a98fc496511432612e3106d1afb9929", + "0x5c9c7eefa7ea99ce35965523b436e2b8d7dd320b9bf924e7cf1503ca67d6b30a", + "0x22c494a2a842ffbeda62baad19683812f2fc5373b6e82ece7b8be00c5dadf10a", + "0xfad62a95acc8e4eb1c386fd88e8e3a0ff7d19ebd004f5ed45e8a7f7802ec43ea", + "0x0d07c634e961f28b282fc210d2913a0bd3bedadad080940281d886721c538fda", + "0x2d431642a8c83263954fd7c0f6e36348003019d9568d9d171f724f4756dd6f34", + "0xf962d76908c9d2a6cb9e7ceb7f8b7147720994e0519f729846b6e4f5e8b560bd", + "0x45d5f87964945970e6ba81c57cb8485fde586b41a05b5e4ac7da385d75fde6ce", + "0x7d07ebdd328f8e536351b424942e1880b14739c2f5cfee967a04056e6c9cc12b", + "0x388e92e3102e5511a304cbec618bde931f1f56010ef54202c065c3ec4686e685", + "0x14460220797e84ffc68d01ced33eb64e9bfba1bad82e29669fc3e74bc2519611", + "0x49c3bc1efc367f64f8329ae665ba1ab00c76ef430fe706c610f2a31b41de30d2", + "0x27e0db3c746b18dcb86d18fec9540bc1df18e8c1d77d17bb224a589d80a4068e", + "0xb933b4c8bbef48b335a34528f157ddc4f7c633c1603e52dcc2886e465deaf134", + "0x36bcb92112ea8b857db34f68c1670b6229b2eb7dd5539c7b97b5ba8e373bf1a2", + "0x32c23d02f0188330e2eef133052f28461ffc97d789e9e6202de57cce0a2afb8b", + "0x9f735c5b8257c702560ccf3dae7354194b62ba8d21150cb57b79fe9cd5d2f548", + "0xa6f28dc870680e9dfca80dd958095e106089558acdb615c2065533f636d96dfc", + "0x5ca9aa3c082751eb51c492710f8c6b02767c3b8b57987b326ff4dc41aa69987f", + "0x6df064448e5cb2fb01bd46e8726c4c17489ff328a359cc3465f5ac251532b471", + "0xbe05bbbfebe039e826f0b84cd5736bd2b4c26ca84b4a8195a547b91f1255744f", + "0x3217a4741751898ce2483d0436a330a195a3cbde875b8841e8ac74e488eb459e", + "0x081b1d042637b49375bec86e347ff7408f8f573841d740eaed1f6c77b9ea1f8d", + "0xbabd33314a1271a2c4f3246a8f2b7b621d967c23b665af0a8930e1ec77841061", + "0x2991cb2501802fa9bdfbea0e49afedb6ac9d88a5c4bf9a9bd29f92d33b988e49", + "0xd776e631ff61f340533010e564d8e0d6961e2f0ffb22836e81b0b5f84ec8fd01", + "0x56cf2f1155bc2d890c0e9c4fcbdd9b57020e3506353ab66d72da5ee36db046ea", + "0x6d7a243f0d1027de2ce938ae9d24f053d689747128c71f7fc8ae73b4c7229555", + "0x7dfd8d8588662e4321e4ccc993b61143b518a2c4d1d7f61fe038f30b78f1ab3e", + "0xaae76061640b8f345a8b0d0a4b9beaa0e0668ce4643da26987a255b0c013266e", + "0x6186aeacb7fe0940b710f69d923ff6b56c3c8d1e5df70e4ddc8b080b1c9684af", + "0x8b3c5b441f8f27c56b79abee2ea69efa2c88032a7b6c0e1e0054247244b9fa5d", + "0x8e5ab48d7c419927522377929b8b94b1d870c1a2c52800a0dfebbee0a32f2cbd", + "0x1ab04630c1432c909873dd6546dc0b60cccbab85b047ca8354f16c92decaa600", + "0x03e1c1c0ef846b5ba7227bfcebc35f19dc06809813f4c53e322bbe2aeaeed2f5", + "0x6d12edc20323412e39ca18adb2e4aad76656fec59d15646c150611a0e8e65ce5", + "0x7de54b33d70ed24d7d53c768c443152d61be5925464292e58179d06a0928821e", + "0x82d97f99c24a4c2c02ac11cf2c2b352e2ef420daf9aaf428898f8136660769a6", + "0x3ec29a3a534395405e907210ec03d9c323e7e3522342da8499d73bc7c55c885e", + "0x04fa1ddb763d17b2c412af9593bcca1886c36f598836cbd59125d6289f7ff794", + "0x88611ebdf982640198b1531ac7a98f0072cd65178c05e45176ccebd243ac2ad8", + "0xe39501353a6e2bc11236c48790960d9e200552e3533fcc87b39f24c2cc650d52", + "0x0859922b862889bd07a54ee28534171c8d329415ec33453fa96ec7fc2e441d88", + "0xb100bc9802ea2df0a91a88da009801c7e8f1ef5ea5799687c884aca8c0a5e207", + "0x1ed792dcec77ce61b7328c6804310f3268be600304e6a691d7138e2ed13d1042", + "0x87f0e29f88b89233b3d19f99d5771c8ff22548c26aa0233c29cf5a946164b55f", + "0x9d3ffac4afbce08c6cf2a37e9aefa41b6a1cb0681d595e09c51d1dbca887eabe", + "0x3053e5f7be54c097208db0fc2096baa2bfca2a3d12b602562507f10b3cfee9d9", + "0x5ef9e12b7a499934d68e75baa3af7ba06d88e7cded5944f050a4dbecf69dae10", + "0x56a05406985762aa30120261ec64ddac08bca126d53a7c9e701efbb228b4732d", + "0x39a2ea51bb3669788ce26930dd80781535dcfece82a9aba2c76a194ed275b8fa", + "0xd5289c0301ae93da8be1907ddf0fc94fc02cee4df121442315451b2fa7d28633", + "0x4b6f894058849249a6edd62a0076b88c7f79d045b2e1996fd7353f7a36fe17ab", + "0x344f0a036426900cfcbb5d258823beeeb43a093b9fcce906581eea90f454fcdc", + "0xe0dadd90a1330f66b5101f69f1fcc3abbae8601aa54805ce4dbb0f75f1505588", + "0xb61cefaced4cda06ea52e85b15069d301f6fe7f3cb95fae6aacad81ef0a061d6", + "0x7e2f2f169c6867a7ba49d709ffb14b38dbec1844c93783b61883174a19deb4e5", + "0x29c593944a9fde3a5e7e5ae63a7d3e8523bc8d02615f9e67cd60472c4938fd75", + "0xbf99b22355f6be28bbf0045286ad04628a3d30efa67638d9ba9f0ce976da9b30", + "0xc9c2b3c00d3b2d5df58f74fc80fdfdf5fb40fb62decc891766ea07bf202db995", + "0x98552dcf56acae020222fd2d42f98f66fa904c746c3caa17e456b85a83fc8f55", + "0x02c689808b40d1dc3e50a67b1d6910799ba0b249988c6cb6b8e71e7eceb98964", + "0x7cd2ac51dc7e81be2599d876756e969b742d62d28f802ba1af977686797fd6af", + "0x200e6f01b8a24964078d91a02901a007ee9c8c25d778fbf0ef36740e8960edf3", + "0x37f1ad082b6fb0a51ea5c88d7fa191a17eff96999fcc481a35b19347aede1ea5", + "0xc25657f6f04a18ed59b576de9eb9243ba403005bcb2623911b965d1f1bca4ec8", + "0xcc28e4da4050f0ae0595a435a1ee408aa7bdbb84d0a42fd2a41457f59f85d32b", + "0x64e5e17cd78170d733b526d19c05ee33d09227c5ec35976c5c312f9df19970b1", + "0x1a0c1afb2c4dcc85fa590143e79c543ceb3d96ea1866926d2df9902caa5830ae", + "0xdbe12369b90b73bcfe3e5268e70573b7ec7a73871c4210eb3ebcaf811942e8ab", + "0xf3af79f72fb8b254ee14c543de63129dee897958c1eac5adcd5335ccc526313e", + "0x44ae78a643c33822076ec29e398608fa1ab3d320c785978931d24972693be19f", + "0x75442e6334004a2d8cf5bae4a11b6239a0bd335299ee5f04fa12f7095078f0df", + "0x5c08d8c3869181ec13d314ad7b4f6f14d0d6b5a7e65635a2619d287fc3e71e6a", + "0x6beeda18d865a70bfa263f16b543d25599d430003bfed1a60c0e986c7929b2e4", + "0xe1d21ced822a50074685521183a3a7ef31d231637e9a0ec4500ce1640d3dd71b", + "0x325bd176c1da2a9eca970f2832124e031cc9c5332d27ec7d96adf9cdc9eb1264", + "0xecfc747869eb075dc4f2da1a775945881be255826bbb4f8e7d94e047269093ce", + "0xe661c2a6c70cd4c4cf803658429603cb8f7c8dd1a8bf1ac000c07b17cf7814b1", + "0x93fe369f8315de72944c7ea1f66506b0aa6a3923b435f352799c6796ba0ba7b2", + "0xdb3db5f92400631bcdc49b0036838b03000f465dde53ebbef11f04963ef5219e", + "0x8fbc83c7c4b16f8d48611d1125c9860b0837c3030aa5d0d64b327d722dec250b", + "0xb238016769421deaa46aa26ab968d6ead614b7e99e63432213bcc0636b03b7ec", + "0x46b91ab2e587a2efba39d7bbe65755e55751313833fcd6e07063a93165b61bc7", + "0x8bfae3a2623b6a73419732b15fe50f99ee75a6952c7ac90f9125ccd960a00b9d", + "0xce4fb96b99be5c663e14d783fb6980ba07c5689317b5fbac3d2e582653b73adc", + "0x7781efcfad9292b5a5a3c6839b9da00c590c0081a1bc9991cabbde69409e0fb9", + "0x4a4bd2eaba40041d0a876897e71ccdf33f82c5db990529e6a95446f1668c1182", + "0xaf099a17cfa6cc2d4414759b07ed319606e7b65d198fa3ba3652af0aa2646531", + "0x384f3d208a7df522ef623ed8e5175350c0ef098cfdb9077c45c92d46c132795f", + "0x4d6b6757fa1bf35f583b03a0469697a6c45c9eb2ea292aedae8bd31d3a806ddf", + "0x015281ce8981abefabecc71d179c2acf0b16ffae29159e58fd323712587d71a5", + "0xe6ee15ec60e4ad82db0dd843c5f75c4f143bc868dcc2ee90919b3b3991218f25", + "0x7e0339afd84ba2f05901431889d906b643bafb8052e169b42fb71b77a9731820", + "0xbef2923f78962680ad1249588c6a103a5989401c9d8f70598013d261d9a55861", + "0x08371f39d235fe240bb0d4c2d7a443a5289e78feb753a51496ba12edb7bccd85", + "0x22390078429bdb6c30fb866979bac61f07d8ee289a6d949d39fbae91b2d71415", + "0x122072162d14ba916547cdb29fc9d35c37456fc2058f69a07c019189eaee8f83", + "0xd23ab5e33fdde2ff43c115f3838a6514c9a3f35e4a6afece49cece76e61c53c7", + "0x141fecf80bc04cb714786bcfb9ece9722e73ed89887e6e68d6fc486471bad0bd", + "0x46604512136e3b3cd7e027de0fbc10c9bf6e1efb32be53a95cd7fdd03e3d53ac", + "0x52abd1afcb310ed61fc785fb4452bdf663a063a52ea6a37adbb902f51750bc11", + "0x361d341722d14fd2fad63c17befde24561a5b2f003be01fea2f892b0eeffc94d", + "0x3eaa9017f1d657975e93722d5c3b4109750945aa8ecc27f39e9b04c275e32106", + "0x9c5e0538ada58b7c54dfb2154668c0493be60d35064b2fbb5bf209a977d07e57", + "0xfc49a9ed903f1eecc775a50d7556b4046b6f66d8ac95d41ba36704f49bd63fce", + "0x85cf6c97570ad666afaf0ad1f7e7dc64e0b259dcb02c42554692a1735fb37ef4", + "0xae861970ecf45c5224f946ba8f6441c93edf83950b12fdd52ceda3d7d53dbf32", + "0xdf47a5dd7b8ade8b279ffe9047c2f0ad5c6a5431e9b4188591839bc7d6ff4173", + "0xa7a367a5d8d13e193051cb123b1cd917de8ebdc07918d682b31af7342ce7a698", + "0x060a49067d27644cb03689d4bb5f39a753224d593d4b91f2b70358d72d88853a", + "0x593aa3167b1dd3beabfc428cc53887c1cc1b5b570dd310143669a22ab2d1a4a8", + "0x9489e935cf00b5f3cbfdebbd6cace9bd8cba9f9e484664e8e76349e694314516", + "0x7549951fb66975623382c573f61c593e1d2096d21c3a68994a791425de4a92a0", + "0x85141c6f34e2a6648aa45b4780fa9179c9c229c774fe8ccb3b8ab0eca3a60391", + "0xe25ab18336722de9b22e3a8b2fe9adf6771b338b695ca5a80061d546caccb288", + "0x44fee3971e29540fcd7cc96cb08a22c6dd6dafc17fd9305e13500fe1de60bb5d", + "0x4d9258fd627ae96774fdeed0e5310ee976498d918e4b54275233da1b5f827362", + "0x485dc1aee5406a251cb437971bf928f447069a307045182d3db3a302155d6779", + "0xd7f6cdcf26d6ad0a7f21b103e958cc437bb5aa7ffecf97850165f43514d4c95b", + "0x6c210901e0218318e01f6d21d7c986f11de1bf84b9eff3e64a52ef42107185dd", + "0xb9d2c5b5e1bd808800bb33b11a7c2be2b872935e8492b7063285c49d7f92f734", + "0x66621907053ce5d1547df388ad8f8bdbbab9dd996f3b1fd640dc422a608a6087", + "0x8d371079e5187bf31d554c34697a9ac3c11ff8fb982e6b6a68429f2e8ffd4f8e", + "0x16951c981020495a83f79be9ad2f340ec5226bb04e4e0ebe1f27d3143feb9042", + "0x5c0efb6c1684f19c5a3c11b1243dadeba3e65b96b1b845fd0a75d8e241c94ca0", + "0xe43349db377ec74f78cfb37eb22bc39d74211646205cb4d1b5d1d0fd16021d05", + "0x4c379254315a3ec38621435b3e126336dd0b41f4d065d8733a358697cd541264", + "0xd84647d81ed4e556655ff9582032a954089e04392412808592ec7bb53cea5a2a", + "0x06a8faea1c4851de651635792445a3f25709514b9fb2bb23a2d24ac22f603ed0", + "0x977cea87698c8a08c874844f4639b146c68773e922a7ceae883dd862ccf95bd9", + "0x0cd0588cce5c2105bea36359363e86f6a6775aa0b3125a1780dbc4bb9537d627", + "0xee35b874e54a538170cb18bbf74d3dc7f7b2522263d2d79841591e9f09e3a1d6", + "0xa40fc1bfca63f507b80a5adad32ac883c7d928c23f3b203759b176cd80f37e35", + "0x6162bdef7a5e89e60b4abb3093af275455eebeaca5348a956d721642162c71b8", + "0xdc4e2606bd212827f8a391134861963fee5aba1005201edbe3178ba63e05500c", + "0x131601b1d34cde268443e666024fe502c9f23d54c975483b3b07d9759033eeb4", + "0x79e3abbb7bcd5ef05f069fbf5ea5d9639588e3f939cff5d2b612c240577c06ff", + "0xf7f88dcb5b486b78fe6c199cf756018e8709548dc7325f9e1e3e2d1169d5dc09", + "0x72d3d5836d6a68b5b4979431479f4a671e1c30539e4146e5859a97a195ed2782", + "0x086c291ddf5fa190e87ab4f76eba1231bcf59ec243d2843788dafe95bf7fcfb7", + "0x04287431e7f53066ab8e1550298ab3ce27c1ccc880e38f0b41ad872de3eff509", + "0x229a56f8970cc64570e8d0cad368d835d52bcb88ad8113cd45749b6d8df0b30c", + "0x89435c2fe11e6b8465da7bbd7663a1972f2416c7ee8d728a26b59898eda1890b", + "0x56367fd86cd4690cea733a6a5df7169e781ce3b991e0082ff3c9d269f58d4b76", + "0x654d2f3844d53c2d2744981355d1049f0f1d04c554959d19eb940c796812168e", + "0x67cd79714d1c59098c47ab8b192c8e4d7e79023f321f66eec9626b78204f4449", + "0x7251e63a3748a60505b4b99af71db656ed9780d49f10dc7dec363f96286dcba0", + "0x55325a35f7aaa81bb0a2027d0b9d013f9e4d6fb1a479056676ce5d31003f4d36", + "0xa02d3d68c32544184436ab09ffff241e2f44d133200122b277df2c33e681c799", + "0x57526b0d3ea172c41d8ac2ab9cf47932dc11b6f3b42027e475da5129c5394b76", + "0xf1cb875fb6c526909f4dac30c94b3fd72852dff282a8fe13bb5564a4a814ddb1", + "0x82b8b549cc4081fc924186fac0ac558c45fcaefd20fc7a9191981877d37ae92f", + "0xfc6e08279867a93c5899140e42279950d084bf3f120870844d123770f696aa50", + "0xdb22589b7b517c5fd6043a573d3171387718308acff879f087c3777f21e72911", + "0xd622734b593395fe457c1b01a073b8a9b88d877be3d04788bfebb7d9fd20192b", + "0xa6738bbf5e58be5f947f3bbe8564a6d64bbd713cb2e785152a56e7a3435fcbac", + "0x2a6093694b4aa95733d29cabe3f9aefa198887ba1d39335844a7789ccfcd25f1", + "0x7c70e33a011b2f8203a3148801cec71432b56474c77b5850c71df7e4b23b6bd3", + "0x2690ae9df75683037d3bbc5bbb068e770493033c3d5140a397b9a759be143422", + "0x54a5fa96035d40fd9557220d835dd82fdd981dbfec66e26e185018a2f7e3f665", + "0xa83c41a25d15d4eeaca2290b9d60e55d2b636f4083700f276eddc875eca857ce", + "0xd978eeaef835b1ece440ae4adc7862480373c635850965cec16dd8598593f7aa", + "0x18f9c6c1e03290d59e261da7caa867844cfe0b87710f41c07608613c86f9b902", + "0xadd7fdf1736b49de85a3d4c381bc055eff6dd8643d995d1bc340cc2651c0e4b9", + "0x7102ee375e3eff75e5a5a5dafb99864db8ad24a24e2817a927e2dc6878a09ef4", + "0x6619d248ef0d74ca1bddfec655b23229e79eecfa4e87d690166623ce94277db5", + "0x4be0eb7f32ba4870009d32c76ce5c3afeeb62dab6e688db7fd6266fa1c5bf8e8", + "0xa65578ed2affeb0b487db90b00f04b05d44cdc49875356b71c1cc1939203ba22", + "0xc70a2c953deed6e690a8d288b838c1e907c65cee4aa58ff7251826b692feead0", + "0x955f4c5bca931d7034384e9291a6777e99b2a8fc55b3fcb76ba63fd9ada41357" + ] + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "activate_at": "0", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "activate_at": "0", + "pricing": { + "linear": { + "base": 60, + "word": 12 + } + } + } + }, + "0x0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { + "name": "ripemd160", + "activate_at": "0", + "pricing": { + "linear": { + "base": 600, + "word": 120 + } + } + } + }, + "0x0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { + "name": "identity", + "activate_at": "0", + "pricing": { + "linear": { + "base": 15, + "word": 3 + } + } + } + }, + "0x0000000000000000000000000000000000000005": { + "balance": "1", + "builtin": { + "name": "modexp", + "activate_at": "0", + "pricing": { + "modexp": { + "divisor": 20 + } + } + } + }, + "0x0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0x0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x1204700000000000000000000000000000000005": { + "constructor": "0x606060405234156200001057600080fd5b6040516200240138038062002401833981016040528080518201919060200180519060200190919050505b600082518260328211158015620000525750818111155b801562000060575060008114155b80156200006e575060008214155b15156200007a57600080fd5b600092505b8451831015620001b6576002600086858151811015156200009c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156200012b5750600085848151811015156200010857fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013757600080fd5b60016002600087868151811015156200014c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b82806001019350506200007f565b8460039080519060200190620001ce929190620001e3565b50836004819055505b5b5050505050620002b8565b8280548282559060005260206000209081019282156200025f579160200282015b828111156200025e5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000204565b5b5090506200026e919062000272565b5090565b620002b591905b80821115620002b157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000279565b5090565b90565b61213980620002c86000396000f3006060604052361561011b576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101da57806320ea8d86146102135780632f54bf6e146102365780633411c81c1461028757806354741525146102e15780637065cb4814610325578063784547a71461035e5780638b51d13f146103995780639ace38c2146103d0578063a0e67e2b146104ce578063a8abe69a14610539578063b5dc40c3146105d1578063b77bf6001461064a578063ba51a6df14610673578063c01a8c8414610696578063c6427474146106b9578063d74f8edd14610752578063dc8452cd1461077b578063e20056e6146107a4578063ee22610b146107fc575b5b6000341115610174573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b5b005b341561018257600080fd5b610198600480803590602001909190505061081f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101e557600080fd5b610211600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061085f565b005b341561021e57600080fd5b6102346004808035906020019091905050610b02565b005b341561024157600080fd5b61026d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cae565b604051808215151515815260200191505060405180910390f35b341561029257600080fd5b6102c7600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cce565b604051808215151515815260200191505060405180910390f35b34156102ec57600080fd5b61030f600480803515159060200190919080351515906020019091905050610cfd565b6040518082815260200191505060405180910390f35b341561033057600080fd5b61035c600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d91565b005b341561036957600080fd5b61037f6004808035906020019091905050610f99565b604051808215151515815260200191505060405180910390f35b34156103a457600080fd5b6103ba6004808035906020019091905050611081565b6040518082815260200191505060405180910390f35b34156103db57600080fd5b6103f16004808035906020019091905050611150565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001831515151581526020018281038252848181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156104bc5780601f10610491576101008083540402835291602001916104bc565b820191906000526020600020905b81548152906001019060200180831161049f57829003601f168201915b50509550505050505060405180910390f35b34156104d957600080fd5b6104e16111ac565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105255780820151818401525b602081019050610509565b505050509050019250505060405180910390f35b341561054457600080fd5b610579600480803590602001909190803590602001909190803515159060200190919080351515906020019091905050611241565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105bd5780820151818401525b6020810190506105a1565b505050509050019250505060405180910390f35b34156105dc57600080fd5b6105f260048080359060200190919050506113a2565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106365780820151818401525b60208101905061061a565b505050509050019250505060405180910390f35b341561065557600080fd5b61065d6115d3565b6040518082815260200191505060405180910390f35b341561067e57600080fd5b61069460048080359060200190919050506115d9565b005b34156106a157600080fd5b6106b76004808035906020019091905050611696565b005b34156106c457600080fd5b61073c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611877565b6040518082815260200191505060405180910390f35b341561075d57600080fd5b610765611897565b6040518082815260200191505060405180910390f35b341561078657600080fd5b61078e61189c565b6040518082815260200191505060405180910390f35b34156107af57600080fd5b6107fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506118a2565b005b341561080757600080fd5b61081d6004808035906020019091905050611bc0565b005b60038181548110151561082e57fe5b906000526020600020900160005b915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561089b57600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156108f457600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610a80578273ffffffffffffffffffffffffffffffffffffffff1660038381548110151561098757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a725760036001600380549050038154811015156109e757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2357fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610a80565b5b8180600101925050610951565b6001600381818054905003915081610a989190611fe8565b506003805490506004541115610ab757610ab66003805490506115d9565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25b5b505b5050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610b5b57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610bc657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610bf657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35b5b505b50505b5050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610d8957838015610d3c575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610d6f5750828015610d6e575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610d7b576001820191505b5b8080600101915050610d05565b5b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dcb57600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610e2557600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610e4c57600080fd5b60016003805490500160045460328211158015610e695750818111155b8015610e76575060008114155b8015610e83575060008214155b1515610e8e57600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038054806001018281610efa9190612014565b916000526020600020900160005b87909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b50505b505b505b50565b6000806000809150600090505b60038054905081101561107957600160008581526020019081526020016000206000600383815481101515610fd757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611058576001820191505b60045482141561106b576001925061107a565b5b8080600101915050610fa6565b5b5050919050565b600080600090505b600380549050811015611149576001600084815260200190815260200160002060006003838154811015156110ba57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561113b576001820191505b5b8080600101915050611089565b5b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169080600101549080600201908060030160009054906101000a900460ff16905084565b6111b4612040565b600380548060200260200160405190810160405280929190818152602001828054801561123657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116111ec575b505050505090505b90565b611249612054565b611251612054565b6000806005546040518059106112645750595b908082528060200260200182016040525b50925060009150600090505b600554811015611322578580156112b8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b806112eb57508480156112ea575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15611314578083838151811015156112ff57fe5b90602001906020020181815250506001820191505b5b8080600101915050611281565b8787036040518059106113325750595b908082528060200260200182016040525b5093508790505b8681101561139657828181518110151561136057fe5b906020019060200201518489830381518110151561137a57fe5b90602001906020020181815250505b808060010191505061134a565b5b505050949350505050565b6113aa612040565b6113b2612040565b6000806003805490506040518059106113c85750595b908082528060200260200182016040525b50925060009150600090505b60038054905081101561152b5760016000868152602001908152602001600020600060038381548110151561141657fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561151d5760038181548110151561149f57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156114da57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b5b80806001019150506113e5565b816040518059106115395750595b908082528060200260200182016040525b509350600090505b818110156115ca57828181518110151561156857fe5b90602001906020020151848281518110151561158057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b8080600101915050611552565b5b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561161357600080fd5b600380549050816032821115801561162b5750818111155b8015611638575060008114155b8015611645575060008214155b151561165057600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a15b5b50505b50565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156116ef57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561174b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156117b757600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361186c85611bc0565b5b5b50505b505b5050565b6000611884848484611e6c565b905061188f81611696565b5b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156118de57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561193757600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151561199157600080fd5b600092505b600380549050831015611a7f578473ffffffffffffffffffffffffffffffffffffffff166003848154811015156119c957fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611a715783600384815481101515611a2257fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611a7f565b5b8280600101935050611996565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b505b505b505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c1b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c8657600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611cb657600080fd5b611cbf86610f99565b15611e6057600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611ddd8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd35780601f10611da857610100808354040283529160200191611dd3565b820191906000526020600020905b815481529060010190602001808311611db657829003601f168201915b5050505050611fc0565b15611e1457857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611e5f565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b5b5b505b50505b505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611e9557600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190611f54929190612068565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a25b5b509392505050565b6000806040516020840160008287838a8c6187965a03f1925050508091505b50949350505050565b81548183558181151161200f5781836000526020600020918201910161200e91906120e8565b5b505050565b81548183558181151161203b5781836000526020600020918201910161203a91906120e8565b5b505050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106120a957805160ff19168380011785556120d7565b828001600101855582156120d7579182015b828111156120d65782518255916020019190600101906120bb565b5b5090506120e491906120e8565b5090565b61210a91905b808211156121065760008160009055506001016120ee565b5090565b905600a165627a7a72305820f1129b699b3017535535a920e15503cd06af1f5c77637c0637cc29355b1dad3400290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc19" + }, + "0x1204700000000000000000000000000000000003": { + "constructor": "0x606060405234156200001057600080fd5b6040516200240138038062002401833981016040528080518201919060200180519060200190919050505b600082518260328211158015620000525750818111155b801562000060575060008114155b80156200006e575060008214155b15156200007a57600080fd5b600092505b8451831015620001b6576002600086858151811015156200009c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156200012b5750600085848151811015156200010857fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013757600080fd5b60016002600087868151811015156200014c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b82806001019350506200007f565b8460039080519060200190620001ce929190620001e3565b50836004819055505b5b5050505050620002b8565b8280548282559060005260206000209081019282156200025f579160200282015b828111156200025e5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000204565b5b5090506200026e919062000272565b5090565b620002b591905b80821115620002b157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000279565b5090565b90565b61213980620002c86000396000f3006060604052361561011b576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101da57806320ea8d86146102135780632f54bf6e146102365780633411c81c1461028757806354741525146102e15780637065cb4814610325578063784547a71461035e5780638b51d13f146103995780639ace38c2146103d0578063a0e67e2b146104ce578063a8abe69a14610539578063b5dc40c3146105d1578063b77bf6001461064a578063ba51a6df14610673578063c01a8c8414610696578063c6427474146106b9578063d74f8edd14610752578063dc8452cd1461077b578063e20056e6146107a4578063ee22610b146107fc575b5b6000341115610174573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b5b005b341561018257600080fd5b610198600480803590602001909190505061081f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101e557600080fd5b610211600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061085f565b005b341561021e57600080fd5b6102346004808035906020019091905050610b02565b005b341561024157600080fd5b61026d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cae565b604051808215151515815260200191505060405180910390f35b341561029257600080fd5b6102c7600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cce565b604051808215151515815260200191505060405180910390f35b34156102ec57600080fd5b61030f600480803515159060200190919080351515906020019091905050610cfd565b6040518082815260200191505060405180910390f35b341561033057600080fd5b61035c600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d91565b005b341561036957600080fd5b61037f6004808035906020019091905050610f99565b604051808215151515815260200191505060405180910390f35b34156103a457600080fd5b6103ba6004808035906020019091905050611081565b6040518082815260200191505060405180910390f35b34156103db57600080fd5b6103f16004808035906020019091905050611150565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001831515151581526020018281038252848181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156104bc5780601f10610491576101008083540402835291602001916104bc565b820191906000526020600020905b81548152906001019060200180831161049f57829003601f168201915b50509550505050505060405180910390f35b34156104d957600080fd5b6104e16111ac565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105255780820151818401525b602081019050610509565b505050509050019250505060405180910390f35b341561054457600080fd5b610579600480803590602001909190803590602001909190803515159060200190919080351515906020019091905050611241565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105bd5780820151818401525b6020810190506105a1565b505050509050019250505060405180910390f35b34156105dc57600080fd5b6105f260048080359060200190919050506113a2565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106365780820151818401525b60208101905061061a565b505050509050019250505060405180910390f35b341561065557600080fd5b61065d6115d3565b6040518082815260200191505060405180910390f35b341561067e57600080fd5b61069460048080359060200190919050506115d9565b005b34156106a157600080fd5b6106b76004808035906020019091905050611696565b005b34156106c457600080fd5b61073c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611877565b6040518082815260200191505060405180910390f35b341561075d57600080fd5b610765611897565b6040518082815260200191505060405180910390f35b341561078657600080fd5b61078e61189c565b6040518082815260200191505060405180910390f35b34156107af57600080fd5b6107fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506118a2565b005b341561080757600080fd5b61081d6004808035906020019091905050611bc0565b005b60038181548110151561082e57fe5b906000526020600020900160005b915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561089b57600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156108f457600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610a80578273ffffffffffffffffffffffffffffffffffffffff1660038381548110151561098757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a725760036001600380549050038154811015156109e757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2357fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610a80565b5b8180600101925050610951565b6001600381818054905003915081610a989190611fe8565b506003805490506004541115610ab757610ab66003805490506115d9565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25b5b505b5050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610b5b57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610bc657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610bf657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35b5b505b50505b5050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610d8957838015610d3c575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610d6f5750828015610d6e575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610d7b576001820191505b5b8080600101915050610d05565b5b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dcb57600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610e2557600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610e4c57600080fd5b60016003805490500160045460328211158015610e695750818111155b8015610e76575060008114155b8015610e83575060008214155b1515610e8e57600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038054806001018281610efa9190612014565b916000526020600020900160005b87909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b50505b505b505b50565b6000806000809150600090505b60038054905081101561107957600160008581526020019081526020016000206000600383815481101515610fd757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611058576001820191505b60045482141561106b576001925061107a565b5b8080600101915050610fa6565b5b5050919050565b600080600090505b600380549050811015611149576001600084815260200190815260200160002060006003838154811015156110ba57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561113b576001820191505b5b8080600101915050611089565b5b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169080600101549080600201908060030160009054906101000a900460ff16905084565b6111b4612040565b600380548060200260200160405190810160405280929190818152602001828054801561123657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116111ec575b505050505090505b90565b611249612054565b611251612054565b6000806005546040518059106112645750595b908082528060200260200182016040525b50925060009150600090505b600554811015611322578580156112b8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b806112eb57508480156112ea575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15611314578083838151811015156112ff57fe5b90602001906020020181815250506001820191505b5b8080600101915050611281565b8787036040518059106113325750595b908082528060200260200182016040525b5093508790505b8681101561139657828181518110151561136057fe5b906020019060200201518489830381518110151561137a57fe5b90602001906020020181815250505b808060010191505061134a565b5b505050949350505050565b6113aa612040565b6113b2612040565b6000806003805490506040518059106113c85750595b908082528060200260200182016040525b50925060009150600090505b60038054905081101561152b5760016000868152602001908152602001600020600060038381548110151561141657fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561151d5760038181548110151561149f57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156114da57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b5b80806001019150506113e5565b816040518059106115395750595b908082528060200260200182016040525b509350600090505b818110156115ca57828181518110151561156857fe5b90602001906020020151848281518110151561158057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b8080600101915050611552565b5b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561161357600080fd5b600380549050816032821115801561162b5750818111155b8015611638575060008114155b8015611645575060008214155b151561165057600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a15b5b50505b50565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156116ef57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561174b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156117b757600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361186c85611bc0565b5b5b50505b505b5050565b6000611884848484611e6c565b905061188f81611696565b5b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156118de57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561193757600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151561199157600080fd5b600092505b600380549050831015611a7f578473ffffffffffffffffffffffffffffffffffffffff166003848154811015156119c957fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611a715783600384815481101515611a2257fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611a7f565b5b8280600101935050611996565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b505b505b505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c1b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c8657600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611cb657600080fd5b611cbf86610f99565b15611e6057600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611ddd8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd35780601f10611da857610100808354040283529160200191611dd3565b820191906000526020600020905b815481529060010190602001808311611db657829003601f168201915b5050505050611fc0565b15611e1457857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611e5f565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b5b5b505b50505b505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611e9557600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190611f54929190612068565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a25b5b509392505050565b6000806040516020840160008287838a8c6187965a03f1925050508091505b50949350505050565b81548183558181151161200f5781836000526020600020918201910161200e91906120e8565b5b505050565b81548183558181151161203b5781836000526020600020918201910161203a91906120e8565b5b505050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106120a957805160ff19168380011785556120d7565b828001600101855582156120d7579182015b828111156120d65782518255916020019190600101906120bb565b5b5090506120e491906120e8565b5090565b61210a91905b808211156121065760008160009055506001016120ee565b5090565b905600a165627a7a72305820f1129b699b3017535535a920e15503cd06af1f5c77637c0637cc29355b1dad3400290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc19" + }, + "0x120470000000000000000000000000000000000a": { + "balance": "11310499300000000000000000", + "constructor": "0x606060405234156200001057600080fd5b6040516200240138038062002401833981016040528080518201919060200180519060200190919050505b600082518260328211158015620000525750818111155b801562000060575060008114155b80156200006e575060008214155b15156200007a57600080fd5b600092505b8451831015620001b6576002600086858151811015156200009c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161580156200012b5750600085848151811015156200010857fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1614155b15156200013757600080fd5b60016002600087868151811015156200014c57fe5b9060200190602002015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b82806001019350506200007f565b8460039080519060200190620001ce929190620001e3565b50836004819055505b5b5050505050620002b8565b8280548282559060005260206000209081019282156200025f579160200282015b828111156200025e5782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509160200191906001019062000204565b5b5090506200026e919062000272565b5090565b620002b591905b80821115620002b157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000279565b5090565b90565b61213980620002c86000396000f3006060604052361561011b576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063025e7c2714610177578063173825d9146101da57806320ea8d86146102135780632f54bf6e146102365780633411c81c1461028757806354741525146102e15780637065cb4814610325578063784547a71461035e5780638b51d13f146103995780639ace38c2146103d0578063a0e67e2b146104ce578063a8abe69a14610539578063b5dc40c3146105d1578063b77bf6001461064a578063ba51a6df14610673578063c01a8c8414610696578063c6427474146106b9578063d74f8edd14610752578063dc8452cd1461077b578063e20056e6146107a4578063ee22610b146107fc575b5b6000341115610174573373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a25b5b005b341561018257600080fd5b610198600480803590602001909190505061081f565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156101e557600080fd5b610211600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061085f565b005b341561021e57600080fd5b6102346004808035906020019091905050610b02565b005b341561024157600080fd5b61026d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cae565b604051808215151515815260200191505060405180910390f35b341561029257600080fd5b6102c7600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610cce565b604051808215151515815260200191505060405180910390f35b34156102ec57600080fd5b61030f600480803515159060200190919080351515906020019091905050610cfd565b6040518082815260200191505060405180910390f35b341561033057600080fd5b61035c600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610d91565b005b341561036957600080fd5b61037f6004808035906020019091905050610f99565b604051808215151515815260200191505060405180910390f35b34156103a457600080fd5b6103ba6004808035906020019091905050611081565b6040518082815260200191505060405180910390f35b34156103db57600080fd5b6103f16004808035906020019091905050611150565b604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200184815260200180602001831515151581526020018281038252848181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156104bc5780601f10610491576101008083540402835291602001916104bc565b820191906000526020600020905b81548152906001019060200180831161049f57829003601f168201915b50509550505050505060405180910390f35b34156104d957600080fd5b6104e16111ac565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105255780820151818401525b602081019050610509565b505050509050019250505060405180910390f35b341561054457600080fd5b610579600480803590602001909190803590602001909190803515159060200190919080351515906020019091905050611241565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156105bd5780820151818401525b6020810190506105a1565b505050509050019250505060405180910390f35b34156105dc57600080fd5b6105f260048080359060200190919050506113a2565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106365780820151818401525b60208101905061061a565b505050509050019250505060405180910390f35b341561065557600080fd5b61065d6115d3565b6040518082815260200191505060405180910390f35b341561067e57600080fd5b61069460048080359060200190919050506115d9565b005b34156106a157600080fd5b6106b76004808035906020019091905050611696565b005b34156106c457600080fd5b61073c600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611877565b6040518082815260200191505060405180910390f35b341561075d57600080fd5b610765611897565b6040518082815260200191505060405180910390f35b341561078657600080fd5b61078e61189c565b6040518082815260200191505060405180910390f35b34156107af57600080fd5b6107fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506118a2565b005b341561080757600080fd5b61081d6004808035906020019091905050611bc0565b005b60038181548110151561082e57fe5b906000526020600020900160005b915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561089b57600080fd5b81600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156108f457600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600091505b600160038054905003821015610a80578273ffffffffffffffffffffffffffffffffffffffff1660038381548110151561098757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415610a725760036001600380549050038154811015156109e757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600383815481101515610a2357fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610a80565b5b8180600101925050610951565b6001600381818054905003915081610a989190611fe8565b506003805490506004541115610ab757610ab66003805490506115d9565b5b8273ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a25b5b505b5050565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610b5b57600080fd5b81336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515610bc657600080fd5b8360008082815260200190815260200160002060030160009054906101000a900460ff16151515610bf657600080fd5b60006001600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167ff6a317157440607f36269043eb55f1287a5a19ba2216afeab88cd46cbcfb88e960405160405180910390a35b5b505b50505b5050565b60026020528060005260406000206000915054906101000a900460ff1681565b60016020528160005260406000206020528060005260406000206000915091509054906101000a900460ff1681565b600080600090505b600554811015610d8957838015610d3c575060008082815260200190815260200160002060030160009054906101000a900460ff16155b80610d6f5750828015610d6e575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15610d7b576001820191505b5b8080600101915050610d05565b5b5092915050565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dcb57600080fd5b80600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151515610e2557600080fd5b8160008173ffffffffffffffffffffffffffffffffffffffff1614151515610e4c57600080fd5b60016003805490500160045460328211158015610e695750818111155b8015610e76575060008114155b8015610e83575060008214155b1515610e8e57600080fd5b6001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060038054806001018281610efa9190612014565b916000526020600020900160005b87909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550508473ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b50505b505b505b50565b6000806000809150600090505b60038054905081101561107957600160008581526020019081526020016000206000600383815481101515610fd757fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615611058576001820191505b60045482141561106b576001925061107a565b5b8080600101915050610fa6565b5b5050919050565b600080600090505b600380549050811015611149576001600084815260200190815260200160002060006003838154811015156110ba57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561113b576001820191505b5b8080600101915050611089565b5b50919050565b60006020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169080600101549080600201908060030160009054906101000a900460ff16905084565b6111b4612040565b600380548060200260200160405190810160405280929190818152602001828054801561123657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116111ec575b505050505090505b90565b611249612054565b611251612054565b6000806005546040518059106112645750595b908082528060200260200182016040525b50925060009150600090505b600554811015611322578580156112b8575060008082815260200190815260200160002060030160009054906101000a900460ff16155b806112eb57508480156112ea575060008082815260200190815260200160002060030160009054906101000a900460ff165b5b15611314578083838151811015156112ff57fe5b90602001906020020181815250506001820191505b5b8080600101915050611281565b8787036040518059106113325750595b908082528060200260200182016040525b5093508790505b8681101561139657828181518110151561136057fe5b906020019060200201518489830381518110151561137a57fe5b90602001906020020181815250505b808060010191505061134a565b5b505050949350505050565b6113aa612040565b6113b2612040565b6000806003805490506040518059106113c85750595b908082528060200260200182016040525b50925060009150600090505b60038054905081101561152b5760016000868152602001908152602001600020600060038381548110151561141657fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161561151d5760038181548110151561149f57fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1683838151811015156114da57fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250506001820191505b5b80806001019150506113e5565b816040518059106115395750595b908082528060200260200182016040525b509350600090505b818110156115ca57828181518110151561156857fe5b90602001906020020151848281518110151561158057fe5b9060200190602002019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b8080600101915050611552565b5b505050919050565b60055481565b3073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561161357600080fd5b600380549050816032821115801561162b5750818111155b8015611638575060008114155b8015611645575060008214155b151561165057600080fd5b826004819055507fa3f1ee9126a074d9326c682f561767f710e927faa811f7a99829d49dc421797a836040518082815260200191505060405180910390a15b5b50505b50565b33600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615156116ef57600080fd5b81600080600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415151561174b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515156117b757600080fd5b600180600087815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550843373ffffffffffffffffffffffffffffffffffffffff167f4a504a94899432a9846e1aa406dceb1bcfd538bb839071d49d1e5e23f5be30ef60405160405180910390a361186c85611bc0565b5b5b50505b505b5050565b6000611884848484611e6c565b905061188f81611696565b5b9392505050565b603281565b60045481565b60003073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156118de57600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151561193757600080fd5b82600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151561199157600080fd5b600092505b600380549050831015611a7f578473ffffffffffffffffffffffffffffffffffffffff166003848154811015156119c957fe5b906000526020600020900160005b9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415611a715783600384815481101515611a2257fe5b906000526020600020900160005b6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550611a7f565b5b8280600101935050611996565b6000600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055506001600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055508473ffffffffffffffffffffffffffffffffffffffff167f8001553a916ef2f495d26a907cc54d96ed840d7bda71e73194bf5a9df7a76b9060405160405180910390a28373ffffffffffffffffffffffffffffffffffffffff167ff39e6e1eb0edcf53c221607b54b00cd28f3196fed0a24994dc308b8f611b682d60405160405180910390a25b5b505b505b505050565b600033600260008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c1b57600080fd5b82336001600083815260200190815260200160002060008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515611c8657600080fd5b8460008082815260200190815260200160002060030160009054906101000a900460ff16151515611cb657600080fd5b611cbf86610f99565b15611e6057600080878152602001908152602001600020945060018560030160006101000a81548160ff021916908315150217905550611ddd8560000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16866001015487600201805460018160011615610100020316600290049050886002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd35780601f10611da857610100808354040283529160200191611dd3565b820191906000526020600020905b815481529060010190602001808311611db657829003601f168201915b5050505050611fc0565b15611e1457857f33e13ecb54c3076d8e8bb8c2881800a4d972b792045ffae98fdf46df365fed7560405160405180910390a2611e5f565b857f526441bb6c1aba3c9a4a6ca1d6545da9c2333c8c48343ef398eb858d72b7923660405160405180910390a260008560030160006101000a81548160ff0219169083151502179055505b5b5b5b505b50505b505050565b60008360008173ffffffffffffffffffffffffffffffffffffffff1614151515611e9557600080fd5b60055491506080604051908101604052808673ffffffffffffffffffffffffffffffffffffffff1681526020018581526020018481526020016000151581525060008084815260200190815260200160002060008201518160000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550602082015181600101556040820151816002019080519060200190611f54929190612068565b5060608201518160030160006101000a81548160ff0219169083151502179055509050506001600560008282540192505081905550817fc0ba8fe4b176c1714197d43b9cc6bcf797a4a7461c5fe8d0ef6e184ae7601e5160405160405180910390a25b5b509392505050565b6000806040516020840160008287838a8c6187965a03f1925050508091505b50949350505050565b81548183558181151161200f5781836000526020600020918201910161200e91906120e8565b5b505050565b81548183558181151161203b5781836000526020600020918201910161203a91906120e8565b5b505050565b602060405190810160405280600081525090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106120a957805160ff19168380011785556120d7565b828001600101855582156120d7579182015b828111156120d65782518255916020019190600101906120bb565b5b5090506120e491906120e8565b5090565b61210a91905b808211156121065760008160009055506001016120ee565b5090565b905600a165627a7a72305820f1129b699b3017535535a920e15503cd06af1f5c77637c0637cc29355b1dad3400290000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000cb1437200aea736788f1fc56f327c0456c3598d00000000000000000000000074dd76e24b2cfb43c1b1a4498295d553d0843746000000000000000000000000eeb4ceee443f9e0d17bdbd6daa241681ee5e51c2000000000000000000000000a005caea55375ae20e3aaef746113535503abc19" + }, + "0x0cB1437200aea736788f1Fc56F327c0456c3598D": { + "balance": "250000000000000000" + }, + "0x74dd76E24B2CFB43C1b1a4498295d553D0843746": { + "balance": "250000000000000000" + }, + "0xeeB4CEEe443F9e0D17BdBD6Daa241681EE5E51c2": { + "balance": "250000000000000000" + }, + "0xA005caEa55375ae20e3aAEF746113535503ABC19": { + "balance": "250000000000000000" + }, + "0x1204700000000000000000000000000000000001": { + "constructor": "0x60806040523480156200001157600080fd5b5060405162002f1538038062002f15833981018060405260608110156200003757600080fd5b81019080805190602001909291908051906020019092919080516401000000008111156200006457600080fd5b828101905060208101848111156200007b57600080fd5b81518560208202830111640100000000821117156200009957600080fd5b5050929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415620001e5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602481526020018062002ec56024913960400191505060405180910390fd5b60018151101562000242576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018062002ee9602c913960400191505060405180910390fd5b6200025c8362000473640100000000026401000000009004565b6200027682620005d5640100000000026401000000009004565b60008090505b81518110156200042057600073ffffffffffffffffffffffffffffffffffffffff16828281518110620002ab57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156200033e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b6001600360008484815181106200035157fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff02191690836003811115620003b257fe5b02179055508060036000848481518110620003c957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018190555080806001019150506200027c565b508060029080519060200190620004399291906200065c565b50600260019080546200044e929190620006eb565b506001600060146101000a81548160ff02191690831515021790555050505062000788565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141562000517576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b828054828255906000526020600020908101928215620006d8579160200282015b82811115620006d75782518260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550916020019190600101906200067d565b5b509050620006e7919062000742565b5090565b8280548282559060005260206000209081019282156200072f5760005260206000209182015b828111156200072e57825482559160010191906001019062000711565b5b5090506200073e919062000742565b5090565b6200078591905b808211156200078157600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555060010162000749565b5090565b90565b61272d80620007986000396000f3fe608060405234801561001057600080fd5b506004361061015f576000357c0100000000000000000000000000000000000000000000000000000000900480639f723637116100d5578063b980490911610099578063b980490914610594578063bd21442a146105f0578063c805f68b146106b3578063d826b7f1146106f7578063f2fde38b14610765578063f3aeac02146107a95761015f565b80639f7236371461040e578063a00745b61461046d578063a6940b07146104c9578063b3f05b9714610513578063b7ab4db5146105355761015f565b8063455701d611610127578063455701d6146102eb5780634d238c8e1461034a578063715018a61461038e57806375286211146103985780638da5cb5b146103a25780638f32d59b146103ec5761015f565b8063267fa41d1461016457806334ba3c1b146101c057806340550a1c146101de57806340a141ff1461023a578063418349551461027e575b600080fd5b6101a66004803603602081101561017a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610805565b604051808215151515815260200191505060405180910390f35b6101c8610877565b6040518082815260200191505060405180910390f35b610220600480360360208110156101f457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610884565b604051808215151515815260200191505060405180910390f35b61027c6004803603602081101561025057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610965565b005b6102c06004803603602081101561029457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ab0565b604051808360038111156102d057fe5b60ff1681526020018281526020019250505060405180910390f35b6102f3610ae1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561033657808201518184015260208101905061031b565b505050509050019250505060405180910390f35b61038c6004803603602081101561036057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b6f565b005b610396610d79565b005b6103a0610eb2565b005b6103aa611328565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103f4611351565b604051808215151515815260200191505060405180910390f35b6104166113a8565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561045957808201518184015260208101905061043e565b505050509050019250505060405180910390f35b6104af6004803603602081101561048357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506114d7565b604051808215151515815260200191505060405180910390f35b6104d16115b8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61051b6115de565b604051808215151515815260200191505060405180910390f35b61053d6115f1565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610580578082015181840152602081019050610565565b505050509050019250505060405180910390f35b6105d6600480360360208110156105aa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061167f565b604051808215151515815260200191505060405180910390f35b6106b16004803603608081101561060657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561066d57600080fd5b82018360208201111561067f57600080fd5b803590602001918460018302840111640100000000831117156106a157600080fd5b90919293919293905050506116f0565b005b6106f5600480360360208110156106c957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061194c565b005b6107636004803603606081101561070d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611aff565b005b6107a76004803603602081101561077b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611d59565b005b6107eb600480360360208110156107bf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611ddf565b604051808215151515815260200191505060405180910390f35b60006001600381111561081457fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561086f57fe5b149050919050565b6000600180549050905090565b60006001600381111561089357fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156108ee57fe5b148061095e575060038081111561090157fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561095c57fe5b145b9050919050565b61096d611351565b6109df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610a44576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126736022913960400191505060405180910390fd5b80610a4e81610884565b610aa3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b610aac82611e51565b5050565b60036020528060005260406000206000915090508060000160009054906101000a900460ff16908060010154905082565b60606002805480602002602001604051908101604052809291908181526020018280548015610b6557602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610b1b575b5050505050905090565b610b77611351565b610be9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600060149054906101000a900460ff16610c4e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126736022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610cf1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f56616c696461746f7220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b610cfa81610884565b15610d6d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f546869732076616c696461746f7220697320616c72656164792061637469766581525060200191505060405180910390fd5b610d76816120a4565b50565b610d81611351565b610df3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600060149054906101000a900460ff1615610f35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f56616c696461746f72207365742069732066696e616c697a656400000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610ff8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b6001600060146101000a81548160ff02191690831515021790555060008090505b6002805490508110156110dd576000600360006002848154811061103957fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020905060018160000160006101000a81548160ff021916908360038111156110c157fe5b0217905550818160010181905550508080600101915050611019565b50600073ffffffffffffffffffffffffffffffffffffffff16600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461126757600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff021916908360038111156111b557fe5b0217905550600060036000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506000600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b6002600190805461127992919061251f565b507f8564cd629b15f47dc310d45bcbfc9bcf5420b0d51bf0659a16c67f91d276325360016040518080602001828103825283818154815260200191508054801561131857602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116112ce575b50509250505060405180910390a1565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6060600180549050600280549050111561144a57600280548060200260200160405190810160405280929190818152602001828054801561143e57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116113f4575b505050505090506114d4565b60018054806020026020016040519081016040528092919081815260200182805480156114cc57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611482575b505050505090505b90565b6000600260038111156114e657fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff16600381111561154157fe5b14806115b1575060038081111561155457fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156115af57fe5b145b9050919050565b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1681565b6060600180548060200260200160405190810160405280929190818152602001828054801561167557602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001906001019080831161162b575b5050505050905090565b600060038081111561168d57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff1660038111156116e857fe5b149050919050565b846116fa81610884565b61174f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b8461175981610884565b6117ae576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b84438110611824576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146118e7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b858773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff167f729a19138e072a5a8d3a56d74ae0b5c84530f09aacd6e12b24c5b2fdc3f8a3d060405160405180910390a45050505050505050565b611954611351565b6119c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611a4c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260248152602001806126066024913960400191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611af3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260408152602001806126956040913960400191505060405180910390fd5b611afc81612179565b50565b82611b0981610884565b611b5e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b82611b6881610884565b611bbd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806126516022913960400191505060405180910390fd5b82438110611c33576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f426c6f636b206e756d626572206973206e6f742076616c69640000000000000081525060200191505060405180910390fd5b600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611cf6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f74207468652052656c617920636f6e747261637481525060200191505060405180910390fd5b838573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fbc459bd9db54016b1966d0fe812bbe0a82cd627ae3eacd01727dc63a432ca41b60405160405180910390a4505050505050565b611d61611351565b611dd3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b611ddc81612200565b50565b600060026003811115611dee57fe5b600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a900460ff166003811115611e4957fe5b149050919050565b600160028054905011611eaf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061262a6027913960400191505060405180910390fd5b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010154905060006001600280549050039050600060028281548110611f1257fe5b9060005260206000200160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508060028481548110611f4d57fe5b9060005260206000200160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555082600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600101819055506002805480919060019003611ff19190612571565b5060038060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff0219169083600381111561205057fe5b021790555083600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061209e612361565b50505050565b6002600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a81548160ff0219169083600381111561210357fe5b021790555060028190806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050612176612361565b50565b80600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff167fe8ec518081a7aa1fc5d586a5443a858ab130be8b8e39b545172c879a7e242c6b60405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156122a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008060146101000a81548160ff021916908315150217905550600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a084718a600143034060026040518363ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018083815260200180602001828103825283818154815260200191508054801561246c57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311612422575b50509350505050602060405180830381600087803b15801561248d57600080fd5b505af11580156124a1573d6000803e3d6000fd5b505050506040513d60208110156124b757600080fd5b810190808051906020019092919050505061251d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602d8152602001806126d5602d913960400191505060405180910390fd5b565b8280548282559060005260206000209081019282156125605760005260206000209182015b8281111561255f578254825591600101919060010190612544565b5b50905061256d919061259d565b5090565b8154818355818111156125985781836000526020600020918201910161259791906125e0565b5b505050565b6125dd91905b808211156125d957600081816101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055506001016125a3565b5090565b90565b61260291905b808211156125fe5760008160009055506001016125e6565b5090565b9056fe52656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f72206c65667441646472657373206973206e6f7420616e206163746976652076616c696461746f7256616c696461746f7220736574206973206e6f742066696e616c697a6564207965744e65772072656c617920636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6552656c617920636f6e747261637420496e6974696174654368616e67652063616c6c6261636b206661696c6564a165627a7a72305820c6b083565ee91eecaf8e5a6f14e1677206b36466bdfe0248a47919657b68255b002952656c617920636f6e747261637420616464726573732063616e6e6f74206265203078305468657265206d757374206265206174206c6561737420312076616c696461746f7220696e697469616c6c79000000000000000000000000120470000000000000000000000000000000000500000000000000000000000012047000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000300000000000000000000000036f67dd84e7327c10c7ead6c429a47189798fbdc00000000000000000000000020df7a4e8408add37c6a5c4afc1b1509924619fe00000000000000000000000077901f14183b1669c80e8c6137ff6721c9a26b25" + }, + "0x1204700000000000000000000000000000000000": { + "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561006557600080fd5b506040516040806116c18339810180604052604081101561008557600080fd5b810190808051906020019092919080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a361017482610193640100000000026401000000009004565b61018c816102f4640100000000026401000000009004565b5050610506565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610236576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610398576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561043f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252604281526020018061167f6042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b61116a806105156000396000f3fe608060405234801561001057600080fd5b50600436106100d1576000357c010000000000000000000000000000000000000000000000000000000090048063b7ab4db51161008e578063b7ab4db51461023b578063bd9656771461029a578063c476dd40146102de578063d3e848f114610381578063d69f13bb146103cb578063f2fde38b14610419576100d1565b8063715018a6146100d657806375286211146100e05780638da5cb5b146100ea5780638f32d59b14610134578063a084718a14610156578063ae3783d6146101f1575b600080fd5b6100de61045d565b005b6100e8610596565b005b6100f26106f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61013c610722565b604051808215151515815260200191505060405180910390f35b6101d76004803603604081101561016c57600080fd5b81019080803590602001909291908035906020019064010000000081111561019357600080fd5b8201836020820111156101a557600080fd5b803590602001918460208302840111640100000000831117156101c757600080fd5b9091929391929390505050610779565b604051808215151515815260200191505060405180910390f35b6101f9610893565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102436108b9565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561028657808201518184015260208101905061026b565b505050509050019250505060405180910390f35b6102dc600480360360208110156102b057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109d5565b005b61037f600480360360608110156102f457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019064010000000081111561033b57600080fd5b82018360208201111561034d57600080fd5b8035906020019184600183028401116401000000008311171561036f57600080fd5b9091929391929390505050610a5b565b005b610389610ba6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610417600480360360408110156103e157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610bcc565b005b61045b6004803603602081101561042f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ce1565b005b610465610722565b6104d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610659576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f53656e646572206973206e6f742073797374656d00000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663752862116040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401600060405180830381600087803b1580156106df57600080fd5b505af11580156106f3573d6000803e3d6000fd5b50505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610821576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061111d6022913960400191505060405180910390fd5b837f55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89848460405180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600190509392505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b7ab4db56040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160006040518083038186803b15801561093f57600080fd5b505afa158015610953573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250602081101561097d57600080fd5b81019080805164010000000081111561099557600080fd5b828101905060208101848111156109ab57600080fd5b81518560208202830111640100000000821117156109c857600080fd5b5050929190505050905090565b6109dd610722565b610a4f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b610a5881610d67565b50565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bd21442a33868686866040518663ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509650505050505050600060405180830381600087803b158015610b8857600080fd5b505af1158015610b9c573d6000803e3d6000fd5b5050505050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d826b7f13384846040518463ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019350505050600060405180830381600087803b158015610cc557600080fd5b505af1158015610cd9573d6000803e3d6000fd5b505050505050565b610ce9610722565b610d5b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b610d6481610f79565b50565b80600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610e0b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f416464726573732063616e6e6f7420626520307830000000000000000000000081525060200191505060405180910390fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415610eb2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260428152602001806110db6042913960600191505060405180910390fd5b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905082600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f4fea88aaf04c303804bb211ecc32a00ac8e5f0656bb854cad8a4a2e438256b7460405160405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561101c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fe4e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6553656e646572206973206e6f74207468652052656c6179656420636f6e7472616374a165627a7a72305820df8e72037cf9237ba09d135b3962a00a0186ba17dcbbe421274543975b2c346100294e65772072656c6179656420636f6e747261637420616464726573732063616e6e6f74206265207468652073616d65206173207468652063757272656e74206f6e6500000000000000000000000012047000000000000000000000000000000000050000000000000000000000001204700000000000000000000000000000000001" + }, + "0x1204700000000000000000000000000000000002": { + "constructor": "0x608060405273fffffffffffffffffffffffffffffffffffffffe600960006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503480156200006657600080fd5b5060405160408062001b22833981018060405260408110156200008857600080fd5b810190808051906020019092919080519060200190929190505050620000bc6200010c640100000000026401000000009004565b81600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600a81905550505062000825565b60405180610f0001604052806704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704398372e97bae8081526020016704395680a6af46808152602001670438cfa9de4a0e808152602001670437eeee904c06808152602001670436b44ebcb52e8081526020016704351fca638586808152602001670433316184bd0e808152602001670430e914205bc680815260200167042e46e23661ae80815260200167042b4acbc6cec6808152602001670427f4d0d1a30e80815260200167042444f156de868081526020016704203b2d56812e80815260200167041bd784d08b0680815260200167041719f7c4fc0e808152602001670412028633d44680815260200167040c91301d13ae808152602001670406c5f580ba46808152602001670400a0d65ec80e8081526020016703fa21d2b73d068081526020016703f348ea8a192e8081526020016703ec161dd75c868081526020016703e4896c9f070e8081526020016703dca2d6e118c68081526020016703d4625c9d91ae8081526020016703cbc7fdd471c68081526020016703c2d3ba85b90e8081526020016703b98592b167868081526020016703afdd86577d2e8081526020016703a5db9577fa0680815260200167039b7fc012de0e808152602001670390ca06282946808152602001670385ba67b7dbae80815260200167037a50e4c1f54680815260200167036e8d7d46760e8081526020016703627031455e06808152602001670355f900bead2e80815260200167034927ebb2638680815260200167033bfcf220810e80815260200167032e78140905c680815260200167032099516bf1ae80815260200167031260aa4944c6808152602001670303ce1ea0ff0e8081526020016702f4e1ae7320868081526020016702e59b59bfa92e8081526020016702d5fb208699068081526020016702c60102c7f00e8081526020016702b5ad0083ae468081526020016702a4ff19b9d3ae808152602001670293f74e6a6046808152602001670282959e95540e808152602001670270da0a3aaf0680815260200167025ec4915a712e80815260200167024c5533f49a86808152602001670238da1d6b04400081526020016702260c5cdf4d240081526020016702138f83989f90008152602001670201639196fb840081526020016701ef8886da61000081526020016701ddfe6362d0040081526020016701ccc5273048900081526020016701bbdcd242caa40081526020016701ab45649a564000815260200167019afede36eb6400815260200167018b093f188a1000815260200167017b64873f324400815260200167016c10b6aae40000815260200167015d0dcd5b9f4400815260200167014e5bcb51641000815260200167013ffab08c3264008152602001670131ea7d0c0a400081526020016701242b30d0eba4008152602001670116bccbdad6900081526020016701099f4e29cb0400815260200166fcd2b7bdc90000815260200166f0570896d08400815260200166e42c40b4e19000815260200166d8526017fc2400815260200166ccc966c0204000815260200166c19154ad4de400815260200166b6aa29df851000815260200166ac13e656c5c400815260200166a1ce8a1310000081526020016697da151463c4008152602001668e36875ac1100081526020016684e3e0e627e4008152602001667be221b6984000815260200166733149cc1224008152602001666ad1592695900081526020016662c24fc62284008152602001665b042daab900008152602001665396f2d45904008152602001664c7a9f4302900081526020016645af32f6b5a4008152602001663f34adef724000815260200166390b102d386400815260200166333259b00810008152602001662daa8a77e144008152602001662873a284c40000815260200166238da1d6b044008152602001661ef8886da610008152602001661ab45649a5640081526020016616c10b6aae4000815260200166131ea7d0c0a4008152602001660fcd2b7bdc90008152602001660ccc966c0204008152602001660a1ce8a131000081526020016607be221b69840081526020016605b042daab900081526020016603f34adef7240081526020016602873a284c4000815260200166016c10b6aae400815260200165a1ce8a1310008152602001652873a284c40081525060009060786200078e929190620007ab565b506208052060018190555060008054905060015402600281905550565b828054828255906000526020600020908101928215620007ea579160200282015b82811115620007e9578251825591602001919060010190620007cc565b5b509050620007f99190620007fd565b5090565b6200082291905b808211156200081e57600081600090555060010162000804565b5090565b90565b6112ed80620008356000396000f3fe608060405234801561001057600080fd5b5060043610610132576000357c0100000000000000000000000000000000000000000000000000000000900480634476d66a116100bf57806394f7f62b1161008e57806394f7f62b14610451578063df6a503014610493578063e6e100db146104b1578063f91c289814610509578063fb1ac5251461067457610132565b80634476d66a14610363578063553a5c85146103a557806358ceb672146103c357806360d4b8be146103cd57610132565b80631a488047116101065780631a4880471461020357806330f6eb161461022157806333ea51a81461028357806337339a16146102c75780633d84b8c11461030b57610132565b8062f380f414610137578063078d8e7a146101815780630f411cdb146101a357806318129375146101c1575b600080fd5b61013f610692565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101896106b8565b604051808215151515815260200191505060405180910390f35b6101ab6106c8565b6040518082815260200191505060405180910390f35b6101ed600480360360208110156101d757600080fd5b81019080803590602001909291905050506106ce565b6040518082815260200191505060405180910390f35b61020b6106ef565b6040518082815260200191505060405180910390f35b61026d6004803603604081101561023757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106f5565b6040518082815260200191505060405180910390f35b6102c56004803603602081101561029957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061071a565b005b610309600480360360208110156102dd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061079b565b005b61034d6004803603602081101561032157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506108a2565b6040518082815260200191505060405180910390f35b61038f6004803603602081101561037957600080fd5b81019080803590602001909291905050506108ba565b6040518082815260200191505060405180910390f35b6103ad6108d2565b6040518082815260200191505060405180910390f35b6103cb6108d8565b005b61040f600480360360208110156103e357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061093c565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61047d6004803603602081101561046757600080fd5b810190808035906020019092919050505061096f565b6040518082815260200191505060405180910390f35b61049b6109b3565b6040518082815260200191505060405180910390f35b6104f3600480360360208110156104c757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109b9565b6040518082815260200191505060405180910390f35b6105d56004803603604081101561051f57600080fd5b810190808035906020019064010000000081111561053c57600080fd5b82018360208201111561054e57600080fd5b8035906020019184602083028401116401000000008311171561057057600080fd5b90919293919293908035906020019064010000000081111561059157600080fd5b8201836020820111156105a357600080fd5b803590602001918460208302840111640100000000831117156105c557600080fd5b90919293919293905050506109d1565b604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561061c578082015181840152602081019050610601565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561065e578082015181840152602081019050610643565b5050505090500194505050505060405180910390f35b61067c610eca565b6040518082815260200191505060405180910390f35b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006106c343610ed0565b905090565b60025481565b600081815481106106db57fe5b906000526020600020016000915090505481565b600a5481565b6008602052816000526040600020602052806000526040600020600091509150505481565b80600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461085e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f43616c6c6572206973206e6f742074686520636f6d6d756e6974792066756e6481525060200191505060405180910390fd5b80600b60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60066020528060005260406000206000915090505481565b60076020528060005260406000206000915090505481565b60035481565b600c60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055565b600c6020528060005260406000206000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061097a82610ed0565b1561098857600090506109ae565b6000600154838161099557fe5b04815481106109a057fe5b906000526020600020015490505b919050565b60045481565b60056020528060005260406000206000915090505481565b606080600960009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a97576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260188152602001807f43616c6c6572206973206e6f74207468652073797374656d000000000000000081525060200191505060405180910390fd5b838390508686905014610af5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602581526020018061129d6025913960400191505060405180910390fd5b60018686905014610b6e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f42656e65666163746f7273206c697374206c656e677468206973206e6f74203181525060200191505060405180910390fd5b600084846000818110610b7d57fe5b9050602002013561ffff1661ffff1614610be2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602281526020018061127b6022913960400191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff1686866000818110610c0757fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161480610c4b5750610c4a43610ed0565b5b15610cc1576000604051908082528060200260200182016040528015610c805781602001602082028038833980820191505090505b506000604051908082528060200260200182016040528015610cb15781602001602082028038833980820191505090505b5081915080905091509150610ec1565b60606002604051908082528060200260200182016040528015610cf35781602001602082028038833980820191505090505b50905060608151604051908082528060200260200182016040528015610d285781602001602082028038833980820191505090505b509050610d5d88886000818110610d3b57fe5b9050602002013573ffffffffffffffffffffffffffffffffffffffff16610edf565b82600081518110610d6a57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050610dad4361096f565b81600081518110610dba57fe5b602002602001018181525050610df1600b60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16610edf565b82600181518110610dfe57fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050600a5481600181518110610e4857fe5b602002602001018181525050610e8682600081518110610e6457fe5b602002602001015182600081518110610e7957fe5b6020026020010151610f8c565b610eb882600181518110610e9657fe5b602002602001015182600181518110610eab57fe5b6020026020010151611134565b81819350935050505b94509492505050565b60015481565b60006002548210159050919050565b600080600c60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f825782915050610f87565b809150505b919050565b610fef81600860008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000438152602001908152602001600020546111f290919063ffffffff16565b600860008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060004381526020019081526020016000208190555061109581600660008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546111f290919063ffffffff16565b600660008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506110fe8160076000438152602001908152602001600020546111f290919063ffffffff16565b600760004381526020019081526020016000208190555061112a816003546111f290919063ffffffff16565b6003819055505050565b611149816004546111f290919063ffffffff16565b6004819055506111a181600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546111f290919063ffffffff16565b600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506111ee8282610f8c565b5050565b600080828401905083811015611270576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600e8152602001807f4f766572666c6f77206572726f7200000000000000000000000000000000000081525060200191505060405180910390fd5b809150509291505056fe42656e65666163746f72206973206e6f742074686520626c6f636b20617574686f7242656e65666163746f72732f7479706573206c697374206c656e6774682064696666657273a165627a7a723058209c17c14dc9f6fcdd3ddef3913263be4e76376e948facfc131a975ceda79ba0d6002900000000000000000000000012047000000000000000000000000000000000030000000000000000000000000000000000000000000000000856d3dfb6e26d00" + }, + "0x1204700000000000000000000000000000000004": { + "balance": "40789499640000000000000000", + "constructor": "0x608060405260006001556a21bd8334ca74c3834c00003073ffffffffffffffffffffffffffffffffffffffff16311462000085576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018062001b5b6023913960400191505060405180910390fd5b6200009e6200010b640100000000026401000000009004565b6a21bd8334ca74c3834c00006001541462000105576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602881526020018062001b0a6028913960400191505060405180910390fd5b620017b4565b6200014a73120470000000000000000000000000000000000a6a084595161401484a000000635d408564620016da640100000000026401000000009004565b62000188735a3977e000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b620001c673aae0debd2a4c519364e6098537145369913b26ec6901c761ff4c24e9210000635db8d244620016da640100000000026401000000009004565b620002047336874a6000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b6200024273108dd64c9e0d603d56304e97656e011831ed07ff6901c761ff4c24e9210000635db8d244620016da640100000000026401000000009004565b6200028073294f3750000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620002be732fb294a0000000000000000000000000000000006902d2cd1fdffd73150000635db8d244620016da640100000000026401000000009004565b620002fc73102fb040000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200033a732b250010000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620003787335c468d000000000000000000000000000000000696abafbb89b2adcd80000635db8d244620016da640100000000026401000000009004565b620003b673c8be7a00000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b620003f47353f3170000000000000000000000000000000000692004e50f922be25a0000635db8d244620016da640100000000026401000000009004565b6200043373dc918900000000000000000000000000000000006a0215a6ea9b07d650380000635db8d244620016da640100000000026401000000009004565b6200047173b476ee7d610dae7b23b671ebc7bd6112e97729696902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620004af732908b900000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620004ed733b2ef740000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200052b731153818a2eb49f0a71b27313c32814fc02e4db50694220bb939da668600000635db8d244620016da640100000000026401000000009004565b6200056973330d6100000000000000000000000000000000006923466459b949a9950000635db8d244620016da640100000000026401000000009004565b620005a7733e5518c20876eab3a42969d032fa7c30599af912691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b620005e57357f33efad76d4b783cf42c9e6cb08f4425dfe96e6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200062373a87e88f0bbc43468cb657294f2479c6f35179b80692004e50f922be25a0000635db8d244620016da640100000000026401000000009004565b62000661734cebef90000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b6200069f73511909cef97475819de66b645f13464285c227ce6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620006dd7322fc2310000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200071b7347919fb8d4e7e360bef0d5a7a2411593dbcc0e776902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200075973949423db1bfee1ddec99c9d24a12a6ea27cb34896901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b620007977343ec5680000000000000000000000000000000006907f0e10af47c1c700000635db8d244620016da640100000000026401000000009004565b620007d57367cf1c40622f39fa067b614f13aad6da5dd95f326969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620008137310b9390000000000000000000000000000000000692ab13175efe0a8630000635db8d244620016da640100000000026401000000009004565b620008517376b707604cbd862050b938739637b7bde1b7ad486901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200088f73568075f0000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620008cd73d44fb8de580d34f44789408cc9335c9a9ce0ce4d691ad0235eb930a0540000635db8d244620016da640100000000026401000000009004565b6200090a727801b12345b6f10b69263cd6d4fc50b58c8d80696d688e69e3a9742b0000635db8d244620016da640100000000026401000000009004565b62000948735d66a150000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b62000987731d258680000000000000000000000000000000006a020057f2c74d5a91080000635db8d244620016da640100000000026401000000009004565b620009c57330e311c00000000000000000000000000000000069355d7ddc4d956e6c0000635db8d244620016da640100000000026401000000009004565b62000a0373428ab4b019ee3a9b9863b2b4bf1885ce6dff9a736902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62000a41738081f20000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b62000a7f735305c8071b604da7fb45059e7ebfa1d674ddfc3569038780827d32a3ab0000635db8d244620016da640100000000026401000000009004565b62000abd7335007170000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000afc733484c850000000000000000000000000000000006a0422ca8b0a00a42500000063608a9f84620016da640100000000026401000000009004565b62000b3a73ffd9b871df6e93803c0877e98fc1722b39c00d786902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62000b787396a5eb172efdf262ed6beaaf0e20c6af71831fc96934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000bb6732dd2e140000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000bf473656e5569bef7781bf0db199d32027766053501ff69438ec266600555e00000635db8d244620016da640100000000026401000000009004565b62000c32731d53db7000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b62000c7073b5b6d8885fbf28f843cc7886de242b811d6952056901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000cae734549ef8e287b94f1c0a8b88f55ed8707d74d843c6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000cec73dacd80d8e1d4f117515caa477ee7599cdfc766196902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62000d2a731f603e000000000000000000000000000000000069d3c21bcecceda1000000635db8d244620016da640100000000026401000000009004565b62000d68735548f000000000000000000000000000000000006902ab1310b5b095920000635db8d244620016da640100000000026401000000009004565b62000da6735bb9ba00000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000de4736dd10e41a7a84fe23ab35fefa2f46c9895f87a2d693c8c4bde2deef1680000635db8d244620016da640100000000026401000000009004565b62000e227316337db0000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62000e6073b999004b49c6b907d4278067da5c85195dcd7fc769081e0dd1d85030b50000635db8d244620016da640100000000026401000000009004565b62000e9e733dbb2290000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000edc73559da540000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000f1a733a9d83766c03c465851a38daa364ef7deccd1ece6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62000f5873b61c11b6e42d459efaee8995c44db08507e468e169477d5529f68a63000000635db8d244620016da640100000000026401000000009004565b62000f9673e803d7955a911106cb8fd79049a2374ff059000369038780827d32a3ab0000635db8d244620016da640100000000026401000000009004565b62000fd473509b057000000000000000000000000000000000691aaebeee26cab7360000635db8d244620016da640100000000026401000000009004565b620010127383980db85394d7c1610f37a90be744432368bad4692393a931b168245d0000635db8d244620016da640100000000026401000000009004565b6200105073db6cc57168c07b83a00f1f8871538446068824fc691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b6200108e735035fba0000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b620010cc73d96c8300000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b6200110b7331178cd0000000000000000000000000000000006a042b4dd5360faca070000063608a9f84620016da640100000000026401000000009004565b62001149732f531158e2305ed4fb4144a2f4085e3d96e1982e6934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b62001188735d5da310000000000000000000000000000000006a017293b0a9e69fd9c00000635db8d244620016da640100000000026401000000009004565b620011c67318b9347000000000000000000000000000000000696abafbb89b2adcd80000635d408564620016da640100000000026401000000009004565b62001203722d4606b65c033769968bcdc63881b90b0853f5692648770b742bb90b0000635db8d244620016da640100000000026401000000009004565b62001241731faa8d10000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b6200127f7333445720000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620012bd730d1d4e623d10f9fba5db95830f7d3839406c6af26901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b620012fa72c2f65230815d30eaa1a4d057bcf0b72fe3cc4e6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62001338739c3a5ec7bd63ecac1b92abe4b01b12ffd50bf3f26969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620013767345635660000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620013b2739be61e41490f5227080fa1adbe3ec0a973d59732678ac7230489e80000635cc83884620016da640100000000026401000000009004565b620013f073572f91b0000000000000000000000000000000006934f086f3b33b68400000635db8d244620016da640100000000026401000000009004565b6200142e7339350e4a75d1e50a5072950325328884c11c12326901c761ff4c24e9210000635db8d244620016da640100000000026401000000009004565b6200146c733c96a530000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620014aa730ad7ba4af33b485e6f2505c417554631a3e5643f6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620014e8735eea7250000000000000000000000000000000006901c3c02f7b2019f50000635db8d244620016da640100000000026401000000009004565b62001526733ab790e0000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b62001564734cf84620000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620015a2735425f6f0000000000000000000000000000000006969e10de76676d0800000635d408564620016da640100000000026401000000009004565b620015e0736d516767e4068fc331bdb331fba7578bdb07a68c69234b04ae4f23156b0000635db8d244620016da640100000000026401000000009004565b6200161e73106a8ff0000000000000000000000000000000006902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b6200165c73102fb9400000000000000000000000000000000069038780827d32a3ab0000635db8d244620016da640100000000026401000000009004565b6200169a737ed62cf71d519d3bf293ef90829508f92f4ccccb6902a5a058fc295ed00000635db8d244620016da640100000000026401000000009004565b620016d873199a8c5000000000000000000000000000000000691a784379d99db4200000635db8d244620016da640100000000026401000000009004565b565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090506000816000015414801562001735575060008160010154145b6200178c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602981526020018062001b326029913960400191505060405180910390fd5b8260016000828254019250508190555082816000018190555081816001018190555050505050565b61034680620017c46000396000f3fe608060405234801561001057600080fd5b5060043610610069576000357c01000000000000000000000000000000000000000000000000000000009004806318a5bbdc1461006e578063192e7a7b146100cd57806330f0dbe0146101115780639976e12f1461012f575b600080fd5b6100b06004803603602081101561008457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061014d565b604051808381526020018281526020019250505060405180910390f35b61010f600480360360208110156100e357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610171565b005b610119610305565b6040518082815260200191505060405180910390f35b610137610314565b6040518082815260200191505060405180910390f35b60006020528060005260406000206000915090508060000154908060010154905082565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209050600081600001541161022d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f417661696c61626c6520616d6f756e742069732030000000000000000000000081525060200191505060405180910390fd5b806001015442116102a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f486f6c64696e6720706572696f64206973206e6f74206f76657200000000000081525060200191505060405180910390fd5b600081600001549050600082600001819055508273ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156102ff573d6000803e3d6000fd5b50505050565b6a21bd8334ca74c3834c000081565b6001548156fea165627a7a7230582078bf501dd1d053c49557b82491b940e47ebf94b95608375e22da53fcc7120a1a002954617267657420616d6f756e742073686f756c6420657175616c2061637475616c20616d6f756e74486f6c64696e6720666f72207468697320616464726573732077617320616c7265616479207365742e42616c616e63652073686f756c6420657175616c2074617267657420616d6f756e742e" + }, + "0x1204700000000000000000000000000000000006": { + "constructor": "0x6080604052670de0b6b3a76400006003553480156200001d57600080fd5b5060405160208062003e58833981018060405260208110156200003f57600080fd5b8101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a362000126816200012d640100000000026401000000009004565b506200028f565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b613bb9806200029f6000396000f3fe6080604052600436106101d4576000357c0100000000000000000000000000000000000000000000000000000000900480639269881411610109578063df57b742116100a7578063ef5454d611610081578063ef5454d614610d59578063f25eb5c114610e17578063f2fde38b14610e2e578063f6d339e414610e7f576101d4565b8063df57b74214610b62578063e30bd74014610bdd578063eadf976014610ca7576101d4565b8063ac72c120116100e3578063ac72c120146109c5578063c3a3582514610a18578063ddca3f4314610abc578063deb931a214610ae7576101d4565b806392698814146108855780639890220b146108d8578063ac4e73f914610907576101d4565b806369fe0e2d1161017657806379ce9fac1161015057806379ce9fac146106e85780638da5cb5b1461075b5780638f32d59b146107b257806390b97fc1146107e1576101d4565b806369fe0e2d146105b45780636a1acc3f14610607578063715018a6146106d1576101d4565b80633f3935d1116101b25780633f3935d1146103ad578063432ced041461044b5780634f39ca59146104915780636795dbcd146104e4576101d4565b806306b2ff47146101d957806319362a2814610242578063267b6922146102f4575b600080fd5b3480156101e557600080fd5b50610228600480360360208110156101fc57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610f47565b604051808215151515815260200191505060405180910390f35b34801561024e57600080fd5b506102da6004803603606081101561026557600080fd5b81019080803590602001909291908035906020019064010000000081111561028c57600080fd5b82018360208201111561029e57600080fd5b803590602001918460018302840111640100000000831117156102c057600080fd5b909192939192939080359060200190929190505050610fa7565b604051808215151515815260200191505060405180910390f35b34801561030057600080fd5b5061032d6004803603602081101561031757600080fd5b81019080803590602001909291905050506111ff565b604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182151515158152602001935050505060405180910390f35b3480156103b957600080fd5b50610431600480360360208110156103d057600080fd5b81019080803590602001906401000000008111156103ed57600080fd5b8201836020820111156103ff57600080fd5b8035906020019184600183028401116401000000008311171561042157600080fd5b9091929391929390505050611276565b604051808215151515815260200191505060405180910390f35b6104776004803603602081101561046157600080fd5b8101908080359060200190929190505050611553565b604051808215151515815260200191505060405180910390f35b34801561049d57600080fd5b506104ca600480360360208110156104b457600080fd5b810190808035906020019092919050505061185c565b604051808215151515815260200191505060405180910390f35b3480156104f057600080fd5b506105726004803603604081101561050757600080fd5b81019080803590602001909291908035906020019064010000000081111561052e57600080fd5b82018360208201111561054057600080fd5b8035906020019184600183028401116401000000008311171561056257600080fd5b9091929391929390505050611acc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156105c057600080fd5b506105ed600480360360208110156105d757600080fd5b8101908080359060200190929190505050611bb0565b604051808215151515815260200191505060405180910390f35b34801561061357600080fd5b506106566004803603602081101561062a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c73565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561069657808201518184015260208101905061067b565b50505050905090810190601f1680156106c35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156106dd57600080fd5b506106e6611d23565b005b3480156106f457600080fd5b506107416004803603604081101561070b57600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611e5c565b604051808215151515815260200191505060405180910390f35b34801561076757600080fd5b5061077061208a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156107be57600080fd5b506107c76120b3565b604051808215151515815260200191505060405180910390f35b3480156107ed57600080fd5b5061086f6004803603604081101561080457600080fd5b81019080803590602001909291908035906020019064010000000081111561082b57600080fd5b82018360208201111561083d57600080fd5b8035906020019184600183028401116401000000008311171561085f57600080fd5b909192939192939050505061210a565b6040518082815260200191505060405180910390f35b34801561089157600080fd5b506108be600480360360208110156108a857600080fd5b81019080803590602001909291905050506121ea565b604051808215151515815260200191505060405180910390f35b3480156108e457600080fd5b506108ed6122f3565b604051808215151515815260200191505060405180910390f35b34801561091357600080fd5b506109ab6004803603604081101561092a57600080fd5b810190808035906020019064010000000081111561094757600080fd5b82018360208201111561095957600080fd5b8035906020019184600183028401116401000000008311171561097b57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612422565b604051808215151515815260200191505060405180910390f35b3480156109d157600080fd5b506109fe600480360360208110156109e857600080fd5b8101908080359060200190929190505050612982565b604051808215151515815260200191505060405180910390f35b348015610a2457600080fd5b50610aa660048036036040811015610a3b57600080fd5b810190808035906020019092919080359060200190640100000000811115610a6257600080fd5b820183602082011115610a7457600080fd5b80359060200191846001830284011164010000000083111715610a9657600080fd5b9091929391929390505050612a8b565b6040518082815260200191505060405180910390f35b348015610ac857600080fd5b50610ad1612b6f565b6040518082815260200191505060405180910390f35b348015610af357600080fd5b50610b2060048036036020811015610b0a57600080fd5b8101908080359060200190929190505050612b75565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b348015610b6e57600080fd5b50610b9b60048036036020811015610b8557600080fd5b8101908080359060200190929190505050612c4e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b348015610be957600080fd5b50610c2c60048036036020811015610c0057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612d27565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610c6c578082015181840152602081019050610c51565b50505050905090810190601f168015610c995780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b348015610cb357600080fd5b50610d3f60048036036060811015610cca57600080fd5b810190808035906020019092919080359060200190640100000000811115610cf157600080fd5b820183602082011115610d0357600080fd5b80359060200191846001830284011164010000000083111715610d2557600080fd5b909192939192939080359060200190929190505050612e08565b604051808215151515815260200191505060405180910390f35b348015610d6557600080fd5b50610dfd60048036036040811015610d7c57600080fd5b8101908080359060200190640100000000811115610d9957600080fd5b820183602082011115610dab57600080fd5b80359060200191846001830284011164010000000083111715610dcd57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613063565b604051808215151515815260200191505060405180910390f35b348015610e2357600080fd5b50610e2c613297565b005b348015610e3a57600080fd5b50610e7d60048036036020811015610e5157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613648565b005b348015610e8b57600080fd5b50610f2d60048036036060811015610ea257600080fd5b810190808035906020019092919080359060200190640100000000811115610ec957600080fd5b820183602082011115610edb57600080fd5b80359060200191846001830284011164010000000083111715610efd57600080fd5b9091929391929390803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506136ce565b604051808215151515815260200191505060405180910390f35b600080600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002080546001816001161561010002031660029004905014159050919050565b6000846001600082815260200190815260200160002060010160149054906101000a900460ff1615611041576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611119576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b60016020528060005260406000206000915090508060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060010160149054906101000a900460ff16905083565b600082828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff161561135b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b83838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050503373ffffffffffffffffffffffffffffffffffffffff16600160008380519060200120815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161461147e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4572726f723a204f6e6c79207768656e2070726f706f7365640000000000000081525060200191505060405180910390fd5b8484600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002091906114cc929190613aa0565b503373ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019250505092915050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff16156115ed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b82600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146116c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f4572726f723a204f6e6c79207768656e20756e7265736572766564000000000081525060200191505060405180910390fd5b60035434101561173e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260198152602001807f4572726f723a206f6e6c79207768656e2066656520706169640000000000000081525060200191505060405180910390fd5b6117466120b3565b6117b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b336001600086815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff16847f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405160405180910390a3600192505050919050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff16156118f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b823373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146119ce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b600260006001600087815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000611a4f9190613b20565b600180600086815260200190815260200160002060010160146101000a81548160ff0219169083151502179055503373ffffffffffffffffffffffffffffffffffffffff16847fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405160405180910390a3600192505050919050565b6000836001600082815260200190815260200160002060010160149054906101000a900460ff1615611b66576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600160008681526020019081526020016000206002018484604051808383808284378083019250505092505050908152602001604051809103902054600190049150509392505050565b6000611bba6120b3565b611c2c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b816003819055507f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c3826040518082815260200191505060405180910390a160019050919050565b60026020528060005260406000206000915090508054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611d1b5780601f10611cf057610100808354040283529160200191611d1b565b820191906000526020600020905b815481529060010190602001808311611cfe57829003601f168201915b505050505081565b611d2b6120b3565b611d9d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000826001600082815260200190815260200160002060010160149054906101000a900460ff1615611ef6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b833373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611fce576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b836001600087815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16867f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405160405180910390a460019250505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000836001600082815260200190815260200160002060010160149054906101000a900460ff16156121a4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600086815260200190815260200160002060020184846040518083838082843780830192505050925050509081526020016040518091039020549150509392505050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612284576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b60006122fd6120b3565b61236f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b7fdef931299fe61d176f949118058530c1f3f539dcb6950b4e372c9b835c33ca073073ffffffffffffffffffffffffffffffffffffffff16316040518082815260200191505060405180910390a13373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f1935050505015801561241a573d6000803e3d6000fd5b506001905090565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff1615612507576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b848460405180838380828437808301925050509250505060405180910390203373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146125fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b6000868660405180838380828437808301925050509250505060405180910390209050600073ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415801561276e575080600260006001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051808280546001816001161561010002031660029004801561275f5780601f1061273d57610100808354040283529182019161275f565b820191906000526020600020905b81548152906001019060200180831161274b575b50509150506040518091039020145b156128a557600260006001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006127f49190613b20565b6001600082815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a25b846001600083815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508473ffffffffffffffffffffffffffffffffffffffff167f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df888860405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a2600193505050509392505050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612a1c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166001600085815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415915050919050565b6000836001600082815260200190815260200160002060010160149054906101000a900460ff1615612b25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b600160008681526020019081526020016000206002018484604051808383808284378083019250505092505050908152602001604051809103902054600190049150509392505050565b60035481565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612c0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b6000816001600082815260200190815260200160002060010160149054906101000a900460ff1615612ce8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b6001600084815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16915050919050565b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612dfc5780601f10612dd157610100808354040283529160200191612dfc565b820191906000526020600020905b815481529060010190602001808311612ddf57829003601f168201915b50505050509050919050565b6000846001600082815260200190815260200160002060010160149054906101000a900460ff1615612ea2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614612f7a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b83600102600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600083838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff1615613148576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b6131506120b3565b6131c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b8484600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209190613210929190613aa0565b508273ffffffffffffffffffffffffffffffffffffffff167f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c91920868660405180806020018281038252848482818152602001925080828437600081840152601f19601f820116905080830192505050935050505060405180910390a260019150509392505050565b600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561336a5780601f1061333f5761010080835404028352916020019161336a565b820191906000526020600020905b81548152906001019060200180831161334d57829003601f168201915b5050505050600160008280519060200120815260200190815260200160002060010160149054906101000a900460ff161561340d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f4572726f723a204f6e6c79207768656e20656e7472790000000000000000000081525060200191505060405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff167f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051808060200182810382528381815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561350d5780601f106134e25761010080835404028352916020019161350d565b820191906000526020600020905b8154815290600101906020018083116134f057829003601f168201915b50509250505060405180910390a260016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156135b95780601f106135975761010080835404028352918201916135b9565b820191906000526020600020905b8154815290600101906020018083116135a5575b50509150506040518091039020815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006136459190613b20565b50565b6136506120b3565b6136c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260138152602001807f53656e646572206973206e6f74206f776e65720000000000000000000000000081525060200191505060405180910390fd5b6136cb8161393f565b50565b6000846001600082815260200190815260200160002060010160149054906101000a900460ff1615613768576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4572726f723a204f6e6c79207768656e20656e7472792072617700000000000081525060200191505060405180910390fd5b853373ffffffffffffffffffffffffffffffffffffffff166001600083815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614613840576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260148152602001807f4572726f723a204f6e6c79206f776e6572206f6600000000000000000000000081525060200191505060405180910390fd5b8373ffffffffffffffffffffffffffffffffffffffff16600102600160008981526020019081526020016000206002018787604051808383808284378083019250505092505050908152602001604051809103902081905550867fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea878789896040518080602001806020018381038352878782818152602001925080828437600081840152601f19601f8201169050808301925050508381038252858582818152602001925080828437600081840152601f19601f820116905080830192505050965050505050505060405180910390a2600192505050949350505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156139e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4e6577206f776e657220616464726573732063616e6e6f74206265203078300081525060200191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10613ae157803560ff1916838001178555613b0f565b82800160010185558215613b0f579182015b82811115613b0e578235825591602001919060010190613af3565b5b509050613b1c9190613b68565b5090565b50805460018160011615610100020316600290046000825580601f10613b465750613b65565b601f016020900490600052602060002090810190613b649190613b68565b5b50565b613b8a91905b80821115613b86576000816000905550600101613b6e565b5090565b9056fea165627a7a723058201b0da92162b975c882f9c56423cbea6ec9afb46d8dccc92abe125e91c84d52de00290000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000007": { + "constructor": "0x608060405234801561001057600080fd5b50604051604080610a5f833981018060405261002f919081019061028d565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101448161014b640100000000026401000000009004565b505061036c565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156101bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101b290610309565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000610285825161033a565b905092915050565b600080604083850312156102a057600080fd5b60006102ae85828601610279565b92505060206102bf85828601610279565b9150509250929050565b60006102d6601f83610329565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b60006020820190508181036000830152610322816102c9565b9050919050565b600082825260208201905092915050565b60006103458261034c565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6106e48061037b6000396000f3fe608060405234801561001057600080fd5b506004361061007f576000357c0100000000000000000000000000000000000000000000000000000000900480636ddc4a0714610084578063715018a6146100a25780638da5cb5b146100ac5780638f32d59b146100ca578063f2fde38b146100e8578063fe64d6ff14610104575b600080fd5b61008c610120565b60405161009991906105b3565b60405180910390f35b6100aa610146565b005b6100b461024c565b6040516100c191906105b3565b60405180910390f35b6100d2610275565b6040516100df91906105ce565b60405180910390f35b61010260048036036100fd91908101906104ec565b6102cc565b005b61011e600480360361011991908101906104ec565b61031f565b005b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b61014e610275565b61018d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161018490610609565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6102d4610275565b610313576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161030a90610609565b60405180910390fd5b61031c816103aa565b50565b610327610275565b610366576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035d90610609565b60405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561041a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610411906105e9565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006104e48235610678565b905092915050565b6000602082840312156104fe57600080fd5b600061050c848285016104d8565b91505092915050565b61051e8161063a565b82525050565b61052d8161064c565b82525050565b6000610540601f83610629565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b6000610580601383610629565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b60006020820190506105c86000830184610515565b92915050565b60006020820190506105e36000830184610524565b92915050565b6000602082019050818103600083015261060281610533565b9050919050565b6000602082019050818103600083015261062281610573565b9050919050565b600082825260208201905092915050565b600061064582610658565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006106838261068a565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff8216905091905056fea265627a7a723058207424d262effee3c4b2cfbe03927ef8931223cda85f89bf647b88b2f7e64cc5516c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000090000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000008": { + "constructor": "0x60806040523480156200001157600080fd5b5060405160408062002b838339810180604052620000339190810190620002af565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506200014a8162000152640100000000026401000000009004565b5050620003ad565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001c5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001bc9062000332565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600062000291825162000365565b905092915050565b6000620002a7825162000379565b905092915050565b60008060408385031215620002c357600080fd5b6000620002d38582860162000299565b9250506020620002e68582860162000283565b9150509250929050565b6000620002ff601f8362000354565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034d81620002f0565b9050919050565b600082825260208201905092915050565b600062000372826200038d565b9050919050565b6000620003868262000365565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6127c680620003bd6000396000f3fe608060405234801561001057600080fd5b506004361061011d576000357c010000000000000000000000000000000000000000000000000000000090048063954029c9116100b4578063eab7ad9211610083578063eab7ad92146102e4578063ee7ee0fd14610300578063f2fde38b14610330578063fbd74f841461034c5761011d565b8063954029c91461024c578063a999809f14610268578063b71da0d114610298578063da5393d5146102c85761011d565b806370a05b18116100f057806370a05b18146101d6578063715018a6146102065780638da5cb5b146102105780638f32d59b1461022e5761011d565b80631bab58f5146101225780632c5653e21461015257806332d91c0e146101705780636a564261146101a0575b600080fd5b61013c60048036036101379190810190611f97565b61037c565b604051610149919061260c565b60405180910390f35b61015a6107b5565b604051610167919061254f565b60405180910390f35b61018a60048036036101859190810190611f97565b6107db565b604051610197919061256a565b60405180910390f35b6101ba60048036036101b59190810190611f97565b6109e8565b6040516101cd97969594939291906124c4565b60405180910390f35b6101f060048036036101eb9190810190611f97565b610c97565b6040516101fd91906124a2565b60405180910390f35b61020e610ea4565b005b610218610faa565b604051610225919061246c565b60405180910390f35b610236610fd3565b6040516102439190612487565b60405180910390f35b61026660048036036102619190810190611f97565b61102a565b005b610282600480360361027d9190810190611f97565b61119d565b60405161028f919061256a565b60405180910390f35b6102b260048036036102ad9190810190611f97565b6113aa565b6040516102bf9190612487565b60405180910390f35b6102e260048036036102dd9190810190611f97565b61143a565b005b6102fe60048036036102f99190810190611fe9565b611535565b005b61031a60048036036103159190810190611f97565b611853565b6040516103279190612487565b60405180910390f35b61034a60048036036103459190810190611f97565b6119d5565b005b61036660048036036103619190810190611f97565b611a28565b60405161037391906124a2565b60405180910390f35b610384611d63565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561040857600080fd5b505afa15801561041c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506104409190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104a49061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060e0016040529081600082018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105905780601f1061056557610100808354040283529160200191610590565b820191906000526020600020905b81548152906001019060200180831161057357829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106325780601f1061060757610100808354040283529160200191610632565b820191906000526020600020905b81548152906001019060200180831161061557829003601f168201915b50505050508152602001600282018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156106d45780601f106106a9576101008083540402835291602001916106d4565b820191906000526020600020905b8154815290600101906020018083116106b757829003601f168201915b50505050508152602001600382018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107765780601f1061074b57610100808354040283529160200191610776565b820191906000526020600020905b81548152906001019060200180831161075957829003601f168201915b505050505081526020016004820160009054906101000a900460ff16151515158152602001600582015481526020016006820154815250509050919050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561086157600080fd5b505afa158015610875573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506108999190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610906576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108fd9061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206003018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156109dc5780601f106109b1576101008083540402835291602001916109dc565b820191906000526020600020905b8154815290600101906020018083116109bf57829003601f168201915b50505050509050919050565b6001602052806000526040600020600091509050806000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a945780601f10610a6957610100808354040283529160200191610a94565b820191906000526020600020905b815481529060010190602001808311610a7757829003601f168201915b505050505090806001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b325780601f10610b0757610100808354040283529160200191610b32565b820191906000526020600020905b815481529060010190602001808311610b1557829003601f168201915b505050505090806002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bd05780601f10610ba557610100808354040283529160200191610bd0565b820191906000526020600020905b815481529060010190602001808311610bb357829003601f168201915b505050505090806003018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c6e5780601f10610c4357610100808354040283529160200191610c6e565b820191906000526020600020905b815481529060010190602001808311610c5157829003601f168201915b5050505050908060040160009054906101000a900460ff16908060050154908060060154905087565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b158015610d1d57600080fd5b505afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d559190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610dc2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610db99061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610e985780601f10610e6d57610100808354040283529160200191610e98565b820191906000526020600020905b815481529060010190602001808311610e7b57829003601f168201915b50505050509050919050565b610eac610fd3565b610eeb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ee2906125ec565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156110ae57600080fd5b505afa1580156110c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110e69190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611153576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161114a9061258c565b60405180910390fd5b43600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206006018190555050565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561122357600080fd5b505afa158015611237573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061125b9190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146112c8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016112bf9061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561139e5780601f106113735761010080835404028352916020019161139e565b820191906000526020600020905b81548152906001019060200180831161138157829003601f168201915b50505050509050919050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060060154600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060050154109050919050565b611442610fd3565b611481576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611478906125ec565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156114f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016114e8906125ac565b60405180910390fd5b80600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156115b957600080fd5b505afa1580156115cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115f19190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461165e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016116559061258c565b60405180910390fd5b8888600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000191906116af929190611da2565b508686600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001019190611701929190611e22565b508484600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002019190611753929190611da2565b508282600160008d73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060030191906117a5929190611e22565b5080600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160006101000a81548160ff02191690831515021790555043600160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206005018190555050505050505050505050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156118d957600080fd5b505afa1580156118ed573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506119119190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461197e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119759061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060040160009054906101000a900460ff169050919050565b6119dd610fd3565b611a1c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611a13906125ec565b60405180910390fd5b611a2581611c35565b50565b6060600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16636ddc4a076040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b158015611aae57600080fd5b505afa158015611ac2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ae69190810190611fc0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611b53576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611b4a9061258c565b60405180910390fd5b600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611c295780601f10611bfe57610100808354040283529160200191611c29565b820191906000526020600020905b815481529060010190602001808311611c0c57829003601f168201915b50505050509050919050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415611ca5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611c9c906125cc565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611de357803560ff1916838001178555611e11565b82800160010185558215611e11579182015b82811115611e10578235825591602001919060010190611df5565b5b509050611e1e9190611ea2565b5090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611e6357803560ff1916838001178555611e91565b82800160010185558215611e91579182015b82811115611e90578235825591602001919060010190611e75565b5b509050611e9e9190611ea2565b5090565b611ec491905b80821115611ec0576000816000905550600101611ea8565b5090565b90565b6000611ed382356126e6565b905092915050565b6000611ee782516126e6565b905092915050565b6000611efb82356126f8565b905092915050565b60008083601f840112611f1557600080fd5b8235905067ffffffffffffffff811115611f2e57600080fd5b602083019150836001820283011115611f4657600080fd5b9250929050565b60008083601f840112611f5f57600080fd5b8235905067ffffffffffffffff811115611f7857600080fd5b602083019150836001820283011115611f9057600080fd5b9250929050565b600060208284031215611fa957600080fd5b6000611fb784828501611ec7565b91505092915050565b600060208284031215611fd257600080fd5b6000611fe084828501611edb565b91505092915050565b60008060008060008060008060008060c08b8d03121561200857600080fd5b60006120168d828e01611ec7565b9a505060208b013567ffffffffffffffff81111561203357600080fd5b61203f8d828e01611f03565b995099505060408b013567ffffffffffffffff81111561205e57600080fd5b61206a8d828e01611f4d565b975097505060608b013567ffffffffffffffff81111561208957600080fd5b6120958d828e01611f03565b955095505060808b013567ffffffffffffffff8111156120b457600080fd5b6120c08d828e01611f4d565b935093505060a06120d38d828e01611eef565b9150509295989b9194979a5092959850565b6120ee8161269e565b82525050565b6120fd816126b0565b82525050565b61210c816126b0565b82525050565b600061211d82612639565b612127818561266b565b9350612137818560208601612748565b6121408161277b565b840191505092915050565b60006121568261262e565b612160818561265a565b9350612170818560208601612748565b6121798161277b565b840191505092915050565b600061218f8261262e565b612199818561266b565b93506121a9818560208601612748565b6121b28161277b565b840191505092915050565b6121c681612724565b82525050565b60006121d78261264f565b6121e1818561268d565b93506121f1818560208601612748565b6121fa8161277b565b840191505092915050565b600061221082612644565b61221a818561267c565b935061222a818560208601612748565b6122338161277b565b840191505092915050565b600061224982612644565b612253818561268d565b9350612263818560208601612748565b61226c8161277b565b840191505092915050565b600061228460138361268d565b91507f4572726f723a206f6e6c794c6f676963204462000000000000000000000000006000830152602082019050919050565b60006122c460298361268d565b91507f4572726f723a206e65774c6f6f6b5570206973206e6f7420616c6c6f7765642060008301527f746f2062652030783000000000000000000000000000000000000000000000006020830152604082019050919050565b600061232a601f8361268d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600061236a60138361268d565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600060e08301600083015184820360008601526123ba828261214b565b915050602083015184820360208601526123d48282612205565b915050604083015184820360408601526123ee828261214b565b915050606083015184820360608601526124088282612205565b915050608083015161241d60808601826120f4565b5060a083015161243060a086018261244e565b5060c083015161244360c086018261244e565b508091505092915050565b612457816126dc565b82525050565b612466816126dc565b82525050565b600060208201905061248160008301846120e5565b92915050565b600060208201905061249c6000830184612103565b92915050565b600060208201905081810360008301526124bc8184612112565b905092915050565b600060e08201905081810360008301526124de818a612184565b905081810360208301526124f2818961223e565b905081810360408301526125068188612184565b9050818103606083015261251a818761223e565b90506125296080830186612103565b61253660a083018561245d565b61254360c083018461245d565b98975050505050505050565b600060208201905061256460008301846121bd565b92915050565b6000602082019050818103600083015261258481846121cc565b905092915050565b600060208201905081810360008301526125a581612277565b9050919050565b600060208201905081810360008301526125c5816122b7565b9050919050565b600060208201905081810360008301526125e58161231d565b9050919050565b600060208201905081810360008301526126058161235d565b9050919050565b60006020820190508181036000830152612626818461239d565b905092915050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b60006126a9826126bc565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006126f182612704565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061272f82612736565b9050919050565b6000612741826126bc565b9050919050565b60005b8381101561276657808201518184015260208101905061274b565b83811115612775576000848401525b50505050565b6000601f19601f830116905091905056fea265627a7a723058207e60ec05ac38073f6d98c9cff2cce094d00db0238d6d4b0c183253539a43fe2b6c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000070000000000000000000000001204700000000000000000000000000000000005" + }, + "0x1204700000000000000000000000000000000009": { + "constructor": "0x60806040523480156200001157600080fd5b50604051604080620021cc8339810180604052620000339190810190620002af565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a381600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506200014a8162000152640100000000026401000000009004565b5050620003ad565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415620001c5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001bc9062000332565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600062000291825162000365565b905092915050565b6000620002a7825162000379565b905092915050565b60008060408385031215620002c357600080fd5b6000620002d38582860162000299565b9250506020620002e68582860162000283565b9150509250929050565b6000620002ff601f8362000354565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b600060208201905081810360008301526200034d81620002f0565b9050919050565b600082825260208201905092915050565b600062000372826200038d565b9050919050565b6000620003868262000365565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b611e0f80620003bd6000396000f3fe608060405234801561001057600080fd5b50600436106100b0576000357c0100000000000000000000000000000000000000000000000000000000900480638da5cb5b116100835780638da5cb5b146101155780638f32d59b14610133578063b71da0d114610151578063ec26261114610181578063f2fde38b1461019f576100b0565b806312127ed7146100b55780633f67c333146100d1578063715018a6146100db57806389c3ce1e146100e5575b600080fd5b6100cf60048036036100ca9190810190611450565b6101bb565b005b6100d96109dd565b005b6100e3610b98565b005b6100ff60048036036100fa9190810190611427565b610c9e565b60405161010c9190611b14565b60405180910390f35b61011d610d85565b60405161012a91906119ab565b60405180910390f35b61013b610dae565b6040516101489190611a5e565b60405180910390f35b61016b60048036036101669190810190611427565b610e05565b6040516101789190611a5e565b60405180910390f35b610189610ed5565b6040516101969190611a79565b60405180910390f35b6101b960048036036101b49190810190611427565b610efb565b005b6101c3610dae565b610202576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101f990611ab4565b60405180910390fd5b6002856040516102129190611994565b602060405180830381855afa15801561022f573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506102529190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84896040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016102cb91906119ab565b60006040518083038186803b1580156102e357600080fd5b505afa1580156102f7573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610320919081019061158b565b60405161032d9190611994565b602060405180830381855afa15801561034a573d6000803e3d6000fd5b5050506040513d601f19601f8201168201806040525061036d9190810190611562565b1480156104e257506002846040516103859190611994565b602060405180830381855afa1580156103a2573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506103c59190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a999809f896040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161043e91906119ab565b60006040518083038186803b15801561045657600080fd5b505afa15801561046a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061049391908101906115cc565b6040516104a09190611994565b602060405180830381855afa1580156104bd573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506104e09190810190611562565b145b801561065657506002836040516104f99190611994565b602060405180830381855afa158015610516573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506105399190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166370a05b18896040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016105b291906119ab565b60006040518083038186803b1580156105ca57600080fd5b505afa1580156105de573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610607919081019061158b565b6040516106149190611994565b602060405180830381855afa158015610631573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506106549190810190611562565b145b80156107ca575060028260405161066d9190611994565b602060405180830381855afa15801561068a573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506106ad9190810190611562565b6002600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166332d91c0e896040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161072691906119ab565b60006040518083038186803b15801561073e57600080fd5b505afa158015610752573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525061077b91908101906115cc565b6040516107889190611994565b602060405180830381855afa1580156107a5573d6000803e3d6000fd5b5050506040513d601f19601f820116820180604052506107c89190810190611562565b145b801561089f5750801515600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663ee7ee0fd886040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040161084b91906119ab565b60206040518083038186803b15801561086357600080fd5b505afa158015610877573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061089b9190810190611539565b1515145b156108df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d690611ad4565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663eab7ad928787878787876040518763ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610960969594939291906119e1565b600060405180830381600087803b15801561097a57600080fd5b505af115801561098e573d6000803e3d6000fd5b505050508573ffffffffffffffffffffffffffffffffffffffff167fc9e49a024d50440c73d2d12d0ae05064094dca76b46dc95e99ea6848eb8f27e960405160405180910390a2505050505050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbd74f84336040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610a5691906119c6565b60006040518083038186803b158015610a6e57600080fd5b505afa158015610a82573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610aab919081019061158b565b511415610aed576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ae490611af4565b60405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663954029c9336040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610b6491906119c6565b600060405180830381600087803b158015610b7e57600080fd5b505af1158015610b92573d6000803e3d6000fd5b50505050565b610ba0610dae565b610bdf576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bd690611ab4565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b610ca661107c565b610cae61107c565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631bab58f5846040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610d2591906119ab565b60006040518083038186803b158015610d3d57600080fd5b505afa158015610d51573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250610d7a919081019061160d565b905080915050919050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614905090565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b71da0d1836040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401610e7e91906119ab565b60206040518083038186803b158015610e9657600080fd5b505afa158015610eaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ece9190810190611539565b9050919050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610f03610dae565b610f42576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f3990611ab4565b60405180910390fd5b610f4b81610f4e565b50565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610fbe576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fb590611a94565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6040518060e001604052806060815260200160608152602001606081526020016060815260200160001515815260200160008152602001600081525090565b60006110c78235611cd6565b905092915050565b60006110db8235611ce8565b905092915050565b60006110ef8251611ce8565b905092915050565b60006111038251611cf4565b905092915050565b600082601f83011261111c57600080fd5b815161112f61112a82611b63565b611b36565b9150808252602083016020830185838301111561114b57600080fd5b611156838284611d91565b50505092915050565b600082601f83011261117057600080fd5b813561118361117e82611b8f565b611b36565b9150808252602083016020830185838301111561119f57600080fd5b6111aa838284611d82565b50505092915050565b600082601f8301126111c457600080fd5b81516111d76111d282611b8f565b611b36565b915080825260208301602083018583830111156111f357600080fd5b6111fe838284611d91565b50505092915050565b600082601f83011261121857600080fd5b815161122b61122682611bbb565b611b36565b9150808252602083016020830185838301111561124757600080fd5b611252838284611d91565b50505092915050565b600082601f83011261126c57600080fd5b813561127f61127a82611be7565b611b36565b9150808252602083016020830185838301111561129b57600080fd5b6112a6838284611d82565b50505092915050565b600082601f8301126112c057600080fd5b81516112d36112ce82611be7565b611b36565b915080825260208301602083018583830111156112ef57600080fd5b6112fa838284611d91565b50505092915050565b600060e0828403121561131557600080fd5b61131f60e0611b36565b9050600082015167ffffffffffffffff81111561133b57600080fd5b6113478482850161110b565b600083015250602082015167ffffffffffffffff81111561136757600080fd5b61137384828501611207565b602083015250604082015167ffffffffffffffff81111561139357600080fd5b61139f8482850161110b565b604083015250606082015167ffffffffffffffff8111156113bf57600080fd5b6113cb84828501611207565b60608301525060806113df848285016110e3565b60808301525060a06113f384828501611413565b60a08301525060c061140784828501611413565b60c08301525092915050565b600061141f8251611d1e565b905092915050565b60006020828403121561143957600080fd5b6000611447848285016110bb565b91505092915050565b60008060008060008060c0878903121561146957600080fd5b600061147789828a016110bb565b965050602087013567ffffffffffffffff81111561149457600080fd5b6114a089828a0161115f565b955050604087013567ffffffffffffffff8111156114bd57600080fd5b6114c989828a0161125b565b945050606087013567ffffffffffffffff8111156114e657600080fd5b6114f289828a0161115f565b935050608087013567ffffffffffffffff81111561150f57600080fd5b61151b89828a0161125b565b92505060a061152c89828a016110cf565b9150509295509295509295565b60006020828403121561154b57600080fd5b6000611559848285016110e3565b91505092915050565b60006020828403121561157457600080fd5b6000611582848285016110f7565b91505092915050565b60006020828403121561159d57600080fd5b600082015167ffffffffffffffff8111156115b757600080fd5b6115c3848285016111b3565b91505092915050565b6000602082840312156115de57600080fd5b600082015167ffffffffffffffff8111156115f857600080fd5b611604848285016112af565b91505092915050565b60006020828403121561161f57600080fd5b600082015167ffffffffffffffff81111561163957600080fd5b61164584828501611303565b91505092915050565b61165781611d28565b82525050565b61166681611c8e565b82525050565b61167581611ca0565b82525050565b61168481611ca0565b82525050565b600061169582611c1e565b61169f8185611c50565b93506116af818560208601611d91565b6116b881611dc4565b840191505092915050565b60006116ce82611c1e565b6116d88185611c61565b93506116e8818560208601611d91565b80840191505092915050565b60006116ff82611c13565b6117098185611c3f565b9350611719818560208601611d91565b61172281611dc4565b840191505092915050565b61173681611d3a565b82525050565b600061174782611c34565b6117518185611c7d565b9350611761818560208601611d91565b61176a81611dc4565b840191505092915050565b600061178082611c29565b61178a8185611c6c565b935061179a818560208601611d91565b6117a381611dc4565b840191505092915050565b60006117bb601f83611c7d565b91507f4e6577206f776e657220616464726573732063616e6e6f7420626520307830006000830152602082019050919050565b60006117fb601383611c7d565b91507f53656e646572206973206e6f74206f776e6572000000000000000000000000006000830152602082019050919050565b600061183b602583611c7d565b91507f4572726f723a204e6f206368616e67657320696e20746865207061737365642060008301527f53746174650000000000000000000000000000000000000000000000000000006020830152604082019050919050565b60006118a1601f83611c7d565b91507f4572726f723a20596f7520617265206e6f7420612076616c696461746f7221006000830152602082019050919050565b600060e08301600083015184820360008601526118f182826116f4565b9150506020830151848203602086015261190b8282611775565b9150506040830151848203604086015261192582826116f4565b9150506060830151848203606086015261193f8282611775565b9150506080830151611954608086018261166c565b5060a083015161196760a0860182611985565b5060c083015161197a60c0860182611985565b508091505092915050565b61198e81611ccc565b82525050565b60006119a082846116c3565b915081905092915050565b60006020820190506119c0600083018461165d565b92915050565b60006020820190506119db600083018461164e565b92915050565b600060c0820190506119f6600083018961165d565b8181036020830152611a08818861168a565b90508181036040830152611a1c818761173c565b90508181036060830152611a30818661168a565b90508181036080830152611a44818561173c565b9050611a5360a083018461167b565b979650505050505050565b6000602082019050611a73600083018461167b565b92915050565b6000602082019050611a8e600083018461172d565b92915050565b60006020820190508181036000830152611aad816117ae565b9050919050565b60006020820190508181036000830152611acd816117ee565b9050919050565b60006020820190508181036000830152611aed8161182e565b9050919050565b60006020820190508181036000830152611b0d81611894565b9050919050565b60006020820190508181036000830152611b2e81846118d4565b905092915050565b6000604051905081810181811067ffffffffffffffff82111715611b5957600080fd5b8060405250919050565b600067ffffffffffffffff821115611b7a57600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611ba657600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611bd257600080fd5b601f19601f8301169050602081019050919050565b600067ffffffffffffffff821115611bfe57600080fd5b601f19601f8301169050602081019050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b6000611c9982611cac565b9050919050565b60008115159050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000611ce182611cfe565b9050919050565b60008115159050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000611d3382611d5e565b9050919050565b6000611d4582611d4c565b9050919050565b6000611d5782611cac565b9050919050565b6000611d6982611d70565b9050919050565b6000611d7b82611cac565b9050919050565b82818337600083830152505050565b60005b83811015611daf578082015181840152602081019050611d94565b83811115611dbe576000848401525b50505050565b6000601f19601f830116905091905056fea265627a7a723058204ed2d756516ea8df8dbc4fbf2bd76bb43e93ab01c8c274fc9169dfd0be6254d06c6578706572696d656e74616cf5003700000000000000000000000012047000000000000000000000000000000000080000000000000000000000001204700000000000000000000000000000000005" + } + }, + "nodes": [ + "enode://59c9250cb805409e84c9cd0038e97d8e5e4605b928663675869ebdfd4c251d80ccad76267a5eb2f4362ddceb5ec671f7595463adfc0a12e9f68dbf233072db41@54.70.158.106:30303", + "enode://e487ebacbdad3418905d2ed7f009fa5dbd17d73880854884acc604c0afc1a60a396aa90cb2741278c555a4e30ffc6ffc1c29e83840aa22009ec92fe53f81ec04@99.81.92.124:30303", + "enode://563f12602a117201b39ebeea108185abb15d9286830c074640c9fccbaaaabcc7fe2c95682cc43f95b95059f6d0dc4c9becbc1b2bd78e0c5ef5fddff07d85ba0e@54.201.62.74:30303", + "enode://5903b3acebdc4a34800f6923e5f3aec3ca7e5d1285bec4adb9f20ebb0f87a3bebdd748b1849ca1108a9f1e37ff9ced0b475292b8effc29e95d49ec438f244b02@3.121.165.10:30303", + "enode://8f8e35a6dcacfee946f46447b4703c84f4e485e478143997f86b1834e1b0bb78dab363d700dff3147442b9d3e2a1c521f79340c436eb7245a97c7fe385b89a5d@54.93.159.98:30303", + "enode://bd228aa03cf4a88491c81c5f3ab4a1437df3b463081cc93943c4d3ab37f1e4f8081c6995eca076f717d4fdf9a277c750bd0289477ac151f1e2b024747dcd1747@52.31.129.130:30303" + ] } diff --git a/ethcore/res/ethereum/xdai.json b/ethcore/res/ethereum/xdai.json index ac2a40e148..0c21462304 100644 --- a/ethcore/res/ethereum/xdai.json +++ b/ethcore/res/ethereum/xdai.json @@ -1,157 +1,3015 @@ { - "name": "xDai Chain", - "dataDir": "xdai", - "engine": { - "authorityRound": { - "params": { - "stepDuration": 5, - "blockReward": "0x0", - "maximumUncleCountTransition": 0, - "maximumUncleCount": 0, - "validators": { - "multi": { - "0": { - "list": ["0xcace5b3c29211740e595850e80478416ee77ca21"] - }, - "1300": { - "safeContract": "0x22e1229a2c5b95a60983b5577f745a603284f535" - } - } - }, - "blockRewardContractAddress": "0x867305d19606aadba405ce534e303d0e225f9556", - "blockRewardContractTransition": 1310 - } - } - }, - "params": { - "gasLimitBoundDivisor": "0x400", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID": "100", - "eip140Transition": "0x0", - "eip211Transition": "0x0", - "eip214Transition": "0x0", - "eip658Transition": "0x0", - "eip145Transition": 1604400, - "eip1014Transition": 1604400, - "eip1052Transition": 1604400, - "eip1283Transition": 1604400, - "eip1283DisableTransition": 2508800, - "registrar": "0x1ec97dc137f5168af053c24460a1200502e1a9d2" - }, - "genesis": { - "seal": { - "authorityRound": { - "step": "0x0", - "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - } - }, - "difficulty": "0x20000", - "gasLimit": "0x989680" - }, - "accounts": { - "0000000000000000000000000000000000000005": { - "builtin": { - "name": "modexp", - "activate_at": "0x0", - "pricing": { - "modexp": { - "divisor": 20 - } - } - } - }, - "0000000000000000000000000000000000000006": { - "builtin": { - "name": "alt_bn128_add", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", - "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 - } - } - } - }, - "0000000000000000000000000000000000000007": { - "builtin": { - "name": "alt_bn128_mul", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", - "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 - } - } - } - }, - "0000000000000000000000000000000000000008": { - "builtin": { - "name": "alt_bn128_pairing", - "activate_at": "0x0", - "eip1108_transition": "0x7fffffffffffff", - "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 - } - } - } - }, - "0x0000000000000000000000000000000000000001": { - "balance": "1", - "builtin": { - "name": "ecrecover", - "pricing": { - "linear": { - "base": 3000, - "word": 0 - } - } - } - }, - "0x0000000000000000000000000000000000000002": { - "balance": "1", - "builtin": { - "name": "sha256", - "pricing": { - "linear": { - "base": 60, - "word": 12 - } - } - } - }, - "0x0000000000000000000000000000000000000003": { - "balance": "1", - "builtin": { - "name": "ripemd160", - "pricing": { - "linear": { - "base": 600, - "word": 120 - } - } - } - }, - "0x0000000000000000000000000000000000000004": { - "balance": "1", - "builtin": { - "name": "identity", - "pricing": { - "linear": { - "base": 15, - "word": 3 - } - } - } - } - }, - "nodes": [ - "enode://66786c15390cb4fef3743571e12ec54ca343e7f119018136d68b670edd93604eedf74e5013dc5c2439f89e0e05593e29c409a97e155ea4165c6b832de131ef1e@3.214.113.185:30303" - ] + "name": "xDai Chain", + "dataDir": "xdai", + "engine": { + "authorityRound": { + "params": { + "stepDuration": 5, + "blockReward": "0x0", + "maximumUncleCountTransition": 0, + "maximumUncleCount": 0, + "validators": { + "multi": { + "0": { + "list": ["0xcace5b3c29211740e595850e80478416ee77ca21"] + }, + "1300": { + "safeContract": "0x22e1229a2c5b95a60983b5577f745a603284f535" + } + } + }, + "blockRewardContractAddress": "0x867305d19606aadba405ce534e303d0e225f9556", + "blockRewardContractTransition": 1310 + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x400", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID": "100", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": 1604400, + "eip1014Transition": 1604400, + "eip1052Transition": 1604400, + "eip1283Transition": 1604400, + "eip1283DisableTransition": 2508800, + "eip1283ReenableTransition": 7298030, + "eip1344Transition": 7298030, + "eip1706Transition": 7298030, + "eip1884Transition": 7298030, + "eip2028Transition": 7298030, + "registrar": "0x1ec97dc137f5168af053c24460a1200502e1a9d2" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x989680" + }, + "hardcodedSync": { + "header": "f90244a0379c072e9f737484d7f6beeb8d6d0c3dc9173efa70e10b3f5ad6d74868d9730da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794a84713b6241260b3caa2c4be00ff62b89c4315c2a0cc0a4827e9d8a1e4e7a30863bfa8cedcac880ddb1e678bab73343e6197c2ad31a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090fffffffffffffffffffffffffffffffe8357b0018398968080845d7b7e769fde830204068f5061726974792d457468657265756d86312e33342e31826c698412b24c7eb841a790425e1bf0ee1ec37c3f3ceceeecd139b0c3155f5cbd08828e17d434a0387d0201240692727d8b49ddbf413eea8dcf973178b706a239ddd49cd453bbe57af301", + "totalDifficulty": "1955496934878520937661876759407460631004578689", + "CHTs": [ + "0x9f5532cbea89ff89b7502cd5926e9d7b41afef2cb2f15f8452cc74cd91148920", + "0x079e67e8a3d9975682aed0ac1602299cc179938168fa2f18662d31bcd6a41956", + "0xd702a18aba2b86b4e1859138605953352c39b8c7dc5eae330a5b638188595867", + "0x105350b6a937d23e3bde813ed0d0e606fe43e6272bb89db5dd12fafa5efbbbce", + "0x44a13a5876f64261f1a3bc8a6b2698af4a8e3dbf5fc4812e5d7398210f9f8dfd", + "0xc2778ac7c7e4bc2e92b08cfe4c9138ea7f5b52c1fa41ac4dcec31b9f0de3e898", + "0x0ed9bbbfb97a07cb8f512d070fefbbd814339bf4302e2721815ccc2c71313ba3", + "0xcbc2b407f3ff2a0c2e866231e003f90742c378e56585fe337b1380538f66f652", + "0x84aba4a2f9f0f61f614ceba55df7eeba5295120fa095b2e848fa329ffd8c7484", + "0x743ebdee96f5717ec0ad355aa7bcb0f3351fe865388b425f84e80fa946419628", + "0x9abedccb839fdc8314f4d32d1380e7ce4ebd7e1011b25d8fc8424b52a183b440", + "0xb56fb42bc7bae4230baa08de9777a5dcf919982f0429afe437397565810507bd", + "0xbbea70ea25005c971fdabe7d8b80142167c848e1bd38f11050866fa3d620fb0f", + "0x8b229101b5377d8d89a3386c5ebbad6f56a701ae592a8ebfad76061d52c7134e", + "0xcae9895e247f9db2df4138d768dffa96cc533574924c57f69bab229b6b19880a", + "0xb925a8e54d1ff91e3c72b5615486e5bf0f815103f175e15275a8be421271d9fe", + "0xe85da4ddda46ee3306510af31a10bf22c2c4fde3aac92f30cb8c61a9cee93ba4", + "0x071a562ebf53caaa8e40747be0fe9270621dcf40a2c972d4d8ea2a70091e6bf1", + "0x4cafada660baa29ad4442c2df6249450b7e5427d5454307496d6bc6803b08ab7", + "0xf14ab138b42cb843fed2f6bdc4a4a0471a9b4c3a2a3f8b5138a5555cb95c2644", + "0x435afb524ce317c552e49c1b3a40be151e1e70320e9886f585c494651c8bb4e0", + "0x2f1d8b8a4f84c1eea89c45643e379ec177d3787fcd4bc19eb56912607af9c88b", + "0x9a85b61e9005de6fd6ae555139b56a2441add46e57a17377bd9c21ea936adf27", + "0x6d82273a52f0aebb3f7b932a1789e662573c0f7e594de7b0fd755a879b592944", + "0xd24cafdadd0cec15588a5be7d9f085ba8b151466132ccbf9aecb7d906c1694d5", + "0xddeeceb800c6ea9b6803431262e7ea366b5f454d760238c9b7ff9bf6f4682c92", + "0xdb1e6399aa711ce8969a66dcdf1f0b0df353fd4496361ebcc041414b0af81372", + "0xd90a1d2b833ca4109c01313c025ddf803aa4b8db8ad12c35d80c62b1f094f9c9", + "0xe8704dcc919987933d20cfd071b3bcd688e7bacb4dc94b11c058d33cd0d016fd", + "0x97857c5777db317dce2a70e56b017483a6aa369258153cf3763db94dd06bb628", + "0xc58ce7e7b7d4706398000fbb19834ec8e1a5cf0d633158cbcc066e8bf1278538", + "0x973d5aaed57633afab5df437abfd430a6fb7977ed81bc467048bc8e4c4151abf", + "0x265e84795e0e6ce34c609cee66b2a7125c0741269d4bcae1b89747d5e60d70ad", + "0x6916f22848a333e063c2f3f1ccc8255043512b2fb5b606055225e4ec7454ad20", + "0x43e9a4bcc1abe938a76332fb3a3b5b18a9503d106943274ba1edfeb24e0493c4", + "0xd7d4a70a3aa8feb66a1b510f8f75b1d39d8154abfed1c38bc3e3a5cf1cfd22b0", + "0x260e50eb3bc55d2535d63c5aac45fed0cb113f96abf282fe59c3b31dd2ebeac5", + "0x4fa8a27a129d2429977e0c221d2e1290f93e5f4135cc7016872aa3dd12a24417", + "0x63f7df6a71586a2f399e336529c16282cd98ef217177d7f44a84182f00781f73", + "0x8167c85f6265b3264af8a4c48ca6ac5f69cd27364fbfab9c5db9096e8a5a3f93", + "0x66e103b3d099455188b760e63905aeaf771a9fdd8a3030f1cdde81d088e77ed4", + "0xae27b09e3aecd49e196911963191c968ba84e86bb0a93f1ba0a261289e17bdf5", + "0xba5c0ebc2d917df1259e1c82b9f2a9976c7c3c565be4b5111f480c5ab61b5311", + "0x320160e3072de7cef1b274bf3a7c0e6e449aea56a8fe1a44ffaa71913739887d", + "0x249759194264458ba63514e0c5df1b68f3fc10c1c060dabd223a5650a78fcc1e", + "0x7a2c8852128c2c465be6c902bc47c9c6ecaea3521277b423bdfb22a48e8f4823", + "0xb88d2649af6bede366ef228d4c562b2848b3c3fe9bae7ec337e743889f88b5ce", + "0x5457256cbb36ad7ec18955cbb5ec85a7cb7912be05e21787f2b29693fc0aed61", + "0x495b2942a2cdd8787bdb3a830dbf599f8081e0803117ee0ba7506c89e4797977", + "0x0146ac012e7be53d0781d30c664eeb14c24fa89004e5d5fa380d228c28748a40", + "0x7f3f3152a93da9dc7623713c440083652d44b5bc92120967a55ce6dd95264aea", + "0xcd2ae49bd9a0714132cd58a1e2ba0a91442a6a70e56f5badf957e7a1a94386e6", + "0xc8b72e7467d17315e56e0465303eaa9f9e715ca9b85679e718b6d72fc7236a6a", + "0xc0037865f5caf21e45c68f1c86b9533946d863a7d7457286d24a73c27c4ae850", + "0xd0d264da96d2c059eaa3c9123e6f474420d34e8191b10cb4d23735be71edf4e8", + "0xcf23eb8d88e0c26837baf773d6395fe97edbdddd03f072acf6248685b23c9204", + "0xc7c4b360734220f7675713c72536f68c7d9d668ed730ce79040a4ac6425596b2", + "0x9edb394dad40d5be15d2eb885cd5b735a0549094aba68ea5788edb81e5291bc2", + "0x3d030fa248f48b139f34a5247ff306f647bd89e754716dfd173f7bdd57ba1c82", + "0xace2c7b5c0b19c4c592bb94b2504b57e314de40c7f88f2885a23e8dbec212e68", + "0x00313f3e654872bbdee14b9ad7fd1c050eff5e8a54d4e56f91aef331466c2545", + "0x6a0dd26a78b8ce14a0971e573757200cc891051e80ad67dce726432d23044741", + "0xb6e0e9df1d9d95e5e0f99f604762b5d782b19221c5b5beb9a932025254eba304", + "0x159d4e9f8cf95b45c9942b5de09ac3acd2627bcea74c6b4a355f95c073f29216", + "0x88a6c06b1d6ad9f27e06b347a4dd493757cdf5124fcb2cc49dc0edd8b8ffca00", + "0x0cf6bc645153fe58e981ad3ed2f1d94d2000a24165c8b07db3c659325ecf2a30", + "0x285f36fd75ee27236ce24dba0abaef4eeb66156a9a802b58a2600e98fe23b3d2", + "0x925efb6bc3bc9b64488bd51a14266dd0303b2617fda2c01c21ebdeb9f46aaf1b", + "0x1d66104f22d10c40917d86b5f305d098aa83c3704f8c9e3e37bff6608ab3dd77", + "0x8ca9dd79b65b1746ec1f938ca011423a63fb97a3168fc761de1ce236263aa912", + "0x8d58d7f3c662fcc1e9a4919e10c60c56fb2db174b0f058c9e1bacaa677d5eda9", + "0x0f24911f26479b703f15bf70e00b59acbdace28327d65f911748e63101dbcc4f", + "0x75addebb5eeb426fdbb10dcb0d4ef7830ab3f9d44fb93c3828bf627e62415507", + "0x5be0bb9bb95b35f71c64434f6ccf72ba964dc2b97b995da693268af8b299e1da", + "0xb54b08f0ddea20500588d5897234f942be955ca0451da713eb3cb848630e2863", + "0xcf371d4cd35427f351b9571f5a537a1058b0611f4f774acb42b0f762e2ac2369", + "0xc55f5dacd819a4f40a51c191a6398e719caca78a51a5e452f00341f441480e21", + "0x101544ccd9e52cd1c81a77116ea02169906f841f0e1792fc0a7f757c007af5cb", + "0xfbca065dd9b1cab9ca0465eaf4ed6a3ffe572d5e9cf6af76a60aecac6e580fb0", + "0x1d2b57d550b3b782ce122ebf3c36acb77ec0733b9a21b3b75cdc8281f9aa0044", + "0x43706c6d8da6dc99882df8c6f9885415f2467d2e49513047a27f9a98cc433634", + "0xeaab6f72398e0922c81e2b0d77f46296f003445dca76e886dc5775a6ca660301", + "0x54c1d372c55b455aa328bcced6a164aaa764e8513d03f470e50cc87ff11bd67a", + "0x76d68f60752776748e3880c2cabe1ca89312779a6899d50e8632245a7d48ccad", + "0x4d8a44c0d055ba4e1405001364530e509b844a9b27caa23045d4e4a2b5c2009e", + "0xbb1c2a8021126fc0de51e97d1f3ef2e83a1de4d5bab2a5a67a1e50a7eae19e53", + "0xa02187aaa7a9c251dc4b01572dafc7d4b02a18aa1d751ed02e76da70c00cd9ad", + "0x0dc48834edc79acc5c233c470016ecb49d8b2f81b48126a662c7f5d2405e9f6a", + "0xd258075c1606fcbb1c16842e416077c23b75875232a0ae87248296630007474a", + "0xff670fcdc5fdd594ee92e5f963e51d301e6b71aa184ec76a5686333a76a7995b", + "0x5db06222c3dcced5e9f3a2191c0e384043a18a343ccec6a3a504dd21162e0f22", + "0x0a6121bceff843d28734de942cf33719d9d0815b08d72136bbd79cf752a39822", + "0x6be75f5f4f0e849fb8a0632cccb8ec3188742e893b9309f6a409fe912e5342ea", + "0x6a6f2a818816a923e7e8f02ada642e2ea5c7adc372915225f15c6a354c9b0107", + "0xd37cc228e24525ad7b5308ea454e9e756a4c1cf960c51529c83d6af87806c18a", + "0xc8443e22b4d37b0f80bc5174dba9de0109dda5d56a1d07c18ab354c255afb2f6", + "0xe8d8b4b0c06ac40ca04fb0ad15428048350836cea3ec20ab77ab5cca7016d902", + "0xee7d19d4cac7e11cbde337f1510643d305e26622150373e2377317cb7de102e1", + "0x6936bfd69288325554c5ac73a27ee8a71265397089315fbb3df101592c0007c3", + "0x8858f87c52c8fa518d16137bef6ad4c14b0cae7616b1a8dd043ebddc662e56b4", + "0xbeb0fa3a2e651186d0ee7a7ac552c94f1ec86e5df565adb15daa83a2a3acd344", + "0x26e871651f7a21b69a4b5c4392586cefc92ff02d07e7d95270d24fe602f15fc1", + "0xa4424abe96d7255a231abd334ef448bdfefe751117908682acf81651ef740ecc", + "0xd7709e7304cdcdc505e4072491affbd2cc8bab19dbc41b7d5784ce4deeecce27", + "0x336e22f78ae0343127a825d1558e5d6708faafc0c95eafe3afa089f0e23e15ca", + "0xd7e9532397d94c0987b5abea69589b2711ae1d8131d50373e6e2797b17a0d483", + "0x081a420bc0915a70c420053dcf44f74ca0fa1f15aabe89cab874aa4f59ea6fee", + "0xa8768e97cdd0c45422cb366ad98d888420fac6943bdf591e7386567cd5a7dda0", + "0x394e116ee5f05dd3ef26c804c1fce1b5196787e9eab129be9328668fc949aeb5", + "0x3a631a21658a24ec3a5621801ca2e88a97ec6f0d26898047f9ecaa8578e2f14d", + "0x60c9080ce0ead399df360dff7e2bb6298f40e1c8153bb7a4c03841e5c6f5d0b1", + "0xfe41205036e4f0d4eddbc5ed305bfc741887ee21b0886047b0d00f5e5c1b469a", + "0x9c17887b0e837eb079c37415630a9f72c660e405f904681824288d385f150351", + "0xb28f080d947a1f634c19411920244861b90e80c81ff6b146ddb089b17de8bd3a", + "0x8de90c188c680e4de284ad969eb396b9d9eaff33b6d0741673998b36ece076ad", + "0x44d5a8a711b5de56b17348b1b62bd61e4a412f019f79cdde59b6eb74b8e4e2cd", + "0xc49a22f02216f0622df612cd2dfac60f5751d07fb2029e92f9eb01185880b899", + "0x1c42ce2de88061cecad18a0327919eaccb4c2e2bde2e858c42640483be2b4c09", + "0x54eba700821ff8e8d6093c9fc56e36704a1be9c7e8f4c2e2a8a4b900e3b2c0aa", + "0x5aae3b1437c66426590240b4ed86ca2c7940672e856629974aec11eed3041013", + "0x69fc335398b577d2fd7c29d7c25c58a642e755ff95fd35cb0c16e9f1012333eb", + "0xf530df719a7b0cee11b0820ca4081af5b9fc7bd29c203ba79808017210f25484", + "0x70efc0518a44421cb5952cb0a7cf955d116e32c003899f99ce436beb3e10914d", + "0xd69f93c90a46f0943c2f322eec1a2e9afa3464234f11b7c3114bf90aaaa578ce", + "0x3a0758a2edbe42021b233205c1dc4d1c5e0a435cf9701272243f66aad247cd09", + "0xf01282dd7bd929ad13e104cbb30f29a63106e0848dfc46de38212f4ff8a14057", + "0x7692ee6ef61f859c0fb106e3289af5b67a428cb939a1752de0bb82a6b5d13bf3", + "0x3a12e2bd8fa6dc8f6fde5e75d186734fafa41cd706574dac597272d253f7f40a", + "0x943adea999c06b17ab8b419ccce01d734b8e6ceada64f5a028d5576dad6621ae", + "0x5c767e80f09c81f5aac900a6d3390276d9be0b7686de846df2fe2b51b2426131", + "0xc4f8eab3a067eb0d71fbb084e5f2719d15fa5a642ee79b6725da3f173bf98014", + "0x8665fbc91017313334a7b250ce933a503165ec2faab01f133be029ee48003206", + "0xd4033112b5710a23cf40035d7c9fc34d90cc1abcd6c6b071c596e40301400020", + "0xa31f280ef35194facd71d939a8756e5bc8ea8681020a27b1fbb889bdd0690252", + "0x49dad7e3a7e626909c643d7d9fa79843f850430a92359375f033c93ccf1a2a58", + "0x3f49df8e77ba964beda6d6579fc13b914826c6fa3aea29936c015c8678493a57", + "0x57d0aee9530e2db650893e1c939d9292ec5a97242c2b33c2237efa727420dee5", + "0x57ef9cffc1188eb504990bb6b1f3ac67bffb77e13946a45bfc96afba4cb94256", + "0x5f22a9eb90f97a117b140b2290bdd205726311ee088bc922d8955bfdb31c09f3", + "0x1fef01c75406b8c2324c9b128d4366c33edfa7278e767052ee8a7db512cea7db", + "0xf93005bf290a92371cca8fda62b7da3029cf997d4af619a09caac4ac9f998c64", + "0xc0926db6446779844eab41b7b5011f7385d557521278359ecc2bf421d938ab1b", + "0x2264a659e024816013d701a9ebe86addbe4d43a53cfbb639b13d15db21eecb14", + "0xcf1c7d4465a56bf4ba0748322c1aa811384856492989cb5ce331c9e2210282b1", + "0xdd026601a0a96534e5291a7e3e8b4e8086aca7a0980df707dd1fd5f0921ca0a9", + "0x076069c875a0058f38e8562dcce3dcfedcf024bb4d2660797421afabf6593d5b", + "0x0b17256d2f92a70389cdb974dc788fd610b98d50e83e1a5bd503618dd70bdc5b", + "0x8590b8d7218acb68276c12e328e7d60a2232f2e1842f832a7b4c15bb007988bb", + "0x127edf2056747629d1811efc0799ed6f6b9cc27082af0e6939191a0044dc7413", + "0x4a08bdcfb922f449707da67fdabf620dd7a599ea10885480eaf26bafef0eecac", + "0x8b60c5fe4eb4614c917df3a96a2546069ea526d5bbff4737d34b6a2d599281d0", + "0xbdc0955e2d39ec6451d78e5fe476532a43d2a1a3ec52a634a1649241b2cdacbd", + "0x524c97982f9f61d603a8b09c6ad3d157d6d38e4507184635766dadd5ca34bca5", + "0x56b2a099d6ec94aa3ba3cc18a3314f4b99ae06c50fcde42e609f2cf486d89a1c", + "0x309074fae9c2f21b90d1ca2c24cc7cee0caa8707895fdf5cdc614d8b0f7481ca", + "0x1550651eaf769e53fa9e47e7ec7ea0649391d59474da324fd96402dfbd7332fd", + "0xf079e430f9d28d7a6eedaa7b10c316315e39581ddb9cb65e53bdf8a2aada731c", + "0x08c90db385f227a78d8b36696421b6fffdd332197138ed2723f75b6f6f854ae4", + "0x081529907fb9f25c36cf26722a68d6c4d9bad5b628833e72d3f491692a4fef9a", + "0x6dac932ee286c8f45ca209623dbc5f1175bb26d4c06da1cbceadccf90e3bb141", + "0xc1096a9750daef3ebe9968be27cfe9cb4afc9fa80bc9e35b394bc9ac011d30a9", + "0x0b2ca78dc4493527c033f2ee249ede27290d7235b2e2e5844bc95278d6a96b72", + "0xe7721c351159d7cc04a60c8d0d3bd516fd18db7a9b2326cd144dad931f12f279", + "0x197b8106fce55f448c4656d2998ca53e7466b9cc6c3225a13213f976d807da19", + "0x6fee245b11f25a01e33eac8e6a6c215bd1988e845b2b0e6a8d5384e6b3a1c43c", + "0x276076b2c37cd344b8ebdcbb153b5e932970138623063535cc357bd65fb37515", + "0x371055113bef1429260e6d6de7ce32c815bfa62e92b70e25042a1297b33d5be6", + "0x7db624ff7603ed1d61cd3b2b0d933358be8beec6daa17afe41fc5c8c040e7110", + "0xc8107aeced27a96b1e55e0036e8238c1a5b379de8993366c2b1b088b3d32db0f", + "0x457ef09be90fde3d02ad832bd303e5265e711aa47bfd37681b7b4a80afbd4c18", + "0x64bfa986f406c22ba7f4febccebc814d43ac90b3a2fec43c43d3195d7e5b75fe", + "0xe02476115256c4f87b436889ed7a089ba62479c67ff8ec123bbf592ad8c5527d", + "0x6e2dccf5730b195da6d9ffd1d121a1c0fd563595fb083922eab24f1d82a2e25b", + "0xba660d5227c8b4d4876feaf8f089ab897e0fd0a1c3df0c8f43977e8280328f5e", + "0x8566e4095917be2e9b4ec5007738a0084855c5d5ab796a18be85e270a9ce19d0", + "0x8fa732227210961948ceebdb825a4e5f10778fa3a040ba5b8ae6ef588a798654", + "0x63fea034291c13344ec416142706f0c84c5d7acb1e3ab46e4b307b07ebea4bfb", + "0xbfeaf5112e551701ddb66f62e106e6988c04a7ccd6987db3a297c6f14ec84db8", + "0xd18cad571300d94fba3d347eb7cafaa086970cd74e3155c3d625297106aa05e9", + "0xcda6e517497299bf5330b4c7e4f686e5911272a6fee004444d132a4e3236d20e", + "0x96cdc4068c32866bc558636970e990d4ccd98a406a99d3c34941b1b23e997a44", + "0x58e168aec982ad3dcecaa7f6bc9526545dd1a97782d0164c05fa0a05eb5b8c6e", + "0x0cbe55d1b236e55246d3ff0bd8770dc11dd2be07279e7f7f9ec6dfe600bba8e1", + "0x0e7a9c4a352a470d78d2d8e4c44d9a79df40c8d12be6a5ab5143dd3a2c32d362", + "0x4ded88ccca9f6b12885bc9d61799722a32e9617c0f4f9142ac46c7d0a3dd9976", + "0x0607a82c4a6b693bd42e7387fd381d6c760ee510be8cd53e03374cfc2d726d50", + "0xee65078eb44ef0a182644f93a59b2ad86c0992fb185bf44c26c48ce25458acfa", + "0xc5da5e853f905e21926b303d5288cff4e3348de699bc8fb4a02197b8af4e23e5", + "0xd74faffffd297b2c17d220495c1f3325014dc41e6807157c726d76e1b50b76dd", + "0x2434e9648dfaaafb7114af2d8c1d42b3406a88d088aa87aa9790dd149764e6e4", + "0x8d5acd17557be52c30191ffeefda675e6d13fbbea896b48a15d919aabd42f892", + "0xf56ca0c7219244ae6114a19e6a4aa7af3b508e9df1dcc37bcd29f9f48aca2683", + "0x1f391cee2ec13abf53fdc6d1d6a0b770385fa4cdd952c7e7dca5cb871a6b3b52", + "0x0db6a12d428023b70d52b38b3310f814f24a1223aee0e5a1266b6efd007dbea5", + "0xbbff49cde16913f94ff8e25b4179e99da2cd88e3798576e40cda27b058c0344a", + "0x224f277a8557cb9b000540dac92644e516b847a1b68a99253655781518e20693", + "0x140a09036539d51f97200da3cac4ef056e7307ea1c35206a92da4d22a806b339", + "0xf3d969aa7d6106033ec78c3e5445a8a9ab6f65e4ca47fed70f9feb2675c2e90c", + "0x22d820f1ff644a096ee0f381c8f73f867f28db5fa86cc4addc88f5143b1fe6f5", + "0xf79f40425583e7dbcdb08576999a90b6a14c1963636c65d42a6d360441430b18", + "0x611705427257cb827d988dce5acdaeea9e97a1686fe36ee00b3320afe93a2978", + "0x179ddf9fbc58c274060aa192b8339aff8aef09f9db7ee5b18f6d05303be6ea89", + "0xd26f5a4c3c2831e769bd0cae932eb8c771c1020c96efab791458d28736142ad8", + "0x9103de0dbaba17f46dad472ed755b530d27ffa1ed677b9e0ffd7e7b85146571c", + "0x342abaf2965dcab6896bd845ac268016dd254c46a576c2f57a8194587d6f637c", + "0xdd4bd4167c3f059411ddf295a57073c0d72a126c6d6c24052ffb7d7e6b7638c8", + "0x508aecc260416d79efc2162cde7dec4d8c49c5a4185944d45a0a9c6a6ca43311", + "0x68c2d3a4cff07fa3b2cde4ab3b8c94ce081b58e46e4cc4e8bb538ef3a498971f", + "0x99b5e5b79dfd03706fda2f318cba28c2bce636f691cd989b899625421a26a37f", + "0x0db0d90478a568a4bfc374bb4924576c010d0da8d045306e6bdfa491945552cd", + "0xfe79d90b0e2281bb8047ebae7ceebfc51645c6ef4a2c3e9f5b817ec200ac1770", + "0x6c6fc0dbe8c8f03866c69135e614672bf2fa7a9a172c7623f59073254d3ba70a", + "0x78bb601221723eb7160db23e0141d7450185c3c97bfbc65ca34fd3058298b3d0", + "0xe29d504ed9b979266ba3470949d046e6cbcbd60ed3fd0c4bf4a16926c950dffe", + "0x927ee822122d6a1450f8b5174865a836730a285cfa8a70b629eeb5df2ff35c85", + "0x2289a0778e7463bf01ae7433470e99ec582faa277c9a3015fc83070da0de7385", + "0xa3bc950a4cf26a4ae9d2fcf145c26861ddfed6845f180294325e86fbfaf71fc9", + "0x0be96751d2d163c9eaf3d4ad325b0d6a0ad68bd1f48db57f04ac202028cb4529", + "0x83abd07e1d423527ede026e1827fb631b0b09ee186a1ba7fe6591970ad33f086", + "0x7e23230aa4ad827fb607bac301fdac202de414a967c9003ece29790d919520e7", + "0x5926aaefb00a53089a84f7e7e60f234810ec840f15de73198a85ffba4e72b21c", + "0xd3ceb1ead4ab4968d969868400d4c495f9781c02de3305a8b600b9d2013208b7", + "0x004de91377fb4ecd49450522c3f9eb1fe4cf58cd62a7ea0d6fe8e60c89fbbe30", + "0xbf4b3502ac6761b1356f6fd96195fe5bc15a0ee6d4b705b118ff3b4c4913787a", + "0x7fb6f0dae1bb69eef39bda52253ebcdcc659302175da66eee96b0fff31eb82b7", + "0xa4641a477431b008009ef9b46bd85fec4a417e1d5a4d2dcb74400c93563709f1", + "0x40e6ab730e228c5ebd7080800331f0ea102b71451ce400373c122f2e1b473d27", + "0x6fe0b5cfa9e2e2313bee55d41f67364a54a9fe9ff755462a209471501844dae4", + "0x9e56a61913becedc02505e5edb787be7fd695d4b10844a8447a538ce8d6de9de", + "0xe63a2cf3e4b41ea2b38b615805b2f28017c1b1cc48fd89200b5a720322c71a59", + "0x85a6ddae5acde613b93a945b30b41c152c1ba697d009d399072a6909ab77e7f2", + "0xdc474371d7adc1d54936973c1c655a90746fe4ed16a7d371a3dc239a7ab798cb", + "0x8bbc19f6e33f27d0e9340a52e1f52c8c2b61d3c2598a21a67adfc316fc1d3b2e", + "0xa692b01ed36027bc75da84a9f69350e3dae7026dbdc5a90f0f8ef1443e1d7c48", + "0x2fe43ccf20849c44c3e5557e69045ec6f6c280054db1027c0b55e3116a75d79d", + "0x2949e6ab78e09e0864ec65fad793a07d3242b967a24daeba9a9f2b419a14b690", + "0x3b67165020ff232d3236b23f3156638c8a5c07bdf28e016e9ed67352752e5ead", + "0x98c8701cb57044bd35f51d591eee6f064434d59a6d19b934df7c793aeff34cb6", + "0x4177c7e2d30b6e7e9c88a58242820ee26c646c2b917338c1027aa332dfb470ae", + "0x8b568f5bb71dfeef5087a26b718bb67b835ed25c1d9568b51ea139c7591580e3", + "0xd7e927ada4a1c4b391a94b0457c91595ef03761511c4a770658ae51968dbeb9e", + "0x42e3b9b1c144d2a3c93950cffd9bbff6f98861e984286f366bad10d49e080088", + "0xca93cca4bd01f99626190aa9d515255f2fb51ac925ba996ef033f377bcb17086", + "0x4ffd9e3efe7e05b99ed126b65e8daffd09fba8248d64a40fdc1b3573de841014", + "0x8ff69eeaf1a70c4f7f146776cc9a84068c8e6f4f9c93733ff26c37ab976fde52", + "0xcec9210cc68e88cb2e0b86ae7003db485222c1be97a5789f60a69712ba5bcc1c", + "0x9576746ca313e1fd832fb2eb4f96e3a193d5dfed7902350bbe07bdfae3a69931", + "0x0932aa2ebc96fd4bbca141f22f30021e41820fcc41212d21845b3cd5479ebd27", + "0x38aba794f2bbcba63399f54e40fe512db1800b76276254731d4ae03df51ef9bf", + "0xdd1fc6abfdb5eff3caf949aec9809388bc0f5a2a6fe73fa19a2fac83c4c904ab", + "0xa9a5b6249e77e4938f691b82e8d8709ad8d9d69edffa3d4c87bb9a56d38a0690", + "0x5c2f7d9184c130eaf66ed64c6721e33ea5d89661d67a31fe98d14b0a306475dc", + "0xfdf7af81856612f6bbe8de6ae41a03ac4befb726402d3dee43ebcfae262beac2", + "0x94924a83f23d4b3b001b6c0c90126591d81c6414b8dc3f6278e25515ef940ebd", + "0x815fddcce6c648ada9c8b90ad6b6aa2a6c372f7b73fdabd0cf0f4e39225bfe2a", + "0x6f78ff6b7ca095ab22b46cf3e4bb7f6479a7883fd7aaddf2aadb4dee08d8c228", + "0xcd222c0cac2ac85234794aecc8ffea050657f082d29fd9f16f307cba3b929a83", + "0x1ff51b0abff500bc6f45cbd028c68d6dfcb4b10537ae75acca862c1618a7003a", + "0x7775833ddd60f60b3d3cab47ed151a726b26571ace981ecb4bff27d5a304f267", + "0xf8d86ab9e6c6c048eb11f31275038ea1834f6c114a3fe5e6edcec31c6c948f4d", + "0x63823ac8744502e9b1cb4f18d04b5c9e36e91a9f2c412f83967200c05287d5b6", + "0x12ffeeadc86f00b8cef710e51d202e4237e700ff9874d2d3c350ff9270ca321e", + "0x1ad55723da5c76bbb7e5ac0bf28414727b3f1fec48335f719d32763c4c1df49a", + "0xe70e3fbfc83eca935a6eb790ce1f4d3ae7700be3190cd548d6f3a405e9df189d", + "0xc5f8e73983b88b490a53f4d06c638d01e790679eeed597b0e5d62095d797a6a8", + "0x53757034dbb38e7aab1dd8e36b710df08835f977d60d701c6dcb6f95b1167a40", + "0xd64dea8fd270734a1e2aff6cb2833b4c855de748c2236c0fed763063ff9e208d", + "0x72cefae756232b979f7a8a00b0c78c7fc3d0a1b50670b847e384034ed7df1e56", + "0xda9aeafc74c4acfda32c03d864d8045e2bd459908fdd0914bd19aaa15cb1d162", + "0x9d7c548f2e0b07be5eb0a1e34f5be4e571edaa4555015845c051707c68e416a7", + "0xa414bc812f2e4de0dc068121e1c8207af8bfdf1bf3f8699bf2406a69d7e1ae05", + "0x40f89a42362679b80596ae8c602a801c100248d666523c61205df86e0a8db141", + "0x3eafa53d2d0aa32098ef86413a7e8453b3f1adf2b6c1a182332a79728becd1c8", + "0x94fa69bf628222d6cc4eae8886b01c5b9fb4db1f121b627d77bb9e559e0f20bc", + "0xdf3008e9b62cec4e265559338610aa7b44f35288ab481b3afa06f16aec6915af", + "0x99ecf69294bd383bba604dd05a902ddd1d2f16cfac3d5725d71e547c7e481ec2", + "0x8afbda39be4dd775f5846f686fa79c21fa8d80212b0558e5b2ae6f77bdad0437", + "0x5f24ca6d67e1f60e29c7686f64909c5228b19ae8fde8e0ab936ddf560eb45052", + "0x665961c221c82d0a05b379e631c1923909e782872b6a552ad32a8af65cc3fa2b", + "0x58b7bde61ef233a9eb84db4561cd7abb312ae6819afbeeaa5108665ef033f8bb", + "0x8f1e2a4f3c8f508c4dc8d1096e61b3daf756f1e4b89694b1fd69aa10bc1ab3e0", + "0xd1022c07a86af3dab26acd68461bbcde506495550048ce4b655d3d3405dfdd88", + "0x8fe1f97e50ae48d7a1d2990b1aac64baaf761172007847f96c44030392909a1d", + "0x772bb09e299cadc10427638b1033df98d16674c95cc37f6ae6571bb7c4775e3d", + "0xdd45ac2772809f1e9edbcbe6c34e2ccf970fe449f14f8f1bbf47fe77dac1a79a", + "0xe229f918df6143bfd72e6bdf11b9b8c8abb784d4328e515fdfebccac1245ad7c", + "0xefbfcc725908ef7c756d6af3d53b54309cb527f4aa464637361ed3a7df0ea2dc", + "0x984012fdb88b912f4896e7a0f70787acd9493dcef06e65d865a1cffdcdc49721", + "0x6cf47c1d496839f9fcdf8aa5ed4422e33848b4863a52637d5ff4f32e5a40a6aa", + "0xd39c13a4b711239b7c0af25a54bf96609e6f192b7506d626a810fba5ec62a830", + "0xbc53d32081ed044799a4f879a51ba3e6b280d8c0a47d6bdcef536e0290138880", + "0x6ebdb40ca7c8500004983c98b23654315eb4f7c4854d5154e04ac4c58fde0162", + "0x366879da9ce35308ccea72f2d2f9cb7b12600eca20fcb382f5fcf8867d8f8aba", + "0xd5075495e02c501b681d99478c8629c1711b5d772dbf142c903ace4ce38f042b", + "0x7f3bdd9a517179afab5c4640287bcb4c100855b988af8c1dcf1a5af83a9fb67b", + "0xdac216000ac5eba7a53bdf6d1e4ef9dc7f1e671832a5ab8cd06746b447075274", + "0xfa0de6a05f67164be0a70bbab199f2429d422b224dc21028414a580664bbf2ca", + "0x6b178cd6235c85d4e5d0cba743ea851ab5c3fdf6174597ec12059fb1fa16d3e4", + "0xf0e7d606d72bdbc548840c13a3bbf399af5287afd0cfc5873afad4c6e0874c6b", + "0x7e64769df7b8a0be2a5bb61150aa956eeac34e2bbc8e3716169f3b5b3a069d03", + "0x45ce31ff3f2b17297d55b1b41c4bd0c52a1073d44c350c42db388333d23a75c3", + "0x8d1c55efb8e97a643673e4db894bcd452dcf2933c8d7ac33261929ae720e0999", + "0x20ae4e7fa6da38d81ad7b6f19bfd3d37f603884ab755bdbde5eb472c05906699", + "0xde4d0cbdaff4dfe4db9884470a83201141382b6c4b62db363e04d1c06c436e9d", + "0x11ac23bb4efb99b5a9f1ed89bc4a66330a3e30ee6df18135b04795e0de5e7d4a", + "0x4b475c6ff5bff9cb7b75573a36cd550a3c298d4a9ea55f2431ce8ee71d56b585", + "0xc0c8e75f3d9c178e40c66411052bda96a6f99ce8c5eaebeca018ed0337830f56", + "0x2e9e518e7a13e9e01eeaa29fe9b9fd7e7a0c6e404f9c61b2f7de0fb956a87367", + "0x6003639d76ed27e8e6bc87bd154767cdde535800b717aabbc08480f440c346d6", + "0x5ead2987a3b516b664abb287399c80b34c81eae773ca432b8e9df040500092e1", + "0xf8bf040f8f0d3e8f4eae5b24fbf19ee05947f1d6a880db58ba7055746f5cab77", + "0x6f95939c3ea146854142031f66ecf1495c7d4778def0872df279fec21661cbb8", + "0xf2b37a980a33ee4f74cf7eb9b1f3a00c7e391d173df59b73363d37f5159ceb6b", + "0x6757467b63bf5228bed5481f1b2db9c45dd77e980b92cca1e50bc963e1a2fde9", + "0xc971e1f7a8ffcb892df5900f18610591c3c09f6ad0f4e0b090b2522f0b3e8dcb", + "0x29f4d83d95cec6b98236c90a67208cc4284db028591134b60078a1f04276ee30", + "0xf3fd53503cbdaad44dd29fc413261c77133556949b38c970e8805c473ef618db", + "0xceed75fe5664104ad7cead96c90314b03644d121a3a8a798a0e15a20e2692bb3", + "0x4ae70eebb7dd9caf651024f35e9a0543717e12d71866b18e76f98fafee2ed594", + "0xcba02fe226df047bdb88249dc6c51c07700ebb3a1fb58c4fa467bcb8db70ad0f", + "0x44f822fef3fe311ef46c6c3e19c36836db39ace888f8c2dc217e999dc5f14184", + "0x6592ef510a17e1147fa9e3fa5a53352da7f383401e7ccf0fa21d34cf043e1a7a", + "0x095ee326f8f313e013eba249b6ecd59a6bb5a02f7abf0f9074a22f91c5b4c326", + "0x35ed27fe813297d49d1a43e0e2d6e417f68da27f1c51c7d3f821c4f1cea85619", + "0xce0db04d6a739da734ec216766dbb28765f31d0da507f12aacca3ed904f0a7a6", + "0x591490b8555953ca8508dedfa79911da30469f9d40cc13bf56688f7e02e96b54", + "0x4214ec6063b0b67e9a0d53fae047b83836e05d12f5307e72599ff4f0cfb1bd28", + "0xf2709c955789abe06e191f983f60bb4441097a84dbc8d972dbd014bdeba5b2ce", + "0xd24704685984daccc449b038232927dbd22276584199206ac7ac8a0598370bf5", + "0xf9168bd18b45bc696db7045531a43d4bfccee92d2da04f0d34a1999ad2e8b95b", + "0x480aba3f4cab0c902d3f31bbf2b2966597ddf7220677a9f602eace03519b823a", + "0xddabf469bad1465284be270f525d3e19b0635bfeab6d2d3bb0e837f524efcf65", + "0x9ebb6ca234853ac42f0da3473aa3fe868b8c5d061bf94a1bd0a6e5cb4832c2a2", + "0xa22bfbe2a344ed4aff05a5293983b0cf2b6abcd4d7bf5b1fd0e2f7410c59bbd5", + "0xba337fa9e7583e46276c30a17344388570fdd2c5200756a7720ada9d38c37ddf", + "0x690e0f2b1e472e59655c94f61b4e5e2e545b5e8bb6e0c0aaa2597c6a3d57eb41", + "0x188f863f7f5f330987a29c0393f90df7f1241a46fa6c3705922010a74c0d867f", + "0xe1f6e287f6c95a687f67c07885393d312fc8f8fa499099819dfc4de98ce180c9", + "0x1825d68d3768f18983f198444dc808650f5ad40e236a6c1b9f1d78f088dfd775", + "0xefa2abe38b1fc3d9890c0443f281109480d430f389079ba2cb60b1163dfc2e6f", + "0x14eeb83517b27fade68cf6ac0bdc197920212f0cfd6746104da69b34611a35cf", + "0x034e7caa41236be340e16683231da92153053c1cda75e0c0ea942ff1f9f1621d", + "0x875a3015a1738d7982209340952efacfcdc8b01ecb1f1b868060c7d7a04bd602", + "0x978678a85a576dc7fefde439c3d67d8fcc4d8df775f93f7b95a639e036662dbc", + "0xe1e8d961797cd6e3a2b3935520ebc820fdf5892c7aa56f7941e0c668b981a54f", + "0xebfa7f827c718f463b703c6a67d801e1727d40493614af69c700e342c9d009b3", + "0xc6c916f39809a5ecda43cf0c633cb8647dee46cbb7caab0e0f7f615f27d7bab1", + "0xe9981143e9108df0954850ac860d287655efe36a23a61c5c39c32cbb015a1892", + "0xc7c993a2c2e48981711dcec221064b3f79c495504758d3e56ec42f9bb4d90039", + "0x63207ca200d554bb8386cbae7b84a64235190fd3cb0b81bc084141f55ca78435", + "0x57880c812816c98df7dd03a5cf7966d3d820a844c2d7bd754e253254b2158582", + "0x2847bc69192faaa2183405a5380ff8d50066dfdf32dc11363d8e2c927295a37a", + "0x2b5e30266fe3b522b7c365c48a5ffbb64f711f0cddca64fe284d2953175341d8", + "0xfa0365aef1544b3e3227ef5106c8bb22175e7a3d607421fb6624c7a4de793e72", + "0x1d1bc1257274a934f92c8f85d18a925f273c09d14189985d8e5efc48f38a05be", + "0x753409cc790f6d89d5e56a461d9f21b900772e34a047b58216be6c3182917595", + "0x5fa3f12c62dc6e92fa3b664df18ea0c488f4006b76d6b6bc49c9475b2b7c97eb", + "0x22f194892457c3dae1677d0f2b3d3b1d4ce1c5aa112456e6ed453ef111468c2a", + "0x131f9dc9d26dcf71485c24181bddeac118e557cde75fa92a7c3b1f9e5b767d54", + "0x3cecab11b1e488c120f753bef7f6e27fe9d1c9d1789eb713b8f9a267176022c8", + "0x7c940ec04494cdd8259d19a60d9f8bab6fc398adab5c6e370a3ef24c31bdd97d", + "0xd8eb0f69f9885e7e5f487e8c5805d15cbea7e46096dc3bd4b99bcb2b3b78a769", + "0xa8c802790c5176280e4197b51bb7fc30656ce99e35e2ae2cc782ce302d589f8f", + "0xe0200768f25e549d9b794d9dbade0325c0854283da2b2d5fa4efccb978bb6d0b", + "0x9499199a278e94f0d30764a841e5808e515271f88631f55bd3c01ba7db097b07", + "0x967bb7ae8b553e58c9cff454dc3701d731fb452360bbbdeae0aa4bcabdfc0413", + "0xdf40a62950353baaccf7b22cad3a4f93948faf93a03f30ff1752c34aae48c419", + "0xd47b6ec238fbebab05f0056114e3669d6d0d53bae5160edd52df4db55d0b33ac", + "0xeebd289e5d50b8840cede6680b673db866df3ea6f570d024cecd22d5aef3c688", + "0x5d885538622f2435911da14749d84a227a728a157f45f71c5622fe877c56918c", + "0xc3f55fcebcbb81fe32405a056e252d71c3b443db77012568df7a27f2ec3a63cd", + "0xd98c11b6c44f927b28ed97bde59dd5f68f20f586a61e3ee7ea5fa26184bec280", + "0x9373338f42bb8c625874090cc677c8c98b807131099a416df564a56c4dc4eb7a", + "0x066ba3fbd12ab1503a23796c414cc26dcc137da86a2ed7ccf487590e12262076", + "0xd786ba4bbd60de0e8e9be3c480e78a1aac393132595880a710f517c8bc62a468", + "0xfc2bfe54cf428c0cfd76d01d65b041fac7bfc2b636bccb20317e6b9678030dd0", + "0xbf5f0ff9b3139ea0a8c9c35e5e50cc630f4185aa54315a0efe4ac0a87c1cfa6e", + "0x59e57da6812925a7d37f59170d31bba45cb499871ae3e3c18a2308e8c8814e55", + "0xadc01054bfdf0ace9399917ac025e40665c7c64a6743fa96501c29a205d1b123", + "0xad03c042462a6c5be2fcf6d5381d6bd79e44f6c55edb68035b1348fa60ea2b1c", + "0xb95a0159131fa8996695ac8e070ffabbf33cec250ceecbaf896dbea6b18c042c", + "0x7eeed484a6b8280cd06949b9a5e6b707b4cfb693abfe3935a4280c81d85b0fc2", + "0x9dc9c356db9562ba86ffa34e044ea3b7128e7c7a1c6ee8437e753be597c5a153", + "0x7aa27d4a640ebdb99aea80f1d5f4e77a1816e790243e6c29a888c6d5b56c8ca7", + "0x59115cffca5051ad532e3cc22e9f1b2b05d6fcf727c19731b532efbf3f47843a", + "0xe706959ef4f86ef7fcd11d64ba0fe61dd8b0cb14e268dd45cc7f073e43a75e2b", + "0x8e716150f38d6c54e9d2e51d782339f133b3f973f107523320cd73f3e5260021", + "0x0a9dee0ca51cd937cccabb971cd9ca251af6b780a85b530ef399f14d93b5a09c", + "0x46db6853a005c25ed07b6f153d1cf763b23bd434af9b7d7f33ae360796c13871", + "0x2cfd254cc33404c9267ddec93e9870f157817901c134e0b35ba63676c15ed6bd", + "0xc5bd84b5dba39f360eb8cb969dca4f44729ba370012454034238671773ccb55a", + "0x551bf639b332a563402aa57732743cf4c058f21d8e34a65316ed389e6eb9780c", + "0xcda748f78fa0d620254c0a296c2a607ace71225f9c4d0190a468df20e84d0d00", + "0x685ca09d12fa542f8adcd93af918ee4b7c8255874d894dd87461e9e9946cb89e", + "0xf66053674e44cb1980fbd869ed84348fda0136ca48cc9810bd0042d570f3cfaa", + "0x6e7bc79bdb60b8997d332d68926b017f41f1507b0bf307b7a096e667e8941e27", + "0x2bfb83c4fdc6adbe453a71ca65222bd4b071ed163557ebf6733e24741551dc94", + "0xfcd2572dc4470f3fc284bb9a49d5429aff837b4077baec00c60fe2686220ab54", + "0x6849dd3c649cda89b472375747f4233fcf1e44f13af67fc555781578dc8a510d", + "0xfc05f2fd55d372a16d9ecc492af594a0094babf184c8755b6d78c11a7b4e4cd0", + "0x5199b55bb3effb0d788b25fec437f3909ee360f4f1082be2127c9ff4108e1e07", + "0x3b54297687a42931634eb3e17ad0cc50a244d201e9b2af3c13b30794f9793156", + "0x1cdd497ec3c077711f8d03aed6d08ae61703d6944e32b075acb62df7bc927501", + "0xaf180c72fa4bf703f96f32c3f738cc1269886e27da26aa040812afde48909d55", + "0x8f8516a368d5bb0c1d4c69608cf505e2d1e591d8014396b25251791a8422dcea", + "0xa4407df4abc8532eee3890c87fb88306fde2cd5f2338d44ec79810f94e60aa6b", + "0xf7d8c333e4e0362f6942b1071150a09f57457b5b214e586cf0c205a1ddead9e6", + "0x016599b6c105e71c22dcc3768d02ab23bde5f5895f5cec7ff241b58bd666e093", + "0xf421da412a74122a4c2f0542adf364c0dbc74557571eee5f3b9d63f0ee2baee0", + "0x9ea93f31e2830c5da8a5005774a33b8a0a50c475387323d3c0ec17869e074e3c", + "0xb9dba998019f2361ae3c1278a007659a918b062bffd048d2b40b0d51a6f87366", + "0x5d8dfd1ba9b43491167776a26f5068393942523886e1a6558c04959aa016a9e2", + "0x8900a7cf473e7a50c5a1998c0fd8a34c85f249486b0c9a9e96b27ad107aadaef", + "0x2132bf9c828dd11a3bcf3d7a25ff5cc77c71e248a8cb35e76866544c673ea803", + "0x3cade26cd3a46e392b12964ca8df2a93c4176d9be35b0019c3fdc703e50e3c00", + "0x728a1247d4144114e5f4581a02783704e619f087b9a362cd0cff8583797ea474", + "0x3fad02e483bf33e516410b0eeb4aac72cdabac43c9d075b66f4893676e56dab3", + "0x42f71132c8d0849df1118264795de747420ec5c8deda7935b12073ffb3e96653", + "0xb4aea097f72b17910846807c4810473c2a9fdf30528283f289f13ecbd4567aac", + "0x0bbd2e1e0aaed729ae2a1148cd2f0020a2ed4beada17639d7cb601b88d9f64dd", + "0x127ea3571870167bb31070bf0fba7a3edaee1d0ec0158bcb2ea8249e5d516825", + "0x7eddb9a5265011bbb717873dbb0114aee0698188b26a3eabc6f82834816c19c3", + "0x6e84fd9a8c6b52249803f4abbea6233d9ceef93068682572b567785a9c860608", + "0x99953c5e359082e47a29e017cd244047f73f44c7945e6d260642abddeb10980d", + "0xf71da4f10d95b5735e41b39109ba47a1369b8263305cfbe8dcde5532ae26a90e", + "0xbb4f40cd480dad3a830510cd14aba5fafa94ef82543aec4bcd3e307520db1ce8", + "0xadcd64c950c24352650bc5397d4652043faadad6ce816970d4f6364e97da7787", + "0xe5bc749df8126a54c4a6473173bce8647a9131e38ff92d6b3ba91590b84fde9a", + "0x53975a3c9d5c7454ca2d91509e2425fe3f8bbe42d5cb6a1ed7f743f3630ba805", + "0xacf95108649c624333aab84ef16d0726a2737b0bb084b33df075ddb8ac1bb53f", + "0x0f59a1f2f12aaeda13df4632b025aac74acd02287fd47a08c7838301fcece25e", + "0xc2f0d652fad1684132423f89948cedf99e039abb2cba097a8219f81aeeeaf623", + "0x995a9139f09e9cf082ba575ac6ffb35f429d8ac93565836b854312267f951f91", + "0x0b0c13322541723421660e43923602f2e1f34529910c6aa15cb7339bc34a6d96", + "0x2a225092d3b1eec5685c220bd9194597fae1781a4e295cfb3c4e06decf4a44d0", + "0x963452ace51e69fc27854880179e8070c81287271b831e4582645d4333feedfb", + "0xcf57009988b9db838dd5ac44903605ca70ac45317112b57bb8c91ca0171b2820", + "0xf685e94da234b839830823dfb0d607c1a4ca942ed68299e264ce9c457bc9aae3", + "0x6bcf336556428a8518034efb811ae793d84c80071cebd9eddbbfbfd00f5088b5", + "0x0a327aed827a909bf771eb516139c8ae8c721417e8536086335c8670a1b52991", + "0x7ea2a67d8b7befc6784f48272c9e3ba6d206dd5ce747a0f3b22b2badc95b93b3", + "0x4d031eb0c78da600bda9d7fc8d07d4319b824aa7ea9e1d9af861e4c2755e13e3", + "0xe8887286e33bccaf28932af775d1503175ff0304605e689f47e850a2b6909582", + "0xbb714380da8d6c48feaf39f1a3c98c6356194a8beb93413a35c68ca5c101e3f0", + "0xecb00ebc31d49e73931328b1a7f1be201e260bfe42197559515f420eab1913fa", + "0x756d6c99947e0e66d31c078a418a0fb1fd09e5280974faf47317b3d341e0d2b1", + "0xf3b2656ebc4803f55b9d80ab98775b9a70d017b94949b04793082fb8d713f716", + "0x7c0f9e4522a045502a9510426f8a06bb3f4170a8fca13731d3befbc7655d29d3", + "0xf63e4fb74386f42c6df92da399eb4137b34f2cfb5ec7146a81b716da6f1e138b", + "0x836142336bc807181f35f45388cb2c097c9e7b9b7605d9d079547957aaac7cb8", + "0xd6dcd8542a975d2129dd0aac86413b47a8838f15fe5aa91ab01165230f58f61b", + "0x7fdc61debead0a01d0dd2984cadfd4b2a22442ffb72e54c0d25ff9d940af19b4", + "0x9fd111c14fe3bfbd62e6ec8386f89b99c7755a422970263580d82c9d70a339ee", + "0x927a0a3093df49c90b5a41bd2f7f63a9b4ebc937b347431e052c945bc8beb252", + "0xc29a4d66e9ae8ff0b70afafd743154f726adc4228ac276fa7a99d29b454c25b0", + "0xea30f99e82d66c231ce97d90681d11573506841a39ea5dfed7eaff0c92979ab4", + "0x415036b80cc20e0ede161f23ebdd82091d1ec0b26d1c6c89a00aef37e0840416", + "0x93a973cfce3f9c466f3fcc840ac449967087732ff89bdceaf7b59c42a72c789e", + "0xd2f8e40dd6f221b4f2fa20ac96a282f5c7c2c384b30daf5415c9c7600f568312", + "0x11c4781ac066d639d307a2517e1ba41078f2b82c96e1fa0b6d1da4a5cf1b726d", + "0xc90ce8ec757e96a323d2a426e64ab4cd84bb435e8539e037f51dae6369e84f11", + "0xd5610ea911ced5bebd9706402c46b121f60971db85c8cd369565e61bbad5bd67", + "0xffc8db355b911abe05da97ef41f016f14a4010fb81066848bee6a48608a9b192", + "0x2e5ce95848feaf7c3a183ad147f39994a4aeec9c30a35b1c46d4cf41952503d6", + "0x952f4891957b8f535e9c5053a57fbe35e533a5575dd5e3bda29c38da8a728720", + "0x909ef935a67b90af72c4eb5b1f4ada920711b4e81f88716dff64a13cfaf68952", + "0x4767e894e2954ca0a3dd8212be921dea3aa3cb5a4dd6c0490a6db122e1e29d19", + "0xa574e4bca14bb011f3184718ce0ad71fa54c79db9bed23bbf3775561cbf83b0b", + "0x35dcc8c1f20c0183d541e346ad4d9d6f1cfc11b349a2e4efeacf7350fb363f7a", + "0xf1d9954f936da8c9a33bd54b055364e47a1a5db52929883ca1f7ccc57f6e9786", + "0x81319c2f5f864a63a862e457a38e1282213235f50fe6c6b489594886a8d5d7e3", + "0xf1a00901944dcff4cf27a8477ec0a00d98ee1898b753fed432bf30090e75cdda", + "0x0130300c68b62f4f8c57f11a5968f9c29061af73cc76735b9106a37c756e002d", + "0x54a71765825a1dba95dd1dc8fb439307d808f3a4670620e2aebcf312b4b3a833", + "0x80bc5996a7796e150c7fc3232e9e401653d01e88eeda0430d4580056709003b4", + "0x49df8b27fd8ccb3106d3e7fa4f14f10c73f2477b56e22d20771e748abab0292b", + "0x409528f921fa74946eda4f512a7a0ebd24b7ebf8fba65aaba2f67810bb236f75", + "0x216816a30268888595d34bebbef045b24cbfeb2d42b4a994c379fc305124791b", + "0xdb4d39e063444b9946baabbc586d3cf8edb3eb0b6de94cb68afb514a26f862dd", + "0x8c7c8bdb7360acdc2acd7e8c397bf896cbc0c32751f6bab7a0abf43eb69c8cfa", + "0x7ca406fa4c91b53fb459d9aa9b40e879550e9ffe750c4b714b8789677eae1beb", + "0x556daeab1392a5937ac1deae4829032e6823339fbda20c9c5fb7cfd163807f05", + "0x52e982fc76239c968e31bb072159945a8a6a202dbf25d74777444e544850dd19", + "0x57ec7b83b26ee2dff1cd3e80e2c4a744d66cdc0e24b7c382acf58c4e55f3fc21", + "0x033efcb7a83b56ce0506d40524bc5e50500f072f983ee913a9f6f4bf6a71c247", + "0x49d92ca8dcc6a02999aabe9950bc3ca2eb86168deca49e90ff02d51bcea06002", + "0xebd8472dd0a966044b8c355f0b6161f49d8d81a196dadf071e7e562e52665287", + "0xba7465220d44c2a5b91489fc89d263054d7094a3db6c6ca8666f6a90f01e2274", + "0x04861be8578c10c1e588d3f29207ccd0216590b00c03789d3227182dcaaa102e", + "0x0656d41e8f11eda38a699b0336d532cb0cc39911515645fd729c0e2440b5570e", + "0xc2f10ce07711e25ccaaf129553530720bfa5fb1132693fdb2d8b93488d00b54f", + "0x31df617ba951999fc7022a2e720836117c66976ce70da16ef3bb34a793082a09", + "0xe76316801f6d250a13c183fed3a6621186f5bce23e3d1077ff3e2848b40e90de", + "0xacd7c04a1f8a18ddd282ab7b9b619649c9d861182c89603714a972fe30953f1c", + "0xe9f8c263e2fa39013deacb061eb00bf1d39ac93553c100bcad7abba2102975ab", + "0x9c0ebbc2904d58ad3ccd619b9ca4e8db2197aefc3142d799f9542c29c6aa0b9d", + "0x71b4f97fbc061063d36edf23465a6f59f62f7271aa912b71f6a8232d5e8642ba", + "0x0d7e4b07e8bf770da9ef0cae2ffe62048a79adfae3a4fb3740e985112cd86f00", + "0xfa71c3644cd985a15998a15399f85d2348bc513e9f641cd0a3cd4dd05746c4db", + "0xc0e5525fc15380092413659f904689b5f6d7bf0eebcba4783a2fa1633ef9b07e", + "0x1006fb0614a85c4110e9471cae67d1f52074847d31d300cfea019ba8fa508824", + "0xb434505517f7dea5b8cb7f3ab54483d638269630b766d95f3f46889ee06451f4", + "0xc480fb6381fb1d54d4cb9631ab7c07600b5a229c4685296b06707dd8781c997d", + "0x3ac903cf49b89306f0e2d17ac47ddb0a4cadbb0a7772ad8722d11a343c649742", + "0x14f65746f3eccd4985cc87165695a378f62550614d08f6125b23389e4cace043", + "0x6b83fe069046dc8720ab0325f5888a4afe2f2f128e686b901b849bfe25d42f35", + "0x2b35e06506c624d16fce41025fef1b4633c364b7ae7d3a36f253a8037d801b5a", + "0x9c4937b915656c509b39d4432c0e75d43566c477287fab2438e9dbe4b3ab7755", + "0x6ddd34db4f7bdc1c30a1551d79ec95eb56c7877886c35b6e36b4a50bbf595e8f", + "0x08ea952dff0a028b2062e9dbebfa4c53ceec13b2b277acb8674a4c33babe9454", + "0x0e221cc701a9f3e98de43ef9769068672f0c7677e56fb1a5519c8404746b7d56", + "0xf5601bd9f85de53feba2c56a909ca46dd3257ec0be8da5fe7d1a5806c8b77329", + "0x5c523a3dd1c76decc83879187ab5927bb4e1ff85b780e28faf484b0395caddd8", + "0xdd4fd3fecd641820c104f8d7e17df1f2ea58b53e3c35fd3822613a25f9228151", + "0xaee15cfa97b8b615a5bccabe8947985400d430e05420feee75887229bb297c6c", + "0x4870772c9a0b3b46068d3a2607348022828cdbc82dd993a4754e14c1f3c89d90", + "0x3d3a52bd47fbac7b6cc70e83eecbe4ef999cc53a6853d173d2f0364f166e4377", + "0x17958ce4d795e73b652ac2280c558fa7f0eb3ef00ea40600dff0f39d3fe445fe", + "0x1bb3a53a926a6f8161d8d531ccaf24d5d680035681467043afa4c78f33c72575", + "0xa9a1022e090868b26331ea022e40f3b724952df83e0f73ed16dbbbf0d5fb2f83", + "0xbe7d1058f062b8c32c3a9f19afdd066497797b54ac02206bbbc7fccece6fd274", + "0x99ae823b78833c5becd38a84600d7baab21caeee5893e26c3b30004462b58082", + "0x5a3e697bacb3e7800b7fbec843cc362c77c43a57a285228c77434d0d6c45c15f", + "0x727c71f2a307a65bdf5e689964f8c2037d2f14e088618223180b1b57b64ad226", + "0x38305b295ffa65f8551b83101b876dfa7646f56ce661b678aaeaa7c4d11cb6b0", + "0x128189add88cee670a10d30f6c5d9d169323ff91054a4f2e340bdc41844661d7", + "0xb9c6edeeeb6b1d3f7cc01ae353ebd1a7b7200ce374ef0822f94b03c1c1628241", + "0x64afd9951f941c2361e4a839983a2e5e1ec40f775e65a80d7c19337a178b89e4", + "0x82d97027ceac657eb0773d370e27ffc8bedd67cc5a247c2b6f50c4c220a6ab60", + "0xe051b114877b74bba56ad9b6778b59ffa2bed253fc10aac742f4c40a0547789f", + "0xfcb0e198b7e4df8d4311d5d4a8b560d0528e848c16af6a1cc76a78e13bded13c", + "0x71b16fd30b87747e71219afbf370510b137534fc0ce2fef1099f7d42471108ea", + "0x5e0abf9923344845ccc744df1fb50743988096cbd9df28c2e06c3769f3487dd2", + "0x3245ed452644ee73349cc25a75d0a2089d3afcf625e1f808c2de0850cc138814", + "0x4fa90f092e36f5b9cf24371825c3d42503cd26bfad2d3eb613d8adaa307b6474", + "0xcddbd5c1f7d791318cf47f8d087c606c897f42d0e56caab505ef1d0c8a9a6fd3", + "0xf486df149d699de7f0a89f1d3d378f899a0b575a9b8c146ee041b250c7452104", + "0xf3821ac434df446cc0adfc2b097103fff88d2952d2ce784442ecc4b8d37898e8", + "0x1268b0cda90fac9eb737b803115a0e86b1dede6345c4c7c0f625e10c9a76f1ca", + "0x25e107a2447adcd981bc76134be36fa92651e076bf9f30ccebed23183454d583", + "0x96dc1981939520ce2cd00028ce05a25b71721efaf16a3de839a7717487bd78cd", + "0x63be069f1f35f8cbdfd66c3a553c42b9da2f2f8bbceb5448c90fee8bedf81f9e", + "0xfb265855a4ad8b13fe69ec2fa25e9518dcead200be9d2db7c34680a9c3410ce1", + "0x796fef3b4352cd114baba1db0401114895cad818b863882ebb8eef03a78f1a17", + "0x36447ecb2582942c0ac71bbcd0f222fdf7768c8b6fadfa3679cc90dd619b7bf3", + "0x801387c1b0f3c1890ebd3df151afac696973d83cba4dce01c4e91f72d23aa543", + "0x914d0c0fc5128b5b4fd61b1110638a80eb6f5c97c791f56a76cd9861e0ec57db", + "0x7c05eabd2c6e6045cd591d39330aaef0a93e77bb709f01a65ca094574d038fad", + "0x384ae578ec9349687484bf7095bf11f72590a8de9a6888e2d33f009ac1f573e9", + "0x7ffa1aeb2eddfbbe5a659f9369f71b061b727e360424dd8956c6ecaa77076909", + "0x750ebae9b761f63de56566a193b1eac55edf7c1d964ff59e7229fc41d64a3c27", + "0xc9ad855e079e3ad5e5fedadc79667440aa4c1eea485643a09d076ed2bec7db9b", + "0x12cc806a00dc28e85ac2c56c375b9b9c8274657f6909eb99343793d584048382", + "0x81cca44e48a1af23a9f127c0fc89e760d18cd7791b0ea16348687f470d7d97e1", + "0x2c200efb120794c77aadb7a0b8443652279366a44c1d0cb4b286d5d5a566d366", + "0xef04f7061dbb0d6525fe383bdf1a9bab6438b76e01e186031ac621c0190eaf35", + "0x04c7b515524b72d4c2b375114669cfe1ee0fc76debcaf75835180b83b96bc0aa", + "0xdebed0c2c026b1e2b949470ce77fe43833af7bb5acdf4a85712a5e13dbd37d59", + "0xa9f933f23474a0c416eb01a1281179e6c0ef1f1b952a98d03a3ba63660529cc9", + "0xe0dba677aaf93e4919f440b788ec29f59282978550adaa8a2bd76ead03867d70", + "0x10536ef3e8c6d7631af5cace70da025d99e2fa39fe2bcfefee1ff219bce5e6cf", + "0x4b0325a93eb70ed2e3cc3b08f40cd44ba97bdfd03bd53c81c470b9cecedbc856", + "0x6b4b4a76d54d15920dfc53f6d8c705faae355fe970109a69ff055d66d3ebc453", + "0x8a23de9be0f51ec60df5d1f51b4188f2d9bf3abf6a98f7fa270ce14b3831ea11", + "0x2fa4dfca9b8d1a7069280252c93f95e415f75cbe5d84f86105bc9849b98d634d", + "0xfdf57b3c3d903cafb12c1a3676e7735b45485230e063895c793458d0ced2d68a", + "0x27d69477626eb7c4847370d01f59005856f0c466c92fd482b069ad38433ecc79", + "0x625de57821627ad45fa358de321248da5207bfb4d9d14e60ba2658925e83c9bd", + "0x4c622f418252e2c012f37cd822f7674253a399f3f854972181ec9bf488308ae4", + "0x70dc2f0c686c2603abef50d54eeabd0703e772a09bd794fe32bd96f95a4ba6fd", + "0x5f370cda9e36b3e50f9e2065182d8a83593857931e9f43018aa5a5a5bd38c85d", + "0xb2e97da5170cf17682f4ce3517f556dba9de96c4fbd4c7e525b02fab1988d2c9", + "0x24ecd46c0fe574dcd6ac2a6955615c53eede0cfe910678b186c20038de2f17c5", + "0x5e523ff5ce388c2049b83224318c353c22cb095776d18b956702e94a5f3e97db", + "0x5be315de6d3d52e5a70f66360a5a85e25303aa8f9c060c80e9bd170c16a25039", + "0x8bfdedc7a869bd7ce6ce9f3957543ae4b5b96842355edeb5f46afd6cc345e4c9", + "0xe7276c09f872ea02dd6114f7a4f904f85c1e7b0a112517b28e9e252db992e117", + "0xb7a53a3fad5e7406262ea20c4d887013144f3f10c02108efbcb6ffa3acfdc42d", + "0xca59b080801552b527c8bd7981e0a6a85144720272a320e48aa6d381ef64dc21", + "0x3f2e76518dd9237706161644a992b6111015cb1a26241814aa2485af7c9409bf", + "0x4890ec2a6d619bf69e7f0cc6d5b6028e2326d63354f668a4d19c4b7e4d1b29cb", + "0x5fb0315a7058ab63abceb31ff5b0212545d84a84d9b81f45f2f7965ff95c9e58", + "0xe5731818fc75df2d2026c4e89f921b50c767dd7dd50f987fea9f8f7bf7cae880", + "0x1ecaea54c6aada24c374b884992fe2b128b54b714e6e56187ffda713680911cf", + "0x92679dc9523783b5a807addd224097f198684c4a32d17769793665b651822932", + "0x9318576fa6b12adc6941f3873d97db85494dbd4c392a7bd7a18297b564469381", + "0x16b777ace95e2d2fdb83d254b3214ba9fa71c03ef8c1b1141feaab47f4e6706b", + "0x3732260d68c8fcedd0126275aad10b13a18ddd93ffcaaace9a4c190c1a4e49b5", + "0x8f6882b8a00b1fd92d86e697d1c9032717420ffa54e77dac92b4eee34056dca5", + "0x21a66d604299169ad1e497653774112ccbe17c233305588102fb9696ba00237d", + "0x56cea1078f003e5589d93bd5aea5708b12d39a433ac246335b676248f65cf0f6", + "0xc39987c272d016d0f6391a5958e3150b0b109fa01c9dcf1a6788653c8ac81a83", + "0x9d78af27163fbe55521e66358108e53943f9b88fb9967e6327cb586bd63b3a6f", + "0x39c4f370fa9442223f785cba2e401e54209c1a901a753a825138b639d0875821", + "0x647130b0b0f99d18a11f6c1ad08462f46388ec58848997ff25aaa372ebdb96bb", + "0x86524920ecfaa6fa3de3d0e39dd0c0728bd984d2094e09be467127cd479295e4", + "0x2ea419241a2d5f1efeb5edf1068c5f912c56be40f6c31d0a0b96f59b8a70618b", + "0x0335692d2484bacd550c6b63ae47a4fe18998d8598f53cfe3de5413c06aed9ff", + "0xc773f5b487d22bc3c6a35fc5cec42b63de4e1950d7c5d86c4e464ce36b8f1133", + "0x86710b387befb7af9d90579f270e1f9d0d54fcee0b4519d2540c4602ae6dc578", + "0x4f1661fe4d1bfc5abd2599841d639bbe1a6e545ae3bf6347c43e37775d66b56c", + "0xed26b01b5261cb6ea576454ff1612128ed70a678146689aaac4f4336c7269428", + "0xa61e9d432b5a03c3fe8416b5971c69653bef0e430c186fca59116fcc4a828845", + "0x590b6d297d7f0774bba39557a9dca9ea7223180e7dac6fea3523dff2be5bfe9b", + "0x630ecd54644e583c828c4c6fa9286a264dad4cd1e9ed66e730ed2e90380c732f", + "0xfaddd4878ad3f2eda3eba14f227363d8a4756f369f7fc11602745b67c2ccd76b", + "0x3737671b007567f54f265856d3a7570a0ae9fae51fd70c7b81f524d287ca24aa", + "0x0959db030c821c1c3eb3c2b71b902165eac34bc7e05e8757b6f97bdb750dd48f", + "0x0c812262e43d60764beb8824517573460de766c7aac2adcee3f1b2b419492b4c", + "0x3bc9ab7ea2ea5ff4529b22ec92af9cdb096ed216c1642c6a4c9dd143478286ba", + "0x8e7ec84dfd93f0ac7fc1accb3d69bcf8a8d0034507005ca5ff15db306b81746e", + "0x5b1e8acc361f184d76f0334f5898dc2f06d8871e1a5d672ba59da084ad30b46a", + "0xe9f8425cccf868bdf9185340862b2525aeb0e3ff3a516fd921830d2814873d99", + "0xf57d5e745fb7627409909ee5da5be6a3bd211bf8f78da7c3b3a4e30ed09fd604", + "0x30f61f4cc3cc52cf1801db1a1247a266177033128c1c9c48037be5a0b4371429", + "0x3280b438a3110e5aaef66be5778cb9871cdbea652c0a7e11fbdc18cbb1d72495", + "0x7dd9cfb4f55452a238b53f5ccd4e7cb04527826a45705c9a513f9ad4703a1c37", + "0x51021386f2deb3c374e6bfdf151feeae59e327f5d5d330c13b56809abc70c752", + "0x29059811d484677e695b3338564c84928aac722bffaed5e7f1f67dab4314ba93", + "0x619dd397e134acb573a4449b7c8e06ff61395af2865a628ba5c7f8d0b0ae9fea", + "0x4bcc431fa98ea826c2da35399d4eeaddd553a61f6e023601534cac845edaa852", + "0x00b3e6647298e8b94cba9c8c7ab6432b22e1eeb412dac764a6619c6cb81bd6fb", + "0x055db48bcc0f00943754e8148dfa518a653f6eca91315c27f249d7eef5cf77d9", + "0x7a549e90a1d68a3f3f383576ccc4b965749957b2417834636ea513412a63c760", + "0x0665bf5ad7f1af89b31fcda37389f02cb1952869e17cc283425d225e725fb91e", + "0xd11079f3f1013099d2ff6664516aec4ff7112373a32fd815fd24f42e7dbf9021", + "0x8bc49de47355f1ff93ff0656cfbf5e6217cdf9228df44b3c487d09c4d6a9fc1e", + "0xb705fd76e1d36a74c2e89e186627516106292f4c3f58fb96eeb695b9dab15b0f", + "0xce94d1de22cbe7536966d0a4dee161e9a09aa35ced516560c8a903228c68abec", + "0x666b1337390cdb9560837357d379f2614b4c09548ac08a75c9dab0f975107f22", + "0xe993a1fffb7c577acc47ba07667908676bad98522db2641b795811072350a462", + "0x905f3325076571f851df1b14872a5e5b525a018994d8ff3a3702d26cc94ed14e", + "0x6b60171a89ba4047f16ed175ade35d78cf37734da7aaadefd4407eea203a0bc6", + "0xf0e279959837a1751e5b198b7418fc0bd8514970b90f6b0f40bf5224d6654ee5", + "0x7179d2b6f5b36808f63fd04c0371a552a6d8481b19a3f3ce6992711fd11df12f", + "0x761553af4b4f923150c2bfbe600adc493f57f6aede87760f2ac8fb5563c3bc52", + "0x996423c82699b88a360bcfaf46dffbc95cdf36c8892d1df33efadf253504413b", + "0x6546c1cf4fa90881410a3b6653d06a4656731dbbf560d97cf9ac2c6bf8b931e7", + "0xb60024a4416a56a271dc588b09a5c666bc70e8138ce5a425897ee37885b2993f", + "0x9f6e30135e702674180c69f06fcfe223318695440afd15b8605bcd2c96887262", + "0xaee344ffb38d0b676db29f530577266b0657a9d06ac2eff0665819ab80ddfff9", + "0x75cd35eb1e324aa350b589059d604c3c020a0bd6d05176fdff45127687e78245", + "0x88a06a5a1aa084c359687a2f25040822ea285142a796865f2ff6469bdce4dd26", + "0x3f9a13ca68ac93d85fdb59524965107129a9a289974637f46bb1468708665959", + "0x0785ccca56e287a489b5b810b553bcf447bd75428601106eb42b793da8d77fcd", + "0xe2e263fa318e4c70b7b6880a5c9e970c033fcbb267dd9ce3e4dbb98a219f1ba8", + "0x013c5f357998b1822109cd823a6257509b4d6d0ba244eb5319ad37f6fbc2582e", + "0x4e57680c68f31f0939f138fd2c42974a1af827af572759f3904e2acb92e2a4e7", + "0x5fd59b25ad0fdc06b3bae5aa01fbf14d36ae4a524a640afac1799ea87fb69965", + "0xa55afebaed7e82bdedda99f4db44d9aef4305840825bd27821db2864f1ed30b6", + "0x23cd48f3c7f5db1ec444ba1da68e072327a5cf9a6b930bd5541c9edab46abbcb", + "0x98217a7b4cafcc5f0d7f31a06cd2d02ba2fe3971d8301029df6941382ef31ccb", + "0x909bc16ea3f89a297f7b11c1861c4575151fce8c3bb7474db764a574abbc5fd7", + "0xa69a748de1825a5e28bf6dfd4ca095d25e6811eeee03b4fbc2a6a70b055b722f", + "0x00e6f1e1a420e6fa9cdada4ee4863a3576ee65b08ca411c8cc43bbd9b9eb3b35", + "0xeb2890b4f7c1dfff13f6a915546a41877b02d4dd9110cdd4a8fbd4a1b7b1a356", + "0xf22fa98106202baee1b086f628bc94feff8b82971442884b679f2f8753ec47e3", + "0x686a720131d8e2b14072dedf4482aba7c542c396439cac487a1622e16c5a8b81", + "0x07b91bf5a901816978532014b500f96735f9807de27aa1cf1d51d457cdefb0b8", + "0x5e8c427a812c16df4c3cb7930c105d9b5331b03059bfba50cd2af6e27e0012cd", + "0xee134610cb36c9e0a3b51006b8715db178b007e35da3cd5a3f17254188376392", + "0x208f6ab18500c5bbe2015c0144a7c775935768a3091a3ce7ee0c2d8fd8797206", + "0x4b67c108ff8b28865196867af9b24530fe7a1ad98e259414c7f21cadca8d5b52", + "0x06cd330ebb6a5ac4681c58a3495b33d61e5bc10019fefbef36f8a00f45eb3c9b", + "0x1d358f1c3745f7a099ca4a4138c898f5f83224154dd59d65006087f5f49c6574", + "0x1285194c6cb5707de8f324c3fae7e51552420c30797b0d59d0d39e5d02ce402d", + "0xcda3293d8c09022354568eee8024043bda2c93c5f9ca9497771ef85ddb37fa94", + "0x10a388c1188cdb14bed20a520954ea5c1908bbabdb6405741cebdc32327eb9a1", + "0x196762f8ca98f4e78a409db28f9fb30a3bd08198fb022a77a9036dd28e0b541a", + "0x48683e3572aa647dc4a29fb0336023b82baa2c5eb2c2d727ef71ca75a66445cf", + "0xd31c4bdaad98eb24da71f4ba8901600e47928422aab194cce458ca5f59ccee63", + "0xcd0c97b7a47bd6ac092382128316dd0122dec900f021d49cfa6461fd9b11b26b", + "0x631b7bebda2b8574c5609d6bec96220797ae89f88c77c9cd2ef6c80dc0d2a37c", + "0x10e30cd9d1c7e3d044059f22b9c3b84f34f29229b263e149d54669ee3486dbe3", + "0x051b571fe2dc2b32ef8b19443fb586045206d9382dcd0a54d02c2551374db41a", + "0x8c75d5c9cac5487ae3dd7011804d5c575179fb49da66932a96473f44455c2c0d", + "0xfc665aeb5cf4068d711cd58f37455631f656b9e618adb9d1ba07ffb684298aad", + "0x305eb411335b9e6bc5d1103e8681e430ca27aa0694236acfd6ce9b81eba40744", + "0x60ea2900172bd0bf5d05766b8a972727fac673067167d77836c396804ee601ec", + "0x78f95eae9286cfef10796fe3bcebb6c0f280a9849488defd386bb443cbc2e74f", + "0x876ecdd62d09cc27b39cdac14a5a48a57153bdc9d6dfa298947d21cb71936e47", + "0x05055cba76f4d22942cd140a6b115414109168b9d899eb9fa8d3a23b6819264e", + "0x46e0baf9a1933dd541d8eefeaa51f6090b2f4890c06f47a2b53a09413d5078a0", + "0x19533b40f6c28b7b5b2f1b66163cc42d418eb43cd7b5563fc4e01d409779d1d2", + "0xc0794c7af0ae385a40a766d1b4141615101c1c65c242890f10a4260c6ff8bf1f", + "0xc66391bbccebeb06b885aa6654a3ad0348feb7dfc08bd58222abf9f8e4554f6e", + "0x306e057861877a714e6280e399fe06089c2817c3bc0a0117a77a50776fa37f52", + "0x297683b47e794d815127421981433b4495b3d8cf0afba1e4f027e90367baf634", + "0x2dfb9245a383ccbc23b367fbee0ea8a955e9ca49eea538c8ab0f129890cd2d35", + "0xe032d1107163197c76df5a9120f4d8d571790ab774780bd82dcce614c2eda9c7", + "0xff4dd1436136a461a0ace023b9a15ffe034db7165b3de2d24227994085b3a0ac", + "0x5b1b4fcd92a828b9e282aa3162153de515f345a953c4f8da31254bd1903fd8da", + "0xcd292772020966675a7c9967db28b563489ccd6d19a5319d446184c43403411a", + "0x876ec1088f57d67a22865786e10ac353a50cba6e1f468c55636e05d28b9d93bd", + "0x6cbd643a24e20992a7c1a6d6fab5f2dff17d78fd67848fe34aeb9b1a1080ed2e", + "0x171dcb39fd244d90b7a86e6d1c6d7bdcb4d79388f1363d4325c9cfa88811ed0b", + "0xd06bc2ee4aa3c75c851920b5bfc92a1f1ea8be5549e1e264295faf9b2d9ba2eb", + "0xc803e39d8441c5a0ea2b3af8a1899227d471c153016b8c895fce1c63ea431fea", + "0xee752588b555c79e43bdf7f8bb1b8f7b262d070e38b01ebfd76a219a70521cbf", + "0xaf62f62a10dfd26fa8c91cf51f5c2cf0f6427a13b80953c9a00f78e0c54dbf37", + "0xce5cd4fb28e5cc589d22bd1e1550414a2d949e54259fae32320ae8f4a0cf31f8", + "0xfa32019b8e81435c75f77d9e4c6b173f67c6cfa822ff8cf4a402ddde924f1c0c", + "0x907ea95bc8a729e6954c2235f1250def424cdbe3c198ebc1811747e079aaf14b", + "0x1691a83839904fe83435eebdd8776fdfb89cfd5e86cc23d04b0519c532634851", + "0x1f72a75de6659e04c99553ded87fb12f4ba44f29ad146abae31bf3e6cf4ef7ce", + "0xafad7340351f3b8365698a344cbb928132742e762557fb91a2aa5c521b3a9a04", + "0x3f8f46e8765d7db0a1e292906240e63962cd9745cc879e9b4e5ab6ebe425e5cd", + "0x9be909697575faa7c8d413d10f33ef95235dd95b639d796cdb892865f3a4d4da", + "0xb5e272e1b917b9809424e2d45bb558524828c2d667273d9ff86911ce20d5c4fd", + "0x062ea67c2e696e9512b65e9739540ddb44786edb6e6d79165648e226cc8108f7", + "0xaac5cf2ac5b7ca8754a80e916b206b34e378cd77cc2f20b3127f43a9dfd95dd9", + "0xe8df9c4d925f09cb6c700867f1def7157095e48e439a60f6f45980e714d4350d", + "0x985b5f10a2f49ffe6bf459f2017dcdd3eaf8c41b58965e2de945f69007dbfe33", + "0x5b0820caf4b5e529b4ae312f22017cea9c1808ae43c74a1e3b80bcc8bd150f4a", + "0x80c85c31d97ff0ab0cd57de2860e1286e0e421eb3985c8579152af4c8924b0c4", + "0xda61efac587126ad8b614f5f09fe0ca1f20a08c55fdc821a52b81967e2b42729", + "0x4628952bf9712961ba59395475ada23d998528415567204fb053d127b4477e6b", + "0xb1c9a40f92ebb179b5a26027681ea199fd9ada142203cd6e7f4127e9ab5d4cb8", + "0x0d2047590e8cfb6ec4de9dd9b45a57c68cda81e47a2825d2a0f8e247dfd9eac0", + "0x7c52211da5859dc159c55ab931edc51591b1cc40c2ed94e3a1b099ecb8461b88", + "0x0ca4d9b79d436ed26fa2b572304810eb84bbd488c00c049196b964a30d0113ec", + "0x7ac588b2a30be81a77672044e8a9edb2473b5dc7dfcedd16eb6dacdf494be0e0", + "0xc534e2721c29f1f203fdb4a52d8bf84512ea537839481c81568f49bc1225e27a", + "0xfa257d5e9411d9f1a79f94409f76dddf0acfbe632d8e23688c29cedfa130d746", + "0x67c4e10fffbcc170abdf547ebfdcba1095b462789cb80c8597e729183e1c7572", + "0xefcde5a9e338849a55d6214532fa1f658cee43b76c0fff9df02c78c785cb7b27", + "0xcd94f9dfac150d943564efe28c4b226627485ca539f3cd7f829e6e908db35979", + "0xb6085e2362920cd7a9d71175c534b2e4d39b67933af0ee813d15319d8f8250d4", + "0x496061782e869574cf8548673121d176fba013d13794d0285598faec125f929f", + "0xb14dc754b24d5ec2bb58cf81c0a46a0247fa39338aab967384ada4dfc4364f8d", + "0x462288b2c173b04fb2296d5adf61e7e494bace7957a42e799e9cabc0dfe3ac83", + "0xebb65c35807154934b78c8d74135206d6644a775f675dbfab95c8371a46673cc", + "0xd55f0c17407e403125a88d451ae7959c49f681f1829a4d6daa824a0f4124608e", + "0x9dde6ba8977b4e08667ba3c50b885f13d3d060cb6148b0abd32c3b661d0376c7", + "0x55fe0a4da8597b69e6795c4382b634950912b30bd8257832e9f7aa01e625e75e", + "0x53caa4154558b658e39d63ff7dd0854e281b9c8e33c82fd8274376ae68ce4179", + "0xa9bc3f833e77e6118750b63e1d2e3ab2d17a79471e9d7dade2a284e17b6cdf20", + "0x3113450c1991c2365e1dedc30c004ae21f90962e0f543b7c33ca959d49503406", + "0x4406c045a865c0a652ceec96e7c7af18c3b95450b15d5e78d41afde733a684c9", + "0xd66d2590826f096c566df26616a31d9678e61fcaaedacad51ea1208cbd5bf795", + "0xd44bd8801a56af06b25e5e875e67c163fa30740f3d216229c679a2954d82c5cd", + "0x60c37fccea28aa15bbc9a95dfbabfd78d04ac7a35a0b4c14f15ad96945b69fbc", + "0x2698ef0bdff39234d0867513be3b66da698dd750739efa9aa9f84091aa186a6e", + "0xee97c2d476dc0eb0e62ac4863c74cc7bb59ae19a8c0bdb4945e25bdc02c2cdcb", + "0xa106b29e29ed9157a864d16d45b8a6eca9ed778a61361ec15ef28243993351ed", + "0x9cdd0133c8a7b3f2b8d287b05512a905a0a7805dab2fc43d8caa83e5026c1307", + "0xaf8f9a8528181e2142cb97fc6fa8100c7c1568fb043dbd92c93f4f093a409638", + "0x4147e1021762d235d783b56997ed121dc82b048e7ca121680eaccbe97a237032", + "0x9c1a31afd83fe89ede631c7020dab7b80f6540c3475edef03a5f40a7d7ad88a7", + "0x9efc4504e17cda213f3a3b1ae349d438fdf3ac791d542e2436bca0ab94ca5304", + "0x3ad62e145d8184d971590be776c77f38eecec31b0dc83bf42ce047894c8c4a07", + "0x2029d50236e61149a938ad2726c6e6aa0e6728a0fa162c48cc3dd570681e5497", + "0x0a6df8e7bcc8c5c9a7e13a126fc3eb77af3f6bfecd110cb8bf8eca7fecac4574", + "0xcab90dcbc750713ab7d038a1e79368e3d4f969604582c52e15334a00ba97ef48", + "0xbc1833fa897d604335c22c10fe45daa27ccd1d0acb9783f738837f0ad115a9d3", + "0xb5a4226444f4013abce38de1572d7a95933a49715cfba7c6c16a9688db55ff86", + "0x8317d0ff9a057bb2c62a1634167991e71de3f1ff836ad788f2e9da2eaf371fe6", + "0x8d6e920380dc7b1ce15fd44cdcb379afb393d7a3744a1e32d9a67010957092e4", + "0x80638e86c6d5a5e5bcff36f05ef0936cafa699a2cb88583f6b0cbeed5602d969", + "0x21e33347e0f53bd1ccb6b22c6593306f0f96d0cf897447796c61c002b1c462a0", + "0xc2b49e94700d3f9bef61d1132d30d297cdf8c27c348ac0dd37818c9e8057c38b", + "0xd8c8ae125ac0939fa973e836eb3209d96dacfefe26cca2d36526fa7a2a5820e3", + "0x5a05278504c5c6aee0aa945e70d175dc1afa17395f8af61d358ae55356a17d50", + "0x1d1fe7bc259b77a1bc6b826c3369b98ae301d47e35a300b7fcd63cbf88ecbc28", + "0x60c4cd5a7c7d22577c0625a2ae717bd29555fe0e6010821c6c801d7dd7aef473", + "0x2494f17945c735563e1627506324d479fb9ab2a01c32374110d441d57bf87b14", + "0x2ba5ee1ecbcba851945ca5531df25ccf4783b084a8c0231e9caa234e9b9ab25f", + "0xa483fa8bebdfb14238f470ba1282c587ad1fcc54fcb34e2c13aff9311fb6ddde", + "0x1109db705d56937250892c347441d64c461a3fa28a5513264a1a82dd5cf497d2", + "0xfa28de144af8d7d0593bd3ad09863ee4f01d960896eac5e01692389bc971ed92", + "0x726d5eb5dd2be92a565f4bcb0d82727c02349c36f796b70d888da153142ee122", + "0x745280b75dbf9e2f7b0339b13e95b86403d48bc54e57d694238a4199046d3e4c", + "0xc01dd3a56f7147fc4f4655ee6f41c5bc987dc861077c01a517e71b1aac5ca600", + "0xb18789f13884ff8e207e0af8d1b2ae90a96efb38ec083c85c92359b1a523ad3f", + "0xc0cbb9e3eb8fecaba920c63eca3c8b6344d4d608ea6a690f59bf95810c3ea424", + "0x7a44454fe998bf337faeccce4ea91b13e17448969fd8f54a88861a863fa084ac", + "0xf8f332bcfd6d19349eb4b1df22e4e64f6f15b6d6e228dce8487638101c7bc266", + "0x6a26b1aafad0d84dcb46e7171b96de89d8fb2c91895e17c95e30fc53135d7a9e", + "0x1ee1fbc382e0ef22f6b469b4ca5fb8fb5cb1b4f724da670aa0b34f41ca680f9a", + "0x868daa362eefc36bb68801085bc34714111c79564be0f414aeba483b20212b44", + "0x72fde6335e163a7391b262cdd557cbeb68d12b145aad078855ad19c7fadb6759", + "0x1805834e8541e70f1b997268569d0a61036c85eda7ccf203d8067c16544c38ec", + "0x0482f1d3560e173831d54d3bc0756400348b7d83b300719ec98d1ef44a307890", + "0x62d86e9a169790fdc4cc0d42c31b2acb29197f2412a900251811a81275ee13d6", + "0x6a94212639e4091348d31ce0e936a910df7edb8068af9a189bad04eeb1f70430", + "0x7cf4e065cc3836adc28cd4ff92f75b77b77c8dc3a048b571a1d0c6cdb896671f", + "0x2d4e012e497b8a7f944d1eed62763a62bc34e8ce5bdc6e0a94a92f4b2271445c", + "0xbb0a682b0b861f24f3dfa07633ac93db6c8030abdbf10383047518f0e33c5c87", + "0xef2a1dec41ec136bb22c97f45b7d27c47ea001b32a6c522cea740d2fbbf9f68c", + "0x19f6747a4dc55fc04c2dfbd312d24482195f2fc7051d631cb6b035f811b9cbfc", + "0x75a61bd26fbd84db128ea208403413aed4c1a191bc85add1fa5b202b3934119f", + "0xce363dd64f88c94fe0e0f1b0a986397a9451a00215b19f47ae2f89ea73e009d3", + "0xc61501ccc6e266bac8014200d7a9e67b90d7391e6ac17aee353d7001e6e6c295", + "0xbb60812a243a8e533801fa84680d1d853e9fee9314d23fa1f6588c2ac6e28d5d", + "0x7dd9a83d4c625adb59d176cd51b850794b418d943266f74494b4c3cceab16152", + "0x15e847cf4c34ae586a877f4ff2d158bcddf0e61df93dd1bd866f21af66c7b0c8", + "0x0bc297a726aae1d4e3cf2adb779ad9995cb32671ce18604e159b606ef964bc48", + "0x5131b60105166a5e91d7f506b9de61143c70c450203995c26546a715bdc711fe", + "0xaed66fdb230b7c2cf156dfd3564a7c5a354b79df9bab6958a8ab55a56a4e4ba1", + "0x2a7285d880cd19a7eddd8ac7530a0bccf3ac497f561cba558561008098b0b6be", + "0xb288b3238fb310fd8e4a8b68421987cbff4158b7ae2f9aa60fc260ca25789d07", + "0x083e307bd6a391319dfe090af1dbe3414f687651531dc8b4befe794aba1a28d9", + "0x1a5fecbf093a54ee0db2789692a0269635dddb611cf265d17bdc00f50669b5fa", + "0x0a3191b2f54ae67f137e63351ac5d62913eee539cdabbaf5a471d2698b9b999c", + "0xcced7db129d21f26e9ee287bb3ddbcf8e64dc4083d52eb2a4e2a70bc0f8ee60a", + "0xf995d09f0a92aea851fda99a1bc8cb60ee58fbad2611e9c7ccde6baff8957b8e", + "0x21dbe9310292c5ff01406b8b1c00ed29746025e42999621cc4587a90dcba3c4d", + "0x9368ffc4ce7a6ca72ce797ca628a6a624e0891bec0c1a290311d2712dc021156", + "0x45728c375d426802b60d327657dd95783bf96cd9f38cea8bde12213fb494231f", + "0xb64d24d242890e9bd0dcd7477dc56358a18a1b21e590b5c796b61061ae3b70e2", + "0x7d52347884ed55507d468cb77f9a16047f304ec3a17f6623c10fd9c9a91a39fa", + "0x5c35a5ddbb5a2ced51e659711fc03e6494a522bf7e3a37f5499830a74dc2b68c", + "0x01d4420ee02d32c15aee1692cddd25a2073980a0fda793daf62d78a47b29e97f", + "0x28b028f51a5d53895bae862a12c963f0f41a09343c5c6c8cdfc16a34020f0442", + "0x46cc4db90a7dd827245ced71c56e49d113cb0c72ae33ba8d54190cdb259d9e65", + "0xeb95c71e0aa281c2206696cad5f9f15655decc77199a344c7e0d274619819228", + "0xb18d594602c0b8e19b66d6d51d165846c11583e235c8ae62a02a7596b26ce581", + "0x993beea57115e7a9a276b041dbcf6142d5cd1e3cbbccc14654834e73bc15e2bf", + "0x9a181c8207e33dfd4e5ea326b8b443c68b4513a983e5f4a0518b067d7692859b", + "0x43932438435f88c8b09b35f4a8b66039ec998be5a87070244c37e15ffdfd5dff", + "0x15f5645542ddbfb4c04f347cdf0c980905b5eb67d6277327f49628b9ab358696", + "0x2522be1ca6cc71879cf4f2f3df2dfe90e196e5f4df7755b380c86f4944ca5e08", + "0x3af6e27765b3188e024cb05b563bd9f56e8e693ac6c74d26a74882fe0cc550d4", + "0x19a702215f0bdcbc46c4efcb92da64b6e3d9a86c2b400d12f963629deef9e4b4", + "0x1dcbca6b91a61330777be1182d68fcaf9ff0d9c43eeebe9ca9c4ab8abdc41bda", + "0x02eaf7a3396487c7502463311a6277e5f69380ba24caa81331298baa9c63b08f", + "0xbadff92a236658ec338889b33eed9715df1905d924ac54d611f3793b9b3ff176", + "0x79bddded76b993af9d5bf07e210f3fc6d4327a98dc54ba13c844ced73a835948", + "0x0bab4da8bf5c6b9bbde53b0c83f7d940b3acb5f5743ce75f62a2c15a1aecb729", + "0xd2b01a6d2a3c0d88bd41c17e60a95f47cd119927a7e42413e56ce5dc8787de12", + "0x4f1ba6859422fec2dfe05ba75d7871b2690d5b0176532ed3a399ebb814091a92", + "0xf0300c27f2f81f794c2869fb7a4ed3f409e642f420169e60386b2d1105d0f8b3", + "0x87b35d08f7dfbcf77aba650898e5143f9f93957250c2c1ff443f0ae875d2b54e", + "0xfc665f0179e97e16f678fda2a0c1b31da66e891844b970c10577d390fe0bd8c1", + "0xeff88423b02371916dade6eb5a86f01268815678eb91171c0797d1f0aa194e41", + "0x2904cce349847eed30a417e9d9797a105d6133b26d3f4d913494d7ac383e173c", + "0xe1ecfe848a3a5e6d6c919b10321e6e762715464c155ee5be6ad5c9a588f158ee", + "0x7659c856f0153fb50558c9df7900e70ae1d07cf48229234f254cc56a3ccea5af", + "0xe1c96d2c8753532435331980144223d6420bf07daa59e82ce45ba0c11e2ea051", + "0xb132f3be09c4dcdeee13ed8a2009bc0c578612de34014a743eefdac08f69cb7d", + "0x28173067327fb946538eefd129ac9f822d392b5ec76c8fe1f10f2ce2af83068e", + "0xad27f2e92484df9d619f407ec2b4e38c18513a7af01e6de1bf20731bb52c9eaa", + "0x664477d7163c2797ab51ff749de0b6ea22bc9cab783604f3bc1f81772eb554f6", + "0xaf31d519224c5891358b4ffac9f87f8cd35827f645a96ad9b0a9befcbb8ab870", + "0xf68913515db527a7397d322d9aa517aeb83878da9788a9a643ac713cb22abcd1", + "0xc27cc43a1eadbcc82f1fe7025006590322dd9fc287e054c0902b9e49d0ec7433", + "0xaba628c7fc30d83b1ad1db8fb1f5751ecc44b420a3c3287e3e3f6257bd896b3e", + "0x04a3af92b1e3e7aeaaa9571ae392312696925875d5b2fc738000a09c5e59ff1e", + "0x5df46ba1de0dad0b92ac06736516073185d816c4391f46b6ef6ce2029a21b6a1", + "0x6ace0379f6c60e7926ff23e304db11f2a0f157f4c98dddbbdc23bf88268332ac", + "0xe40ffa54ae497095575c9783f0ed03c28a0c12dfe68c16bb9d8671e5d30e4e29", + "0x2c72b6f49ac763f6d96824c58d7d5da8a02c9f281acceb4dacd423fc7bd0a8cf", + "0xdbb5c5f0407f7964743370c5e9ac58ef217ab1e5a1366fb92b428859440fadb2", + "0xbedc55f65b76a6ad3dc13d5d7c9509b2c0099b69a940556ed6a5727aa2cc095d", + "0x4b31681a06db2856098aa0710a2747e725d1067caef42d6dd9a11c0cedf8304f", + "0x42bc9650a26d47e3e3a8ef96a7323a619cf67c6f6b50f52225ebdb8bb998745e", + "0x46b230c2f457cfb878ef0a37e94e7899e87de00eac9bbd622491c7a66b543cc0", + "0xa1b61da03511a16a78b088d31ff8f1f029b812e22015f329950d52b72a145e4e", + "0x8f66b59429ac66c05ff2421d78faf64d9dff6784666aeb24764ad20dfd9bb6b3", + "0x6023e25c4636da9efb0808604b628de4a3396171504dd92f6a0b15af4dda71d8", + "0x5f588624cf59a57345df1bc7b4102c71b40192c5ada3d752be4da8f11cac7f3b", + "0x0e926bfd2d2646dff25ee0ea7276b22e54f8e999631997ea1c47ab00dce1a0f8", + "0x4ca751e3eda2e457311a642e0cd758a30178525ccb2dd0541a9a9d9493309b11", + "0x85e9e617c10999ba7363e839d5552c55c0d0f9addc63f6dbedc4ac66989a2634", + "0x81643302113f1656b0399a79918100f8a8782d84b97e77ab8cfe50fd0c6bafe0", + "0xd8c529ad44a0807f1e158626cf3efaa55fde606a6df79df80ccd2625812c536c", + "0x7e97688b4c5a2f7138cf9436aa1f71d7724197565f9dbcd2415c1fdf97a9b70e", + "0xdc9b6af3ca940343bb79cac69d70318cd9162d17cf32ff77e4f7a372a5f8373a", + "0x3bbee82242426e5ea6d0b7d33fa1e8ff6132a0621612e7eaaa12ecd91e0ec009", + "0x45b81447d9ae01a109be4a2c7e3f9ee425b76de2a7d8435a916c3962e37e5c50", + "0x05056498b2e8fed9dc5001f9ab24cbcaab9de8f29a9311c307075577acaf0b25", + "0x59a845afc513cd2e38ef638fc36c0e1bffd7636866457ed30d915bb1f345d3ae", + "0x0551039e2580b62a1e09b371c42289e37e2b0ce110cc2494132d555cf567b969", + "0xc8a7f5125d61870f632d10dddfba40badbde558c2790371a65012cab3fb4c148", + "0x2be25f9dd47ec1e1ba42bc3dcf2aac69750b124b36948396babf4d3ca7e5dd35", + "0x090bacdd16b7f0ea693e2a2c41e9f111dee8a5de5309b09e3d4bc426345f8995", + "0xf37f5f98be997d2ecabd3019fa57037a18edaf106e12f0d4f31de77d60403972", + "0x688da240928620a03ebb920424d407fc1643f238051ca86b24645562e8ccbd95", + "0x154c62c1c40f1eb98305ccb23c1f2555819fcd8b4caaa04b7eec2536f43cea66", + "0xdef4ed8c27caacf9c35479d0ed8fc415cdc5452a7a445af16fcd691b28f3ab2f", + "0xe54e5db4d2644f653100b10409866c8dc61844217d3326e7fe3dcc094ee74b84", + "0xc58d84f9ab8514967c2a31146d200de33588a0ea4d77e5574ba563ac6789f987", + "0xf7cc63e55ef6f65ff633a46cfa9f164e68901a1fbe9682ef65dc47eb16e343e5", + "0x98c741caa63a1f33d8aab4612959f748286a1285b46d3a5551d9bf28ef830182", + "0xbe29b2f2be5e3c9b716c890c204a9da445af5e6b4e50c6402a93b32f42ebac89", + "0xbeb0e76ce9106e020487f0c981a557164d46356736227d81cb2eabeff660334a", + "0xf67ce427d55fc9311933cb663e6eb469bd130d69e312fe1367caf2c7fb1277c4", + "0xafdd0ffb7e7b5e90a7502148d9d3134f7677a6e410122b908469daa70224d553", + "0x8f74c65c0d22705a3979a72b8b3021a0d0a82687ce22615e51108a33b4168a7c", + "0xba35a0f2627ef78f55548b5d3ba2b5e61e08e30a077b7ada59bfa8e30c636da4", + "0xa045b51f64e3fad57989f75fc10274dfbb26e03d0dec1aa9b8a020a77a452b26", + "0x0f6d2689f323317fe066af81057ebb50b59cb1b614212f950527dfe555e1e9aa", + "0x17281c57fd0b47611e9fbf51e2bd1c9522408cf2e738b335310c499628eeded0", + "0xb29c157f1076789921646300ef81051b0645893705cd3b522e261f6abb8b5ab7", + "0x3d491eac830429b90e939a34633423fe28c0ecf512db003acc5672329b5140f3", + "0x99aa35ab08d27ad4ddeb0af6b041f1ea24f9cc791b02d69872bef66a9451d7a7", + "0x2566d0b64d3be342603d570b118ff75fb7f26a3b9acbda07da417e9d04b90832", + "0xa18155f9663223b728c0da042ba14633463a03c88d8b53cd3f119bff9e1c72bc", + "0x0046250dbb527137c049b92e9bf61fbeabc8b9654d8cc920b8f5c2dd67d94c0d", + "0x31bcb0b19536109ef1010cb37a4b6eafe2e45cb1e4acbdc590a94f75d735efae", + "0xb03c3b34fe5738ff7595fa87d228e57f1f3e689913ac7d6e46f4bebb6496c3f5", + "0x34ccd9475562aea89fa44e7f41f1f05f821603a799afd7d34793f7d41dee39c0", + "0x3f6c646a7c1c2f121d4d780b8b0705b9031da6930c643bc3bf491b9f28a1886e", + "0xf78ba2ea52a06e703bcc56148e29c6095dc2c3fbcf245fb1b668b752d5981a8d", + "0x9c3375280b4fd748a8025639a52a63f0523fb8fa4d3942e0b358a93c286f061e", + "0xca6d5be54183e0b043fb521c6f68f686eb65b491af837b5538b8cfe448f8c3ab", + "0x320d1301fc107767ee4da3eb0ce9559691fdf9361ba97f9d4afb1088acfd9fbf", + "0x2665e7d3351b00a718812ecd4ee2b3dbcbf8b8311d83a23e7e96045c760deab9", + "0xd62ca6ba780efa5adb18bb627e479b296a5a3f30d3e21719388f1169e70ba59a", + "0x280ea84739ed8d58a98a894a320134d7792f5082cdd309f54561fd7ea4cdca74", + "0x6931c3241edef29f03d931debf8cfa5064587c1b21634b1caddf0b3ec985bfc0", + "0x55590c1237ad52f4f1d27b8034d61cc5125fb51949be6d976e09529130cc9015", + "0x53555531ab8a761e2895908e8fe8c7d7859d7f7413e7779d25e0c7a806f171b5", + "0x14505e0d5951ccd97bcfd999099f203ed2c47d917b24934ab1a98713c38af21a", + "0x7baae66ad3c2935e0145c8ddaaab9a3ec96608d4fa68130a417a419a7c81af76", + "0xc2a6a51a242ae696131dd5f1e5d224ec7915a81ff50849fcb82d2e9b219e0769", + "0xc0f362fa6bd1c9f3f11b48e786f624c8da05c41a1c6943822f806bc66c966273", + "0x577c7d22b1c2ee961eac2d5ecc2ede8d08caf245a6dbfc3bcc444fe66ef3a668", + "0x09ae4c551fc876ca178f6e8dd2a0dbdd209ee93f8f1010177182d4f3145364c0", + "0xaab268258e1251c31edd3b9b8025b0c1f5b1f7e1e62e92593da795d86d9ef5c4", + "0x87f0f5908a682671b151edbfcb5898f004b289537148113c42d0923bc15c5bee", + "0x06bf3127f27eb60c9cc4a74629a66c44dbbbb9b68d65923cadd0ba28f1015cf8", + "0x23e0db958147b5c8db869369ecd816d7ce815720237ddb6da8a05e622e1dbe20", + "0xd4ce983759e2f9710e80b0ae458eb96cca9867858288cd2d7eab982f7dd26a66", + "0xf6bf598b019ff749002bb402e16497b0b3c9ee63125a13be881912f7009ede6c", + "0x7251bf281e9c72c043f57d39418ad568a5bd7e4710eb3e17cb42382114d68203", + "0xd8145c5f2de332ff21be501448d091385ffcbc56c41dc70a42707069159befa9", + "0x2559c0a30e9163f757e37ade5b2b5eeba9a85798fb0613ba4f915e50f2e57a2a", + "0xe3253db4c4d9f03f7c01d7c605e1d5ae6731484f0c7c28890fe6f16483a2d43b", + "0xd1b14798b014e64205914ab3c005146ebabf3f638abb7916371bb1eb3b5cb9c0", + "0x9e926beb792042c73c5f56b8b80b96b7e61f5c330395059f91184e9cedf176c5", + "0x4faacb7e85312a3fc3be57a617971adb47c90e01a62494beaf7f98aaec6a4ea4", + "0xedfcecf1217cd00b3c310e10e733f7ea5bb65773f5c1facfa73e4c62098fb07b", + "0x1001b41b4895a70d561f21e4db156bd3f1d3e295f5bfc1d5bd141f3c1a67383a", + "0x77566c4f063d17f44af69bec97291c7882a5dfa6b2082b3781af1ae58cd31591", + "0x0f3947cd52db657baf53a7739ce90d6d20a1afc29afca59171bdeb95b179f535", + "0x82e7731a0ffd6859e2780dc0e0b416b9644900ff324eb7ec3fe14844548dda7e", + "0x73029b7da326cf66bb0465860faef079fb04d1debef9c196bf4399df9ad07c32", + "0xfb838099a79e229c7377dff94c4c4776974059980e35d7786ab21433cc066be9", + "0x395797b06377d50f6dda53d3e731229d2e66bddde5ff58c3d4dbd30588d29561", + "0xbd6366c58c6537c6596809527ece0812f6835a7d636b45dcae3cc9c7721c45ca", + "0xc3d33249a30bd802b8d47fbbc04ae4a312a46ade5c4ffcfb11917dab72d053a0", + "0x13094e2e787e70f2d53700967a971ccd0eddafe6cd0b2ab64dd45e5046196f5e", + "0x80de11fb3db309cbf33068fcb4768291bad907b2e6c5c701a16e751535224a85", + "0xcd697e246e53d9383b5a2bdcd1099105d5c03b1bf0094db4d9b2f4adb014e780", + "0x30e146fa5cffd44b897be7150ddf15c1545691d7767c25628cf9de4754fdfc42", + "0xde108b7e443d5a3b671105ed6ebcf9d719d4c816e59afa3171cee1bc8e6b2d8c", + "0x816e555cc8140053e16ad4deb018de16c1fb7a0ce764c54d0aab2ae217815570", + "0x92ddeaf0792e08bbe3664818fad462f121976a67a073ecba8166cf2efba5ca31", + "0xf402ff440a86d90329158b2a849927a335c72c4aa851b2dad01aa302b0cc32cf", + "0xeb0ada610916739c798c6b82458f7f4aa8583b40e1d0d03b0b1143a3a5008608", + "0x05aee956f26b6b5793c626abd0d8c36843de3470c97ee96259a6bfc5bd4923f1", + "0x81572b951ca3b8af2dcf6dac16c6cc215a2dcc2fc201ba5057035ad1c2101fea", + "0x75db52fff9f78fc51f44a4550016a1774b7d396a195d1f9fdefa04fd7fcd29d0", + "0xccecbbe8891c6a6b5601146b25634ae17914d4fe687d456472c2207ba2809a86", + "0x38d07283f2f7df234942f87e52dedacf4a11d3013d83e6f2dff75ba1628d9886", + "0x4dba63d0eece8c063904d9e2f2861e7d9eb144c2d9764928498fca8de371ae10", + "0xb40a83b846d964fb6060634faf1cf8621ca94e53d780b6e905d46114f9b9a27d", + "0xe2ce7be74294f7359a49bfae41a01072f7c3954d6f95ae842a386175ef317785", + "0x37ef59f57e88b4a3009ceffd2565d3508b5c1a036240db70e93f1ee54553bee6", + "0xff374581040383683eba25152d67271fee82ee255c3632d390552b8f5e578f78", + "0x1efa86187dce4c29eceacfe4c6ff1d4950c862984dae8a20f9fa8acdd437264e", + "0xd73d592632129f6bb002f9ab122c0564689357eecb1f6d888440c495f0771ec4", + "0xd3ca394b8fdcf3660bd097d38e0fd5bf33c13c04b2659abb70e4d13694cdd9e9", + "0x0428176e61a3732d57beb0f493845de7fe0e31baff8a5e1563d232bbccf93de3", + "0xe8fe77555e55b3260afc9034f885e03c2debf36c48ff69b209223b1829cffd52", + "0xdc7cd23f15d5e07d3aefb0012ac38fc8b823a6263107a3cf8b89c3368715d7ab", + "0xc523f34972db97d218fda1709162beaaa9ba660c4df97a109d50f38340a48f05", + "0x10be73f074922b6e3df4909d3b9e7ed323c2a19264dc93d37bd7513fb1d6b4e7", + "0x4c9b2adb87844975e677546e3a1cebb9aaf42a561f884386915e68687557ecab", + "0xf916f18da6474460fe4ab8a0293f26a28fd3d90679e0bbae2b063b771a8c998a", + "0x04172ccdfef91ef69bbf1f0dd36bad79fceda5c04b8ebc566ff08da83d59aa46", + "0x895732611c267295e71c15d6e87e3519203957cd29bb60aac32cb19d7a9af211", + "0x370ab55158b9ea9badca83eb5b09335aecfce8bdf18fbade818a119a4aab63b5", + "0x9320a338272e9d0aa5a5227b05265eb77a8dac2cb8c4b021395cb0edf2f80077", + "0x173b2ac92367b0cf4585ee21916825f7bca2066cea520c464b908ed47542cad7", + "0xc75a82f1f03938fabb0dfad843460cf7dbc71550bb262d06b4e4bd303912aa36", + "0x422d7e015bfab1626eccd58dcb0fb98be644711367c0cacc707d36ac40fc0b2f", + "0x3360f78c16305e9aaca57148a335b5bdf9c6527a9703c03cb5a8ef9eb38f2b6c", + "0xc682332f99928a8f6de539428c706c2c48ce79fde10883414843f913c594ef50", + "0xe8f0bcfe5e2e2fdda4c84d9e02689f7d6b1e916f3734580318cd3e67a7dcd633", + "0xbf80b39efff256301bd1aeb7292d3879bc0956c73ce00703564b7c7248a06b42", + "0xc8dc8e74c62fca5c317ffe9a4217fba5b3d73b73201f1af84c1e50df9dcac044", + "0x74e0220562dcd2a42516fd4499ebddf06d25bd515e1f652437d88eee48bfc27f", + "0xfdcf535542126d739401eff7b293640853ce9ad75fe2ca6f7c46422dd436c3a3", + "0xc373ab92780c26c4a1324d3e539cc64a6c1ad586dd803676574590ab44f1bc27", + "0xfcb26864728e20b81252b3881ab840b92b427f7115b1f5e2f3a61380f88a4116", + "0xbb685ce31d2029e47d5ea693da8a01c2bfeb8169b5d0eda7cdf84c6468595e74", + "0xcd18a3b622502d167c8ccaf5635a03574738f2c13a278dd7a688cf7ab0a2c5b0", + "0xa8248f6713673e2fa7381e9e1206ae3ac9019fee55cf076106573f53424e8dba", + "0x0fe434aaa52d8f09b727398a8968c36aaf2be82cbb23778d58d4e991d3b7fa9e", + "0x185bd0f6ce85efc5a2579cfaada211d8071403f57ed9f7d60f6edeb915a1c432", + "0xf5f2057df40a078ff754494f4c6baf4063e3eda68b07dd5712f398865cda4d99", + "0xa2bb9488dce8bf3e580c3147dfe435c1638db0ec04a9880301f13a3e9d6cdeed", + "0xaecc304df9c226d4c3dc6eb2e57efe67a3f30807ec1724f9c6de81449d6354b5", + "0xd5f6438693c32880596df1c9516eeaa0c4ec13c0a1deb55b54b7f75b05bc0f97", + "0x11a7312511cecd8f0a87dd9ae9e51f404eae666a25c07e8435302c0388614baa", + "0x2e33bcd6479d2ec471017dfeedd7dc5acfea9bc39c7b92116956015ee7942bed", + "0xaf288bb222823c6d874f35356c4d00fd23cbb3a60472929e4dc6d36a04b7539a", + "0x0e23f7aa6514cfa58a64843a5fd47718f963477b51f056a861a9b12d9fef5f62", + "0xc11827d60b3729a09d7a0ecf664144a936a2a7306f221e26e4ce7e7010d56115", + "0x0366e38d49c40a485ce8410df1f6be0f96c6cb64b8c4eae0a7f045c8a8f1d1ce", + "0x5a10b623c1a9e2ebfa0da73df2cc85b1a11c5de81d36ce9a173ca458cdd01523", + "0x523323217f955b390b82b719660026bc7206bf13792f33f4a5ae70e7d5c6a96d", + "0x0729c9bf38e3cb392201d4dc47efddc969c8a40c495c36686624231380e77878", + "0xb1a6bcd34bc44b9c15d6a801c840f656385c0668572d313096ba2fa63a961efd", + "0x5d3fe5975286dfe199556949782234b6268083b3f81a990f81c20ada16e7e625", + "0xca029b29e95664f8b678dae0fc16309a4026a785e42a05bfac1eb798c505fc80", + "0xb69a389e7343d2b4f2dc5cd6531806c9da908df96ee6a4f98daaf26a7e2d4ecf", + "0x10ae2c8d22fad8711df2c9a1af01bb51a8dd53977500ac74d490e2482bf16271", + "0x46e2a6cd2a7bd80d364be9d72970c607741a0dfdd9b7caa3a3ae36449e5e6c3b", + "0x43ffb2f3740adf7e49c9aba44831f9f72754b1c82e1836f71c29cba55144177d", + "0x4f9f90cb4ee561c62358285bccb0ce753da1ef841da876a15c60cd6f09e1d6ac", + "0x951eac5d02ec830c99c869d501b8a2c711a9d3c57410ade7cd1e3c5287133334", + "0x328ee3ffa889d19bb0a52210628d3ee79b3fd6000b5f564891eb446c8a450282", + "0xa4bf55830e5af5dbcad9846370c44cde2bc5ce7c214e10030cc3eea34b79d6ab", + "0x56556986c284b379da74a1176c53df781039b6e598223d0b2e745d7f6415e330", + "0xaee9e7445a2490fecad2d94a9d83d47c8544c94085bf5ac5234a14de9fbb7f69", + "0x132d2a018737f9056fd9cd695eaabe7ce9e04fc21cd35ba1f892c6810546d286", + "0xeaa54789bf7ab23b3a3bf5472a964606adc49881425e34ba7d9a536f82ab133e", + "0x23e30e437f469d9f175e350dde559ef6ed578a0adf16dff7ba41050a6d99080e", + "0x6da9606fe9e8c103e29ef33edfdaf5b325c48a6466e7f6ab1c89c522dd7f934a", + "0x70365c62d260b7edb08fb3fd2a9fe865cd67b286aebcf9d70204f8ef64e6c4fc", + "0x9eaae564df93682a5e6fd8f0d626500f452f8dedae1232e810965f0bfadc2311", + "0x9b74b87a77b1a52eeb206b846d7644166dd1f69e5b3995bb3e5c481c63630cd5", + "0x04b02985a9ae4fcbf9db44a04c71b86620864536475c7a4fe6da406845dc3393", + "0x764525d62cdd4eb36fccee7fb2917a7c3b0e76fefaecaa8f968d6ae2e6a5b1ae", + "0x009c739117f511c9e7a00aec33e64d19a516ccb2aa3d0177e865f36099829272", + "0x8b040f52a383cd87d46ae07844e4b53018dffd0d0332e7b78a4b3521ee063132", + "0x726e1f9c132d65d0750290bd1a785d2bd81534c1c0e83def056a743ac80ef4ff", + "0x0c309b58787f41c317b3176ab230cf3f077134ba50935936a09e5e82235b8453", + "0x4641d9be01a25d494511cc37e90de9fcde07602ccea4b661971b5e5a00d03fca", + "0x1bd977d1bd0eb32e49b38ba600e0f0fec7f102c6c3914f7e1c944ee10bf4ef11", + "0x269e8b5be03bae74bbba23289b1e3e7c9a1423dbd03a2b121a22b9eea7520eb2", + "0xae7d7852bddcd107b74dd8885191756cb082f97b613b7ed5cb855ee7712f706f", + "0xf678e32a8e94206518dc953a8c9d684dd5d08b6a5628a6f7dc429c80ff1fe9c3", + "0xf899f563dd1a48f10ab7c55a6f7efb2aea08d3be82411cb73403aada18109d2a", + "0xd665f961ce7568716482bf85647d9dee82c8b719b21c1b555752cd9184fd4ccc", + "0xf71a0ab542f61d880734d2c186da35a2575c05403d3a36a4aa7746714b98eb23", + "0x84ed91096287ce802dc09b1f3606ad48c1a281332707476148b6bca30380638b", + "0x5f227b98ba91a67d4207d5fbc064403864fbc04d790ecb940da2fa851de35e8a", + "0xa98b1b6c440fd131d60f7451e20346da06618a2dc45db9550f3aba0833853d6e", + "0x5814b4dd8d00d71326bc8814d3feb6de2e3b3c7b45ef1d2ad52ea98ce9ee754e", + "0x3e28116f85c8d06d7a40ca747aebdbae3fd0fb857c21045b8934c155ad650f75", + "0xe7b32da2212379928559cffde6762845f4743f338ab5120454736c9afacbb957", + "0xacaf36117398c0f40aeb6114e6c01dfac7c524613cb701e534eff119e3146086", + "0x3e41b0cea0110bc0acaeed46a0a20f4bddb803640a652a87f78a1261e5660f12", + "0x081f66954c6ec53936855e51395680684755129be65adeee1617ad94787f7f5c", + "0x1e53cfd05241d851f964632a997390588d3305a664e516fae222fb8f9ad9138f", + "0x56fada3ac16c6997c3315f5b9430a62f2a856806a0eb48866f9d718b3bc3793a", + "0xa9dada52926a2d7bb6f4ed91f26ad69bb006332fbe6aee11434027200dab60b5", + "0x819a2ab193730227617c65c0e52159798cd8f2494341268064de24d2ec79b8ec", + "0x409686b4c67adc9bbf88f9f312634c1985e8eedbc746511c70ac907535de05b2", + "0xa32af0000d95f6b8f11f11c58f42284e89e058c450337ab42148b6f1866dee00", + "0x7f75da8d5b0d3325483a408df85d12f60692d8712481ce510ac38d04f7563b8d", + "0x33c32bca7de6d98c1bcad486ea2fe57803739903e3ce77ef028165440fec766b", + "0xe1db4790f2696ce283312ba16b6a27fe4910c617e79f6a36ae5088570c170a1d", + "0x954845731a92b9f21d546d12a17e2b5e9ef8d082cee9d407434cfe96ef1bb682", + "0xb9fd370d18c4c546fbadd57d2e9e4abb743875be98157a080c8b0f7f40fb42d0", + "0x863c00fe56d7ce27bdf675d5e95da139a7a16c9c26e5b9e98b399f0a2445ace4", + "0x1e91dbb2b7088b8c725c60c76169b292f2dcda65c6ac50cf50e12c0edfe5737e", + "0x071d303629c0beed31978fb2447ea32caaf7656f4a9b8080fdc6d1952a4b7617", + "0x123634611e4dcb0997bcc81f0c6afdccb4b1dd81cec57ee6db7211819993b49b", + "0xebbea8cd68d15c06bb89ad6f7e19db535e17bf59e78b6a84a53b3ad97d2573a0", + "0x412b7fe1f9a30e81a3fccda253aaf3922d611e1e933dfb4911003b6389dcbf65", + "0x31b60ff521a92e30fd7810b70b6f61e24da4369f009dedd2d9439c56f1f2d5ce", + "0x0ef0f84cd6eaa4ed9313265fc61d8dd7daf5c96f7f31127cc9a8eac44837cc22", + "0x2445135fd61f87235bd1aab72893687096967a2b9c580548be2a74dfd809fb86", + "0xfaca5eb580b02b7b54a3115c21155539e437cd21ad4e201b0b4dffa3195b6f43", + "0x0968fb86df334327b14672b2cdfe85e7b51606905fdc3a05a0db2b954fe7ea43", + "0xbc53988f11fa0bbb1c43ef2ff7bb23e4bc537d7f4296fadc5f7d9c910fe5ac00", + "0x2037f5636ac5a997fe5a8f71f3dc6f9965ddba3597b455c7417d1054ad4c3580", + "0xc837e3fa51e643a8783ff425fd4a4d58ec74a6d782daa40d210398ac1877f405", + "0x08ab20e172c449b79ce305849184ee364f66c4f60d9ab876943d6bf75e30c246", + "0x110578694915653de01f07a74b99b3ac9e5326102923b7de5bed1e6d4c6bd7d8", + "0x736f4223a2372f1f5be589a2b050ea33e8cc94badfce83daf73ba0fbf8fb581f", + "0x32972e9c9fb98b96620b9419a946cf33f5b68ed205778f9f4c8a2a179579ca02", + "0xcc14e8337475108ae1226633115fe28700e9c238667820e4c8f6e8a28ff74dd8", + "0x4f1a0f9897f43ec0a954c44601e2bb41da1116d9032cdbba4936da6d40b47ff8", + "0x8e1530bf92e34b1bc0a1dbf20f9b49161f815002b41203b3c8ec00ce4a903d58", + "0x0901143101854314deaf490dafc2b5e3272bdd6e5a0a431c439727a9efe51848", + "0x18ca27c56369d7a3f11a88eaefff5c9a9acd83136b67f0c84877fb7c53fb26c0", + "0x65ebad6b523c8b10cbf53a0e8702f3cda3856dfe2c09f45f740ab9f47b67e3f8", + "0x3b8a1210c97e2565816f80011eabf9520d36ac55ae7b40d5b1a90d56aa0e8d45", + "0xfb15f56b1fdd48b65ba61fd08841df7516979c822d0ef18119664d1e5bb73d63", + "0xf9f7938169addd969c185aa425d10416897852c95e08b0bd5f366e9ad92d6242", + "0x602cc3e58c87d5e98e07aba885c5b87692b4c7448a76f009bdc4abd6141fa790", + "0xe945c49868606b017204f0272a37b3a3e5e16bf4ca4ef24c7b5f97e0867b6775", + "0x3fbefde286dbd17d6f87d3732624b45021bc8ee91a58a6f9fa937c66045e7a6b", + "0x19132f74f6ed69c9c57c5a9061d3b10aae933e752954c3ee0b35f2827c0f4210", + "0x4acc5f1f84e81475689883b0fbbcf745735ef59b5c868d7b3fa25052292a5976", + "0xcab5213ac5a5e40ea3f405d1f01314708baa64e425944b5c0ff0255810b605fb", + "0xb30892d119a465ddf14ebe5666427985152fd7d7c20777a606d50a50a52109ef", + "0x26782e7e5f51b8e78932d438a13840446b8a40bf7f616e485cd1769cc23bc0a8", + "0x582f3b5e5113be3c78da83d650a64f36e9c84a769538ae89b299f561a474c27e", + "0x08a4bc80e056407eb93b6cde527deef627011f3146281ac02c32ce6de2667cfd", + "0x4028336b128bbdbf30a9f5a4f46e551121cd9a7ad1d433ad0b4b36d951a8aac3", + "0x5acebaa71a6f14765e456460ebd1b76706c97c994acf93dedb711217713010a4", + "0x7e7bba439ab8656ca63853bb3714ff63eefa38153c179edde9e1b532d9a032f6", + "0x74d564d753fafd74f72304913cd8984357ee5cd0d2bb27e364248adc77812a3b", + "0xfaae86e17962909b4097a5ff4ea7079a492fa651d953175a36c03a7d2e4e0cfa", + "0x6a444dbed0f88402344f5aead6a2e5b9246687376fff6e6bc15d5565c484b305", + "0xf479016030a59da1b0699bee79a088aac374367cf4035efb87bcf298624bb69d", + "0x4c15b9a757be6a9cc782905297b1ea7373dfb491910a74293e3d224428eedef6", + "0xcb897fb4dfe7677de77f0404e64d9724b6fff2d8e66700d8efcd7cedb9d011b8", + "0x600e102cad5f182fb2c789712db63f55f5fa3ebc7cac677cdd67f976c220c667", + "0x41a18deca8297801782074ec9b27a9ec570199c4a648ed90cb011333383bc05d", + "0xac91842d3f4f50a6c5e7a58901867792ff906022cb13fdfaa246b45e6b09458a", + "0x9699510c10bbc758337177591c2d0163ef404ec8ce8e314cfd73c8db19d819d4", + "0xb448a625cba4e20e1a9e2c88f65a059aadd80c597022b76bc5585180ac0da018", + "0xf3ba217e5fd9deb0d8c4edd307bd18dfef819141865a7d619eae7d0c92961546", + "0xe33e574660c9568b0180b205cde5a8b3c2a7f80565befb4fc9944b3372d26bed", + "0x39c73cc2f2c738cf1c4861c0f128df7b63107228a0d4335e83a7cedc1f503fb8", + "0xd4f609261521c4c44240216bd2aa3bab16fc5412b39944d56b6c4c6255e134bf", + "0xe1518677bd130add527731b6872d5aa59fa0373a856062ba0ba01068ce88c6a9", + "0xfd9657266575e1cf582ca115573b0cb9ec0a6d3b4d018b782a2c8de489f623b9", + "0x09dafd93880a3ed6795860d7799d47f684d726a89e7f47e68641b0c9731cfcea", + "0x8e1539df31abb29cc9efd09742f21e34619fa550c32c81c91af0399570a667c4", + "0xc2acbea05338e323a5ca282621be56161691bffa0852bd5d492c9771fbbde515", + "0xc3924aa297f0456f23afcfb287bd231c45201519cea87802b71523e0d882ceeb", + "0x0f77870d7a8238934de8fbcc89822438e09999db4cc86ac7887032f9bfdfb3fd", + "0x19746fa87ad24d6f39e36793022b548d03b4b97d34e09eabc27f42384975943e", + "0xf502c896a9950e450f76dc77daf301dfb1134119f2fb027087be9a5ded232c68", + "0x88a9157932afb4973408d953dbd4c115d8f345f241d92a113b640174596d1fe7", + "0x1f4d9466faa30dc34cba41379d3891b406e8f1cabf2919a14e3dcba0b8d7296e", + "0x3a7656143811296bc4ebd4da6d8e668cf85baec2c36241fa1e8a5e3e038f6e74", + "0xb72d30ba8fe9041fcf26e74238a41477f22287242868192dd1557a1e76b5b1b9", + "0x6f669597d1d71f6f5b5f9067ea087e28cc9644b641e762648878c7f84adaf3e4", + "0x43403f7e07221a22880e141432b042ddb088e77de3e3ef204d15ea454a9a316d", + "0x9e4617d1c57bc882cee51b2ee8240b216d5b8f8526750bb9c70ac050d31cca1c", + "0xb99e1c2d68d7716d1d0bdf0b87acd3aa8c3fdef01ee10085c339d9418c87e672", + "0x3b1ea702b3bd6638788874b111a2a4c5db0c557f2149b441515aa4efa20a0309", + "0xe7c92f95d0d2a49e019933f627d88f5598a24ae48ed85deed8be699c81cca4b3", + "0x5a0eea5cf28cb4341e5e247652748ca80a852a8c30a355f76971cedc8aa0719d", + "0xb14838455b175680b2ed7e6031c524971e819f27baa888db20ec4ef74455b6a0", + "0x129ecc196d6caad0aa010419e79092856d2bbef910637497b254ecfce12978cc", + "0x63cc5dc9bc8ac6fa744a078cb05e233ce89ab288b5f75843b904c69dda603f5f", + "0x8d466f68f1ea3bb04ad379e8e8b4c49b21ada69e08ae1b1152fe4aa1472405c2", + "0xec34528089706748279d5adc1c57b557d366bbb2769c845beea807600b0a0b95", + "0xcda3fc77337abf8aa097ae96372fa943c083239ca462cba8eeb308c691a24357", + "0x16ff959a37f0850424da85a50adb5befda492e51264b3c67b1ddd9b93a6e8e92", + "0x138c4b9b5b584c117c179dcadf448add3155fdae2984c20f6ac31b65a4e320c6", + "0x7841f8dbd078bb621c3cf52abec3d3a864500ccae14287a1d8e135b0f502af7c", + "0xa1098ed3e4c8469b3dfd8cb590cbc17963b04d38affe2b1d3426da6bff14a6fb", + "0x69a0e0f75ab37c38572693e27f315ad9a96efca6f6b70132a3887b9447078ec1", + "0x4228616bca7dceee33c9c8c1bda5223abe6ede68690ed15ca4d5aa2f8c568c61", + "0x8f2b912d571a48332a83c7b587c2ec905c776d0f3fc81473148f1e2d333dca89", + "0xb6c57d52aac8bbc17ba9e3331bab16259dc74aff6ff3383a2fb07f28c4ec915c", + "0x410a36900c0186aa34dcf5abdb1927d9af9ea6a8d3c8eecd1dcc2106b5a2611f", + "0xa7cd3bb40617c3e0e46768164ea9e1b50a3abb2c43e601353fe0cffad2191c38", + "0x3679837b7cda24d135f565d8c4c90fb9889775c8b22ad3cedbe65b9ae409a8a8", + "0x243526fcd0716e35d26b24fa8857387225e0a1cef1c8761277751e4f6e55ed67", + "0x557bc92e787ccf77f5ac6d8aa85afac3821308cb70dbb2bc0f22a24cd62e79a5", + "0x4097357dfab4c3a3265e339defd202d799a68a2ebd236ae211555ffedfae67fc", + "0xab78e659459a59d7723ff8017307d734351747835f81d0e4a5b5dbbb95add7e4", + "0x1b0a95a9b31d27f877f87d3f202a4bd142a4f632faa614ab537c9b5a4f1587ce", + "0x6533b72e1019c4fdb9144ece3093e91acc1e8bc141b5da9141417583be0f33e4", + "0xce27c1a106345130fd67902a182cb867b2f48551c4070666299c30f779573ad7", + "0x000e79cd097e821d66aead3ef94cc8898264faa774b013e47f521b2dd1bcc264", + "0x210b7bf0618fd4b1fa5f920046006a9f9be602d18cb19fd7798404983e242d81", + "0x896eb7dfedf2b14a7c59f86f9f0050e24f10356f1f44dc933b4773387060eb64", + "0x5585709bf8ac4b63ce4611b62379942eef5c5a243b1d5f0aa295a4bc41d98c8a", + "0x60bb1c8affb555a104ba0e941b7788a4f5e37411b7ee47810e8bb23fcac52436", + "0xa1387e2a41b4ecf7f60625006657ef730fb7828fe7b80def99b708db1f763094", + "0x58cd32fa531b17e4355347212305b16031c44608ff161451d733f77ef27cd457", + "0xd69a43e8ce8cdaae72adc3d5707ade963d83a33d93e220b8aba7e1b8a777cc58", + "0xc8077d1d257d52afb7e8fc5fac5793050adaa7fbbb524d86ac6daca5e348b575", + "0x109dbb9b121d982d55f4cf5f98a73f1d6f8d48539fb3575757b66e914b891d44", + "0xa0d531c0a7777f880582f130e275469c0fffb157cb10b6042360c40dddfe1ef0", + "0xc3da259e5210977890f66981fea488f2bd7fbb946ac29ca3c1abd07f6dc4c52a", + "0x86b8179ac2f43d96a2d74503f9924f28f9caa2c3e7cdc60a81a58d4304ae2768", + "0x3b2b32b4b2543a7d1a644e0ebd8ecb098375e21202bae51d47d255d2d1ab1a47", + "0x7501f81441a362710acb6054c46f05443d0e2f1c49b656f123821dc72a746a38", + "0xc3b8e61ec93002f3fc7bc48838892b739dd1a5f6a14098235925c8604d46ea02", + "0xacdc1fb6aa0dbb3887b41434de084cb9785989535fcc48fa2281497bafd469c7", + "0x832289ffef9cf3053b6086bbbee2c8367ff0be7f5c891be831e19924d94613e8", + "0x8da3a234e0a331eba20e56a6fba40d7cd1157a4d742bdc5d8ee580acdbe29148", + "0xb2c0e3c9b032835cc747eaafb29e484b345ae5215742936d574ddb1fdf7fda58", + "0xe9a511b4fb829457e6ba57753f28d7698e658db1633358d3fa8882d986aa80cf", + "0x2c10dbbfb213e8d124231a68e23d982ba56225bbf085fc38557e3825705976b1", + "0xbf603365158a92d2b61fb0cdd5747938e357e88c02c782f3876074fad940ecda", + "0x9f86441064c7bc377e40b98f279ce38bcbac62240bdf0591645b27fd9175a6ba", + "0xb794b35974d4c2316ee5a56c5e34bf253a05cf0fb2d1f85d401447a679f3547c", + "0x1ca7da1e1e6c8515ff3887f5288de1421f17d73cf8b68123480ecc185ad8937f", + "0x8d5b5f34b43d775b79971a1baa889eb45937f9c278995f3e2ad1268c1d3c2751", + "0x0b7a3becb8e0759586f744cbdb7af141b9186514bc44e566b9f955a917273b8e", + "0x404bea9b91faaf65fd4bb1556e08cad3b23a095bb9a8494af021240d417c60f2", + "0x5650f6dcb640b9e4bc064dc3fd8019007930a5a0372d768022ebe6971db0cfd7", + "0xbe6e7f579859504662f433f56b4aafc1a4b9099e5102c505d24c1a262eaefb1e", + "0x989864b3bc371b0181fd45ea0845b3ae3dfdd8c369e8637b5b4d9c7df3b5e6ce", + "0x33f6c0f0320f82dc4f13896eb2947877f46d8a8281f9dda5ae7eb752b2ad9047", + "0x86b7572aa83291fd2ffa21c360f881a13babc6f36ddbeb3ad2be804057cebdc2", + "0x8a1c13f6b77188447d6c161004d8dbae40927d71bc3b9fc54bbb2ded4a04c663", + "0x59e939e5c71c08d215e73f791c99880f9d0b5501b18d1fa993f6a9cef59bcd5b", + "0x2dfd9cfd2536168f316a4c8f8982832cf2499cf39c3cdd6f70a97850309fea32", + "0x88da00db297f48e35e847b561f9ee59f920b59654dd49f99636f0bc1f1f45781", + "0x60dd105a42c8dce8976464927826a4af14da6871fe50c31b4d2610acb602081a", + "0x548966191d66e18449edc52d2d65aa2b0ebde851ab873c9b56472d51678ccf03", + "0x8d16c253a947b6bcc6a0f3efaf9d081eef00fa2dc858ae20ed67f7485857d1b2", + "0x20bdfab7c921317eae1527da0cf9d90d2b7e1d7f50e31e9b3535ebe2b8c6ae3e", + "0x302df478766f91f7ddfc40fcba19c807203a7ab70296037e0ff3e5871bfdc852", + "0x0fb688c06049b4f8af826d227d228fe183ad9f3bcf62e5b973cce5a0da23d58b", + "0xadedb7e13337974e5ca73f4a8b8b75a69bd78e2b17d42fdd0632eda492708d78", + "0x9725381e1d854ae734beec25a5c7a4936e6ab890be14b886f7721325d5e742a5", + "0xa79b59eb5bbb839bdeb1ea59112c3e0486b069e27197c95c8045523a58ed305e", + "0xe07017f5ee0be6a17a305ac06edcbbab2cacefadd903a2917b76c5e11ea682ca", + "0x999a6246c4fb2d9ca81aafe5bafb8e3c82b1655c788da4a44f54774e63479e54", + "0x00cc5d91f4b07fc68d1fd45363c2d53f7bf2e4e7810e593d0466bc221c4eb8f3", + "0x7f0e7352c18e4a6e4c33bd4a5de50ea814934a3653d3ca6f9a6a0745c4b6f57f", + "0xdb2df5fa08bc94ad1e84d0f8e556dd64a3bd9d2fbbff59293e4bc6caada81622", + "0x6447c05d7dc4f4841ae5d810f49a3889c6b0bee9ec5195c6e889f8b9c0de25ae", + "0xf6e3d4dc0245bf613b9449c0bb378129f8fe665a36b476cf64c1e7272a73fbd1", + "0x720040b98e90d281efd174a93abac57f2ff6da26f76a60c2ae219a3e76b376fc", + "0x715f1e4e6a60b1727e30196b18fcdfeeeebf12331e3f12339e45df37257d432f", + "0x2387ea609b0482b96208ab5f1a560c21973347360ec4e9d619f892fb92e8737c", + "0xa98b62386f4eacc365968597acc2bc1158acda7282c76f41f71389fe9531affb", + "0xd7a927a8388beff9e3c007710c43f66b0aa839f7adb8c0a5ae2486b1c7e29573", + "0xd843264dcac5af1f949eacf5b58ad695c9002a526f12d5dc8d39e0ed6ff1188c", + "0x7bbdc9af7ef11c7bfe753fcf752f55868603b4b3196c6dfa1a00e7190c8f1775", + "0x47b5448a62e86ec0c45d5fc3984d3ca0ba7a5e6608602e5e84f93019eed89d32", + "0x92e001b0c699aa9a8448a73ebdc9ab5ae272b0e1a833214e4da808df9f97eba6", + "0x881aab7fd977013d9e86166acc1992ff377c415d41bca4c3ae94251b437b2f85", + "0x429f0c0d19c054aefb66d378d2ccc7e29669ffe71b07409d6f277cf5791ce156", + "0x68be209d88bd215009ead8feaf952100eaf528ba73b6baf04ae7c3b190698bff", + "0x48e68f65e761a2e9e9fc02876d60bceac4625b7c943dfd8bb7d73d0fe074ec44", + "0x6eeb6ce0899fc3736fc2a9a27f4be5d1828bea5d17d27b0c85752dd6d91ba3e9", + "0x4c486b5a6fc62fd024072e93f0a98b8423b9ae5cff5fe01f4535eb4b8c8cfc92", + "0x6a31b0fc8db8c998f5d38486561310cfd85c7513863ecb2637b635cab53b97d6", + "0xec6fdb483ee04cacf220132daa1b9ae9006524391f7975831919e85eead2166e", + "0xa703934617db422e98f6862458cf6fb523c46e1e54bdea35782f90c84ed2d36b", + "0x0f7c7856b90d80e63d756b4afdce99694f4b7724e6dbb966443fbf161acff6fe", + "0x4b6f557f545daf95b9004dc1e030b2b3e29dc09a6c4b5eaa7e8492a17da61481", + "0xbc22e919fe06caedfac708a3b8f62f9129f76e771a71b09b744db38fe03fd3a8", + "0x7c10fd373b628b3b5488102f37d468ec1f3e2ef031285b1156747cf8679f5d0b", + "0x7a93f44d81e5c6a06936c9da5ecb707b0e6f5facde1a142f982f26383dcf271f", + "0x2a683b279fce3a641bf342ca8c71c8e297b2d09cb933c17326abbe114234d8a8", + "0x464d61675fa98a0375228eff94c3814a5f0d65659b79cf93d77dc4abbcaacba8", + "0xb53b63f419275209c232089f6ae4c8807f2e63cf4ddc2cf795206ae3600fcb47", + "0xf3e551b633e7e618afe1fe5ee53fd8f20b78b7afd20e8e19c490aad66030a3a5", + "0x0bcb3cd9549673a57ae5ff0c92d412a24874ff11020c5eb38f5b686f7b65cf38", + "0x008e5ddc4c8a1cc19728535045bd6c4910f6dd92de75391efe58984452e6d08e", + "0xb344c68e2e70501cfe05b740b1f7eff47330e5d8ee650731a59372e73a02450a", + "0xbdd169f9fb2cdaa9bbc4b7da03e28d22af529b6b7e3f1743d6982578f6d6aad8", + "0x854212000ea89c0648247439433e8ca821cec6c44b2b1c9d972d48c3b85e376c", + "0x55679d61dd61ac0e4dada1d524fa3655713474c53a4ea188cb884a215049b078", + "0x542846f5abff67428e3831167332adcd6c0211c9c3aefbb2f29c5c814583fd7d", + "0x807c69782f16ce537db354121cf6ee437fb81291a337d0b77cee7a7de5573471", + "0x948cd37cd998bef4832bd62ff7b94aabc46b6629d09706545def1794c45dcca9", + "0x9bba5e5818425f62d2ef478d44977447c133dc37059e1387d9631454ea3b899f", + "0x0c17f0d79fdabe535f77df6473120b9d1e72f9345dee3ed94ed99d715e757635", + "0x53692738c355dc38296753a31a22b79a152798fa61be5d86019fb7a8f1fb1046", + "0x2e367f047a031fbf76747f9abf419888cdb2514ab9b7d3058a97e96f241a9042", + "0xb64747ed2d3f922dbf91792656bbd5e2a5a4491d8eb2382c4316f45e037ec864", + "0x054a0b50f58e8fa37ce01d8471d297372742360897e125d83219a5fd87900eeb", + "0x26bb823fa26f0f733b79968819ffe8189b33019370c5b5a1182dc355b193cf88", + "0x99969ddcd8b698e499901c70ea29d216dd215c2cd5b018ed911732d107c11999", + "0x176e811b33b46c80d3f9dcfbd51b30dbf920544d07405102a2209525eca734d8", + "0xe75b6bde6415e1fc9a1e34a71c6f1d957d740982d38adc36f730bb05e994a3ff", + "0xfb6e3359ace0a13a285d97e2d46f7faa013119b6183c4788c1d4f5fd04a1a4aa", + "0x9e7335cdbdc2fd86cf41c2230b855ce50378d40815d9a3297df1f21c8def3b3c", + "0xfe33bfe53b4ca5ab9737c29b0462a95905f84ba86db676da56df4273b75d4545", + "0x59f63ddc97a0e240ab488d842c81de04e0b9920d6b74697680fddc8574a87432", + "0x17acdef0bfc767753c8a6a4ced301374dd29417d2d2c736b3d92e02a56393074", + "0xaff2ae6cc5b9394f32978e44e5d960b94930e2da971a629761a08319392fbd08", + "0x95870448ccfa6f59e8aa0be3f2734fb58e6df98b822bc29477f9b1dd1d6ba646", + "0x23c02a4e9c4471df4553de4fdc82c6037eb7df523e9f898ac1da5dd6485488a3", + "0xce73f43c6edd129339eaff5ee9eaa4484aa7fa6cc7bd396f8a9d7c021ab2b687", + "0x439ac8493df476760c9f80a18936658ba5a3ea9a659b1653ef4c5f2057ffccf1", + "0xade0ff816ec1e24bb08f9ac978258773c6f0deec7fac83d7969533a32ba3b987", + "0x1a02baa6db8e64aa935c838d3078582d61f23870d5b465a96212ea95fa666d8e", + "0x2fb8d73c2f6a876afe358fe0b6d38e7b6d9b8fb98df13f0a36d0725a25b82599", + "0xc0edbad07ebd58dcf1f82374da62e81b7b9ece3313a3a6c942eea6061701e50c", + "0xed15ffad36a7390e4f18eb59dcbbf60c9de42dce55328a9bf3805ef5e30c7d8f", + "0xac4be4feeae8925fcfc679df7cee16d5ecda99889037f97a7c78a3fa7e0a160f", + "0xf22e7fdc91f0c4e663376049bdb106f52153c7ea2316d75c5e5dcedbd6992df2", + "0x5d811cf2f49297a38ac69e40c7d3e5e737c0b097ccd4aea3036ac1a303444ff4", + "0x66e68bd9b5b343802c8f5c40d49a478ac67125714c776ec17eb683a2f9438916", + "0x5a7c6102ce1f938dd08d6a93cace698d4cd295a86dab65153781449fb0504167", + "0xc989321bb12394585bf622f368e4e588b97906249cae4a97979578799a263f61", + "0xdc84659a0db7d0f35d25fd29c96a2f77a174fee3811cf118d3c35fa146c7ada3", + "0x3c69c854311dbf3f001e21957e7293088ab3e4f05f47c2289b5b2eba08a376d6", + "0xae67ead86b267c1aa67bcb99daf2ebc2f6572faa9aecc7c4a37fa587852997f9", + "0x4f1ad2f626abc9956b72a94e64232f25efc73eef2c3f7dc938f866f42258ae36", + "0x74cafcedccd73791e4d244863eddea985e8848fa51551f52f3e7513c2daf8a30", + "0x56f938ec41546b7512cdacc0455b9ca80ee8fc4420690b78b34b0ac3b4bbc6f0", + "0x263a2880d6941b4639e6fc6967b189b6f5f8845af145cf46b1af74eedaf55bbb", + "0xe198d77ea77eb49472e5c18b507969a5979b90587e2ab28026835464ac8701db", + "0x8f2a0301c9f0c20a00f50ba28a6ef7ad36256deebb94761590d9a878b750523c", + "0x18c5605fbf66c824c488d36a74e1fa48ce1609c638533663b5bf9c312a93a80e", + "0x565c3aa28dbb9ff7f042d8c53c05154ac93e6f00f61e6a9174a8d46858ec0a93", + "0x15189008247760b2434bbcd84088c81c6bdb2369eff387a5711ffc136bf2805e", + "0x7f7cefebf424401bf0db9e48c924161ba2d7512cf27713b54b22a5bf81dc25a2", + "0x08cc52f9a650091ef3daa669d91b25575ceca7750e221a62cd4c2de1ef631cbc", + "0x6ecce4aae452935e17b765c53841c106da256d2b9dbdfd5eb1cbf11d552e5356", + "0x8e12e683890c59e203e77a4608237ef595cde946b18030d705fdf70cf00be927", + "0x31b77d6801cc8567a5f27244b6a2f900c80baf2926d66573e58b6fc19e6c73c2", + "0x2012a461bc8daf576e70f6c1e40135ca4f77988b583ca9943e6a67bd6aedb6e3", + "0x50f46536206bceb37062b03b703f87310f577e9c1925642d23f43a8857d8ffbd", + "0xce7721626010691294c34dd375d8ff8147a21fd0866e064cab55f0fd27238f55", + "0x1dec4fd0980dcf226edfb82d2783f906a8f936976e7ea499c2ea98e85e0526c0", + "0x8110364a9887b604bdf6783271b2b5a6b4a67cc20b104f5e6ccb9a51e6125c4b", + "0x5b4f4c8dc46eb7a1e19baaaa523f30079e5503e7507c140dc0ae25f90e426c84", + "0x86efa5f16676bdec7f0c1df26f76c9a3fbbe11365fe45d0df67e9cee2ca60cad", + "0x974795c16d6f6b4a1b37a538b70f7123b83439876f9358b3ed5c90ce92b26990", + "0x4c2431e48a91db252d5122e739239f15fa18abae834bd9106db083632ab4cb62", + "0x4836ad3c62e6650ec7f09a1046d247fbc056bb03ff422fd1ac696afd79e4eee0", + "0xd0cb3ebf762433e2ad93c6f8a39e8cb92eaae13f704c899602e40314db3e0298", + "0xa87b48c1599223e0ef2a9b12a2c8e605f73752a97406fb337e56b8178a888330", + "0x28f6c9136d42412f0067645f1c6aa3f7cbb8555c17d566cdb477b12652f55af3", + "0xc8c911d278a2fed8d9f6bdf4dba7df8fd5252d16ee666fec6cd34af6b7228cfb", + "0x7f7137e81382470536cd3c850a454ce107cbc3a2785090aec0ec4a538626a16c", + "0xc3fed2ad3d7be93b23b23647e9bb7b15bba7acff47cb8f7ec9f441d939107f18", + "0x186dfbc31fdd26956b6d94f2ee42867f86892d6cab198b88b9b58f9d5c619c02", + "0x57c4415af634cdd5c573df5a0efc3c6e34fb1761a19257ad364626e6fbe3b347", + "0x8cd67242053515cd24c6b943fd4a14a8eb9fbfff689621c553678f5d441d82d3", + "0x1c6842a10c204825d19f733624c780241bdbbbdc65293f129b3fea7b09744603", + "0xff56b683816388a2a80dddb66f0d3cd070c631c60a79fac3dcd3741c425dd5cb", + "0x4785f0965de0881c87840185006fdcf1751ed045a7ea6636e6d0b905a176c8b6", + "0x68fcad4b9c66df6b5cd1d80b87dc4f3044866d8574f92576ac10413cc15532e3", + "0xc4dfea902c49b230aadb9593835884b2b1029064d29a630f7149bddb73af6b19", + "0xb7511dc1de274ee7e4838d6dd26cac2ca4d7554a4dca33c2ee00eeeaeae93767", + "0x360d59d25d4130c9a4f1ea61871207982026a61b1e8d2dd28c0bee31d52757db", + "0x67e880c090f01c58ca5538e183038104a73b397cbf893d5982a42abc8a327573", + "0x8597dd0964357b0424a0c3d48fce0b4296f2baa6bb1d0258c3147eb98ed2a037", + "0x6467b688ba1cf942acdd68093fbf24cf73ad1e3463c27a82c90c4be11867b3f1", + "0xcb22594e73d7780e07f7cc1d6b19ce8561b3c8e407df5a56f4b9c06f716a430a", + "0xc3aa17380019027e0aab232147a4f493f6f4d1854952a38e6d5b5c094eb454ea", + "0x2e465a99a151f08e81cb33c6d1f86c92ed98dcc8491b3cc081b31cbdd23bdf3b", + "0x830e95c099d60876151b0ecf521d532d6785d8db34d74dedd79ee2a025023972", + "0xe2aa96e03564756209ff5748aa33c689aa57ea19d89036cafb137f08bcc87799", + "0xac2e2dd7ff3b4f87b93aed1df34e9f1c269bfa279639695727865a10a235b301", + "0xe93753f4a68b39b10c82f821f5d3ee0a671e1a46546719298f715b74f87fcdc3", + "0x118b608f2976c7fa751f0b21692814afae46d77236d53ee6c11c78e9adf9edbe", + "0x4a6fc577f6d0b9389d18d935cc9f16fd367ebc0e477a50c5bcafb1654bcec717", + "0x1c5d7ca8ee5044f80826dac59eb4281d5a1b8ae0f8e87ab9b57d253fbd5ea4b9", + "0x4ec06c6d19b5027b8752b48f5024def394c8a0e42a7abd609c931d8391292564", + "0x227f1e1e6123f2346b8e1fd8ecbbc91f71eae5ae28a32901e052b1a5e03181ba", + "0xf25b9a7c792e76455c0b8f6f3e93e9c01d4e9188e81e7428d7c5f07c698581dd", + "0x096ec8290951c1a707ca5341cb9811bdaed865086e476389de627f3ad588eaab", + "0x54537f76ccff6afbfd6ceade8bc5a5a60b37a8d3a6e35367704d91ca7f6eb7d7", + "0xd16a3d72a8bb0b759b465299d6cab9aa817476897d17f112f16093ed4cadfe39", + "0x83bbc19cbb1292b7a4537c08f3436ab6809d5c504e2f945db861db7b94b53f6d", + "0x5a1daaea20b9f1c90502db70e0d16b3826cc7e79efffad85854d69e65ed7bb31", + "0x706cf14c52a6da8f39e0f6a155d663cf25c64dc0dd9d473b1f4b6a9b431bb827", + "0x23bc842418fd28110548ace0149a1bd1c5365968aea4702d2e028af8c84542ed", + "0x2754644e77d1bc059fbcbc26cdb5af8d88ad715d64c0367948de7bcb5110d1d2", + "0x4131bc1c8bf15df1288874f41095171bb1fc78524eab0fca5231f2efa5d2b0e0", + "0x18b45909ca386002a82c65a532d3b3abd86db6d50ecc1956fdf622f7c6530e0a", + "0x6244ffc00c5cf03d390cee752c7f0d1d4908ed2abba4f0b178a7f7ba643f2e12", + "0xeb8261d9a97ccb00ce6cd41bbc4459b0473547d63bc5dbf17d16694a5ea09ffa", + "0x7ede33387e9edd4bc9ddb25ee0e67c197df4cb2e7ace65da40ff981bc4eb8628", + "0x39c705ffeb12fb95610bb589fc2b425084addc1e6937a5faaddb42cbeca90890", + "0xbf7d8329b3ed418ef85730a2954b48c17b8d046f45ce7b46162487698fdf1b5b", + "0xaa2c7ce8f4bbb0cdd1fe490fdbf29476a89126d201b9bc113a24618c531c9622", + "0xe38112ada8e0d5af0420aa9f4d35c45076428a68ea0d72a91536d63c67f7497d", + "0x2f6b6adb7661b24dc55a1d5e9ffc87ec3ae4040aa0265a9ae3e57e6c0ca58d72", + "0xd3647f33dff1ca5f2ced886c032cde60047e12e1fc616439c14f895fb548522d", + "0xe4620f60f9032268b753075db2c22ee7ac16d8bc88cd06bd359b32e113012567", + "0x254391803545cd4a1535041f1dd5a559a3b9c5b0914200ea384adb8a6e830a1b", + "0x2997006eda5d2e391ad0e23b8fc8c279648a15c46757b2f6da0a26aeaff45a73", + "0x6a31221113ba50d4d2208d82bc7d7a24c8dbcbd91454cf0a59bfa6644596903c", + "0x2b5c3bbbfef813f8de20f6069c0a09f987aac6788699353dd3af27c8d4dc8e8d", + "0x2fbebeab188140d45db2bc051daa896bd268f784d8f026aeeab57d51ef3f597b", + "0xeb498e1efa69801ca17a286e13608590a3dd0dc2a9507b0fc9bf52997f09e015", + "0xb00c8fadb7815a83faf47512e4f2e4ec1b7ab363a20ac1d2335950eba2835b1f", + "0x4944a082a7b17f88957f80dbb64a337ee62d5deec61e0a3ef32adeb6e9ef6366", + "0x6c18abcac3eab4f269f3589e0a4b404c5c8cb81499eb1fa9eb72c534a81ee7f0", + "0xb8efb3078dd1770d81728213a8ac037e27c438aef3648f6e176ca9ffcc017515", + "0x3289634879d9b7a29351bddbbb6edf09f0c03feaa0ab3dd169c55cb16adc57d1", + "0x4321b7a713898553346db335661db57ebe877df3890ecdafe7b9fbd2ad3704ec", + "0xc143229e840b1dce19546ba80654725336f6940121b4e69e2d240facd962c071", + "0x2ef474d215e87dd9e97a2ea9dc6eb4428b805c423856e77169888f1b6bb5b079", + "0xd350a5a27faf52e48431375f23136db50492f19d03477c6ef8055f22745b5a45", + "0xef18832a8abfc10a9c0b10cf2946f2831acc610acd6b3650a046ffe45c669dd3", + "0xd1efc93d1b0f8880904b6aec3315bbb18ac6c88c9a790aece5479d5ee76632df", + "0x1459b3c101e6d1a4540031a5da50e93427774a21166d01d5439984b08d834377", + "0x6d49888ab094f3a4d161e35b6de579455abb627e006de79d384041808a6790b2", + "0x047de2e4bceb83a90df84ec40e16e4c9e9c9c12491c888361b2f0d43145d72a9", + "0x6929fdf6b48c5af7d169860c26856eeb50c37b1dcd154b2b7342e9595a85e9e0", + "0x08fc80ff0f188308a072dd11685c4c9ac1ec7c5a35ec819e191d97f9a24f75e3", + "0x47a030c03e6ecd6dde93952d06aa57b4159d0c7b0f5941dac1eda96faeab077a", + "0x9bfa2d4bdd687a79549ec28ac73383f8611bc5cd7e51f57258e8142cebc30fd4", + "0x8f0436d3cefe944e0f38449978561592dca47ac0507fa0c07d1f81beaf7f692b", + "0x168b5aad11cb09e02e0d6c9cd5c7090c554f0d0986cd31453ec728ccbdcfbdd2", + "0x84f183e0d04013eb58a55886c5b48cf1337a3109802594bd215f798c7a61261f", + "0x39ae977d7cfeefd57447def40052e2e8955995b957d70669672d21e42b190a8f", + "0xf16c96c241f48843b45b0c947699d5916b66061c965e2249a595de26b38e35bf", + "0x04c17267062e6ae15fbe943f7d9afb9707a979466c96f1236166feaa8d2c3bda", + "0x3868f2840b3e946ccd5f6af5a3afc15a6fafb27581acf1682cbf81eeb927a34b", + "0xd1e3ba0bd87f1e5b2f85c47521393d321925aec1a123118b2a06dca37a79bf0f", + "0x5172ff7d80669807c6d801d10c5a05b1daab6df2986779d439472a25d561d6ba", + "0xcadd13ab20d5f8abdb7ab57d7e3b3dff13294295d19b218bd59ae211dcc9f36b", + "0xe41370e6078860055af37dda5971bb9bf2ee34e7069cd3194de4874132f88a2c", + "0x461eddf926e0ea86e6b383e154576d6248af3756eb936a0639b2afb5c664d59f", + "0x7448ad04a839cf8723416bb117c7f4834c461b33bf22ea753ea61d7dbe716398", + "0xf75170e49a7e46d78fa8555c73aab63cf0f0c49e3d7a3e0e2bb5ba49b3831eea", + "0x67d1610cdcfde03c10055df757f0ebcb99c82ffc437d3b1b79a9504c8901f13a", + "0x414eac8ee6f962630fee4a9520e80a603efb97712df0ae629e495c7f37a4f2f0", + "0xf6f866926b481bcb549bf4ad4f4a2bc6c6395e7f00fdc2ff6eb75c8cf9ca9ae4", + "0xee12a097ed1652b7c75beda9415e1bdd2d6be3c83943838c430a43958ce12513", + "0x4e422ea575b93d06d18b992894cccc4c86f0c021b196273c2182bebe21ae33a1", + "0x9dce37a6198c02e1e38e2a78efba593c22e698efce7905563afecc00a0180a67", + "0xc90c289a93badcf6ba66cf57cdbeab5de68dddc9f9936505dfa601b8b15d6a96", + "0x22bdfc5e914e0d6133c8635da275e309cf4cbcaee5311e7f75ebeff86016b6ab", + "0xc22f4c254d27abe9d40aff2021797c29c3a9cdf72288f31799b3356857e6e186", + "0x9d2c7c155d5a08470cff43a513b61aedc48be91a23ba644c344852996c3e6fa5", + "0xd31eb8b5081bef3c6b7d9e9ab5a89d654a5ea32530fbf93bbb688276ec26d276", + "0x392f93800a9f1397ee7b5687bdc53b3827a870a9dcb25d21b4c711d2771f2fd4", + "0x31db066594ad41c6ae9fc906d2d4ca408cdab26f29f6e11e28009619ae0965c5", + "0x3527e882cf075093d29bbd62234c3ce2e3a3f6975f9959a442a1699a24863f8f", + "0x41a1d565a0862bd6f58e66592c5fa9308571909b38cf19035787a02f2030adf3", + "0xf401735a02099a7cea6acc873ea170550f42ddbcb3c511bd760273819d14a81f", + "0x43e0ee63875a08a864978a1152a58faba856d0de375e985354cf6fd0ff69c057", + "0x05f1712732382006bdefd429df836aa2ad4c0cf2aad98d6711d84a8bf6a86457", + "0x7102412851bff05aceabb4ffeb9e3500dab145e47b1fc164875cca1cbf78398c", + "0x6785e405d5ffd9c3b00981e9e9648d40cf14af982043a98ff9cd93a95610c4b1", + "0xbb853eb11a4d79ea4d0a7734d79c2ada0c1c5222460207001cb86503a360a336", + "0x75a142b9b3d21bc32d474bb02800d9780af0def0ccbfb0ae39d212d775ddd386", + "0x4c09e3d07460761dd0fdc568acb3b953e315f188b24060bc65a01e7ab84d93e2", + "0x54121ebe4f99ce9ef752e41ae2d6dd06070c44922d71ee509797828fe0ab7192", + "0x941036caef35f47c2e0d204edcb468f8a38090c356428178353281e08ced6434", + "0xe3a12f69bdd71c85731baa47b227a03ee86c59ba197e31898d76a3da767c696d", + "0x198f7dd094f758f6c84581c0fd73044fbd0bd76f4341ff66d5e57e583af195e2", + "0x0b97d6ae2902a3d4c6878aa929aad11abe0254f2d48c0ba381cf753ee708b466", + "0xef336f374c5a225939b288ce95e0c1cae2027ce236156834e6511d1c62cf809a", + "0xbe314df0c58825b04aa5d6c2b3fafe0cb40a4f85b6b063842c1eb3b6e57b493a", + "0x8e0a43f5a2d5010be9598fff3dd1f74bc29be6132b45bd76add6c0d0ea73e4b9", + "0x44ab9907d09df3599ab57314462e3a3c9c96ad2e79320e3c02bc621ddb8b45d5", + "0x19b08b926f44079710049c631793d11cf4f16063ef7678ceea35e2832824cf01", + "0xf0583addc5c9acc9052494042ac9bdfb4af7b8abf6f2fe74ab52338bcac92abc", + "0x8c09477b0843cc63636d9dcbd02f82b86231d58f1cda90568d3a8a94a83c0288", + "0xd52d36880b3d66bfed13c01a862521f41d69a86fa82128bbc1e0a1103d4c9064", + "0xe8e3cececae0573329783434e7054ee235c2d696d7a9886f38a0cac985c0490b", + "0xf473ebbbf10fa24e5b69d41051f7b413a5112aa03b7d224a9f93a552025d36ff", + "0x1b8c35df755c3268a58913ded58615493ca8e74e5bfb21357c9ce06f23c84b5b", + "0xa72dd6a23df8df0b6ed7f162928632a3aa76d5c31a27ad1f3f8409f47550f533", + "0xaff1873033f6eb405113365db615b57f5856619f4a7aee743f9d6f7fe84d4b9a", + "0xd6b243bd0f7aef93957ba7593ef5cdedfda6039a4db66f717ca404f88e6e8c88", + "0xf61a8948f9d2d5f0e847c533260b90b5cefd0c432e2ad38f2f958bf514922fdd", + "0xff98924e701d17b2ab96555543535f0becfd7a528dacc827ba84176972153574", + "0x9aaf83d167ba1097c5b97973e548199166138f99293b43abf41e6fd472806b94", + "0x926ab6fbea17e13e07caf13f03c4a321e1402316055001cfc738c7536b1940c0", + "0x5e0e5f2183201db12111029f703a06a2683d4c6a1878d5e862b734e3f4bf02e1", + "0x9fc0c71959624875fbac0d861510f4ee8cc59e27a8fac1165657c058c71bf1bd", + "0x74846f6d509b071715a01a4f86cf93a8380ac8c466a0402f65e19da32bfe3aa9", + "0x000b1d3a7adc2d7654cc6a13a5d4afab2d58ea1874e4d266027f7e3f379591e5", + "0x6e28d7062323bb4817c220b2d1f142da6b6ae43e021d3f51739a6f5b6f60ccd7", + "0x6b1e539275da923e35068eb3a378da1c58d6bbfbaff2445378e7439d6177d22e", + "0x01a5ea09722078172808dea23d42c5aa074540bb9e589d17ae0cb59284cd5a4a", + "0x32e53cab2f37d1bda906fae7449d23b2fa69b0326eee0f1481301966cf204fc3", + "0x0489ae94deacbecd5edc16fafd0822d4f4cca6e4f2d7bae588846fad017f3de8", + "0xfda4309b57951890fff3e07a93820f880094ce4c927e4f16e2bd152240eefc70", + "0xb7b3d2f94ec5d45092c543d96caa0ed20c896874f7644b2263e2936a54054aeb", + "0x3ba5ea7b9390ce37ccfed6b32eb9c5af5a77818105b0317c1d29c0f6e94a901c", + "0x99e1827ba24fce1860fddf2d7fade56f35a48d165161d2f4f6dac92c1d0fee8e", + "0x16bec1a80d5e5a6e827eafcb7dbff670a3b3331babd43301b124f392cfa7404e", + "0x1fea97657f1b7af6586111a081b4b00ba1f6f4d1f229ab276f5acf6b15278e8a", + "0x1814cec9b1c2edf43e3236fc1a1785237a091b64860e863ff5a9cc91ab43dad0", + "0x4c15afc5573e70608a72d03c9f38e61f52e41697f33d50a6de855cbe9d8adb22", + "0x739704386e447efc6e8f6dffbf9bf505675bb1c411a90f5a5b3b83dba718f9ef", + "0xa5d9a9f3ab6816933763ae264fcc32890a8ab588e1720ed807d52b5128e098a7", + "0xbc73915c1657f69d9dec208843d00d8e3b931be16d29ab5b0615541e414b1177", + "0x0a1d5d05d855c44f4b9b0604bc3e8c1f39bf98b686b93329c7eb5d684d3def4a", + "0x6b4cc3632823719a2db7f97a533d1cfa76316fd09cfe77353f47f97490392bdb", + "0x057d200b707de2f6dea7d8e804424fac8fd4b8259c77e0fbcd32e31f30fb6668", + "0x8a309f5da2e2e5b585bc451dbd1f6ed9b2b6da1435aef886ffe382a96fb18c0a", + "0x216fb50ca3b72f41a03e57bc8a098207f7aad442e68ee5220ab716e226c38b22", + "0x7203031465c73bfc14520d66220c7948bc870760da704fa4866b1c3b2178a84c", + "0x1c16804920ba326d5d560ad8427dd63638d268b715b92ff70bfa513f1cb811f8", + "0x1b65d7d85f4400f484faf261c6acb4c572e3de3b21a772edcd44de5ac5ede3e5", + "0x41fd00de2a7219b8e9e50a578f968bfe3dad8ac118977da7f3f9014afe6ed528", + "0x1193980128e8444cccf6570940b6ed96d6cb84ed3d295522ce0d5dbb6f5efbc4", + "0xce5f9cbb104bc55d59e6a46f49c26a956bf6aecc72acc3dd33f73bb9ecfc3b7a", + "0x5078e11dddadc7f8666a92678568c0df7e5717edfa1715767887fe64b498050a", + "0xb2bc143ba83a5510642de6f5c188ca3e532a566cefc85fdd40f7d41105485d1f", + "0xa0ace5a238c47a71652bb5fd5dd74701fcf083959251c39d95484ef7fdf0203c", + "0x7a99146ebe30477990f8ebd7781f5e4e613135663b07aae66f644f28f1613bbf", + "0x3f3b48625ada1b4d06ae9dbf1850d062efc00298a8892e55db54525e884b09de", + "0xca61b81af1ae724c7a96a407b235ebf4c2e541cbffa7ead81add29c990f24cd0", + "0x1a9600926bbc903f87751504ff1b06a86fd839ab2737ab57cfc72f83d6573378", + "0x98b1322dfb85167a8029d2e0409ab7ed8998b2b57fc2322a7660d5575aecab94", + "0x4dff31bda99a6a0c14af8b4504e920033c28bee86949c1ac6f31209ca3d328f0", + "0x9b0be4e5a57853b11e66f8c5c518de0aa9963b7cf5da5269cdeff1c791cdd569", + "0xb187b2a0aa947cb169e1ab49dbc83a8011b70c6563510a3bc83eab82f85e4645", + "0x6e19072361f6ae03cb9960c013b6605a91ea052aede290016cb1cd57996d5b7c", + "0x2eb96aae2bfb9ecec13d4f966376ea273b6dd131ebff85c0a412870490918a77", + "0xc2f5f732793714f7796ae13b34caa7271705b71dfa6fd3c05339136a8e9130ec", + "0xbaf6e1a6308cb296c95d5e8cba9bd3e2e4fe3a37fb6535df5bddffa5db62953b", + "0xd5178c769f95ca086166b926bab1f70590a0fbf2261415cb90d7a268762c3a9d", + "0x63c9586c3e590f521edfcd504041c38f24a8e854974648d0539f60ea72850fcb", + "0xeb9208f055cb3276b03a0b69b0bcc10c11e2a71b7c13fbe861ffb71e9dc265cc", + "0x8ebcf3753dd515fb08bbac5040703cd23772dbb0984ea12dbd9a430fe54bc619", + "0xd017fe9e0b5719f925536fee65a10c7243102ec4a7e6d50ebcd0e63847e2e842", + "0xf6a688195992961b66ed8b0134203e4623ee5e378ee4072da8263042aee6b352", + "0x54264173783c6739730974454f194250e5949cd4a2c8fa4b38283b1788d378a1", + "0x2d3313f5196a6fffd61e22b1cb5c1aaa554aa84980ba2d1caf5070700edbcd90", + "0x3e13bf40c11a9f380a6d298a38fc277445d5ea478b6e5b829be1a98274f10b42", + "0x4163361bd433f5ff49789468c0717bbf430140b5085a43eea1b5401bd70d0c68", + "0x0e2ba3b3dcb56d21f4cf657c36cfa32a5fbb556b79cb783e880793ede88e1587", + "0xdf5b6113931c6ac18648a78dda782f02f8effccbc9d514103c12ebbcb9d8293e", + "0x4d28ad7b10d7f8bd8eda839b25c6df172091391db2f02deaf9fd8f3e0102e365", + "0x142d6665bb5cf064c4f2db1b4e30330b740545622fdfa7d2a97052d21eb42daf", + "0x07c692df427b84b309a0521a72ccda8484af324029406bb25f27c299bded8da2", + "0x99be01ccbda270f851dff8309cfce33c5642d135d75b72256957df3646c23c46", + "0x9ee5eee7d0617dab4fa4b7284cf98e97807e2c65c13aae6254bda63d9232f91a", + "0xa5b57af9489676aac1454e5e24a6de88e6c2b36aee5f26a125233a95679f6ffd", + "0x386c450d2b24582769ad8549c79e7e212af082fae9df4fb8e938c82c9e47af51", + "0xe913ad2b4ca5103d24b42becc0051a028fde7ffb891c84d77958bf294e686b8a", + "0xd08b2df40e95a27e21188712b92ce6016e45d1ca8dc68b9168f3b0f722017bea", + "0x1169d69a359d9f7114238f6f9f34fa61db1399bff1cb763fbb6f3f87a654a6c1", + "0x351f11ea01fffaf2e4da16b78872a74ff22a4a1d3c6ad5416d21a209f4a718f4", + "0x46b3b58c7dad05a9db61f69cd3f9e0f926a6e9d3710a2433b5f8152371751823", + "0x2f5e696c904e68c09468f5514831912ab202adea4b0ddd3eea6d46ac2aa43446", + "0xe6d547cdbb2ccc845c8a8412882cad142b09d53d43814aeee6a55f7676485fd1", + "0x881a4f56ddb65368a8bc96c0a81e37088649016ead7682f2a05478282b829b7c", + "0x3773ef5c4af9f0dce35721474e31288ae14262dac328d5c2a44ca257ee71db4a", + "0xc8986d77632b3e773efff2feb07a98bcb53866139233a357959de4d0f216fbdf", + "0xc390abfb8cfcfcb942ff1b1bc503ff0bbc67e7593ef414721f3c0ce929eda1a0", + "0x50288c1e9007a2ac5caba696fb354b8fd663f6d712ffe302090cf5aae16a9b47", + "0x8246a3aa5a4835a872baf03fefcd1772402154c837eb6f6a788c595fa4d4bceb", + "0x5f56b6e3a061cd933fdde779e145d71960190539fabf7db1cd279eaadd73871b", + "0xbd972befc1f97187cb90ceac34bc0491a3ccd098bc698358e6374223eca1d724", + "0x0ffdaabb5dcb1f3607ba91370ba9918809ac44d488b02c3edb19d9f0b6955384", + "0x6fe59edf6d8fb20afe4b93b12335b82ba70f76fad094c627af0d925bd4199347", + "0x468067b58ba553ef267217469dad7a37eec007513a5310b20f5dae21f980d191", + "0x6f5d3864cab4e72548feb1413907a153187c0f3ae495933ba477f5d95d4a5088", + "0xe1c481892afa48abd31d0875781c1fca65d22aa527b6de87fccd3152bf62ddc6", + "0x70208f57edc221cce58084fc3bfe64c9799b103a64c15e5ba124473022e87ff2", + "0x99e577af847a8b58726414a842c82c152c55094bbf770b9bd9941883b5b9d521", + "0x7e1a5cf44f006020f31646e629d4a2cbff7ddcf749a355ef6bc77dd3acb0ba3d", + "0x56bc61050b27d0d1ecd2c402c23daf28239b546695de11402fe1f40a9ab204b2", + "0xdf3421f5bd2a77ba6e6c8aa34b34dd688c5907bfc8d93d668b3a2afd1658aa27", + "0x8d0d621436cb6ff4ab3b9b74bdeccfa8004011f801e18c50db57da1ed512f110", + "0xcbe9acd428aa0aefc9553948254541ea19b8a946fd8c41b8aa459faf54f84d19", + "0x36368e2f53d4bc00ce6d2e6e4ee6a24f2951109ec4e10ecb0efd4e8c6ed09606", + "0x986723f87eec11d507d326664a9da484f2a51d9e32d4cbdc7291593cb5fcb0e6", + "0xc79edddc3767bbf120a27107539f61bc8708d1c56cf3fcf2a1a6070b2c95e1af", + "0x86d52eb2fcd43279737670e812ce4ed215ccd9c79bcb1f1c2b3ed611d5e0e11f", + "0x36364a121b0f86bf46e7a669186df228839b650341dac0a52be214f991ee0462", + "0xa41eff36fb29742c7e56e3622410871648a20c1af346eea1bd109b642eabb264", + "0x8023f7728f6d027e33b2e0765c05c7da25c0fac40d5df4d466e6f2bc7dd1fd92", + "0xf3921f10e2efda27c5b8b459f0a4054490856dc024c45f980d463dfd50abe7a2", + "0xc091201fba2fa8bcc402f9816a2ee24668ea842414ca259ee50a138d352f10e7", + "0xc1a3d71c10860e14afaf83dbe4e8fd221d024ce410c4c395c1c0f4da4784ac82", + "0x364ba9f6f645a68a4c2e972c9cda468a5e962fa6601789a7090e88aafa4ef641", + "0xa3028b2350b2bf0a79c8384126b76e3de009f70b00717ce3c3c4aacd22f7a09a", + "0xa45de2e04250ad9eb13804104510cbf4e844b05291240ced4f2e31954b19df08", + "0x1e31ae63bdba13de9d1487ce1baab93d2cf8ffacb177412425b1e074dac8515a", + "0x7b4fb3f7dae80534b3ad7dd7ed0cb25813e9472da67634b7fe38a2ae4de6a5ad", + "0xf07f5e7d394f669d6c36341db8c0ee285f57490bfe610f8182de35f811bc043d", + "0x01b3279dc6b9fd492155e9647cc99c98773486f69eb8f255822001fe9fcfeaeb", + "0x14909fb2b40ef309d6525e776884c90e6b36eee9f27ed7770ec89eb180f0fd90", + "0xa88d68444485af65267e315b5766691ac4c93b229f315596f32c00b0589525f5", + "0xbf948d401a64266fc5e0a0b5e32cfa9b357c313f60deac137bd04ca13913877c", + "0xc483c1021f43bcd615334adc07828c091f1b65bf4ff66e7d2e9242307d42551a", + "0x0833c9541c6de0d3694197520402811b3daa87941fa2b313d47e0389688aee5e", + "0x3e62de29e9dc9b4bbbeb6b2517b964d638882129d8a69167282d619747563d4c", + "0xe17a557a2746fef006ce09cfe6ff9808b0db155cdd89063cd7e87dd319b48eeb", + "0x5107efdde4c04692d0463421c28faee9e374e536ddeeafe1c82b0ee95ac661e5", + "0x69478bd07fb306d6a45797cc1c7dff9a9baf816a723cb35caf3492db256cef16", + "0xc46a2985a72cd12a1f15bc58b79afe86c8717492c3430ca244d9cc3d8a112fd0", + "0xda7783054f92e09bb7ae297a8f0c93ae28526b5c3dea08bf2ad34cdb503571da", + "0x331c877c9bbe1ed1d3f719db2737b25c6d98ae3e832d0b41fe8eb24b678542b5", + "0x2b7afb862c8d49e1dd7656c9e4bd43ef3f2f77ea9fb6bcd432840a0356a0ed87", + "0x764b571be7c608f9f2de5d53dc6612b6223d75db37925f4b3a29ef9546afb5be", + "0xf8be6fcd1e336f2749f51e9ac0ac7260e991ed2d2b8ebd7770bad2a522dc7e65", + "0xdab6e0db7bcc8341d1a5414f78ce5c0ce7629b0a572bb9bb02d87be540bef279", + "0x5e7dcf06b6dd84f8c45c17e4df670716d98d946c465cbc1469c5509d32cf5be0", + "0x152a6e3e34aba601a8a6f3889bb06c94e2fd85dde795fd0d275e8c827d0691c6", + "0xd9a809b50ea823539bae8371b81ef5a618bbc98dce372adc55c2e907b071b8c4", + "0xa784293620ba294fde92333d42ced76af6279221dbdda763e8b432e67a61d6a1", + "0x720e1d02a6f416b36399e8bf3fb7114d4cb44cd4def46d1c4c311d06bd62ceb8", + "0xdf9d159c28173e2ade642f7a5009cdf316ec78c11fd3e0f60cb5e84462709aaf", + "0xeac39869f0df87eacd462bc0c1fc866cabc647db1cf36ab8210d832446b1753d", + "0xda98d7e88550a672acb3e921278ee6e6ebffc3537f87bf2b8eea897bc439ab69", + "0x90730c148cb34c10edae1301f65f7c2b622c525d2b5e1ca9acbb2f8493606124", + "0x38a96b3faf062ceefdec463e88e67d202e4c7857227080f583fed156e74b9211", + "0x00677cc482ccd7bdab6006674e6fb5d206db7b9af4a18417fd8d2beae76c71ed", + "0x53ce9788e0fcc3e3d1be86effea30079ce64d65c1f5389ed8efa9d9451c209e9", + "0x95435e7a61982c289583c3497b3e8d1c7446ce7b016ca1b93f323bd468a6144b", + "0x7df1bb66f65418fd3f623298e960611c03a11cfdb96259b26aea88070c518fa7", + "0xa19418676012e58f42e06741066abfccff2e768c0d0a9532f2026ced5205efe3", + "0x59f8cd10446ba237f286fc992a37b0d52ab92b22b5a32a44f3604ae3ea142471", + "0xcde90f91577b4eb9aa11d155fbf46abb66a18bbd92e48b2c5db7a9892a8c0cd4", + "0xa1f15bb33372eaee3f90ee9c14364254c67028b1172d0603d04de6c30349ca71", + "0xe9f0f38dbed75372904e7275d0a55631898b6e8ad338906d9346f05d041b00e0", + "0x5edb8565893188092d66a0fe4a3af1d81ec1f3c36a5dfbef4f8bb74daf929306", + "0x736709f1a21c888ebe0d8844fae6105b835a9302c2007c8b8bd90ec9b2567b79", + "0xfc6af0bbc38561377750eb69081483df99c35b76c50ad411631bda3fe7daa349", + "0x54e4fcb5c4ec8b39f12ca8f52f12fd076dff76905f0b5d2e3d138e368eae8da0", + "0x996213174cd9b926ad2fad8d39ac43d359f1afb2a64df77fb8dcbc0a2986251b", + "0x6e91f2a084b87227b16850adb0aa6a48db1cd2def8181316171fb83ee924fd9d", + "0x3ff0b0ba0ba01594508ea05cf8eba196873b7996f5f24b8a3c22b536dafd6608", + "0xcdc206ffc56ef75b6959eb5445faf59cd74a467324782634e7714800f112e7a6", + "0x706715ce7cd424bf247ba07b3a931b0815565a14e1915e701a6bc4c3569e3f25", + "0x8837c325168f4ac5a3c0854bc9fbd0f3867353911517d701a08522cff9b28f45", + "0x57633cb4cdbc8574b0ad08cb99a79dfa1e9eca2c140417446170fbfe947fb286", + "0xb27af713338452c97f3eabfca09f7082918b5c3a08cfecd41e9a7ebca3886dbc", + "0xcece03eeb5684beb21bcc1a98e8c5ce3eb54f476cbe03f25da36c4f4fc9dbe8b", + "0x535b5bae889e8851585d7c1a4665c145a125d038813cf09536ccc1903648c905", + "0x4a0d4558a2dccb95216d741d5c0c27abdf42741a90ade08da66bf3e42bd2c890", + "0x66873748a8f2578005412e9a4735090360cb577a5130ce14ed03c3d932f57979", + "0xdadcbbf9328a21d34d4b34a5e926c96d4a0cc4b000b8de028644ee7a206be497", + "0x425e98f31250b8d5b30ee2edb09378ef9956de7890d69c738f6333aca5018e6d", + "0x4796893c4ee589aecc1cb5c402bb1f844a9265fdbbd4344de413b96a54e7eb08", + "0xd71b61f1c6f2cb24e35ac83eb5c9e6b5d6daa68b7286f3479fc1e11ac0c41571", + "0xee5384cda1377490a836ff4aaff51b82da429a25809c275912ca4864e9dfc122", + "0x539fd1f60f2d989d524cba763d75f67d6e48833bfac254b4324e3524f3cdf02e", + "0x71b56cabe7ff3f632f08898c677cd957ebeb7d21001c6078a1b55063c2771d3c", + "0xb9ffee36d9b31c41af39f8d645aefa4ebdae48504504c347eed472fbcb344014", + "0xe56f5abb5d875ce5cbf2e8c5ac2cf411bf019cfd028320782ed9799343d14833", + "0x70577c9821f82ce25f3c4d0d4bebaaff42a6b310bec46017b86df69bc4dbf2b3", + "0xa61d296d7e0f48fe5aee2c9e056ea0148fd97bee82ce3712e499ea8831c581d0", + "0xa324c05ea6675a448d4931f59bd39fd43b90dd5a9f1eeb4d023119de0dd32f12", + "0x7dbee82e43d805d6a6bee3b9a70bfc927536865705ecc6e4ab1ac8061433bf48", + "0xc062ca0e8f83649693ae5aa5b0b85ae41fbfa0e2cf9c3525118300cf8a771dbf", + "0xe294ecec43538bbcb29fe72d003f1493db7cc4fc0c54eb96c3119c572941ca5f", + "0xb87363fdcf8b64269f5ff35829fea7607ab47ddc09a8f578bf2868edcaf57641", + "0xcbf4a7b5300cddaf09611baa0a5c60cb12dea7766ef1a815968fa6cdcb0edeff", + "0x1ea4d193adeb12bc50607fc0fdc25c1aca43d97b74dcdd33cc53e206f846ab07", + "0x3d3c830a16e2422755b8beaf4fa55c8b290297d113e687d9e6641a9d4965888a", + "0x5b4038f6c8ae5d810caa842a9086a80dc2e33b04817fa36ead553d6a80d8f153", + "0x7f380231c3a6cbd31f195a5decc89f5c6aba16ece2b42da06b1bd2ac1d45eac7", + "0x190239fd32c27e4b267c48deac962f872ce190bf7718baf7913281ec9691deec", + "0xe58df321ac6ed705ad3bc8a2208e5eef7c5f6c0b94e3f71b6925f8a2687b0770", + "0x9de481bd70ae4f26539439c7bfc857db9e367b4ae2ebc56710f9261ee8d98118", + "0x30ebb04d674a5063ed539dc73d31c4e7c414074d102f022388548b7058fc3781", + "0x5992f81ad280f18a3f87f65688fba05fcdfb56603892c7a88e77ff8c2cbc32b9", + "0x25abc5275f862e48815073b9fe3c4f15d47bfedd9005f43a277e62f8224449b2", + "0xa3ad68a3db617e4b1c512c921b98c9116ce36b5a9107d566fe479c84a9ce7622", + "0x6abb1fd095cd271a4aa6e26dbd526d75d8f836ee90905c6a5f667a061442f4be", + "0xb843340f70a5ffba65460d6ed959c03bb3d11a5d897c2d98e148645ed7d3ba4e", + "0x8ec990099c79a38d51ee78975011ab30cae174db7a17ceba392a5cef24fd878e", + "0xb1b6ae9cee08973e448abf09eea43b92d0eeccdfef25e0471c20827cdf43a759", + "0x8aaeb4141887b7ed39ea1ecb0415b95215133ac9ebdcbbe244f0f8608ed07e22", + "0x5fefc0cc93aba8d5832edb2f20e556b00dc3c0a62133a30169fbaf345e028fc2", + "0x36eac0aa2192f5ed51e062b9bfbe16f9e4e77ec1fd02d5346e4efec8ed1a55a3", + "0x70b543c45187b8361bcc5d57a182eef1246e8ce3a57b3d9c9f2fb5bbeb8ad7cd", + "0xa869e91844a8dcbc323426f53a87ea7ee737d05b7da44d27ebe5e9dd1bd68c94", + "0x4383733b62da92ca95dde42f5912461d58cc7b6e02d4886cbb1633e7c4e91938", + "0xa7e79ebca20b363568340fa3d292d099176023b650bbf5108112a3a45a9386e5", + "0x6f47f6ef09cca3ea0d758701380cc8ecd4a9caabc78c71cee20b12f600166c44", + "0xdba2c0a135178a8714d295e1921be53ec23184e933c0e872dbd2c73ce1c65167", + "0xd441973ccfef8cb6bdb5a385d6e18780268544cf2413a3104608f23de4bda477", + "0x310f2b843c19ee61ac705586647ba11b30cafc0e761fb5cd853df079e3232ae2", + "0x9cc003fffe6c8718710bef9356432e2102c2cbfe783e02e5b2ddad299130b553", + "0x4e8d34c5bf52a6aadc59441b9583976fa619aab95ede59057cb2cf99bce5c1b0", + "0x967a05f47e2884cc7d9a12c1626338cd818b1cec458af9e3df5490373adaaed9", + "0xf505cdb536cfe06c0399e9a81d1d70d13c716834ea3593d1565f6357a3ca569f", + "0x7507645edd82b46de060ad0725bf4d987598242d9167df8e842a2231828ec1fe", + "0x3cf03c8de7182a733f4d159721b14dedacc6f042309e3ebfccef049e2c5020f2", + "0x271a063eaa45d12fda3b36e8941db8ffb6e3312cdccdf1eac587f7f812500266", + "0x5b8159c437bbd7fab286c205a922ddb46d2b36ce9fe026ec8c2a8fe32b1dd058", + "0x5803edfa4c5fd7afdff7d93abdfeef5d4b24ef381ef44375c6c45a14267661d4", + "0xa8bcb46a21f3aab34fd0023093214739ed9cb31c9436b9ea29cff49668ccfef1", + "0xfb1b3d562741a4c053f90076fb98f0de53d022dec82b3d022d05559fae297fbe", + "0x9a513379bf4d22593cafa4c76872404448d5c71f0fac883985b5528bf89df7c3", + "0x061fdaaa08796262dcea10fb107e319d39b58e6e81f4e22a5bde2c36f092c300", + "0xe8e23d389c313f906d4bff516ee505accf61d43700bd76a35ea797332e26dfc0", + "0xbda24139e3cb6492536baf8afd5944a5ff5d6347b4426f208d0c5f711ccba6d8", + "0xd744a6e5b4315bf2e0a29ac298ce1c1b7947b9b146bdb8dd5edbcaeabfe814a6", + "0xa441545150c5a8754f10c5e41804608062af6c779712f959793c4c65c1521578", + "0xd270437aa6daa27eec4e4da26cb1c0e8f2859aea34972bab45aa8d1aa37f177e", + "0x3b5ef123eabc73e19cd1e3cdb1bf3249535e9d01811ba0dcf50eae6b22c97929", + "0xb0d0e9e597858944e92b1c2328c9cff5bc10ff37a75b84aaf4729ddfa99a9797", + "0xd98573087a3df3b47258862354819ca399e0379638112f590bbf77f54d754bf7", + "0x0f2ae41274716fa68c24a23b24fef8e1c969f76fcd803d69898c8b03251ab6a7", + "0xa9bae84958088e5450b72c468501f5f6f58cb9d1550277b68c79ee481e3790c8", + "0x44becf48e3a7dbe386e5aa571d42ce408bd536be5bec88a29f0d7c4e64c02347", + "0x76efa9804b3b61aaf93a098091a82e489c888050262154bf35255e02cbec5f86", + "0x4bdec2826c587eb92bcebd658beb6a4eb80249396ef0693af28a5d4e5d2e967c", + "0x099c77576b5b731efc552511c1ff16fdf1badc0c26c34bbbb56f1c374176ae66", + "0x5cf461781389c0628d0630ad8adca64b51bc8b9313afd1cf1e6b0aceed31c70e", + "0x7ff9af4c236acf6c74b1977b2c4308cc8b0248acdf9a371263ec77cb480ed9a7", + "0x81be4c30bdaf556c520aad3c1b1f6cb6857de4c8fc3b64ebc8095d3767632636", + "0x58aa72dc9f5a07e6e20d7a8253f771cfed9745ea3b4e0ecb31a22e9ae6602d21", + "0x718d9f720164a0d090286d6f199158a99c0a11d361d136e2e823ca683f4c4f1d", + "0x059b9fd379d28722579a6abe267c297d841639763c4f8dee2d22c4c16200fca7", + "0xa862c53636c7d025f1744404cd467299a1741f03226f22cad48a3bb6616c8a90", + "0xa969081a884282f064aaf9fd279653ccc9e1f21a1648878da5d0c64ca37d1ad9", + "0x714c090b8694bf6528e57ae34debf38116198df1b3f5434f1267def29bfdc59e", + "0x9580351e40b3a4cbb24899d24e544034419db55df0260f2ea8a019019c4a4bb6", + "0xc18e1dfd3b9b6d737de3822a8fddc2f013c3ce9d43b93e23386bddc6d5adeb8d", + "0x2fe3f88c2506319f6ac4de9eda256457205a31903833310d90d594096c8aca6a", + "0x9462b4df622177a3ee8811ef00cf7dd3f10f39f02abcb49b45b247e940b8603d", + "0x124714071262f3535bc3681df4ff64e479364c6573edd2aeb5bf20e99038a3ba", + "0xb59ee1da82b845b2a39e8be37703add9a511cac7f326d2eca7df454ba4438788", + "0x5ab4e66aceada0aab89bdfcaefc973542036f2e4e6b3d24d9cb2d9823fe6313a", + "0xc862a93c669746c82a9eb7a7c482916d17dc38cd146d2c8f9dd66084e7b74054", + "0xb1a5ef459150f35ca49a66c60a0b8f67a931a622bc8bc8fe9ea4221c7c18d804", + "0x7fdd4fb1bb717b0bb12150d9992a92396e397b05ff1fee9371763bbc98ab6fe9", + "0xf3f56b8dc466fce08727886ece3560d7e98739cfa1990801351555b162c161b0", + "0x534ca5836ebc01c872204980da4ac87fc738a03cbd12d6356fc014e97c59f90c", + "0xf768a756d4c804a29fd5b2d64ecd9719d425d1067235374c5cf952109c121404", + "0x4aa2054b44e48a4cdd3b4d4a3cc19cca2e116b585fa874330aa221650f92d4f3", + "0x080cf4340fbfcf154e2888dd47fd7fa9eb794d3caefd60a268f348574227c805", + "0xdc83f6d0b206768e8881af1457a16e4d91a7b9569c88d852356fc59c67da105d", + "0xd6f633fa1b9862d1b00c3eb015bcdfc383e8bc0f7a6e020d2461520c61f29ddc", + "0xe1399c141556433ec8fbb36b0096107d590384c1b22f83e76b08f84395c69fb9", + "0x22b6ea1ab84ef7caf066b12efaa9a7fe9a00b0a84b361bbbd8b75991c641fcc4", + "0x9bcfc0a093c6f861bb53fdb8f1b25e6a64792811555b724393ad0266659d0483", + "0x1de4a1c19c2342dd68a9fc37d1100677982ad975d4100381150b2058bc3f230f", + "0x20c30870890f8b2970ed369a91475ae7f79cdf9657ccd077c79c586bb71d7060", + "0x3b3ea1b91547eeef42b69a47b5f6e9751018dc2d5a3b9b69c4ef1c109cb89f65", + "0x9d8c7bac0207b0b2f13a412bbdb0cf8d2133f48231396f05f8e1843da8d31da2", + "0x0d7d9e0a598c89200f67d42aa097f4a229bc785b9aef2f710587b25e8d1f800d", + "0x4038cab84d4c1bb9d849b4fc2fe4128a0071ee68832d0bc60a463b3180e0369c", + "0xb95a96be8d5cbf74ccda4d47c52807997c8152e204dafcde90e302c61de5a35f", + "0x3aa264910a3904368ac05c95ceeb96280f600d081f56f56682a9d52c7919a7b1", + "0x113a95a67d268674751fee8381a3c5d62932cc40a77531e96be91927acf0264e", + "0x955a1913360a1cc67110debf9a30b069ce27cf2fa527e6e4c50e7c0a1c656e3f", + "0x1a463b022f31a39e36305ab969e6889574ebfae8857323e089b58b6f228ea939", + "0x1ecb0e4d7e29787c3972509cce760181550d8b6657687a04402ec52a546d8927", + "0x662480ba5fe2f06a7e97d111f9bec349492ba005ec7e427f34979a7f394bb2db", + "0xa268f6633eb777d1e66cc5645a92d5f0f2b70101faf27cc801a8b24675f71099", + "0x02b4e8a4938ac4c31b0d8ef7753da5dcb3675306709d569581e0cb373a3be32c", + "0x78c671c14bd0d8fa378294c4f00e53322398f156e5a8de7f73c6af363d1f45f8", + "0xa939c556156ce686c26e7db69bd8e94a40db88b3b8d5c310cda3abedbd0e7513", + "0x91fc8f68da57a3615b13b3d548085eea8e927cdcdaea432a08eec4013c2b3e59", + "0x29f3169c4687ced4cba228ed0e26fb8d24dee61c662b029b4904314ba58481eb", + "0x165ce1a68016f0d3365b2eef27cd4cdb9796fce85ea9c523ae3153b4c5730e9b", + "0xc3be6f4920845b082e6b9fa367f0e280a2f99238f3b4da80579228b6c4ac7bb8", + "0xf6a9dc76b4f84660ead26ddfeed89808bb08450f3b0377eeaea1ce711654de07", + "0x6ae0610a733a17ba561e65b82f94657c6b7090e8543dc794d3a57efbaafa22e9", + "0x8bb89307318187e3d054d4dfc251f5f5654107e2d3a72fb22eca41671f65bb1b", + "0x13396841e164da968eb88e5eaaadedbd59eef8fd2da1f34a9adcf22f31589fd0", + "0xb1320304bdf53e1d57c3b97349b7ea16fd19c57817a247428a3181e5a85202aa", + "0x7440c18bc9d6b70d4483df41593511c724a89d29b4eddb48476067bf4c831f5a", + "0x23b967b23b553f09d4e9ab01848f7c0c3f2dd35c62b42df8e02e1138d62ffb7d", + "0x1405e1f362d098db90243b3a1656344b80fcb07f6e9bfcd910f9de1ceb0d5c8d", + "0xaf842855c8a237ff1d6a18071ca2588e37ff2715891e3d7d4a239e44acfb0472", + "0xe22be6d60381d62ccdf2648bc7bb6b8ed0d4679d5cf66e8d05af0180e183d2e9", + "0x08d080b1c3829f19882bd4100cf7073503e313982489ba23f7f8b65bb230ce24", + "0x6755d7c7b4e08538759dad8b9dce268296d42ccebfadb21c528ab38977836418", + "0x380e7553805ac401b465d4708563b042d856b84a0b1a82647db83267c9694604", + "0x4318520dcc73a3ebd9ae77219aed288bf4b85c78b380c870894fe5b98a9fd36d", + "0xc9d722bfa751b190504bafbe66355a01d5e3dc4c016280400226706355386781", + "0x8a5d0058ef51ee9bf858a7f99ceeb37312e341a5d3b4a4367ea272199fc5659c", + "0x006d42a7e60a8e62ae4d6eecfa2aa3e1dfa6e72aa955f42642bc9e929b56e978", + "0xce139c6286d66928219167f2844bbd9ddcad62c90adabaa249adab681865139f", + "0xaa186ffe45e1f583534bdb4c863ffc1b3871627ebe36cb04a20117f65bed0597", + "0x363ba0bab9688ec6a32cbcea7926c8ff63883738227a27b46f30c3176fb4941f", + "0xc53fa178fd4a6d28407d356a76b2b222bba2c91a40aeea378d011c56819bf3d6", + "0x2e911e80b5221049a32c54e76b6283b7a0d7619a5e937d4dfc0c3bc17f173723", + "0x7f42cc501f58cdce23203358f7ec9c9123c0c1e181f1910570b66e1cd8e2ab34", + "0xe78b6f562057f85696246e3d6e2050dd38edeff50a695199b1cd2190cd6412ec", + "0x75f5fd17986d3449b642ae4e3bdb0c02c7277678855a1f2d02200d0bdc4cc2ff", + "0x9c9ff244b821a66b672e1290c0157e09c7480c2087b5b24275351f1b3273aaf9", + "0x4836c8c912882f4b5b285803395fa356a13a868d6f1310a179fafdd621ab2a13", + "0x1604745c79ad3ad2a69bb6ad6587302caa04d2f926218592bb18ef3a0cb5b078", + "0xde7f126b0d49a12f363bbfe2eadcd411e2d4f50e0c3d87a80f303790949b3a2b", + "0x03f400d75cad3c915236e9902f3e8583d3fa4e63f78a23fdb7e9f70f3c113128", + "0x668cfcf9e1c1febe283538375432b7a780a5bcd76a3f5d869f2379a109a8f400", + "0xfcf5cbc3282bb6773dd13fc7f010be269539b9f21be617846bb7a5d64ef18c6b", + "0x23012fe4732216125343cf8b01bc90a5423744c34e74113cbf399bb97cf33e8a", + "0x4beeb7a956a80f28ca8980f5464d54a57188cadb7a36ab4a24dc914324c16640", + "0xa80877fe126d3ff79ae533198a73b419bcb4d509fbcfd7c0698690c598d14e55", + "0xea1fd8aff8064f832c452ac88c1beb1cf6dee3fa9144909b2ae8287ab1b8d2be", + "0x5b8520c4de8c98a524981bfe76d9c00ad7021ad4f0b852d437ea47fac689b494", + "0x5e8adb9588b81b6fe3aa1d501e94f15cbaec23ea06121fbc83f909057de080b7", + "0xcb719116bc94c258a43058e4a526ae70b0408693b586f6b87c12437e26d3d5d7", + "0x5b57d22bb3649b04b421b9574e5261d6d5021a7a6583890b45ebb1b7075bb28f", + "0x86a1fe15fc2a2417a87a044aa935d02553bd7dc43cc6b38d417f89ffec41afe4", + "0x8a59ec0b6a74d535a9a96c7b014ebb00485820007ba983bf4390df46a2bfcb62", + "0x4a21481ab25bad53f7f5efc4b57f1377bcef96a412036602525393582c36c255", + "0x53d214771e70e49d0bcf4a2a6bdaa6f268161ce89e69898b55982b4ab9a868ff", + "0xea794fd7cc5d6c8ace8e82168fd8663b2399279ae9e0f17a427f75562eb8538f", + "0xb3e76b97ffc51e4787bdd378e5efc1afab206f670948329fa6bf69577e5c6613", + "0xe2b89a9a044cf001e5840fa9175669a4ae55b0b2bb109fdfe097027eadcb31b2", + "0x2ab1f30724c79d09163b5a2fbd5b2ebdef7c88414f7e4fef1b99c4efe23a71b4", + "0x09def05f239c66b1ce35666e534f09f631dc1e05a89bfce9b7eba4b57e1de5cd", + "0x0278c2926b77e1624393c46f09b995c24eaa957b2fe05ff1db5938d967f3cdd4", + "0x76ce194d103f7fb8f42a8fdbf2fa587f949f23150a34a0912bad635199fdcbd3", + "0x44059c0342dabc5a51c7ece17313860b4501453dff5d6c8442681bac25be9b3e", + "0xedadf66e234e7515f6b9a1476a5c937755cd667c60fdf338c6f1502625800c62", + "0xcb739f0b6d65d1fd0bafa69437e729ef5c5846e62b55df97b1986097bdd36915", + "0x3b71bd9c9e479bcca3275a12005b629d87e20bffacbbf3ca2ae9804b4dbc4d40", + "0x59a19969de9f0ade2384bec9c774455e46aa774b9755063526b98cf8d07f81eb", + "0x233f00a42c674a36053a43552c93d9beef3090cf3ec74190d0ee34332a72cd4c", + "0xa1ccca2c71c314edf251df629b8d5c8d1c861d3c29a67743d2a4fd7cf5b47ac4", + "0x13e0ae852a0c22f85555ba69b747b303edc1fe981390caa2ac4b082cb8c959db", + "0x6df3580af6310948e36482fdf09005eaae7016c21ee46bd2ade3b54d027d2bfa", + "0xc6f5f07b379e5497bc44b270551061d6f06f660319f3f3b44580dd998179a50f", + "0x9669432bf5caf88ded238912a168f70438669df23bbe81cc80ca754c37c9dd8d", + "0xcec75f43d7b594af760370ede82a47ddc673c06390a5f781c36ac1c6e221b01d", + "0x8094dc8bd32c97ae7353b90f910b32fae2d4eb0d189e6864a93c92ea10db2ec9", + "0xd9670bb50d0ee3bc1e2c20dddb6dadf6664edfcd72fd682fb8def859c5bc27a4", + "0x7a44ccce092d0a9a0d0724fa4075ad354e1375ab159f902cbbb91c230855c8a0", + "0x5ea5914237acb8cf171b872b5cdf3ebb3c8a663394e8317816c429c1bd5e7099", + "0xdbcdc35576c3b3da8e8269e45337f6366bf67423d695c1a15317f922f23ac4f4", + "0xbccfbd34105a3b9977573851d1210ae3331c2b36ac8fe210d3a30fd398dec1f0", + "0x12a4844dcac185e4c45a7672bc2e6f1ee36e1c1016e8d317e5798810826c3f3f", + "0x2e5dc9ac2863a598262a9775043c59cd80b887d8ed8a0bc8ec68d96aefb91edc", + "0x532fdf6e4641ef267dd67f80637b464313e901c9568772f42415710204b3a856", + "0xf4c86654ade4f2cc6b456b48db328ee487329b3d8f3a9d5f2176b11bf7dd725f", + "0x9adba701c6a53b7819619ca2ea25ff0c37d91aeefeee0f095a5451390e5df4ed", + "0x2bd3b170ed326506a94174425aa570a8321918ba1e03aa53abea8763ff589dc5", + "0x7cdf7fb3a35b61be41c7290a90f805260f2a2d81dce6002e3fe8b708e72e1dc0", + "0x8d5ccdf783d5aa162dc26d8d1465d4763322730a59ec7f282be71b02aedf9f3c", + "0xe5f4a15065e3a6783115a27a14ecc91caa828f43ac7ab8adad52e09c4aec2f85", + "0xbba853f33a286a5587c066d560fa553bfd85ed332d75df02c8e4d349a589098f", + "0x9d7237315d191d8507d7750e2c8e6dba0bb3319dbe4c2f0622c95036e634b445", + "0xe5668e0d8935b4967c5408c8838dd765c55fedce083f4dbfee272c8c97106464", + "0x1ebe355446bb38959b25a51d12fba915b627d7f25ff6aa6b8295ce492b508697", + "0x166a938c186cd8048257f92ee9192c6a9c3e896e312aa9df0f2898c43a7e1bec", + "0xd3147112bbd14cdc9d7934790007e139dfe4fdb8ce44f4faf372087e3ba395f8", + "0xfe8e7f405b68bd3966154259a2bd2407f11473cdf9670ebbb4a4bb09034df174", + "0xa173ab56a6332f0814b4701352ec22e8a6d0bcce0d13e3b74e45ce1b45549141", + "0xc3984af5b47a865285ba2c1b4df82fc7a6e6b09382b8dd465b2dc657a1a8b04e", + "0xf1955c0b7051d2ac8b86f6e3b4602fc27bcb130ae343055d0a3de78ee913db95", + "0xccaf3436cab712925b8f350e985120e05a9619e398ef43414fa1d26390d7fe20", + "0x88210a78fc14dcc3d65c919786d107288de40bb79b2d2bb09ab2c5e1c139df29", + "0x8d0ffe25ed0a585feebb79f8778fce6382905616563bbb8629cbf7ec5b29d51b", + "0x55b93d2c7796ce61ee6978ce960617a21c29cda241f53753f6255d89e2ededf1", + "0x4a4fe1acbbaa8b14445333c332c4a6832fd73397a728d0b3022dbb4f1912ad81", + "0x6d48c2f6953fc4841dad7ab81b84b96e9553b35d6957ea1db4f8bba31866bbf2", + "0xb25054684d400eadd8b4ca49834d2233ac3c251e9acddc2911ce5e52c6a4a173", + "0x7c9f6a8932a5bf13b28aef6dfcdbfd33171bd185ddf9b64fb16f5594e670d174", + "0xe40383714634f09796367d01d224444648136e5862926e146e82edc8d57309fb", + "0xf8837e14126d53efbffea441ff94c9d4afb0d6c99426ad1e3fe2458f3b61aa4d", + "0xf18a670a52f0b91abf5d4611d6f26bcb528b56311684223fc8c82f24bb042f82", + "0x84ce97283455aede3cb3e21afacae90e0ece81d9af4b73546f0a4a62f53424f4", + "0xa39cae777908072343ce11f6593ad8458910e44a9de981aef0214e192e510366", + "0xbe6aa0e55cf02b0a159423f1ade1aeb8fdf6867b3dc62455941c534302459a1c", + "0xee33943ccd188913db3733e9d2b1c055c2e66baa780d1e68bc8589cc5a8814b1", + "0x66d5264aa8273ce5b3998ea110d54589e615e065502d2594bd90eb95789abf06", + "0x60e02cc54a0404257a86b6ca33a09f272ee96fbd5cb4c012ec813739e31bca4b", + "0x57a26b6635b9485137582875afb296f2fb864fe3f8a5bd2cbe32562f8f3dd385", + "0x417255e3ac1cf38c014f3b37288fffc268d554d54b13c46e9c2b42d2ebf596cb", + "0x0859b744191e9948a2a9e2947ee60e427260257a2a56e2c7c03841ac1c4468e7", + "0x0b81dc3821176c5a0e7e960786056e9918a3650f0584930cb0b89059affdd0b3", + "0xdf7faed41fadfa0577bc756e7af556fcf033df04cf8459d6d1b231104b77a733", + "0xbb5b876859de7ee7dd882f085e349760c7996ba7d0a829e18fee5a255a061eaa", + "0xb6db25cc62e95b36fc219afd865de67e7c40357ced0feaf7eb151a6a82b6e616", + "0x07fe6f23359b19dd27475dc2b82695259e8d505c1f4167c490f00a23a9971217", + "0x67940189346c4f47499057313bc849a4f69d28bc4f77370f113ea8912bee1191", + "0x077d45f39bcc2196ea3598041f4a31e5c8d00ed661f20520722ad78ede7c887f", + "0x5f5da7f14328252ab942c3a7ecd11d6b119a6c259d992f986b1a326ecd89acb9", + "0x58c77aa09d0fbb009eaf6f658fa3f60d0d9664fe01b8d32aeb5661d6403449f6", + "0xe077b8264813c304e996e5b127a03eb752f8b9867c25528657c574df8e7c759d", + "0x68ff6d81a23952e262fd32e80cda289bfb36b434203d1b85d84b908d4353f2e3", + "0x2abf132b2121dd67a86b8a32a9145c586c41052f44424f6ef3f9568aef12b2bc", + "0xfae3cafe107c23300f72adde498ca50ffbb287223f55c3564c3d0c1bce29e8dc", + "0xe0bf584e38f84a2d4d2638fd5470dc0034268581ac088847649d0ea57542b1eb", + "0x66f9050a45a1d17d78533f5376d97acd1ef3e766baa0389f24d21dab99b4bb0f", + "0xacb8fe23999be8fc75202adcb0270af88fd7da59c01d7020dd3d57cc32f7d2d8", + "0x58112f5cbf474bd9460f04a119fa6ea6f047f4f44ca73b104acd2a1fe83720a2", + "0x48022409864ccc25ee8c1eba9c28a7e4cc565190553c184ffa4f7b038377e640", + "0xf1788ab3076a2fcbe377f95d770e2058d2744b3e076852f23226c1a22de57f7a", + "0x20533ed0965efa1ed3ad4a2a702a59d007d0fd3e94f5ed2eb3b96cfd1071d97d", + "0x90c67ca3cb37f22540771d87becfe1b4448d3ca3bb8bb6351392abd73cc573e5", + "0x809ee1a4362f02abf1b679cb61b29ff396f3856eafd79149e493fb4fede3059b", + "0xf853ec630dea66b272ec942b3772b63985b08813934c0e21d9bae24d8b6db367", + "0x3fcc0cfb7cafffa7ae9289351d42e4e965e9636a924fac77796342349e0baf10", + "0x228cc6502f3a1c19f532f0dd670468d7b5289c2758ac425841c0f6d1e94a34f9", + "0xd2654a3e2552fd87358d0b8ba95e16dbaa65c7bb79fae48f450c4f5fb27c4032", + "0x62d1021e9a6a5cbaf30adc9a450ab6bab9755d4421136684ade76aaa354debcf", + "0xa1a3cee13da4846536f4f69b95fb97040efea89016e7f3cecb2f497a5d61e128", + "0x4adf6d282db490aa075ba077e2dd106e5150dd4c1198ef120226c56824162bce", + "0x94d2703e917bef3103a2d8f0b8bf71e4ace0d5656f60837378d791a90ae5734d", + "0x687bde51927227ad0885ee15ed6dbe7c122588d8ec6905c6b55c3dce8457ec47", + "0x0b8d6a8be92322a923b8530a68d1e650b4576fbafc357e894b34fceaa03a363d", + "0x96dccf22931af2c16a578cdbddc92372e41411eb799e7ab61a65dd69fdc905d8", + "0xbf409de805f7d48ae5cdcf4de165ad84716860dece1b8e91142a184deb7dcb7c", + "0xf394a819276f59a5f4a5fb87229126778f154b90cfab2d3e214a07dbbab0d11f", + "0x14d5300f5ca4a29ce0e5d16acd43a20d9d0890e8466d89a65853f0efdeb27ec1", + "0xf71203cbd2b286342e8c9996595dd5476a84128b803264f43a71c8c48ecdb706", + "0x1ae4464000ca84f8e981c4e0c3a0fe3eddc830ce88ca8ac050c5d44d1259bb74", + "0xf34ad422474f5703f65615423e0a3e889b6627fe9dc282eaa627ecb0405d14f3", + "0x8491f05a58ea8d00c38fe6ac434919db6e9433d7d02e535701bae12d13ef176e", + "0xbf4fa62763d21ef32824c980973742b8057566054abbf3e1a772e25b1cdc73fc", + "0xe8b2f9d73154fb69a3bea562d6ba7162d824582c23b21433c9ca504790e1ed9c", + "0xcac888e4af9578c4480d787204af3502946992e27e2f892be8c3332c2f49fb7d", + "0xb5ed774a21d3f1691c81f7f4bb201166e969d26e094c8538655808209ab70130", + "0xec88c9bbd6a8aec177d36de60770ebc121276f189090ad524dc522009bd89a07", + "0x083ad1d943ef811a011c2bd28454002cb29b06239918e4f43edd33e68a33ee43", + "0x58d67cddcf94940b080f252517aa07670a3662e3504410d9e76e50458ac77d98", + "0x0a6c61586afe583bb97299f6ac28681de60dbeb1c2432cd5ebf9bb0e5a39ad09", + "0x0d1e2ce99424a09e28155c2aa77ac4147f0391d53d52034f8ba8c4cee7132dbb", + "0xab5f1154eacf26fc973e335d99a2109fd2e877c95ec54969e45991e8818e6ec5", + "0x5e408f7245667770b2a25c81014651070afefce64790460193d6445b536a2db0", + "0xaca10e3b3e0c99b4ca8aba538a2f6919341a93f8a24ebbd8f6fe685bb5a7ab95", + "0x81f6744b553128542acaa7a498a33a6f90fb0c6df6cb8fe29de0cb46aacd34af", + "0xefa48a9233bb32174509a666e735dc965d13aebcb24ff69e352ff03cf1d73e9a", + "0xce46f7f863c56f707bea41b0bb86dacd36754ef2b86c32d7ad0dbc40b08d2942", + "0x50c08c3572f9de38793be735e30cd7cf96af4e520b8473f72a93715ed93388d0", + "0x224d030da1f6b7657ea86b22792193d8f0e122cf429102e6c47df993f44e5d59", + "0x0dc8e3373112b803564996657cb20664455e29637e8d45de1c3c57e853b6f87f", + "0x9839b40636ef870c357e1a33faa6dec71e391187b15f170b6d7cfdd0fdd9f4c4", + "0x4c5612fbf36ffa2b0067b2588bfd12917de598bb7af9711db64a81315667d0eb", + "0x85c244b09d9ee4e7725dde1f9039d5c2aaf31598dbeea7f010e28d2dc9081b89", + "0xd26fa5fe42f80d525e4816379ca34af4a89f7296e5714759ac9af4fad8ba1acf", + "0xc3acb7581558677e73e6f97a3590b2529c583f409bbb1d201642ece3a4be5572", + "0xb4d4918760bcf2d0a28b2b8193814c4320d37cf8d35aff4e6a898d4ce3889a39", + "0xf7bb6cf892e638b1f2c304e83ca78ba647b93b63109d341cf4baf63fad3d02c5", + "0xa7b7c639cf65879920cc1aa3f98f45feb702ecb119d6ab298853d0e20377f1ec", + "0x7b2e005aa74d2876cc4f0646c06bc59a48878774d08764c110abc8a50a92d426", + "0x76ada65e0386d48d3c867df6d439fb9bb1a608146707d8052f4b080c6b8e689e", + "0xa6facbee4a65f08a29bdbab400fafb2deeed1db89d7faebf2fbec3f7cb425350", + "0x1b2ee7aa4ee7e04ede141512920961d66c0fbb0c348f6f1cbc3ad13df48dd1ba", + "0xcde762c0e376a5b6dbe271d4e2a66892c5f72b7c75039344399bd755ce68eca7", + "0xcc4b920ad5933b3d0d327cabc3eb3fd34969631da9d2f06c331c2756c39a276f", + "0x2802d6e759d9529748604aef9b8df1e77cc7d18f691bcefb158e4bd7186664bb", + "0xc5726ca8d34ac73ddb8879245fa250490e351a1f4582bf425e3f7293d583fe7e", + "0xa906dedc4bbe7866f3c462828cc53f1b93104eb728add1f2966d293a2bcb74d1", + "0x72318ff68e055276964e8d3b5e168e6fe1c74312b599056ec8da430a85cdddb3", + "0x62ad64d2fce9f80e3019f351be06e7351d9785b1d637b73069a612b78939b350", + "0x89f34063b37ebed1c26903bb1ecd9d69fe6998917e8293f91ab30e8f0b07ea39", + "0x24cb61d008014a5f51a9b116eb6932fccd058be0a5fb01f90dea1e1a0017ab34", + "0xe21e0d06f20dd7445be5ea6793d4d8beb74417480a576fdf42a86a25691fa80d", + "0x7189ac6c03e856d28163ff6d6c24ab1cdeeae0148e00c069a8d42bab48c97240", + "0xc5d1d37281bafb991bff970fa977476b25b0ab6530474c3d808defeba7f5d77c", + "0x66a953220388e4d910057d0e5b5c6e20a8f94ccc6f3b806788f7cc4e94a7e8b9", + "0x7da1f7b2682d44775fa3d576a05c42c8d475060d87fca19861c75a10c0378d6b", + "0xbb08cd7e8b0fb9302488b9d54e034094fe6857ae62ccfe9c25634768ab23ff3e", + "0xd26d8273e3e4065a40c867ebb3a011106d550ee05b08f65bf1045b47373d1464", + "0xc30068b4ff924dfa708fce01b610aa6a0b8f359f11e616e6850078a602c22027", + "0x583db9f816b88ba25c59f549033fbc003b774650d63e44fbae2c4707386cfc79", + "0x839b943b0137db53e6b7df9286def81e772863ed5d6c90c3963be9ddff133af4", + "0x6f0eac2fa9e03c0d192e8cac846e798f2f8a78784f7ee7bae52b57385970b840", + "0x9d72d31f4932ab3d5f8e440b4dda759ec5b1b25a1e8ffdc790b14e41ca35138f", + "0x695403d015ba69aff26afa5ecdcce55503fc00518a38c088100c4f480bf94514", + "0xd63b618eff90747ece1d0b47a522dfb6ab5a6176bbfbecf17426bcb38e8d9a18", + "0x756e0663c6545869f8786e193245915004d4710b4a93150c42684aab2be0f6c3", + "0x7715a46068d56b44b396802eeea1c68db81bbba1a6dbaa8427e61170a24c9e4d", + "0x495d573a167483b1e146600e617b6ee02d4251ffa22a0d6d1b353ea87bc5b9db", + "0x0bb82dca3d49b7ebe0eb6d90db38557864530257ad2c61317f51141c5a33f9a0", + "0x16070c602b167e7f1502c6cffb4522b1d7d0c9b3b5593c55e2e692a57e450f92", + "0x29e503b966b5539890bc4ebbcac1056ae47b8a84183d9fc2c17f1c05fcdb94e2", + "0x9756fbe6322304f743ff0763922e7d13773432bb2be64764001415cfedd1425c", + "0x1fdcabc4b94b12cd9b9a028b11a2f790929793013c7e361c0f839489ad254302", + "0x1bbe892f8b1916f711c6d70c31b1c3e27d8d0546ab85bb0787d58bc43f699ed0", + "0x985800fe0abdf6158f409457c37cced1b4e246806ea0d72f0774e72369be1160", + "0x8d39f8467d28dff357687b9641bda4f514a3d1ad1922df02f95bcc52f181ca54", + "0x3502e2271a684b787e243a20e5ac22a3c7b95649da2931bc8a5063b78879f7f7", + "0x9beb20f3dafa156af31d81fb8e4f3f3960bde7c1fd01919c2d98f24ad320b52f", + "0xc5cfa5d6164e5d1e5cb3b3cceb85389151dc80f8c6c4e9445836b24e473af0cc", + "0xa57546eda7100eceb3072fd7798141312d6221a2978668a97a247b6e581504f6", + "0x2ba9586d46bed38441e94dc851b06f5109c938591baea3e47de4ff7e60f1a805", + "0xe930cbedebbbc3fb4b3b3f5a8de16ed885e27d8be53e6864919e76b22df5af95", + "0x0fc51538512a095006e99d471d78acdea3dae62e5185ae2fe1c3a3eb9130f553", + "0x5247ccaaf7367a412c4db0b2e4598fdb151f587197f1433de36c3830c19a8e91", + "0xf77d63e05187b1ce2394619615563f913537e8c02afa23975cb456dcc9dbcd0f", + "0x094d6c3af5ec6794be7e5880915e372514288c31de8916a280d61ff07c7f3d87", + "0x17525c4bf2f2732405cd052f1e91718bd588850ca4f54fc3fd43a5b7383f592b", + "0xb3eb902d635c737cac76517ef391bdff88252e110515a5c56e84e940a721b5e8", + "0x7673b9a000041a364140ab516ca039dde607b80f174f67ff85c18ade40228630", + "0xd39da82e96a846c01821984bc127f8ffd0ebc330950641ca06703669b8d57451", + "0x7fe8cf7691ee10c90c4609d865396314ce29df52f2d67b27f156df3c00902ba2", + "0xf7188c00b7bea23b2e2ce2f8e9465d97b01d94f6574e152550d9d6b55d1c4bbe", + "0x8638a0100fa0bf7609a26adf0cc57bdb5d4f23cfe0b85cb1746c6431a223477e", + "0xff24ccf6c383aa136c8a965c9c662f792580f9e0fe26d26ea7e2be8d62c55923", + "0x70d0adbb8bd87ca574bd96bf57937dee0951bdba3a6a91262acae09e44630dc9", + "0x71aed45ccb9c939883712fd1850f4658a972fe43e35152e49ac7e89bd40301d2", + "0xd322b449315b680ed08f55e782c28150117686891d3c6088f8e3069fbcac755a", + "0x811f0cbe7bfd3dc6bdedcd3d19732688cad12e967b69f401a1110190a6e1377f", + "0xb3c4d5bccc43cbaaf2af2c2a66ed5d8472f58f614f40dad00e34387255165a7b", + "0xca7af87583497566eb41ebc76e410518dd95ddea235c07007df34cabd3d99ab3", + "0x6976a62c198a4ce188a4fc631b19f9cd48de5fb5c0de398ee471102a210ca27a", + "0x23cf18c236f314e35d27da75fb563572789abcd00432a8f1bbda64c6e6e59d8f", + "0xd644911deb0f6615c188b6af13f2da207797b29ffb72d4b0a4eda0bafeca678c", + "0x7ebe35f2cbaf19022d0b7903e74ca4de74242ec19e98d7841d632d8e59dde8f0", + "0xa27e78937218bb040ec0f7caabe51f8d7906b29468bf45add6feae42e8b07299", + "0x30f1bb66164f01e20415030a50ab97162d814a956e11b6da734890a0fe883760", + "0x054f872176a9d562bcbaee63147bbc5b280021e0a4879986f59b83ae4df40346", + "0xaf61e403e31690263f2eed567c7116b7e111c5cbc4bb5d60eb3a787de33e8895", + "0xf1a38f843476227855f3deba6c3f5d26fd0fb707b4e24a96e2f07fdb47cc0c0d", + "0x92087b30f1e6d6eab77a866b1c46f9a3302ed892fb453722ccd506ea9b226e34", + "0x1cb905e3c69008fee40c3a515bb2b7a59a6abe54ee25245218ee681e5a658818", + "0xbc97e9699e64eeb4a4a9866392cf58b15a2bb457de2677bc35d1e0ce65e94551", + "0x8be22cbc0f9c98f99c0a3bed1c04eb89b979c2c6200a51808091e493407d438b", + "0xf5a81ff4b16cb3b8eb6c506881ec324fb0c7ff0777437d959a7e59b1194e5d23", + "0xe1c41c6504ba65485716a9954f181e2cce5129a5ee966dd4fba6f93d3426ac2d", + "0x752b5b4f050e8fc7c4c320713d618bd61977c5df50ba9553c8254f2d867e7313", + "0x628e2a835f6fa408bc8afdc4c8485e31f7f2e3a415cf7d35e5fab43128300c55", + "0x4ac9c76d532893fe7919a7f0e87df2bc9aaaeda6981ad9b28612e46e6dbbe25b", + "0x513462367803ce86643ba7a972e2a1e437fc0d0bafc9403cb1fe388dee013679", + "0xaa6b9cfe94b7acc00e03fa0b456e8c8254211d518f1262b5709382903026487a", + "0x955f02f133bbc61a008b7c792f3195dad5d2c625e1c8d4cda1152b9688dfbda5", + "0xfdfad2a2a882724e049605f0206a47ea7bf44c498c3c5e1a63bc785496b33212", + "0x9674158bc48d35cd4125878933e6a3b5823dd9eb43eb377afd4f93ecb9480063", + "0x90ae20496a2ce74abf0ad2da39cd91d3585ecba7341b34d4a0f6e8158b90bfe8", + "0x5b887310cafc5e331653ceece1e65f78e548354a7d3118b20206d398a3623fd4", + "0x4ff519ea4d5d9a148c82ae90f2d372cfc8cdde48b2648f043d988f2b510a4103", + "0xa54982d4e1a513b39ce62fb1c6a9e3a825a4b96bfb4898808dcb98f9e1988d75", + "0x275e3e6fdba2e002f16e7f7dec933c8f17d1a778ed4c5c38beee03f1193fe442", + "0xa83116181ab004af15bd8ede11c115fb597faeefcc35826becfa57cf5d54e236", + "0xddb6c64c865bcf0aad929f474970a5800dd0369e6de50dee2226b214eae8c52b", + "0x3e3dc4189ae64d4a95a9983c0cb82b315093ea9dd30c562563244f933da0847d", + "0x20ca5e9ae40d1cdafccd7aa96c11edb440ba044f6a2ccea57275f5c3437ce3ee", + "0x1999d24dc88249f02b41e4d730c9bece3267af843e3ce976396f7023951fe7b5", + "0x2b3e6b9484cc45816bb6e66d1f02a10d439502cc14ff393e23298d12a4db16a7", + "0x3e9ed28ec5847afc75a7a451ef4f7af6220f7e4440e369dc95cb046052eea95b", + "0xeba280195fe23954cf38eac9302db972771516d78c8e007236a5d10a50bc3cb4", + "0xc0a8afe0e65314c7d0ae45cbc554a09b46e9b38950df0826234a90010d01099a", + "0x583ce305fe3b025a311849c87d2fd46298f43afca87ba1bbad9f0d27210790c7", + "0x928df61f68f1c16de55447570ec7e102f20aa6580abaa2aedf9fe80e44678d25", + "0x6ad94f79ad779570caea91c6809364574b52e8a57855771505a05342b96e2ffd", + "0x5be523e72ef4721b0ad80e113a68c51c9ad39ba316805e6673aea6e3825e0ebb", + "0xdc1435b62dc73e79f36b66c5dfad319374571537f153a7c7bf2fcf081bb9ad2e", + "0x6edf08306c1d14702f0ebdd09262840bec574192eda513bc41deee162e302539", + "0x8f5837f2ad8b6f7333d3ab730a5ad565129a747b9b0eff98ab5e68ec49e96b2b", + "0x9064d478cabae5395dcf24531ecfc8a1fc35006f1833451cdb7f217f2d723afb", + "0x80c477a74be695f84ea5cca4ad5a935458ab9d1a54ff90efcb5d55ab9fd8c791", + "0x45e928f375e30cbad4440d738a02cfdc295c5489be07abf07d0e5391c6d53491", + "0x4d9b41371bb6f3bf2d6d5d3a46437826b44cc8ca50ae0840c749a3e6e6a50502", + "0xe8bf9c11b1d2b4dfca70deae5a0a6977e4cc764febb88f830585e953006b4303", + "0x7983f445b39a66062116e992cf8935753635ffb73d1104d130d27e92e1b309a1", + "0x1202ff855f9b70d3b031349002efc4d1920860a721f07e44be337e9e4b8af327", + "0x17f0566ba5f741fa0a23ea7e9bb7db2fd5db2ab78a37825fab8a1ef31caaa112", + "0xa8b18cc87c7f4d950623f99ab1cf463c18226766bd6e7fae7a7d31863f08fe04", + "0xae68af64e4305e88e46c330d57d337dd0b887a5af88b8145a2537908be75e6e3", + "0x439e0ea137ce29ead44aca5c937b5c8e57a895af4892223d70820710eed063b9", + "0x3ec1b9024d9e106a1b91c6a8614bd64a53d69b080b4b2683596f83edc07dc2ad", + "0xf29bfc05a63689a0d7aa2c662442aee6e17dc9308912b788faf3ceba4ed00f52", + "0xee2c541c040f53e71f0fe074b785b71945e76b297fa2101aacef3ddb8c97ad1b", + "0xf03bb330bdbda5aca914385ea98ca770277fdaad9d0cc34cefc3f533d7aa0b89", + "0x30c4849b4310f0ba07edd0b2c3f00b250347044635888521076c0bae6ad8e192", + "0x503000ef966e2bcd1af2db5cc4ba232a1c07adc21b6a529b463f3a57ec7d9be5", + "0x3397d19d88ea960918a79b6052009a3a17a5a337023b3351c8df7d9940568524", + "0xfa1e9ebd9b1ba68706307e369d541874fed8519f3a3c86915c69cc59382f4df7", + "0x2fb359960dcd14dc8c1d274cfd0bd16f0082e314d7e857f1005000200c0e607f", + "0x9ae6c73045c2f4f1349ff75cb02ce3f0ca8ca5a15a9315329869329bdbaf53bd", + "0x0a725ad1294fa66189eac033e2bad210043bb0ca75f1920a4c5e037a2af4896b", + "0xfda1a87ffc19d7d2bdde6d4f605ed611af6a3ee932c5ea3460e6f252db95fb52", + "0xf4ea036b0a18f0882481a6b8c098b3fa82708786c20abf1f996e48ac30213912", + "0x48efe1fdc5cad08aa4fa80ed5d9bc9fce5cef1bebeeb33bff4efc294f1cf85ee", + "0x578e2d666f1ed2d28bb03753b9448d733dcaff4e844f975098e917076196bc99", + "0x6853f937f8118ae6cbf792d953465c7833400827af7f6e51effe43a6296e3e72", + "0x4539ab758b39945d33272f1b93a1a55cd411fe2f46bbbd1c1a4a36ed36d3a19a", + "0x54414abb3faaaad4a39a20a6b0d12b8b3ff0f55f1bead836492f5cc6f43db911", + "0x47141c077e1cde4cc4447da303a793c74f71aa589b036fbdbdad2f653b391a66", + "0x557264a0d2fbf0627cec7fef01f1759df99cde3668be638113be32c228f37f3e", + "0x3b3789a39a1f9d2140798f27f610d8861a5d7c6be1a04481a06e58b17dbd2fad", + "0x24ffb2fb440c75988d31a0fb7c32b10e373c62454ff1c7eb07f479c733f261e5", + "0x85a67cd24a5b7a10e8efa6d204c78503b94cfbeb26c53161993ba70825c5eb1b", + "0xf097fa797f51848743a29e0d43a50f5d70decc77756531fa7d2f139e6338aa0b", + "0x2130274df2a95c987a32b305f8fda842aaefb24f0d9310fe611ae52d85ba7602", + "0x201c7dd1ee4521630e9e123fc5da3c51069c79dbc185bba1d235e418ea20ad4e", + "0x3d8bf2f10e0f102eee64e475af0f95f28985574b36b33dbfd99f74ecd6e46de1", + "0x37ea6f30a6514f6a8296f6fa5df99e26110d4d7cb64e5467a1021901831f945d", + "0xf70ed7e263cc1a8b8ccd434a73a45ca1d2343e5a09cdeb0ed10ce10a34c8fdf1", + "0x5d549bbef1a42d8bb60085c6373bc7d783ed5ec22f8c7dcbb03a89435c32e2a0", + "0xfd2a7f14f01b4507896ea2e72efc561511f8967b6dde507886918d5afd67532f", + "0x2344d08c70d5c53fdd708a73f64d351236bdf4bc10e53b562edd6bff3f1b6a04", + "0x5f28d7886d96f15b52ef1ed006ad947b84accd8bcac2ef6c32ba413f009e6631", + "0x2255dc3dca94722c129f6889501ea4dfa1c19dbd612ea406421efac4555e7050", + "0x68cc831fbfd2524c0270f16da49a976c355e5eba10e3241e55535078109da351", + "0x59578c32cb583ea049b0c60dce9e645e94ed22d30dfd74116c27835c74cc6bed", + "0xd258a8b1362f9641553854d3f51c052d14d943fb16b83047874631b84259d5f4", + "0x5cecc09b7737277885f1b9961b5378dd1c076f6399e004638ca3374472b8ae19", + "0x4f74269eecea7a58a8ec2c5524fde33a52d3b8592ef023b9252091c0bdfdcf8e", + "0x9f904a7128518d1c0d4826932ac4cbf51d6ef70ffbb31eb94a6e721941f096ab", + "0x2a5eaeedc85401dc05687420839b62b4fa3ab8100ee87976a72f1830365f179c", + "0xf9b135dce4b201209d4233af18a436145105ff4c7b7f99df121aedbed8cc0da3", + "0x71e08e46114da134d9ad648117083ec1c494d8edf539ec821167c578f1297bce", + "0x688bd64b1a1429f49b3576c71fd5b813a9963ee1a5b756abab3c604cc1cdf645", + "0xb887209cf18232cd26fce8c4818c5d5d085dfb195668eb54ac40613f68847fc9", + "0x189318956d2b36f18272c8570a2e2f4de0ffc07f0cbb0c57cd95e6a427f0195e", + "0xa9363a512e7192cd9b5456482d687cc18b9ae12783000b3cde44d68f00f779d9", + "0xbaebb37746a2859526d2a609811bbb1d75099be4d61f2abecb09a9acd7b3c0ef", + "0x2b5aa9a26df8007edb4b26d75f88b25615c01f06afcf1751a8511f03205ba742", + "0x73349b2631a81e7e9eb5530502eda043e9bc56a4272be016b9d5661f91a79504", + "0x22c226439a1c7a3aa9d90d41cf898c9d765740a3e6f75ca116b6c1aed9c68b3d", + "0xa326aa644f30b38505f02fe13b2d918cd4c188004d0f280d482861ae9c9fe62c", + "0xaec66f092a01fbbcfca0082d677062340c122cec346a3afaf3839045971c415f", + "0x35d59e4bcf622a76809b2583ec20aa2bc10886edc989512f979eac15dcad6c6f", + "0x93266148093c25d81d53ed091e9a7468f9b74b999ef276c733d915c1377efc2b", + "0x0e7cb127eb026e4b44838590e4a1870151afbda01ed1af047da53106a8b04a05", + "0x55e95632b8bb88688d71071969e5caeb12cd0ead0f67c3bb1f75c421da4be344", + "0xaa0683b6a4e10fdac28956259868deb5915ca162b58d0e4377cf5f5a0e2f93f8", + "0x67dc47df8f6187049d80eba053bcf1d45ea34fabfba1f7407bf9ae43ac955c11", + "0x9580cf16d6214f5d68594432511b06ec9d0ff7fa0bcaffc9946cf7ffc546263a", + "0xdbb4129a3e593b1e37c71327b8acfc84c4d9c61359557417604b2f9a80327c53", + "0x22ef12d66cf2660fc912cf205d3561cb7b4414886061b4e5e1acaf4650eb74d1", + "0xab4aa5cd0e2dbc7b9a7d9c8c1eab2a7bfcb21f8f2c678a570d00736c8ac62e5c", + "0x9fbc3d8860e660776f794c260efebaa08c2ac8974cc794a8d3953293b3e3a390", + "0x2ddb4ba299d6f3d11739d4ff08de1a0050af0da791e385ecbcf491917c055334", + "0xa178a9931c00021c75869b9503329fb9241c3630c52a8542d6cfe82f8ec03266", + "0x196ea5c5d34e132eda367864f39532d764e55f6fc383e23faef8efea807888d2", + "0x9a78d02d3919f7df2ffe73fc9155b3f2f747506e55376ced38414df0938ebacd", + "0x010806ee3ae253894fd362cf27710d07b5505ff877e8c8b274a4a3952ba3d9f7", + "0x94872cbcdee6ce4d54ab4489d11bc9e16795faa4024da5b398cfe898bda63311", + "0x77f68f228f2691134345af813206d27efecc1557753a04e40e008a0c2d8798a9", + "0x97298d6d5733c14d3b0f88e386623d03f10a6c3d2ba8985be1a2aeb2a75c4107", + "0xa47cbf5a8e3cdf6ebf91183fa7846e13fe7625ffa493a81cad00ffb765b488a0", + "0x41d473c7c817143e73647b031b3e1aee2208ddcd3a815e2d5fcf291fdb2ddb03", + "0x513ca3c49983af6a13706ae02687a7b15397b4832eda6f794983d0b0fe78bd28", + "0xaefefd2d59b3d5606031a2b064d8d9fee001540cb29852807fdc21e5dda97066", + "0xbb301d86a5a4aa2e36741e3f2a613cc5bb1be608dfa437c3612044e9510303b1", + "0x4cdba39041935fe08470ae2197f394b0923d8caaef7272fc28436f6081e44ec6", + "0x9bdab6557da457a8b1db8760586f71e1c1399247fd99d91cc1b9b6e0662b76cf", + "0xa0762954743d50d3e935e1734f523469c2c1ddfb4045e4b0ebc879000f977373", + "0xc50ae1e1fbf40af21503d9218ff9a39d172cb8c56ea69c7dc23dbe89b9ff37d3", + "0x76c1622f865cb08e94e7e7018ccbc42d35aeb6a53d0c79804bdb67d8e062dcf5", + "0x2d2d882b403caa55f52640adc3a6eb456269911626ade3d49c488dc9f8813b44", + "0xc8cf555f828365936630ed61a9763206746a62e27a37693702d1a4475d921227", + "0x718926c6250c56a946c1fd32bec1b4c68693fd4e048de72f52a309b914f63c42", + "0xab5231abb1263ae5511fa8b2d184698059ceb2a7a8017552e9c0ede986ffd8b0", + "0x8216ef510d657a533b70f956e3a84ccb1f1e2d5891337c6edbd1c0b189cf9982", + "0xcefcd43485c45ba18c85f37edf66a9d2c1e1ba8756d9262a672d3fed4a5d586b", + "0x6dc04e851daa30362b6973c0a2854c397cb3a5a636e67a9ebb0d8d0e730f99bc", + "0x5031bdc6fb4267e481032fd03b60a7ecf2752f9e576b26cc609785969312e1c8", + "0x87d8d4342c2586d8c1004d2f865be6773ab3cabedafef07351f21a74f765b57b", + "0x2345e6289ab6555b859ca29288576c7a65f0b7f6b9cd334c853d69babc9e854a", + "0xd5ef4fa4df310a6e441777f88e15b3de832d859f8eb1f0edea4a39c0258ce32c", + "0xbe25e288895543c0bf90523b04ef7861ef6794c3986c226e81aae480c493ac23", + "0x8c7f00d99c5f47670cbd95889fed40b32df59fa92c8417aa993b9ac379273d27", + "0x0124587a58897ac32ba973fa6b517138f24a3acb1e38f79ab90a816b20e1b38e", + "0x5df55f8a13cacfe5f8d757ab6b8f721a3b6593779487eb5ca11c52bf8805f7ab", + "0x34c79e1572da0509c5ec8cfcba07ff666413f2a929b93095ecad54f3a0864fd8", + "0xce543c5ff873c8d5b4acbc0acc2c9984605fea0cba9c758cee8e7aa647337e6b", + "0xfd532b65e4de1338059dc38dd2b91636369e9f424490bcb2cb36f8a28d342143", + "0xb1ab5b925e8896d5836de1a4f9cbd99a0318a9a85c921e91940763b789a885d4", + "0xe29832dfb5416b7c24e67e2f18e49dc5809798844eec2994a50514155f487357", + "0x0807c6a00edaae8679b97e97a998a6f1bc14ae1a4623a73049212a3915b60b34", + "0xfa8282c80db992becbcbe39d38890ae41fe9d6668b9860208d2ae34ac134f870", + "0x3cd9c105542f4b07de97efefecd4e646b461d732dadecaaca62ef194ca7e3e35", + "0xe058776090fbfe48392afb81b5f1f41c1e82db7ebb18268a52fcaf49186309c1", + "0x079b972cdff157e020e52e59930ad73d9c2ad969f3b0a8d1d41577ca2253f77d", + "0x9482ec3ebe9fb953d381a17d5e0ca16653deed828125ca866571648a7c1b9549", + "0xb8b5f8710d86e34b56c46dac0cf3590f39f98d3818ca0172cccbf870cfe8acdf", + "0xcfcc15c331926f1319858a284cbcda9e770e831cc7e3a9ed4620750c9cf7cef1", + "0x86d3b3052166bda9879d41466c1b62024570d1aa425ffc3f281932cdde595de1", + "0xd80ec1fef4f09d7a075448cff856ac32d68fc505916c23f34c6b83ce1311175c", + "0x0d0dd143a7a722dbce448a8ba6d7554958d31fdde2280369a64d59e1cf5aa314", + "0xed00e1fc92348f7729c817fb23a1b84624cd158beb3c089d79e51d11e9382b76", + "0x0efc3c01569ead63fd016b3b1c756bb71a87e429f272c17363601e2ad2ba5b36", + "0x0a75caf669139ae08e5d8fca448948453896967e2541876a83d695e832e6051a", + "0xb2736a2ea7aa41ed57a002a844cc342c12039cbb25ec7ffb7de8958b3e3a74fc", + "0x1867036374c560e97f41da619181343b0a7ae829a86fd318f22f506333e709e7", + "0xebc06a3ecd2ced0b079ebf4dc7675d348c6c683d65cab0370fc3b602d3ea4395", + "0xefa0f21d2d8cb3cb69c8d1045a1c30da48419570355939cf1a6e9af092905c13", + "0x37febb7fabcbabcf5a6b65512f1ab8debf02f115497e927f8b402d1ce9472cba", + "0x641d3a4f6b08d166c64c29b7dc22e63801e574767d8201aaf34c67b254fed745", + "0x07eb048c2cfedbcb3fa9b6d6bc7f757e91e9b8b79e3043c077bfaeeae487f080", + "0xd30682ed67452732a3eabef896e9ce816cf5501e0ed69083fc129e9d31fc76b6", + "0x68f2d281c7e142addae4f47c049da4995be5c091a24ad186e9c36b70f8bcd557", + "0x22c4314f47bbecb7227741deb3e4344a8a094f73d9c65e7b5022e80a3d0c4c1f", + "0x81988a185f4509f58f7125fa6d58434a6cc5d84bfdeba24176c3058749192d1e", + "0x9f91988c8469540df60471084e90454c61970aa3953a7fdb2404ab75ad4dcb8f", + "0x072745b7287bde6de34224d7570d0cf685e612217ce0d0c4964f795b82fa0f39", + "0x8272f0fbc10ccfc220c656ff690517ec2b3f42a7fce72d768f5de3645e849209", + "0x7b80b87178e2ee00cd2d2d8bbd444357502aa5accfe8c6c5eec00443ffa0648a", + "0xd6874ded8b2aa54687f10122fc298f055ef05dda03dbcf85cf58a66320761d46", + "0x510bf4bd2bb07891a321d152bb8d5048ecdf82d2d6acd622081cb598c4a6a5d3", + "0x4fc1a01f39df3d9aaf2ce10078c042aab67f46b219dcf1c36e4e09f4573d7628", + "0x8b871035d2816adadc5fa0516b53b417c3579628081a14ead4d703b940eaf597", + "0x1f361d235c348d9d4077c30e2b5cd27f957ea81af59b0bbc25342b44a41f6dd8", + "0xb7b873f75d98e4d4e7982ad7c8ef99b3350c6ef52891a3b1ddd1a63356d96ffe", + "0x110d87315da2e4a6fbb07fe42396c703b6210c8858afe80f8add517ba656ad10", + "0xfcbe4b39d6b762fedba100d6c70ca959e527a405d83b0498b7d3cce9b50c7110", + "0xb6bf89baf937ef3cf3a4e2f6d96f8dd2b7a4195880a968d28f65e71c94970dbc", + "0xea27e0d9a95d46b7160471dc9abd866f12e2d91ba6150dbfe549b244f6aa720c", + "0x9645c40ac088182e43c5fbdc5c3853e12aaea736d5a3f94e5a11d21a78d1d288", + "0xb5a15d98b2cd290a3105b8917eb2693bf570777507526c77e116f2e84ca36c51", + "0x476ab567d76f89e0e7361dc39545d2f892b0267e07a4dc4dfeb24ef446493e38", + "0x427f68e5eb21122c9944f50553f0a73ce7221930b1470681b46cba95899b3915", + "0x1c1fea68a0cd43c0fa6ec1376278d4727ddc82de33e8833d7b05d223d1a3dc4e", + "0x28976b28bc4eb641a43f33d45f83c1df9d9e0fcd706c34a2541c5b642449e0fd", + "0x0006563b5d39b40f927ecbf64ec1ad3e58b78b8f56252cb92964f984c51e5699", + "0xf8c65920b310804481d91cde01a2c480a811827ed81ed13622dee24545829102", + "0x51e8f5f2730a8a529535ce4969bc8bd8bffa2059841b075c5df2236de23b530f", + "0xcfa41ba442e084974e56d63e802fe96aa57c536f70ebb5b3b39985ca5d8e2b0b", + "0x0061901879421b13c687dc8e64b28b7705e99d0e7e298ae9dde84984bdf4cbe9", + "0x9143dd26eb566ab38199c059a5bc100ea91a5c4c1719d50fa7e4d25ecfcbd683", + "0x6599786a5d715df51e6b8efbc5f18885d756471db329c49ce6e4ada3c9ae3583", + "0x42d0e96e81f91fe7e04885210d7a54780fe1a0419f54218271b222816de91de7", + "0x078557c161640a6c568ed9c58c10f401cf2c4ff6dfe2e4806ce3f98b591e058e", + "0xb02669f634de8c935845db0063b2b3c51f8c7cbcf7ac5eb21af2a84bf8d15858", + "0xb50c5b7ad91a053cc4c050eb908c2d3fe3efe8a955ace8bbd01c4d50b45d55b5", + "0x3fb69c64fab132e1e9aae039a8491839ef09eac3b5ddcc1a982909265cee2785", + "0x38aa9e6447d61c3a5a148d604f41842e57ca3ee66dc6f365a1cc7325f69e7014", + "0x955866f5f7098813478b00c4c9afb5a17eac107e6724f9cf15cd709ad777ab61", + "0x13a1d2c66768950aaf6ae041321a31bc1fcdcc4174d1fbc19e0c04dd2d6aac43", + "0x2870943d80c6fceba20feb50c0bc0692841eaa0b2369cd6ab934780f60b84a30", + "0x53df5e8e56d2dcc01648d7676f63d7412416dde1a4f4abb8ee8457bb0852d65b", + "0x07376b354aaf8e4dd3552d31887a69a3d24b2fc19872ce3f3df777d80ec4b7fa", + "0x3a0ab25c5d4b60b86196db0b6dee5b7d73ecd13c469d802e659fc4a635782d6a", + "0x430f3eced0fce40e41a34fade6cb90c54d26e68862272aa4d622ef28656ac048", + "0xb4db714133f6a4a9bf25bd865b8a8f628a695d502dea2ddecf805a9704651ca7", + "0x8e5ee32cc4a49f9060a4fd889f70746b264c8ea87c95713aff55292dafb420fb", + "0xb6a227be907d8705a85d583c887ab39109917a73ff4318b4511258e55331bb80", + "0x847452a491831ad8f9066605537bb7ec79348a4af5e44eeba76a76197819fdea", + "0x549aa259155441a822db8f58e5c6297aebce8e85b653c0efb3be1374f480cb47", + "0xf8d34963b0291b168e073cade3a47fa8a21512dcc8abf950a0a0634259295ca0", + "0xf19f3c05186eddd9b6e368548cf87451396ee0d420c718336f51236300aa0af7", + "0xb896f0365e479d78d7dad90f68a8e9aa40dbf8c72355c363d630e84ab2067b86", + "0x0dda4fac66853ff4f893bc332b8d4123a6fa0eaef4093482859a93a73f8890a4", + "0x0ed8410577d9e7b145c08ad09c786a7b58b1e620d15777510883f789ec080e42", + "0x0330e9dbb51a3bf97f864eff9b3c3716a7c4700c7676ad5099e12338d39c8044", + "0x8369e5eca383fb86cf5bbb30c3369cbade7f2f11586be3665d64f507d921866b", + "0xfa5630ef6bbf09eb568ddcb257fadc31e5b0722efc866d9584c874979b2b615d", + "0x8604a59a0fe2ca077be2dbd87cbf69fb6e5a617829feea196aba48bce357d2cb", + "0x470a06d531bf849a0861d3d3415bdbe6ff6d937f0c52936eef709a381d6c2d7e", + "0x7aa5d3e5e9398fd1f21a68740aa27b8c6a4deac3eb2c7f011e7684c997c347d9", + "0x68fc7791abae4dfc1167115eb9a60c5b68da2fffbba22005172b6590ba9b1951", + "0x2f66c951098fae8d39cd4d6cd99c84a9d27c3b1cdcc5756f49d690f032567119", + "0x12a44eefda6ce99e4bb46e4051634544bad627bcd8744f4fc670f8f78f4ed972", + "0xd0bf93b32bbc07d3897256198b2ad8751ae6cc944ac4119e6432a5de9be74c76", + "0xa4b57a32ada21bff0348af1854dc0915d7e1e9e5287fa34886171523f787fa3a", + "0x9edb7bf6a423c8c6277e47311d13447e41adfef94c86fcb476ca2e460698edb4", + "0xf983eb4110361e33f7ce5f1b64be09cb7c37fb39635e7b2cdea6c8bf759ff731", + "0x4fccddb8c8da74a4f6f8a93977350c630495282e2cd2c17ac3da3e89c5028add", + "0x58aa9ba3950dd2657a920e7b872f07f90a69f233588f430de0f23b1fc23eeffa", + "0x004eb62927fc9ccb759337c474f104d1c22d54e45b72ffcf897ed3c2b177c0a3", + "0x24123c826952775a00c38ff71262ee7b7c592824d9d703341bd32fc2c9bf1f2c", + "0xaf32b8e97944d5b4098ce54071ad716e51cef925678b4e5ad9aca11d40c6df9a", + "0x2570341ee5dcd3e221b84083913b8c4468904bce4e606402aa7f36cd5485d5af", + "0x900b98150b0352a4d16ca93c4457b7b2f883cbd876e3f5d84f641aa010e16d1c", + "0x87cc7d34b9511e3b1384b8e6282a88e6cc871dee3519d822bc104128d0b78202", + "0xe4e13e69e40aeea7cea98c9d4986f07221ddcab435a0f5c2ac51390f11e1022e", + "0x4c35262d4b49ad6ab37cbdfdb5979615b3c3e3948c5428faab0b053be220b278", + "0x8acf39760e9b16088b633eb1fe4ca5f3875da8d8cc98dd12eec94fb3ce24b96d", + "0x2ea80f852c9be82fbf358da34f9895be9241ccfce1e88afc7e7cbc4922aefc60", + "0xc8408153052c696c5f44cbfbebf3b76ae02a847848b122fdcafb9bcf28f7eaa3", + "0x14bfaeb2a43bab4afe17daba2615e16240b5d07727d3e87ec6e03bc43bf2b66b", + "0x759b9c537aa9ce97794b48604256bd67d0018dc9df78e4399bf8d523daf7bde7", + "0x80afb19beff915650e8b240456c242521cb0e233839f372e229175cea648729e", + "0xb95920759edf6f117332a111c557530656d990fd8185227c977e23cb696f2bc9", + "0x56b341e91985de6362a731978b64588145747432b51dd5cb52d6d1fe6cd6ef7f", + "0x136327e9bfc7b8f3df76c36074a8f13d19d2cd4a74b9db9cce857cefe93df89e", + "0x8ef3f31af50ba3534ec1fc56d4a7a0a556efb9c591e5096ab008a5453dab6508", + "0x9f0981a1fa4168b5e427a63e0f18ac931d3fbd1e1b55a9d1232df2aea29605a2", + "0x7080fd8467508a892011515c672dabf37972ad72558b8fa27068292b29e156e2", + "0x9df3567b5a70dedc8ce013b47921dc17cbaa29f4dbe800ae2f4755e1bdf63ac1", + "0x3eb605a8ea1470f0bf84edb23fbd2b6c07d1ba84e8ef98e04247d93a3ae1a73b", + "0x70d533a46a303b1f664dfa4a7fadc76a1814a01e04da701fb8a1f0bf96e1a085", + "0x648c4f212a3c3b9a8d503269bc041e1047520f0eef6a959c6232468402ff56b0", + "0x00ed2033ac3cd30a0915b377cd70c10f54a81ffbdacdfe7eb5851e931cd5fe31", + "0x2a129be624f16f9bb33b79d4b3f0aa1583df8b76732100c66997697367f3525e", + "0x2704fef7878817b82d6dc4aa1881130158759f1cd8024755a613f81a0551ea3d", + "0x876183912c7a86ac972ba77b8e9eded386c4d6af93edaef4d97982f735af0e24", + "0x105df85ff73467fcc2ddc449bbe1df464109414404eff40739704c727a8dcc24", + "0x95a8569ea985b1d7c3d489142b0a8b04442f09ed3dfa0db43bdeb306e4f6d124", + "0x07b6f3c1521537c7b0a1e1344dc88ce64dc659c0e5854df5406ecc38380b1c20", + "0x8603f626bdb8793298d89d4ce4454d3e0e58a19e800140351b02efa7d9f5011b", + "0xa457c55e9716248a825ea9f8b63c847da97349aeb4ba81108c9bce3f53dbd288", + "0x0f9de9a7188dd56eb7f151d4ec43c4366a27df031eae5febbb4b7664c2d76b7b", + "0x620c40eb62b5abffa166e955383fc3e6ec29acf33be3c8221f8ef3b3724c0ed3", + "0x74a6a6b20caab913ab6129536a88a73fa7ebfafdff97099410acc33356a18ead", + "0x9bf2a484fc5806010b68214a78a1039d2f87f298fca5af2abe86739a53460b68", + "0xc72124dd615226c6f46f70087589856e04e9450eeaa97aa936174c57b1ef93b2", + "0x5756965831d7c71e09b2e38a6ed9257a1ac0d3f2257e74c6bfa4cfb80dd94240", + "0x7f9b2bc69f3433c2789941771dffd751cbc0328272d0e42e36a56250a9f661ee", + "0xa079edf9d48b9cc82f3c92b4d4dd40137ee96c962bfc8af30e7efef77e6fd07f", + "0xcb102825fbc4b6424fb4f49579d9b0e35201284ea97b5bafcf77e6abbd2efbc2", + "0x459068ca78d09a8bd9aee003fc06ed4dd555c7f8b9e6813a04e8721dd44a64b7", + "0xad76ef6d6c096d53467898cdf99152769d7fab12cbd74c1d6c0f45f9815c7e47", + "0xaeff5ea998bf42fcfb848030fe4ed4a81c30dd1c4e43840385c418ea43296f1c", + "0xe6e9a69da29a444691fc92c4901aedefb390d10730af1a63f8f1375f6b6c0c27", + "0x40383faad4bb532b4c3b2ec31548fbd3c2c1240fa9f2fd31984e8f4805a82bf7", + "0xa456d50ad36e0e6a46ce9b46bed6e62f76438a0b57130f76fd8b42754d5f8466", + "0x5f5f817a28a8e406359f30f314db445479eadb37d40d173f379519f3472e6084", + "0xf0a08ab4c07f3ae48dc4d871098ca3ee4c9affc1dbca664982b8234f460fbe77", + "0xae47f124c99399a99dbe9b7426c260b868633bba1e6cd2e34be8a9b7578e8025", + "0x338eee92983400c3d0a81a1a0fceb748357825d7371c48029ecfb3b600f7ed87", + "0xc8abc03ffa4dac476ec6b90ed2db2cf4444124e4c1f5263230668b00d834150a", + "0x1ec6eb94f5cc7f6c5b8bb2f869b8de6692489dc6beb10f9e7cb04e9cf26250d7", + "0xf61f156fc1d1f2eb7a6ed0f2d8f377c16647dc4c1fc17b076e014042ab3e1085", + "0xc25e6bd26088a92e208a6f2b4cdd043604e177452b88552472618fa6fe369b74", + "0x784b503173b21478ce921f29d7d5fcb372697a3b1ea7a5026d3d5679bde82f2c", + "0xb51e8021cc66bd2fcac69bb48cd03a3c0b9f84ff20cb2cfa947b626e0df06304", + "0x3f743366f73f214a392527392f9cb781595da50f1d81cd8cc13c0d590a5bd880", + "0x973d09a3f621b218f5a3e4ebed608b4a1f8136aa7cc31aba479597aa6c0c8d21", + "0x39456eff8cddba6ffd56b477b3752087cb348be520be37c41f9e34655e2f1aba", + "0x8805949bf9ee489622e0bca8dd0e11316d3b2bf3965228c10e1a5ae207eda023", + "0xa10347e3193b85ef9b0c4be5688a52f6e8957b1deed3de0aa2d97adc71d10416", + "0xd40f74cff977db8b5a28c4ec6ca6d38626ff130d36936bf601229bb950404abf", + "0x2c93f97f033b505364538d1458578ead42967e5ee1437d7dfa03b9bc8279cb52", + "0x1da97afbc0cabd5177f0efbd4da9cdf6bded007519f52410762dfa806809c354", + "0xb49d04f0c000b05ca716024ef39fad6208187fb9d51a25a2ac73c18759ecf7a6", + "0xbc089a8a4506c6ee6e0e10b095c75bccaa6924e7a8600b0ff7257fc01330e5e0", + "0x09368b7227248b05c02bcc3616352507d9858148cd853aefd8df7b1731cefea0", + "0xc283232e40bf0bc1af7b514326d33ce9615f6137f5fe0e38a8059865746ce7f4", + "0x9b3f2b7f16136bf964ce30119fdacb84e0d77a5227219c50173affe216e76651", + "0x76830f1a9b1a547b9c0cd48c6fdfeb72c8d653776410ee0979aa11fe0e15165f", + "0x472b5d2a717e4c7ecb3e7457d9f040bb1180af28095eccbc4cde3f4dfbf24b72", + "0x121293ed312445efdb016a523cc704fe9c4cc3e824bdec422a878bf6fab7aa7e", + "0x0c16f8219cf6ebe5ccc0f973c7384cf8b96c9c78e8424cd0f0771e4d2fe0b0b2", + "0x142c13a397ac45da71ff04ee2ef665c1541c4c5d1c0a0ee891189a88fa4de4d1", + "0x9b9c767db95a3326e18982bab7061977279b4a418378763b616127c8e5b32a87", + "0xb1b0253b4605f963183eae33af799b538de5477040300190fadcb9c80de2652f", + "0xec8ff0d8c200a577822ebb416401998f12a40ee130d09f905584f45bfcfdbe67", + "0x2e2f0f33e0ac96b26bf1176658e886e5e213c0e63749dc19fdd8be5d351b85d5", + "0xc9fac6d90ef0be61d437e4aa1dd0390fecaa3586961e886741d9cbf8b99ed9bf", + "0x31d9c7556370e7b597a201b4e1e2d30c7c99f847cfdfe2148fc5d5154435e450", + "0x6c58ffcc0354e3f68d64a3d28f57ade04aeb66ccc99d80237c53016b88ee0563", + "0x81677456e19b0d7c678819cf6610d5270cebcde9e257234e73906e3cbd85651d", + "0x9bc992c4202007bbb3ead511a6714fd5cea12c24708984876a061d516255903d", + "0xf91c98ff9be5dedd65b6bd723750694a5a1f7e3f2b4a38d4efe0f46fcc250abf", + "0xcb53241165d81e7a7865ed45a20d197677e82481790f3d2aa6ff57e79d299e4a", + "0xce7367565c9554e12f01d3a2e31ae26e4b768a8a10f1d77e52e52d0b3cf6a2ea", + "0xaa5c74a457166178edf4509730523dd6f680d50a26116f68442a4a04263365e7", + "0x3270974abb9a1cc8be6fc911d391cd2979e2ba29926e7312b809cd7c582c9d77", + "0xb26bd32748ebfecfe6eb7f8e22b0c952cf35862d215310c7676cc0817c55d2dc", + "0x476d25b82c776922864ed31b5885e37fab0a854be9b11cc15298614d4ff00aaa", + "0x5591c00c815a469bb63e402411f5fe81573a547520bdf509f2bcd1ef434b9353", + "0x600228b1bf657d6d31cac208cf4af5c6f59c51bbb3f085aaf96d6ec793ca26eb", + "0xddc7a5d558ce74838eebf776a8da0aa72cbfb002659e91cbe2e0aa58f1e0d2bb", + "0xbe36bab1e6fba6c797408234c90266b71f937066680f5a0b89e63b5d392e74fd", + "0x9f00aaaa7ea55454bd12dfa22c8fb53ab9847942401ed240ee65bd70cbdcb3e9", + "0xab82c9c6440e3bbd9bfa1239008d111bd56ce0b013adfd3387fbb15eb9b9beb9", + "0xe8b3ea7145a16fece3ba0d0b5d8a230d11872521d83d50e19e6f76df192ba00e", + "0xcc8265ec86bd2e7ee7da04e3f44186c01735acadae0c27f91098b4a8eea6c601", + "0xca088d51d7cbafb9abc29cb65e7b0762b3124c9df2d3bd52095eba55a0e7313f", + "0xa55462cebbecf74e6e211c6d9c2c220aba6d7fd70a47f5e4acdd70b9489f03cb", + "0xb2ab75e8a3c6d1f7f74fe887ad7018096367a14979134471b50efa50e81c269f", + "0xbc13011869ab47e14983935317322c13f3b978e8be2d674e67ee91255d399d3a", + "0x60179126e30ba6643039f96d3e6ba7c817cb5f7aeb336a987b1142901bee0061", + "0xe220e7d93da29846684fa4b7a58a50ea3001c422698b5693497b6b1c4b7a018e", + "0xa0c3ed28f86d65e5c39c1cbc648fc580a9a891fcf80a8dd066cf11eb66489845", + "0x80ba93fc8cd4a1d8e0bc2dcc44926e4d430dcf52c852453cdfa3a05835f31d69", + "0x59049398c91f241ee3d8db478a44a042e30ddf8e498c193e94cb323f14aeef4e", + "0xd444d7cac86ff9d9666d48fe23072400007fcba6fa5c26a52cec085d9f7e1d84", + "0x03945a47703437b69d1bc37537938c5ac3967e0718d97c127f2b678c183c4469", + "0x162ceea6d20d80497a0e70738b9597a947e5fbaa3dacca50ae6c3752d28a829e", + "0xf4ce029db7b9598f173fa69c22c3cf3074e910e604025e6572f2b86dddd76e8e", + "0x9fa27928d9639ee69eebdf7add64bbb2560d8b1bc00a3c2ed535ef1b5877e242", + "0x1bd0a9ae4b974eed6a643084549a7d8fa393ebb5f0f60597eea01e9302283075", + "0xdb792817ee8337f5600a94bb37448602af334c4cef9a43741ad7dc73a0003829", + "0x45a3d8cf70b324094db77b5b51644ccc8c880ae99935d5404eb25f5feb5fb68b", + "0x56c0b3930330fe0a65956330b78fc0647b1396656422f786d26a74dc9e67d88c", + "0x9080bc35acbb6df2f1f8221e7516b75de85cde93fd1043a42127e98094b8dbbf", + "0xfceca6d32145290ec0178d0823b2012d3eb4d6334645bcb7de281571b8ef9f05", + "0xd8e233e7c8c57d5972b6f41412ec500296f13c3e7bed744384106ca0e5827885", + "0x1f0165079aceee6985bd0f97c8ccc6f9a5789d5d1a4eea7795ddfe7afe45e85b", + "0x155a554de7294941299ecd94a89e1f8d4b649e4e7220cfe7a00e84a224c757b1", + "0x1fbc89844c8a2aba2aea8d47138ff8a4d914f14207f93b4dd87ec9d3280544be", + "0xe780ce46a4037c2589c19ca70411f186de82bb8635ae3d02928a6dcaabc2a8c6", + "0xebb6adb6e390250e1b51c1472af8a5f2819c83c58475513839cd0e9c9e95739e", + "0x511d7d3db913db1e12f8beffa23bfe63fb192cc3f15a18a057e3fe49f0cce40f", + "0x42d252d9d137ef4e84888eb3c399b5e84b57674ea70d44619dbe869b58ede31a", + "0xfa7e75a5726f283e355957f2ad215cd899e42e5a450aa927b6b7fc78e3188279", + "0xaf9e6b01f31c97df644f8c57a309b1de3c4fb0a84ec29c270abac7ae78366f70", + "0x3b4947dea13610ab6bc4d0cf732311f79a440c9ea29d01871e245db658413e8c", + "0x779810ed9d3f14b0fa4d853a6d8c1bb75a83e28c200b7b142dc92fa2e10e3c5b", + "0xc35b23a27a537053e46800854fa9aa92e3aa7813c09f46429751e4901ab0b44a", + "0x46e4633078ec596ee049e5c5f5acd9f905b7755efa858086bed9f08ab7405f5d", + "0x45a4c2580cbe37d45b6802f8fcedddc3d2a013f5f8f70d53b3541cbd24b34c8a", + "0xb1108c643d253d3cc5d3ae8df0fa51fc09faab0eef93fcede665abb9a34eb49d", + "0x32c47a7ae97b75077f22136aa20a78c621ce83b3a7c3b05815f4da7bcbdc97e0", + "0x2d208a493ac61470768a923d8ca6ac6b0216fab391b881a9cf5a34534a14fe48", + "0x013d533980e7a434b8f7ab1ada2602cc2d98aca542ca1e9efa749931aff19cc7", + "0xf0469721dfbef37d48c3ca77c99748cb159b287892135eba383a2a87caa45123", + "0x1cd51b4a7a00570e2c28f3ab3adbf3892ffae0aa3669ef24522f506a2680bc2d", + "0xe29d92c919a58753db3ab1d8a9418630dd25600e1b107ff52b2fdf662870db57", + "0x20b5f057ecdf8571389a73156f6e17c5bbd90e53e740c24fa26e5647cc9a111b", + "0xd295fcf8e02d66e4a58a4dc00d673ee1dd1851dd4f462a1adcab1a195268c725", + "0x63cc756a7232fa0b2ed91110be537c340f57d3c68303857bce6d2c9146caeafb", + "0x00ddcd7849476a35dd03787659d08cb772fa87920fb64c42571beaf493af9466", + "0x0adb1831ca011f9f1eda828e931b1ba49cf7aa53e32fa2097531e16d07c4dc83", + "0x91097e50891b7810aa83440028b443011a0c3f14ba8469cc4eabe03c77e04cf0", + "0x8a7679036cd96cfcc65e10af7acd2dea4d0cb201f234efd84ffe56277febba72", + "0xf00eb36917994233bc547384b78af3d5add1f7af57737cd48eedb1e32dd0be8b", + "0xb7a1790f18d84d4dc73c553f3352c66478214fe85650e05117a0a787a045a0d0", + "0x1a1456a5aa3ee0a93ee776475d509ac5cddcb73dc45140ad96b3a0fef27e4d62", + "0x9d7d7234322f51824c7f183ece04e681236a535187b4cb07a0400f6792fc85e5", + "0xb9933f344a5198ad186fa84315e68f2753df0747943928fa4f3e64831ca58221", + "0xe61ee5f8dc00e34d1e10d37fa7d7d14f6d7dc75dcba9333164c3abbcae6cdad2", + "0xbf96c5722954e7fa88ee06357f881863ed9646e6fe1a37ac285fc9b46415f4a7", + "0xda6671affbcb07e6a26c468c9aebc505febf0b7756673d53d06c9077ae57f989", + "0xd5a00fcbf7eb03e64898e62e358315ad4235208a1decc6f466dfcb55ef6a0803", + "0x43565547b6ac1691420edb5de9f0e835ff499d3acfd635f52bf156f15380f45a", + "0xfb753567ad8d6988224abb69c87f654c9c41d09a4479065e3e3881a8038977de", + "0x1d187060318ca9db21b192f9ca2f8dcdb930e3cc782753ef67e9bab428bc45ff", + "0x1806a3fec2a2bab259898c5b3fbf32a08a67936d71bfe8e8a67047a43ce8acd6", + "0xf0c35b42944a703847cff3aeba794159856a9237e9a8918e0df23f237e0741ca", + "0x8ebd2e087e9b1aa10f43ad7b2043665ae291476ca9e9b195ed423191283f5ab7", + "0x9dc3a05a31af43647026611cd970fbbe364cbad1e87f5a570c1d0480ea50fd87", + "0x012b35b6b22539f56b7429319943d5581ed9e47c7bbec2918be729b2f2babc00", + "0xfbf376ea281ca53df225895990b296f99fac43921d94b1dcec44ae877430d505", + "0xe83291c8c94cb3cc80b7d082aeba19449c297190248e2aee3ab9d584b2a767cf", + "0x2f94ed1c0125985664b47994119f09835f16668554cd52503889b7d82bd49bbf", + "0x1492eb4e10dc52ddf9deeee82676e95b3f20113fa63b13313ef92c9f8984433d", + "0xd21887b57d236c8e11afcb77ac6c785a68a37ff06bc84818f5b3a669773a4e3e", + "0x215e47845ffabdb94e6eed1bf3470038016d0581f01050569e376e99ad6df2bb", + "0xd8dbcf27d0afb945f867c18dde2d717505e70578b70deb6955a1637e1d9579c1", + "0x176eba9df7dfef339754d9de98b3ad7eb27831b0bf5eeff1666c5749658d60d7", + "0x305e84458e1d7cc21906c393f55b08a47cb93a71b15d3b5b3251431d859486e7", + "0x310c1e0b72f9b52bf8ec6bd65bc87f3269543c2c03ca5cb5162e9c5934cdfefa", + "0xf04f601f20d92472c8774abe2bf1ca99f9df40aa91ed44ae80c57c0ab2f365a3", + "0x9723cb14d643a31a3dd4aaa0a65b8f3a80e09a3e7139ff758566788e0536eabd", + "0x03c7330e1650ad571be8204a4ca255107bd12d27f3ba0cf5505df39cb5494ad1", + "0x29b540add9cb34edfaa8d6fb114088b2c115844085651b58478365d52cf1f746", + "0xa31cc09ebd74500e28eeb28063f8acb027eebe26e3340c8e606158ecb3daf387", + "0xcad7b3e07237b4a5ad7b9ab1c55fe8891e7e536e304ce437aeb65e8772376e24", + "0x5c33559994fdc5d478e272a6e102aa12b66ccd4a64e97b184eb471176de9cbd3", + "0xafa9659d0926bb8f0149cf11acf574e452b313de06fbcab49c37e2c6100e308d", + "0xc9c36f994b5532754d7f301122a600e07e97c3bd646de40960491f8d55c4b3e8", + "0xdb2bddca55f3d8514952a6b9ed70b0949f8a67180b281e85bd91436a93461e6d", + "0x168545a48f1c88a3022a8f19f9f9af71e80946e43289725bbd150b6ab4f8b25b", + "0xee669e9c733a79c4dcb80609713c2920ca61d96e7696d7736badeb599980bda0", + "0x0a587b34a043523a5343a7a60bccc878c37a4cf80232dddf15c110ea25a3f6a6", + "0x53b80d9be44113b006004e0f5d399ae362fc3a512804f659868b74b0a299bc4f", + "0x2b07fe112bc5aec796d756163ceac36627d9fb04b2d5238dd83a06ba473ee184", + "0x62701cc84f09ad90a93e40506368a0ce9debfc6332f19056cb513ceaac5e197f", + "0xfbbabf5ff59b3ff84aa8342a11ee8eff0fbba66787b4b81d99a0daa687f2414e", + "0xbef86ead2a0b6289a49432ef845c12ec855319b90a12aec347f26cb3ee01af26", + "0x89f1f322f1679976b56e2d5217de5e59ed3c188350f3e4aeb99c1bdaaf016553", + "0x287c4575e57302823607b463670645ec0bc958046ca16d8c6eee8880ce24fa23", + "0x0a2c853032820e053b6aee9244a05a737734d44bdba4b13f1682c2054559b768", + "0xed1a1fbd436047063d42eb18126bf1b2bc713c2276d4466855029ce660ff0b21", + "0x70164d226f19a8c0fb43d7ebe039dab7ae019c89b2fb79459c79a35ad816a737", + "0xd3269943847a848f52315d0ea5f03f2cc1630b2ae67cecbb6570b0c763f6b173", + "0xda84b557b9b08a1477efc513a9825c23b01ef6999bde45c6564ab5d23a806fe6", + "0x290e8a219b089090dfe66f7455ccabcf1c7c5083eb5f356a0972b70a324d9f48", + "0x3a36c65d0dd269c114cc234c987b368cec0d85af96462d6e65ddc0df39b891b7", + "0x368a5ae8165c24c8cc7c290b2cfbad87954ae5bd608a475eb38b69425816fc28", + "0x6c96afc7c8529090069985faea331df832a56474d7a3578da838e1d0a637f6ed", + "0xab2743334a8feb961021834da8b327eca6e1cb92d03f9763d2593b3cfbb11896", + "0x36c63cd8f35a484a7a747e91f1e5ffdd0f08ce3dca8767a0aa4f09106e6a7c11", + "0xb3280b3c33801be364d648444daf6f2bf2ccd34d43a88174e6b398b1369f8a61", + "0x8c40e36583c2bde82fe680695fab00e750ef3697b063adcdc0a89f44baed5604", + "0x6f06392dcdcd1d4aadafc62be81f298790390f0efbdd6fb82ce6cd5ab87ca6c4", + "0x0f754c072e36624473db47d326c90500f0827bf1b26e162d1b8836ad80f1ba85", + "0x8c9917569fef1fe821793332cdbe2973bbcbbbe1edd622caffe13edcd858c4fd", + "0x9253791a0e3fc025b77e35ccae9708f61107372fa4cab2e65d04636c1f412c7a", + "0x6c64e1c0cdbf5afe46e1074e4cffccd3d79540cdffc8c08d9aa13d3d5af97659", + "0xca6722167191824dab24f240b5dfe96fe4042752fd6ba116189de04467074892", + "0xcbe96ebb79b9438f876f142b1091479860bb888e22104d609fa4c0d324213264", + "0xbada1690fea645b649ce0f5573e298fa1c4047f1efb984fd02f02ee68527d11c", + "0x2f1df07d06673e0f233cdeb1ee333837c68564ddca44f37c3700f1828f431e0a", + "0xa9a6c643d140f1f8b7ed0034ce655ae5e629905c98ac3a9dfc509c6c095f4962", + "0x0c8de9f46a37b65335a6d612fe76065870df9bccd44f45a9e7b35e4b2ae6c514", + "0x6f9a470f1f3d799151c3ef3e88aa015ff2a9596986b21e3d41b32e3a61854e10", + "0x2f0bb3bab2eddb624d5bd5e3751e9b3cc9768ea3d00e102bc330ca7ab2739e3e", + "0xb354636138b0a90a54d4bfdd6d753dfeabe966eac6fb8f9936828e2ed22e00d6", + "0xd7446d05158c86b8dc8e610ffd44bac922e29e3a2a6c02c4bf6bc65494a34921", + "0xe605ab28c06ebf469f22dfd6609e764ae91ceeb0f08d005d0d269f39bd92b2de", + "0xc040eadf39805baa34688861efe5532e005b8e392409e56ca84051838f660547", + "0xd1a14142157703fdca04e19adcbc9c4c2206472e51851fc5dec6cc897bdca0e0", + "0x8518d36a3d1fd366e55af5baa5f84bf26b4b147537aad6987092c2d5ff1076ec", + "0x7c09e3ae9af7ddcf860463f125af46501787768a2a32f04a8e30c41622bd18a9", + "0xff0a61898d1f7b8617b93fc03e045ace32667e87231444211b8ed3e1d6d9091a", + "0xc617d5bf9f7b9556ac62b80a3b7d3a0800a8e29d857bbd214f8ba0fe5dc84777", + "0x959d6c79d9897acb1f7af8b0f67147e48901f09dd6f3ceb98e15475fc7a8722d", + "0x825c063de66f1d0940e561bf42c8782cd559ffb940a81b763e3476a51f90c840", + "0xc6a235f1fc6f28fdb60dd5065760395eb9e55013a70b2d04711d9cb210e659c9", + "0x0425e1047f0861e81a56f4a9a8ccb7dbce2322284700f1abfe20e5109470c509", + "0x48cdee281ffbe98aa7193370a093eaa620c14d161abbf52906b4e1d20f93f345", + "0x5e40e2155659f19ff91dd8c01e30d377ff1c0fceb857f65e149238e9e0810dcd", + "0x04c19aecf54bb9b72940870e62f918ef34a0e69014a3f6beabd08e88a715eae5", + "0x70a7f8305fccd7056e60799a0ffb706ad1517eac1ae491d47e38adbc1b74cecf", + "0xb2e30b98823886050c204b10f9945dbbe6c7533b562a309a1377724403c6d69e", + "0x421c178e7ed2514d8474a474bb0119b762a27d07c4d2b53a5299d4605a589e55", + "0x1be35c7abb5f5aa152e51fa4c985abb822c76d1a015cc00e08120fd5e119ca7a", + "0x370b1c1d2f7dde6fc9e8cc013e4914e29c7a2a69d22f6e76ecd621620ef1ca56", + "0x5b347124455e21117d0e58abe95ac6c8620207607ab5791956f157e6116d39a0", + "0xc827bbff3c8b3af593130575bfdd1740b6d4c2112f3ee097079fb257aff313c6", + "0x36abeb1686803078e3c43b824b4711779abd39410b1f90df15b14372d87a4e62", + "0x4c5013300fa18777749c67fc2222c165cc25f702e37f5216518532eadab0ffdc", + "0xa133d995cee5b21daf0b8923196dd9022f9c1e92644a087816c717db693fafca", + "0x706a6945f9e336a6fdedef7e5e030fe80ef85396f793e1d7186bafad5f89e895", + "0x70c5bf8c72ec8b8503be9c57b0a36fe8ab8c125b15a24478584c1a2ecaf619e8", + "0x7827ea5109deb5301adce34225d3205a960f4ef04f9947b6abbd7f15415714f8", + "0xe8a92316584f49b78aa44b4444fd929a5722e1d3c726557c4c717a95f7cf481e", + "0x6d09421c949bf4390c2d6c6311dec1e84b6e27e57f4cb58241fe4f0b398a4987", + "0x0c59b1fdd47dad0e3a0ac32e22bf62d32179c5ac549b0dcc848a6b21a1ed7731", + "0x7eb81ab843c48038d28cce0d7ed6bfc1ce54bb58e6e67c2e2902de39bd03dda1", + "0xeff68ca1e5c3636a1a99f85eac6da3ebb60fd71f60af5d56e9d74d2ebe1720be", + "0x622f2e6d4e6caec693ecc2f26636bdfd9469c0209eeada729322b877cceda9f5", + "0x75a71bafabc7493f49315e513475c1913d7f63c03f15f11c84710b6932e225d5", + "0xe95a306d2fe31703e72c78978abe2f33a2e2b53460ec3f519bb154acf3dce430", + "0x129c576ad5a6bff661ca7c6b98911f6763cbc627040b990cbd452354a9ffd206", + "0x6a4fb71bf8c14d1f71de308cae35ca4e13c98367c2b2371d81f56fbbd25518c0", + "0xff36f1a3f8f896ec0d88a709ec039e013e67d7bf6ac302635a4e030272e2bc2b", + "0x4e0cd123af26b0e68da5435b6ae38b5fbed553d93ee30118011daa6858778138", + "0x730643d8137b48f19acc99f2af0ee5d597876ce2b685f1c302f46af15e01d362", + "0xdab79a5c1b8bd67d0548e4437541643047c27afcefe17120faa5118e086aa24d", + "0xabfc7ba1e31ae0eca9f2540ad659e2398258a5bb8e4d5ac825070edb681590bc", + "0xbb99b0f3536b68335c224a145b0f529d12cb338993dc5af8470869ae70905234", + "0xac9a8b037cff1254121c961f01b4f7b930d41b1fd892820531e0842054f482e4", + "0x580081162b8af8ef1f54faf787dbeb5d37c4a2edd47373725e06faa797050bb7", + "0xc9b25d249b92ad5917db4b830f270249d546d419368207f2aa0bc290d80223c3", + "0x97e8ea15a814ca9a8d02ea199d02b697785d3aa7e73360561d37509799910ead", + "0xe62a23f04006f542b9cbb8ed97acb6bd95a232c93d98c8ec4187dcacdbcd3045", + "0xfe9e76148bd3e88593fb3b0583085f74a8745036e64fe5b55cf37dbe3c75901c", + "0x9b4ef5709b47cc3e282a4676df6c8c34b86baf7eaf518eb130dd738222e856d5", + "0x4bdc5432c63e079ca7f8659141037e5cf7b887ac766da2c107dbcc523dd14573", + "0x65f032673743dec1e5369a3e94ac2cdc3b4a0be94dd0e178bdfe5830c621d06c", + "0x214ea5319fc5d024eb2bb417e89cec35b80178d0b20bb20b42b72f070516edc6", + "0x8aa6b0b5c40e043a90a1594e47a44cc590b1f99f86a68a4919ffdbbf71565800", + "0x70f8f0b5651c40637960598a596a96cf1ed92aabb5ed9f467e0e7b7673149438", + "0xd7cc04a4b65a9680e3b4443e49079a4e94523c382937e6a343e9b989c22e235d", + "0x25f64fd9763296c6ab16d130c0edac67e4752889911b1cfd5d3f158aaab9862f", + "0x5c5fd33d8881217fe1382452e347ed82d1a94dbb4837978365646561017e5e53", + "0xa6a8927ae26d4c2f211c2ef04148010c327426e0334dcce3158683df029a24dd", + "0x40bb1f3b424aa5e99317dc741de376a80fb94542669a3feb99b40b2a6d22ec2f", + "0xc6e9eb24ce8e0c04aa42b93e141b4c3d5b138559a1dc5c5a7be956e5f05aeb2e", + "0xe617ee5f2dc435e602e1d430edf0ea2add116d4ba5e9cb32b4f643f2601aa84d", + "0x8adb3fefae75bb49316d320895a1ffd7bfc0480aa9daa5b121b40e5d56e04d33", + "0x02fb213eadf6879b9a5d15cde4e790f9105f5c3c7708d13986ef07b1847c2e1e", + "0x070b7aea86427b86acab643d7cf954154c2b32328aaa362b66e9ecca6114e2bc", + "0x94f35d87c36b0ea66d266ab78db91da34d7a20963b0df25ffb2761f5f1143775", + "0x928bb11bd6dd6279e6b76cbaad9b4b52321a5fcbbe90c3478ddb054388a89937", + "0xc90b9a430d7bdfff20b06c353bfe442fd037a498d3f014fe1c40e5f21eb19828", + "0xb75cd777e161ce99e3e5e7f5d806aec8d439013a6cf9690aa1048a1c5739b4dc", + "0x058a7e4ef26e205a882f494f494c6e7129b0fe86b876d9c00973cdc180295486", + "0xa4887c96632cb505ad3852d945d196771d0f7bd40ab9b72d2bc09238172d5846", + "0x50ab789f6752f80e682597918466fa68056a37b0c465de838a55d736ea84bdf6", + "0xbbee13312c2367e59a82c309072508746382acba41d663fe1e911afb8a482efb", + "0x28a66c81fea6102d5043803247eb0e1dd52978447fdd5b32e1e7b1768e35377b", + "0x73744419b7418b9a2be4ee3273f8821c7b9bfd56a860004a87b5c213a591afa9", + "0xf9d871c681e564861733365a3f5af1aaa1d3cf95c1710202415c88d7a1f94482", + "0x899333c6bef47acb176a06c3f14ed3b07dc15f8da60e216ebc953d6852cc43c5", + "0x4fae646dd3f8e191af1718a7ef564b7465a0edcaff7d7bab2ea072c5e9e40843", + "0x65f00d4aabceb364f0b84d182e1516ed8f52df5db636a0d1a0acd77585e74b78", + "0x68170c02bb4aa0c5397544a6cbe12096ecc220cd5dc2298dd14b36808a617a90", + "0x26cf67931d27d3c57aa5f9f3c126d06139baf2cbb6fa93be598c1b9c56377136", + "0x37e6904e1e589529ff001e2aa3b43360c15a69044fc313778091c30a82f6118e", + "0x8ebcad0d38672b49be9fc1f1c0d25ed2855c2ac2b725c3338f3590d82d5486ed", + "0xcbe274dabf2709b47d7e092f242c622ca0f6259686ddecad6b22a43a4a46bc07", + "0x3f96331d637966e39876d9b6f892e11f74fcf3f498b5dfa602a0af336d2b0a83", + "0x4ae1d1d79137bdcdd0f5b5d3ab4cd43f01e61e30c587d44d0543e785094cc851", + "0xbd44440a0d673f1882aecaf16c2327afb9df874eb2daf31adf24d449b0e728ec", + "0x3b975b689813ba2aadb8a108f19f1db664ac138e815f0d731d91f84399be21f9", + "0x1db1ce07ff17018cdd28ae30e2dfcaeb55882f3bd49b0beaf8f195f4867e1450", + "0xd5a35916b5faea1f64912a28275725beec995d669009202b5e8650809ed698d2", + "0xc129b330a4c2b6cdb5cc0cb98fd13558e0ad917ae77f04544be21368aa893996", + "0xc65b552566a173e86c9104bd7c008b93c0df8292cfb819f5901175993b7e8c43", + "0xfb596a05f4ae037bddeca195978b5e2fa06f82267bb9babb1a899cdb54994daa", + "0x71abeef5139340b5056402f9249231e0985a8573648ccaae3c1eba5f35b28719", + "0x425a029c98a7d44898a017a57f68ce4e5855b86f9420fc414066a4b6ecde63fa", + "0x297b42cd7edf7a98bc06be48f67d3c37d9b76ad56e66cd6a801102ab3da2fc97", + "0x1cb82224122cd583990e9c47891f9765ca58a18c766bd4137e22955d58c4d2a9", + "0xef28e699bd86b66dc8226ee81646cab9a72fb4cba231a1850a479c1e0dd26b62", + "0xa29cf03e433f96cf569f60ba4d9dab643ee1e2a91139a627156709a613f413e6", + "0xb2415cad444d14d55961acb12d4a865b7a842b1053c5e49afb99688e5c860b12", + "0x95b76af4f8adcaaf9db4538c7eb2c363ee2f81b35e59cf87837e47d6f65f8a2b", + "0xd21316c66ff394c5b5c68ab58f58d3ddb2bd7dd69d95f1f0a60d96af431e87f3", + "0x1e51a49b0dbfa32ec21b5e8deab7e77c538b12a85b7f4fc52835a63190f19bf4", + "0xe7a409a75e6f15b1aafe1f7e21e3e748f739052aee29330c5596fc93812d6215", + "0x04dc2eec1265e13c7cf4d30e4a9b5651ac3877bcf23cf0739e7acc7d2ada04e7", + "0x295ae29c936f69548ad8d4bb0e1eff47053e6a91a5e8a0c7feb46fd27163cb5d", + "0xa00802d28c986489fb853f552288839649f8ef12931917e9d9c6ffc3a1882613", + "0xd6b0528cacd65e282bb430f7cd85f8618b149bd7af5f7954d2153c843f30f5d5", + "0x845fcb6340daad5a17b4c593eb7e8be51aeb293c3175cd64e75721a6eff21132", + "0x75861ecbe5acf9ef5ede44780f48246aa41d066cd3648d96f047c99a29326ffb", + "0xbe7ad3308b03d4efe4064f3e410b5679b945eece14ae88b17fa8a6f1851b9adf", + "0xb2480065dbf650792e751b31292a5b5b4d3741c4953fa9453bfb13b9b301e1d0", + "0x8ad00c6223281317266cd8ad55dcd787a83ffbf7b06e1ca140ac6c9df1ec3d4d", + "0x769613ab817f33ad8e850edc3ce134c433a78d5621400db68b2d329093071cba", + "0xda7ceaf48d05b365dc5f0dd8e7e99dd2c936b6d56b4f5206913f5b4d0204b6e4", + "0x22f37954e5fac57d4be50165bdbb2d5dcf6cc3e408a00082395dab4062362df4", + "0x4171311ce9e6861edd0b7cf23f6985b842db3533ac8742672c7057ecf2af422f", + "0x1dfdec887f9a8afd6c1aa5d3873eb75757c78fc4059749336cc436a0813a882e", + "0x1b06e12f2b08761337c327dd042211d150a56447b3ac862764c1bf1099c76acc", + "0x840538edcc20e749c6e54911ed935feea5fe3ddcedd053893f9ec5c2d883e430", + "0x65e02a556f7081cbbbb6b330ed515f49d33ad333fd08fbea316eef6f9f1e1bc5", + "0x0259ac911da531b69612380ed23b76722db0db69825b62dde584258796c20f2a", + "0xf474319ef1006ad4b71c252c716fce6c86ce3eda4a85e9a9a429d52847eac60e", + "0x57589ba25ed4dd7ba73a2eddd699330e45c41215e58a32f0d90c2df38e304314", + "0xbf48f75427f0599d90b71a1a4ff9793f160280551c68f4926d3fbefe29a1c269", + "0x75702a49d856e21181e941afe8c06f0b9f0ea75df42ed42488d378290ebe385b", + "0xecf8efab88d554d2b23c6e2fe655439a2ef500b2281e94fb594ee052bc14fcbf", + "0xc6abed44a3d6b9d472d5a70f3c94c919e1a83f989d5e0c5e014d057d0a0a4afa", + "0xb39f873d565515cb0df952b13d6c1032e1b9f15467e35c63a6dd178e384d078b", + "0xc1a1462a79fa154936dfed0bac9ab1d97e43337d91aa8c62552b7e369a2885ff", + "0x83f9c5eccade7bd48532e222f47438e6355f6e0f996d81b87ba4e5221d06b320", + "0x062fc493ef0afc47456bb4bd210930ac486c9ebcc7266583c3fa1e8dfa59a628", + "0x500cb212e4723a40afea27078d27a0a37f4731d33116c7a184ae60a1398e2636", + "0x67d8cb5704920651f1d9b6d72561debd109dfcdfca4e0ecddae4f09c65fbaf83", + "0x3f3cb63f47a39f133a225100630675d7368eea9fd6e7690d739dbe751631fe75", + "0xccc7ecee542d88a7f38d527b4e442d42fcddce3c9d74d35f6fd33e135500f3b2", + "0x695005da9ea891913e1bec5f6d97c9552169247f1a22533d26404458e4dbbcf5", + "0xcea875ff006a32d8dad269f55669f8cd955697c7f7fd7b07226136043b52a4bb", + "0xfbe09841656a3c65a4d831c0927957584099acf699fdaa00e71b9de26f34525b", + "0xdc8b42886fa76307307d4edb923b42cc6eb67d3e25e195e7e05f8aa0eb967af2", + "0x3a3d8e27197099a63e09c23840148874acf9567e5fe8d88ca6a2d99e3f116145", + "0x74c4e73dc04859a08ae7bf7d469299093cb8401e6beb720f534ed01a30dbc0c7", + "0x84787f46bb75471c391717109fbb4a92c4f4c161537adf1e9661cde296918b12", + "0xebce6d93d882b8eeda6f9348f3e8d572cbcf2e6df88fa127e8e1a614dab1b0f7", + "0x1fbac072344ca14c05930fc19aad195dae3c25ffe29a4a8475b0b64b218a33cb", + "0x381b4df5a1d717ad98041480122b6023fa792c6148de5db58bbea7e40cd41d20", + "0xc87bfc610476d389c81fed1d2a1b3d9bc5535f69974dc5643c1535fe8c10f985", + "0xced86f4ad4ff19d8a1ea486e4bd5fa6c206e0b6db21f8fa438a5a22c6cb1e250", + "0x7e65fde194082e34c06d646c6f1d21ec7706e458e7faea03e54a0baafd7556cb", + "0x3179342dea037d102b7f1f74e769de04c449cec68dde436a2fab0585b566f3fc", + "0x441c3bf395e8bd203257174a71d5524d7156f568c5383c62d958067d5f340a3b", + "0x082b814f46a74d13db3f6ac1e52c925891c7aca430ca8e89a88cab883d2cc24f", + "0x9a764446ec809034dfa3d413661ec5c3aec7bc5710e199286e588e70cb176da3", + "0x62b23721832be3b0c4538ad6fe1c5bb7c3b92abe578b1b7ffbfde944a80c200d", + "0xf52a79b71d2252e98418bc07ef73f2ea4bb8cb5a995877f9a61521453f7e3450", + "0x5659dd8fa8b8753b813198fa296b156e643cf922180d38cf1bac91d8bca5ad9d", + "0x09e3afd7d44b0b539271b0b870a92c3221d2f7f743b9ba02d4323dac307ea139", + "0xd72486d95617ff5f5dc361b9b2d728e04578e30851dd0729316946d95fbd9cd0", + "0xca6a9a0ec9a63e06d7da875764e68778456b1c98bb4c0d3fc2c986493dc73c72", + "0xb16eb7bb1564fb7d2d49e6f2f97f26a2c5df622f5b9c68e955c5205735cff001", + "0xd6c65001ff49b50505603ea0f2d34279b48d8ca298359736eb7034626ee4c317", + "0xcd5ba2623df896bf8b43d1c3386425fa940ecbd3629547b051de0aed4322c2d4", + "0x0fad5d5af8aeebb6f5e858c34d51a714f51f8e3d747bcabc286fb07b2f61308c", + "0x93f62cfc4585256a380af952edcbefcc02511cbd55d70e98b6ed7aa5dc0de993", + "0x9093289e66a8f346c84ea4682ebb419716305b7f45d8360b233a3abd9e7a2c05", + "0x62da3d62467bb7469641feac39db85acf269ecdce7bc0addb89f1ddc78f2d3de", + "0xafd6af93bea2e77e7043af55205aba278f729273ff21cbaf00d3ff9ff6e8b8f6", + "0x3051aaa4803fa6f80dc36b435b4c3a5bb8cb9f348e51e7e152a27b4a683c7c8e", + "0x373f6231a73364404bb65305c7a89094013f6a8ae98ca2a75c4230cc1b8d12ad", + "0x3890eaaffc80003294d3797f70881598e7f198fb71dad62cbde054178b5a3ef8", + "0x538faf4c2d6ed3b97821245a38b47695655363bf211d241ff089c52ba6f61bbe", + "0x29548e5c68a65811c8c1eea9cbbd8760d69bf06e93a2fbf1b514bfac0a6282ff", + "0xc4afd9fce4c32c098b6ee5986dd352cf9dd09801ba7c29506cc2a274eb1bb2a3", + "0x933d6dc350acc540c0f95ac6677f927ba7975b650a8ea9c78f1fd73f7483de66", + "0xd14d5991e05ce14fe2bcbd654746fed2ee3baff27689bd83c44ca7a4a43b0650", + "0x973e65b2331d4626ebcc97626b3e54141652363ae890fa79d390cb805b268196", + "0x97b95a0144bba045308021f8a44e34d79f36f6fa7a7504c10fdd090519d98c4e", + "0x6e54a6b12db8f6d74e7c06cd180c9d2a8cb73bdd53f09eb024e107834c054f8d", + "0x29c7b35aeed09d15805c83619c530357262513a712a6baafb75d28c68283a7ad", + "0xd242050949baaa9803b167527cabc4894b314af2ad089d83c5c4cc3f2168038a", + "0xa9c6b7ed7e270b42446895024efc73360a3dd2e5c83c188fb0e428c197e0382b", + "0x3ba79a736c20063395df402cf936221780b586c558eb7199bddc5f951c8a6138", + "0xcaadfa54cfe198b8facfe562bee3d5f5181a4874dbce9fd832efe2c442e97601", + "0xe63a5c0e4963019c6ca9be2df546d4cf6eda4dcdd5b4cc59063ef37d5caaee3c", + "0x11cb204ac6c475b46b330e5188a97506d31ee514fb5423c5cdf7b4ff3f8c2dd3", + "0xec67432c6a36b4f860496173a545fd9501dfb1d35d5d56b9c7e436e85d23eecc", + "0x0905f798d8dcdf854ac0081555b2baff032b1757896813d60bb97fae91fd0a41", + "0xbe28fba21b4f0a161609edfd58c681c69501d25ff2bd93cff4fad72e2c1403a0", + "0x9d68038eb2cec9f0a25466e9a92e512f71f1d3f42baa0fe0a9ace9581721ef59", + "0xe303ef36737f887993b33a55fd72128171c4b10a8af0f2e9d65037b2fd5b0bf4", + "0x69695054a9d422100f001f80a78cdb7d68a160fb735c833e3e29b463151c671d", + "0x3422a23d87658ce8f3496057288e5d82c773c608a5ccea7a2e9db79867147ed2", + "0xd7f7d77b2046684c584f05cf2b9f053a09de702b01e236d14c5d7171ae90277c", + "0x72a1118ec0450001756141d860e1cd0554e711598238901b2ee8f281c427de70", + "0xd19be6dcb740c15351e0d802859a13ac418fa296cee788a2e88696281886d57e", + "0xd3a04e9d3943a1ec817dfc862c34ea20bec6703f20b59a5131e14175624b9f57", + "0xe53093925106f3ab9b0e5104a6cb9833e84c81eeb32525dbf198da95bb0a692e", + "0xf614510acc1dc376c2f9ec0cdabe185c5387ae57e1e7dc2b21e8c9af15e7b0dd", + "0x5397cc3e5ce518f68d40a0371eba89166eb691c85394d2782b87187e5fba95a5", + "0x9156e924006cd6f0e75e5f1b6c6b67d2aa5cd329485633a1f8c81542c6ad73fe", + "0x51947014d3658dc93561abfdf5e59c5545bff2006be70d20752fd0041b4c4993", + "0x17960456465e1fe23d9212dc7379f88de6839d76fd34e328c714109068d1d79d", + "0x71a004f35328853ace7ae57ceaea04ca0ff40a638e2a422d9a77a442f9f8594d", + "0xedb821e436a29e2304c4e582b1edaeab4fa4ce815e327140d34dea1b28eb3cfd", + "0x0fef692f940b1040a8f06dfdd345a509d0d50a47026cb8d76f1137cb0cd172ca", + "0x20df1d7b21fa8f3aa4ae122c90444959ffce4b3bf91bbd12ede4a730cd5281f1", + "0x5a77441704fb00b4e37cd7d992e419cf03c9db67855682b5dc9cc2ec5642d1af", + "0x70e572d6816a924ba8059f7d80ac3dd2da6e378235d9b13f9f75c3bba7358fa6", + "0x820a5912370c8911460bcaf34800c9aa0442c5f6f6346778eb8d0881f0c8bb0c", + "0x03a7ba58c25e37f3eb4632575cd5043335a93256a0df25d4a40054771ae2f1f9", + "0xecd2166fba2cf74b9c39fbff60e390b719cd89d944a2a279fce7548b0449c0cc", + "0x4084df94d1c481f270ec12d24021c2c617c57266c84e5c3c7110f435a23c1f88", + "0x3e3bfdec73b4a43f78177843d0800856eea15d6e4f3421cbd416e30415438090", + "0x9e1b68d2c1da24388da8866036cc9fd41ab619ed4fee244409294690550ded0d", + "0x7fa35eea26c1b7565d773a13519b56788df9d552da0eb31f5f932cf16698cc4a", + "0x6a5327f7999fd0f13d92990c9de4de4326ea7c36b2886c10851e7d8eb6725500", + "0xc43b78bb69ba7948d95bbe6fa417d48c50563d1a299dfc055a45a9ab99463f40", + "0xffeff6f990e8db57d4982faa0ecd50acbd56a0eb408e20e28037b1a7a5da5b59", + "0xc2334f59cd63c7ff3fc656ddc1be9267cabfa0775a2c1d0b6aa031ca0f6d509a", + "0x1e78eeaba6416f172197e83af9d7cffabebc1a7965b7efcc5d81fba214214ba4", + "0xf8af478a5f4950e0f14c533ad42f0fb678d5a4133dd6c24cc99a0325bebcb6d1", + "0x893b2cac08a72a47a50688703bbfa59354d3178ba6edffefe12c1b19fec00764", + "0x14aa1633a0b52136d59bead586a4efafbcc8b39a380979b33c319c1b4eb3c8ac", + "0x7a449e7d7349bed91d4969b6c2930bfd651389b18e6fd95e166a5f3b3dfd6a9e", + "0xc013d618ec1fe221e7542266573294f6a14b03a492c764bd8f9b086a43319c77", + "0x5ae106689e13a32cd3469bb7bffd9650d626088ef3fb6f0efc73cfe53134ff5b", + "0xf64afb533a3e37a80f7edd723cec317d7102d2443d43046a1c7aa961ff961493", + "0x6ae14b675f46a21a82a1a3c159cd776ca3f38a964eaf732a5ac741488de4e00e", + "0xba7cc771fddf1d827ff2e29bbffc15902567a1f1e4b480d44f67031d8ed63793", + "0x40a627652cf9735a888f09dda1d706b98c957eaf01519338eb84a8453e8c753e", + "0x845f31cc82334bf7065a3dc2f567b1ceb1b4ffb2271dc6c53f881c6ab95dba64", + "0xd12c514ff09de9f213b56724dbec8bb105d4460adba79916ff6a1a172d3e263f", + "0x2327c4236f51735b982dd05887d2c80fef76d4024eed66994ab1580052e4d7fb", + "0x05e136fef4858eef97a056a210373a067874a3c9a9f0de31a6723098b5d717c7", + "0xd0e32a96ae165e2d61f1b483b22532f25bfe32aa45f94c6be43a9cbb724c202e", + "0x7b8776f0cb373249d0a55518716efd1e85052e758e19f543c241aa1eff158ca1", + "0xafa4f1c453ce6de8b39979692eda55a9cf5be39ff3cd730d8d5221ea21dc9f28", + "0x54795f6c31496ad41286c5f6c3e52115ef3497d59b926d0594a21907d816988f", + "0xffc48e89cbd6b8368439f57924835d36aec456015a9a3caed6bcc19686c69b51", + "0xcb3c516accb4ffe4e5035f84729c0e26c58a84b86fa6dd454ff6e1071dc402b3", + "0x51a67409c5d05ef1af96dd6c1e3646e0406b3e35baea5d632d8f4038778ca27d", + "0xf094d57aaa807bb6f4e8134467460e8753b50364df56717197e6f627f737906e", + "0x86cb75940bf6be0c4bb3d3cdb88159918afae4c55d26a279402cea6f8d2ccfd0", + "0x85d528f7078e70e3fa3d04fe4ac0e2487960c1e06fefb5f5d5c768f8fde829f8", + "0x7c18f5c8a9f1762cc7cee964d36d0d12e9e64132b9d64708afe614b03fcf2e1f", + "0x8fe9e91f40f64f2a393cd135296f2bd1048d59459f5f6aa92a339349dbc61999", + "0xa61ea3415cf5e47a3da59d7acc939466c1d20c48756ad135bcf544d38824076b", + "0x9d890bb7aed350295e4d0257d9fff826581c80395c5526a97300a6980061822f", + "0x0a677a540c74223bede5ac77d6b53e40cd061841f5ec2acbc1f7875c9569fc53", + "0x82bd62cfa2cfc52bda9d99a2c31ca2ee4b70a4dfc50d08f0dca514bf5005482f", + "0xb26d15256ce584284d73f5b0af2c8ee200d16e40e30110f7b7823869b95eae86", + "0xe586127218814e8feb2f9906ffa74f04bc01c1bb0d685e4b70f37b078e8a2ddf", + "0x63d60bdcd23ea605099aa7f5e46cafff74d271ae3614b7b24c89a3d693422068", + "0x07dd6971aa0e032c8562eeb4299e41c74f5afab19613fef8d976bdf7892e6462", + "0x68d630ba083be8fef27f9846d35b271314448cb2e9f34bbcd4f61cef2db80653", + "0xd40eafd8c080c71f7d7940f0da435be6127051223c9052c383ac02dc3049a7eb", + "0x3a3eaccf11c9648c05656827eb8cbd18905d5795e7cd6ddec682572ba6fc3b20", + "0x6190aa5fe42aeb259a6830672b1968870301cacce8ef21fb6dbf9eef3ace773d", + "0x239bbcbcd51c6aff9f9efa6ff5cc614f72e6df3aed121756006b1187f4d86c79", + "0x123d4c2f434d1a8d59111830fddce85d7b4f64f1e62f30deee3f97de0162bac5", + "0x3e730ce7ba743b7ca91f3acd93b30bfb824722d1f72b2ee584b151ff3c3bea19", + "0x3def4dbaf973b8f612dd400e45a33b193ea77e4a75df97180ec9537d7957f87e", + "0x6a4b4d6b96521f22ce56ae384def1428aa7bb6ca4cd0b1116cb6b9ecf73c9af0", + "0x0e7af6af3a0805046f1e02469d0921ed3128fc5201d773f0979d33d2f336cae7", + "0x21bbf154d3e704b1078b32b0d4a97c468a3d00a2efa1d4a7c0ebbe2b2f572e07", + "0x809330ad07a6a897e5f4d3e88f9c0e8b2b3d7883219afbc37af062f215cc7b92", + "0x928710a7c75346c6a7984b55057cbba2e05cd4d3d185c763769db4130f24105b", + "0xde04fe66a27a785d28e871dcd26f21b57e916cf1793f4584e4e969e7bc2c37b1", + "0x5af449491866d210bfaf3c47e45ea0954a131aad4d7637ddfd7cc5ad3425f8fd", + "0xbf383c8d01735d77b6207bbd9eba88e0d7c8a916610d86be263cb4032c13fde1", + "0xb93c0e248ff221aac5c090475c777975d8bb9d5c93d44fe073fd29f465ef25d4", + "0x3d16452b3cf0109768630044086b7f5f9949b60acf4b007d323ef476bc3f0855", + "0x1ccf023ff32b0751eacdcb7a0e6af68fbd5e7dd6d71db40dfe2f5f0b4c031bc3", + "0x061f48dbae0f5b0eb2ed1536f5f494ea6ca560a2c833b1dc9cc3804b429d01de", + "0x37ad2c019ce57c0ad520940199a304d84277bb68c6921103a8ed05e9e92350c8", + "0xa00a8323f032589d2d2bcee02c844b6c121144a17d5cd980718cb4365ef52680", + "0x21392973724ba280fd1aaa9bd9af4a39492d05dd26b9859fdeb3dc2de94850ca", + "0xa52bddb394523970d8483c93ebd1587d19bf41d91db4478a3d59e04688923ee7", + "0xa9a5ec6db281726f1e29d847510f454caa79ad70655e05ff37b396cd07f8943d", + "0xddb529c1dc9e915a1138822ba9856f11f1a2cd6ee396b435264910a7b8a3822d", + "0x16837c1968a89f8b06f81fb6d762a176da0f76a6e33702c874b827f53acf2cec", + "0x22c6a11cb8b4c6abd624a097eef3afbddfc20b19b0a28cc2342bc527edc81eeb", + "0x1fca7e93a1c9a9a194e177c3e61ce180e1c555908127ab751afd128de00ca934", + "0xbc8c239ad0f1241d35e3ce0a0b76f9e1cd3648932f59a45e1cd7ed431941d43f", + "0xb05d13ed67183420496de5beb3e70796e97e324a4917f646808b37a071a420ba", + "0x27832944c1d8257a21c7ca3ef09c0d2da2a6adc231a1d3cc1f05326c523ce5e4", + "0x16bd1db9f272fb09880fc56d0d479ea83bb7d3fed115be5148ea567731ead1ce", + "0xa9f4f3e273a684e3ebe1d4c2c5c3e99690dc9b0be6c998270d2dcf88392daced", + "0xa541cffed5ee8f0ed241e164443df8bb8d86d4bf709848f082067a15048dfefc", + "0xa0018276c51bab875d0604f0ce4a742f172a3e2966ea532131896b6016c5f117", + "0xe39f2c1d9c1e787738c97ddb7a572bf68a3caaa8770ba29bfa19a7bb07523f3d", + "0x7bcad3b97bd9cf53d62907a328c8a3afc2299b7803ee49d51b48195362f4cd0e", + "0x3b2d518f0246d68a05015598478fe9699c59ca9bbb9a2407209195238511e6ea", + "0xcb67150a83c179ea6485edf64e7d048b4ee700bd4f25e58ddb39035c2de70596", + "0xf06a3f425c380c6f41b30d12b7ec84807ea8a48f847ca39c022dfee4b1d329b0", + "0x45cae9a13323cca0620866bfdd29738086d29b3f13be2d7ba37435f2d6970133", + "0xd28a77dafa38b03ef77ec931863d714c2606da08ee4650da537d37195554ff46", + "0x5654fac4aa4fdd1b9e861d60bd5c8b439ea22be1cc6a5bf8ae33f6527557c614", + "0x3b8bcd37cd5213cba8fb58766e02723a940bfc7a3bf1c2d86798fa96591dc1fd", + "0xb83158b79995fb0194b90d469bd778372fdb05f7ac73c5dee4054663915bef65", + "0xbcc57ae2d7de6e4f035199645aafa7c5a2961a2a719492e4383dc10f175a74c0", + "0x064b737e28145b5966f8e6b57a82b55aa3259394bb2d9724f56a38774c9da28b", + "0xa5603f7d9f8074cf15c38b81ecbe74d204287dd20658f1fa8defec7fd1af82e2", + "0x7f84a0cc9f7c00416c1ead9cd147b032ee331cc4b8f8a50286a3c0490f3e3ae3", + "0xcafa7be96bd0eec075b24e104c7a45984fc485b465ddd07c605196773d1d2c8b", + "0x1df7d26ff663b82de2e8b61508c6f2d4a887780bc352adc8de04757bf893482e", + "0x1aa8dc5bcab34596fdeace8239cfc0e276338870e93f98729a186ed79f31367b", + "0x843fb16c8c27cbb4939e7866d5c85fe520af6837edf73623216e775bf20f06d9", + "0xb71ecf25beb948644b41ccc6d5757fe79ce134b04d57cd7c926784fcdaf7b5fb", + "0xc537fa771675139a050e2bfa006aa11f57f004073c194c35d651f6121a30f6b2", + "0xf255d36d2bf6820b49cb24eb73cbb8c47ccab618de312ff7eb6cfc7937611fc1", + "0xd49dd620f0d4a4f987815d31cfc3b560005a069f3e83443fd33f46f091f4c506", + "0x6dcc0592f053243a8350705a0dc97507216dfdb4176c0738d5a355ac7e3045cc", + "0x60640bf7d73e49e213734edb953cd4a013cb8a5b69c29aa0ce02233c9ddd1233", + "0x786951be5c53fccec94f014d4e27806f5cc7d3392f1358fd8e11c27beb2b4183", + "0x4c66f28fd5c9990332a4887662dbc99ed5367cfc3ce10acbeed6e55f2efad9a4", + "0x54b2a8eed380430a9ef311ff8540089080fb7925a2d2251ddec3b8afef8234ad", + "0x84a6a38cb3e78edf671ae775997414ee25e61f2729c54c91efdb66df630742de", + "0x9c49abf630b8fb3d73d063a189cad528c0abc4b8c0278687da74b649a2ddceca", + "0x29b1704440ab5d6e9c0d33b73743ecadac8d4bcc266c41f65c698f0cb5a7f63b", + "0x6e674e7459e87c76259ec1a2043ba85d813491afb80beb4f52cbce2a0094f12b", + "0x8041a526aa1b7902848007e195d12226b1bb6bc6edb4f6908f9f8280641925cb", + "0xe61ad33e75f8be18a12d3e19d9d21c3cd025e7cddf471ca81afc5ffff037e6f3", + "0x966df4d98827b298391c2ddc42020d616317c3fada2b2669624df77eb094e8f9", + "0xda2d77843ab5d4d04bc2f969052ff2e9ca5a37bb337d5cb88c4e6586bbd68d7f", + "0x5138cbf64fc819e065e2747648615b4e885638b63e7559c4ab48038d79b5d736", + "0x4135daf130280c035422841f66801a1d5a47d1a6ff0ce369001f1e69d56efb36", + "0x211e30168bd1511b45f627506892a09590e5fc8c9445a083a2de9ce19abfbb90", + "0x15d35f6144c6a7efbd7ff31a5e74fac71d77828cd1c7d1b8585a50a3a0470b52", + "0xe3a65c1bbac89de608a238d16fe4b731f3a1bb891aefff297a9a814c020c076d", + "0x6b451effa44e9b9a78ea9f75d6a703631597f758b6a72b09156a1f649a40a469", + "0x95b208a56b41def22bf993ffbb2b3203d2ea77c4950e400356e4a93d0e39eca7", + "0x4586ff01941a4aed359b8da7bbd781e68e389041abba7c7d4799d2c0b69a68ab", + "0xfbbfb1e0b5076d6a7b27305d49e85e753989efb8d0b60f05873aaf389e145188", + "0x4cdfe0f66e345d5e236707a630779c9d2626949105b8496c5a474372041c51b9", + "0x4009331fafcbcd51419930e20371a81a728f587fc5451064b3e30f96b92006ac", + "0x1fccb0663f77a7d8fd817e27dd23f944072f9cdd8f4db2bcb18cb25c43a44e65", + "0x8acb488d06ef20328d1e2273dc1f2b86333cf141ee45ef197a0d85b4511f71ad", + "0x6fdeb5d759d17d9a41c5c23ccce53be6a339a4281a94229605ff3a8ef0108bf3", + "0x91fae20d86ecbf24bce76f5549919e14e0c68b626b0e1029b757f6c47e6d0ccb", + "0xf94240a3835f000b16978ac341b78cf47749ee4f50b75fa604b05609a1a93480", + "0x48724b3a9fc3c561dece8290e98ca194a82303ee668986b4ab4cfda1024cbaf4", + "0x54be60b8d2cb74111a722a7701c003320f903711a28f3fd95f0595a145f549e9", + "0x3dcdb34a158e3e772ff2c59ee5445a3ca61be010275e7326cc932875709f9875", + "0xf3b121df3e45d4f19d9f4d44b129d3882383c71ae5551752d9be0686a9320bd3", + "0x2fafef8a4c9efd8d964dd837b6ecda0f96a126881ce1d49e9cdf2e87b665a6c6", + "0xed4ca0414f558cfba4da48618992dd35b8763086100d8d2b18d65a100c6b4607", + "0x97c0b83741c4e2c8f9201254f40d7f67b1a54962d02a1d578ec7eab0559b023b", + "0x81e9f38d62c67f456135450e87155eb0f71a490a06292fb582d012f287872974", + "0x38bea72de640a233cd181d16f80873b71015963a06636bdce39683f4adce850a", + "0xb0dffaab6ecd9acd3e18ecac5cff4ae6d3218630c2c92463762dc40282529dec", + "0x380555e6207cd035475dc10d8d69d49d2b6c6762f3c12fcb9dd0bc8c977dc330", + "0x57eed5f25a20f20b1fdcc4b03c29457e278ba9d854adbd0b30311aa72717393c", + "0xe66251a1f18e28afce206bd9778458a14c34799751b8dbce11a54b7a33ce5961", + "0x030c049cb797c288248def3fae9d82ac16244b1e97defe570497e85a47132534", + "0xaf32abb9a98798238af391309e8abe48d082cf20b38b85e2be433b710cf8df23", + "0x36935c94d5aaf8818c77ec5858030c547ee41a39c5d7d6cb47c728f8addefea3", + "0xec783f1d068308d0731336afca7f01a4d74319a95135a4aa7dc148516a54821f", + "0x342c01e292f2dd33e4f27caff58421da0589e8c14496d26abc6c76fccc0a50b5", + "0x2b64b35b10ec75bab483c242a1a35c943840b5c2c4b4c469afc845d7c5bba02a", + "0x34762a557170ed1a82d921c04240fe265f20908f264b670916ebbcfeb3bee59e", + "0xd566f848784e163a432c3a5de93d41b428e48710d22f27e3ce3b576977824bd7", + "0x1df1e674b2fadbbb970dcef6c13037d7688646d4ae063dcebfc6e0e2ff858898", + "0x706edfb454d513f47d290c0571b3a2cdd249928ae0ea7b06a39da05abf3aa637", + "0xa67886701eb8d0a28efebaf06acab77965595cda6970dbd26e50178ff09e6757", + "0x9dba4d0baa8c0a035e1cbb5e8e751013da8a98d8b70c2a4dc037f89d166f0180", + "0x5a5fdb3656bd50635f2a62685199c0ae135f2ad68059681219066aed079ee23e", + "0x243602e30ea061853ceb1c2dc0c39240548b656354be710cec3bb9facad8550b", + "0x97beaca9ffc0142d5bdb759b8c2ca340e60f422dab507ccb710009e49641bd79", + "0x371cca4f59c01ef18996aba60bd647df238128b58245e2fcfcd46b8ad37fe67b", + "0xd4800dafc3cf31fb7126cb750cc04c9a09c8c313ac21c4d104455bc1205cb62f", + "0x5a4540b39ad3a2b0b0628cc63cabaa95c3f380f0589ae05abf28ca7eba9bd2c7", + "0xe895260d744db389d7f4db88319fc604b6e8518837b4c74bcf306e6ff5380bb0", + "0xc59f667005ce3d53c1ffa9d20182b90a2088e10eb9283de3baae435956c48293", + "0x6d7b1f5141e279b906bac93e6ec82c701edb1a9a7e3db1bca7d164515c1251ac", + "0x1de48aee06ef7f90c4f16a553a8c142cd42212cf5dd1c68935d7a3a11cf42ee9", + "0x4d0322187e4aa50cc91fce589824e5db8542c0246a861571ecc8df118fb0f970" + ] + }, + "accounts": { + "0x0000000000000000000000000000000000000005": { + "builtin": { + "name": "modexp", + "pricing": { + "0": { + "price": { + "modexp": { + "divisor": 20 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000006": { + "builtin": { + "name": "alt_bn128_add", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "7298030": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} + } + } + } + }, + "0x0000000000000000000000000000000000000007": { + "builtin": { + "name": "alt_bn128_mul", + "pricing": { + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "7298030": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000008": { + "builtin": { + "name": "alt_bn128_pairing", + "pricing": { + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "7298030": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} + } + } + } + }, + "0x0000000000000000000000000000000000000009": { + "builtin": { + "name": "blake2_f", + "pricing": { + "7298030": { + "info": "EIP 1108 transition", + "price": { + "blake2_f": { + "gas_per_round": 1 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "pricing": { + "0": { + "price": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "pricing": { + "0": { + "price": { + "linear": { + "base": 60, + "word": 12 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { + "name": "ripemd160", + "pricing": { + "0": { + "price": { + "linear": { + "base": 600, + "word": 120 + } + } + } + } + } + }, + "0x0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { + "name": "identity", + "pricing": { + "0": { + "price": { + "linear": { + "base": 15, + "word": 3 + } + } + } + } + } + } + }, + "nodes": [ + "enode://c1c3a604950119f82d78189792b73f5a82a239017c77465e3c32fc51c1d758a9a772ffddd58436d465342f2cfa6d4a442a49e526743f4d8354d7c5ce794c3ee5@95.179.222.48:30303", + "enode://2784b0173e345df9911875e68ccfcb0627ad4ae1dfb9f77634435692e8626508d9a6a04adff7719d3d73b25e72cbedee8d8e431492afbbd5fb4082e78c52d934@80.240.29.162:30303", + "enode://dc3dd8711e84a6ccab1e443cf27a566c92eea157baf12adc6ec3605302e90750287d6fa7b71b31648f8672b66a39f769bff6b522d2492678748409bca2b7e41c@95.179.222.48:30303", + "enode://9faaa6505b7051c67e0e3cc8e83b8ab91cf9073321e359125c5d6b3da886b9093a2b5f9342ac33314e079a0c52869317cea416f1386505d36b7d1bfcb607ce59@96.30.194.28:30303", + "enode://ab7f6c633ba2dc54795dfd2c739ba7d964f499541c0b8d8ba9d275bd3df1b789470a21a921a469fa515a3dfccc96a434a3fd016a169d88d0043fc6744f34288e@104.248.254.129:30303", + "enode://e71d7ca47cdf6683186190b863e7b78c98ac8a669b671be9565fb86b4b310ca1927c0e5ae7d9e25909dff65d3466976287f3f5684fa85787b6d097fdba7ca07f@80.240.16.221:30303", + "enode://c3e4abe0c20dbd7b75794d4a4c0f5599be2a224e5ec697ac9daa0d6a3773dbcaae9d02176348d78557a6a68e787516714f254a76e1cc4c991474c04a4743efb2@157.245.255.48:30303" + ] } diff --git a/ethcore/res/instant_seal.json b/ethcore/res/instant_seal.json index baafeb5bdf..b2199ebc35 100644 --- a/ethcore/res/instant_seal.json +++ b/ethcore/res/instant_seal.json @@ -24,6 +24,9 @@ "eip211Transition": "0x0", "eip214Transition": "0x0", "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", "wasmActivationTransition": "0x0" }, "genesis": { @@ -47,12 +50,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_add", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -61,12 +65,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_mul", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -75,14 +80,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/null.json b/ethcore/res/null.json index 5128f26c59..82062366d8 100644 --- a/ethcore/res/null.json +++ b/ethcore/res/null.json @@ -40,12 +40,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_add", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 500, - "eip1108_transition_price": 150 + "0": { + "price": { "alt_bn128_const_operations": { "price": 500 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 150 }} } } } @@ -54,12 +55,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_mul", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_const_operations": { - "price": 40000, - "eip1108_transition_price": 6000 + "0": { + "price": { "alt_bn128_const_operations": { "price": 40000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_const_operations": { "price": 6000 }} } } } @@ -68,14 +70,13 @@ "balance": "1", "builtin": { "name": "alt_bn128_pairing", - "activate_at": 0, - "eip1108_transition": "0x7fffffffffffff", "pricing": { - "alt_bn128_pairing": { - "base": 100000, - "pair": 80000, - "eip1108_transition_base": 45000, - "eip1108_transition_pair": 34000 + "0": { + "price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }} + }, + "0x7fffffffffffff": { + "info": "EIP 1108 transition", + "price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }} } } } diff --git a/ethcore/res/null_morden_with_finality.json b/ethcore/res/null_morden_with_finality.json new file mode 100644 index 0000000000..ea503ed29a --- /dev/null +++ b/ethcore/res/null_morden_with_finality.json @@ -0,0 +1,38 @@ +{ + "name": "Morden", + "engine": { + "null": { + "params": { + "immediateFinalization": true + } + } + }, + "params": { + "gasLimitBoundDivisor": "0x0400", + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2" + }, + "genesis": { + "seal": { + "ethereum": { + "nonce": "0x00006d6f7264656e", + "mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" + }, + "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 } } } }, + "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } + } +} diff --git a/ethcore/res/spec_backward_compability.json b/ethcore/res/spec_backward_compability.json new file mode 100644 index 0000000000..e8ce98a6ec --- /dev/null +++ b/ethcore/res/spec_backward_compability.json @@ -0,0 +1,155 @@ +{ + "name": "Volta", + "engine": { + "authorityRound": { + "params": { + "stepDuration": "5", + "validators": { + "contract": "0x1204700000000000000000000000000000000000" + }, + "maximumUncleCountTransition": "0", + "maximumUncleCount": "0", + "blockRewardContractAddress": "0x1204700000000000000000000000000000000002", + "blockRewardContractTransition": "0" + } + } + }, + "params": { + "networkID": "0x12047", + "maximumExtraDataSize": "0x20", + "gasLimitBoundDivisor": "0x400", + "minGasLimit": "0x1388", + "maxCodeSize": "0x6000", + "eip140Transition": "0x0", + "eip211Transition": "0x0", + "eip214Transition": "0x0", + "eip658Transition": "0x0", + "eip145Transition": "0x0", + "eip1014Transition": "0x0", + "eip1052Transition": "0x0", + "registrar": "0x1204700000000000000000000000000000000006" + }, + "genesis": { + "seal": { + "authorityRound": { + "step": "0x0", + "signature": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }, + "difficulty": "0x20000", + "gasLimit": "0x5B8D80" + }, + "accounts": { + "0x0000000000000000000000000000000000000001": { + "balance": "1", + "builtin": { + "name": "ecrecover", + "activate_at": "0", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000002": { + "balance": "1", + "builtin": { + "name": "sha256", + "activate_at": "0", + "pricing": { + "linear": { + "base": 60, + "word": 12 + } + } + } + }, + "0x0000000000000000000000000000000000000003": { + "balance": "1", + "builtin": { + "name": "ripemd160", + "activate_at": "0", + "pricing": { + "linear": { + "base": 600, + "word": 120 + } + } + } + }, + "0x0000000000000000000000000000000000000004": { + "balance": "1", + "builtin": { + "name": "identity", + "activate_at": "0", + "pricing": { + "linear": { + "base": 15, + "word": 3 + } + } + } + }, + "0x0000000000000000000000000000000000000005": { + "balance": "1", + "builtin": { + "name": "modexp", + "activate_at": "0", + "pricing": { + "modexp": { + "divisor": 20 + } + } + } + }, + "0x0000000000000000000000000000000000000006": { + "balance": "1", + "builtin": { + "name": "alt_bn128_add", + "activate_at": "0", + "pricing": { + "linear": { + "base": 500, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000007": { + "balance": "1", + "builtin": { + "name": "alt_bn128_mul", + "activate_at": "0", + "pricing": { + "linear": { + "base": 40000, + "word": 0 + } + } + } + }, + "0x0000000000000000000000000000000000000008": { + "balance": "1", + "builtin": { + "name": "alt_bn128_pairing", + "activate_at": "0", + "pricing": { + "alt_bn128_pairing": { + "base": 100000, + "pair": 80000 + } + } + } + } + }, + "nodes": [ + "enode://59c9250cb805409e84c9cd0038e97d8e5e4605b928663675869ebdfd4c251d80ccad76267a5eb2f4362ddceb5ec671f7595463adfc0a12e9f68dbf233072db41@54.70.158.106:30303", + "enode://e487ebacbdad3418905d2ed7f009fa5dbd17d73880854884acc604c0afc1a60a396aa90cb2741278c555a4e30ffc6ffc1c29e83840aa22009ec92fe53f81ec04@99.81.92.124:30303", + "enode://563f12602a117201b39ebeea108185abb15d9286830c074640c9fccbaaaabcc7fe2c95682cc43f95b95059f6d0dc4c9becbc1b2bd78e0c5ef5fddff07d85ba0e@54.201.62.74:30303", + "enode://5903b3acebdc4a34800f6923e5f3aec3ca7e5d1285bec4adb9f20ebb0f87a3bebdd748b1849ca1108a9f1e37ff9ced0b475292b8effc29e95d49ec438f244b02@3.121.165.10:30303", + "enode://8f8e35a6dcacfee946f46447b4703c84f4e485e478143997f86b1834e1b0bb78dab363d700dff3147442b9d3e2a1c521f79340c436eb7245a97c7fe385b89a5d@54.93.159.98:30303", + "enode://bd228aa03cf4a88491c81c5f3ab4a1437df3b463081cc93943c4d3ab37f1e4f8081c6995eca076f717d4fdf9a277c750bd0289477ac151f1e2b024747dcd1747@52.31.129.130:30303" + ] +} diff --git a/ethcore/res/tx_permission_tests/contract_ver_3.sol b/ethcore/res/tx_permission_tests/contract_ver_3.sol new file mode 100644 index 0000000000..b361815dc6 --- /dev/null +++ b/ethcore/res/tx_permission_tests/contract_ver_3.sol @@ -0,0 +1,60 @@ +pragma solidity ^0.4.20; + +// Adapted from https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f +// and: https://github.com/poanetwork/posdao-contracts/blob/master/contracts/TxPermission.sol + +contract TxPermission { + /// Allowed transaction types mask + uint32 constant None = 0; + uint32 constant All = 0xffffffff; + uint32 constant Basic = 0x01; + uint32 constant Call = 0x02; + uint32 constant Create = 0x04; + uint32 constant Private = 0x08; + + /// Contract name + function contractName() public constant returns (string) { + return "TX_PERMISSION_CONTRACT"; + } + + /// Contract name hash + function contractNameHash() public constant returns (bytes32) { + return keccak256(contractName()); + } + + /// Contract version + function contractVersion() public constant returns (uint256) { + return 3; + } + + /// @dev Defines the allowed transaction types which may be initiated by the specified sender with + /// the specified gas price and data. Used by the Parity engine each time a transaction is about to be + /// included into a block. See https://wiki.parity.io/Permissioning.html#how-it-works-1 + /// @param _sender Transaction sender address. + /// @param _to Transaction recipient address. If creating a contract, the `_to` address is zero. + /// @param _value Transaction amount in wei. + /// @param _gasPrice Gas price in wei for the transaction. + /// @param _data Transaction data. + /// @return `uint32 typesMask` - Set of allowed transactions for `_sender` depending on tx `_to` address, + /// `_gasPrice`, and `_data`. The result is represented as a set of flags: + /// 0x01 - basic transaction (e.g. ether transferring to user wallet); + /// 0x02 - contract call; + /// 0x04 - contract creation; + /// 0x08 - private transaction. + /// `bool cache` - If `true` is returned, the same permissions will be applied from the same + /// `_sender` without calling this contract again. + function allowedTxTypes( + address _sender, + address _to, + uint256 _value, + uint256 _gasPrice, + bytes memory _data + ) + public + view + returns(uint32 typesMask, bool cache) + { + if (_gasPrice > 0 || _data.length < 4) return (All, false); + return (None, false); + } +} diff --git a/ethcore/res/tx_permission_tests/contract_ver_3_genesis.json b/ethcore/res/tx_permission_tests/contract_ver_3_genesis.json new file mode 100644 index 0000000000..a40b6be05a --- /dev/null +++ b/ethcore/res/tx_permission_tests/contract_ver_3_genesis.json @@ -0,0 +1,44 @@ +{ + "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", + "transactionPermissionContractTransition": "1" + }, + "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": "6060604052341561000f57600080fd5b61035e8061001e6000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063469ab1e31461006757806375d0c0dc14610098578063a0a8e46014610126578063b9056afa1461014f575b600080fd5b341561007257600080fd5b61007a610227565b60405180826000191660001916815260200191505060405180910390f35b34156100a357600080fd5b6100ab610298565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100eb5780820151818401526020810190506100d0565b50505050905090810190601f1680156101185780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561013157600080fd5b6101396102db565b6040518082815260200191505060405180910390f35b341561015a57600080fd5b6101fa600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190803590602001909190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506102e4565b604051808363ffffffff1663ffffffff168152602001821515151581526020019250505060405180910390f35b6000610231610298565b6040518082805190602001908083835b6020831015156102665780518252602082019150602081019050602083039250610241565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020905090565b6102a061031e565b6040805190810160405280601681526020017f54585f5045524d495353494f4e5f434f4e545241435400000000000000000000815250905090565b60006003905090565b60008060008411806102f7575060048351105b1561030c5763ffffffff600091509150610314565b600080915091505b9550959350505050565b6020604051908101604052806000815250905600a165627a7a72305820be61565bc09fec6e9223a1fecd2e94783ca5c6f506c03f71d479a8c3285493310029" + } + } +} diff --git a/ethcore/service/Cargo.toml b/ethcore/service/Cargo.toml index c0be41038b..13f2c6c0c9 100644 --- a/ethcore/service/Cargo.toml +++ b/ethcore/service/Cargo.toml @@ -5,20 +5,23 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] -ansi_term = "0.10" -error-chain = { version = "0.12", default-features = false } +ansi_term = "0.11" +client-traits = { path = "../client-traits" } +common-types = { path = "../types" } ethcore = { path = ".." } ethcore-blockchain = { path = "../blockchain" } ethcore-io = { path = "../../util/io" } ethcore-private-tx = { path = "../private-tx" } ethcore-sync = { path = "../sync" } -ethereum-types = "0.4" -kvdb = "0.1" +ethereum-types = "0.8.0" +kvdb = "0.3.1" log = "0.4" +snapshot = { path = "../snapshot" } +spec = { path = "../spec" } trace-time = "0.1" [dev-dependencies] -ethcore-db = { path = "../db" } ethcore = { path = "..", features = ["test-helpers"] } +ethcore-db = { path = "../db" } +kvdb-rocksdb = "0.4.1" tempdir = "0.3" -kvdb-rocksdb = "0.1.3" diff --git a/ethcore/service/src/error.rs b/ethcore/service/src/error.rs deleted file mode 100644 index c73cb0dfc1..0000000000 --- a/ethcore/service/src/error.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -// Silence: `use of deprecated item 'std::error::Error::cause': replaced by Error::source, which can support downcasting` -// https://github.com/paritytech/parity-ethereum/issues/10302 -#![allow(deprecated)] - -use ethcore; -use io; -use ethcore_private_tx; - -error_chain! { - foreign_links { - Ethcore(ethcore::error::Error); - IoError(io::IoError); - PrivateTransactions(ethcore_private_tx::Error); - } -} diff --git a/ethcore/service/src/lib.rs b/ethcore/service/src/lib.rs index 7828fff8b4..61274e6fe5 100644 --- a/ethcore/service/src/lib.rs +++ b/ethcore/service/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,6 +15,8 @@ // along with Parity Ethereum. If not, see . extern crate ansi_term; +extern crate common_types; +extern crate client_traits; extern crate ethcore; extern crate ethcore_blockchain as blockchain; extern crate ethcore_io as io; @@ -22,9 +24,9 @@ extern crate ethcore_private_tx; extern crate ethcore_sync as sync; extern crate ethereum_types; extern crate kvdb; +extern crate spec; +extern crate snapshot; -#[macro_use] -extern crate error_chain; #[macro_use] extern crate log; #[macro_use] @@ -35,12 +37,9 @@ extern crate ethcore_db; #[cfg(test)] extern crate tempdir; -mod error; mod service; -mod stop_guard; #[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 0953970376..d2eac4a2e9 100644 --- a/ethcore/service/src/service.rs +++ b/ethcore/service/src/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,27 +16,30 @@ //! Creates and registers client and network services. -use std::sync::Arc; use std::path::Path; +use std::sync::Arc; use std::time::Duration; use ansi_term::Colour; use ethereum_types::H256; use io::{IoContext, TimerToken, IoHandler, IoService, IoError}; -use stop_guard::StopGuard; - +use client_traits::ChainNotify; use sync::PrivateTxHandler; use blockchain::{BlockChainDB, BlockChainDBHandler}; -use ethcore::client::{Client, ClientConfig, ChainNotify, ClientIoMessage}; +use ethcore::client::{Client, ClientConfig}; use ethcore::miner::Miner; -use ethcore::snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams}; -use ethcore::snapshot::{SnapshotService as _SnapshotService, RestorationStatus, Error as SnapshotError}; -use ethcore::spec::Spec; -use ethcore::error::{Error as EthcoreError, ErrorKind}; +use snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams}; +use snapshot::{SnapshotService as _SnapshotService, SnapshotClient}; +use spec::Spec; +use common_types::{ + io_message::ClientIoMessage, + errors::{EthcoreError, SnapshotError}, + snapshot::RestorationStatus, +}; +use client_traits::{ImportBlock, Tick}; use ethcore_private_tx::{self, Importer, Signer}; -use Error; pub struct PrivateTxService { provider: Arc, @@ -61,7 +64,7 @@ impl PrivateTxHandler for PrivateTxService { Ok(import_result) => Ok(import_result), Err(err) => { warn!(target: "privatetx", "Unable to import private transaction packet: {}", err); - bail!(err.to_string()) + return Err(err.to_string()) } } } @@ -71,7 +74,17 @@ impl PrivateTxHandler for PrivateTxService { Ok(import_result) => Ok(import_result), Err(err) => { warn!(target: "privatetx", "Unable to import signed private transaction packet: {}", err); - bail!(err.to_string()) + return Err(err.to_string()) + } + } + } + + fn private_state_synced(&self, hash: &H256) -> Result<(), String> { + match self.provider.private_state_synced(hash) { + Ok(handle_result) => Ok(handle_result), + Err(err) => { + warn!(target: "privatetx", "Unable to handle private state synced message: {}", err); + return Err(err.to_string()) } } } @@ -79,12 +92,11 @@ impl PrivateTxHandler for PrivateTxService { /// Client service setup. Creates and registers client and network services with the IO subsystem. pub struct ClientService { - io_service: Arc>, + io_service: Arc>>, client: Arc, - snapshot: Arc, + snapshot: Arc>, private_tx: Arc, - database: Arc, - _stop_guard: StopGuard, + database: Arc, } impl ClientService { @@ -92,20 +104,20 @@ impl ClientService { pub fn start( config: ClientConfig, spec: &Spec, - blockchain_db: Arc, + blockchain_db: Arc, snapshot_path: &Path, - restoration_db_handler: Box, + restoration_db_handler: Box, _ipc_path: &Path, miner: Arc, - signer: Arc, - encryptor: Box, + signer: Arc, + encryptor: Box, private_tx_conf: ethcore_private_tx::ProviderConfig, private_encryptor_conf: ethcore_private_tx::EncryptorConfig, - ) -> Result + ) -> Result { - let io_service = IoService::::start()?; + let io_service = IoService::>::start()?; - info!("Configured for {} using {} engine", Colour::White.bold().paint(spec.name.clone()), Colour::Yellow.bold().paint(spec.engine.name())); + info!("Configured for {} using {} engine", Colour::White.bold().paint(spec.name.clone()), Colour::Yellow.bold().paint(spec.engine.name().to_string())); let pruning = config.pruning; let client = Client::new( @@ -115,14 +127,15 @@ impl ClientService { miner.clone(), io_service.channel(), )?; + spec.engine.register_client(Arc::downgrade(&client) as _); miner.set_io_channel(io_service.channel()); miner.set_in_chain_checker(&client.clone()); let snapshot_params = SnapServiceParams { engine: spec.engine.clone(), genesis_block: spec.genesis_block(), - restoration_db_handler: restoration_db_handler, - pruning: pruning, + restoration_db_handler, + pruning, channel: io_service.channel(), snapshot_root: snapshot_path.into(), client: client.clone(), @@ -141,8 +154,10 @@ impl ClientService { private_tx_conf, io_service.channel(), private_keys, + blockchain_db.key_value().clone(), )); - let private_tx = Arc::new(PrivateTxService::new(provider)); + let private_tx = Arc::new(PrivateTxService::new(provider.clone())); + io_service.register_handler(provider)?; let client_io = Arc::new(ClientIoHandler { client: client.clone(), @@ -150,22 +165,17 @@ impl ClientService { }); io_service.register_handler(client_io)?; - spec.engine.register_client(Arc::downgrade(&client) as _); - - let stop_guard = StopGuard::new(); - Ok(ClientService { io_service: Arc::new(io_service), - client: client, - snapshot: snapshot, + client, + snapshot, private_tx, database: blockchain_db, - _stop_guard: stop_guard, }) } /// Get general IO interface - pub fn register_io_handler(&self, handler: Arc + Send>) -> Result<(), IoError> { + pub fn register_io_handler(&self, handler: Arc> + Send>) -> Result<(), IoError> { self.io_service.register_handler(handler) } @@ -175,7 +185,7 @@ impl ClientService { } /// Get snapshot interface. - pub fn snapshot_service(&self) -> Arc { + pub fn snapshot_service(&self) -> Arc> { self.snapshot.clone() } @@ -185,17 +195,17 @@ impl ClientService { } /// Get network service component - pub fn io(&self) -> Arc> { + pub fn io(&self) -> Arc>> { self.io_service.clone() } /// Set the actor to be notified on certain chain events - pub fn add_notify(&self, notify: Arc) { + pub fn add_notify(&self, notify: Arc) { self.client.add_notify(notify); } /// Get a handle to the database. - pub fn db(&self) -> Arc { self.database.clone() } + pub fn db(&self) -> Arc { self.database.clone() } /// Shutdown the Client Service pub fn shutdown(&self) { @@ -205,9 +215,9 @@ impl ClientService { } /// IO interface for the Client handler -struct ClientIoHandler { - client: Arc, - snapshot: Arc, +struct ClientIoHandler { + client: Arc, + snapshot: Arc>, } const CLIENT_TICK_TIMER: TimerToken = 0; @@ -216,17 +226,20 @@ const SNAPSHOT_TICK_TIMER: TimerToken = 1; 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) { +impl IoHandler> for ClientIoHandler +where + C: ImportBlock + SnapshotClient + Tick + 'static, +{ + fn initialize(&self, io: &IoContext>) { 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) { + fn timeout(&self, _io: &IoContext>, timer: TimerToken) { trace_time!("service::read"); match timer { CLIENT_TICK_TIMER => { - use ethcore::snapshot::SnapshotService; + use snapshot::SnapshotService; let snapshot_restoration = if let RestorationStatus::Ongoing{..} = self.snapshot.status() { true } else { false }; self.client.tick(snapshot_restoration) }, @@ -235,7 +248,7 @@ impl IoHandler for ClientIoHandler { } } - fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) { + fn message(&self, _io: &IoContext>, net_message: &ClientIoMessage) { trace_time!("service::message"); use std::thread; @@ -257,14 +270,12 @@ impl IoHandler for ClientIoHandler { ClientIoMessage::TakeSnapshot(num) => { let client = self.client.clone(); let snapshot = self.snapshot.clone(); - let res = thread::Builder::new().name("Periodic Snapshot".into()).spawn(move || { if let Err(e) = snapshot.take_snapshot(&*client, num) { match e { - EthcoreError(ErrorKind::Snapshot(SnapshotError::SnapshotAborted), _) => info!("Snapshot aborted"), + EthcoreError::Snapshot(SnapshotError::SnapshotAborted) => info!("Snapshot aborted"), _ => warn!("Failed to take snapshot at block #{}: {}", num, e), } - } }); @@ -282,6 +293,7 @@ impl IoHandler for ClientIoHandler { #[cfg(test)] mod tests { + use std::collections::HashMap; use std::sync::Arc; use std::{time, thread}; @@ -290,7 +302,7 @@ mod tests { use ethcore_db::NUM_COLUMNS; use ethcore::client::ClientConfig; use ethcore::miner::Miner; - use ethcore::spec::Spec; + use spec; use ethcore::test_helpers; use kvdb_rocksdb::{DatabaseConfig, CompactionProfile}; use super::*; @@ -303,17 +315,16 @@ mod tests { 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.memory_budget = HashMap::new(); client_db_config.compaction = CompactionProfile::auto(&client_path); let client_db_handler = test_helpers::restoration_db_handler(client_db_config.clone()); let client_db = client_db_handler.open(&client_path).unwrap(); let restoration_db_handler = test_helpers::restoration_db_handler(client_db_config); - let spec = Spec::new_test(); + let spec = spec::new_test(); let service = ClientService::start( ClientConfig::default(), &spec, diff --git a/ethcore/snapshot/Cargo.toml b/ethcore/snapshot/Cargo.toml new file mode 100644 index 0000000000..5d03048e9e --- /dev/null +++ b/ethcore/snapshot/Cargo.toml @@ -0,0 +1,66 @@ +[package] +description = "Take and restore snapshots of the blockchain and read/write it in chunks from/to disk" +name = "snapshot" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[[bench]] +name = "to_fat_rlps" +harness = false + +[dependencies] +account-db = { path = "../account-db" } +account-state = { path = "../account-state" } +blockchain = { package = "ethcore-blockchain", path = "../blockchain" } +bloom-journal = { package = "ethcore-bloom-journal", path = "../../util/bloom" } +bytes = { package = "parity-bytes", version = "0.1.0" } +client-traits = { path = "../client-traits" } +common-types = { path = "../types" } +crossbeam-utils = "0.6" +engine = { path = "../engine" } +ethcore-db = { path = "../db" } +ethcore-io = { path = "../../util/io" } +ethereum-types = "0.8.0" +ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +hash-db = "0.15.0" +itertools = "0.5" +journaldb = { path = "../../util/journaldb" } +keccak-hash = "0.4.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +kvdb = "0.3.1" +log = "0.4.8" +num_cpus = "1.10.1" +rand = "0.7" +rand_xorshift = "0.2" +parking_lot = "0.9" +rlp = "0.4.2" +rlp_derive = { path = "../../util/rlp-derive" } +scopeguard = "1.0.0" +snappy = { package = "parity-snappy", version ="0.1.0" } +state-db = { path = "../state-db" } +trie-db = "0.18.0" +triehash = { package = "triehash-ethereum", version = "0.2", path = "../../util/triehash-ethereum" } + +[dev-dependencies] +accounts = { package = "ethcore-accounts", path = "../../accounts" } +criterion = "0.3.0" +engine = { path = "../engine", features = ["test-helpers"] } +env_logger = "0.5" +ethabi = "9.0.1" +ethabi-contract = "9.0.0" +ethabi-derive = "9.0.1" +ethcore = { path = "..", features = ["test-helpers"] } +ethkey = { path = "../../accounts/ethkey" } +kvdb-rocksdb = "0.4.1" +lazy_static = { version = "1.3" } +spec = { path = "../spec" } +tempdir = "0.3" +trie-standardmap = "0.15.0" +# Note[dvdplm]: Ensure the snapshot tests are included in the dependency tree, which in turn means that +# `cargo test --all` runs the tests. +snapshot-tests = { path = "./snapshot-tests" } + +[features] +test-helpers = [] diff --git a/ethcore/snapshot/benches/state-chunk-5279-0x2032dfb6ad93f1928dac70627a8767d2232568a1a7bf1c91ea416988000f8275.rlp b/ethcore/snapshot/benches/state-chunk-5279-0x2032dfb6ad93f1928dac70627a8767d2232568a1a7bf1c91ea416988000f8275.rlp new file mode 100644 index 0000000000..aa39e4dc03 Binary files /dev/null and b/ethcore/snapshot/benches/state-chunk-5279-0x2032dfb6ad93f1928dac70627a8767d2232568a1a7bf1c91ea416988000f8275.rlp differ diff --git a/ethcore/snapshot/benches/state-chunk-5905-0x104ff12a3fda9e0cb1aeef41fe7092982134eb116292c0eec725c32a815ef0ea.rlp b/ethcore/snapshot/benches/state-chunk-5905-0x104ff12a3fda9e0cb1aeef41fe7092982134eb116292c0eec725c32a815ef0ea.rlp new file mode 100644 index 0000000000..1bca9bcaea Binary files /dev/null and b/ethcore/snapshot/benches/state-chunk-5905-0x104ff12a3fda9e0cb1aeef41fe7092982134eb116292c0eec725c32a815ef0ea.rlp differ diff --git a/ethcore/snapshot/benches/state-chunk-6341-0x3042ea62f982fd0cea02847ff0fd103a0beef3bb19389f5e77113c3ea355f803.rlp b/ethcore/snapshot/benches/state-chunk-6341-0x3042ea62f982fd0cea02847ff0fd103a0beef3bb19389f5e77113c3ea355f803.rlp new file mode 100644 index 0000000000..81465ba346 Binary files /dev/null and b/ethcore/snapshot/benches/state-chunk-6341-0x3042ea62f982fd0cea02847ff0fd103a0beef3bb19389f5e77113c3ea355f803.rlp differ diff --git a/ethcore/snapshot/benches/state-chunk-6720-0x2075481dccdc2c4419112bfea2d09219a7223614656722a1a05a930baf2b0dd7.rlp b/ethcore/snapshot/benches/state-chunk-6720-0x2075481dccdc2c4419112bfea2d09219a7223614656722a1a05a930baf2b0dd7.rlp new file mode 100644 index 0000000000..5265728cb8 Binary files /dev/null and b/ethcore/snapshot/benches/state-chunk-6720-0x2075481dccdc2c4419112bfea2d09219a7223614656722a1a05a930baf2b0dd7.rlp differ diff --git a/ethcore/snapshot/benches/state-chunk-6933-0x104102770901b53230e78cfc8f6edce282eb21bfa00aa1c3543c79cb3402cf2d.rlp b/ethcore/snapshot/benches/state-chunk-6933-0x104102770901b53230e78cfc8f6edce282eb21bfa00aa1c3543c79cb3402cf2d.rlp new file mode 100644 index 0000000000..53f5aad5b7 Binary files /dev/null and b/ethcore/snapshot/benches/state-chunk-6933-0x104102770901b53230e78cfc8f6edce282eb21bfa00aa1c3543c79cb3402cf2d.rlp differ diff --git a/ethcore/snapshot/benches/to_fat_rlps.rs b/ethcore/snapshot/benches/to_fat_rlps.rs new file mode 100644 index 0000000000..743d3d788e --- /dev/null +++ b/ethcore/snapshot/benches/to_fat_rlps.rs @@ -0,0 +1,94 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Benchmark snapshot::account::to_fat_rlps() which is a hot call during snapshots. + +use std::collections::HashSet; + +use account_db::AccountDB; +use common_types::{ + basic_account::BasicAccount, + snapshot::Progress +}; +use criterion::{Criterion, criterion_group, criterion_main, black_box}; +use ethcore::test_helpers::new_temp_db; +use ethereum_types::H256; +use parking_lot::RwLock; +use snapshot::test_helpers::to_fat_rlps; +use tempdir::TempDir; +use ethtrie::TrieDB; +use trie_db::Trie; + +fn fat_rlps(c: &mut Criterion) { + let tempdir = TempDir::new("").unwrap(); + let blockchain_db = new_temp_db(tempdir.path()); + + let mut state_rebuilder = snapshot::StateRebuilder::new(blockchain_db.key_value().clone(), journaldb::Algorithm::OverlayRecent); + + // Chunk data collected from mainnet/ropsten around blocks 8.7M/6.8M (end of Oct '19). The data + // sizes represent roughly the 99-percentile of account sizes. It takes some effort to find + // accounts of representative size that are self-contained (i.e. do not have code in other + // chunks). + let chunks = vec![ + // Ropsten + include_bytes!("./state-chunk-5279-0x2032dfb6ad93f1928dac70627a8767d2232568a1a7bf1c91ea416988000f8275.rlp").to_vec(), + // Ropsten + include_bytes!("./state-chunk-5905-0x104ff12a3fda9e0cb1aeef41fe7092982134eb116292c0eec725c32a815ef0ea.rlp").to_vec(), + // Ropsten + include_bytes!("./state-chunk-6341-0x3042ea62f982fd0cea02847ff0fd103a0beef3bb19389f5e77113c3ea355f803.rlp").to_vec(), + // Ropsten + include_bytes!("./state-chunk-6720-0x2075481dccdc2c4419112bfea2d09219a7223614656722a1a05a930baf2b0dd7.rlp").to_vec(), + // Mainnet + include_bytes!("./state-chunk-6933-0x104102770901b53230e78cfc8f6edce282eb21bfa00aa1c3543c79cb3402cf2d.rlp").to_vec(), + ]; + + let flag = std::sync::atomic::AtomicBool::new(true); + for chunk in &chunks { + state_rebuilder.feed(&chunk, &flag).expect("feed fail"); + } + let state_root = state_rebuilder.state_root(); + let journal_db = state_rebuilder.finalize(123, H256::random()).expect("finalize fail"); + let hashdb = journal_db.as_hash_db(); + let account_trie = TrieDB::new(&hashdb, &state_root).expect("triedb has our root"); + let account_iter = account_trie.iter().expect("there's a root in our trie"); + + for (idx, item) in account_iter.enumerate() { + let (account_key, account_data) = item.expect("data is the db is ok"); + let account_hash = H256::from_slice(&account_key); + let basic_account: BasicAccount = rlp::decode(&*account_data).expect("rlp from disk is ok"); + let account_db = AccountDB::from_hash(hashdb, account_hash); + let progress = RwLock::new(Progress::new()); + let mut used_code = HashSet::new(); + + let bench_name = format!("to_fat_rlps, {} bytes, ({})", chunks[idx].len(), account_hash); + c.bench_function(&bench_name, |b| { + b.iter(|| { + let _ = to_fat_rlps( + black_box(&account_hash), + black_box(&basic_account), + black_box(&account_db), + black_box(&mut used_code), + black_box(4194304), + black_box(4194304), + &progress + ); + }) + }); + } +} + +criterion_group!(benches, fat_rlps); +criterion_main!(benches); diff --git a/ethcore/snapshot/snapshot-tests/Cargo.toml b/ethcore/snapshot/snapshot-tests/Cargo.toml new file mode 100644 index 0000000000..1e3f2fb5be --- /dev/null +++ b/ethcore/snapshot/snapshot-tests/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "snapshot-tests" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +accounts = { package = "ethcore-accounts", path = "../../../accounts" } +account-db = { path = "../../account-db" } +account-state = { path = "../../account-state" } +blockchain = { package = "ethcore-blockchain", path = "../../blockchain" } +bytes = { package = "parity-bytes", version = "0.1.0" } +client-traits = { path = "../../client-traits" } +common-types = { path = "../../types" } +engine = { path = "../../engine", features = ["test-helpers"] } +env_logger = "0.5" +ethcore = { path = "../..", features = ["test-helpers"] } +ethcore-db = { path = "../../db" } +ethcore-io = { path = "../../../util/io" } +ethereum-types = "0.8.0" +ethtrie = { package = "patricia-trie-ethereum", path = "../../../util/patricia-trie-ethereum" } +hash-db = "0.15.0" +journaldb = { path = "../../../util/journaldb" } +keccak-hash = "0.4.0" +keccak-hasher = { path = "../../../util/keccak-hasher" } +kvdb = "0.3.1" +kvdb-rocksdb = "0.4.1" +log = "0.4.8" +parking_lot = "0.9" +parity-crypto = { version = "0.4.2", features = ["publickey"] } +rand = "0.7" +rand_xorshift = "0.2" +rlp = "0.4.2" +snappy = { package = "parity-snappy", version ="0.1.0" } +snapshot = { path = "../../snapshot", features = ["test-helpers"] } +spec = { path = "../../spec" } +tempdir = "0.3" +trie-db = "0.18.0" +trie-standardmap = "0.15.0" +ethabi = "9.0.1" +ethabi-contract = "9.0.0" +ethabi-derive = "9.0.1" +lazy_static = { version = "1.3" } +triehash = { package = "triehash-ethereum", version = "0.2", path = "../../../util/triehash-ethereum" } diff --git a/ethcore/snapshot/snapshot-tests/src/abridged_block.rs b/ethcore/snapshot/snapshot-tests/src/abridged_block.rs new file mode 100644 index 0000000000..715177bc5b --- /dev/null +++ b/ethcore/snapshot/snapshot-tests/src/abridged_block.rs @@ -0,0 +1,89 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Tests for block RLP encoding + +use snapshot::test_helpers::AbridgedBlock; + +use bytes::Bytes; +use ethereum_types::{H256, U256, Address}; +use common_types::{ + transaction::{Action, Transaction}, + block::Block, + view, + views::BlockView, +}; + +fn encode_block(b: &Block) -> Bytes { + b.rlp_bytes() +} + +#[test] +fn empty_block_abridging() { + let b = Block::default(); + let receipts_root = b.header.receipts_root().clone(); + let encoded = encode_block(&b); + + let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded)); + assert_eq!(abridged.to_block(H256::zero(), 0, receipts_root).unwrap(), b); +} + +#[test] +#[should_panic] +fn wrong_number() { + let b = Block::default(); + let receipts_root = b.header.receipts_root().clone(); + let encoded = encode_block(&b); + + let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded)); + assert_eq!(abridged.to_block(H256::zero(), 2, receipts_root).unwrap(), b); +} + +#[test] +fn with_transactions() { + let mut b = Block::default(); + + let t1 = Transaction { + action: Action::Create, + nonce: U256::from(42), + gas_price: U256::from(3000), + gas: U256::from(50_000), + value: U256::from(1), + data: b"Hello!".to_vec() + }.fake_sign(Address::from_low_u64_be(0x69)); + + let t2 = Transaction { + action: Action::Create, + nonce: U256::from(88), + gas_price: U256::from(12345), + gas: U256::from(300000), + value: U256::from(1000000000), + data: "Eep!".into(), + }.fake_sign(Address::from_low_u64_be(0x55)); + + b.transactions.push(t1.into()); + b.transactions.push(t2.into()); + + let receipts_root = b.header.receipts_root().clone(); + b.header.set_transactions_root(triehash::ordered_trie_root( + b.transactions.iter().map(::rlp::encode) + )); + + let encoded = encode_block(&b); + + let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded[..])); + assert_eq!(abridged.to_block(H256::zero(), 0, receipts_root).unwrap(), b); +} diff --git a/ethcore/snapshot/snapshot-tests/src/account.rs b/ethcore/snapshot/snapshot-tests/src/account.rs new file mode 100644 index 0000000000..ab39d773a8 --- /dev/null +++ b/ethcore/snapshot/snapshot-tests/src/account.rs @@ -0,0 +1,195 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Tests for account state encoding and decoding + +use std::collections::HashSet; + +use account_db::{AccountDB, AccountDBMut}; +use common_types::{ + basic_account::BasicAccount, + snapshot::Progress +}; +use ethcore::test_helpers::get_temp_state_db; +use ethereum_types::{H256, Address}; +use hash_db::{HashDB, EMPTY_PREFIX}; +use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; +use parking_lot::RwLock; +use rlp::Rlp; +use snapshot::test_helpers::{ACC_EMPTY, to_fat_rlps, from_fat_rlp}; + +use crate::helpers::fill_storage; + +#[test] +fn encoding_basic() { + let mut db = get_temp_state_db(); + let addr = Address::random(); + + let account = BasicAccount { + nonce: 50.into(), + balance: 123456789.into(), + storage_root: KECCAK_NULL_RLP, + code_hash: KECCAK_EMPTY, + code_version: 0.into(), + }; + + let thin_rlp = ::rlp::encode(&account); + assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); + let p = RwLock::new(Progress::new()); + let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); + let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap(); + assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, H256::zero()).unwrap().0, account); +} + +#[test] +fn encoding_version() { + let mut db = get_temp_state_db(); + let addr = Address::random(); + + let account = BasicAccount { + nonce: 50.into(), + balance: 123456789.into(), + storage_root: KECCAK_NULL_RLP, + code_hash: KECCAK_EMPTY, + code_version: 1.into(), + }; + + let thin_rlp = ::rlp::encode(&account); + assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); + let p = RwLock::new(Progress::new()); + let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); + let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap(); + assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, H256::zero()).unwrap().0, account); +} + +#[test] +fn encoding_storage() { + let mut db = get_temp_state_db(); + let addr = Address::random(); + + let account = { + let acct_db = AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)); + let mut root = KECCAK_NULL_RLP; + fill_storage(acct_db, &mut root, &mut H256::zero()); + BasicAccount { + nonce: 25.into(), + balance: 987654321.into(), + storage_root: root, + code_hash: KECCAK_EMPTY, + code_version: 0.into(), + } + }; + + let thin_rlp = ::rlp::encode(&account); + assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); + + let p = RwLock::new(Progress::new()); + + let fat_rlp = to_fat_rlps(&keccak(&addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); + let fat_rlp = Rlp::new(&fat_rlp[0]).at(1).unwrap(); + assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, H256::zero()).unwrap().0, account); +} + +#[test] +fn encoding_storage_split() { + let mut db = get_temp_state_db(); + let addr = Address::random(); + + let account = { + let acct_db = AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)); + let mut root = KECCAK_NULL_RLP; + fill_storage(acct_db, &mut root, &mut H256::zero()); + BasicAccount { + nonce: 25.into(), + balance: 987654321.into(), + storage_root: root, + code_hash: KECCAK_EMPTY, + code_version: 0.into(), + } + }; + + let thin_rlp = ::rlp::encode(&account); + assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); + + let p = RwLock::new(Progress::new()); + let fat_rlps = to_fat_rlps(&keccak(addr), &account, &AccountDB::from_hash(db.as_hash_db(), keccak(addr)), &mut Default::default(), 500, 1000, &p).unwrap(); + let mut root = KECCAK_NULL_RLP; + let mut restored_account = None; + for rlp in fat_rlps { + let fat_rlp = Rlp::new(&rlp).at(1).unwrap(); + restored_account = Some(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr)), fat_rlp, root).unwrap().0); + root = restored_account.as_ref().unwrap().storage_root.clone(); + } + assert_eq!(restored_account, Some(account)); +} + +#[test] +fn encoding_code() { + let mut db = get_temp_state_db(); + + let addr1 = Address::random(); + let addr2 = Address::random(); + + let code_hash = { + let mut acct_db = AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr1)); + acct_db.insert(EMPTY_PREFIX, b"this is definitely code") + }; + + { + let mut acct_db = AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr2)); + acct_db.emplace(code_hash.clone(), EMPTY_PREFIX, b"this is definitely code".to_vec()); + } + + let account1 = BasicAccount { + nonce: 50.into(), + balance: 123456789.into(), + storage_root: KECCAK_NULL_RLP, + code_hash, + code_version: 0.into(), + }; + + let account2 = BasicAccount { + nonce: 400.into(), + balance: 98765432123456789usize.into(), + storage_root: KECCAK_NULL_RLP, + code_hash, + code_version: 0.into(), + }; + + let mut used_code = HashSet::new(); + let p1 = RwLock::new(Progress::new()); + let p2 = RwLock::new(Progress::new()); + let fat_rlp1 = to_fat_rlps(&keccak(&addr1), &account1, &AccountDB::from_hash(db.as_hash_db(), keccak(addr1)), &mut used_code, usize::max_value(), usize::max_value(), &p1).unwrap(); + let fat_rlp2 = to_fat_rlps(&keccak(&addr2), &account2, &AccountDB::from_hash(db.as_hash_db(), keccak(addr2)), &mut used_code, usize::max_value(), usize::max_value(), &p2).unwrap(); + assert_eq!(used_code.len(), 1); + + 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::from_hash(db.as_hash_db_mut(), keccak(addr2)), fat_rlp2, H256::zero()).unwrap(); + assert!(maybe_code.is_none()); + assert_eq!(acc, account2); + + let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(addr1)), fat_rlp1, H256::zero()).unwrap(); + assert_eq!(maybe_code, Some(b"this is definitely code".to_vec())); + assert_eq!(acc, account1); +} + +#[test] +fn encoding_empty_acc() { + let mut db = get_temp_state_db(); + assert_eq!(from_fat_rlp(&mut AccountDBMut::from_hash(db.as_hash_db_mut(), keccak(Address::zero())), Rlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None)); +} diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/snapshot/snapshot-tests/src/helpers.rs similarity index 78% rename from ethcore/src/snapshot/tests/helpers.rs rename to ethcore/snapshot/snapshot-tests/src/helpers.rs index 817e024998..d32b50e9a0 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/snapshot/snapshot-tests/src/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,30 +17,41 @@ //! Snapshot test helpers. These are used to build blockchains and state tries //! which can be queried before and after a full snapshot/restore cycle. -extern crate trie_standardmap; - use std::sync::Arc; -use hash::{KECCAK_NULL_RLP}; +use std::sync::atomic::AtomicBool; use account_db::AccountDBMut; -use types::basic_account::BasicAccount; +use account_state; use blockchain::{BlockChain, BlockChainDB}; -use client::{Client, ChainInfo}; -use engines::EthEngine; -use snapshot::{StateRebuilder}; -use snapshot::io::{SnapshotReader, PackedWriter, PackedReader}; - -use tempdir::TempDir; -use rand::Rng; - -use kvdb::DBValue; +use client_traits::ChainInfo; +use common_types::{ + ids::BlockId, + basic_account::BasicAccount, + errors::EthcoreError, + snapshot::Progress, +}; +use engine::Engine; +use ethcore::client::Client; use ethereum_types::H256; +use ethtrie::{SecTrieDBMut, TrieDB, TrieDBMut}; use hash_db::HashDB; -use keccak_hasher::KeccakHasher; use journaldb; -use trie::{TrieMut, Trie}; -use ethtrie::{SecTrieDBMut, TrieDB, TrieDBMut}; -use self::trie_standardmap::{Alphabet, StandardMap, ValueMode}; +use keccak_hash::{KECCAK_NULL_RLP}; +use keccak_hasher::KeccakHasher; +use kvdb::DBValue; +use log::trace; +use parking_lot::RwLock; +use rand::Rng; +use rlp; +use snapshot::{ + SnapshotClient, + StateRebuilder, + io::{SnapshotReader, PackedWriter, PackedReader}, + chunker, +}; +use tempdir::TempDir; +use trie_db::{TrieMut, Trie}; +use trie_standardmap::{Alphabet, StandardMap, ValueMode}; // the proportion of accounts we will alter each tick. const ACCOUNT_CHURN: f32 = 0.01; @@ -62,7 +73,7 @@ impl StateProducer { /// Tick the state producer. This alters the state, writing new data into /// the database. - pub fn tick(&mut self, rng: &mut R, db: &mut HashDB) { + pub fn tick(&mut self, rng: &mut R, db: &mut dyn HashDB) { // modify existing accounts. let mut accounts_to_modify: Vec<_> = { let trie = TrieDB::new(&db, &self.state_root).unwrap(); @@ -77,10 +88,10 @@ 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).expect("error decoding basic account"); + 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)); + *account_data = rlp::encode(&account); } // sweep again to alter account trie. @@ -97,7 +108,7 @@ impl StateProducer { let address_hash = H256(rng.gen()); let balance: usize = rng.gen(); let nonce: usize = rng.gen(); - let acc = ::state::Account::new_basic(balance.into(), nonce.into()).rlp(); + let acc = account_state::Account::new_basic(balance.into(), nonce.into()).rlp(); trie.insert(&address_hash[..], &acc).unwrap(); } } @@ -124,7 +135,7 @@ pub fn fill_storage(mut db: AccountDBMut, root: &mut H256, seed: &mut H256) { SecTrieDBMut::from_existing(&mut db, root).unwrap() }; - for (k, v) in map.make_with(seed) { + for (k, v) in map.make_with(&mut seed.to_fixed_bytes()) { trie.insert(&k, &v).unwrap(); } } @@ -132,13 +143,11 @@ pub fn fill_storage(mut db: AccountDBMut, root: &mut H256, seed: &mut H256) { /// Take a snapshot from the given client into a temporary file. /// Return a snapshot reader for it. -pub fn snap(client: &Client) -> (Box, TempDir) { - use types::ids::BlockId; - +pub fn snap(client: &Client) -> (Box, TempDir) { let tempdir = TempDir::new("").unwrap(); let path = tempdir.path().join("file"); let writer = PackedWriter::new(&path).unwrap(); - let progress = Default::default(); + let progress = RwLock::new(Progress::new()); let hash = client.chain_info().best_block_hash; client.take_snapshot(writer, BlockId::Hash(hash), &progress).unwrap(); @@ -151,22 +160,19 @@ pub fn snap(client: &Client) -> (Box, TempDir) { /// Restore a snapshot into a given database. This will read chunks from the given reader /// write into the given database. pub fn restore( - db: Arc, - engine: &EthEngine, - reader: &SnapshotReader, + db: Arc, + engine: &dyn Engine, + reader: &dyn SnapshotReader, genesis: &[u8], -) -> Result<(), ::error::Error> { - use std::sync::atomic::AtomicBool; - use snappy; - +) -> Result<(), EthcoreError> { let flag = AtomicBool::new(true); - let components = engine.snapshot_components().unwrap(); + let chunker = chunker(engine.snapshot_mode()).expect("the engine used here supports snapshots"); let manifest = reader.manifest(); let mut state = StateRebuilder::new(db.key_value().clone(), journaldb::Algorithm::Archive); let mut secondary = { let chain = BlockChain::new(Default::default(), genesis, db.clone()); - components.rebuilder(chain, db, manifest).unwrap() + chunker.rebuilder(chain, db, manifest).unwrap() }; let mut snappy_buffer = Vec::new(); @@ -188,5 +194,5 @@ pub fn restore( trace!(target: "snapshot", "finalizing"); state.finalize(manifest.block_number, manifest.block_hash)?; - secondary.finalize(engine) + secondary.finalize() } diff --git a/ethcore/snapshot/snapshot-tests/src/io.rs b/ethcore/snapshot/snapshot-tests/src/io.rs new file mode 100644 index 0000000000..69952f6c1a --- /dev/null +++ b/ethcore/snapshot/snapshot-tests/src/io.rs @@ -0,0 +1,109 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Tests for snapshot i/o. + +use tempdir::TempDir; +use keccak_hash::keccak; + +use common_types::snapshot::ManifestData; +use snapshot::io::{ + SnapshotWriter,SnapshotReader, + PackedWriter, PackedReader, LooseWriter, LooseReader, + SNAPSHOT_VERSION, +}; + +const STATE_CHUNKS: &'static [&'static [u8]] = &[b"dog", b"cat", b"hello world", b"hi", b"notarealchunk"]; +const BLOCK_CHUNKS: &'static [&'static [u8]] = &[b"hello!", b"goodbye!", b"abcdefg", b"hijklmnop", b"qrstuvwxy", b"and", b"z"]; + +#[test] +fn packed_write_and_read() { + let tempdir = TempDir::new("").unwrap(); + let path = tempdir.path().join("packed"); + let mut writer = PackedWriter::new(&path).unwrap(); + + let mut state_hashes = Vec::new(); + let mut block_hashes = Vec::new(); + + for chunk in STATE_CHUNKS { + let hash = keccak(&chunk); + state_hashes.push(hash.clone()); + writer.write_state_chunk(hash, chunk).unwrap(); + } + + for chunk in BLOCK_CHUNKS { + let hash = keccak(&chunk); + block_hashes.push(hash.clone()); + writer.write_block_chunk(keccak(&chunk), chunk).unwrap(); + } + + let manifest = ManifestData { + version: SNAPSHOT_VERSION, + state_hashes, + block_hashes, + state_root: keccak(b"notarealroot"), + block_number: 12345678987654321, + block_hash: keccak(b"notarealblock"), + }; + + writer.finish(manifest.clone()).unwrap(); + + let reader = PackedReader::new(&path).unwrap().unwrap(); + assert_eq!(reader.manifest(), &manifest); + + for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) { + reader.chunk(hash.clone()).unwrap(); + } +} + +#[test] +fn loose_write_and_read() { + let tempdir = TempDir::new("").unwrap(); + let mut writer = LooseWriter::new(tempdir.path().into()).unwrap(); + + let mut state_hashes = Vec::new(); + let mut block_hashes = Vec::new(); + + for chunk in STATE_CHUNKS { + let hash = keccak(&chunk); + state_hashes.push(hash.clone()); + writer.write_state_chunk(hash, chunk).unwrap(); + } + + for chunk in BLOCK_CHUNKS { + let hash = keccak(&chunk); + block_hashes.push(hash.clone()); + writer.write_block_chunk(keccak(&chunk), chunk).unwrap(); + } + + let manifest = ManifestData { + version: SNAPSHOT_VERSION, + state_hashes, + block_hashes, + state_root: keccak(b"notarealroot"), + block_number: 12345678987654321, + block_hash: keccak(b"notarealblock)"), + }; + + writer.finish(manifest.clone()).unwrap(); + + let reader = LooseReader::new(tempdir.path().into()).unwrap(); + assert_eq!(reader.manifest(), &manifest); + + for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) { + reader.chunk(hash.clone()).unwrap(); + } +} diff --git a/ethcore/src/snapshot/tests/mod.rs b/ethcore/snapshot/snapshot-tests/src/lib.rs similarity index 60% rename from ethcore/src/snapshot/tests/mod.rs rename to ethcore/snapshot/snapshot-tests/src/lib.rs index f25fd03b25..26d603a279 100644 --- a/ethcore/src/snapshot/tests/mod.rs +++ b/ethcore/snapshot/snapshot-tests/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,25 +16,30 @@ //! Snapshot tests. +#![cfg(test)] + +mod abridged_block; +mod account; +mod io; mod proof_of_work; mod proof_of_authority; mod state; mod service; +mod watcher; -pub mod helpers; - -use super::ManifestData; +mod helpers; #[test] fn manifest_rlp() { - let manifest = ManifestData { - version: 2, - block_hashes: Vec::new(), - state_hashes: Vec::new(), - block_number: 1234567, - state_root: Default::default(), - block_hash: Default::default(), - }; - let raw = manifest.clone().into_rlp(); - assert_eq!(ManifestData::from_rlp(&raw).unwrap(), manifest); + use common_types::snapshot::ManifestData; + let manifest = ManifestData { + version: 2, + block_hashes: Vec::new(), + state_hashes: Vec::new(), + block_number: 1234567, + state_root: Default::default(), + block_hash: Default::default(), + }; + let raw = manifest.clone().into_rlp(); + assert_eq!(ManifestData::from_rlp(&raw).unwrap(), manifest); } diff --git a/ethcore/src/snapshot/tests/proof_of_authority.rs b/ethcore/snapshot/snapshot-tests/src/proof_of_authority.rs similarity index 92% rename from ethcore/src/snapshot/tests/proof_of_authority.rs rename to ethcore/snapshot/snapshot-tests/src/proof_of_authority.rs index f1610e6ccd..4dadf6cfba 100644 --- a/ethcore/src/snapshot/tests/proof_of_authority.rs +++ b/ethcore/snapshot/snapshot-tests/src/proof_of_authority.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,25 +21,32 @@ use std::sync::Arc; use std::str::FromStr; use accounts::AccountProvider; -use client::{Client, BlockChainClient, ChainInfo}; -use ethkey::Secret; -use snapshot::tests::helpers as snapshot_helpers; +use client_traits::{BlockChainClient, ChainInfo}; +use common_types::transaction::{Transaction, Action, SignedTransaction}; +use ethabi_contract::use_contract; +use ethcore::{ + client::Client, + test_helpers::{self, generate_dummy_client_with_spec}, + miner::{self, MinerService}, +}; +use ethereum_types::Address; +use parity_crypto::publickey::Secret; +use keccak_hash::keccak; +use lazy_static::lazy_static; +use log::trace; use spec::Spec; -use test_helpers::generate_dummy_client_with_spec; -use types::transaction::{Transaction, Action, SignedTransaction}; use tempdir::TempDir; -use ethereum_types::Address; -use test_helpers; +use crate::helpers as snapshot_helpers; -use_contract!(test_validator_set, "res/contracts/test_validator_set.json"); +use_contract!(test_validator_set, "../../res/contracts/test_validator_set.json"); const PASS: &'static str = ""; const TRANSITION_BLOCK_1: usize = 2; // block at which the contract becomes activated. const TRANSITION_BLOCK_2: usize = 10; // block at which the second contract activates. macro_rules! secret { - ($e: expr) => { Secret::from($crate::hash::keccak($e).0) } + ($e: expr) => { Secret::from(keccak($e).0) } } lazy_static! { @@ -99,8 +106,6 @@ fn make_chain(accounts: Arc, blocks_beyond: usize, transitions: { // push a block with given number, signed by one of the signers, with given transactions. let push_block = |signers: &[Address], n, txs: Vec| { - use miner::{self, MinerService}; - let idx = n as usize % signers.len(); trace!(target: "snapshot", "Pushing block #{}, {} txs, author={}", n, txs.len(), signers[idx]); @@ -126,7 +131,7 @@ fn make_chain(accounts: Arc, blocks_beyond: usize, transitions: nonce: *nonce, gas_price: 1.into(), gas: 21_000.into(), - action: Action::Call(Address::new()), + action: Action::Call(Address::zero()), value: 1.into(), data: Vec::new(), }.sign(&*RICH_SECRET, client.signing_chain_id()); diff --git a/ethcore/src/snapshot/tests/proof_of_work.rs b/ethcore/snapshot/snapshot-tests/src/proof_of_work.rs similarity index 79% rename from ethcore/src/snapshot/tests/proof_of_work.rs rename to ethcore/snapshot/snapshot-tests/src/proof_of_work.rs index d970da406c..0a1fb746a8 100644 --- a/ethcore/src/snapshot/tests/proof_of_work.rs +++ b/ethcore/snapshot/snapshot-tests/src/proof_of_work.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,19 +18,27 @@ use std::sync::atomic::AtomicBool; use tempdir::TempDir; -use error::{Error, ErrorKind}; - +use common_types::{ + errors::{EthcoreError as Error, SnapshotError}, + engines::ForkChoice, + snapshot::{Progress, ManifestData}, +}; use blockchain::generator::{BlockGenerator, BlockBuilder}; use blockchain::{BlockChain, ExtrasInsert}; -use snapshot::{chunk_secondary, Error as SnapshotError, Progress, SnapshotComponents}; -use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; - -use parking_lot::Mutex; +use snapshot::{ + chunk_secondary, + SnapshotComponents, + io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}, + PowSnapshot, +}; +use parking_lot::{Mutex, RwLock}; use snappy; +use keccak_hash::KECCAK_NULL_RLP; use kvdb::DBTransaction; -use test_helpers; +use ethcore::test_helpers; +use spec; -const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot { blocks: 30000, max_restore_blocks: 30000 }; +const SNAPSHOT_MODE: PowSnapshot = PowSnapshot { blocks: 30000, max_restore_blocks: 30000 }; fn chunk_and_restore(amount: u64) { let genesis = BlockBuilder::genesis(); @@ -38,7 +46,7 @@ fn chunk_and_restore(amount: u64) { let generator = BlockGenerator::new(vec![rest]); let genesis = genesis.last(); - let engine = ::spec::Spec::new_test().engine; + let engine = spec::new_test().engine; let tempdir = TempDir::new("").unwrap(); let snapshot_path = tempdir.path().join("SNAP"); @@ -49,7 +57,7 @@ fn chunk_and_restore(amount: u64) { let mut batch = DBTransaction::new(); for block in generator { bc.insert_block(&mut batch, block.encoded(), vec![], ExtrasInsert { - fork_choice: ::engines::ForkChoice::New, + fork_choice: ForkChoice::New, is_finalized: false, }); bc.commit(); @@ -66,14 +74,14 @@ fn chunk_and_restore(amount: u64) { &bc, best_hash, &writer, - &Progress::default() + &RwLock::new(Progress::new()) ).unwrap(); - let manifest = ::snapshot::ManifestData { + let manifest = ManifestData { version: 2, state_hashes: Vec::new(), - block_hashes: block_hashes, - state_root: ::hash::KECCAK_NULL_RLP, + block_hashes, + state_root: KECCAK_NULL_RLP, block_number: amount, block_hash: best_hash, }; @@ -93,7 +101,7 @@ fn chunk_and_restore(amount: u64) { rebuilder.feed(&chunk, engine.as_ref(), &flag).unwrap(); } - rebuilder.finalize(engine.as_ref()).unwrap(); + rebuilder.finalize().unwrap(); drop(rebuilder); // and test it. @@ -119,7 +127,7 @@ fn checks_flag() { let mut stream = RlpStream::new_list(5); stream.append(&100u64) - .append(&H256::default()) + .append(&H256::zero()) .append(&(!0u64)); stream.append_empty_data().append_empty_data(); @@ -128,22 +136,22 @@ fn checks_flag() { let chunk = stream.out(); let db = test_helpers::new_db(); - let engine = ::spec::Spec::new_test().engine; + let engine = spec::new_test().engine; let chain = BlockChain::new(Default::default(), genesis.last().encoded().raw(), db.clone()); - let manifest = ::snapshot::ManifestData { + let manifest = ManifestData { version: 2, state_hashes: Vec::new(), block_hashes: Vec::new(), - state_root: ::hash::KECCAK_NULL_RLP, + state_root: KECCAK_NULL_RLP, block_number: 102, - block_hash: H256::default(), + block_hash: H256::zero(), }; let mut rebuilder = SNAPSHOT_MODE.rebuilder(chain, db.clone(), &manifest).unwrap(); match rebuilder.feed(&chunk, engine.as_ref(), &AtomicBool::new(false)) { - Err(Error(ErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => {} + Err(Error::Snapshot(SnapshotError::RestorationAborted)) => {} _ => panic!("Wrong result on abort flag set") } } diff --git a/ethcore/src/snapshot/tests/service.rs b/ethcore/snapshot/snapshot-tests/src/service.rs similarity index 66% rename from ethcore/src/snapshot/tests/service.rs rename to ethcore/snapshot/snapshot-tests/src/service.rs index 515e5992ff..f7e3be3f91 100644 --- a/ethcore/src/snapshot/tests/service.rs +++ b/ethcore/snapshot/snapshot-tests/src/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,18 +21,119 @@ use std::sync::Arc; use tempdir::TempDir; use blockchain::BlockProvider; -use client::{Client, ClientConfig, ImportBlock, BlockInfo}; -use types::ids::BlockId; -use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; -use snapshot::service::{Service, ServiceParams}; -use snapshot::{chunk_state, chunk_secondary, ManifestData, Progress, SnapshotService, RestorationStatus}; -use spec::Spec; -use test_helpers::{new_db, new_temp_db, generate_dummy_client_with_spec_and_data, restoration_db_handler}; - -use parking_lot::Mutex; -use io::IoChannel; +use ethcore::client::{Client, ClientConfig}; +use client_traits::{BlockInfo, ImportBlock}; +use common_types::{ + io_message::ClientIoMessage, + ids::BlockId, + snapshot::Progress, + verification::Unverified, + snapshot::{ManifestData, RestorationStatus}, +}; +use snapshot::{ + chunk_state, chunk_secondary, SnapshotService, + io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}, + service::{Service, ServiceParams, Guard, Restoration, RestorationParams}, + PowSnapshot, +}; +use spec; +use ethcore::{ + miner, + test_helpers::{new_db, new_temp_db, generate_dummy_client_with_spec_and_data, restoration_db_handler} +}; + +use parking_lot::{Mutex, RwLock}; +use ethcore_io::{IoChannel, IoService}; use kvdb_rocksdb::DatabaseConfig; -use verification::queue::kind::blocks::Unverified; +use journaldb::Algorithm; + +#[test] +fn sends_async_messages() { + let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; + let client = generate_dummy_client_with_spec_and_data(spec::new_null, 400, 5, &gas_prices, false); + let service = IoService::>::start().unwrap(); + let spec = spec::new_test(); + + let tempdir = TempDir::new("").unwrap(); + let dir = tempdir.path().join("snapshot"); + + let snapshot_params = ServiceParams { + engine: spec.engine.clone(), + genesis_block: spec.genesis_block(), + restoration_db_handler: restoration_db_handler(Default::default()), + pruning: Algorithm::Archive, + channel: service.channel(), + snapshot_root: dir, + client, + }; + + let service = Service::new(snapshot_params).unwrap(); + + assert!(service.manifest().is_none()); + assert!(service.chunk(Default::default()).is_none()); + assert_eq!(service.status(), RestorationStatus::Inactive); + + let manifest = ManifestData { + version: 2, + state_hashes: vec![], + block_hashes: vec![], + state_root: Default::default(), + block_number: 0, + block_hash: Default::default(), + }; + + service.begin_restore(manifest); + service.abort_restore(); + service.restore_state_chunk(Default::default(), vec![]); + service.restore_block_chunk(Default::default(), vec![]); +} + +#[test] +fn cannot_finish_with_invalid_chunks() { + use ethereum_types::H256; + use kvdb_rocksdb::DatabaseConfig; + + let spec = spec::new_test(); + let tempdir = TempDir::new("").unwrap(); + + let state_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect(); + let block_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect(); + let db_config = DatabaseConfig::with_columns(ethcore_db::NUM_COLUMNS); + let gb = spec.genesis_block(); + let flag = ::std::sync::atomic::AtomicBool::new(true); + + let engine = &*spec.engine.clone(); + let params = RestorationParams::new( + ManifestData { + version: 2, + state_hashes: state_hashes.clone(), + block_hashes: block_hashes.clone(), + state_root: H256::zero(), + block_number: 100000, + block_hash: H256::zero(), + }, + Algorithm::Archive, + restoration_db_handler(db_config).open(&tempdir.path().to_owned()).unwrap(), + None, + &gb, + Guard::benign(), + engine, + ); + + let mut restoration = Restoration::new(params).unwrap(); + let definitely_bad_chunk = [1, 2, 3, 4, 5]; + + for hash in state_hashes { + assert!(restoration.feed_state(hash, &definitely_bad_chunk, &flag).is_err()); + assert!(!restoration.is_done()); + } + + for hash in block_hashes { + assert!(restoration.feed_blocks(hash, &definitely_bad_chunk, &*spec.engine, &flag).is_err()); + assert!(!restoration.is_done()); + } +} + #[test] fn restored_is_equivalent() { @@ -42,22 +143,22 @@ fn restored_is_equivalent() { const TX_PER: usize = 5; let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; - let client = generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, TX_PER, &gas_prices); + let client = generate_dummy_client_with_spec_and_data(spec::new_null, NUM_BLOCKS, TX_PER, &gas_prices, false); let tempdir = TempDir::new("").unwrap(); let client_db = tempdir.path().join("client_db"); let path = tempdir.path().join("snapshot"); - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let db_config = DatabaseConfig::with_columns(ethcore_db::NUM_COLUMNS); let restoration = restoration_db_handler(db_config); let blockchain_db = restoration.open(&client_db).unwrap(); - let spec = Spec::new_null(); + let spec = spec::new_null(); let client2 = Client::new( Default::default(), &spec, blockchain_db, - Arc::new(::miner::Miner::new_for_tests(&spec, None)), + Arc::new(miner::Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); @@ -72,7 +173,7 @@ fn restored_is_equivalent() { }; let service = Service::new(service_params).unwrap(); - service.take_snapshot(&client, NUM_BLOCKS as u64).unwrap(); + service.take_snapshot(&*client, NUM_BLOCKS as u64).unwrap(); let manifest = service.manifest().unwrap(); @@ -106,14 +207,14 @@ fn restored_is_equivalent() { #[test] fn guards_delete_folders() { let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; - let client = generate_dummy_client_with_spec_and_data(Spec::new_null, 400, 5, &gas_prices); + let client = generate_dummy_client_with_spec_and_data(spec::new_null, 400, 5, &gas_prices, false); - let spec = Spec::new_null(); + let spec = spec::new_null(); let tempdir = TempDir::new("").unwrap(); let service_params = ServiceParams { engine: spec.engine.clone(), genesis_block: spec.genesis_block(), - restoration_db_handler: restoration_db_handler(DatabaseConfig::with_columns(::db::NUM_COLUMNS)), + restoration_db_handler: restoration_db_handler(DatabaseConfig::with_columns(ethcore_db::NUM_COLUMNS)), pruning: ::journaldb::Algorithm::Archive, channel: IoChannel::disconnected(), snapshot_root: tempdir.path().to_owned(), @@ -152,11 +253,10 @@ fn guards_delete_folders() { #[test] fn keep_ancient_blocks() { let _ = ::env_logger::try_init(); - // Test variables const NUM_BLOCKS: u64 = 500; const NUM_SNAPSHOT_BLOCKS: u64 = 300; - const SNAPSHOT_MODE: ::snapshot::PowSnapshot = ::snapshot::PowSnapshot { blocks: NUM_SNAPSHOT_BLOCKS, max_restore_blocks: NUM_SNAPSHOT_BLOCKS }; + const SNAPSHOT_MODE: PowSnapshot = PowSnapshot { blocks: NUM_SNAPSHOT_BLOCKS, max_restore_blocks: NUM_SNAPSHOT_BLOCKS }; // Temporary folders let tempdir = TempDir::new("").unwrap(); @@ -164,9 +264,9 @@ fn keep_ancient_blocks() { // Generate blocks let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; - let spec_f = Spec::new_null; + let spec_f = spec::new_null; let spec = spec_f(); - let client = generate_dummy_client_with_spec_and_data(spec_f, NUM_BLOCKS as u32, 5, &gas_prices); + let client = generate_dummy_client_with_spec_and_data(spec_f, NUM_BLOCKS as u32, 5, &gas_prices, false); let bc = client.chain(); @@ -178,7 +278,7 @@ fn keep_ancient_blocks() { &bc, best_hash, &writer, - &Progress::default() + &RwLock::new(Progress::new()) ).unwrap(); let state_db = client.state_db().journal_db().boxed_clone(); let start_header = bc.block_header_data(&best_hash).unwrap(); @@ -187,12 +287,12 @@ fn keep_ancient_blocks() { state_db.as_hash_db(), &state_root, &writer, - &Progress::default(), + &RwLock::new(Progress::new()), None, 0 ).unwrap(); - let manifest = ::snapshot::ManifestData { + let manifest = ManifestData { version: 2, state_hashes, state_root, @@ -204,13 +304,13 @@ fn keep_ancient_blocks() { writer.into_inner().finish(manifest.clone()).unwrap(); // Initialize the Client - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let db_config = DatabaseConfig::with_columns(ethcore_db::NUM_COLUMNS); let client_db = new_temp_db(&tempdir.path()); let client2 = Client::new( ClientConfig::default(), &spec, client_db, - Arc::new(::miner::Miner::new_for_tests(&spec, None)), + Arc::new(miner::Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); @@ -221,7 +321,6 @@ fn keep_ancient_blocks() { client2.import_block(Unverified::from_rlp(block.into_inner()).unwrap()).unwrap(); } - client2.import_verified_blocks(); client2.flush_queue(); // Restore the Snapshot @@ -271,21 +370,21 @@ fn keep_ancient_blocks() { #[test] fn recover_aborted_recovery() { - let _ = ::env_logger::try_init(); + let _ = env_logger::try_init(); const NUM_BLOCKS: u32 = 400; let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; - let client = generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, 5, &gas_prices); + let client = generate_dummy_client_with_spec_and_data(spec::new_null, NUM_BLOCKS, 5, &gas_prices, false); - let spec = Spec::new_null(); + let spec = spec::new_null(); let tempdir = TempDir::new("").unwrap(); - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let db_config = DatabaseConfig::with_columns(ethcore_db::NUM_COLUMNS); let client_db = new_db(); let client2 = Client::new( Default::default(), &spec, client_db, - Arc::new(::miner::Miner::new_for_tests(&spec, None)), + Arc::new(miner::Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); let service_params = ServiceParams { @@ -299,7 +398,7 @@ fn recover_aborted_recovery() { }; let service = Service::new(service_params).unwrap(); - service.take_snapshot(&client, NUM_BLOCKS as u64).unwrap(); + service.take_snapshot(&*client, NUM_BLOCKS as u64).unwrap(); let manifest = service.manifest().unwrap(); service.init_restore(manifest.clone(), true).unwrap(); diff --git a/ethcore/src/snapshot/tests/state.rs b/ethcore/snapshot/snapshot-tests/src/state.rs similarity index 69% rename from ethcore/src/snapshot/tests/state.rs rename to ethcore/snapshot/snapshot-tests/src/state.rs index 0d97603324..e0602b976e 100644 --- a/ethcore/src/snapshot/tests/state.rs +++ b/ethcore/snapshot/snapshot-tests/src/state.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,30 +18,37 @@ use std::sync::Arc; use std::sync::atomic::AtomicBool; -use hash::{KECCAK_NULL_RLP, keccak}; -use types::basic_account::BasicAccount; -use snapshot::account; -use snapshot::{chunk_state, Error as SnapshotError, Progress, StateRebuilder, SNAPSHOT_SUBPARTS}; -use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}; -use super::helpers::StateProducer; - -use error::{Error, ErrorKind}; - -use rand::{XorShiftRng, SeedableRng}; +use keccak_hash::{KECCAK_NULL_RLP, keccak}; +use common_types::{ + basic_account::BasicAccount, + errors::{EthcoreError as Error, SnapshotError}, + snapshot::{ManifestData, Progress}, +}; +use snapshot::{ + test_helpers::to_fat_rlps, + chunk_state, StateRebuilder, SNAPSHOT_SUBPARTS, + io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter}, +}; +use rand::SeedableRng; +use rand_xorshift::XorShiftRng; use ethereum_types::H256; use journaldb::{self, Algorithm}; use kvdb_rocksdb::{Database, DatabaseConfig}; -use parking_lot::Mutex; +use parking_lot::{Mutex, RwLock}; use tempdir::TempDir; +use crate::helpers::StateProducer; + +const RNG_SEED: [u8; 16] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]; + #[test] fn snap_and_restore() { - use hash_db::HashDB; + use hash_db::{HashDB, EMPTY_PREFIX}; let mut producer = StateProducer::new(); - let mut rng = XorShiftRng::from_seed([1, 2, 3, 4]); + let mut rng = XorShiftRng::from_seed(RNG_SEED); let mut old_db = journaldb::new_memory_db(); - let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let db_cfg = DatabaseConfig::with_columns(ethcore_db::NUM_COLUMNS); for _ in 0..150 { producer.tick(&mut rng, &mut old_db); @@ -54,18 +61,19 @@ fn snap_and_restore() { let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap()); let mut state_hashes = Vec::new(); + let progress = RwLock::new(Progress::new()); for part in 0..SNAPSHOT_SUBPARTS { - let mut hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default(), Some(part), 0).unwrap(); + let mut hashes = chunk_state(&old_db, &state_root, &writer, &progress, Some(part), 0).unwrap(); state_hashes.append(&mut hashes); } - writer.into_inner().finish(::snapshot::ManifestData { + writer.into_inner().finish(ManifestData { version: 2, - state_hashes: state_hashes, + state_hashes, block_hashes: Vec::new(), - state_root: state_root, + state_root, block_number: 1000, - block_hash: H256::default(), + block_hash: H256::zero(), }).unwrap(); let db_path = tempdir.path().join("db"); @@ -78,23 +86,23 @@ fn snap_and_restore() { for chunk_hash in &reader.manifest().state_hashes { let raw = reader.chunk(*chunk_hash).unwrap(); - let chunk = ::snappy::decompress(&raw).unwrap(); + let chunk = snappy::decompress(&raw).unwrap(); rebuilder.feed(&chunk, &flag).unwrap(); } assert_eq!(rebuilder.state_root(), state_root); - rebuilder.finalize(1000, H256::default()).unwrap(); + rebuilder.finalize(1000, H256::zero()).unwrap(); new_db }; - let new_db = journaldb::new(db, Algorithm::OverlayRecent, ::db::COL_STATE); + let new_db = journaldb::new(db, Algorithm::OverlayRecent, ethcore_db::COL_STATE); assert_eq!(new_db.earliest_era(), Some(1000)); let keys = old_db.keys(); for key in keys.keys() { - assert_eq!(old_db.get(&key).unwrap(), new_db.as_hash_db().get(&key).unwrap()); + assert_eq!(old_db.get(&key, EMPTY_PREFIX).unwrap(), new_db.as_hash_db().get(&key, EMPTY_PREFIX).unwrap()); } } @@ -103,7 +111,7 @@ fn get_code_from_prev_chunk() { use std::collections::HashSet; use rlp::RlpStream; use ethereum_types::{H256, U256}; - use hash_db::HashDB; + use hash_db::{HashDB, EMPTY_PREFIX}; use account_db::{AccountDBMut, AccountDB}; @@ -125,9 +133,17 @@ fn get_code_from_prev_chunk() { let mut make_chunk = |acc, hash| { let mut db = journaldb::new_memory_db(); - AccountDBMut::from_hash(&mut db, hash).insert(&code[..]); - let p = Progress::default(); - let fat_rlp = account::to_fat_rlps(&hash, &acc, &AccountDB::from_hash(&db, hash), &mut used_code, usize::max_value(), usize::max_value(), &p).unwrap(); + AccountDBMut::from_hash(&mut db, hash).insert(EMPTY_PREFIX, &code[..]); + let p = RwLock::new(Progress::new()); + let fat_rlp = to_fat_rlps( + &hash, + &acc, + &AccountDB::from_hash(&db, hash), + &mut used_code, + usize::max_value(), + usize::max_value(), + &p + ).unwrap(); let mut stream = RlpStream::new_list(1); stream.append_raw(&fat_rlp[0], 1); stream.out() @@ -137,7 +153,7 @@ fn get_code_from_prev_chunk() { let chunk2 = make_chunk(acc, h2); let tempdir = TempDir::new("").unwrap(); - let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let db_cfg = DatabaseConfig::with_columns(ethcore_db::NUM_COLUMNS); let new_db = Arc::new(Database::open(&db_cfg, tempdir.path().to_str().unwrap()).unwrap()); { @@ -150,16 +166,16 @@ fn get_code_from_prev_chunk() { rebuilder.finalize(1000, H256::random()).unwrap(); } - let state_db = journaldb::new(new_db, Algorithm::OverlayRecent, ::db::COL_STATE); + let state_db = journaldb::new(new_db, Algorithm::OverlayRecent, ethcore_db::COL_STATE); assert_eq!(state_db.earliest_era(), Some(1000)); } #[test] fn checks_flag() { let mut producer = StateProducer::new(); - let mut rng = XorShiftRng::from_seed([5, 6, 7, 8]); + let mut rng = XorShiftRng::from_seed(RNG_SEED); let mut old_db = journaldb::new_memory_db(); - let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); + let db_cfg = DatabaseConfig::with_columns(ethcore_db::NUM_COLUMNS); for _ in 0..10 { producer.tick(&mut rng, &mut old_db); @@ -170,16 +186,17 @@ fn checks_flag() { let state_root = producer.state_root(); let writer = Mutex::new(PackedWriter::new(&snap_file).unwrap()); + let progress = RwLock::new(Progress::new()); - let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default(), None, 0).unwrap(); + let state_hashes = chunk_state(&old_db, &state_root, &writer, &progress, None, 0).unwrap(); - writer.into_inner().finish(::snapshot::ManifestData { + writer.into_inner().finish(ManifestData { version: 2, state_hashes, block_hashes: Vec::new(), state_root, block_number: 0, - block_hash: H256::default(), + block_hash: H256::zero(), }).unwrap(); let tempdir = TempDir::new("").unwrap(); @@ -193,10 +210,10 @@ fn checks_flag() { for chunk_hash in &reader.manifest().state_hashes { let raw = reader.chunk(*chunk_hash).unwrap(); - let chunk = ::snappy::decompress(&raw).unwrap(); + let chunk = snappy::decompress(&raw).unwrap(); match rebuilder.feed(&chunk, &flag) { - Err(Error(ErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => {}, + Err(Error::Snapshot(SnapshotError::RestorationAborted)) => {}, _ => panic!("unexpected result when feeding with flag off"), } } diff --git a/ethcore/src/snapshot/tests/test_validator_contract.json b/ethcore/snapshot/snapshot-tests/src/test_validator_contract.json similarity index 100% rename from ethcore/src/snapshot/tests/test_validator_contract.json rename to ethcore/snapshot/snapshot-tests/src/test_validator_contract.json diff --git a/ethcore/snapshot/snapshot-tests/src/watcher.rs b/ethcore/snapshot/snapshot-tests/src/watcher.rs new file mode 100644 index 0000000000..73dbe91319 --- /dev/null +++ b/ethcore/snapshot/snapshot-tests/src/watcher.rs @@ -0,0 +1,93 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Tests for block RLP encoding + +use std::collections::HashMap; +use std::time::Duration; + +use client_traits::ChainNotify; +use common_types::chain_notify::{NewBlocks, ChainRoute}; +use ethereum_types::{H256, U256, BigEndianHash}; + +use snapshot::{ + Broadcast, + Oracle, + test_helpers::Watcher, +}; + +struct TestOracle(HashMap); + +impl Oracle for TestOracle { + fn to_number(&self, hash: H256) -> Option { + self.0.get(&hash).cloned() + } + + fn is_major_importing(&self) -> bool { false } +} + +struct TestBroadcast(Option); +impl Broadcast for TestBroadcast { + fn request_snapshot_at(&self, num: u64) { + if Some(num) != self.0 { + panic!("Watcher broadcast wrong number. Expected {:?}, found {:?}", self.0, num); + } + } +} + +// 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| BigEndianHash::from_uint(&U256::from(x))).collect(); + let map = hashes.clone().into_iter().zip(numbers).collect(); + let watcher = Watcher::new_test( + Box::new(TestOracle(map)), + Box::new(TestBroadcast(expected)), + period, + history, + ); + + watcher.new_blocks(NewBlocks::new( + hashes, + vec![], + ChainRoute::default(), + vec![], + vec![], + DURATION_ZERO, + false + )); +} + +#[test] +fn should_not_fire() { + harness(vec![0], 5, 0, None); +} + +#[test] +fn fires_once_for_two() { + harness(vec![14, 15], 10, 5, Some(10)); +} + +#[test] +fn finds_highest() { + harness(vec![15, 25], 10, 5, Some(20)); +} + +#[test] +fn doesnt_fire_before_history() { + harness(vec![10, 11], 10, 5, None); +} diff --git a/ethcore/snapshot/src/account.rs b/ethcore/snapshot/src/account.rs new file mode 100644 index 0000000000..dd7650c321 --- /dev/null +++ b/ethcore/snapshot/src/account.rs @@ -0,0 +1,246 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Account state encoding and decoding + +use std::collections::HashSet; + +use account_db::{AccountDB, AccountDBMut}; +use bytes::Bytes; +use common_types::{ + basic_account::BasicAccount, + snapshot::Progress, + errors::SnapshotError as Error, +}; +use ethereum_types::{H256, U256}; +use ethtrie::{TrieDB, TrieDBMut}; +use hash_db::HashDB; +use keccak_hash::{KECCAK_EMPTY, KECCAK_NULL_RLP}; +use log::{trace, warn}; +use parking_lot::RwLock; +use rlp::{RlpStream, Rlp}; +use trie_db::{Trie, TrieMut}; + +// An empty account -- these were replaced with RLP null data for a space optimization in v1. +pub const ACC_EMPTY: BasicAccount = BasicAccount { + nonce: U256([0, 0, 0, 0]), + balance: U256([0, 0, 0, 0]), + storage_root: KECCAK_NULL_RLP, + code_hash: KECCAK_EMPTY, + code_version: U256([0, 0, 0, 0]), +}; + +// whether an encoded account has code and how it is referred to. +#[repr(u8)] +enum CodeState { + // the account has no code. + Empty = 0, + // raw code is encoded. + Inline = 1, + // the code is referred to by hash. + Hash = 2, +} + +impl CodeState { + fn from(x: u8) -> Result { + match x { + 0 => Ok(CodeState::Empty), + 1 => Ok(CodeState::Inline), + 2 => Ok(CodeState::Hash), + _ => Err(Error::UnrecognizedCodeState(x)) + } + } + + fn raw(self) -> u8 { + self as u8 + } +} + +// walk the account's storage trie, returning a vector of RLP items containing the +// account address hash, account properties and the storage. Each item contains at most `max_storage_items` +// storage records split according to snapshot format definition. +pub fn to_fat_rlps( + account_hash: &H256, + acc: &BasicAccount, + acct_db: &AccountDB, + used_code: &mut HashSet, + first_chunk_size: usize, + max_chunk_size: usize, + p: &RwLock, +) -> Result, Error> { + let db = &(acct_db as &dyn HashDB<_,_>); + let db = TrieDB::new(db, &acc.storage_root)?; + let mut chunks = Vec::new(); + let mut db_iter = db.iter()?; + let mut target_chunk_size = first_chunk_size; + let mut account_stream = RlpStream::new_list(2); + let mut leftover: Option> = None; + loop { + account_stream.append(account_hash); + let use_short_version = acc.code_version.is_zero(); + match use_short_version { + true => { account_stream.begin_list(5); }, + false => { account_stream.begin_list(6); }, + } + + account_stream.append(&acc.nonce) + .append(&acc.balance); + + // [has_code, code_hash]. + if acc.code_hash == KECCAK_EMPTY { + account_stream.append(&CodeState::Empty.raw()).append_empty_data(); + } else if used_code.contains(&acc.code_hash) { + account_stream.append(&CodeState::Hash.raw()).append(&acc.code_hash); + } else { + match acct_db.get(&acc.code_hash, hash_db::EMPTY_PREFIX) { + Some(c) => { + used_code.insert(acc.code_hash.clone()); + account_stream.append(&CodeState::Inline.raw()).append(&&*c); + } + None => { + warn!("code lookup failed during snapshot"); + account_stream.append(&false).append_empty_data(); + } + } + } + + if !use_short_version { + account_stream.append(&acc.code_version); + } + + account_stream.begin_unbounded_list(); + if account_stream.len() > target_chunk_size { + // account does not fit, push an empty record to mark a new chunk + target_chunk_size = max_chunk_size; + chunks.push(Vec::new()); + } + + if let Some(pair) = leftover.take() { + if !account_stream.append_raw_checked(&pair, 1, target_chunk_size) { + return Err(Error::ChunkTooSmall); + } + } + + loop { + if p.read().abort { + trace!(target: "snapshot", "to_fat_rlps: aborting snapshot"); + return Err(Error::SnapshotAborted); + } + match db_iter.next() { + Some(Ok((k, v))) => { + let pair = { + let mut stream = RlpStream::new_list(2); + stream.append(&k).append(&&*v); + stream.drain() + }; + if !account_stream.append_raw_checked(&pair, 1, target_chunk_size) { + account_stream.finalize_unbounded_list(); + let stream = ::std::mem::replace(&mut account_stream, RlpStream::new_list(2)); + chunks.push(stream.out()); + target_chunk_size = max_chunk_size; + leftover = Some(pair); + break; + } + }, + Some(Err(e)) => { + return Err(e.into()); + }, + None => { + account_stream.finalize_unbounded_list(); + let stream = ::std::mem::replace(&mut account_stream, RlpStream::new_list(2)); + chunks.push(stream.out()); + return Ok(chunks); + } + } + + } + } +} + +// decode a fat rlp, and rebuild the storage trie as we go. +// returns the account structure along with its newly recovered code, +// if it exists. +pub fn from_fat_rlp( + acct_db: &mut AccountDBMut, + rlp: Rlp, + mut storage_root: H256, +) -> Result<(BasicAccount, Option), Error> { + + // check for special case of empty account. + if rlp.is_empty() { + return Ok((ACC_EMPTY, None)); + } + + let use_short_version = match rlp.item_count()? { + 5 => true, + 6 => false, + _ => return Err(rlp::DecoderError::RlpIncorrectListLen.into()), + }; + + let nonce = rlp.val_at(0)?; + let balance = rlp.val_at(1)?; + let code_state: CodeState = { + let raw: u8 = rlp.val_at(2)?; + CodeState::from(raw)? + }; + + // load the code if it exists. + let (code_hash, new_code) = match code_state { + CodeState::Empty => (KECCAK_EMPTY, None), + CodeState::Inline => { + let code: Bytes = rlp.val_at(3)?; + let code_hash = acct_db.insert(hash_db::EMPTY_PREFIX, &code); + + (code_hash, Some(code)) + } + CodeState::Hash => { + let code_hash = rlp.val_at(3)?; + + (code_hash, None) + } + }; + + let code_version = if use_short_version { + U256::zero() + } else { + rlp.val_at(4)? + }; + + { + let mut storage_trie = if storage_root.is_zero() { + TrieDBMut::new(acct_db, &mut storage_root) + } else { + TrieDBMut::from_existing(acct_db, &mut storage_root)? + }; + let pairs = rlp.at(if use_short_version { 4 } else { 5 })?; + for pair_rlp in pairs.iter() { + let k: Bytes = pair_rlp.val_at(0)?; + let v: Bytes = pair_rlp.val_at(1)?; + + storage_trie.insert(&k, &v)?; + } + } + + let acc = BasicAccount { + nonce, + balance, + storage_root, + code_hash, + code_version, + }; + + Ok((acc, new_code)) +} diff --git a/ethcore/src/snapshot/block.rs b/ethcore/snapshot/src/block.rs similarity index 61% rename from ethcore/src/snapshot/block.rs rename to ethcore/snapshot/src/block.rs index 0fc590763c..3436f5a528 100644 --- a/ethcore/src/snapshot/block.rs +++ b/ethcore/snapshot/src/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,17 +17,20 @@ //! Block RLP compression. use bytes::Bytes; +use common_types::{ + block::Block, + header::Header, + views::BlockView, +}; use ethereum_types::H256; -use hash::keccak; +use keccak_hash::keccak; use rlp::{DecoderError, RlpStream, Rlp}; use triehash::ordered_trie_root; -use types::block::Block; -use types::header::Header; -use types::views::BlockView; const HEADER_FIELDS: usize = 8; const BLOCK_FIELDS: usize = 2; +/// Convenience type to convert raw RLP to and from blocks. pub struct AbridgedBlock { rlp: Bytes, } @@ -122,83 +125,6 @@ impl AbridgedBlock { header.set_seal(seal_fields); - Ok(Block { - header: header, - transactions: transactions, - uncles: uncles, - }) - } -} - -#[cfg(test)] -mod tests { - use super::AbridgedBlock; - - use bytes::Bytes; - use ethereum_types::{H256, U256, Address}; - use types::transaction::{Action, Transaction}; - use types::block::Block; - use types::view; - use types::views::BlockView; - - fn encode_block(b: &Block) -> Bytes { - b.rlp_bytes() - } - - #[test] - fn empty_block_abridging() { - let b = Block::default(); - let receipts_root = b.header.receipts_root().clone(); - let encoded = encode_block(&b); - - let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded)); - assert_eq!(abridged.to_block(H256::new(), 0, receipts_root).unwrap(), b); - } - - #[test] - #[should_panic] - fn wrong_number() { - let b = Block::default(); - let receipts_root = b.header.receipts_root().clone(); - let encoded = encode_block(&b); - - let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded)); - assert_eq!(abridged.to_block(H256::new(), 2, receipts_root).unwrap(), b); - } - - #[test] - fn with_transactions() { - let mut b = Block::default(); - - let t1 = Transaction { - action: Action::Create, - nonce: U256::from(42), - gas_price: U256::from(3000), - gas: U256::from(50_000), - value: U256::from(1), - data: b"Hello!".to_vec() - }.fake_sign(Address::from(0x69)); - - let t2 = Transaction { - action: Action::Create, - nonce: U256::from(88), - gas_price: U256::from(12345), - gas: U256::from(300000), - value: U256::from(1000000000), - data: "Eep!".into(), - }.fake_sign(Address::from(0x55)); - - b.transactions.push(t1.into()); - b.transactions.push(t2.into()); - - let receipts_root = b.header.receipts_root().clone(); - b.header.set_transactions_root(::triehash::ordered_trie_root( - b.transactions.iter().map(::rlp::encode) - )); - - let encoded = encode_block(&b); - - let abridged = AbridgedBlock::from_block_view(&view!(BlockView, &encoded[..])); - assert_eq!(abridged.to_block(H256::new(), 0, receipts_root).unwrap(), b); + Ok(Block { header, transactions, uncles }) } } diff --git a/ethcore/src/snapshot/consensus/authority.rs b/ethcore/snapshot/src/consensus/authority.rs similarity index 81% rename from ethcore/src/snapshot/consensus/authority.rs rename to ethcore/snapshot/src/consensus/authority.rs index 4423e07401..eb843a2e21 100644 --- a/ethcore/src/snapshot/consensus/authority.rs +++ b/ethcore/snapshot/src/consensus/authority.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,25 +19,30 @@ //! //! The chunks here contain state proofs of transitions, along with validator proofs. -use super::{SnapshotComponents, Rebuilder, ChunkSink}; - use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use engines::{EthEngine, EpochVerifier, EpochTransition}; -use machine::EthereumMachine; -use snapshot::{Error, ManifestData, Progress}; - use blockchain::{BlockChain, BlockChainDB, BlockProvider}; use bytes::Bytes; +use common_types::{ + encoded, + engines::epoch::Transition as EpochTransition, + header::Header, + ids::BlockId, + errors::{SnapshotError, EthcoreError}, + receipt::Receipt, + snapshot::{ChunkSink, Progress, ManifestData} +}; +use engine::{Engine, EpochVerifier}; use ethereum_types::{H256, U256}; use itertools::{Position, Itertools}; use kvdb::KeyValueDB; +use log::trace; +use parking_lot::RwLock; use rlp::{RlpStream, Rlp}; -use types::encoded; -use types::header::Header; -use types::ids::BlockId; -use types::receipt::Receipt; + +use crate::{SnapshotComponents, Rebuilder}; + /// Snapshot creation and restoration for PoA chains. /// Chunk format: @@ -58,11 +63,11 @@ impl SnapshotComponents for PoaSnapshot { chain: &BlockChain, block_at: H256, sink: &mut ChunkSink, - _progress: &Progress, + _progress: &RwLock, preferred_size: usize, - ) -> Result<(), Error> { + ) -> Result<(), SnapshotError> { let number = chain.block_number(&block_at) - .ok_or_else(|| Error::InvalidStartingBlock(BlockId::Hash(block_at)))?; + .ok_or_else(||SnapshotError::InvalidStartingBlock(BlockId::Hash(block_at)))?; let mut pending_size = 0; let mut rlps = Vec::new(); @@ -76,7 +81,7 @@ impl SnapshotComponents for PoaSnapshot { } let header = chain.block_header_data(&transition.block_hash) - .ok_or_else(|| Error::BlockNotFound(transition.block_hash))?; + .ok_or_else(||SnapshotError::BlockNotFound(transition.block_hash))?; let entry = { let mut entry_stream = RlpStream::new_list(2); @@ -101,12 +106,12 @@ 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_else(|| Error::BlockNotFound(block_at))?; + .ok_or_else(||SnapshotError::BlockNotFound(block_at))?; let block = block.decode()?; let parent_td = chain.block_details(block.header.parent_hash()) .map(|d| d.total_difficulty) - .ok_or_else(|| Error::BlockNotFound(block_at))?; + .ok_or_else(||SnapshotError::BlockNotFound(block_at))?; rlps.push({ let mut stream = RlpStream::new_list(5); @@ -127,13 +132,13 @@ impl SnapshotComponents for PoaSnapshot { fn rebuilder( &self, chain: BlockChain, - db: Arc, + db: Arc, manifest: &ManifestData, - ) -> Result, ::error::Error> { + ) -> Result, EthcoreError> { Ok(Box::new(ChunkRebuilder { manifest: manifest.clone(), warp_target: None, - chain: chain, + chain, db: db.key_value().clone(), had_genesis: false, unverified_firsts: Vec::new(), @@ -147,7 +152,7 @@ impl SnapshotComponents for PoaSnapshot { // writes a chunk composed of the inner RLPs here. // flag indicates whether the chunk is the last chunk. -fn write_chunk(last: bool, chunk_data: &mut Vec, sink: &mut ChunkSink) -> Result<(), Error> { +fn write_chunk(last: bool, chunk_data: &mut Vec, sink: &mut ChunkSink) -> Result<(), SnapshotError> { let mut stream = RlpStream::new_list(1 + chunk_data.len()); stream.append(&last); @@ -164,14 +169,14 @@ struct ChunkRebuilder { manifest: ManifestData, warp_target: Option
, chain: BlockChain, - db: Arc, + db: Arc, had_genesis: bool, // sorted vectors of unverified first blocks in a chunk // and epoch data from last blocks in chunks. // verification for these will be done at the end. unverified_firsts: Vec<(Header, Bytes, H256)>, - last_epochs: Vec<(Header, Box>)>, + last_epochs: Vec<(Header, Box)>, } // verified data. @@ -183,11 +188,11 @@ struct Verified { impl ChunkRebuilder { fn verify_transition( &mut self, - last_verifier: &mut Option>>, + last_verifier: &mut Option>, transition_rlp: Rlp, - engine: &EthEngine, - ) -> Result { - use engines::ConstructedVerifier; + engine: &dyn Engine, + ) -> Result { + use engine::ConstructedVerifier; // decode. let header: Header = transition_rlp.val_at(0)?; @@ -203,7 +208,7 @@ impl ChunkRebuilder { Some(ref last) => if last.check_finality_proof(finality_proof).map_or(true, |hashes| !hashes.contains(&hash)) { - return Err(Error::BadEpochProof(header.number()).into()); + return Err(SnapshotError::BadEpochProof(header.number()).into()); }, None if header.number() != 0 => { // genesis never requires additional validation. @@ -232,7 +237,7 @@ impl ChunkRebuilder { block_number: header.number(), proof: epoch_data, }, - header: header, + header, }) } } @@ -241,9 +246,9 @@ impl Rebuilder for ChunkRebuilder { fn feed( &mut self, chunk: &[u8], - engine: &EthEngine, + engine: &dyn Engine, abort_flag: &AtomicBool, - ) -> Result<(), ::error::Error> { + ) -> Result<(), EthcoreError> { let rlp = Rlp::new(chunk); let is_last_chunk: bool = rlp.val_at(0)?; let num_items = rlp.item_count()?; @@ -256,13 +261,13 @@ impl Rebuilder for ChunkRebuilder { }; if num_transitions == 0 && !is_last_chunk { - return Err(Error::WrongChunkFormat("Found non-last chunk without any data.".into()).into()); + return Err(SnapshotError::WrongChunkFormat("Found non-last chunk without any data.".into()).into()); } let mut last_verifier = None; let mut last_number = None; for transition_rlp in rlp.iter().skip(1).take(num_transitions).with_position() { - if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } + if !abort_flag.load(Ordering::SeqCst) { return Err(SnapshotError::RestorationAborted.into()) } let (is_first, is_last) = match transition_rlp { Position::First(_) => (true, false), @@ -279,7 +284,7 @@ impl Rebuilder for ChunkRebuilder { )?; if last_number.map_or(false, |num| verified.header.number() <= num) { - return Err(Error::WrongChunkFormat("Later epoch transition in earlier or same block.".into()).into()); + return Err(SnapshotError::WrongChunkFormat("Later epoch transition in earlier or same block.".into()).into()); } last_number = Some(verified.header.number()); @@ -290,7 +295,7 @@ impl Rebuilder for ChunkRebuilder { // but it doesn't need verification later. if verified.header.number() == 0 { if verified.header.hash() != self.chain.genesis_hash() { - return Err(Error::WrongBlockHash(0, verified.header.hash(), self.chain.genesis_hash()).into()); + return Err(SnapshotError::WrongBlockHash(0, verified.header.hash(), self.chain.genesis_hash()).into()); } self.had_genesis = true; @@ -318,7 +323,7 @@ impl Rebuilder for ChunkRebuilder { } if is_last_chunk { - use types::block::Block; + use common_types::block::Block; let last_rlp = rlp.at(num_items - 1)?; let block = Block { @@ -333,7 +338,7 @@ impl Rebuilder for ChunkRebuilder { let hash = block.header.hash(); let best_hash = self.manifest.block_hash; if hash != best_hash { - return Err(Error::WrongBlockHash(block.header.number(), best_hash, hash).into()) + return Err(SnapshotError::WrongBlockHash(block.header.number(), best_hash, hash).into()) } } @@ -349,16 +354,17 @@ impl Rebuilder for ChunkRebuilder { Ok(()) } - fn finalize(&mut self, _engine: &EthEngine) -> Result<(), ::error::Error> { + fn finalize(&mut self) -> Result<(), EthcoreError> { if !self.had_genesis { - return Err(Error::WrongChunkFormat("No genesis transition included.".into()).into()); + return Err(SnapshotError::WrongChunkFormat("No genesis transition included.".into()).into()); } let target_header = match self.warp_target.take() { Some(x) => x, - None => return Err(Error::WrongChunkFormat("Warp target block not included.".into()).into()), + None => return Err(SnapshotError::WrongChunkFormat("Warp target block not included.".into()).into()), }; + trace!(target: "snapshot", "rebuilder, finalize: verifying {} unverified first blocks", self.unverified_firsts.len()); // verify the first entries of chunks we couldn't before. // we store all last verifiers, but not all firsts. // match each unverified first epoch with a last epoch verifier. @@ -368,7 +374,7 @@ impl Rebuilder for ChunkRebuilder { while let Some(&(ref last_header, ref last_verifier)) = lasts_reversed.next() { if last_header.number() < header.number() { if last_verifier.check_finality_proof(&finality_proof).map_or(true, |hashes| !hashes.contains(&hash)) { - return Err(Error::BadEpochProof(header.number()).into()); + return Err(SnapshotError::BadEpochProof(header.number()).into()); } found = true; break; @@ -376,7 +382,7 @@ impl Rebuilder for ChunkRebuilder { } if !found { - return Err(Error::WrongChunkFormat("Inconsistent chunk ordering.".into()).into()); + return Err(SnapshotError::WrongChunkFormat("Inconsistent chunk ordering.".into()).into()); } } diff --git a/ethcore/snapshot/src/consensus/mod.rs b/ethcore/snapshot/src/consensus/mod.rs new file mode 100644 index 0000000000..a753441dc2 --- /dev/null +++ b/ethcore/snapshot/src/consensus/mod.rs @@ -0,0 +1,37 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Secondary chunk creation and restoration, implementations for different consensus +//! engines. + +mod authority; +mod work; + +pub use self::authority::*; +pub use self::work::*; + +use crate::SnapshotComponents; +use common_types::snapshot::Snapshotting::{self, *}; + +/// Create a factory for building snapshot chunks and restoring from them. +/// `None` indicates that the engine doesn't support snapshot creation. +pub fn chunker(snapshot_type: Snapshotting) -> Option> { + match snapshot_type { + PoA => Some(Box::new(PoaSnapshot)), + PoW { blocks, max_restore_blocks } => Some(Box::new(PowSnapshot::new(blocks, max_restore_blocks))), + Unsupported => None, + } +} diff --git a/ethcore/src/snapshot/consensus/work.rs b/ethcore/snapshot/src/consensus/work.rs similarity index 75% rename from ethcore/src/snapshot/consensus/work.rs rename to ethcore/snapshot/src/consensus/work.rs index 106fe4474c..e1715c026e 100644 --- a/ethcore/src/snapshot/consensus/work.rs +++ b/ethcore/snapshot/src/consensus/work.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,22 +20,33 @@ //! The secondary chunks in this instance are 30,000 "abridged blocks" from the head //! of the chain, which serve as an indication of valid chain. -use super::{SnapshotComponents, Rebuilder, ChunkSink}; - use std::collections::VecDeque; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use blockchain::{BlockChain, BlockChainDB, BlockProvider}; -use engines::EthEngine; -use snapshot::{Error, ManifestData, Progress}; -use snapshot::block::AbridgedBlock; -use ethereum_types::H256; -use kvdb::KeyValueDB; use bytes::Bytes; +use common_types::{ + encoded, + engines::epoch::Transition as EpochTransition, + errors::{SnapshotError, EthcoreError}, + snapshot::{ChunkSink, ManifestData, Progress}, + receipt::Receipt, +}; +use engine::Engine; +use ethereum_types::{H256, U256}; +use kvdb::KeyValueDB; +use log::trace; +use parking_lot::RwLock; +use rand::rngs::OsRng; use rlp::{RlpStream, Rlp}; -use rand::OsRng; -use types::encoded; +use triehash::ordered_trie_root; + +use crate::{ + SnapshotComponents, Rebuilder, + block::AbridgedBlock, + verify_old_block +}; /// Snapshot creation and restoration for PoW chains. /// This includes blocks from the head of the chain as a @@ -45,17 +56,14 @@ pub struct PowSnapshot { /// Number of blocks from the head of the chain /// to include in the snapshot. pub blocks: u64, - /// Number of to allow in the snapshot when restoring. + /// Number of blocks to allow in the snapshot when restoring. pub max_restore_blocks: u64, } impl PowSnapshot { /// Create a new instance. pub fn new(blocks: u64, max_restore_blocks: u64) -> PowSnapshot { - PowSnapshot { - blocks: blocks, - max_restore_blocks: max_restore_blocks, - } + PowSnapshot { blocks, max_restore_blocks } } } @@ -65,30 +73,35 @@ impl SnapshotComponents for PowSnapshot { chain: &BlockChain, block_at: H256, chunk_sink: &mut ChunkSink, - progress: &Progress, + progress: &RwLock, preferred_size: usize, - ) -> Result<(), Error> { + ) -> Result<(), SnapshotError> { PowWorker { - chain: chain, + chain, rlps: VecDeque::new(), current_hash: block_at, writer: chunk_sink, - progress: progress, - preferred_size: preferred_size, + progress, + preferred_size, }.chunk_all(self.blocks) } fn rebuilder( &self, chain: BlockChain, - db: Arc, + db: Arc, manifest: &ManifestData, - ) -> Result, ::error::Error> { - PowRebuilder::new(chain, db.key_value().clone(), manifest, self.max_restore_blocks).map(|r| Box::new(r) as Box<_>) + ) -> Result, EthcoreError> { + PowRebuilder::new( + chain, + db.key_value().clone(), + manifest, + self.max_restore_blocks + ).map(|r| Box::new(r) as Box<_>) } - fn min_supported_version(&self) -> u64 { ::snapshot::MIN_SUPPORTED_STATE_CHUNK_VERSION } - fn current_version(&self) -> u64 { ::snapshot::STATE_CHUNK_VERSION } + fn min_supported_version(&self) -> u64 { crate::MIN_SUPPORTED_STATE_CHUNK_VERSION } + fn current_version(&self) -> u64 { crate::STATE_CHUNK_VERSION } } /// Used to build block chunks. @@ -98,14 +111,14 @@ struct PowWorker<'a> { rlps: VecDeque, current_hash: H256, writer: &'a mut ChunkSink<'a>, - progress: &'a Progress, + progress: &'a RwLock, preferred_size: usize, } impl<'a> PowWorker<'a> { // Repeatedly fill the buffers and writes out chunks, moving backwards from starting block hash. // Loops until we reach the first desired block, and writes out the remainder. - fn chunk_all(&mut self, snapshot_blocks: u64) -> Result<(), Error> { + fn chunk_all(&mut self, snapshot_blocks: u64) -> Result<(), SnapshotError> { let mut loaded_size = 0; let mut last = self.current_hash; @@ -116,7 +129,7 @@ impl<'a> PowWorker<'a> { let (block, receipts) = self.chain.block(&self.current_hash) .and_then(|b| self.chain.block_receipts(&self.current_hash).map(|r| (b, r))) - .ok_or_else(|| Error::BlockNotFound(self.current_hash))?; + .ok_or_else(||SnapshotError::BlockNotFound(self.current_hash))?; let abridged_rlp = AbridgedBlock::from_block_view(&block.view()).into_inner(); @@ -141,7 +154,7 @@ impl<'a> PowWorker<'a> { last = self.current_hash; self.current_hash = block.header_view().parent_hash(); - self.progress.blocks.fetch_add(1, Ordering::SeqCst); + self.progress.write().blocks += 1; } if loaded_size != 0 { @@ -155,18 +168,18 @@ impl<'a> PowWorker<'a> { // // we preface each chunk with the parent of the first block's details, // obtained from the details of the last block written. - fn write_chunk(&mut self, last: H256) -> Result<(), Error> { + fn write_chunk(&mut self, last: H256) -> Result<(), SnapshotError> { trace!(target: "snapshot", "prepared block chunk with {} blocks", self.rlps.len()); 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_else(|| Error::BlockNotFound(last))?; + .ok_or_else(||SnapshotError::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(); - trace!(target: "snapshot", "parent last written block: {}", parent_hash); + trace!(target: "snapshot", "parent last written block: #{}/{}", parent_number, parent_hash); let num_entries = self.rlps.len(); let mut rlp_stream = RlpStream::new_list(3 + num_entries); @@ -194,7 +207,7 @@ impl<'a> PowWorker<'a> { /// After all chunks have been submitted, we "glue" the chunks together. pub struct PowRebuilder { chain: BlockChain, - db: Arc, + db: Arc, rng: OsRng, disconnected: Vec<(u64, H256)>, best_number: u64, @@ -206,17 +219,17 @@ pub struct PowRebuilder { impl PowRebuilder { /// Create a new PowRebuilder. - fn new(chain: BlockChain, db: Arc, manifest: &ManifestData, snapshot_blocks: u64) -> Result { + fn new(chain: BlockChain, db: Arc, manifest: &ManifestData, snapshot_blocks: u64) -> Result { Ok(PowRebuilder { - chain: chain, - db: db, - rng: OsRng::new()?, + chain, + db, + rng: OsRng, disconnected: Vec::new(), best_number: manifest.block_number, best_hash: manifest.block_hash, best_root: manifest.state_root, fed_blocks: 0, - snapshot_blocks: snapshot_blocks, + snapshot_blocks, }) } } @@ -224,11 +237,7 @@ impl PowRebuilder { 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 snapshot::verify_old_block; - use ethereum_types::U256; - use triehash::ordered_trie_root; - + fn feed(&mut self, chunk: &[u8], engine: &dyn Engine, abort_flag: &AtomicBool) -> Result<(), EthcoreError> { let rlp = Rlp::new(chunk); let item_count = rlp.item_count()?; let num_blocks = (item_count - 3) as u64; @@ -236,7 +245,7 @@ impl Rebuilder for PowRebuilder { 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()) + return Err(SnapshotError::TooManyBlocks(self.snapshot_blocks, self.fed_blocks + num_blocks).into()) } // todo: assert here that these values are consistent with chunks being in order. @@ -245,12 +254,12 @@ impl Rebuilder for PowRebuilder { let parent_total_difficulty = rlp.val_at::(2)?; for idx in 3..item_count { - if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } + if !abort_flag.load(Ordering::SeqCst) { return Err(SnapshotError::RestorationAborted.into()) } let pair = rlp.at(idx)?; let abridged_rlp = pair.at(0)?.as_raw().to_owned(); let abridged_block = AbridgedBlock::from_raw(abridged_rlp); - let receipts: Vec<::types::receipt::Receipt> = pair.list_at(1)?; + let receipts: Vec = pair.list_at(1)?; 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)?; @@ -259,11 +268,11 @@ impl Rebuilder for PowRebuilder { if is_best { if block.header.hash() != self.best_hash { - return Err(Error::WrongBlockHash(cur_number, self.best_hash, block.header.hash()).into()) + return Err(SnapshotError::WrongBlockHash(cur_number, self.best_hash, block.header.hash()).into()) } if block.header.state_root() != &self.best_root { - return Err(Error::WrongStateRoot(self.best_root, *block.header.state_root()).into()) + return Err(SnapshotError::WrongStateRoot(self.best_root, *block.header.state_root()).into()) } } @@ -298,9 +307,9 @@ impl Rebuilder for PowRebuilder { } /// Glue together any disconnected chunks and check that the chain is complete. - fn finalize(&mut self, _: &EthEngine) -> Result<(), ::error::Error> { + fn finalize(&mut self) -> Result<(), EthcoreError> { let mut batch = self.db.transaction(); - + trace!(target: "snapshot", "rebuilder, finalize: inserting {} disconnected chunks", self.disconnected.len()); for (first_num, first_hash) in self.disconnected.drain(..) { let parent_num = first_num - 1; @@ -314,7 +323,7 @@ impl Rebuilder for PowRebuilder { } let genesis_hash = self.chain.genesis_hash(); - self.chain.insert_epoch_transition(&mut batch, 0, ::engines::EpochTransition { + self.chain.insert_epoch_transition(&mut batch, 0, EpochTransition { block_number: 0, block_hash: genesis_hash, proof: vec![], diff --git a/ethcore/src/snapshot/io.rs b/ethcore/snapshot/src/io.rs similarity index 73% rename from ethcore/src/snapshot/io.rs rename to ethcore/snapshot/src/io.rs index 536862e7be..b3561fbfa6 100644 --- a/ethcore/src/snapshot/io.rs +++ b/ethcore/snapshot/src/io.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -26,27 +26,16 @@ use std::fs::{self, File}; use std::path::{Path, PathBuf}; use bytes::Bytes; +use common_types::{ + errors::{SnapshotError, EthcoreError}, + snapshot::ManifestData, +}; use ethereum_types::H256; +use log::trace; use rlp::{RlpStream, Rlp}; +use rlp_derive::*; -use super::ManifestData; - -const SNAPSHOT_VERSION: u64 = 2; - -/// Something which can write snapshots. -/// Writing the same chunk multiple times will lead to implementation-defined -/// behavior, and is not advised. -pub trait SnapshotWriter { - /// Write a compressed state chunk. - fn write_state_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()>; - - /// Write a compressed block chunk. - fn write_block_chunk(&mut self, hash: H256, chunk: &[u8]) -> io::Result<()>; - - /// Complete writing. The manifest's chunk lists must be consistent - /// with the chunks written. - fn finish(self, manifest: ManifestData) -> io::Result<()> where Self: Sized; -} +pub const SNAPSHOT_VERSION: u64 = 2; // (hash, len, offset) #[derive(RlpEncodable, RlpDecodable)] @@ -194,6 +183,21 @@ pub trait SnapshotReader { fn chunk(&self, hash: H256) -> io::Result; } +/// Something which can write snapshots. +/// Writing the same chunk multiple times will lead to implementation-defined +/// behavior, and is not advised. +pub trait SnapshotWriter { + /// Write a compressed state chunk. + fn write_state_chunk(&mut self, hash: H256, chunk: &[u8]) -> std::io::Result<()>; + + /// Write a compressed block chunk. + fn write_block_chunk(&mut self, hash: H256, chunk: &[u8]) -> std::io::Result<()>; + + /// Complete writing. The manifest's chunk lists must be consistent + /// with the chunks written. + fn finish(self, manifest: ManifestData) -> std::io::Result<()> where Self: Sized; +} + /// Packed snapshot reader. pub struct PackedReader { file: File, @@ -206,7 +210,7 @@ impl PackedReader { /// Create a new `PackedReader` for the file at the given path. /// This will fail if any io errors are encountered or the file /// is not a valid packed snapshot. - pub fn new(path: &Path) -> Result, ::snapshot::error::Error> { + pub fn new(path: &Path) -> Result, SnapshotError> { let mut file = File::open(path)?; let file_len = file.metadata()?.len(); if file_len < 8 { @@ -246,7 +250,7 @@ impl PackedReader { }; if version > SNAPSHOT_VERSION { - return Err(::snapshot::error::Error::VersionNotSupported(version)); + return Err(SnapshotError::VersionNotSupported(version)); } let state: Vec = rlp.list_at(0 + start)?; @@ -299,7 +303,7 @@ pub struct LooseReader { impl LooseReader { /// Create a new `LooseReader` which will read the manifest and chunk data from /// the given directory. - pub fn new(mut dir: PathBuf) -> Result { + pub fn new(mut dir: PathBuf) -> Result { let mut manifest_buf = Vec::new(); dir.push("MANIFEST"); @@ -327,94 +331,3 @@ impl SnapshotReader for LooseReader { Ok(buf) } } - -#[cfg(test)] -mod tests { - use tempdir::TempDir; - use hash::keccak; - - use snapshot::ManifestData; - use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader, SNAPSHOT_VERSION}; - - const STATE_CHUNKS: &'static [&'static [u8]] = &[b"dog", b"cat", b"hello world", b"hi", b"notarealchunk"]; - const BLOCK_CHUNKS: &'static [&'static [u8]] = &[b"hello!", b"goodbye!", b"abcdefg", b"hijklmnop", b"qrstuvwxy", b"and", b"z"]; - - #[test] - fn packed_write_and_read() { - let tempdir = TempDir::new("").unwrap(); - let path = tempdir.path().join("packed"); - let mut writer = PackedWriter::new(&path).unwrap(); - - let mut state_hashes = Vec::new(); - let mut block_hashes = Vec::new(); - - for chunk in STATE_CHUNKS { - let hash = keccak(&chunk); - state_hashes.push(hash.clone()); - writer.write_state_chunk(hash, chunk).unwrap(); - } - - for chunk in BLOCK_CHUNKS { - let hash = keccak(&chunk); - block_hashes.push(hash.clone()); - writer.write_block_chunk(keccak(&chunk), chunk).unwrap(); - } - - let manifest = ManifestData { - version: SNAPSHOT_VERSION, - state_hashes: state_hashes, - block_hashes: block_hashes, - state_root: keccak(b"notarealroot"), - block_number: 12345678987654321, - block_hash: keccak(b"notarealblock"), - }; - - writer.finish(manifest.clone()).unwrap(); - - let reader = PackedReader::new(&path).unwrap().unwrap(); - assert_eq!(reader.manifest(), &manifest); - - for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) { - reader.chunk(hash.clone()).unwrap(); - } - } - - #[test] - fn loose_write_and_read() { - let tempdir = TempDir::new("").unwrap(); - let mut writer = LooseWriter::new(tempdir.path().into()).unwrap(); - - let mut state_hashes = Vec::new(); - let mut block_hashes = Vec::new(); - - for chunk in STATE_CHUNKS { - let hash = keccak(&chunk); - state_hashes.push(hash.clone()); - writer.write_state_chunk(hash, chunk).unwrap(); - } - - for chunk in BLOCK_CHUNKS { - let hash = keccak(&chunk); - block_hashes.push(hash.clone()); - writer.write_block_chunk(keccak(&chunk), chunk).unwrap(); - } - - let manifest = ManifestData { - version: SNAPSHOT_VERSION, - state_hashes: state_hashes, - block_hashes: block_hashes, - state_root: keccak(b"notarealroot"), - block_number: 12345678987654321, - block_hash: keccak(b"notarealblock)"), - }; - - writer.finish(manifest.clone()).unwrap(); - - let reader = LooseReader::new(tempdir.path().into()).unwrap(); - assert_eq!(reader.manifest(), &manifest); - - for hash in manifest.state_hashes.iter().chain(&manifest.block_hashes) { - reader.chunk(hash.clone()).unwrap(); - } - } -} diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/snapshot/src/lib.rs similarity index 81% rename from ethcore/src/snapshot/mod.rs rename to ethcore/snapshot/src/lib.rs index 79b4eae949..4c119d5599 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/snapshot/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,60 +22,62 @@ use std::collections::{HashMap, HashSet}; use std::cmp; use std::sync::Arc; -use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; -use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY}; +use std::sync::atomic::{AtomicBool, Ordering}; + +use keccak_hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY}; use account_db::{AccountDB, AccountDBMut}; +use account_state::Account as StateAccount; use blockchain::{BlockChain, BlockProvider}; -use engines::EthEngine; -use types::header::Header; -use types::ids::BlockId; - +use bloom_journal::Bloom; +use bytes::Bytes; +use common_types::{ + ids::BlockId, + header::Header, + errors::{SnapshotError as Error, EthcoreError}, + snapshot::{Progress, ManifestData}, +}; +use crossbeam_utils::thread; +use engine::Engine; use ethereum_types::{H256, U256}; +use ethtrie::{TrieDB, TrieDBMut}; use hash_db::HashDB; -use keccak_hasher::KeccakHasher; -use snappy; -use bytes::Bytes; -use parking_lot::Mutex; use journaldb::{self, Algorithm, JournalDB}; +use keccak_hasher::KeccakHasher; +use parking_lot::{Mutex, RwLock}; use kvdb::{KeyValueDB, DBValue}; -use trie::{Trie, TrieMut}; -use ethtrie::{TrieDB, TrieDBMut}; -use rlp::{RlpStream, Rlp}; -use bloom_journal::Bloom; +use log::{debug, info, trace}; use num_cpus; - -use self::io::SnapshotWriter; - -use super::state_db::StateDB; -use super::state::Account as StateAccount; - -use crossbeam_utils::thread; -use rand::{Rng, OsRng}; - -pub use self::error::Error; +use rand::{Rng, rngs::OsRng}; +use rlp::{RlpStream, Rlp}; +use snappy; +use state_db::StateDB; +use trie_db::{Trie, TrieMut}; pub use self::consensus::*; -pub use self::service::{SnapshotClient, Service, DatabaseRestore}; -pub use self::traits::SnapshotService; +pub use self::service::{Service, Guard, Restoration, RestorationParams}; +pub use self::traits::{Broadcast, Oracle, SnapshotService, SnapshotClient, SnapshotComponents, Rebuilder}; +pub use self::io::SnapshotWriter; pub use self::watcher::Watcher; -pub use types::snapshot_manifest::ManifestData; -pub use types::restoration_status::RestorationStatus; -pub use types::basic_account::BasicAccount; +use common_types::basic_account::BasicAccount; pub mod io; pub mod service; +#[cfg(feature = "test-helpers" )] +pub mod test_helpers { + pub use super::{ + account::{ACC_EMPTY, to_fat_rlps, from_fat_rlp}, + block::AbridgedBlock, + watcher::Watcher, + }; +} + mod account; mod block; mod consensus; -mod error; -mod watcher; - -#[cfg(test)] -mod tests; - mod traits; +mod watcher; // Try to have chunks be around 4MB (before compression) const PREFERRED_CHUNK_SIZE: usize = 4 * 1024 * 1024; @@ -90,7 +92,7 @@ const MIN_SUPPORTED_STATE_CHUNK_VERSION: u64 = 1; // current state chunk version. const STATE_CHUNK_VERSION: u64 = 2; /// number of snapshot subparts, must be a power of 2 in [1; 256] -const SNAPSHOT_SUBPARTS: usize = 16; +pub const SNAPSHOT_SUBPARTS: usize = 16; /// Maximum number of snapshot subparts (must be a multiple of `SNAPSHOT_SUBPARTS`) const MAX_SNAPSHOT_SUBPARTS: usize = 256; @@ -112,42 +114,6 @@ impl Default for SnapshotConfiguration { } } -/// A progress indicator for snapshots. -#[derive(Debug, Default)] -pub struct Progress { - accounts: AtomicUsize, - blocks: AtomicUsize, - size: AtomicU64, - done: AtomicBool, - abort: AtomicBool, -} - -impl Progress { - /// Reset the progress. - pub fn reset(&self) { - self.accounts.store(0, Ordering::Release); - self.blocks.store(0, Ordering::Release); - self.size.store(0, Ordering::Release); - self.abort.store(false, Ordering::Release); - - // atomic fence here to ensure the others are written first? - // logs might very rarely get polluted if not. - self.done.store(false, Ordering::Release); - } - - /// Get the number of accounts snapshotted thus far. - pub fn accounts(&self) -> usize { self.accounts.load(Ordering::Acquire) } - - /// Get the number of blocks snapshotted thus far. - pub fn blocks(&self) -> usize { self.blocks.load(Ordering::Acquire) } - - /// Get the written size of the snapshot in bytes. - pub fn size(&self) -> u64 { self.size.load(Ordering::Acquire) } - - /// Whether the snapshot is complete. - pub fn done(&self) -> bool { self.done.load(Ordering::Acquire) } - -} /// Take a snapshot using the given blockchain, starting block hash, and database, writing into the given writer. pub fn take_snapshot( chunker: Box, @@ -155,7 +121,7 @@ pub fn take_snapshot( block_hash: H256, state_db: &dyn HashDB, writer: W, - p: &Progress, + p: &RwLock, processing_threads: usize, ) -> Result<(), Error> { let start_header = chain.block_header_data(&block_hash) @@ -163,35 +129,34 @@ pub fn take_snapshot( let state_root = start_header.state_root(); let block_number = start_header.number(); - info!("Taking snapshot starting at block {}", block_number); - + info!("Taking snapshot starting at block #{}/{:?}", block_number, block_hash); let version = chunker.current_version(); let writer = Mutex::new(writer); let (state_hashes, block_hashes) = thread::scope(|scope| -> Result<(Vec, Vec), Error> { let writer = &writer; - let block_guard = scope.spawn(move |_| { + let tb = scope.builder().name("Snapshot Worker - Blocks".to_string()); + let block_guard = tb.spawn(move |_| { chunk_secondary(chunker, chain, block_hash, writer, p) - }); + })?; // The number of threads must be between 1 and SNAPSHOT_SUBPARTS assert!(processing_threads >= 1, "Cannot use less than 1 threads for creating snapshots"); - let num_threads: usize = cmp::min(processing_threads, SNAPSHOT_SUBPARTS); + let num_threads = cmp::min(processing_threads, SNAPSHOT_SUBPARTS); info!(target: "snapshot", "Using {} threads for Snapshot creation.", num_threads); - let mut state_guards = Vec::with_capacity(num_threads as usize); + let mut state_guards = Vec::with_capacity(num_threads); for thread_idx in 0..num_threads { - let state_guard = scope.spawn(move |_| -> Result, Error> { + let tb = scope.builder().name(format!("Snapshot Worker #{} - State", thread_idx).to_string()); + let state_guard = tb.spawn(move |_| -> Result, Error> { let mut chunk_hashes = Vec::new(); - for part in (thread_idx..SNAPSHOT_SUBPARTS).step_by(num_threads) { - debug!(target: "snapshot", "Chunking part {} in thread {}", part, thread_idx); + debug!(target: "snapshot", "Chunking part {} of the state at {} in thread {}", part, block_number, thread_idx); let mut hashes = chunk_state(state_db, &state_root, writer, p, Some(part), thread_idx)?; chunk_hashes.append(&mut hashes); } - Ok(chunk_hashes) - }); + })?; state_guards.push(state_guard); } @@ -203,7 +168,8 @@ pub fn take_snapshot( state_hashes.extend(part_state_hashes); } - debug!(target: "snapshot", "Took a snapshot of {} accounts", p.accounts.load(Ordering::SeqCst)); + info!("Took a snapshot at #{} of {} accounts", block_number, p.read().accounts()); + Ok((state_hashes, block_hashes)) }).expect("Sub-thread never panics; qed")?; @@ -220,7 +186,7 @@ pub fn take_snapshot( writer.into_inner().finish(manifest_data)?; - p.done.store(true, Ordering::SeqCst); + p.write().done = true; Ok(()) } @@ -236,7 +202,7 @@ pub fn chunk_secondary<'a>( chain: &'a BlockChain, start_hash: H256, writer: &Mutex, - progress: &'a Progress + progress: &'a RwLock ) -> Result, Error> { let mut chunk_hashes = Vec::new(); let mut snappy_buffer = vec![0; snappy::max_compressed_len(PREFERRED_CHUNK_SIZE)]; @@ -252,7 +218,7 @@ pub fn chunk_secondary<'a>( trace!(target: "snapshot", "wrote secondary chunk. hash: {:x}, size: {}, uncompressed size: {}", hash, size, raw_data.len()); - progress.size.fetch_add(size as u64, Ordering::SeqCst); + progress.write().update(0, size as u64); chunk_hashes.push(hash); Ok(()) }; @@ -276,7 +242,7 @@ struct StateChunker<'a> { cur_size: usize, snappy_buffer: Vec, writer: &'a Mutex, - progress: &'a Progress, + progress: &'a RwLock, thread_idx: usize, } @@ -309,8 +275,7 @@ impl<'a> StateChunker<'a> { self.writer.lock().write_state_chunk(hash, compressed)?; trace!(target: "snapshot", "Thread {} wrote state chunk. size: {}, uncompressed size: {}", self.thread_idx, compressed_size, raw_data.len()); - self.progress.accounts.fetch_add(num_entries, Ordering::SeqCst); - self.progress.size.fetch_add(compressed_size as u64, Ordering::SeqCst); + self.progress.write().update(num_entries as u64, compressed_size as u64); self.hashes.push(hash); self.cur_size = 0; @@ -335,7 +300,7 @@ pub fn chunk_state<'a>( db: &dyn HashDB, root: &H256, writer: &Mutex, - progress: &'a Progress, + progress: &'a RwLock, part: Option, thread_idx: usize, ) -> Result, Error> { @@ -361,7 +326,7 @@ pub fn chunk_state<'a>( if let Some(part) = part { assert!(part < 16, "Wrong chunk state part number (must be <16) in snapshot creation."); - let part_offset = MAX_SNAPSHOT_SUBPARTS / SNAPSHOT_SUBPARTS; + let part_offset = MAX_SNAPSHOT_SUBPARTS / SNAPSHOT_SUBPARTS; // 16 let mut seek_from = vec![0; 32]; seek_from[0] = (part * part_offset) as u8; account_iter.seek(&seek_from)?; @@ -383,7 +348,15 @@ pub fn chunk_state<'a>( let account = ::rlp::decode(&*account_data)?; let account_db = AccountDB::from_hash(db, account_key_hash); - let fat_rlps = account::to_fat_rlps(&account_key_hash, &account, &account_db, &mut used_code, PREFERRED_CHUNK_SIZE - chunker.chunk_size(), PREFERRED_CHUNK_SIZE, progress)?; + let fat_rlps = account::to_fat_rlps( + &account_key_hash, + &account, + &account_db, + &mut used_code, + PREFERRED_CHUNK_SIZE - chunker.chunk_size(), + PREFERRED_CHUNK_SIZE, + progress + )?; for (i, fat_rlp) in fat_rlps.into_iter().enumerate() { if i > 0 { chunker.write_chunk()?; @@ -413,7 +386,7 @@ impl StateRebuilder { /// Create a new state rebuilder to write into the given backing DB. pub fn new(db: Arc, pruning: Algorithm) -> Self { StateRebuilder { - db: journaldb::new(db.clone(), pruning, ::db::COL_STATE), + db: journaldb::new(db.clone(), pruning, ethcore_db::COL_STATE), state_root: KECCAK_NULL_RLP, known_code: HashMap::new(), missing_code: HashMap::new(), @@ -423,7 +396,7 @@ impl StateRebuilder { } /// Feed an uncompressed state chunk into the rebuilder. - pub fn feed(&mut self, chunk: &[u8], flag: &AtomicBool) -> Result<(), ::error::Error> { + pub fn feed(&mut self, chunk: &[u8], flag: &AtomicBool) -> Result<(), EthcoreError> { 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()?); @@ -448,7 +421,7 @@ impl StateRebuilder { for (code_hash, code, first_with) in status.new_code { for addr_hash in self.missing_code.remove(&code_hash).unwrap_or_else(Vec::new) { let mut db = AccountDBMut::from_hash(self.db.as_hash_db_mut(), addr_hash); - db.emplace(code_hash, DBValue::from_slice(&code)); + db.emplace(code_hash, hash_db::EMPTY_PREFIX, code.to_vec()); } self.known_code.insert(code_hash, first_with); @@ -468,9 +441,9 @@ impl StateRebuilder { if !flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } if &thin_rlp[..] != &empty_rlp[..] { - self.bloom.set(&*hash); + self.bloom.set(hash.as_bytes()); } - account_trie.insert(&hash, &thin_rlp)?; + account_trie.insert(hash.as_bytes(), &thin_rlp)?; } } @@ -479,14 +452,13 @@ impl StateRebuilder { StateDB::commit_bloom(&mut batch, bloom_journal)?; self.db.inject(&mut batch)?; backing.write_buffered(batch); - trace!(target: "snapshot", "current state root: {:?}", self.state_root); Ok(()) } /// Finalize the restoration. Check for accounts missing code and make a dummy /// journal entry. /// Once all chunks have been fed, there should be nothing missing. - pub fn finalize(mut self, era: u64, id: H256) -> Result, ::error::Error> { + pub fn finalize(mut self, era: u64, id: H256) -> Result, EthcoreError> { let missing = self.missing_code.keys().cloned().collect::>(); if !missing.is_empty() { return Err(Error::MissingCode(missing).into()) } @@ -517,7 +489,7 @@ fn rebuild_accounts( known_code: &HashMap, known_storage_roots: &mut HashMap, abort_flag: &AtomicBool, -) -> Result { +) -> Result { let mut status = RebuiltStatus::default(); for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk.iter_mut()) { if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } @@ -545,11 +517,11 @@ fn rebuild_accounts( Some(&first_with) => { // if so, load it from the database. let code = AccountDB::from_hash(db, first_with) - .get(&code_hash) + .get(&code_hash, hash_db::EMPTY_PREFIX) .ok_or_else(|| Error::MissingCode(vec![first_with]))?; // and write it again under a different mangled key - AccountDBMut::from_hash(db, hash).emplace(code_hash, code); + AccountDBMut::from_hash(db, hash).emplace(code_hash, hash_db::EMPTY_PREFIX, code); } // if not, queue it up to be filled later None => status.missing_code.push((hash, code_hash)), @@ -578,13 +550,13 @@ const POW_VERIFY_RATE: f32 = 0.02; /// Verify an old block with the given header, engine, blockchain, body. If `always` is set, it will perform /// the fullest verification possible. If not, it will take a random sample to determine whether it will /// do heavy or light verification. -pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &dyn EthEngine, chain: &BlockChain, always: bool) -> Result<(), ::error::Error> { +pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &dyn Engine, chain: &BlockChain, always: bool) -> Result<(), EthcoreError> { engine.verify_block_basic(header)?; if always || rng.gen::() <= POW_VERIFY_RATE { engine.verify_block_unordered(header)?; match chain.block_header_data(header.parent_hash()) { - Some(parent) => engine.verify_block_family(header, &parent.decode()?), + Some(parent) => engine.verify_block_family(header, &parent.decode()?).map_err(Into::into), None => Ok(()), } } else { diff --git a/ethcore/src/snapshot/service.rs b/ethcore/snapshot/src/service.rs similarity index 68% rename from ethcore/src/snapshot/service.rs rename to ethcore/snapshot/src/service.rs index ddae76a00a..b474ead54d 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/snapshot/src/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,34 +24,45 @@ use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::cmp; -use super::{ManifestData, StateRebuilder, Rebuilder, RestorationStatus, SnapshotService, MAX_CHUNK_SIZE}; -use super::io::{SnapshotReader, LooseReader, SnapshotWriter, LooseWriter}; - use blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler}; -use client::{BlockInfo, BlockChainClient, Client, ChainInfo, ClientIoMessage}; -use engines::EthEngine; -use error::{Error, ErrorKind as SnapshotErrorKind}; -use snapshot::{Error as SnapshotError}; -use hash::keccak; -use types::ids::BlockId; - -use io::IoChannel; - -use ethereum_types::H256; -use parking_lot::{Mutex, RwLock, RwLockReadGuard}; use bytes::Bytes; +use common_types::{ + io_message::ClientIoMessage, + errors::{EthcoreError as Error, SnapshotError, SnapshotError::UnlinkedAncientBlockChain}, + ids::BlockId, + snapshot::{ManifestData, Progress, RestorationStatus}, +}; +use client_traits::ChainInfo; +use engine::Engine; +use ethereum_types::H256; +use ethcore_io::IoChannel; use journaldb::Algorithm; +use keccak_hash::keccak; use kvdb::DBTransaction; +use log::{debug, error, info, trace, warn}; +use parking_lot::{Mutex, RwLock, RwLockReadGuard}; use snappy; +use trie_db::TrieError; + +use crate::{SnapshotClient, SnapshotWriter}; + +use super::{ + StateRebuilder, + SnapshotService, + Rebuilder, + MAX_CHUNK_SIZE, + io::{SnapshotReader, LooseReader, LooseWriter}, + chunker, +}; /// Helper for removing directories in case of error. -struct Guard(bool, PathBuf); +pub struct Guard(bool, PathBuf); impl Guard { fn new(path: PathBuf) -> Self { Guard(true, path) } - #[cfg(test)] - fn benign() -> Self { Guard(false, PathBuf::default()) } + #[cfg(any(test, feature = "test-helpers"))] + pub fn benign() -> Self { Guard(false, PathBuf::default()) } fn disarm(mut self) { self.0 = false } } @@ -64,39 +75,49 @@ impl Drop for Guard { } } -/// External database restoration handler -pub trait DatabaseRestore: Send + Sync { - /// Restart with a new backend. Takes ownership of passed database and moves it to a new location. - fn restore_db(&self, new_db: &str) -> Result<(), Error>; -} - /// State restoration manager. -struct Restoration { +pub struct Restoration { manifest: ManifestData, state_chunks_left: HashSet, block_chunks_left: HashSet, state: StateRebuilder, - secondary: Box, + secondary: Box, writer: Option, snappy_buffer: Bytes, final_state_root: H256, guard: Guard, - db: Arc, + db: Arc, } -struct RestorationParams<'a> { +/// Params to initialise restoration +pub struct RestorationParams<'a> { manifest: ManifestData, // manifest to base restoration on. pruning: Algorithm, // pruning algorithm for the database. - db: Arc, // 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. - engine: &'a EthEngine, + engine: &'a dyn Engine, +} + +#[cfg(any(test, feature = "test-helpers"))] +impl<'a> RestorationParams<'a> { + pub fn new( + manifest: ManifestData, + pruning: Algorithm, + db: Arc, + writer: Option, + genesis: &'a [u8], + guard: Guard, + engine: &'a dyn Engine, + ) -> Self { + Self { manifest, pruning, db, writer, genesis, guard, engine } + } } impl Restoration { - // make a new restoration using the given parameters. - fn new(params: RestorationParams) -> Result { + /// Build a Restoration using the given parameters. + pub fn new(params: RestorationParams) -> Result { let manifest = params.manifest; let state_chunks = manifest.state_hashes.iter().cloned().collect(); @@ -105,34 +126,34 @@ impl Restoration { let raw_db = params.db; let chain = BlockChain::new(Default::default(), params.genesis, raw_db.clone()); - let components = params.engine.snapshot_components() - .ok_or_else(|| ::snapshot::Error::SnapshotsUnsupported)?; + let chunker = chunker(params.engine.snapshot_mode()) + .ok_or_else(|| Error::Snapshot(SnapshotError::SnapshotsUnsupported))?; - let secondary = components.rebuilder(chain, raw_db.clone(), &manifest)?; + let secondary = chunker.rebuilder(chain, raw_db.clone(), &manifest)?; - let root = manifest.state_root.clone(); + let final_state_root = manifest.state_root.clone(); Ok(Restoration { - manifest: manifest, + manifest, state_chunks_left: state_chunks, block_chunks_left: block_chunks, state: StateRebuilder::new(raw_db.key_value().clone(), params.pruning), - secondary: secondary, + secondary, writer: params.writer, snappy_buffer: Vec::new(), - final_state_root: root, + final_state_root, guard: params.guard, db: raw_db, }) } - // feeds a state chunk, aborts early if `flag` becomes false. - fn feed_state(&mut self, hash: H256, chunk: &[u8], flag: &AtomicBool) -> Result<(), Error> { + /// Feeds a chunk of state data to the Restoration. Aborts early if `flag` becomes false. + pub fn feed_state(&mut self, hash: H256, chunk: &[u8], flag: &AtomicBool) -> Result<(), Error> { if self.state_chunks_left.contains(&hash) { let expected_len = snappy::decompressed_len(chunk)?; if expected_len > MAX_CHUNK_SIZE { trace!(target: "snapshot", "Discarding large chunk: {} vs {}", expected_len, MAX_CHUNK_SIZE); - return Err(::snapshot::Error::ChunkTooLarge.into()); + return Err(SnapshotError::ChunkTooLarge.into()); } let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?; @@ -140,6 +161,7 @@ impl Restoration { if let Some(ref mut writer) = self.writer.as_mut() { writer.write_state_chunk(hash, chunk)?; + trace!(target: "snapshot", "Wrote {}/{} bytes of state to db/disk. Current state root: {:?}", len, chunk.len(), self.state.state_root()); } self.state_chunks_left.remove(&hash); @@ -148,13 +170,13 @@ impl Restoration { Ok(()) } - // feeds a block chunk - fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &EthEngine, flag: &AtomicBool) -> Result<(), Error> { + /// Feeds a chunk of block data to the `Restoration`. Aborts early if `flag` becomes false. + pub fn feed_blocks(&mut self, hash: H256, chunk: &[u8], engine: &dyn Engine, flag: &AtomicBool) -> Result<(), Error> { if self.block_chunks_left.contains(&hash) { let expected_len = snappy::decompressed_len(chunk)?; if expected_len > MAX_CHUNK_SIZE { trace!(target: "snapshot", "Discarding large chunk: {} vs {}", expected_len, MAX_CHUNK_SIZE); - return Err(::snapshot::Error::ChunkTooLarge.into()); + return Err(SnapshotError::ChunkTooLarge.into()); } let len = snappy::decompress_into(chunk, &mut self.snappy_buffer)?; @@ -170,9 +192,7 @@ impl Restoration { } // finish up restoration. - fn finalize(mut self, engine: &EthEngine) -> Result<(), Error> { - use trie::TrieError; - + fn finalize(mut self) -> Result<(), Error> { if !self.is_done() { return Ok(()) } // verify final state root. @@ -186,70 +206,68 @@ impl Restoration { self.state.finalize(self.manifest.block_number, self.manifest.block_hash)?; // connect out-of-order chunks and verify chain integrity. - self.secondary.finalize(engine)?; + self.secondary.finalize()?; if let Some(writer) = self.writer { writer.finish(self.manifest)?; } self.guard.disarm(); + trace!(target: "snapshot", "Restoration finalised correctly"); Ok(()) } - // is everything done? - fn is_done(&self) -> bool { + /// Check if we're done restoring: no more block chunks and no more state chunks to process. + pub fn is_done(&self) -> bool { self.block_chunks_left.is_empty() && self.state_chunks_left.is_empty() } } /// Type alias for client io channel. -pub type Channel = IoChannel; - -/// Trait alias for the Client Service used -pub trait SnapshotClient: BlockChainClient + BlockInfo + DatabaseRestore {} +pub type Channel = IoChannel>; /// Snapshot service parameters. -pub struct ServiceParams { +pub struct ServiceParams { /// The consensus engine this is built on. - pub engine: Arc, + pub engine: Arc, /// The chain's genesis block. pub genesis_block: Bytes, /// State pruning algorithm. pub pruning: Algorithm, /// Handler for opening a restoration DB. - pub restoration_db_handler: Box, + pub restoration_db_handler: Box, /// Async IO channel for sending messages. - pub channel: Channel, + pub channel: Channel, /// The directory to put snapshots in. /// Usually "/snapshot" pub snapshot_root: PathBuf, /// A handle for database restoration. - pub client: Arc, + pub client: Arc, } /// `SnapshotService` implementation. /// This controls taking snapshots and restoring from them. -pub struct Service { +pub struct Service { restoration: Mutex>, - restoration_db_handler: Box, + restoration_db_handler: Box, snapshot_root: PathBuf, - io_channel: Mutex, + io_channel: Mutex>, pruning: Algorithm, status: Mutex, reader: RwLock>, - engine: Arc, + engine: Arc, genesis_block: Bytes, state_chunks: AtomicUsize, block_chunks: AtomicUsize, - client: Arc, - progress: super::Progress, + client: Arc, + progress: RwLock, taking_snapshot: AtomicBool, restoring_snapshot: AtomicBool, } -impl Service { +impl Service where C: SnapshotClient + ChainInfo { /// Create a new snapshot service from the given parameters. - pub fn new(params: ServiceParams) -> Result { + pub fn new(params: ServiceParams) -> Result { let mut service = Service { restoration: Mutex::new(None), restoration_db_handler: params.restoration_db_handler, @@ -263,7 +281,7 @@ impl Service { state_chunks: AtomicUsize::new(0), block_chunks: AtomicUsize::new(0), client: params.client, - progress: Default::default(), + progress: RwLock::new(Progress::new()), taking_snapshot: AtomicBool::new(false), restoring_snapshot: AtomicBool::new(false), }; @@ -337,16 +355,6 @@ impl Service { dir } - // replace one the client's database with our own. - fn replace_client_db(&self) -> Result<(), Error> { - let migrated_blocks = self.migrate_blocks()?; - info!(target: "snapshot", "Migrated {} ancient blocks", migrated_blocks); - - let rest_db = self.restoration_db(); - self.client.restore_db(&*rest_db.to_string_lossy())?; - Ok(()) - } - // Migrate the blocks in the current DB into the new chain fn migrate_blocks(&self) -> Result { // Count the number of migrated blocks @@ -361,11 +369,27 @@ impl Service { // The old database looks like this: // [genesis, best_ancient_block] ... [first_block, best_block] - // If we are fully synced neither `best_ancient_block` nor `first_block` is set, and we can assume that the whole range from [genesis, best_block] is imported. - // The new database only contains the tip of the chain ([first_block, best_block]), + // If we are fully synced neither `best_ancient_block` nor `first_block` is set, and we can + // assume that the whole range from [genesis, best_block] is imported. + // The new database only contains the tip of the chain ([new_first_block, new_best_block]), // so the useful set of blocks is defined as: // [0 ... min(new.first_block, best_ancient_block or best_block)] + // + // If, for whatever reason, the old db does not have ancient blocks (i.e. + // `best_ancient_block` is `None` AND a non-zero `first_block`), such that the old db looks + // like [old_first_block..old_best_block] (which may or may not partially overlap with + // [new_first_block..new_best_block]) we do the conservative thing and do not migrate the + // old blocks. let find_range = || -> Option<(H256, H256)> { + // In theory, if the current best_block is > new first_block (i.e. ranges overlap) + // we could salvage them but what if there's been a re-org at the boundary and the two + // chains do not match anymore? We'd have to check the existing blocks carefully. + if cur_chain_info.ancient_block_number.is_none() && cur_chain_info.first_block_number.unwrap_or(0) > 0 { + info!(target: "blockchain", "blocks in the current DB do not stretch back to genesis; can't salvage them into the new DB. In current DB, first block: #{:?}/{:?}, best block: #{:?}/{:?}", + cur_chain_info.first_block_number, cur_chain_info.first_block_hash, + cur_chain_info.best_block_number, cur_chain_info.best_block_hash); + return None; + } let next_available_from = next_chain_info.first_block_number?; let cur_available_to = cur_chain_info.ancient_block_number.unwrap_or(cur_chain_info.best_block_number); @@ -375,10 +399,11 @@ impl Service { return None; } - trace!(target: "snapshot", "Trying to import ancient blocks until {}", highest_block_num); + trace!(target: "snapshot", "Trying to import ancient blocks until {}. First block in new chain=#{}, first block in old chain=#{:?}, best block in old chain=#{}", + highest_block_num, next_available_from, cur_chain_info.first_block_number, cur_chain_info.best_block_number); // Here we start from the highest block number and go backward to 0, - // thus starting at `highest_block_num` and targetting `0`. + // thus starting at `highest_block_num` and targeting `0`. let target_hash = self.client.block_hash(BlockId::Number(0))?; let start_hash = self.client.block_hash(BlockId::Number(highest_block_num))?; @@ -389,7 +414,10 @@ impl Service { Some(x) => x, None => return Ok(0), }; - + info!(target: "snapshot", "Migrating blocks from old db to new. Start: #{}/{:?}, Target: #{}/{:?}", + self.client.block_number(BlockId::Hash(start_hash)).unwrap_or_default(), start_hash, + self.client.block_number(BlockId::Hash(target_hash)).unwrap_or_default(), target_hash, + ); let mut batch = DBTransaction::new(); let mut parent_hash = start_hash; while parent_hash != target_hash { @@ -398,7 +426,10 @@ impl Service { return Ok(count); } - let block = self.client.block(BlockId::Hash(parent_hash)).ok_or(::snapshot::error::Error::UnlinkedAncientBlockChain)?; + let block = self.client.block(BlockId::Hash(parent_hash)).ok_or_else(|| { + error!(target: "snapshot", "migrate_blocks: did not find block from parent_hash={:#x} (start_hash={:#x})", parent_hash, start_hash); + UnlinkedAncientBlockChain(parent_hash) + })?; parent_hash = block.parent_hash(); let block_number = block.number(); @@ -412,7 +443,14 @@ impl Service { next_chain.insert_unordered_block(&mut batch, block, block_receipts, Some(parent_total_difficulty), false, true); count += 1; }, - _ => break, + _ => { + // We couldn't reach the targeted hash + error!(target: "snapshot", "migrate_blocks: failed to find receipts and parent total difficulty; cannot reach the target_hash ({:#x}). Block #{}, parent_hash={:#x}, parent_total_difficulty={:?}, start_hash={:#x}, ancient_block_number={:?}, best_block_number={:?}", + target_hash, block_number, parent_hash, parent_total_difficulty, + start_hash, cur_chain_info.ancient_block_number, cur_chain_info.best_block_number, + ); + return Err(UnlinkedAncientBlockChain(parent_hash).into()); + }, } // Writing changes to DB and logging every now and then @@ -421,10 +459,10 @@ impl Service { next_chain.commit(); next_db.key_value().flush().expect("DB flush failed."); batch = DBTransaction::new(); - } - if block_number % 10_000 == 0 { - info!(target: "snapshot", "Block restoration at #{}", block_number); + if block_number % 10_000 == 0 { + info!(target: "snapshot", "Block restoration at #{}", block_number); + } } } @@ -433,11 +471,6 @@ impl Service { next_chain.commit(); next_db.key_value().flush().expect("DB flush failed."); - // We couldn't reach the targeted hash - if parent_hash != target_hash { - return Err(::snapshot::error::Error::UnlinkedAncientBlockChain.into()); - } - // Update best ancient block in the Next Chain next_chain.update_best_ancient_block(&start_hash); Ok(count) @@ -451,62 +484,59 @@ impl Service { /// Tick the snapshot service. This will log any active snapshot /// being taken. pub fn tick(&self) { - if self.progress.done() || !self.taking_snapshot.load(Ordering::SeqCst) { return } + if self.progress.read().done() || !self.taking_snapshot.load(Ordering::SeqCst) { return } - let p = &self.progress; - info!("Snapshot: {} accounts {} blocks {} bytes", p.accounts(), p.blocks(), p.size()); + let p = &self.progress.read(); + info!("Snapshot: {} accounts, {} blocks, {} bytes", p.accounts(), p.blocks(), p.bytes()); + let rate = p.rate(); + debug!(target: "snapshot", "Current progress rate: {:.0} acc/s, {:.0} bytes/s (compressed)", rate.0, rate.1); } /// Take a snapshot at the block with the given number. - /// calling this while a restoration is in progress or vice versa + /// Calling this while a restoration is in progress or vice versa /// will lead to a race condition where the first one to finish will /// have their produced snapshot overwritten. - pub fn take_snapshot(&self, client: &Client, num: u64) -> Result<(), Error> { + pub fn take_snapshot(&self, client: &C, num: u64) -> Result<(), Error> { if self.taking_snapshot.compare_and_swap(false, true, Ordering::SeqCst) { info!("Skipping snapshot at #{} as another one is currently in-progress.", num); return Ok(()); } info!("Taking snapshot at #{}", num); - self.progress.reset(); + { + scopeguard::defer! {{ + self.taking_snapshot.store(false, Ordering::SeqCst); + }} + let start_time = std::time::Instant::now(); + *self.progress.write() = Progress::new(); + + let temp_dir = self.temp_snapshot_dir(); + let snapshot_dir = self.snapshot_dir(); - let temp_dir = self.temp_snapshot_dir(); - let snapshot_dir = self.snapshot_dir(); + let _ = fs::remove_dir_all(&temp_dir); // expected to fail - let _ = fs::remove_dir_all(&temp_dir); + let writer = LooseWriter::new(temp_dir.clone())?; - let writer = LooseWriter::new(temp_dir.clone())?; + let guard = Guard::new(temp_dir.clone()); + let _ = client.take_snapshot(writer, BlockId::Number(num), &self.progress)?; + info!("Finished taking snapshot at #{}, in {:.0?}", num, start_time.elapsed()); - let guard = Guard::new(temp_dir.clone()); - let res = client.take_snapshot(writer, BlockId::Number(num), &self.progress); - self.taking_snapshot.store(false, Ordering::SeqCst); - if let Err(e) = res { - if client.chain_info().best_block_number >= num + client.pruning_history() { - // The state we were snapshotting was pruned before we could finish. - info!("Periodic snapshot failed: block state pruned. Run with a longer `--pruning-history` or with `--no-periodic-snapshot`"); - return Err(e); - } else { - return Err(e); - } - } - - info!("Finished taking snapshot at #{}", num); + // destroy the old snapshot reader. + let mut reader = self.reader.write(); + *reader = None; - let mut reader = self.reader.write(); + if snapshot_dir.exists() { + trace!(target: "snapshot", "Removing previous snapshot at {:?}", &snapshot_dir); + fs::remove_dir_all(&snapshot_dir)?; + } - // destroy the old snapshot reader. - *reader = None; + fs::rename(temp_dir, &snapshot_dir)?; + trace!(target: "snapshot", "Moved new snapshot into place at {:?}", &snapshot_dir); + *reader = Some(LooseReader::new(snapshot_dir)?); - if snapshot_dir.exists() { - fs::remove_dir_all(&snapshot_dir)?; + guard.disarm(); + Ok(()) } - - fs::rename(temp_dir, &snapshot_dir)?; - - *reader = Some(LooseReader::new(snapshot_dir)?); - - guard.disarm(); - Ok(()) } /// Initialize the restoration synchronously. @@ -549,6 +579,8 @@ impl Service { *self.status.lock() = RestorationStatus::Initializing { chunks_done: 0, + state_chunks: manifest.state_hashes.len() as u32, + block_chunks: manifest.block_hashes.len() as u32, }; fs::create_dir_all(&rest_dir)?; @@ -563,7 +595,7 @@ impl Service { manifest: manifest.clone(), pruning: self.pruning, db: self.restoration_db_handler.open(&rest_db)?, - writer: writer, + writer, genesis: &self.genesis_block, guard: Guard::new(rest_db), engine: &*self.engine, @@ -623,13 +655,19 @@ impl Service { 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 { + /// Import a previous chunk at the given path. Returns whether the chunk 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(); + let filesize = file.metadata()?.len(); + let mut buffer = Vec::with_capacity(filesize as usize + 1); // +1 for EOF file.read_to_end(&mut buffer)?; let hash = keccak(&buffer); @@ -649,20 +687,24 @@ impl Service { Ok(true) } - // finalize the restoration. this accepts an already-locked - // restoration as an argument -- so acquiring it again _will_ - // lead to deadlock. + // Finalize the restoration. This accepts an already-locked restoration as an argument -- so + // acquiring it again _will_ lead to deadlock. fn finalize_restoration(&self, rest: &mut Option) -> Result<(), Error> { - trace!(target: "snapshot", "finalizing restoration"); + trace!(target: "snapshot", "Finalizing restoration"); + *self.status.lock() = RestorationStatus::Finalizing; let recover = rest.as_ref().map_or(false, |rest| rest.writer.is_some()); // destroy the restoration before replacing databases and snapshot. rest.take() - .map(|r| r.finalize(&*self.engine)) + .map(|r| r.finalize()) .unwrap_or(Ok(()))?; - self.replace_client_db()?; + let migrated_blocks = self.migrate_blocks()?; + info!(target: "snapshot", "Migrated {} ancient blocks from the old DB", migrated_blocks); + + // replace the Client's database with the new one (restart the Client). + self.client.restore_db(&*self.restoration_db().to_string_lossy())?; if recover { let mut reader = self.reader.write(); @@ -671,11 +713,11 @@ impl Service { let snapshot_dir = self.snapshot_dir(); if snapshot_dir.exists() { - trace!(target: "snapshot", "removing old snapshot dir at {}", snapshot_dir.to_string_lossy()); + trace!(target: "snapshot", "Removing old snapshot dir at {}", snapshot_dir.to_string_lossy()); fs::remove_dir_all(&snapshot_dir)?; } - trace!(target: "snapshot", "copying restored snapshot files over"); + trace!(target: "snapshot", "Copying restored snapshot files over"); fs::rename(self.temp_recovery_dir(), &snapshot_dir)?; *reader = Some(LooseReader::new(snapshot_dir)?); @@ -690,14 +732,20 @@ impl Service { /// Feed a chunk of either kind (block or state). no-op if no restoration or status is wrong. fn feed_chunk(&self, hash: H256, chunk: &[u8], is_state: bool) { // TODO: be able to process block chunks and state chunks at same time? - let mut restoration = self.restoration.lock(); - match self.feed_chunk_with_restoration(&mut restoration, hash, chunk, is_state) { + let r = { + let mut restoration = self.restoration.lock(); + self.feed_chunk_with_restoration(&mut restoration, hash, chunk, is_state) + }; + match r { Ok(()) | - Err(Error(SnapshotErrorKind::Snapshot(SnapshotError::RestorationAborted), _)) => (), + Err(Error::Snapshot(SnapshotError::RestorationAborted)) => (), Err(e) => { + // TODO: after this we're sometimes deadlocked warn!("Encountered error during snapshot restoration: {}", e); - *self.restoration.lock() = None; - *self.status.lock() = RestorationStatus::Failed; + self.abort_restore(); + if let Some(mut status) = self.status.try_lock_for(std::time::Duration::from_millis(10)) { + *status = RestorationStatus::Failed; + } let _ = fs::remove_dir_all(self.restoration_dir()); } } @@ -707,8 +755,8 @@ impl Service { 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 => { - trace!(target: "snapshot", "Tried to restore chunk {:x} while inactive or failed", hash); + RestorationStatus::Inactive | RestorationStatus::Failed | RestorationStatus::Finalizing => { + trace!(target: "snapshot", "Tried to restore chunk {:x} while inactive, failed or finalizing", hash); return Ok(()); }, RestorationStatus::Ongoing { .. } | RestorationStatus::Initializing { .. } => { @@ -740,7 +788,7 @@ impl Service { false => Ok(()) } } - other => other.map(drop), + Err(e) => Err(e) }; (res, db) } @@ -763,20 +811,16 @@ impl Service { } } -impl SnapshotService for Service { +impl SnapshotService for Service { fn manifest(&self) -> Option { self.reader.read().as_ref().map(|r| r.manifest().clone()) } fn supported_versions(&self) -> Option<(u64, u64)> { - self.engine.snapshot_components() + chunker(self.engine.snapshot_mode()) .map(|c| (c.min_supported_version(), c.current_version())) } - fn chunk(&self, hash: H256) -> Option { - self.reader.read().as_ref().and_then(|r| r.chunk(hash).ok()) - } - fn completed_chunks(&self) -> Option> { let restoration = self.restoration.lock(); @@ -799,11 +843,15 @@ impl SnapshotService for Service { } } + fn chunk(&self, hash: H256) -> Option { + self.reader.read().as_ref().and_then(|r| r.chunk(hash).ok()) + } + fn status(&self) -> RestorationStatus { let mut cur_status = self.status.lock(); match *cur_status { - RestorationStatus::Initializing { ref mut chunks_done } => { + RestorationStatus::Initializing { ref mut chunks_done, .. } => { *chunks_done = self.state_chunks.load(Ordering::SeqCst) as u32 + self.block_chunks.load(Ordering::SeqCst) as u32; } @@ -845,7 +893,7 @@ impl SnapshotService for Service { fn abort_snapshot(&self) { if self.taking_snapshot.load(Ordering::SeqCst) { trace!(target: "snapshot", "Aborting snapshot – Snapshot under way"); - self.progress.abort.store(true, Ordering::SeqCst); + self.progress.write().abort = true; } } @@ -858,7 +906,7 @@ impl SnapshotService for Service { } } -impl Drop for Service { +impl Drop for Service { fn drop(&mut self) { trace!(target: "shutdown", "Dropping Service"); self.abort_restore(); @@ -867,101 +915,3 @@ impl Drop for Service { trace!(target: "shutdown", "Dropping Service - snapshot aborted"); } } - -#[cfg(test)] -mod tests { - use client::ClientIoMessage; - use io::{IoService}; - use spec::Spec; - use journaldb::Algorithm; - use snapshot::{ManifestData, RestorationStatus, SnapshotService}; - use super::*; - use tempdir::TempDir; - use test_helpers::{generate_dummy_client_with_spec_and_data, restoration_db_handler}; - - #[test] - fn sends_async_messages() { - let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()]; - let client = generate_dummy_client_with_spec_and_data(Spec::new_null, 400, 5, &gas_prices); - let service = IoService::::start().unwrap(); - let spec = Spec::new_test(); - - let tempdir = TempDir::new("").unwrap(); - let dir = tempdir.path().join("snapshot"); - - let snapshot_params = ServiceParams { - engine: spec.engine.clone(), - genesis_block: spec.genesis_block(), - restoration_db_handler: restoration_db_handler(Default::default()), - pruning: Algorithm::Archive, - channel: service.channel(), - snapshot_root: dir, - client: client, - }; - - let service = Service::new(snapshot_params).unwrap(); - - assert!(service.manifest().is_none()); - assert!(service.chunk(Default::default()).is_none()); - assert_eq!(service.status(), RestorationStatus::Inactive); - - let manifest = ManifestData { - version: 2, - state_hashes: vec![], - block_hashes: vec![], - state_root: Default::default(), - block_number: 0, - block_hash: Default::default(), - }; - - service.begin_restore(manifest); - service.abort_restore(); - service.restore_state_chunk(Default::default(), vec![]); - service.restore_block_chunk(Default::default(), vec![]); - } - - #[test] - fn cannot_finish_with_invalid_chunks() { - use ethereum_types::H256; - use kvdb_rocksdb::DatabaseConfig; - - let spec = Spec::new_test(); - let tempdir = TempDir::new("").unwrap(); - - let state_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect(); - let block_hashes: Vec<_> = (0..5).map(|_| H256::random()).collect(); - let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); - let gb = spec.genesis_block(); - let flag = ::std::sync::atomic::AtomicBool::new(true); - - let params = RestorationParams { - manifest: ManifestData { - version: 2, - state_hashes: state_hashes.clone(), - block_hashes: block_hashes.clone(), - state_root: H256::default(), - block_number: 100000, - block_hash: H256::default(), - }, - pruning: Algorithm::Archive, - db: restoration_db_handler(db_config).open(&tempdir.path().to_owned()).unwrap(), - writer: None, - genesis: &gb, - guard: Guard::benign(), - engine: &*spec.engine.clone(), - }; - - let mut restoration = Restoration::new(params).unwrap(); - let definitely_bad_chunk = [1, 2, 3, 4, 5]; - - for hash in state_hashes { - assert!(restoration.feed_state(hash, &definitely_bad_chunk, &flag).is_err()); - assert!(!restoration.is_done()); - } - - for hash in block_hashes { - assert!(restoration.feed_blocks(hash, &definitely_bad_chunk, &*spec.engine, &flag).is_err()); - assert!(!restoration.is_done()); - } - } -} diff --git a/ethcore/snapshot/src/traits.rs b/ethcore/snapshot/src/traits.rs new file mode 100644 index 0000000000..8caab6c1b7 --- /dev/null +++ b/ethcore/snapshot/src/traits.rs @@ -0,0 +1,162 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +use std::sync::{Arc, atomic::AtomicBool}; + +use blockchain::{BlockChain, BlockChainDB}; +use bytes::Bytes; +use client_traits::{BlockChainClient, BlockInfo, DatabaseRestore, BlockChainReset}; +use common_types::{ + ids::BlockId, + errors::{EthcoreError as Error, SnapshotError}, + snapshot::{ManifestData, ChunkSink, Progress, RestorationStatus}, +}; +use engine::Engine; +use ethereum_types::H256; +use parking_lot::RwLock; + +use crate::io::SnapshotWriter; + +/// The interface for a snapshot network service. +/// This handles: +/// - restoration of snapshots to temporary databases. +/// - responding to queries for snapshot manifests and chunks +pub trait SnapshotService : Sync + Send { + /// Query the most recent manifest data. + fn manifest(&self) -> Option; + + /// Get the supported range of snapshot version numbers. + /// `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; + + /// Ask the snapshot service for the restoration status. + fn status(&self) -> RestorationStatus; + + /// Begin snapshot restoration. + /// If a restoration is in progress, this will reset it and clear all data. + fn begin_restore(&self, manifest: ManifestData); + + /// Abort an in-progress restoration if there is one. + fn abort_restore(&self); + + /// Feed a raw state chunk to the service to be processed asynchronously. + /// no-op if not currently restoring. + fn restore_state_chunk(&self, hash: H256, chunk: Bytes); + + /// 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); + + /// Abort in-progress snapshotting if there is one. + fn abort_snapshot(&self); + + /// Shutdown the Snapshot Service by aborting any ongoing restore + fn shutdown(&self); +} + +/// Restore from secondary snapshot chunks. +pub trait Rebuilder: Send { + /// Feed a chunk, potentially out of order. + /// + /// Check `abort_flag` periodically while doing heavy work. If set to `false`, should bail with + /// `Error::RestorationAborted`. + fn feed( + &mut self, + chunk: &[u8], + engine: &dyn Engine, + abort_flag: &AtomicBool, + ) -> Result<(), Error>; + + /// Finalize the restoration. Will be done after all chunks have been + /// fed successfully. + /// + /// This should apply the necessary "glue" between chunks, + /// and verify against the restored state. + fn finalize(&mut self) -> Result<(), Error>; +} + +/// Components necessary for snapshot creation and restoration. +pub trait SnapshotComponents: Send { + /// Create secondary snapshot chunks; these corroborate the state data + /// in the state chunks. + /// + /// Chunks shouldn't exceed the given preferred size, and should be fed + /// uncompressed into the sink. + /// + /// This will vary by consensus engine, so it's exposed as a trait. + fn chunk_all( + &mut self, + chain: &BlockChain, + block_at: H256, + chunk_sink: &mut ChunkSink, + progress: &RwLock, + preferred_size: usize, + ) -> Result<(), SnapshotError>; + + /// Create a rebuilder, which will have chunks fed into it in arbitrary + /// order and then be finalized. + /// + /// The manifest, a database, and fresh `BlockChain` are supplied. + /// + /// The engine passed to the `Rebuilder` methods will be the same instance + /// that created the `SnapshotComponents`. + fn rebuilder( + &self, + chain: BlockChain, + db: Arc, + manifest: &ManifestData, + ) -> Result, Error>; + + /// Minimum supported snapshot version number. + fn min_supported_version(&self) -> u64; + + /// Current version number + fn current_version(&self) -> u64; +} + +/// Snapshot related functionality +pub trait SnapshotClient: BlockChainClient + BlockInfo + DatabaseRestore + BlockChainReset { + /// Take a snapshot at the given block. + /// If the BlockId is 'Latest', this will default to 1000 blocks behind. + fn take_snapshot( + &self, + writer: W, + at: BlockId, + p: &RwLock, + ) -> Result<(), Error>; +} + +/// Helper trait for broadcasting a block to take a snapshot at. +pub trait Broadcast: Send + Sync { + /// Start a snapshot from the given block number. + fn request_snapshot_at(&self, num: u64); +} + + +/// Helper trait for transforming hashes to block numbers and checking if syncing. +pub trait Oracle: Send + Sync { + /// Maps a block hash to a block number + fn to_number(&self, hash: H256) -> Option; + + /// Are we currently syncing? + fn is_major_importing(&self) -> bool; +} diff --git a/ethcore/snapshot/src/watcher.rs b/ethcore/snapshot/src/watcher.rs new file mode 100644 index 0000000000..534c1c1bdb --- /dev/null +++ b/ethcore/snapshot/src/watcher.rs @@ -0,0 +1,125 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Watcher for snapshot-related chain events. + +use std::sync::Arc; + +use client_traits::{BlockInfo, ChainNotify}; +use common_types::{ + ids::BlockId, + io_message::ClientIoMessage, + chain_notify::NewBlocks, +}; +use ethereum_types::H256; +use ethcore_io::IoChannel; +use log::{trace, warn}; +use parking_lot::Mutex; + +use crate::traits::{Broadcast, Oracle}; + +struct StandardOracle where F: 'static + Send + Sync + Fn() -> bool { + client: Arc, + sync_status: F, +} + +impl Oracle for StandardOracle + where F: Send + Sync + Fn() -> bool +{ + fn to_number(&self, hash: H256) -> Option { + self.client.block_header(BlockId::Hash(hash)).map(|h| h.number()) + } + + fn is_major_importing(&self) -> bool { + (self.sync_status)() + } +} + +impl Broadcast for Mutex>> { + fn request_snapshot_at(&self, num: u64) { + if let Err(e) = self.lock().send(ClientIoMessage::TakeSnapshot(num)) { + warn!(target: "snapshot_watcher", "Snapshot watcher disconnected from IoService: {}", e); + } else { + trace!(target: "snapshot_watcher", "Snapshot requested at block #{}", num); + } + } +} + +/// A `ChainNotify` implementation which will trigger a snapshot event +/// at certain block numbers. +pub struct Watcher { + oracle: Box, + broadcast: Box, + // How often we attempt to take a snapshot: only snapshot on blocknumbers that are multiples of + // `period`. Always set to `SNAPSHOT_PERIOD`, i.e. 5000. + period: u64, + // Start snapshots `history` blocks from the tip. Always set to `SNAPSHOT_HISTORY`, i.e. 100. + history: u64, +} + +impl Watcher { + /// Create a new `Watcher` which will trigger a snapshot event + /// once every `period` blocks, but only after that block is + /// `history` blocks old. + pub fn new( + client: Arc, + sync_status: F, + channel: IoChannel>, + period: u64, + history: u64 + ) -> Self + where + F: 'static + Send + Sync + Fn() -> bool, + C: 'static + Send + Sync, + { + Watcher { + oracle: Box::new(StandardOracle { client, sync_status }), + broadcast: Box::new(Mutex::new(channel)), + period, + history, + } + } + + #[cfg(any(test, feature = "test-helpers"))] + /// Instantiate a `Watcher` using anything that impls `Oracle` and `Broadcast`. Test only. + pub fn new_test(oracle: Box, broadcast: Box, period: u64, history: u64) -> Self { + Watcher { oracle, broadcast, period, history } + } +} + +impl ChainNotify for Watcher { + fn new_blocks(&self, new_blocks: NewBlocks) { + if self.oracle.is_major_importing() || new_blocks.has_more_blocks_to_import { return } + + // Decide if it's time for a snapshot: the highest of the imported blocks is a multiple of 5000? + let highest = new_blocks.imported.into_iter() + // Convert block hashes to block numbers for all newly imported blocks + .filter_map(|h| self.oracle.to_number(h)) + // Subtract `history` (i.e. `SNAPSHOT_HISTORY`, i.e. 100) from the block numbers to stay + // clear of reorgs. + .map(|num| num.saturating_sub(self.history) ) + // …filter out blocks that do not fall on the a multiple of `period`. This regulates the + // frequency of snapshots and ensures more snapshots are produced from similar points in + // the chain. + .filter(|num| num % self.period == 0 ) + // Pick newest of the candidates: this is where we want to snapshot from. + .fold(0, ::std::cmp::max); + + if highest > 0 { + self.broadcast.request_snapshot_at(highest); + } + } +} diff --git a/ethcore/spec/Cargo.toml b/ethcore/spec/Cargo.toml new file mode 100644 index 0000000000..570a2df71f --- /dev/null +++ b/ethcore/spec/Cargo.toml @@ -0,0 +1,41 @@ +[package] +description = "Ethereum engine specification" +name = "spec" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +account-state = { path = "../account-state" } +authority-round = { path = "../engines/authority-round" } +basic-authority = { path = "../engines/basic-authority" } +builtin = { package = "ethcore-builtin", path = "../builtin" } +bytes = { package = "parity-bytes", version = "0.1.0" } +clique = { path = "../engines/clique" } +common-types = { path = "../types" } +engine = { path = "../engine" } +ethash = { path = "../../ethash" } +ethash-engine = { path = "../engines/ethash" } +ethereum-types = "0.8.0" +ethjson = { path = "../../json" } +evm = { path = "../evm" } +executive-state = { path = "../executive-state" } +hash-db = "0.15.0" +instant-seal = { path = "../engines/instant-seal" } +journaldb = { path = "../../util/journaldb" } +keccak-hash = "0.4.0" +kvdb-memorydb = "0.3.1" +log = "0.4.8" +machine = { path = "../machine" } +null-engine = { path = "../engines/null-engine" } +pod = { path = "../pod" } +rlp = "0.4.2" +trace = { path = "../trace" } +trie-vm-factories = { path = "../trie-vm-factories" } +vm = { path = "../vm" } + +[dev-dependencies] +ethcore = { path = "..", features = ["test-helpers"] } +env_logger = "0.5" +tempdir = "0.3.7" diff --git a/ethcore/spec/src/chain.rs b/ethcore/spec/src/chain.rs new file mode 100644 index 0000000000..b037c023d2 --- /dev/null +++ b/ethcore/spec/src/chain.rs @@ -0,0 +1,174 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Load chain specifications for all chains supported by the parity-ethereum client. + +macro_rules! bundle_release_spec { + ($($path: expr => $name: ident), *) => { + $( + /// Bundled release spec + pub fn $name<'a, T: Into>>(params: T) -> crate::spec::Spec { + let params = params.into(); + crate::spec::Spec::load( + params, + include_bytes!(concat!("../../res/", $path, ".json")) as &[u8] + ).expect(concat!("Chain spec ", $path, " is invalid.")) + } + )* + } +} + +macro_rules! bundle_test_spec { + ($($path: expr => $name: ident), *) => { + $( + /// Bundled test spec + pub fn $name() -> crate::spec::Spec { + crate::spec::Spec::load( + &::std::env::temp_dir(), + include_bytes!(concat!("../../res/", $path, ".json")) as &[u8] + ).expect(concat!("Chain spec ", $path, " is invalid.")) + } + )* + } +} + +macro_rules! bundle_test_machine { + ($($path: expr => $name: ident), *) => { + $( + /// Bundled test spec + pub fn $name() -> machine::Machine { + crate::spec::Spec::load_machine( + include_bytes!(concat!("../../res/", $path, ".json")) as &[u8] + ).expect(concat!("Chain spec ", $path, " is invalid.")) + } + )* + } +} + +bundle_release_spec! { + "ethereum/callisto" => new_callisto, + "ethereum/classic" => new_classic, + "ethereum/ellaism" => new_ellaism, + "ethereum/evantestcore" => new_evantestcore, + "ethereum/evancore" => new_evancore, + "ethereum/expanse" => new_expanse, + "ethereum/foundation" => new_foundation, + "ethereum/goerli" => new_goerli, + "ethereum/kotti" => new_kotti, + "ethereum/kovan" => new_kovan, + "ethereum/mix" => new_mix, + "ethereum/morden" => new_morden, + "ethereum/mordor" => new_mordor, + "ethereum/musicoin" => new_musicoin, + "ethereum/poacore" => new_poanet, + "ethereum/xdai" => new_xdai, + "ethereum/ethercore" => new_ethercore, + "ethereum/poasokol" => new_sokol, + "ethereum/rinkeby" => new_rinkeby, + "ethereum/ropsten" => new_ropsten, + "ethereum/volta" => new_volta, + "ethereum/ewc" => new_ewc +} + +bundle_test_spec! { + "authority_round" => new_test_round, + "authority_round_block_reward_contract" => new_test_round_block_reward_contract, + "authority_round_empty_steps" => new_test_round_empty_steps, + "authority_round_randomness_contract" => new_test_round_randomness_contract, + "constructor" => new_test_constructor, + "ethereum/byzantium_test" => new_byzantium_test, + "ethereum/constantinople_test" => new_constantinople_test, + "ethereum/istanbul_test" => new_istanbul_test, + "ethereum/eip150_test" => new_eip150_test, + "ethereum/eip161_test" => new_eip161_test, + "ethereum/eip210_test" => new_eip210_test, + "ethereum/frontier_like_test" => new_mainnet_like, + "ethereum/frontier_test" => new_frontier_test, + "ethereum/homestead_test" => new_homestead_test, + "ethereum/kovan_wasm_test" => new_kovan_wasm_test, + "ethereum/mcip3_test" => new_mcip3_test, + "ethereum/morden" => new_morden_test, + "ethereum/mordor" => new_mordor_test, + "ethereum/ropsten" => new_ropsten_test, + "ethereum/st_peters_test" => new_constantinople_fix_test, + "ethereum/transition_test" => new_transition_test, + "instant_seal" => new_instant, + "null" => new_null, + "null_morden" => new_test, + "null_morden_with_reward" => new_test_with_reward, + "null_morden_with_finality" => new_test_with_finality, + "validator_contract" => new_validator_contract, + "validator_multi" => new_validator_multi, + "validator_safe_contract" => new_validator_safe_contract +} + +bundle_test_machine! { + "ethereum/byzantium_test" => new_byzantium_test_machine, + "ethereum/constantinople_test" => new_constantinople_test_machine, + "ethereum/istanbul_test" => new_istanbul_test_machine, + "ethereum/eip210_test" => new_eip210_test_machine, + "ethereum/frontier_test" => new_frontier_test_machine, + "ethereum/homestead_test" => new_homestead_test_machine, + "ethereum/kovan_wasm_test" => new_kovan_wasm_test_machine, + "null_morden" => new_test_machine +} + +#[cfg(test)] +mod tests { + use account_state::State; + use common_types::{view, views::BlockView}; + use ethereum_types::U256; + use tempdir::TempDir; + use ethcore::test_helpers::get_temp_state_db; + + use super::{new_morden, new_foundation}; + + #[test] + fn ensure_db_good() { + let tempdir = TempDir::new("").unwrap(); + let spec = new_morden(&tempdir.path()); + let engine = &spec.engine; + let genesis_header = spec.genesis_header(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); + let s = State::from_existing(db, genesis_header.state_root().clone(), engine.account_start_nonce(0), Default::default()).unwrap(); + assert_eq!(s.balance(&"0000000000000000000000000000000000000001".parse().unwrap()).unwrap(), 1u64.into()); + assert_eq!(s.balance(&"0000000000000000000000000000000000000002".parse().unwrap()).unwrap(), 1u64.into()); + assert_eq!(s.balance(&"0000000000000000000000000000000000000003".parse().unwrap()).unwrap(), 1u64.into()); + assert_eq!(s.balance(&"0000000000000000000000000000000000000004".parse().unwrap()).unwrap(), 1u64.into()); + assert_eq!(s.balance(&"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c".parse().unwrap()).unwrap(), U256::from(1u64) << 200); + assert_eq!(s.balance(&"0000000000000000000000000000000000000000".parse().unwrap()).unwrap(), 0u64.into()); + } + + #[test] + fn morden() { + let tempdir = TempDir::new("").unwrap(); + let morden = new_morden(&tempdir.path()); + + assert_eq!(morden.state_root, "f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9".parse().unwrap()); + let genesis = morden.genesis_block(); + assert_eq!(view!(BlockView, &genesis).header_view().hash(), "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".parse().unwrap()); + } + + #[test] + fn frontier() { + let tempdir = TempDir::new("").unwrap(); + let frontier = new_foundation(&tempdir.path()); + + assert_eq!(frontier.state_root, "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".parse().unwrap()); + let genesis = frontier.genesis_block(); + assert_eq!(view!(BlockView, &genesis).header_view().hash(), "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3".parse().unwrap()); + } +} diff --git a/ethcore/src/spec/genesis.rs b/ethcore/spec/src/genesis.rs similarity index 93% rename from ethcore/src/spec/genesis.rs rename to ethcore/spec/src/genesis.rs index 96a42178dd..92eb969c4a 100644 --- a/ethcore/src/spec/genesis.rs +++ b/ethcore/spec/src/genesis.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,10 +16,11 @@ use ethereum_types::{H256, U256, Address}; use ethjson; -use hash::KECCAK_NULL_RLP; -use spec::seal::Seal; +use keccak_hash::KECCAK_NULL_RLP; +use crate::seal::Seal; /// Genesis components. +#[derive(Debug)] pub struct Genesis { /// Seal. pub seal: Seal, diff --git a/ethcore/src/spec/mod.rs b/ethcore/spec/src/lib.rs similarity index 84% rename from ethcore/src/spec/mod.rs rename to ethcore/spec/src/lib.rs index 5d90b5fbf8..8a3069e9f4 100644 --- a/ethcore/src/spec/mod.rs +++ b/ethcore/spec/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,9 +16,11 @@ //! Blockchain params. +mod chain; mod genesis; mod seal; mod spec; +pub use self::chain::*; pub use self::genesis::Genesis; -pub use self::spec::{Spec, SpecHardcodedSync, SpecParams, CommonParams, OptimizeFor}; +pub use self::spec::{Spec, SpecHardcodedSync, SpecParams}; diff --git a/ethcore/src/spec/seal.rs b/ethcore/spec/src/seal.rs similarity index 95% rename from ethcore/src/spec/seal.rs rename to ethcore/spec/src/seal.rs index ed70ac8b54..4f86f6e98e 100644 --- a/ethcore/src/spec/seal.rs +++ b/ethcore/spec/src/seal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,6 +21,7 @@ use ethereum_types::{H64, H256, H520}; use ethjson; /// Classic ethereum seal. +#[derive(Debug)] pub struct Ethereum { /// Seal nonce. pub nonce: H64, @@ -37,6 +38,7 @@ impl Into for Ethereum { } /// AuthorityRound seal. +#[derive(Debug)] pub struct AuthorityRound { /// Seal step. pub step: usize, @@ -45,6 +47,7 @@ pub struct AuthorityRound { } /// Tendermint seal. +#[derive(Debug)] pub struct Tendermint { /// Seal round. pub round: usize, @@ -73,9 +76,11 @@ impl Into for Tendermint { } } +#[derive(Debug)] pub struct Generic(pub Vec); /// Genesis seal type. +#[derive(Debug)] pub enum Seal { /// Classic ethereum seal. Ethereum(Ethereum), diff --git a/ethcore/spec/src/spec.rs b/ethcore/spec/src/spec.rs new file mode 100644 index 0000000000..1116fc626a --- /dev/null +++ b/ethcore/spec/src/spec.rs @@ -0,0 +1,609 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Parameters for a block chain. + +use std::{ + collections::BTreeMap, + convert::TryFrom, + fmt, + io::Read, + path::Path, + sync::Arc, +}; + +use common_types::{ + BlockNumber, + header::Header, + encoded, + engines::{OptimizeFor, params::CommonParams}, + errors::EthcoreError as Error, + transaction::{Action, Transaction}, +}; +use account_state::{Backend, State, backend::Basic as BasicBackend}; +use authority_round::AuthorityRound; +use basic_authority::BasicAuthority; +use bytes::Bytes; +use builtin::Builtin; +use clique::Clique; +use engine::Engine; +use ethash_engine::Ethash; +use ethereum_types::{H256, Bloom, U256, Address}; +use ethjson; +use instant_seal::{InstantSeal, InstantSealParams}; +use keccak_hash::{KECCAK_NULL_RLP, keccak}; +use log::{trace, warn}; +use machine::{executive::Executive, Machine, substate::Substate}; +use null_engine::NullEngine; +use pod::PodState; +use rlp::{Rlp, RlpStream}; +use trace::{NoopTracer, NoopVMTracer}; +use trie_vm_factories::Factories; +use vm::{EnvInfo, ActionType, ActionValue, ActionParams, ParamsType}; + +use crate::{ + Genesis, + seal::Generic as GenericSeal, +}; + +/// Runtime parameters for the spec that are related to how the software should run the chain, +/// rather than integral properties of the chain itself. +pub struct SpecParams<'a> { + /// The path to the folder used to cache nodes. This is typically /tmp/ on Unix-like systems + pub cache_dir: &'a Path, + /// Whether to run slower at the expense of better memory usage, or run faster while using + /// more + /// memory. This may get more fine-grained in the future but for now is simply a binary + /// option. + pub optimization_setting: Option, +} + +impl<'a> SpecParams<'a> { + /// Create from a cache path, with null values for the other fields + pub fn from_path(path: &'a Path) -> Self { + SpecParams { + cache_dir: path, + optimization_setting: None, + } + } + + /// Create from a cache path and an optimization setting + pub fn new(path: &'a Path, optimization: OptimizeFor) -> Self { + SpecParams { + cache_dir: path, + optimization_setting: Some(optimization), + } + } +} + +impl<'a, T: AsRef> From<&'a T> for SpecParams<'a> { + fn from(path: &'a T) -> Self { + Self::from_path(path.as_ref()) + } +} + +/// given a pre-constructor state, run all the given constructors and produce a new state and +/// state root. +fn run_constructors( + genesis_state: &PodState, + constructors: &[(Address, Bytes)], + engine: &dyn Engine, + author: Address, + timestamp: u64, + difficulty: U256, + factories: &Factories, + mut db: T +) -> Result<(H256, T), Error> { + let mut root = KECCAK_NULL_RLP; + + // basic accounts in spec. + { + let mut t = factories.trie.create(db.as_hash_db_mut(), &mut root); + + for (address, account) in genesis_state.get().iter() { + t.insert(address.as_bytes(), &account.rlp())?; + } + } + + for (address, account) in genesis_state.get().iter() { + db.note_non_null_account(address); + account.insert_additional( + &mut *factories.accountdb.create( + db.as_hash_db_mut(), + keccak(address), + ), + &factories.trie, + ); + } + + let start_nonce = engine.account_start_nonce(0); + + let mut state = State::from_existing(db, root, start_nonce, factories.clone())?; + if constructors.is_empty() { + state.populate_from(genesis_state.clone()); + let _ = state.commit()?; + } else { + // Execute contract constructors. + let env_info = EnvInfo { + number: 0, + author, + timestamp, + difficulty, + last_hashes: Default::default(), + gas_used: U256::zero(), + gas_limit: U256::max_value(), + }; + + let from = Address::zero(); + for &(ref address, ref constructor) in constructors.iter() { + trace!(target: "spec", "run_constructors: Creating a contract at {}.", address); + trace!(target: "spec", " .. root before = {}", state.root()); + let params = ActionParams { + code_address: address.clone(), + code_hash: Some(keccak(constructor)), + code_version: U256::zero(), + address: address.clone(), + sender: from.clone(), + origin: from.clone(), + gas: U256::max_value(), + gas_price: Default::default(), + value: ActionValue::Transfer(Default::default()), + code: Some(Arc::new(constructor.clone())), + data: None, + action_type: ActionType::Create, + params_type: ParamsType::Embedded, + }; + + let mut substate = Substate::new(); + + { + let machine = engine.machine(); + let schedule = machine.schedule(env_info.number); + let mut exec = Executive::new(&mut state, &env_info, &machine, &schedule); + // failing create is not a bug + if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { + warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e); + } + } + + let _ = state.commit()?; + } + } + Ok(state.drop()) +} + +/// 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 { + /// User friendly spec name. + pub name: String, + /// Engine specified by json file. + pub engine: Arc, + /// Name of the subdir inside the main data dir to use for chain data and settings. + pub data_dir: String, + /// Known nodes on the network in enode format. + pub nodes: Vec, + /// The genesis block's parent hash field. + pub parent_hash: H256, + /// The genesis block's author field. + pub author: Address, + /// The genesis block's difficulty field. + pub difficulty: U256, + /// The genesis block's gas limit field. + pub gas_limit: U256, + /// The genesis block's gas used field. + pub gas_used: U256, + /// The genesis block's timestamp field. + pub timestamp: u64, + /// Transactions root of the genesis block. Should be KECCAK_NULL_RLP. + pub transactions_root: H256, + /// Receipts root of the genesis block. Should be KECCAK_NULL_RLP. + pub receipts_root: H256, + /// The genesis block's extra data field. + pub extra_data: Bytes, + /// Each seal field, expressed as RLP, concatenated. + pub seal_rlp: Bytes, + /// Hardcoded synchronization. Allows the light client to immediately jump to a specific block. + pub hardcoded_sync: Option, + /// Contract constructors to be executed on genesis. + pub constructors: Vec<(Address, Bytes)>, + /// May be pre-populated if we know this in advance. + pub state_root: H256, + /// Genesis state as plain old data. + pub genesis_state: PodState, +} + +/// Part of `Spec`. Describes the hardcoded synchronization parameters. +pub struct SpecHardcodedSync { + /// Header of the block to jump to for hardcoded sync, and total difficulty. + pub header: encoded::Header, + /// Total difficulty of the block to jump to. + pub total_difficulty: U256, + /// List of hardcoded CHTs, in order. If `hardcoded_sync` is set, the CHTs should include the + /// header of `hardcoded_sync`. + pub chts: Vec, +} + +impl From for SpecHardcodedSync { + fn from(sync: ethjson::spec::HardcodedSync) -> Self { + SpecHardcodedSync { + header: encoded::Header::new(sync.header.into()), + total_difficulty: sync.total_difficulty.into(), + chts: sync.chts.into_iter().map(Into::into).collect(), + } + } +} + +impl fmt::Display for SpecHardcodedSync { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "{{")?; + writeln!(f, r#""header": "{:x}","#, self.header)?; + writeln!(f, r#""totalDifficulty": "{:?}""#, self.total_difficulty)?; + // TODO: #11415 - fix trailing comma for CHTs + writeln!(f, r#""CHTs": {:#?}"#, self.chts.iter().map(|x| format!("{:?}", x)).collect::>())?; + writeln!(f, "}}") + } +} + +fn convert_json_to_spec( + (address, builtin): (ethjson::hash::Address, ethjson::spec::builtin::Builtin), +) -> Result<(Address, Builtin), Error> { + let builtin = Builtin::try_from(builtin)?; + Ok((address.into(), builtin)) +} + +/// Load from JSON object. +fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result { + let builtins: Result, _> = s + .accounts + .builtins() + .into_iter() + .map(convert_json_to_spec) + .collect(); + let builtins = builtins?; + let g = Genesis::from(s.genesis); + let GenericSeal(seal_rlp) = g.seal.into(); + let params = CommonParams::from(s.params); + + let hardcoded_sync = s.hardcoded_sync.map(Into::into); + + let engine = Spec::engine(spec_params, s.engine, params, builtins); + let author = g.author; + let timestamp = g.timestamp; + let difficulty = g.difficulty; + let constructors: Vec<_> = s.accounts + .constructors() + .into_iter() + .map(|(a, c)| (a.into(), c.into())) + .collect(); + let genesis_state: PodState = s.accounts.into(); + + let (state_root, _) = run_constructors( + &genesis_state, + &constructors, + &*engine, + author, + timestamp, + difficulty, + &Default::default(), + BasicBackend(journaldb::new_memory_db()), + )?; + + let s = Spec { + engine, + name: s.name.clone().into(), + data_dir: s.data_dir.unwrap_or(s.name).into(), + nodes: s.nodes.unwrap_or_else(Vec::new), + parent_hash: g.parent_hash, + transactions_root: g.transactions_root, + receipts_root: g.receipts_root, + author, + difficulty, + gas_limit: g.gas_limit, + gas_used: g.gas_used, + timestamp, + extra_data: g.extra_data, + seal_rlp, + hardcoded_sync, + constructors, + genesis_state, + state_root, + }; + + Ok(s) +} + +impl Spec { + // create an instance of an Ethereum state machine, minus consensus logic. + fn machine( + engine_spec: ðjson::spec::Engine, + params: CommonParams, + builtins: BTreeMap, + ) -> Machine { + if let ethjson::spec::Engine::Ethash(ref ethash) = *engine_spec { + Machine::with_ethash_extensions(params, builtins, ethash.params.clone().into()) + } else { + Machine::regular(params, builtins) + } + } + + /// Convert engine spec into a arc'd Engine of the right underlying type. + /// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead. + fn engine( + spec_params: SpecParams, + engine_spec: ethjson::spec::Engine, + params: CommonParams, + builtins: BTreeMap, + ) -> Arc { + let machine = Self::machine(&engine_spec, params, builtins); + + match engine_spec { + ethjson::spec::Engine::Null(null) => Arc::new(NullEngine::new(null.params.into(), machine)), + ethjson::spec::Engine::Ethash(ethash) => Arc::new(Ethash::new(spec_params.cache_dir, ethash.params.into(), machine, spec_params.optimization_setting)), + ethjson::spec::Engine::InstantSeal(Some(instant_seal)) => Arc::new(InstantSeal::new(instant_seal.params.into(), machine)), + ethjson::spec::Engine::InstantSeal(None) => Arc::new(InstantSeal::new(InstantSealParams::default(), machine)), + ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)), + ethjson::spec::Engine::Clique(clique) => Clique::new(clique.params.into(), machine) + .expect("Failed to start Clique consensus engine."), + ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(authority_round.params.into(), machine) + .expect("Failed to start AuthorityRound consensus engine."), + } + } + + /// Get common blockchain parameters. + pub fn params(&self) -> &CommonParams { + &self.engine.params() + } + + /// Get the configured Network ID. + pub fn network_id(&self) -> u64 { + self.params().network_id + } + + /// Get the chain ID used for signing. + pub fn chain_id(&self) -> u64 { + self.params().chain_id + } + + /// Get the configured subprotocol name. + pub fn subprotocol_name(&self) -> String { + self.params().subprotocol_name.clone() + } + + /// Get the configured network fork block. + pub fn fork_block(&self) -> Option<(BlockNumber, H256)> { + self.params().fork_block + } + + /// Get the header of the genesis block. + pub fn genesis_header(&self) -> Header { + let mut header: Header = Default::default(); + header.set_parent_hash(self.parent_hash.clone()); + header.set_timestamp(self.timestamp); + header.set_number(0); + header.set_author(self.author.clone()); + header.set_transactions_root(self.transactions_root.clone()); + header.set_uncles_hash(keccak(RlpStream::new_list(0).out())); + header.set_extra_data(self.extra_data.clone()); + header.set_state_root(self.state_root); + header.set_receipts_root(self.receipts_root.clone()); + header.set_log_bloom(Bloom::default()); + header.set_gas_used(self.gas_used.clone()); + header.set_gas_limit(self.gas_limit.clone()); + header.set_difficulty(self.difficulty.clone()); + header.set_seal({ + let r = Rlp::new(&self.seal_rlp); + r.iter().map(|f| f.as_raw().to_vec()).collect() + }); + trace!(target: "spec", "Header hash is {}", header.hash()); + header + } + + /// Compose the genesis block for this chain. + pub fn genesis_block(&self) -> Bytes { + let empty_list = RlpStream::new_list(0).out(); + let header = self.genesis_header(); + let mut ret = RlpStream::new_list(3); + ret.append(&header); + ret.append_raw(&empty_list, 1); + ret.append_raw(&empty_list, 1); + ret.out() + } + + /// Overwrite the genesis components. + pub fn overwrite_genesis_params(&mut self, g: Genesis) { + let GenericSeal(seal_rlp) = g.seal.into(); + self.parent_hash = g.parent_hash; + self.transactions_root = g.transactions_root; + self.receipts_root = g.receipts_root; + self.author = g.author; + self.difficulty = g.difficulty; + self.gas_limit = g.gas_limit; + self.gas_used = g.gas_used; + self.timestamp = g.timestamp; + self.extra_data = g.extra_data; + self.seal_rlp = seal_rlp; + } + + /// Alter the value of the genesis state. + pub fn set_genesis_state(&mut self, s: PodState) -> Result<(), Error> { + self.genesis_state = s; + let (root, _) = run_constructors( + &self.genesis_state, + &self.constructors, + &*self.engine, + self.author, + self.timestamp, + self.difficulty, + &Default::default(), + BasicBackend(journaldb::new_memory_db()), + )?; + + self.state_root = root; + Ok(()) + } + + /// Ensure that the given state DB has the trie nodes in for the genesis state. + pub fn ensure_db_good(&self, db: T, factories: &Factories) -> Result { + if db.as_hash_db().contains(&self.state_root, hash_db::EMPTY_PREFIX) { + return Ok(db); + } + + // TODO: could optimize so we don't re-run, but `ensure_db_good` is barely ever + // called anyway. + let (root, db) = run_constructors( + &self.genesis_state, + &self.constructors, + &*self.engine, + self.author, + self.timestamp, + self.difficulty, + factories, + db + )?; + + assert_eq!(root, self.state_root, "Spec's state root has not been precomputed correctly."); + Ok(db) + } + + /// Loads just the state machine from a json file. + pub fn load_machine(reader: R) -> Result { + ethjson::spec::Spec::load(reader) + .map_err(|e| Error::Msg(e.to_string())) + .and_then(|s| { + let builtins: Result, _> = s + .accounts + .builtins() + .into_iter() + .map(convert_json_to_spec) + .collect(); + let builtins = builtins?; + let params = CommonParams::from(s.params); + Ok(Spec::machine(&s.engine, params, builtins)) + }) + } + + /// Loads spec from json file. Provide factories for executing contracts and ensuring + /// storage goes to the right place. + pub fn load<'a, T: Into>, R: Read>(params: T, reader: R) -> Result { + ethjson::spec::Spec::load(reader) + .map_err(|e| Error::Msg(e.to_string())) + .and_then(|x| load_from(params.into(), x)) + } + + /// initialize genesis epoch data, using in-memory database for + /// constructor. + pub fn genesis_epoch_data(&self) -> Result, String> { + let genesis = self.genesis_header(); + + let factories = Default::default(); + let mut db = journaldb::new( + Arc::new(kvdb_memorydb::create(1)), + journaldb::Algorithm::Archive, + 0, + ); + + self.ensure_db_good(BasicBackend(db.as_hash_db_mut()), &factories) + .map_err(|e| format!("Unable to initialize genesis state: {}", e))?; + + let call = |a, d| { + let mut db = db.boxed_clone(); + let env_info = evm::EnvInfo { + number: 0, + author: *genesis.author(), + timestamp: genesis.timestamp(), + difficulty: *genesis.difficulty(), + gas_limit: U256::max_value(), + last_hashes: Arc::new(Vec::new()), + gas_used: 0.into(), + }; + + let from = Address::zero(); + let tx = Transaction { + nonce: self.engine.account_start_nonce(0), + action: Action::Call(a), + gas: U256::max_value(), + gas_price: U256::default(), + value: U256::default(), + data: d, + }.fake_sign(from); + + executive_state::prove_transaction_virtual( + db.as_hash_db_mut(), + *genesis.state_root(), + &tx, + self.engine.machine(), + &env_info, + factories.clone(), + ).ok_or_else(|| "Failed to prove call: insufficient state".into()) + }; + + self.engine.genesis_epoch_data(&genesis, &call) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use account_state::State; + use common_types::{view, views::BlockView}; + use ethereum_types::{Address, H256}; + use ethcore::test_helpers::get_temp_state_db; + use tempdir::TempDir; + + use super::Spec; + + #[test] + fn test_load_empty() { + let tempdir = TempDir::new("").unwrap(); + assert!(Spec::load(&tempdir.path(), &[] as &[u8]).is_err()); + } + + #[test] + fn test_chain() { + let test_spec = crate::new_test(); + + assert_eq!( + test_spec.state_root, + H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap() + ); + let genesis = test_spec.genesis_block(); + assert_eq!( + view!(BlockView, &genesis).header_view().hash(), + H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap() + ); + } + + #[test] + fn genesis_constructor() { + let _ = ::env_logger::try_init(); + let spec = crate::new_test_constructor(); + let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()) + .unwrap(); + let state = State::from_existing( + db.boxed_clone(), + spec.state_root, + spec.engine.account_start_nonce(0), + Default::default(), + ).unwrap(); + let expected = H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(); + let address = Address::from_str("0000000000000000000000000000000000001337").unwrap(); + + assert_eq!(state.storage_at(&address, &H256::zero()).unwrap(), expected); + assert_eq!(state.balance(&address).unwrap(), 1.into()); + } +} diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 56cfc1c4c1..381e218755 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,38 +22,41 @@ //! and can be appended to with transactions and uncles. //! //! When ready, `OpenBlock` can be closed and turned into a `ClosedBlock`. A `ClosedBlock` can -//! be reopend again by a miner under certain circumstances. On block close, state commit is +//! be re-opened again by a miner under certain circumstances. On block close, state commit is //! performed. //! //! `LockedBlock` is a version of a `ClosedBlock` that cannot be reopened. It can be sealed //! using an engine. //! -//! `ExecutedBlock` is an underlaying data structure used by all structs above to store block -//! related info. +//! `ExecutedBlock` from the `machine` crate is the underlying data structure used by all structs +//! above to store block related info. use std::{cmp, ops}; -use std::collections::HashSet; use std::sync::Arc; use bytes::Bytes; use ethereum_types::{H256, U256, Address, Bloom}; -use engines::EthEngine; -use error::{Error, BlockError}; -use factory::Factories; +use engine::Engine; +use trie_vm_factories::Factories; use state_db::StateDB; -use state::State; +use account_state::State; use trace::Tracing; use triehash::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; -use verification::PreverifiedBlock; -use vm::{EnvInfo, LastHashes}; +use vm::LastHashes; use hash::keccak; use rlp::{RlpStream, Encodable, encode_list}; -use types::transaction::{SignedTransaction, Error as TransactionError}; -use types::header::{Header, ExtendedHeader}; -use types::receipt::{Receipt, TransactionOutcome}; +use types::{ + block::PreverifiedBlock, + errors::{EthcoreError as Error, BlockError}, + transaction::{SignedTransaction, Error as TransactionError}, + header::Header, + receipt::{Receipt, TransactionOutcome}, +}; +use executive_state::ExecutiveState; +use machine::ExecutedBlock; /// Block that is ready for transactions to be added. /// @@ -61,7 +64,8 @@ use types::receipt::{Receipt, TransactionOutcome}; /// maintain the system `state()`. We also archive execution receipts in preparation for later block creation. pub struct OpenBlock<'x> { block: ExecutedBlock, - engine: &'x EthEngine, + engine: &'x dyn Engine, + parent: Header, } /// Just like `OpenBlock`, except that we've applied `Engine::on_close_block`, finished up the non-seal header fields, @@ -72,6 +76,7 @@ pub struct OpenBlock<'x> { pub struct ClosedBlock { block: ExecutedBlock, unclosed_state: State, + parent: Header, } /// Just like `ClosedBlock` except that we can't reopen it and it's faster. @@ -89,71 +94,6 @@ pub struct SealedBlock { block: ExecutedBlock, } -/// An internal type for a block's common elements. -#[derive(Clone)] -pub struct ExecutedBlock { - /// Executed block header. - pub header: Header, - /// Executed transactions. - pub transactions: Vec, - /// Uncles. - pub uncles: Vec
, - /// Transaction receipts. - pub receipts: Vec, - /// Hashes of already executed transactions. - pub transactions_set: HashSet, - /// Underlaying state. - pub state: State, - /// Transaction traces. - pub traces: Tracing, - /// Hashes of last 256 blocks. - pub last_hashes: Arc, -} - -impl ExecutedBlock { - /// Create a new block from the given `state`. - fn new(state: State, last_hashes: Arc, tracing: bool) -> ExecutedBlock { - ExecutedBlock { - header: Default::default(), - transactions: Default::default(), - uncles: Default::default(), - receipts: Default::default(), - transactions_set: Default::default(), - state: state, - traces: if tracing { - Tracing::enabled() - } else { - Tracing::Disabled - }, - last_hashes: last_hashes, - } - } - - /// Get the environment info concerning this block. - pub fn env_info(&self) -> EnvInfo { - // TODO: memoise. - EnvInfo { - number: self.header.number(), - author: self.header.author().clone(), - timestamp: self.header.timestamp(), - difficulty: self.header.difficulty().clone(), - last_hashes: self.last_hashes.clone(), - gas_used: self.receipts.last().map_or(U256::zero(), |r| r.gas_used), - gas_limit: self.header.gas_limit().clone(), - } - } - - /// Get mutable access to a state. - pub fn state_mut(&mut self) -> &mut State { - &mut self.state - } - - /// Get mutable reference to traces. - pub fn traces_mut(&mut self) -> &mut Tracing { - &mut self.traces - } -} - /// Trait for an object that owns an `ExecutedBlock` pub trait Drain { /// Returns `ExecutedBlock` @@ -162,8 +102,8 @@ pub trait Drain { impl<'x> OpenBlock<'x> { /// Create a new `OpenBlock` ready for transaction pushing. - pub fn new<'a, I: IntoIterator>( - engine: &'x EthEngine, + pub fn new<'a>( + engine: &'x dyn Engine, factories: Factories, tracing: bool, db: StateDB, @@ -173,14 +113,10 @@ impl<'x> OpenBlock<'x> { gas_range_target: (U256, U256), extra_data: Bytes, is_epoch_begin: bool, - ancestry: I, ) -> Result { let number = parent.number() + 1; let state = State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(number), factories)?; - let mut r = OpenBlock { - block: ExecutedBlock::new(state, last_hashes, tracing), - engine: engine, - }; + let mut r = OpenBlock { block: ExecutedBlock::new(state, last_hashes, tracing), engine, parent: parent.clone()}; r.block.header.set_parent_hash(parent.hash()); r.block.header.set_number(number); @@ -195,7 +131,7 @@ 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, &mut ancestry.into_iter())?; + engine.on_new_block(&mut r.block, is_epoch_begin)?; Ok(r) } @@ -271,7 +207,7 @@ impl<'x> OpenBlock<'x> { 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, self.block.header().number(), hash); + warn!("Heavy ({} ms) transaction in block {:?}: {:?}", took_ms, self.block.header.number(), hash); } debug!(target: "tx", "Transaction {:?} took: {} ms", hash, took_ms); } @@ -297,19 +233,20 @@ impl<'x> OpenBlock<'x> { /// Turn this into a `ClosedBlock`. pub fn close(self) -> Result { let unclosed_state = self.block.state.clone(); + let parent = self.parent.clone(); let locked = self.close_and_lock()?; Ok(ClosedBlock { block: locked.block, unclosed_state, + parent, }) } /// Turn this into a `LockedBlock`. pub fn close_and_lock(self) -> Result { let mut s = self; - - s.engine.on_close_block(&mut s.block)?; + s.engine.on_close_block(&mut s.block, &s.parent)?; s.block.state.commit()?; s.block.header.set_transactions_root(ordered_trie_root(s.block.transactions.iter().map(|e| e.rlp_bytes()))); @@ -328,7 +265,7 @@ impl<'x> OpenBlock<'x> { }) } - #[cfg(test)] + #[cfg(any(test, feature = "test-helpers"))] /// Return mutable block reference. To be used in tests only. pub fn block_mut(&mut self) -> &mut ExecutedBlock { &mut self.block } } @@ -374,14 +311,12 @@ impl ClosedBlock { } /// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`. - pub fn reopen(self, engine: &EthEngine) -> OpenBlock { + pub fn reopen(self, engine: &dyn Engine) -> OpenBlock { // revert rewards (i.e. set state back at last transaction's state). let mut block = self.block; block.state = self.unclosed_state; - OpenBlock { - block: block, - engine: engine, - } + let parent = self.parent; + OpenBlock { block, engine, parent } } } @@ -404,7 +339,7 @@ impl LockedBlock { /// Provide a valid seal in order to turn this into a `SealedBlock`. /// /// NOTE: This does not check the validity of `seal` with the engine. - pub fn seal(self, engine: &EthEngine, seal: Vec) -> Result { + pub fn seal(self, engine: &dyn Engine, seal: Vec) -> Result { let expected_seal_fields = engine.seal_fields(&self.header); let mut s = self; if seal.len() != expected_seal_fields { @@ -429,7 +364,7 @@ impl LockedBlock { /// TODO(https://github.com/paritytech/parity-ethereum/issues/10407): This is currently only used in POW chain call paths, we should really merge it with seal() above. pub fn try_seal( self, - engine: &EthEngine, + engine: &dyn Engine, seal: Vec, ) -> Result { let mut s = self; @@ -472,14 +407,13 @@ pub(crate) fn enact( header: Header, transactions: Vec, uncles: Vec
, - engine: &EthEngine, + engine: &dyn Engine, tracing: bool, db: StateDB, parent: &Header, last_hashes: Arc, factories: Factories, is_epoch_begin: bool, - ancestry: &mut Iterator, ) -> Result { // For trace log let trace_state = if log_enabled!(target: "enact", ::log::Level::Trace) { @@ -501,7 +435,6 @@ pub(crate) fn enact( (3141562.into(), 31415620.into()), vec![], is_epoch_begin, - ancestry, )?; if let Some(ref s) = trace_state { @@ -522,17 +455,16 @@ pub(crate) fn enact( b.close_and_lock() } -/// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header +/// Enact the block given by `block_bytes` using `engine` on the database `db` with the given `parent` block header pub fn enact_verified( block: PreverifiedBlock, - engine: &EthEngine, + engine: &dyn Engine, tracing: bool, db: StateDB, parent: &Header, last_hashes: Arc, factories: Factories, is_epoch_begin: bool, - ancestry: &mut Iterator, ) -> Result { enact( @@ -546,7 +478,6 @@ pub fn enact_verified( last_hashes, factories, is_epoch_begin, - ancestry, ) } @@ -554,23 +485,27 @@ pub fn enact_verified( mod tests { use test_helpers::get_temp_state_db; use super::*; - use engines::EthEngine; + use engine::Engine; use vm::LastHashes; - use error::Error; - use factory::Factories; + use trie_vm_factories::Factories; use state_db::StateDB; use ethereum_types::Address; use std::sync::Arc; - use verification::queue::kind::blocks::Unverified; - use types::transaction::SignedTransaction; - use types::header::Header; - use types::view; - use types::views::BlockView; + use types::{ + errors::EthcoreError as Error, + header::Header, + transaction::SignedTransaction, + view, + views::BlockView, + verification::Unverified, + }; + use hash_db::EMPTY_PREFIX; + use spec; /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header fn enact_bytes( block_bytes: Vec, - engine: &EthEngine, + engine: &dyn Engine, tracing: bool, db: StateDB, parent: &Header, @@ -603,11 +538,10 @@ mod tests { db, parent, last_hashes, - Address::new(), + Address::zero(), (3141562.into(), 31415620.into()), vec![], false, - None, )?; b.populate_from(&header); @@ -623,7 +557,7 @@ mod tests { /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards fn enact_and_seal( block_bytes: Vec, - engine: &EthEngine, + engine: &dyn Engine, tracing: bool, db: StateDB, parent: &Header, @@ -637,26 +571,24 @@ mod tests { #[test] fn open_block() { - use spec::*; - let spec = Spec::new_test(); + let spec = spec::new_test(); 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, None).unwrap(); + 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 = b.close_and_lock().unwrap(); let _ = b.seal(&*spec.engine, vec![]); } #[test] fn enact_block() { - use spec::*; - let spec = Spec::new_test(); + let spec = spec::new_test(); let engine = &*spec.engine; 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.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false, None).unwrap() + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap() .close_and_lock().unwrap().seal(engine, vec![]).unwrap(); let orig_bytes = b.rlp_bytes(); let orig_db = b.drain().state.drop().1; @@ -668,19 +600,19 @@ mod tests { let db = e.drain().state.drop().1; assert_eq!(orig_db.journal_db().keys(), db.journal_db().keys()); - assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)).next() == None); + assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0, EMPTY_PREFIX) + != db.journal_db().get(k.0, EMPTY_PREFIX)).next() == None); } #[test] fn enact_block_with_uncle() { - use spec::*; - let spec = Spec::new_test(); + let spec = spec::new_test(); let engine = &*spec.engine; 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 open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![], false, None).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).unwrap(); let mut uncle1_header = Header::new(); uncle1_header.set_extra_data(b"uncle1".to_vec()); let mut uncle2_header = Header::new(); @@ -702,6 +634,7 @@ mod tests { let db = e.drain().state.drop().1; assert_eq!(orig_db.journal_db().keys(), db.journal_db().keys()); - assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0) != db.journal_db().get(k.0)).next() == None); + assert!(orig_db.journal_db().keys().iter().filter(|k| orig_db.journal_db().get(k.0, EMPTY_PREFIX) + != db.journal_db().get(k.0, EMPTY_PREFIX)).next() == None); } } diff --git a/ethcore/src/client/ancient_import.rs b/ethcore/src/client/ancient_import.rs index 2a0a970cd6..87c1ed3979 100644 --- a/ethcore/src/client/ancient_import.rs +++ b/ethcore/src/client/ancient_import.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,13 +18,15 @@ use std::sync::Arc; -use engines::{EthEngine, EpochVerifier}; -use machine::EthereumMachine; +use engine::{Engine, EpochVerifier}; use blockchain::BlockChain; use parking_lot::RwLock; use rand::Rng; -use types::header::Header; +use types::{ + header::Header, + errors::EthcoreError, +}; // do "heavy" verification on ~1/50 blocks, randomly sampled. const HEAVY_VERIFY_RATE: f32 = 0.02; @@ -32,13 +34,13 @@ 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>>>, - engine: Arc, + cur_verifier: RwLock>>, + engine: Arc, } impl AncientVerifier { /// Create a new ancient block verifier with the given engine. - pub fn new(engine: Arc) -> Self { + pub fn new(engine: Arc) -> Self { AncientVerifier { cur_verifier: RwLock::new(None), engine, @@ -52,7 +54,7 @@ impl AncientVerifier { rng: &mut R, header: &Header, chain: &BlockChain, - ) -> Result<(), ::error::Error> { + ) -> Result<(), EthcoreError> { // perform verification let verified = if let Some(ref cur_verifier) = *self.cur_verifier.read() { match rng.gen::() <= HEAVY_VERIFY_RATE { @@ -87,7 +89,7 @@ impl AncientVerifier { } fn initial_verifier(&self, header: &Header, chain: &BlockChain) - -> Result>, ::error::Error> + -> Result, EthcoreError> { trace!(target: "client", "Initializing ancient block restoration."); let current_epoch_data = chain.epoch_transitions() diff --git a/ethcore/src/client/bad_blocks.rs b/ethcore/src/client/bad_blocks.rs index 6af24cc409..a08c0851a5 100644 --- a/ethcore/src/client/bad_blocks.rs +++ b/ethcore/src/client/bad_blocks.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use ethereum_types::H256; use itertools::Itertools; use memory_cache::MemoryLruCache; use parking_lot::RwLock; -use verification::queue::kind::blocks::Unverified; +use types::verification::Unverified; /// Recently seen bad blocks. pub struct BadBlocks { diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 12372b83ee..699f543eaf 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,80 +15,128 @@ // along with Parity Ethereum. If not, see . use std::cmp; -use std::collections::{HashSet, BTreeMap, VecDeque}; -use std::str::FromStr; -use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; +use std::collections::{BTreeMap, HashSet, VecDeque}; +use std::convert::TryFrom; +use std::io::{BufRead, BufReader}; +use std::str::from_utf8; use std::sync::{Arc, Weak}; -use std::time::{Instant, Duration}; +use std::sync::atomic::{AtomicBool, AtomicI64, Ordering as AtomicOrdering, Ordering, AtomicU64}; +use std::time::{Duration, Instant}; -use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert, BlockNumberKey}; +use ansi_term::Colour; use bytes::Bytes; -use call_contract::{CallContract, RegistryInfo}; -use ethcore_miner::pool::VerifiedTransaction; -use ethereum_types::{H256, H264, Address, U256}; -use evm::Schedule; +use bytes::ToPretty; +use ethereum_types::{Address, H256, H264, U256}; use hash::keccak; -use io::IoChannel; +use hash_db::EMPTY_PREFIX; use itertools::Itertools; -use journaldb; -use kvdb::{DBValue, KeyValueDB, DBTransaction}; +use kvdb::{DBTransaction, DBValue, KeyValueDB}; use parking_lot::{Mutex, RwLock}; -use rand::OsRng; -use types::transaction::{self, LocalizedTransaction, UnverifiedTransaction, SignedTransaction, Action}; -use trie::{TrieSpec, TrieFactory, Trie}; -use types::ancestry_action::AncestryAction; -use types::encoded; -use types::filter::Filter; -use types::log_entry::LocalizedLogEntry; -use types::receipt::{Receipt, LocalizedReceipt}; -use types::{BlockNumber, header::{Header, ExtendedHeader}}; -use vm::{EnvInfo, LastHashes}; - -use block::{LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock}; -use client::ancient_import::AncientVerifier; -use client::{ - Nonce, Balance, ChainInfo, BlockInfo, TransactionInfo, - ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock, - BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call, - AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter, - ClientIoMessage, BlockChainReset +use rand::rngs::OsRng; +use rlp::PayloadInfo; +use rustc_hex::FromHex; +use trie::{Trie, TrieFactory, TrieSpec}; + +use account_state::State; +use account_state::state::StateInfo; +use block::{ClosedBlock, Drain, enact_verified, LockedBlock, OpenBlock, SealedBlock}; +use blockchain::{ + BlockChain, + BlockChainDB, + BlockNumberKey, + BlockProvider, + BlockReceipts, + CacheSize as BlockChainCacheSize, + ExtrasInsert, + TransactionAddress, + TreeRoute }; +use call_contract::CallContract; use client::{ - BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, - TraceFilter, CallAnalytics, Mode, - ChainNotify, NewBlocks, ChainRoute, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType, - IoClient, BadBlocks, + bad_blocks, BlockProducer, BroadcastProposalBlock, Call, + ClientConfig, EngineInfo, ImportSealedBlock, PrepareOpenBlock, + ReopenBlock, SealedBlockImporter, +}; +use client::ancient_import::AncientVerifier; +use client_traits::{ + AccountData, + BadBlocks, + Balance, + BlockChain as BlockChainTrait, + BlockChainClient, + BlockChainReset, + BlockInfo, + ChainInfo, + ChainNotify, + DatabaseRestore, + ImportBlock, + ImportExportBlocks, + IoClient, + Nonce, + ProvingBlockChainClient, + ScheduleInfo, + StateClient, + StateOrBlock, + Tick, + TransactionInfo, + TransactionRequest, + ForceUpdateSealing }; -use client::bad_blocks; -use engines::{MAX_UNCLE_AGE, EthEngine, EpochTransition, ForkChoice, EngineError}; -use engines::epoch::PendingTransition; -use error::{ - ImportErrorKind, ExecutionError, CallError, BlockError, - QueueError, QueueErrorKind, Error as EthcoreError, EthcoreResult, ErrorKind as EthcoreErrorKind +use db::{keys::BlockDetails, Readable, Writable}; +use engine::Engine; +use ethcore_miner::pool::VerifiedTransaction; +use ethtrie::Layout; +use evm::Schedule; +use executive_state; +use io::IoChannel; +use journaldb; +use machine::{ + executed::Executed, + executive::{contract_address, Executive, TransactOptions}, + transaction_ext::Transaction, }; -use executive::{Executive, Executed, TransactOptions, contract_address}; -use factory::{Factories, VmFactory}; -use miner::{Miner, MinerService}; -use snapshot::{self, io as snapshot_io, SnapshotClient}; +use miner::{Miner, MinerService, PendingOrdering}; +use registrar::RegistrarClient; +use snapshot::{self, SnapshotClient, SnapshotWriter}; use spec::Spec; -use state::{self, State}; use state_db::StateDB; -use trace::{self, TraceDB, ImportRequest as TraceImportRequest, LocalizedTrace, Database as TraceDatabase}; -use transaction_ext::Transaction; +use trace::{self, Database as TraceDatabase, ImportRequest as TraceImportRequest, LocalizedTrace, TraceDB}; +use trie_vm_factories::{Factories, VmFactory}; +use types::{ + ancestry_action::AncestryAction, + block::PreverifiedBlock, + block_status::BlockStatus, + blockchain_info::BlockChainInfo, + BlockNumber, + call_analytics::CallAnalytics, + chain_notify::{ChainMessageType, ChainRoute, NewBlocks}, + client_types::{ClientReport, Mode, StateResult}, + encoded, + engines::{ + epoch::{PendingTransition, Transition as EpochTransition}, + ForkChoice, + machine::{AuxiliaryData, Call as MachineCall}, + MAX_UNCLE_AGE, + SealingState, + }, + errors::{BlockError, EngineError, EthcoreError, EthcoreResult, ExecutionError, ImportError, SnapshotError}, + filter::Filter, + header::Header, + ids::{BlockId, TraceId, TransactionId, UncleId}, + import_route::ImportRoute, + io_message::ClientIoMessage, + log_entry::LocalizedLogEntry, + pruning_info::PruningInfo, + receipt::{LocalizedReceipt, Receipt}, + snapshot::{Progress, Snapshotting}, + trace_filter::Filter as TraceFilter, + transaction::{self, Action, CallError, LocalizedTransaction, SignedTransaction, UnverifiedTransaction}, + verification::{Unverified, VerificationQueueInfo as BlockQueueInfo}, +}; +use types::data_format::DataFormat; +use verification::{self, BlockQueue}; use verification::queue::kind::BlockLike; -use verification::queue::kind::blocks::Unverified; -use verification::{PreverifiedBlock, Verifier, BlockQueue}; -use verification; -use ansi_term::Colour; - -// re-export -pub use types::blockchain_info::BlockChainInfo; -pub use types::block_status::BlockStatus; -pub use blockchain::CacheSize as BlockChainCacheSize; -pub use verification::QueueInfo as BlockQueueInfo; -use db::{Writable, Readable, keys::BlockDetails}; - -use_contract!(registry, "res/contracts/registrar.json"); +use vm::{CreateContractAddress, EnvInfo, LastHashes}; const MAX_ANCIENT_BLOCKS_QUEUE_SIZE: usize = 4096; // Max number of blocks imported at once. @@ -96,44 +144,6 @@ const MAX_ANCIENT_BLOCKS_TO_IMPORT: usize = 4; const MAX_QUEUE_SIZE_TO_SLEEP_ON: usize = 2; const MIN_HISTORY_SIZE: u64 = 8; -/// Report on the status of a client. -#[derive(Default, Clone, Debug, Eq, PartialEq)] -pub struct ClientReport { - /// How many blocks have been imported so far. - pub blocks_imported: usize, - /// How many transactions have been applied so far. - pub transactions_applied: usize, - /// How much gas has been processed so far. - pub gas_processed: U256, - /// Memory used by state DB - pub state_db_mem: usize, -} - -impl ClientReport { - /// Alter internal reporting to reflect the additional `block` has been processed. - pub fn accrue_block(&mut self, header: &Header, transactions: usize) { - self.blocks_imported += 1; - self.transactions_applied += transactions; - self.gas_processed = self.gas_processed + *header.gas_used(); - } -} - -impl<'a> ::std::ops::Sub<&'a ClientReport> for ClientReport { - type Output = Self; - - fn sub(mut self, other: &'a ClientReport) -> Self { - let higher_mem = ::std::cmp::max(self.state_db_mem, other.state_db_mem); - let lower_mem = ::std::cmp::min(self.state_db_mem, other.state_db_mem); - - 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 - } -} - struct SleepState { last_activity: Option, last_autosleep: Option, @@ -152,11 +162,8 @@ struct Importer { /// Lock used during block import pub import_lock: Mutex<()>, // FIXME Maybe wrap the whole `Importer` instead? - /// Used to verify blocks - pub verifier: Box>, - /// Queue containing pending blocks - pub block_queue: BlockQueue, + pub block_queue: BlockQueue, /// Handles block sealing pub miner: Arc, @@ -165,7 +172,7 @@ struct Importer { pub ancient_verifier: AncientVerifier, /// Ethereum engine to be used during import - pub engine: Arc, + pub engine: Arc, /// A lru cache of recently detected bad blocks pub bad_blocks: bad_blocks::BadBlocks, @@ -187,7 +194,7 @@ pub struct Client { chain: RwLock>, tracedb: RwLock>, - engine: Arc, + engine: Arc, /// Client configuration config: ClientConfig, @@ -195,8 +202,11 @@ pub struct Client { /// Database pruning strategy to use for StateDB pruning: journaldb::Algorithm, + /// Don't prune the state we're currently snapshotting + snapshotting_at: AtomicU64, + /// Client uses this to store blocks, traces, etc. - db: RwLock>, + db: RwLock>, state_db: RwLock, @@ -207,10 +217,10 @@ pub struct Client { /// Flag changed by `sleep` and `wake_up` methods. Not to be confused with `enabled`. liveness: AtomicBool, - io_channel: RwLock>, + io_channel: RwLock>>, /// List of actors to be notified on certain chain events - notify: RwLock>>, + notify: RwLock>>, /// Queued transactions from IO queue_transactions: IoChannelQueue, @@ -232,12 +242,12 @@ pub struct Client { history: u64, /// An action to be done if a mode/spec_name change happens - on_user_defaults_change: Mutex) + 'static + Send>>>, + on_user_defaults_change: Mutex) + 'static + Send>>>, registrar_address: Option
, /// A closure to call when we want to restart the client - exit_handler: Mutex>>, + exit_handler: Mutex>>, importer: Importer, } @@ -245,15 +255,19 @@ pub struct Client { impl Importer { pub fn new( config: &ClientConfig, - engine: Arc, - message_channel: IoChannel, + engine: Arc, + message_channel: IoChannel>, miner: Arc, - ) -> Result { - let block_queue = BlockQueue::new(config.queue.clone(), engine.clone(), message_channel.clone(), config.verifier_type.verifying_seal()); + ) -> Result { + let block_queue = BlockQueue::new( + config.queue.clone(), + engine.clone(), + message_channel, + config.verifier_type.verifying_seal() + ); Ok(Importer { import_lock: Mutex::new(()), - verifier: verification::new(config.verifier_type.clone()), block_queue, miner, ancient_verifier: AncientVerifier::new(engine.clone()), @@ -273,7 +287,7 @@ impl Importer { let (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration, has_more_blocks_to_import) = { let mut imported_blocks = Vec::with_capacity(max_blocks_to_import); let mut invalid_blocks = HashSet::new(); - let mut proposed_blocks = Vec::with_capacity(max_blocks_to_import); + let proposed_blocks = Vec::with_capacity(max_blocks_to_import); let mut import_results = Vec::with_capacity(max_blocks_to_import); let _import_lock = self.import_lock.lock(); @@ -357,7 +371,7 @@ impl Importer { let best_block_number = client.chain.read().best_block_number(); if client.pruning_info().earliest_state > header.number() { warn!(target: "client", "Block import failed for #{} ({})\nBlock is ancient (current best block: #{}).", header.number(), header.hash(), best_block_number); - bail!("Block is ancient"); + return Err("Block is ancient".into()); } // Check if parent is in chain @@ -365,36 +379,36 @@ impl Importer { Some(h) => h, None => { warn!(target: "client", "Block import failed for #{} ({}): Parent not found ({}) ", header.number(), header.hash(), header.parent_hash()); - bail!("Parent not found"); + return Err("Parent not found".into()); } }; let chain = client.chain.read(); // Verify Block Family - let verify_family_result = self.verifier.verify_block_family( + let verify_family_result = verification::verify_block_family( &header, &parent, engine, - Some(verification::FullFamilyParams { + verification::FullFamilyParams { block: &block, block_provider: &**chain, client - }), + }, ); if let Err(e) = verify_family_result { warn!(target: "client", "Stage 3 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - bail!(e); + return Err(e); }; - let verify_external_result = self.verifier.verify_block_external(&header, engine); + let verify_external_result = engine.verify_block_external(&header); if let Err(e) = verify_external_result { warn!(target: "client", "Stage 4 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - bail!(e); + return Err(e); }; // Enact Verified Block - let last_hashes = client.build_last_hashes(header.parent_hash()); + let last_hashes = client.build_last_hashes(*header.parent_hash()); 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(); @@ -408,14 +422,13 @@ impl Importer { last_hashes, client.factories.clone(), is_epoch_begin, - &mut chain.ancestry_with_metadata_iter(*header.parent_hash()), ); let mut locked_block = match enact_result { Ok(b) => b, Err(e) => { warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - bail!(e); + return Err(e); } }; @@ -429,9 +442,9 @@ impl Importer { } // Final Verification - if let Err(e) = self.verifier.verify_block_final(&header, &locked_block.header) { + if let Err(e) = verification::verify_block_final(&header, &locked_block.header) { warn!(target: "client", "Stage 5 block verification failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); - bail!(e); + return Err(e); } let pending = self.check_epoch_end_signal( @@ -449,14 +462,14 @@ 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, unverified: Unverified, receipts_bytes: &[u8], db: &KeyValueDB, chain: &BlockChain) -> EthcoreResult<()> { + fn import_old_block(&self, unverified: Unverified, receipts_bytes: &[u8], db: &dyn KeyValueDB, chain: &BlockChain) -> EthcoreResult<()> { let receipts = ::rlp::decode_list(receipts_bytes); let _import_lock = self.import_lock.lock(); { trace_time!("import_old_block"); // verify the block, passing the chain for updating the epoch verifier. - let mut rng = OsRng::new()?; + let mut rng = OsRng; self.ancient_verifier.verify(&mut rng, &unverified.header, &chain)?; // Commit results @@ -475,7 +488,16 @@ impl Importer { // // The header passed is from the original block data and is sealed. // TODO: should return an error if ImportRoute is none, issue #9910 - fn commit_block(&self, block: B, header: &Header, block_data: encoded::Block, pending: Option, client: &Client) -> ImportRoute where B: Drain { + fn commit_block( + &self, + block: B, + header: &Header, + block_data: encoded::Block, + pending: Option, + client: &Client + ) -> ImportRoute + where B: Drain + { let hash = &header.hash(); let number = header.number(); let parent = header.parent_hash(); @@ -494,33 +516,24 @@ impl Importer { let traces = block.traces.drain(); let best_hash = chain.best_block_hash(); - let new = ExtendedHeader { - header: header.clone(), - is_finalized, - parent_total_difficulty: chain.block_details(&parent).expect("Parent block is in the database; qed").total_difficulty + let new_total_difficulty = { + let parent_total_difficulty = chain.block_details(&parent) + .expect("Parent block is in the database; qed") + .total_difficulty; + parent_total_difficulty + header.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, - header: header, - } - }; + let best_total_difficulty = chain.block_details(&best_hash) + .expect("Best block is in the database; qed") + .total_difficulty; 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 if new_total_difficulty > best_total_difficulty { + ForkChoice::New } else { - self.engine.fork_choice(&new, &best) + ForkChoice::Old }; // CHECK! I *think* this is fine, even if the state_root is equal to another @@ -549,14 +562,14 @@ impl Importer { a }).collect(); - let route = chain.insert_block(&mut batch, block_data, receipts.clone(), ExtrasInsert { - fork_choice: fork_choice, + let route = chain.insert_block(&mut batch, block_data, receipts, ExtrasInsert { + fork_choice, is_finalized, }); client.tracedb.read().import(&mut batch, TraceImportRequest { traces: traces.into(), - block_hash: hash.clone(), + block_hash: *hash, block_number: number, enacted: route.enacted.clone(), retracted: route.retracted.len() @@ -589,41 +602,41 @@ impl Importer { state_db: &StateDB, client: &Client, ) -> EthcoreResult> { - use engines::EpochChange; + use engine::EpochChange; let hash = header.hash(); - let auxiliary = ::machine::AuxiliaryData { + let auxiliary = AuxiliaryData { bytes: Some(block_bytes), receipts: Some(&receipts), }; match self.engine.signals_epoch_end(header, auxiliary) { EpochChange::Yes(proof) => { - use engines::Proof; + use engine::Proof; let proof = match proof { Proof::Known(proof) => proof, Proof::WithState(with_state) => { let env_info = EnvInfo { number: header.number(), - author: header.author().clone(), + author: *header.author(), timestamp: header.timestamp(), - difficulty: header.difficulty().clone(), - last_hashes: client.build_last_hashes(header.parent_hash()), + difficulty: *header.difficulty(), + last_hashes: client.build_last_hashes(*header.parent_hash()), gas_used: U256::default(), gas_limit: u64::max_value().into(), }; let call = move |addr, data| { let mut state_db = state_db.boxed_clone(); - let backend = ::state::backend::Proving::new(state_db.as_hash_db_mut()); + let backend = account_state::backend::Proving::new(state_db.as_hash_db_mut()); let transaction = client.contract_call_tx(BlockId::Hash(*header.parent_hash()), addr, data); let mut state = State::from_existing( backend, - header.state_root().clone(), + *header.state_root(), self.engine.account_start_nonce(header.number()), client.factories.clone(), ).expect("state known to be available for just-imported block; qed"); @@ -634,15 +647,13 @@ impl Importer { let res = Executive::new(&mut state, &env_info, &machine, &schedule) .transact(&transaction, options); - let res = match res { + match res { Err(e) => { trace!(target: "client", "Proved call failed: {}", e); Err(e.to_string()) } Ok(res) => Ok((res.output, state.drop().1.extract_proof())), - }; - - res.map(|(output, proof)| (output, proof.into_iter().map(|x| x.into_vec()).collect())) + } }; match with_state.generate_proof(&call) { @@ -658,7 +669,7 @@ impl Importer { debug!(target: "client", "Block {} signals epoch end.", hash); - Ok(Some(PendingTransition { proof: proof })) + Ok(Some(PendingTransition { proof })) }, EpochChange::No => Ok(None), EpochChange::Unsure(_) => { @@ -685,7 +696,7 @@ impl Importer { chain.insert_epoch_transition(&mut batch, header.number(), EpochTransition { block_hash: header.hash(), block_number: header.number(), - proof: proof, + proof, }); // always write the batch directly since epoch transition proofs are @@ -702,18 +713,18 @@ impl Client { pub fn new( config: ClientConfig, spec: &Spec, - db: Arc, + db: Arc, miner: Arc, - message_channel: IoChannel, - ) -> Result, ::error::Error> { + message_channel: IoChannel>, + ) -> Result, EthcoreError> { let trie_spec = match config.fat_db { true => TrieSpec::Fat, false => TrieSpec::Secure, }; - let trie_factory = TrieFactory::new(trie_spec); + let trie_factory = TrieFactory::new(trie_spec, Layout); let factories = Factories { - vm: VmFactory::new(config.vm_type.clone(), config.jump_table_size), + vm: VmFactory::new(config.jump_table_size), trie: trie_factory, accountdb: Default::default(), }; @@ -743,7 +754,7 @@ impl Client { config.history }; - if !chain.block_header_data(&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(), EMPTY_PREFIX)) { warn!("State root not found for block #{} ({:x})", chain.best_block_number(), chain.best_block_hash()); } @@ -753,7 +764,7 @@ impl Client { let importer = Importer::new(&config, engine.clone(), message_channel.clone(), miner)?; - let registrar_address = engine.additional_params().get("registrar").and_then(|s| Address::from_str(s).ok()); + let registrar_address = engine.machine().params().registrar; if let Some(ref addr) = registrar_address { trace!(target: "client", "Found registrar at {}", addr); } @@ -766,7 +777,8 @@ impl Client { chain: RwLock::new(chain), tracedb, engine, - pruning: config.pruning.clone(), + pruning: config.pruning, + snapshotting_at: AtomicU64::new(0), db: RwLock::new(db.clone()), state_db: RwLock::new(state_db), report: RwLock::new(Default::default()), @@ -787,13 +799,6 @@ impl Client { config, }); - // prune old states. - { - let state_db = client.state_db.read().boxed_clone(); - let chain = client.chain.read(); - client.prune_ancient(state_db, &chain)?; - } - // ensure genesis epoch proof in the DB. { let chain = client.chain.read(); @@ -819,7 +824,7 @@ impl Client { chain.insert_epoch_transition(&mut batch, 0, EpochTransition { block_hash: gh.hash(), block_number: 0, - proof: proof, + proof, }); client.db.read().key_value().write_buffered(batch); @@ -844,7 +849,7 @@ impl Client { } /// Adds an actor to be notified on certain events - pub fn add_notify(&self, target: Arc) { + pub fn add_notify(&self, target: Arc) { self.notify.write().push(Arc::downgrade(&target)); } @@ -857,11 +862,11 @@ impl Client { } /// Returns engine reference. - pub fn engine(&self) -> &EthEngine { + pub fn engine(&self) -> &dyn Engine { &*self.engine } - fn notify(&self, f: F) where F: Fn(&ChainNotify) { + fn notify(&self, f: F) where F: Fn(&dyn ChainNotify) { for np in &*self.notify.read() { if let Some(n) = np.upgrade() { f(&*n); @@ -896,30 +901,30 @@ impl Client { author: header.author(), timestamp: header.timestamp(), difficulty: header.difficulty(), - last_hashes: self.build_last_hashes(&header.parent_hash()), + last_hashes: self.build_last_hashes(header.parent_hash()), gas_used: U256::default(), gas_limit: header.gas_limit(), } }) } - fn build_last_hashes(&self, parent_hash: &H256) -> Arc { + fn build_last_hashes(&self, parent_hash: H256) -> Arc { { let hashes = self.last_hashes.read(); - if hashes.front().map_or(false, |h| h == parent_hash) { + if hashes.front().map_or(false, |h| h == &parent_hash) { let mut res = Vec::from(hashes.clone()); - res.resize(256, H256::default()); + res.resize(256, H256::zero()); return Arc::new(res); } } let mut last_hashes = LastHashes::new(); - last_hashes.resize(256, H256::default()); - last_hashes[0] = parent_hash.clone(); + last_hashes.resize(256, H256::zero()); + last_hashes[0] = parent_hash; let chain = self.chain.read(); for i in 0..255 { match chain.block_details(&last_hashes[i]) { Some(details) => { - last_hashes[i + 1] = details.parent.clone(); + last_hashes[i + 1] = details.parent; }, None => break, } @@ -929,19 +934,14 @@ 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) - } - // use a state-proving closure for the given block. fn with_proving_caller(&self, id: BlockId, with_call: F) -> T - where F: FnOnce(&::machine::Call) -> T + where F: FnOnce(&MachineCall) -> T { let call = |a, d| { let tx = self.contract_call_tx(id, a, d); let (result, items) = self.prove_transaction(tx, id) - .ok_or_else(|| format!("Unable to make call. State unavailable?"))?; + .ok_or_else(|| "Unable to make call. State unavailable?".to_string())?; let items = items.into_iter().map(|x| x.to_vec()).collect(); Ok((result, items)) @@ -951,31 +951,49 @@ impl Client { } // prune ancient states until below the memory limit or only the minimum amount remain. - fn prune_ancient(&self, mut state_db: StateDB, chain: &BlockChain) -> Result<(), ::error::Error> { - let number = match state_db.journal_db().latest_era() { + fn prune_ancient(&self, mut state_db: StateDB, chain: &BlockChain) -> Result<(), EthcoreError> { + if !state_db.journal_db().is_prunable() { + return Ok(()) + } + + let latest_era = match state_db.journal_db().latest_era() { Some(n) => n, None => return Ok(()), }; - // prune all ancient eras until we're below the memory target, - // but have at least the minimum number of states. + // Prune all ancient eras until we're below the memory target (default: 32Mb), + // but have at least the minimum number of states, i.e. `history`. + // If a snapshot is under way, no pruning happens and memory consumption is allowed to + // increase above the memory target until the snapshot has finished. loop { - let needs_pruning = state_db.journal_db().is_pruned() && - state_db.journal_db().journal_size() >= self.config.history_mem; + let needs_pruning = state_db.journal_db().journal_size() >= self.config.history_mem; + + if !needs_pruning { + break + } - if !needs_pruning { break } match state_db.journal_db().earliest_era() { - Some(era) if era + self.history <= number => { - trace!(target: "client", "Pruning state for ancient era {}", era); - match chain.block_hash(era) { + Some(earliest_era) if earliest_era + self.history <= latest_era => { + let freeze_at = self.snapshotting_at.load(Ordering::SeqCst); + if freeze_at > 0 && freeze_at == earliest_era { + // Note: journal_db().mem_used() can be used for a more accurate memory + // consumption measurement but it can be expensive so sticking with the + // faster `journal_size()` instead. + trace!(target: "pruning", "Pruning is paused at era {} (snapshot under way); earliest era={}, latest era={}, journal_size={} – Not pruning.", + freeze_at, earliest_era, latest_era, state_db.journal_db().journal_size()); + break; + } + trace!(target: "pruning", "Pruning state for ancient era #{}; latest era={}, journal_size={}", + earliest_era, latest_era, state_db.journal_db().journal_size()); + match chain.block_hash(earliest_era) { Some(ancient_hash) => { let mut batch = DBTransaction::new(); - state_db.mark_canonical(&mut batch, era, &ancient_hash)?; + state_db.mark_canonical(&mut batch, earliest_era, &ancient_hash)?; self.db.read().key_value().write_buffered(batch); state_db.journal_db().flush(); } None => - debug!(target: "client", "Missing expected hash for block {}", era), + debug!(target: "pruning", "Missing expected hash for block {}", earliest_era), } } _ => break, // means that every era is kept, no pruning necessary. @@ -996,36 +1014,39 @@ impl Client { } /// Get shared miner reference. - #[cfg(test)] + #[cfg(any(test, feature = "test-helpers"))] pub fn miner(&self) -> Arc { self.importer.miner.clone() } - #[cfg(test)] + /// Access state from tests + #[cfg(any(test, feature = "test-helpers"))] pub fn state_db(&self) -> ::parking_lot::RwLockReadGuard { self.state_db.read() } - #[cfg(test)] + /// Access the BlockChain from tests + #[cfg(any(test, feature = "test-helpers"))] pub fn chain(&self) -> Arc { self.chain.read().clone() } /// Replace io channel. Useful for testing. - pub fn set_io_channel(&self, io_channel: IoChannel) { + pub fn set_io_channel(&self, io_channel: IoChannel>) { *self.io_channel.write() = io_channel; } /// Get a copy of the best block's state. - pub fn latest_state(&self) -> State { + pub fn latest_state_and_header(&self) -> (State, Header) { let header = self.best_block_header(); - State::from_existing( + let state = State::from_existing( self.state_db.read().boxed_clone_canon(&header.hash()), *header.state_root(), self.engine.account_start_nonce(header.number()), self.factories.clone() ) - .expect("State root of best block header always valid.") + .expect("State root of best block header always valid."); + (state, header) } /// Attempt to get a copy of a specific block's final state. @@ -1035,9 +1056,9 @@ impl Client { /// is unknown. pub fn state_at(&self, id: BlockId) -> Option> { // fast path for latest state. - match id.clone() { - BlockId::Latest => return Some(self.latest_state()), - _ => {}, + if let BlockId::Latest = id { + let (state, _) = self.latest_state_and_header(); + return Some(state) } let block_number = match self.block_number(id) { @@ -1049,7 +1070,7 @@ impl Client { let db = self.state_db.read().boxed_clone(); // early exit for pruned blocks - if db.is_pruned() && self.pruning_info().earliest_state > block_number { + if db.is_prunable() && self.pruning_info().earliest_state > block_number { return None; } @@ -1071,8 +1092,9 @@ impl Client { } /// Get a copy of the best block's state. - pub fn state(&self) -> Box { - Box::new(self.latest_state()) as Box<_> + pub fn state(&self) -> impl StateInfo { + let (state, _) = self.latest_state_and_header(); + state } /// Get info on the cache. @@ -1087,15 +1109,6 @@ impl Client { report } - /// Tick the client. - // TODO: manage by real events. - pub fn tick(&self, prevent_sleep: bool) { - self.check_garbage(); - if !prevent_sleep { - self.check_snooze(); - } - } - fn check_garbage(&self) { self.chain.read().collect_garbage(); self.importer.block_queue.collect_garbage(); @@ -1136,61 +1149,6 @@ impl Client { } } - /// Take a snapshot at the given block. - /// If the ID given is "latest", this will default to 1000 blocks behind. - pub fn take_snapshot( - &self, - writer: W, - at: BlockId, - p: &snapshot::Progress, - ) -> Result<(), EthcoreError> { - let db = self.state_db.read().journal_db().boxed_clone(); - let best_block_number = self.chain_info().best_block_number; - let block_number = self.block_number(at).ok_or_else(|| snapshot::Error::InvalidStartingBlock(at))?; - - if db.is_pruned() && self.pruning_info().earliest_state > block_number { - return Err(snapshot::Error::OldBlockPrunedDB.into()); - } - - let history = ::std::cmp::min(self.history, 1000); - - let start_hash = match at { - BlockId::Latest => { - let start_num = match db.earliest_era() { - Some(era) => ::std::cmp::max(era, best_block_number.saturating_sub(history)), - None => best_block_number.saturating_sub(history), - }; - - match self.block_hash(BlockId::Number(start_num)) { - Some(h) => h, - None => return Err(snapshot::Error::InvalidStartingBlock(at).into()), - } - } - _ => match self.block_hash(at) { - Some(hash) => hash, - None => return Err(snapshot::Error::InvalidStartingBlock(at).into()), - }, - }; - - let processing_threads = self.config.snapshot.processing_threads; - let chunker = self.engine.snapshot_components().ok_or(snapshot::Error::SnapshotsUnsupported)?; - snapshot::take_snapshot( - chunker, - &self.chain.read(), - start_hash, - db.as_hash_db(), - writer, - p, - processing_threads, - )?; - Ok(()) - } - - /// Ask the client what the history parameter is. - pub fn pruning_history(&self) -> u64 { - self.history - } - fn block_hash(chain: &BlockChain, id: BlockId) -> Option { match id { BlockId::Hash(hash) => Some(hash), @@ -1203,10 +1161,8 @@ impl Client { fn transaction_address(&self, id: TransactionId) -> Option { match id { TransactionId::Hash(ref hash) => self.chain.read().transaction_address(hash), - TransactionId::Location(id, index) => Self::block_hash(&self.chain.read(), id).map(|hash| TransactionAddress { - block_hash: hash, - index: index, - }) + TransactionId::Location(id, index) => Self::block_hash(&self.chain.read(), id).map(|block_hash| + TransactionAddress { block_hash, index }) } } @@ -1236,32 +1192,33 @@ impl Client { // transaction for calling contracts from services like engine. // from the null sender, with 50M gas. fn contract_call_tx(&self, block_id: BlockId, address: Address, data: Bytes) -> SignedTransaction { - let from = Address::default(); + let from = Address::zero(); transaction::Transaction { nonce: self.nonce(&from, block_id).unwrap_or_else(|| self.engine.account_start_nonce(0)), action: Action::Call(address), gas: U256::from(50_000_000), gas_price: U256::default(), value: U256::default(), - data: data, + data, }.fake_sign(from) } fn do_virtual_call( - machine: &::machine::EthereumMachine, + machine: &::machine::Machine, env_info: &EnvInfo, state: &mut State, t: &SignedTransaction, analytics: CallAnalytics, ) -> Result { + use types::engines::machine::Executed as RawExecuted; fn call( state: &mut State, env_info: &EnvInfo, - machine: &::machine::EthereumMachine, + machine: &::machine::Machine, state_diff: bool, transaction: &SignedTransaction, options: TransactOptions, - ) -> Result, CallError> where + ) -> Result, CallError> where T: trace::Tracer, V: trace::VMTracer, { @@ -1315,7 +1272,7 @@ impl Client { } } -impl snapshot::DatabaseRestore for Client { +impl DatabaseRestore for Client { /// Restart the client with a new backend fn restore_db(&self, new_db: &str) -> Result<(), EthcoreError> { trace!(target: "snapshot", "Replacing client database with {:?}", new_db); @@ -1339,9 +1296,13 @@ impl snapshot::DatabaseRestore for Client { impl BlockChainReset for Client { fn reset(&self, num: u32) -> Result<(), String> { if num as u64 > self.pruning_history() { - return Err("Attempting to reset to block with pruned state".into()) + return Err( + format!("Attempting to reset the chain {} blocks back failed: state is pruned (max available: {})", + num, + self.pruning_history() + )); } else if num == 0 { - return Err("invalid number of blocks to reset".into()) + return Err("0 is an invalid number of blocks to reset".into()) } let mut blocks_to_delete = Vec::with_capacity(num as usize); @@ -1354,8 +1315,8 @@ impl BlockChainReset for Client { best_block_hash = current_header.parent_hash(); let (number, hash) = (current_header.number(), current_header.hash()); - batch.delete(::db::COL_HEADERS, &hash); - batch.delete(::db::COL_BODIES, &hash); + batch.delete(::db::COL_HEADERS, hash.as_bytes()); + batch.delete(::db::COL_BODIES, hash.as_bytes()); Writable::delete:: (&mut batch, ::db::COL_EXTRA, &hash); Writable::delete:: @@ -1388,7 +1349,7 @@ impl BlockChainReset for Client { &best_block_details ); // update the new best block hash - batch.put(::db::COL_EXTRA, b"best", &best_block_hash); + batch.put(::db::COL_EXTRA, b"best", best_block_hash.as_bytes()); self.db.read() .key_value() @@ -1399,6 +1360,11 @@ impl BlockChainReset for Client { Ok(()) } + + /// Ask the client what the history parameter is. + fn pruning_history(&self) -> u64 { + self.history + } } impl Nonce for Client { @@ -1456,22 +1422,6 @@ impl TransactionInfo for Client { impl BlockChainTrait for Client {} -impl RegistryInfo for Client { - fn registry_address(&self, name: String, block: BlockId) -> Option
{ - use ethabi::FunctionOutputDecoder; - - let address = self.registrar_address?; - - let (data, decoder) = registry::functions::get_address::call(keccak(name.as_bytes()), "A"); - let value = decoder.decode(&self.call_contract(block, address, data).ok()?).ok()?; - if value.is_zero() { - None - } else { - Some(value) - } - } -} - impl CallContract for Client { fn call_contract(&self, block_id: BlockId, address: Address, data: Bytes) -> Result { let state_pruned = || CallError::StatePruned.to_string(); @@ -1486,47 +1436,60 @@ impl CallContract for Client { } } +impl RegistrarClient for Client { + fn registrar_address(&self) -> Option
{ + self.registrar_address + } +} + impl ImportBlock for Client { fn import_block(&self, unverified: Unverified) -> EthcoreResult { if self.chain.read().is_known(&unverified.hash()) { - bail!(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain)); + return Err(EthcoreError::Import(ImportError::AlreadyInChain)); } let status = self.block_status(BlockId::Hash(unverified.parent_hash())); if status == BlockStatus::Unknown { - bail!(EthcoreErrorKind::Block(BlockError::UnknownParent(unverified.parent_hash()))); + return Err(EthcoreError::Block(BlockError::UnknownParent(unverified.parent_hash()))); } let raw = if self.importer.block_queue.is_empty() { - Some(( - unverified.bytes.clone(), - unverified.header.hash(), - *unverified.header.difficulty(), - )) - } else { None }; + Some((unverified.bytes.clone(), *unverified.header.difficulty())) + } else { + None + }; match self.importer.block_queue.import(unverified) { Ok(hash) => { - if let Some((raw, hash, difficulty)) = raw { - self.notify(move |n| n.block_pre_import(&raw, &hash, &difficulty)); + if let Some((bytes, difficulty)) = raw { + self.notify(move |n| n.block_pre_import(&bytes, &hash, &difficulty)); } Ok(hash) }, // we only care about block errors (not import errors) - Err((block, EthcoreError(EthcoreErrorKind::Block(err), _))) => { - self.importer.bad_blocks.report(block.bytes, format!("{:?}", err)); - bail!(EthcoreErrorKind::Block(err)) + Err((EthcoreError::Block(e), Some(input))) => { + self.importer.bad_blocks.report(input.bytes, e.to_string()); + Err(EthcoreError::Block(e)) }, - Err((_, e)) => Err(e), + Err((EthcoreError::Block(e), None)) => { + error!(target: "client", "BlockError {} detected but it was missing raw_bytes of the block", e); + Err(EthcoreError::Block(e)) + } + Err((e, _input)) => Err(e), } } + + /// Triggered by a message from a block queue when the block is ready for insertion + fn import_verified_blocks(&self) -> usize { + self.importer.import_verified_blocks(self) + } } impl StateClient for Client { type State = State<::state_db::StateDB>; - fn latest_state(&self) -> Self::State { - Client::latest_state(self) + fn latest_state_and_header(&self) -> (Self::State, Header) { + Client::latest_state_and_header(self) } fn state_at(&self, id: BlockId) -> Option { @@ -1540,10 +1503,10 @@ impl Call for Client { fn call(&self, transaction: &SignedTransaction, analytics: CallAnalytics, state: &mut Self::State, header: &Header) -> Result { let env_info = EnvInfo { number: header.number(), - author: header.author().clone(), + author: *header.author(), timestamp: header.timestamp(), - difficulty: header.difficulty().clone(), - last_hashes: self.build_last_hashes(header.parent_hash()), + difficulty: *header.difficulty(), + last_hashes: self.build_last_hashes(*header.parent_hash()), gas_used: U256::default(), gas_limit: U256::max_value(), }; @@ -1555,10 +1518,10 @@ impl Call for Client { fn call_many(&self, transactions: &[(SignedTransaction, CallAnalytics)], state: &mut Self::State, header: &Header) -> Result, CallError> { let mut env_info = EnvInfo { number: header.number(), - author: header.author().clone(), + author: *header.author(), timestamp: header.timestamp(), - difficulty: header.difficulty().clone(), - last_hashes: self.build_last_hashes(header.parent_hash()), + difficulty: *header.difficulty(), + last_hashes: self.build_last_hashes(*header.parent_hash()), gas_used: U256::default(), gas_limit: U256::max_value(), }; @@ -1582,10 +1545,10 @@ impl Call for Client { let env_info = EnvInfo { number: header.number(), - author: header.author().clone(), + author: *header.author(), timestamp: header.timestamp(), - difficulty: header.difficulty().clone(), - last_hashes: self.build_last_hashes(header.parent_hash()), + difficulty: *header.difficulty(), + last_hashes: self.build_last_hashes(*header.parent_hash()), gas_used: U256::default(), gas_limit: max, }; @@ -1661,7 +1624,7 @@ impl Call for Client { } impl EngineInfo for Client { - fn engine(&self) -> &EthEngine { + fn engine(&self) -> &dyn Engine { Client::engine(self) } } @@ -1674,22 +1637,22 @@ impl BadBlocks for Client { impl BlockChainClient for Client { fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result { - let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?; + let address = self.transaction_address(id).ok_or_else(|| CallError::TransactionNotFound)?; let block = BlockId::Hash(address.block_hash); - const PROOF: &'static str = "The transaction address contains a valid index within block; qed"; + const PROOF: &str = "The transaction address contains a valid index within block; qed"; Ok(self.replay_block_transactions(block, analytics)?.nth(address.index).expect(PROOF).1) } - fn replay_block_transactions(&self, block: BlockId, analytics: CallAnalytics) -> Result>, CallError> { - let mut env_info = self.env_info(block).ok_or(CallError::StatePruned)?; - let body = self.block_body(block).ok_or(CallError::StatePruned)?; - let mut state = self.state_at_beginning(block).ok_or(CallError::StatePruned)?; + fn replay_block_transactions(&self, block: BlockId, analytics: CallAnalytics) -> Result>, CallError> { + let mut env_info = self.env_info(block).ok_or_else(|| CallError::StatePruned)?; + let body = self.block_body(block).ok_or_else(|| CallError::StatePruned)?; + let mut state = self.state_at_beginning(block).ok_or_else(|| CallError::StatePruned)?; let txs = body.transactions(); let engine = self.engine.clone(); - const PROOF: &'static str = "Transactions fetched from blockchain; blockchain transactions are valid; qed"; - const EXECUTE_PROOF: &'static str = "Transaction replayed; qed"; + const PROOF: &str = "Transactions fetched from blockchain; blockchain transactions are valid; qed"; + const EXECUTE_PROOF: &str = "Transaction replayed; qed"; Ok(Box::new(txs.into_iter() .map(move |t| { @@ -1703,9 +1666,11 @@ impl BlockChainClient for Client { } fn mode(&self) -> Mode { - let r = self.mode.lock().clone().into(); - trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r); - r + self.mode.lock().clone() + } + + fn queue_info(&self) -> BlockQueueInfo { + self.importer.block_queue.queue_info() } fn disable(&self) { @@ -1721,7 +1686,7 @@ impl BlockChainClient for Client { } { let mut mode = self.mode.lock(); - *mode = new_mode.clone().into(); + *mode = new_mode.clone(); trace!(target: "mode", "Mode now {:?}", &*mode); if let Some(ref mut f) = *self.on_user_defaults_change.lock() { trace!(target: "mode", "Making callback..."); @@ -1739,6 +1704,10 @@ impl BlockChainClient for Client { self.config.spec_name.clone() } + fn chain(&self) -> Arc { + self.chain.read().clone() + } + fn set_spec_name(&self, new_spec_name: String) -> Result<(), ()> { trace!(target: "mode", "Client::set_spec_name({:?})", new_spec_name); if !self.enabled.load(AtomicOrdering::Relaxed) { @@ -1787,14 +1756,14 @@ impl BlockChainClient for Client { Self::block_hash(&chain, id) } - fn code(&self, address: &Address, state: StateOrBlock) -> Option> { + fn code(&self, address: &Address, state: StateOrBlock) -> StateResult> { let result = match state { StateOrBlock::State(s) => s.code(address).ok(), StateOrBlock::Block(id) => self.state_at(id).and_then(|s| s.code(address).ok()) }; - // Converting from `Option>>` to `Option>` - result.map(|c| c.map(|c| (&*c).clone())) + // Converting from `Option>>` to `StateResult>` + result.map_or(StateResult::Missing, |c| StateResult::Some(c.map(|c| (&*c).clone()))) } fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option { @@ -1831,7 +1800,7 @@ impl BlockChainClient for Client { }; if let Some(after) = after { - if let Err(e) = iter.seek(after) { + if let Err(e) = iter.seek(after.as_bytes()) { trace!(target: "fatdb", "list_accounts: Couldn't seek the DB: {:?}", e); } else { // Position the iterator after the `after` element @@ -1846,7 +1815,7 @@ impl BlockChainClient for Client { Some(accounts) } - fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: u64) -> Option> { + fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: Option) -> Option> { if !self.factories.trie.is_fat() { trace!(target: "fatdb", "list_storage: Not a fat DB"); return None; @@ -1879,7 +1848,7 @@ impl BlockChainClient for Client { }; if let Some(after) = after { - if let Err(e) = iter.seek(after) { + if let Err(e) = iter.seek(after.as_bytes()) { trace!(target: "fatdb", "list_storage: Couldn't seek the DB: {:?}", e); } else { // Position the iterator after the `after` element @@ -1887,9 +1856,16 @@ impl BlockChainClient for Client { } } - let keys = iter.filter_map(|item| { - item.ok().map(|(key, _)| H256::from_slice(&key)) - }).take(count as usize).collect(); + let keys = { + let f = iter.filter_map(|item| { + item.ok().map(|(key, _)| H256::from_slice(&key)) + }); + if let Some(count) = count { + f.take(count as usize).collect() + } else { + f.collect() + } + }; Some(keys) } @@ -1919,7 +1895,7 @@ impl BlockChainClient for Client { let gas_used = receipts.last().map_or_else(|| 0.into(), |r| r.gas_used); let no_of_logs = receipts.into_iter().map(|receipt| receipt.logs.len()).sum::(); - let receipt = transaction_receipt(self.engine().machine(), transaction, receipt, gas_used, no_of_logs); + let receipt = transaction_receipt(transaction, receipt, gas_used, no_of_logs); Some(receipt) } @@ -1930,7 +1906,6 @@ impl BlockChainClient for Client { let receipts = chain.block_receipts(&hash)?; let number = chain.block_number(&hash)?; let body = chain.block_body(&hash)?; - let engine = self.engine.clone(); let mut gas_used = 0.into(); let mut no_of_logs = 0; @@ -1941,7 +1916,7 @@ impl BlockChainClient for Client { .into_iter() .zip(receipts.receipts) .map(move |(transaction, receipt)| { - let result = transaction_receipt(engine.machine(), transaction, receipt, gas_used, no_of_logs); + let result = transaction_receipt(transaction, receipt, gas_used, no_of_logs); gas_used = result.cumulative_gas_used; no_of_logs += result.logs.len(); result @@ -1970,10 +1945,6 @@ impl BlockChainClient for Client { self.chain.read().block_receipts(hash) } - fn queue_info(&self) -> BlockQueueInfo { - self.importer.block_queue.queue_info() - } - fn is_queue_empty(&self) -> bool { self.importer.block_queue.is_empty() } @@ -1982,10 +1953,6 @@ impl BlockChainClient for Client { self.importer.block_queue.clear(); } - fn additional_params(&self) -> BTreeMap { - self.engine.additional_params().into_iter().collect() - } - fn logs(&self, filter: Filter) -> Result, BlockId> { let chain = self.chain.read(); @@ -2009,17 +1976,17 @@ impl BlockChainClient for Client { // pending logs themselves). let from = match self.block_number_ref(&filter.from_block) { Some(val) if val <= chain.best_block_number() => val, - _ => return Err(filter.from_block.clone()), + _ => return Err(filter.from_block), }; let to = match self.block_number_ref(&filter.to_block) { Some(val) if val <= chain.best_block_number() => val, - _ => return Err(filter.to_block.clone()), + _ => return Err(filter.to_block), }; // If from is greater than to, then the current bloom filter behavior is to just return empty // result. There's no point to continue here. if from > to { - return Err(filter.to_block.clone()); + return Err(filter.to_block); } chain.blocks_with_bloom(&filter.bloom_possibilities(), from, to) @@ -2030,7 +1997,7 @@ impl BlockChainClient for Client { // Otherwise, we use a slower version that finds a link between from_block and to_block. let from_hash = match Self::block_hash(&chain, filter.from_block) { Some(val) => val, - None => return Err(filter.from_block.clone()), + None => return Err(filter.from_block), }; let from_number = match chain.block_number(&from_hash) { Some(val) => val, @@ -2038,7 +2005,7 @@ impl BlockChainClient for Client { }; let to_hash = match Self::block_hash(&chain, filter.to_block) { Some(val) => val, - None => return Err(filter.to_block.clone()), + None => return Err(filter.to_block), }; let blooms = filter.bloom_possibilities(); @@ -2079,7 +2046,7 @@ impl BlockChainClient for Client { blocks }; - Ok(self.chain.read().logs(blocks, |entry| filter.matches(entry), filter.limit)) + Ok(chain.logs(blocks, |entry| filter.matches(entry), filter.limit)) } fn filter_traces(&self, filter: TraceFilter) -> Option> { @@ -2140,7 +2107,7 @@ impl BlockChainClient for Client { } fn last_hashes(&self) -> LastHashes { - (*self.build_last_hashes(&self.chain.read().best_block_hash())).clone() + self.build_last_hashes(self.chain.read().best_block_hash()).to_vec() } fn transactions_to_propagate(&self) -> Vec> { @@ -2162,7 +2129,7 @@ impl BlockChainClient for Client { ).as_u64() as usize ) }; - self.importer.miner.ready_transactions(self, max_len, ::miner::PendingOrdering::Priority) + self.importer.miner.ready_transactions(self, max_len, PendingOrdering::Priority) } fn signing_chain_id(&self) -> Option { @@ -2190,34 +2157,36 @@ impl BlockChainClient for Client { } } - fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> { + fn create_transaction(&self, TransactionRequest { action, data, gas, gas_price, nonce }: TransactionRequest) + -> Result + { let authoring_params = self.importer.miner.authoring_params(); let service_transaction_checker = self.importer.miner.service_transaction_checker(); let gas_price = if let Some(checker) = service_transaction_checker { match checker.check_address(self, authoring_params.author) { Ok(true) => U256::zero(), - _ => self.importer.miner.sensible_gas_price(), + _ => gas_price.unwrap_or_else(|| self.importer.miner.sensible_gas_price()), } } else { self.importer.miner.sensible_gas_price() }; let transaction = transaction::Transaction { - nonce: self.latest_nonce(&authoring_params.author), - action: Action::Call(address), - gas: self.importer.miner.sensible_gas_limit(), + nonce: nonce.unwrap_or_else(|| self.latest_nonce(&authoring_params.author)), + action, + gas: gas.unwrap_or_else(|| self.importer.miner.sensible_gas_limit()), gas_price, value: U256::zero(), - data: data, + data, }; let chain_id = self.engine.signing_chain_id(&self.latest_env_info()); 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()) + Ok(SignedTransaction::new(transaction.with_signature(signature, chain_id))?) } - fn registrar_address(&self) -> Option
{ - self.registrar_address.clone() + fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error> { + let signed = self.create_transaction(tx_request)?; + self.importer.miner.import_own_transaction(self, signed.into()) } } @@ -2250,14 +2219,14 @@ impl IoClient for Client { { // check block order if self.chain.read().is_known(&hash) { - bail!(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain)); + return Err(EthcoreError::Import(ImportError::AlreadyInChain)); } let parent_hash = unverified.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 && !self.chain.read().is_known(&parent_hash) { - bail!(EthcoreErrorKind::Block(BlockError::UnknownParent(parent_hash))); + return Err(EthcoreError::Block(BlockError::UnknownParent(parent_hash))); } } @@ -2315,6 +2284,18 @@ impl IoClient for Client { } } } + +} + +impl Tick for Client { + /// Tick the client. + // TODO: manage by real events. + fn tick(&self, prevent_sleep: bool) { + self.check_garbage(); + if !prevent_sleep { + self.check_snooze(); + } + } } impl ReopenBlock for Client { @@ -2361,12 +2342,11 @@ impl PrepareOpenBlock for Client { self.tracedb.read().tracing_enabled(), self.state_db.read().boxed_clone_canon(&h), &best_header, - self.build_last_hashes(&h), + self.build_last_hashes(h), author, gas_range_target, extra_data, is_epoch_begin, - chain.ancestry_with_metadata_iter(best_header.hash()), )?; // Add uncles @@ -2401,7 +2381,9 @@ impl ImportSealedBlock for Client { let raw = block.rlp_bytes(); let header = block.header.clone(); let hash = header.hash(); - self.notify(|n| n.block_pre_import(&raw, &hash, header.difficulty())); + self.notify(|n| { + n.block_pre_import(&raw, &hash, header.difficulty()) + }); let route = { // Do a super duper basic verification to detect potential bugs @@ -2410,18 +2392,18 @@ impl ImportSealedBlock for Client { block.rlp_bytes(), format!("Detected an issue with locally sealed block: {}", e), ); - return Err(e.into()); + return Err(e); } // scope for self.import_lock let _import_lock = self.importer.import_lock.lock(); trace_time!("import_sealed_block"); - let block_data = block.rlp_bytes(); + let block_bytes = block.rlp_bytes(); let pending = self.importer.check_epoch_end_signal( &header, - &block_data, + &block_bytes, &block.receipts, block.state.db(), self @@ -2429,7 +2411,7 @@ impl ImportSealedBlock for Client { let route = self.importer.commit_block( block, &header, - encoded::Block::new(block_data), + encoded::Block::new(block_bytes), pending, self ); @@ -2444,7 +2426,7 @@ impl ImportSealedBlock for Client { &[], route.enacted(), route.retracted(), - self.engine.seals_internally().is_some(), + self.engine.sealing_state() != SealingState::External, ); self.notify(|notify| { notify.new_blocks( @@ -2488,27 +2470,30 @@ impl SealedBlockImporter for Client {} impl ::miner::TransactionVerifierClient for Client {} impl ::miner::BlockChainClient for Client {} -impl super::traits::EngineClient for Client { - fn update_sealing(&self) { - self.importer.miner.update_sealing(self) +impl client_traits::EngineClient for Client { + fn update_sealing(&self, force: ForceUpdateSealing) { + self.importer.miner.update_sealing(self, force) } fn submit_seal(&self, block_hash: H256, seal: Vec) { - let import = self.importer.miner.submit_seal(block_hash, seal).and_then(|block| self.import_sealed_block(block)); + 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(ChainMessageType::Consensus(message.clone()))); + self.notify(|notify| { + notify.broadcast(ChainMessageType::Consensus(message.clone())) + }); } - fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition> { + fn epoch_transition_for(&self, parent_hash: H256) -> Option { self.chain.read().epoch_transition_for(parent_hash) } - fn as_full_client(&self) -> Option<&BlockChainClient> { Some(self) } + fn as_full_client(&self) -> Option<&dyn BlockChainClient> { Some(self) } fn block_number(&self, id: BlockId) -> Option { BlockChainClient::block_number(self, id) @@ -2536,12 +2521,12 @@ impl ProvingBlockChainClient for Client { _ => return None, }; - env_info.gas_limit = transaction.gas.clone(); + env_info.gas_limit = transaction.gas; let mut jdb = self.state_db.read().journal_db().boxed_clone(); - state::prove_transaction_virtual( + executive_state::prove_transaction_virtual( jdb.as_hash_db_mut(), - header.state_root().clone(), + header.state_root(), &transaction, self.engine.machine(), &env_info, @@ -2556,22 +2541,212 @@ impl ProvingBlockChainClient for Client { } } -impl SnapshotClient for Client {} +impl SnapshotClient for Client { + fn take_snapshot( + &self, + writer: W, + at: BlockId, + p: &RwLock, + ) -> Result<(), EthcoreError> { + if let Snapshotting::Unsupported = self.engine.snapshot_mode() { + return Err(EthcoreError::Snapshot(SnapshotError::SnapshotsUnsupported)); + } + let db = self.state_db.read().journal_db().boxed_clone(); -impl Drop for Client { - fn drop(&mut self) { - if let Some(c) = Arc::get_mut(&mut self.engine) { - c.stop() - } else { - warn!(target: "shutdown", "unable to get mut ref for engine for shutdown."); + let block_number = self.block_number(at).ok_or_else(|| SnapshotError::InvalidStartingBlock(at))?; + let earliest_era = db.earliest_era().unwrap_or(0); + if db.is_prunable() && earliest_era > block_number { + return Err(SnapshotError::OldBlockPrunedDB.into()); + } + + + let (actual_block_nr, block_hash) = match at { + BlockId::Latest => { + // Start `self.history` blocks from the best block, but no further back than 1000 + // blocks (or earliest era, whichever is greatest). + let history = cmp::min(self.history, 1000); + let best_block_number = self.chain_info().best_block_number; + let start_num = cmp::max(earliest_era, best_block_number.saturating_sub(history)); + + match self.block_hash(BlockId::Number(start_num)) { + Some(hash) => (start_num, hash), + None => { + error!(target: "snapshot", "Can't take snapshot at {:?}: missing hash for the starting block #{}", at, start_num); + return Err(SnapshotError::InvalidStartingBlock(at).into()) + }, + } + } + _ => match self.block_hash(at) { + Some(hash) => (block_number, hash), + None => return Err(SnapshotError::InvalidStartingBlock(at).into()), + }, + }; + + let processing_threads = self.config.snapshot.processing_threads; + trace!(target: "snapshot", "Snapshot requested at block {:?}. Using block #{}/{:?}. Earliest block: #{}, earliest state era #{}. Using {} threads.", + at, actual_block_nr, block_hash, self.pruning_info().earliest_chain, earliest_era, processing_threads, + ); + // Stop pruning from happening while the snapshot is under way. + self.snapshotting_at.store(actual_block_nr, Ordering::SeqCst); + { + scopeguard::defer! {{ + trace!(target: "snapshot", "Re-enabling pruning."); + self.snapshotting_at.store(0, Ordering::SeqCst) + }}; + let chunker = snapshot::chunker(self.engine.snapshot_mode()).ok_or_else(|| SnapshotError::SnapshotsUnsupported)?; + // Spawn threads and take snapshot + snapshot::take_snapshot( + chunker, + &self.chain.read(), + block_hash, + db.as_hash_db(), + writer, + p, + processing_threads, + )?; + Ok(()) } } } +impl ImportExportBlocks for Client { + fn export_blocks<'a>( + &self, + mut out: Box, + from: BlockId, + to: BlockId, + format: Option + ) -> Result<(), String> { + let from = self.block_number(from).ok_or("Starting block could not be found")?; + let to = self.block_number(to).ok_or("End block could not be found")?; + let format = format.unwrap_or_default(); + + for i in from..=to { + if i % 10000 == 0 { + info!("#{}", i); + } + let b = self.block(BlockId::Number(i)) + .ok_or("Error exporting incomplete chain")? + .into_inner(); + match format { + DataFormat::Binary => { + out.write(&b) + .map_err(|e| { + format!("Couldn't write to stream. Cause: {}", e) + })?; + } + DataFormat::Hex => { + out.write_fmt(format_args!("{}\n", b.pretty())) + .map_err(|e| { + format!("Couldn't write to stream. Cause: {}", e) + })?; + } + } + } + Ok(()) + } + + fn import_blocks<'a>( + &self, + mut source: Box, + format: Option + ) -> Result<(), String> { + const READAHEAD_BYTES: usize = 8; + + let mut first_bytes: Vec = vec![0; READAHEAD_BYTES]; + let mut first_read = 0; + + let format = match format { + Some(format) => format, + None => { + first_read = source.read(&mut first_bytes) + .map_err(|_| { + "Error reading from the file/stream." + })?; + match first_bytes[0] { + 0xf9 => DataFormat::Binary, + _ => DataFormat::Hex, + } + } + }; + + let do_import = |bytes: Vec| { + let block = Unverified::from_rlp(bytes).map_err(|_| "Invalid block rlp")?; + let number = block.header.number(); + while self.queue_info().is_full() { + std::thread::sleep(Duration::from_secs(1)); + } + match self.import_block(block) { + Err(EthcoreError::Import(ImportError::AlreadyInChain)) => { + trace!("Skipping block #{}: already in chain.", number); + } + Err(e) => { + return Err(format!("Cannot import block #{}: {:?}", number, e)); + }, + Ok(_) => {}, + } + Ok(()) + }; + + match format { + DataFormat::Binary => { + loop { + let (mut bytes, n) = if first_read > 0 { + (first_bytes.clone(), first_read) + } else { + let mut bytes = vec![0; READAHEAD_BYTES]; + let n = source.read(&mut bytes) + .map_err(|err| { + format!("Error reading from the file/stream: {:?}", err) + })?; + (bytes, n) + }; + if n == 0 { break; } + first_read = 0; + let s = PayloadInfo::from(&bytes) + .map_err(|e| { + format!("Invalid RLP in the file/stream: {:?}", e) + })?.total(); + bytes.resize(s, 0); + source.read_exact(&mut bytes[n..]) + .map_err(|err| { + format!("Error reading from the file/stream: {:?}", err) + })?; + do_import(bytes)?; + } + } + DataFormat::Hex => { + for line in BufReader::new(source).lines() { + let s = line + .map_err(|err| { + format!("Error reading from the file/stream: {:?}", err) + })?; + let s = if first_read > 0 { + from_utf8(&first_bytes) + .map_err(|err| { + format!("Invalid UTF-8: {:?}", err) + })? + .to_owned() + &(s[..]) + } else { + s + }; + first_read = 0; + let bytes = s.from_hex() + .map_err(|err| { + format!("Invalid hex in file/stream: {:?}", err) + })?; + do_import(bytes)?; + } + } + }; + self.flush_queue(); + Ok(()) + } +} + /// Returns `LocalizedReceipt` given `LocalizedTransaction` /// and a vector of receipts from given block up to transaction index. fn transaction_receipt( - machine: &::machine::EthereumMachine, mut tx: LocalizedTransaction, receipt: Receipt, prior_gas_used: U256, @@ -2587,24 +2762,24 @@ fn transaction_receipt( from: sender, to: match tx.action { Action::Create => None, - Action::Call(ref address) => Some(address.clone().into()) + Action::Call(ref address) => Some(*address) }, - transaction_hash: transaction_hash, - transaction_index: transaction_index, - block_hash: block_hash, - block_number: block_number, + transaction_hash, + transaction_index, + block_hash, + block_number, cumulative_gas_used: receipt.gas_used, gas_used: receipt.gas_used - prior_gas_used, contract_address: match tx.action { Action::Call(_) => None, - Action::Create => Some(contract_address(machine.create_address_scheme(block_number), &sender, &tx.nonce, &tx.data).0) + Action::Create => Some(contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &tx.nonce, &tx.data).0) }, logs: receipt.logs.into_iter().enumerate().map(|(i, log)| LocalizedLogEntry { entry: log, - block_hash: block_hash, - block_number: block_number, - transaction_hash: transaction_hash, - transaction_index: transaction_index, + block_hash, + block_number, + transaction_hash, + transaction_index, transaction_log_index: i, log_index: prior_no_of_logs + i, }).collect(), @@ -2613,22 +2788,74 @@ fn transaction_receipt( } } +/// Queue some items to be processed by IO client. +struct IoChannelQueue { + /// Using a *signed* integer for counting currently queued messages since the + /// order in which the counter is incremented and decremented is not defined. + /// Using an unsigned integer can (and will) result in integer underflow, + /// incorrectly rejecting messages and returning a FullQueue error. + currently_queued: Arc, + limit: i64, +} + +impl IoChannelQueue { + pub fn new(limit: usize) -> Self { + let limit = i64::try_from(limit).unwrap_or(i64::max_value()); + IoChannelQueue { + currently_queued: Default::default(), + limit, + } + } + + pub fn queue(&self, channel: &IoChannel>, count: usize, fun: F) -> EthcoreResult<()> where + F: Fn(&Client) + Send + Sync + 'static, + { + let queue_size = self.currently_queued.load(AtomicOrdering::Relaxed); + if queue_size >= self.limit { + let err_limit = usize::try_from(self.limit).unwrap_or(usize::max_value()); + return Err(EthcoreError::FullQueue(err_limit)) + }; + + let count = i64::try_from(count).unwrap_or(i64::max_value()); + + let currently_queued = self.currently_queued.clone(); + let _ok = channel.send(ClientIoMessage::execute(move |client| { + currently_queued.fetch_sub(count, AtomicOrdering::SeqCst); + fun(client); + }))?; + + self.currently_queued.fetch_add(count, AtomicOrdering::SeqCst); + Ok(()) + } +} + #[cfg(test)] mod tests { + use std::sync::Arc; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::thread; + use std::time::Duration; + + use ethereum_types::{Address, H256}; + use hash::keccak; + use kvdb::DBTransaction; + + use blockchain::{ExtrasInsert, BlockProvider}; + use client_traits::{BlockChainClient, ChainInfo}; + use parity_crypto::publickey::KeyPair; + use types::{ + encoded, + engines::ForkChoice, + ids::{BlockId, TransactionId}, + log_entry::{LocalizedLogEntry, LogEntry}, + receipt::{LocalizedReceipt, Receipt, TransactionOutcome}, + transaction::{Action, LocalizedTransaction, Transaction}, + }; + use test_helpers::{generate_dummy_client, generate_dummy_client_with_data, generate_dummy_client_with_spec_and_data, get_good_dummy_block_hash}; + use super::transaction_receipt; #[test] fn should_not_cache_details_before_commit() { - use client::{BlockChainClient, ChainInfo}; - 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; - use types::encoded; - let client = generate_dummy_client(0); let genesis = client.chain_info().best_block_hash; let (new_hash, new_block) = get_good_dummy_block_hash(); @@ -2641,7 +2868,7 @@ mod tests { thread::spawn(move || { let mut batch = DBTransaction::new(); another_client.chain.read().insert_block(&mut batch, encoded::Block::new(new_block), Vec::new(), ExtrasInsert { - fork_choice: ::engines::ForkChoice::New, + fork_choice: ForkChoice::New, is_finalized: false, }); go_thread.store(true, Ordering::SeqCst); @@ -2656,9 +2883,6 @@ mod tests { #[test] fn should_return_block_receipts() { - use client::{BlockChainClient, BlockId, TransactionId}; - use test_helpers::{generate_dummy_client_with_data}; - let client = generate_dummy_client_with_data(2, 2, &[1.into(), 1.into()]); let receipts = client.localized_block_receipts(BlockId::Latest).unwrap(); @@ -2682,56 +2906,48 @@ mod tests { #[test] fn should_return_correct_log_index() { - use hash::keccak; - use super::transaction_receipt; - use ethkey::KeyPair; - use types::log_entry::{LogEntry, LocalizedLogEntry}; - use types::receipt::{Receipt, LocalizedReceipt, TransactionOutcome}; - use types::transaction::{Transaction, LocalizedTransaction, Action}; - // given - let key = KeyPair::from_secret_slice(&keccak("test")).unwrap(); + let key = KeyPair::from_secret_slice(keccak("test").as_bytes()).unwrap(); let secret = key.secret(); - let machine = ::ethereum::new_frontier_test_machine(); let block_number = 1; - let block_hash = 5.into(); - let state_root = 99.into(); + let block_hash = H256::from_low_u64_be(5); + let state_root = H256::from_low_u64_be(99); let gas_used = 10.into(); let raw_tx = Transaction { nonce: 0.into(), gas_price: 0.into(), gas: 21000.into(), - action: Action::Call(10.into()), + action: Action::Call(Address::from_low_u64_be(10)), value: 0.into(), data: vec![], }; let tx1 = raw_tx.clone().sign(secret, None); let transaction = LocalizedTransaction { signed: tx1.clone().into(), - block_number: block_number, - block_hash: block_hash, + block_number, + block_hash, transaction_index: 1, cached_sender: Some(tx1.sender()), }; let logs = vec![LogEntry { - address: 5.into(), + address: Address::from_low_u64_be(5), topics: vec![], data: vec![], }, LogEntry { - address: 15.into(), + address: Address::from_low_u64_be(15), topics: vec![], data: vec![], }]; let receipt = Receipt { outcome: TransactionOutcome::StateRoot(state_root), - gas_used: gas_used, + gas_used, log_bloom: Default::default(), logs: logs.clone(), }; // when - let receipt = transaction_receipt(&machine, transaction, receipt, 5.into(), 1); + let receipt = transaction_receipt(transaction, receipt, 5.into(), 1); // then assert_eq!(receipt, LocalizedReceipt { @@ -2768,40 +2984,22 @@ mod tests { outcome: TransactionOutcome::StateRoot(state_root), }); } -} - -/// 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: &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, QueueErrorKind::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) => bail!(QueueErrorKind::Channel(e)), - } + #[test] + fn should_mark_finalization_correctly_for_parent() { + let client = generate_dummy_client_with_spec_and_data(spec::new_test_with_finality, 2, 0, &[], false); + let chain = client.chain(); + + let block1_details = chain.block_hash(1).and_then(|h| chain.block_details(&h)); + assert!(block1_details.is_some()); + let block1_details = block1_details.unwrap(); + assert_eq!(block1_details.children.len(), 1); + assert!(block1_details.is_finalized); + + let block2_details = chain.block_hash(2).and_then(|h| chain.block_details(&h)); + assert!(block2_details.is_some()); + let block2_details = block2_details.unwrap(); + assert_eq!(block2_details.children.len(), 0); + assert!(!block2_details.is_finalized); } } diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 59fc4f8135..690596c332 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,16 +15,13 @@ // along with Parity Ethereum. If not, see . use std::str::FromStr; -use std::fmt::{Display, Formatter, Error as FmtError}; -use verification::{VerifierType, QueueConfig}; +use blockchain::Config as BlockChainConfig; use journaldb; use snapshot::SnapshotConfiguration; - -pub use std::time::Duration; -pub use blockchain::Config as BlockChainConfig; -pub use trace::Config as TraceConfig; -pub use evm::VMType; +use trace::Config as TraceConfig; +use types::client_types::Mode; +use verification::{VerifierType, QueueConfig}; /// Client state db compaction profile #[derive(Debug, PartialEq, Clone)] @@ -56,32 +53,6 @@ impl FromStr for DatabaseCompactionProfile { } } -/// Operating mode for the client. -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum Mode { - /// Always on. - Active, - /// 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 client is inactive for some (given) time and - /// stays inactive. - Dark(Duration), - /// Always off. - Off, -} - -impl Display for Mode { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - match *self { - Mode::Active => write!(f, "active"), - Mode::Passive(..) => write!(f, "passive"), - Mode::Dark(..) => write!(f, "dark"), - Mode::Off => write!(f, "offline"), - } - } -} - /// Client configuration. Includes configs for all sub-systems. #[derive(Debug, PartialEq, Clone)] pub struct ClientConfig { @@ -91,8 +62,6 @@ pub struct ClientConfig { pub blockchain: BlockChainConfig, /// Trace configuration. pub tracing: TraceConfig, - /// VM type. - pub vm_type: VMType, /// Fat DB enabled? pub fat_db: bool, /// The JournalDB ("pruning") algorithm to use. @@ -117,7 +86,7 @@ pub struct ClientConfig { pub history: u64, /// Ideal memory usage for state pruning history. pub history_mem: usize, - /// Check seal valididity on block import + /// Check seal validity on block import pub check_seal: bool, /// Maximal number of transactions queued for verification in a separate thread. pub transaction_verification_queue_size: usize, @@ -134,7 +103,6 @@ impl Default for ClientConfig { queue: Default::default(), blockchain: Default::default(), tracing: Default::default(), - vm_type: Default::default(), fat_db: false, pruning: journaldb::Algorithm::OverlayRecent, name: "default".into(), diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index d710a6b4dd..c361be2113 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,41 +20,11 @@ mod ancient_import; mod bad_blocks; mod client; mod config; -#[cfg(any(test, feature = "test-helpers"))] -mod evm_test_client; -mod io_message; -#[cfg(any(test, feature = "test-helpers"))] -mod test_client; -mod trace; +mod traits; -pub use self::client::*; -pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChainConfig, VMType}; -#[cfg(any(test, feature = "test-helpers"))] -pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; -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, NewBlocks, ChainRoute, ChainRouteType, ChainMessageType}; +pub use self::client::Client; +pub use self::config::{ClientConfig, DatabaseCompactionProfile}; pub use self::traits::{ - Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, TransactionInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, - StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks, - BlockChainReset + ReopenBlock, PrepareOpenBlock, ImportSealedBlock, BroadcastProposalBlock, + Call, EngineInfo, BlockProducer, SealedBlockImporter, }; -pub use state::StateInfo; -pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient}; - -pub use types::ids::*; -pub use types::trace_filter::Filter as TraceFilter; -pub use types::pruning_info::PruningInfo; -pub use types::call_analytics::CallAnalytics; - -pub use executive::{Executed, Executive, TransactOptions}; -pub use vm::{LastHashes, EnvInfo}; - -pub use error::TransactionImportError; -pub use verification::VerifierType; - -pub mod traits; - -mod chain_notify; -mod private_notify; diff --git a/ethcore/src/client/private_notify.rs b/ethcore/src/client/private_notify.rs deleted file mode 100644 index 4be1838732..0000000000 --- a/ethcore/src/client/private_notify.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use error::TransactionImportError; - -/// 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/trace.rs b/ethcore/src/client/trace.rs deleted file mode 100644 index 73563a1d0f..0000000000 --- a/ethcore/src/client/trace.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Bridge between Tracedb and Blockchain. - -use blockchain::{BlockChain, BlockProvider, TransactionAddress}; -use ethereum_types::H256; -use trace::DatabaseExtras as TraceDatabaseExtras; -use types::BlockNumber; - -impl TraceDatabaseExtras for BlockChain { - fn block_hash(&self, block_number: BlockNumber) -> Option { - (self as &BlockProvider).block_hash(block_number) - } - - fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { - (self as &BlockProvider).block_hash(block_number) - .and_then(|block_hash| { - let tx_address = TransactionAddress { - block_hash: block_hash, - index: tx_position - }; - self.transaction(&tx_address) - }) - .map(|tx| tx.hash()) - } -} diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 8e4abc01cd..dbe33fce94 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,156 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::collections::BTreeMap; -use std::sync::Arc; - -use blockchain::{BlockReceipts, TreeRoute}; use bytes::Bytes; -use call_contract::{CallContract, RegistryInfo}; -use ethcore_miner::pool::VerifiedTransaction; use ethereum_types::{H256, U256, Address}; -use evm::Schedule; -use itertools::Itertools; -use kvdb::DBValue; -use types::transaction::{self, LocalizedTransaction, SignedTransaction}; -use types::BlockNumber; -use types::basic_account::BasicAccount; -use types::block_status::BlockStatus; -use types::blockchain_info::BlockChainInfo; -use types::call_analytics::CallAnalytics; -use types::encoded; -use types::filter::Filter; -use types::header::Header; -use types::ids::*; -use types::log_entry::LocalizedLogEntry; -use types::pruning_info::PruningInfo; -use types::receipt::LocalizedReceipt; -use types::trace_filter::Filter as TraceFilter; -use vm::LastHashes; +use types::{ + transaction::{SignedTransaction, CallError}, + call_analytics::CallAnalytics, + errors::EthcoreError as Error, + errors::EthcoreResult, + header::Header, +}; use block::{OpenBlock, SealedBlock, ClosedBlock}; -use client::Mode; -use engines::EthEngine; -use error::{Error, EthcoreResult}; -use executed::CallError; -use executive::Executed; -use state::StateInfo; -use trace::LocalizedTrace; -use verification::queue::QueueInfo as BlockQueueInfo; -use verification::queue::kind::blocks::Unverified; - -/// State information to be used during client query -pub enum StateOrBlock { - /// State to be used, may be pending - State(Box), - - /// Id of an existing block from a chain to get state from - Block(BlockId) -} - -impl From for StateOrBlock { - fn from(info: S) -> StateOrBlock { - StateOrBlock::State(Box::new(info) as Box<_>) - } -} - -impl From> for StateOrBlock { - fn from(info: Box) -> StateOrBlock { - StateOrBlock::State(info) - } -} - -impl From for StateOrBlock { - fn from(id: BlockId) -> StateOrBlock { - StateOrBlock::Block(id) - } -} - -/// Provides `nonce` and `latest_nonce` methods -pub trait Nonce { - /// Attempt to get address nonce at given block. - /// May not fail on BlockId::Latest. - fn nonce(&self, address: &Address, id: BlockId) -> Option; - - /// Get address nonce at the latest block's state. - fn latest_nonce(&self, address: &Address) -> U256 { - self.nonce(address, BlockId::Latest) - .expect("nonce will return Some when given BlockId::Latest. nonce was given BlockId::Latest. \ - Therefore nonce has returned Some; qed") - } -} - -/// Provides `balance` and `latest_balance` methods -pub trait Balance { - /// Get address balance at the given block's state. - /// - /// May not return None if given BlockId::Latest. - /// Returns None if and only if the block's root hash has been pruned from the DB. - fn balance(&self, address: &Address, state: StateOrBlock) -> Option; - - /// Get address balance at the latest block's state. - fn latest_balance(&self, address: &Address) -> U256 { - self.balance(address, BlockId::Latest.into()) - .expect("balance will return Some if given BlockId::Latest. balance was given BlockId::Latest \ - Therefore balance has returned Some; qed") - } -} - -/// Provides methods to access account info -pub trait AccountData: Nonce + Balance {} - -/// Provides `chain_info` method -pub trait ChainInfo { - /// Get blockchain information. - fn chain_info(&self) -> BlockChainInfo; -} - -/// Provides various information on a block by it's ID -pub trait BlockInfo { - /// Get raw block header data by block id. - fn block_header(&self, id: BlockId) -> Option; - - /// Get the best block header. - fn best_block_header(&self) -> Header; - - /// Get raw block data by block header hash. - fn block(&self, id: BlockId) -> Option; - - /// Get address code hash at given block's state. - fn code_hash(&self, address: &Address, id: BlockId) -> Option; -} - -/// Provides various information on a transaction by it's ID -pub trait TransactionInfo { - /// Get the hash of block that contains the transaction, if any. - fn transaction_block(&self, id: TransactionId) -> Option; -} - -/// Provides methods to access chain state -pub trait StateClient { - /// Type representing chain state - type State: StateInfo; - - /// Get a copy of the best block's state. - fn latest_state(&self) -> Self::State; - - /// Attempt to get a copy of a specific block's final state. - /// - /// This will not fail if given BlockId::Latest. - /// Otherwise, this can fail (but may not) if the DB prunes state or the block - /// is unknown. - fn state_at(&self, id: BlockId) -> Option; -} - -/// Provides various blockchain information, like block header, chain state etc. -pub trait BlockChain: ChainInfo + BlockInfo + TransactionInfo {} - -// FIXME Why these methods belong to BlockChainClient and not MiningBlockChainClient? -/// Provides methods to import block into blockchain -pub trait ImportBlock { - /// Import a block into the blockchain. - fn import_block(&self, block: Unverified) -> EthcoreResult; -} +use engine::Engine; +use machine::executed::Executed; +use account_state::state::StateInfo; /// Provides `call` and `call_many` methods pub trait Call { @@ -184,202 +48,7 @@ pub trait Call { /// Provides `engine` method pub trait EngineInfo { /// Get underlying engine object - 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: Unverified, receipts_bytes: Bytes) -> EthcoreResult; - - /// Queue conensus engine message. - fn queue_consensus_message(&self, message: Bytes); -} - -/// Provides recently seen bad blocks. -pub trait BadBlocks { - /// Returns a list of blocks that were recently not imported because they were invalid. - fn bad_blocks(&self) -> Vec<(Unverified, String)>; -} - -/// Blockchain database client. Owns and manages a blockchain and a block queue. -pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContract + RegistryInfo + ImportBlock -+ IoClient + BadBlocks { - /// Look up the block number for the given block ID. - fn block_number(&self, id: BlockId) -> Option; - - /// Get raw block body data by block id. - /// Block body is an RLP list of two items: uncles and transactions. - fn block_body(&self, id: BlockId) -> Option; - - /// Get block status by block header hash. - fn block_status(&self, id: BlockId) -> BlockStatus; - - /// Get block total difficulty. - fn block_total_difficulty(&self, id: BlockId) -> Option; - - /// Attempt to get address storage root at given block. - /// May not fail on BlockId::Latest. - fn storage_root(&self, address: &Address, id: BlockId) -> Option; - - /// Get block hash. - fn block_hash(&self, id: BlockId) -> Option; - - /// Get address code at given block's state. - fn code(&self, address: &Address, state: StateOrBlock) -> Option>; - - /// Get address code at the latest block's state. - fn latest_code(&self, address: &Address) -> Option { - self.code(address, BlockId::Latest.into()) - .expect("code will return Some if given BlockId::Latest; qed") - } - - /// Get address code hash at given block's state. - - /// Get value of the storage at given position at the given block's state. - /// - /// May not return None if given BlockId::Latest. - /// Returns None if and only if the block's root hash has been pruned from the DB. - fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option; - - /// Get value of the storage at given position at the latest block's state. - fn latest_storage_at(&self, address: &Address, position: &H256) -> H256 { - self.storage_at(address, position, BlockId::Latest.into()) - .expect("storage_at will return Some if given BlockId::Latest. storage_at was given BlockId::Latest. \ - Therefore storage_at has returned Some; qed") - } - - /// Get a list of all accounts in the block `id`, if fat DB is in operation, otherwise `None`. - /// If `after` is set the list starts with the following item. - fn list_accounts(&self, id: BlockId, after: Option<&Address>, count: u64) -> Option>; - - /// Get a list of all storage keys in the block `id`, if fat DB is in operation, otherwise `None`. - /// If `after` is set the list starts with the following item. - fn list_storage(&self, id: BlockId, account: &Address, after: Option<&H256>, count: u64) -> Option>; - - /// Get transaction with given hash. - fn transaction(&self, id: TransactionId) -> Option; - - /// Get uncle with given id. - fn uncle(&self, id: UncleId) -> Option; - - /// Get transaction receipt with given hash. - fn transaction_receipt(&self, id: TransactionId) -> Option; - - /// Get localized receipts for all transaction in given block. - fn localized_block_receipts(&self, id: BlockId) -> Option>; - - /// Get a tree route between `from` and `to`. - /// See `BlockChain::tree_route`. - fn tree_route(&self, from: &H256, to: &H256) -> Option; - - /// Get all possible uncle hashes for a block. - fn find_uncles(&self, hash: &H256) -> Option>; - - /// Get latest state node - fn state_data(&self, hash: &H256) -> Option; - - /// Get block receipts data by block header hash. - fn block_receipts(&self, hash: &H256) -> Option; - - /// Get block queue information. - fn queue_info(&self) -> BlockQueueInfo; - - /// Returns true if block queue is empty. - fn is_queue_empty(&self) -> bool { - self.queue_info().is_empty() - } - - /// Clear block queue and abort all import activity. - fn clear_queue(&self); - - /// Get the registrar address, if it exists. - fn additional_params(&self) -> BTreeMap; - - /// Returns logs matching given filter. If one of the filtering block cannot be found, returns the block id that caused the error. - fn logs(&self, filter: Filter) -> Result, BlockId>; - - /// Replays a given transaction for inspection. - fn replay(&self, t: TransactionId, analytics: CallAnalytics) -> Result; - - /// Replays all the transactions in a given block for inspection. - fn replay_block_transactions(&self, block: BlockId, analytics: CallAnalytics) -> Result>, CallError>; - - /// Returns traces matching given filter. - fn filter_traces(&self, filter: TraceFilter) -> Option>; - - /// Returns trace with given id. - fn trace(&self, trace: TraceId) -> Option; - - /// Returns traces created by transaction. - fn transaction_traces(&self, trace: TransactionId) -> Option>; - - /// Returns traces created by transaction from block. - fn block_traces(&self, trace: BlockId) -> Option>; - - /// Get last hashes starting from best block. - fn last_hashes(&self) -> LastHashes; - - /// List all ready transactions that should be propagated to other peers. - fn transactions_to_propagate(&self) -> Vec>; - - /// Sorted list of transaction gas prices from at least last sample_size blocks. - fn gas_price_corpus(&self, sample_size: usize) -> ::stats::Corpus { - let mut h = self.chain_info().best_block_hash; - let mut corpus = Vec::new(); - while corpus.is_empty() { - for _ in 0..sample_size { - let block = match self.block(BlockId::Hash(h)) { - Some(block) => block, - None => return corpus.into(), - }; - - if block.number() == 0 { - return corpus.into(); - } - block.transaction_views().iter().foreach(|t| corpus.push(t.gas_price())); - h = block.parent_hash().clone(); - } - } - corpus.into() - } - - /// Get the preferred chain ID to sign on - fn signing_chain_id(&self) -> Option; - - /// Get the mode. - fn mode(&self) -> Mode; - - /// Set the mode. - fn set_mode(&self, mode: Mode); - - /// Get the chain spec name. - fn spec_name(&self) -> String; - - /// Set the chain via a spec name. - fn set_spec_name(&self, spec_name: String) -> Result<(), ()>; - - /// Disable the client from importing blocks. This cannot be undone in this session and indicates - /// that a subsystem has reason to believe this executable incapable of syncing the chain. - fn disable(&self); - - /// Returns engine-related extra info for `BlockId`. - fn block_extra_info(&self, id: BlockId) -> Option>; - - /// Returns engine-related extra info for `UncleId`. - fn uncle_extra_info(&self, id: UncleId) -> Option>; - - /// Returns information about pruning/data availability. - fn pruning_info(&self) -> PruningInfo; - - /// 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
; + fn engine(&self) -> &dyn Engine; } /// Provides `reopen_block` method @@ -401,12 +70,6 @@ pub trait PrepareOpenBlock { /// Provides methods used for sealing new state pub trait BlockProducer: PrepareOpenBlock + ReopenBlock {} -/// Provides `latest_schedule` method -pub trait ScheduleInfo { - /// Returns latest schedule. - fn latest_schedule(&self) -> Schedule; -} - ///Provides `import_sealed_block` method pub trait ImportSealedBlock { /// Import sealed block. Skips all verifications. @@ -421,59 +84,3 @@ pub trait BroadcastProposalBlock { /// Provides methods to import sealed block and broadcast a block proposal pub trait SealedBlockImporter: ImportSealedBlock + BroadcastProposalBlock {} - -/// Client facilities used by internally sealing Engines. -pub trait EngineClient: Sync + Send + ChainInfo { - /// Make a new block and seal it. - fn update_sealing(&self); - - /// Submit a seal for a block in the mining queue. - fn submit_seal(&self, block_hash: H256, seal: Vec); - - /// Broadcast a consensus message to the network. - fn broadcast_consensus_message(&self, message: Bytes); - - /// Get the transition to the epoch the given parent hash is part of - /// or transitions to. - /// This will give the epoch that any children of this parent belong to. - /// - /// The block corresponding the the parent hash must be stored already. - fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition>; - - /// Attempt to cast the engine client to a full client. - fn as_full_client(&self) -> Option<&BlockChainClient>; - - /// Get a block number by ID. - fn block_number(&self, id: BlockId) -> Option; - - /// Get raw block header data by block id. - fn block_header(&self, id: BlockId) -> Option; -} - -/// Extended client interface for providing proofs of the state. -pub trait ProvingBlockChainClient: BlockChainClient { - /// Prove account storage at a specific block id. - /// - /// Both provided keys assume a secure trie. - /// Returns a vector of raw trie nodes (in order from the root) proving the storage query. - fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec, H256)>; - - /// Prove account existence at a specific block id. - /// The key is the keccak hash of the account's address. - /// Returns a vector of raw trie nodes (in order from the root) proving the query. - fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec, BasicAccount)>; - - /// Prove execution of a transaction at the given block. - /// Returns the output of the call and a vector of database items necessary - /// to reproduce it. - fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec)>; - - /// Get an epoch change signal by block hash. - fn epoch_signal(&self, hash: H256) -> Option>; -} - -/// resets the blockchain -pub trait BlockChainReset { - /// reset to best_block - n - fn reset(&self, num: u32) -> Result<(), String>; -} diff --git a/ethcore/src/engines/authority_round/finality.rs b/ethcore/src/engines/authority_round/finality.rs deleted file mode 100644 index af57278c9f..0000000000 --- a/ethcore/src/engines/authority_round/finality.rs +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Finality proof generation and checking. - -use std::collections::{VecDeque}; -use std::collections::hash_map::{HashMap, Entry}; - -use ethereum_types::{H256, Address}; - -use engines::validator_set::SimpleList; - -/// Error indicating unknown validator. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct UnknownValidator; - -/// Rolling finality checker for authority round consensus. -/// Stores a chain of unfinalized hashes that can be pushed onto. -pub struct RollingFinality { - headers: VecDeque<(H256, Vec
)>, - signers: SimpleList, - sign_count: HashMap, - last_pushed: Option, -} - -impl RollingFinality { - /// Create a blank finality checker under the given validator set. - pub fn blank(signers: Vec
) -> Self { - RollingFinality { - headers: VecDeque::new(), - signers: SimpleList::new(signers), - sign_count: HashMap::new(), - last_pushed: None, - } - } - - /// Extract unfinalized subchain from ancestry iterator. - /// Clears the current subchain. - /// - /// Fails if any provided signature isn't part of the signers set. - pub fn build_ancestry_subchain(&mut self, iterable: I) -> Result<(), UnknownValidator> - where I: IntoIterator)> - { - self.clear(); - for (hash, signers) in iterable { - if signers.iter().any(|s| !self.signers.contains(s)) { return Err(UnknownValidator) } - if self.last_pushed.is_none() { self.last_pushed = Some(hash) } - - // break when we've got our first finalized block. - { - let current_signed = self.sign_count.len(); - - let new_signers = signers.iter().filter(|s| !self.sign_count.contains_key(s)).count(); - let would_be_finalized = (current_signed + new_signers) * 2 > self.signers.len(); - - if would_be_finalized { - trace!(target: "finality", "Encountered already finalized block {}", hash); - break - } - - for signer in signers.iter() { - *self.sign_count.entry(*signer).or_insert(0) += 1; - } - } - - self.headers.push_front((hash, signers)); - } - - trace!(target: "finality", "Rolling finality state: {:?}", self.headers); - Ok(()) - } - - /// Clear the finality status, but keeps the validator set. - pub fn clear(&mut self) { - self.headers.clear(); - self.sign_count.clear(); - self.last_pushed = None; - } - - /// Returns the last pushed hash. - pub fn subchain_head(&self) -> Option { - self.last_pushed - } - - /// Get an iterator over stored hashes in order. - #[cfg(test)] - pub fn unfinalized_hashes(&self) -> impl Iterator { - self.headers.iter().map(|(h, _)| h) - } - - /// Get the validator set. - pub fn validators(&self) -> &SimpleList { &self.signers } - - /// Push a hash onto the rolling finality checker (implying `subchain_head` == head.parent) - /// - /// Fails if `signer` isn't a member of the active validator set. - /// Returns a list of all newly finalized headers. - // TODO: optimize with smallvec. - pub fn push_hash(&mut self, head: H256, signers: Vec
) -> Result, UnknownValidator> { - if signers.iter().any(|s| !self.signers.contains(s)) { return Err(UnknownValidator) } - - for signer in signers.iter() { - *self.sign_count.entry(*signer).or_insert(0) += 1; - } - - self.headers.push_back((head, signers)); - - let mut newly_finalized = Vec::new(); - - while self.sign_count.len() * 2 > self.signers.len() { - let (hash, signers) = self.headers.pop_front() - .expect("headers length always greater than sign count length; qed"); - - newly_finalized.push(hash); - - for signer in signers { - match self.sign_count.entry(signer) { - Entry::Occupied(mut entry) => { - // decrement count for this signer and purge on zero. - *entry.get_mut() -= 1; - - if *entry.get() == 0 { - entry.remove(); - } - } - Entry::Vacant(_) => panic!("all hashes in `header` should have entries in `sign_count` for their signers; qed"), - } - } - } - - trace!(target: "finality", "Blocks finalized by {:?}: {:?}", head, newly_finalized); - - self.last_pushed = Some(head); - Ok(newly_finalized) - } -} - -#[cfg(test)] -mod tests { - use ethereum_types::{H256, Address}; - use super::RollingFinality; - - #[test] - fn rejects_unknown_signers() { - let signers = (0..3).map(|_| Address::random()).collect::>(); - let mut finality = RollingFinality::blank(signers.clone()); - assert!(finality.push_hash(H256::random(), vec![signers[0], Address::random()]).is_err()); - } - - #[test] - fn finalize_multiple() { - let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); - - let mut finality = RollingFinality::blank(signers.clone()); - let hashes: Vec<_> = (0..7).map(|_| H256::random()).collect(); - - // 3 / 6 signers is < 51% so no finality. - for (i, hash) in hashes.iter().take(6).cloned().enumerate() { - let i = i % 3; - assert!(finality.push_hash(hash, vec![signers[i]]).unwrap().len() == 0); - } - - // after pushing a block signed by a fourth validator, the first four - // blocks of the unverified chain become verified. - assert_eq!(finality.push_hash(hashes[6], vec![signers[4]]).unwrap(), - vec![hashes[0], hashes[1], hashes[2], hashes[3]]); - } - - #[test] - fn finalize_multiple_signers() { - let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); - let mut finality = RollingFinality::blank(signers.clone()); - let hash = H256::random(); - - // after pushing a block signed by four validators, it becomes verified right away. - assert_eq!(finality.push_hash(hash, signers[0..4].to_vec()).unwrap(), vec![hash]); - } - - #[test] - fn from_ancestry() { - let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); - let hashes: Vec<_> = (0..12).map(|i| (H256::random(), vec![signers[i % 6]])).collect(); - - let mut finality = RollingFinality::blank(signers.clone()); - finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap(); - - assert_eq!(finality.unfinalized_hashes().count(), 3); - assert_eq!(finality.subchain_head(), Some(hashes[11].0)); - } - - #[test] - fn from_ancestry_multiple_signers() { - let signers: Vec<_> = (0..6).map(|_| Address::random()).collect(); - let hashes: Vec<_> = (0..12).map(|i| { - (H256::random(), vec![signers[i % 6], signers[(i + 1) % 6], signers[(i + 2) % 6]]) - }).collect(); - - let mut finality = RollingFinality::blank(signers.clone()); - finality.build_ancestry_subchain(hashes.iter().rev().cloned()).unwrap(); - - // only the last hash has < 51% of authorities' signatures - assert_eq!(finality.unfinalized_hashes().count(), 1); - assert_eq!(finality.unfinalized_hashes().next(), Some(&hashes[11].0)); - assert_eq!(finality.subchain_head(), Some(hashes[11].0)); - } -} diff --git a/ethcore/src/engines/signer.rs b/ethcore/src/engines/signer.rs deleted file mode 100644 index bccaca1915..0000000000 --- a/ethcore/src/engines/signer.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! A signer used by Engines which need to sign messages. - -use ethereum_types::{H256, Address}; -use ethkey::{self, Signature}; - -/// Everything that an Engine needs to sign messages. -pub trait EngineSigner: Send + Sync { - /// Sign a consensus message hash. - fn sign(&self, hash: H256) -> Result; - - /// Signing address - fn address(&self) -> Address; -} - -/// Creates a new `EngineSigner` from given key pair. -pub fn from_keypair(keypair: ethkey::KeyPair) -> Box { - Box::new(Signer(keypair)) -} - -struct Signer(ethkey::KeyPair); - -impl EngineSigner for Signer { - fn sign(&self, hash: H256) -> Result { - ethkey::sign(self.0.secret(), &hash) - } - - fn address(&self) -> Address { - self.0.address() - } -} - -#[cfg(test)] -mod test_signer { - use std::sync::Arc; - - use ethkey::Password; - use accounts::{self, AccountProvider, SignError}; - - use super::*; - - impl EngineSigner for (Arc, Address, Password) { - fn sign(&self, hash: H256) -> Result { - match self.0.sign(self.1, Some(self.2.clone()), hash) { - Err(SignError::NotUnlocked) => unreachable!(), - Err(SignError::NotFound) => Err(ethkey::Error::InvalidAddress), - Err(SignError::Hardware(err)) => { - warn!("Error using hardware wallet for engine: {:?}", err); - Err(ethkey::Error::InvalidSecret) - }, - Err(SignError::SStore(accounts::Error::EthKey(err))) => Err(err), - Err(SignError::SStore(accounts::Error::EthKeyCrypto(err))) => { - warn!("Low level crypto error: {:?}", err); - Err(ethkey::Error::InvalidSecret) - }, - Err(SignError::SStore(err)) => { - warn!("Error signing for engine: {:?}", err); - Err(ethkey::Error::InvalidSignature) - }, - Ok(ok) => Ok(ok), - } - } - - fn address(&self) -> Address { - self.1 - } - } -} diff --git a/ethcore/src/error.rs b/ethcore/src/error.rs deleted file mode 100644 index d5aa45ba0e..0000000000 --- a/ethcore/src/error.rs +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! General error types for use in ethcore. - -// Silence: `use of deprecated item 'std::error::Error::cause': replaced by Error::source, which can support downcasting` -// https://github.com/paritytech/parity-ethereum/issues/10302 -#![allow(deprecated)] - -use std::{fmt, error}; -use std::time::SystemTime; - -use ethereum_types::{H256, U256, Address, Bloom}; -use ethkey::Error as EthkeyError; -use ethtrie::TrieError; -use rlp; -use snappy::InvalidInput; -use snapshot::Error as SnapshotError; -use types::BlockNumber; -use types::transaction::Error as TransactionError; -use unexpected::{Mismatch, OutOfBounds}; - -use engines::EngineError; - -pub use executed::{ExecutionError, CallError}; - -#[derive(Debug, PartialEq, Clone, Eq)] -/// Errors concerning block processing. -pub enum BlockError { - /// Block has too many uncles. - TooManyUncles(OutOfBounds), - /// Extra data is of an invalid length. - ExtraDataOutOfBounds(OutOfBounds), - /// Seal is incorrect format. - InvalidSealArity(Mismatch), - /// Block has too much gas used. - TooMuchGasUsed(OutOfBounds), - /// Uncles hash in header is invalid. - InvalidUnclesHash(Mismatch), - /// An uncle is from a generation too old. - UncleTooOld(OutOfBounds), - /// An uncle is from the same generation as the block. - UncleIsBrother(OutOfBounds), - /// An uncle is already in the chain. - UncleInChain(H256), - /// An uncle is included twice. - DuplicateUncle(H256), - /// An uncle has a parent not in the chain. - UncleParentNotInChain(H256), - /// State root header field is invalid. - InvalidStateRoot(Mismatch), - /// Gas used header field is invalid. - InvalidGasUsed(Mismatch), - /// Transactions root header field is invalid. - InvalidTransactionsRoot(Mismatch), - /// Difficulty is out of range; this can be used as an looser error prior to getting a definitive - /// value for difficulty. This error needs only provide bounds of which it is out. - DifficultyOutOfBounds(OutOfBounds), - /// Difficulty header field is invalid; this is a strong error used after getting a definitive - /// value for difficulty (which is provided). - InvalidDifficulty(Mismatch), - /// Seal element of type H256 (max_hash for Ethash, but could be something else for - /// other seal engines) is out of bounds. - MismatchedH256SealElement(Mismatch), - /// Proof-of-work aspect of seal, which we assume is a 256-bit value, is invalid. - InvalidProofOfWork(OutOfBounds), - /// Some low-level aspect of the seal is incorrect. - InvalidSeal, - /// Gas limit header field is invalid. - InvalidGasLimit(OutOfBounds), - /// Receipts trie root header field is invalid. - InvalidReceiptsRoot(Mismatch), - /// Timestamp header field is invalid. - InvalidTimestamp(OutOfBounds), - /// Timestamp header field is too far in future. - TemporarilyInvalid(OutOfBounds), - /// Log bloom header field is invalid. - InvalidLogBloom(Box>), - /// Number field of header is invalid. - InvalidNumber(Mismatch), - /// Block number isn't sensible. - RidiculousNumber(OutOfBounds), - /// Timestamp header overflowed - TimestampOverflow, - /// Too many transactions from a particular address. - TooManyTransactions(Address), - /// Parent given is unknown. - UnknownParent(H256), - /// Uncle parent given is unknown. - UnknownUncleParent(H256), - /// No transition to epoch number. - UnknownEpochTransition(u64), -} - -impl fmt::Display for BlockError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::BlockError::*; - - let msg = match *self { - TooManyUncles(ref oob) => format!("Block has too many uncles. {}", oob), - ExtraDataOutOfBounds(ref oob) => format!("Extra block data too long. {}", oob), - InvalidSealArity(ref mis) => format!("Block seal in incorrect format: {}", mis), - TooMuchGasUsed(ref oob) => format!("Block has too much gas used. {}", oob), - InvalidUnclesHash(ref mis) => format!("Block has invalid uncles hash: {}", mis), - UncleTooOld(ref oob) => format!("Uncle block is too old. {}", oob), - UncleIsBrother(ref oob) => format!("Uncle from same generation as block. {}", oob), - UncleInChain(ref hash) => format!("Uncle {} already in chain", hash), - DuplicateUncle(ref hash) => format!("Uncle {} already in the header", hash), - UncleParentNotInChain(ref hash) => format!("Uncle {} has a parent not in the chain", hash), - InvalidStateRoot(ref mis) => format!("Invalid state root in header: {}", mis), - InvalidGasUsed(ref mis) => format!("Invalid gas used in header: {}", mis), - InvalidTransactionsRoot(ref mis) => format!("Invalid transactions root in header: {}", mis), - DifficultyOutOfBounds(ref oob) => format!("Invalid block difficulty: {}", oob), - InvalidDifficulty(ref mis) => format!("Invalid block difficulty: {}", mis), - MismatchedH256SealElement(ref mis) => format!("Seal element out of bounds: {}", mis), - InvalidProofOfWork(ref oob) => format!("Block has invalid PoW: {}", oob), - 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) => { - 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), - InvalidNumber(ref mis) => format!("Invalid number in header: {}", mis), - RidiculousNumber(ref oob) => format!("Implausible block number. {}", oob), - UnknownParent(ref hash) => format!("Unknown parent: {}", hash), - UnknownUncleParent(ref hash) => format!("Unknown uncle parent: {}", hash), - UnknownEpochTransition(ref num) => format!("Unknown transition to epoch number: {}", num), - TimestampOverflow => format!("Timestamp overflow"), - TooManyTransactions(ref address) => format!("Too many transactions from: {}", address), - }; - - f.write_fmt(format_args!("Block error ({})", msg)) - } -} - -impl error::Error for BlockError { - fn description(&self) -> &str { - "Block error" - } -} - -error_chain! { - types { - QueueError, QueueErrorKind, QueueErrorResultExt, QueueErrorResult; - } - - errors { - #[doc = "Queue is full"] - Full(limit: usize) { - description("Queue is full") - display("The queue is full ({})", limit) - } - } - - foreign_links { - Channel(::io::IoError) #[doc = "Io channel error"]; - } -} - -error_chain! { - types { - ImportError, ImportErrorKind, ImportErrorResultExt, ImportErrorResult; - } - - 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") - } - } -} - -/// Api-level error for transaction import -#[derive(Debug, Clone)] -pub enum TransactionImportError { - /// Transaction error - Transaction(TransactionError), - /// Other error - Other(String), -} - -impl From for TransactionImportError { - fn from(e: Error) -> Self { - match e { - Error(ErrorKind::Transaction(transaction_error), _) => TransactionImportError::Transaction(transaction_error), - _ => TransactionImportError::Other(format!("other block import error: {:?}", e)), - } - } -} - -error_chain! { - types { - Error, ErrorKind, ErrorResultExt, EthcoreResult; - } - - links { - Import(ImportError, ImportErrorKind) #[doc = "Error concerning block import." ]; - Queue(QueueError, QueueErrorKind) #[doc = "Io channel queue error"]; - } - - foreign_links { - Io(::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."]; - Decoder(rlp::DecoderError) #[doc = "RLP decoding errors"]; - } - - errors { - #[doc = "Snapshot error."] - Snapshot(err: SnapshotError) { - description("Snapshot error.") - display("Snapshot error {}", 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.") - } - - #[doc = "Unknown engine given"] - UnknownEngineName(name: String) { - description("Unknown engine name") - display("Unknown engine name ({})", name) - } - } -} - -impl From for Error { - fn from(err: SnapshotError) -> Error { - match err { - SnapshotError::Io(err) => ErrorKind::StdIo(err).into(), - SnapshotError::Trie(err) => ErrorKind::Trie(err).into(), - SnapshotError::Decoder(err) => err.into(), - other => ErrorKind::Snapshot(other).into(), - } - } -} - -impl From> for Error where Error: From { - fn from(err: Box) -> Error { - Error::from(*err) - } -} diff --git a/ethcore/src/ethereum/mod.rs b/ethcore/src/ethereum/mod.rs deleted file mode 100644 index f66eb453ad..0000000000 --- a/ethcore/src/ethereum/mod.rs +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Ethereum protocol module. -//! -//! Contains all Ethereum network specific stuff, such as denominations and -//! consensus specifications. - -/// Export the ethash module. -pub mod ethash; -/// Export the denominations module. -pub mod denominations; - -pub use self::ethash::{Ethash}; -pub use self::denominations::*; - -use machine::EthereumMachine; -use super::spec::*; - -/// Load chain spec from `SpecParams` and JSON. -pub fn load<'a, T: Into>>>(params: T, b: &[u8]) -> Spec { - match params.into() { - Some(params) => Spec::load(params, b), - None => Spec::load(&::std::env::temp_dir(), b) - }.expect("chain spec is invalid") -} - -fn load_machine(b: &[u8]) -> EthereumMachine { - Spec::load_machine(b).expect("chain spec is invalid") -} - -/// Create a new Foundation mainnet chain spec. -pub fn new_foundation<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/foundation.json")) -} - -/// Create a new Classic mainnet chain spec without the DAO hardfork. -pub fn new_classic<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/classic.json")) -} - -/// Create a new POA Network mainnet chain spec. -pub fn new_poanet<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/poacore.json")) -} - -/// Create a new xDai chain spec. -pub fn new_xdai<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/xdai.json")) -} - -/// Create a new Volta mainnet chain spec. -pub fn new_volta<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/volta.json")) -} - -/// Create a new EWC mainnet chain spec. -pub fn new_ewc<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/ewc.json")) -} - -/// Create a new Expanse mainnet chain spec. -pub fn new_expanse<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/expanse.json")) -} - -/// Create a new Musicoin mainnet chain spec. -pub fn new_musicoin<'a, T: Into>>(params: T) -> Spec { - // The musicoin chain spec uses a block reward contract which can be found at - // https://gist.github.com/andresilva/6f2afaf9486732a0797f4bdeae018ee9 - load(params.into(), include_bytes!("../../res/ethereum/musicoin.json")) -} - -/// Create a new Ellaism mainnet chain spec. -pub fn new_ellaism<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/ellaism.json")) -} - -/// Create a new MIX mainnet chain spec. -pub fn new_mix<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/mix.json")) -} - -/// Create a new Callisto chain spec -pub fn new_callisto<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/callisto.json")) -} - -/// Create a new Morden testnet chain spec. -pub fn new_morden<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/morden.json")) -} - -/// Create a new Ropsten testnet chain spec. -pub fn new_ropsten<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/ropsten.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")) -} - -/// Create a new Rinkeby testnet chain spec. -pub fn new_rinkeby<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/rinkeby.json")) -} - -/// Create a new Görli testnet chain spec. -pub fn new_goerli<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/goerli.json")) -} - -/// Create a new Kotti testnet chain spec. -pub fn new_kotti<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/kotti.json")) -} - -/// Create a new POA Sokol testnet chain spec. -pub fn new_sokol<'a, T: Into>>(params: T) -> Spec { - load(params.into(), include_bytes!("../../res/ethereum/poasokol.json")) -} - -// For tests - -/// 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")) } - -/// Create a new Foundation Homestead-EIP150-era chain spec as though it never changed from Homestead/Frontier. -pub fn new_eip150_test() -> Spec { load(None, include_bytes!("../../res/ethereum/eip150_test.json")) } - -/// Create a new Foundation Homestead-EIP161-era chain spec as though it never changed from Homestead/Frontier. -pub fn new_eip161_test() -> Spec { load(None, include_bytes!("../../res/ethereum/eip161_test.json")) } - -/// Create a new Foundation Frontier/Homestead/DAO chain spec with transition points at #5 and #8. -pub fn new_transition_test() -> Spec { load(None, include_bytes!("../../res/ethereum/transition_test.json")) } - -/// Create a new Foundation Mainnet chain spec without genesis accounts. -pub fn new_mainnet_like() -> Spec { load(None, include_bytes!("../../res/ethereum/frontier_like_test.json")) } - -/// Create a new Foundation Byzantium era spec. -pub fn new_byzantium_test() -> Spec { load(None, include_bytes!("../../res/ethereum/byzantium_test.json")) } - -/// Create a new Foundation Constantinople era spec. -pub fn new_constantinople_test() -> Spec { load(None, include_bytes!("../../res/ethereum/constantinople_test.json")) } - -/// Create a new Foundation St. Peter's (Contantinople Fix) era spec. -pub fn new_constantinople_fix_test() -> Spec { load(None, include_bytes!("../../res/ethereum/st_peters_test.json")) } - -/// Create a new Foundation Istanbul era spec. -pub fn new_istanbul_test() -> Spec { load(None, include_bytes!("../../res/ethereum/istanbul_test.json")) } - -/// Create a new Musicoin-MCIP3-era spec. -pub fn new_mcip3_test() -> Spec { load(None, include_bytes!("../../res/ethereum/mcip3_test.json")) } - -// For tests - -/// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead. -pub fn new_frontier_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/frontier_test.json")) } - -/// Create a new Foundation Homestead-era chain spec as though it never changed from Frontier. -pub fn new_homestead_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/homestead_test.json")) } - -/// Create a new Foundation Homestead-EIP210-era chain spec as though it never changed from Homestead/Frontier. -pub fn new_eip210_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/eip210_test.json")) } - -/// Create a new Foundation Byzantium era spec. -pub fn new_byzantium_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/byzantium_test.json")) } - -/// Create a new Foundation Constantinople era spec. -pub fn new_constantinople_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/constantinople_test.json")) } - -/// Create a new Foundation St. Peter's (Contantinople Fix) era spec. -pub fn new_constantinople_fix_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/st_peters_test.json")) } - -/// Create a new Foundation Istanbul era spec. -pub fn new_istanbul_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/istanbul_test.json")) } - -/// Create a new Musicoin-MCIP3-era spec. -pub fn new_mcip3_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/mcip3_test.json")) } - -/// Create new Kovan spec with wasm activated at certain block -pub fn new_kovan_wasm_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/kovan_wasm_test.json")) } - -#[cfg(test)] -mod tests { - use ethereum_types::U256; - use state::*; - use super::*; - use test_helpers::get_temp_state_db; - use types::view; - use types::views::BlockView; - - #[test] - fn ensure_db_good() { - let spec = new_morden(&::std::env::temp_dir()); - let engine = &spec.engine; - let genesis_header = spec.genesis_header(); - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap(); - let s = State::from_existing(db, genesis_header.state_root().clone(), engine.account_start_nonce(0), Default::default()).unwrap(); - assert_eq!(s.balance(&"0000000000000000000000000000000000000001".into()).unwrap(), 1u64.into()); - assert_eq!(s.balance(&"0000000000000000000000000000000000000002".into()).unwrap(), 1u64.into()); - assert_eq!(s.balance(&"0000000000000000000000000000000000000003".into()).unwrap(), 1u64.into()); - assert_eq!(s.balance(&"0000000000000000000000000000000000000004".into()).unwrap(), 1u64.into()); - assert_eq!(s.balance(&"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c".into()).unwrap(), U256::from(1u64) << 200); - assert_eq!(s.balance(&"0000000000000000000000000000000000000000".into()).unwrap(), 0u64.into()); - } - - #[test] - fn morden() { - let morden = new_morden(&::std::env::temp_dir()); - - assert_eq!(morden.state_root(), "f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9".into()); - let genesis = morden.genesis_block(); - assert_eq!(view!(BlockView, &genesis).header_view().hash(), "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".into()); - - let _ = morden.engine; - } - - #[test] - fn frontier() { - let frontier = new_foundation(&::std::env::temp_dir()); - - assert_eq!(frontier.state_root(), "d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544".into()); - let genesis = frontier.genesis_block(); - assert_eq!(view!(BlockView, &genesis).header_view().hash(), "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3".into()); - - let _ = frontier.engine; - } -} diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index 4488d0f326..ab6935025e 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,59 +16,61 @@ use std::path::Path; use std::sync::Arc; -use client::{EvmTestClient, Client, ClientConfig, ChainInfo, ImportBlock}; +use client::{Client, ClientConfig}; +use client_traits::{ImportBlock, ChainInfo}; use spec::Genesis; -use ethjson; +use ethjson::test_helpers::blockchain; use miner::Miner; use io::IoChannel; -use test_helpers; -use verification::queue::kind::blocks::Unverified; -use verification::VerifierType; -use super::SKIP_TEST_STATE; +use test_helpers::{self, EvmTestClient}; +use types::verification::Unverified; +use verification::{VerifierType, queue::kind::BlockLike}; +use super::SKIP_TESTS; use super::HookType; -/// Run chain jsontests on a given folder. -pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, json_chain_test, h) -} - -/// Run chain jsontests on a given file. -pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, json_chain_test, h) -} - +#[allow(dead_code)] fn skip_test(name: &String) -> bool { - SKIP_TEST_STATE.block.iter().any(|block_test|block_test.subtests.contains(name)) + SKIP_TESTS + .block + .iter() + .any(|block_test|block_test.subtests.contains(name)) } -pub fn json_chain_test(json_data: &[u8], start_stop_hook: &mut H) -> Vec { +#[allow(dead_code)] +pub fn json_chain_test(path: &Path, json_data: &[u8], start_stop_hook: &mut H) -> Vec { let _ = ::env_logger::try_init(); - let tests = ethjson::blockchain::Test::load(json_data).unwrap(); + let tests = blockchain::Test::load(json_data) + .expect(&format!("Could not parse JSON chain test data from {}", path.display())); let mut failed = Vec::new(); for (name, blockchain) in tests.into_iter() { if skip_test(&name) { - println!(" - {} | {:?} Ignoring tests because in skip list", name, blockchain.network); + println!(" - {} | {:?}: SKIPPED", name, blockchain.network); continue; } + start_stop_hook(&name, HookType::OnStart); let mut fail = false; { - let mut fail_unless = |cond: bool| if !cond && !fail { - failed.push(name.clone()); - flushln!("FAIL"); - fail = true; - true - } else {false}; + let mut fail_unless = |cond: bool| { + if !cond && !fail { + failed.push(name.clone()); + flushln!("FAIL"); + fail = true; + true + } else { + false + } + }; flush!(" - {}...", name); let spec = { - let mut spec = match EvmTestClient::spec_from_json(&blockchain.network) { + let mut spec = match EvmTestClient::fork_spec_from_json(&blockchain.network) { Some(spec) => spec, None => { - println!(" - {} | {:?} Ignoring tests because of missing spec", name, blockchain.network); + println!(" - {} | {:?} Ignoring tests because of missing chainspec", name, blockchain.network); continue; } }; @@ -77,30 +79,43 @@ pub fn json_chain_test(json_data: &[u8], start_stop_ho let state = From::from(blockchain.pre_state.clone()); spec.set_genesis_state(state).expect("Failed to overwrite genesis state"); spec.overwrite_genesis_params(genesis); - assert!(spec.is_state_root_valid()); spec }; { let db = test_helpers::new_db(); let mut config = ClientConfig::default(); - if ethjson::blockchain::Engine::NoProof == blockchain.engine { + if ethjson::test_helpers::blockchain::Engine::NoProof == blockchain.engine { config.verifier_type = VerifierType::CanonNoSeal; config.check_seal = false; } config.history = 8; + config.queue.verifier_settings.num_verifiers = 1; let client = Client::new( config, &spec, db, Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), - ).unwrap(); + ).expect("Failed to instantiate a new Client"); + for b in blockchain.blocks_rlp() { - if let Ok(block) = Unverified::from_rlp(b) { - let _ = client.import_block(block); - client.flush_queue(); - client.import_verified_blocks(); + let bytes_len = b.len(); + let block = Unverified::from_rlp(b); + match block { + Ok(block) => { + let num = block.header.number(); + let hash = block.hash(); + trace!(target: "json-tests", "{} – Importing {} bytes. Block #{}/{}", name, bytes_len, num, hash); + let res = client.import_block(block); + if let Err(e) = res { + warn!(target: "json-tests", "{} – Error importing block #{}/{}: {:?}", name, num, hash, e); + } + client.flush_queue(); + }, + Err(decoder_err) => { + warn!(target: "json-tests", "Error decoding test block: {:?} ({} bytes)", decoder_err, bytes_len); + } } } fail_unless(client.chain_info().best_block_hash == blockchain.best_block.into()); @@ -109,24 +124,31 @@ pub fn json_chain_test(json_data: &[u8], start_stop_ho if !fail { flushln!("ok"); + } else { + flushln!("fail"); } start_stop_hook(&name, HookType::OnStop); } - println!("!!! {:?} tests from failed.", failed.len()); + if failed.len() > 0 { + println!("!!! {:?} tests failed.", failed.len()); + } failed } #[cfg(test)] mod block_tests { + use std::path::Path; + use super::json_chain_test; use json_tests::HookType; - fn do_json_test(json_data: &[u8], h: &mut H) -> Vec { - json_chain_test(json_data, h) + fn do_json_test(path: &Path, json_data: &[u8], h: &mut H) -> Vec { + json_chain_test(path, json_data, h) } - + //todo[dvdplm] do these tests match all folders in `res/` or are there tests we're missing? + //Issue: https://github.com/paritytech/parity-ethereum/issues/11085 declare_test!{BlockchainTests_bcBlockGasLimitTest, "BlockchainTests/bcBlockGasLimitTest"} declare_test!{BlockchainTests_bcExploitTest, "BlockchainTests/bcExploitTest"} declare_test!{BlockchainTests_bcForgedTest, "BlockchainTests/bcForgedTest"} @@ -173,7 +195,12 @@ mod block_tests { declare_test!{BlockchainTests_GeneralStateTest_stRandom2, "BlockchainTests/GeneralStateTests/stRandom2/"} declare_test!{BlockchainTests_GeneralStateTest_stRecursiveCreate, "BlockchainTests/GeneralStateTests/stRecursiveCreate/"} declare_test!{BlockchainTests_GeneralStateTest_stRefundTest, "BlockchainTests/GeneralStateTests/stRefundTest/"} - declare_test!{BlockchainTests_GeneralStateTest_stReturnDataTest, "BlockchainTests/GeneralStateTests/stReturnDataTest/"} + declare_test!{ BlockchainTests_GeneralStateTest_stReturnDataTest, "BlockchainTests/GeneralStateTests/stReturnDataTest/"} + // todo[dvdplm]: + // "RevertPrecompiledTouch_storage" contains 4 tests, only two fails + // "RevertPrecompiledTouchExactOOG" contains a ton of tests, only two fails + // "RevertPrecompiledTouch" has 4 tests, 2 failures + // Ignored in currents.json, issue: https://github.com/paritytech/parity-ethereum/issues/11073 declare_test!{BlockchainTests_GeneralStateTest_stRevertTest, "BlockchainTests/GeneralStateTests/stRevertTest/"} declare_test!{BlockchainTests_GeneralStateTest_stShift, "BlockchainTests/GeneralStateTests/stShift/"} declare_test!{BlockchainTests_GeneralStateTest_stSolidityTest, "BlockchainTests/GeneralStateTests/stSolidityTest/"} diff --git a/ethcore/src/json_tests/difficulty.rs b/ethcore/src/json_tests/difficulty.rs index bf3a48fff1..fccd9b4eba 100644 --- a/ethcore/src/json_tests/difficulty.rs +++ b/ethcore/src/json_tests/difficulty.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,16 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use ethjson; -use types::header::Header; +use std::path::Path; + use ethereum_types::U256; +use ethjson::test_helpers::difficulty::DifficultyTest; +use types::header::Header; use spec::Spec; use super::HookType; -pub fn json_difficulty_test(json_data: &[u8], spec: Spec, start_stop_hook: &mut H) -> Vec { - let _ = ::env_logger::try_init(); - let tests = ethjson::test::DifficultyTest::load(json_data).unwrap(); +pub fn json_difficulty_test( + path: &Path, + json_data: &[u8], + spec: Spec, + start_stop_hook: &mut H +) -> Vec { + let _ = env_logger::try_init(); + let tests = DifficultyTest::load(json_data) + .expect(&format!("Could not parse JSON difficulty test data from {}", path.display())); let engine = &spec.engine; for (name, test) in tests.into_iter() { @@ -55,13 +63,14 @@ pub fn json_difficulty_test(json_data: &[u8], spec: Sp macro_rules! difficulty_json_test { ( $spec:ident ) => { + use std::path::Path; use super::json_difficulty_test; use tempdir::TempDir; use json_tests::HookType; - fn do_json_test(json_data: &[u8], h: &mut H) -> Vec { + fn do_json_test(path: &Path, json_data: &[u8], h: &mut H) -> Vec { let tempdir = TempDir::new("").unwrap(); - json_difficulty_test(json_data, ::ethereum::$spec(&tempdir.path()), h) + json_difficulty_test(path, json_data, crate::spec::$spec(&tempdir.path()), h) } } @@ -69,12 +78,13 @@ macro_rules! difficulty_json_test { macro_rules! difficulty_json_test_nopath { ( $spec:ident ) => { + use std::path::Path; use super::json_difficulty_test; use json_tests::HookType; - fn do_json_test(json_data: &[u8], h: &mut H) -> Vec { - json_difficulty_test(json_data, ::ethereum::$spec(), h) + fn do_json_test(path: &Path, json_data: &[u8], h: &mut H) -> Vec { + json_difficulty_test(path, json_data, crate::spec::$spec(), h) } } diff --git a/ethcore/src/json_tests/executive.rs b/ethcore/src/json_tests/executive.rs index 0dae76e414..212197e902 100644 --- a/ethcore/src/json_tests/executive.rs +++ b/ethcore/src/json_tests/executive.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,24 +17,29 @@ use std::path::Path; use std::sync::Arc; use super::test_common::*; -use state::{Backend as StateBackend, State, Substate}; -use executive::*; -use evm::{VMType, Finalize}; +use account_state::{Backend as StateBackend, State}; +use evm::Finalize; use vm::{ - self, ActionParams, CallType, Schedule, Ext, + self, ActionParams, ActionType, Schedule, Ext, ContractCreateResult, EnvInfo, MessageCallResult, CreateContractAddress, ReturnData, }; -use externalities::*; +use machine::{ + Machine, + externalities::{OutputPolicy, OriginInfo, Externalities}, + substate::Substate, + executive::contract_address, +}; + use test_helpers::get_temp_state; use ethjson; -use trace::{Tracer, NoopTracer}; -use trace::{VMTracer, NoopVMTracer}; +use trace::{Tracer, NoopTracer, VMTracer, NoopVMTracer}; use bytes::Bytes; use ethtrie; use rlp::RlpStream; use hash::keccak; -use machine::EthereumMachine as Machine; +use ethereum_types::BigEndianHash; +use spec; use super::HookType; @@ -145,6 +150,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> gas: &U256, value: &U256, code: &[u8], + _code_version: &U256, address: CreateContractAddress, _trap: bool ) -> Result { @@ -166,7 +172,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> value: Option, data: &[u8], _code_address: &Address, - _call_type: CallType, + _call_type: ActionType, _trap: bool ) -> Result { self.callcreates.push(CallCreate { @@ -229,26 +235,23 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B> } } -fn do_json_test(json_data: &[u8], h: &mut H) -> Vec { - let vms = VMType::all(); - vms - .iter() - .flat_map(|vm| do_json_test_for(vm, json_data, h)) - .collect() -} - -fn do_json_test_for(vm_type: &VMType, json_data: &[u8], start_stop_hook: &mut H) -> Vec { - let tests = ethjson::vm::Test::load(json_data).unwrap(); +fn do_json_test( + path: &Path, + json_data: &[u8], + start_stop_hook: &mut H +) -> Vec { + let tests = ethjson::test_helpers::vm::Test::load(json_data) + .expect(&format!("Could not parse JSON executive test data from {}", path.display())); let mut failed = Vec::new(); for (name, vm) in tests.into_iter() { - start_stop_hook(&format!("{}-{}", name, vm_type), HookType::OnStart); + start_stop_hook(&format!("{}", name), HookType::OnStart); info!(target: "jsontests", "name: {:?}", name); let mut fail = false; let mut fail_unless = |cond: bool, s: &str | if !cond && !fail { - failed.push(format!("[{}] {}: {}", vm_type, name, s)); + failed.push(format!("{}: {}", name, s)); fail = true }; @@ -270,7 +273,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8] state.populate_from(From::from(vm.pre_state.clone())); let info: EnvInfo = From::from(vm.env); let machine = { - let mut machine = ::ethereum::new_frontier_test_machine(); + let mut machine = spec::new_frontier_test_machine(); machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = 1)); machine }; @@ -299,7 +302,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8] &mut tracer, &mut vm_tracer, )); - let mut evm = vm_factory.create(params, &schedule, 0); + let evm = vm_factory.create(params, &schedule, 0).expect("Current tests are all of version 0; factory always return Some; qed"); let res = evm.exec(&mut ex).ok().expect("TestExt never trap; resume error never happens; qed"); // a return in finalize will not alter callcreates let callcreates = ex.callcreates.clone(); @@ -330,19 +333,19 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8] for (address, account) in vm.post_state.unwrap().into_iter() { let address = address.into(); - let code: Vec = account.code.into(); + let code: Vec = account.code.expect("code is missing from json; test should have code").into(); let found_code = try_fail!(state.code(&address)); let found_balance = try_fail!(state.balance(&address)); let found_nonce = try_fail!(state.nonce(&address)); fail_unless(found_code.as_ref().map_or_else(|| code.is_empty(), |c| &**c == &code), "code is incorrect"); - fail_unless(found_balance == account.balance.into(), "balance is incorrect"); - fail_unless(found_nonce == account.nonce.into(), "nonce is incorrect"); - for (k, v) in account.storage { + fail_unless(account.balance.as_ref().map_or(false, |b| b.0 == found_balance), "balance is incorrect"); + fail_unless(account.nonce.as_ref().map_or(false, |n| n.0 == found_nonce), "nonce is incorrect"); + for (k, v) in account.storage.expect("test should have storage") { let key: U256 = k.into(); let value: U256 = v.into(); - let found_storage = try_fail!(state.storage_at(&address, &From::from(key))); - fail_unless(found_storage == From::from(value), "storage is incorrect"); + let found_storage = try_fail!(state.storage_at(&address, &BigEndianHash::from_uint(&key))); + fail_unless(found_storage == BigEndianHash::from_uint(&value), "storage is incorrect"); } } @@ -351,7 +354,7 @@ fn do_json_test_for(vm_type: &VMType, json_data: &[u8] } }; - start_stop_hook(&format!("{}-{}", name, vm_type), HookType::OnStop); + start_stop_hook(&format!("{}", name), HookType::OnStop); } for f in &failed { diff --git a/ethcore/src/json_tests/mod.rs b/ethcore/src/json_tests/mod.rs index 99cbdb21ee..8a5d13d779 100644 --- a/ethcore/src/json_tests/mod.rs +++ b/ethcore/src/json_tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -30,17 +30,7 @@ mod skip; mod difficulty; pub use self::test_common::HookType; - -pub use self::transaction::run_test_path as run_transaction_test_path; -pub use self::transaction::run_test_file as run_transaction_test_file; pub use self::executive::run_test_path as run_executive_test_path; pub use self::executive::run_test_file as run_executive_test_file; -pub use self::state::run_test_path as run_state_test_path; -pub use self::state::run_test_file as run_state_test_file; -pub use self::chain::run_test_path as run_chain_test_path; -pub use self::chain::run_test_file as run_chain_test_file; -pub use self::trie::run_generic_test_path as run_generic_trie_test_path; -pub use self::trie::run_generic_test_file as run_generic_trie_test_file; -pub use self::trie::run_secure_test_path as run_secure_trie_test_path; -pub use self::trie::run_secure_test_file as run_secure_trie_test_file; -use self::skip::SKIP_TEST_STATE; + +use self::skip::SKIP_TESTS; diff --git a/ethcore/src/json_tests/skip.rs b/ethcore/src/json_tests/skip.rs index b6ef9795f6..132103f877 100644 --- a/ethcore/src/json_tests/skip.rs +++ b/ethcore/src/json_tests/skip.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,21 +14,30 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! State tests to skip. +//! State or blockchain tests to skip. +//! +//! Looks in the `ethereum/tests/test-issues/currents.json` file. This file contains two +//! collections, `block` and `state`, each with a different format to specify single tests to skip. +//! +//! To skip a blockchain test, add a JSON object to the `block` array, where `failing` names the +//! leaf folder with the tests to skip. The `subtests` array contains the names of the tests to skip. +//! Note that this does not handle duplicate folder names, e.g. `ValidBlocks/funTests/` and +//! `Something/funTests` would both be matched when `failing` is set to `funTests`. +//! +//! To skip a state test, add a JSON object to the `state` array. The `failing` works like for block +//! tests, but the `subtests` key is an object on the form: +//! "testName": {"subnumbers": [INDEX_OF_SKIPPED_SUBTESTS | "*"], "chain": "Blockchain name (informational)"}` +//! +//! Use the `reference` key to point to the github issue tracking to solution to the problem. +//! +//! Note: the `declare_test!` macro can also be use to skip tests, but skips entire files rather +//! than single tests. -use ethjson; +use ethjson::test_helpers::skip::SkipTests; -#[cfg(feature="ci-skip-tests")] -lazy_static!{ - pub static ref SKIP_TEST_STATE: ethjson::test::SkipStates = { +lazy_static! { + pub static ref SKIP_TESTS: SkipTests = { let skip_data = include_bytes!("../../res/ethereum/tests-issues/currents.json"); - ethjson::test::SkipStates::load(&skip_data[..]).expect("No invalid json allowed") - }; -} - -#[cfg(not(feature="ci-skip-tests"))] -lazy_static!{ - pub static ref SKIP_TEST_STATE: ethjson::test::SkipStates = { - ethjson::test::SkipStates::empty() + SkipTests::load(&skip_data[..]).expect("JSON from disk is valid") }; } diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index c51a2c361c..177afb949d 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,40 +16,37 @@ use std::path::Path; use super::test_common::*; -use pod_state::PodState; +use pod::PodState; use trace; -use client::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; use ethjson; +use test_helpers::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; use types::transaction::SignedTransaction; use vm::EnvInfo; -use super::SKIP_TEST_STATE; +use super::SKIP_TESTS; use super::HookType; -/// Run state jsontests on a given folder. -pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, json_chain_test, h) -} - -/// Run state jsontests on a given file. -pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, json_chain_test, h) -} - +#[allow(dead_code)] fn skip_test(subname: &str, chain: &String, number: usize) -> bool { - SKIP_TEST_STATE.state.iter().any(|state_test|{ + trace!(target: "json-tests", "[state, skip_test] subname: '{}', chain: '{}', number: {}", subname, chain, number); + SKIP_TESTS.state.iter().any(|state_test|{ if let Some(subtest) = state_test.subtests.get(subname) { + trace!(target: "json-tests", "[state, skip_test] Maybe skipping {:?}", subtest); chain == &subtest.chain && - (subtest.subnumbers[0] == "*" - || subtest.subnumbers.contains(&number.to_string())) + ( + subtest.subnumbers[0] == "*" || + subtest.subnumbers.contains(&number.to_string()) + ) } else { false } }) } -pub fn json_chain_test(json_data: &[u8], start_stop_hook: &mut H) -> Vec { +#[allow(dead_code)] +pub fn json_chain_test(path: &Path, json_data: &[u8], start_stop_hook: &mut H) -> Vec { let _ = ::env_logger::try_init(); - let tests = ethjson::state::test::Test::load(json_data).unwrap(); + let tests = ethjson::test_helpers::state::Test::load(json_data) + .expect(&format!("Could not parse JSON state test data from {}", path.display())); let mut failed = Vec::new(); for (name, test) in tests.into_iter() { @@ -62,10 +59,10 @@ pub fn json_chain_test(json_data: &[u8], start_stop_ho for (spec_name, states) in test.post_states { let total = states.len(); - let spec = match EvmTestClient::spec_from_json(&spec_name) { + let spec = match EvmTestClient::fork_spec_from_json(&spec_name) { Some(spec) => spec, None => { - println!(" - {} | {:?} Ignoring tests because of missing spec", name, spec_name); + println!(" - {} | {:?} Ignoring tests because of missing chainspec", name, spec_name); continue; } }; @@ -73,7 +70,7 @@ pub fn json_chain_test(json_data: &[u8], start_stop_ho for (i, state) in states.into_iter().enumerate() { let info = format!(" - {} | {:?} ({}/{}) ...", name, spec_name, i + 1, total); if skip_test(&name, &spec.name, i + 1) { - println!("{} in skip list : SKIPPED", info); + println!("{}: SKIPPED", info); continue; } @@ -123,11 +120,13 @@ pub fn json_chain_test(json_data: &[u8], start_stop_ho #[cfg(test)] mod state_tests { + use std::path::Path; + use super::json_chain_test; use json_tests::HookType; - fn do_json_test(json_data: &[u8], h: &mut H) -> Vec { - json_chain_test(json_data, h) + fn do_json_test(path: &Path, json_data: &[u8], h: &mut H) -> Vec { + json_chain_test(path, json_data, h) } declare_test!{GeneralStateTest_stArgsZeroOneBalance, "GeneralStateTests/stArgsZeroOneBalance/"} @@ -164,6 +163,15 @@ mod state_tests { declare_test!{GeneralStateTest_stRecursiveCreate, "GeneralStateTests/stRecursiveCreate/"} declare_test!{GeneralStateTest_stRefundTest, "GeneralStateTests/stRefundTest/"} declare_test!{GeneralStateTest_stReturnDataTest, "GeneralStateTests/stReturnDataTest/"} + // todo[dvdplm]: + // "RevertPrecompiledTouch_storage" contains 4 tests, only two fails + // "RevertPrecompiledTouchExactOOG" contains a ton of tests, only two fails + // "RevertPrecompiledTouch" has 4 tests, 2 failures + // Ignored in `currents.json`. + // Issues: + // https://github.com/paritytech/parity-ethereum/issues/11078 + // https://github.com/paritytech/parity-ethereum/issues/11079 + // https://github.com/paritytech/parity-ethereum/issues/11080 declare_test!{GeneralStateTest_stRevertTest, "GeneralStateTests/stRevertTest/"} declare_test!{GeneralStateTest_stSStoreTest, "GeneralStateTests/stSStoreTest/"} declare_test!{GeneralStateTest_stShift, "GeneralStateTests/stShift/"} diff --git a/ethcore/src/json_tests/test_common.rs b/ethcore/src/json_tests/test_common.rs index 7e3842ecb7..c6e9995335 100644 --- a/ethcore/src/json_tests/test_common.rs +++ b/ethcore/src/json_tests/test_common.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -30,43 +30,61 @@ pub enum HookType { OnStop } +/// Run all tests under the given path (except for the test files named in the skip list) using the +/// provided runner function. pub fn run_test_path( - p: &Path, skip: &[&'static str], - runner: fn(json_data: &[u8], start_stop_hook: &mut H) -> Vec, + path: &Path, + skip: &[&'static str], + runner: fn(path: &Path, json_data: &[u8], start_stop_hook: &mut H) -> Vec, start_stop_hook: &mut H ) { + if !skip.is_empty() { + // todo[dvdplm] it's really annoying to have to use flushln here. Should be `info!(target: + // "json-tests", …)`. Issue https://github.com/paritytech/parity-ethereum/issues/11084 + flushln!("[run_test_path] Skipping tests in {}: {:?}", path.display(), skip); + } let mut errors = Vec::new(); - run_test_path_inner(p, skip, runner, start_stop_hook, &mut errors); + run_test_path_inner(path, skip, runner, start_stop_hook, &mut errors); let empty: [String; 0] = []; - assert_eq!(errors, empty); + assert_eq!(errors, empty, "\nThere were {} tests in '{}' that failed.", errors.len(), path.display()); } fn run_test_path_inner( - p: &Path, skip: &[&'static str], - runner: fn(json_data: &[u8], start_stop_hook: &mut H) -> Vec, + p: &Path, + skip: &[&'static str], + runner: fn(path: &Path, json_data: &[u8], start_stop_hook: &mut H) -> Vec, start_stop_hook: &mut H, errors: &mut Vec ) { let path = Path::new(p); - let s: HashSet = skip.iter().map(|s| { + let extension = path.extension().and_then(|s| s.to_str()); + let skip_list: HashSet = skip.iter().map(|s| { let mut os: OsString = s.into(); os.push(".json"); os }).collect(); - let extension = path.extension().and_then(|s| s.to_str()); + if path.is_dir() { - for p in read_dir(path).unwrap().filter_map(|e| { - let e = e.unwrap(); - if s.contains(&e.file_name()) { - None - } else { - Some(e.path()) - }}) { - run_test_path_inner(&p, skip, runner, start_stop_hook, errors); + trace!(target: "json-tests", "running tests contained in '{}'", path.display()); + let test_files = read_dir(path) + .expect("Directory exists on disk") + .filter_map(|dir_entry| { + let dir_entry = dir_entry.expect("Entry in directory listing exists"); + if skip_list.contains(&dir_entry.file_name()) { + debug!(target: "json-tests", "'{:?}' is on the skip list.", dir_entry.file_name()); + None + } else { + Some(dir_entry.path()) + } + }); + for test_file in test_files { + run_test_path_inner(&test_file, skip, runner, start_stop_hook, errors); } } else if extension == Some("swp") || extension == None { + trace!(target: "json-tests", "ignoring '{}', extension {:?} – Junk?", path.display(), extension); // Ignore junk } else { + trace!(target: "json-tests", "running tests in '{}'", path.display()); let mut path = p.to_path_buf(); path.set_extension("json"); run_test_file_append(&path, runner, start_stop_hook, errors) @@ -75,7 +93,7 @@ fn run_test_path_inner( fn run_test_file_append( path: &Path, - runner: fn(json_data: &[u8], start_stop_hook: &mut H) -> Vec, + runner: fn(path: &Path, json_data: &[u8], start_stop_hook: &mut H) -> Vec, start_stop_hook: &mut H, errors: &mut Vec ) { @@ -85,12 +103,12 @@ fn run_test_file_append( Err(_) => panic!("Error opening test file at: {:?}", path), }; file.read_to_end(&mut data).expect("Error reading test file"); - errors.append(&mut runner(&data, start_stop_hook)); + errors.append(&mut runner(&path, &data, start_stop_hook)); } pub fn run_test_file( path: &Path, - runner: fn(json_data: &[u8], start_stop_hook: &mut H) -> Vec, + runner: fn(path: &Path, json_data: &[u8], start_stop_hook: &mut H) -> Vec, start_stop_hook: &mut H ) { let mut data = Vec::new(); @@ -99,7 +117,7 @@ pub fn run_test_file( Err(_) => panic!("Error opening test file at: {:?}", path), }; file.read_to_end(&mut data).expect("Error reading test file"); - let results = runner(&data, start_stop_hook); + let results = runner(&path, &data, start_stop_hook); let empty: [String; 0] = []; assert_eq!(results, empty); } @@ -107,10 +125,25 @@ pub fn run_test_file( #[cfg(test)] macro_rules! test { ($name: expr, $skip: expr) => { - ::json_tests::test_common::run_test_path(::std::path::Path::new(concat!("res/ethereum/tests/", $name)), &$skip, do_json_test, &mut |_, _| ()); + ::json_tests::test_common::run_test_path( + ::std::path::Path::new(concat!("res/ethereum/tests/", $name)), + &$skip, + do_json_test, + &mut |_, _| () + ); } } +/// Declares a test: +/// +/// declare_test!(test_name, "path/to/folder/with/tests"); +/// +/// Declares a test but skip the named test files inside the folder (no extension): +/// +/// declare_test!(skip => ["a-test-file", "other-test-file"], test_name, "path/to/folder/with/tests"); +/// +/// NOTE: a skipped test is considered a passing test as far as `cargo test` is concerned. Normally +/// one test corresponds to a folder full of test files, each of which may contain many tests. #[macro_export] macro_rules! declare_test { (skip => $arr: expr, $id: ident, $name: expr) => { diff --git a/ethcore/src/json_tests/transaction.rs b/ethcore/src/json_tests/transaction.rs index febc61404d..0066b68159 100644 --- a/ethcore/src/json_tests/transaction.rs +++ b/ethcore/src/json_tests/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,35 +16,30 @@ use std::path::Path; use super::test_common::*; -use client::EvmTestClient; +use test_helpers::EvmTestClient; use ethjson; use rlp::Rlp; -use types::header::Header; -use types::transaction::UnverifiedTransaction; -use transaction_ext::Transaction; - -/// Run transaction jsontests on a given folder. -pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, do_json_test, h) -} - -/// Run transaction jsontests on a given file. -pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, do_json_test, h) -} - -// Block number used to run the tests. -// Make sure that all the specified features are activated. -const BLOCK_NUMBER: u64 = 0x6ffffffffffffe; - -fn do_json_test(json_data: &[u8], start_stop_hook: &mut H) -> Vec { - let tests = ethjson::transaction::Test::load(json_data).unwrap(); +use types::{ + header::Header, + errors::EthcoreError as Error, + transaction::UnverifiedTransaction +}; +use machine::transaction_ext::Transaction; + +#[allow(dead_code)] +fn do_json_test(path: &Path, json_data: &[u8], start_stop_hook: &mut H) -> Vec { + // Block number used to run the tests. + // Make sure that all the specified features are activated. + const BLOCK_NUMBER: u64 = 0x6ffffffffffffe; + + let tests = ethjson::test_helpers::transaction::Test::load(json_data) + .expect(&format!("Could not parse JSON transaction test data from {}", path.display())); let mut failed = Vec::new(); for (name, test) in tests.into_iter() { start_stop_hook(&name, HookType::OnStart); for (spec_name, result) in test.post_state { - let spec = match EvmTestClient::spec_from_json(&spec_name) { + let spec = match EvmTestClient::fork_spec_from_json(&spec_name) { Some(spec) => spec, None => { println!(" - {} | {:?} Ignoring tests because of missing spec", name, spec_name); @@ -60,7 +55,7 @@ fn do_json_test(json_data: &[u8], start_stop_hook: &mu let rlp: Vec = test.rlp.clone().into(); let res = Rlp::new(&rlp) .as_val() - .map_err(::error::Error::from) + .map_err(Error::from) .and_then(|t: UnverifiedTransaction| { let mut header: Header = Default::default(); // Use high enough number to activate all required features. @@ -73,7 +68,7 @@ fn do_json_test(json_data: &[u8], start_stop_hook: &mu }.into()); } spec.engine.verify_transaction_basic(&t, &header)?; - Ok(spec.engine.verify_transaction_unordered(t, &header)?) + Ok(t.verify_unordered()?) }); match (res, result.hash, result.sender) { diff --git a/ethcore/src/json_tests/trie.rs b/ethcore/src/json_tests/trie.rs index d56490ec7e..1820458f2f 100644 --- a/ethcore/src/json_tests/trie.rs +++ b/ethcore/src/json_tests/trie.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,28 +14,26 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +use std::path::Path; + use ethjson; use trie::{TrieFactory, TrieSpec}; -use ethtrie::RlpCodec; use ethereum_types::H256; use super::HookType; -pub use self::generic::run_test_path as run_generic_test_path; -pub use self::generic::run_test_file as run_generic_test_file; -pub use self::secure::run_test_path as run_secure_test_path; -pub use self::secure::run_test_file as run_secure_test_file; - -fn test_trie(json: &[u8], trie: TrieSpec, start_stop_hook: &mut H) -> Vec { - let tests = ethjson::trie::Test::load(json).unwrap(); - let factory = TrieFactory::<_, RlpCodec>::new(trie); +#[allow(dead_code)] +fn test_trie(path: &Path, json: &[u8], trie: TrieSpec, start_stop_hook: &mut H) -> Vec { + let tests = ethjson::test_helpers::trie::Test::load(json) + .expect(&format!("Could not parse JSON trie test data from {}", path.display())); + let factory = TrieFactory::new(trie, ethtrie::Layout); let mut result = vec![]; for (name, test) in tests.into_iter() { start_stop_hook(&name, HookType::OnStart); let mut memdb = journaldb::new_memory_db(); - let mut root = H256::default(); + let mut root = H256::zero(); let mut t = factory.create(&mut memdb, &mut root); for (key, value) in test.input.data.into_iter() { @@ -65,18 +63,9 @@ mod generic { use super::HookType; - /// Run generic trie jsontests on a given folder. - pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, do_json_test, h) - } - - /// Run generic trie jsontests on a given file. - pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, do_json_test, h) - } - - fn do_json_test(json: &[u8], h: &mut H) -> Vec { - super::test_trie(json, TrieSpec::Generic, h) + #[allow(dead_code)] + fn do_json_test(path: &Path, json: &[u8], h: &mut H) -> Vec { + super::test_trie(path, json, TrieSpec::Generic, h) } declare_test!{TrieTests_trietest, "TrieTests/trietest"} @@ -89,18 +78,9 @@ mod secure { use super::HookType; - /// Run secure trie jsontests on a given folder. - pub fn run_test_path(p: &Path, skip: &[&'static str], h: &mut H) { - ::json_tests::test_common::run_test_path(p, skip, do_json_test, h) - } - - /// Run secure trie jsontests on a given file. - pub fn run_test_file(p: &Path, h: &mut H) { - ::json_tests::test_common::run_test_file(p, do_json_test, h) - } - - fn do_json_test(json: &[u8], h: &mut H) -> Vec { - super::test_trie(json, TrieSpec::Secure, h) + #[allow(dead_code)] + fn do_json_test(path: &Path, json: &[u8], h: &mut H) -> Vec { + super::test_trie(path, json, TrieSpec::Secure, h) } declare_test!{TrieTests_hex_encoded_secure, "TrieTests/hex_encoded_securetrie_test"} diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 4437ec6883..8bacc77095 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,132 +17,90 @@ #![warn(missing_docs, unused_extern_crates)] //! Ethcore library -//! -//! ### Rust version: -//! - nightly -//! -//! ### Supported platforms: -//! - OSX -//! - Linux -//! -//! ### Building: -//! -//! - Ubuntu 14.04 and later: -//! -//! ```bash -//! -//! # install rustup -//! curl https://sh.rustup.rs -sSf | sh -//! -//! # download and build parity -//! git clone https://github.com/paritytech/parity-ethereum -//! cd parity -//! cargo build --release -//! ``` -//! -//! - OSX: -//! -//! ```bash -//! # install rocksdb && rustup -//! brew update -//! curl https://sh.rustup.rs -sSf | sh -//! -//! # download and build parity -//! git clone https://github.com/paritytech/parity-ethereum -//! cd parity -//! cargo build --release -//! ``` - -// Recursion limit required because of -// error_chain foreign_links. -#![recursion_limit="128"] +extern crate account_state; extern crate ansi_term; -extern crate bn; -extern crate byteorder; +extern crate client_traits; extern crate common_types as types; -extern crate crossbeam_utils; -extern crate eip_152; -extern crate ethabi; -extern crate ethash; +extern crate engine; extern crate ethcore_blockchain as blockchain; -extern crate ethcore_bloom_journal as bloom_journal; extern crate ethcore_call_contract as call_contract; extern crate ethcore_db as db; extern crate ethcore_io as io; extern crate ethcore_miner; extern crate ethereum_types; -extern crate ethjson; -extern crate ethkey; +extern crate executive_state; +extern crate futures; extern crate hash_db; -extern crate heapsize; extern crate itertools; extern crate journaldb; extern crate keccak_hash as hash; -extern crate keccak_hasher; extern crate kvdb; -extern crate kvdb_memorydb; -extern crate len_caching_lock; -extern crate lru_cache; +extern crate machine; extern crate memory_cache; -extern crate memory_db; -extern crate num; -extern crate num_cpus; extern crate parity_bytes as bytes; -extern crate parity_crypto; -extern crate parity_snappy as snappy; extern crate parking_lot; extern crate trie_db as trie; extern crate patricia_trie_ethereum as ethtrie; extern crate rand; extern crate rayon; +extern crate registrar; extern crate rlp; extern crate rustc_hex; extern crate serde; -extern crate stats; -extern crate time_utils; +extern crate snapshot; +extern crate spec; +extern crate state_db; +extern crate trace; +extern crate trie_vm_factories; extern crate triehash_ethereum as triehash; extern crate unexpected; extern crate using_queue; +extern crate verification; extern crate vm; -extern crate wasm; +#[cfg(test)] +extern crate account_db; #[cfg(test)] extern crate ethcore_accounts as accounts; +#[cfg(test)] +extern crate stats; + #[cfg(feature = "stratum")] extern crate ethcore_stratum; -#[cfg(any(test, feature = "tempdir"))] -extern crate tempdir; + +#[cfg(feature = "stratum")] +extern crate ethash; + +#[cfg(any(test, feature = "test-helpers"))] +extern crate parity_crypto; +#[cfg(any(test, feature = "test-helpers"))] +extern crate ethjson; +#[cfg(any(test, feature = "test-helpers"))] +extern crate kvdb_memorydb; #[cfg(any(test, feature = "kvdb-rocksdb"))] extern crate kvdb_rocksdb; +#[cfg(feature = "json-tests")] +#[macro_use] +extern crate lazy_static; +#[cfg(any(test, feature = "json-tests"))] +#[macro_use] +extern crate macros; +#[cfg(any(test, feature = "test-helpers"))] +extern crate pod; #[cfg(any(test, feature = "blooms-db"))] extern crate blooms_db; -#[cfg(any(test, feature = "env_logger"))] +#[cfg(feature = "env_logger")] extern crate env_logger; #[cfg(test)] -extern crate rlp_compress; -#[cfg(test)] -#[macro_use] -extern crate hex_literal; +extern crate serde_json; +#[cfg(any(test, feature = "tempdir"))] +extern crate tempdir; -#[macro_use] -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; -#[macro_use] -extern crate serde_derive; #[cfg_attr(test, macro_use)] extern crate evm; @@ -154,29 +112,8 @@ extern crate fetch; extern crate parity_runtime; pub mod block; -pub mod builtin; pub mod client; -pub mod engines; -pub mod error; -pub mod ethereum; -pub mod executed; -pub mod executive; -pub mod machine; pub mod miner; -pub mod pod_state; -pub mod pod_account; -pub mod snapshot; -pub mod spec; -pub mod state; -pub mod state_db; -pub mod trace; -pub mod transaction_ext; -pub mod verification; - -mod account_db; -mod externalities; -mod factory; -mod tx_filter; #[cfg(test)] mod tests; @@ -184,7 +121,3 @@ mod tests; pub mod json_tests; #[cfg(any(test, feature = "test-helpers"))] pub mod test_helpers; - -pub use executive::contract_address; -pub use evm::CreateContractAddress; -pub use trie::TrieSpec; diff --git a/ethcore/src/miner/filter_options.rs b/ethcore/src/miner/filter_options.rs new file mode 100644 index 0000000000..66f8b44a1d --- /dev/null +++ b/ethcore/src/miner/filter_options.rs @@ -0,0 +1,917 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +use std::fmt; +use std::marker::PhantomData; + +use ethereum_types::{Address, U256}; +use serde::de::{Deserialize, Deserializer, Error, MapAccess, Visitor}; +use types::transaction::SignedTransaction; + +/// Filtering options for the pending transactions +/// May be used for filtering transactions based on gas, gas price, value and/or nonce. +// NOTE: the fields are only `pub` because they are needed for tests +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct FilterOptions { + /// Filter based on the `sender` of the transaction. + pub from: FilterOperator
, + /// Filter based on `receiver` of the transaction. + pub to: FilterOperator>, + /// Filter based on `gas` of the transaction. + pub gas: FilterOperator, + /// Filter based on `gas price` of the transaction. + pub gas_price: FilterOperator, + /// Filter based on `value` of the transaction. + pub value: FilterOperator, + /// Filter based on `nonce` of the transaction. + pub nonce: FilterOperator, +} + +impl FilterOptions { + fn sender_matcher(filter: &FilterOperator
, candidate: &Address) -> bool { + match filter { + FilterOperator::Eq(address) => candidate == address, + FilterOperator::Any => true, + // Handled during deserialization + _ => unreachable!(), + } + } + + fn receiver_matcher(filter: &FilterOperator>, candidate: &Option
) -> bool { + match filter { + FilterOperator::Eq(address) => candidate == address, + FilterOperator::Any => true, + // Handled during deserialization + _ => unreachable!(), + } + } + fn value_matcher(filter: &FilterOperator, tx_value: &U256) -> bool { + match filter { + FilterOperator::Eq(ref value) => tx_value == value, + FilterOperator::GreaterThan(ref value) => tx_value > value, + FilterOperator::LessThan(ref value) => tx_value < value, + FilterOperator::Any => true, + } + } + + /// Determines whether a transaction passes the configured filter + pub fn matches(&self, tx: &SignedTransaction) -> bool { + Self::sender_matcher(&self.from, &tx.sender()) && + Self::receiver_matcher(&self.to, &tx.receiver()) && + Self::value_matcher(&self.gas, &tx.gas) && + Self::value_matcher(&self.gas_price, &tx.gas_price) && + Self::value_matcher(&self.value, &tx.value) && + Self::value_matcher(&self.nonce, &tx.nonce) + } +} + +impl Default for FilterOptions { + fn default() -> Self { + FilterOptions { + from: FilterOperator::Any, + to: FilterOperator::Any, + gas: FilterOperator::Any, + gas_price: FilterOperator::Any, + value: FilterOperator::Any, + nonce: FilterOperator::Any, + } + } +} + +/// The highly generic use of implementing Deserialize for FilterOperator +/// will result in a compiler error if the type FilterOperator::Eq(None) +/// gets returned explicitly. Therefore this Wrapper will be used for +/// deserialization, directly identifying the contract creation. +enum Wrapper { + /// FilterOperations + O(FilterOperator), + /// Contract Creation + CC, +} + +/// Available operators for filtering options. +/// The `from` filter only accepts Any and Eq(Address) +/// The `to` filter only accepts Any, Eq(Address) and Eq(None) for contract creation. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum FilterOperator { + /// Any (no filter) + Any, + /// Equal + Eq(T), + /// Greather than + GreaterThan(T), + /// Less than + LessThan(T), +} + +/// Since there are multiple operators which are not supported equally by all filters, +/// this trait will validate each of those operators. The corresponding method is called +/// inside the `Deserialize` -> `Visitor` implementation for FilterOperator. In case new +/// operators get introduced, a whitelist instead of a blacklist is used. +/// +/// The `from` filter validates with `validate_from` +/// The `to` filter validates with `validate_from` +/// All other filters such as gas and price validate with `validate_value` +trait Validate<'de, T, M: MapAccess<'de>> { + fn validate_from(&mut self) -> Result, M::Error>; + fn validate_to(&mut self) -> Result>, M::Error>; + fn validate_value(&mut self) -> Result, M::Error>; +} + +impl<'de, T, M> Validate<'de, T, M> for M +where + T: Deserialize<'de>, M: MapAccess<'de> +{ + fn validate_from(&mut self) -> Result, M::Error> { + use self::Wrapper as W; + use self::FilterOperator::*; + let wrapper = self.next_value()?; + match wrapper { + W::O(val) => { + match val { + Any | Eq(_) => Ok(val), + _ => { + Err(M::Error::custom( + "the `from` filter only supports the `eq` operator", + )) + } + } + }, + W::CC => { + Err(M::Error::custom( + "the `from` filter only supports the `eq` operator", + )) + } + } + } + fn validate_to(&mut self) -> Result>, M::Error> { + use self::Wrapper as W; + use self::FilterOperator::*; + let wrapper = self.next_value()?; + match wrapper { + W::O(val) => { + match val { + Any => Ok(Any), + Eq(address) => Ok(Eq(Some(address))), + _ => { + Err(M::Error::custom( + "the `to` filter only supports the `eq` or `action` operator", + )) + } + } + }, + W::CC => Ok(FilterOperator::Eq(None)), + } + } + fn validate_value(&mut self) -> Result, M::Error> { + use self::Wrapper as W; + let wrapper = self.next_value()?; + match wrapper { + W::O(val) => Ok(val), + W::CC => { + Err(M::Error::custom( + "the operator `action` is only supported by the `to` filter", + )) + } + } + } +} + +impl<'de> Deserialize<'de> for FilterOptions { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FilterOptionsVisitor; + impl<'de> Visitor<'de> for FilterOptionsVisitor { + type Value = FilterOptions; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + // "This Visitor expects to receive ..." + formatter.write_str("a map with one valid filter such as `from`, `to`, `gas`, `gas_price`, `value` or `nonce`") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'de>, + { + let mut filter = FilterOptions::default(); + while let Some(key) = map.next_key::()? { + match key.as_str() { + "from" => { + filter.from = map.validate_from()?; + }, + "to" => { + // Compiler cannot infer type, so set one (nothing specific for this method) + filter.to = Validate::<(), _>::validate_to(&mut map)?; + }, + "gas" => { + filter.gas = map.validate_value()?; + }, + "gas_price" => { + filter.gas_price = map.validate_value()?; + }, + "value" => { + filter.value = map.validate_value()?; + }, + "nonce" => { + filter.nonce = map.validate_value()?; + }, + unknown => { + return Err(M::Error::unknown_field( + unknown, + &["from", "to", "gas", "gas_price", "value", "nonce"], + )) + } + } + } + Ok(filter) + } + } + + impl<'de, T: Deserialize<'de>> Deserialize<'de> for Wrapper { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct WrapperVisitor { + data: PhantomData, + }; + impl<'de, T: Deserialize<'de>> Visitor<'de> for WrapperVisitor { + type Value = Wrapper; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + // "This Visitor expects to receive ..." + formatter.write_str( + "a map with one valid operator such as `eq`, `gt` or `lt`. \ + The to filter can also contain `action`", + ) + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'de>, + { + use self::Wrapper as W; + let mut counter = 0; + let mut f_op = Wrapper::O(FilterOperator::Any); + + while let Some(key) = map.next_key::()? { + match key.as_str() { + "eq" => f_op = W::O(FilterOperator::Eq(map.next_value()?)), + "gt" => f_op = W::O(FilterOperator::GreaterThan(map.next_value()?)), + "lt" => f_op = W::O(FilterOperator::LessThan(map.next_value()?)), + "action" => { + match map.next_value()? { + "contract_creation" => { + f_op = W::CC; + }, + _ => { + return Err(M::Error::custom( + "`action` only supports the value `contract_creation`", + )) + } + } + } + unknown => { + // skip mentioning `action` since it's a special/rare + // case and might confuse the usage with other filters. + return Err(M::Error::unknown_field(unknown, &["eq", "gt", "lt"])); + } + } + + counter += 1; + } + + // Good practices ensured: only one operator per filter field is allowed. + // In case there is more than just one operator, this method must still process + // all of them, otherwise serde returns an error mentioning a trailing comma issue + // (even on valid JSON), which is misleading to the user of this software. + if counter > 1 { + return Err(M::Error::custom( + "only one operator per filter type allowed", + )); + } + + Ok(f_op) + } + } + + deserializer.deserialize_map(WrapperVisitor { data: PhantomData }) + } + } + + deserializer.deserialize_map(FilterOptionsVisitor) + } +} + +#[cfg(test)] +mod tests { + use ethereum_types::{Address, U256}; + use serde_json; + use super::*; + use std::str::FromStr; + + #[test] + fn valid_defaults() { + let default = FilterOptions::default(); + assert_eq!(default.from, FilterOperator::Any); + assert_eq!(default.to, FilterOperator::Any); + assert_eq!(default.gas, FilterOperator::Any); + assert_eq!(default.gas_price, FilterOperator::Any); + assert_eq!(default.value, FilterOperator::Any); + assert_eq!(default.nonce, FilterOperator::Any); + + let json = r#"{}"#; + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, default); + } + + #[test] + fn valid_full_deserialization() { + let json = r#" + { + "from": { + "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f" + }, + "to": { + "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" + }, + "gas": { + "eq": "0x493e0" + }, + "gas_price": { + "eq": "0x12a05f200" + }, + "value": { + "eq": "0x0" + }, + "nonce": { + "eq": "0x577" + } + } + "#; + + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + from: FilterOperator::Eq(Address::from_str("5f3dffcf347944d3739b0805c934d86c8621997f").unwrap()), + to: FilterOperator::Eq(Some(Address::from_str("e8b2d01ffa0a15736b2370b6e5064f9702c891b6").unwrap())), + gas: FilterOperator::Eq(U256::from(300_000)), + gas_price: FilterOperator::Eq(U256::from(5_000_000_000 as i64)), + value: FilterOperator::Eq(U256::from(0)), + nonce: FilterOperator::Eq(U256::from(1399)), + }) + } + + #[test] + fn invalid_full_deserialization() { + // Invalid filter type `zyx` + let json = r#" + { + "from": { + "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f" + }, + "to": { + "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" + }, + "zyx": { + "eq": "0x493e0" + }, + "gas_price": { + "eq": "0x12a05f200" + }, + "value": { + "eq": "0x0" + }, + "nonce": { + "eq": "0x577" + } + } + "#; + + let res = serde_json::from_str::(json); + assert!(res.is_err()) + } + + #[test] + fn valid_from_operators() { + // Only one valid operator for from + let json = r#" + { + "from": { + "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + from: FilterOperator::Eq(Address::from_str("5f3dffcf347944d3739b0805c934d86c8621997f").unwrap()), + ..default + }); + } + + #[test] + fn invalid_from_operators() { + // Multiple operators are invalid + let json = r#" + { + "from": { + "eq": "0x5f3dffcf347944d3739b0805c934d86c8621997f", + "lt": "0x407d73d8a49eeb85d32cf465507dd71d507100c1" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Gt + let json = r#" + { + "from": { + "gt": "0x5f3dffcf347944d3739b0805c934d86c8621997f" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Lt + let json = r#" + { + "from": { + "lt": "0x5f3dffcf347944d3739b0805c934d86c8621997f" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Action + let json = r#" + { + "from": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Unknown operator + let json = r#" + { + "from": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } + + #[test] + fn valid_to_operators() { + // Only two valid operator for to + // Eq + let json = r#" + { + "to": { + "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + to: FilterOperator::Eq(Some(Address::from_str("e8b2d01ffa0a15736b2370b6e5064f9702c891b6").unwrap())), + ..default.clone() + }); + + // Action + let json = r#" + { + "to": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + to: FilterOperator::Eq(None), + ..default + }); + } + + #[test] + fn invalid_to_operators() { + // Multiple operators are invalid + let json = r#" + { + "to": { + "eq": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6", + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Gt + let json = r#" + { + "to": { + "gt": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Lt + let json = r#" + { + "to": { + "lt": "0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Action (invalid value, must be "contract_creation") + let json = r#" + { + "to": { + "action": "some_invalid_value" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Unknown operator + let json = r#" + { + "to": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } + + #[test] + fn valid_gas_operators() { + // Eq + let json = r#" + { + "gas": { + "eq": "0x493e0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas: FilterOperator::Eq(U256::from(300_000)), + ..default.clone() + }); + + // Gt + let json = r#" + { + "gas": { + "gt": "0x493e0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas: FilterOperator::GreaterThan(U256::from(300_000)), + ..default.clone() + }); + + // Lt + let json = r#" + { + "gas": { + "lt": "0x493e0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas: FilterOperator::LessThan(U256::from(300_000)), + ..default + }); + } + + #[test] + fn invalid_gas_operators() { + // Multiple operators are invalid + let json = r#" + { + "gas": { + "eq": "0x493e0", + "lt": "0x493e0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Action + let json = r#" + { + "gas": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Unknown operator + let json = r#" + { + "gas": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } + + #[test] + fn valid_gas_price_operators() { + // Eq + let json = r#" + { + "gas_price": { + "eq": "0x12a05f200" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas_price: FilterOperator::Eq(U256::from(5_000_000_000 as i64)), + ..default.clone() + }); + + // Gt + let json = r#" + { + "gas_price": { + "gt": "0x12a05f200" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas_price: FilterOperator::GreaterThan(U256::from(5_000_000_000 as i64)), + ..default.clone() + }); + + // Lt + let json = r#" + { + "gas_price": { + "lt": "0x12a05f200" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + gas_price: FilterOperator::LessThan(U256::from(5_000_000_000 as i64)), + ..default + }); + } + + #[test] + fn invalid_gas_price_operators() { + // Multiple operators are invalid + let json = r#" + { + "gas_price": { + "eq": "0x12a05f200", + "lt": "0x12a05f200" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Action + let json = r#" + { + "gas_price": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Unknown operator + let json = r#" + { + "gas_price": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } + + #[test] + fn valid_value_operators() { + // Eq + let json = r#" + { + "value": { + "eq": "0x0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + value: FilterOperator::Eq(U256::from(0)), + ..default.clone() + }); + + // Gt + let json = r#" + { + "value": { + "gt": "0x0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + value: FilterOperator::GreaterThan(U256::from(0)), + ..default.clone() + }); + + // Lt + let json = r#" + { + "value": { + "lt": "0x0" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + value: FilterOperator::LessThan(U256::from(0)), + ..default + }); + } + + #[test] + fn invalid_value_operators() { + // Multiple operators are invalid + let json = r#" + { + "value": { + "eq": "0x0", + "lt": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Action + let json = r#" + { + "value": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Unknown operator + let json = r#" + { + "value": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } + + #[test] + fn valid_nonce_operators() { + // Eq + let json = r#" + { + "nonce": { + "eq": "0x577" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + nonce: FilterOperator::Eq(U256::from(1399)), + ..default.clone() + }); + + // Gt + let json = r#" + { + "nonce": { + "gt": "0x577" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + nonce: FilterOperator::GreaterThan(U256::from(1399)), + ..default.clone() + }); + + // Lt + let json = r#" + { + "nonce": { + "lt": "0x577" + } + } + "#; + let default = FilterOptions::default(); + let res = serde_json::from_str::(json).unwrap(); + assert_eq!(res, FilterOptions { + nonce: FilterOperator::LessThan(U256::from(1399)), + ..default + }); + } + + #[test] + fn invalid_nonce_operators() { + // Multiple operators are invalid + let json = r#" + { + "nonce": { + "eq": "0x577", + "lt": "0x577" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Action + let json = r#" + { + "nonce": { + "action": "contract_creation" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + + // Unknown operator + let json = r#" + { + "nonce": { + "abc": "0x0" + } + } + "#; + let res = serde_json::from_str::(json); + assert!(res.is_err()); + } +} diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index f31973c13c..ddddf5a184 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,41 +24,46 @@ use bytes::Bytes; use call_contract::CallContract; use ethcore_miner::gas_pricer::GasPricer; use ethcore_miner::local_accounts::LocalAccounts; -use ethcore_miner::pool::{self, TransactionQueue, VerifiedTransaction, QueueStatus, PrioritizationStrategy}; +use ethcore_miner::pool::{self, TransactionQueue, VerifiedTransaction, QueueStatus, PrioritizationStrategy, TxStatus}; use ethcore_miner::service_transaction_checker::ServiceTransactionChecker; #[cfg(feature = "work-notify")] use ethcore_miner::work_notify::NotifyWork; use ethereum_types::{H256, U256, Address}; +use futures::sync::mpsc; use io::IoChannel; +use miner::filter_options::FilterOptions; use miner::pool_client::{PoolClient, CachedNonceClient, NonceCache}; use miner::{self, MinerService}; use parking_lot::{Mutex, RwLock}; use rayon::prelude::*; -use types::transaction::{ - self, - Action, - UnverifiedTransaction, - SignedTransaction, - PendingTransaction, +use types::{ + BlockNumber, + ids::TransactionId, + block::Block, + header::Header, + ids::BlockId, + io_message::ClientIoMessage, + engines::{Seal, SealingState}, + errors::{EthcoreError as Error, ExecutionError}, + receipt::RichReceipt, + transaction::{ + self, + Action, + UnverifiedTransaction, + SignedTransaction, + PendingTransaction, + }, }; -use types::BlockNumber; -use types::block::Block; -use types::header::Header; -use types::receipt::RichReceipt; use using_queue::{UsingQueue, GetAction}; use block::{ClosedBlock, SealedBlock}; -use client::{ - BlockChain, ChainInfo, BlockProducer, SealedBlockImporter, Nonce, TransactionInfo, TransactionId -}; -use client::{BlockId, ClientIoMessage}; -use client::traits::EngineClient; -use engines::{EthEngine, Seal, EngineSigner}; -use error::{Error, ErrorKind}; -use executed::ExecutionError; -use executive::contract_address; +use client::{BlockProducer, SealedBlockImporter, Client}; +use client_traits::{BlockChain, ChainInfo, Nonce, TransactionInfo, EngineClient, ForceUpdateSealing}; +use engine::{Engine, signer::EngineSigner}; +use machine::executive::contract_address; use spec::Spec; -use state::State; +use account_state::State; +use vm::CreateContractAddress; /// Different possible definitions for pending transaction set. #[derive(Debug, PartialEq)] @@ -145,7 +150,7 @@ pub struct MinerOptions { 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? + /// Do we want to mark transactions received 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, @@ -203,7 +208,7 @@ pub enum Author { /// Sealing block is external and we only need a reward beneficiary (i.e. PoW) External(Address), /// Sealing is done internally, we need a way to create signatures to seal block (i.e. PoA) - Sealer(Box), + Sealer(Box), } impl Author { @@ -230,6 +235,10 @@ impl SealingWork { fn reseal_allowed(&self) -> bool { Instant::now() > self.next_allowed_reseal } + + fn work_available(&self) -> bool { + self.queue.peek_last_ref().is_some() + } } /// Keeps track of transactions using priority queue and holds currently mined block. @@ -239,29 +248,38 @@ pub struct Miner { sealing: Mutex, params: RwLock, #[cfg(feature = "work-notify")] - listeners: RwLock>>, + listeners: RwLock>>, nonce_cache: NonceCache, gas_pricer: Mutex, options: MinerOptions, // TODO [ToDr] Arc is only required because of price updater transaction_queue: Arc, - engine: Arc, - accounts: Arc, - io_channel: RwLock>>, + engine: Arc, + accounts: Arc, + io_channel: RwLock>>>, service_transaction_checker: Option, } impl Miner { /// Push listener that will handle new jobs #[cfg(feature = "work-notify")] - pub fn add_work_listener(&self, notifier: Box) { + pub fn add_work_listener(&self, notifier: Box) { self.listeners.write().push(notifier); self.sealing.lock().enabled = true; } /// Set a callback to be notified about imported transactions' hashes. - pub fn add_transactions_listener(&self, f: Box) { - self.transaction_queue.add_listener(f); + pub fn pending_transactions_receiver(&self) -> mpsc::UnboundedReceiver>> { + let (sender, receiver) = mpsc::unbounded(); + self.transaction_queue.add_pending_listener(sender); + receiver + } + + /// Set a callback to be notified about imported transactions' hashes. + pub fn full_transactions_receiver(&self) -> mpsc::UnboundedReceiver>> { + let (sender, receiver) = mpsc::unbounded(); + self.transaction_queue.add_full_listener(sender); + receiver } /// Creates new instance of miner Arc. @@ -276,12 +294,13 @@ impl Miner { let tx_queue_strategy = options.tx_queue_strategy; let nonce_cache_size = cmp::max(4096, limits.max_count / 4); let refuse_service_transactions = options.refuse_service_transactions; + let engine = spec.engine.clone(); Miner { sealing: Mutex::new(SealingWork { queue: UsingQueue::new(options.work_queue_size), enabled: options.force_sealing - || spec.engine.seals_internally().is_some(), + || spec.engine.sealing_state() != SealingState::External, next_allowed_reseal: Instant::now(), next_mandatory_reseal: Instant::now() + options.reseal_max_period, last_request: None, @@ -294,7 +313,7 @@ impl Miner { options, transaction_queue: Arc::new(TransactionQueue::new(limits, verifier_options, tx_queue_strategy)), accounts: Arc::new(accounts), - engine: spec.engine.clone(), + engine, io_channel: RwLock::new(None), service_transaction_checker: if refuse_service_transactions { None @@ -308,6 +327,13 @@ impl Miner { /// /// NOTE This should be only used for tests. pub fn new_for_tests(spec: &Spec, accounts: Option>) -> Miner { + Miner::new_for_tests_force_sealing(spec, accounts, false) + } + + /// Creates new instance of miner with given spec and accounts. + /// + /// NOTE This should be only used for tests. + pub fn new_for_tests_force_sealing(spec: &Spec, accounts: Option>, force_sealing: bool) -> Miner { let minimal_gas_price = 0.into(); Miner::new(MinerOptions { pool_verification_options: pool::verifier::Options { @@ -317,12 +343,13 @@ impl Miner { no_early_reject: false, }, reseal_min_period: Duration::from_secs(0), + force_sealing, ..Default::default() }, GasPricer::new_fixed(minimal_gas_price), spec, accounts.unwrap_or_default()) } /// Sets `IoChannel` - pub fn set_io_channel(&self, io_channel: IoChannel) { + pub fn set_io_channel(&self, io_channel: IoChannel>) { *self.io_channel.write() = Some(io_channel); } @@ -403,7 +430,8 @@ impl Miner { let chain_info = chain.chain_info(); // Open block - let (mut open_block, original_work_hash) = { + // Some engines add transactions to the block for their own purposes, e.g. AuthorityRound RANDAO. + let (mut open_block, original_work_hash, engine_txs) = { let mut sealing = self.sealing.lock(); let last_work_hash = sealing.queue.peek_last_ref().map(|pb| pb.header.hash()); let best_hash = chain_info.best_block_hash; @@ -414,38 +442,48 @@ 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.queue.get_pending_if(|b| b.header.parent_hash() == &best_hash) { + match sealing.queue.get_pending_if(|b| b.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 - chain.reopen_block(old_block) + (chain.reopen_block(old_block), last_work_hash, Vec::new()) } None => { // block not found - create it. trace!(target: "miner", "prepare_block: No existing work - making new block"); let params = self.params.read().clone(); - match chain.prepare_open_block( + let block = match chain.prepare_open_block( params.author, params.gas_range_target, params.extra_data, ) { Ok(block) => block, Err(err) => { - warn!(target: "miner", "Open new block failed with error {:?}. This is likely an error in chain specificiations or on-chain consensus smart contracts.", err); + warn!(target: "miner", "Open new block failed with error {:?}. This is likely an error in \ + chain specification or on-chain consensus smart contracts.", err); + return None; + } + }; + + // Before adding from the queue to the new block, give the engine a chance to add transactions. + match self.engine.generate_engine_transactions(&block) { + Ok(transactions) => (block, last_work_hash, transactions), + Err(err) => { + error!(target: "miner", "Failed to prepare engine transactions for new block: {:?}. \ + This is likely an error in chain specification or on-chain consensus smart \ + contracts.", err); return None; } } } - }; - - if self.options.infinite_pending_block { - open_block.remove_gas_limit(); } - - (open_block, last_work_hash) }; + if self.options.infinite_pending_block { + open_block.remove_gas_limit(); + } + let mut invalid_transactions = HashSet::new(); let mut not_allowed_transactions = HashSet::new(); let mut senders_to_penalize = HashSet::new(); @@ -469,13 +507,13 @@ impl Miner { MAX_SKIPPED_TRANSACTIONS.saturating_add(cmp::min(*open_block.header.gas_limit() / min_tx_gas, u64::max_value().into()).as_u64() as usize) }; - let pending: Vec> = self.transaction_queue.pending( + let queue_txs: 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, + max_len: max_transactions.saturating_sub(engine_txs.len()), ordering: miner::PendingOrdering::Priority, } ); @@ -485,12 +523,11 @@ impl Miner { }; let block_start = Instant::now(); - debug!(target: "miner", "Attempting to push {} transactions.", pending.len()); + debug!(target: "miner", "Attempting to push {} transactions.", engine_txs.len() + queue_txs.len()); - for tx in pending { + for transaction in engine_txs.into_iter().chain(queue_txs.into_iter().map(|tx| tx.signed().clone())) { let start = Instant::now(); - let transaction = tx.signed().clone(); let hash = transaction.hash(); let sender = transaction.sender(); @@ -514,7 +551,7 @@ impl Miner { debug!(target: "miner", "Adding tx {:?} took {} ms", hash, took_ms(&took)); match result { - Err(Error(ErrorKind::Execution(ExecutionError::BlockGasLimitReached { gas_limit, gas_used, gas }), _)) => { + Err(Error::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 @@ -538,13 +575,13 @@ impl Miner { } }, // 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(ErrorKind::Execution(ExecutionError::InvalidNonce { expected, got }), _)) => { + // If there is erroneous state of transaction queue it will be fixed when next block is imported. + Err(Error::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(ErrorKind::Transaction(transaction::Error::AlreadyImported), _)) => {}, - Err(Error(ErrorKind::Transaction(transaction::Error::NotAllowed), _)) => { + Err(Error::Transaction(transaction::Error::AlreadyImported)) => {}, + Err(Error::Transaction(transaction::Error::NotAllowed)) => { not_allowed_transactions.insert(hash); debug!(target: "miner", "Skipping non-allowed transaction for sender {:?}", hash); }, @@ -610,6 +647,7 @@ impl Miner { trace!(target: "miner", "requires_reseal: sealing enabled"); + const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5; // Disable sealing if there were no requests for SEALING_TIMEOUT_IN_BLOCKS let had_requests = sealing.last_request.map(|last_request| best_block.saturating_sub(last_request) <= SEALING_TIMEOUT_IN_BLOCKS @@ -618,7 +656,7 @@ impl Miner { // 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) + || self.engine.sealing_state() == SealingState::Ready || had_requests; let should_disable_sealing = !sealing_enabled; @@ -627,7 +665,7 @@ impl Miner { should_disable_sealing, self.forced_sealing(), self.transaction_queue.has_local_pending_transactions(), - self.engine.seals_internally(), + self.engine.sealing_state(), had_requests, ); @@ -645,8 +683,8 @@ impl Miner { // TODO: (https://github.com/paritytech/parity-ethereum/issues/10407) // This is only used in authority_round path, and should be refactored to merge with the other seal() path. - // Attempts to perform internal sealing (one that does not require work) and handles the result depending on the - // type of Seal. + // Attempts to perform internal sealing (one that does not require work: e.g. Clique + // and Aura) 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, { @@ -659,63 +697,55 @@ impl Miner { return false } } - - trace!(target: "miner", "seal_block_internally: attempting internal seal."); + let block_number = block.header.number(); + trace!(target: "miner", "seal_block_internally: attempting internal seal for block #{}", block_number); 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 + Err(e) => { + error!(target: "miner", "seal_block_internally: Block #{}, Could not decode header from parent block (hash={}): {:?}", block_number, block.header.parent_hash(), e); + return false + } } - } - None => return false, + }, + None => { + trace!(target: "miner", "Block #{}: Parent with hash={} does not exist in our DB", block_number, block.header.parent_hash()); + return false + }, }; - match self.engine.generate_seal(&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.set_pending(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; - } + let sealing_result = + match self.engine.generate_seal(&block, &parent_header) { + // Directly import a regular sealed block. + Seal::Regular(seal) => { + trace!(target: "miner", "Block #{}: Received a Regular seal.", block_number); + { + 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, - } + block + .lock() + .seal(&*self.engine, seal) + .map(|sealed| { + match chain.import_sealed_block(sealed) { + Ok(_) => true, + Err(e) => { + error!(target: "miner", "Block #{}: seal_and_import_block_internally: import_sealed_block returned {:?}", block_number, e); + false + } + } + }) + .unwrap_or_else(|e| { + warn!("ERROR: Block #{}, importing sealed block failed when given internally generated seal: {}", block_number, e); + false + }) + }, + Seal::None => false, + }; + sealing_result } /// Prepares work which has to be done to seal. @@ -783,23 +813,30 @@ impl Miner { } /// Prepare a pending block. Returns the preparation status. - fn prepare_pending_block(&self, client: &C) -> BlockPreparationStatus where - C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, + /// Only used by externally sealing engines. + fn prepare_pending_block(&self, client: &C) -> BlockPreparationStatus + where + C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, { trace!(target: "miner", "prepare_pending_block: entering"); - let prepare_new = { - 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.enabled = true; - true - } else { - false - } - }; + // Unless we are `--force-sealing` we create pending blocks if + // 1. we have local pending transactions + // 2. or someone is requesting `eth_getWork` + // When either condition is true, `sealing.enabled` is flipped to true (and if you + // have no more local transactions or stop calling `eth_getWork`, it is set to `false`). + + // Here we check if there are pending blocks already (if so, we don't need to create + // a new one); if there are none, we set `sealing.enabled` to true because the + // calling code expects it to be on (or they wouldn't have called this method). + // Yes, it's a bit convoluted. + let prepare_new_block = self.maybe_enable_sealing(); + + if self.engine.sealing_state() != SealingState::External { + trace!(target: "miner", "prepare_pending_block: engine not sealing externally; not preparing"); + return BlockPreparationStatus::NotPrepared; + } - let preparation_status = if prepare_new { + let preparation_status = if prepare_new_block { // -------------------------------------------------------------------------- // | NOTE Code below requires sealing locks. | // | Make sure to release the locks before calling that method. | @@ -829,22 +866,37 @@ impl Miner { preparation_status } + + /// Set `sealing.enabled` to true if there is available work to do (pending or in the queue). + fn maybe_enable_sealing(&self) -> bool { + let mut sealing = self.sealing.lock(); + if !sealing.work_available() { + trace!(target: "miner", "maybe_enable_sealing: we have work to do so enabling sealing"); + sealing.enabled = true; + true + } else { + false + } + } + /// Prepare pending block, check whether sealing is needed, and then update sealing. fn prepare_and_update_sealing(&self, chain: &C) { - - // 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) == BlockPreparationStatus::NotPrepared { - // 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); + match self.engine.sealing_state() { + SealingState::Ready => { + self.maybe_enable_sealing(); + self.update_sealing(chain, ForceUpdateSealing::No); + } + SealingState::External => { + // this calls `maybe_enable_sealing()` + if self.prepare_pending_block(chain) == BlockPreparationStatus::NotPrepared { + self.update_sealing(chain, ForceUpdateSealing::No); + } + } + SealingState::NotReady => { self.maybe_enable_sealing(); }, } } } -const SEALING_TIMEOUT_IN_BLOCKS : u64 = 5; - impl miner::MinerService for Miner { type State = State<::state_db::StateDB>; @@ -860,21 +912,38 @@ impl miner::MinerService for Miner { self.params.write().extra_data = extra_data; } - fn set_author(&self, author: Author) { - self.params.write().author = author.address(); - - if let Author::Sealer(signer) = author { - if self.engine.seals_internally().is_some() { - // Enable sealing - self.sealing.lock().enabled = true; - // -------------------------------------------------------------------------- - // | 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(signer); - } else { - warn!("Setting an EngineSigner while Engine does not require one."); + fn set_author>>(&self, author: T) { + let author_opt = author.into(); + self.params.write().author = author_opt.as_ref().map(Author::address).unwrap_or_default(); + + match author_opt { + Some(Author::Sealer(signer)) => { + if self.engine.sealing_state() != SealingState::External { + // Enable sealing + self.sealing.lock().enabled = true; + // -------------------------------------------------------------------------- + // | 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(Some(signer)); + } else { + warn!("Setting an EngineSigner while Engine does not require one."); + } + } + Some(Author::External(_address)) => (), + None => { + // Clear the author. + if self.engine.sealing_state() != SealingState::External { + // Disable sealing. + self.sealing.lock().enabled = false; + // -------------------------------------------------------------------------- + // | 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(None); + } } } } @@ -923,7 +992,7 @@ impl miner::MinerService for Miner { let client = self.pool_client(chain); let results = self.transaction_queue.import( client, - transactions.into_iter().map(pool::verifier::Transaction::Unverified).collect(), + transactions.into_iter().map(pool::verifier::Transaction::Unverified), ); // -------------------------------------------------------------------------- @@ -949,7 +1018,7 @@ impl miner::MinerService for Miner { let client = self.pool_client(chain); let imported = self.transaction_queue.import( client, - vec![pool::verifier::Transaction::Local(pending)] + Some(pool::verifier::Transaction::Local(pending)) ).pop().expect("one result returned per added transaction; one added => one result; qed"); // -------------------------------------------------------------------------- @@ -1031,6 +1100,20 @@ impl miner::MinerService for Miner { fn ready_transactions(&self, chain: &C, max_len: usize, ordering: miner::PendingOrdering) -> Vec> + where + C: ChainInfo + Nonce + Sync, + { + // No special filtering options applied (neither tx_hash, receiver or sender) + self.ready_transactions_filtered(chain, max_len, None, ordering) + } + + fn ready_transactions_filtered( + &self, + chain: &C, + max_len: usize, + filter: Option, + ordering: miner::PendingOrdering, + ) -> Vec> where C: ChainInfo + Nonce + Sync, { @@ -1042,7 +1125,7 @@ impl miner::MinerService for Miner { // those transactions are valid and will just be ready to be included in next block. let nonce_cap = None; - self.transaction_queue.pending( + let mut pending = self.transaction_queue.pending( CachedNonceClient::new(chain, &self.nonce_cache), pool::PendingSettings { block_number: chain_info.best_block_number, @@ -1051,7 +1134,15 @@ impl miner::MinerService for Miner { max_len, ordering, }, - ) + ); + + pending.retain(|tx| { + filter.as_ref().map_or(true, |filter| { + filter.matches(tx.signed()) + }) + }); + + pending }; let from_pending = || { @@ -1060,6 +1151,11 @@ impl miner::MinerService for Miner { .iter() .map(|signed| pool::VerifiedTransaction::from_pending_block_transaction(signed.clone())) .map(Arc::new) + .filter(|tx| { + filter.as_ref().map_or(true, |filter| { + filter.matches(tx.signed()) + }) + }) .take(max_len) .collect() }, chain_info.best_block_number) @@ -1109,6 +1205,11 @@ impl miner::MinerService for Miner { let prev_gas = if index == 0 { Default::default() } else { receipts[index - 1].gas_used }; let receipt = &receipts[index]; RichReceipt { + from: tx.sender(), + to: match tx.action { + Action::Create => None, + Action::Call(ref address) => Some(*address), + }, transaction_hash: tx.hash(), transaction_index: index, cumulative_gas_used: receipt.gas_used, @@ -1117,7 +1218,7 @@ impl miner::MinerService for Miner { 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) + Some(contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &tx.nonce, &tx.data).0) } }, logs: receipt.logs.clone(), @@ -1131,14 +1232,21 @@ impl miner::MinerService for Miner { /// Update sealing if required. /// Prepare the block and work if the Engine does not seal internally. - fn update_sealing(&self, chain: &C) where + fn update_sealing(&self, chain: &C, force: ForceUpdateSealing) where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, { trace!(target: "miner", "update_sealing"); - // Do nothing if reseal is not required, + // Do nothing if we don't want to force update_sealing and reseal is not required. // but note that `requires_reseal` updates internal state. - if !self.requires_reseal(chain.chain_info().best_block_number) { + if force == ForceUpdateSealing::No && + !self.requires_reseal(chain.chain_info().best_block_number) + { + return; + } + + let sealing_state = self.engine.sealing_state(); + if sealing_state == SealingState::NotReady { return; } @@ -1162,23 +1270,20 @@ impl miner::MinerService for Miner { } } - match self.engine.seals_internally() { - Some(true) => { + match sealing_state { + SealingState::Ready => { 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"); } + return }, - 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.set_pending(block); - }, - None => { + SealingState::NotReady => unreachable!("We returned right after sealing_state was computed. qed."), + SealingState::External => { trace!(target: "miner", "update_sealing: engine does not seal internally, preparing work"); - self.prepare_work(block, original_work_hash) + self.prepare_work(block, original_work_hash); }, - } + }; } fn is_currently_sealing(&self) -> bool { @@ -1188,7 +1293,7 @@ impl miner::MinerService for Miner { fn work_package(&self, chain: &C) -> Option<(H256, BlockNumber, u64, U256)> where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync, { - if self.engine.seals_internally().is_some() { + if self.engine.sealing_state() != SealingState::External { return None; } @@ -1202,31 +1307,43 @@ impl miner::MinerService for Miner { // 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.lock().queue.get_used_if( - if self.options.enable_resubmission { - GetAction::Clone - } else { - GetAction::Take - }, - |b| &b.header.bare_hash() == &block_hash - ) { - trace!(target: "miner", "Submitted block {}={} with seal {:?}", block_hash, b.header.bare_hash(), seal); - b.lock().try_seal(&*self.engine, seal).or_else(|e| { - warn!(target: "miner", "Mined solution rejected: {}", e); - Err(ErrorKind::PowInvalid.into()) - }) - } else { + let action = if self.options.enable_resubmission { + GetAction::Clone + } else { + GetAction::Take + }; + + let block = self.sealing.lock().queue + .get_used_if(action, |b| &b.header.bare_hash() == &block_hash) + .ok_or_else(|| { warn!(target: "miner", "Submitted solution rejected: Block unknown or out of date."); - Err(ErrorKind::PowHashInvalid.into()) - }; + Error::PowHashInvalid + })?; + + trace!( + target: "miner", "Submitted block {hash}={bare_hash} with seal {seal:?}", + hash = block_hash, + bare_hash = block.header.bare_hash(), + seal = seal + ); - result.and_then(|sealed| { - let n = sealed.header.number(); - let h = sealed.header.hash(); - info!(target: "miner", "Submitted block imported OK. #{}: {}", Colour::White.bold().paint(format!("{}", n)), Colour::White.bold().paint(format!("{:x}", h))); - Ok(sealed) - }) + let sealed = block.lock() + .try_seal(&*self.engine, seal) + .map_err(|e| { + warn!(target: "miner", "Mined solution rejected: {}", e); + Error::PowInvalid + })?; + + let n = sealed.header.number(); + let h = sealed.header.hash(); + + info!( + target: "miner", "Submitted block imported OK. #{number}: {hash}", + number = Colour::White.bold().paint(n.to_string()), + hash = Colour::White.bold().paint(format!("{:x}", h)) + ); + + Ok(sealed) } fn chain_new_blocks(&self, chain: &C, imported: &[H256], _invalid: &[H256], enacted: &[H256], retracted: &[H256], is_internal_import: bool) @@ -1259,8 +1376,7 @@ impl miner::MinerService for Miner { .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(); + .map(pool::verifier::Transaction::Retracted); let _ = self.transaction_queue.import( client.clone(), txs, @@ -1279,7 +1395,7 @@ impl miner::MinerService for Miner { // | NOTE Code below requires sealing locks. | // | Make sure to release the locks before calling that method. | // -------------------------------------------------------------------------- - self.update_sealing(chain); + self.update_sealing(chain, ForceUpdateSealing::No); } } @@ -1297,8 +1413,7 @@ impl miner::MinerService for Miner { let engine = self.engine.clone(); let accounts = self.accounts.clone(); let service_transaction_checker = self.service_transaction_checker.clone(); - - let cull = move |chain: &::client::Client| { + let cull = move |chain: &Client| { let client = PoolClient::new( chain, &nonce_cache, @@ -1307,21 +1422,24 @@ impl miner::MinerService for Miner { service_transaction_checker.as_ref(), ); queue.cull(client); - if is_internal_import { - chain.update_sealing(); + if engine.should_reseal_on_update() { + // force update_sealing here to skip `reseal_required` checks + chain.update_sealing(ForceUpdateSealing::Yes); } }; - if let Err(e) = channel.send(ClientIoMessage::execute(cull)) { + if let Err(e) = channel.send(ClientIoMessage::::execute(cull)) { warn!(target: "miner", "Error queueing cull: {:?}", e); } } else { self.transaction_queue.cull(client); - if is_internal_import { - self.update_sealing(chain); + if self.engine.should_reseal_on_update() { + // force update_sealing here to skip `reseal_required` checks + self.update_sealing(chain, ForceUpdateSealing::Yes); } } } + if let Some(ref service_transaction_checker) = self.service_transaction_checker { match service_transaction_checker.refresh_cache(chain) { Ok(true) => { @@ -1364,21 +1482,27 @@ mod tests { use super::*; use accounts::AccountProvider; - use ethkey::{Generator, Random}; + use parity_crypto::publickey::{Generator, Random}; use hash::keccak; use rustc_hex::FromHex; - use types::BlockNumber; - use client::{TestBlockChainClient, EachBlockWith, ChainInfo, ImportSealedBlock}; - use miner::{MinerService, PendingOrdering}; - use test_helpers::{generate_dummy_client, generate_dummy_client_with_spec}; - use types::transaction::{Transaction}; + use client_traits::ChainInfo; + use client::ImportSealedBlock; + use miner::{MinerService, PendingOrdering, filter_options::FilterOperator}; + use test_helpers::{ + generate_dummy_client, generate_dummy_client_with_spec, TestBlockChainClient, EachBlockWith + }; + use types::{ + BlockNumber, + transaction::Transaction + }; + use spec; #[test] fn should_prepare_block_to_seal() { // given let client = TestBlockChainClient::default(); - let miner = Miner::new_for_tests(&Spec::new_test(), None); + let miner = Miner::new_for_tests(&spec::new_test(), None); // when let sealing_work = miner.work_package(&client); @@ -1389,7 +1513,7 @@ mod tests { fn should_still_work_after_a_couple_of_blocks() { // given let client = TestBlockChainClient::default(); - let miner = Miner::new_for_tests(&Spec::new_test(), None); + let miner = Miner::new_for_tests(&spec::new_test(), None); let res = miner.work_package(&client); let hash = res.unwrap().0; @@ -1433,7 +1557,7 @@ mod tests { }, }, GasPricer::new_fixed(0u64.into()), - &Spec::new_test(), + &spec::new_test(), ::std::collections::HashSet::new(), // local accounts ) } @@ -1489,7 +1613,7 @@ mod tests { // when new block is imported let client = generate_dummy_client(2); - let imported = [0.into()]; + let imported = [H256::zero()]; let empty = &[]; miner.chain_new_blocks(&*client, &imported, empty, &imported, empty, false); @@ -1556,7 +1680,7 @@ mod tests { ..miner().options }, GasPricer::new_fixed(0u64.into()), - &Spec::new_test(), + &spec::new_test(), local_accounts, ); let transaction = transaction(); @@ -1589,6 +1713,30 @@ mod tests { assert_eq!(miner.prepare_pending_block(&client), BlockPreparationStatus::NotPrepared); } + #[test] + fn should_reject_local_transaction_with_invalid_chain_id() { + let spec = spec::new_test(); + let miner = Miner::new_for_tests(&spec, None); + let client = TestBlockChainClient::default(); + let chain_id = spec.chain_id(); + + // chain_id + 100500 is invalid + let import = miner.import_claimed_local_transaction( + &client, + PendingTransaction::new(transaction_with_chain_id(chain_id + 10500), None), + false, + ); + assert_eq!(import, Err(transaction::Error::InvalidChainId)); + + // chain_id is valid + let import = miner.import_claimed_local_transaction( + &client, + PendingTransaction::new(transaction_with_chain_id(chain_id), None), + false, + ); + assert_eq!(import, Ok(())); + } + #[test] fn should_prioritize_locals() { let client = TestBlockChainClient::default(); @@ -1599,7 +1747,7 @@ mod tests { ..miner().options }, GasPricer::new_fixed(0u64.into()), - &Spec::new_test(), + &spec::new_test(), HashSet::from_iter(vec![transaction.sender()].into_iter()), ); let best_block = 0; @@ -1630,22 +1778,29 @@ mod tests { #[test] fn internal_seals_without_work() { - let spec = Spec::new_instant(); + let _ = env_logger::try_init(); + let spec = spec::new_instant(); let miner = Miner::new_for_tests(&spec, None); let client = generate_dummy_client(2); - let import = miner.import_external_transactions(&*client, vec![transaction_with_chain_id(spec.chain_id()).into()]).pop().unwrap(); + 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); + miner.update_sealing(&*client, ForceUpdateSealing::No); client.flush_queue(); assert!(miner.pending_block(0).is_none()); assert_eq!(client.chain_info().best_block_number, 3 as BlockNumber); - assert!(miner.import_own_transaction(&*client, PendingTransaction::new(transaction_with_chain_id(spec.chain_id()).into(), None)).is_ok()); + assert!(miner.import_own_transaction( + &*client, + PendingTransaction::new(transaction_with_chain_id(spec.chain_id()).into(), None) + ).is_ok()); - miner.update_sealing(&*client); + miner.update_sealing(&*client, ForceUpdateSealing::No); client.flush_queue(); assert!(miner.pending_block(0).is_none()); assert_eq!(client.chain_info().best_block_number, 4 as BlockNumber); @@ -1653,7 +1808,7 @@ mod tests { #[test] fn should_not_fail_setting_engine_signer_without_account_provider() { - let spec = Spec::new_test_round; + let spec = spec::new_test_round; let tap = Arc::new(AccountProvider::transient_provider()); let addr = tap.insert_account(keccak("1").into(), &"".into()).unwrap(); let client = generate_dummy_client_with_spec(spec); @@ -1669,33 +1824,33 @@ mod tests { #[test] fn should_mine_if_internal_sealing_is_enabled() { - let spec = Spec::new_instant(); + let spec = spec::new_instant(); let miner = Miner::new_for_tests(&spec, None); let client = generate_dummy_client(2); - miner.update_sealing(&*client); + miner.update_sealing(&*client, ForceUpdateSealing::No); assert!(miner.is_currently_sealing()); } #[test] fn should_not_mine_if_internal_sealing_is_disabled() { - let spec = Spec::new_test_round(); + let spec = spec::new_test_round(); let miner = Miner::new_for_tests(&spec, None); let client = generate_dummy_client(2); - miner.update_sealing(&*client); + miner.update_sealing(&*client, ForceUpdateSealing::No); assert!(!miner.is_currently_sealing()); } #[test] fn should_not_mine_if_no_fetch_work_request() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let miner = Miner::new_for_tests(&spec, None); let client = generate_dummy_client(2); - miner.update_sealing(&*client); + miner.update_sealing(&*client, ForceUpdateSealing::No); assert!(!miner.is_currently_sealing()); } @@ -1709,12 +1864,12 @@ mod tests { fn notify(&self, _pow_hash: H256, _difficulty: U256, _number: u64) { } } - let spec = Spec::new_test(); + 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); + miner.update_sealing(&*client, ForceUpdateSealing::No); assert!(miner.is_currently_sealing()); } @@ -1722,7 +1877,7 @@ mod tests { #[test] fn should_set_new_minimum_gas_price() { // Creates a new GasPricer::Fixed behind the scenes - let miner = Miner::new_for_tests(&Spec::new_test(), None); + let miner = Miner::new_for_tests(&spec::new_test(), None); let expected_minimum_gas_price: U256 = 0x1337.into(); miner.set_minimal_gas_price(expected_minimum_gas_price).unwrap(); @@ -1735,7 +1890,6 @@ mod tests { #[cfg(feature = "price-info")] fn dynamic_gas_pricer() -> GasPricer { - use std::time::Duration; use parity_runtime::Executor; use fetch::Client as FetchClient; use ethcore_miner::gas_price_calibrator::{GasPriceCalibrator, GasPriceCalibratorOptions}; @@ -1753,6 +1907,7 @@ mod tests { }, fetch, p, + "fake_endpoint".to_owned() ) ) } @@ -1761,7 +1916,7 @@ mod tests { #[cfg(feature = "price-info")] fn should_fail_to_set_new_minimum_gas_price() { // We get a fixed gas pricer by default, need to change that - let miner = Miner::new_for_tests(&Spec::new_test(), None); + let miner = Miner::new_for_tests(&spec::new_test(), None); let calibrated_gas_pricer = dynamic_gas_pricer(); *miner.gas_pricer.lock() = calibrated_gas_pricer; @@ -1774,4 +1929,93 @@ mod tests { assert!(received_error_msg == expected_error_msg); } + + fn filter_tester(option: PendingSet) { + let client = TestBlockChainClient::default(); + let mut miner = miner(); + miner.options.pending_set = option; + let transaction = transaction(); + let best_block = 10; + let mut sender = transaction.sender(); + + miner.import_own_transaction(&client, PendingTransaction::new(transaction, None)).unwrap(); + + assert_eq!(miner.pending_transactions(best_block), None); + assert_eq!(miner.pending_receipts(best_block), None); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(FilterOptions::default()), PendingOrdering::Priority).len(), + 1 + ); + + // sender filter + { + // reverse address for false match + for byte in sender.as_bytes_mut() { + *byte = !*byte; + } + let mut filter = FilterOptions::default(); + filter.from = FilterOperator::Eq(sender); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + + // receiver filter + { + let mut filter = FilterOptions::default(); + filter.to = FilterOperator::Eq(Some(sender)); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + + // gas filter + { + let mut filter = FilterOptions::default(); + filter.gas = FilterOperator::LessThan(U256::from(100_000)); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + + // gas_price filter + { + let mut filter = FilterOptions::default(); + filter.gas_price = FilterOperator::GreaterThan(U256::from(10)); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + + // value filter + { + let mut filter = FilterOptions::default(); + filter.value = FilterOperator::Eq(U256::from(19)); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + + // nonce filter + { + let mut filter = FilterOptions::default(); + filter.nonce = FilterOperator::GreaterThan(U256::from(10)); + assert_eq!( + miner.ready_transactions_filtered(&client, 10, Some(filter), PendingOrdering::Priority).len(), + 0 + ); + } + } + + #[test] + fn transaction_filtering() { + filter_tester(PendingSet::AlwaysQueue); + filter_tester(PendingSet::AlwaysSealing); + filter_tester(PendingSet::SealingOrElseQueue); + } } diff --git a/ethcore/src/miner/mod.rs b/ethcore/src/miner/mod.rs index fd7ab96513..2033bd3dcf 100644 --- a/ethcore/src/miner/mod.rs +++ b/ethcore/src/miner/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,12 +20,13 @@ //! Keeps track of transactions and currently sealed pending block. mod miner; - +mod filter_options; pub mod pool_client; #[cfg(feature = "stratum")] pub mod stratum; pub use self::miner::{Miner, MinerOptions, Penalization, PendingSet, AuthoringParams, Author}; +pub use self::filter_options::FilterOptions; pub use ethcore_miner::local_accounts::LocalAccounts; pub use ethcore_miner::pool::PendingOrdering; @@ -36,25 +37,28 @@ use bytes::Bytes; use ethcore_miner::pool::{VerifiedTransaction, QueueStatus, local_transactions}; use ethereum_types::{H256, U256, Address}; use types::transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}; -use types::BlockNumber; -use types::block::Block; -use types::header::Header; -use types::receipt::RichReceipt; - -use block::SealedBlock; -use call_contract::{CallContract, RegistryInfo}; -use client::{ - ScheduleInfo, - BlockChain, BlockProducer, SealedBlockImporter, ChainInfo, - AccountData, Nonce, +use types::{ + BlockNumber, + errors::EthcoreError as Error, + block::Block, + header::Header, + receipt::RichReceipt, +}; + +use call_contract::CallContract; +use registrar::RegistrarClient; +use client_traits::{BlockChain, ChainInfo, AccountData, Nonce, ScheduleInfo, ForceUpdateSealing}; +use account_state::state::StateInfo; + +use crate::{ + block::SealedBlock, + client::{BlockProducer, SealedBlockImporter}, }; -use error::Error; -use state::StateInfo; /// Provides methods to verify incoming external transactions pub trait TransactionVerifierClient: Send + Sync // Required for ServiceTransactionChecker - + CallContract + RegistryInfo + + CallContract + RegistrarClient // Required for verifiying transactions + BlockChain + ScheduleInfo + AccountData {} @@ -83,7 +87,7 @@ pub trait MinerService : Send + Sync { where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync; /// Update current pending block - fn update_sealing(&self, chain: &C) + fn update_sealing(&self, chain: &C, force: ForceUpdateSealing) where C: BlockChain + CallContract + BlockProducer + SealedBlockImporter + Nonce + Sync; // Notifications @@ -131,7 +135,7 @@ pub trait MinerService : Send + Sync { /// Set info necessary to sign consensus messages and block authoring. /// /// On chains where sealing is done externally (e.g. PoW) we provide only reward beneficiary. - fn set_author(&self, author: Author); + fn set_author>>(&self, author: T); // Transaction Pool @@ -184,6 +188,14 @@ pub trait MinerService : Send + Sync { fn ready_transactions(&self, chain: &C, max_len: usize, ordering: PendingOrdering) -> Vec> where C: ChainInfo + Nonce + Sync; + /// Get a list of all ready transactions either ordered by priority or unordered (cheaper), optionally filtered by hash, sender or receiver. + /// + /// 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_filtered(&self, chain: &C, max_len: usize, filter: Option, 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>; diff --git a/ethcore/src/miner/pool_client.rs b/ethcore/src/miner/pool_client.rs index 93cc0e8cfb..df086f576d 100644 --- a/ethcore/src/miner/pool_client.rs +++ b/ethcore/src/miner/pool_client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -32,14 +32,17 @@ use types::transaction::{ UnverifiedTransaction, SignedTransaction, }; -use types::header::Header; +use types::{ + header::Header, + ids::TransactionId, +}; use parking_lot::RwLock; use call_contract::CallContract; -use client::{TransactionId, BlockInfo, Nonce}; -use engines::EthEngine; +use client_traits::{BlockInfo, Nonce}; +use engine::Engine; +use machine::transaction_ext::Transaction; use miner; -use transaction_ext::Transaction; /// Cache for state nonces. #[derive(Debug, Clone)] @@ -72,8 +75,8 @@ impl NonceCache { pub struct PoolClient<'a, C: 'a> { chain: &'a C, cached_nonces: CachedNonceClient<'a, C>, - engine: &'a EthEngine, - accounts: &'a LocalAccounts, + engine: &'a dyn Engine, + accounts: &'a dyn LocalAccounts, best_block_header: Header, service_transaction_checker: Option<&'a ServiceTransactionChecker>, } @@ -98,8 +101,8 @@ impl<'a, C: 'a> PoolClient<'a, C> where pub fn new( chain: &'a C, cache: &'a NonceCache, - engine: &'a EthEngine, - accounts: &'a LocalAccounts, + engine: &'a dyn Engine, + accounts: &'a dyn LocalAccounts, service_transaction_checker: Option<&'a ServiceTransactionChecker>, ) -> Self { let best_block_header = chain.best_block_header(); @@ -136,9 +139,14 @@ impl<'a, C: 'a> pool::client::Client for PoolClient<'a, C> where self.chain.transaction_block(TransactionId::Hash(*hash)).is_some() } - fn verify_transaction(&self, tx: UnverifiedTransaction)-> Result { + fn verify_transaction_basic(&self, tx: &UnverifiedTransaction) -> Result<(), transaction::Error> { + self.engine.verify_transaction_basic(tx, &self.best_block_header)?; + Ok(()) + } + + 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)?; + let tx = tx.verify_unordered()?; self.engine.machine().verify_transaction(&tx, &self.best_block_header, self.chain)?; Ok(tx) @@ -218,30 +226,30 @@ impl<'a, C: 'a> CachedNonceClient<'a, C> { 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.nonces.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.nonces.write(); - let nonce = self.client.latest_nonce(address); - cache.insert(*address, nonce); - - if cache.len() < self.cache.limit { - return nonce - } - - debug!(target: "txpool", "NonceCache: reached limit."); - trace_time!("nonce_cache:clear"); - - // Remove excessive amount of entries from the cache - let to_remove: Vec<_> = cache.keys().take(self.cache.limit / 2).cloned().collect(); - for x in to_remove { - cache.remove(&x); - } - - nonce - } + fn account_nonce(&self, address: &Address) -> U256 { + if let Some(nonce) = self.cache.nonces.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.nonces.write(); + let nonce = self.client.latest_nonce(address); + cache.insert(*address, nonce); + + if cache.len() < self.cache.limit { + return nonce + } + + debug!(target: "txpool", "NonceCache: reached limit."); + trace_time!("nonce_cache:clear"); + + // Remove excessive amount of entries from the cache + let to_remove: Vec<_> = cache.keys().take(self.cache.limit / 2).cloned().collect(); + for x in to_remove { + cache.remove(&x); + } + + nonce + } } diff --git a/ethcore/src/miner/stratum.rs b/ethcore/src/miner/stratum.rs index 71862225ab..bddb483c74 100644 --- a/ethcore/src/miner/stratum.rs +++ b/ethcore/src/miner/stratum.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use std::net::{SocketAddr, AddrParseError}; use std::fmt; use client::{Client, ImportSealedBlock}; -use ethereum_types::{H64, H256, clean_0x, U256}; +use ethereum_types::{H64, H256, U256}; use ethash::{self, SeedHashCompute}; #[cfg(feature = "work-notify")] use ethcore_miner::work_notify::NotifyWork; @@ -47,6 +47,14 @@ pub struct Options { pub secret: Option, } +fn clean_0x(s: &str) -> &str { + if s.starts_with("0x") { + &s[2..] + } else { + s + } +} + struct SubmitPayload { nonce: H64, pow_hash: H256, @@ -217,8 +225,6 @@ impl NotifyWork for Stratum { self.service.push_work_all( self.dispatcher.payload(pow_hash, difficulty, number) - ).unwrap_or_else( - |e| warn!(target: "stratum", "Error while pushing work: {:?}", e) ); } } @@ -231,23 +237,20 @@ impl Stratum { let dispatcher = Arc::new(StratumJobDispatcher::new(miner, client)); - let stratum_svc = StratumService::start( + let service = StratumService::start( &SocketAddr::new(options.listen_addr.parse::()?, options.port), dispatcher.clone(), options.secret.clone(), )?; - Ok(Stratum { - dispatcher: dispatcher, - service: stratum_svc, - }) + Ok(Stratum { dispatcher, service }) } /// Start STRATUM job dispatcher and register it in the miner #[cfg(feature = "work-notify")] pub fn register(cfg: &Options, miner: Arc, client: Weak) -> Result<(), Error> { let stratum = Stratum::start(cfg, Arc::downgrade(&miner.clone()), client)?; - miner.add_work_listener(Box::new(stratum) as Box); + miner.add_work_listener(Box::new(stratum) as Box); Ok(()) } } diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs deleted file mode 100644 index 2a9ac911f1..0000000000 --- a/ethcore/src/snapshot/account.rs +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Account state encoding and decoding - -use account_db::{AccountDB, AccountDBMut}; -use types::basic_account::BasicAccount; -use bytes::Bytes; -use ethereum_types::{H256, U256}; -use ethtrie::{TrieDB, TrieDBMut}; -use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP}; -use hash_db::HashDB; -use rlp::{RlpStream, Rlp}; -use snapshot::{Error, Progress}; -use std::collections::HashSet; -use trie::{Trie, TrieMut}; -use std::sync::atomic::Ordering; - -// An empty account -- these were replaced with RLP null data for a space optimization in v1. -const ACC_EMPTY: BasicAccount = BasicAccount { - nonce: U256([0, 0, 0, 0]), - balance: U256([0, 0, 0, 0]), - storage_root: KECCAK_NULL_RLP, - code_hash: KECCAK_EMPTY, -}; - -// whether an encoded account has code and how it is referred to. -#[repr(u8)] -enum CodeState { - // the account has no code. - Empty = 0, - // raw code is encoded. - Inline = 1, - // the code is referred to by hash. - Hash = 2, -} - -impl CodeState { - fn from(x: u8) -> Result { - match x { - 0 => Ok(CodeState::Empty), - 1 => Ok(CodeState::Inline), - 2 => Ok(CodeState::Hash), - _ => Err(Error::UnrecognizedCodeState(x)) - } - } - - fn raw(self) -> u8 { - self as u8 - } -} - -// walk the account's storage trie, returning a vector of RLP items containing the -// account address hash, account properties and the storage. Each item contains at most `max_storage_items` -// storage records split according to snapshot format definition. -pub fn to_fat_rlps( - account_hash: &H256, - acc: &BasicAccount, - acct_db: &AccountDB, - used_code: &mut HashSet, - first_chunk_size: usize, - max_chunk_size: usize, - p: &Progress, -) -> Result, Error> { - let db = &(acct_db as &dyn HashDB<_,_>); - let db = TrieDB::new(db, &acc.storage_root)?; - let mut chunks = Vec::new(); - let mut db_iter = db.iter()?; - let mut target_chunk_size = first_chunk_size; - let mut account_stream = RlpStream::new_list(2); - let mut leftover: Option> = None; - loop { - account_stream.append(account_hash); - account_stream.begin_list(5); - - account_stream.append(&acc.nonce) - .append(&acc.balance); - - // [has_code, code_hash]. - if acc.code_hash == KECCAK_EMPTY { - account_stream.append(&CodeState::Empty.raw()).append_empty_data(); - } else if used_code.contains(&acc.code_hash) { - account_stream.append(&CodeState::Hash.raw()).append(&acc.code_hash); - } else { - match acct_db.get(&acc.code_hash) { - Some(c) => { - used_code.insert(acc.code_hash.clone()); - account_stream.append(&CodeState::Inline.raw()).append(&&*c); - } - None => { - warn!("code lookup failed during snapshot"); - account_stream.append(&false).append_empty_data(); - } - } - } - - account_stream.begin_unbounded_list(); - if account_stream.len() > target_chunk_size { - // account does not fit, push an empty record to mark a new chunk - target_chunk_size = max_chunk_size; - chunks.push(Vec::new()); - } - - if let Some(pair) = leftover.take() { - if !account_stream.append_raw_checked(&pair, 1, target_chunk_size) { - return Err(Error::ChunkTooSmall); - } - } - - loop { - if p.abort.load(Ordering::SeqCst) { - trace!(target: "snapshot", "to_fat_rlps: aborting snapshot"); - return Err(Error::SnapshotAborted); - } - match db_iter.next() { - Some(Ok((k, v))) => { - let pair = { - let mut stream = RlpStream::new_list(2); - stream.append(&k).append(&&*v); - stream.drain() - }; - if !account_stream.append_raw_checked(&pair, 1, target_chunk_size) { - account_stream.complete_unbounded_list(); - let stream = ::std::mem::replace(&mut account_stream, RlpStream::new_list(2)); - chunks.push(stream.out()); - target_chunk_size = max_chunk_size; - leftover = Some(pair); - break; - } - }, - Some(Err(e)) => { - return Err(e.into()); - }, - None => { - account_stream.complete_unbounded_list(); - let stream = ::std::mem::replace(&mut account_stream, RlpStream::new_list(2)); - chunks.push(stream.out()); - return Ok(chunks); - } - } - - } - } -} - -// decode a fat rlp, and rebuild the storage trie as we go. -// returns the account structure along with its newly recovered code, -// if it exists. -pub fn from_fat_rlp( - acct_db: &mut AccountDBMut, - rlp: Rlp, - mut storage_root: H256, -) -> Result<(BasicAccount, Option), Error> { - - // check for special case of empty account. - if rlp.is_empty() { - return Ok((ACC_EMPTY, None)); - } - - let nonce = rlp.val_at(0)?; - let balance = rlp.val_at(1)?; - let code_state: CodeState = { - let raw: u8 = rlp.val_at(2)?; - CodeState::from(raw)? - }; - - // load the code if it exists. - let (code_hash, new_code) = match code_state { - CodeState::Empty => (KECCAK_EMPTY, None), - CodeState::Inline => { - let code: Bytes = rlp.val_at(3)?; - let code_hash = acct_db.insert(&code); - - (code_hash, Some(code)) - } - CodeState::Hash => { - let code_hash = rlp.val_at(3)?; - - (code_hash, None) - } - }; - - { - let mut storage_trie = if storage_root.is_zero() { - TrieDBMut::new(acct_db, &mut storage_root) - } else { - TrieDBMut::from_existing(acct_db, &mut storage_root)? - }; - let pairs = rlp.at(4)?; - for pair_rlp in pairs.iter() { - let k: Bytes = pair_rlp.val_at(0)?; - let v: Bytes = pair_rlp.val_at(1)?; - - storage_trie.insert(&k, &v)?; - } - } - - let acc = BasicAccount { - nonce: nonce, - balance: balance, - storage_root: storage_root, - code_hash: code_hash, - }; - - Ok((acc, new_code)) -} - -#[cfg(test)] -mod tests { - use account_db::{AccountDB, AccountDBMut}; - use types::basic_account::BasicAccount; - use test_helpers::get_temp_state_db; - use snapshot::tests::helpers::fill_storage; - use snapshot::Progress; - - use hash::{KECCAK_EMPTY, KECCAK_NULL_RLP, keccak}; - use ethereum_types::{H256, Address}; - use hash_db::HashDB; - use kvdb::DBValue; - use rlp::Rlp; - - use std::collections::HashSet; - - use super::{ACC_EMPTY, to_fat_rlps, from_fat_rlp}; - - #[test] - fn encoding_basic() { - let mut db = get_temp_state_db(); - let addr = Address::random(); - - let account = BasicAccount { - nonce: 50.into(), - balance: 123456789.into(), - storage_root: KECCAK_NULL_RLP, - code_hash: KECCAK_EMPTY, - }; - - let thin_rlp = ::rlp::encode(&account); - assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); - let p = Progress::default(); - let fat_rlps = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); - let fat_rlp = Rlp::new(&fat_rlps[0]).at(1).unwrap(); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account); - } - - #[test] - fn encoding_storage() { - let mut db = get_temp_state_db(); - let addr = Address::random(); - - let account = { - let acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr); - let mut root = KECCAK_NULL_RLP; - fill_storage(acct_db, &mut root, &mut H256::zero()); - BasicAccount { - nonce: 25.into(), - balance: 987654321.into(), - storage_root: root, - code_hash: KECCAK_EMPTY, - } - }; - - let thin_rlp = ::rlp::encode(&account); - assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); - - let p = Progress::default(); - - let fat_rlp = to_fat_rlps(&keccak(&addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), usize::max_value(), usize::max_value(), &p).unwrap(); - let fat_rlp = Rlp::new(&fat_rlp[0]).at(1).unwrap(); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account); - } - - #[test] - fn encoding_storage_split() { - let mut db = get_temp_state_db(); - let addr = Address::random(); - - let account = { - let acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr); - let mut root = KECCAK_NULL_RLP; - fill_storage(acct_db, &mut root, &mut H256::zero()); - BasicAccount { - nonce: 25.into(), - balance: 987654321.into(), - storage_root: root, - code_hash: KECCAK_EMPTY, - } - }; - - let thin_rlp = ::rlp::encode(&account); - assert_eq!(::rlp::decode::(&thin_rlp).unwrap(), account); - - let p = Progress::default(); - let fat_rlps = to_fat_rlps(&keccak(addr), &account, &AccountDB::new(db.as_hash_db(), &addr), &mut Default::default(), 500, 1000, &p).unwrap(); - let mut root = KECCAK_NULL_RLP; - let mut restored_account = None; - for rlp in fat_rlps { - let fat_rlp = Rlp::new(&rlp).at(1).unwrap(); - restored_account = Some(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr), fat_rlp, root).unwrap().0); - root = restored_account.as_ref().unwrap().storage_root.clone(); - } - assert_eq!(restored_account, Some(account)); - } - - #[test] - fn encoding_code() { - let mut db = get_temp_state_db(); - - let addr1 = Address::random(); - let addr2 = Address::random(); - - let code_hash = { - let mut acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr1); - acct_db.insert(b"this is definitely code") - }; - - { - let mut acct_db = AccountDBMut::new(db.as_hash_db_mut(), &addr2); - acct_db.emplace(code_hash.clone(), DBValue::from_slice(b"this is definitely code")); - } - - let account1 = BasicAccount { - nonce: 50.into(), - balance: 123456789.into(), - storage_root: KECCAK_NULL_RLP, - code_hash, - }; - - let account2 = BasicAccount { - nonce: 400.into(), - balance: 98765432123456789usize.into(), - storage_root: KECCAK_NULL_RLP, - code_hash, - }; - - let mut used_code = HashSet::new(); - let p1 = Progress::default(); - let p2 = Progress::default(); - let fat_rlp1 = to_fat_rlps(&keccak(&addr1), &account1, &AccountDB::new(db.as_hash_db(), &addr1), &mut used_code, usize::max_value(), usize::max_value(), &p1).unwrap(); - let fat_rlp2 = to_fat_rlps(&keccak(&addr2), &account2, &AccountDB::new(db.as_hash_db(), &addr2), &mut used_code, usize::max_value(), usize::max_value(), &p2).unwrap(); - assert_eq!(used_code.len(), 1); - - 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_hash_db_mut(), &addr2), fat_rlp2, H256::zero()).unwrap(); - assert!(maybe_code.is_none()); - assert_eq!(acc, account2); - - let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &addr1), fat_rlp1, H256::zero()).unwrap(); - assert_eq!(maybe_code, Some(b"this is definitely code".to_vec())); - assert_eq!(acc, account1); - } - - #[test] - fn encoding_empty_acc() { - let mut db = get_temp_state_db(); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hash_db_mut(), &Address::zero()), Rlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None)); - } -} diff --git a/ethcore/src/snapshot/error.rs b/ethcore/src/snapshot/error.rs deleted file mode 100644 index 6faa19da22..0000000000 --- a/ethcore/src/snapshot/error.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Snapshot-related errors. - -use std::fmt; - -use types::ids::BlockId; - -use ethereum_types::H256; -use ethtrie::TrieError; -use rlp::DecoderError; - -/// Snapshot-related errors. -#[derive(Debug)] -pub enum Error { - /// Invalid starting block for snapshot. - InvalidStartingBlock(BlockId), - /// Block not found. - BlockNotFound(H256), - /// Incomplete chain. - IncompleteChain, - /// Best block has wrong state root. - WrongStateRoot(H256, H256), - /// Wrong block hash. - WrongBlockHash(u64, H256, H256), - /// Too many blocks contained within the snapshot. - TooManyBlocks(u64, u64), - /// Old starting block in a pruned database. - OldBlockPrunedDB, - /// Missing code. - MissingCode(Vec), - /// Unrecognized code encoding. - UnrecognizedCodeState(u8), - /// Restoration aborted. - RestorationAborted, - /// Trie error. - Trie(TrieError), - /// Decoder error. - Decoder(DecoderError), - /// Io error. - Io(::std::io::Error), - /// Snapshot version is not supported. - VersionNotSupported(u64), - /// Max chunk size is to small to fit basic account data. - ChunkTooSmall, - /// Oversized chunk - ChunkTooLarge, - /// Snapshots not supported by the consensus engine. - SnapshotsUnsupported, - /// Aborted snapshot - SnapshotAborted, - /// Bad epoch transition. - BadEpochProof(u64), - /// Wrong chunk format. - WrongChunkFormat(String), - /// Unlinked ancient block chain - UnlinkedAncientBlockChain, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::InvalidStartingBlock(ref id) => write!(f, "Invalid starting block: {:?}", id), - Error::BlockNotFound(ref hash) => write!(f, "Block not found in chain: {}", hash), - Error::IncompleteChain => write!(f, "Incomplete blockchain."), - Error::WrongStateRoot(ref expected, ref found) => write!(f, "Final block has wrong state root. Expected {:?}, got {:?}", expected, found), - Error::WrongBlockHash(ref num, ref expected, ref found) => - write!(f, "Block {} had wrong hash. expected {:?}, got {:?}", num, expected, found), - Error::TooManyBlocks(ref expected, ref found) => write!(f, "Snapshot contained too many blocks. Expected {}, got {}", expected, found), - Error::OldBlockPrunedDB => write!(f, "Attempted to create a snapshot at an old block while using \ - a pruned database. Please re-run with the --pruning archive flag."), - Error::MissingCode(ref missing) => write!(f, "Incomplete snapshot: {} contract codes not found.", missing.len()), - Error::UnrecognizedCodeState(state) => write!(f, "Unrecognized code encoding ({})", state), - Error::RestorationAborted => write!(f, "Snapshot restoration aborted."), - Error::Io(ref err) => err.fmt(f), - Error::Decoder(ref err) => err.fmt(f), - Error::Trie(ref err) => err.fmt(f), - Error::VersionNotSupported(ref ver) => write!(f, "Snapshot version {} is not supprted.", ver), - Error::ChunkTooSmall => write!(f, "Chunk size is too small."), - Error::ChunkTooLarge => write!(f, "Chunk size is too large."), - Error::SnapshotsUnsupported => write!(f, "Snapshots unsupported by consensus engine."), - Error::SnapshotAborted => write!(f, "Snapshot was aborted."), - Error::BadEpochProof(i) => write!(f, "Bad epoch proof for transition to epoch {}", i), - Error::WrongChunkFormat(ref msg) => write!(f, "Wrong chunk format: {}", msg), - Error::UnlinkedAncientBlockChain => write!(f, "Unlinked ancient blocks chain"), - } - } -} - -impl From<::std::io::Error> for Error { - fn from(err: ::std::io::Error) -> Self { - Error::Io(err) - } -} - -impl From for Error { - fn from(err: TrieError) -> Self { - Error::Trie(err) - } -} - -impl From for Error { - fn from(err: DecoderError) -> Self { - Error::Decoder(err) - } -} - -impl From> for Error where Error: From { - fn from(err: Box) -> Self { - Error::from(*err) - } -} diff --git a/ethcore/src/snapshot/traits.rs b/ethcore/src/snapshot/traits.rs deleted file mode 100644 index aa61b595bf..0000000000 --- a/ethcore/src/snapshot/traits.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use super::{ManifestData, RestorationStatus}; -use ethereum_types::H256; -use bytes::Bytes; - -/// The interface for a snapshot network service. -/// This handles: -/// - restoration of snapshots to temporary databases. -/// - responding to queries for snapshot manifests and chunks -pub trait SnapshotService : Sync + Send { - /// Query the most recent manifest data. - fn manifest(&self) -> Option; - - /// Get the supported range of snapshot version numbers. - /// `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; - - /// Ask the snapshot service for the restoration status. - fn status(&self) -> RestorationStatus; - - /// Begin snapshot restoration. - /// If restoration in-progress, this will reset it. - /// From this point on, any previous snapshot may become unavailable. - fn begin_restore(&self, manifest: ManifestData); - - /// Abort an in-progress restoration if there is one. - fn abort_restore(&self); - - /// Feed a raw state chunk to the service to be processed asynchronously. - /// no-op if not currently restoring. - fn restore_state_chunk(&self, hash: H256, chunk: Bytes); - - /// 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); - - /// Abort in-progress snapshotting if there is one. - fn abort_snapshot(&self); - - /// 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 deleted file mode 100644 index 5c4712cff9..0000000000 --- a/ethcore/src/snapshot/watcher.rs +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Watcher for snapshot-related chain events. - -use parking_lot::Mutex; -use client::{BlockInfo, Client, ChainNotify, NewBlocks, ClientIoMessage}; -use types::ids::BlockId; - -use io::IoChannel; -use ethereum_types::H256; - -use std::sync::Arc; - -// helper trait for transforming hashes to numbers and checking if syncing. -trait Oracle: Send + Sync { - fn to_number(&self, hash: H256) -> Option; - - fn is_major_importing(&self) -> bool; -} - -struct StandardOracle where F: 'static + Send + Sync + Fn() -> bool { - client: Arc, - sync_status: F, -} - -impl Oracle for StandardOracle - where F: Send + Sync + Fn() -> bool -{ - fn to_number(&self, hash: H256) -> Option { - self.client.block_header(BlockId::Hash(hash)).map(|h| h.number()) - } - - fn is_major_importing(&self) -> bool { - (self.sync_status)() - } -} - -// helper trait for broadcasting a block to take a snapshot at. -trait Broadcast: Send + Sync { - fn take_at(&self, num: Option); -} - -impl Broadcast for Mutex> { - fn take_at(&self, num: Option) { - let num = match num { - Some(n) => n, - None => return, - }; - - trace!(target: "snapshot_watcher", "broadcast: {}", num); - - if let Err(e) = self.lock().send(ClientIoMessage::TakeSnapshot(num)) { - warn!("Snapshot watcher disconnected from IoService: {}", e); - } - } -} - -/// A `ChainNotify` implementation which will trigger a snapshot event -/// at certain block numbers. -pub struct Watcher { - oracle: Box, - broadcast: Box, - period: u64, - history: u64, -} - -impl Watcher { - /// Create a new `Watcher` which will trigger a snapshot event - /// once every `period` blocks, but only after that block is - /// `history` blocks old. - pub fn new(client: Arc, sync_status: F, channel: IoChannel, period: u64, history: u64) -> Self - where F: 'static + Send + Sync + Fn() -> bool - { - Watcher { - oracle: Box::new(StandardOracle { - client: client, - sync_status: sync_status, - }), - broadcast: Box::new(Mutex::new(channel)), - period: period, - history: history, - } - } -} - -impl ChainNotify for Watcher { - fn new_blocks(&self, new_blocks: NewBlocks) { - if self.oracle.is_major_importing() || new_blocks.has_more_blocks_to_import { return } - - trace!(target: "snapshot_watcher", "{} imported", new_blocks.imported.len()); - - let highest = new_blocks.imported.into_iter() - .filter_map(|h| self.oracle.to_number(h)) - .filter(|&num| num >= self.period + self.history) - .map(|num| num - self.history) - .filter(|num| num % self.period == 0) - .fold(0, ::std::cmp::max); - - match highest { - 0 => self.broadcast.take_at(None), - _ => self.broadcast.take_at(Some(highest)), - } - } -} - -#[cfg(test)] -mod tests { - use super::{Broadcast, Oracle, Watcher}; - - use client::{ChainNotify, NewBlocks, ChainRoute}; - - use ethereum_types::{H256, U256}; - - use std::collections::HashMap; - use std::time::Duration; - - struct TestOracle(HashMap); - - impl Oracle for TestOracle { - fn to_number(&self, hash: H256) -> Option { - self.0.get(&hash).cloned() - } - - fn is_major_importing(&self) -> bool { false } - } - - struct TestBroadcast(Option); - impl Broadcast for TestBroadcast { - fn take_at(&self, num: Option) { - if num != self.0 { - panic!("Watcher broadcast wrong number. Expected {:?}, found {:?}", self.0, num); - } - } - } - - // 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(); - - let watcher = Watcher { - oracle: Box::new(TestOracle(map)), - broadcast: Box::new(TestBroadcast(expected)), - period: period, - history: history, - }; - - watcher.new_blocks(NewBlocks::new( - hashes, - vec![], - ChainRoute::default(), - vec![], - vec![], - DURATION_ZERO, - false - )); - } - - // helper - - #[test] - fn should_not_fire() { - harness(vec![0], 5, 0, None); - } - - #[test] - fn fires_once_for_two() { - harness(vec![14, 15], 10, 5, Some(10)); - } - - #[test] - fn finds_highest() { - harness(vec![15, 25], 10, 5, Some(20)); - } - - #[test] - fn doesnt_fire_before_history() { - harness(vec![10, 11], 10, 5, None); - } -} diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs deleted file mode 100644 index ec3a8f4aba..0000000000 --- a/ethcore/src/spec/spec.rs +++ /dev/null @@ -1,1086 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Parameters for a block chain. - -use std::collections::BTreeMap; -use std::io::Read; -use std::path::Path; -use std::sync::Arc; - -use bytes::Bytes; -use ethereum_types::{H256, Bloom, U256, Address}; -use ethjson; -use hash::{KECCAK_NULL_RLP, keccak}; -use parking_lot::RwLock; -use rlp::{Rlp, RlpStream}; -use rustc_hex::{FromHex, ToHex}; -use types::BlockNumber; -use types::encoded; -use types::header::Header; -use vm::{EnvInfo, CallType, ActionValue, ActionParams, ParamsType}; - -use builtin::Builtin; -use engines::{ - EthEngine, NullEngine, InstantSeal, InstantSealParams, BasicAuthority, Clique, - AuthorityRound, DEFAULT_BLOCKHASH_CONTRACT -}; -use error::Error; -use executive::Executive; -use factory::Factories; -use machine::EthereumMachine; -use pod_state::PodState; -use spec::Genesis; -use spec::seal::Generic as GenericSeal; -use state::backend::Basic as BasicBackend; -use state::{Backend, State, Substate}; -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) -} - -/// Parameters common to ethereum-like blockchains. -/// NOTE: when adding bugfix hard-fork parameters, -/// add to `nonzero_bugfix_hard_fork` -/// -/// we define a "bugfix" hard fork as any hard fork which -/// you would put on-by-default in a new chain. -#[derive(Debug, PartialEq, Default)] -#[cfg_attr(any(test, feature = "test-helpers"), derive(Clone))] -pub struct CommonParams { - /// Account start nonce. - pub account_start_nonce: U256, - /// Maximum size of extra data. - pub maximum_extra_data_size: usize, - /// Network id. - pub network_id: u64, - /// Chain id. - pub chain_id: u64, - /// Main subprotocol name. - pub subprotocol_name: String, - /// Minimum gas limit. - 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: BlockNumber, - /// Number of first block where EIP-161.abc begin. - pub eip161abc_transition: BlockNumber, - /// Number of first block where EIP-161.d begins. - pub eip161d_transition: BlockNumber, - /// Number of first block where EIP-98 rules begin. - pub eip98_transition: BlockNumber, - /// Number of first block where EIP-658 rules begin. - pub eip658_transition: BlockNumber, - /// Number of first block where EIP-155 rules begin. - pub eip155_transition: BlockNumber, - /// Validate block receipts root. - pub validate_receipts_transition: BlockNumber, - /// Validate transaction chain id. - pub validate_chain_id_transition: BlockNumber, - /// Number of first block where EIP-140 rules begin. - pub eip140_transition: BlockNumber, - /// Number of first block where EIP-210 rules begin. - pub eip210_transition: BlockNumber, - /// EIP-210 Blockhash contract address. - pub eip210_contract_address: Address, - /// EIP-210 Blockhash contract code. - pub eip210_contract_code: Bytes, - /// Gas allocated for EIP-210 blockhash update. - pub eip210_contract_gas: U256, - /// Number of first block where EIP-211 rules begin. - 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 EIP-1052 rules begin. - pub eip1052_transition: BlockNumber, - /// Number of first block where EIP-1283 rules begin. - pub eip1283_transition: BlockNumber, - /// Number of first block where EIP-1283 rules end. - pub eip1283_disable_transition: BlockNumber, - /// Number of first block where EIP-1283 rules re-enabled. - pub eip1283_reenable_transition: BlockNumber, - /// Number of first block where EIP-1014 rules begin. - pub eip1014_transition: BlockNumber, - /// Number of first block where EIP-1706 rules begin. - pub eip1706_transition: BlockNumber, - /// Number of first block where EIP-1344 rules begin: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1344.md - pub eip1344_transition: BlockNumber, - /// Number of first block where EIP-1884 rules begin:https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1884.md - pub eip1884_transition: BlockNumber, - /// Number of first block where EIP-2028 rules begin. - pub eip2028_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. - pub nonce_cap_increment: u64, - /// Enable dust cleanup for contracts. - pub remove_dust_contracts: bool, - /// Wasm activation blocknumber, if any disabled initially. - pub wasm_activation_transition: BlockNumber, - /// Number of first block where KIP-4 rules begin. Only has effect if Wasm is activated. - pub kip4_transition: BlockNumber, - /// Number of first block where KIP-6 rules begin. Only has effect if Wasm is activated. - pub kip6_transition: BlockNumber, - /// Gas limit bound divisor (how much gas limit can change per block) - pub gas_limit_bound_divisor: U256, - /// Registrar contract address. - pub registrar: Address, - /// Node permission managing contract address. - pub node_permission_contract: Option
, - /// Maximum contract code size that can be deployed. - pub max_code_size: u64, - /// Number of first block where max code size limit is active. - pub max_code_size_transition: BlockNumber, - /// Transaction permission managing contract address. - pub transaction_permission_contract: Option
, - /// Block at which the transaction permission contract should start being used. - pub transaction_permission_contract_transition: BlockNumber, - /// 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 { - 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. - pub fn max_code_size(&self, block_number: u64) -> u64 { - if block_number >= self.max_code_size_transition { - self.max_code_size - } else { - u64::max_value() - } - } - - /// Apply common spec config parameters to the schedule. - pub fn update_schedule(&self, block_number: u64, schedule: &mut ::vm::Schedule) { - schedule.have_create2 = block_number >= self.eip1014_transition; - 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; - schedule.have_extcodehash = block_number >= self.eip1052_transition; - schedule.have_chain_id = block_number >= self.eip1344_transition; - schedule.eip1283 = - (block_number >= self.eip1283_transition && - !(block_number >= self.eip1283_disable_transition)) || - block_number >= self.eip1283_reenable_transition; - schedule.eip1706 = block_number >= self.eip1706_transition; - - if block_number >= self.eip1884_transition { - schedule.have_selfbalance = true; - schedule.sload_gas = 800; - schedule.balance_gas = 700; - schedule.extcodehash_gas = 700; - } - if block_number >= self.eip2028_transition { - schedule.tx_data_non_zero_gas = 16; - } - if block_number >= self.eip210_transition { - schedule.blockhash_gas = 800; - } - if block_number >= self.dust_protection_transition { - schedule.kill_dust = match self.remove_dust_contracts { - true => ::vm::CleanDustMode::WithCodeAndStorage, - false => ::vm::CleanDustMode::BasicOnly, - }; - } - if block_number >= self.wasm_activation_transition { - let mut wasm = ::vm::WasmCosts::default(); - if block_number >= self.kip4_transition { - wasm.have_create2 = true; - } - if block_number >= self.kip6_transition { - wasm.have_gasleft = true; - } - schedule.wasm = Some(wasm); - } - } - - /// Return Some if the current parameters contain a bugfix hard fork not on block 0. - pub fn nonzero_bugfix_hard_fork(&self) -> Option<&str> { - if self.eip155_transition != 0 { - return Some("eip155Transition"); - } - - if self.validate_receipts_transition != 0 { - return Some("validateReceiptsTransition"); - } - - if self.validate_chain_id_transition != 0 { - return Some("validateChainIdTransition"); - } - - None - } -} - -impl From for CommonParams { - fn from(p: ethjson::spec::Params) -> Self { - CommonParams { - account_start_nonce: p.account_start_nonce.map_or_else(U256::zero, Into::into), - maximum_extra_data_size: p.maximum_extra_data_size.into(), - network_id: p.network_id.into(), - chain_id: if let Some(n) = p.chain_id { - n.into() - } else { - p.network_id.into() - }, - subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()), - min_gas_limit: p.min_gas_limit.into(), - fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { - Some((n.into(), h.into())) - } 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_else( - BlockNumber::max_value, - 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), - eip140_transition: p.eip140_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - 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), - eip210_contract_code: p.eip210_contract_code.map_or_else( - || { - DEFAULT_BLOCKHASH_CONTRACT.from_hex().expect( - "Default BLOCKHASH contract is valid", - ) - }, - Into::into, - ), - eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into), - 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_else( - BlockNumber::max_value, - Into::into, - ), - eip658_transition: p.eip658_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1052_transition: p.eip1052_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1283_transition: p.eip1283_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1283_disable_transition: p.eip1283_disable_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1283_reenable_transition: p.eip1283_reenable_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1706_transition: p.eip1706_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1014_transition: p.eip1014_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1344_transition: p.eip1344_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip1884_transition: p.eip1884_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - eip2028_transition: p.eip2028_transition.map_or_else( - BlockNumber::max_value, - Into::into, - ), - 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), - remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false), - gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), - 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), - transaction_permission_contract_transition: - p.transaction_permission_contract_transition.map_or(0, Into::into), - wasm_activation_transition: p.wasm_activation_transition.map_or_else( - BlockNumber::max_value, - Into::into - ), - kip4_transition: p.kip4_transition.map_or_else( - BlockNumber::max_value, - Into::into - ), - kip6_transition: p.kip6_transition.map_or_else( - BlockNumber::max_value, - Into::into - ), - } - } -} - -/// Runtime parameters for the spec that are related to how the software should run the chain, -/// rather than integral properties of the chain itself. -#[derive(Debug, Clone, Copy)] -pub struct SpecParams<'a> { - /// The path to the folder used to cache nodes. This is typically /tmp/ on Unix-like systems - pub cache_dir: &'a Path, - /// Whether to run slower at the expense of better memory usage, or run faster while using - /// more - /// memory. This may get more fine-grained in the future but for now is simply a binary - /// option. - pub optimization_setting: Option, -} - -impl<'a> SpecParams<'a> { - /// Create from a cache path, with null values for the other fields - pub fn from_path(path: &'a Path) -> Self { - SpecParams { - cache_dir: path, - optimization_setting: None, - } - } - - /// Create from a cache path and an optimization setting - pub fn new(path: &'a Path, optimization: OptimizeFor) -> Self { - SpecParams { - cache_dir: path, - optimization_setting: Some(optimization), - } - } -} - -impl<'a, T: AsRef> From<&'a T> for SpecParams<'a> { - fn from(path: &'a T) -> Self { - Self::from_path(path.as_ref()) - } -} - -/// 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 { - /// User friendly spec name - pub name: String, - /// What engine are we using for this? - pub engine: Arc, - /// Name of the subdir inside the main data dir to use for chain data and settings. - pub data_dir: String, - - /// Known nodes on the network in enode format. - pub nodes: Vec, - - /// The genesis block's parent hash field. - pub parent_hash: H256, - /// The genesis block's author field. - pub author: Address, - /// The genesis block's difficulty field. - pub difficulty: U256, - /// The genesis block's gas limit field. - pub gas_limit: U256, - /// The genesis block's gas used field. - pub gas_used: U256, - /// The genesis block's timestamp field. - pub timestamp: u64, - /// Transactions root of the genesis block. Should be KECCAK_NULL_RLP. - pub transactions_root: H256, - /// Receipts root of the genesis block. Should be KECCAK_NULL_RLP. - pub receipts_root: H256, - /// The genesis block's extra data field. - pub extra_data: Bytes, - /// Each seal field, expressed as RLP, concatenated. - pub seal_rlp: Bytes, - - /// Hardcoded synchronization. Allows the light client to immediately jump to a specific block. - pub hardcoded_sync: Option, - - /// Contract constructors to be executed on genesis. - constructors: Vec<(Address, Bytes)>, - - /// May be prepopulated if we know this in advance. - state_root_memo: RwLock, - - /// Genesis state as plain old data. - genesis_state: PodState, -} - -#[cfg(test)] -impl Clone for Spec { - fn clone(&self) -> Spec { - Spec { - name: self.name.clone(), - engine: self.engine.clone(), - data_dir: self.data_dir.clone(), - nodes: self.nodes.clone(), - parent_hash: self.parent_hash.clone(), - transactions_root: self.transactions_root.clone(), - receipts_root: self.receipts_root.clone(), - author: self.author.clone(), - difficulty: self.difficulty.clone(), - gas_limit: self.gas_limit.clone(), - gas_used: self.gas_used.clone(), - timestamp: self.timestamp.clone(), - extra_data: self.extra_data.clone(), - seal_rlp: self.seal_rlp.clone(), - hardcoded_sync: self.hardcoded_sync.clone(), - constructors: self.constructors.clone(), - state_root_memo: RwLock::new(*self.state_root_memo.read()), - genesis_state: self.genesis_state.clone(), - } - } -} - -/// Part of `Spec`. Describes the hardcoded synchronization parameters. -pub struct SpecHardcodedSync { - /// Header of the block to jump to for hardcoded sync, and total difficulty. - pub header: encoded::Header, - /// Total difficulty of the block to jump to. - pub total_difficulty: U256, - /// List of hardcoded CHTs, in order. If `hardcoded_sync` is set, the CHTs should include the - /// header of `hardcoded_sync`. - pub chts: Vec, -} - -impl SpecHardcodedSync { - /// Turns this specifications back into JSON. Useful for pretty printing. - pub fn to_json(self) -> ethjson::spec::HardcodedSync { - self.into() - } -} - -#[cfg(test)] -impl Clone for SpecHardcodedSync { - fn clone(&self) -> SpecHardcodedSync { - SpecHardcodedSync { - header: self.header.clone(), - total_difficulty: self.total_difficulty.clone(), - chts: self.chts.clone(), - } - } -} - -impl From for ethjson::spec::HardcodedSync { - fn from(sync: SpecHardcodedSync) -> ethjson::spec::HardcodedSync { - ethjson::spec::HardcodedSync { - header: sync.header.into_inner().to_hex(), - total_difficulty: ethjson::uint::Uint(sync.total_difficulty), - chts: sync.chts.into_iter().map(Into::into).collect(), - } - } -} - -fn load_machine_from(s: ethjson::spec::Spec) -> EthereumMachine { - let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect(); - let params = CommonParams::from(s.params); - - Spec::machine(&s.engine, params, builtins) -} - -/// Load from JSON object. -fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result { - let builtins = s.accounts - .builtins() - .into_iter() - .map(|p| (p.0.into(), From::from(p.1))) - .collect(); - let g = Genesis::from(s.genesis); - let GenericSeal(seal_rlp) = g.seal.into(); - let params = CommonParams::from(s.params); - - let hardcoded_sync = if let Some(ref hs) = s.hardcoded_sync { - if let Ok(header) = hs.header.from_hex() { - Some(SpecHardcodedSync { - header: encoded::Header::new(header), - total_difficulty: hs.total_difficulty.into(), - chts: s.hardcoded_sync - .as_ref() - .map(|s| s.chts.iter().map(|c| c.clone().into()).collect()) - .unwrap_or_default() - }) - } else { - None - } - } else { - None - }; - - let mut s = Spec { - name: s.name.clone().into(), - engine: Spec::engine(spec_params, s.engine, params, builtins), - data_dir: s.data_dir.unwrap_or(s.name).into(), - nodes: s.nodes.unwrap_or_else(Vec::new), - parent_hash: g.parent_hash, - transactions_root: g.transactions_root, - receipts_root: g.receipts_root, - author: g.author, - difficulty: g.difficulty, - gas_limit: g.gas_limit, - gas_used: g.gas_used, - timestamp: g.timestamp, - extra_data: g.extra_data, - seal_rlp: seal_rlp, - hardcoded_sync: hardcoded_sync, - constructors: s.accounts - .constructors() - .into_iter() - .map(|(a, c)| (a.into(), c.into())) - .collect(), - state_root_memo: RwLock::new(Default::default()), // will be overwritten right after. - genesis_state: s.accounts.into(), - }; - - // use memoized state root if provided. - match g.state_root { - Some(root) => *s.state_root_memo.get_mut() = root, - None => { - let _ = s.run_constructors( - &Default::default(), - BasicBackend(journaldb::new_memory_db()), - )?; - } - } - - Ok(s) -} - -macro_rules! load_bundled { - ($e:expr) => { - Spec::load( - &::std::env::temp_dir(), - include_bytes!(concat!("../../res/", $e, ".json")) as &[u8] - ).expect(concat!("Chain spec ", $e, " is invalid.")) - }; -} - -#[cfg(any(test, feature = "test-helpers"))] -macro_rules! load_machine_bundled { - ($e:expr) => { - Spec::load_machine( - include_bytes!(concat!("../../res/", $e, ".json")) as &[u8] - ).expect(concat!("Chain spec ", $e, " is invalid.")) - }; -} - -impl Spec { - // create an instance of an Ethereum state machine, minus consensus logic. - fn machine( - engine_spec: ðjson::spec::Engine, - params: CommonParams, - builtins: BTreeMap, - ) -> EthereumMachine { - if let ethjson::spec::Engine::Ethash(ref ethash) = *engine_spec { - EthereumMachine::with_ethash_extensions(params, builtins, ethash.params.clone().into()) - } else { - EthereumMachine::regular(params, builtins) - } - } - - /// Convert engine spec into a arc'd Engine of the right underlying type. - /// TODO avoid this hard-coded nastiness - use dynamic-linked plugin framework instead. - fn engine( - spec_params: SpecParams, - engine_spec: ethjson::spec::Engine, - params: CommonParams, - builtins: BTreeMap, - ) -> Arc { - let machine = Self::machine(&engine_spec, params, builtins); - - match engine_spec { - ethjson::spec::Engine::Null(null) => Arc::new(NullEngine::new(null.params.into(), machine)), - ethjson::spec::Engine::Ethash(ethash) => Arc::new(::ethereum::Ethash::new(spec_params.cache_dir, ethash.params.into(), machine, spec_params.optimization_setting)), - ethjson::spec::Engine::InstantSeal(Some(instant_seal)) => Arc::new(InstantSeal::new(instant_seal.params.into(), machine)), - ethjson::spec::Engine::InstantSeal(None) => Arc::new(InstantSeal::new(InstantSealParams::default(), machine)), - ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)), - ethjson::spec::Engine::Clique(clique) => Clique::new(clique.params.into(), machine) - .expect("Failed to start Clique consensus engine."), - ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(authority_round.params.into(), machine) - .expect("Failed to start AuthorityRound consensus engine."), - } - } - - // given a pre-constructor state, run all the given constructors and produce a new state and - // state root. - fn run_constructors(&self, factories: &Factories, mut db: T) -> Result { - let mut root = KECCAK_NULL_RLP; - - // basic accounts in spec. - { - let mut t = factories.trie.create(db.as_hash_db_mut(), &mut root); - - for (address, account) in self.genesis_state.get().iter() { - t.insert(&**address, &account.rlp())?; - } - } - - for (address, account) in self.genesis_state.get().iter() { - db.note_non_null_account(address); - account.insert_additional( - &mut *factories.accountdb.create( - db.as_hash_db_mut(), - keccak(address), - ), - &factories.trie, - ); - } - - let start_nonce = self.engine.account_start_nonce(0); - - let (root, db) = { - let mut state = State::from_existing(db, root, start_nonce, factories.clone())?; - - // Execute contract constructors. - let env_info = EnvInfo { - number: 0, - author: self.author, - timestamp: self.timestamp, - difficulty: self.difficulty, - last_hashes: Default::default(), - gas_used: U256::zero(), - gas_limit: U256::max_value(), - }; - - let from = Address::default(); - for &(ref address, ref constructor) in self.constructors.iter() { - trace!(target: "spec", "run_constructors: Creating a contract at {}.", address); - trace!(target: "spec", " .. root before = {}", state.root()); - let params = ActionParams { - code_address: address.clone(), - code_hash: Some(keccak(constructor)), - address: address.clone(), - sender: from.clone(), - origin: from.clone(), - gas: U256::max_value(), - gas_price: Default::default(), - value: ActionValue::Transfer(Default::default()), - code: Some(Arc::new(constructor.clone())), - data: None, - call_type: CallType::None, - params_type: ParamsType::Embedded, - }; - - let mut substate = Substate::new(); - - { - let machine = self.engine.machine(); - let schedule = machine.schedule(env_info.number); - let mut exec = Executive::new(&mut state, &env_info, &machine, &schedule); - if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { - warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e); - } - } - - if let Err(e) = state.commit() { - warn!(target: "spec", "Genesis constructor trie commit at {} failed: {}.", address, e); - } - - trace!(target: "spec", " .. root after = {}", state.root()); - } - - state.drop() - }; - - *self.state_root_memo.write() = root; - Ok(db) - } - - /// Return the state root for the genesis state, memoising accordingly. - pub fn state_root(&self) -> H256 { - self.state_root_memo.read().clone() - } - - /// Get common blockchain parameters. - pub fn params(&self) -> &CommonParams { - &self.engine.params() - } - - /// Get the known knodes of the network in enode format. - pub fn nodes(&self) -> &[String] { - &self.nodes - } - - /// Get the configured Network ID. - pub fn network_id(&self) -> u64 { - self.params().network_id - } - - /// Get the chain ID used for signing. - pub fn chain_id(&self) -> u64 { - self.params().chain_id - } - - /// Get the configured subprotocol name. - pub fn subprotocol_name(&self) -> String { - self.params().subprotocol_name.clone() - } - - /// Get the configured network fork block. - pub fn fork_block(&self) -> Option<(BlockNumber, H256)> { - self.params().fork_block - } - - /// Get the header of the genesis block. - pub fn genesis_header(&self) -> Header { - let mut header: Header = Default::default(); - header.set_parent_hash(self.parent_hash.clone()); - header.set_timestamp(self.timestamp); - header.set_number(0); - header.set_author(self.author.clone()); - header.set_transactions_root(self.transactions_root.clone()); - header.set_uncles_hash(keccak(RlpStream::new_list(0).out())); - header.set_extra_data(self.extra_data.clone()); - header.set_state_root(self.state_root()); - header.set_receipts_root(self.receipts_root.clone()); - header.set_log_bloom(Bloom::default()); - header.set_gas_used(self.gas_used.clone()); - header.set_gas_limit(self.gas_limit.clone()); - header.set_difficulty(self.difficulty.clone()); - header.set_seal({ - let r = Rlp::new(&self.seal_rlp); - r.iter().map(|f| f.as_raw().to_vec()).collect() - }); - trace!(target: "spec", "Header hash is {}", header.hash()); - header - } - - /// Compose the genesis block for this chain. - pub fn genesis_block(&self) -> Bytes { - let empty_list = RlpStream::new_list(0).out(); - let header = self.genesis_header(); - let mut ret = RlpStream::new_list(3); - ret.append(&header); - ret.append_raw(&empty_list, 1); - ret.append_raw(&empty_list, 1); - ret.out() - } - - /// Overwrite the genesis components. - pub fn overwrite_genesis_params(&mut self, g: Genesis) { - let GenericSeal(seal_rlp) = g.seal.into(); - self.parent_hash = g.parent_hash; - self.transactions_root = g.transactions_root; - self.receipts_root = g.receipts_root; - self.author = g.author; - self.difficulty = g.difficulty; - self.gas_limit = g.gas_limit; - self.gas_used = g.gas_used; - self.timestamp = g.timestamp; - self.extra_data = g.extra_data; - self.seal_rlp = seal_rlp; - } - - /// Alter the value of the genesis state. - pub fn set_genesis_state(&mut self, s: PodState) -> Result<(), Error> { - self.genesis_state = s; - let _ = self.run_constructors( - &Default::default(), - BasicBackend(journaldb::new_memory_db()), - )?; - - Ok(()) - } - - /// Return genesis state as Plain old data. - pub fn genesis_state(&self) -> &PodState { - &self.genesis_state - } - - /// Returns `false` if the memoized state root is invalid. `true` otherwise. - pub fn is_state_root_valid(&self) -> bool { - // TODO: get rid of this function and ensure state root always is valid. - // we're mostly there, but `self.genesis_state.root()` doesn't encompass - // post-constructor state. - *self.state_root_memo.read() == self.genesis_state.root() - } - - /// Ensure that the given state DB has the trie nodes in for the genesis state. - pub fn ensure_db_good(&self, db: T, factories: &Factories) -> Result { - if db.as_hash_db().contains(&self.state_root()) { - return Ok(db); - } - - // TODO: could optimize so we don't re-run, but `ensure_db_good` is barely ever - // called anyway. - let db = self.run_constructors(factories, db)?; - Ok(db) - } - - /// Loads just the state machine from a json file. - pub fn load_machine(reader: R) -> Result { - ethjson::spec::Spec::load(reader) - .map_err(fmt_err) - .map(load_machine_from) - } - - /// Loads spec from json file. Provide factories for executing contracts and ensuring - /// storage goes to the right place. - pub fn load<'a, T: Into>, R>(params: T, reader: R) -> Result - where - R: Read, - { - ethjson::spec::Spec::load(reader).map_err(fmt_err).and_then( - |x| { - load_from(params.into(), x).map_err(fmt_err) - }, - ) - } - - /// initialize genesis epoch data, using in-memory database for - /// constructor. - pub fn genesis_epoch_data(&self) -> Result, String> { - use types::transaction::{Action, Transaction}; - use journaldb; - use kvdb_memorydb; - - let genesis = self.genesis_header(); - - let factories = Default::default(); - let mut db = journaldb::new( - Arc::new(kvdb_memorydb::create(0)), - journaldb::Algorithm::Archive, - None, - ); - - self.ensure_db_good(BasicBackend(db.as_hash_db_mut()), &factories) - .map_err(|e| format!("Unable to initialize genesis state: {}", e))?; - - let call = |a, d| { - let mut db = db.boxed_clone(); - let env_info = ::evm::EnvInfo { - number: 0, - author: *genesis.author(), - timestamp: genesis.timestamp(), - difficulty: *genesis.difficulty(), - gas_limit: U256::max_value(), - last_hashes: Arc::new(Vec::new()), - gas_used: 0.into(), - }; - - let from = Address::default(); - let tx = Transaction { - nonce: self.engine.account_start_nonce(0), - action: Action::Call(a), - gas: U256::max_value(), - gas_price: U256::default(), - value: U256::default(), - data: d, - }.fake_sign(from); - - let res = ::state::prove_transaction_virtual( - db.as_hash_db_mut(), - *genesis.state_root(), - &tx, - self.engine.machine(), - &env_info, - factories.clone(), - ); - - res.map(|(out, proof)| { - (out, proof.into_iter().map(|x| x.into_vec()).collect()) - }).ok_or_else(|| "Failed to prove call: insufficient state".into()) - }; - - 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 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") - } - - /// 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") - } - - /// TestList.sol used in both specs: https://github.com/paritytech/contracts/pull/30/files - /// Accounts with secrets keccak("0") and keccak("1") are initially the validators. - /// Create a new Spec with BasicAuthority which uses a contract at address 5 to determine - /// the current validators using `getValidators`. - /// Second validator can be removed with - /// "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") - } - - /// 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") - } - - /// Create a new Spec with BasicAuthority which uses multiple validator sets changing with - /// 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") - } -} - -#[cfg(test)] -mod tests { - use super::*; - use state::State; - use test_helpers::get_temp_state_db; - use tempdir::TempDir; - use types::view; - use types::views::BlockView; - - #[test] - fn test_load_empty() { - let tempdir = TempDir::new("").unwrap(); - assert!(Spec::load(&tempdir.path(), &[] as &[u8]).is_err()); - } - - #[test] - fn test_chain() { - let test_spec = Spec::new_test(); - - assert_eq!( - test_spec.state_root(), - "f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9".into() - ); - let genesis = test_spec.genesis_block(); - assert_eq!( - view!(BlockView, &genesis).header_view().hash(), - "0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303".into() - ); - } - - #[test] - fn genesis_constructor() { - let _ = ::env_logger::try_init(); - let spec = Spec::new_test_constructor(); - let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()) - .unwrap(); - let state = State::from_existing( - db.boxed_clone(), - spec.state_root(), - spec.engine.account_start_nonce(0), - Default::default(), - ).unwrap(); - let expected = "0000000000000000000000000000000000000000000000000000000000000001".into(); - let address = "0000000000000000000000000000000000001337".into(); - - assert_eq!(state.storage_at(&address, &H256::zero()).unwrap(), expected); - assert_eq!(state.balance(&address).unwrap(), 1.into()); - } -} diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs deleted file mode 100644 index ddad10c40d..0000000000 --- a/ethcore/src/state/mod.rs +++ /dev/null @@ -1,2767 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! A mutable state representation suitable to execute transactions. -//! Generic over a `Backend`. Deals with `Account`s. -//! Unconfirmed sub-states are managed with `checkpoint`s which may be canonicalized -//! or rolled back. - -use std::cell::{RefCell, RefMut}; -use std::collections::hash_map::Entry; -use std::collections::{HashMap, BTreeMap, BTreeSet, HashSet}; -use std::fmt; -use std::sync::Arc; -use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY}; - -use types::receipt::{Receipt, TransactionOutcome}; -use machine::EthereumMachine as Machine; -use vm::EnvInfo; -use error::Error; -use executive::{Executive, TransactOptions}; -use factory::Factories; -use trace::{self, FlatTrace, VMTrace}; -use pod_account::*; -use pod_state::{self, PodState}; -use types::basic_account::BasicAccount; -use executed::{Executed, ExecutionError}; -use types::state_diff::StateDiff; -use types::transaction::SignedTransaction; -use state_db::StateDB; -use factory::VmFactory; - -use ethereum_types::{H256, U256, Address}; -use hash_db::{HashDB, AsHashDB}; -use keccak_hasher::KeccakHasher; -use kvdb::DBValue; -use bytes::Bytes; - -use trie::{Trie, TrieError, Recorder}; -use ethtrie::{TrieDB, Result as TrieResult}; - -mod account; -mod substate; - -pub mod backend; - -pub use self::account::Account; -pub use self::backend::Backend; -pub use self::substate::Substate; - -/// Used to return information about an `State::apply` operation. -pub struct ApplyOutcome { - /// The receipt for the applied transaction. - pub receipt: Receipt, - /// The output of the applied transaction. - pub output: Bytes, - /// The trace for the applied transaction, empty if tracing was not produced. - pub trace: Vec, - /// The VM trace for the applied transaction, None if tracing was not produced. - pub vm_trace: Option -} - -/// Result type for the execution ("application") of a transaction. -pub type ApplyResult = Result, Error>; - -/// Return type of proof validity check. -#[derive(Debug, Clone)] -pub enum ProvedExecution { - /// Proof wasn't enough to complete execution. - BadProof, - /// The transaction failed, but not due to a bad proof. - Failed(ExecutionError), - /// The transaction successfully completed with the given proof. - Complete(Box), -} - -#[derive(Eq, PartialEq, Clone, Copy, Debug)] -/// Account modification state. Used to check if the account was -/// Modified in between commits and overall. -enum AccountState { - /// Account was loaded from disk and never modified in this state object. - CleanFresh, - /// Account was loaded from the global cache and never modified. - CleanCached, - /// Account has been modified and is not committed to the trie yet. - /// This is set if any of the account data is changed, including - /// storage and code. - Dirty, - /// Account was modified and committed to the trie. - Committed, -} - -#[derive(Debug)] -/// In-memory copy of the account data. Holds the optional account -/// and the modification status. -/// Account entry can contain existing (`Some`) or non-existing -/// account (`None`) -struct AccountEntry { - /// Account entry. `None` if account known to be non-existant. - account: Option, - /// Unmodified account balance. - old_balance: Option, - /// Entry state. - state: AccountState, -} - -// Account cache item. Contains account data and -// modification state -impl AccountEntry { - fn is_dirty(&self) -> bool { - self.state == AccountState::Dirty - } - - fn exists_and_is_null(&self) -> bool { - self.account.as_ref().map_or(false, |a| a.is_null()) - } - - /// Clone dirty data into new `AccountEntry`. This includes - /// basic account data and modified storage keys. - /// Returns None if clean. - fn clone_if_dirty(&self) -> Option { - match self.is_dirty() { - true => Some(self.clone_dirty()), - false => None, - } - } - - /// Clone dirty data into new `AccountEntry`. This includes - /// basic account data and modified storage keys. - fn clone_dirty(&self) -> AccountEntry { - AccountEntry { - old_balance: self.old_balance, - account: self.account.as_ref().map(Account::clone_dirty), - state: self.state, - } - } - - // Create a new account entry and mark it as dirty. - fn new_dirty(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::Dirty, - } - } - - // Create a new account entry and mark it as clean. - fn new_clean(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::CleanFresh, - } - } - - // Create a new account entry and mark it as clean and cached. - fn new_clean_cached(account: Option) -> AccountEntry { - AccountEntry { - old_balance: account.as_ref().map(|a| a.balance().clone()), - account: account, - state: AccountState::CleanCached, - } - } - - // Replace data with another entry but preserve storage cache. - fn overwrite_with(&mut self, other: AccountEntry) { - self.state = other.state; - match other.account { - Some(acc) => { - if let Some(ref mut ours) = self.account { - ours.overwrite_with(acc); - } else { - self.account = Some(acc); - } - }, - None => self.account = None, - } - } -} - -/// Check the given proof of execution. -/// `Err(ExecutionError::Internal)` indicates failure, everything else indicates -/// a successful proof (as the transaction itself may be poorly chosen). -pub fn check_proof( - proof: &[DBValue], - root: H256, - transaction: &SignedTransaction, - machine: &Machine, - env_info: &EnvInfo, -) -> ProvedExecution { - let backend = self::backend::ProofCheck::new(proof); - let mut factories = Factories::default(); - factories.accountdb = ::account_db::Factory::Plain; - - let res = State::from_existing( - backend, - root, - machine.account_start_nonce(env_info.number), - factories - ); - - let mut state = match res { - Ok(state) => state, - Err(_) => return ProvedExecution::BadProof, - }; - - let options = TransactOptions::with_no_tracing().save_output_from_contract(); - match state.execute(env_info, machine, transaction, options, true) { - Ok(executed) => ProvedExecution::Complete(Box::new(executed)), - Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, - Err(e) => ProvedExecution::Failed(e), - } -} - -/// Prove a `virtual` transaction on the given state. -/// Returns `None` when the transacion could not be proved, -/// and a proof otherwise. -pub fn prove_transaction_virtual + Send + Sync>( - db: H, - root: H256, - transaction: &SignedTransaction, - machine: &Machine, - env_info: &EnvInfo, - factories: Factories, -) -> Option<(Bytes, Vec)> { - use self::backend::Proving; - - let backend = Proving::new(db); - let res = State::from_existing( - backend, - root, - machine.account_start_nonce(env_info.number), - factories, - ); - - let mut state = match res { - Ok(state) => state, - Err(_) => return None, - }; - - let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); - match state.execute(env_info, machine, transaction, options, true) { - Err(ExecutionError::Internal(_)) => None, - Err(e) => { - trace!(target: "state", "Proved call failed: {}", e); - Some((Vec::new(), state.drop().1.extract_proof())) - } - Ok(res) => Some((res.output, state.drop().1.extract_proof())), - } -} - -/// Representation of the entire state of all accounts in the system. -/// -/// `State` can work together with `StateDB` to share account cache. -/// -/// Local cache contains changes made locally and changes accumulated -/// locally from previous commits. Global cache reflects the database -/// state and never contains any changes. -/// -/// Cache items contains account data, or the flag that account does not exist -/// and modification state (see `AccountState`) -/// -/// Account data can be in the following cache states: -/// * In global but not local - something that was queried from the database, -/// but never modified -/// * In local but not global - something that was just added (e.g. new account) -/// * In both with the same value - something that was changed to a new value, -/// but changed back to a previous block in the same block (same State instance) -/// * In both with different values - something that was overwritten with a -/// new value. -/// -/// All read-only state queries check local cache/modifications first, -/// then global state cache. If data is not found in any of the caches -/// it is loaded from the DB to the local cache. -/// -/// **** IMPORTANT ************************************************************* -/// All the modifications to the account data must set the `Dirty` state in the -/// `AccountEntry`. This is done in `require` and `require_or_from`. So just -/// use that. -/// **************************************************************************** -/// -/// Upon destruction all the local cache data propagated into the global cache. -/// Propagated items might be rejected if current state is non-canonical. -/// -/// State checkpointing. -/// -/// A new checkpoint can be created with `checkpoint()`. checkpoints can be -/// created in a hierarchy. -/// When a checkpoint is active all changes are applied directly into -/// `cache` and the original value is copied into an active checkpoint. -/// Reverting a checkpoint with `revert_to_checkpoint` involves copying -/// original values from the latest checkpoint back into `cache`. The code -/// takes care not to overwrite cached storage while doing that. -/// checkpoint can be discarded with `discard_checkpoint`. All of the orignal -/// backed-up values are moved into a parent checkpoint (if any). -/// -pub struct State { - db: B, - root: H256, - cache: RefCell>, - // The original account is preserved in - checkpoints: RefCell>>>, - account_start_nonce: U256, - factories: Factories, -} - -#[derive(Copy, Clone)] -enum RequireCache { - None, - CodeSize, - Code, -} - -/// Mode of dealing with null accounts. -#[derive(PartialEq)] -pub enum CleanupMode<'a> { - /// Create accounts which would be null. - ForceCreate, - /// Don't delete null accounts upon touching, but also don't create them. - NoEmpty, - /// Mark all touched accounts. - TrackTouched(&'a mut HashSet
), -} - -/// Provides subset of `State` methods to query state information -pub trait StateInfo { - /// Get the nonce of account `a`. - fn nonce(&self, a: &Address) -> TrieResult; - - /// Get the balance of account `a`. - fn balance(&self, a: &Address) -> TrieResult; - - /// Mutate storage of account `address` so that it is `value` for `key`. - fn storage_at(&self, address: &Address, key: &H256) -> TrieResult; - - /// Get accounts' code. - fn code(&self, a: &Address) -> TrieResult>>; -} - -impl StateInfo for State { - fn nonce(&self, a: &Address) -> TrieResult { State::nonce(self, a) } - fn balance(&self, a: &Address) -> TrieResult { State::balance(self, a) } - fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { State::storage_at(self, address, key) } - fn code(&self, address: &Address) -> TrieResult>> { State::code(self, address) } -} - -const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ - Therefore creating a SecTrieDB with this state's root will not fail."; - -impl State { - /// Creates new state with empty state root - /// Used for tests. - pub fn new(mut db: B, account_start_nonce: U256, factories: Factories) -> State { - let mut root = H256::new(); - { - // init trie and reset root to null - let _ = factories.trie.create(db.as_hash_db_mut(), &mut root); - } - - State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - factories: factories, - } - } - - /// Creates new state with existing state root - pub fn from_existing(db: B, root: H256, account_start_nonce: U256, factories: Factories) -> TrieResult> { - if !db.as_hash_db().contains(&root) { - return Err(Box::new(TrieError::InvalidStateRoot(root))); - } - - let state = State { - db: db, - root: root, - cache: RefCell::new(HashMap::new()), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: account_start_nonce, - factories: factories - }; - - Ok(state) - } - - /// Get a VM factory that can execute on this state. - pub fn vm_factory(&self) -> VmFactory { - self.factories.vm.clone() - } - - /// Create a recoverable checkpoint of this state. Return the checkpoint index. - pub fn checkpoint(&mut self) -> usize { - let checkpoints = self.checkpoints.get_mut(); - let index = checkpoints.len(); - checkpoints.push(HashMap::new()); - index - } - - /// Merge last checkpoint with previous. - pub fn discard_checkpoint(&mut self) { - // merge with previous checkpoint - let last = self.checkpoints.get_mut().pop(); - if let Some(mut checkpoint) = last { - if let Some(ref mut prev) = self.checkpoints.get_mut().last_mut() { - if prev.is_empty() { - **prev = checkpoint; - } else { - for (k, v) in checkpoint.drain() { - prev.entry(k).or_insert(v); - } - } - } - } - } - - /// Revert to the last checkpoint and discard it. - pub fn revert_to_checkpoint(&mut self) { - if let Some(mut checkpoint) = self.checkpoints.get_mut().pop() { - for (k, v) in checkpoint.drain() { - match v { - Some(v) => { - match self.cache.get_mut().entry(k) { - Entry::Occupied(mut e) => { - // Merge checkpointed changes back into the main account - // storage preserving the cache. - e.get_mut().overwrite_with(v); - }, - Entry::Vacant(e) => { - e.insert(v); - } - } - }, - None => { - if let Entry::Occupied(e) = self.cache.get_mut().entry(k) { - if e.get().is_dirty() { - e.remove(); - } - } - } - } - } - } - } - - fn insert_cache(&self, address: &Address, account: AccountEntry) { - // Dirty account which is not in the cache means this is a new account. - // It goes directly into the checkpoint as there's nothing to rever to. - // - // In all other cases account is read as clean first, and after that made - // dirty in and added to the checkpoint with `note_cache`. - let is_dirty = account.is_dirty(); - let old_value = self.cache.borrow_mut().insert(*address, account); - if is_dirty { - if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { - checkpoint.entry(*address).or_insert(old_value); - } - } - } - - fn note_cache(&self, address: &Address) { - if let Some(ref mut checkpoint) = self.checkpoints.borrow_mut().last_mut() { - checkpoint.entry(*address) - .or_insert_with(|| self.cache.borrow().get(address).map(AccountEntry::clone_dirty)); - } - } - - /// Destroy the current object and return root and database. - pub fn drop(mut self) -> (H256, B) { - self.propagate_to_global_cache(); - (self.root, self.db) - } - - /// Destroy the current object and return single account data. - pub fn into_account(self, account: &Address) -> TrieResult<(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 - } - - /// Create a new contract at address `contract`. If there is already an account at the address - /// it will have its code reset, ready for `init_code()`. - pub fn new_contract(&mut self, contract: &Address, balance: U256, nonce_offset: U256) -> TrieResult<()> { - let original_storage_root = self.original_storage_root(contract)?; - let (nonce, overflow) = self.account_start_nonce.overflowing_add(nonce_offset); - if overflow { - return Err(Box::new(TrieError::DecoderError(H256::from(contract), - rlp::DecoderError::Custom("Nonce overflow".into())))); - } - self.insert_cache(contract, AccountEntry::new_dirty(Some(Account::new_contract(balance, nonce, original_storage_root)))); - Ok(()) - } - - /// Remove an existing account. - pub fn kill_account(&mut self, account: &Address) { - self.insert_cache(account, AccountEntry::new_dirty(None)); - } - - /// Determine whether an account exists. - pub fn exists(&self, a: &Address) -> TrieResult { - // Bloom filter does not contain empty accounts, so it is important here to - // check if account exists in the database directly before EIP-161 is in effect. - self.ensure_cached(a, RequireCache::None, false, |a| a.is_some()) - } - - /// Determine whether an account exists and if not empty. - pub fn exists_and_not_null(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, false, |a| a.map_or(false, |a| !a.is_null())) - } - - /// Determine whether an account exists and has code or non-zero nonce. - pub fn exists_and_has_code_or_nonce(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::CodeSize, false, - |a| a.map_or(false, |a| a.code_hash() != KECCAK_EMPTY || *a.nonce() != self.account_start_nonce)) - } - - /// Get the balance of account `a`. - pub fn balance(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(U256::zero(), |account| *account.balance())) - } - - /// Get the nonce of account `a`. - pub fn nonce(&self, a: &Address) -> TrieResult { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) - } - - /// Whether the base storage root of an account remains unchanged. - pub fn is_base_storage_root_unchanged(&self, a: &Address) -> TrieResult { - Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.is_base_storage_root_unchanged()))? - .unwrap_or(true)) - } - - /// Get the storage root of account `a`. - pub fn storage_root(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().and_then(|account| account.storage_root())) - } - - /// Get the original storage root since last commit of account `a`. - pub fn original_storage_root(&self, a: &Address) -> TrieResult { - Ok(self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|account| account.original_storage_root()))? - .unwrap_or(KECCAK_NULL_RLP)) - } - - /// Get the value of storage at a specific checkpoint. - pub fn checkpoint_storage_at(&self, start_checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult> { - #[must_use] - enum ReturnKind { - /// Use original storage at value at this address. - OriginalAt, - /// The checkpoint storage value is the same as the checkpoint storage value at the next checkpoint. - SameAsNext, - } - - let kind = { - let checkpoints = self.checkpoints.borrow(); - - if start_checkpoint_index >= checkpoints.len() { - // The checkpoint was not found. Return None. - return Ok(None); - } - - let mut kind = None; - - for checkpoint in checkpoints.iter().skip(start_checkpoint_index) { - match checkpoint.get(address) { - // The account exists at this checkpoint. - Some(Some(AccountEntry { account: Some(ref account), .. })) => { - if let Some(value) = account.cached_storage_at(key) { - return Ok(Some(value)); - } else { - // This account has checkpoint entry, but the key is not in the entry's cache. We can use - // original_storage_at if current account's original storage root is the same as checkpoint - // account's original storage root. Otherwise, the account must be a newly created contract. - if account.base_storage_root() == self.original_storage_root(address)? { - kind = Some(ReturnKind::OriginalAt); - break - } else { - // If account base storage root is different from the original storage root since last - // commit, then it can only be created from a new contract, where the base storage root - // would always be empty. Note that this branch is actually never called, because - // `cached_storage_at` handled this case. - warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty."); - return Ok(Some(H256::new())); - } - } - }, - // The account didn't exist at that point. Return empty value. - Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::new())), - // The value was not cached at that checkpoint, meaning it was not modified at all. - Some(None) => { - kind = Some(ReturnKind::OriginalAt); - break - }, - // This key does not have a checkpoint entry. - None => { - kind = Some(ReturnKind::SameAsNext); - }, - } - } - - kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed") - }; - - match kind { - ReturnKind::SameAsNext => { - // If we reached here, all previous SameAsNext failed to early return. It means that the value we want - // to fetch is the same as current. - Ok(Some(self.storage_at(address, key)?)) - }, - ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)), - } - } - - fn storage_at_inner( - &self, address: &Address, key: &H256, f_cached_at: FCachedStorageAt, f_at: FStorageAt, - ) -> TrieResult where - FCachedStorageAt: Fn(&Account, &H256) -> Option, - FStorageAt: Fn(&Account, &HashDB, &H256) -> TrieResult - { - // Storage key search and update works like this: - // 1. If there's an entry for the account in the local cache check for the key and return it if found. - // 2. If there's an entry for the account in the global cache check for the key or load it into that account. - // 3. If account is missing in the global cache load it into the local cache and cache the key there. - - { - // check local cache first without updating - let local_cache = self.cache.borrow_mut(); - let mut local_account = None; - if let Some(maybe_acc) = local_cache.get(address) { - match maybe_acc.account { - Some(ref account) => { - if let Some(value) = f_cached_at(account, key) { - return Ok(value); - } else { - local_account = Some(maybe_acc); - } - }, - _ => return Ok(H256::new()), - } - } - // check the global cache and and cache storage key there if found, - let trie_res = self.db.get_cached(address, |acc| match acc { - None => Ok(H256::new()), - Some(a) => { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), a.address_hash(address)); - f_at(a, account_db.as_hash_db(), key) - } - }); - - if let Some(res) = trie_res { - return res; - } - - // otherwise cache the account localy and cache storage key there. - if let Some(ref mut acc) = local_account { - if let Some(ref account) = acc.account { - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(address)); - return f_at(account, account_db.as_hash_db(), key) - } else { - return Ok(H256::new()) - } - } - } - - // check if the account could exist before any requests to trie - if self.db.is_known_null(address) { return Ok(H256::zero()) } - - // account is not found in the global cache, get from the DB and insert into local - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); - 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_hash_db(), a.address_hash(address)); - f_at(a, account_db.as_hash_db(), key) - }); - self.insert_cache(address, AccountEntry::new_clean(maybe_acc)); - r - } - - /// Mutate storage of account `address` so that it is `value` for `key`. - pub fn storage_at(&self, address: &Address, key: &H256) -> TrieResult { - self.storage_at_inner( - address, - key, - |account, key| { account.cached_storage_at(key) }, - |account, db, key| { account.storage_at(db, key) }, - ) - } - - /// Get the value of storage after last state commitment. - pub fn original_storage_at(&self, address: &Address, key: &H256) -> TrieResult { - self.storage_at_inner( - address, - key, - |account, key| { account.cached_original_storage_at(key) }, - |account, db, key| { account.original_storage_at(db, key) }, - ) - } - - /// Get accounts' code. - pub fn code(&self, a: &Address) -> TrieResult>> { - self.ensure_cached(a, RequireCache::Code, true, - |a| a.as_ref().map_or(None, |a| a.code().clone())) - } - - /// Get an account's code hash. - pub fn code_hash(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map(|a| a.code_hash())) - } - - /// Get accounts' code size. - pub fn code_size(&self, a: &Address) -> TrieResult> { - self.ensure_cached(a, RequireCache::CodeSize, true, - |a| a.as_ref().and_then(|a| a.code_size())) - } - - /// Add `incr` to the balance of account `a`. - pub fn add_balance(&mut self, a: &Address, incr: &U256, cleanup_mode: CleanupMode) -> TrieResult<()> { - trace!(target: "state", "add_balance({}, {}): {}", a, incr, self.balance(a)?); - let is_value_transfer = !incr.is_zero(); - if is_value_transfer || (cleanup_mode == CleanupMode::ForceCreate && !self.exists(a)?) { - self.require(a, false)?.add_balance(incr); - } else if let CleanupMode::TrackTouched(set) = cleanup_mode { - if self.exists(a)? { - set.insert(*a); - self.touch(a)?; - } - } - Ok(()) - } - - /// Subtract `decr` from the balance of account `a`. - pub fn sub_balance(&mut self, a: &Address, decr: &U256, cleanup_mode: &mut CleanupMode) -> TrieResult<()> { - trace!(target: "state", "sub_balance({}, {}): {}", a, decr, self.balance(a)?); - if !decr.is_zero() || !self.exists(a)? { - self.require(a, false)?.sub_balance(decr); - } - if let CleanupMode::TrackTouched(ref mut set) = *cleanup_mode { - set.insert(*a); - } - Ok(()) - } - - /// Subtracts `by` from the balance of `from` and adds it to that of `to`. - pub fn transfer_balance(&mut self, from: &Address, to: &Address, by: &U256, mut cleanup_mode: CleanupMode) -> TrieResult<()> { - self.sub_balance(from, by, &mut cleanup_mode)?; - self.add_balance(to, by, cleanup_mode)?; - Ok(()) - } - - /// Increment the nonce of account `a` by 1. - pub fn inc_nonce(&mut self, a: &Address) -> TrieResult<()> { - self.require(a, false).map(|mut x| x.inc_nonce()) - } - - /// Mutate storage of account `a` so that it is `value` for `key`. - pub fn set_storage(&mut self, a: &Address, key: H256, value: H256) -> TrieResult<()> { - trace!(target: "state", "set_storage({}:{:x} to {:x})", a, key, value); - if self.storage_at(a, &key)? != value { - self.require(a, false)?.set_storage(key, value) - } - - Ok(()) - } - - /// Initialise the code of account `a` so that it is `code`. - /// NOTE: Account should have been created with `new_contract`. - pub fn init_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.init_code(code); - Ok(()) - } - - /// Reset the code of account `a` so that it is `code`. - pub fn reset_code(&mut self, a: &Address, code: Bytes) -> TrieResult<()> { - self.require_or_from(a, true, || Account::new_contract(0.into(), self.account_start_nonce, KECCAK_NULL_RLP), |_| {})?.reset_code(code); - Ok(()) - } - - /// Execute a given transaction, producing a receipt and an optional trace. - /// This will change the state accordingly. - pub fn apply(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, tracing: bool) -> ApplyResult { - if tracing { - let options = TransactOptions::with_tracing(); - self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) - } else { - let options = TransactOptions::with_no_tracing(); - self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) - } - } - - /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. - /// This will change the state accordingly. - pub fn apply_with_tracing( - &mut self, - env_info: &EnvInfo, - machine: &Machine, - t: &SignedTransaction, - tracer: T, - vm_tracer: V, - ) -> ApplyResult where - T: trace::Tracer, - V: trace::VMTracer, - { - let options = TransactOptions::new(tracer, vm_tracer); - let e = self.execute(env_info, machine, t, options, false)?; - let params = machine.params(); - - let eip658 = env_info.number >= params.eip658_transition; - let no_intermediate_commits = - eip658 || - (env_info.number >= params.eip98_transition && env_info.number >= params.validate_receipts_transition); - - let outcome = if no_intermediate_commits { - if eip658 { - TransactionOutcome::StatusCode(if e.exception.is_some() { 0 } else { 1 }) - } else { - TransactionOutcome::Unknown - } - } else { - self.commit()?; - TransactionOutcome::StateRoot(self.root().clone()) - }; - - let output = e.output; - let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs); - trace!(target: "state", "Transaction receipt: {:?}", receipt); - - Ok(ApplyOutcome { - receipt, - output, - trace: e.trace, - vm_trace: e.vm_trace, - }) - } - - // Execute a given transaction without committing changes. - // - // `virt` signals that we are executing outside of a block set and restrictions like - // gas limits and gas costs should be lifted. - fn execute(&mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, options: TransactOptions, virt: bool) - -> Result, ExecutionError> where T: trace::Tracer, V: trace::VMTracer, - { - let schedule = machine.schedule(env_info.number); - let mut e = Executive::new(self, env_info, machine, &schedule); - - match virt { - true => e.transact_virtual(t, options), - false => e.transact(t, options), - } - } - - fn touch(&mut self, a: &Address) -> TrieResult<()> { - self.require(a, false)?; - Ok(()) - } - - /// Commits our cached account changes into the trie. - pub fn commit(&mut self) -> Result<(), Error> { - assert!(self.checkpoints.borrow().is_empty()); - // first, commit the sub trees. - let mut accounts = self.cache.borrow_mut(); - for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - if let Some(ref mut account) = a.account { - let addr_hash = account.address_hash(address); - { - let mut account_db = self.factories.accountdb.create(self.db.as_hash_db_mut(), addr_hash); - account.commit_storage(&self.factories.trie, account_db.as_hash_db_mut())?; - account.commit_code(account_db.as_hash_db_mut()); - } - if !account.is_empty() { - self.db.note_non_null_account(address); - } - } - } - - { - let mut trie = self.factories.trie.from_existing(self.db.as_hash_db_mut(), &mut self.root)?; - for (address, ref mut a) in accounts.iter_mut().filter(|&(_, ref a)| a.is_dirty()) { - a.state = AccountState::Committed; - match a.account { - Some(ref mut account) => { - trie.insert(address, &account.rlp())?; - }, - None => { - trie.remove(address)?; - }, - }; - } - } - - Ok(()) - } - - /// Propagate local cache into shared canonical state cache. - fn propagate_to_global_cache(&mut self) { - let mut addresses = self.cache.borrow_mut(); - trace!("Committing cache {:?} entries", addresses.len()); - for (address, a) in addresses.drain().filter(|&(_, ref a)| a.state == AccountState::Committed || a.state == AccountState::CleanFresh) { - self.db.add_to_account_cache(address, a.account, a.state == AccountState::Committed); - } - } - - /// Clear state cache - pub fn clear(&mut self) { - assert!(self.checkpoints.borrow().is_empty()); - self.cache.borrow_mut().clear(); - } - - /// Remove any touched empty or dust accounts. - pub fn kill_garbage(&mut self, touched: &HashSet
, remove_empty_touched: bool, min_balance: &Option, kill_contracts: bool) -> TrieResult<()> { - let to_kill: HashSet<_> = { - self.cache.borrow().iter().filter_map(|(address, ref entry)| - if touched.contains(address) && // Check all touched accounts - ((remove_empty_touched && entry.exists_and_is_null()) // Remove all empty touched accounts. - || min_balance.map_or(false, |ref balance| entry.account.as_ref().map_or(false, |account| - (account.is_basic() || kill_contracts) // Remove all basic and optionally contract accounts where balance has been decreased. - && account.balance() < balance && entry.old_balance.as_ref().map_or(false, |b| account.balance() < b)))) { - - Some(address.clone()) - } else { None }).collect() - }; - for address in to_kill { - self.kill_account(&address); - } - Ok(()) - } - - /// Populate the state from `accounts`. - /// Used for tests. - pub fn populate_from(&mut self, accounts: PodState) { - assert!(self.checkpoints.borrow().is_empty()); - for (add, acc) in accounts.drain().into_iter() { - self.cache.borrow_mut().insert(add, AccountEntry::new_dirty(Some(Account::from_pod(acc)))); - } - } - - /// Populate a PodAccount map from this state. - fn to_pod_cache(&self) -> PodState { - assert!(self.checkpoints.borrow().is_empty()); - PodState::from(self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| { - if let Some(ref acc) = opt.account { - m.insert(*add, PodAccount::from_account(acc)); - } - m - })) - } - - #[cfg(feature="to-pod-full")] - /// Populate a PodAccount map from this state. - /// Warning this is not for real time use. - /// Use of this method requires FatDB mode to be able - /// to iterate on accounts. - pub fn to_pod_full(&self) -> Result { - - assert!(self.checkpoints.borrow().is_empty()); - assert!(self.factories.trie.is_fat()); - - let mut result = BTreeMap::new(); - - let db = &self.db.as_hash_db(); - let trie = self.factories.trie.readonly(db, &self.root)?; - - // put trie in cache - for item in trie.iter()? { - if let Ok((addr, _dbval)) = item { - let address = Address::from_slice(&addr); - let _ = self.require(&address, true); - } - } - - // Resolve missing part - for (add, opt) in self.cache.borrow().iter() { - if let Some(ref acc) = opt.account { - let pod_account = self.account_to_pod_account(acc, add)?; - result.insert(add.clone(), pod_account); - } - } - - Ok(PodState::from(result)) - } - - /// Create a PodAccount from an account. - /// Differs from existing method by including all storage - /// values of the account to the PodAccount. - /// This function is only intended for use in small tests or with fresh accounts. - /// It requires FatDB. - #[cfg(feature="to-pod-full")] - fn account_to_pod_account(&self, account: &Account, address: &Address) -> Result { - let mut pod_storage = BTreeMap::new(); - let addr_hash = account.address_hash(address); - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); - let root = account.base_storage_root(); - - let accountdb = &accountdb.as_hash_db(); - let trie = self.factories.trie.readonly(accountdb, &root)?; - for o_kv in trie.iter()? { - if let Ok((key, val)) = o_kv { - pod_storage.insert(key[..].into(), rlp::decode::(&val[..]).expect("Decoded from trie which was encoded from the same type; qed").into()); - } - } - - let mut pod_account = PodAccount::from_account(&account); - // cached one first - pod_storage.append(&mut pod_account.storage); - pod_account.storage = pod_storage; - Ok(pod_account) - } - - /// Populate a PodAccount map from this state, with another state as the account and storage query. - fn to_pod_diff(&mut self, query: &State) -> TrieResult { - 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: TrieResult<_>, 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::>() - } - }; - - // 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: TrieResult<_>, key| { - let mut s = s?; - - s.insert(key, self.storage_at(&address, &key)?); - Ok(s) - })?; - - m.insert(address, PodAccount { - balance, nonce, storage, code - }); - } - - Ok(m) - })?)) - } - - /// Returns a `StateDiff` describing the difference from `orig` to `self`. - /// Consumes self. - pub fn diff_from(&self, mut orig: State) -> TrieResult { - let pod_state_post = self.to_pod_cache(); - 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. Returns whether the cache succeeds. - #[must_use] - fn update_account_cache(require: RequireCache, account: &mut Account, state_db: &B, db: &HashDB) -> bool { - if let RequireCache::None = require { - return true; - } - - if account.is_cached() { - return true; - } - - // if there's already code in the global cache, always cache it localy - let hash = account.code_hash(); - match state_db.get_cached_code(&hash) { - Some(code) => { - account.cache_given_code(code); - true - }, - None => match require { - RequireCache::None => true, - RequireCache::Code => { - if let Some(code) = account.cache_code(db) { - // propagate code loaded from the database to - // the global code cache. - state_db.cache_code(hash, code); - true - } else { - false - } - }, - RequireCache::CodeSize => { - account.cache_code_size(db) - } - } - } - } - - /// Check caches for required data - /// First searches for account in the local, then the shared cache. - /// Populates local cache if nothing found. - fn ensure_cached(&self, a: &Address, require: RequireCache, check_null: bool, f: F) -> TrieResult - where F: Fn(Option<&Account>) -> U { - // check local cache first - if let Some(ref mut maybe_acc) = self.cache.borrow_mut().get_mut(a) { - if let Some(ref mut account) = maybe_acc.account { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Ok(f(Some(account))); - } else { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))); - } - } - return Ok(f(None)); - } - // check global cache - let result = self.db.get_cached(a, |mut acc| { - if let Some(ref mut account) = acc { - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), account.address_hash(a)); - if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))); - } - } - Ok(f(acc.map(|a| &*a))) - }); - match result { - Some(r) => Ok(r?), - None => { - // first check if it is not in database for sure - if check_null && self.db.is_known_null(a) { return Ok(f(None)); } - - // not found in the global cache, get from the DB and insert into local - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root)?; - 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_hash_db(), account.address_hash(a)); - if !Self::update_account_cache(require, account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))); - } - } - let r = f(maybe_acc.as_ref()); - self.insert_cache(a, AccountEntry::new_clean(maybe_acc)); - Ok(r) - } - } - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { - self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {}) - } - - /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - /// If it doesn't exist, make account equal the evaluation of `default`. - fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> - where F: FnOnce() -> Account, G: FnOnce(&mut Account), - { - let contains_key = self.cache.borrow().contains_key(a); - if !contains_key { - match self.db.get_cached_account(a) { - Some(acc) => self.insert_cache(a, AccountEntry::new_clean_cached(acc)), - None => { - let maybe_acc = if !self.db.is_known_null(a) { - let db = &self.db.as_hash_db(); - let db = self.factories.trie.readonly(db, &self.root)?; - 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) - }; - self.insert_cache(a, maybe_acc); - } - } - } - self.note_cache(a); - - // at this point the entry is guaranteed to be in the cache. - let mut account = RefMut::map(self.cache.borrow_mut(), |c| { - let entry = c.get_mut(a).expect("entry known to exist in the cache; qed"); - - match &mut entry.account { - &mut Some(ref mut acc) => not_default(acc), - slot => *slot = Some(default()), - } - - // set the dirty flag after changing account data. - entry.state = AccountState::Dirty; - entry.account.as_mut().expect("Required account must always exist; qed") - }); - - if require_code { - let addr_hash = account.address_hash(a); - let accountdb = self.factories.accountdb.readonly(self.db.as_hash_db(), addr_hash); - - if !Self::update_account_cache(RequireCache::Code, &mut account, &self.db, accountdb.as_hash_db()) { - return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a)))) - } - } - - Ok(account) - } - - /// Replace account code and storage. Creates account if it does not exist. - pub fn patch_account(&self, a: &Address, code: Arc, storage: HashMap) -> TrieResult<()> { - Ok(self.require(a, false)?.reset_code_and_storage(code, storage)) - } -} - -// State proof implementations; useful for light client protocols. -impl State { - /// Prove an account's existence or nonexistence in the state trie. - /// Returns a merkle proof of the account's trie node omitted or an encountered trie error. - /// If the account doesn't exist in the trie, prove that and return defaults. - /// Requires a secure trie to be used for accurate results. - /// `account_key` == keccak(address) - pub fn prove_account(&self, account_key: H256) -> TrieResult<(Vec, BasicAccount)> { - let mut recorder = Recorder::new(); - let db = &self.db.as_hash_db(); - let trie = TrieDB::new(db, &self.root)?; - let maybe_account: Option = { - let panicky_decoder = |bytes: &[u8]| { - ::rlp::decode(bytes).unwrap_or_else(|_| panic!("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 { - balance: 0.into(), - nonce: self.account_start_nonce, - code_hash: KECCAK_EMPTY, - storage_root: KECCAK_NULL_RLP, - }); - - Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) - } - - /// Prove an account's storage key's existence or nonexistence in the state. - /// Returns a merkle proof of the account's storage trie. - /// Requires a secure trie to be used for correctness. - /// `account_key` == keccak(address) - /// `storage_key` == keccak(key) - pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> TrieResult<(Vec, H256)> { - // TODO: probably could look into cache somehow but it's keyed by - // address, not keccak(address). - let db = &self.db.as_hash_db(); - let trie = TrieDB::new(db, &self.root)?; - 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())), - }; - - let account_db = self.factories.accountdb.readonly(self.db.as_hash_db(), account_key); - acc.prove_storage(account_db.as_hash_db(), storage_key) - } -} - -impl fmt::Debug for State { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.cache.borrow()) - } -} - -impl State { - /// Get a reference to the underlying state DB. - pub fn db(&self) -> &StateDB { - &self.db - } -} - -// TODO: cloning for `State` shouldn't be possible in general; Remove this and use -// checkpoints where possible. -impl Clone for State { - fn clone(&self) -> State { - let cache = { - let mut cache: HashMap = HashMap::new(); - for (key, val) in self.cache.borrow().iter() { - if let Some(entry) = val.clone_if_dirty() { - cache.insert(key.clone(), entry); - } - } - cache - }; - - State { - db: self.db.boxed_clone(), - root: self.root.clone(), - cache: RefCell::new(cache), - checkpoints: RefCell::new(Vec::new()), - account_start_nonce: self.account_start_nonce.clone(), - factories: self.factories.clone(), - } - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::str::FromStr; - use rustc_hex::FromHex; - use hash::{keccak, KECCAK_NULL_RLP}; - use super::*; - use ethkey::Secret; - use ethereum_types::{H256, U256, Address}; - use test_helpers::{get_temp_state, get_temp_state_db}; - use machine::EthereumMachine; - use vm::EnvInfo; - use spec::*; - use types::transaction::*; - use trace::{FlatTrace, TraceError, trace}; - use evm::CallType; - - fn secret() -> Secret { - keccak("").into() - } - - fn make_frontier_machine(max_depth: usize) -> EthereumMachine { - let mut machine = ::ethereum::new_frontier_test_machine(); - machine.set_schedule_creation_rules(Box::new(move |s, _| s.max_depth = max_depth)); - machine - } - - #[test] - fn should_apply_create_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), - }.sign(&secret(), None); - - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Create(trace::Create { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - value: 100.into(), - gas: 77412.into(), - init: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], - }), - result: trace::Res::Create(trace::CreateResult { - gas_used: U256::from(3224), - address: Address::from_str("8988167e088c87cd314df6d3c2b83da5acb93ace").unwrap(), - code: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_work_when_cloned() { - let _ = env_logger::try_init(); - - let a = Address::zero(); - - let mut state = { - let mut state = get_temp_state(); - assert_eq!(state.exists(&a).unwrap(), false); - state.inc_nonce(&a).unwrap(); - state.commit().unwrap(); - state.clone() - }; - - state.inc_nonce(&a).unwrap(); - state.commit().unwrap(); - } - - #[test] - fn should_trace_failed_create_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Create, - value: 100.into(), - data: FromHex::from_hex("5b600056").unwrap(), - }.sign(&secret(), None); - - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Create(trace::Create { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - value: 100.into(), - gas: 78792.into(), - init: vec![91, 96, 0, 86], - }), - result: trace::Res::FailedCreate(TraceError::OutOfGas), - subtraces: 0 - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_basic_call_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(0), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_transaction_to_builtin() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0x1.into()), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: "0000000000000000000000000000000000000001".into(), - value: 0.into(), - gas: 79_000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3000), - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_not_trace_subcall_transaction_to_builtin() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3_721), // in post-eip150 - output: vec![] - }), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_callcode_properly() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 724.into(), // in post-eip150 - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 4096.into(), - input: vec![], - call_type: CallType::CallCode, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![], - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_delegatecall_properly() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - info.number = 0x789b0; - let machine = Spec::new_test_machine(); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 0.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("60056000526001601ff3").unwrap()).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 0.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(736), // in post-eip150 - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 32768.into(), - input: vec![], - call_type: CallType::DelegateCall, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 18.into(), - output: vec![5], - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_failed_call_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - subtraces: 0, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_with_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_with_basic_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(31761), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 69.into(), - gas: 2300.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult::default()), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_not_trace_call_with_invalid_basic_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()).unwrap(); // not enough funds. - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(31761), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_failed_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![],//600480600b6000396000f35b600056 - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(79_000), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_call_with_subcall_with_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()).unwrap(); - state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(135), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(69), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0, 0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xb.into(), - to: 0xc.into(), - value: 0.into(), - gas: 78868.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_failed_subcall_with_subcall_transaction() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![],//600480600b6000396000f35b600056 - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()).unwrap(); - state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()).unwrap(); - state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(79_000), - output: vec![] - }) - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: 0xa.into(), - to: 0xb.into(), - value: 0.into(), - gas: 78934.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::FailedCall(TraceError::OutOfGas), - }, FlatTrace { - trace_address: vec![0, 0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Call(trace::Call { - from: 0xb.into(), - to: 0xc.into(), - value: 0.into(), - gas: 78868.into(), - call_type: CallType::Call, - input: vec![], - }), - result: trace::Res::Call(trace::CallResult { - gas_used: U256::from(3), - output: vec![] - }), - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn should_trace_suicide() { - let _ = env_logger::try_init(); - - let mut state = get_temp_state(); - - let mut info = EnvInfo::default(); - info.gas_limit = 1_000_000.into(); - let machine = make_frontier_machine(5); - - let t = Transaction { - nonce: 0.into(), - gas_price: 0.into(), - gas: 100_000.into(), - action: Action::Call(0xa.into()), - value: 100.into(), - data: vec![], - }.sign(&secret(), None); - - state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()).unwrap(); - state.add_balance(&0xa.into(), &50.into(), CleanupMode::NoEmpty).unwrap(); - state.add_balance(&t.sender(), &100.into(), CleanupMode::NoEmpty).unwrap(); - let result = state.apply(&info, &machine, &t, true).unwrap(); - let expected_trace = vec![FlatTrace { - trace_address: Default::default(), - subtraces: 1, - action: trace::Action::Call(trace::Call { - from: "9cce34f7ab185c7aba1b7c8140d620b4bda941d6".into(), - to: 0xa.into(), - value: 100.into(), - gas: 79000.into(), - input: vec![], - call_type: CallType::Call, - }), - result: trace::Res::Call(trace::CallResult { - gas_used: 3.into(), - output: vec![] - }), - }, FlatTrace { - trace_address: vec![0].into_iter().collect(), - subtraces: 0, - action: trace::Action::Suicide(trace::Suicide { - address: 0xa.into(), - refund_address: 0xb.into(), - balance: 150.into(), - }), - result: trace::Res::None, - }]; - - assert_eq!(result.trace, expected_trace); - } - - #[test] - fn code_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.require_or_from(&a, false, || Account::new_contract(42.into(), 0.into(), KECCAK_NULL_RLP), |_|{}).unwrap(); - state.init_code(&a, vec![1, 2, 3]).unwrap(); - assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); - state.commit().unwrap(); - assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.code(&a).unwrap(), Some(Arc::new(vec![1u8, 2, 3]))); - } - - #[test] - fn storage_at_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.set_storage(&a, H256::from(&U256::from(1u64)), H256::from(&U256::from(69u64))).unwrap(); - state.commit().unwrap(); - state.drop() - }; - - let s = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(s.storage_at(&a, &H256::from(&U256::from(1u64))).unwrap(), H256::from(&U256::from(69u64))); - } - - #[test] - fn get_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.inc_nonce(&a).unwrap(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - } - - #[test] - fn remove() { - let a = Address::zero(); - let mut state = get_temp_state(); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.exists_and_not_null(&a).unwrap(), false); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.exists(&a).unwrap(), true); - assert_eq!(state.exists_and_not_null(&a).unwrap(), true); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.kill_account(&a); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.exists_and_not_null(&a).unwrap(), false); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - } - - #[test] - fn empty_account_is_not_created() { - let a = Address::zero(); - let db = get_temp_state_db(); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default(), CleanupMode::NoEmpty).unwrap(); // create an empty account - state.commit().unwrap(); - state.drop() - }; - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert!(!state.exists(&a).unwrap()); - assert!(!state.exists_and_not_null(&a).unwrap()); - } - - #[test] - fn empty_account_exists_when_creation_forced() { - let a = Address::zero(); - let db = get_temp_state_db(); - let (root, db) = { - let mut state = State::new(db, U256::from(0), Default::default()); - state.add_balance(&a, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account - state.commit().unwrap(); - state.drop() - }; - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert!(state.exists(&a).unwrap()); - assert!(!state.exists_and_not_null(&a).unwrap()); - } - - #[test] - fn remove_from_database() { - let a = Address::zero(); - let (root, db) = { - let mut state = get_temp_state(); - state.inc_nonce(&a).unwrap(); - state.commit().unwrap(); - assert_eq!(state.exists(&a).unwrap(), true); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.drop() - }; - - let (root, db) = { - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.exists(&a).unwrap(), true); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.kill_account(&a); - state.commit().unwrap(); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - state.drop() - }; - - let state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - assert_eq!(state.exists(&a).unwrap(), false); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - } - - #[test] - fn alter_balance() { - let mut state = get_temp_state(); - let a = Address::zero(); - let b = 1u64.into(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.sub_balance(&a, &U256::from(42u64), &mut CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(27u64)); - state.transfer_balance(&a, &b, &U256::from(18u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); - assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(9u64)); - assert_eq!(state.balance(&b).unwrap(), U256::from(18u64)); - } - - #[test] - fn alter_nonce() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(1u64)); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); - state.commit().unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(2u64)); - state.inc_nonce(&a).unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); - state.commit().unwrap(); - assert_eq!(state.nonce(&a).unwrap(), U256::from(3u64)); - } - - #[test] - fn balance_nonce() { - let mut state = get_temp_state(); - let a = Address::zero(); - assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - state.commit().unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(0u64)); - assert_eq!(state.nonce(&a).unwrap(), U256::from(0u64)); - } - - #[test] - fn ensure_cached() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.require(&a, false).unwrap(); - state.commit().unwrap(); - assert_eq!(*state.root(), "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785".into()); - } - - #[test] - fn checkpoint_basic() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.checkpoint(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.discard_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.checkpoint(); - state.add_balance(&a, &U256::from(1u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(70u64)); - state.revert_to_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - } - - #[test] - fn checkpoint_nested() { - let mut state = get_temp_state(); - let a = Address::zero(); - state.checkpoint(); - state.checkpoint(); - state.add_balance(&a, &U256::from(69u64), CleanupMode::NoEmpty).unwrap(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.discard_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(69u64)); - state.revert_to_checkpoint(); - assert_eq!(state.balance(&a).unwrap(), U256::from(0)); - } - - #[test] - fn checkpoint_revert_to_get_storage_at() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = H256::from(U256::from(0)); - - let c0 = state.checkpoint(); - let c1 = state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); - - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(1))); - - state.revert_to_checkpoint(); // Revert to c1. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); - } - - #[test] - fn checkpoint_from_empty_get_storage_at() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = H256::from(U256::from(0)); - let k2 = H256::from(U256::from(1)); - - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); - state.clear(); - - let c0 = state.checkpoint(); - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - let c1 = state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); - let c2 = state.checkpoint(); - let c3 = state.checkpoint(); - state.set_storage(&a, k2, H256::from(U256::from(3))).unwrap(); - state.set_storage(&a, k, H256::from(U256::from(3))).unwrap(); - let c4 = state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(4))).unwrap(); - let c5 = state.checkpoint(); - - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(H256::from(U256::from(3)))); - assert_eq!(state.checkpoint_storage_at(c5, &a, &k).unwrap(), Some(H256::from(U256::from(4)))); - - state.discard_checkpoint(); // Commit/discard c5. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(H256::from(U256::from(3)))); - - state.revert_to_checkpoint(); // Revert to c4. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - - state.discard_checkpoint(); // Commit/discard c3. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - - state.revert_to_checkpoint(); // Revert to c2. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - - state.discard_checkpoint(); // Commit/discard c1. - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - } - - #[test] - fn checkpoint_get_storage_at() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = H256::from(U256::from(0)); - let k2 = H256::from(U256::from(1)); - - state.set_storage(&a, k, H256::from(U256::from(0xffff))).unwrap(); - state.commit().unwrap(); - state.clear(); - - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0xffff))); - state.clear(); - - let cm1 = state.checkpoint(); - let c0 = state.checkpoint(); - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - let c1 = state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); - let c2 = state.checkpoint(); - let c3 = state.checkpoint(); - state.set_storage(&a, k2, H256::from(U256::from(3))).unwrap(); - state.set_storage(&a, k, H256::from(U256::from(3))).unwrap(); - let c4 = state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(4))).unwrap(); - let c5 = state.checkpoint(); - - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(H256::from(U256::from(3)))); - assert_eq!(state.checkpoint_storage_at(c5, &a, &k).unwrap(), Some(H256::from(U256::from(4)))); - - state.discard_checkpoint(); // Commit/discard c5. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c4, &a, &k).unwrap(), Some(H256::from(U256::from(3)))); - - state.revert_to_checkpoint(); // Revert to c4. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - assert_eq!(state.checkpoint_storage_at(c3, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - - state.discard_checkpoint(); // Commit/discard c3. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - assert_eq!(state.checkpoint_storage_at(c2, &a, &k).unwrap(), Some(H256::from(U256::from(1)))); - - state.revert_to_checkpoint(); // Revert to c2. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c1, &a, &k).unwrap(), Some(H256::from(U256::from(0)))); - - state.discard_checkpoint(); // Commit/discard c1. - assert_eq!(state.checkpoint_storage_at(cm1, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - assert_eq!(state.checkpoint_storage_at(c0, &a, &k).unwrap(), Some(H256::from(U256::from(0xffff)))); - } - - #[test] - fn kill_account_with_checkpoints() { - let mut state = get_temp_state(); - let a = Address::zero(); - let k = H256::from(U256::from(0)); - state.checkpoint(); - state.set_storage(&a, k, H256::from(U256::from(1))).unwrap(); - state.checkpoint(); - state.kill_account(&a); - - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); - state.revert_to_checkpoint(); - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(1))); - } - - #[test] - fn create_contract_fail() { - let mut state = get_temp_state(); - let orig_root = state.root().clone(); - let a: Address = 1000.into(); - - state.checkpoint(); // c1 - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); - state.checkpoint(); // c2 - state.add_balance(&a, &U256::from(1), CleanupMode::ForceCreate).unwrap(); - state.discard_checkpoint(); // discard c2 - state.revert_to_checkpoint(); // revert to c1 - assert_eq!(state.exists(&a).unwrap(), false); - - state.commit().unwrap(); - assert_eq!(orig_root, state.root().clone()); - } - - #[test] - fn create_contract_fail_previous_storage() { - let mut state = get_temp_state(); - let a: Address = 1000.into(); - let k = H256::from(U256::from(0)); - - state.set_storage(&a, k, H256::from(U256::from(0xffff))).unwrap(); - state.commit().unwrap(); - state.clear(); - - let orig_root = state.root().clone(); - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0xffff))); - state.clear(); - - state.checkpoint(); // c1 - state.new_contract(&a, U256::zero(), U256::zero()).unwrap(); - state.checkpoint(); // c2 - state.set_storage(&a, k, H256::from(U256::from(2))).unwrap(); - state.revert_to_checkpoint(); // revert to c2 - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0))); - state.revert_to_checkpoint(); // revert to c1 - assert_eq!(state.storage_at(&a, &k).unwrap(), H256::from(U256::from(0xffff))); - - state.commit().unwrap(); - assert_eq!(orig_root, state.root().clone()); - } - - #[test] - fn create_empty() { - let mut state = get_temp_state(); - state.commit().unwrap(); - assert_eq!(*state.root(), "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".into()); - } - - #[test] - fn should_not_panic_on_state_diff_with_storage() { - let mut state = get_temp_state(); - - let a: Address = 0xa.into(); - state.init_code(&a, b"abcdefg".to_vec()).unwrap();; - state.add_balance(&a, &256.into(), CleanupMode::NoEmpty).unwrap(); - state.set_storage(&a, 0xb.into(), 0xc.into()).unwrap(); - - let mut new_state = state.clone(); - new_state.set_storage(&a, 0xb.into(), 0xd.into()).unwrap(); - - new_state.diff_from(state).unwrap(); - } - - #[test] - fn should_kill_garbage() { - let a = 10.into(); - let b = 20.into(); - let c = 30.into(); - let d = 40.into(); - let e = 50.into(); - let x = 0.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, &U256::default(), CleanupMode::ForceCreate).unwrap(); // create an empty account - state.add_balance(&b, &100.into(), CleanupMode::ForceCreate).unwrap(); // create a dust account - state.add_balance(&c, &101.into(), CleanupMode::ForceCreate).unwrap(); // create a normal account - state.add_balance(&d, &99.into(), CleanupMode::ForceCreate).unwrap(); // create another dust account - state.new_contract(&e, 100.into(), 1.into()).unwrap(); // create a contract account - state.init_code(&e, vec![0x00]).unwrap(); - state.commit().unwrap(); - state.drop() - }; - - let mut state = State::from_existing(db, root, U256::from(0u8), Default::default()).unwrap(); - let mut touched = HashSet::new(); - state.add_balance(&a, &U256::default(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account - state.transfer_balance(&b, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance - state.transfer_balance(&c, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance - state.transfer_balance(&e, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance - state.kill_garbage(&touched, true, &None, false).unwrap(); - assert!(!state.exists(&a).unwrap()); - assert!(state.exists(&b).unwrap()); - state.kill_garbage(&touched, true, &Some(100.into()), false).unwrap(); - assert!(!state.exists(&b).unwrap()); - assert!(state.exists(&c).unwrap()); - assert!(state.exists(&d).unwrap()); - assert!(state.exists(&e).unwrap()); - state.kill_garbage(&touched, true, &Some(100.into()), true).unwrap(); - assert!(state.exists(&c).unwrap()); - 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()); - } - - #[cfg(feature="to-pod-full")] - #[test] - fn should_get_full_pod_storage_values() { - use trie::{TrieFactory, TrieSpec}; - - let a = 10.into(); - let db = get_temp_state_db(); - - let factories = Factories { - vm: Default::default(), - trie: TrieFactory::new(TrieSpec::Fat), - accountdb: Default::default(), - }; - - let get_pod_state_val = |pod_state : &PodState, ak, k| { - pod_state.get().get(ak).unwrap().storage.get(&k).unwrap().clone() - }; - - let storage_address = H256::from(&U256::from(1u64)); - - let (root, db) = { - let mut state = State::new(db, U256::from(0), factories.clone()); - state.set_storage(&a, storage_address.clone(), H256::from(&U256::from(20u64))).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(20u64))); - state.commit().unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(20u64))); - state.drop() - }; - - let mut state = State::from_existing(db, root, U256::from(0u8), factories).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(20u64))); - state.set_storage(&a, storage_address.clone(), H256::from(&U256::from(21u64))).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(21u64))); - state.commit().unwrap(); - state.set_storage(&a, storage_address.clone(), H256::from(&U256::from(0u64))).unwrap(); - let dump = state.to_pod_full().unwrap(); - assert_eq!(get_pod_state_val(&dump, &a, storage_address.clone()), H256::from(&U256::from(0u64))); - - } - -} diff --git a/ethcore/src/client/evm_test_client.rs b/ethcore/src/test_helpers/evm_test_client.rs similarity index 75% rename from ethcore/src/client/evm_test_client.rs rename to ethcore/src/test_helpers/evm_test_client.rs index d65e20691e..252de7dbc2 100644 --- a/ethcore/src/client/evm_test_client.rs +++ b/ethcore/src/test_helpers/evm_test_client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,14 +19,28 @@ use std::fmt; use std::sync::Arc; use ethereum_types::{H256, U256, H160}; -use {factory, journaldb, trie, kvdb_memorydb}; +use {trie_vm_factories, journaldb, trie, kvdb_memorydb}; use kvdb::{self, KeyValueDB}; -use {state, state_db, client, executive, trace, db, spec, pod_state}; -use types::{log_entry, receipt, transaction}; -use factory::Factories; -use evm::{VMType, FinalizationResult}; -use vm::{self, ActionParams}; +use {state_db, trace, db, spec}; +use pod::PodState; +use types::{ + errors::EthcoreError, + log_entry, + receipt, + transaction +}; +use ethjson::spec::ForkSpec; +use trie_vm_factories::Factories; +use evm::FinalizationResult; +use vm::{self, ActionParams, CreateContractAddress}; use ethtrie; +use account_state::{CleanupMode, State}; +use machine::{ + executive, + substate::Substate, +}; + +use executive_state::ExecutiveState; /// EVM test Error. #[derive(Debug)] @@ -36,12 +50,12 @@ pub enum EvmTestError { /// EVM error. Evm(vm::Error), /// Initialization error. - ClientError(::error::Error), + ClientError(EthcoreError), /// Post-condition failure, PostCondition(String), } -impl> From for EvmTestError { +impl> From for EvmTestError { fn from(err: E) -> Self { EvmTestError::ClientError(err.into()) } @@ -60,20 +74,21 @@ impl fmt::Display for EvmTestError { } } -use ethereum; -use ethjson::spec::ForkSpec; - /// Simplified, single-block EVM test client. pub struct EvmTestClient<'a> { - state: state::State, + state: State, spec: &'a spec::Spec, - dump_state: fn(&state::State) -> Option, + dump_state: fn(&State) -> Option, } -fn no_dump_state(_: &state::State) -> Option { +fn no_dump_state(_: &State) -> Option { None } +fn dump_state(state: &State) -> Option { + state.to_pod_full().ok() +} + impl<'a> fmt::Debug for EvmTestClient<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("EvmTestClient") @@ -85,23 +100,23 @@ impl<'a> fmt::Debug for EvmTestClient<'a> { impl<'a> EvmTestClient<'a> { /// Converts a json spec definition into spec. - pub fn spec_from_json(spec: &ForkSpec) -> Option { + pub fn fork_spec_from_json(spec: &ForkSpec) -> Option { match *spec { - ForkSpec::Frontier => Some(ethereum::new_frontier_test()), - ForkSpec::Homestead => Some(ethereum::new_homestead_test()), - ForkSpec::EIP150 => Some(ethereum::new_eip150_test()), - ForkSpec::EIP158 => Some(ethereum::new_eip161_test()), - ForkSpec::Byzantium => Some(ethereum::new_byzantium_test()), - ForkSpec::Constantinople => Some(ethereum::new_constantinople_test()), - ForkSpec::ConstantinopleFix => Some(ethereum::new_constantinople_fix_test()), - ForkSpec::Istanbul => Some(ethereum::new_istanbul_test()), - ForkSpec::EIP158ToByzantiumAt5 => Some(ethereum::new_transition_test()), + ForkSpec::Frontier => Some(spec::new_frontier_test()), + ForkSpec::Homestead => Some(spec::new_homestead_test()), + ForkSpec::EIP150 => Some(spec::new_eip150_test()), + ForkSpec::EIP158 => Some(spec::new_eip161_test()), + ForkSpec::Byzantium => Some(spec::new_byzantium_test()), + ForkSpec::Constantinople => Some(spec::new_constantinople_test()), + ForkSpec::ConstantinopleFix => Some(spec::new_constantinople_fix_test()), + ForkSpec::Istanbul => Some(spec::new_istanbul_test()), + ForkSpec::EIP158ToByzantiumAt5 => Some(spec::new_transition_test()), ForkSpec::FrontierToHomesteadAt5 | ForkSpec::HomesteadToDaoAt5 | ForkSpec::HomesteadToEIP150At5 => None, } } /// Change default function for dump state (default does not dump) - pub fn set_dump_state_fn(&mut self, dump_state: fn(&state::State) -> Option) { + pub fn set_dump_state(&mut self) { self.dump_state = dump_state; } @@ -125,7 +140,7 @@ impl<'a> EvmTestClient<'a> { /// Creates new EVM test client with an in-memory DB initialized with given PodState. /// Takes a `TrieSpec` to set the type of trie. - pub fn from_pod_state_with_trie(spec: &'a spec::Spec, pod_state: pod_state::PodState, trie_spec: trie::TrieSpec) -> Result { + pub fn from_pod_state_with_trie(spec: &'a spec::Spec, pod_state: PodState, trie_spec: trie::TrieSpec) -> Result { let factories = Self::factories(trie_spec); let state = Self::state_from_pod(spec, &factories, pod_state)?; @@ -137,20 +152,20 @@ impl<'a> EvmTestClient<'a> { } /// Creates new EVM test client with an in-memory DB initialized with given PodState. - pub fn from_pod_state(spec: &'a spec::Spec, pod_state: pod_state::PodState) -> Result { + pub fn from_pod_state(spec: &'a spec::Spec, pod_state: PodState) -> Result { Self::from_pod_state_with_trie(spec, pod_state, trie::TrieSpec::Secure) } fn factories(trie_spec: trie::TrieSpec) -> Factories { Factories { - vm: factory::VmFactory::new(VMType::Interpreter, 5 * 1024), - trie: trie::TrieFactory::new(trie_spec), + vm: trie_vm_factories::VmFactory::new(5 * 1024), + trie: trie::TrieFactory::new(trie_spec, ethtrie::Layout), accountdb: Default::default(), } } - fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result, EvmTestError> { - let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); + fn state_from_spec(spec: &'a spec::Spec, factories: &Factories) -> Result, EvmTestError> { + let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS)); let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); let mut state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); state_db = spec.ensure_db_good(state_db, factories)?; @@ -163,7 +178,7 @@ impl<'a> EvmTestClient<'a> { db.write(batch)?; } - state::State::from_existing( + State::from_existing( state_db, *genesis.state_root(), spec.engine.account_start_nonce(0), @@ -171,11 +186,11 @@ impl<'a> EvmTestClient<'a> { ).map_err(EvmTestError::Trie) } - fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: pod_state::PodState) -> Result, EvmTestError> { - let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS.expect("We use column-based DB; qed"))); + fn state_from_pod(spec: &'a spec::Spec, factories: &Factories, pod_state: PodState) -> Result, EvmTestError> { + let db = Arc::new(kvdb_memorydb::create(db::NUM_COLUMNS)); let journal_db = journaldb::new(db.clone(), journaldb::Algorithm::EarlyMerge, db::COL_STATE); let state_db = state_db::StateDB::new(journal_db, 5 * 1024 * 1024); - let mut state = state::State::new( + let mut state = State::new( state_db, spec.engine.account_start_nonce(0), factories.clone(), @@ -186,7 +201,7 @@ impl<'a> EvmTestClient<'a> { } /// Return current state. - pub fn state(&self) -> &state::State { + pub fn state(&self) -> &State { &self.state } @@ -200,12 +215,12 @@ impl<'a> EvmTestClient<'a> { ) -> Result { let genesis = self.spec.genesis_header(); - let info = client::EnvInfo { + let info = vm::EnvInfo { number: genesis.number(), author: *genesis.author(), timestamp: genesis.timestamp(), difficulty: *genesis.difficulty(), - last_hashes: Arc::new([H256::default(); 256].to_vec()), + last_hashes: Arc::new([H256::zero(); 256].to_vec()), gas_used: 0.into(), gas_limit: *genesis.gas_limit(), }; @@ -219,10 +234,10 @@ impl<'a> EvmTestClient<'a> { params: ActionParams, tracer: &mut T, vm_tracer: &mut V, - info: client::EnvInfo, + info: vm::EnvInfo, ) -> Result { - let mut substate = state::Substate::new(); + let mut substate = Substate::new(); let machine = self.spec.engine.machine(); let schedule = machine.schedule(info.number); let mut executive = executive::Executive::new(&mut self.state, &info, &machine, &schedule); @@ -238,14 +253,14 @@ impl<'a> EvmTestClient<'a> { /// Returns the state root, gas left and the output. pub fn transact( &mut self, - env_info: &client::EnvInfo, + env_info: &vm::EnvInfo, transaction: transaction::SignedTransaction, tracer: T, vm_tracer: V, ) -> std::result::Result, TransactErr> { let initial_gas = transaction.gas; // Verify transaction - let is_ok = transaction.verify_basic(true, None, false); + let is_ok = transaction.verify_basic(true, None); if let Err(error) = is_ok { return Err( TransactErr{ @@ -257,16 +272,16 @@ impl<'a> EvmTestClient<'a> { // Apply transaction let result = self.state.apply_with_tracing(&env_info, self.spec.engine.machine(), &transaction, tracer, vm_tracer); - let scheme = self.spec.engine.machine().create_address_scheme(env_info.number); + let scheme = CreateContractAddress::FromSenderAndNonce; // Touch the coinbase at the end of the test to simulate // miner reward. // Details: https://github.com/paritytech/parity-ethereum/issues/9431 let schedule = self.spec.engine.machine().schedule(env_info.number); self.state.add_balance(&env_info.author, &0.into(), if schedule.no_empty { - state::CleanupMode::NoEmpty + CleanupMode::NoEmpty } else { - state::CleanupMode::ForceCreate + CleanupMode::ForceCreate }).ok(); // Touching also means that we should remove the account if it's within eip161 // conditions. @@ -301,11 +316,7 @@ impl<'a> EvmTestClient<'a> { end_state, } )}, - Err(error) => Err(TransactErr { - state_root, - error, - end_state, - }), + Err(e) => Err(TransactErr {state_root, error: e.into(), end_state}), } } } @@ -331,7 +342,7 @@ pub struct TransactSuccess { /// outcome pub outcome: receipt::TransactionOutcome, /// end state if needed - pub end_state: Option, + pub end_state: Option, } /// To be returned inside a std::result::Result::Err after a failed @@ -341,7 +352,7 @@ pub struct TransactErr { /// State root pub state_root: H256, /// Execution error - pub error: ::error::Error, + pub error: EthcoreError, /// end state if needed - pub end_state: Option, + pub end_state: Option, } diff --git a/ethcore/src/test_helpers.rs b/ethcore/src/test_helpers/mod.rs similarity index 85% rename from ethcore/src/test_helpers.rs rename to ethcore/src/test_helpers/mod.rs index b5575f36cd..216086934f 100644 --- a/ethcore/src/test_helpers.rs +++ b/ethcore/src/test_helpers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,6 +16,18 @@ //! Set of different helpers for client tests +mod test_client; +mod evm_test_client; + +/// Re-export for tests only +pub use evm::CreateContractAddress; +/// Re-export for tests only +pub use trie::TrieSpec; +/// Re-export for tests only +pub use self::test_client::{TestBlockChainClient, EachBlockWith, TestState}; +/// Re-export for tests only +pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; + use std::path::Path; use std::sync::Arc; use std::{fs, io}; @@ -24,7 +36,7 @@ use blockchain::{BlockChain, BlockChainDB, BlockChainDBHandler, Config as BlockC use blooms_db; use bytes::Bytes; use ethereum_types::{H256, U256, Address}; -use ethkey::KeyPair; +use parity_crypto::publickey::KeyPair; use evm::Factory as EvmFactory; use hash::keccak; use io::IoChannel; @@ -33,20 +45,26 @@ use kvdb_rocksdb::{self, Database, DatabaseConfig}; use parking_lot::RwLock; use rlp::{self, RlpStream}; use tempdir::TempDir; -use types::transaction::{Action, Transaction, SignedTransaction}; -use types::encoded; -use types::header::Header; -use types::view; -use types::views::BlockView; +use types::{ + chain_notify::ChainMessageType, + transaction::{Action, Transaction, SignedTransaction}, + encoded, + engines::ForkChoice, + header::Header, + view, + views::BlockView, + verification::Unverified, +}; use block::{OpenBlock, Drain}; -use client::{Client, ClientConfig, ChainInfo, ImportBlock, ChainNotify, ChainMessageType, PrepareOpenBlock}; -use factory::Factories; +use client::{Client, ClientConfig, PrepareOpenBlock}; +use client_traits::{ChainInfo, ChainNotify, ImportBlock}; +use trie_vm_factories::Factories; use miner::Miner; -use spec::Spec; -use state::*; +use spec::{Spec, self}; +use account_state::*; use state_db::StateDB; -use verification::queue::kind::blocks::Unverified; + /// Creates test block with corresponding header pub fn create_test_block(header: &Header) -> Bytes { @@ -100,31 +118,35 @@ pub fn create_test_block_with_data(header: &Header, transactions: &[SignedTransa /// 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, &[]) + generate_dummy_client_with_spec_and_data(spec::new_test, block_number, 0, &[], false) } /// 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) + generate_dummy_client_with_spec_and_data(spec::new_null, block_number, txs_per_block, tx_gas_prices, false) } /// Generates dummy client (not test client) with corresponding spec and accounts -pub fn generate_dummy_client_with_spec(test_spec: F) -> Arc where F: Fn()->Spec { - generate_dummy_client_with_spec_and_data(test_spec, 0, 0, &[]) +pub fn generate_dummy_client_with_spec(test_spec: F) -> Arc where F: Fn() -> Spec { + generate_dummy_client_with_spec_and_data(test_spec, 0, 0, &[], false) } /// 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 +pub fn generate_dummy_client_with_spec_and_data( + test_spec: F, block_number: u32, txs_per_block: usize, tx_gas_prices: &[U256], force_sealing: bool, +) -> Arc where F: Fn() -> Spec { let test_spec = test_spec(); let client_db = new_db(); + let miner = Miner::new_for_tests_force_sealing(&test_spec, None, force_sealing); + let client = Client::new( ClientConfig::default(), &test_spec, client_db, - Arc::new(Miner::new_for_tests(&test_spec, None)), + Arc::new(miner), IoChannel::disconnected(), ).unwrap(); let test_engine = &*test_spec.engine; @@ -136,7 +158,7 @@ pub fn generate_dummy_client_with_spec_and_data(test_spec: F, block_number: u let mut last_hashes = vec![]; let mut last_header = genesis_header.clone(); - let kp = KeyPair::from_secret_slice(&keccak("")).unwrap(); + let kp = KeyPair::from_secret_slice(keccak("").as_bytes()).unwrap(); let author = kp.address(); let mut n = 0; @@ -155,7 +177,6 @@ pub fn generate_dummy_client_with_spec_and_data(test_spec: F, block_number: u (3141562.into(), 31415620.into()), vec![], false, - None, ).unwrap(); rolling_timestamp += 10; b.set_timestamp(rolling_timestamp); @@ -183,13 +204,12 @@ pub fn generate_dummy_client_with_spec_and_data(test_spec: F, block_number: u db = b.drain().state.drop().1; } client.flush_queue(); - client.import_verified_blocks(); 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 test_spec = spec::new_test(); let state_root = test_spec.genesis_header().state_root().clone(); let genesis_gas = test_spec.genesis_header().gas_limit().clone(); @@ -219,11 +239,11 @@ 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_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()).unwrap(); + let mut b = client.prepare_open_block(Address::zero(), (0.into(), 5000000.into()), Bytes::new()).unwrap(); b.set_timestamp(block_number * 10); for t in transactions { @@ -236,12 +256,11 @@ pub fn push_block_with_transactions(client: &Arc, transactions: &[Signed } 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 test_spec = spec::new_test(); let client_db = new_db(); let client = Client::new( @@ -258,7 +277,6 @@ pub fn get_test_client_with_blocks(blocks: Vec) -> Arc { } } client.flush_queue(); - client.import_verified_blocks(); client } @@ -267,11 +285,11 @@ struct TestBlockChainDB { _trace_blooms_dir: TempDir, blooms: blooms_db::Database, trace_blooms: blooms_db::Database, - key_value: Arc, + key_value: Arc, } impl BlockChainDB for TestBlockChainDB { - fn key_value(&self) -> &Arc { + fn key_value(&self) -> &Arc { &self.key_value } @@ -285,7 +303,7 @@ impl BlockChainDB for TestBlockChainDB { } /// Creates new test instance of `BlockChainDB` -pub fn new_db() -> Arc { +pub fn new_db() -> Arc { let blooms_dir = TempDir::new("").unwrap(); let trace_blooms_dir = TempDir::new("").unwrap(); @@ -294,14 +312,14 @@ pub fn new_db() -> Arc { trace_blooms: blooms_db::Database::open(trace_blooms_dir.path()).unwrap(), _blooms_dir: blooms_dir, _trace_blooms_dir: trace_blooms_dir, - key_value: Arc::new(::kvdb_memorydb::create(::db::NUM_COLUMNS.unwrap())) + key_value: Arc::new(::kvdb_memorydb::create(::db::NUM_COLUMNS)) }; Arc::new(db) } /// Creates a new temporary `BlockChainDB` on FS -pub fn new_temp_db(tempdir: &Path) -> Arc { +pub fn new_temp_db(tempdir: &Path) -> Arc { let blooms_dir = TempDir::new("").unwrap(); let trace_blooms_dir = TempDir::new("").unwrap(); let key_value_dir = tempdir.join("key_value"); @@ -321,7 +339,7 @@ pub fn new_temp_db(tempdir: &Path) -> Arc { } /// Creates new instance of KeyValueDBHandler -pub fn restoration_db_handler(config: kvdb_rocksdb::DatabaseConfig) -> Box { +pub fn restoration_db_handler(config: kvdb_rocksdb::DatabaseConfig) -> Box { struct RestorationDBHandler { config: kvdb_rocksdb::DatabaseConfig, } @@ -329,11 +347,11 @@ pub fn restoration_db_handler(config: kvdb_rocksdb::DatabaseConfig) -> Box, + key_value: Arc, } impl BlockChainDB for RestorationDB { - fn key_value(&self) -> &Arc { + fn key_value(&self) -> &Arc { &self.key_value } @@ -347,7 +365,7 @@ pub fn restoration_db_handler(config: kvdb_rocksdb::DatabaseConfig) -> Box io::Result> { + fn open(&self, db_path: &Path) -> io::Result> { let key_value = Arc::new(kvdb_rocksdb::Database::open(&self.config, &db_path.to_string_lossy())?); let blooms_path = db_path.join("blooms"); let trace_blooms_path = db_path.join("trace_blooms"); @@ -376,7 +394,7 @@ pub fn generate_dummy_blockchain(block_number: u32) -> BlockChain { for block_order in 1..block_number { // Total difficulty is always 0 here. bc.insert_block(&mut batch, encoded::Block::new(create_unverifiable_block(block_order, bc.best_block_hash())), vec![], ExtrasInsert { - fork_choice: ::engines::ForkChoice::New, + fork_choice: ForkChoice::New, is_finalized: false, }); bc.commit(); @@ -394,7 +412,7 @@ pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> BlockChain { for block_order in 1..block_number { // Total difficulty is always 0 here. bc.insert_block(&mut batch, encoded::Block::new(create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None)), vec![], ExtrasInsert { - fork_choice: ::engines::ForkChoice::New, + fork_choice: ForkChoice::New, is_finalized: false, }); bc.commit(); @@ -433,13 +451,13 @@ pub fn get_temp_state_db() -> StateDB { /// Returns sequence of hashes of the dummy blocks pub fn get_good_dummy_block_seq(count: usize) -> Vec { - let test_spec = Spec::new_test(); + 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 test_spec = spec::new_test(); let genesis_gas = test_spec.genesis_header().gas_limit().clone(); let mut rolling_timestamp = start_number as u64 * 10; let mut parent = *parent_hash; @@ -464,7 +482,7 @@ pub fn get_good_dummy_block_fork_seq(start_number: usize, count: usize, parent_h /// 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(); + let test_spec = spec::new_test(); let genesis_gas = test_spec.genesis_header().gas_limit().clone(); block_header.set_gas_limit(genesis_gas); block_header.set_difficulty(U256::from(0x20000)); @@ -485,7 +503,7 @@ pub fn get_good_dummy_block() -> 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(); + let test_spec = spec::new_test(); let genesis_gas = test_spec.genesis_header().gas_limit().clone(); block_header.set_gas_limit(genesis_gas); @@ -493,7 +511,7 @@ pub fn get_bad_state_dummy_block() -> Bytes { block_header.set_timestamp(40); block_header.set_number(1); block_header.set_parent_hash(test_spec.genesis_header().hash()); - block_header.set_state_root(0xbad.into()); + block_header.set_state_root(H256::from_low_u64_be(0xbad)); create_test_block(&block_header) } @@ -511,6 +529,7 @@ impl ChainNotify for TestNotify { ChainMessageType::Consensus(data) => data, ChainMessageType::SignedPrivateTransaction(_, data) => data, ChainMessageType::PrivateTransaction(_, data) => data, + ChainMessageType::PrivateStateRequest(_) => Vec::new(), }; self.messages.write().push(data); } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/test_helpers/test_client.rs similarity index 85% rename from ethcore/src/client/test_client.rs rename to ethcore/src/test_helpers/test_client.rs index 502a9ee72b..ae5bb5b731 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/test_helpers/test_client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,9 +16,11 @@ //! Test client. +use std::str::FromStr; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrder}; use std::sync::Arc; use std::collections::{HashMap, BTreeMap}; +use blockchain::BlockProvider; use std::mem; use blockchain::{TreeRoute, BlockReceipts}; @@ -26,7 +28,7 @@ use bytes::Bytes; use db::{NUM_COLUMNS, COL_STATE}; use ethcore_miner::pool::VerifiedTransaction; use ethereum_types::{H256, U256, Address}; -use ethkey::{Generator, Random}; +use parity_crypto::publickey::{Generator, Random}; use ethtrie; use hash::keccak; use itertools::Itertools; @@ -35,41 +37,51 @@ use kvdb_memorydb; use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; use rustc_hex::FromHex; -use types::transaction::{self, Transaction, LocalizedTransaction, SignedTransaction, Action}; -use types::BlockNumber; -use types::basic_account::BasicAccount; -use types::encoded; -use types::filter::Filter; -use types::header::Header; -use types::log_entry::LocalizedLogEntry; -use types::pruning_info::PruningInfo; -use types::receipt::{Receipt, LocalizedReceipt, TransactionOutcome}; -use types::view; -use types::views::BlockView; -use vm::Schedule; +use types::{ + BlockNumber, + encoded, + engines::epoch::Transition as EpochTransition, + ids::{BlockId, TransactionId, UncleId, TraceId}, + basic_account::BasicAccount, + errors::{EthcoreError as Error, EthcoreResult}, + transaction::{self, Transaction, LocalizedTransaction, SignedTransaction, Action, CallError}, + filter::Filter, + trace_filter::Filter as TraceFilter, + call_analytics::CallAnalytics, + header::Header, + log_entry::LocalizedLogEntry, + pruning_info::PruningInfo, + receipt::{Receipt, LocalizedReceipt, TransactionOutcome}, + view, + views::BlockView, + verification::Unverified, + client_types::{Mode, StateResult}, + blockchain_info::BlockChainInfo, + block_status::BlockStatus, + verification::VerificationQueueInfo as BlockQueueInfo, +}; +use vm::{Schedule, LastHashes}; use block::{OpenBlock, SealedBlock, ClosedBlock}; -use call_contract::{CallContract, RegistryInfo}; +use call_contract::CallContract; +use registrar::RegistrarClient; use client::{ - Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, TransactionInfo, - PrepareOpenBlock, BlockChainClient, BlockChainInfo, BlockStatus, BlockId, Mode, - TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, - ProvingBlockChainClient, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, - Call, StateClient, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, IoClient, - BadBlocks, + ReopenBlock, PrepareOpenBlock, ImportSealedBlock, BroadcastProposalBlock, Call, + EngineInfo, BlockProducer, SealedBlockImporter, +}; +use client_traits::{ + BlockInfo, Nonce, Balance, ChainInfo, TransactionInfo, BlockChainClient, ImportBlock, + AccountData, BlockChain, IoClient, BadBlocks, ScheduleInfo, StateClient, ProvingBlockChainClient, + StateOrBlock, ForceUpdateSealing, TransactionRequest }; -use engines::EthEngine; -use error::{Error, EthcoreResult}; -use executed::CallError; -use executive::Executed; +use engine::Engine; +use machine::executed::Executed; use journaldb; use miner::{self, Miner, MinerService}; -use spec::Spec; -use state::StateInfo; +use spec::{Spec, self}; +use account_state::state::StateInfo; use state_db::StateDB; use trace::LocalizedTrace; -use verification::queue::QueueInfo; -use verification::queue::kind::blocks::Unverified; /// Test client. pub struct TestBlockChainClient { @@ -150,7 +162,7 @@ impl TestBlockChainClient { /// Creates new test client with specified extra data for each block pub fn new_with_extra_data(extra_data: Bytes) -> Self { - let spec = Spec::new_test(); + let spec = spec::new_test(); TestBlockChainClient::new_with_spec_and_extra(spec, extra_data) } @@ -167,9 +179,9 @@ impl TestBlockChainClient { let mut client = TestBlockChainClient { blocks: RwLock::new(HashMap::new()), numbers: RwLock::new(HashMap::new()), - genesis_hash: H256::new(), + genesis_hash: H256::zero(), extra_data: extra_data, - last_hash: RwLock::new(H256::new()), + last_hash: RwLock::new(H256::zero()), difficulty: RwLock::new(spec.genesis_header().difficulty().clone()), balances: RwLock::new(HashMap::new()), nonces: RwLock::new(HashMap::new()), @@ -326,7 +338,7 @@ impl TestBlockChainClient { pub fn corrupt_block_parent(&self, n: BlockNumber) { let hash = self.block_hash(BlockId::Number(n)).unwrap(); let mut header: Header = self.block_header(BlockId::Number(n)).unwrap().decode().expect("decoding failed"); - header.set_parent_hash(H256::from(42)); + header.set_parent_hash(H256::from_low_u64_be(42)); let mut rlp = RlpStream::new_list(3); rlp.append(&header); rlp.append_raw(&::rlp::NULL_RLP, 1); @@ -387,7 +399,7 @@ impl TestBlockChainClient { } pub fn get_temp_state_db() -> StateDB { - let db = kvdb_memorydb::create(NUM_COLUMNS.unwrap_or(0)); + let db = kvdb_memorydb::create(NUM_COLUMNS); let journal_db = journaldb::new(Arc::new(db), journaldb::Algorithm::EarlyMerge, COL_STATE); StateDB::new(journal_db, 1024 * 1024) } @@ -416,7 +428,6 @@ impl PrepareOpenBlock for TestBlockChainClient { gas_range_target, extra_data, false, - None, )?; // TODO [todr] Override timestamp for predictability open_block.set_timestamp(*self.latest_block_timestamp.read()); @@ -432,7 +443,7 @@ impl ScheduleInfo for TestBlockChainClient { impl ImportSealedBlock for TestBlockChainClient { fn import_sealed_block(&self, _block: SealedBlock) -> EthcoreResult { - Ok(H256::default()) + Ok(H256::zero()) } } @@ -522,7 +533,20 @@ impl BlockInfo for TestBlockChainClient { } impl CallContract for TestBlockChainClient { - fn call_contract(&self, _id: BlockId, _address: Address, _data: Bytes) -> Result { Ok(vec![]) } + fn call_contract( + &self, + _block_id: BlockId, + _address: Address, + _data: Bytes + ) -> Result { + Ok(vec![]) + } +} + +impl RegistrarClient for TestBlockChainClient { + fn registrar_address(&self) -> Option
{ + None + } } impl TransactionInfo for TestBlockChainClient { @@ -533,10 +557,6 @@ impl TransactionInfo for TestBlockChainClient { impl BlockChain for TestBlockChainClient {} -impl RegistryInfo for TestBlockChainClient { - fn registry_address(&self, _name: String, _block: BlockId) -> Option
{ None } -} - impl ImportBlock for TestBlockChainClient { fn import_block(&self, unverified: Unverified) -> EthcoreResult { let header = unverified.header; @@ -582,11 +602,15 @@ impl ImportBlock for TestBlockChainClient { } Ok(h) } + + fn import_verified_blocks(&self) -> usize { + unimplemented!("TestClient does not implement import_verified_blocks()") + } } impl Call for TestBlockChainClient { // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + type State = TestState; fn call(&self, _t: &SignedTransaction, _analytics: CallAnalytics, _state: &mut Self::State, _header: &Header) -> Result { self.execution_result.read().clone().unwrap() @@ -605,28 +629,32 @@ impl Call for TestBlockChainClient { } } -impl StateInfo for () { +/// NewType wrapper around `()` to impersonate `State` in trait impls. State will not be used by +/// test client, since all methods that accept state are mocked. +pub struct TestState; + +impl StateInfo for TestState { fn nonce(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } fn balance(&self, _address: &Address) -> ethtrie::Result { unimplemented!() } fn storage_at(&self, _address: &Address, _key: &H256) -> ethtrie::Result { unimplemented!() } fn code(&self, _address: &Address) -> ethtrie::Result>> { unimplemented!() } } + impl StateClient for TestBlockChainClient { - // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + type State = TestState; - fn latest_state(&self) -> Self::State { - () + fn latest_state_and_header(&self) -> (Self::State, Header) { + (TestState, self.best_block_header()) } fn state_at(&self, _id: BlockId) -> Option { - Some(()) + Some(TestState) } } impl EngineInfo for TestBlockChainClient { - fn engine(&self) -> &EthEngine { + fn engine(&self) -> &dyn Engine { unimplemented!() } } @@ -649,8 +677,27 @@ impl BlockChainClient for TestBlockChainClient { self.execution_result.read().clone().unwrap() } - fn replay_block_transactions(&self, _block: BlockId, _analytics: CallAnalytics) -> Result>, CallError> { - Ok(Box::new(self.traces.read().clone().unwrap().into_iter().map(|t| t.transaction_hash.unwrap_or(H256::new())).zip(self.execution_result.read().clone().unwrap().into_iter()))) + fn queue_info(&self) -> BlockQueueInfo { + BlockQueueInfo { + verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed), + unverified_queue_size: 0, + verifying_queue_size: 0, + max_queue_size: 0, + max_mem_use: 0, + mem_used: 0, + } + } + + fn replay_block_transactions(&self, _block: BlockId, _analytics: CallAnalytics) -> Result>, CallError> { + Ok(Box::new( + self.traces + .read() + .clone() + .unwrap() + .into_iter() + .map(|t| t.transaction_hash.unwrap_or_default()) + .zip(self.execution_result.read().clone().unwrap().into_iter()) + )) } fn block_total_difficulty(&self, _id: BlockId) -> Option { @@ -665,25 +712,30 @@ impl BlockChainClient for TestBlockChainClient { None } - fn code(&self, address: &Address, state: StateOrBlock) -> Option> { + fn code(&self, address: &Address, state: StateOrBlock) -> StateResult> { match state { - StateOrBlock::Block(BlockId::Latest) => Some(self.code.read().get(address).cloned()), - _ => None, + StateOrBlock::Block(BlockId::Latest) => StateResult::Some(self.code.read().get(address).cloned()), + _ => StateResult::Missing, } } fn storage_at(&self, address: &Address, position: &H256, state: StateOrBlock) -> Option { match state { - StateOrBlock::Block(BlockId::Latest) => Some(self.storage.read().get(&(address.clone(), position.clone())).cloned().unwrap_or_else(H256::new)), + StateOrBlock::Block(BlockId::Latest) => + Some(self.storage.read().get(&(address.clone(), position.clone())).cloned().unwrap_or_default()), _ => None, } } + fn chain(&self) -> Arc { + unimplemented!() + } + fn list_accounts(&self, _id: BlockId, _after: Option<&Address>, _count: u64) -> Option> { None } - fn list_storage(&self, _id: BlockId, _account: &Address, _after: Option<&H256>, _count: u64) -> Option> { + fn list_storage(&self, _id: BlockId, _account: &Address, _after: Option<&H256>, _count: Option) -> Option> { None } fn transaction(&self, _id: TransactionId) -> Option { @@ -762,7 +814,7 @@ impl BlockChainClient for TestBlockChainClient { // works only if blocks are one after another 1 -> 2 -> 3 fn tree_route(&self, from: &H256, to: &H256) -> Option { Some(TreeRoute { - ancestor: H256::new(), + ancestor: H256::zero(), index: 0, blocks: { let numbers_read = self.numbers.read(); @@ -797,7 +849,7 @@ impl BlockChainClient for TestBlockChainClient { // TODO: returns just hashes instead of node state rlp(?) fn state_data(&self, hash: &H256) -> Option { // starts with 'f' ? - if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") { + if *hash > H256::from_str("f000000000000000000000000000000000000000000000000000000000000000").unwrap() { let mut rlp = RlpStream::new(); rlp.append(&hash.clone()); return Some(rlp.out()); @@ -807,7 +859,7 @@ impl BlockChainClient for TestBlockChainClient { fn block_receipts(&self, hash: &H256) -> Option { // starts with 'f' ? - if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") { + if *hash > H256::from_str("f000000000000000000000000000000000000000000000000000000000000000").unwrap() { let receipt = BlockReceipts::new(vec![Receipt::new( TransactionOutcome::StateRoot(H256::zero()), U256::zero(), @@ -817,24 +869,9 @@ impl BlockChainClient for TestBlockChainClient { None } - fn queue_info(&self) -> QueueInfo { - QueueInfo { - verified_queue_size: self.queue_size.load(AtomicOrder::Relaxed), - unverified_queue_size: 0, - verifying_queue_size: 0, - max_queue_size: 0, - max_mem_use: 0, - mem_used: 0, - } - } - fn clear_queue(&self) { } - fn additional_params(&self) -> BTreeMap { - Default::default() - } - fn filter_traces(&self, _filter: TraceFilter) -> Option> { self.traces.read().clone() } @@ -875,22 +912,26 @@ impl BlockChainClient for TestBlockChainClient { } } - fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> { + fn create_transaction(&self, TransactionRequest { action, data, gas, gas_price, nonce }: TransactionRequest) + -> Result + { let transaction = Transaction { - nonce: self.latest_nonce(&self.miner.authoring_params().author), - action: Action::Call(address), - gas: self.spec.gas_limit, - gas_price: U256::zero(), + nonce: nonce.unwrap_or_else(|| self.latest_nonce(&self.miner.authoring_params().author)), + action, + gas: gas.unwrap_or(self.spec.gas_limit), + gas_price: gas_price.unwrap_or_else(U256::zero), value: U256::default(), data: data, }; let chain_id = Some(self.spec.chain_id()); let sig = self.spec.engine.sign(transaction.hash(chain_id)).unwrap(); - let signed = SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap(); - self.miner.import_own_transaction(self, signed.into()) + Ok(SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap()) } - fn registrar_address(&self) -> Option
{ None } + fn transact(&self, tx_request: TransactionRequest) -> Result<(), transaction::Error> { + let signed = self.create_transaction(tx_request)?; + self.miner.import_own_transaction(self, signed.into()) + } } impl IoClient for TestBlockChainClient { @@ -927,9 +968,9 @@ impl ProvingBlockChainClient for TestBlockChainClient { } } -impl super::traits::EngineClient for TestBlockChainClient { - fn update_sealing(&self) { - self.miner.update_sealing(self) +impl client_traits::EngineClient for TestBlockChainClient { + fn update_sealing(&self, force: ForceUpdateSealing) { + self.miner.update_sealing(self, force) } fn submit_seal(&self, block_hash: H256, seal: Vec) { @@ -941,11 +982,11 @@ impl super::traits::EngineClient for TestBlockChainClient { fn broadcast_consensus_message(&self, _message: Bytes) {} - fn epoch_transition_for(&self, _block_hash: H256) -> Option<::engines::EpochTransition> { + fn epoch_transition_for(&self, _block_hash: H256) -> Option { None } - fn as_full_client(&self) -> Option<&BlockChainClient> { Some(self) } + fn as_full_client(&self) -> Option<&dyn BlockChainClient> { Some(self) } fn block_number(&self, id: BlockId) -> Option { BlockChainClient::block_number(self, id) diff --git a/ethcore/src/tests/blockchain.rs b/ethcore/src/tests/blockchain.rs index 9f2cdbdb9c..c589575053 100644 --- a/ethcore/src/tests/blockchain.rs +++ b/ethcore/src/tests/blockchain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 9086d07e08..634ae7daa2 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,36 +14,47 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::str::FromStr; +use std::str::{FromStr, from_utf8}; use std::sync::Arc; +use account_state::state::StateInfo; use ethereum_types::{U256, Address}; -use ethkey::KeyPair; +use parity_crypto::publickey::KeyPair; use hash::keccak; use io::IoChannel; use tempdir::TempDir; -use types::transaction::{PendingTransaction, Transaction, Action, Condition}; -use types::filter::Filter; -use types::view; -use types::views::BlockView; - -use client::{BlockChainClient, BlockChainReset, Client, ClientConfig, BlockId, ChainInfo, BlockInfo, PrepareOpenBlock, ImportSealedBlock, ImportBlock}; -use ethereum; -use executive::{Executive, TransactOptions}; +use types::{ + data_format::DataFormat, + ids::BlockId, + transaction::{PendingTransaction, Transaction, Action, Condition}, + filter::Filter, + verification::Unverified, + view, + views::BlockView, +}; + +use client::{Client, ClientConfig, PrepareOpenBlock, ImportSealedBlock}; +use client_traits::{ + BlockInfo, BlockChainClient, BlockChainReset, ChainInfo, + ImportExportBlocks, Tick, ImportBlock +}; +use spec; +use stats; +use machine::executive::{Executive, TransactOptions}; use miner::{Miner, PendingOrdering, MinerService}; -use spec::Spec; -use state::{self, State, CleanupMode}; +use account_state::{State, CleanupMode, backend}; use test_helpers::{ self, 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 verification::queue::kind::blocks::Unverified; +use rustc_hex::ToHex; +use registrar::RegistrarClient; #[test] fn imports_from_empty() { let db = test_helpers::new_db(); - let spec = Spec::new_test(); + let spec = spec::new_test(); let client = Client::new( ClientConfig::default(), @@ -52,7 +63,6 @@ fn imports_from_empty() { Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); - client.import_verified_blocks(); client.flush_queue(); } @@ -60,7 +70,7 @@ fn imports_from_empty() { fn should_return_registrar() { let db = test_helpers::new_db(); let tempdir = TempDir::new("").unwrap(); - let spec = ethereum::new_morden(&tempdir.path().to_owned()); + let spec = spec::new_morden(&tempdir.path().to_owned()); let client = Client::new( ClientConfig::default(), @@ -69,17 +79,14 @@ fn should_return_registrar() { Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); - let params = client.additional_params(); - let address = ¶ms["registrar"]; - - assert_eq!(address.len(), 40); - assert!(U256::from_str(address).is_ok()); + let address = client.registrar_address(); + assert_eq!(address, Some("52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d".parse().unwrap())); } #[test] fn returns_state_root_basic() { let client = generate_dummy_client(6); - let test_spec = Spec::new_test(); + let test_spec = spec::new_test(); let genesis_header = test_spec.genesis_header(); assert!(client.state_data(genesis_header.state_root()).is_some()); @@ -88,7 +95,7 @@ fn returns_state_root_basic() { #[test] fn imports_good_block() { let db = test_helpers::new_db(); - let spec = Spec::new_test(); + let spec = spec::new_test(); let client = Client::new( ClientConfig::default(), @@ -102,7 +109,6 @@ fn imports_good_block() { panic!("error importing block being good by definition"); } client.flush_queue(); - client.import_verified_blocks(); let block = client.block_header(BlockId::Number(1)).unwrap(); assert!(!block.into_inner().is_empty()); @@ -111,7 +117,7 @@ fn imports_good_block() { #[test] fn query_none_block() { let db = test_helpers::new_db(); - let spec = Spec::new_test(); + let spec = spec::new_test(); let client = Client::new( ClientConfig::default(), @@ -120,7 +126,7 @@ fn query_none_block() { Arc::new(Miner::new_for_tests(&spec, None)), IoChannel::disconnected(), ).unwrap(); - let non_existant = client.block_header(BlockId::Number(188)); + let non_existant = client.block_header(BlockId::Number(188)); assert!(non_existant.is_none()); } @@ -210,7 +216,7 @@ fn can_generate_gas_price_histogram() { let client = generate_dummy_client_with_data(20, 1, slice_into![6354,8593,6065,4842,7845,7002,689,4958,4250,6098,5804,4320,643,8895,2296,8589,7145,2000,2512,1408]); let hist = client.gas_price_corpus(20).histogram(5).unwrap(); - let correct_hist = ::stats::Histogram { bucket_bounds: vec_into![643, 2294, 3945, 5596, 7247, 8898], counts: vec![4,2,4,6,4] }; + let correct_hist = stats::Histogram { bucket_bounds: vec_into![643, 2294, 3945, 5596, 7247, 8898], counts: vec![4,2,4,6,4] }; assert_eq!(hist, correct_hist); } @@ -251,7 +257,7 @@ fn can_mine() { let dummy_blocks = get_good_dummy_block_seq(2); let client = get_test_client_with_blocks(vec![dummy_blocks[0].clone()]); - let b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap().close().unwrap(); + let b = client.prepare_open_block(Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap().close().unwrap(); assert_eq!(*b.header.parent_hash(), view!(BlockView, &dummy_blocks[0]).header_view().hash()); } @@ -259,7 +265,7 @@ fn can_mine() { #[test] fn change_history_size() { let db = test_helpers::new_db(); - let test_spec = Spec::new_null(); + let test_spec = spec::new_null(); let mut config = ClientConfig::default(); config.history = 2; @@ -274,7 +280,7 @@ fn change_history_size() { ).unwrap(); for _ in 0..20 { - let mut b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let mut b = client.prepare_open_block(Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); b.block_mut().state_mut().add_balance(&address, &5.into(), CleanupMode::NoEmpty).unwrap(); b.block_mut().state_mut().commit().unwrap(); let b = b.close_and_lock().unwrap().seal(&*test_spec.engine, vec![]).unwrap(); @@ -301,7 +307,7 @@ fn does_not_propagate_delayed_transactions() { nonce: 0.into(), gas_price: 0.into(), gas: 21000.into(), - action: Action::Call(Address::default()), + action: Action::Call(Address::zero()), value: 0.into(), data: Vec::new(), }.sign(secret, None), Some(Condition::Number(2))); @@ -309,7 +315,7 @@ fn does_not_propagate_delayed_transactions() { nonce: 1.into(), gas_price: 0.into(), gas: 21000.into(), - action: Action::Call(Address::default()), + action: Action::Call(Address::zero()), value: 0.into(), data: Vec::new(), }.sign(secret, None), None); @@ -327,13 +333,13 @@ fn does_not_propagate_delayed_transactions() { #[test] fn transaction_proof() { - use ::client::ProvingBlockChainClient; + use client_traits::ProvingBlockChainClient; let client = generate_dummy_client(0); let address = Address::random(); - let test_spec = Spec::new_test(); + let test_spec = spec::new_test(); for _ in 0..20 { - let mut b = client.prepare_open_block(Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let mut b = client.prepare_open_block(Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); b.block_mut().state_mut().add_balance(&address, &5.into(), CleanupMode::NoEmpty).unwrap(); b.block_mut().state_mut().commit().unwrap(); let b = b.close_and_lock().unwrap().seal(&*test_spec.engine, vec![]).unwrap(); @@ -344,15 +350,15 @@ fn transaction_proof() { nonce: 0.into(), gas_price: 0.into(), gas: 21000.into(), - action: Action::Call(Address::default()), + action: Action::Call(Address::zero()), value: 5.into(), data: Vec::new(), }.fake_sign(address); let proof = client.prove_transaction(transaction.clone(), BlockId::Latest).unwrap().1; - let backend = state::backend::ProofCheck::new(&proof); + let backend = backend::ProofCheck::new(&proof); - let mut factories = ::factory::Factories::default(); + let mut factories = ::trie_vm_factories::Factories::default(); factories.accountdb = ::account_db::Factory::Plain; // raw state values, no mangled keys. let root = *client.best_block_header().state_root(); @@ -363,7 +369,7 @@ fn transaction_proof() { Executive::new(&mut state, &env_info, &machine, &schedule) .transact(&transaction, TransactOptions::with_no_tracing().dont_check_nonce()).unwrap(); - assert_eq!(state.balance(&Address::default()).unwrap(), 5.into()); + assert_eq!(state.balance(&Address::zero()).unwrap(), 5.into()); assert_eq!(state.balance(&address).unwrap(), 95.into()); } @@ -386,3 +392,79 @@ fn reset_blockchain() { assert!(client.block_header(BlockId::Number(15)).is_some()); } + +#[test] +fn import_export_hex() { + let client = get_test_client_with_blocks(get_good_dummy_block_seq(19)); + let block_rlps: Vec = (15..20) + .filter_map(|num| client.block(BlockId::Number(num))) + .map(|header| { + header.raw().to_hex() + }) + .collect(); + + let mut out = Vec::new(); + + client.export_blocks( + Box::new(&mut out), + BlockId::Number(15), + BlockId::Number(20), + Some(DataFormat::Hex) + ).unwrap(); + + let written = from_utf8(&out) + .unwrap() + .split("\n") + // last line is empty, ignore it. + .take(5) + .collect::>(); + assert_eq!(block_rlps, written); + + assert!(client.reset(5).is_ok()); + client.chain().clear_cache(); + + assert!(client.block_header(BlockId::Number(20)).is_none()); + assert!(client.block_header(BlockId::Number(19)).is_none()); + assert!(client.block_header(BlockId::Number(18)).is_none()); + assert!(client.block_header(BlockId::Number(17)).is_none()); + assert!(client.block_header(BlockId::Number(16)).is_none()); + + client.import_blocks(Box::new(&*out), Some(DataFormat::Hex)).unwrap(); + + assert!(client.block_header(BlockId::Number(20)).is_some()); + assert!(client.block_header(BlockId::Number(19)).is_some()); + assert!(client.block_header(BlockId::Number(18)).is_some()); + assert!(client.block_header(BlockId::Number(17)).is_some()); + assert!(client.block_header(BlockId::Number(16)).is_some()); +} + +#[test] +fn import_export_binary() { + let client = get_test_client_with_blocks(get_good_dummy_block_seq(19)); + + let mut out = Vec::new(); + + client.export_blocks( + Box::new(&mut out), + BlockId::Number(15), + BlockId::Number(20), + Some(DataFormat::Binary) + ).unwrap(); + + assert!(client.reset(5).is_ok()); + client.chain().clear_cache(); + + assert!(client.block_header(BlockId::Number(20)).is_none()); + assert!(client.block_header(BlockId::Number(19)).is_none()); + assert!(client.block_header(BlockId::Number(18)).is_none()); + assert!(client.block_header(BlockId::Number(17)).is_none()); + assert!(client.block_header(BlockId::Number(16)).is_none()); + + client.import_blocks(Box::new(&*out), Some(DataFormat::Binary)).unwrap(); + + assert!(client.block_header(BlockId::Number(19)).is_some()); + assert!(client.block_header(BlockId::Number(18)).is_some()); + assert!(client.block_header(BlockId::Number(20)).is_some()); + assert!(client.block_header(BlockId::Number(17)).is_some()); + assert!(client.block_header(BlockId::Number(16)).is_some()); +} diff --git a/ethcore/src/tests/evm.rs b/ethcore/src/tests/evm.rs index ec0b1dd8e6..f435502f80 100644 --- a/ethcore/src/tests/evm.rs +++ b/ethcore/src/tests/evm.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,10 +18,13 @@ use std::sync::Arc; use hash::keccak; -use vm::{EnvInfo, ActionParams, ActionValue, CallType, ParamsType}; -use evm::{Factory, VMType}; -use executive::Executive; -use state::Substate; +use vm::{EnvInfo, ActionParams, ActionValue, ActionType, ParamsType}; +use evm::Factory; +use machine::{ + executive::Executive, + substate::Substate, + test_helpers::new_eip210_test_machine, +}; use test_helpers::get_temp_state_with_factory; use trace::{NoopVMTracer, NoopTracer}; use types::transaction::SYSTEM_ADDRESS; @@ -32,18 +35,18 @@ use ethereum_types::{H256, Address}; 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 = Arc::new("600143034060205260206020f3".from_hex::>().unwrap()); // this returns previous block hash let get_prev_hash_code_hash = keccak(get_prev_hash_code.as_ref()); // This is same as DEFAULT_BLOCKHASH_CONTRACT except for metropolis transition block check removed. let test_blockhash_contract = "73fffffffffffffffffffffffffffffffffffffffe33141561007a57600143036020526000356101006020510755600061010060205107141561005057600035610100610100602051050761010001555b6000620100006020510714156100755760003561010062010000602051050761020001555b61014a565b4360003512151561009057600060405260206040f35b610100600035430312156100b357610100600035075460605260206060f3610149565b62010000600035430312156100d157600061010060003507146100d4565b60005b156100f6576101006101006000350507610100015460805260206080f3610148565b630100000060003543031215610116576000620100006000350714610119565b60005b1561013c57610100620100006000350507610200015460a052602060a0f3610147565b600060c052602060c0f35b5b5b5b5b"; - let blockhash_contract_code = Arc::new(test_blockhash_contract.from_hex().unwrap()); + let blockhash_contract_code = Arc::new(test_blockhash_contract.from_hex::>().unwrap()); let blockhash_contract_code_hash = keccak(blockhash_contract_code.as_ref()); - let machine = ::ethereum::new_eip210_test_machine(); + let machine = new_eip210_test_machine(); let mut env_info = EnvInfo::default(); // populate state with 256 last hashes let mut state = get_temp_state_with_factory(factory); - let contract_address: Address = 0xf0.into(); + let contract_address = Address::from_low_u64_be(0xf0); state.init_code(&contract_address, (*blockhash_contract_code).clone()).unwrap(); for i in 1 .. 257 { env_info.number = i.into(); @@ -57,8 +60,9 @@ fn test_blockhash_eip210(factory: Factory) { value: ActionValue::Transfer(0.into()), code: Some(blockhash_contract_code.clone()), code_hash: Some(blockhash_contract_code_hash), - data: Some(H256::from(i - 1).to_vec()), - call_type: CallType::Call, + code_version: 0.into(), + data: Some(H256::from_low_u64_be(i - 1).as_bytes().to_vec()), + action_type: ActionType::Call, params_type: ParamsType::Separate, }; let schedule = machine.schedule(env_info.number); @@ -71,17 +75,18 @@ fn test_blockhash_eip210(factory: Factory) { env_info.number = 256; let params = ActionParams { - code_address: Address::new(), - address: Address::new(), - sender: Address::new(), - origin: Address::new(), + code_address: Address::zero(), + address: Address::zero(), + sender: Address::zero(), + origin: Address::zero(), gas: 100000.into(), gas_price: 0.into(), value: ActionValue::Transfer(0.into()), code: Some(get_prev_hash_code), code_hash: Some(get_prev_hash_code_hash), + code_version: 0.into(), data: None, - call_type: CallType::Call, + action_type: ActionType::Call, params_type: ParamsType::Separate, }; let schedule = machine.schedule(env_info.number); @@ -89,10 +94,10 @@ fn test_blockhash_eip210(factory: Factory) { let mut substate = Substate::new(); let res = ex.call(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer); let output = match res { - Ok(res) => H256::from(&res.return_data[..32]), + Ok(res) => H256::from_slice(&res.return_data[..32]), Err(e) => { panic!("Encountered error on getting last hash: {}", e); }, }; - assert_eq!(output, 255.into()); + assert_eq!(output, H256::from_low_u64_be(255)); } diff --git a/ethcore/src/tests/mod.rs b/ethcore/src/tests/mod.rs index ee45c73856..78ef151dc0 100644 --- a/ethcore/src/tests/mod.rs +++ b/ethcore/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/src/tests/trace.rs b/ethcore/src/tests/trace.rs index c14f13cf59..cc4bb096db 100644 --- a/ethcore/src/tests/trace.rs +++ b/ethcore/src/tests/trace.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,30 +16,35 @@ //! Client tests of tracing -use ethkey::KeyPair; +use parity_crypto::publickey::KeyPair; use hash::keccak; use block::*; use ethereum_types::{U256, Address}; use io::*; -use spec::*; -use client::*; +use spec; use test_helpers::get_temp_state_db; -use client::{BlockChainClient, Client, ClientConfig}; +use client::{Client, ClientConfig}; +use client_traits::{BlockChainClient, ImportBlock}; use std::sync::Arc; +use std::str::FromStr; use miner::Miner; -use types::transaction::{Action, Transaction}; use trace::{RewardType, LocalizedTrace}; use trace::trace::Action::Reward; use test_helpers; -use verification::queue::kind::blocks::Unverified; -use types::header::Header; -use types::view; -use types::views::BlockView; +use types::{ + ids::BlockId, + transaction::{Action, Transaction}, + trace_filter::Filter as TraceFilter, + header::Header, + verification::Unverified, + view, + views::BlockView, +}; #[test] fn can_trace_block_and_uncle_reward() { let db = test_helpers::new_db(); - let spec = Spec::new_test_with_reward(); + let spec = spec::new_test_with_reward(); let engine = &*spec.engine; // Create client @@ -71,7 +76,7 @@ fn can_trace_block_and_uncle_reward() { let mut last_header = genesis_header.clone(); last_hashes.push(last_header.hash()); - let kp = KeyPair::from_secret_slice(&keccak("")).unwrap(); + let kp = KeyPair::from_secret_slice(keccak("").as_bytes()).unwrap(); let author = kp.address(); // Add root block first @@ -86,7 +91,6 @@ fn can_trace_block_and_uncle_reward() { (3141562.into(), 31415620.into()), vec![], false, - None, ).unwrap(); rolling_timestamp += 10; root_block.set_timestamp(rolling_timestamp); @@ -115,7 +119,6 @@ fn can_trace_block_and_uncle_reward() { (3141562.into(), 31415620.into()), vec![], false, - None, ).unwrap(); rolling_timestamp += 10; parent_block.set_timestamp(rolling_timestamp); @@ -143,7 +146,6 @@ fn can_trace_block_and_uncle_reward() { (3141562.into(), 31415620.into()), vec![], false, - None, ).unwrap(); rolling_timestamp += 10; block.set_timestamp(rolling_timestamp); @@ -162,7 +164,7 @@ fn can_trace_block_and_uncle_reward() { } let mut uncle = Header::new(); - let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); + let uncle_author = Address::from_str("ef2d6d194084c2de36e0dabfce45d046b37d1106").unwrap(); uncle.set_author(uncle_author); uncle.set_parent_hash(root_header.hash()); uncle.set_gas_limit(genesis_gas); @@ -179,7 +181,6 @@ fn can_trace_block_and_uncle_reward() { block.drain(); client.flush_queue(); - client.import_verified_blocks(); // Test0. Check overall filter let filter = TraceFilter { diff --git a/ethcore/src/verification/canon_verifier.rs b/ethcore/src/verification/canon_verifier.rs deleted file mode 100644 index 03a1c7155f..0000000000 --- a/ethcore/src/verification/canon_verifier.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Canonical verifier. - -use call_contract::CallContract; -use client::BlockInfo; -use engines::EthEngine; -use error::Error; -use types::header::Header; -use super::Verifier; -use super::verification; - -/// A canonial verifier -- this does full verification. -pub struct CanonVerifier; - -impl Verifier for CanonVerifier { - fn verify_block_family( - &self, - header: &Header, - parent: &Header, - engine: &EthEngine, - do_full: Option>, - ) -> Result<(), Error> { - verification::verify_block_family(header, parent, engine, do_full) - } - - fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error> { - verification::verify_block_final(expected, got) - } - - fn verify_block_external(&self, header: &Header, engine: &EthEngine) -> Result<(), Error> { - engine.verify_block_external(header) - } -} diff --git a/ethcore/src/verification/noop_verifier.rs b/ethcore/src/verification/noop_verifier.rs deleted file mode 100644 index d68f1eb885..0000000000 --- a/ethcore/src/verification/noop_verifier.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! No-op verifier. - -use call_contract::CallContract; -use client::BlockInfo; -use engines::EthEngine; -use error::Error; -use types::header::Header; -use super::{verification, Verifier}; - -/// A no-op verifier -- this will verify everything it's given immediately. -#[allow(dead_code)] -pub struct NoopVerifier; - -impl Verifier for NoopVerifier { - fn verify_block_family( - &self, - _: &Header, - _t: &Header, - _: &EthEngine, - _: Option> - ) -> Result<(), Error> { - Ok(()) - } - - fn verify_block_final(&self, _expected: &Header, _got: &Header) -> Result<(), Error> { - Ok(()) - } - - fn verify_block_external(&self, _header: &Header, _engine: &EthEngine) -> Result<(), Error> { - Ok(()) - } -} diff --git a/ethcore/src/verification/verifier.rs b/ethcore/src/verification/verifier.rs deleted file mode 100644 index 76eb60b9a1..0000000000 --- a/ethcore/src/verification/verifier.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! A generic verifier trait. - -use call_contract::CallContract; -use client::BlockInfo; -use engines::EthEngine; -use error::Error; -use types::header::Header; -use super::verification; - -/// Should be used to verify blocks. -pub trait Verifier: Send + Sync - where C: BlockInfo + CallContract -{ - /// Verify a block relative to its parent and uncles. - fn verify_block_family( - &self, - header: &Header, - parent: &Header, - engine: &EthEngine, - do_full: Option> - ) -> Result<(), Error>; - - /// Do a final verification check for an enacted header vs its expected counterpart. - fn verify_block_final(&self, expected: &Header, got: &Header) -> Result<(), Error>; - /// Verify a block, inspecing external state. - fn verify_block_external(&self, header: &Header, engine: &EthEngine) -> Result<(), Error>; -} diff --git a/ethcore/state-db/Cargo.toml b/ethcore/state-db/Cargo.toml new file mode 100644 index 0000000000..1cd738e13c --- /dev/null +++ b/ethcore/state-db/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "state-db" +description = "State database" +authors = ["Parity Technologies "] +license = "GPL-3.0" +version = "0.1.0" +edition = "2018" + +[dependencies] +account-state = { path = "../account-state" } +bloom_journal = { package = "ethcore-bloom-journal", path = "../../util/bloom" } +common-types = { path = "../types"} +ethcore-db = { path = "../db" } +ethereum-types = "0.8.0" +hash-db = "0.15.0" +keccak-hash = "0.4.0" +keccak-hasher = { path = "../../util/keccak-hasher" } +journaldb = { path = "../../util/journaldb" } +kvdb = "0.3.1" +log = "0.4.6" +lru-cache = "0.1.2" +memory-cache = { path = "../../util/memory-cache" } +parking_lot = "0.9" + +[dev-dependencies] +env_logger = "0.5" +# Used for test helpers +ethcore = { path = "..", features = ["test-helpers"] } diff --git a/ethcore/src/state_db.rs b/ethcore/state-db/src/lib.rs similarity index 89% rename from ethcore/src/state_db.rs rename to ethcore/state-db/src/lib.rs index 066a4f6162..20c1d698ad 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/state-db/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,25 +16,25 @@ //! State database abstraction. For more info, see the doc for `StateDB` -use std::collections::{VecDeque, HashSet}; +use std::collections::{HashSet, VecDeque}; use std::io; use std::sync::Arc; -use bloom_journal::{Bloom, BloomJournal}; -use byteorder::{LittleEndian, ByteOrder}; -use db::COL_ACCOUNT_BLOOM; -use ethereum_types::{H256, Address}; -use hash::keccak; +use ethereum_types::{Address, H256}; use hash_db::HashDB; -use journaldb::JournalDB; -use keccak_hasher::KeccakHasher; -use kvdb::{KeyValueDB, DBTransaction, DBValue}; +use keccak_hash::keccak; +use kvdb::{DBTransaction, DBValue, KeyValueDB}; +use log::trace; use lru_cache::LruCache; -use memory_cache::MemoryLruCache; use parking_lot::Mutex; -use types::BlockNumber; -use state::{self, Account}; +use account_state::{self, Account}; +use bloom_journal::{Bloom, BloomJournal}; +use common_types::BlockNumber; +use ethcore_db::COL_ACCOUNT_BLOOM; +use journaldb::JournalDB; +use keccak_hasher::KeccakHasher; +use memory_cache::MemoryLruCache; /// Value used to initialize bloom bitmap size. /// @@ -69,7 +69,7 @@ struct AccountCache { struct CacheQueueItem { /// Account address. address: Address, - /// Acccount data or `None` if account does not exist. + /// Account data or `None` if account does not exist. account: SyncAccount, /// Indicates that the account was modified before being /// added to the cache. @@ -107,7 +107,7 @@ struct BlockChanges { /// `StateDB` is propagated into the global cache. pub struct StateDB { /// Backing database. - db: Box, + db: Box, /// Shared canonical state cache. account_cache: Arc>, /// DB Code cache. Maps code hashes to shared bytes. @@ -126,20 +126,25 @@ pub struct StateDB { commit_number: Option, } -impl StateDB { +impl Clone for StateDB { + fn clone(&self) -> Self { + self.boxed_clone() + } +} +impl StateDB { /// Create a new instance wrapping `JournalDB` and the maximum allowed size /// of the LRU cache in bytes. Actual used memory may (read: will) be higher due to bookkeeping. // TODO: make the cache size actually accurate by moving the account storage cache // into the `AccountCache` structure as its own `LruCache<(Address, H256), H256>`. - pub fn new(db: Box, cache_size: usize) -> StateDB { + pub fn new(db: Box, cache_size: usize) -> StateDB { let bloom = Self::load_bloom(&**db.backing()); let acc_cache_size = cache_size * ACCOUNT_CACHE_RATIO / 100; let code_cache_size = cache_size - acc_cache_size; let cache_items = acc_cache_size / ::std::mem::size_of::>(); StateDB { - db: db, + db, account_cache: Arc::new(Mutex::new(AccountCache { accounts: LruCache::new(cache_items), modifications: VecDeque::new(), @@ -147,7 +152,7 @@ impl StateDB { code_cache: Arc::new(Mutex::new(MemoryLruCache::new(code_cache_size))), local_cache: Vec::new(), account_bloom: Arc::new(Mutex::new(bloom)), - cache_size: cache_size, + cache_size, parent_hash: None, commit_hash: None, commit_number: None, @@ -155,8 +160,8 @@ impl StateDB { } /// Loads accounts bloom from the database - /// This bloom is used to handle request for the non-existant account fast - pub fn load_bloom(db: &KeyValueDB) -> Bloom { + /// This bloom is used to handle request for the non-existent account fast + pub fn load_bloom(db: &dyn KeyValueDB) -> Bloom { let hash_count_entry = db.get(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY) .expect("Low-level database error"); @@ -169,11 +174,15 @@ impl StateDB { let hash_count = hash_count_bytes[0]; let mut bloom_parts = vec![0u64; ACCOUNT_BLOOM_SPACE / 8]; - let mut key = [0u8; 8]; for i in 0..ACCOUNT_BLOOM_SPACE / 8 { - LittleEndian::write_u64(&mut key, i as u64); + let key: [u8; 8] = (i as u64).to_le_bytes(); bloom_parts[i] = db.get(COL_ACCOUNT_BLOOM, &key).expect("low-level database error") - .and_then(|val| Some(LittleEndian::read_u64(&val[..]))) + .map(|val| { + assert_eq!(val.len(), 8, "low-level database error"); + let mut buff = [0u8; 8]; + buff.copy_from_slice(&*val); + u64::from_le_bytes(buff) + }) .unwrap_or(0u64); } @@ -186,12 +195,10 @@ impl StateDB { pub fn commit_bloom(batch: &mut DBTransaction, journal: BloomJournal) -> io::Result<()> { assert!(journal.hash_functions <= 255); batch.put(COL_ACCOUNT_BLOOM, ACCOUNT_BLOOM_HASHCOUNT_KEY, &[journal.hash_functions as u8]); - let mut key = [0u8; 8]; - let mut val = [0u8; 8]; for (bloom_part_index, bloom_part_value) in journal.entries { - LittleEndian::write_u64(&mut key, bloom_part_index as u64); - LittleEndian::write_u64(&mut val, bloom_part_value); + let key: [u8; 8] = (bloom_part_index as u64).to_le_bytes(); + let val: [u8; 8] = bloom_part_value.to_le_bytes(); batch.put(COL_ACCOUNT_BLOOM, &key, &val); } Ok(()) @@ -200,9 +207,9 @@ impl StateDB { /// Journal all recent operations under the given era and ID. pub fn journal_under(&mut self, batch: &mut DBTransaction, now: u64, id: &H256) -> io::Result { { - let mut bloom_lock = self.account_bloom.lock(); - Self::commit_bloom(batch, bloom_lock.drain_journal())?; - } + let mut bloom_lock = self.account_bloom.lock(); + Self::commit_bloom(batch, bloom_lock.drain_journal())?; + } let records = self.db.journal_under(batch, now, id)?; self.commit_hash = Some(id.clone()); self.commit_number = Some(now); @@ -227,7 +234,7 @@ impl StateDB { let cache = &mut *cache; // Purge changes from re-enacted and retracted blocks. - // Filter out commiting block if any. + // Filter out committing block if any. let mut clear = false; for block in enacted.iter().filter(|h| self.commit_hash.as_ref().map_or(true, |p| *h != p)) { clear = clear || { @@ -283,7 +290,7 @@ impl StateDB { if is_best { let acc = account.account.0; if let Some(&mut Some(ref mut existing)) = cache.accounts.get_mut(&account.address) { - if let Some(new) = acc { + if let Some(new) = acc { if account.modified { existing.overwrite_with(new); } @@ -313,12 +320,12 @@ impl StateDB { } /// Conversion method to interpret self as `HashDB` reference - pub fn as_hash_db(&self) -> &HashDB { + pub fn as_hash_db(&self) -> &dyn HashDB { self.db.as_hash_db() } /// Conversion method to interpret self as mutable `HashDB` reference - pub fn as_hash_db_mut(&mut self) -> &mut HashDB { + pub fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self.db.as_hash_db_mut() } @@ -353,8 +360,8 @@ impl StateDB { } /// Check if pruning is enabled on the database. - pub fn is_pruned(&self) -> bool { - self.db.is_pruned() + pub fn is_prunable(&self) -> bool { + self.db.is_prunable() } /// Heap size used. @@ -368,7 +375,7 @@ impl StateDB { } /// Returns underlying `JournalDB`. - pub fn journal_db(&self) -> &JournalDB { + pub fn journal_db(&self) -> &dyn JournalDB { &*self.db } @@ -406,18 +413,18 @@ impl StateDB { } } -impl state::Backend for StateDB { - fn as_hash_db(&self) -> &HashDB { self.db.as_hash_db() } +impl account_state::Backend for StateDB { + fn as_hash_db(&self) -> &dyn HashDB { self.db.as_hash_db() } - fn as_hash_db_mut(&mut self) -> &mut HashDB { + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self.db.as_hash_db_mut() } - fn add_to_account_cache(&mut self, addr: Address, data: Option, modified: bool) { + fn add_to_account_cache(&mut self, address: Address, data: Option, modified: bool) { self.local_cache.push(CacheQueueItem { - address: addr, + address, account: SyncAccount(data), - modified: modified, + modified, }) } @@ -458,13 +465,13 @@ impl state::Backend for StateDB { fn note_non_null_account(&self, address: &Address) { trace!(target: "account_bloom", "Note account bloom: {:?}", address); let mut bloom = self.account_bloom.lock(); - bloom.set(&*keccak(address)); + bloom.set(keccak(address).as_bytes()); } fn is_known_null(&self, address: &Address) -> bool { trace!(target: "account_bloom", "Check account bloom: {:?}", address); let bloom = self.account_bloom.lock(); - let is_null = !bloom.check(&*keccak(address)); + let is_null = !bloom.check(keccak(address).as_bytes()); is_null } } @@ -478,10 +485,11 @@ unsafe impl Sync for SyncAccount {} #[cfg(test)] mod tests { - use ethereum_types::{H256, U256, Address}; + use ethereum_types::{Address, H256, U256}; use kvdb::DBTransaction; - use test_helpers::get_temp_state_db; - use state::{Account, Backend}; + + use account_state::{Account, Backend}; + use ethcore::test_helpers::get_temp_state_db; #[test] fn state_db_smoke() { diff --git a/ethcore/sync/Cargo.toml b/ethcore/sync/Cargo.toml index 66515ef3ba..14e1d758bf 100644 --- a/ethcore/sync/Cargo.toml +++ b/ethcore/sync/Cargo.toml @@ -4,39 +4,44 @@ name = "ethcore-sync" version = "1.12.0" license = "GPL-3.0" authors = ["Parity Technologies "] +edition = "2018" [lib] [dependencies] +bytes = { package = "parity-bytes", version = "0.1" } +client-traits = { path = "../client-traits" } common-types = { path = "../types" } +devp2p = { package = "ethcore-network-devp2p", path = "../../util/network-devp2p" } enum_primitive = "0.1.1" -ethcore = { path = ".." } ethcore-io = { path = "../../util/io" } -ethcore-light = { path = "../light" } -ethcore-network = { path = "../../util/network" } -ethcore-network-devp2p = { path = "../../util/network-devp2p" } -ethereum-types = "0.4" -ethkey = { path = "../../accounts/ethkey" } -ethstore = { path = "../../accounts/ethstore" } +ethcore-private-tx = { path = "../private-tx" } +ethereum-types = "0.8.0" fastmap = { path = "../../util/fastmap" } -hash-db = "0.11.0" -heapsize = "0.4" -keccak-hash = "0.1" -keccak-hasher = { path = "../../util/keccak-hasher" } -kvdb = "0.1" +futures = "0.1" +indexmap = "1.3.0" +keccak-hash = "0.4.0" +light = { package = "ethcore-light", path = "../light" } log = "0.4" macros = { path = "../../util/macros" } -parity-bytes = "0.1" -parking_lot = "0.7" -rand = "0.4" -rlp = { version = "0.3.0", features = ["ethereum"] } +network = { package = "ethcore-network", path = "../../util/network" } +parity-runtime = { path = "../../util/runtime" } +parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-util-mem = "0.3.0" +rand = "0.7" +parking_lot = "0.9" +rlp = "0.4.0" +snapshot = { path = "../snapshot" } trace-time = "0.1" -triehash-ethereum = {version = "0.2", path = "../../util/triehash-ethereum" } +triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" } [dev-dependencies] env_logger = "0.5" +engine = { path = "../engine" } ethcore = { path = "..", features = ["test-helpers"] } ethcore-io = { path = "../../util/io", features = ["mio"] } -ethcore-private-tx = { path = "../private-tx" } -kvdb-memorydb = "0.1" +kvdb-memorydb = "0.3.1" +machine = { path = "../machine" } +rand_xorshift = "0.2" rustc-hex = "1.0" +spec = { path = "../spec" } diff --git a/ethcore/sync/src/api.rs b/ethcore/sync/src/api.rs index 4a66f468d5..61b5f34f29 100644 --- a/ethcore/sync/src/api.rs +++ b/ethcore/sync/src/api.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,39 +19,54 @@ use std::collections::{HashMap, BTreeMap}; use std::io; use std::ops::RangeInclusive; use std::time::Duration; +use std::net::{SocketAddr, AddrParseError}; +use std::str::FromStr; +use std::sync::atomic::{AtomicBool, Ordering}; + +use crate::sync_io::NetSyncIo; +use crate::light_sync::{self, SyncInfo}; +use crate::private_tx::PrivateTxHandler; +use crate::chain::{ + sync_packet::SyncPacket::{PrivateTransactionPacket, SignedPrivateTransactionPacket}, + ChainSyncApi, SyncState, SyncStatus as EthSyncStatus, ETH_PROTOCOL_VERSION_62, + ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, + PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4, +}; + use bytes::Bytes; +use client_traits::{BlockChainClient, ChainNotify}; use devp2p::NetworkService; -use network::{NetworkProtocolHandler, NetworkContext, PeerId, ProtocolId, - NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, Error, ErrorKind, - ConnectionFilter}; -use network::client_version::ClientVersion; - -use types::pruning_info::PruningInfo; +use ethcore_io::TimerToken; +use ethcore_private_tx::PrivateStateDB; use ethereum_types::{H256, H512, U256}; -use io::{TimerToken}; -use ethkey::Secret; -use ethcore::client::{BlockChainClient, ChainNotify, NewBlocks, ChainMessageType}; -use ethcore::snapshot::SnapshotService; -use types::BlockNumber; -use sync_io::NetSyncIo; -use chain::{ChainSyncApi, SyncStatus as EthSyncStatus}; -use std::net::{SocketAddr, AddrParseError}; -use std::str::FromStr; -use parking_lot::{RwLock, Mutex}; -use chain::{ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_62, - PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3}; -use chain::sync_packet::SyncPacket::{PrivateTransactionPacket, SignedPrivateTransactionPacket}; +use parity_crypto::publickey::Secret; +use futures::sync::mpsc as futures_mpsc; +use futures::Stream; use light::client::AsLightClient; use light::Provider; use light::net::{ self as light_net, LightProtocol, Params as LightParams, Capabilities, Handler as LightHandler, EventContext, SampleStore, }; -use network::IpFilter; -use private_tx::PrivateTxHandler; -use types::transaction::UnverifiedTransaction; +use log::{trace, warn}; +use macros::hash_map; +use network::{ + client_version::ClientVersion, + NetworkProtocolHandler, NetworkContext, PeerId, ProtocolId, + NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, Error, + ConnectionFilter, IpFilter, NatType +}; +use snapshot::SnapshotService; +use parking_lot::{RwLock, Mutex}; +use parity_runtime::Executor; +use trace_time::trace_time; +use common_types::{ + BlockNumber, + chain_notify::{NewBlocks, ChainMessageType}, + pruning_info::PruningInfo, + transaction::UnverifiedTransaction, +}; -use super::light_sync::SyncInfo; /// Parity sync protocol pub const WARP_SYNC_PROTOCOL_ID: ProtocolId = *b"par"; @@ -61,7 +76,7 @@ pub const ETH_PROTOCOL: ProtocolId = *b"eth"; pub const LIGHT_PROTOCOL: ProtocolId = *b"pip"; /// Determine warp sync status. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, MallocSizeOf)] pub enum WarpSync { /// Warp sync is enabled. Enabled, @@ -131,6 +146,9 @@ impl Default for SyncConfig { } } +/// receiving end of a futures::mpsc channel +pub type Notification = futures_mpsc::UnboundedReceiver; + /// Current sync status pub trait SyncProvider: Send + Sync { /// Get sync status @@ -142,8 +160,14 @@ pub trait SyncProvider: Send + Sync { /// Get the enode if available. fn enode(&self) -> Option; + /// gets sync status notifications + fn sync_notification(&self) -> Notification; + /// Returns propagation count for pending transactions. fn transactions_stats(&self) -> BTreeMap; + + /// are we in the middle of a major sync? + fn is_major_syncing(&self) -> bool; } /// Transaction stats @@ -206,31 +230,6 @@ impl From for PipProtocolInfo { } } -/// Configuration to attach alternate protocol handlers. -/// Only works when IPC is disabled. -pub struct AttachedProtocol { - /// The protocol handler in question. - pub handler: Arc, - /// 3-character ID for the protocol. - pub protocol_id: ProtocolId, - /// Supported versions and their packet counts. - pub versions: &'static [(u8, u8)], -} - -impl AttachedProtocol { - fn register(&self, network: &NetworkService) { - let res = network.register_protocol( - self.handler.clone(), - self.protocol_id, - self.versions - ); - - if let Err(e) = res { - warn!(target: "sync", "Error attaching protocol {:?}: {:?}", self.protocol_id, e); - } - } -} - /// A prioritized tasks run in a specialised timer. /// Every task should be completed within a hard deadline, /// if it's not it's either cancelled or split into multiple tasks. @@ -266,18 +265,20 @@ impl PriorityTask { pub struct Params { /// Configuration. pub config: SyncConfig, + /// Runtime executor + pub executor: Executor, /// Blockchain client. - pub chain: Arc, + pub chain: Arc, /// Snapshot service. - pub snapshot_service: Arc, + pub snapshot_service: Arc, /// Private tx service. - pub private_tx_handler: Option>, + pub private_tx_handler: Option>, + /// Private state wrapper + pub private_state: Option>, /// Light data provider. - pub provider: Arc<::light::Provider>, + pub provider: Arc, /// Network layer configuration. pub network_config: NetworkConfiguration, - /// Other protocols to attach. - pub attached_protos: Vec, } /// Ethereum network protocol handler @@ -288,21 +289,21 @@ pub struct EthSync { eth_handler: Arc, /// Light (pip) protocol handler light_proto: Option>, - /// Other protocols to attach. - attached_protos: Vec, /// The main subprotocol name subprotocol_name: [u8; 3], /// Light subprotocol name. light_subprotocol_name: [u8; 3], /// Priority tasks notification channel priority_tasks: Mutex>, + /// Track the sync state: are we importing or verifying blocks? + is_major_syncing: Arc } fn light_params( network_id: u64, median_peers: f64, pruning_info: PruningInfo, - sample_store: Option>, + sample_store: Option>, ) -> LightParams { let mut light_params = LightParams { network_id: network_id, @@ -322,7 +323,7 @@ fn light_params( impl EthSync { /// Creates and register protocol with the network service - pub fn new(params: Params, connection_filter: Option>) -> Result, Error> { + pub fn new(params: Params, connection_filter: Option>) -> Result, Error> { let pruning_info = params.chain.pruning_info(); let light_proto = match params.config.serve_light { false => None, @@ -355,6 +356,30 @@ impl EthSync { params.private_tx_handler.as_ref().cloned(), priority_tasks_rx, ); + + let is_major_syncing = Arc::new(AtomicBool::new(false)); + + { + // spawn task that constantly updates EthSync.is_major_sync + let notifications = sync.write().sync_notifications(); + let moved_client = Arc::downgrade(¶ms.chain); + let moved_is_major_syncing = is_major_syncing.clone(); + + params.executor.spawn(notifications.for_each(move |sync_status| { + if let Some(queue_info) = moved_client.upgrade().map(|client| client.queue_info()) { + let is_syncing_state = match sync_status { + SyncState::Idle | SyncState::NewBlocks => false, + _ => true + }; + let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3; + moved_is_major_syncing.store(is_verifying || is_syncing_state, Ordering::SeqCst); + return Ok(()) + } + + // client has been dropped + return Err(()) + })); + } let service = NetworkService::new(params.network_config.clone().into_basic()?, connection_filter)?; let sync = Arc::new(EthSync { @@ -364,12 +389,13 @@ impl EthSync { chain: params.chain, snapshot_service: params.snapshot_service, overlay: RwLock::new(HashMap::new()), + private_state: params.private_state, }), light_proto: light_proto, subprotocol_name: params.config.subprotocol_name, light_subprotocol_name: params.config.light_subprotocol_name, - attached_protos: params.attached_protos, priority_tasks: Mutex::new(priority_tasks_tx), + is_major_syncing }); Ok(sync) @@ -420,6 +446,14 @@ impl SyncProvider for EthSync { fn transactions_stats(&self) -> BTreeMap { self.eth_handler.sync.transactions_stats() } + + fn sync_notification(&self) -> Notification { + self.eth_handler.sync.write().sync_notifications() + } + + fn is_major_syncing(&self) -> bool { + self.is_major_syncing.load(Ordering::SeqCst) + } } const PEERS_TIMER: TimerToken = 0; @@ -432,17 +466,19 @@ pub(crate) const PRIORITY_TIMER_INTERVAL: Duration = Duration::from_millis(250); struct SyncProtocolHandler { /// Shared blockchain client. - chain: Arc, + chain: Arc, /// Shared snapshot service. - snapshot_service: Arc, + snapshot_service: Arc, /// Sync strategy sync: ChainSyncApi, /// Chain overlay used to cache data such as fork block. overlay: RwLock>, + /// Private state db + private_state: Option>, } impl NetworkProtocolHandler for SyncProtocolHandler { - fn initialize(&self, io: &NetworkContext) { + fn initialize(&self, io: &dyn NetworkContext) { if io.subprotocol_name() != WARP_SYNC_PROTOCOL_ID { io.register_timer(PEERS_TIMER, Duration::from_millis(700)).expect("Error registering peers timer"); io.register_timer(MAINTAIN_SYNC_TIMER, Duration::from_millis(1100)).expect("Error registering sync timer"); @@ -453,30 +489,45 @@ impl NetworkProtocolHandler for SyncProtocolHandler { } } - fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { - self.sync.dispatch_packet(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer, packet_id, data); + fn read(&self, io: &dyn NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { + self.sync.dispatch_packet(&mut NetSyncIo::new(io, + &*self.chain, + &*self.snapshot_service, + &self.overlay, + self.private_state.clone()), + *peer, packet_id, data); } - fn connected(&self, io: &NetworkContext, peer: &PeerId) { + fn connected(&self, io: &dyn 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; if warp_protocol == warp_context { - self.sync.write().on_peer_connected(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer); + self.sync.write().on_peer_connected(&mut NetSyncIo::new(io, + &*self.chain, + &*self.snapshot_service, + &self.overlay, + self.private_state.clone()), + *peer); } } - fn disconnected(&self, io: &NetworkContext, peer: &PeerId) { + fn disconnected(&self, io: &dyn 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); + self.sync.write().on_peer_aborting(&mut NetSyncIo::new(io, + &*self.chain, + &*self.snapshot_service, + &self.overlay, + self.private_state.clone()), + *peer); } } - fn timeout(&self, io: &NetworkContext, timer: TimerToken) { + fn timeout(&self, io: &dyn NetworkContext, timer: TimerToken) { trace_time!("sync::timeout"); - let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay); + let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay, self.private_state.clone()); match timer { PEERS_TIMER => self.sync.write().maintain_peers(&mut io), MAINTAIN_SYNC_TIMER => self.sync.write().maintain_sync(&mut io), @@ -507,8 +558,11 @@ impl ChainNotify for EthSync { 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.private_state.clone()); self.eth_handler.sync.write().chain_new_blocks( &mut sync_io, &new_blocks.imported, @@ -543,7 +597,7 @@ impl ChainNotify for EthSync { match self.network.start() { Err((err, listen_address)) => { match err.into() { - ErrorKind::Io(ref e) if e.kind() == io::ErrorKind::AddrInUse => { + Error::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), @@ -555,7 +609,7 @@ impl ChainNotify for EthSync { 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, &[PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3]) + self.network.register_protocol(self.eth_handler.clone(), WARP_SYNC_PROTOCOL_ID, &[PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4]) .unwrap_or_else(|e| warn!("Error registering snapshot sync protocol: {:?}", e)); // register the light protocol. @@ -563,9 +617,6 @@ impl ChainNotify for EthSync { 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)); } - - // register any attached protocols. - for proto in &self.attached_protos { proto.register(&self.network) } } fn stop(&self) { @@ -575,13 +626,19 @@ impl ChainNotify for EthSync { 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); + let mut sync_io = NetSyncIo::new(context, + &*self.eth_handler.chain, + &*self.eth_handler.snapshot_service, + &self.eth_handler.overlay, + self.eth_handler.private_state.clone()); match message_type { ChainMessageType::Consensus(message) => self.eth_handler.sync.write().propagate_consensus_packet(&mut sync_io, message), ChainMessageType::PrivateTransaction(transaction_hash, message) => self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, transaction_hash, PrivateTransactionPacket, message), ChainMessageType::SignedPrivateTransaction(transaction_hash, message) => self.eth_handler.sync.write().propagate_private_transaction(&mut sync_io, transaction_hash, SignedPrivateTransactionPacket, message), + ChainMessageType::PrivateStateRequest(hash) => + self.eth_handler.sync.write().request_private_state(&mut sync_io, &hash), } }); } @@ -594,17 +651,17 @@ impl ChainNotify for EthSync { /// PIP event handler. /// Simply queues transactions from light client peers. -struct TxRelay(Arc); +struct TxRelay(Arc); impl LightHandler for TxRelay { - fn on_transactions(&self, ctx: &EventContext, relay: &[::types::transaction::UnverifiedTransaction]) { + fn on_transactions(&self, ctx: &dyn EventContext, relay: &[UnverifiedTransaction]) { trace!(target: "pip", "Relaying {} transactions from peer {}", relay.len(), ctx.peer()); - self.0.queue_transactions(relay.iter().map(|tx| ::rlp::encode(tx)).collect(), ctx.peer()) + self.0.queue_transactions(relay.iter().map(|tx| rlp::encode(tx)).collect(), ctx.peer()) } } /// Trait for managing network -pub trait ManageNetwork : Send + Sync { +pub trait ManageNetwork: Send + Sync { /// Set to allow unreserved peers to connect fn accept_unreserved_peers(&self); /// Set to deny unreserved peers to connect @@ -620,7 +677,7 @@ pub trait ManageNetwork : Send + Sync { /// Returns the minimum and maximum peers. fn num_peers_range(&self) -> RangeInclusive; /// Get network context for protocol. - fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)); + fn with_proto_context(&self, proto: ProtocolId, f: &mut dyn FnMut(&dyn NetworkContext)); } impl ManageNetwork for EthSync { @@ -646,7 +703,11 @@ impl ManageNetwork for EthSync { fn stop_network(&self) { 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.private_state.clone()); self.eth_handler.sync.write().abort(&mut sync_io); }); @@ -661,7 +722,7 @@ impl ManageNetwork for EthSync { self.network.num_peers_range() } - fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)) { + fn with_proto_context(&self, proto: ProtocolId, f: &mut dyn FnMut(&dyn NetworkContext)) { self.network.with_context_eval(proto, f); } } @@ -681,6 +742,8 @@ pub struct NetworkConfiguration { pub udp_port: Option, /// Enable NAT configuration pub nat_enabled: bool, + /// Nat type + pub nat_type: NatType, /// Enable discovery pub discovery_enabled: bool, /// List of initial node addresses @@ -725,6 +788,7 @@ impl NetworkConfiguration { 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, + nat_type: self.nat_type, discovery_enabled: self.discovery_enabled, boot_nodes: self.boot_nodes, use_secret: self.use_secret, @@ -749,6 +813,7 @@ impl From for NetworkConfiguration { public_address: other.public_address.and_then(|addr| Some(format!("{}", addr))), udp_port: other.udp_port, nat_enabled: other.nat_enabled, + nat_type: other.nat_type, discovery_enabled: other.discovery_enabled, boot_nodes: other.boot_nodes, use_secret: other.use_secret, @@ -821,7 +886,7 @@ pub trait LightSyncInfo: Send + Sync { /// Execute a closure with a protocol context. pub trait LightNetworkDispatcher { /// Execute a closure with a protocol context. - fn with_context(&self, f: F) -> Option where F: FnOnce(&::light::net::BasicContext) -> T; + fn with_context(&self, f: F) -> Option where F: FnOnce(&dyn (::light::net::BasicContext)) -> T; } /// Configuration for the light sync. @@ -835,16 +900,13 @@ pub struct LightSyncParams { /// Subprotocol name. pub subprotocol_name: [u8; 3], /// Other handlers to attach. - pub handlers: Vec>, - /// Other subprotocols to run. - pub attached_protos: Vec, + pub handlers: Vec>, } /// Service for light synchronization. pub struct LightSync { proto: Arc, - sync: Arc, - attached_protos: Vec, + sync: Arc, network: NetworkService, subprotocol_name: [u8; 3], network_id: u64, @@ -887,7 +949,6 @@ impl LightSync { Ok(LightSync { proto: light_proto, sync: sync, - attached_protos: params.attached_protos, network: service, subprotocol_name: params.subprotocol_name, network_id: params.network_id, @@ -896,15 +957,15 @@ impl LightSync { } -impl ::std::ops::Deref for LightSync { - type Target = ::light_sync::SyncInfo; +impl std::ops::Deref for LightSync { + type Target = dyn (light_sync::SyncInfo); fn deref(&self) -> &Self::Target { &*self.sync } } impl LightNetworkDispatcher for LightSync { - fn with_context(&self, f: F) -> Option where F: FnOnce(&::light::net::BasicContext) -> T { + fn with_context(&self, f: F) -> Option where F: FnOnce(&dyn (light::net::BasicContext)) -> T { self.network.with_context_eval( self.subprotocol_name, move |ctx| self.proto.with_context(&ctx, f), @@ -933,7 +994,7 @@ impl ManageNetwork for LightSync { match self.network.start() { Err((err, listen_address)) => { match err.into() { - ErrorKind::Io(ref e) if e.kind() == io::ErrorKind::AddrInUse => { + Error::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), @@ -946,8 +1007,6 @@ impl ManageNetwork for LightSync { 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) } } fn stop_network(&self) { @@ -959,7 +1018,7 @@ impl ManageNetwork for LightSync { self.network.num_peers_range() } - fn with_proto_context(&self, proto: ProtocolId, f: &mut FnMut(&NetworkContext)) { + fn with_proto_context(&self, proto: ProtocolId, f: &mut dyn FnMut(&dyn NetworkContext)) { self.network.with_context_eval(proto, f); } } diff --git a/ethcore/sync/src/block_sync.rs b/ethcore/sync/src/block_sync.rs index 04ffa5f18d..e7b01a260f 100644 --- a/ethcore/sync/src/block_sync.rs +++ b/ethcore/sync/src/block_sync.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,17 +20,24 @@ use std::collections::{HashSet, VecDeque}; use std::cmp; -use heapsize::HeapSizeOf; + +use crate::{ + blocks::{BlockCollection, SyncBody, SyncHeader}, + chain::BlockSet, + sync_io::SyncIo +}; + use ethereum_types::H256; -use rlp::{self, Rlp}; -use types::BlockNumber; -use ethcore::client::{BlockStatus, BlockId}; -use ethcore::error::{ImportErrorKind, QueueErrorKind, BlockError, Error as EthcoreError, ErrorKind as EthcoreErrorKind}; -use sync_io::SyncIo; -use blocks::{BlockCollection, SyncBody, SyncHeader}; -use chain::BlockSet; -use network::PeerId; -use network::client_version::ClientCapabilities; +use log::{debug, trace}; +use network::{client_version::ClientCapabilities, PeerId}; +use rlp::Rlp; +use parity_util_mem::MallocSizeOf; +use common_types::{ + BlockNumber, + block_status::BlockStatus, + ids::BlockId, + errors::{EthcoreError, BlockError, ImportError}, +}; const MAX_HEADERS_TO_REQUEST: usize = 128; const MAX_BODIES_TO_REQUEST_LARGE: usize = 128; @@ -60,7 +67,7 @@ macro_rules! debug_sync { }; } -#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, MallocSizeOf)] /// Downloader state pub enum State { /// No active downloads. @@ -113,6 +120,7 @@ impl From for BlockDownloaderImportError { /// Block downloader strategy. /// Manages state and block data for a block download process. +#[derive(MallocSizeOf)] pub struct BlockDownloader { /// Which set of blocks to download block_set: BlockSet, @@ -223,18 +231,13 @@ impl BlockDownloader { self.state = State::Blocks; } - /// Returns used heap memory size. - pub fn heap_size(&self) -> usize { - self.blocks.heap_size() + self.round_parents.heap_size_of_children() - } - /// Returns best imported block number. pub fn last_imported_block_number(&self) -> BlockNumber { self.last_imported_block } /// Add new block headers. - pub fn import_headers(&mut self, io: &mut SyncIo, r: &Rlp, expected_hash: H256) -> Result { + pub fn import_headers(&mut self, io: &mut dyn SyncIo, r: &Rlp, expected_hash: H256) -> Result { let item_count = r.item_count().unwrap_or(0); if self.state == State::Idle { trace_sync!(self, "Ignored unexpected block headers"); @@ -306,7 +309,7 @@ impl BlockDownloader { } } } - + // Update the highest block number seen on the network from the header. if let Some((number, _)) = last_header { if self.highest_block.as_ref().map_or(true, |n| number > *n) { self.highest_block = Some(number); @@ -419,7 +422,7 @@ impl BlockDownloader { Ok(()) } - fn start_sync_round(&mut self, io: &mut SyncIo) { + fn start_sync_round(&mut self, io: &mut dyn SyncIo) { self.state = State::ChainHead; trace_sync!(self, "Starting round (last imported count = {:?}, last started = {}, block = {:?}", self.imported_this_round, self.last_round_start, self.last_imported_block); // Check if need to retract to find the common block. The problem is that the peers still return headers by hash even @@ -467,7 +470,7 @@ impl BlockDownloader { } /// Find some headers or blocks to download for a peer. - pub fn request_blocks(&mut self, peer_id: PeerId, io: &mut SyncIo, num_active_peers: usize) -> Option { + pub fn request_blocks(&mut self, peer_id: PeerId, io: &mut dyn SyncIo, num_active_peers: usize) -> Option { match self.state { State::Idle => { self.start_sync_round(io); @@ -530,7 +533,7 @@ impl BlockDownloader { /// Checks if there are blocks fully downloaded that can be imported into the blockchain and does the import. /// Returns DownloadAction::Reset if it is imported all the the blocks it can and all downloading peers should be reset - pub fn collect_blocks(&mut self, io: &mut SyncIo, allow_out_of_order: bool) -> DownloadAction { + pub fn collect_blocks(&mut self, io: &mut dyn SyncIo, allow_out_of_order: bool) -> DownloadAction { let mut download_action = DownloadAction::None; let mut imported = HashSet::new(); let blocks = self.blocks.drain(); @@ -552,16 +555,24 @@ impl BlockDownloader { let result = if let Some(receipts) = receipts { io.chain().queue_ancient_block(block, receipts) } else { + trace_sync!(self, "Importing block #{}/{}", number, h); io.chain().import_block(block) }; match result { - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { - trace_sync!(self, "Block already in chain {:?}", h); + Err(EthcoreError::Import(ImportError::AlreadyInChain)) => { + let is_canonical = if io.chain().block_hash(BlockId::Number(number)).is_some() { + "canoncial" + } else { + "not canonical" + }; + trace_sync!(self, "Block #{} is already in chain {:?} – {}", number, h, is_canonical); self.block_imported(&h, number, &parent); }, - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { + Err(EthcoreError::Import(ImportError::AlreadyQueued)) => { trace_sync!(self, "Block already queued {:?}", h); + // Treat blocks in queue as imported in order not to start retraction too early + imported.insert(h.clone()); self.block_imported(&h, number, &parent); }, Ok(_) => { @@ -569,18 +580,18 @@ impl BlockDownloader { imported.insert(h.clone()); self.block_imported(&h, number, &parent); }, - Err(EthcoreError(EthcoreErrorKind::Block(BlockError::UnknownParent(_)), _)) if allow_out_of_order => { + Err(EthcoreError::Block(BlockError::UnknownParent(_))) if allow_out_of_order => { break; }, - Err(EthcoreError(EthcoreErrorKind::Block(BlockError::UnknownParent(_)), _)) => { + Err(EthcoreError::Block(BlockError::UnknownParent(_))) => { trace_sync!(self, "Unknown new block parent, restarting sync"); break; }, - Err(EthcoreError(EthcoreErrorKind::Block(BlockError::TemporarilyInvalid(_)), _)) => { + Err(EthcoreError::Block(BlockError::TemporarilyInvalid(_))) => { debug_sync!(self, "Block temporarily invalid: {:?}, restarting sync", h); break; }, - Err(EthcoreError(EthcoreErrorKind::Queue(QueueErrorKind::Full(limit)), _)) => { + Err(EthcoreError::FullQueue(limit)) => { debug_sync!(self, "Block import queue full ({}), restarting sync", limit); download_action = DownloadAction::Reset; break; @@ -630,18 +641,23 @@ fn all_expected(values: &[A], expected_values: &[B], is_expected: F) -> #[cfg(test)] mod tests { - use super::*; - use ethcore::client::TestBlockChainClient; - use ethcore::spec::Spec; - use ethkey::{Generator,Random}; - use hash::keccak; + use super::{ + BlockSet, BlockDownloader, BlockDownloaderImportError, DownloadAction, SyncIo, H256, + MAX_HEADERS_TO_REQUEST, MAX_USELESS_HEADERS_PER_ROUND, SUBCHAIN_SIZE, State, Rlp, VecDeque + }; + + use crate::tests::{helpers::TestIo, snapshot::TestSnapshotService}; + + use ethcore::test_helpers::TestBlockChainClient; + use parity_crypto::publickey::{Random, Generator}; + use keccak_hash::keccak; use parking_lot::RwLock; - use rlp::{encode_list,RlpStream}; - use tests::helpers::TestIo; - use tests::snapshot::TestSnapshotService; - use types::transaction::{Transaction,SignedTransaction}; + use rlp::{encode_list, RlpStream}; use triehash_ethereum::ordered_trie_root; - use types::header::Header as BlockHeader; + use common_types::{ + transaction::{Transaction, SignedTransaction}, + header::Header as BlockHeader, + }; fn dummy_header(number: u64, parent_hash: H256) -> BlockHeader { let mut header = BlockHeader::new(); @@ -659,7 +675,7 @@ mod tests { Transaction::default().sign(keypair.secret(), None) } - fn import_headers(headers: &[BlockHeader], downloader: &mut BlockDownloader, io: &mut SyncIo) -> Result { + fn import_headers(headers: &[BlockHeader], downloader: &mut BlockDownloader, io: &mut dyn SyncIo) -> Result { let mut stream = RlpStream::new(); stream.append_list(headers); let bytes = stream.out(); @@ -668,16 +684,16 @@ mod tests { downloader.import_headers(io, &rlp, expected_hash) } - fn import_headers_ok(headers: &[BlockHeader], downloader: &mut BlockDownloader, io: &mut SyncIo) { + fn import_headers_ok(headers: &[BlockHeader], downloader: &mut BlockDownloader, io: &mut dyn SyncIo) { let res = import_headers(headers, downloader, io); assert!(res.is_ok()); } #[test] fn import_headers_in_chain_head_state() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); - let spec = Spec::new_test(); + let spec = spec::new_test(); let genesis_hash = spec.genesis_header().hash(); let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &genesis_hash, 0); @@ -686,7 +702,7 @@ mod tests { let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); // Valid headers sequence. let valid_headers = [ @@ -747,12 +763,12 @@ mod tests { #[test] fn import_headers_in_blocks_state() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); let mut headers = Vec::with_capacity(3); let parent_hash = H256::random(); @@ -797,12 +813,12 @@ mod tests { #[test] fn import_bodies() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); // Import block headers. let mut headers = Vec::with_capacity(4); @@ -810,13 +826,13 @@ mod tests { let mut parent_hash = H256::zero(); for i in 0..4 { // Construct the block body - let mut uncles = if i > 0 { + let uncles = if i > 0 { encode_list(&[dummy_header(i - 1, H256::random())]) } else { ::rlp::EMPTY_LIST_RLP.to_vec() }; - let mut txs = encode_list(&[dummy_signed_tx()]); + let txs = encode_list(&[dummy_signed_tx()]); let tx_root = ordered_trie_root(Rlp::new(&txs).iter().map(|r| r.as_raw())); let mut rlp = RlpStream::new_list(2); @@ -865,12 +881,12 @@ mod tests { #[test] fn import_receipts() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); // Import block headers. let mut headers = Vec::with_capacity(4); @@ -881,7 +897,7 @@ mod tests { // // The RLP-encoded integers are clearly not receipts, but the BlockDownloader treats // all receipts as byte blobs, so it does not matter. - let mut receipts_rlp = if i < 2 { + let receipts_rlp = if i < 2 { encode_list(&[0u32]) } else { encode_list(&[i as u32]) @@ -924,9 +940,9 @@ mod tests { #[test] fn reset_after_multiple_sets_of_useless_headers() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); - let spec = Spec::new_test(); + let spec = spec::new_test(); let genesis_hash = spec.genesis_header().hash(); let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &genesis_hash, 0); @@ -935,7 +951,7 @@ mod tests { let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); let heads = [ spec.genesis_header(), @@ -964,9 +980,9 @@ mod tests { #[test] fn dont_reset_after_multiple_sets_of_useless_headers_for_chain_head() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); - let spec = Spec::new_test(); + let spec = spec::new_test(); let genesis_hash = spec.genesis_header().hash(); let mut downloader = BlockDownloader::new(BlockSet::NewBlocks, &genesis_hash, 0); @@ -975,7 +991,7 @@ mod tests { let mut chain = TestBlockChainClient::new(); let snapshot_service = TestSnapshotService::new(); let queue = RwLock::new(VecDeque::new()); - let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None); + let mut io = TestIo::new(&mut chain, &snapshot_service, &queue, None, None); let heads = [ spec.genesis_header() diff --git a/ethcore/sync/src/blocks.rs b/ethcore/sync/src/blocks.rs index 125c8d0b9c..9d0babc16a 100644 --- a/ethcore/sync/src/blocks.rs +++ b/ethcore/sync/src/blocks.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,36 +15,33 @@ // along with Parity Ethereum. If not, see . use std::collections::{HashSet, HashMap, hash_map}; -use hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP}; -use heapsize::HeapSizeOf; -use ethereum_types::H256; -use triehash_ethereum::ordered_trie_root; + use bytes::Bytes; +use ethereum_types::H256; +use keccak_hash::{keccak, KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP}; +use log::{trace, warn}; +use parity_util_mem::MallocSizeOf; use rlp::{Rlp, RlpStream, DecoderError}; -use network; -use ethcore::verification::queue::kind::blocks::Unverified; -use types::transaction::UnverifiedTransaction; -use types::header::Header as BlockHeader; +use triehash_ethereum::ordered_trie_root; +use common_types::{ + transaction::UnverifiedTransaction, + header::Header as BlockHeader, + verification::Unverified, +}; -known_heap_size!(0, HeaderId); +malloc_size_of_is_0!(HeaderId); #[derive(PartialEq, Debug, Clone)] +#[derive(MallocSizeOf)] pub struct SyncHeader { pub bytes: Bytes, pub header: BlockHeader, } -impl HeapSizeOf for SyncHeader { - fn heap_size_of_children(&self) -> usize { - self.bytes.heap_size_of_children() - + self.header.heap_size_of_children() - } -} - impl SyncHeader { pub fn from_rlp(bytes: Bytes) -> Result { let result = SyncHeader { - header: ::rlp::decode(&bytes)?, + header: rlp::decode(&bytes)?, bytes, }; @@ -52,6 +49,7 @@ impl SyncHeader { } } +#[derive(MallocSizeOf)] pub struct SyncBody { pub transactions_bytes: Bytes, pub transactions: Vec, @@ -85,16 +83,8 @@ impl SyncBody { } } -impl HeapSizeOf for SyncBody { - fn heap_size_of_children(&self) -> usize { - self.transactions_bytes.heap_size_of_children() - + self.transactions.heap_size_of_children() - + self.uncles_bytes.heap_size_of_children() - + self.uncles.heap_size_of_children() - } -} - /// Block data with optional body. +#[derive(MallocSizeOf)] struct SyncBlock { header: SyncHeader, body: Option, @@ -102,12 +92,6 @@ struct SyncBlock { receipts_root: H256, } -impl HeapSizeOf for SyncBlock { - fn heap_size_of_children(&self) -> usize { - self.header.heap_size_of_children() + self.body.heap_size_of_children() - } -} - fn unverified_from_sync(header: SyncHeader, body: Option) -> Unverified { let mut stream = RlpStream::new_list(3); stream.append_raw(&header.bytes, 1); @@ -141,7 +125,7 @@ struct HeaderId { /// A collection of blocks and subchain pointers being downloaded. This keeps track of /// which headers/bodies need to be downloaded, which are being downloaded and also holds /// the downloaded blocks. -#[derive(Default)] +#[derive(Default, MallocSizeOf)] pub struct BlockCollection { /// Does this collection need block receipts. need_receipts: bool, @@ -168,18 +152,7 @@ pub struct BlockCollection { impl BlockCollection { /// Create a new instance. pub fn new(download_receipts: bool) -> BlockCollection { - BlockCollection { - need_receipts: download_receipts, - blocks: HashMap::new(), - header_ids: HashMap::new(), - receipt_ids: HashMap::new(), - heads: Vec::new(), - parents: HashMap::new(), - head: None, - downloading_headers: HashSet::new(), - downloading_bodies: HashSet::new(), - downloading_receipts: HashSet::new(), - } + Self { need_receipts: download_receipts, ..Default::default() } } /// Clear everything. @@ -399,16 +372,6 @@ impl BlockCollection { self.heads.len() } - /// Return used heap size. - pub fn heap_size(&self) -> usize { - self.heads.heap_size_of_children() - + self.blocks.heap_size_of_children() - + self.parents.heap_size_of_children() - + self.header_ids.heap_size_of_children() - + self.downloading_headers.heap_size_of_children() - + self.downloading_bodies.heap_size_of_children() - } - /// Check if given block hash is marked as being downloaded. pub fn is_downloading(&self, hash: &H256) -> bool { self.downloading_headers.contains(hash) || self.downloading_bodies.contains(hash) @@ -435,13 +398,13 @@ impl BlockCollection { }, None => { warn!("Got body with no header {}", h); - Err(network::ErrorKind::BadProtocol.into()) + Err(network::Error::BadProtocol) } } } None => { trace!(target: "sync", "Ignored unknown/stale block body. tx_root = {:?}, uncles = {:?}", header_id.transactions_root, header_id.uncles); - Err(network::ErrorKind::BadProtocol.into()) + Err(network::Error::BadProtocol) } } } @@ -463,7 +426,7 @@ impl BlockCollection { }, None => { warn!("Got receipt with no header {}", h); - return Err(network::ErrorKind::BadProtocol.into()) + return Err(network::Error::BadProtocol) } } } @@ -471,7 +434,7 @@ impl BlockCollection { }, hash_map::Entry::Vacant(_) => { trace!(target: "sync", "Ignored unknown/stale block receipt {:?}", receipt_root); - Err(network::ErrorKind::BadProtocol.into()) + Err(network::Error::BadProtocol) } } } @@ -520,7 +483,7 @@ impl BlockCollection { (None, receipt_root) } } else { - (None, H256::new()) + (None, H256::zero()) }; self.parents.insert(*info.header.parent_hash(), hash); @@ -570,10 +533,14 @@ impl BlockCollection { #[cfg(test)] mod test { use super::{BlockCollection, SyncHeader}; - use ethcore::client::{TestBlockChainClient, EachBlockWith, BlockId, BlockChainClient}; - use types::BlockNumber; - use ethcore::verification::queue::kind::blocks::Unverified; - use rlp::*; + use client_traits::BlockChainClient; + use ethcore::test_helpers::{TestBlockChainClient, EachBlockWith}; + use common_types::{ + ids::BlockId, + BlockNumber, + verification::Unverified, + }; + use rlp::Rlp; fn is_empty(bc: &BlockCollection) -> bool { bc.heads.is_empty() && @@ -591,7 +558,7 @@ mod test { assert!(is_empty(&bc)); let client = TestBlockChainClient::new(); client.add_blocks(100, EachBlockWith::Nothing); - let hashes = (0 .. 100).map(|i| (&client as &BlockChainClient).block_hash(BlockId::Number(i)).unwrap()).collect(); + let hashes = (0 .. 100).map(|i| (&client as &dyn BlockChainClient).block_hash(BlockId::Number(i)).unwrap()).collect(); bc.reset_to(hashes); assert!(!is_empty(&bc)); bc.clear(); @@ -606,7 +573,7 @@ mod test { let nblocks = 200; client.add_blocks(nblocks, EachBlockWith::Nothing); let blocks: Vec<_> = (0..nblocks) - .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) + .map(|i| (&client as &dyn BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) .collect(); let headers: Vec<_> = blocks.iter().map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()).collect(); let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); @@ -668,7 +635,7 @@ mod test { let nblocks = 200; client.add_blocks(nblocks, EachBlockWith::Nothing); let blocks: Vec<_> = (0..nblocks) - .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) + .map(|i| (&client as &dyn BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) .collect(); let headers: Vec<_> = blocks.iter().map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()).collect(); let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); @@ -692,7 +659,7 @@ mod test { let nblocks = 200; client.add_blocks(nblocks, EachBlockWith::Nothing); let blocks: Vec<_> = (0..nblocks) - .map(|i| (&client as &BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) + .map(|i| (&client as &dyn BlockChainClient).block(BlockId::Number(i as BlockNumber)).unwrap().into_inner()) .collect(); let headers: Vec<_> = blocks.iter().map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()).collect(); let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); diff --git a/ethcore/sync/src/chain/handler.rs b/ethcore/sync/src/chain/handler.rs index 63ab891613..9716db1514 100644 --- a/ethcore/sync/src/chain/handler.rs +++ b/ethcore/sync/src/chain/handler.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,63 +14,53 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use api::WARP_SYNC_PROTOCOL_ID; -use block_sync::{BlockDownloaderImportError as DownloaderImportError, DownloadAction}; +use std::time::Instant; +use std::{mem, cmp}; + +use crate::{ + snapshot_sync::ChunkType, + sync_io::SyncIo, + api::WARP_SYNC_PROTOCOL_ID, + block_sync::{BlockDownloaderImportError as DownloaderImportError, DownloadAction}, + chain::{ + sync_packet::{ + PacketInfo, + SyncPacket::{ + self, BlockBodiesPacket, BlockHeadersPacket, NewBlockHashesPacket, NewBlockPacket, + PrivateStatePacket, PrivateTransactionPacket, ReceiptsPacket, SignedPrivateTransactionPacket, + SnapshotDataPacket, SnapshotManifestPacket, StatusPacket, + } + }, + 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, PAR_PROTOCOL_VERSION_4, + } +}; + use bytes::Bytes; use enum_primitive::FromPrimitive; -use ethcore::error::{Error as EthcoreError, ErrorKind as EthcoreErrorKind, ImportErrorKind, BlockError}; -use ethcore::snapshot::{ManifestData, RestorationStatus}; -use ethcore::verification::queue::kind::blocks::Unverified; use ethereum_types::{H256, U256}; -use hash::keccak; +use keccak_hash::keccak; use network::PeerId; use network::client_version::ClientVersion; +use log::{debug, trace, error, warn}; use rlp::Rlp; -use snapshot::ChunkType; -use std::time::Instant; -use std::{mem, cmp}; -use sync_io::SyncIo; -use types::BlockNumber; -use types::block_status::BlockStatus; -use types::ids::BlockId; - -use super::sync_packet::{PacketInfo, SyncPacket}; -use super::sync_packet::SyncPacket::{ - StatusPacket, - NewBlockHashesPacket, - BlockHeadersPacket, - BlockBodiesPacket, - NewBlockPacket, - ReceiptsPacket, - SnapshotManifestPacket, - SnapshotDataPacket, - PrivateTransactionPacket, - SignedPrivateTransactionPacket, +use common_types::{ + BlockNumber, + block_status::BlockStatus, + ids::BlockId, + errors::{EthcoreError, ImportError, BlockError}, + verification::Unverified, + snapshot::{ManifestData, RestorationStatus}, }; -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, -}; /// 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]) { + pub fn on_packet(sync: &mut ChainSync, io: &mut dyn SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { let rlp = Rlp::new(data); if let Some(packet_id) = SyncPacket::from_u8(packet_id) { let result = match packet_id { @@ -84,15 +74,16 @@ impl SyncHandler { SnapshotDataPacket => SyncHandler::on_snapshot_data(sync, io, peer, &rlp), PrivateTransactionPacket => SyncHandler::on_private_transaction(sync, io, peer, &rlp), SignedPrivateTransactionPacket => SyncHandler::on_signed_private_transaction(sync, io, peer, &rlp), + PrivateStatePacket => SyncHandler::on_private_state_data(sync, io, peer, &rlp), _ => { - debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id.id()); + trace!(target: "sync", "{}: Unknown packet {}", peer, packet_id.id()); Ok(()) } }; match result { Err(DownloaderImportError::Invalid) => { - debug!(target:"sync", "{} -> Invalid packet {}", peer, packet_id.id()); + trace!(target:"sync", "{} -> Invalid packet {}", peer, packet_id.id()); io.disable_peer(peer); sync.deactivate_peer(io, peer); }, @@ -105,18 +96,18 @@ impl SyncHandler { }, } } else { - debug!(target: "sync", "{}: Unknown packet {}", peer, packet_id); + trace!(target: "sync", "{}: Unknown packet {}", peer, packet_id); } } /// Called when peer sends us new consensus packet - pub fn on_consensus_packet(io: &mut SyncIo, peer_id: PeerId, r: &Rlp) { + pub fn on_consensus_packet(io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) { trace!(target: "sync", "Received consensus packet from {:?}", peer_id); io.chain().queue_consensus_message(r.as_raw().to_vec()); } /// Called by peer when it is disconnecting - pub fn on_peer_aborting(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId) { + pub fn on_peer_aborting(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId) { trace!(target: "sync", "== Disconnecting {}: {}", peer_id, io.peer_version(peer_id)); sync.handshaking_peers.remove(&peer_id); if sync.peers.contains_key(&peer_id) { @@ -126,14 +117,14 @@ impl SyncHandler { 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() + // Check if we are asking other peers for a snapshot manifest as well. If not, + // set our state to initial state (`Idle` or `WaitingPeers`). + let still_seeking_manifest = sync.peers.iter() .filter(|&(id, p)| sync.active_peers.contains(id) && p.asking == PeerAsking::SnapshotManifest) - .next().is_none(); + .next().is_some(); - if still_asking_manifest { + if !still_seeking_manifest { + warn!(target: "snapshot_sync", "The peer we were downloading a snapshot from ({}) went away. Retrying.", peer_id); sync.state = ChainSync::get_init_state(sync.warp_sync, io.chain()); } } @@ -142,7 +133,7 @@ impl SyncHandler { } /// Called when a new peer is connected - pub fn on_peer_connected(sync: &mut ChainSync, io: &mut SyncIo, peer: PeerId) { + pub fn on_peer_connected(sync: &mut ChainSync, io: &mut dyn SyncIo, peer: PeerId) { trace!(target: "sync", "== Connected {}: {}", peer, io.peer_version(peer)); if let Err(e) = sync.send_status(io, peer) { debug!(target:"sync", "Error sending status request: {:?}", e); @@ -153,7 +144,7 @@ impl SyncHandler { } /// 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<(), DownloaderImportError> { + pub fn on_peer_new_block(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { 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(()); @@ -183,10 +174,10 @@ impl SyncHandler { return Err(DownloaderImportError::Invalid); } match io.chain().import_block(block) { - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { + Err(EthcoreError::Import(ImportError::AlreadyInChain)) => { trace!(target: "sync", "New block already in chain {:?}", hash); }, - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { + Err(EthcoreError::Import(ImportError::AlreadyQueued)) => { trace!(target: "sync", "New block already queued {:?}", hash); }, Ok(_) => { @@ -195,7 +186,7 @@ impl SyncHandler { sync.new_blocks.mark_as_known(&hash, number); trace!(target: "sync", "New block queued {:?} ({})", hash, number); }, - Err(EthcoreError(EthcoreErrorKind::Block(BlockError::UnknownParent(p)), _)) => { + Err(EthcoreError::Block(BlockError::UnknownParent(p))) => { unknown = true; trace!(target: "sync", "New block with unknown parent ({:?}) {:?}", p, hash); }, @@ -217,7 +208,7 @@ impl SyncHandler { } /// 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<(), DownloaderImportError> { + pub fn on_peer_new_hashes(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { 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(()); @@ -256,7 +247,7 @@ impl SyncHandler { return Err(DownloaderImportError::Invalid); } match io.chain().block_status(BlockId::Hash(hash.clone())) { - BlockStatus::InChain => { + BlockStatus::InChain => { trace!(target: "sync", "New block hash already in chain {:?}", hash); }, BlockStatus::Queued => { @@ -288,7 +279,7 @@ impl SyncHandler { } /// 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<(), DownloaderImportError> { + fn on_peer_block_bodies(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { sync.clear_peer_download(peer_id); let block_set = sync.peers.get(&peer_id) .and_then(|p| p.block_set) @@ -332,7 +323,7 @@ impl SyncHandler { } } - fn on_peer_fork_header(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { + fn on_peer_fork_header(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { { let peer = sync.peers.get_mut(&peer_id).expect("Is only called when peer is present in peers"); peer.asking = PeerAsking::Nothing; @@ -364,7 +355,7 @@ impl SyncHandler { } /// 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<(), DownloaderImportError> { + fn on_peer_block_headers(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { let is_fork_header_request = match sync.peers.get(&peer_id) { Some(peer) if peer.asking == PeerAsking::ForkHeader => true, _ => false, @@ -380,18 +371,18 @@ impl SyncHandler { 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) { - debug!(target: "sync", "{}: Ignored unexpected headers", peer_id); + trace!(target: "sync", "{}: Ignored unexpected headers", peer_id); return Ok(()); } let expected_hash = match expected_hash { Some(hash) => hash, None => { - debug!(target: "sync", "{}: Ignored unexpected headers (expected_hash is None)", peer_id); + trace!(target: "sync", "{}: Ignored unexpected headers (expected_hash is None)", peer_id); return Ok(()); } }; if !allowed { - debug!(target: "sync", "{}: Ignored unexpected headers (peer not allowed)", peer_id); + trace!(target: "sync", "{}: Ignored unexpected headers (peer not allowed)", peer_id); return Ok(()); } @@ -431,7 +422,7 @@ impl SyncHandler { } /// 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<(), DownloaderImportError> { + fn on_peer_block_receipts(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { sync.clear_peer_download(peer_id); let block_set = sync.peers.get(&peer_id).and_then(|p| p.block_set).unwrap_or(BlockSet::NewBlocks); let allowed = sync.peers.get(&peer_id).map(|p| p.is_allowed()).unwrap_or(false); @@ -473,14 +464,14 @@ impl SyncHandler { } /// 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<(), DownloaderImportError> { + fn on_snapshot_manifest(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "Ignoring snapshot manifest from unconfirmed peer {}", peer_id); + trace!(target: "snapshot_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); + trace!(target: "snapshot_sync", "{}: Ignored unexpected/expired manifest", peer_id); return Ok(()); } @@ -491,10 +482,12 @@ impl SyncHandler { .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); + warn!(target: "snapshot_sync", "{}: Snapshot manifest version not supported: {}", peer_id, manifest.version); return Err(DownloaderImportError::Invalid); } sync.snapshot.reset_to(&manifest, &keccak(manifest_rlp.as_raw())); + debug!(target: "snapshot_sync", "{}: Peer sent a snapshot manifest we can use. Block number #{}, block chunks: {}, state chunks: {}", + peer_id, manifest.block_number, manifest.block_hashes.len(), manifest.state_hashes.len()); io.snapshot_service().begin_restore(manifest); sync.state = SyncState::SnapshotData; @@ -502,14 +495,14 @@ impl SyncHandler { } /// 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<(), DownloaderImportError> { + fn on_snapshot_data(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { if !sync.peers.get(&peer_id).map_or(false, |p| p.can_sync()) { - trace!(target: "sync", "Ignoring snapshot data from unconfirmed peer {}", peer_id); + trace!(target: "snapshot_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); + trace!(target: "snapshot_sync", "{}: Ignored unexpected snapshot data", peer_id); return Ok(()); } @@ -517,39 +510,43 @@ impl SyncHandler { let status = io.snapshot_service().status(); match status { RestorationStatus::Inactive | RestorationStatus::Failed => { - trace!(target: "sync", "{}: Snapshot restoration aborted", peer_id); + trace!(target: "snapshot_sync", "{}: Snapshot restoration status: {:?}", peer_id, status); 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); + debug!(target: "snapshot_sync", "Marking snapshot manifest hash {} as bad", hash); sync.snapshot.note_bad(hash); } sync.snapshot.clear(); return Ok(()); }, - RestorationStatus::Initializing { .. } => { - trace!(target: "warp", "{}: Snapshot restoration is initializing", peer_id); + RestorationStatus::Initializing { .. } => { + trace!(target: "snapshot_sync", "{}: Snapshot restoration is initializing. Can't accept data right now.", peer_id); + return Ok(()); + } + RestorationStatus::Finalizing => { + trace!(target: "snapshot_sync", "{}: Snapshot finalizing restoration. Can't accept data right now.", peer_id); return Ok(()); } RestorationStatus::Ongoing { .. } => { - trace!(target: "sync", "{}: Snapshot restoration is ongoing", peer_id); + trace!(target: "snapshot_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); + trace!(target: "snapshot_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); + trace!(target: "snapshot_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); + trace!(target: "snapshot_sync", "{}: Got bad snapshot chunk", peer_id); io.disconnect_peer(peer_id); return Ok(()); } @@ -564,14 +561,14 @@ impl SyncHandler { } /// Called by peer to report status - fn on_peer_status(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { + fn on_peer_status(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { sync.handshaking_peers.remove(&peer_id); let protocol_version: u8 = r.val_at(0)?; let warp_protocol_version = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer_id); let warp_protocol = warp_protocol_version != 0; let private_tx_protocol = warp_protocol_version >= PAR_PROTOCOL_VERSION_3.0; let peer = PeerInfo { - protocol_version: protocol_version, + protocol_version, network_id: r.val_at(1)?, difficulty: Some(r.val_at(2)?), latest_hash: r.val_at(3)?, @@ -579,6 +576,7 @@ impl SyncHandler { asking: PeerAsking::Nothing, asking_blocks: Vec::new(), asking_hash: None, + asking_private_state: None, ask_time: Instant::now(), last_sent_transactions: Default::default(), last_sent_private_transactions: Default::default(), @@ -599,7 +597,8 @@ impl SyncHandler { latest:{}, \ genesis:{}, \ snapshot:{:?}, \ - private_tx_enabled:{})", + private_tx_enabled:{}, \ + client_version: {})", peer_id, peer.protocol_version, peer.network_id, @@ -607,7 +606,8 @@ impl SyncHandler { peer.latest_hash, peer.genesis, peer.snapshot_number, - peer.private_tx_enabled + peer.private_tx_enabled, + peer.client_version, ); if io.is_expired() { trace!(target: "sync", "Status packet from expired session {}:{}", peer_id, io.peer_version(peer_id)); @@ -629,7 +629,7 @@ impl SyncHandler { } 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 < PAR_PROTOCOL_VERSION_1.0 || peer.protocol_version > PAR_PROTOCOL_VERSION_4.0)) || (!warp_protocol && (peer.protocol_version < ETH_PROTOCOL_VERSION_62.0 || peer.protocol_version > ETH_PROTOCOL_VERSION_63.0)) { trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version); @@ -654,7 +654,7 @@ impl SyncHandler { } /// Called when peer sends us new transactions - pub fn on_peer_transactions(sync: &ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> { + pub fn on_peer_transactions(sync: &ChainSync, io: &mut dyn 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); @@ -678,7 +678,7 @@ impl SyncHandler { } /// Called when peer sends us signed private transaction packet - fn on_signed_private_transaction(sync: &mut ChainSync, _io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { + fn on_signed_private_transaction(sync: &mut ChainSync, _io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { 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(()); @@ -690,7 +690,7 @@ impl SyncHandler { return Ok(()); } }; - trace!(target: "sync", "Received signed private transaction packet from {:?}", peer_id); + trace!(target: "privatetx", "Received signed private transaction packet from {:?}", peer_id); match private_handler.import_signed_private_transaction(r.as_raw()) { Ok(transaction_hash) => { //don't send the packet back @@ -699,14 +699,14 @@ impl SyncHandler { } }, Err(e) => { - trace!(target: "sync", "Ignoring the message, error queueing: {}", e); + trace!(target: "privatetx", "Ignoring the message, error queueing: {}", e); } - } + } Ok(()) } /// Called when peer sends us new private transaction packet - fn on_private_transaction(sync: &mut ChainSync, _io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { + fn on_private_transaction(sync: &mut ChainSync, _io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { 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(()); @@ -718,7 +718,7 @@ impl SyncHandler { return Ok(()); } }; - trace!(target: "sync", "Received private transaction packet from {:?}", peer_id); + trace!(target: "privatetx", "Received private transaction packet from {:?}", peer_id); match private_handler.import_private_transaction(r.as_raw()) { Ok(transaction_hash) => { //don't send the packet back @@ -727,30 +727,81 @@ impl SyncHandler { } }, Err(e) => { - trace!(target: "sync", "Ignoring the message, error queueing: {}", e); + trace!(target: "privatetx", "Ignoring the message, error queueing: {}", e); + } + } + Ok(()) + } + + fn on_private_state_data(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { + 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(()); + } + if !sync.reset_peer_asking(peer_id, PeerAsking::PrivateState) { + trace!(target: "sync", "{}: Ignored unexpected private state data", peer_id); + return Ok(()); + } + let requested_hash = sync.peers.get(&peer_id).and_then(|p| p.asking_private_state); + let requested_hash = match requested_hash { + Some(hash) => hash, + None => { + debug!(target: "sync", "{}: Ignored unexpected private state (requested_hash is None)", peer_id); + return Ok(()); } - } + }; + let private_handler = match sync.private_tx_handler { + Some(ref handler) => handler, + None => { + trace!(target: "sync", "{} Ignoring private tx packet from peer", peer_id); + return Ok(()); + } + }; + trace!(target: "privatetx", "Received private state data packet from {:?}", peer_id); + let private_state_data: Bytes = r.val_at(0)?; + match io.private_state() { + Some(db) => { + // Check hash of the rececived data before submitting it to DB + let received_hash = db.state_hash(&private_state_data).unwrap_or_default(); + if received_hash != requested_hash { + trace!(target: "sync", "{} Ignoring private state data with unexpected hash from peer", peer_id); + return Ok(()); + } + match db.save_state(&private_state_data) { + Ok(hash) => { + if let Err(err) = private_handler.private_state_synced(&hash) { + trace!(target: "privatetx", "Ignoring received private state message, error queueing: {}", err); + } + } + Err(e) => { + error!(target: "privatetx", "Cannot save received private state {:?}", e); + } + } + } + None => { + trace!(target: "sync", "{} Ignoring private tx packet from peer", peer_id); + } + }; 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, + use std::collections::VecDeque; + + use super::{ + super::tests::{dummy_sync_with_peer, get_dummy_block, get_dummy_blocks, get_dummy_hashes}, + SyncHandler }; + use crate::tests::{helpers::TestIo, snapshot::TestSnapshotService}; + + use client_traits::ChainInfo; + use ethcore::test_helpers::{EachBlockWith, TestBlockChainClient}; + use parking_lot::RwLock; + use rlp::Rlp; + #[test] fn handles_peer_new_hashes() { let mut client = TestBlockChainClient::new(); @@ -758,7 +809,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let hashes_data = get_dummy_hashes(); let hashes_rlp = Rlp::new(&hashes_data); @@ -779,7 +830,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let block = Rlp::new(&block_data); @@ -798,7 +849,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let block = Rlp::new(&block_data); @@ -812,7 +863,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let empty_data = vec![]; let block = Rlp::new(&empty_data); @@ -829,7 +880,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let empty_hashes_data = vec![]; let hashes_rlp = Rlp::new(&empty_hashes_data); diff --git a/ethcore/sync/src/chain/mod.rs b/ethcore/sync/src/chain/mod.rs index 4b918bd00c..2b506c04f2 100644 --- a/ethcore/sync/src/chain/mod.rs +++ b/ethcore/sync/src/chain/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -88,36 +88,49 @@ //! All other messages are ignored. mod handler; -pub mod sync_packet; mod propagator; mod requester; mod supplier; +pub mod sync_packet; + use std::sync::{Arc, mpsc}; use std::collections::{HashSet, HashMap, BTreeMap}; use std::cmp; use std::time::{Duration, Instant}; -use hash::keccak; -use heapsize::HeapSizeOf; + +use crate::{ + EthProtocolInfo as PeerInfoDigest, PriorityTask, SyncConfig, WarpSync, WARP_SYNC_PROTOCOL_ID, + api::{Notification, PRIORITY_TIMER_INTERVAL}, + block_sync::{BlockDownloader, DownloadAction}, + sync_io::SyncIo, + snapshot_sync::Snapshot, + transactions_stats::{TransactionsStats, Stats as TransactionStats}, + private_tx::PrivateTxHandler, +}; + +use bytes::Bytes; +use client_traits::BlockChainClient; use ethereum_types::{H256, U256}; use fastmap::{H256FastMap, H256FastSet}; +use futures::sync::mpsc as futures_mpsc; +use keccak_hash::keccak; +use log::{error, trace, debug, warn}; +use network::client_version::ClientVersion; +use network::{self, PeerId, PacketId}; +use parity_util_mem::{MallocSizeOfExt, malloc_size_of_is_0}; use parking_lot::{Mutex, RwLock, RwLockWriteGuard}; -use bytes::Bytes; +use rand::{Rng, seq::SliceRandom}; use rlp::{RlpStream, DecoderError}; -use network::{self, PeerId, PacketId}; -use network::client_version::ClientVersion; -use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockQueueInfo}; -use ethcore::snapshot::{RestorationStatus}; -use sync_io::SyncIo; -use super::{WarpSync, SyncConfig}; -use block_sync::{BlockDownloader, DownloadAction}; -use rand::Rng; -use snapshot::{Snapshot}; -use api::{EthProtocolInfo as PeerInfoDigest, WARP_SYNC_PROTOCOL_ID, PriorityTask}; -use private_tx::PrivateTxHandler; -use transactions_stats::{TransactionsStats, Stats as TransactionStats}; -use types::transaction::UnverifiedTransaction; -use types::BlockNumber; +use common_types::{ + BlockNumber, + ids::BlockId, + transaction::UnverifiedTransaction, + verification::VerificationQueueInfo as BlockQueueInfo, + blockchain_info::BlockChainInfo, + block_status::BlockStatus, + snapshot::RestorationStatus, +}; use self::handler::SyncHandler; use self::sync_packet::{PacketInfo, SyncPacket}; @@ -130,7 +143,7 @@ use self::propagator::SyncPropagator; use self::requester::SyncRequester; pub(crate) use self::supplier::SyncSupplier; -known_heap_size!(0, PeerInfo); +malloc_size_of_is_0!(PeerInfo); pub type PacketDecodeError = DecoderError; @@ -144,10 +157,17 @@ pub const PAR_PROTOCOL_VERSION_1: (u8, u8) = (1, 0x15); 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); +/// 4 version of Parity protocol (private state sync added). +pub const PAR_PROTOCOL_VERSION_4: (u8, u8) = (4, 0x20); pub const MAX_BODIES_TO_SEND: usize = 256; pub const MAX_HEADERS_TO_SEND: usize = 512; +/// Maximum number of "entries" to include in a GetDataNode request. pub const MAX_NODE_DATA_TO_SEND: usize = 1024; +/// Maximum allowed duration for serving a batch GetNodeData request. +const MAX_NODE_DATA_TOTAL_DURATION: Duration = Duration::from_secs(2); +/// Maximum allowed duration for serving a single GetNodeData request. +const MAX_NODE_DATA_SINGLE_DURATION: Duration = Duration::from_millis(100); pub const MAX_RECEIPTS_HEADERS_TO_SEND: usize = 256; const MIN_PEERS_PROPAGATION: usize = 4; const MAX_PEERS_PROPAGATION: usize = 128; @@ -157,27 +177,39 @@ const MAX_NEW_BLOCK_AGE: BlockNumber = 20; // maximal packet size with transactions (cannot be greater than 16MB - protocol limitation). // keep it under 8MB as well, cause it seems that it may result oversized after compression. const MAX_TRANSACTION_PACKET_SIZE: usize = 5 * 1024 * 1024; -// Min number of blocks to be behind for a snapshot sync +// Min number of blocks to be behind the tip for a snapshot sync to be considered useful to us. const SNAPSHOT_RESTORE_THRESHOLD: BlockNumber = 30000; +/// We prefer to sync snapshots that are available from this many peers. If we have not found a +/// snapshot available from `SNAPSHOT_MIN_PEERS` peers within `WAIT_PEERS_TIMEOUT`, then we make do +/// with a single peer to sync from. const SNAPSHOT_MIN_PEERS: usize = 3; - -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); +/// To keep memory from growing uncontrollably we restore chunks as we download them and write them +/// to disk only after we have processed them; we also want to avoid pausing the chunk download too +/// often, so we allow a little bit of leeway here and let the downloading be +/// `MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD` chunks ahead of the restoration. +const MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD: usize = 5; + +/// Time to wait for snapshotting peers to show up with a snapshot we want to use. Beyond this time, +/// a single peer is enough to start downloading. +const WAIT_PEERS_TIMEOUT: Duration = Duration::from_secs(10); +/// Time to wait for a peer to start being useful to us in some form. After this they are +/// disconnected. +const STATUS_TIMEOUT: Duration = Duration::from_secs(10); 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); +/// Max time to wait for the Snapshot Manifest packet to arrive from a peer after it's being asked. const SNAPSHOT_MANIFEST_TIMEOUT: Duration = Duration::from_secs(5); const SNAPSHOT_DATA_TIMEOUT: Duration = Duration::from_secs(120); +const PRIVATE_STATE_TIMEOUT: Duration = Duration::from_secs(120); /// Defines how much time we have to complete priority transaction or block propagation. /// after the deadline is reached the task is considered finished /// (so we might sent only to some part of the peers we originally intended to send to) const PRIORITY_TASK_DEADLINE: Duration = Duration::from_millis(100); -#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, MallocSizeOf)] /// Sync state pub enum SyncState { /// Collecting enough peers to start syncing. @@ -260,7 +292,7 @@ impl SyncStatus { } #[derive(PartialEq, Eq, Debug, Clone)] -/// Peer data type requested +/// Peer data type requested from a peer by us. pub enum PeerAsking { Nothing, ForkHeader, @@ -269,9 +301,10 @@ pub enum PeerAsking { BlockReceipts, SnapshotManifest, SnapshotData, + PrivateState, } -#[derive(PartialEq, Eq, Debug, Clone, Copy)] +#[derive(PartialEq, Eq, Debug, Clone, Copy, MallocSizeOf)] /// Block downloader channel. pub enum BlockSet { /// New blocks better than out best blocks @@ -279,7 +312,7 @@ pub enum BlockSet { /// Missing old blocks OldBlocks, } -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq, Debug)] pub enum ForkConfirmation { /// Fork block confirmation pending. Unconfirmed, @@ -289,7 +322,7 @@ pub enum ForkConfirmation { Confirmed, } -#[derive(Clone)] +#[derive(Clone, Debug)] /// Syncing peer information pub struct PeerInfo { /// eth protocol version @@ -302,12 +335,14 @@ pub struct PeerInfo { latest_hash: H256, /// Peer total difficulty if known difficulty: Option, - /// Type of data currenty being requested from peer. + /// Type of data currently being requested by us from a 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 private state hash + asking_private_state: Option, /// Holds requested snapshot chunk hash if any. asking_snapshot_data: Option, /// Request timestamp @@ -344,6 +379,7 @@ impl PeerInfo { fn reset_asking(&mut self) { self.asking_blocks.clear(); self.asking_hash = None; + self.asking_private_state = None; // mark any pending requests as expired if self.asking != PeerAsking::Nothing && self.is_allowed() { self.expired = true; @@ -358,12 +394,19 @@ impl PeerInfo { #[cfg(not(test))] pub mod random { use rand; - pub fn new() -> rand::ThreadRng { rand::thread_rng() } + pub fn new() -> rand::rngs::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]) } + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + + const RNG_SEED: [u8; 16] = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]; + + pub fn new() -> XorShiftRng { + XorShiftRng::from_seed(RNG_SEED) + } } pub type RlpResponseResult = Result, PacketDecodeError>; @@ -383,8 +426,8 @@ impl ChainSyncApi { /// Creates new `ChainSyncApi` pub fn new( config: SyncConfig, - chain: &BlockChainClient, - private_tx_handler: Option>, + chain: &dyn BlockChainClient, + private_tx_handler: Option>, priority_tasks: mpsc::Receiver, ) -> Self { ChainSyncApi { @@ -410,7 +453,7 @@ impl ChainSyncApi { } /// Returns transactions propagation statistics - pub fn transactions_stats(&self) -> BTreeMap { + pub fn transactions_stats(&self) -> BTreeMap { self.sync.read().transactions_stats() .iter() .map(|(hash, stats)| (*hash, stats.into())) @@ -418,7 +461,7 @@ impl ChainSyncApi { } /// Dispatch incoming requests and responses - pub fn dispatch_packet(&self, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { + pub fn dispatch_packet(&self, io: &mut dyn SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { SyncSupplier::dispatch_packet(&self.sync, io, peer, packet_id, data) } @@ -428,7 +471,8 @@ impl ChainSyncApi { /// /// NOTE This method should only handle stuff that can be canceled and would reach other peers /// by other means. - pub fn process_priority_queue(&self, io: &mut SyncIo) { + /// Called every `PRIORITY_TIMER` (0.25sec) + pub fn process_priority_queue(&self, io: &mut dyn SyncIo) { fn check_deadline(deadline: Instant) -> Option { let now = Instant::now(); if now > deadline { @@ -439,7 +483,7 @@ impl ChainSyncApi { } // deadline to get the task from the queue - let deadline = Instant::now() + ::api::PRIORITY_TIMER_INTERVAL; + let deadline = Instant::now() + PRIORITY_TIMER_INTERVAL; let mut work = || { let task = { let tasks = self.priority_tasks.try_lock_until(deadline)?; @@ -500,7 +544,7 @@ impl ChainSyncApi { // Static methods impl ChainSync { /// 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 { + fn create_new_hashes_rlp(chain: &dyn 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); @@ -535,7 +579,7 @@ impl ChainSync { } /// creates latest block rlp for the given client - fn create_latest_block_rlp(chain: &BlockChainClient) -> Bytes { + fn create_latest_block_rlp(chain: &dyn BlockChainClient) -> Bytes { Self::create_block_rlp( &chain.block(BlockId::Hash(chain.chain_info().best_block_hash)) .expect("Best block always exists").into_inner(), @@ -544,7 +588,7 @@ impl ChainSync { } /// creates given hash block rlp for the given client - fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes { + fn create_new_block_rlp(chain: &dyn BlockChainClient, hash: &H256) -> Bytes { Self::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.") @@ -557,17 +601,31 @@ impl ChainSync { 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.shuffle(&mut random::new()); peers.truncate(count); peers } - fn get_init_state(warp_sync: WarpSync, chain: &BlockChainClient) -> SyncState { + /// Reset the client to its initial state: + /// - if warp sync is enabled, start looking for peers to sync a snapshot from + /// - if `--warp-barrier` is used, ensure we're not synced beyond the barrier and start + /// looking for peers to sync a snapshot from + /// - otherwise, go `Idle`. + fn get_init_state(warp_sync: WarpSync, chain: &dyn 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, + WarpSync::Enabled => { + debug!(target: "sync", "Setting the initial state to `WaitingPeers`. Our best block: #{}; warp_sync: {:?}", best_block, warp_sync); + SyncState::WaitingPeers + }, + WarpSync::OnlyAndAfter(block) if block > best_block => { + debug!(target: "sync", "Setting the initial state to `WaitingPeers`. Our best block: #{}; warp_sync: {:?}", best_block, warp_sync); + SyncState::WaitingPeers + }, + _ => { + debug!(target: "sync", "Setting the initial state to `Idle`. Our best block: #{}", best_block); + SyncState::Idle + }, } } } @@ -582,12 +640,13 @@ enum PeerState { /// Blockchain sync handler. /// See module documentation for more details. +#[derive(MallocSizeOf)] pub struct ChainSync { /// Sync state state: SyncState, /// Last block number for the start of sync starting_block: BlockNumber, - /// Highest block number seen + /// Highest block number seen on the network. highest_block: Option, /// All connected peers peers: Peers, @@ -615,17 +674,21 @@ pub struct ChainSync { /// Enable ancient block downloading download_old_blocks: bool, /// Shared private tx service. - private_tx_handler: Option>, + #[ignore_malloc_size_of = "arc on dyn trait here seems tricky, ignoring"] + private_tx_handler: Option>, /// Enable warp sync. warp_sync: WarpSync, + + #[ignore_malloc_size_of = "mpsc unmettered, ignoring"] + status_sinks: Vec> } impl ChainSync { /// Create a new instance of syncing strategy. pub fn new( config: SyncConfig, - chain: &BlockChainClient, - private_tx_handler: Option>, + chain: &dyn BlockChainClient, + private_tx_handler: Option>, ) -> Self { let chain_info = chain.chain_info(); let best_block = chain.chain_info().best_block_number; @@ -649,12 +712,13 @@ impl ChainSync { transactions_stats: TransactionsStats::default(), private_tx_handler, warp_sync: config.warp_sync, + status_sinks: Vec::new() }; sync.update_targets(chain); sync } - /// Returns synchonization status + /// Returns synchronization status pub fn status(&self) -> SyncStatus { let last_imported_number = self.new_blocks.last_imported_block_number(); SyncStatus { @@ -671,10 +735,7 @@ impl ChainSync { 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(), + mem_used: self.malloc_size_of(), } } @@ -702,17 +763,40 @@ impl ChainSync { } /// Abort all sync activity - pub fn abort(&mut self, io: &mut SyncIo) { + pub fn abort(&mut self, io: &mut dyn SyncIo) { self.reset_and_continue(io); self.peers.clear(); } + /// returns the receiving end of a future::mpsc channel that can + /// be polled for changes to node's SyncState. + pub fn sync_notifications(&mut self) -> Notification { + let (sender, receiver) = futures_mpsc::unbounded(); + self.status_sinks.push(sender); + receiver + } + + /// Notify all subscribers of a new SyncState + fn notify_sync_state(&mut self, state: SyncState) { + // remove any sender whose receiving end has been dropped + self.status_sinks.retain(|sender| { + sender.unbounded_send(state).is_ok() + }); + } + + /// sets a new SyncState + fn set_state(&mut self, state: SyncState) { + self.notify_sync_state(state); + + self.state = state; + } + /// Reset sync. Clear all downloaded data but keep the queue. /// Set sync state to the given state or to the initial state if `None` is provided. - fn reset(&mut self, io: &mut SyncIo, state: Option) { + fn reset(&mut self, io: &mut dyn SyncIo, state: Option) { self.new_blocks.reset(); let chain_info = io.chain().chain_info(); - for (_, ref mut p) in &mut self.peers { + for (_, mut p) in &mut self.peers { if p.block_set != Some(BlockSet::OldBlocks) { p.reset_asking(); if p.difficulty.is_none() { @@ -721,38 +805,43 @@ impl ChainSync { } } } - self.state = state.unwrap_or_else(|| Self::get_init_state(self.warp_sync, io.chain())); + + let warp_sync = self.warp_sync; + + self.set_state(state.unwrap_or_else(|| Self::get_init_state(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) { + pub fn reset_and_continue(&mut self, io: &mut dyn SyncIo) { trace!(target: "sync", "Restarting"); if self.state == SyncState::SnapshotData { - debug!(target:"sync", "Aborting snapshot restore"); + debug!(target:"snapshot_sync", "Aborting snapshot restore"); io.snapshot_service().abort_restore(); } self.snapshot.clear(); + // Passing `None` here means we'll end up in either `SnapshotWaiting` or `Idle` depending on + // the warp sync settings. self.reset(io, None); 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); + fn deactivate_peer(&mut self, _io: &mut dyn SyncIo, peer_id: PeerId) { + debug!(target: "sync", "Deactivating peer {}", peer_id); self.active_peers.remove(&peer_id); } - fn maybe_start_snapshot_sync(&mut self, io: &mut SyncIo) { + /// Decide if we should start downloading a snapshot and from who. Called once per second. + fn maybe_start_snapshot_sync(&mut self, io: &mut dyn 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); + use SyncState::*; + if self.state != WaitingPeers && self.state != Blocks && self.state != Waiting { return; } // Make sure the snapshot block is not too far away from best block and network best block and @@ -760,82 +849,123 @@ impl ChainSync { 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 expected_warp_block = match self.warp_sync { + WarpSync::OnlyAndAfter(warp_block) => { + if our_best_block >= warp_block { + trace!(target: "snapshot_sync", + "Our best block (#{}) is already beyond the warp barrier block (#{})", + our_best_block, warp_block); + return; + } + warp_block + }, + _ => 0, + }; + // Collect snapshot info from peers and check if we can use their snapshots to sync. + let (best_snapshot_block, best_hash, max_peers, snapshot_peers) = { + let mut snapshots = self.peers.iter() + .filter(|&(_, p)| + // filter out expired peers and peers from whom we do not have fork confirmation. + p.is_allowed() && + p.snapshot_number.map_or(false, |sn| + // Snapshot must be sufficiently better than what we have that it's useful to + // sync with it: more than 30k blocks beyond our best block + our_best_block < sn && (sn - our_best_block) > SNAPSHOT_RESTORE_THRESHOLD && + // Snapshot must have been taken after the fork block (if any is configured) + sn > fork_block && + // Snapshot must be greater or equal to the warp barrier, if any + sn >= expected_warp_block + ) + ) + .filter_map(|(p, peer)| { + peer.snapshot_hash.map(|hash| (p, hash)) + .filter(|(_, hash)| !self.snapshot.is_known_bad(&hash) ) + .and_then(|(p, hash)| peer.snapshot_number.map(|n| (*p, n, hash) ) ) + }) + .collect::>(); + + // Sort collection of peers by highest block number. + snapshots.sort_by(|&(_, ref b1, _), &(_, ref b2, _)| b2.cmp(b1) ); let mut snapshot_peers = HashMap::new(); let mut max_peers: usize = 0; let mut best_hash = None; - for (p, hash) in snapshots { + let mut best_snapshot_block = None; + // Of the available snapshots, find the one seeded by the most peers. On a tie, the + // snapshot closest to the tip will be used (unfortunately this is the common case). + for (p, snapshot_block, hash) in snapshots { let peers = snapshot_peers.entry(hash).or_insert_with(Vec::new); - peers.push(*p); + peers.push(p); if peers.len() > max_peers { + trace!(target: "snapshot_sync", "{} is the new best snapshotting peer, has snapshot at block #{}/{}", p, snapshot_block, hash); max_peers = peers.len(); best_hash = Some(hash); + best_snapshot_block = Some(snapshot_block); } } - (best_hash, max_peers, snapshot_peers) + (best_snapshot_block, 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 we've waited long enough (10sec), a single peer will have to be enough for the snapshot sync to start. + let timeout = (self.state == WaitingPeers) && + self.sync_start_time.map_or(false, |t| t.elapsed() > WAIT_PEERS_TIMEOUT); + + + if let (Some(block), Some(hash), Some(peers)) = ( + best_snapshot_block, + best_hash, + best_hash.map_or(None, |h| snapshot_peers.get(&h)) + ) { + trace!(target: "snapshot_sync", "We can sync a snapshot at #{:?}/{:?} from {} peer(s): {:?}", + best_snapshot_block, best_hash, max_peers, snapshot_peers.values()); if max_peers >= SNAPSHOT_MIN_PEERS { - trace!(target: "sync", "Starting confirmed snapshot sync {:?} with {:?}", hash, peers); + debug!(target: "snapshot_sync", "Starting confirmed snapshot sync for a snapshot at #{}/{:?} with peer {:?}", block, hash, peers); self.start_snapshot_sync(io, peers); } else if timeout { - trace!(target: "sync", "Starting unconfirmed snapshot sync {:?} with {:?}", hash, peers); + debug!(target: "snapshot_sync", "Starting unconfirmed snapshot sync for a snapshot at #{}/{:?} with peer {:?}", block, hash, peers); self.start_snapshot_sync(io, peers); + } else { + trace!(target: "snapshot_sync", "Waiting a little more to let more snapshot peers connect.") + } + } else if timeout { + if !self.warp_sync.is_warp_only() { + debug!(target: "snapshot_sync", "Not syncing snapshots (or none found), proceeding with normal sync."); + self.set_state(SyncState::Idle); + self.continue_sync(io); + } else { + warn!(target: "snapshot_sync", "No snapshots currently available at #{}. Try using a smaller value for --warp-barrier", expected_warp_block); } - } 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]) { + /// Start a snapshot with all peers that we are not currently asking something else from. If + /// we're already snapshotting with a peer, set sync state to `SnapshotData` and continue + /// fetching the snapshot. Note that we only ever sync snapshots from one peer so here we send + /// out the request for a manifest to all the peers that have it and start syncing the snapshot + /// with the first that responds. + fn start_snapshot_sync(&mut self, io: &mut dyn 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) { + // When we get a response we call `SyncHandler::on_snapshot_manifest` SyncRequester::request_snapshot_manifest(self, io, *p); } } - self.state = SyncState::SnapshotManifest; - trace!(target: "sync", "New snapshot sync with {:?}", peers); + self.set_state(SyncState::SnapshotManifest); + trace!(target: "snapshot_sync", "New snapshot sync with {:?}", peers); } else { - self.state = SyncState::SnapshotData; - trace!(target: "sync", "Resumed snapshot sync with {:?}", peers); + self.set_state(SyncState::SnapshotData); + trace!(target: "snapshot_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) { + pub fn restart(&mut self, io: &mut dyn 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) { + pub fn update_targets(&mut self, chain: &dyn 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(BlockSet::NewBlocks, &chain.best_block_hash, chain.best_block_number); @@ -854,8 +984,9 @@ impl ChainSync { } } - /// Resume downloading - pub fn continue_sync(&mut self, io: &mut SyncIo) { + /// Resume downloading. + /// Called every `CONTINUE_SYNC_TIMER` (2.5sec) + pub fn continue_sync(&mut self, io: &mut dyn SyncIo) { if self.state == SyncState::Waiting { trace!(target: "sync", "Waiting for the block queue"); } else if self.state == SyncState::SnapshotWaiting { @@ -871,13 +1002,13 @@ impl ChainSync { ).collect(); if peers.len() > 0 { - trace!( + debug!( target: "sync", "Syncing with peers: {} active, {} available, {} total", self.active_peers.len(), peers.len(), self.peers.len() ); - random::new().shuffle(&mut peers); // TODO (#646): sort by rating + peers.shuffle(&mut random::new()); // TODO (#646): sort by rating // prefer peers with higher protocol version peers.sort_by(|&(_, ref v1), &(_, ref v2)| v1.cmp(v2)); @@ -887,16 +1018,15 @@ impl ChainSync { } } - 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()) + 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) { + fn complete_sync(&mut self, io: &mut dyn SyncIo) { trace!(target: "sync", "Sync complete"); self.reset(io, Some(SyncState::Idle)); } @@ -904,11 +1034,11 @@ impl ChainSync { /// Enter waiting state fn pause_sync(&mut self) { trace!(target: "sync", "Block queue full, pausing sync"); - self.state = SyncState::Waiting; + self.set_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) { + fn sync_peer(&mut self, io: &mut dyn SyncIo, peer_id: PeerId, force: bool) { if !self.active_peers.contains(&peer_id) { trace!(target: "sync", "Skipping deactivated peer {}", peer_id); return; @@ -931,13 +1061,14 @@ impl ChainSync { 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 => { + SyncState::WaitingPeers if peer_snapshot_number > 0 => { trace!( - target: "sync", - "Checking snapshot sync: {} vs {} (peer: {})", + target: "snapshot_sync", + "{}: Potential snapshot sync peer; their highest block: #{} vs our highest: #{} (peer: {})", + peer_id, peer_snapshot_number, chain_info.best_block_number, - peer_id + io.peer_enode(peer_id).unwrap_or_else(|| "enode://???".to_string()), ); self.maybe_start_snapshot_sync(io); }, @@ -955,7 +1086,7 @@ impl ChainSync { if let Some(request) = self.new_blocks.request_blocks(peer_id, io, num_active_peers) { SyncRequester::request_blocks(self, io, peer_id, request, BlockSet::NewBlocks); if self.state == SyncState::Idle { - self.state = SyncState::Blocks; + self.set_state(SyncState::Blocks); } return; } @@ -982,17 +1113,18 @@ impl ChainSync { }, SyncState::SnapshotData => { match io.snapshot_service().status() { - RestorationStatus::Ongoing { state_chunks_done, block_chunks_done, .. } => { + RestorationStatus::Ongoing { state_chunks_done, block_chunks_done, state_chunks, block_chunks } => { // Initialize the snapshot if not already done - self.snapshot.initialize(io.snapshot_service()); + self.snapshot.initialize(io.snapshot_service(), block_chunks as usize + state_chunks as usize); 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; + trace!(target: "snapshot_sync", "Snapshot queue full, pausing sync"); + self.set_state(SyncState::SnapshotWaiting); return; } }, - RestorationStatus::Initializing { .. } => { - trace!(target: "warp", "Snapshot is stil initializing."); + RestorationStatus::Initializing { state_chunks, block_chunks, chunks_done } => { + debug!(target: "snapshot_sync", "Snapshot is initializing: state chunks={}, block chunks={}, chunks done={}", + state_chunks, block_chunks, chunks_done); return; }, _ => { @@ -1007,16 +1139,17 @@ impl ChainSync { }, SyncState::SnapshotManifest | //already downloading from other peer SyncState::Waiting | - SyncState::SnapshotWaiting => () + 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. + /// Clear all blocks/headers marked as being downloaded by us from a peer. fn clear_peer_download(&mut self, peer_id: PeerId) { - if let Some(ref peer) = self.peers.get(&peer_id) { + if let Some(peer) = self.peers.get(&peer_id) { match peer.asking { PeerAsking::BlockHeaders => { if let Some(ref hash) = peer.asking_hash { @@ -1049,7 +1182,7 @@ impl ChainSync { } /// 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) { + fn collect_blocks(&mut self, io: &mut dyn SyncIo, block_set: BlockSet) { match block_set { BlockSet::NewBlocks => { if self.new_blocks.collect_blocks(io, self.state == SyncState::NewBlocks) == DownloadAction::Reset { @@ -1094,7 +1227,7 @@ impl ChainSync { peer.expired = false; peer.block_set = None; if peer.asking != asking { - trace!(target:"sync", "Asking {:?} while expected {:?}", peer.asking, asking); + trace!(target:"sync", "{}: Asking {:?} while expected {:?}", peer_id, peer.asking, asking); peer.asking = PeerAsking::Nothing; return false; } else { @@ -1106,7 +1239,7 @@ impl ChainSync { } /// Send Status message - fn send_status(&mut self, io: &mut SyncIo, peer: PeerId) -> Result<(), network::Error> { + fn send_status(&mut self, io: &mut dyn 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 private_tx_protocol = warp_protocol_version >= PAR_PROTOCOL_VERSION_3.0; @@ -1123,18 +1256,21 @@ impl ChainSync { 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())); + let manifest_hash = manifest.map_or(H256::zero(), |m| keccak(m.into_rlp())); packet.append(&manifest_hash); packet.append(&block_number); if private_tx_protocol { packet.append(&self.private_tx_handler.is_some()); } } - packet.complete_unbounded_list(); + packet.finalize_unbounded_list(); io.respond(StatusPacket.id(), packet.out()) } - pub fn maintain_peers(&mut self, io: &mut SyncIo) { + /// Check if any tasks we have on-going with a peer is taking too long (if so, disconnect them). + /// Also checks handshaking peers. + /// Called every `PEERS_TIMER` (0.7sec). + pub fn maintain_peers(&mut self, io: &mut dyn SyncIo) { let tick = Instant::now(); let mut aborting = Vec::new(); for (peer_id, peer) in &self.peers { @@ -1147,9 +1283,10 @@ impl ChainSync { PeerAsking::ForkHeader => elapsed > FORK_HEADER_TIMEOUT, PeerAsking::SnapshotManifest => elapsed > SNAPSHOT_MANIFEST_TIMEOUT, PeerAsking::SnapshotData => elapsed > SNAPSHOT_DATA_TIMEOUT, + PeerAsking::PrivateState => elapsed > PRIVATE_STATE_TIMEOUT, }; if timeout { - debug!(target:"sync", "Timeout {}", peer_id); + debug!(target:"sync", "Peer {} timeout while we were asking them for {:?}; disconnecting.", peer_id, peer.asking); io.disconnect_peer(*peer_id); aborting.push(*peer_id); } @@ -1168,37 +1305,40 @@ impl ChainSync { } } - fn check_resume(&mut self, io: &mut SyncIo) { + fn check_resume(&mut self, io: &mut dyn SyncIo) { match self.state { SyncState::Waiting if !io.chain().queue_info().is_full() => { - self.state = SyncState::Blocks; + self.set_state(SyncState::Blocks); self.continue_sync(io); }, SyncState::SnapshotData => match io.snapshot_service().status() { RestorationStatus::Inactive | RestorationStatus::Failed => { - self.state = SyncState::SnapshotWaiting; + self.set_state(SyncState::SnapshotWaiting); }, - RestorationStatus::Initializing { .. } | RestorationStatus::Ongoing { .. } => (), + RestorationStatus::Initializing { .. } | RestorationStatus::Ongoing { .. } | RestorationStatus::Finalizing => (), }, SyncState::SnapshotWaiting => { match io.snapshot_service().status() { RestorationStatus::Inactive => { - trace!(target:"sync", "Snapshot restoration is complete"); + trace!(target:"snapshot_sync", "Snapshot restoration is complete"); self.restart(io); }, RestorationStatus::Initializing { .. } => { - trace!(target:"sync", "Snapshot restoration is initializing"); + trace!(target:"snapshot_sync", "Snapshot restoration is initializing"); + }, + RestorationStatus::Finalizing { .. } => { + trace!(target:"snapshot_sync", "Snapshot finalizing restoration"); }, 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; + trace!(target:"snapshot_sync", "Resuming snapshot sync"); + self.set_state(SyncState::SnapshotData); self.continue_sync(io); } }, RestorationStatus::Failed => { - trace!(target: "sync", "Snapshot restoration aborted"); - self.state = SyncState::WaitingPeers; + trace!(target: "snapshot_sync", "Snapshot restoration aborted"); + self.set_state(SyncState::WaitingPeers); self.snapshot.clear(); self.continue_sync(io); }, @@ -1250,14 +1390,27 @@ impl ChainSync { ).collect() } - /// Maintain other peers. Send out any new blocks and transactions - pub fn maintain_sync(&mut self, io: &mut SyncIo) { + fn get_private_state_peers(&self) -> Vec { + self.peers.iter().filter_map( + |(id, p)| if p.protocol_version >= PAR_PROTOCOL_VERSION_4.0 + && p.private_tx_enabled + && self.active_peers.contains(id) { + Some(*id) + } else { + None + } + ).collect() + } + + /// Maintain other peers. Send out any new blocks and transactions. Called every + /// `MAINTAIN_SYNC_TIMER` (1.1sec). + pub fn maintain_sync(&mut self, io: &mut dyn 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]) { + pub fn chain_new_blocks(&mut self, io: &mut dyn 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); @@ -1283,22 +1436,23 @@ impl ChainSync { } } - pub fn on_packet(&mut self, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { + pub fn on_packet(&mut self, io: &mut dyn SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { SyncHandler::on_packet(self, io, peer, packet_id, data); } /// Called by peer when it is disconnecting - pub fn on_peer_aborting(&mut self, io: &mut SyncIo, peer: PeerId) { + pub fn on_peer_aborting(&mut self, io: &mut dyn 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) { + pub fn on_peer_connected(&mut self, io: &mut dyn 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) { + /// Propagates new transactions to all peers. + /// Called every `TX_TIMER` (1.3sec). + pub fn propagate_new_transactions(&mut self, io: &mut dyn SyncIo) { let deadline = Instant::now() + Duration::from_millis(500); SyncPropagator::propagate_new_transactions(self, io, || { if deadline > Instant::now() { @@ -1311,33 +1465,52 @@ impl ChainSync { } /// Broadcast consensus message to peers. - pub fn propagate_consensus_packet(&mut self, io: &mut SyncIo, packet: Bytes) { + pub fn propagate_consensus_packet(&mut self, io: &mut dyn 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, transaction_hash: H256, packet_id: SyncPacket, packet: Bytes) { + pub fn propagate_private_transaction(&mut self, io: &mut dyn SyncIo, transaction_hash: H256, packet_id: SyncPacket, packet: Bytes) { SyncPropagator::propagate_private_transaction(self, io, transaction_hash, packet_id, packet); } + + /// Request private state from peers + pub fn request_private_state(&mut self, io: &mut dyn SyncIo, hash: &H256) { + let private_state_peers = self.get_private_state_peers(); + if private_state_peers.is_empty() { + error!(target: "privatetx", "Cannot request private state, no peers with private tx enabled available"); + } else { + trace!(target: "privatetx", "Requesting private stats from {:?}", private_state_peers); + for peer_id in private_state_peers { + SyncRequester::request_private_state(self, io, peer_id, hash); + } + } + } } #[cfg(test)] pub mod tests { - use std::collections::{VecDeque}; - use ethkey; - use network::PeerId; - use tests::helpers::{TestIo}; - use tests::snapshot::TestSnapshotService; + use std::{collections::VecDeque, time::Instant}; + + use super::{ + BlockId, BlockQueueInfo, ChainSync, ClientVersion, PeerInfo, PeerAsking, + SyncHandler, SyncState, SyncStatus, SyncPropagator, UnverifiedTransaction + }; + + use crate::{ + api::SyncConfig, + tests::{helpers::TestIo, snapshot::TestSnapshotService}, + }; + + use bytes::Bytes; + use client_traits::{BlockInfo, BlockChainClient, ChainInfo}; + use ethcore::test_helpers::{EachBlockWith, TestBlockChainClient}; + use ethcore::miner::{MinerService, PendingOrdering}; use ethereum_types::{H256, U256, Address}; + use network::PeerId; use parking_lot::RwLock; - use bytes::Bytes; use rlp::{Rlp, RlpStream}; - use super::*; - use ::SyncConfig; - use super::{PeerInfo, PeerAsking}; - use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient, ChainInfo, BlockInfo}; - use ethcore::miner::{MinerService, PendingOrdering}; - use types::header::Header; + use common_types::header::Header; pub fn get_dummy_block(order: u32, parent_hash: H256) -> Bytes { let mut header = Header::new(); @@ -1367,7 +1540,7 @@ pub mod tests { 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 hash: H256 = H256::zero(); let diff: U256 = U256::from(1u64); hash_d_rlp.append(&hash); hash_d_rlp.append(&diff); @@ -1420,8 +1593,9 @@ pub mod tests { 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, None); + pub fn dummy_sync_with_peer(peer_latest_hash: H256, client: &dyn BlockChainClient) -> ChainSync { + + let mut sync = ChainSync::new(SyncConfig::default(), client, None,); insert_dummy_peer(&mut sync, 0, peer_latest_hash); sync } @@ -1437,6 +1611,7 @@ pub mod tests { asking: PeerAsking::Nothing, asking_blocks: Vec::new(), asking_hash: None, + asking_private_state: None, ask_time: Instant::now(), last_sent_transactions: Default::default(), last_sent_private_transactions: Default::default(), @@ -1490,7 +1665,7 @@ pub mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peers = sync.get_lagging_peers(&chain_info); SyncPropagator::propagate_new_hashes(&mut sync, &chain_info, &mut io, &peers); @@ -1510,7 +1685,7 @@ pub mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peers = sync.get_lagging_peers(&chain_info); SyncPropagator::propagate_blocks(&mut sync, &chain_info, &mut io, &[], &peers); @@ -1523,7 +1698,7 @@ pub mod tests { #[test] fn should_add_transactions_to_queue() { fn sender(tx: &UnverifiedTransaction) -> Address { - ethkey::public_to_address(&tx.recover_public().unwrap()) + parity_crypto::publickey::public_to_address(&tx.recover_public().unwrap()) } // given @@ -1539,7 +1714,7 @@ pub mod tests { // 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]);; + 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)); } @@ -1548,7 +1723,7 @@ pub mod tests { { let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, 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); @@ -1561,7 +1736,7 @@ pub mod tests { { let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&client, &ss, &queue, None); + let mut io = TestIo::new(&client, &ss, &queue, None, None); io.chain.miner.chain_new_blocks(io.chain, &[], &[], &good_blocks, &retracted_blocks, false); sync.chain_new_blocks(&mut io, &[], &[], &good_blocks, &retracted_blocks, &[], &[]); } @@ -1584,7 +1759,7 @@ pub mod tests { let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); // when sync.chain_new_blocks(&mut io, &[], &[], &[], &good_blocks, &[], &[]); @@ -1596,4 +1771,3 @@ pub mod tests { assert_eq!(status.status.transaction_count, 0); } } - diff --git a/ethcore/sync/src/chain/propagator.rs b/ethcore/sync/src/chain/propagator.rs index c3654553ff..2066b7f657 100644 --- a/ethcore/sync/src/chain/propagator.rs +++ b/ethcore/sync/src/chain/propagator.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,19 +17,18 @@ use std::cmp; use std::collections::HashSet; +use crate::{sync_io::SyncIo, chain::sync_packet::SyncPacket}; + use bytes::Bytes; use ethereum_types::H256; use fastmap::H256FastSet; +use log::{debug, error, trace}; use network::client_version::ClientCapabilities; use network::PeerId; -use rand::Rng; +use rand::RngCore; use rlp::{Encodable, RlpStream}; -use sync_io::SyncIo; -use types::transaction::SignedTransaction; -use types::BlockNumber; -use types::blockchain_info::BlockChainInfo; +use common_types::{blockchain_info::BlockChainInfo, transaction::SignedTransaction, BlockNumber}; -use super::sync_packet::SyncPacket; use super::sync_packet::SyncPacket::{ NewBlockHashesPacket, TransactionsPacket, @@ -51,10 +50,10 @@ 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 { + pub fn propagate_blocks(sync: &mut ChainSync, chain_info: &BlockChainInfo, io: &mut dyn SyncIo, blocks: &[H256], peers: &[PeerId]) -> usize { trace!(target: "sync", "Sending NewBlocks to {:?}", peers); let sent = peers.len(); - let mut send_packet = |io: &mut SyncIo, rlp: Bytes| { + let mut send_packet = |io: &mut dyn SyncIo, rlp: Bytes| { for peer_id in peers { SyncPropagator::send_packet(io, *peer_id, NewBlockPacket, rlp.clone()); @@ -78,7 +77,7 @@ impl SyncPropagator { } /// propagates new known hashes to all peers - pub fn propagate_new_hashes(sync: &mut ChainSync, chain_info: &BlockChainInfo, io: &mut SyncIo, peers: &[PeerId]) -> usize { + pub fn propagate_new_hashes(sync: &mut ChainSync, chain_info: &BlockChainInfo, io: &mut dyn SyncIo, peers: &[PeerId]) -> usize { trace!(target: "sync", "Sending NewHashes to {:?}", peers); let last_parent = *io.chain().best_block_header().parent_hash(); let best_block_hash = chain_info.best_block_hash; @@ -98,7 +97,7 @@ impl SyncPropagator { } /// propagates new transactions to all peers - pub fn propagate_new_transactions bool>(sync: &mut ChainSync, io: &mut SyncIo, mut should_continue: F) -> usize { + pub fn propagate_new_transactions bool>(sync: &mut ChainSync, io: &mut dyn SyncIo, mut should_continue: F) -> usize { // Early out if nobody to send to. if sync.peers.is_empty() { return 0; @@ -141,7 +140,7 @@ impl SyncPropagator { fn propagate_transactions_to_peers bool>( sync: &mut ChainSync, - io: &mut SyncIo, + io: &mut dyn SyncIo, peers: Vec, transactions: Vec<&SignedTransaction>, mut should_continue: F, @@ -158,7 +157,7 @@ impl SyncPropagator { // Clear old transactions from stats sync.transactions_stats.retain(&all_transactions_hashes); - let send_packet = |io: &mut SyncIo, peer_id: PeerId, sent: usize, rlp: Bytes| { + let send_packet = |io: &mut dyn SyncIo, peer_id: PeerId, sent: usize, rlp: Bytes| { let size = rlp.len(); SyncPropagator::send_packet(io, peer_id, TransactionsPacket, rlp); trace!(target: "sync", "{:02} <- Transactions ({} entries; {} bytes)", peer_id, sent, size); @@ -223,7 +222,7 @@ impl SyncPropagator { pushed += 1; } } - packet.complete_unbounded_list(); + packet.finalize_unbounded_list(); (packet, to_send) }; @@ -249,7 +248,7 @@ impl SyncPropagator { sent_to_peers } - pub fn propagate_latest_blocks(sync: &mut ChainSync, io: &mut SyncIo, sealed: &[H256]) { + pub fn propagate_latest_blocks(sync: &mut ChainSync, io: &mut dyn SyncIo, sealed: &[H256]) { let chain_info = io.chain().chain_info(); if (((chain_info.best_block_number as i64) - (sync.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION { let peers = sync.get_lagging_peers(&chain_info); @@ -270,7 +269,7 @@ impl SyncPropagator { } /// Distribute valid proposed blocks to subset of current peers. - pub fn propagate_proposed_blocks(sync: &mut ChainSync, io: &mut SyncIo, proposed: &[Bytes]) { + pub fn propagate_proposed_blocks(sync: &mut ChainSync, io: &mut dyn SyncIo, proposed: &[Bytes]) { let peers = sync.get_consensus_peers(); trace!(target: "sync", "Sending proposed blocks to {:?}", peers); for block in proposed { @@ -285,7 +284,7 @@ impl SyncPropagator { } /// Broadcast consensus message to peers. - pub fn propagate_consensus_packet(sync: &mut ChainSync, io: &mut SyncIo, packet: Bytes) { + pub fn propagate_consensus_packet(sync: &mut ChainSync, io: &mut dyn 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 { @@ -294,7 +293,7 @@ impl SyncPropagator { } /// Broadcast private transaction message to peers. - pub fn propagate_private_transaction(sync: &mut ChainSync, io: &mut SyncIo, transaction_hash: H256, packet_id: SyncPacket, packet: Bytes) { + pub fn propagate_private_transaction(sync: &mut ChainSync, io: &mut dyn SyncIo, transaction_hash: H256, packet_id: SyncPacket, packet: Bytes) { let lucky_peers = ChainSync::select_random_peers(&sync.get_private_transaction_peers(&transaction_hash)); if lucky_peers.is_empty() { error!(target: "privatetx", "Cannot propagate the packet, no peers with private tx enabled connected"); @@ -325,7 +324,7 @@ impl SyncPropagator { } /// Generic packet sender - pub fn send_packet(sync: &mut SyncIo, peer_id: PeerId, packet_id: SyncPacket, packet: Bytes) { + pub fn send_packet(sync: &mut dyn SyncIo, peer_id: PeerId, packet_id: SyncPacket, 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); @@ -335,14 +334,26 @@ impl SyncPropagator { #[cfg(test)] mod tests { - use ethcore::client::{BlockInfo, ChainInfo, EachBlockWith, TestBlockChainClient}; + use std::{collections::VecDeque, time::Instant}; + + use crate::{ + api::SyncConfig, + chain::{ChainSync, ForkConfirmation, PeerAsking, PeerInfo}, + tests::{helpers::TestIo, snapshot::TestSnapshotService}, + }; + + use super::{ + super::tests::{dummy_sync_with_peer, insert_dummy_peer}, + SyncPropagator, + }; + + use client_traits::{BlockChainClient, BlockInfo, ChainInfo}; + use ethcore::test_helpers::{EachBlockWith, TestBlockChainClient}; + use ethereum_types::{H256, U256}; + use network::client_version::ClientVersion; use parking_lot::RwLock; - use rlp::{Rlp}; - use std::collections::{VecDeque}; - use tests::helpers::{TestIo}; - use tests::snapshot::TestSnapshotService; - - use super::{*, super::{*, tests::*}}; + use rlp::Rlp; + use common_types::{ids::BlockId, transaction::UnverifiedTransaction}; #[test] fn sends_new_hashes_to_lagging_peer() { @@ -352,7 +363,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peers = sync.get_lagging_peers(&chain_info); let peer_count = SyncPropagator::propagate_new_hashes(&mut sync, &chain_info, &mut io, &peers); @@ -373,7 +384,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peers = sync.get_lagging_peers(&chain_info); let peer_count = SyncPropagator::propagate_blocks(&mut sync, &chain_info, &mut io, &[], &peers); @@ -394,7 +405,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peers = sync.get_lagging_peers(&chain_info); let peer_count = SyncPropagator::propagate_blocks(&mut sync ,&chain_info, &mut io, &[hash.clone()], &peers); @@ -424,6 +435,7 @@ mod tests { asking: PeerAsking::Nothing, asking_blocks: Vec::new(), asking_hash: None, + asking_private_state: None, ask_time: Instant::now(), last_sent_transactions: Default::default(), last_sent_private_transactions: Default::default(), @@ -437,7 +449,7 @@ mod tests { client_version: ClientVersion::from(""), }); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); SyncPropagator::propagate_proposed_blocks(&mut sync, &mut io, &[block]); // 1 message should be sent @@ -454,7 +466,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); // Try to propagate same transactions for the second time let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); @@ -481,7 +493,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); io.chain.insert_transaction_to_queue(); // New block import should not trigger propagation. @@ -505,7 +517,7 @@ mod tests { let mut sync = ChainSync::new(SyncConfig::default(), &client, None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]); // Try to propagate same transactions for the second time @@ -526,7 +538,7 @@ mod tests { let ss = TestSnapshotService::new(); // should sent some { - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); assert_eq!(1, io.packets.len()); assert_eq!(1, peer_count); @@ -534,7 +546,7 @@ mod tests { // Insert some more client.insert_transaction_to_queue(); let (peer_count2, peer_count3) = { - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); // Propagate new transactions let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); // And now the peer should have all transactions @@ -560,7 +572,7 @@ mod tests { 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 mut io = TestIo::new(&mut client, &ss, &queue, None, None); SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true); let stats = sync.transactions_stats(); @@ -575,7 +587,7 @@ mod tests { let mut sync = ChainSync::new(SyncConfig::default(), &client, None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); // when peer#1 is Geth insert_dummy_peer(&mut sync, 1, block_hash); @@ -605,7 +617,7 @@ mod tests { let mut sync = ChainSync::new(SyncConfig::default(), &client, None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, None); // when peer#1 is Parity, accepting service transactions insert_dummy_peer(&mut sync, 1, block_hash); diff --git a/ethcore/sync/src/chain/requester.rs b/ethcore/sync/src/chain/requester.rs index 31d3ce5900..60a4578664 100644 --- a/ethcore/sync/src/chain/requester.rs +++ b/ethcore/sync/src/chain/requester.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,14 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use block_sync::BlockRequest; +use std::time::Instant; + +use crate::{ + block_sync::BlockRequest, + sync_io::SyncIo +}; + use bytes::Bytes; use ethereum_types::H256; +use log::{debug, trace, warn}; use network::{PeerId}; use rlp::RlpStream; -use std::time::Instant; -use sync_io::SyncIo; -use types::BlockNumber; +use common_types::BlockNumber; use super::sync_packet::SyncPacket; use super::sync_packet::SyncPacket::{ @@ -30,6 +35,7 @@ use super::sync_packet::SyncPacket::{ GetReceiptsPacket, GetSnapshotManifestPacket, GetSnapshotDataPacket, + GetPrivateStatePacket, }; use super::{ @@ -43,7 +49,7 @@ 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) { + pub fn request_blocks(sync: &mut ChainSync, io: &mut dyn 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); @@ -58,7 +64,7 @@ impl SyncRequester { } /// Request block bodies from a peer - fn request_bodies(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, hashes: Vec, set: BlockSet) { + fn request_bodies(sync: &mut ChainSync, io: &mut dyn 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 { @@ -71,7 +77,7 @@ impl SyncRequester { } /// Request headers from a peer by block number - pub fn request_fork_header(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, n: BlockNumber) { + pub fn request_fork_header(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, n: BlockNumber) { trace!(target: "sync", "{} <- GetForkHeader: at {}", peer_id, n); let mut rlp = RlpStream::new_list(4); rlp.append(&n); @@ -81,11 +87,11 @@ impl SyncRequester { SyncRequester::send_request(sync, io, peer_id, PeerAsking::ForkHeader, GetBlockHeadersPacket, 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 some headers or blocks to download from a peer. + pub fn request_snapshot_data(sync: &mut ChainSync, io: &mut dyn 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) { + if let Some(mut peer) = sync.peers.get_mut(&peer_id) { peer.asking_snapshot_data = Some(hash.clone()); } SyncRequester::request_snapshot_chunk(sync, io, peer_id, &hash); @@ -93,14 +99,22 @@ impl SyncRequester { } /// 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, GetSnapshotManifestPacket, rlp.out()); + pub fn request_snapshot_manifest(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId) { + trace!(target: "sync", "{}: requesting a snapshot manifest", peer_id); + SyncRequester::send_request(sync, io, peer_id, PeerAsking::SnapshotManifest, GetSnapshotManifestPacket, rlp::EMPTY_LIST_RLP.to_vec()); + } + + pub fn request_private_state(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, hash: &H256) { + trace!(target: "privatetx", "{} <- GetPrivateStatePacket", peer_id); + let mut rlp = RlpStream::new_list(1); + rlp.append(hash); + SyncRequester::send_request(sync, io, peer_id, PeerAsking::PrivateState, GetPrivateStatePacket, 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_private_state = Some(hash.clone()); } /// 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) { + fn request_headers_by_hash(sync: &mut ChainSync, io: &mut dyn 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); @@ -114,7 +128,7 @@ impl SyncRequester { } /// Request block receipts from a peer - fn request_receipts(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, hashes: Vec, set: BlockSet) { + fn request_receipts(sync: &mut ChainSync, io: &mut dyn 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 { @@ -127,7 +141,7 @@ impl SyncRequester { } /// Request snapshot chunk from a peer. - fn request_snapshot_chunk(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, chunk: &H256) { + fn request_snapshot_chunk(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, chunk: &H256) { trace!(target: "sync", "{} <- GetSnapshotData {:?}", peer_id, chunk); let mut rlp = RlpStream::new_list(1); rlp.append(chunk); @@ -135,7 +149,7 @@ impl SyncRequester { } /// Generic request sender - fn send_request(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, asking: PeerAsking, packet_id: SyncPacket, packet: Bytes) { + fn send_request(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, asking: PeerAsking, packet_id: SyncPacket, 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); diff --git a/ethcore/sync/src/chain/supplier.rs b/ethcore/sync/src/chain/supplier.rs index 7e71e6aeec..6596506ed7 100644 --- a/ethcore/sync/src/chain/supplier.rs +++ b/ethcore/sync/src/chain/supplier.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,17 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +use std::cmp; +use std::time::{Duration, Instant}; + +use crate::sync_io::SyncIo; + use bytes::Bytes; use enum_primitive::FromPrimitive; use ethereum_types::H256; +use log::{debug, trace, warn}; use network::{self, PeerId}; use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; -use std::cmp; -use types::BlockNumber; -use types::ids::BlockId; - -use sync_io::SyncIo; +use common_types::{ids::BlockId, BlockNumber}; use super::sync_packet::{PacketInfo, SyncPacket}; use super::sync_packet::SyncPacket::{ @@ -43,6 +45,8 @@ use super::sync_packet::SyncPacket::{ GetSnapshotDataPacket, SnapshotDataPacket, ConsensusDataPacket, + GetPrivateStatePacket, + PrivateStatePacket, }; use super::{ @@ -53,6 +57,8 @@ use super::{ MAX_BODIES_TO_SEND, MAX_HEADERS_TO_SEND, MAX_NODE_DATA_TO_SEND, + MAX_NODE_DATA_TOTAL_DURATION, + MAX_NODE_DATA_SINGLE_DURATION, MAX_RECEIPTS_HEADERS_TO_SEND, }; @@ -63,7 +69,7 @@ impl SyncSupplier { /// Dispatch incoming requests and responses // Take a u8 and not a SyncPacketId because this is the entry point // to chain sync from the outside world. - pub fn dispatch_packet(sync: &RwLock, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { + pub fn dispatch_packet(sync: &RwLock, io: &mut dyn SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) { let rlp = Rlp::new(data); if let Some(id) = SyncPacket::from_u8(packet_id) { @@ -98,6 +104,11 @@ impl SyncSupplier { SyncSupplier::return_snapshot_data, |e| format!("Error sending snapshot data: {:?}", e)), + GetPrivateStatePacket => SyncSupplier::return_rlp( + io, &rlp, peer, + SyncSupplier::return_private_state, + |e| format!("Error sending private state data: {:?}", e)), + StatusPacket => { sync.write().on_packet(io, peer, packet_id, data); Ok(()) @@ -108,7 +119,7 @@ impl SyncSupplier { debug!(target:"sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_version(peer)); return; } - debug!(target: "sync", "{} -> Dispatching packet: {}", peer, packet_id); + trace!(target: "sync", "{} -> Dispatching packet: {}", peer, packet_id); match id { ConsensusDataPacket => { @@ -141,7 +152,7 @@ impl SyncSupplier { } /// Respond to GetBlockHeaders request - fn return_block_headers(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + fn return_block_headers(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { let payload_soft_limit = io.payload_soft_limit(); // Packet layout: // [ block: { P , B_32 }, maxHeaders: P, skip: P, reverse: P in { 0 , 1 } ] @@ -222,7 +233,7 @@ impl SyncSupplier { } /// Respond to GetBlockBodies request - fn return_block_bodies(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + fn return_block_bodies(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { let payload_soft_limit = io.payload_soft_limit(); let mut count = r.item_count().unwrap_or(0); if count == 0 { @@ -249,10 +260,10 @@ impl SyncSupplier { } /// Respond to GetNodeData request - fn return_node_data(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { - let payload_soft_limit = io.payload_soft_limit(); + fn return_node_data(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let payload_soft_limit = io.payload_soft_limit(); // 4Mb let mut count = r.item_count().unwrap_or(0); - trace!(target: "sync", "{} -> GetNodeData: {} entries", peer_id, count); + trace!(target: "sync", "{} -> GetNodeData: {} entries requested", peer_id, count); if count == 0 { debug!(target: "sync", "Empty GetNodeData request, ignoring."); return Ok(None); @@ -261,10 +272,20 @@ impl SyncSupplier { let mut added = 0usize; let mut data = Vec::new(); let mut total_bytes = 0; + let mut total_elpsd = Duration::from_secs(0); for i in 0..count { - if let Some(node) = io.chain().state_data(&r.val_at::(i)?) { + let hash = &r.val_at(i)?; + let elpsd = Instant::now(); + let state = io.chain().state_data(hash); + + total_elpsd += elpsd.elapsed(); + if elpsd.elapsed() > MAX_NODE_DATA_SINGLE_DURATION || total_elpsd > MAX_NODE_DATA_TOTAL_DURATION { + warn!(target: "sync", "{} -> GetNodeData: item {}/{} – slow state fetch for hash {:?}; took {:?}", + peer_id, i, count, hash, elpsd); + break; + } + if let Some(node) = state { total_bytes += node.len(); - // Check that the packet won't be oversized if total_bytes > payload_soft_limit { break; } @@ -272,7 +293,8 @@ impl SyncSupplier { added += 1; } } - trace!(target: "sync", "{} -> GetNodeData: return {} entries", peer_id, added); + trace!(target: "sync", "{} -> GetNodeData: returning {}/{} entries ({} bytes total in {:?})", + peer_id, added, count, total_bytes, total_elpsd); let mut rlp = RlpStream::new_list(added); for d in data { rlp.append(&d); @@ -280,7 +302,7 @@ impl SyncSupplier { Ok(Some((NodeDataPacket.id(), rlp))) } - fn return_receipts(io: &SyncIo, rlp: &Rlp, peer_id: PeerId) -> RlpResponseResult { + fn return_receipts(io: &dyn SyncIo, rlp: &Rlp, peer_id: PeerId) -> RlpResponseResult { let payload_soft_limit = io.payload_soft_limit(); let mut count = rlp.item_count().unwrap_or(0); trace!(target: "sync", "{} -> GetReceipts: {} entries", peer_id, count); @@ -307,7 +329,7 @@ impl SyncSupplier { } /// Respond to GetSnapshotManifest request - fn return_snapshot_manifest(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + fn return_snapshot_manifest(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { let count = r.item_count().unwrap_or(0); trace!(target: "warp", "{} -> GetSnapshotManifest", peer_id); if count != 0 { @@ -330,7 +352,7 @@ impl SyncSupplier { } /// Respond to GetSnapshotData request - fn return_snapshot_data(io: &SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + fn return_snapshot_data(io: &dyn 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) { @@ -348,8 +370,28 @@ impl SyncSupplier { Ok(Some((SnapshotDataPacket.id(), 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, + /// Respond to GetPrivateStatePacket + fn return_private_state(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { + let hash: H256 = r.val_at(0)?; + trace!(target: "privatetx", "{} -> GetPrivateStatePacket {:?}", peer_id, hash); + io.private_state().map_or(Ok(None), |db| { + let state = db.state(&hash); + match state { + Err(err) => { + trace!(target: "privatetx", "Cannot retrieve state from db {:?}", err); + Ok(None) + } + Ok(bytes) => { + let mut rlp = RlpStream::new_list(1); + rlp.append(&bytes); + Ok(Some((PrivateStatePacket.id(), rlp))) + } + } + }) + } + + fn return_rlp(io: &mut dyn SyncIo, rlp: &Rlp, peer: PeerId, rlp_func: FRlp, error_func: FError) -> Result<(), PacketDecodeError> + where FRlp : Fn(&dyn SyncIo, &Rlp, PeerId) -> RlpResponseResult, FError : FnOnce(network::Error) -> String { let response = rlp_func(io, rlp, peer); @@ -367,16 +409,27 @@ impl SyncSupplier { #[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 std::{collections::VecDeque, str::FromStr}; + + use crate::{ + blocks::SyncHeader, + chain::RlpResponseResult, + tests::{helpers::TestIo, snapshot::TestSnapshotService} + }; + + use super::{ + SyncPacket::{GetReceiptsPacket, GetNodeDataPacket}, + BlockNumber, BlockId, SyncSupplier, PacketInfo + }; + + use super::super::tests::dummy_sync_with_peer; + use bytes::Bytes; + use client_traits::BlockChainClient; + use ethcore::test_helpers::{EachBlockWith, TestBlockChainClient}; + use ethereum_types::H256; + use parking_lot::RwLock; use rlp::{Rlp, RlpStream}; - use super::{*, super::tests::*}; - use blocks::SyncHeader; - use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; #[test] fn return_block_headers() { @@ -397,22 +450,23 @@ mod test { rlp.append(&if reverse {1u32} else {0u32}); rlp.out() } - fn to_header_vec(rlp: ::chain::RlpResponseResult) -> Vec { + + fn to_header_vec(rlp: RlpResponseResult) -> Vec { Rlp::new(&rlp.unwrap().unwrap().1.out()).iter().map(|r| SyncHeader::from_rlp(r.as_raw().to_vec()).unwrap()).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(); + .map(|i| (&client as &dyn BlockChainClient).block(BlockId::Number(i as BlockNumber)).map(|b| b.into_inner()).unwrap()).collect(); let headers: Vec<_> = blocks.iter().map(|b| SyncHeader::from_rlp(Rlp::new(b).at(0).unwrap().as_raw().to_vec()).unwrap()).collect(); let hashes: Vec<_> = headers.iter().map(|h| h.header.hash()).collect(); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let io = TestIo::new(&mut client, &ss, &queue, None); + let io = TestIo::new(&mut client, &ss, &queue, None, None); - let unknown: H256 = H256::new(); + let unknown: H256 = H256::zero(); 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); @@ -468,7 +522,7 @@ mod test { let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); - let io = TestIo::new(&mut client, &ss, &queue, None); + let io = TestIo::new(&mut client, &ss, &queue, None, None); let small_result = SyncSupplier::return_block_bodies(&io, &Rlp::new(&small_rlp_request.out()), 0); let small_result = small_result.unwrap().unwrap().1; @@ -483,14 +537,14 @@ mod 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 sync = dummy_sync_with_peer(H256::zero(), &client); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, 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")); + node_list.append(&H256::from_str("0000000000000000000000000000000000000000000000005555555555555555").unwrap()); + node_list.append(&H256::from_str("ffffffffffffffffffffffffffffffffffffffffffffaaaaaaaaaaaaaaaaaaaa").unwrap()); + node_list.append(&H256::from_str("aff0000000000000000000000000000000000000000000000000000000000000").unwrap()); let node_request = node_list.out(); // it returns rlp ONLY for hashes started with "f" @@ -500,7 +554,7 @@ mod test { let rlp_result = result.unwrap(); assert!(rlp_result.is_some()); - // the length of one rlp-encoded hashe + // the length of one rlp-encoded hash let rlp = rlp_result.unwrap().1.out(); let rlp = Rlp::new(&rlp); assert_eq!(Ok(1), rlp.item_count()); @@ -516,7 +570,7 @@ mod test { 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 io = TestIo::new(&mut client, &ss, &queue, None, None); let result = SyncSupplier::return_receipts(&io, &Rlp::new(&[0xc0]), 0); @@ -527,15 +581,15 @@ mod 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 sync = dummy_sync_with_peer(H256::zero(), &client); let ss = TestSnapshotService::new(); - let mut io = TestIo::new(&mut client, &ss, &queue, None); + let mut io = TestIo::new(&mut client, &ss, &queue, None, 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")); + receipt_list.append(&H256::from_str("0000000000000000000000000000000000000000000000005555555555555555").unwrap()); + receipt_list.append(&H256::from_str("ff00000000000000000000000000000000000000000000000000000000000000").unwrap()); + receipt_list.append(&H256::from_str("fff0000000000000000000000000000000000000000000000000000000000000").unwrap()); + receipt_list.append(&H256::from_str("aff0000000000000000000000000000000000000000000000000000000000000").unwrap()); let receipts_request = receipt_list.out(); // it returns rlp ONLY for hashes started with "f" diff --git a/ethcore/sync/src/chain/sync_packet.rs b/ethcore/sync/src/chain/sync_packet.rs index 3891090f65..5ebe859dbc 100644 --- a/ethcore/sync/src/chain/sync_packet.rs +++ b/ethcore/sync/src/chain/sync_packet.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,48 +22,55 @@ //! to convert to/from the packet id values transmitted over the //! wire. -use api::{ETH_PROTOCOL, WARP_SYNC_PROTOCOL_ID}; +use crate::api::{ETH_PROTOCOL, WARP_SYNC_PROTOCOL_ID}; +use self::SyncPacket::*; + +use enum_primitive::{enum_from_primitive, enum_from_primitive_impl, enum_from_primitive_impl_ty}; use network::{PacketId, ProtocolId}; -/// An enum that defines all known packet ids in the context of -/// synchronization and provides a mechanism to convert from -/// packet ids (of type PacketId or u8) directly read from the network -/// to enum variants. This implicitly provides a mechanism to -/// check whether a given packet id is known, and to prevent -/// packet id clashes when defining new ids. enum_from_primitive! { -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum SyncPacket { - StatusPacket = 0x00, - NewBlockHashesPacket = 0x01, - TransactionsPacket = 0x02, - GetBlockHeadersPacket = 0x03, - BlockHeadersPacket = 0x04, - GetBlockBodiesPacket = 0x05, - BlockBodiesPacket = 0x06, - NewBlockPacket = 0x07, - - GetNodeDataPacket = 0x0d, - NodeDataPacket = 0x0e, - GetReceiptsPacket = 0x0f, - ReceiptsPacket = 0x10, - - GetSnapshotManifestPacket = 0x11, - SnapshotManifestPacket = 0x12, - GetSnapshotDataPacket = 0x13, - SnapshotDataPacket = 0x14, - ConsensusDataPacket = 0x15, - PrivateTransactionPacket = 0x16, - SignedPrivateTransactionPacket = 0x17, -} + /// An enum that defines all known packet ids in the context of + /// synchronization and provides a mechanism to convert from + /// packet ids (of type PacketId or u8) directly read from the network + /// to enum variants. This implicitly provides a mechanism to + /// check whether a given packet id is known, and to prevent + /// packet id clashes when defining new ids. + #[derive(Clone, Copy, Debug, PartialEq)] + pub enum SyncPacket { + StatusPacket = 0x00, + NewBlockHashesPacket = 0x01, + TransactionsPacket = 0x02, + GetBlockHeadersPacket = 0x03, + BlockHeadersPacket = 0x04, + GetBlockBodiesPacket = 0x05, + BlockBodiesPacket = 0x06, + NewBlockPacket = 0x07, + + GetNodeDataPacket = 0x0d, + NodeDataPacket = 0x0e, + GetReceiptsPacket = 0x0f, + ReceiptsPacket = 0x10, + + GetSnapshotManifestPacket = 0x11, + SnapshotManifestPacket = 0x12, + GetSnapshotDataPacket = 0x13, + SnapshotDataPacket = 0x14, + ConsensusDataPacket = 0x15, + PrivateTransactionPacket = 0x16, + SignedPrivateTransactionPacket = 0x17, + GetPrivateStatePacket = 0x18, + PrivateStatePacket = 0x19, + } } -use self::SyncPacket::*; /// Provide both subprotocol and packet id information within the /// same object. pub trait PacketInfo { + /// Get packet id fn id(&self) -> PacketId; + + /// Get protocol id fn protocol(&self) -> ProtocolId; } @@ -94,7 +101,9 @@ impl PacketInfo for SyncPacket { SnapshotDataPacket | ConsensusDataPacket | PrivateTransactionPacket | - SignedPrivateTransactionPacket + SignedPrivateTransactionPacket | + GetPrivateStatePacket | + PrivateStatePacket => WARP_SYNC_PROTOCOL_ID, } @@ -109,7 +118,6 @@ impl PacketInfo for SyncPacket { #[cfg(test)] mod tests { use super::*; - use enum_primitive::FromPrimitive; #[test] diff --git a/ethcore/sync/src/lib.rs b/ethcore/sync/src/lib.rs index 8a1e19569a..b29217bf6b 100644 --- a/ethcore/sync/src/lib.rs +++ b/ethcore/sync/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,53 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -#![warn(missing_docs)] +#![warn(missing_docs, unused_extern_crates)] //! Blockchain sync module //! Implements ethereum protocol version 63 as specified here: //! https://github.com/ethereum/wiki/wiki/Ethereum-Wire-Protocol //! -extern crate common_types as types; -extern crate ethcore; -extern crate ethcore_io as io; -extern crate ethcore_network as network; -extern crate ethcore_network_devp2p as devp2p; -extern crate ethereum_types; -extern crate ethkey; -extern crate ethstore; -extern crate fastmap; -extern crate keccak_hash as hash; -extern crate parity_bytes as bytes; -extern crate parking_lot; -extern crate rand; -extern crate rlp; -extern crate triehash_ethereum; - -extern crate ethcore_light as light; - -#[cfg(test)] extern crate env_logger; -#[cfg(test)] extern crate ethcore_private_tx; -#[cfg(test)] extern crate kvdb_memorydb; -#[cfg(test)] extern crate rustc_hex; - -#[macro_use] -extern crate enum_primitive; -#[macro_use] -extern crate macros; -#[macro_use] -extern crate log; -#[macro_use] -extern crate heapsize; -#[macro_use] -extern crate trace_time; +// needed to make the procedural macro `MallocSizeOf` to work +#[macro_use] extern crate parity_util_mem as malloc_size_of; +mod api; mod chain; mod blocks; mod block_sync; mod sync_io; mod private_tx; -mod snapshot; +mod snapshot_sync; mod transactions_stats; pub mod light_sync; @@ -68,10 +38,8 @@ pub mod light_sync; #[cfg(test)] mod tests; -mod api; - pub use api::*; pub use chain::{SyncStatus, SyncState}; pub use devp2p::validate_node_url; -pub use network::{NonReservedPeerMode, Error, ErrorKind, ConnectionFilter, ConnectionDirection}; +pub use network::{NonReservedPeerMode, Error, ConnectionFilter, ConnectionDirection}; pub use private_tx::{PrivateTxHandler, NoopPrivateTxHandler, SimplePrivateTxHandler}; diff --git a/ethcore/sync/src/light_sync/mod.rs b/ethcore/sync/src/light_sync/mod.rs index dae05c3188..05ddebacd9 100644 --- a/ethcore/sync/src/light_sync/mod.rs +++ b/ethcore/sync/src/light_sync/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -38,7 +38,12 @@ use std::ops::Deref; use std::sync::Arc; use std::time::{Instant, Duration}; -use types::encoded; +use crate::{ + api::Notification, + chain::SyncState as ChainSyncState, +}; + +use common_types::encoded; use light::client::{AsLightClient, LightChainClient}; use light::net::{ PeerStatus, Announcement, Handler, BasicContext, @@ -46,10 +51,12 @@ use light::net::{ Error as NetError, }; use light::request::{self, CompleteHeadersRequest as HeadersRequest}; +use log::{debug, trace}; use network::PeerId; use ethereum_types::{H256, U256}; use parking_lot::{Mutex, RwLock}; -use rand::{Rng, OsRng}; +use rand::{rngs::OsRng, seq::SliceRandom}; +use futures::sync::mpsc; use self::sync_round::{AbortReason, SyncRound, ResponseContext}; @@ -75,13 +82,13 @@ struct ChainInfo { } impl PartialOrd for ChainInfo { - fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { + fn partial_cmp(&self, other: &Self) -> Option { self.head_td.partial_cmp(&other.head_td) } } impl Ord for ChainInfo { - fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.head_td.cmp(&other.head_td) } } @@ -99,14 +106,20 @@ impl Peer { } } -// search for a common ancestor with the best chain. +/// Search for a common ancestor with the best chain. #[derive(Debug)] enum AncestorSearch { - Queued(u64), // queued to search for blocks starting from here. - Awaiting(ReqId, u64, HeadersRequest), // awaiting response for this request. - Prehistoric, // prehistoric block found. TODO: start to roll back CHTs. - FoundCommon(u64, H256), // common block found. - Genesis, // common ancestor is the genesis. + /// Queued to search for blocks starting from here. + Queued(u64), // + /// Awaiting response for this request. + Awaiting(ReqId, u64, HeadersRequest), + /// Pre-historic block found. + // TODO: start to roll back CHTs. + Prehistoric, + /// Common block found. + FoundCommon(u64, H256), + /// Common ancestor is the genesis. + Genesis, } impl AncestorSearch { @@ -117,7 +130,7 @@ impl AncestorSearch { } } - fn process_response(self, ctx: &ResponseContext, client: &L) -> AncestorSearch + fn process_response(self, ctx: &dyn ResponseContext, client: &L) -> AncestorSearch where L: AsLightClient { let client = client.as_light_client(); @@ -255,7 +268,7 @@ impl Deref for SyncStateWrapper { struct ResponseCtx<'a> { peer: PeerId, req_id: ReqId, - ctx: &'a BasicContext, + ctx: &'a dyn BasicContext, data: &'a [encoded::Header], } @@ -275,6 +288,7 @@ pub struct LightSync { client: Arc, rng: Mutex, state: Mutex, + senders: RwLock>>, // We duplicate this state tracking to avoid deadlocks in `is_major_importing`. is_idle: Mutex, } @@ -288,7 +302,7 @@ struct PendingReq { impl Handler for LightSync { fn on_connect( &self, - ctx: &EventContext, + ctx: &dyn EventContext, status: &Status, capabilities: &Capabilities ) -> PeerStatus { @@ -315,7 +329,7 @@ impl Handler for LightSync { } } - fn on_disconnect(&self, ctx: &EventContext, unfulfilled: &[ReqId]) { + fn on_disconnect(&self, ctx: &dyn EventContext, unfulfilled: &[ReqId]) { let peer_id = ctx.peer(); let peer = match self.peers.write().remove(&peer_id).map(|p| p.into_inner()) { @@ -366,7 +380,7 @@ impl Handler for LightSync { self.maintain_sync(ctx.as_basic()); } - fn on_announcement(&self, ctx: &EventContext, announcement: &Announcement) { + fn on_announcement(&self, ctx: &dyn EventContext, announcement: &Announcement) { let (last_td, chain_info) = { let peers = self.peers.read(); match peers.get(&ctx.peer()) { @@ -402,7 +416,7 @@ impl Handler for LightSync { self.maintain_sync(ctx.as_basic()); } - fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[request::Response]) { + fn on_responses(&self, ctx: &dyn EventContext, req_id: ReqId, responses: &[request::Response]) { let peer = ctx.peer(); if !self.peers.read().contains_key(&peer) { return @@ -444,7 +458,7 @@ impl Handler for LightSync { self.maintain_sync(ctx.as_basic()); } - fn tick(&self, ctx: &BasicContext) { + fn tick(&self, ctx: &dyn BasicContext) { self.maintain_sync(ctx); } } @@ -454,9 +468,21 @@ impl LightSync { /// Sets the LightSync's state, and update /// `is_idle` fn set_state(&self, state: &mut SyncStateWrapper, next_state: SyncState) { + + match next_state { + SyncState::Idle => self.notify_senders(ChainSyncState::Idle), + _ => self.notify_senders(ChainSyncState::Blocks) + }; + state.set(next_state, &mut self.is_idle.lock()); } + fn notify_senders(&self, state: ChainSyncState) { + self.senders.write().retain(|sender| { + sender.unbounded_send(state).is_ok() + }) + } + // Begins a search for the common ancestor and our best block. // does not lock state, instead has a mutable reference to it passed. fn begin_search(&self, state: &mut SyncStateWrapper) { @@ -476,8 +502,8 @@ impl LightSync { } // handles request dispatch, block import, state machine transitions, and timeouts. - fn maintain_sync(&self, ctx: &BasicContext) { - use ethcore::error::{Error as EthcoreError, ErrorKind as EthcoreErrorKind, ImportErrorKind}; + fn maintain_sync(&self, ctx: &dyn BasicContext) { + use common_types::errors::{EthcoreError, ImportError}; const DRAIN_AMOUNT: usize = 128; @@ -508,10 +534,10 @@ impl LightSync { for header in sink.drain(..) { match client.queue_header(header) { Ok(_) => {} - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { + Err(EthcoreError::Import(ImportError::AlreadyInChain)) => { trace!(target: "sync", "Block already in chain. Continuing."); }, - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyQueued), _)) => { + Err(EthcoreError::Import(ImportError::AlreadyQueued)) => { trace!(target: "sync", "Block already queued. Continuing."); }, Err(e) => { @@ -621,7 +647,7 @@ impl LightSync { // naive request dispatcher: just give to any peer which says it will // give us responses. but only one request per peer per state transition. let dispatcher = move |req: HeadersRequest| { - rng.shuffle(&mut peer_ids); + peer_ids.shuffle(&mut *rng); let request = { let mut builder = request::Builder::default(); @@ -667,6 +693,14 @@ impl LightSync { self.set_state(&mut state, next_state); } } + + // returns receiving end of futures::mpsc::unbounded channel + // poll the channel for changes to sync state. + fn sync_notification(&self) -> Notification { + let (sender, receiver) = futures::sync::mpsc::unbounded(); + self.senders.write().push(sender); + receiver + } } // public API @@ -682,7 +716,8 @@ impl LightSync { peers: RwLock::new(HashMap::new()), pending_reqs: Mutex::new(HashMap::new()), client: client, - rng: Mutex::new(OsRng::new()?), + rng: Mutex::new(OsRng), + senders: RwLock::new(Vec::new()), state: Mutex::new(SyncStateWrapper::idle()), is_idle: Mutex::new(true), }) @@ -699,6 +734,10 @@ pub trait SyncInfo { /// Whether major sync is underway. fn is_major_importing(&self) -> bool; + + /// returns the receieving end of a futures::mpsc unbounded channel + /// poll the channel for changes to sync state + fn sync_notification(&self) -> Notification; } impl SyncInfo for LightSync { @@ -720,4 +759,7 @@ impl SyncInfo for LightSync { is_verifying || is_syncing } + fn sync_notification(&self) -> Notification { + self.sync_notification() + } } diff --git a/ethcore/sync/src/light_sync/response.rs b/ethcore/sync/src/light_sync/response.rs index 96d2a8822e..39354a6327 100644 --- a/ethcore/sync/src/light_sync/response.rs +++ b/ethcore/sync/src/light_sync/response.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ //! Helpers for decoding and verifying responses for headers. -use types::{encoded, header::Header}; +use common_types::{encoded, header::Header}; use ethereum_types::H256; use light::request::{HashOrNumber, CompleteHeadersRequest as HeadersRequest}; use rlp::DecoderError; @@ -153,8 +153,8 @@ impl Constraint for Max { #[cfg(test)] mod tests { - use types::encoded; - use types::header::Header; + use common_types::encoded; + use common_types::header::Header; use light::request::CompleteHeadersRequest as HeadersRequest; use super::*; diff --git a/ethcore/sync/src/light_sync/sync_round.rs b/ethcore/sync/src/light_sync/sync_round.rs index 7c2a2bc016..2ec2973337 100644 --- a/ethcore/sync/src/light_sync/sync_round.rs +++ b/ethcore/sync/src/light_sync/sync_round.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,11 +20,11 @@ use std::cmp::Ordering; use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque}; use std::fmt; -use types::encoded; -use types::header::Header; +use common_types::{encoded, header::Header}; use light::net::ReqId; use light::request::CompleteHeadersRequest as HeadersRequest; +use log::trace; use network::PeerId; use ethereum_types::H256; @@ -37,7 +37,7 @@ const SCAFFOLD_ATTEMPTS: usize = 3; /// Context for a headers response. pub trait ResponseContext { /// Get the peer who sent this response. - fn responder(&self) -> PeerId; + fn responder(&self) -> PeerId; /// Get the request ID this response corresponds to. fn req_id(&self) -> &ReqId; /// Get the (unverified) response data. @@ -234,7 +234,7 @@ impl Fetcher { } // state transition not triggered until drain is finished. - (SyncRound::Fetch(self)) + SyncRound::Fetch(self) } } } diff --git a/ethcore/sync/src/light_sync/tests/mod.rs b/ethcore/sync/src/light_sync/tests/mod.rs index 9bfb99ed0d..cb9a54f060 100644 --- a/ethcore/sync/src/light_sync/tests/mod.rs +++ b/ethcore/sync/src/light_sync/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,9 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use tests::helpers::TestNet; +use crate::tests::helpers::TestNet; -use ethcore::client::{BlockInfo, BlockId, EachBlockWith}; +use ethcore::test_helpers::EachBlockWith; +use client_traits::BlockInfo; +use common_types::ids::BlockId; mod test_net; diff --git a/ethcore/sync/src/light_sync/tests/test_net.rs b/ethcore/sync/src/light_sync/tests/test_net.rs index 74567c1192..0f658b435a 100644 --- a/ethcore/sync/src/light_sync/tests/test_net.rs +++ b/ethcore/sync/src/light_sync/tests/test_net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,26 +18,27 @@ use std::collections::{HashSet, VecDeque}; use std::sync::Arc; +use std::time::Duration; -use light_sync::*; -use tests::helpers::{TestNet, Peer as PeerLike, TestPacket}; - -use ethcore::client::TestBlockChainClient; -use ethcore::spec::Spec; -use io::IoChannel; -use kvdb_memorydb; -use light::client::fetch::{self, Unavailable}; -use light::net::{LightProtocol, IoContext, Capabilities, Params as LightParams}; -use light::provider::LightProvider; +use crate::{ + light_sync::LightSync, + tests::helpers::{TestNet, Peer as PeerLike, TestPacket} +}; + +use ethcore::test_helpers::TestBlockChainClient; +use ethcore_io::IoChannel; +use light::{ + cache::Cache, + client::fetch::{self, Unavailable}, + net::{LightProtocol, IoContext, Capabilities, Params as LightParams}, + provider::LightProvider +}; use network::{NodeId, PeerId}; -use parking_lot::RwLock; - -use std::time::Duration; -use light::cache::Cache; +use parking_lot::{Mutex, RwLock}; const NETWORK_ID: u64 = 0xcafebabe; -pub type LightClient = ::light::client::Client; +pub type LightClient = light::client::Client; struct TestIoContext<'a> { queue: &'a RwLock>, @@ -49,7 +50,7 @@ impl<'a> IoContext for TestIoContext<'a> { fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { self.queue.write().push_back(TestPacket { data: packet_body, - packet_id: packet_id, + packet_id, recipient: peer, }) } @@ -64,11 +65,21 @@ impl<'a> IoContext for TestIoContext<'a> { self.to_disconnect.write().insert(peer); } - fn disable_peer(&self, peer: PeerId) { self.disconnect_peer(peer) } - fn protocol_version(&self, _peer: PeerId) -> Option { Some(::light::net::MAX_PROTOCOL_VERSION) } + fn disable_peer(&self, peer: PeerId) { + self.disconnect_peer(peer) + } + + fn protocol_version(&self, _peer: PeerId) -> Option { + Some(light::net::MAX_PROTOCOL_VERSION) + } - fn persistent_peer_id(&self, _peer: PeerId) -> Option { unimplemented!() } - fn is_reserved_peer(&self, _peer: PeerId) -> bool { false } + fn persistent_peer_id(&self, _peer: PeerId) -> Option { + unimplemented!() + } + + fn is_reserved_peer(&self, _peer: PeerId) -> bool { + false + } } // peer-specific data. @@ -219,17 +230,17 @@ impl TestNet { pub fn light(n_light: usize, n_full: usize) -> Self { let mut peers = Vec::with_capacity(n_light + n_full); for _ in 0..n_light { - let mut config = ::light::client::Config::default(); + let mut config = light::client::Config::default(); // skip full verification because the blocks are bad. config.verify_full = false; let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::from_secs(6 * 3600)))); - let db = kvdb_memorydb::create(0); + let db = kvdb_memorydb::create(1); let client = LightClient::new( config, Arc::new(db), - None, - &Spec::new_test(), + 0, + &spec::new_test(), fetch::unavailable(), // TODO: allow fetch from full nodes. IoChannel::disconnected(), cache @@ -242,8 +253,8 @@ impl TestNet { peers.push(Arc::new(Peer::new_full(Arc::new(TestBlockChainClient::new())))) } - TestNet { - peers: peers, + Self { + peers, started: false, disconnect_events: Vec::new(), } diff --git a/ethcore/sync/src/private_tx.rs b/ethcore/sync/src/private_tx.rs index c9396af5b4..13e1430a61 100644 --- a/ethcore/sync/src/private_tx.rs +++ b/ethcore/sync/src/private_tx.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -26,6 +26,9 @@ pub trait PrivateTxHandler: Send + Sync + 'static { /// Function called on new signed private transaction received. /// Returns the hash of the imported transaction fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result; + + /// Function called when requested private state retrieved from peer and saved to DB. + fn private_state_synced(&self, hash: &H256) -> Result<(), String>; } /// Nonoperative private transaction handler. @@ -33,11 +36,15 @@ pub struct NoopPrivateTxHandler; impl PrivateTxHandler for NoopPrivateTxHandler { fn import_private_transaction(&self, _rlp: &[u8]) -> Result { - Ok(H256::default()) + Ok(H256::zero()) } fn import_signed_private_transaction(&self, _rlp: &[u8]) -> Result { - Ok(H256::default()) + Ok(H256::zero()) + } + + fn private_state_synced(&self, _hash: &H256) -> Result<(), String> { + Ok(()) } } @@ -48,16 +55,23 @@ pub struct SimplePrivateTxHandler { pub txs: Mutex>>, /// imported signed private transactions pub signed_txs: Mutex>>, + /// synced private state hash + pub synced_hash: Mutex, } impl PrivateTxHandler for SimplePrivateTxHandler { fn import_private_transaction(&self, rlp: &[u8]) -> Result { self.txs.lock().push(rlp.to_vec()); - Ok(H256::default()) + Ok(H256::zero()) } fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result { self.signed_txs.lock().push(rlp.to_vec()); - Ok(H256::default()) + Ok(H256::zero()) + } + + fn private_state_synced(&self, hash: &H256) -> Result<(), String> { + *self.synced_hash.lock() = *hash; + Ok(()) } } diff --git a/ethcore/sync/src/snapshot.rs b/ethcore/sync/src/snapshot.rs deleted file mode 100644 index 64e463c7b2..0000000000 --- a/ethcore/sync/src/snapshot.rs +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use ethcore::snapshot::{ManifestData, SnapshotService}; -use ethereum_types::H256; -use hash::keccak; - -use std::collections::HashSet; -use std::iter::FromIterator; - -#[derive(PartialEq, Eq, Debug)] -pub enum ChunkType { - State(H256), - Block(H256), -} - -pub struct Snapshot { - pending_state_chunks: Vec, - pending_block_chunks: Vec, - downloading_chunks: HashSet, - completed_chunks: HashSet, - snapshot_hash: Option, - bad_hashes: HashSet, - initialized: bool, -} - -impl Snapshot { - /// Create a new instance. - pub fn new() -> Snapshot { - Snapshot { - pending_state_chunks: Vec::new(), - pending_block_chunks: Vec::new(), - downloading_chunks: HashSet::new(), - 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(); - self.pending_block_chunks.clear(); - self.downloading_chunks.clear(); - self.completed_chunks.clear(); - self.snapshot_hash = None; - self.initialized = false; - } - - /// Check if currently downloading a snapshot. - pub fn have_manifest(&self) -> bool { - self.snapshot_hash.is_some() - } - - /// Reset collection for a manifest RLP - pub fn reset_to(&mut self, manifest: &ManifestData, hash: &H256) { - self.clear(); - self.pending_state_chunks = manifest.state_hashes.clone(); - self.pending_block_chunks = manifest.block_hashes.clone(); - self.snapshot_hash = Some(hash.clone()); - } - - /// Validate chunk and mark it as downloaded - pub fn validate_chunk(&mut self, chunk: &[u8]) -> Result { - let hash = keccak(chunk); - if self.completed_chunks.contains(&hash) { - trace!(target: "sync", "Ignored proccessed chunk: {:x}", hash); - return Err(()); - } - self.downloading_chunks.remove(&hash); - if self.pending_block_chunks.iter().any(|h| h == &hash) { - self.completed_chunks.insert(hash.clone()); - return Ok(ChunkType::Block(hash)); - } - if self.pending_state_chunks.iter().any(|h| h == &hash) { - self.completed_chunks.insert(hash.clone()); - return Ok(ChunkType::State(hash)); - } - trace!(target: "sync", "Ignored unknown chunk: {:x}", hash); - Err(()) - } - - /// Find a chunk to download - pub fn needed_chunk(&mut self) -> Option { - // Find next needed chunk: first block, then state chunks - let chunk = { - let chunk_filter = |h| !self.downloading_chunks.contains(h) && !self.completed_chunks.contains(h); - - let needed_block_chunk = self.pending_block_chunks.iter() - .filter(|&h| chunk_filter(h)) - .map(|h| *h) - .next(); - - // If no block chunks to download, get the state chunks - if needed_block_chunk.is_none() { - self.pending_state_chunks.iter() - .filter(|&h| chunk_filter(h)) - .map(|h| *h) - .next() - } else { - needed_block_chunk - } - }; - - if let Some(hash) = chunk { - self.downloading_chunks.insert(hash.clone()); - } - chunk - } - - pub fn clear_chunk_download(&mut self, hash: &H256) { - self.downloading_chunks.remove(hash); - } - - // note snapshot hash as bad. - pub fn note_bad(&mut self, hash: H256) { - self.bad_hashes.insert(hash); - } - - // whether snapshot hash is known to be bad. - pub fn is_known_bad(&self, hash: &H256) -> bool { - self.bad_hashes.contains(hash) - } - - pub fn snapshot_hash(&self) -> Option { - self.snapshot_hash - } - - pub fn total_chunks(&self) -> usize { - self.pending_block_chunks.len() + self.pending_state_chunks.len() - } - - pub fn done_chunks(&self) -> usize { - self.completed_chunks.len() - } - - pub fn is_complete(&self) -> bool { - self.total_chunks() == self.completed_chunks.len() - } -} - -#[cfg(test)] -mod test { - use hash::keccak; - use bytes::Bytes; - use super::*; - use ethcore::snapshot::ManifestData; - - fn is_empty(snapshot: &Snapshot) -> bool { - snapshot.pending_block_chunks.is_empty() && - snapshot.pending_state_chunks.is_empty() && - snapshot.completed_chunks.is_empty() && - snapshot.downloading_chunks.is_empty() && - snapshot.snapshot_hash.is_none() - } - - fn test_manifest() -> (ManifestData, H256, Vec, Vec) { - let state_chunks: Vec = (0..20).map(|_| H256::random().to_vec()).collect(); - let block_chunks: Vec = (0..20).map(|_| H256::random().to_vec()).collect(); - let manifest = ManifestData { - version: 2, - state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(), - block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(), - state_root: H256::new(), - block_number: 42, - block_hash: H256::new(), - }; - let mhash = keccak(manifest.clone().into_rlp()); - (manifest, mhash, state_chunks, block_chunks) - } - - #[test] - fn create_clear() { - let mut snapshot = Snapshot::new(); - assert!(is_empty(&snapshot)); - let (manifest, mhash, _, _,) = test_manifest(); - snapshot.reset_to(&manifest, &mhash); - assert!(!is_empty(&snapshot)); - snapshot.clear(); - assert!(is_empty(&snapshot)); - } - - #[test] - fn validate_chunks() { - let mut snapshot = Snapshot::new(); - let (manifest, mhash, state_chunks, block_chunks) = test_manifest(); - snapshot.reset_to(&manifest, &mhash); - assert_eq!(snapshot.done_chunks(), 0); - assert!(snapshot.validate_chunk(&H256::random().to_vec()).is_err()); - - let requested: Vec = (0..40).map(|_| snapshot.needed_chunk().unwrap()).collect(); - assert!(snapshot.needed_chunk().is_none()); - - 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()))); - assert_eq!(snapshot.completed_chunks.len(), 1); - assert_eq!(snapshot.downloading_chunks.len(), 39); - - assert_eq!(snapshot.validate_chunk(&block_chunks[10]), Ok(ChunkType::Block(manifest.block_hashes[10].clone()))); - assert_eq!(snapshot.completed_chunks.len(), 2); - assert_eq!(snapshot.downloading_chunks.len(), 38); - - for (i, data) in state_chunks.iter().enumerate() { - if i != 4 { - assert!(snapshot.validate_chunk(data).is_ok()); - } - } - - for (i, data) in block_chunks.iter().enumerate() { - if i != 10 { - assert!(snapshot.validate_chunk(data).is_ok()); - } - } - - assert!(snapshot.is_complete()); - assert_eq!(snapshot.done_chunks(), 40); - assert_eq!(snapshot.done_chunks(), snapshot.total_chunks()); - assert_eq!(snapshot.snapshot_hash(), Some(keccak(manifest.into_rlp()))); - } - - #[test] - fn tracks_known_bad() { - let mut snapshot = Snapshot::new(); - let hash = H256::random(); - - assert_eq!(snapshot.is_known_bad(&hash), false); - snapshot.note_bad(hash); - assert_eq!(snapshot.is_known_bad(&hash), true); - } -} diff --git a/ethcore/sync/src/snapshot_sync.rs b/ethcore/sync/src/snapshot_sync.rs new file mode 100644 index 0000000000..ad518b4571 --- /dev/null +++ b/ethcore/sync/src/snapshot_sync.rs @@ -0,0 +1,307 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +use std::collections::HashSet; +use std::iter::FromIterator; + +use ethereum_types::H256; +use keccak_hash::keccak; +use log::trace; +use snapshot::SnapshotService; +use common_types::snapshot::ManifestData; +use indexmap::IndexSet; + +#[derive(PartialEq, Eq, Debug)] +/// The type of data contained in a chunk: state or block. +pub enum ChunkType { + /// The chunk contains state data (aka account data). + State(H256), + /// The chunk contains block data. + Block(H256), +} + +#[derive(Default, MallocSizeOf)] +pub struct Snapshot { + /// List of hashes of the state chunks we need to complete the warp sync from this snapshot. + /// These hashes are contained in the Manifest we downloaded from the peer(s). + /// Note: this is an ordered set so that state restoration happens in order, which keeps + /// memory usage down. + // See https://github.com/paritytech/parity-common/issues/255 + #[ignore_malloc_size_of = "no impl for IndexSet (yet)"] + pending_state_chunks: IndexSet, + /// List of hashes of the block chunks we need to complete the warp sync from this snapshot. + /// These hashes are contained in the Manifest we downloaded from the peer(s). + /// Note: this is an ordered set so that state restoration happens in order, which keeps + /// memory usage down. + // See https://github.com/paritytech/parity-common/issues/255 + #[ignore_malloc_size_of = "no impl for IndexSet (yet)"] + pending_block_chunks: IndexSet, + /// Set of hashes of chunks we are currently downloading. + downloading_chunks: HashSet, + /// The set of chunks (block or state) that we have successfully downloaded. + completed_chunks: HashSet, + /// The hash of the the `ManifestData` RLP that we're downloading. + snapshot_hash: Option, + /// Total number of chunks in the current snapshot. + total_chunks: Option, + /// Set of snapshot hashes we failed to import. We will not try to sync with + /// this snapshot again until restart. + bad_hashes: HashSet, + initialized: bool, +} + +impl Snapshot { + /// Create a new instance. + pub fn new() -> Self { + Default::default() + } + + /// Sync the Snapshot completed chunks with the Snapshot Service + pub fn initialize(&mut self, snapshot_service: &dyn SnapshotService, total_chunks: usize) { + if self.initialized { + return; + } + + if let Some(completed_chunks) = snapshot_service.completed_chunks() { + self.completed_chunks = HashSet::from_iter(completed_chunks); + } + + trace!( + target: "snapshot_sync", + "Snapshot initialized. {}/{} completed chunks.", + self.completed_chunks.len(), total_chunks + ); + self.total_chunks = Some(total_chunks); + self.initialized = true; + } + + /// Clear everything and set `initialized` to false. + pub fn clear(&mut self) { + self.pending_state_chunks.clear(); + self.pending_block_chunks.clear(); + self.downloading_chunks.clear(); + self.completed_chunks.clear(); + self.snapshot_hash = None; + self.total_chunks = None; + self.initialized = false; + } + + /// Check if we're currently downloading a snapshot. + pub fn have_manifest(&self) -> bool { + self.snapshot_hash.is_some() + } + + /// Clear the `Snapshot` and reset it with data from a `ManifestData` (i.e. the lists of + /// block&state chunk hashes contained in the `ManifestData`). + pub fn reset_to(&mut self, manifest: &ManifestData, hash: &H256) { + self.clear(); + self.pending_state_chunks = IndexSet::from_iter(manifest.state_hashes.clone()); + self.pending_block_chunks = IndexSet::from_iter(manifest.block_hashes.clone()); + self.total_chunks = Some(self.pending_block_chunks.len() + self.pending_state_chunks.len()); + self.snapshot_hash = Some(hash.clone()); + } + + /// Check if the the chunk is known, i.e. downloaded already or currently downloading; if so add + /// it to the `completed_chunks` set. + /// Returns a `ChunkType` with the hash of the chunk. + pub fn validate_chunk(&mut self, chunk: &[u8]) -> Result { + let hash = keccak(chunk); + if self.completed_chunks.contains(&hash) { + trace!(target: "snapshot_sync", "Already proccessed chunk {:x}. Ignoring.", hash); + return Err(()); + } + self.downloading_chunks.remove(&hash); + + self.pending_block_chunks.take(&hash) + .and_then(|h| { + self.completed_chunks.insert(h); + Some(ChunkType::Block(hash)) + }) + .or( + self.pending_state_chunks.take(&hash) + .and_then(|h| { + self.completed_chunks.insert(h); + Some(ChunkType::State(hash)) + }) + ).ok_or_else(|| { + trace!(target: "snapshot_sync", "Ignoring unknown chunk: {:x}", hash); + () + }) + } + + /// Pick a chunk to download. + /// Note: the order in which chunks are processed is somewhat important. The account state + /// sometimes spills over into more than one chunk and the parts of state that are missing + /// pieces are held in memory while waiting for the next chunk(s) to show up. This means that + /// when chunks are processed out-of-order, memory usage goes up, sometimes significantly (see + /// e.g. https://github.com/paritytech/parity-ethereum/issues/8825). + pub fn needed_chunk(&mut self) -> Option { + // Find next needed chunk: first block, then state chunks + let chunk = { + let filter = |h| !self.downloading_chunks.contains(h) && !self.completed_chunks.contains(h); + self.pending_block_chunks.iter() + .find(|&h| filter(h)) + .or(self.pending_state_chunks.iter() + .find(|&h| filter(h)) + ) + .map(|h| *h) + }; + if let Some(hash) = chunk { + self.downloading_chunks.insert(hash.clone()); + } + chunk + } + + /// Remove a chunk from the set of chunks we're interested in downloading. + pub fn clear_chunk_download(&mut self, hash: &H256) { + self.downloading_chunks.remove(hash); + } + + /// Mark a snapshot hash as bad. + pub fn note_bad(&mut self, hash: H256) { + self.bad_hashes.insert(hash); + } + + /// Whether a snapshot hash is known to be bad. + pub fn is_known_bad(&self, hash: &H256) -> bool { + self.bad_hashes.contains(hash) + } + + /// Hash of the snapshot we're currently downloading/importing. + pub fn snapshot_hash(&self) -> Option { + self.snapshot_hash + } + + /// Total number of chunks in the snapshot we're currently working on (state + block chunks). + pub fn total_chunks(&self) -> usize { + self.total_chunks.unwrap_or_default() + } + + /// Number of chunks we've processed so far (state and block chunks). + pub fn done_chunks(&self) -> usize { + self.completed_chunks.len() + } + + /// Are we done downloading all chunks? + pub fn is_complete(&self) -> bool { + self.total_chunks() == self.completed_chunks.len() + } +} + +#[cfg(test)] +mod test { + use super::{ChunkType, H256, Snapshot}; + + use bytes::Bytes; + use keccak_hash::keccak; + use common_types::snapshot::ManifestData; + + fn is_empty(snapshot: &Snapshot) -> bool { + snapshot.pending_block_chunks.is_empty() && + snapshot.pending_state_chunks.is_empty() && + snapshot.completed_chunks.is_empty() && + snapshot.downloading_chunks.is_empty() && + snapshot.snapshot_hash.is_none() + } + + fn test_manifest() -> (ManifestData, H256, Vec, Vec) { + let state_chunks: Vec = (0..20).map(|_| H256::random().as_bytes().to_vec()).collect(); + let block_chunks: Vec = (0..20).map(|_| H256::random().as_bytes().to_vec()).collect(); + let manifest = ManifestData { + version: 2, + state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(), + block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(), + state_root: H256::zero(), + block_number: 42, + block_hash: H256::zero(), + }; + let mhash = keccak(manifest.clone().into_rlp()); + (manifest, mhash, state_chunks, block_chunks) + } + + #[test] + fn create_clear() { + let mut snapshot = Snapshot::new(); + assert!(is_empty(&snapshot)); + let (manifest, mhash, _, _,) = test_manifest(); + snapshot.reset_to(&manifest, &mhash); + assert!(!is_empty(&snapshot)); + snapshot.clear(); + assert!(is_empty(&snapshot)); + } + + #[test] + fn validate_chunks() { + let mut snapshot = Snapshot::new(); + let (manifest, mhash, state_chunks, block_chunks) = test_manifest(); + snapshot.reset_to(&manifest, &mhash); + assert_eq!(snapshot.done_chunks(), 0, "no chunks done at outset"); + assert!(snapshot.validate_chunk(&H256::random().as_bytes().to_vec()).is_err(), "random chunk is invalid"); + + // request all 20 + 20 chunks + let requested: Vec = (0..40).map(|_| snapshot.needed_chunk().unwrap()).collect(); + assert!(snapshot.needed_chunk().is_none(), "no chunks left after all are drained"); + + let requested_all_block_chunks = manifest.block_hashes.iter() + .all(|h| requested.iter().any(|rh| rh == h)); + assert!(requested_all_block_chunks, "all block chunks in the manifest accounted for"); + + let requested_all_state_chunks = manifest.state_hashes.iter() + .all(|h| requested.iter().any(|rh| rh == h)); + assert!(requested_all_state_chunks, "all state chunks in the manifest accounted for"); + + assert_eq!(snapshot.downloading_chunks.len(), 40, "all requested chunks are downloading"); + + assert_eq!( + snapshot.validate_chunk(&state_chunks[4]), + Ok(ChunkType::State(manifest.state_hashes[4].clone())), + "4th state chunk hash validates as such" + ); + assert_eq!(snapshot.completed_chunks.len(), 1, "after validating a chunk, it's in the completed set"); + assert_eq!(snapshot.downloading_chunks.len(), 39, "after validating a chunk, there's one less in the downloading set"); + + assert_eq!(snapshot.validate_chunk(&block_chunks[10]), Ok(ChunkType::Block(manifest.block_hashes[10].clone()))); + assert_eq!(snapshot.completed_chunks.len(), 2); + assert_eq!(snapshot.downloading_chunks.len(), 38); + + for (i, data) in state_chunks.iter().enumerate() { + if i != 4 { + assert!(snapshot.validate_chunk(data).is_ok()); + } + } + + for (i, data) in block_chunks.iter().enumerate() { + if i != 10 { + assert!(snapshot.validate_chunk(data).is_ok()); + } + } + + assert!(snapshot.is_complete(), "when all chunks have been validated, we're done"); + assert_eq!(snapshot.done_chunks(), 40); + assert_eq!(snapshot.done_chunks(), snapshot.total_chunks()); + assert_eq!(snapshot.snapshot_hash(), Some(keccak(manifest.into_rlp()))); + } + + #[test] + fn tracks_known_bad() { + let mut snapshot = Snapshot::new(); + let hash = H256::random(); + + assert_eq!(snapshot.is_known_bad(&hash), false); + snapshot.note_bad(hash); + assert_eq!(snapshot.is_known_bad(&hash), true); + } +} diff --git a/ethcore/sync/src/sync_io.rs b/ethcore/sync/src/sync_io.rs index 56bf98ab2e..b92e0cd296 100644 --- a/ethcore/sync/src/sync_io.rs +++ b/ethcore/sync/src/sync_io.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,15 +14,19 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +use std::sync::Arc; use std::collections::HashMap; -use chain::sync_packet::{PacketInfo, SyncPacket}; -use network::{NetworkContext, PeerId, PacketId, Error, SessionInfo, ProtocolId}; -use network::client_version::ClientVersion; + +use crate::chain::sync_packet::{PacketInfo, SyncPacket}; + use bytes::Bytes; -use ethcore::client::BlockChainClient; -use types::BlockNumber; -use ethcore::snapshot::SnapshotService; +use client_traits::BlockChainClient; +use ethcore_private_tx::PrivateStateDB; +use network::client_version::ClientVersion; +use network::{NetworkContext, PeerId, PacketId, Error, SessionInfo, ProtocolId}; use parking_lot::RwLock; +use snapshot::SnapshotService; +use common_types::BlockNumber; /// IO interface for the syncing handler. /// Provides peer connection management and an interface to the blockchain client. @@ -37,13 +41,17 @@ pub trait SyncIo { /// Send a packet to a peer using specified protocol. fn send(&mut self, peer_id: PeerId, packet_id: SyncPacket, data: Vec) -> Result<(), Error>; /// Get the blockchain - fn chain(&self) -> &BlockChainClient; + fn chain(&self) -> &dyn BlockChainClient; /// Get the snapshot service. - fn snapshot_service(&self) -> &SnapshotService; + fn snapshot_service(&self) -> &dyn SnapshotService; + /// Get the private state wrapper + fn private_state(&self) -> Option>; /// Returns peer version identifier fn peer_version(&self, peer_id: PeerId) -> ClientVersion { ClientVersion::from(peer_id.to_string()) } + /// Returns the peer enode string + fn peer_enode(&self, peer_id: PeerId) -> Option; /// Returns information on p2p session fn peer_session_info(&self, peer_id: PeerId) -> Option; /// Maximum mutually supported ETH protocol version @@ -64,23 +72,26 @@ pub trait SyncIo { /// Wraps `NetworkContext` and the blockchain client pub struct NetSyncIo<'s> { - network: &'s NetworkContext, - chain: &'s BlockChainClient, - snapshot_service: &'s SnapshotService, + network: &'s dyn NetworkContext, + chain: &'s dyn BlockChainClient, + snapshot_service: &'s dyn SnapshotService, chain_overlay: &'s RwLock>, + private_state: Option>, } impl<'s> NetSyncIo<'s> { /// Creates a new instance from the `NetworkContext` and the blockchain client reference. - pub fn new(network: &'s NetworkContext, - chain: &'s BlockChainClient, - snapshot_service: &'s SnapshotService, - chain_overlay: &'s RwLock>) -> NetSyncIo<'s> { + pub fn new(network: &'s dyn NetworkContext, + chain: &'s dyn BlockChainClient, + snapshot_service: &'s dyn SnapshotService, + chain_overlay: &'s RwLock>, + private_state: Option>) -> NetSyncIo<'s> { NetSyncIo { - network: network, - chain: chain, - snapshot_service: snapshot_service, - chain_overlay: chain_overlay, + network, + chain, + snapshot_service, + chain_overlay, + private_state, } } } @@ -102,24 +113,32 @@ impl<'s> SyncIo for NetSyncIo<'s> { self.network.send_protocol(packet_id.protocol(), peer_id, packet_id.id(), data) } - fn chain(&self) -> &BlockChainClient { + fn chain(&self) -> &dyn BlockChainClient { self.chain } - fn chain_overlay(&self) -> &RwLock> { - self.chain_overlay + fn snapshot_service(&self) -> &dyn SnapshotService { + self.snapshot_service } - fn snapshot_service(&self) -> &SnapshotService { - self.snapshot_service + fn private_state(&self) -> Option> { + self.private_state.clone() } - fn peer_session_info(&self, peer_id: PeerId) -> Option { - self.network.session_info(peer_id) + fn peer_version(&self, peer_id: PeerId) -> ClientVersion { + self.network.peer_client_version(peer_id) } - fn is_expired(&self) -> bool { - self.network.is_expired() + fn peer_enode(&self, peer_id: PeerId) -> Option { + self.network.session_info(peer_id).and_then(|info| { + info.id.map(|node_id| { + format!("enode:://{}@{}", node_id, info.remote_address) + }) + }) + } + + fn peer_session_info(&self, peer_id: PeerId) -> Option { + self.network.session_info(peer_id) } fn eth_protocol_version(&self, peer_id: PeerId) -> u8 { @@ -130,8 +149,12 @@ impl<'s> SyncIo for NetSyncIo<'s> { self.network.protocol_version(*protocol, peer_id).unwrap_or(0) } - fn peer_version(&self, peer_id: PeerId) -> ClientVersion { - self.network.peer_client_version(peer_id) + fn is_expired(&self) -> bool { + self.network.is_expired() + } + + fn chain_overlay(&self) -> &RwLock> { + self.chain_overlay } fn payload_soft_limit(&self) -> usize { diff --git a/ethcore/sync/src/tests/chain.rs b/ethcore/sync/src/tests/chain.rs index d81a876d7a..cd6a8b7c63 100644 --- a/ethcore/sync/src/tests/chain.rs +++ b/ethcore/sync/src/tests/chain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,14 +15,20 @@ // along with Parity Ethereum. If not, see . use std::sync::Arc; -use ethcore::client::{TestBlockChainClient, BlockChainClient, BlockId, EachBlockWith, ChainInfo, BlockInfo}; -use chain::{SyncState}; -use super::helpers::*; -use {SyncConfig, WarpSync}; + +use crate::{ + api::{SyncConfig, WarpSync}, + chain::SyncState, + tests::helpers::TestNet, +}; + +use client_traits::{BlockChainClient, BlockInfo, ChainInfo}; +use common_types::ids::BlockId; +use ethcore::test_helpers::{TestBlockChainClient, EachBlockWith}; #[test] fn two_peers() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut net = TestNet::new(3); net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); @@ -33,7 +39,7 @@ fn two_peers() { #[test] fn long_chain() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut net = TestNet::new(2); net.peer(1).chain.add_blocks(50000, EachBlockWith::Nothing); net.sync(); @@ -43,7 +49,7 @@ fn long_chain() { #[test] fn status_after_sync() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut net = TestNet::new(3); net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); @@ -63,7 +69,7 @@ fn takes_few_steps() { #[test] fn empty_blocks() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut net = TestNet::new(3); for n in 0..200 { let with = if n % 2 == 0 { EachBlockWith::Nothing } else { EachBlockWith::Uncle }; @@ -77,7 +83,7 @@ fn empty_blocks() { #[test] fn forked() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut net = TestNet::new(3); net.peer(0).chain.add_blocks(30, EachBlockWith::Uncle); net.peer(1).chain.add_blocks(30, EachBlockWith::Uncle); @@ -98,10 +104,10 @@ fn forked() { #[test] fn forked_with_misbehaving_peer() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut net = TestNet::new(3); - let mut alt_spec = ::ethcore::spec::Spec::new_test(); + let mut alt_spec = spec::new_test(); alt_spec.extra_data = b"fork".to_vec(); // peer 0 is on a totally different chain with higher total difficulty net.peer_mut(0).chain = Arc::new(TestBlockChainClient::new_with_spec(alt_spec)); @@ -122,7 +128,7 @@ fn forked_with_misbehaving_peer() { #[test] fn net_hard_fork() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let ref_client = TestBlockChainClient::new(); ref_client.add_blocks(50, EachBlockWith::Uncle); { @@ -141,7 +147,7 @@ fn net_hard_fork() { #[test] fn restart() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut net = TestNet::new(3); net.peer(1).chain.add_blocks(1000, EachBlockWith::Uncle); net.peer(2).chain.add_blocks(1000, EachBlockWith::Uncle); @@ -225,7 +231,7 @@ fn propagate_blocks() { #[test] fn restart_on_malformed_block() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut net = TestNet::new(2); net.peer(1).chain.add_blocks(5, EachBlockWith::Nothing); net.peer(1).chain.add_block(EachBlockWith::Nothing, |mut header| { @@ -251,7 +257,7 @@ fn reject_on_broken_chain() { #[test] fn disconnect_on_unrelated_chain() { - ::env_logger::try_init().ok(); + env_logger::try_init().ok(); let mut net = TestNet::new(2); net.peer(0).chain.set_history(Some(20)); net.peer(1).chain.set_history(Some(20)); diff --git a/ethcore/sync/src/tests/consensus.rs b/ethcore/sync/src/tests/consensus.rs index df09366338..258609ec10 100644 --- a/ethcore/sync/src/tests/consensus.rs +++ b/ethcore/sync/src/tests/consensus.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,24 +15,31 @@ // along with Parity Ethereum. If not, see . use std::sync::Arc; -use hash::keccak; -use ethereum_types::{U256, Address}; -use io::{IoHandler, IoChannel}; -use ethcore::client::{ChainInfo, ClientIoMessage}; -use ethcore::engines; -use ethcore::spec::Spec; + +use crate::{ + api::SyncConfig, + tests::helpers::{TestIoHandler, TestNet}, +}; + +use client_traits::ChainInfo; +use engine::signer; +use ethcore::client::Client; use ethcore::miner::{self, MinerService}; -use ethkey::{KeyPair, Secret}; -use types::transaction::{Action, PendingTransaction, Transaction}; -use super::helpers::*; -use SyncConfig; +use ethcore_io::{IoHandler, IoChannel}; +use ethereum_types::{U256, Address}; +use parity_crypto::publickey::{KeyPair, Secret}; +use keccak_hash::keccak; +use common_types::{ + io_message::ClientIoMessage, + transaction::{Action, PendingTransaction, Transaction} +}; fn new_tx(secret: &Secret, nonce: U256, chain_id: u64) -> PendingTransaction { let signed = Transaction { nonce: nonce.into(), gas_price: 0.into(), gas: 21000.into(), - action: Action::Call(Address::default()), + action: Action::Call(Address::zero()), value: 0.into(), data: Vec::new(), }.sign(secret, Some(chain_id)); @@ -41,16 +48,16 @@ fn new_tx(secret: &Secret, nonce: U256, chain_id: u64) -> PendingTransaction { #[test] fn authority_round() { - let s0 = KeyPair::from_secret_slice(&keccak("1")).unwrap(); - let s1 = KeyPair::from_secret_slice(&keccak("0")).unwrap(); + let s0 = KeyPair::from_secret_slice(keccak("1").as_bytes()).unwrap(); + let s1 = KeyPair::from_secret_slice(keccak("0").as_bytes()).unwrap(); - let chain_id = Spec::new_test_round().chain_id(); - let mut net = TestNet::with_spec(2, SyncConfig::default(), Spec::new_test_round); - 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())); + let chain_id = spec::new_test_round().chain_id(); + let mut net = TestNet::with_spec(2, SyncConfig::default(), spec::new_test_round); + 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).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s0.clone()))); - net.peer(1).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s1.clone()))); + net.peer(0).miner.set_author(miner::Author::Sealer(signer::from_keypair(s0.clone()))); + net.peer(1).miner.set_author(miner::Author::Sealer(signer::from_keypair(s1.clone()))); 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))); @@ -114,7 +121,7 @@ fn authority_round() { net.peer(1).chain.engine().step(); net.peer(1).chain.engine().step(); assert_eq!(net.peer(1).chain.chain_info().best_block_number, 5); - // Reorg to the longest chain one not ealier view one. + // Reorg to the longest chain one not earlier view one. net.sync(); let ci0 = net.peer(0).chain.chain_info(); let ci1 = net.peer(1).chain.chain_info(); diff --git a/ethcore/sync/src/tests/helpers.rs b/ethcore/sync/src/tests/helpers.rs index 8bc4b542e2..02839f6877 100644 --- a/ethcore/sync/src/tests/helpers.rs +++ b/ethcore/sync/src/tests/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,28 +16,42 @@ use std::collections::{VecDeque, HashSet, HashMap}; use std::sync::Arc; + +use crate::{ + api::{SyncConfig, WARP_SYNC_PROTOCOL_ID}, + chain::{ + sync_packet::{ + PacketInfo, + SyncPacket::{self, PrivateTransactionPacket, SignedPrivateTransactionPacket} + }, + ChainSync, SyncSupplier, ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_4 + }, + private_tx::SimplePrivateTxHandler, + sync_io::SyncIo, + tests::snapshot::TestSnapshotService, +}; + +use client_traits::{BlockChainClient, ChainNotify}; +use common_types::{ + chain_notify::{NewBlocks, ChainMessageType}, + io_message::ClientIoMessage, + BlockNumber, +}; +use ethcore::{ + client::{Client as EthcoreClient, ClientConfig}, + test_helpers::{self, TestBlockChainClient}, +}; +use ethcore::miner::Miner; +use ethcore_io::{IoChannel, IoContext, IoHandler}; +use ethcore_private_tx::PrivateStateDB; use ethereum_types::H256; -use parking_lot::{RwLock, Mutex}; use bytes::Bytes; use network::{self, PeerId, ProtocolId, PacketId, SessionInfo}; use network::client_version::ClientVersion; -use tests::snapshot::*; -use ethcore::client::{TestBlockChainClient, BlockChainClient, Client as EthcoreClient, - ClientConfig, ChainNotify, NewBlocks, ChainMessageType, ClientIoMessage}; -use ethcore::snapshot::SnapshotService; -use ethcore::spec::Spec; -use ethcore::miner::Miner; -use ethcore::test_helpers; -use sync_io::SyncIo; -use io::{IoChannel, IoContext, IoHandler}; -use api::WARP_SYNC_PROTOCOL_ID; -use chain::{ChainSync, SyncSupplier, ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_3}; -use chain::sync_packet::{PacketInfo, SyncPacket}; -use chain::sync_packet::SyncPacket::{PrivateTransactionPacket, SignedPrivateTransactionPacket}; - -use SyncConfig; -use private_tx::SimplePrivateTxHandler; -use types::BlockNumber; +use log::trace; +use snapshot::SnapshotService; +use spec::Spec; +use parking_lot::{RwLock, Mutex}; pub trait FlushingBlockChainClient: BlockChainClient { fn flush(&self) {} @@ -59,20 +73,28 @@ pub struct TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { pub to_disconnect: HashSet, pub packets: Vec, pub peers_info: HashMap, + pub private_state_db: Option>, overlay: RwLock>, } impl<'p, C> TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { - pub fn new(chain: &'p C, ss: &'p TestSnapshotService, queue: &'p RwLock>, sender: Option) -> TestIo<'p, C> { + pub fn new( + chain: &'p C, + ss: &'p TestSnapshotService, + queue: &'p RwLock>, + sender: Option, + private_state_db: Option> + ) -> TestIo<'p, C> { TestIo { - chain: chain, + chain, snapshot_service: ss, - queue: queue, - sender: sender, + queue, + sender, to_disconnect: HashSet::new(), - overlay: RwLock::new(HashMap::new()), packets: Vec::new(), peers_info: HashMap::new(), + private_state_db, + overlay: RwLock::new(HashMap::new()), } } } @@ -92,42 +114,41 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { self.to_disconnect.insert(peer_id); } - fn is_expired(&self) -> bool { - false - } - fn respond(&mut self, packet_id: PacketId, data: Vec) -> Result<(), network::Error> { - self.packets.push(TestPacket { - data: data, - packet_id: packet_id, - recipient: self.sender.unwrap() - }); + self.packets.push( + TestPacket { data, packet_id, recipient: self.sender.unwrap() } + ); Ok(()) } fn send(&mut self,peer_id: PeerId, packet_id: SyncPacket, data: Vec) -> Result<(), network::Error> { - self.packets.push(TestPacket { - data: data, - packet_id: packet_id.id(), - recipient: peer_id, - }); + self.packets.push( + TestPacket { data, packet_id: packet_id.id(), recipient: peer_id } + ); Ok(()) } - fn chain(&self) -> &BlockChainClient { + fn chain(&self) -> &dyn BlockChainClient { &*self.chain } + fn snapshot_service(&self) -> &dyn SnapshotService { + self.snapshot_service + } + + fn private_state(&self) -> Option> { + self.private_state_db.clone() + } + fn peer_version(&self, peer_id: PeerId) -> ClientVersion { - let client_id = self.peers_info.get(&peer_id) + self.peers_info.get(&peer_id) .cloned() - .unwrap_or_else(|| peer_id.to_string()); - - ClientVersion::from(client_id) + .unwrap_or_else(|| peer_id.to_string()) + .into() } - fn snapshot_service(&self) -> &SnapshotService { - self.snapshot_service + fn peer_enode(&self, _peer_id: usize) -> Option { + unimplemented!() } fn peer_session_info(&self, _peer_id: PeerId) -> Option { @@ -139,7 +160,11 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { } fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { - if protocol == &WARP_SYNC_PROTOCOL_ID { PAR_PROTOCOL_VERSION_3.0 } else { self.eth_protocol_version(peer_id) } + if protocol == &WARP_SYNC_PROTOCOL_ID { PAR_PROTOCOL_VERSION_4.0 } else { self.eth_protocol_version(peer_id) } + } + + fn is_expired(&self) -> bool { + false } fn chain_overlay(&self) -> &RwLock> { @@ -219,6 +244,7 @@ pub struct EthPeer where C: FlushingBlockChainClient { pub private_tx_handler: Arc, pub io_queue: RwLock>, new_blocks_queue: RwLock>, + private_state_db: RwLock>>, } impl EthPeer where C: FlushingBlockChainClient { @@ -231,18 +257,20 @@ impl EthPeer where C: FlushingBlockChainClient { } fn process_io_message(&self, message: ChainMessageType) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None, self.private_state_db()); match message { ChainMessageType::Consensus(data) => self.sync.write().propagate_consensus_packet(&mut io, data), ChainMessageType::PrivateTransaction(transaction_hash, data) => self.sync.write().propagate_private_transaction(&mut io, transaction_hash, PrivateTransactionPacket, data), ChainMessageType::SignedPrivateTransaction(transaction_hash, data) => self.sync.write().propagate_private_transaction(&mut io, transaction_hash, SignedPrivateTransactionPacket, data), + ChainMessageType::PrivateStateRequest(hash) => + self.sync.write().request_private_state(&mut io, &hash), } } fn process_new_block_message(&self, message: NewBlockMessage) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None, self.private_state_db()); self.sync.write().chain_new_blocks( &mut io, &message.imported, @@ -253,6 +281,15 @@ impl EthPeer where C: FlushingBlockChainClient { &message.proposed ); } + + pub fn set_private_state_db(&self, db: Arc) { + *self.private_state_db.write() = Some(db); + } + + fn private_state_db(&self) -> Option> { + let db = self.private_state_db.read(); + db.clone() + } } impl Peer for EthPeer { @@ -264,17 +301,18 @@ impl Peer for EthPeer { &*self.chain, &self.snapshot_service, &self.queue, - Some(other)), + Some(other), + self.private_state_db()), other); } fn on_disconnect(&self, other: PeerId) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(other)); + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(other), self.private_state_db()); self.sync.write().on_peer_aborting(&mut io, other); } fn receive_message(&self, from: PeerId, msg: TestPacket) -> HashSet { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(from)); + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(from), self.private_state_db()); SyncSupplier::dispatch_packet(&self.sync, &mut io, from, msg.packet_id, &msg.data); self.chain.flush(); io.to_disconnect.clone() @@ -290,7 +328,7 @@ impl Peer for EthPeer { } fn sync_step(&self) { - let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None); + let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None, self.private_state_db()); self.chain.flush(); self.sync.write().maintain_peers(&mut io); self.sync.write().maintain_sync(&mut io); @@ -299,7 +337,7 @@ impl Peer for EthPeer { } fn restart_sync(&self) { - self.sync.write().restart(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None)); + self.sync.write().restart(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None, self.private_state_db())); } fn process_all_io_messages(&self) { @@ -351,11 +389,12 @@ impl TestNet> { sync: RwLock::new(sync), snapshot_service: ss, chain: Arc::new(chain), - miner: Arc::new(Miner::new_for_tests(&Spec::new_test(), None)), + 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()), + private_state_db: RwLock::new(None), })); } net @@ -409,6 +448,7 @@ impl TestNet> { private_tx_handler, io_queue: RwLock::new(VecDeque::new()), new_blocks_queue: RwLock::new(VecDeque::new()), + private_state_db: RwLock::new(None), }); peer.chain.add_notify(peer.clone()); //private_provider.add_notify(peer.clone()); @@ -507,7 +547,7 @@ impl

TestNet

where P: Peer { impl TestNet> { pub fn trigger_chain_new_blocks(&mut self, peer_id: usize) { let peer = &mut self.peers[peer_id]; - peer.sync.write().chain_new_blocks(&mut TestIo::new(&*peer.chain, &peer.snapshot_service, &peer.queue, None), &[], &[], &[], &[], &[], &[]); + peer.sync.write().chain_new_blocks(&mut TestIo::new(&*peer.chain, &peer.snapshot_service, &peer.queue, None, None), &[], &[], &[], &[], &[], &[]); } } @@ -525,8 +565,8 @@ impl TestIoHandler { } } -impl IoHandler for TestIoHandler { - fn message(&self, _io: &IoContext, net_message: &ClientIoMessage) { +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; diff --git a/ethcore/sync/src/tests/mod.rs b/ethcore/sync/src/tests/mod.rs index 34e04d1960..aafaff5dc2 100644 --- a/ethcore/sync/src/tests/mod.rs +++ b/ethcore/sync/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/sync/src/tests/private.rs b/ethcore/sync/src/tests/private.rs index 24de14d936..8350a8a3d0 100644 --- a/ethcore/sync/src/tests/private.rs +++ b/ethcore/sync/src/tests/private.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,45 +15,56 @@ // along with Parity Ethereum. If not, see . use std::sync::Arc; -use hash::keccak; -use io::{IoHandler, IoChannel}; -use types::transaction::{Transaction, Action}; -use types::ids::BlockId; -use ethcore::CreateContractAddress; -use ethcore::client::{ClientIoMessage, BlockChainClient}; -use ethcore::executive::{contract_address}; -use ethcore::engines; -use ethcore::miner::{self, MinerService}; -use ethcore::spec::Spec; -use ethcore::test_helpers::{push_block_with_transactions}; -use ethcore_private_tx::{Provider, ProviderConfig, NoopEncryptor, Importer, SignedPrivateTransaction, StoringKeyProvider}; -use ethkey::KeyPair; -use tests::helpers::{TestNet, TestIoHandler}; + +use crate::{ + api::SyncConfig, + tests::helpers::{TestIoHandler, TestNet} +}; + +use client_traits::BlockChainClient; +use common_types::{ + ids::BlockId, + io_message::ClientIoMessage, + transaction::{Transaction, Action}, +}; +use engine::signer; +use ethcore::{ + client::Client, + miner::{self, MinerService}, + test_helpers::{CreateContractAddress, push_block_with_transactions, new_db}, +}; +use ethcore_io::{IoHandler, IoChannel}; +use ethcore_private_tx::{ + Provider, ProviderConfig, NoopEncryptor, Importer, SignedPrivateTransaction, StoringKeyProvider +}; +use parity_crypto::publickey::KeyPair; +use keccak_hash::keccak; +use machine::executive::contract_address; use rustc_hex::FromHex; use rlp::Rlp; -use SyncConfig; +use spec::Spec; fn seal_spec() -> Spec { let spec_data = include_str!("../res/private_spec.json"); - Spec::load(&::std::env::temp_dir(), spec_data.as_bytes()).unwrap() + 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 s0 = KeyPair::from_secret_slice(keccak("1").as_bytes()).unwrap(); + let s1 = KeyPair::from_secret_slice(keccak("0").as_bytes()).unwrap(); let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![s0.clone(), s1.clone()])); let mut net = TestNet::with_spec(2, SyncConfig::default(), seal_spec); 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())); + 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(miner::Author::Sealer(engines::signer::from_keypair(s0.clone()))); - net.peer(1).miner.set_author(miner::Author::Sealer(engines::signer::from_keypair(s1.clone()))); + net.peer(0).miner.set_author(miner::Author::Sealer(signer::from_keypair(s0.clone()))); + net.peer(1).miner.set_author(miner::Author::Sealer(signer::from_keypair(s1.clone()))); 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))); @@ -62,22 +73,26 @@ fn send_private_transaction() { let (address, _) = contract_address(CreateContractAddress::FromSenderAndNonce, &s0.address(), &0.into(), &[]); let chain_id = client0.signing_chain_id(); - // Exhange statuses + // Exchange statuses net.sync(); // Setup private providers let validator_config = ProviderConfig{ validator_accounts: vec![s1.address()], signer_account: None, + logs_path: None, + use_offchain_storage: false, }; let signer_config = ProviderConfig{ validator_accounts: Vec::new(), signer_account: Some(s0.address()), + logs_path: None, + use_offchain_storage: false, }; let private_keys = Arc::new(StoringKeyProvider::default()); - + let db = new_db(); let pm0 = Arc::new(Provider::new( client0.clone(), net.peer(0).miner.clone(), @@ -86,6 +101,7 @@ fn send_private_transaction() { signer_config, IoChannel::to_handler(Arc::downgrade(&io_handler0)), private_keys.clone(), + db.key_value().clone(), )); pm0.add_notify(net.peers[0].clone()); @@ -97,6 +113,7 @@ fn send_private_transaction() { validator_config, IoChannel::to_handler(Arc::downgrade(&io_handler1)), private_keys.clone(), + db.key_value().clone(), )); pm1.add_notify(net.peers[1].clone()); @@ -152,3 +169,136 @@ fn send_private_transaction() { let local_transactions = net.peer(0).miner.local_transactions(); assert_eq!(local_transactions.len(), 1); } + +#[test] +fn sync_private_state() { + // Setup two clients + let s0 = KeyPair::from_secret_slice(&keccak("1").as_bytes()).unwrap(); + let s1 = KeyPair::from_secret_slice(&keccak("0").as_bytes()).unwrap(); + + let signer = Arc::new(ethcore_private_tx::KeyPairSigner(vec![s0.clone(), s1.clone()])); + + let mut net = TestNet::with_spec(2, SyncConfig::default(), seal_spec); + 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(miner::Author::Sealer(signer::from_keypair(s0.clone()))); + net.peer(1).miner.set_author(miner::Author::Sealer(signer::from_keypair(s1.clone()))); + 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, + logs_path: None, + use_offchain_storage: true, + }; + + let signer_config = ProviderConfig{ + validator_accounts: Vec::new(), + signer_account: Some(s0.address()), + logs_path: None, + use_offchain_storage: true, + }; + + let private_keys = Arc::new(StoringKeyProvider::default()); + let db0 = new_db(); + let pm0 = Arc::new(Provider::new( + client0.clone(), + net.peer(0).miner.clone(), + signer.clone(), + Box::new(NoopEncryptor::default()), + signer_config, + IoChannel::to_handler(Arc::downgrade(&io_handler0)), + private_keys.clone(), + db0.key_value().clone(), + )); + pm0.add_notify(net.peers[0].clone()); + + let db1 = new_db(); + let pm1 = Arc::new(Provider::new( + client1.clone(), + net.peer(1).miner.clone(), + signer.clone(), + Box::new(NoopEncryptor::default()), + validator_config, + IoChannel::to_handler(Arc::downgrade(&io_handler1)), + private_keys.clone(), + db1.key_value().clone(), + )); + pm1.add_notify(net.peers[1].clone()); + + net.peer(0).set_private_state_db(pm0.private_state_db()); + net.peer(1).set_private_state_db(pm1.private_state_db()); + + // 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); + let _create_res = pm0.create_private_transaction(private_tx); + + //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(); + let _import_res = pm1.import_private_transaction(&private_transaction); + + // Second node requests the state from the first one + net.sync(); + + let synced_hash = validator_handler.synced_hash.lock().clone(); + assert!(pm1.private_state_synced(&synced_hash).is_ok()); + + // Second node has private state up-to-date and can verify the private transaction + // Further should work the standard flow + 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 signature: SignedPrivateTransaction = Rlp::new(&signed_private_transaction).as_val().unwrap(); + assert!(pm0.process_signature(&signature).is_ok()); + let local_transactions = net.peer(0).miner.local_transactions(); + assert_eq!(local_transactions.len(), 1); +} diff --git a/ethcore/sync/src/tests/rpc.rs b/ethcore/sync/src/tests/rpc.rs index 3e0523931e..2122f303f9 100644 --- a/ethcore/sync/src/tests/rpc.rs +++ b/ethcore/sync/src/tests/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/sync/src/tests/snapshot.rs b/ethcore/sync/src/tests/snapshot.rs index d865adc2ad..aac6abe95e 100644 --- a/ethcore/sync/src/tests/snapshot.rs +++ b/ethcore/sync/src/tests/snapshot.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,46 +16,47 @@ use std::collections::HashMap; use std::sync::Arc; -use hash::keccak; + +use crate::{ + api::{SyncConfig, WarpSync}, + tests::helpers::TestNet +}; + +use bytes::Bytes; +use ethcore::test_helpers::EachBlockWith; use ethereum_types::H256; +use keccak_hash::keccak; use parking_lot::Mutex; -use bytes::Bytes; -use ethcore::snapshot::{SnapshotService, ManifestData, RestorationStatus}; -use ethcore::client::EachBlockWith; -use types::BlockNumber; -use super::helpers::*; -use {SyncConfig, WarpSync}; +use snapshot::SnapshotService; +use common_types::{ + BlockNumber, + snapshot::{ManifestData, RestorationStatus}, +}; +#[derive(Default)] pub struct TestSnapshotService { manifest: Option, chunks: HashMap, - restoration_manifest: Mutex>, state_restoration_chunks: Mutex>, block_restoration_chunks: Mutex>, } impl TestSnapshotService { - pub fn new() -> TestSnapshotService { - TestSnapshotService { - manifest: None, - chunks: HashMap::new(), - restoration_manifest: Mutex::new(None), - state_restoration_chunks: Mutex::new(HashMap::new()), - block_restoration_chunks: Mutex::new(HashMap::new()), - } + pub fn new() -> Self { + Default::default() } pub fn new_with_snapshot(num_chunks: usize, block_hash: H256, block_number: BlockNumber) -> TestSnapshotService { let num_state_chunks = num_chunks / 2; let num_block_chunks = num_chunks - num_state_chunks; - let state_chunks: Vec = (0..num_state_chunks).map(|_| H256::random().to_vec()).collect(); - let block_chunks: Vec = (0..num_block_chunks).map(|_| H256::random().to_vec()).collect(); + let state_chunks: Vec = (0..num_state_chunks).map(|_| H256::random().as_bytes().to_vec()).collect(); + let block_chunks: Vec = (0..num_block_chunks).map(|_| H256::random().as_bytes().to_vec()).collect(); let manifest = ManifestData { version: 2, state_hashes: state_chunks.iter().map(|data| keccak(data)).collect(), block_hashes: block_chunks.iter().map(|data| keccak(data)).collect(), - state_root: H256::new(), + state_root: H256::zero(), block_number: block_number, block_hash: block_hash, }; @@ -147,7 +148,7 @@ fn snapshot_sync() { let mut config = SyncConfig::default(); 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)); + let snapshot_service = Arc::new(TestSnapshotService::new_with_snapshot(16, H256::zero(), 500000)); for i in 0..4 { net.peer_mut(i).snapshot_service = snapshot_service.clone(); net.peer(i).chain.add_blocks(1, EachBlockWith::Nothing); diff --git a/ethcore/sync/src/transactions_stats.rs b/ethcore/sync/src/transactions_stats.rs index 91094fa5fd..443f5bfaba 100644 --- a/ethcore/sync/src/transactions_stats.rs +++ b/ethcore/sync/src/transactions_stats.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,16 +14,18 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use api::TransactionStats; use std::hash::BuildHasher; use std::collections::{HashSet, HashMap}; + +use crate::api::TransactionStats; + use ethereum_types::{H256, H512}; use fastmap::H256FastMap; -use types::BlockNumber; +use common_types::BlockNumber; type NodeId = H512; -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, MallocSizeOf)] pub struct Stats { first_seen: BlockNumber, propagated_to: HashMap, @@ -50,7 +52,7 @@ impl<'a> From<&'a Stats> for TransactionStats { } } -#[derive(Debug, Default)] +#[derive(Debug, Default, MallocSizeOf)] pub struct TransactionsStats { pending_transactions: H256FastMap, } @@ -89,17 +91,17 @@ impl TransactionsStats { #[cfg(test)] mod tests { - use std::collections::{HashMap, HashSet}; - use super::{Stats, TransactionsStats}; + use super::{Stats, TransactionsStats, NodeId, H256}; + use macros::hash_map; #[test] fn should_keep_track_of_propagations() { // given let mut stats = TransactionsStats::default(); - let hash = 5.into(); - let enodeid1 = 2.into(); - let enodeid2 = 5.into(); + let hash = H256::from_low_u64_be(5); + let enodeid1 = NodeId::from_low_u64_be(2); + let enodeid2 = NodeId::from_low_u64_be(5); // when stats.propagated(&hash, Some(enodeid1), 5); @@ -121,8 +123,8 @@ mod tests { fn should_remove_hash_from_tracking() { // given let mut stats = TransactionsStats::default(); - let hash = 5.into(); - let enodeid1 = 5.into(); + let hash = H256::from_low_u64_be(5); + let enodeid1 = NodeId::from_low_u64_be(5); stats.propagated(&hash, Some(enodeid1), 10); // when diff --git a/ethcore/trace/Cargo.toml b/ethcore/trace/Cargo.toml new file mode 100644 index 0000000000..ddfd208011 --- /dev/null +++ b/ethcore/trace/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "trace" +description = "Transaction tracing" +version = "0.1.0" +authors = ["Parity Technologies "] +license = "GPL-3.0" +edition = "2018" + +[dependencies] +ethcore-blockchain = { path = "../blockchain" } +ethcore-db = { path = "../db" } +ethereum-types = "0.8.0" +evm = { path = "../evm" } +kvdb = "0.3.1" +log = "0.4" +parity-bytes = "0.1.0" +parity-util-mem = "0.3.0" +parking_lot = "0.9" +rlp = "0.4.0" +rlp_derive = { path = "../../util/rlp-derive" } +vm = { path = "../vm" } + +[dev-dependencies] +# Used for test helpers +ethcore = { path = "..", features = ["test-helpers"] } diff --git a/ethcore/src/trace/config.rs b/ethcore/trace/src/config.rs similarity index 93% rename from ethcore/src/trace/config.rs rename to ethcore/trace/src/config.rs index 72fc176551..5963036974 100644 --- a/ethcore/src/trace/config.rs +++ b/ethcore/trace/src/config.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ pub struct Config { /// Indicates if tracing should be enabled or not. /// If it's None, it will be automatically configured. pub enabled: bool, - /// Preferef cache-size. + /// Preferred cache-size. pub pref_cache_size: usize, /// Max cache-size. pub max_cache_size: usize, diff --git a/ethcore/src/trace/db.rs b/ethcore/trace/src/db.rs similarity index 84% rename from ethcore/src/trace/db.rs rename to ethcore/trace/src/db.rs index 8dbd38449f..4de60272ce 100644 --- a/ethcore/src/trace/db.rs +++ b/ethcore/trace/src/db.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,17 +18,22 @@ use std::collections::HashMap; use std::sync::Arc; -use blockchain::BlockChainDB; -use db::cache_manager::CacheManager; -use db::{self, Key, Writable, Readable, CacheUpdatePolicy}; +use ethcore_blockchain::{BlockProvider, BlockChainDB, TransactionAddress}; +use ethcore_db::{ + self as db, + cache_manager::CacheManager, + Key, Writable, Readable, CacheUpdatePolicy, +}; use ethereum_types::{H256, H264}; -use heapsize::HeapSizeOf; -use kvdb::{DBTransaction}; +use kvdb::DBTransaction; +use parity_util_mem::MallocSizeOfExt; use parking_lot::RwLock; -use types::BlockNumber; -use trace::{LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, DatabaseExtras}; -use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; +use crate::{ + BlockNumber, + LocalizedTrace, Config, Filter, Database as TraceDatabase, ImportRequest, + flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}, +}; const TRACE_DB_VER: &'static [u8] = b"1.0"; @@ -43,12 +48,43 @@ impl Key for H256 { fn key(&self) -> H264 { let mut result = H264::default(); - result[0] = TraceDBIndex::BlockTraces as u8; - result[1..33].copy_from_slice(self); + { + let bytes = result.as_bytes_mut(); + bytes[0] = TraceDBIndex::BlockTraces as u8; + bytes[1..33].copy_from_slice(self.as_bytes()); + } result } } +/// `DatabaseExtras` provides an interface to query extra data which is not stored in TraceDB, +/// but necessary to work correctly. +pub trait DatabaseExtras { + /// Returns hash of given block number. + fn block_hash(&self, block_number: BlockNumber) -> Option; + + /// Returns hash of transaction at given position. + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option; +} + +impl DatabaseExtras for T { + fn block_hash(&self, block_number: BlockNumber) -> Option { + (&*self as &dyn BlockProvider).block_hash(block_number) + } + + fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option { + self.block_hash(block_number) + .and_then(|block_hash| { + let tx_address = TransactionAddress { + block_hash, + index: tx_position + }; + self.transaction(&tx_address) + }) + .map(|tx| tx.hash()) + } +} + /// Database to store transaction execution trace. /// /// Whenever a transaction is executed by EVM it's execution trace is stored @@ -61,7 +97,7 @@ pub struct TraceDB where T: DatabaseExtras { /// hashes of cached traces cache_manager: RwLock>, /// db - db: Arc, + db: Arc, /// tracing enabled enabled: bool, /// extras @@ -70,7 +106,7 @@ pub struct TraceDB where T: DatabaseExtras { impl TraceDB where T: DatabaseExtras { /// Creates new instance of `TraceDB`. - pub fn new(config: Config, db: Arc, extras: Arc) -> Self { + pub fn new(config: Config, db: Arc, extras: Arc) -> Self { let mut batch = DBTransaction::new(); let genesis = extras.block_hash(0) .expect("Genesis block is always inserted upon extras db creation qed"); @@ -83,12 +119,12 @@ impl TraceDB where T: DatabaseExtras { cache_manager: RwLock::new(CacheManager::new(config.pref_cache_size, config.max_cache_size, 10 * 1024)), db, enabled: config.enabled, - extras: extras, + extras, } } fn cache_size(&self) -> usize { - self.traces.read().heap_size_of_children() + self.traces.read().malloc_size_of() } /// Let the cache system know that a cacheable item has been used. @@ -110,7 +146,7 @@ impl TraceDB where T: DatabaseExtras { } traces.shrink_to_fit(); - traces.heap_size_of_children() + traces.malloc_size_of() }); } @@ -167,8 +203,8 @@ impl TraceDB where T: DatabaseExtras { trace_address: trace.trace_address.into_iter().collect(), transaction_number: trace_tx_number, transaction_hash: trace_tx_hash, - block_number: block_number, - block_hash: block_hash + block_number, + block_hash, }), false => None } @@ -246,8 +282,8 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { trace_address: trace.trace_address.into_iter().collect(), transaction_number: Some(tx_position), transaction_hash: Some(tx_hash), - block_number: block_number, - block_hash: block_hash, + block_number, + block_hash, } }) ) @@ -270,8 +306,8 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { trace_address: trace.trace_address.into_iter().collect(), transaction_number: Some(tx_position), transaction_hash: Some(tx_hash.clone()), - block_number: block_number, - block_hash: block_hash + block_number, + block_hash, }) .collect() }) @@ -300,8 +336,8 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { trace_address: trace.trace_address.into_iter().collect(), transaction_number: trace_tx_number, transaction_hash: trace_tx_hash, - block_number: block_number, - block_hash: block_hash, + block_number, + block_hash, }) .collect::>() }) @@ -331,24 +367,28 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { #[cfg(test)] mod tests { - use std::collections::HashMap; - use std::sync::Arc; + use std::{ + collections::HashMap, + sync::Arc, + }; + + use ethcore::test_helpers::new_db; use ethereum_types::{H256, U256, Address}; - use kvdb::{DBTransaction}; - use types::BlockNumber; - use trace::{Config, TraceDB, Database as TraceDatabase, DatabaseExtras, ImportRequest}; - use trace::{Filter, LocalizedTrace, AddressesFilter, TraceError}; - use trace::trace::{Call, Action, Res}; - use trace::flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces}; - use evm::CallType; - use test_helpers::new_db; + use kvdb::DBTransaction; + + use crate::{ + BlockNumber, Config, TraceDB, Database as TraceDatabase, ImportRequest, DatabaseExtras, + Filter, LocalizedTrace, AddressesFilter, TraceError, + trace::{Call, CallType, Action, Res}, + flat::{FlatTrace, FlatBlockTraces, FlatTransactionTraces} + }; struct NoopExtras; impl DatabaseExtras for NoopExtras { fn block_hash(&self, block_number: BlockNumber) -> Option { if block_number == 0 { - Some(H256::default()) + Some(H256::zero()) } else { unimplemented!() } @@ -419,17 +459,17 @@ mod tests { trace_address: Default::default(), subtraces: 0, action: Action::Call(Call { - from: 1.into(), - to: 2.into(), + from: Address::from_low_u64_be(1), + to: Address::from_low_u64_be(2), value: 3.into(), gas: 4.into(), input: vec![], - call_type: CallType::Call, + call_type: Some(CallType::Call).into(), }), result: Res::FailedCall(TraceError::OutOfGas), }])]), block_hash: block_hash.clone(), - block_number: block_number, + block_number, enacted: vec![block_hash], retracted: 0, } @@ -441,17 +481,17 @@ mod tests { trace_address: Default::default(), subtraces: 0, action: Action::Call(Call { - from: 1.into(), - to: 2.into(), + from: Address::from_low_u64_be(1), + to: Address::from_low_u64_be(2), value: 3.into(), gas: 4.into(), input: vec![], - call_type: CallType::Call, + call_type: Some(CallType::Call).into(), }), result: Res::FailedCall(TraceError::OutOfGas), }])]), block_hash: block_hash.clone(), - block_number: block_number, + block_number, enacted: vec![], retracted: 0, } @@ -460,20 +500,20 @@ mod tests { fn create_simple_localized_trace(block_number: BlockNumber, block_hash: H256, tx_hash: H256) -> LocalizedTrace { LocalizedTrace { action: Action::Call(Call { - from: Address::from(1), - to: Address::from(2), + from: Address::from_low_u64_be(1), + to: Address::from_low_u64_be(2), value: U256::from(3), gas: U256::from(4), input: vec![], - call_type: CallType::Call, + call_type: Some(CallType::Call).into(), }), result: Res::FailedCall(TraceError::OutOfGas), trace_address: vec![], subtraces: 0, transaction_number: Some(0), transaction_hash: Some(tx_hash), - block_number: block_number, - block_hash: block_hash, + block_number, + block_hash, } } @@ -482,10 +522,10 @@ mod tests { let db = new_db(); let mut config = Config::default(); config.enabled = true; - let block_0 = H256::from(0xa1); - let block_1 = H256::from(0xa2); - let tx_0 = H256::from(0xff); - let tx_1 = H256::from(0xaf); + let block_0 = H256::from_low_u64_be(0xa1); + let block_1 = H256::from_low_u64_be(0xa2); + let tx_0 = H256::from_low_u64_be(0xff); + let tx_1 = H256::from_low_u64_be(0xaf); let mut extras = Extras::default(); extras.block_hashes.insert(0, block_0.clone()); @@ -509,13 +549,13 @@ mod tests { let db = new_db(); let mut config = Config::default(); config.enabled = true; - let block_1 = H256::from(0xa1); - let block_2 = H256::from(0xa2); - let tx_1 = H256::from(0xff); - let tx_2 = H256::from(0xaf); + let block_1 = H256::from_low_u64_be(0xa1); + let block_2 = H256::from_low_u64_be(0xa2); + let tx_1 = H256::from_low_u64_be(0xff); + let tx_2 = H256::from_low_u64_be(0xaf); let mut extras = Extras::default(); - extras.block_hashes.insert(0, H256::default()); + extras.block_hashes.insert(0, H256::zero()); extras.block_hashes.insert(1, block_1.clone()); extras.block_hashes.insert(2, block_2.clone()); @@ -532,7 +572,7 @@ mod tests { let filter = Filter { range: (1..1), - from_address: AddressesFilter::from(vec![Address::from(1)]), + from_address: AddressesFilter::from(vec![Address::from_low_u64_be(1)]), to_address: AddressesFilter::from(vec![]), }; @@ -548,7 +588,7 @@ mod tests { let filter = Filter { range: (1..2), - from_address: AddressesFilter::from(vec![Address::from(1)]), + from_address: AddressesFilter::from(vec![Address::from_low_u64_be(1)]), to_address: AddressesFilter::from(vec![]), }; @@ -588,10 +628,10 @@ mod tests { let db = new_db(); let mut config = Config::default(); let mut extras = Extras::default(); - let block_0 = H256::from(0xa1); - let tx_0 = H256::from(0xff); + let block_0 = H256::from_low_u64_be(0xa1); + let tx_0 = H256::from_low_u64_be(0xff); - extras.block_hashes.insert(0, H256::default()); + extras.block_hashes.insert(0, H256::zero()); extras.transaction_hashes.insert(0, vec![]); extras.block_hashes.insert(1, block_0.clone()); extras.transaction_hashes.insert(1, vec![tx_0.clone()]); @@ -621,7 +661,7 @@ mod tests { let db = new_db(); let mut config = Config::default(); let mut extras = Extras::default(); - let block_0 = H256::from(0xa1); + let block_0 = H256::from_low_u64_be(0xa1); extras.block_hashes.insert(0, block_0.clone()); extras.transaction_hashes.insert(0, vec![]); diff --git a/ethcore/src/trace/executive_tracer.rs b/ethcore/trace/src/executive_tracer.rs similarity index 97% rename from ethcore/src/trace/executive_tracer.rs rename to ethcore/trace/src/executive_tracer.rs index 1653011c32..04aef1eae2 100644 --- a/ethcore/src/trace/executive_tracer.rs +++ b/ethcore/trace/src/executive_tracer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,8 +20,10 @@ use std::cmp::min; use ethereum_types::{U256, Address}; use vm::{Error as VmError, ActionParams}; use log::{debug, warn}; -use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType}; -use trace::{Tracer, VMTracer, FlatTrace}; +use crate::{ + Tracer, VMTracer, FlatTrace, + trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType}, +}; /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. #[derive(Default)] @@ -77,7 +79,7 @@ impl Tracer for ExecutiveTracer { result: Res::Create(CreateResult { gas_used: U256::zero(), code: Vec::new(), - address: Address::default(), + address: Address::zero(), }), }; self.vecindex_stack.push(self.traces.len()); diff --git a/ethcore/src/trace/import.rs b/ethcore/trace/src/import.rs similarity index 91% rename from ethcore/src/trace/import.rs rename to ethcore/trace/src/import.rs index e9ec9c77ba..389073540f 100644 --- a/ethcore/src/trace/import.rs +++ b/ethcore/trace/src/import.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,9 +16,8 @@ //! Traces import request. use ethereum_types::H256; -use types::BlockNumber; -use trace::FlatBlockTraces; +use crate::{FlatBlockTraces, BlockNumber}; /// Traces import request. pub struct ImportRequest { diff --git a/ethcore/src/trace/mod.rs b/ethcore/trace/src/lib.rs similarity index 78% rename from ethcore/src/trace/mod.rs rename to ethcore/trace/src/lib.rs index 98521dbb0c..89dd297f43 100644 --- a/ethcore/src/trace/mod.rs +++ b/ethcore/trace/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,6 +16,12 @@ //! Tracing +use ethereum_types::{U256, Address}; +use kvdb::DBTransaction; +use vm::{Error as VmError, ActionParams}; +// The MallocSizeOf derive looks for this in the root +use parity_util_mem as malloc_size_of; + mod config; mod db; mod executive_tracer; @@ -23,23 +29,25 @@ mod import; mod noop_tracer; mod types; -pub use self::config::Config; -pub use self::db::TraceDB; -pub use self::noop_tracer::{NoopTracer, NoopVMTracer}; -pub use self::executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}; -pub use self::import::ImportRequest; -pub use self::localized::LocalizedTrace; - -pub use self::types::{filter, flat, localized, trace, Tracing}; -pub use self::types::error::Error as TraceError; -pub use self::types::trace::{VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, RewardType}; -pub use self::types::flat::{FlatTrace, FlatTransactionTraces, FlatBlockTraces}; -pub use self::types::filter::{Filter, AddressesFilter}; - -use ethereum_types::{H256, U256, Address}; -use kvdb::DBTransaction; -use vm::{Error as VmError, ActionParams}; -use types::BlockNumber; +pub use crate::{ + config::Config, + db::{TraceDB, DatabaseExtras}, + localized::LocalizedTrace, + executive_tracer::{ExecutiveTracer, ExecutiveVMTracer}, + import::ImportRequest, + noop_tracer::{NoopTracer, NoopVMTracer}, + types::{ + Tracing, + error::Error as TraceError, + localized, + trace::{self, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, RewardType}, + flat::{self, FlatTrace, FlatTransactionTraces, FlatBlockTraces}, + filter::{self, Filter, AddressesFilter}, + } +}; + +/// Type for block number. +pub(crate) type BlockNumber = u64; /// This trait is used by executive to build traces. pub trait Tracer: Send { @@ -102,16 +110,6 @@ pub trait VMTracer: Send { } -/// `DbExtras` provides an interface to query extra data which is not stored in tracesdb, -/// but necessary to work correctly. -pub trait DatabaseExtras { - /// Returns hash of given block number. - fn block_hash(&self, block_number: BlockNumber) -> Option; - - /// Returns hash of transaction at given position. - fn transaction_hash(&self, block_number: BlockNumber, tx_position: usize) -> Option; -} - /// Db provides an interface to query tracesdb. pub trait Database { /// Returns true if tracing is enabled. Otherwise false. diff --git a/ethcore/src/trace/noop_tracer.rs b/ethcore/trace/src/noop_tracer.rs similarity index 92% rename from ethcore/src/trace/noop_tracer.rs rename to ethcore/trace/src/noop_tracer.rs index 62cce8e011..74d3e57505 100644 --- a/ethcore/src/trace/noop_tracer.rs +++ b/ethcore/trace/src/noop_tracer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,8 +18,10 @@ use ethereum_types::{U256, Address}; use vm::{Error as VmError, ActionParams}; -use trace::{Tracer, VMTracer, FlatTrace}; -use trace::trace::{VMTrace, RewardType}; +use crate::{ + Tracer, VMTracer, FlatTrace, + trace::{VMTrace, RewardType} +}; /// Nonoperative tracer. Does not trace anything. pub struct NoopTracer; diff --git a/ethcore/src/trace/types/error.rs b/ethcore/trace/src/types/error.rs similarity index 98% rename from ethcore/src/trace/types/error.rs rename to ethcore/trace/src/types/error.rs index 5c775dcb6e..1a59e9dea7 100644 --- a/ethcore/src/trace/types/error.rs +++ b/ethcore/trace/src/types/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -137,7 +137,7 @@ impl Decodable for Error { #[cfg(test)] mod tests { - use rlp::*; + use rlp::RlpStream; use super::Error; #[test] diff --git a/ethcore/src/trace/types/filter.rs b/ethcore/trace/src/types/filter.rs similarity index 59% rename from ethcore/src/trace/types/filter.rs rename to ethcore/trace/src/types/filter.rs index d7b8fcc180..b25e1de02a 100644 --- a/ethcore/src/trace/types/filter.rs +++ b/ethcore/trace/src/types/filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,8 +18,7 @@ use std::ops::Range; use ethereum_types::{Address, Bloom, BloomInput}; -use trace::flat::FlatTrace; -use super::trace::{Action, Res}; +use crate::{flat::FlatTrace, trace::{Action, Res}}; /// Addresses filter. /// @@ -51,7 +50,7 @@ impl AddressesFilter { match self.list.is_empty() { true => vec![Bloom::default()], false => self.list.iter() - .map(|address| Bloom::from(BloomInput::Raw(address))) + .map(|address| Bloom::from(BloomInput::Raw(address.as_bytes()))) .collect(), } } @@ -65,7 +64,7 @@ impl AddressesFilter { .flat_map(|bloom| self.list.iter() .map(|address| { let mut bloom = bloom.clone(); - bloom.accrue(BloomInput::Raw(address)); + bloom.accrue(BloomInput::Raw(address.as_bytes())); bloom }) .collect::>()) @@ -126,10 +125,11 @@ impl Filter { #[cfg(test)] mod tests { use ethereum_types::{Address, Bloom, BloomInput}; - use trace::trace::{Action, Call, Res, Create, CreateResult, Suicide, Reward}; - use trace::flat::FlatTrace; - use trace::{Filter, AddressesFilter, TraceError, RewardType}; - use evm::CallType; + use crate::{ + Filter, AddressesFilter, TraceError, RewardType, + trace::{Action, Call, CallType, Res, Create, CreationMethod, CreateResult, Suicide, Reward}, + flat::FlatTrace, + }; #[test] fn empty_trace_filter_bloom_possibilities() { @@ -147,31 +147,31 @@ mod tests { fn single_trace_filter_bloom_possibility() { let filter = Filter { range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1)]), - to_address: AddressesFilter::from(vec![Address::from(2)]), + from_address: AddressesFilter::from(vec![Address::from_low_u64_be(1)]), + to_address: AddressesFilter::from(vec![Address::from_low_u64_be(2)]), }; let blooms = filter.bloom_possibilities(); assert_eq!(blooms.len(), 1); - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); - assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(3)))); + assert!(blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(1).as_bytes()))); + assert!(blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(2).as_bytes()))); + assert!(!blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(3).as_bytes()))); } #[test] fn only_from_trace_filter_bloom_possibility() { let filter = Filter { range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1)]), + from_address: AddressesFilter::from(vec![Address::from_low_u64_be(1)]), to_address: AddressesFilter::from(vec![]), }; let blooms = filter.bloom_possibilities(); assert_eq!(blooms.len(), 1); - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); + assert!(blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(1).as_bytes()))); + assert!(!blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(2).as_bytes()))); } #[test] @@ -179,59 +179,59 @@ mod tests { let filter = Filter { range: (0..0), from_address: AddressesFilter::from(vec![]), - to_address: AddressesFilter::from(vec![Address::from(1)]), + to_address: AddressesFilter::from(vec![Address::from_low_u64_be(1)]), }; let blooms = filter.bloom_possibilities(); assert_eq!(blooms.len(), 1); - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); + assert!(blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(1).as_bytes()))); + assert!(!blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(2).as_bytes()))); } #[test] fn multiple_trace_filter_bloom_possibility() { let filter = Filter { range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1), Address::from(3)]), - to_address: AddressesFilter::from(vec![Address::from(2), Address::from(4)]), + from_address: AddressesFilter::from(vec![Address::from_low_u64_be(1), Address::from_low_u64_be(3)]), + to_address: AddressesFilter::from(vec![Address::from_low_u64_be(2), Address::from_low_u64_be(4)]), }; let blooms = filter.bloom_possibilities(); assert_eq!(blooms.len(), 4); - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(blooms[0].contains_input(BloomInput::Raw(&Address::from(2)))); - assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(3)))); - assert!(!blooms[0].contains_input(BloomInput::Raw(&Address::from(4)))); - - assert!(blooms[1].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(blooms[1].contains_input(BloomInput::Raw(&Address::from(4)))); - assert!(!blooms[1].contains_input(BloomInput::Raw(&Address::from(2)))); - assert!(!blooms[1].contains_input(BloomInput::Raw(&Address::from(3)))); - - assert!(blooms[2].contains_input(BloomInput::Raw(&Address::from(2)))); - assert!(blooms[2].contains_input(BloomInput::Raw(&Address::from(3)))); - assert!(!blooms[2].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(!blooms[2].contains_input(BloomInput::Raw(&Address::from(4)))); - - assert!(blooms[3].contains_input(BloomInput::Raw(&Address::from(3)))); - assert!(blooms[3].contains_input(BloomInput::Raw(&Address::from(4)))); - assert!(!blooms[3].contains_input(BloomInput::Raw(&Address::from(1)))); - assert!(!blooms[3].contains_input(BloomInput::Raw(&Address::from(2)))); + assert!(blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(1).as_bytes()))); + assert!(blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(2).as_bytes()))); + assert!(!blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(3).as_bytes()))); + assert!(!blooms[0].contains_input(BloomInput::Raw(Address::from_low_u64_be(4).as_bytes()))); + + assert!(blooms[1].contains_input(BloomInput::Raw(Address::from_low_u64_be(1).as_bytes()))); + assert!(blooms[1].contains_input(BloomInput::Raw(Address::from_low_u64_be(4).as_bytes()))); + assert!(!blooms[1].contains_input(BloomInput::Raw(Address::from_low_u64_be(2).as_bytes()))); + assert!(!blooms[1].contains_input(BloomInput::Raw(Address::from_low_u64_be(3).as_bytes()))); + + assert!(blooms[2].contains_input(BloomInput::Raw(Address::from_low_u64_be(2).as_bytes()))); + assert!(blooms[2].contains_input(BloomInput::Raw(Address::from_low_u64_be(3).as_bytes()))); + assert!(!blooms[2].contains_input(BloomInput::Raw(Address::from_low_u64_be(1).as_bytes()))); + assert!(!blooms[2].contains_input(BloomInput::Raw(Address::from_low_u64_be(4).as_bytes()))); + + assert!(blooms[3].contains_input(BloomInput::Raw(Address::from_low_u64_be(3).as_bytes()))); + assert!(blooms[3].contains_input(BloomInput::Raw(Address::from_low_u64_be(4).as_bytes()))); + assert!(!blooms[3].contains_input(BloomInput::Raw(Address::from_low_u64_be(1).as_bytes()))); + assert!(!blooms[3].contains_input(BloomInput::Raw(Address::from_low_u64_be(2).as_bytes()))); } #[test] fn filter_matches() { let f0 = Filter { range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1)]), + from_address: AddressesFilter::from(vec![Address::from_low_u64_be(1)]), to_address: AddressesFilter::from(vec![]), }; let f1 = Filter { range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(3), Address::from(1)]), + from_address: AddressesFilter::from(vec![Address::from_low_u64_be(3), Address::from_low_u64_be(1)]), to_address: AddressesFilter::from(vec![]), }; @@ -244,35 +244,35 @@ mod tests { let f3 = Filter { range: (0..0), from_address: AddressesFilter::from(vec![]), - to_address: AddressesFilter::from(vec![Address::from(2)]), + to_address: AddressesFilter::from(vec![Address::from_low_u64_be(2)]), }; let f4 = Filter { range: (0..0), from_address: AddressesFilter::from(vec![]), - to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]), + to_address: AddressesFilter::from(vec![Address::from_low_u64_be(2), Address::from_low_u64_be(3)]), }; let f5 = Filter { range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1)]), - to_address: AddressesFilter::from(vec![Address::from(2), Address::from(3)]), + from_address: AddressesFilter::from(vec![Address::from_low_u64_be(1)]), + to_address: AddressesFilter::from(vec![Address::from_low_u64_be(2), Address::from_low_u64_be(3)]), }; let f6 = Filter { range: (0..0), - from_address: AddressesFilter::from(vec![Address::from(1)]), - to_address: AddressesFilter::from(vec![Address::from(4)]), + from_address: AddressesFilter::from(vec![Address::from_low_u64_be(1)]), + to_address: AddressesFilter::from(vec![Address::from_low_u64_be(4)]), }; let trace = FlatTrace { action: Action::Call(Call { - from: 1.into(), - to: 2.into(), + from: Address::from_low_u64_be(1), + to: Address::from_low_u64_be(2), value: 3.into(), gas: 4.into(), input: vec![0x5], - call_type: CallType::Call, + call_type: Some(CallType::Call).into(), }), result: Res::FailedCall(TraceError::OutOfGas), trace_address: vec![0].into_iter().collect(), @@ -289,15 +289,16 @@ mod tests { let trace = FlatTrace { action: Action::Create(Create { - from: 1.into(), + from: Address::from_low_u64_be(1), value: 3.into(), gas: 4.into(), init: vec![0x5], + creation_method: Some(CreationMethod::Create), }), result: Res::Create(CreateResult { gas_used: 10.into(), code: vec![], - address: 2.into(), + address: Address::from_low_u64_be(2), }), trace_address: vec![0].into_iter().collect(), subtraces: 0, @@ -313,8 +314,8 @@ mod tests { let trace = FlatTrace { action: Action::Suicide(Suicide { - address: 1.into(), - refund_address: 2.into(), + address: Address::from_low_u64_be(1), + refund_address: Address::from_low_u64_be(2), balance: 3.into(), }), result: Res::None, @@ -332,7 +333,7 @@ mod tests { let trace = FlatTrace { action: Action::Reward(Reward { - author: 2.into(), + author: Address::from_low_u64_be(2), value: 100.into(), reward_type: RewardType::Block, }), @@ -354,7 +355,7 @@ mod tests { fn filter_match_block_reward_fix_8070() { let f0 = Filter { range: (0..0), - from_address: vec![1.into()].into(), + from_address: vec![Address::from_low_u64_be(1)].into(), to_address: vec![].into(), }; @@ -367,12 +368,12 @@ mod tests { let f2 = Filter { range: (0..0), from_address: vec![].into(), - to_address: vec![2.into()].into(), + to_address: vec![Address::from_low_u64_be(2)].into(), }; let trace = FlatTrace { action: Action::Reward(Reward { - author: 2.into(), + author: Address::from_low_u64_be(2), value: 10.into(), reward_type: RewardType::Block, }), @@ -386,43 +387,41 @@ mod tests { assert!(f2.matches(&trace)); } - #[test] - fn filter_match_failed_contract_creation_fix_9822() { - - let f0 = Filter { - range: (0..0), - from_address: vec![1.into()].into(), - to_address: vec![].into(), - }; - - let f1 = Filter { - range: (0..0), - from_address: vec![].into(), - to_address: vec![].into(), - }; - - let f2 = Filter { - range: (0..0), - from_address: vec![].into(), - to_address: vec![2.into()].into(), - }; - - let trace = FlatTrace { - action: Action::Create(Create { - from: 1.into(), - gas: 4.into(), - init: vec![0x5], - value: 3.into(), - }), - result: Res::FailedCall(TraceError::BadInstruction), - trace_address: vec![].into_iter().collect(), - subtraces: 0 - }; - - assert!(f0.matches(&trace)); - assert!(f1.matches(&trace)); - assert!(!f2.matches(&trace)); - } + #[test] + fn filter_match_failed_contract_creation_fix_9822() { + let f0 = Filter { + range: (0..0), + from_address: vec![Address::from_low_u64_be(1)].into(), + to_address: vec![].into(), + }; -} + let f1 = Filter { + range: (0..0), + from_address: vec![].into(), + to_address: vec![].into(), + }; + + let f2 = Filter { + range: (0..0), + from_address: vec![].into(), + to_address: vec![Address::from_low_u64_be(2)].into(), + }; + let trace = FlatTrace { + action: Action::Create(Create { + from: Address::from_low_u64_be(1), + gas: 4.into(), + init: vec![0x5], + value: 3.into(), + creation_method: Some(CreationMethod::Create), + }), + result: Res::FailedCall(TraceError::BadInstruction), + trace_address: vec![].into_iter().collect(), + subtraces: 0 + }; + + assert!(f0.matches(&trace)); + assert!(f1.matches(&trace)); + assert!(!f2.matches(&trace)); + } +} diff --git a/ethcore/src/trace/types/flat.rs b/ethcore/trace/src/types/flat.rs similarity index 87% rename from ethcore/src/trace/types/flat.rs rename to ethcore/trace/src/types/flat.rs index cb3e1229b7..2fad0c27fd 100644 --- a/ethcore/src/trace/types/flat.rs +++ b/ethcore/trace/src/types/flat.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,17 +17,20 @@ //! Flat trace module use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError}; -use heapsize::HeapSizeOf; +use rlp_derive::{RlpEncodableWrapper, RlpDecodableWrapper}; +use parity_util_mem::MallocSizeOf; use ethereum_types::Bloom; use super::trace::{Action, Res}; /// Trace localized in vector of traces produced by a single transaction. /// /// Parent and children indexes refer to positions in this vector. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, MallocSizeOf)] pub struct FlatTrace { + #[ignore_malloc_size_of = "ignored for performance reason"] /// Type of action performed by a transaction. pub action: Action, + #[ignore_malloc_size_of = "ignored for performance reason"] /// Result of this action. pub result: Res, /// Number of subtraces. @@ -45,12 +48,6 @@ impl FlatTrace { } } -impl HeapSizeOf for FlatTrace { - fn heap_size_of_children(&self) -> usize { - self.trace_address.heap_size_of_children() - } -} - impl Encodable for FlatTrace { fn rlp_append(&self, s: &mut RlpStream) { s.begin_list(4); @@ -76,8 +73,8 @@ impl Decodable for FlatTrace { } /// Represents all traces produced by a single transaction. -#[derive(Debug, PartialEq, Clone, RlpEncodableWrapper, RlpDecodableWrapper)] -pub struct FlatTransactionTraces(Vec); +#[derive(Debug, PartialEq, Clone, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] +pub struct FlatTransactionTraces(pub(crate) Vec); impl From> for FlatTransactionTraces { fn from(v: Vec) -> Self { @@ -85,12 +82,6 @@ impl From> for FlatTransactionTraces { } } -impl HeapSizeOf for FlatTransactionTraces { - fn heap_size_of_children(&self) -> usize { - self.0.heap_size_of_children() - } -} - impl FlatTransactionTraces { /// Returns bloom of all traces in the collection. pub fn bloom(&self) -> Bloom { @@ -105,14 +96,8 @@ impl Into> for FlatTransactionTraces { } /// Represents all traces produced by transactions in a single block. -#[derive(Debug, PartialEq, Clone, Default, RlpEncodableWrapper, RlpDecodableWrapper)] -pub struct FlatBlockTraces(Vec); - -impl HeapSizeOf for FlatBlockTraces { - fn heap_size_of_children(&self) -> usize { - self.0.heap_size_of_children() - } -} +#[derive(Debug, PartialEq, Clone, Default, RlpEncodableWrapper, RlpDecodableWrapper, MallocSizeOf)] +pub struct FlatBlockTraces(pub(crate) Vec); impl From> for FlatBlockTraces { fn from(v: Vec) -> Self { @@ -136,10 +121,10 @@ impl Into> for FlatBlockTraces { #[cfg(test)] mod tests { use rlp::*; - use super::{FlatBlockTraces, FlatTransactionTraces, FlatTrace}; - use trace::trace::{Action, Res, CallResult, Call, Suicide, Reward}; - use evm::CallType; - use trace::RewardType; + use crate::{ + FlatBlockTraces, FlatTransactionTraces, FlatTrace, + trace::{Action, Res, CallResult, Call, CallType, Suicide, Reward, RewardType} + }; #[test] fn encode_flat_transaction_traces() { @@ -176,7 +161,7 @@ mod tests { value: "3627e8f712373c0000".parse().unwrap(), gas: 0x03e8.into(), input: vec![], - call_type: CallType::Call, + call_type: Some(CallType::Call).into(), }), result: Res::Call(CallResult { gas_used: 0.into(), @@ -193,7 +178,7 @@ mod tests { value: 0.into(), gas: 0x010c78.into(), input: vec![0x41, 0xc0, 0xe1, 0xb5], - call_type: CallType::Call, + call_type: Some(CallType::Call).into(), }), result: Res::Call(CallResult { gas_used: 0x0127.into(), diff --git a/ethcore/src/trace/types/localized.rs b/ethcore/trace/src/types/localized.rs similarity index 92% rename from ethcore/src/trace/types/localized.rs rename to ethcore/trace/src/types/localized.rs index 330d23a728..81217c722b 100644 --- a/ethcore/src/trace/types/localized.rs +++ b/ethcore/trace/src/types/localized.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,8 +17,10 @@ //! Localized traces type definitions use ethereum_types::H256; -use super::trace::{Action, Res}; -use types::BlockNumber; +use crate::{ + BlockNumber, + trace::{Action, Res} +}; /// Localized trace. #[derive(Debug, PartialEq, Clone)] diff --git a/ethcore/src/trace/types/mod.rs b/ethcore/trace/src/types/mod.rs similarity index 96% rename from ethcore/src/trace/types/mod.rs rename to ethcore/trace/src/types/mod.rs index c1ef3ac1a5..0c697f1a16 100644 --- a/ethcore/src/trace/types/mod.rs +++ b/ethcore/trace/src/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/src/trace/types/trace.rs b/ethcore/trace/src/types/trace.rs similarity index 52% rename from ethcore/src/trace/types/trace.rs rename to ethcore/trace/src/types/trace.rs index 16084e94cb..72bac5858c 100644 --- a/ethcore/src/trace/types/trace.rs +++ b/ethcore/trace/src/types/trace.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,14 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Tracing datatypes. +//! Tracing data types. +// ================== NOTE ======================== +// IF YOU'RE ADDING A FIELD TO A STRUCT WITH +// RLP ENCODING, MAKE SURE IT'S DONE IN A BACKWARDS +// COMPATIBLE WAY! +// ================== NOTE ======================== + +use std::convert::TryFrom; use ethereum_types::{U256, Address, Bloom, BloomInput}; -use bytes::Bytes; +use parity_bytes::Bytes; use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable}; - +use rlp_derive::{RlpEncodable, RlpDecodable}; use vm::ActionParams; -use evm::CallType; +use evm::ActionType; use super::error::Error; /// `Call` result. @@ -33,6 +40,33 @@ pub struct CallResult { pub output: Bytes, } +/// `Call` type. Distinguish between different types of contract interactions. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum CallType { + /// Call + Call, + /// Call code + CallCode, + /// Delegate call + DelegateCall, + /// Static call + StaticCall, +} + +impl TryFrom for CallType { + type Error = &'static str; + fn try_from(action_type: ActionType) -> Result { + match action_type { + ActionType::Call => Ok(CallType::Call), + ActionType::CallCode => Ok(CallType::CallCode), + ActionType::DelegateCall => Ok(CallType::DelegateCall), + ActionType::StaticCall => Ok(CallType::StaticCall), + ActionType::Create => Err("Create cannot be converted to CallType"), + ActionType::Create2 => Err("Create2 cannot be converted to CallType"), + } + } +} + /// `Create` result. #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] pub struct CreateResult { @@ -47,11 +81,54 @@ pub struct CreateResult { impl CreateResult { /// Returns bloom. pub fn bloom(&self) -> Bloom { - BloomInput::Raw(&self.address).into() + BloomInput::Raw(self.address.as_bytes()).into() + } +} + +/// `Create` method. Distinguish between use of `CREATE` and `CREATE2` opcodes in an action. +#[derive(Debug, Clone, PartialEq)] +pub enum CreationMethod { + /// Create + Create, + /// Create2 + Create2, +} + +impl TryFrom for CreationMethod { + type Error = &'static str; + fn try_from(action_type: ActionType) -> Result { + match action_type { + ActionType::Call => Err("Call cannot be converted to CreationMethod"), + ActionType::CallCode => Err("CallCode cannot be converted to CreationMethod"), + ActionType::DelegateCall => Err("DelegateCall cannot be converted to CreationMethod"), + ActionType::StaticCall => Err("StaticCall cannot be converted to CreationMethod"), + ActionType::Create => Ok(CreationMethod::Create), + ActionType::Create2 => Ok(CreationMethod::Create2), + } + } +} + +impl Encodable for CreationMethod { + fn rlp_append(&self, s: &mut RlpStream) { + let v = match *self { + CreationMethod::Create => 0u32, + CreationMethod::Create2 => 1, + }; + Encodable::rlp_append(&v, s); } } -/// Description of a _call_ action, either a `CALL` operation or a message transction. +impl Decodable for CreationMethod { + fn decode(rlp: &Rlp) -> Result { + rlp.as_val().and_then(|v| Ok(match v { + 0u32 => CreationMethod::Create, + 1 => CreationMethod::Create2, + _ => return Err(DecoderError::Custom("Invalid value of CreationMethod item")), + })) + } +} + +/// Description of a _call_ action, either a `CALL` operation or a message transaction. #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] pub struct Call { /// The sending account. @@ -65,19 +142,96 @@ pub struct Call { /// The input data provided to the call. pub input: Bytes, /// The type of the call. - pub call_type: CallType, + pub call_type: BackwardsCompatibleCallType, +} + +/// This is essentially an `Option`, but with a custom +/// `rlp` en/de-coding which preserves backwards compatibility with +/// the older encodings used in parity-ethereum versions < 2.7 and 2.7.0. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct BackwardsCompatibleCallType(pub Option); + +impl From> for BackwardsCompatibleCallType { + fn from(option: Option) -> Self { + BackwardsCompatibleCallType(option) + } +} + +// Encoding is the same as `CallType_v2_6_x`. +impl Encodable for BackwardsCompatibleCallType { + fn rlp_append(&self, s: &mut RlpStream) { + let v = match self.0 { + None => 0u32, + Some(CallType::Call) => 1, + Some(CallType::CallCode) => 2, + Some(CallType::DelegateCall) => 3, + Some(CallType::StaticCall) => 4, + }; + Encodable::rlp_append(&v, s); + } +} + +// Try to decode it as `CallType_v2_6_x` first, and then as `Option`. +impl Decodable for BackwardsCompatibleCallType { + fn decode(rlp: &Rlp) -> Result { + if rlp.is_data() { + rlp.as_val().and_then(|v| Ok(match v { + 0u32 => None, + 1 => Some(CallType::Call), + 2 => Some(CallType::CallCode), + 3 => Some(CallType::DelegateCall), + 4 => Some(CallType::StaticCall), + _ => return Err(DecoderError::Custom("Invalid value of CallType item")), + }.into())) + } else { + #[allow(non_camel_case_types)] + #[derive(Debug, Clone, Copy, PartialEq)] + enum CallType_v2_7_0 { + Call, + CallCode, + DelegateCall, + StaticCall, + } + + impl Decodable for CallType_v2_7_0 { + fn decode(rlp: &Rlp) -> Result { + rlp.as_val().and_then(|v| Ok(match v { + 0u32 => CallType_v2_7_0::Call, + 1 => CallType_v2_7_0::CallCode, + 2 => CallType_v2_7_0::DelegateCall, + 3 => CallType_v2_7_0::StaticCall, + _ => return Err(DecoderError::Custom("Invalid value of CallType item")), + })) + } + } + + impl From for CallType { + fn from(old_call_type: CallType_v2_7_0) -> Self { + match old_call_type { + CallType_v2_7_0::Call => Self::Call, + CallType_v2_7_0::CallCode => Self::CallCode, + CallType_v2_7_0::DelegateCall => Self::DelegateCall, + CallType_v2_7_0::StaticCall => Self::StaticCall, + } + } + } + + let optional: Option = Decodable::decode(rlp)?; + Ok(optional.map(Into::into).into()) + } + } } impl From for Call { fn from(p: ActionParams) -> Self { - match p.call_type { - CallType::DelegateCall | CallType::CallCode => Call { + match p.action_type { + ActionType::DelegateCall | ActionType::CallCode => Call { from: p.address, to: p.code_address, value: p.value.value(), gas: p.gas, input: p.data.unwrap_or_else(Vec::new), - call_type: p.call_type, + call_type: CallType::try_from(p.action_type).ok().into(), }, _ => Call { from: p.sender, @@ -85,7 +239,7 @@ impl From for Call { value: p.value.value(), gas: p.gas, input: p.data.unwrap_or_else(Vec::new), - call_type: p.call_type, + call_type: CallType::try_from(p.action_type).ok().into(), }, } } @@ -96,13 +250,13 @@ impl Call { /// The bloom contains from and to addresses. pub fn bloom(&self) -> Bloom { let mut bloom = Bloom::default(); - bloom.accrue(BloomInput::Raw(&self.from)); - bloom.accrue(BloomInput::Raw(&self.to)); + bloom.accrue(BloomInput::Raw(self.from.as_bytes())); + bloom.accrue(BloomInput::Raw(self.to.as_bytes())); bloom } } -/// Description of a _create_ action, either a `CREATE` operation or a create transction. +/// Description of a _create_ action, either a `CREATE` operation or a create transaction. #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] pub struct Create { /// The address of the creator. @@ -113,6 +267,9 @@ pub struct Create { pub gas: U256, /// The init code. pub init: Bytes, + /// Creation method (CREATE vs CREATE2). + #[rlp(default)] + pub creation_method: Option, } impl From for Create { @@ -122,6 +279,7 @@ impl From for Create { value: p.value.value(), gas: p.gas, init: p.code.map_or_else(Vec::new, |c| (*c).clone()), + creation_method: CreationMethod::try_from(p.action_type).ok().into(), } } } @@ -130,7 +288,7 @@ impl Create { /// Returns bloom create action bloom. /// The bloom contains only from address. pub fn bloom(&self) -> Bloom { - BloomInput::Raw(&self.from).into() + BloomInput::Raw(self.from.as_bytes()).into() } } @@ -185,7 +343,7 @@ pub struct Reward { impl Reward { /// Return reward action bloom. pub fn bloom(&self) -> Bloom { - BloomInput::Raw(&self.author).into() + BloomInput::Raw(self.author.as_bytes()).into() } } @@ -225,8 +383,8 @@ impl Suicide { /// Return suicide action bloom. pub fn bloom(&self) -> Bloom { let mut bloom = Bloom::default(); - bloom.accrue(BloomInput::Raw(&self.address)); - bloom.accrue(BloomInput::Raw(&self.refund_address)); + bloom.accrue(BloomInput::Raw(self.address.as_bytes())); + bloom.accrue(BloomInput::Raw(self.refund_address.as_bytes())); bloom } } @@ -429,3 +587,132 @@ pub struct VMTrace { /// Thre is a 1:1 correspondance between these and a CALL/CREATE/CALLCODE/DELEGATECALL instruction. pub subs: Vec, } + +#[cfg(test)] +mod tests { + use rlp::{RlpStream, Encodable}; + use rlp_derive::{RlpEncodable, RlpDecodable}; + use super::{Address, Bytes, Call, CallType, Create, CreationMethod, U256}; + + #[test] + fn test_call_type_backwards_compatibility() { + // Call type in version < 2.7. + #[derive(Debug, Clone, PartialEq, RlpEncodable)] + struct OldCall { + from: Address, + to: Address, + value: U256, + gas: U256, + input: Bytes, + call_type: OldCallType, + } + + // CallType type in version < 2.7. + #[allow(dead_code)] + #[derive(Debug, PartialEq, Clone)] + enum OldCallType { + None, + Call, + CallCode, + DelegateCall, + StaticCall, + } + + // CallType rlp encoding in version < 2.7. + impl Encodable for OldCallType { + fn rlp_append(&self, s: &mut RlpStream) { + let v = match *self { + OldCallType::None => 0u32, + OldCallType::Call => 1, + OldCallType::CallCode => 2, + OldCallType::DelegateCall => 3, + OldCallType::StaticCall => 4, + }; + Encodable::rlp_append(&v, s); + } + } + + let old_call = OldCall { + from: Address::from_low_u64_be(1), + to: Address::from_low_u64_be(2), + value: U256::from(3), + gas: U256::from(4), + input: vec![5], + call_type: OldCallType::DelegateCall, + }; + + let old_encoded = rlp::encode(&old_call); + + let new_call = Call { + from: Address::from_low_u64_be(1), + to: Address::from_low_u64_be(2), + value: U256::from(3), + gas: U256::from(4), + input: vec![5], + call_type: Some(CallType::DelegateCall).into(), + }; + + // `old_call` should be deserialized successfully into `new_call` + assert_eq!(rlp::decode(&old_encoded), Ok(new_call.clone())); + // test a roundtrip with `Some` `call_type` + let new_encoded = rlp::encode(&new_call); + assert_eq!(rlp::decode(&new_encoded), Ok(new_call)); + + // test a roundtrip with `None` `call_type` + let none_call = Call { + from: Address::from_low_u64_be(1), + to: Address::from_low_u64_be(2), + value: U256::from(3), + gas: U256::from(4), + input: vec![5], + call_type: None.into(), + }; + let none_encoded = rlp::encode(&none_call); + assert_eq!(rlp::decode(&none_encoded), Ok(none_call)); + } + + #[test] + fn test_creation_method_backwards_compatibility() { + // Create type in version < 2.7. + #[derive(Debug, Clone, PartialEq, RlpEncodable, RlpDecodable)] + struct OldCreate { + from: Address, + value: U256, + gas: U256, + init: Bytes, + } + + let old_create = OldCreate { + from: Address::from_low_u64_be(1), + value: U256::from(3), + gas: U256::from(4), + init: vec![5], + }; + + let old_encoded = rlp::encode(&old_create); + let new_create = Create { + from: Address::from_low_u64_be(1), + value: U256::from(3), + gas: U256::from(4), + init: vec![5], + creation_method: None, + }; + + // `old_create` should be deserialized successfully into `new_create` + assert_eq!(rlp::decode(&old_encoded), Ok(new_create.clone())); + // test a roundtrip with `None` `creation_method` + let new_encoded = rlp::encode(&new_create); + assert_eq!(rlp::decode(&new_encoded), Ok(new_create)); + + // test a roundtrip with `Some` `creation_method` + let some_create = Create { + from: Address::from_low_u64_be(1), + value: U256::from(3), + gas: U256::from(4), + init: vec![5], + creation_method: Some(CreationMethod::Create2), + }; + let some_encoded = rlp::encode(&some_create); + assert_eq!(rlp::decode(&some_encoded), Ok(some_create)); + } +} diff --git a/ethcore/trie-vm-factories/Cargo.toml b/ethcore/trie-vm-factories/Cargo.toml new file mode 100644 index 0000000000..b8d337094f --- /dev/null +++ b/ethcore/trie-vm-factories/Cargo.toml @@ -0,0 +1,15 @@ +[package] +description = "Collection of factories for VM and Tries" +name = "trie-vm-factories" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +trie-db = "0.18.0" +ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +account-db = { path = "../account-db" } +evm = { path = "../evm" } +vm = { path = "../vm" } +wasm = { path = "../wasm" } +keccak-hasher = { path = "../../util/keccak-hasher" } diff --git a/ethcore/src/factory.rs b/ethcore/trie-vm-factories/src/lib.rs similarity index 59% rename from ethcore/src/factory.rs rename to ethcore/trie-vm-factories/src/lib.rs index 06b77da9aa..7d8c590af9 100644 --- a/ethcore/src/factory.rs +++ b/ethcore/trie-vm-factories/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,13 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use trie::TrieFactory; -use ethtrie::RlpCodec; +use trie_db::TrieFactory; +use ethtrie::Layout; use account_db::Factory as AccountFactory; -use evm::{Factory as EvmFactory, VMType}; -use vm::{Exec, ActionParams, Schedule}; +use evm::{Factory as EvmFactory}; +use vm::{Exec, ActionParams, VersionedSchedule, Schedule}; use wasm::WasmInterpreter; -use keccak_hasher::KeccakHasher; const WASM_MAGIC_NUMBER: &'static [u8; 4] = b"\0asm"; @@ -31,22 +30,33 @@ pub struct VmFactory { } impl VmFactory { - pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box { - if schedule.wasm.is_some() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) { - Box::new(WasmInterpreter::new(params)) + pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Option> { + if params.code_version.is_zero() { + Some(if schedule.wasm.is_some() && schedule.versions.is_empty() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) { + Box::new(WasmInterpreter::new(params)) + } else { + self.evm.create(params, schedule, depth) + }) } else { - self.evm.create(params, schedule, depth) + let version_config = schedule.versions.get(¶ms.code_version); + + match version_config { + Some(VersionedSchedule::PWasm) => { + Some(Box::new(WasmInterpreter::new(params))) + }, + None => None, + } } } - pub fn new(evm: VMType, cache_size: usize) -> Self { - VmFactory { evm: EvmFactory::new(evm, cache_size) } + pub fn new(cache_size: usize) -> Self { + VmFactory { evm: EvmFactory::new(cache_size) } } } impl From for VmFactory { fn from(evm: EvmFactory) -> Self { - VmFactory { evm: evm } + VmFactory { evm } } } @@ -56,7 +66,7 @@ pub struct Factories { /// factory for evm. pub vm: VmFactory, /// factory for tries. - pub trie: TrieFactory, + pub trie: TrieFactory, /// factory for account databases. pub accountdb: AccountFactory, } diff --git a/ethcore/types/Cargo.toml b/ethcore/types/Cargo.toml index 1d831278f8..275e2e0271 100644 --- a/ethcore/types/Cargo.toml +++ b/ethcore/types/Cargo.toml @@ -5,18 +5,24 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] -ethereum-types = "0.4" +derive_more = "0.15.0" +ethbloom = "0.8.0" +ethcore-io = { path = "../../util/io" } +ethereum-types = "0.8.0" ethjson = { path = "../../json" } -ethkey = { path = "../../accounts/ethkey" } -heapsize = "0.4" -keccak-hash = "0.1" +keccak-hash = "0.4.0" parity-bytes = "0.1" -rlp = { version = "0.3.0", features = ["ethereum"] } +parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-util-mem = "0.3.0" +parity-snappy = "0.1" +patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" } +rlp = "0.4.0" rlp_derive = { path = "../../util/rlp-derive" } unexpected = { path = "../../util/unexpected" } +vm = { path = "../vm"} [dev-dependencies] -rustc-hex = "1.0" +rustc-hex = "2.0" [features] test-helpers = [] diff --git a/ethcore/types/src/account_diff.rs b/ethcore/types/src/account_diff.rs index 09751ba455..c09e8dbfb4 100644 --- a/ethcore/types/src/account_diff.rs +++ b/ethcore/types/src/account_diff.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,11 +16,9 @@ //! Diff between two accounts. -use std::cmp::*; -use std::fmt; use std::collections::BTreeMap; -use ethereum_types::{H256, U256}; use bytes::Bytes; +use ethereum_types::{H256, U256}; #[derive(Debug, PartialEq, Eq, Clone)] /// Diff type for specifying a change (or not). @@ -45,14 +43,13 @@ impl Diff { } } - /// Get the before value, if there is one. - pub fn pre(&self) -> Option<&T> { match *self { Diff::Died(ref x) | Diff::Changed(ref x, _) => Some(x), _ => None } } - - /// Get the after value, if there is one. - pub fn post(&self) -> Option<&T> { match *self { Diff::Born(ref x) | Diff::Changed(_, ref x) => Some(x), _ => None } } - /// Determine whether there was a change or not. - pub fn is_same(&self) -> bool { match *self { Diff::Same => true, _ => false }} + pub fn is_same(&self) -> bool { + match *self { + Diff::Same => true, + _ => false + } + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -61,86 +58,9 @@ pub struct AccountDiff { /// Change in balance, allowed to be `Diff::Same`. pub balance: Diff, /// Change in nonce, allowed to be `Diff::Same`. - pub nonce: Diff, // Allowed to be Same + pub nonce: Diff, /// Change in code, allowed to be `Diff::Same`. - pub code: Diff, // Allowed to be Same + pub code: Diff, /// Change in storage, values are not allowed to be `Diff::Same`. pub storage: BTreeMap>, } - -#[derive(Debug, PartialEq, Eq, Clone)] -/// Change in existance type. -// TODO: include other types of change. -pub enum Existance { - /// Item came into existance. - Born, - /// Item stayed in existance. - Alive, - /// Item went out of existance. - Died, -} - -impl fmt::Display for Existance { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Existance::Born => write!(f, "+++")?, - Existance::Alive => write!(f, "***")?, - Existance::Died => write!(f, "XXX")?, - } - Ok(()) - } -} - -impl AccountDiff { - /// Get `Existance` projection. - pub fn existance(&self) -> Existance { - match self.balance { - Diff::Born(_) => Existance::Born, - Diff::Died(_) => Existance::Died, - _ => Existance::Alive, - } - } -} - -// TODO: refactor into something nicer. -fn interpreted_hash(u: &H256) -> String { - if u <= &H256::from(0xffffffff) { - format!("{} = 0x{:x}", U256::from(&**u).low_u32(), U256::from(&**u).low_u32()) - } else if u <= &H256::from(u64::max_value()) { - format!("{} = 0x{:x}", U256::from(&**u).low_u64(), U256::from(&**u).low_u64()) -// } else if u <= &H256::from("0xffffffffffffffffffffffffffffffffffffffff") { -// format!("@{}", Address::from(u)) - } else { - format!("#{}", u) - } -} - -impl fmt::Display for AccountDiff { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use bytes::ToPretty; - - match self.nonce { - Diff::Born(ref x) => write!(f, " non {}", x)?, - Diff::Changed(ref pre, ref post) => write!(f, "#{} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - * min(pre, post))?, - _ => {}, - } - match self.balance { - Diff::Born(ref x) => write!(f, " bal {}", x)?, - Diff::Changed(ref pre, ref post) => write!(f, "${} ({} {} {})", post, pre, if pre > post {"-"} else {"+"}, *max(pre, post) - *min(pre, post))?, - _ => {}, - } - if let Diff::Born(ref x) = self.code { - write!(f, " code {}", x.pretty())?; - } - write!(f, "\n")?; - for (k, dv) in &self.storage { - match *dv { - Diff::Born(ref v) => write!(f, " + {} => {}\n", interpreted_hash(k), interpreted_hash(v))?, - Diff::Changed(ref pre, ref post) => write!(f, " * {} => {} (was {})\n", interpreted_hash(k), interpreted_hash(post), interpreted_hash(pre))?, - Diff::Died(_) => write!(f, " X {}\n", interpreted_hash(k))?, - _ => {}, - } - } - Ok(()) - } -} diff --git a/ethcore/types/src/ancestry_action.rs b/ethcore/types/src/ancestry_action.rs index 39b73ef99e..cc55d46ee9 100644 --- a/ethcore/types/src/ancestry_action.rs +++ b/ethcore/types/src/ancestry_action.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/basic_account.rs b/ethcore/types/src/basic_account.rs index 039dbac949..9941c1351e 100644 --- a/ethcore/types/src/basic_account.rs +++ b/ethcore/types/src/basic_account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use ethereum_types::{U256, H256}; /// Basic account type. -#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct BasicAccount { /// Nonce of the account. pub nonce: U256, @@ -29,4 +29,48 @@ pub struct BasicAccount { pub storage_root: H256, /// Code hash of the account. pub code_hash: H256, + /// Code version of the account. + pub code_version: U256, +} + +impl rlp::Encodable for BasicAccount { + fn rlp_append(&self, stream: &mut rlp::RlpStream) { + let use_short_version = self.code_version == U256::zero(); + + match use_short_version { + true => { stream.begin_list(4); } + false => { stream.begin_list(5); } + } + + stream.append(&self.nonce); + stream.append(&self.balance); + stream.append(&self.storage_root); + stream.append(&self.code_hash); + + if !use_short_version { + stream.append(&self.code_version); + } + } +} + +impl rlp::Decodable for BasicAccount { + fn decode(rlp: &rlp::Rlp) -> Result { + let use_short_version = match rlp.item_count()? { + 4 => true, + 5 => false, + _ => return Err(rlp::DecoderError::RlpIncorrectListLen), + }; + + Ok(BasicAccount { + nonce: rlp.val_at(0)?, + balance: rlp.val_at(1)?, + storage_root: rlp.val_at(2)?, + code_hash: rlp.val_at(3)?, + code_version: if use_short_version { + U256::zero() + } else { + rlp.val_at(4)? + }, + }) + } } diff --git a/ethcore/types/src/block.rs b/ethcore/types/src/block.rs index a423fb1ed1..9eba9baefd 100644 --- a/ethcore/types/src/block.rs +++ b/ethcore/types/src/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,24 +18,16 @@ //! //! Blocks can be produced by a local node or they may be received from the network. //! -//! To create a block locally, we start with an `OpenBlock`. This block is mutable -//! and can be appended to with transactions and uncles. -//! -//! When ready, `OpenBlock` can be closed and turned into a `ClosedBlock`. A `ClosedBlock` can -//! be reopend again by a miner under certain circumstances. On block close, state commit is -//! performed. -//! -//! `LockedBlock` is a version of a `ClosedBlock` that cannot be reopened. It can be sealed -//! using an engine. -//! -//! `ExecutedBlock` is an underlaying data structure used by all structs above to store block -//! related info. +//! Other block types are found in `ethcore` use bytes::Bytes; +use ethereum_types::{H256, U256}; +use parity_util_mem::MallocSizeOf; +use BlockNumber; use header::Header; use rlp::{Rlp, RlpStream, Decodable, DecoderError}; -use transaction::UnverifiedTransaction; +use transaction::{UnverifiedTransaction, SignedTransaction}; /// A block, encoded as it is on the block chain. #[derive(Default, Debug, Clone, PartialEq)] @@ -74,3 +66,53 @@ impl Decodable for Block { }) } } + +/// Preprocessed block data gathered in `verify_block_unordered` call +#[derive(MallocSizeOf)] +pub struct PreverifiedBlock { + /// Populated block header + pub header: Header, + /// Populated block transactions + pub transactions: Vec, + /// Populated block uncles + pub uncles: Vec

, + /// Block bytes + pub bytes: Bytes, +} + +/// Brief info about inserted block. +#[derive(Clone)] +pub struct BlockInfo { + /// Block hash. + pub hash: H256, + /// Block number. + pub number: BlockNumber, + /// Total block difficulty. + pub total_difficulty: U256, + /// Block location in blockchain. + pub location: BlockLocation +} + +/// Describes location of newly inserted block. +#[derive(Debug, Clone, PartialEq)] +pub enum BlockLocation { + /// It's part of the canon chain. + CanonChain, + /// It's not a part of the canon chain. + Branch, + /// It's part of the fork which should become canon chain, + /// because its total difficulty is higher than current + /// canon chain difficulty. + BranchBecomingCanonChain(BranchBecomingCanonChainData), +} + +/// Info about heaviest fork +#[derive(Debug, Clone, PartialEq)] +pub struct BranchBecomingCanonChainData { + /// Hash of the newest common ancestor with old canon chain. + pub ancestor: H256, + /// Hashes of the blocks between ancestor and this block. + pub enacted: Vec, + /// Hashes of the blocks which were invalidated. + pub retracted: Vec, +} diff --git a/ethcore/types/src/block_status.rs b/ethcore/types/src/block_status.rs index 0460fcbe6d..ac955067b3 100644 --- a/ethcore/types/src/block_status.rs +++ b/ethcore/types/src/block_status.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/blockchain_info.rs b/ethcore/types/src/blockchain_info.rs index 42158638da..d9079acbab 100644 --- a/ethcore/types/src/blockchain_info.rs +++ b/ethcore/types/src/blockchain_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use std::fmt; use ethereum_types::{U256, H256}; use security_level::SecurityLevel; -use {BlockNumber}; +use BlockNumber; /// Information about the blockchain gathered together. #[derive(Clone, Debug)] diff --git a/ethcore/types/src/call_analytics.rs b/ethcore/types/src/call_analytics.rs index 902c75e56a..ba67f6f623 100644 --- a/ethcore/types/src/call_analytics.rs +++ b/ethcore/types/src/call_analytics.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/src/client/chain_notify.rs b/ethcore/types/src/chain_notify.rs similarity index 78% rename from ethcore/src/client/chain_notify.rs rename to ethcore/types/src/chain_notify.rs index 5f9b8ed314..da17088fe7 100644 --- a/ethcore/src/client/chain_notify.rs +++ b/ethcore/types/src/chain_notify.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,10 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +//! Types pertaining to sending messages and finding routes through the chain. Used mostly by the +//! ChainNotify trait. + use bytes::Bytes; -use ethereum_types::{H256, U256}; -use types::transaction::UnverifiedTransaction; -use blockchain::ImportRoute; +use ethereum_types::H256; +use crate::{ + import_route::ImportRoute, +}; use std::time::Duration; use std::collections::HashMap; @@ -29,6 +33,8 @@ pub enum ChainMessageType { PrivateTransaction(H256, Vec), /// Message with signed private transaction SignedPrivateTransaction(H256, Vec), + /// Private state request for the particular private contract + PrivateStateRequest(H256), } /// Route type to indicate whether it is enacted or retracted. @@ -114,7 +120,8 @@ impl ChainRoute { } } -/// Used by `ChainNotify` `new_blocks()` +/// Used by `ChainNotify` `new_blocks()` and contains information about new blocks imported to the +/// chain. pub struct NewBlocks { /// Imported blocks pub imported: Vec, @@ -154,40 +161,3 @@ impl NewBlocks { } } } - -/// Represents what has to be handled by actor listening to chain events -pub trait ChainNotify : Send + Sync { - /// fires when chain has new blocks. - fn new_blocks(&self, _new_blocks: NewBlocks) { - // does nothing by default - } - - /// fires when chain achieves active mode - fn start(&self) { - // does nothing by default - } - - /// fires when chain achieves passive mode - fn stop(&self) { - // does nothing by default - } - - /// fires when chain broadcasts a message - fn broadcast(&self, _message_type: ChainMessageType) { - // does nothing by default - } - - /// fires when new block is about to be imported - /// implementations should be light - fn block_pre_import(&self, _bytes: &Bytes, _hash: &H256, _difficulty: &U256) { - // does nothing by default - } - - /// fires when new transactions are received from a peer - fn transactions_received(&self, - _txs: &[UnverifiedTransaction], - _peer_id: usize, - ) { - // does nothing by default - } -} diff --git a/ethcore/types/src/client_types.rs b/ethcore/types/src/client_types.rs new file mode 100644 index 0000000000..dd70a082be --- /dev/null +++ b/ethcore/types/src/client_types.rs @@ -0,0 +1,101 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Client related types. + +use std::{ + cmp, + fmt::{Display, Formatter, Error as FmtError}, + ops, + time::Duration, +}; + +use ethereum_types::U256; +use crate::header::Header; + +/// Operating mode for the client. +#[derive(Debug, Eq, PartialEq, Clone)] +pub enum Mode { + /// Always on. + Active, + /// 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 client is inactive for some (given) time and + /// stays inactive. + Dark(Duration), + /// Always off. + Off, +} + +impl Display for Mode { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + match *self { + Mode::Active => write!(f, "active"), + Mode::Passive(..) => write!(f, "passive"), + Mode::Dark(..) => write!(f, "dark"), + Mode::Off => write!(f, "offline"), + } + } +} + +/// Report on the status of a client. +#[derive(Default, Clone, Debug, Eq, PartialEq)] +pub struct ClientReport { + /// How many blocks have been imported so far. + pub blocks_imported: usize, + /// How many transactions have been applied so far. + pub transactions_applied: usize, + /// How much gas has been processed so far. + pub gas_processed: U256, + /// Memory used by state DB + pub state_db_mem: usize, +} + +impl ClientReport { + /// Alter internal reporting to reflect the additional `block` has been processed. + pub fn accrue_block(&mut self, header: &Header, transactions: usize) { + self.blocks_imported += 1; + self.transactions_applied += transactions; + self.gas_processed = self.gas_processed + *header.gas_used(); + } +} + +impl<'a> ops::Sub<&'a ClientReport> for ClientReport { + type Output = Self; + + fn sub(mut self, other: &'a ClientReport) -> Self { + let higher_mem = cmp::max(self.state_db_mem, other.state_db_mem); + let lower_mem = cmp::min(self.state_db_mem, other.state_db_mem); + + 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 + } +} + +/// Result to be used during get address code at given block's state +pub enum StateResult { + /// State is missing + Missing, + + /// State is some + Some(T), +} + diff --git a/ethcore/evm/src/vmtype.rs b/ethcore/types/src/data_format.rs similarity index 55% rename from ethcore/evm/src/vmtype.rs rename to ethcore/types/src/data_format.rs index 2ae40e2c11..78fd383bc7 100644 --- a/ethcore/evm/src/vmtype.rs +++ b/ethcore/types/src/data_format.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,32 +14,32 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::fmt; +//! Data format for importing/exporting blocks from disk +use std::str::FromStr; -/// Type of EVM to use. -#[derive(Debug, PartialEq, Clone)] -pub enum VMType { - /// RUST EVM - Interpreter +/// Format for importing/exporting blocks +#[derive(Debug, PartialEq)] +pub enum DataFormat { + /// Hexadecimal format + Hex, + /// Binary format + Binary, } -impl fmt::Display for VMType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", match *self { - VMType::Interpreter => "INT" - }) - } -} - -impl Default for VMType { +impl Default for DataFormat { fn default() -> Self { - VMType::Interpreter + DataFormat::Binary } } -impl VMType { - /// Return all possible VMs (Interpreter) - pub fn all() -> Vec { - vec![VMType::Interpreter] +impl FromStr for DataFormat { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "binary" | "bin" => Ok(DataFormat::Binary), + "hex" => Ok(DataFormat::Hex), + x => Err(format!("Invalid format: {}", x)) + } } } diff --git a/ethcore/types/src/encoded.rs b/ethcore/types/src/encoded.rs index 4680f95af5..a7c7b5c0cc 100644 --- a/ethcore/types/src/encoded.rs +++ b/ethcore/types/src/encoded.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -26,21 +26,17 @@ use block::Block as FullBlock; use ethereum_types::{H256, Bloom, U256, Address}; use hash::keccak; -use header::{Header as FullHeader}; -use heapsize::HeapSizeOf; +use header::Header as FullHeader; +use parity_util_mem::MallocSizeOf; use rlp::{self, Rlp, RlpStream}; use transaction::UnverifiedTransaction; use views::{self, BlockView, HeaderView, BodyView}; use BlockNumber; /// Owning header view. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, MallocSizeOf)] pub struct Header(Vec); -impl HeapSizeOf for Header { - fn heap_size_of_children(&self) -> usize { self.0.heap_size_of_children() } -} - impl Header { /// Create a new owning header view. /// Expects the data to be an RLP-encoded header -- any other case will likely lead to @@ -64,6 +60,15 @@ impl Header { pub fn into_inner(self) -> Vec { self.0 } } +impl std::fmt::LowerHex for Header { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for byte in &self.0 { + write!(f, "{:02x}", byte)?; + } + Ok(()) + } +} + // forwarders to borrowed view. impl Header { /// Returns the header hash. @@ -113,13 +118,9 @@ impl Header { } /// Owning block body view. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, MallocSizeOf)] pub struct Body(Vec); -impl HeapSizeOf for Body { - fn heap_size_of_children(&self) -> usize { self.0.heap_size_of_children() } -} - impl Body { /// Create a new owning block body view. The raw bytes passed in must be an rlp-encoded block /// body. @@ -178,13 +179,9 @@ impl Body { } /// Owning block view. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, MallocSizeOf)] pub struct Block(Vec); -impl HeapSizeOf for Block { - fn heap_size_of_children(&self) -> usize { self.0.heap_size_of_children() } -} - 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) } diff --git a/ethcore/types/src/engines/epoch.rs b/ethcore/types/src/engines/epoch.rs index 2a43b47755..905f2e47d2 100644 --- a/ethcore/types/src/engines/epoch.rs +++ b/ethcore/types/src/engines/epoch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,6 @@ //! Epoch verifiers and transitions. use ethereum_types::H256; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; /// A full epoch transition. @@ -70,4 +69,3 @@ impl Decodable for PendingTransition { }) } } - diff --git a/ethcore/types/src/engines/machine.rs b/ethcore/types/src/engines/machine.rs new file mode 100644 index 0000000000..80b9bf6222 --- /dev/null +++ b/ethcore/types/src/engines/machine.rs @@ -0,0 +1,95 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! State machine types + +use ethereum_types::{Address, U256}; +use bytes::Bytes; + +use crate::{ + log_entry::LogEntry, + receipt, + state_diff::StateDiff, +}; + +/// Type alias for a function we can make calls through synchronously. +/// Returns the call result and state proof for each call. +pub type Call<'a> = dyn Fn(Address, Vec) -> Result<(Vec, Vec>), String> + 'a; + +/// Request for auxiliary data of a block. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum AuxiliaryRequest { + /// Needs the body. + Body, + /// Needs the receipts. + Receipts, + /// Needs both body and receipts. + Both, +} + +/// Auxiliary data fetcher for an Ethereum machine. In Ethereum-like machines +/// there are two kinds of auxiliary data: bodies and receipts. +#[derive(Default, Clone)] +pub struct AuxiliaryData<'a> { + /// The full block bytes, including the header. + pub bytes: Option<&'a [u8]>, + /// The block receipts. + pub receipts: Option<&'a [receipt::Receipt]>, +} + + +/// Transaction execution receipt. +#[derive(Debug, PartialEq, Clone)] +pub struct Executed { + /// True if the outer call/create resulted in an exceptional exit. + pub exception: Option, + + /// Gas paid up front for execution of transaction. + pub gas: U256, + + /// Gas used during execution of transaction. + pub gas_used: U256, + + /// Gas refunded after the execution of transaction. + /// To get gas that was required up front, add `refunded` and `gas_used`. + pub refunded: U256, + + /// Cumulative gas used in current block so far. + /// + /// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)` + /// + /// where `tn` is current transaction. + pub cumulative_gas_used: U256, + + /// Vector of logs generated by transaction. + pub logs: Vec, + + /// Addresses of contracts created during execution of transaction. + /// Ordered from earliest creation. + /// + /// eg. sender creates contract A and A in constructor creates contract B + /// + /// B creation ends first, and it will be the first element of the vector. + pub contracts_created: Vec
, + /// Transaction output. + pub output: Bytes, + /// The trace of this transaction. + pub trace: Vec, + /// The VM trace of this transaction. + pub vm_trace: Option, + /// The state diff, if we traced it. + pub state_diff: Option, +} diff --git a/ethcore/types/src/engines/mod.rs b/ethcore/types/src/engines/mod.rs index cc622bbe65..0aca91c777 100644 --- a/ethcore/types/src/engines/mod.rs +++ b/ethcore/types/src/engines/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,107 @@ //! Engine-specific types. +use ethereum_types::{Address, H256, H64}; +use bytes::Bytes; +use ethjson; +use rlp::Rlp; +use unexpected::Mismatch; + +use crate::{BlockNumber, errors::{BlockError, EthcoreError}}; + pub mod epoch; +pub mod params; +pub mod machine; + +/// Optimize cache for CPU or memory usage +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +pub enum OptimizeFor { + /// Optimize cache for CPU + Cpu, + /// Optimize cache for memory + Memory, +} + +impl Default for OptimizeFor { + fn default() -> Self { + OptimizeFor::Cpu + } +} + +/// Ethash/Clique specific seal +#[derive(Debug, PartialEq)] +pub struct EthashSeal { + /// Ethash seal mix_hash + pub mix_hash: H256, + /// Ethash seal nonce + pub nonce: H64, +} + +impl EthashSeal { + /// Tries to parse rlp encoded bytes as an Ethash/Clique seal. + pub fn parse_seal>(seal: &[T]) -> Result { + if seal.len() != 2 { + return Err(BlockError::InvalidSealArity( + Mismatch { + expected: 2, + found: seal.len() + } + ).into()); + } + + let mix_hash = Rlp::new(seal[0].as_ref()).as_val::()?; + let nonce = Rlp::new(seal[1].as_ref()).as_val::()?; + Ok(EthashSeal { mix_hash, nonce }) + } +} + + +/// Seal type. +#[derive(Debug, PartialEq, Eq)] +pub enum Seal { + /// Regular block seal; should be part of the blockchain. + Regular(Vec), + /// Engine does not generate seal for this block right now. + None, +} + +/// The type of sealing the engine is currently able to perform. +#[derive(Debug, PartialEq, Eq)] +pub enum SealingState { + /// The engine is ready to seal a block. + Ready, + /// The engine can't seal at the moment, and no block should be prepared and queued. + NotReady, + /// The engine does not seal internally. + External, +} + +/// The number of generations back that uncles can be. +pub const MAX_UNCLE_AGE: u64 = 6; + +/// Default EIP-210 contract code. +/// As defined in https://github.com/ethereum/EIPs/pull/210 +pub const DEFAULT_BLOCKHASH_CONTRACT: &'static [u8] = &[ + 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0x33, 0x14, 0x15, 0x61, 0x00, 0x6a, 0x57, 0x60, 0x01, 0x43, 0x03, + 0x60, 0x00, 0x35, 0x61, 0x01, 0x00, 0x82, 0x07, 0x55, 0x61, 0x01, 0x00, 0x81, 0x07, 0x15, 0x15, + 0x61, 0x00, 0x45, 0x57, 0x60, 0x00, 0x35, 0x61, 0x01, 0x00, 0x61, 0x01, 0x00, 0x83, 0x05, 0x07, + 0x61, 0x01, 0x00, 0x01, 0x55, 0x5b, 0x62, 0x01, 0x00, 0x00, 0x81, 0x07, 0x15, 0x15, 0x61, 0x00, + 0x64, 0x57, 0x60, 0x00, 0x35, 0x61, 0x01, 0x00, 0x62, 0x01, 0x00, 0x00, 0x83, 0x05, 0x07, 0x61, + 0x02, 0x00, 0x01, 0x55, 0x5b, 0x50, 0x61, 0x01, 0x3e, 0x56, 0x5b, 0x43, 0x60, 0x00, 0x35, 0x12, + 0x15, 0x15, 0x61, 0x00, 0x84, 0x57, 0x60, 0x00, 0x60, 0x40, 0x52, 0x60, 0x20, 0x60, 0x40, 0xf3, + 0x61, 0x01, 0x3d, 0x56, 0x5b, 0x61, 0x01, 0x00, 0x60, 0x00, 0x35, 0x43, 0x03, 0x13, 0x15, 0x15, + 0x61, 0x00, 0xa8, 0x57, 0x61, 0x01, 0x00, 0x60, 0x00, 0x35, 0x07, 0x54, 0x60, 0x60, 0x52, 0x60, + 0x20, 0x60, 0x60, 0xf3, 0x61, 0x01, 0x3c, 0x56, 0x5b, 0x61, 0x01, 0x00, 0x60, 0x00, 0x35, 0x07, + 0x15, 0x15, 0x61, 0x00, 0xc5, 0x57, 0x62, 0x01, 0x00, 0x00, 0x60, 0x00, 0x35, 0x43, 0x03, 0x13, + 0x15, 0x61, 0x00, 0xc8, 0x56, 0x5b, 0x60, 0x00, 0x5b, 0x15, 0x61, 0x00, 0xea, 0x57, 0x61, 0x01, + 0x00, 0x61, 0x01, 0x00, 0x60, 0x00, 0x35, 0x05, 0x07, 0x61, 0x01, 0x00, 0x01, 0x54, 0x60, 0x80, + 0x52, 0x60, 0x20, 0x60, 0x80, 0xf3, 0x61, 0x01, 0x3b, 0x56, 0x5b, 0x62, 0x01, 0x00, 0x00, 0x60, + 0x00, 0x35, 0x07, 0x15, 0x15, 0x61, 0x01, 0x09, 0x57, 0x63, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x35, 0x43, 0x03, 0x13, 0x15, 0x61, 0x01, 0x0c, 0x56, 0x5b, 0x60, 0x00, 0x5b, 0x15, 0x61, 0x01, + 0x2f, 0x57, 0x61, 0x01, 0x00, 0x62, 0x01, 0x00, 0x00, 0x60, 0x00, 0x35, 0x05, 0x07, 0x61, 0x02, + 0x00, 0x01, 0x54, 0x60, 0xa0, 0x52, 0x60, 0x20, 0x60, 0xa0, 0xf3, 0x61, 0x01, 0x3a, 0x56, 0x5b, + 0x60, 0x00, 0x60, 0xc0, 0x52, 0x60, 0x20, 0x60, 0xc0, 0xf3, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b]; /// Fork choice. #[derive(Debug, PartialEq, Eq)] @@ -26,3 +126,33 @@ pub enum ForkChoice { /// Choose the current best block. Old, } + +/// Ethash-specific extensions. +#[derive(Debug, Clone)] +pub struct EthashExtensions { + /// Homestead transition block number. + pub homestead_transition: BlockNumber, + /// DAO hard-fork transition block (X). + pub dao_hardfork_transition: u64, + /// DAO hard-fork refund contract address (C). + pub dao_hardfork_beneficiary: Address, + /// DAO hard-fork DAO accounts list (L) + pub dao_hardfork_accounts: Vec
, +} + +impl From for EthashExtensions { + fn from(p: ::ethjson::spec::EthashParams) -> Self { + EthashExtensions { + homestead_transition: p.homestead_transition.map_or(0, 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::zero, Into::into), + dao_hardfork_accounts: p.dao_hardfork_accounts.unwrap_or_else(Vec::new).into_iter().map(Into::into).collect(), + } + } +} + +/// Type alias for a function we can get headers by hash through. +pub type Headers<'a, H> = dyn Fn(H256) -> Option + 'a; + +/// Type alias for a function we can query pending transitions by block hash through. +pub type PendingTransitionStore<'a> = dyn Fn(H256) -> Option + 'a; diff --git a/ethcore/types/src/engines/params.rs b/ethcore/types/src/engines/params.rs new file mode 100644 index 0000000000..9750c87a61 --- /dev/null +++ b/ethcore/types/src/engines/params.rs @@ -0,0 +1,364 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Engine-specific parameter types. + +use ethereum_types::{Address, U256, H256}; +use bytes::Bytes; +use ethjson; + +use BlockNumber; +use engines::DEFAULT_BLOCKHASH_CONTRACT; + +const MAX_TRANSACTION_SIZE: usize = 300 * 1024; + +/// Parameters common to ethereum-like blockchains. +/// NOTE: when adding bugfix hard-fork parameters, +/// add to `nonzero_bugfix_hard_fork` +/// +/// we define a "bugfix" hard fork as any hard fork which +/// you would put on-by-default in a new chain. +#[derive(Debug, PartialEq, Default)] +#[cfg_attr(any(test, feature = "test-helpers"), derive(Clone))] +pub struct CommonParams { + /// Account start nonce. + pub account_start_nonce: U256, + /// Maximum size of extra data. + pub maximum_extra_data_size: usize, + /// Network id. + pub network_id: u64, + /// Chain id. + pub chain_id: u64, + /// Main subprotocol name. + pub subprotocol_name: String, + /// Minimum gas limit. + 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: BlockNumber, + /// Number of first block where EIP-161.abc begin. + pub eip161abc_transition: BlockNumber, + /// Number of first block where EIP-161.d begins. + pub eip161d_transition: BlockNumber, + /// Number of first block where EIP-98 rules begin. + pub eip98_transition: BlockNumber, + /// Number of first block where EIP-658 rules begin. + pub eip658_transition: BlockNumber, + /// Number of first block where EIP-155 rules begin. + pub eip155_transition: BlockNumber, + /// Validate block receipts root. + pub validate_receipts_transition: BlockNumber, + /// Validate transaction chain id. + pub validate_chain_id_transition: BlockNumber, + /// Number of first block where EIP-140 rules begin. + pub eip140_transition: BlockNumber, + /// Number of first block where EIP-210 rules begin. + pub eip210_transition: BlockNumber, + /// EIP-210 Blockhash contract address. + pub eip210_contract_address: Address, + /// EIP-210 Blockhash contract code. + pub eip210_contract_code: Bytes, + /// Gas allocated for EIP-210 blockhash update. + pub eip210_contract_gas: U256, + /// Number of first block where EIP-211 rules begin. + 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 EIP-1052 rules begin. + pub eip1052_transition: BlockNumber, + /// Number of first block where EIP-1283 rules begin. + pub eip1283_transition: BlockNumber, + /// Number of first block where EIP-1283 rules end. + pub eip1283_disable_transition: BlockNumber, + /// Number of first block where EIP-1283 rules re-enabled. + pub eip1283_reenable_transition: BlockNumber, + /// Number of first block where EIP-1014 rules begin. + pub eip1014_transition: BlockNumber, + /// Number of first block where EIP-1706 rules begin. + pub eip1706_transition: BlockNumber, + /// Number of first block where EIP-1344 rules begin: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1344.md + pub eip1344_transition: BlockNumber, + /// Number of first block where EIP-1884 rules begin:https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1884.md + pub eip1884_transition: BlockNumber, + /// Number of first block where EIP-2028 rules begin. + pub eip2028_transition: BlockNumber, + /// Number of first block where EIP-2200 advance transition begin. + pub eip2200_advance_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. + pub nonce_cap_increment: u64, + /// Enable dust cleanup for contracts. + pub remove_dust_contracts: bool, + /// Wasm activation blocknumber, if any disabled initially. + pub wasm_activation_transition: BlockNumber, + /// Wasm account version, activated after `wasm_activation_transition`. If this field is defined, do not use code + /// prefix to determine VM to execute. + pub wasm_version: Option, + /// Number of first block where KIP-4 rules begin. Only has effect if Wasm is activated. + pub kip4_transition: BlockNumber, + /// Number of first block where KIP-6 rules begin. Only has effect if Wasm is activated. + pub kip6_transition: BlockNumber, + /// Gas limit bound divisor (how much gas limit can change per block) + pub gas_limit_bound_divisor: U256, + /// Registrar contract address. + pub registrar: Option
, + /// Node permission managing contract address. + pub node_permission_contract: Option
, + /// Maximum contract code size that can be deployed. + pub max_code_size: u64, + /// Number of first block where max code size limit is active. + pub max_code_size_transition: BlockNumber, + /// Transaction permission managing contract address. + pub transaction_permission_contract: Option
, + /// Block at which the transaction permission contract should start being used. + pub transaction_permission_contract_transition: BlockNumber, + /// 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 { + 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. + pub fn max_code_size(&self, block_number: u64) -> u64 { + if block_number >= self.max_code_size_transition { + self.max_code_size + } else { + u64::max_value() + } + } + + /// Apply common spec config parameters to the schedule. + pub fn update_schedule(&self, block_number: u64, schedule: &mut vm::Schedule) { + schedule.have_create2 = block_number >= self.eip1014_transition; + 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; + schedule.have_extcodehash = block_number >= self.eip1052_transition; + schedule.have_chain_id = block_number >= self.eip1344_transition; + schedule.eip1283 = + (block_number >= self.eip1283_transition && + !(block_number >= self.eip1283_disable_transition)) || + block_number >= self.eip1283_reenable_transition; + schedule.eip1706 = block_number >= self.eip1706_transition; + + if block_number >= self.eip1884_transition { + schedule.have_selfbalance = true; + schedule.sload_gas = 800; + schedule.balance_gas = 700; + schedule.extcodehash_gas = 700; + } + if block_number >= self.eip2028_transition { + schedule.tx_data_non_zero_gas = 16; + } + if block_number >= self.eip2200_advance_transition { + schedule.sstore_dirty_gas = Some(800); + } + if block_number >= self.eip210_transition { + schedule.blockhash_gas = 800; + } + if block_number >= self.dust_protection_transition { + schedule.kill_dust = match self.remove_dust_contracts { + true => vm::CleanDustMode::WithCodeAndStorage, + false => vm::CleanDustMode::BasicOnly, + }; + } + if block_number >= self.wasm_activation_transition { + let mut wasm = vm::WasmCosts::default(); + if block_number >= self.kip4_transition { + wasm.have_create2 = true; + } + if block_number >= self.kip6_transition { + wasm.have_gasleft = true; + } + schedule.wasm = Some(wasm); + if let Some(version) = self.wasm_version { + schedule.versions.insert(version, vm::VersionedSchedule::PWasm); + } + } + } + + /// Return Some if the current parameters contain a bugfix hard fork not on block 0. + pub fn nonzero_bugfix_hard_fork(&self) -> Option<&str> { + if self.eip155_transition != 0 { + return Some("eip155Transition"); + } + + if self.validate_receipts_transition != 0 { + return Some("validateReceiptsTransition"); + } + + if self.validate_chain_id_transition != 0 { + return Some("validateChainIdTransition"); + } + + None + } +} + +impl From for CommonParams { + fn from(p: ethjson::spec::Params) -> Self { + CommonParams { + account_start_nonce: p.account_start_nonce.map_or_else(U256::zero, Into::into), + maximum_extra_data_size: p.maximum_extra_data_size.into(), + network_id: p.network_id.into(), + chain_id: if let Some(n) = p.chain_id { + n.into() + } else { + p.network_id.into() + }, + subprotocol_name: p.subprotocol_name.unwrap_or_else(|| "eth".to_owned()), + min_gas_limit: p.min_gas_limit.into(), + fork_block: if let (Some(n), Some(h)) = (p.fork_block, p.fork_hash) { + Some((n.into(), h.into())) + } 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_else( + BlockNumber::max_value, + 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), + eip140_transition: p.eip140_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip210_transition: p.eip210_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip210_contract_address: p.eip210_contract_address.map_or(Address::from_low_u64_be(0xf0), Into::into), + eip210_contract_code: p.eip210_contract_code.map_or_else( + || DEFAULT_BLOCKHASH_CONTRACT.to_vec(), + Into::into, + ), + eip210_contract_gas: p.eip210_contract_gas.map_or(1000000.into(), Into::into), + 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_else( + BlockNumber::max_value, + Into::into, + ), + eip658_transition: p.eip658_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1052_transition: p.eip1052_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1283_transition: p.eip1283_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1283_disable_transition: p.eip1283_disable_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1283_reenable_transition: p.eip1283_reenable_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1706_transition: p.eip1706_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1014_transition: p.eip1014_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1344_transition: p.eip1344_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip1884_transition: p.eip1884_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip2028_transition: p.eip2028_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + eip2200_advance_transition: p.eip2200_advance_transition.map_or_else( + BlockNumber::max_value, + Into::into, + ), + 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), + remove_dust_contracts: p.remove_dust_contracts.unwrap_or(false), + gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(), + registrar: p.registrar.map(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), + transaction_permission_contract_transition: + p.transaction_permission_contract_transition.map_or(0, Into::into), + wasm_activation_transition: p.wasm_activation_transition.map_or_else( + BlockNumber::max_value, + Into::into + ), + wasm_version: p.wasm_version.map(Into::into), + kip4_transition: p.kip4_transition.map_or_else( + BlockNumber::max_value, + Into::into + ), + kip6_transition: p.kip6_transition.map_or_else( + BlockNumber::max_value, + Into::into + ), + } + } +} diff --git a/ethcore/types/src/errors/block_error.rs b/ethcore/types/src/errors/block_error.rs new file mode 100644 index 0000000000..c72588d6ed --- /dev/null +++ b/ethcore/types/src/errors/block_error.rs @@ -0,0 +1,156 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::{ + fmt, + error, + time::SystemTime +}; + +use ethbloom::Bloom; +use ethereum_types::{H256, U256, Address}; +use unexpected::{Mismatch, OutOfBounds}; + +use BlockNumber; + +/// Errors concerning block processing. +#[derive(Debug, Display, PartialEq, Clone, Eq)] +pub enum BlockError { + /// Block has too many uncles. + #[display(fmt = "Block has too many uncles. {}", _0)] + TooManyUncles(OutOfBounds), + /// Extra data is of an invalid length. + #[display(fmt = "Extra block data too long. {}", _0)] + ExtraDataOutOfBounds(OutOfBounds), + /// Seal is incorrect format. + #[display(fmt = "Block seal in incorrect format: {}", _0)] + InvalidSealArity(Mismatch), + /// Block has too much gas used. + #[display(fmt = "Block has too much gas used. {}", _0)] + TooMuchGasUsed(OutOfBounds), + /// Uncles hash in header is invalid. + #[display(fmt = "Block has invalid uncles hash: {}", _0)] + InvalidUnclesHash(Mismatch), + /// An uncle is from a wrong generation. + #[display(fmt = "Uncle block is too old. {}", _0)] + UncleOutOfBounds(OutOfBounds), + /// An uncle is already in the chain. + #[display(fmt = "Uncle {} already in chain", _0)] + UncleInChain(H256), + /// An uncle is included twice. + #[display(fmt = "Uncle {} already in the header", _0)] + DuplicateUncle(H256), + /// An uncle has a parent not in the chain. + #[display(fmt = "Uncle {} has a parent not in the chain", _0)] + UncleParentNotInChain(H256), + /// State root header field is invalid. + #[display(fmt = "Invalid state root in header: {}", _0)] + InvalidStateRoot(Mismatch), + /// Gas used header field is invalid. + #[display(fmt = "Invalid gas used in header: {}", _0)] + InvalidGasUsed(Mismatch), + /// Transactions root header field is invalid. + #[display(fmt = "Invalid transactions root in header: {}", _0)] + InvalidTransactionsRoot(Mismatch), + /// Difficulty is out of range; this can be used as an looser error prior to getting a definitive + /// value for difficulty. This error needs only provide bounds of which it is out. + #[display(fmt = "Difficulty out of bounds: {}", _0)] + DifficultyOutOfBounds(OutOfBounds), + /// Difficulty header field is invalid; this is a strong error used after getting a definitive + /// value for difficulty (which is provided). + #[display(fmt = "Invalid block difficulty: {}", _0)] + InvalidDifficulty(Mismatch), + /// Seal element of type H256 (max_hash for Ethash, but could be something else for + /// other seal engines) is out of bounds. + #[display(fmt = "Seal element out of bounds: {}", _0)] + MismatchedH256SealElement(Mismatch), + /// Proof-of-work aspect of seal, which we assume is a 256-bit value, is invalid. + #[display(fmt = "Block has invalid PoW: {}", _0)] + InvalidProofOfWork(OutOfBounds), + /// Some low-level aspect of the seal is incorrect. + #[display(fmt = "Block has invalid seal.")] + InvalidSeal, + /// Gas limit header field is invalid. + #[display(fmt = "Invalid gas limit: {}", _0)] + InvalidGasLimit(OutOfBounds), + /// Receipts trie root header field is invalid. + #[display(fmt = "Invalid receipts trie root in header: {}", _0)] + InvalidReceiptsRoot(Mismatch), + /// Timestamp header field is invalid. + #[display(fmt = "Invalid timestamp in header: {}", _0)] + InvalidTimestamp(OutOfBoundsTime), + /// Timestamp header field is too far in future. + #[display(fmt = "Future timestamp in header: {}", _0)] + TemporarilyInvalid(OutOfBoundsTime), + /// Log bloom header field is invalid. + #[display(fmt = "Invalid log bloom in header: {}", _0)] + InvalidLogBloom(Box>), + /// Number field of header is invalid. + #[display(fmt = "Invalid number in header: {}", _0)] + InvalidNumber(Mismatch), + /// Block number isn't sensible. + #[display(fmt = "Implausible block number. {}", _0)] + RidiculousNumber(OutOfBounds), + /// Timestamp header overflowed + #[display(fmt = "Timestamp overflow")] + TimestampOverflow, + /// Too many transactions from a particular address. + #[display(fmt = "Too many transactions from: {}", _0)] + TooManyTransactions(Address), + /// Parent given is unknown. + #[display(fmt = "Unknown parent: {}", _0)] + UnknownParent(H256), + /// Uncle parent given is unknown. + #[display(fmt = "Unknown uncle parent: {}", _0)] + UnknownUncleParent(H256), + /// No transition to epoch number. + #[display(fmt = "Unknown transition to epoch number: {}", _0)] + UnknownEpochTransition(u64), +} + +/// Newtype for Display impl to show seconds +#[derive(Debug, Clone, From, PartialEq, Eq)] +pub struct OutOfBoundsTime(OutOfBounds); + +impl fmt::Display for OutOfBoundsTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let seconds = self.0 + .map(|st| st.elapsed().unwrap_or_default().as_secs()); + f.write_fmt(format_args!("{}", seconds)) + } +} + +impl error::Error for BlockError { + fn description(&self) -> &str { + "Block error" + } +} + +/// Block import Error +#[derive(Debug, Display)] +pub enum ImportError { + /// Already in the block chain. + #[display(fmt = "Block already in chain")] + AlreadyInChain, + /// Already in the block queue + #[display(fmt = "block already in the block queue")] + AlreadyQueued, + /// Already marked as bad from a previous import (could mean parent is bad) + #[display(fmt = "block known to be bad")] + KnownBad, +} + +impl error::Error for ImportError {} diff --git a/ethcore/types/src/errors/engine_error.rs b/ethcore/types/src/errors/engine_error.rs new file mode 100644 index 0000000000..6b5b0b38ed --- /dev/null +++ b/ethcore/types/src/errors/engine_error.rs @@ -0,0 +1,115 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::fmt; + +use derive_more::From; +use ethereum_types::{Address, H64, H256}; +use unexpected::{Mismatch, OutOfBounds}; + +/// Voting errors. +#[derive(Debug, From)] +pub enum EngineError { + /// Signature or author field does not belong to an authority. + NotAuthorized(Address), + /// The same author issued different votes at the same step. + DoubleVote(Address), + /// The received block is from an incorrect proposer. + NotProposer(Mismatch
), + /// Message was not expected. + UnexpectedMessage, + /// Seal field has an unexpected size. + BadSealFieldSize(OutOfBounds), + /// Validation proof insufficient. + InsufficientProof(String), + /// Failed system call. + FailedSystemCall(String), + /// Failed to decode the result of a system call. + SystemCallResultDecoding(String), + /// The result of a system call is invalid. + SystemCallResultInvalid(String), + /// Malformed consensus message. + MalformedMessage(String), + /// Requires client ref, but none registered. + RequiresClient, + /// Invalid engine specification or implementation. + InvalidEngine, + /// Requires signer ref, but none registered. + RequiresSigner, + /// Missing Parent Epoch + MissingParent(H256), + /// Checkpoint is missing + CliqueMissingCheckpoint(H256), + /// Missing vanity data + CliqueMissingVanity, + /// Missing signature + CliqueMissingSignature, + /// Missing signers + CliqueCheckpointNoSigner, + /// List of signers is invalid + CliqueCheckpointInvalidSigners(usize), + /// Wrong author on a checkpoint + CliqueWrongAuthorCheckpoint(Mismatch
), + /// Wrong checkpoint authors recovered + CliqueFaultyRecoveredSigners(Vec), + /// Invalid nonce (should contain vote) + CliqueInvalidNonce(H64), + /// The signer signed a block to recently + CliqueTooRecentlySigned(Address), + /// Custom + Custom(String), +} + +impl fmt::Display for EngineError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::EngineError::*; + let msg = match *self { + CliqueMissingCheckpoint(ref hash) => format!("Missing checkpoint block: {}", hash), + CliqueMissingVanity => format!("Extra data is missing vanity data"), + CliqueMissingSignature => format!("Extra data is missing signature"), + CliqueCheckpointInvalidSigners(len) => format!("Checkpoint block list was of length: {} of checkpoint but + it needs to be bigger than zero and a divisible by 20", len), + CliqueCheckpointNoSigner => format!("Checkpoint block list of signers was empty"), + CliqueInvalidNonce(ref mis) => format!("Unexpected nonce {} expected {} or {}", mis, 0_u64, u64::max_value()), + CliqueWrongAuthorCheckpoint(ref oob) => format!("Unexpected checkpoint author: {}", oob), + CliqueFaultyRecoveredSigners(ref mis) => format!("Faulty recovered signers {:?}", mis), + CliqueTooRecentlySigned(ref address) => format!("The signer: {} has signed a block too recently", address), + Custom(ref s) => s.clone(), + DoubleVote(ref address) => format!("Author {} issued too many blocks.", address), + NotProposer(ref mis) => format!("Author is not a current proposer: {}", mis), + NotAuthorized(ref address) => format!("Signer {} is not authorized.", address), + UnexpectedMessage => "This Engine should not be fed messages.".into(), + BadSealFieldSize(ref oob) => format!("Seal field has an unexpected length: {}", oob), + InsufficientProof(ref msg) => format!("Insufficient validation proof: {}", msg), + FailedSystemCall(ref msg) => format!("Failed to make system call: {}", msg), + SystemCallResultDecoding(ref msg) => format!("Failed to decode the result of a system call: {}", msg), + SystemCallResultInvalid(ref msg) => format!("The result of a system call is invalid: {}", msg), + MalformedMessage(ref msg) => format!("Received malformed consensus message: {}", msg), + RequiresClient => format!("Call requires client but none registered"), + RequiresSigner => format!("Call requires signer but none registered"), + InvalidEngine => format!("Invalid engine specification or implementation"), + MissingParent(ref hash) => format!("Parent Epoch is missing from database: {}", hash), + }; + + f.write_fmt(format_args!("Engine error ({})", msg)) + } +} + +impl std::error::Error for EngineError { + fn description(&self) -> &str { + "Engine error" + } +} diff --git a/ethcore/src/executed.rs b/ethcore/types/src/errors/ethcore_error.rs similarity index 52% rename from ethcore/src/executed.rs rename to ethcore/types/src/errors/ethcore_error.rs index 10e06fd05e..1a99c65dcd 100644 --- a/ethcore/src/executed.rs +++ b/ethcore/types/src/errors/ethcore_error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,62 +14,107 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Transaction execution format module. - -use ethereum_types::{U256, U512, Address}; -use bytes::Bytes; -use ethtrie; -use vm; -use trace::{VMTrace, FlatTrace}; -use types::state_diff::StateDiff; -use types::log_entry::LogEntry; - -use std::{fmt, error}; - -/// Transaction execution receipt. -#[derive(Debug, PartialEq, Clone)] -pub struct Executed { - /// True if the outer call/create resulted in an exceptional exit. - pub exception: Option, - - /// Gas paid up front for execution of transaction. - pub gas: U256, - - /// Gas used during execution of transaction. - pub gas_used: U256, - - /// Gas refunded after the execution of transaction. - /// To get gas that was required up front, add `refunded` and `gas_used`. - pub refunded: U256, +//! General error types for use in ethcore. + +use std::{error, fmt}; +use derive_more::{Display, From}; +use ethereum_types::{U256, U512}; +use ethtrie::TrieError; +use parity_snappy::InvalidInput; +use parity_crypto::publickey::Error as EthPublicKeyCryptoError; + +use errors::{BlockError, EngineError, ImportError, SnapshotError}; +use transaction::Error as TransactionError; + +/// Ethcore Result +pub type EthcoreResult = Result; + +/// Ethcore Error +#[derive(Debug, Display, From)] +pub enum EthcoreError { + /// Error concerning block import. + #[display(fmt = "Import error: {}", _0)] + Import(ImportError), + /// Io channel queue error + #[display(fmt = "Queue is full: {}", _0)] + FullQueue(usize), + /// Io create error + #[display(fmt = "Io error: {}", _0)] + Io(ethcore_io::IoError), + /// Error concerning the Rust standard library's IO subsystem. + #[display(fmt = "Std Io error: {}", _0)] + StdIo(::std::io::Error), + /// Error concerning TrieDBs. + #[display(fmt = "Trie error: {}", _0)] + Trie(TrieError), + /// Error concerning EVM code execution. + #[display(fmt = "Execution error: {}", _0)] + Execution(ExecutionError), + /// Error concerning block processing. + #[display(fmt = "Block error: {}", _0)] + Block(BlockError), + /// Error concerning transaction processing. + #[display(fmt = "Transaction error: {}", _0)] + Transaction(TransactionError), + /// Snappy error + #[display(fmt = "Snappy error: {}", _0)] + Snappy(InvalidInput), + /// Consensus vote error. + #[display(fmt = "Engine error: {}", _0)] + Engine(EngineError), + /// Ethkey error." + #[display(fmt = "Ethkey error: {}", _0)] + Ethkey(EthPublicKeyCryptoError), + /// RLP decoding errors + #[display(fmt = "Decoder error: {}", _0)] + Decoder(rlp::DecoderError), + /// Snapshot error. + #[display(fmt = "Snapshot error {}", _0)] + Snapshot(SnapshotError), + /// PoW hash is invalid or out of date. + #[display(fmt = "PoW hash is invalid or out of date.")] + PowHashInvalid, + /// The value of the nonce or mishash is invalid. + #[display(fmt = "The value of the nonce or mishash is invalid.")] + PowInvalid, + /// A convenient variant for String. + #[display(fmt = "{}", _0)] + Msg(String), +} - /// Cumulative gas used in current block so far. - /// - /// `cumulative_gas_used = gas_used(t0) + gas_used(t1) + ... gas_used(tn)` - /// - /// where `tn` is current transaction. - pub cumulative_gas_used: U256, +impl error::Error for EthcoreError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + use self::EthcoreError::*; + match self { + Io(e) => Some(e), + StdIo(e) => Some(e), + Trie(e) => Some(e), + Execution(e) => Some(e), + Block(e) => Some(e), + Transaction(e) => Some(e), + Snappy(e) => Some(e), + Engine(e) => Some(e), + Ethkey(e) => Some(e), + Decoder(e) => Some(e), + Snapshot(e) => Some(e), + _ => None, + } + } +} - /// Vector of logs generated by transaction. - pub logs: Vec, +impl From<&str> for EthcoreError { + fn from(s: &str) -> Self { + EthcoreError::Msg(s.into()) + } +} - /// Addresses of contracts created during execution of transaction. - /// Ordered from earliest creation. - /// - /// eg. sender creates contract A and A in constructor creates contract B - /// - /// B creation ends first, and it will be the first element of the vector. - pub contracts_created: Vec
, - /// Transaction output. - pub output: Bytes, - /// The trace of this transaction. - pub trace: Vec, - /// The VM trace of this transaction. - pub vm_trace: Option, - /// The state diff, if we traced it. - pub state_diff: Option, +impl From> for EthcoreError where EthcoreError: From { + fn from(err: Box) -> EthcoreError { + EthcoreError::from(*err) + } } -/// Result of executing the transaction. +/// Error type for executing a transaction. #[derive(PartialEq, Debug, Clone)] pub enum ExecutionError { /// Returned when there gas paid for transaction execution is @@ -117,13 +162,19 @@ pub enum ExecutionError { TransactionMalformed(String), } -impl From> for ExecutionError { - fn from(err: Box) -> Self { +impl error::Error for ExecutionError { + fn description(&self) -> &str { + "Transaction execution error" + } +} + +impl From> for ExecutionError { + fn from(err: Box) -> Self { ExecutionError::Internal(format!("{:?}", err)) } } -impl From for ExecutionError { - fn from(err: ethtrie::TrieError) -> Self { +impl From for ExecutionError { + fn from(err: TrieError) -> Self { ExecutionError::Internal(format!("{:?}", err)) } } @@ -152,49 +203,3 @@ impl fmt::Display for ExecutionError { f.write_fmt(format_args!("Transaction execution error ({}).", msg)) } } - -impl error::Error for ExecutionError { - fn description(&self) -> &str { - "Transaction execution error" - } -} - -/// Result of executing the transaction. -#[derive(PartialEq, Debug, Clone)] -pub enum CallError { - /// Couldn't find the transaction in the chain. - TransactionNotFound, - /// Couldn't find requested block's state in the chain. - StatePruned, - /// Couldn't find an amount of gas that didn't result in an exception. - Exceptional(vm::Error), - /// Corrupt state. - StateCorrupt, - /// Error executing. - Execution(ExecutionError), -} - -impl From for CallError { - fn from(error: ExecutionError) -> Self { - CallError::Execution(error) - } -} - -impl fmt::Display for CallError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::CallError::*; - - let msg = match *self { - TransactionNotFound => "Transaction couldn't be found in the chain".into(), - StatePruned => "Couldn't find the transaction block's state in the chain".into(), - Exceptional(ref e) => format!("An exception ({}) happened in the execution", e), - StateCorrupt => "Stored state found to be corrupted.".into(), - Execution(ref e) => format!("{}", e), - }; - - f.write_fmt(format_args!("Transaction execution error ({}).", msg)) - } -} - -/// Transaction execution result. -pub type ExecutionResult = Result, ExecutionError>; diff --git a/json/src/state/mod.rs b/ethcore/types/src/errors/mod.rs similarity index 65% rename from json/src/state/mod.rs rename to ethcore/types/src/errors/mod.rs index 8c2ac28753..f0f8fa77f1 100644 --- a/json/src/state/mod.rs +++ b/ethcore/types/src/errors/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,16 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! State test deserialization. +//! General error types for use in parity-ethereum. -pub mod state; -pub mod transaction; -pub mod test; -pub mod log; +mod block_error; +mod engine_error; +mod ethcore_error; +mod snapshot_error; -pub use self::state::State; -pub use self::transaction::Transaction; -pub use self::test::Test; -pub use self::log::Log; -pub use vm::Env as Env; -pub use blockchain::State as AccountState; +pub use self::{ + block_error::{BlockError, ImportError}, + engine_error::EngineError, + ethcore_error::{EthcoreError, ExecutionError, EthcoreResult}, + snapshot_error::SnapshotError, +}; diff --git a/ethcore/types/src/errors/snapshot_error.rs b/ethcore/types/src/errors/snapshot_error.rs new file mode 100644 index 0000000000..e90e60e480 --- /dev/null +++ b/ethcore/types/src/errors/snapshot_error.rs @@ -0,0 +1,140 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Snapshot-related errors. + +use std::error; +use std::fmt; + +use ethereum_types::H256; +use ethtrie::TrieError; +use rlp::DecoderError; + +use ids::BlockId; + +/// Snapshot-related errors. +#[derive(Debug)] +pub enum SnapshotError { + /// Invalid starting block for snapshot. + InvalidStartingBlock(BlockId), + /// Block not found. + BlockNotFound(H256), + /// Incomplete chain. + IncompleteChain, + /// Best block has wrong state root. + WrongStateRoot(H256, H256), + /// Wrong block hash. + WrongBlockHash(u64, H256, H256), + /// Too many blocks contained within the snapshot. + TooManyBlocks(u64, u64), + /// Old starting block in a pruned database. + OldBlockPrunedDB, + /// Missing code. + MissingCode(Vec), + /// Unrecognized code encoding. + UnrecognizedCodeState(u8), + /// Restoration aborted. + RestorationAborted, + /// Trie error. + Trie(TrieError), + /// Decoder error. + Decoder(DecoderError), + /// Io error. + Io(::std::io::Error), + /// Snapshot version is not supported. + VersionNotSupported(u64), + /// Max chunk size is to small to fit basic account data. + ChunkTooSmall, + /// Oversized chunk + ChunkTooLarge, + /// Snapshots not supported by the consensus engine. + SnapshotsUnsupported, + /// Aborted snapshot + SnapshotAborted, + /// Bad epoch transition. + BadEpochProof(u64), + /// Wrong chunk format. + WrongChunkFormat(String), + /// Unlinked ancient block chain; includes the parent hash where linkage failed + UnlinkedAncientBlockChain(H256), +} + +impl error::Error for SnapshotError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + use self::SnapshotError::*; + match self { + Trie(e) => Some(e), + Decoder(e) => Some(e), + Io(e) => Some(e), + _ => None, + } + } +} + +impl fmt::Display for SnapshotError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::SnapshotError::*; + match *self { + InvalidStartingBlock(ref id) => write!(f, "Invalid starting block: {:?}", id), + BlockNotFound(ref hash) => write!(f, "Block not found in chain: {}", hash), + IncompleteChain => write!(f, "Incomplete blockchain."), + WrongStateRoot(ref expected, ref found) => write!(f, "Final block has wrong state root. Expected {:?}, got {:?}", expected, found), + WrongBlockHash(ref num, ref expected, ref found) => + write!(f, "Block {} had wrong hash. expected {:?}, got {:?}", num, expected, found), + TooManyBlocks(ref expected, ref found) => write!(f, "Snapshot contained too many blocks. Expected {}, got {}", expected, found), + OldBlockPrunedDB => write!(f, "Attempted to create a snapshot at an old block while using \ + a pruned database. Please re-run with the --pruning archive flag."), + MissingCode(ref missing) => write!(f, "Incomplete snapshot: {} contract codes not found.", missing.len()), + UnrecognizedCodeState(state) => write!(f, "Unrecognized code encoding ({})", state), + RestorationAborted => write!(f, "Snapshot restoration aborted."), + Io(ref err) => err.fmt(f), + Decoder(ref err) => err.fmt(f), + Trie(ref err) => err.fmt(f), + VersionNotSupported(ref ver) => write!(f, "Snapshot version {} is not supprted.", ver), + ChunkTooSmall => write!(f, "Chunk size is too small."), + ChunkTooLarge => write!(f, "Chunk size is too large."), + SnapshotsUnsupported => write!(f, "Snapshots unsupported by consensus engine."), + SnapshotAborted => write!(f, "Snapshot was aborted."), + BadEpochProof(i) => write!(f, "Bad epoch proof for transition to epoch {}", i), + WrongChunkFormat(ref msg) => write!(f, "Wrong chunk format: {}", msg), + UnlinkedAncientBlockChain(parent_hash) => write!(f, "Unlinked ancient blocks chain at parent_hash={:#x}", parent_hash), + } + } +} + +impl From<::std::io::Error> for SnapshotError { + fn from(err: ::std::io::Error) -> Self { + SnapshotError::Io(err) + } +} + +impl From for SnapshotError { + fn from(err: TrieError) -> Self { + SnapshotError::Trie(err) + } +} + +impl From for SnapshotError { + fn from(err: DecoderError) -> Self { + SnapshotError::Decoder(err) + } +} + +impl From> for SnapshotError where SnapshotError: From { + fn from(err: Box) -> Self { + SnapshotError::from(*err) + } +} diff --git a/ethcore/types/src/filter.rs b/ethcore/types/src/filter.rs index 71e8d39441..2d588e2ab0 100644 --- a/ethcore/types/src/filter.rs +++ b/ethcore/types/src/filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -76,7 +76,7 @@ impl Filter { let blooms = match self.address { Some(ref addresses) if !addresses.is_empty() => addresses.iter() - .map(|ref address| Bloom::from(BloomInput::Raw(address))) + .map(|ref address| Bloom::from(BloomInput::Raw(address.as_bytes()))) .collect(), _ => vec![Bloom::default()] }; @@ -86,7 +86,7 @@ impl Filter { Some(ref topics) => bs.into_iter().flat_map(|bloom| { topics.into_iter().map(|topic| { let mut b = bloom.clone(); - b.accrue(BloomInput::Raw(topic)); + b.accrue(BloomInput::Raw(topic.as_bytes())); b }).collect::>() }).collect() @@ -109,10 +109,11 @@ impl Filter { #[cfg(test)] mod tests { - use ethereum_types::Bloom; + use ethereum_types::{Bloom, Address, H256}; use filter::Filter; use ids::BlockId; use log_entry::LogEntry; + use std::str::FromStr; #[test] fn test_bloom_possibilities_none() { @@ -135,9 +136,9 @@ mod tests { let filter = Filter { from_block: BlockId::Earliest, to_block: BlockId::Latest, - address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), + address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]), topics: vec![ - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), + Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]), None, None, None, @@ -146,7 +147,7 @@ mod tests { }; let possibilities = filter.bloom_possibilities(); - assert_eq!(possibilities, vec!["00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000".into()] as Vec); + assert_eq!(possibilities, vec![Bloom::from_str("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000").unwrap()]); } #[test] @@ -154,10 +155,10 @@ mod tests { let filter = Filter { from_block: BlockId::Earliest, to_block: BlockId::Latest, - address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), + address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]), topics: vec![ - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), + Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]), + Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]), None, None, ], @@ -165,7 +166,7 @@ mod tests { }; let possibilities = filter.bloom_possibilities(); - assert_eq!(possibilities, vec!["00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000".into()] as Vec); + assert_eq!(possibilities, vec![Bloom::from_str("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000").unwrap()]); } #[test] @@ -174,19 +175,19 @@ mod tests { from_block: BlockId::Earliest, to_block: BlockId::Latest, address: Some(vec![ - "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), - "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), + Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(), + Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(), ]), topics: vec![ Some(vec![ - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into() + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(), + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap() ]), Some(vec![ - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into() + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(), + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap() ]), - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), + Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]), None ], limit: None, @@ -195,7 +196,7 @@ mod tests { // number of possibilites should be equal 2 * 2 * 2 * 1 = 8 let possibilities = filter.bloom_possibilities(); assert_eq!(possibilities.len(), 8); - assert_eq!(possibilities[0], Bloom::from("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000")); + assert_eq!(possibilities[0], Bloom::from_str("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000004000000004000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000").unwrap()); } #[test] @@ -203,10 +204,10 @@ mod tests { let filter = Filter { from_block: BlockId::Earliest, to_block: BlockId::Latest, - address: Some(vec!["b372018f3be9e171df0581136b59d2faf73a7d5d".into()]), + address: Some(vec![Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap()]), topics: vec![ - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into()]), - Some(vec!["ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa".into()]), + Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap()]), + Some(vec![H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap()]), None, None, ], @@ -214,29 +215,29 @@ mod tests { }; let entry0 = LogEntry { - address: "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), + address: Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(), topics: vec![ - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(), + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap(), + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(), ], data: vec![] }; let entry1 = LogEntry { - address: "b372018f3be9e171df0581136b59d2faf73a7d5e".into(), + address: Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5e").unwrap(), topics: vec![ - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa".into(), - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(), + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23fa").unwrap(), + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(), ], data: vec![] }; let entry2 = LogEntry { - address: "b372018f3be9e171df0581136b59d2faf73a7d5d".into(), + address: Address::from_str("b372018f3be9e171df0581136b59d2faf73a7d5d").unwrap(), topics: vec![ - "ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9".into(), + H256::from_str("ff74e91598aed6ae5d2fdcf8b24cd2c7be49a0808112a305069355b7160f23f9").unwrap(), ], data: vec![] }; diff --git a/ethcore/types/src/header.rs b/ethcore/types/src/header.rs index cfe8f5bb65..a36dca4358 100644 --- a/ethcore/types/src/header.rs +++ b/ethcore/types/src/header.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Block header. use hash::{KECCAK_NULL_RLP, KECCAK_EMPTY_LIST_RLP, keccak}; -use heapsize::HeapSizeOf; +use parity_util_mem::MallocSizeOf; use ethereum_types::{H256, U256, Address, Bloom}; use bytes::Bytes; use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable}; @@ -49,7 +49,7 @@ pub struct ExtendedHeader { /// which is non-specific. /// /// Doesn't do all that much on its own. -#[derive(Debug, Clone, Eq)] +#[derive(Debug, Clone, Eq, MallocSizeOf)] pub struct Header { /// Parent hash. parent_hash: H256, @@ -115,10 +115,10 @@ impl PartialEq for Header { impl Default for Header { fn default() -> Self { Header { - parent_hash: H256::default(), + parent_hash: H256::zero(), timestamp: 0, number: 0, - author: Address::default(), + author: Address::zero(), transactions_root: KECCAK_NULL_RLP, uncles_hash: KECCAK_EMPTY_LIST_RLP, @@ -361,12 +361,6 @@ impl Encodable for Header { } } -impl HeapSizeOf for Header { - fn heap_size_of_children(&self) -> usize { - self.extra_data.heap_size_of_children() + self.seal.heap_size_of_children() - } -} - impl ExtendedHeader { /// Returns combined difficulty of all ancestors together with the difficulty of this header. pub fn total_score(&self) -> U256 { @@ -383,11 +377,11 @@ mod tests { #[test] fn test_header_seal_fields() { // that's rlp of block header created with ethash engine. - let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); - let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); - let mix_hash_decoded = "a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); - let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); - let nonce_decoded = "ab4e252a7e8c2a23".from_hex().unwrap(); + let header_rlp: Vec = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + let mix_hash: Vec = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); + let mix_hash_decoded: Vec = "a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); + let nonce: Vec = "88ab4e252a7e8c2a23".from_hex().unwrap(); + let nonce_decoded: Vec = "ab4e252a7e8c2a23".from_hex().unwrap(); let header: Header = rlp::decode(&header_rlp).expect("error decoding header"); let seal_fields = header.seal.clone(); @@ -404,7 +398,7 @@ mod tests { #[test] fn decode_and_encode_header() { // that's rlp of block header created with ethash engine. - let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + let header_rlp: Vec = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); let header: Header = rlp::decode(&header_rlp).expect("error decoding header"); let encoded_header = rlp::encode(&header); @@ -416,7 +410,7 @@ mod tests { fn reject_header_with_large_timestamp() { // that's rlp of block header created with ethash engine. // The encoding contains a large timestamp (295147905179352825856) - let header_rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d891000000000000000000080a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + let header_rlp: Vec = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d891000000000000000000080a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); // This should fail decoding timestamp let header: Result = rlp::decode(&header_rlp); diff --git a/ethcore/types/src/ids.rs b/ethcore/types/src/ids.rs index 1f099be57d..7579a21667 100644 --- a/ethcore/types/src/ids.rs +++ b/ethcore/types/src/ids.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/blockchain/src/import_route.rs b/ethcore/types/src/import_route.rs similarity index 74% rename from ethcore/blockchain/src/import_route.rs rename to ethcore/types/src/import_route.rs index 8c635b4e5a..9df6b28a73 100644 --- a/ethcore/blockchain/src/import_route.rs +++ b/ethcore/types/src/import_route.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Import route. +//! Calculate import route for newly inserted blocks. use ethereum_types::H256; -use crate::block_info::{BlockInfo, BlockLocation}; +use crate::block::{BlockInfo, BlockLocation}; /// Import route for newly inserted block. #[derive(Debug, PartialEq, Clone)] @@ -68,8 +68,8 @@ impl From for ImportRoute { #[cfg(test)] mod tests { - use ethereum_types::{H256, U256}; - use crate::block_info::{BlockInfo, BlockLocation, BranchBecomingCanonChainData}; + use ethereum_types::{U256, BigEndianHash}; + use crate::block::{BlockInfo, BlockLocation, BranchBecomingCanonChainData}; use super::ImportRoute; #[test] @@ -84,7 +84,7 @@ mod tests { #[test] fn import_route_branch() { let info = BlockInfo { - hash: H256::from(U256::from(1)), + hash: BigEndianHash::from_uint(&U256::from(1)), number: 0, total_difficulty: U256::from(0), location: BlockLocation::Branch, @@ -93,14 +93,14 @@ mod tests { assert_eq!(ImportRoute::from(info), ImportRoute { retracted: vec![], enacted: vec![], - omitted: vec![H256::from(U256::from(1))], + omitted: vec![BigEndianHash::from_uint(&U256::from(1))], }); } #[test] fn import_route_canon_chain() { let info = BlockInfo { - hash: H256::from(U256::from(1)), + hash: BigEndianHash::from_uint(&U256::from(1)), number: 0, total_difficulty: U256::from(0), location: BlockLocation::CanonChain, @@ -108,7 +108,7 @@ mod tests { assert_eq!(ImportRoute::from(info), ImportRoute { retracted: vec![], - enacted: vec![H256::from(U256::from(1))], + enacted: vec![BigEndianHash::from_uint(&U256::from(1))], omitted: vec![], }); } @@ -116,19 +116,19 @@ mod tests { #[test] fn import_route_branch_becoming_canon_chain() { let info = BlockInfo { - hash: H256::from(U256::from(2)), + hash: BigEndianHash::from_uint(&U256::from(2)), number: 0, total_difficulty: U256::from(0), location: BlockLocation::BranchBecomingCanonChain(BranchBecomingCanonChainData { - ancestor: H256::from(U256::from(0)), - enacted: vec![H256::from(U256::from(1))], - retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))], + ancestor: BigEndianHash::from_uint(&U256::from(0)), + enacted: vec![BigEndianHash::from_uint(&U256::from(1))], + retracted: vec![BigEndianHash::from_uint(&U256::from(3)), BigEndianHash::from_uint(&U256::from(4))], }) }; assert_eq!(ImportRoute::from(info), ImportRoute { - retracted: vec![H256::from(U256::from(3)), H256::from(U256::from(4))], - enacted: vec![H256::from(U256::from(1)), H256::from(U256::from(2))], + retracted: vec![BigEndianHash::from_uint(&U256::from(3)), BigEndianHash::from_uint(&U256::from(4))], + enacted: vec![BigEndianHash::from_uint(&U256::from(1)), BigEndianHash::from_uint(&U256::from(2))], omitted: vec![], }); } diff --git a/ethcore/src/client/io_message.rs b/ethcore/types/src/io_message.rs similarity index 76% rename from ethcore/src/client/io_message.rs rename to ethcore/types/src/io_message.rs index 92e2d3e258..bac6695f53 100644 --- a/ethcore/src/client/io_message.rs +++ b/ethcore/types/src/io_message.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,15 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +//! Defines the `ClientIoMessage` type, used pervasively throughout various parts of the project to +//! communicate between each other. + use std::fmt; use bytes::Bytes; -use client::Client; use ethereum_types::H256; -use snapshot::ManifestData; +use crate::snapshot::ManifestData; /// Message type for external and internal events #[derive(Debug)] -pub enum ClientIoMessage { +pub enum ClientIoMessage { /// Best Block Hash in chain has been changed NewChainHead, /// A block is ready @@ -36,20 +38,20 @@ pub enum ClientIoMessage { /// Take a snapshot for the block with given number. TakeSnapshot(u64), /// Execute wrapped closure - Execute(Callback), + Execute(Callback), } -impl ClientIoMessage { +impl ClientIoMessage { /// Create new `ClientIoMessage` that executes given procedure. - pub fn execute(fun: F) -> Self { + 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); +pub struct Callback(pub Box); -impl fmt::Debug for Callback { +impl fmt::Debug for Callback { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "") } diff --git a/ethcore/types/src/lib.rs b/ethcore/types/src/lib.rs index 3223db7220..5b664e9dc2 100644 --- a/ethcore/types/src/lib.rs +++ b/ethcore/types/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -33,17 +33,23 @@ #![warn(missing_docs, unused_extern_crates)] +extern crate ethbloom; extern crate ethereum_types; extern crate ethjson; -extern crate ethkey; -extern crate heapsize; +extern crate parity_crypto; +#[macro_use] +extern crate derive_more; extern crate keccak_hash as hash; extern crate parity_bytes as bytes; +extern crate patricia_trie_ethereum as ethtrie; +extern crate parity_snappy; extern crate rlp; extern crate unexpected; #[macro_use] extern crate rlp_derive; +extern crate parity_util_mem; +extern crate parity_util_mem as malloc_size_of; #[cfg(test)] extern crate rustc_hex; @@ -58,22 +64,27 @@ pub mod block; pub mod block_status; pub mod blockchain_info; pub mod call_analytics; +pub mod chain_notify; +pub mod client_types; pub mod encoded; pub mod engines; +pub mod errors; pub mod filter; pub mod header; pub mod ids; +pub mod io_message; +pub mod import_route; pub mod log_entry; pub mod pruning_info; pub mod receipt; -pub mod restoration_status; pub mod security_level; -pub mod snapshot_manifest; +pub mod snapshot; pub mod state_diff; pub mod trace_filter; pub mod transaction; pub mod tree_route; -pub mod verification_queue_info; +pub mod verification; +pub mod data_format; /// 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 a5087b2a00..155ac5da03 100644 --- a/ethcore/types/src/log_entry.rs +++ b/ethcore/types/src/log_entry.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Log entry type definition. use std::ops::Deref; -use heapsize::HeapSizeOf; +use parity_util_mem::MallocSizeOf; use bytes::Bytes; use ethereum_types::{H256, Address, Bloom, BloomInput}; @@ -25,7 +25,7 @@ use {BlockNumber}; use ethjson; /// A record of execution for a `LOG` operation. -#[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)] +#[derive(Default, Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable, MallocSizeOf)] pub struct LogEntry { /// The address of the contract executing at the point of the `LOG` operation. pub address: Address, @@ -35,17 +35,11 @@ pub struct LogEntry { pub data: Bytes, } -impl HeapSizeOf for LogEntry { - fn heap_size_of_children(&self) -> usize { - self.topics.heap_size_of_children() + self.data.heap_size_of_children() - } -} - impl LogEntry { /// Calculates the bloom of this log entry. pub fn bloom(&self) -> Bloom { - self.topics.iter().fold(Bloom::from(BloomInput::Raw(&self.address)), |mut b, t| { - b.accrue(BloomInput::Raw(t)); + self.topics.iter().fold(Bloom::from(BloomInput::Raw(self.address.as_bytes())), |mut b, t| { + b.accrue(BloomInput::Raw(t.as_bytes())); b }) } diff --git a/ethcore/types/src/pruning_info.rs b/ethcore/types/src/pruning_info.rs index 76f775cb7e..c358fea7ba 100644 --- a/ethcore/types/src/pruning_info.rs +++ b/ethcore/types/src/pruning_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/receipt.rs b/ethcore/types/src/receipt.rs index aedb208a78..b630c048a3 100644 --- a/ethcore/types/src/receipt.rs +++ b/ethcore/types/src/receipt.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,14 +17,14 @@ //! Receipt use ethereum_types::{H160, H256, U256, Address, Bloom}; -use heapsize::HeapSizeOf; +use parity_util_mem::MallocSizeOf; use rlp::{Rlp, RlpStream, Encodable, Decodable, DecoderError}; use BlockNumber; use log_entry::{LogEntry, LocalizedLogEntry}; /// Transaction outcome store in the receipt. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, MallocSizeOf)] pub enum TransactionOutcome { /// Status and state root are unknown under EIP-98 rules. Unknown, @@ -35,7 +35,7 @@ pub enum TransactionOutcome { } /// Information describing execution of a transaction. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, MallocSizeOf)] pub struct Receipt { /// The total gas used in the block following execution of the transaction. pub gas_used: U256, @@ -110,12 +110,6 @@ impl Decodable for Receipt { } } -impl HeapSizeOf for Receipt { - fn heap_size_of_children(&self) -> usize { - self.logs.heap_size_of_children() - } -} - /// Receipt with additional info. #[derive(Debug, Clone, PartialEq)] pub struct RichReceipt { @@ -128,6 +122,7 @@ pub struct RichReceipt { /// The gas used in the execution of the transaction. Note the difference of meaning to `Receipt::gas_used`. pub gas_used: U256, /// Contract address. + /// NOTE: It is an Option because only `Action::Create` transactions has a contract address pub contract_address: Option
, /// Logs pub logs: Vec, @@ -135,6 +130,11 @@ pub struct RichReceipt { pub log_bloom: Bloom, /// Transaction outcome. pub outcome: TransactionOutcome, + /// Receiver address + /// NOTE: It is an Option because only `Action::Call` transactions has a receiver address + pub to: Option, + /// Sender + pub from: H160 } /// Receipt with additional info. @@ -153,6 +153,7 @@ pub struct LocalizedReceipt { /// The gas used in the execution of the transaction. Note the difference of meaning to `Receipt::gas_used`. pub gas_used: U256, /// Contract address. + /// NOTE: It is an Option because only `Action::Create` transactions has a contract address pub contract_address: Option
, /// Logs pub logs: Vec, @@ -161,6 +162,7 @@ pub struct LocalizedReceipt { /// Transaction outcome. pub outcome: TransactionOutcome, /// Receiver address + /// NOTE: It is an Option because only `Action::Call` transactions has a receiver address pub to: Option, /// Sender pub from: H160 @@ -168,17 +170,20 @@ pub struct LocalizedReceipt { #[cfg(test)] mod tests { - use super::{Receipt, TransactionOutcome}; + use std::str::FromStr; + + use super::{Receipt, TransactionOutcome, Address, H256}; use log_entry::LogEntry; + use rustc_hex::FromHex; #[test] fn test_no_state_root() { - let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let expected: Vec = FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let r = Receipt::new( TransactionOutcome::Unknown, 0x40cae.into(), vec![LogEntry { - address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), + address: Address::from_str("dcf421d093428b096ca501a7cd1a740855a7976f").unwrap(), topics: vec![], data: vec![0u8; 32] }] @@ -188,37 +193,37 @@ mod tests { #[test] fn test_basic() { - let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let expected: Vec = FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let r = Receipt::new( - TransactionOutcome::StateRoot("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into()), + TransactionOutcome::StateRoot(H256::from_str("2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee").unwrap()), 0x40cae.into(), vec![LogEntry { - address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), + address: Address::from_str("dcf421d093428b096ca501a7cd1a740855a7976f").unwrap(), topics: vec![], data: vec![0u8; 32] }] ); - let encoded = ::rlp::encode(&r); + let encoded = rlp::encode(&r); assert_eq!(&encoded[..], &expected[..]); - let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed"); + let decoded: Receipt = rlp::decode(&encoded).expect("decoding receipt failed"); assert_eq!(decoded, r); } #[test] fn test_status_code() { - let expected = ::rustc_hex::FromHex::from_hex("f901428083040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let expected: Vec = FromHex::from_hex("f901428083040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let r = Receipt::new( TransactionOutcome::StatusCode(0), 0x40cae.into(), vec![LogEntry { - address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(), + address: Address::from_str("dcf421d093428b096ca501a7cd1a740855a7976f").unwrap(), topics: vec![], data: vec![0u8; 32] }] ); - let encoded = ::rlp::encode(&r); + let encoded = rlp::encode(&r); assert_eq!(&encoded[..], &expected[..]); - let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed"); + 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 deleted file mode 100644 index b36ec7ef4a..0000000000 --- a/ethcore/types/src/restoration_status.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Restoration status type definition - -/// Statuses for restorations. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub enum RestorationStatus { - /// No restoration. - Inactive, - /// Restoration is initializing - Initializing { - /// Number of chunks done/imported - chunks_done: u32, - }, - /// Ongoing restoration. - Ongoing { - /// Total number of state chunks. - state_chunks: u32, - /// Total number of block chunks. - block_chunks: u32, - /// Number of state chunks completed. - state_chunks_done: u32, - /// Number of block chunks completed. - block_chunks_done: u32, - }, - /// Failed restoration. - Failed, -} diff --git a/ethcore/types/src/security_level.rs b/ethcore/types/src/security_level.rs index eb87317e7f..9d7a61440f 100644 --- a/ethcore/types/src/security_level.rs +++ b/ethcore/types/src/security_level.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/snapshot.rs b/ethcore/types/src/snapshot.rs new file mode 100644 index 0000000000..e08ba70d4c --- /dev/null +++ b/ethcore/types/src/snapshot.rs @@ -0,0 +1,199 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Snapshot type definitions + +use std::time::Instant; + +use bytes::Bytes; +use ethereum_types::H256; +use rlp::{Rlp, RlpStream, DecoderError}; + +/// Modes of snapshotting +pub enum Snapshotting { + /// Snapshotting and warp sync is not supported + Unsupported, + /// Snapshots for proof-of-work chains + PoW { + /// Number of blocks from the head of the chain + /// to include in the snapshot. + blocks: u64, + /// Number of blocks to allow in the snapshot when restoring. + max_restore_blocks: u64 + }, + /// Snapshots for proof-of-authority chains + PoA, +} + +/// A progress indicator for snapshots. +#[derive(Debug)] +pub struct Progress { + /// Number of accounts processed so far + accounts: u64, + // Number of accounts processed at last tick. + prev_accounts: u64, + /// Number of blocks processed so far + pub blocks: u64, + /// Size in bytes of a all compressed chunks processed so far + bytes: u64, + // Number of bytes processed at last tick. + prev_bytes: u64, + /// Signals that the snapshotting process is completed + pub done: bool, + /// Signal snapshotting process to abort + pub abort: bool, + + last_tick: Instant, +} + +impl Progress { + /// Create a new progress tracker. + pub fn new() -> Progress { + Progress { + accounts: 0, + prev_accounts: 0, + blocks: 0, + bytes: 0, + prev_bytes: 0, + abort: false, + done: false, + last_tick: Instant::now(), + } + } + + /// Get the number of accounts snapshotted thus far. + pub fn accounts(&self) -> u64 { self.accounts } + + /// Get the number of blocks snapshotted thus far. + pub fn blocks(&self) -> u64 { self.blocks } + + /// Get the written size of the snapshot in bytes. + pub fn bytes(&self) -> u64 { self.bytes } + + /// Whether the snapshot is complete. + pub fn done(&self) -> bool { self.done } + + /// Return the progress rate over the last tick (i.e. since last update). + pub fn rate(&self) -> (f64, f64) { + let dt = self.last_tick.elapsed().as_secs_f64(); + if dt < 1.0 { + return (0f64, 0f64); + } + let delta_acc = self.accounts.saturating_sub(self.prev_accounts); + let delta_bytes = self.bytes.saturating_sub(self.prev_bytes); + (delta_acc as f64 / dt, delta_bytes as f64 / dt) + } + + /// Update state progress counters and set the last tick. + pub fn update(&mut self, accounts_delta: u64, bytes_delta: u64) { + self.last_tick = Instant::now(); + self.prev_accounts = self.accounts; + self.prev_bytes = self.bytes; + self.accounts += accounts_delta; + self.bytes += bytes_delta; + } +} + +/// Manifest data. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ManifestData { + /// Snapshot format version. + pub version: u64, + /// List of state chunk hashes. + pub state_hashes: Vec, + /// List of block chunk hashes. + pub block_hashes: Vec, + /// The final, expected state root. + pub state_root: H256, + /// Block number this snapshot was taken at. + pub block_number: u64, + /// Block hash this snapshot was taken at. + pub block_hash: H256, +} + +impl ManifestData { + /// Encode the manifest data to rlp. + pub fn into_rlp(self) -> Bytes { + let mut stream = RlpStream::new_list(6); + stream.append(&self.version); + stream.append_list(&self.state_hashes); + stream.append_list(&self.block_hashes); + stream.append(&self.state_root); + stream.append(&self.block_number); + stream.append(&self.block_hash); + + stream.out() + } + + /// Try to restore manifest data from raw bytes, interpreted as RLP. + pub fn from_rlp(raw: &[u8]) -> Result { + let decoder = Rlp::new(raw); + let (start, version) = if decoder.item_count()? == 5 { + (0, 1) + } else { + (1, decoder.val_at(0)?) + }; + + let state_hashes: Vec = decoder.list_at(start + 0)?; + let block_hashes: Vec = decoder.list_at(start + 1)?; + let state_root: H256 = decoder.val_at(start + 2)?; + let block_number: u64 = decoder.val_at(start + 3)?; + let block_hash: H256 = decoder.val_at(start + 4)?; + + Ok(ManifestData { + version, + state_hashes, + block_hashes, + state_root, + block_number, + block_hash, + }) + } +} + +/// A sink for produced chunks. +pub type ChunkSink<'a> = dyn FnMut(&[u8]) -> std::io::Result<()> + 'a; + +/// Statuses for snapshot restoration. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum RestorationStatus { + /// No restoration activity currently. + Inactive, + /// Restoration is initializing. + Initializing { + /// Total number of state chunks. + state_chunks: u32, + /// Total number of block chunks. + block_chunks: u32, + /// Number of chunks done/imported + chunks_done: u32, + }, + /// Ongoing restoration. + Ongoing { + /// Total number of state chunks. + state_chunks: u32, + /// Total number of block chunks. + block_chunks: u32, + /// Number of state chunks completed. + state_chunks_done: u32, + /// Number of block chunks completed. + block_chunks_done: u32, + }, + /// Finalizing restoration. + Finalizing, + /// Failed restoration. + Failed, +} diff --git a/ethcore/types/src/snapshot_manifest.rs b/ethcore/types/src/snapshot_manifest.rs deleted file mode 100644 index 8ed19fcfc2..0000000000 --- a/ethcore/types/src/snapshot_manifest.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Snapshot manifest type definition - -use ethereum_types::H256; -use rlp::{Rlp, RlpStream, DecoderError}; -use bytes::Bytes; - -/// Manifest data. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ManifestData { - /// Snapshot format version. - pub version: u64, - /// List of state chunk hashes. - pub state_hashes: Vec, - /// List of block chunk hashes. - pub block_hashes: Vec, - /// The final, expected state root. - pub state_root: H256, - /// Block number this snapshot was taken at. - pub block_number: u64, - /// Block hash this snapshot was taken at. - pub block_hash: H256, -} - -impl ManifestData { - /// Encode the manifest data to rlp. - pub fn into_rlp(self) -> Bytes { - let mut stream = RlpStream::new_list(6); - stream.append(&self.version); - stream.append_list(&self.state_hashes); - stream.append_list(&self.block_hashes); - stream.append(&self.state_root); - stream.append(&self.block_number); - stream.append(&self.block_hash); - - stream.out() - } - - /// Try to restore manifest data from raw bytes, interpreted as RLP. - pub fn from_rlp(raw: &[u8]) -> Result { - let decoder = Rlp::new(raw); - let (start, version) = if decoder.item_count()? == 5 { - (0, 1) - } else { - (1, decoder.val_at(0)?) - }; - - let state_hashes: Vec = decoder.list_at(start + 0)?; - let block_hashes: Vec = decoder.list_at(start + 1)?; - let state_root: H256 = decoder.val_at(start + 2)?; - let block_number: u64 = decoder.val_at(start + 3)?; - let block_hash: H256 = decoder.val_at(start + 4)?; - - Ok(ManifestData { - version: version, - state_hashes: state_hashes, - block_hashes: block_hashes, - state_root: state_root, - block_number: block_number, - block_hash: block_hash, - }) - } -} diff --git a/ethcore/types/src/state_diff.rs b/ethcore/types/src/state_diff.rs index 5605719f01..0996c79d8b 100644 --- a/ethcore/types/src/state_diff.rs +++ b/ethcore/types/src/state_diff.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,11 +16,9 @@ //! State diff module. -use std::fmt; -use std::ops::*; use std::collections::BTreeMap; +use account_diff::AccountDiff; use ethereum_types::Address; -use account_diff::*; /// Expression for the delta between two system states. Encoded the /// delta of every altered account. @@ -29,27 +27,3 @@ pub struct StateDiff { /// Raw diff key-value pub raw: BTreeMap } - -impl StateDiff { - /// Get the actual data. - pub fn get(&self) -> &BTreeMap { - &self.raw - } -} - -impl fmt::Display for StateDiff { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for (add, acc) in &self.raw { - write!(f, "{} {}: {}", acc.existance(), add, acc)?; - } - Ok(()) - } -} - -impl Deref for StateDiff { - type Target = BTreeMap; - - fn deref(&self) -> &Self::Target { - &self.raw - } -} diff --git a/ethcore/types/src/trace_filter.rs b/ethcore/types/src/trace_filter.rs index 8b1d715b42..630cd31dee 100644 --- a/ethcore/types/src/trace_filter.rs +++ b/ethcore/types/src/trace_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/transaction/error.rs b/ethcore/types/src/transaction/error.rs index 68c0b2c0fe..8907da80e7 100644 --- a/ethcore/types/src/transaction/error.rs +++ b/ethcore/types/src/transaction/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,10 +17,12 @@ use std::{fmt, error}; use ethereum_types::U256; -use ethkey; +use parity_crypto::publickey::{Error as EthPublicKeyCryptoError}; use rlp; use unexpected::OutOfBounds; +use errors::ExecutionError; + #[derive(Debug, PartialEq, Clone)] /// Errors concerning transaction processing. pub enum Error { @@ -86,8 +88,8 @@ pub enum Error { InvalidRlp(String), } -impl From for Error { - fn from(err: ethkey::Error) -> Self { +impl From for Error { + fn from(err: EthPublicKeyCryptoError) -> Self { Error::InvalidSignature(format!("{}", err)) } } @@ -124,7 +126,7 @@ impl fmt::Display for Error { CodeBanned => "Contract code is temporarily banned.".into(), 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(), + NotAllowed => "Sender does not have permissions to execute this type of transaction".into(), TooBig => "Transaction too big".into(), InvalidRlp(ref err) => format!("Transaction has invalid RLP structure: {}.", err), }; @@ -138,3 +140,40 @@ impl error::Error for Error { "Transaction error" } } + +/// Result of executing the transaction. +#[derive(PartialEq, Debug, Clone)] +pub enum CallError { + /// Couldn't find the transaction in the chain. + TransactionNotFound, + /// Couldn't find requested block's state in the chain. + StatePruned, + /// Couldn't find an amount of gas that didn't result in an exception. + Exceptional(vm::Error), + /// Corrupt state. + StateCorrupt, + /// Error executing. + Execution(ExecutionError), +} + +impl From for CallError { + fn from(error: ExecutionError) -> Self { + CallError::Execution(error) + } +} + +impl fmt::Display for CallError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::CallError::*; + let msg = match *self { + TransactionNotFound => "Transaction couldn't be found in the chain".into(), + StatePruned => "Couldn't find the transaction block's state in the chain".into(), + Exceptional(ref e) => format!("An exception ({}) happened in the execution", e), + StateCorrupt => "Stored state found to be corrupted.".into(), + Execution(ref e) => format!("{}", e), + }; + + f.write_fmt(format_args!("Transaction execution error ({}).", msg)) + } +} + diff --git a/ethcore/types/src/transaction/mod.rs b/ethcore/types/src/transaction/mod.rs index 4b26b7dc14..04de86461c 100644 --- a/ethcore/types/src/transaction/mod.rs +++ b/ethcore/types/src/transaction/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,5 +19,5 @@ mod error; mod transaction; -pub use self::error::Error; +pub use self::error::{Error, CallError}; pub use self::transaction::*; diff --git a/ethcore/types/src/transaction/transaction.rs b/ethcore/types/src/transaction/transaction.rs index 439880089e..0fe4bb67aa 100644 --- a/ethcore/types/src/transaction/transaction.rs +++ b/ethcore/types/src/transaction/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,11 +18,12 @@ use std::ops::Deref; -use ethereum_types::{H256, H160, Address, U256}; +use ethereum_types::{H256, H160, Address, U256, BigEndianHash}; use ethjson; -use ethkey::{self, Signature, Secret, Public, recover, public_to_address}; +use parity_crypto::publickey::{Signature, Secret, Public, recover, public_to_address}; use hash::keccak; -use heapsize::HeapSizeOf; +use parity_util_mem::MallocSizeOf; + use rlp::{self, RlpStream, Rlp, DecoderError, Encodable}; use transaction::error; @@ -37,7 +38,7 @@ pub const UNSIGNED_SENDER: Address = H160([0xff; 20]); pub const SYSTEM_ADDRESS: Address = H160([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,0xff, 0xff, 0xff, 0xff,0xff, 0xff, 0xff, 0xff,0xff, 0xff, 0xff, 0xfe]); /// Transaction action type. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, MallocSizeOf)] pub enum Action { /// Create creates new contract. Create, @@ -103,7 +104,7 @@ pub mod signature { /// A set of information describing an externally-originating message call /// or contract creation operation. -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq, Eq, MallocSizeOf)] pub struct Transaction { /// Nonce. pub nonce: U256, @@ -137,14 +138,9 @@ impl Transaction { } } -impl HeapSizeOf for Transaction { - fn heap_size_of_children(&self) -> usize { - self.data.heap_size_of_children() - } -} - -impl From for SignedTransaction { - fn from(t: ethjson::state::Transaction) -> Self { +#[cfg(any(test, feature = "test-helpers"))] +impl From for SignedTransaction { + fn from(t: ethjson::transaction::Transaction) -> Self { let to: Option = t.to.into(); let secret = t.secret.map(|s| Secret::from(s.0)); let tx = Transaction { @@ -183,7 +179,7 @@ impl From for UnverifiedTransaction { r: t.r.into(), s: t.s.into(), v: t.v.into(), - hash: 0.into(), + hash: H256::zero(), }.compute_hash() } } @@ -198,7 +194,7 @@ impl Transaction { /// Signs the transaction as coming from `sender`. pub fn sign(self, secret: &Secret, chain_id: Option) -> SignedTransaction { - let sig = ::ethkey::sign(secret, &self.hash(chain_id)) + let sig = parity_crypto::publickey::sign(secret, &self.hash(chain_id)) .expect("data is valid and context has signing capabilities; qed"); SignedTransaction::new(self.with_signature(sig, chain_id)) .expect("secret is valid so it's recoverable") @@ -211,7 +207,7 @@ impl Transaction { r: sig.r().into(), s: sig.s().into(), v: signature::add_chain_replay_protection(sig.v() as u64, chain_id), - hash: 0.into(), + hash: H256::zero(), }.compute_hash() } @@ -223,7 +219,7 @@ impl Transaction { r: U256::one(), s: U256::one(), v: 0, - hash: 0.into(), + hash: H256::zero(), }.compute_hash() } @@ -235,14 +231,17 @@ impl Transaction { r: U256::one(), s: U256::one(), v: 0, - hash: 0.into(), + hash: H256::zero(), }.compute_hash(), sender: from, public: None, } } - /// Add EIP-86 compatible empty signature. + /// Legacy EIP-86 compatible empty signature. + /// This method is used in json tests as well as + /// signature verification tests. + #[cfg(any(test, feature = "test-helpers"))] pub fn null_sign(self, chain_id: u64) -> SignedTransaction { SignedTransaction { transaction: UnverifiedTransaction { @@ -250,7 +249,7 @@ impl Transaction { r: U256::zero(), s: U256::zero(), v: chain_id, - hash: 0.into(), + hash: H256::zero(), }.compute_hash(), sender: UNSIGNED_SENDER, public: None, @@ -259,7 +258,7 @@ impl Transaction { } /// Signed transaction information without verified signature. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, MallocSizeOf)] pub struct UnverifiedTransaction { /// Plain Transaction. unsigned: Transaction, @@ -274,12 +273,6 @@ pub struct UnverifiedTransaction { hash: H256, } -impl HeapSizeOf for UnverifiedTransaction { - fn heap_size_of_children(&self) -> usize { - self.unsigned.heap_size_of_children() - } -} - impl Deref for UnverifiedTransaction { type Target = Transaction; @@ -306,7 +299,7 @@ impl rlp::Decodable for UnverifiedTransaction { v: d.val_at(6)?, r: d.val_at(7)?, s: d.val_at(8)?, - hash: hash, + hash, }) } } @@ -323,11 +316,19 @@ impl UnverifiedTransaction { self } - /// Checks is signature is empty. + /// Checks if the signature is empty. pub fn is_unsigned(&self) -> bool { self.r.is_zero() && self.s.is_zero() } + /// Returns transaction receiver, if any + pub fn receiver(&self) -> Option
{ + match self.unsigned.action { + Action::Create => None, + Action::Call(receiver) => Some(receiver), + } + } + /// Append object with a signature into RLP stream fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) { s.begin_list(9); @@ -364,13 +365,15 @@ impl UnverifiedTransaction { /// Construct a signature object from the sig. pub fn signature(&self) -> Signature { - Signature::from_rsv(&self.r.into(), &self.s.into(), self.standard_v()) + let r: H256 = BigEndianHash::from_uint(&self.r); + let s: H256 = BigEndianHash::from_uint(&self.s); + Signature::from_rsv(&r, &s, self.standard_v()) } /// Checks whether the signature has a low 's' value. - pub fn check_low_s(&self) -> Result<(), ethkey::Error> { + pub fn check_low_s(&self) -> Result<(), parity_crypto::publickey::Error> { if !self.signature().is_low_s() { - Err(ethkey::Error::InvalidSignature.into()) + Err(parity_crypto::publickey::Error::InvalidSignature) } else { Ok(()) } @@ -382,22 +385,17 @@ impl UnverifiedTransaction { } /// Recovers the public key of the sender. - pub fn recover_public(&self) -> Result { + pub fn recover_public(&self) -> Result { Ok(recover(&self.signature(), &self.unsigned.hash(self.chain_id()))?) } /// Verify basic signature params. Does not attempt sender recovery. - pub fn verify_basic(&self, check_low_s: bool, chain_id: Option, allow_empty_signature: bool) -> Result<(), error::Error> { - 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()); + pub fn verify_basic(&self, check_low_s: bool, chain_id: Option) -> Result<(), error::Error> { + if self.is_unsigned() { + return Err(parity_crypto::publickey::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()) + if check_low_s { + self.check_low_s()?; } match (self.chain_id(), chain_id) { (None, _) => {}, @@ -406,22 +404,21 @@ impl UnverifiedTransaction { }; Ok(()) } + + /// Try to verify transaction and recover sender. + pub fn verify_unordered(self) -> Result { + SignedTransaction::new(self) + } } /// A `UnverifiedTransaction` with successfully recovered `sender`. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, MallocSizeOf)] pub struct SignedTransaction { transaction: UnverifiedTransaction, sender: Address, public: Option, } -impl HeapSizeOf for SignedTransaction { - fn heap_size_of_children(&self) -> usize { - self.transaction.heap_size_of_children() - } -} - impl rlp::Encodable for SignedTransaction { fn rlp_append(&self, s: &mut RlpStream) { self.transaction.rlp_append_sealed_transaction(s) } } @@ -441,22 +438,17 @@ impl From for UnverifiedTransaction { impl SignedTransaction { /// Try to verify transaction and recover sender. - pub fn new(transaction: UnverifiedTransaction) -> Result { + pub fn new(transaction: UnverifiedTransaction) -> Result { if transaction.is_unsigned() { - Ok(SignedTransaction { - transaction: transaction, - sender: UNSIGNED_SENDER, - public: None, - }) - } else { - let public = transaction.recover_public()?; - let sender = public_to_address(&public); - Ok(SignedTransaction { - transaction: transaction, - sender: sender, - public: Some(public), - }) + return Err(parity_crypto::publickey::Error::InvalidSignature); } + let public = transaction.recover_public()?; + let sender = public_to_address(&public); + Ok(SignedTransaction { + transaction, + sender, + public: Some(public), + }) } /// Returns transaction sender. @@ -556,23 +548,26 @@ impl From for PendingTransaction { #[cfg(test)] mod tests { + use std::str::FromStr; + use super::*; - use ethereum_types::U256; + use ethereum_types::{U256, Address}; use hash::keccak; + use rustc_hex::FromHex; #[test] fn sender_test() { - let bytes = ::rustc_hex::FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); + let bytes: Vec = 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)); assert_eq!(t.nonce, U256::from(0x00u64)); if let Action::Call(ref to) = t.action { - assert_eq!(*to, "095e7baea6a6c7c4c2dfeb977efac326af552d87".into()); + assert_eq!(*to, Address::from_str("095e7baea6a6c7c4c2dfeb977efac326af552d87").unwrap()); } else { panic!(); } assert_eq!(t.value, U256::from(0x0au64)); - assert_eq!(public_to_address(&t.recover_public().unwrap()), "0f65fe9276bc9a24ae7083ae28e2660ef72df99e".into()); + assert_eq!(public_to_address(&t.recover_public().unwrap()), Address::from_str("0f65fe9276bc9a24ae7083ae28e2660ef72df99e").unwrap()); assert_eq!(t.chain_id(), None); } @@ -592,7 +587,7 @@ mod tests { #[test] fn signing_eip155_zero_chainid() { - use ethkey::{Random, Generator}; + use parity_crypto::publickey::{Random, Generator}; let key = Random.generate().unwrap(); let t = Transaction { @@ -605,7 +600,7 @@ mod tests { }; let hash = t.hash(Some(0)); - let sig = ::ethkey::sign(&key.secret(), &hash).unwrap(); + let sig = parity_crypto::publickey::sign(&key.secret(), &hash).unwrap(); let u = t.with_signature(sig, Some(0)); assert!(SignedTransaction::new(u).is_ok()); @@ -613,7 +608,7 @@ mod tests { #[test] fn signing() { - use ethkey::{Random, Generator}; + use parity_crypto::publickey::{Random, Generator}; let key = Random.generate().unwrap(); let t = Transaction { @@ -637,18 +632,36 @@ mod tests { gas: U256::from(50_000), value: U256::from(1), data: b"Hello!".to_vec() - }.fake_sign(Address::from(0x69)); - assert_eq!(Address::from(0x69), t.sender()); + }.fake_sign(Address::from_low_u64_be(0x69)); + assert_eq!(Address::from_low_u64_be(0x69), t.sender()); assert_eq!(t.chain_id(), None); let t = t.clone(); - assert_eq!(Address::from(0x69), t.sender()); + assert_eq!(Address::from_low_u64_be(0x69), t.sender()); assert_eq!(t.chain_id(), None); } + #[test] + fn should_reject_null_signature() { + let t = Transaction { + nonce: U256::zero(), + gas_price: U256::from(10000000000u64), + gas: U256::from(21000), + action: Action::Call(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + value: U256::from(1), + data: vec![] + }.null_sign(1); + + let res = SignedTransaction::new(t.transaction); + match res { + Err(parity_crypto::publickey::Error::InvalidSignature) => {} + _ => panic!("null signature should be rejected"), + } + } + #[test] fn should_recover_from_chain_specific_signing() { - use ethkey::{Random, Generator}; + use parity_crypto::publickey::{Random, Generator}; let key = Random.generate().unwrap(); let t = Transaction { action: Action::Create, @@ -664,12 +677,10 @@ mod tests { #[test] fn should_agree_with_vitalik() { - use rustc_hex::FromHex; - let test_vector = |tx_data: &str, address: &'static str| { - 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()); + let bytes = rlp::decode(&tx_data.from_hex::>().unwrap()).expect("decoding tx data failed"); + let signed = SignedTransaction::new(bytes).unwrap(); + assert_eq!(signed.sender(), Address::from_str(&address[2..]).unwrap()); println!("chainid: {:?}", signed.chain_id()); }; diff --git a/ethcore/types/src/tree_route.rs b/ethcore/types/src/tree_route.rs index 0386472b85..db9e9d9a43 100644 --- a/ethcore/types/src/tree_route.rs +++ b/ethcore/types/src/tree_route.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/verification_queue_info.rs b/ethcore/types/src/verification.rs similarity index 64% rename from ethcore/types/src/verification_queue_info.rs rename to ethcore/types/src/verification.rs index a855fee6a1..8beb2d0782 100644 --- a/ethcore/types/src/verification_queue_info.rs +++ b/ethcore/types/src/verification.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,7 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Verification queue info types +//! Verification types + +use crate::{ + header::Header, + transaction::UnverifiedTransaction, +}; +use bytes::Bytes; +use parity_util_mem::MallocSizeOf; /// Verification queue status #[derive(Debug, Clone)] @@ -37,9 +44,6 @@ impl VerificationQueueInfo { /// The total size of the queues. pub fn total_queue_size(&self) -> usize { self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size } - /// The size of the unverified and verifying queues. - pub fn incomplete_queue_size(&self) -> usize { self.unverified_queue_size + self.verifying_queue_size } - /// Indicates that queue is full pub fn is_full(&self) -> bool { self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size > self.max_queue_size || @@ -51,3 +55,38 @@ impl VerificationQueueInfo { self.unverified_queue_size + self.verified_queue_size + self.verifying_queue_size == 0 } } + +/// An unverified block. +#[derive(Clone, PartialEq, Debug, MallocSizeOf)] +pub struct Unverified { + /// Unverified block header. + pub header: Header, + /// Unverified block transactions. + pub transactions: Vec, + /// Unverified block uncles. + pub uncles: Vec
, + /// Raw block bytes. + pub bytes: Bytes, +} + +impl Unverified { + /// Create an `Unverified` from raw bytes. + pub fn from_rlp(bytes: Bytes) -> Result { + use rlp::Rlp; + let (header, transactions, uncles) = { + let rlp = Rlp::new(&bytes); + let header = rlp.val_at(0)?; + let transactions = rlp.list_at(1)?; + let uncles = rlp.list_at(2)?; + (header, transactions, uncles) + }; + + Ok(Unverified { + header, + transactions, + uncles, + bytes, + }) + } +} + diff --git a/ethcore/types/src/views/block.rs b/ethcore/types/src/views/block.rs index 9ad67ddd66..5666a10318 100644 --- a/ethcore/types/src/views/block.rs +++ b/ethcore/types/src/views/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -177,15 +177,16 @@ impl<'a> BlockView<'a> { #[cfg(test)] mod tests { use rustc_hex::FromHex; - use super::BlockView; + use super::{BlockView, H256}; + use std::str::FromStr; #[test] fn test_block_view() { // that's rlp of block created with ethash engine. - let rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); + let rlp: Vec = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); let view = view!(BlockView, &rlp); - assert_eq!(view.hash(), "2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259".into()); + assert_eq!(view.hash(), H256::from_str("2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259").unwrap()); assert_eq!(view.transactions_count(), 1); assert_eq!(view.uncles_count(), 0); } diff --git a/ethcore/types/src/views/body.rs b/ethcore/types/src/views/body.rs index 1ea4999b8b..dbc6e88013 100644 --- a/ethcore/types/src/views/body.rs +++ b/ethcore/types/src/views/body.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -167,7 +167,7 @@ mod tests { #[test] fn test_block_view() { // that's rlp of block created with ethash engine. - let rlp = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); + let rlp: Vec = "f90261f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23f862f86002018304cb2f94ec0e71ad0a90ffe1909d27dac207f7680abba42d01801ba03a347e72953c860f32b1eb2c78a680d8734b2ea08085d949d729479796f218d5a047ea6239d9e31ccac8af3366f5ca37184d26e7646e3191a3aeb81c4cf74de500c0".from_hex().unwrap(); let body = block_to_body(&rlp); let view = view!(BodyView, &body); assert_eq!(view.transactions_count(), 1); diff --git a/ethcore/types/src/views/header.rs b/ethcore/types/src/views/header.rs index 4009892910..0e52dd8b1b 100644 --- a/ethcore/types/src/views/header.rs +++ b/ethcore/types/src/views/header.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -120,24 +120,25 @@ impl<'a> HeaderView<'a> { #[cfg(test)] mod tests { use rustc_hex::FromHex; - use ethereum_types::Bloom; + use ethereum_types::{Bloom, H256, Address}; use super::HeaderView; + use std::str::FromStr; #[test] fn test_header_view() { // that's rlp of block header created with ethash engine. - let rlp = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); - let mix_hash = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); - let nonce = "88ab4e252a7e8c2a23".from_hex().unwrap(); + let rlp: Vec = "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".from_hex().unwrap(); + let mix_hash: Vec = "a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd".from_hex().unwrap(); + let nonce: Vec = "88ab4e252a7e8c2a23".from_hex().unwrap(); 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()); - assert_eq!(view.author(), "8888f1f195afa192cfee860698584c030f4c9db1".into()); - assert_eq!(view.state_root(), "5fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25".into()); - assert_eq!(view.transactions_root(), "88d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158".into()); - assert_eq!(view.receipts_root(), "07c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1".into()); + assert_eq!(view.hash(), H256::from_str("2c9747e804293bd3f1a986484343f23bc88fd5be75dfe9d5c2860aff61e6f259").unwrap()); + assert_eq!(view.parent_hash(), H256::from_str("d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7").unwrap()); + assert_eq!(view.uncles_hash(), H256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap()); + assert_eq!(view.author(), Address::from_str("8888f1f195afa192cfee860698584c030f4c9db1").unwrap()); + assert_eq!(view.state_root(), H256::from_str("5fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25").unwrap()); + assert_eq!(view.transactions_root(), H256::from_str("88d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158").unwrap()); + assert_eq!(view.receipts_root(), H256::from_str("07c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1").unwrap()); assert_eq!(view.log_bloom(), Bloom::default()); assert_eq!(view.difficulty(), 0x020080.into()); assert_eq!(view.number(), 3); diff --git a/ethcore/types/src/views/mod.rs b/ethcore/types/src/views/mod.rs index f5c2eab941..ff3a9be3d8 100644 --- a/ethcore/types/src/views/mod.rs +++ b/ethcore/types/src/views/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/types/src/views/transaction.rs b/ethcore/types/src/views/transaction.rs index b7d412f6ce..67fcd31f1b 100644 --- a/ethcore/types/src/views/transaction.rs +++ b/ethcore/types/src/views/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -90,14 +90,14 @@ mod tests { #[test] fn test_transaction_view() { - let rlp = "f87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804".from_hex().unwrap(); + let rlp: Vec = "f87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804".from_hex().unwrap(); let view = view!(TransactionView, &rlp); assert_eq!(view.nonce(), 0.into()); assert_eq!(view.gas_price(), 1.into()); assert_eq!(view.gas(), 0x61a8.into()); assert_eq!(view.value(), 0xa.into()); - assert_eq!(view.data(), "0000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); + assert_eq!(view.data(), "0000000000000000000000000000000000000000000000000000000000".from_hex::>().unwrap()); assert_eq!(view.r(), "48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353".into()); assert_eq!(view.s(), "efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804".into()); assert_eq!(view.v(), 0x1b); diff --git a/ethcore/types/src/views/view_rlp.rs b/ethcore/types/src/views/view_rlp.rs index a6c789de98..e209db35f8 100644 --- a/ethcore/types/src/views/view_rlp.rs +++ b/ethcore/types/src/views/view_rlp.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -128,6 +128,7 @@ impl<'a, 'view> Iterator for ViewRlpIterator<'a, 'view> { } #[macro_export] +/// Create a view into RLP-data macro_rules! view { ($view: ident, $bytes: expr) => { $view::new($crate::views::ViewRlp::new($bytes, file!(), line!())) diff --git a/ethcore/verification/Cargo.toml b/ethcore/verification/Cargo.toml new file mode 100644 index 0000000000..3943413ab0 --- /dev/null +++ b/ethcore/verification/Cargo.toml @@ -0,0 +1,48 @@ +[package] +description = "Block verification utilities." +name = "verification" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" + +[[bench]] +name = "verification" +harness = false +required-features = ['bench'] + +[dependencies] +blockchain = { package = "ethcore-blockchain", path = "../blockchain" } +call-contract = { package = "ethcore-call-contract", path = "../call-contract" } +client-traits = { path = "../client-traits" } +common-types = { path = "../types" } +engine = { path = "../engine" } +ethcore-io = { path = "../../util/io" } +ethereum-types = "0.8.0" +keccak-hash = "0.4.0" +len-caching-lock = { path = "../../util/len-caching-lock" } +log = "0.4" +num_cpus = "1.2" +parity-bytes = "0.1.0" +parity-util-mem = "0.3.0" +parking_lot = "0.9" +rlp = "0.4.2" +time-utils = { path = "../../util/time-utils" } +triehash = { package = "triehash-ethereum", version = "0.2", path = "../../util/triehash-ethereum" } +unexpected = { path = "../../util/unexpected" } + +[dev-dependencies] +criterion = "0.3" +ethcore = { path = "../", features = ["test-helpers"] } +parity-crypto = { version = "0.4.2", features = ["publickey"] } +machine = { path = "../machine" } +null-engine = { path = "../engines/null-engine" } +spec = { path = "../spec" } + +# Benches +ethash = { package = "ethash-engine", path = "../engines/ethash" } +tempdir = "0.3.7" + +[features] +# Used to selectively expose code for benchmarks. +bench = [] diff --git a/ethcore/verification/benches/8447675.rlp b/ethcore/verification/benches/8447675.rlp new file mode 100644 index 0000000000..32e50f143f Binary files /dev/null and b/ethcore/verification/benches/8447675.rlp differ diff --git a/ethcore/verification/benches/8481474-parent-to-uncle.rlp b/ethcore/verification/benches/8481474-parent-to-uncle.rlp new file mode 100644 index 0000000000..d795f0c87d Binary files /dev/null and b/ethcore/verification/benches/8481474-parent-to-uncle.rlp differ diff --git a/ethcore/verification/benches/8481475.rlp b/ethcore/verification/benches/8481475.rlp new file mode 100644 index 0000000000..ac41fd9663 Binary files /dev/null and b/ethcore/verification/benches/8481475.rlp differ diff --git a/ethcore/verification/benches/8481476-one-uncle.rlp b/ethcore/verification/benches/8481476-one-uncle.rlp new file mode 100644 index 0000000000..bd9196a8c8 Binary files /dev/null and b/ethcore/verification/benches/8481476-one-uncle.rlp differ diff --git a/ethcore/verification/benches/verification.rs b/ethcore/verification/benches/verification.rs new file mode 100644 index 0000000000..23bcf4c685 --- /dev/null +++ b/ethcore/verification/benches/verification.rs @@ -0,0 +1,161 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! benchmarking for verification + +use std::collections::BTreeMap; + +use common_types::verification::Unverified; +use criterion::{Criterion, criterion_group, criterion_main}; +use ethash::{EthashParams, Ethash}; +use ethereum_types::U256; +use ethcore::test_helpers::TestBlockChainClient; +use spec::new_constantinople_test_machine; +use tempdir::TempDir; + +use ::verification::{ + FullFamilyParams, + verification, + test_helpers::TestBlockChain, +}; + +// These are current production values. Needed when using real blocks. +fn ethash_params() -> EthashParams { + EthashParams { + minimum_difficulty: U256::from(131072), + difficulty_bound_divisor: U256::from(2048), + difficulty_increment_divisor: 10, + metropolis_difficulty_increment_divisor: 9, + duration_limit: 13, + homestead_transition: 1150000, + difficulty_hardfork_transition: u64::max_value(), + difficulty_hardfork_bound_divisor: U256::from(2048), + bomb_defuse_transition: u64::max_value(), + eip100b_transition: 4370000, + ecip1010_pause_transition: u64::max_value(), + ecip1010_continue_transition: u64::max_value(), + ecip1017_era_rounds: u64::max_value(), + block_reward: { + let mut m = BTreeMap::::new(); + m.insert(0, 5000000000000000000u64.into()); + m.insert(4370000, 3000000000000000000u64.into()); + m.insert(7280000, 2000000000000000000u64.into()); + m + }, + expip2_transition: u64::max_value(), + expip2_duration_limit: 30, + block_reward_contract_transition: 0, + block_reward_contract: None, + difficulty_bomb_delays: { + let mut m = BTreeMap::new(); + m.insert(4370000, 3000000); + m.insert(7280000, 2000000); + m + }, + progpow_transition: u64::max_value() + } +} + +fn build_ethash() -> Ethash { + let machine = new_constantinople_test_machine(); + let ethash_params = ethash_params(); + let cache_dir = TempDir::new("").unwrap(); + Ethash::new( + cache_dir.path(), + ethash_params, + machine, + None + ) +} + +fn block_verification(c: &mut Criterion) { + const PROOF: &str = "bytes from disk are ok"; + + let ethash = build_ethash(); + + // A fairly large block (32kb) with one uncle + let rlp_8481476 = include_bytes!("./8481476-one-uncle.rlp").to_vec(); + // Parent of #8481476 + let rlp_8481475 = include_bytes!("./8481475.rlp").to_vec(); + // Parent of the uncle in #8481476 + let rlp_8481474 = include_bytes!("./8481474-parent-to-uncle.rlp").to_vec(); + + // Phase 1 verification + c.bench_function("verify_block_basic", |b| { + let block = Unverified::from_rlp(rlp_8481476.clone()).expect(PROOF); + b.iter(|| { + assert!(verification::verify_block_basic( + &block, + ðash, + true + ).is_ok()); + }) + }); + + // Phase 2 verification + c.bench_function("verify_block_unordered", |b| { + let block = Unverified::from_rlp(rlp_8481476.clone()).expect(PROOF); + b.iter( || { + assert!(verification::verify_block_unordered( + block.clone(), + ðash, + true + ).is_ok()); + }) + }); + + // Phase 3 verification + let block = Unverified::from_rlp(rlp_8481476.clone()).expect(PROOF); + let preverified = verification::verify_block_unordered(block, ðash, true).expect(PROOF); + let parent = Unverified::from_rlp(rlp_8481475.clone()).expect(PROOF); + + // "partial" means we skip uncle and tx verification + c.bench_function("verify_block_family (partial)", |b| { + b.iter(|| { + if let Err(e) = verification::verify_block_family::( + &preverified.header, + &parent.header, + ðash, + None + ) { + panic!("verify_block_family (partial) ERROR: {:?}", e); + } + }); + }); + + let mut block_provider = TestBlockChain::new(); + block_provider.insert(rlp_8481476.clone()); // block to verify + block_provider.insert(rlp_8481475.clone()); // parent + block_provider.insert(rlp_8481474.clone()); // uncle's parent + + let client = TestBlockChainClient::default(); + c.bench_function("verify_block_family (full)", |b| { + b.iter(|| { + let full = FullFamilyParams { block: &preverified, block_provider: &block_provider, client: &client }; + if let Err(e) = verification::verify_block_family::( + &preverified.header, + &parent.header, + ðash, + Some(full), + ) { + panic!("verify_block_family (full) ERROR: {:?}", e) + } + }); + }); +} + +criterion_group!(benches, block_verification); +criterion_main!(benches); diff --git a/ethcore/src/verification/mod.rs b/ethcore/verification/src/lib.rs similarity index 56% rename from ethcore/src/verification/mod.rs rename to ethcore/verification/src/lib.rs index 5546bd60c9..eda5533265 100644 --- a/ethcore/src/verification/mod.rs +++ b/ethcore/verification/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,39 +16,27 @@ //! Block verification utilities. +// The MallocSizeOf derive looks for this in the root +use parity_util_mem as malloc_size_of; + +#[cfg(feature = "bench" )] +pub mod verification; +#[cfg(not(feature = "bench" ))] mod verification; -mod verifier; pub mod queue; -mod canon_verifier; -mod noop_verifier; - -pub use self::verification::*; -pub use self::verifier::Verifier; -pub use self::canon_verifier::CanonVerifier; -pub use self::noop_verifier::NoopVerifier; -pub use self::queue::{BlockQueue, Config as QueueConfig, VerificationQueue, QueueInfo}; +#[cfg(any(test, feature = "bench" ))] +pub mod test_helpers; -use call_contract::CallContract; -use client::BlockInfo; +pub use self::verification::{FullFamilyParams, verify_block_family, verify_block_final}; +pub use self::queue::{BlockQueue, Config as QueueConfig}; /// Verifier type. #[derive(Debug, PartialEq, Clone)] pub enum VerifierType { /// Verifies block normally. Canon, - /// Verifies block normallly, but skips seal verification. + /// Verifies block normally, but skips seal verification. CanonNoSeal, - /// Does not verify block at all. - /// Used in tests. - Noop, -} - -/// Create a new verifier based on type. -pub fn new(v: VerifierType) -> Box> { - match v { - VerifierType::Canon | VerifierType::CanonNoSeal => Box::new(CanonVerifier), - VerifierType::Noop => Box::new(NoopVerifier), - } } impl VerifierType { @@ -56,7 +44,7 @@ impl VerifierType { pub fn verifying_seal(&self) -> bool { match *self { VerifierType::Canon => true, - VerifierType::Noop | VerifierType::CanonNoSeal => false, + VerifierType::CanonNoSeal => false, } } } diff --git a/ethcore/src/verification/queue/kind.rs b/ethcore/verification/src/queue/kind.rs similarity index 53% rename from ethcore/src/verification/queue/kind.rs rename to ethcore/verification/src/queue/kind.rs index dd9798c092..546df2d5a5 100644 --- a/ethcore/src/verification/queue/kind.rs +++ b/ethcore/verification/src/queue/kind.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,20 +16,24 @@ //! Definition of valid items for the verification queue. -use engines::EthEngine; -use error::Error; +use engine::Engine; -use heapsize::HeapSizeOf; +use parity_util_mem::MallocSizeOf; use ethereum_types::{H256, U256}; +use common_types::errors::EthcoreError as Error; + pub use self::blocks::Blocks; pub use self::headers::Headers; /// Something which can produce a hash and a parent hash. pub trait BlockLike { - /// Get the hash of this item. + /// Get the hash of this item - i.e. the header hash. fn hash(&self) -> H256; + /// Get a raw hash of this item - i.e. the hash of the RLP representation. + fn raw_hash(&self) -> H256; + /// Get the hash of this item's parent. fn parent_hash(&self) -> H256; @@ -49,34 +53,42 @@ pub trait BlockLike { /// consistent. pub trait Kind: 'static + Sized + Send + Sync { /// The first stage: completely unverified. - type Input: Sized + Send + BlockLike + HeapSizeOf; + type Input: Sized + Send + BlockLike + MallocSizeOf; /// The second stage: partially verified. - type Unverified: Sized + Send + BlockLike + HeapSizeOf; + type Unverified: Sized + Send + BlockLike + MallocSizeOf; /// The third stage: completely verified. - type Verified: Sized + Send + BlockLike + HeapSizeOf; + type Verified: Sized + Send + BlockLike + MallocSizeOf; /// Attempt to create the `Unverified` item from the input. - fn create(input: Self::Input, engine: &EthEngine, check_seal: bool) -> Result; + /// + /// The return type is quite complex because in some scenarios the input + /// is needed (typically for BlockError) to get the raw block bytes without cloning them + fn create( + input: Self::Input, + engine: &dyn Engine, + check_seal: bool + ) -> Result)>; /// Attempt to verify the `Unverified` item using the given engine. - fn verify(unverified: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result; + fn verify(unverified: Self::Unverified, engine: &dyn Engine, check_seal: bool) -> Result; } /// The blocks verification module. pub mod blocks { use super::{Kind, BlockLike}; - use engines::EthEngine; - use error::{Error, ErrorKind, BlockError}; - use types::header::Header; - use verification::{PreverifiedBlock, verify_block_basic, verify_block_unordered}; - use types::transaction::UnverifiedTransaction; + use engine::Engine; + use common_types::{ + block::PreverifiedBlock, + errors::{EthcoreError as Error, BlockError}, + verification::Unverified, + }; + use log::{debug, warn}; + use crate::verification::{verify_block_basic, verify_block_unordered}; - use heapsize::HeapSizeOf; use ethereum_types::{H256, U256}; - use bytes::Bytes; /// A mode for verifying blocks. pub struct Blocks; @@ -86,21 +98,25 @@ pub mod blocks { type Unverified = Unverified; type Verified = PreverifiedBlock; - fn create(input: Self::Input, engine: &EthEngine, check_seal: bool) -> Result { + fn create( + input: Self::Input, + engine: &dyn Engine, + check_seal: bool + ) -> Result)> { match verify_block_basic(&input, engine, check_seal) { Ok(()) => Ok(input), - Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(oob)), _)) => { + Err(Error::Block(BlockError::TemporarilyInvalid(oob))) => { debug!(target: "client", "Block received too early {}: {:?}", input.hash(), oob); - Err((input, BlockError::TemporarilyInvalid(oob).into())) + Err((BlockError::TemporarilyInvalid(oob).into(), Some(input))) }, Err(e) => { warn!(target: "client", "Stage 1 block verification failed for {}: {:?}", input.hash(), e); - Err((input, e)) + Err((e, Some(input))) } } } - fn verify(un: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result { + fn verify(un: Self::Unverified, engine: &dyn Engine, check_seal: bool) -> Result { let hash = un.hash(); match verify_block_unordered(un, engine, check_seal) { Ok(verified) => Ok(verified), @@ -112,60 +128,21 @@ pub mod blocks { } } - /// An unverified block. - #[derive(PartialEq, Debug)] - pub struct Unverified { - /// Unverified block header. - pub header: Header, - /// Unverified block transactions. - pub transactions: Vec, - /// Unverified block uncles. - pub uncles: Vec
, - /// Raw block bytes. - pub bytes: Bytes, - } - - impl Unverified { - /// Create an `Unverified` from raw bytes. - pub fn from_rlp(bytes: Bytes) -> Result { - use rlp::Rlp; - let (header, transactions, uncles) = { - let rlp = Rlp::new(&bytes); - let header = rlp.val_at(0)?; - let transactions = rlp.list_at(1)?; - let uncles = rlp.list_at(2)?; - (header, transactions, uncles) - }; - - Ok(Unverified { - header, - transactions, - uncles, - bytes, - }) - } - } - - impl HeapSizeOf for Unverified { - fn heap_size_of_children(&self) -> usize { - self.header.heap_size_of_children() - + self.transactions.heap_size_of_children() - + self.uncles.heap_size_of_children() - + self.bytes.heap_size_of_children() - } - } - impl BlockLike for Unverified { fn hash(&self) -> H256 { self.header.hash() } + fn raw_hash(&self) -> H256 { + keccak_hash::keccak(&self.bytes) + } + fn parent_hash(&self) -> H256 { - self.header.parent_hash().clone() + *self.header.parent_hash() } fn difficulty(&self) -> U256 { - self.header.difficulty().clone() + *self.header.difficulty() } } @@ -174,12 +151,16 @@ pub mod blocks { self.header.hash() } + fn raw_hash(&self) -> H256 { + keccak_hash::keccak(&self.bytes) + } + fn parent_hash(&self) -> H256 { - self.header.parent_hash().clone() + *self.header.parent_hash() } fn difficulty(&self) -> U256 { - self.header.difficulty().clone() + *self.header.difficulty() } } } @@ -188,17 +169,20 @@ pub mod blocks { pub mod headers { use super::{Kind, BlockLike}; - use engines::EthEngine; - use error::Error; - use types::header::Header; - use verification::verify_header_params; + use engine::Engine; + use common_types::{ + header::Header, + errors::EthcoreError as Error, + }; + use crate::verification::{verify_header_params, verify_header_time}; use ethereum_types::{H256, U256}; impl BlockLike for Header { fn hash(&self) -> H256 { self.hash() } - fn parent_hash(&self) -> H256 { self.parent_hash().clone() } - fn difficulty(&self) -> U256 { self.difficulty().clone() } + fn raw_hash(&self) -> H256 { self.hash() } + fn parent_hash(&self) -> H256 { *self.parent_hash() } + fn difficulty(&self) -> U256 { *self.difficulty() } } /// A mode for verifying headers. @@ -209,16 +193,23 @@ pub mod headers { type Unverified = Header; type Verified = Header; - fn create(input: Self::Input, engine: &EthEngine, check_seal: bool) -> Result { - match verify_header_params(&input, engine, true, check_seal) { + fn create( + input: Self::Input, + engine: &dyn Engine, + check_seal: bool + ) -> Result)> { + let res = verify_header_params(&input, engine, check_seal) + .and_then(|_| verify_header_time(&input)); + + match res { Ok(_) => Ok(input), - Err(err) => Err((input, err)) + Err(e) => Err((e, Some(input))), } } - fn verify(unverified: Self::Unverified, engine: &EthEngine, check_seal: bool) -> Result { + fn verify(unverified: Self::Unverified, engine: &dyn Engine, check_seal: bool) -> Result { match check_seal { - true => engine.verify_block_unordered(&unverified,).map(|_| unverified), + true => engine.verify_block_unordered(&unverified).map(|_| unverified), false => Ok(unverified), } } diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/verification/src/queue/mod.rs similarity index 84% rename from ethcore/src/verification/queue/mod.rs rename to ethcore/verification/src/queue/mod.rs index 423121eefa..31d205c798 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/verification/src/queue/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,29 +22,32 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrdering}; use std::sync::Arc; use std::cmp; use std::collections::{VecDeque, HashSet, HashMap}; -use heapsize::HeapSizeOf; +use common_types::{ + block_status::BlockStatus, + io_message::ClientIoMessage, + errors::{BlockError, EthcoreError as Error, ImportError}, + verification::VerificationQueueInfo as QueueInfo, +}; +use ethcore_io::*; use ethereum_types::{H256, U256}; -use parking_lot::{Condvar, Mutex, RwLock}; -use io::*; -use error::{BlockError, ImportErrorKind, ErrorKind, Error}; -use engines::EthEngine; -use client::ClientIoMessage; +use engine::Engine; use len_caching_lock::LenCachingMutex; +use log::{debug, trace}; +use parity_util_mem::{MallocSizeOf, MallocSizeOfExt}; +use parking_lot::{Condvar, Mutex, RwLock}; use self::kind::{BlockLike, Kind}; -pub use types::verification_queue_info::VerificationQueueInfo as QueueInfo; - pub mod kind; const MIN_MEM_LIMIT: usize = 16384; const MIN_QUEUE_LIMIT: usize = 512; /// Type alias for block queue convenience. -pub type BlockQueue = VerificationQueue; +pub type BlockQueue = VerificationQueue; /// Type alias for header queue convenience. -pub type HeaderQueue = VerificationQueue; +pub type HeaderQueue = VerificationQueue; /// Verification queue configuration #[derive(Debug, PartialEq, Clone)] @@ -83,7 +86,7 @@ impl Default for VerifierSettings { fn default() -> Self { VerifierSettings { scale_verifiers: false, - num_verifiers: ::num_cpus::get(), + num_verifiers: num_cpus::get(), } } } @@ -96,17 +99,12 @@ enum State { } /// An item which is in the process of being verified. +#[derive(MallocSizeOf)] pub struct Verifying { hash: H256, output: Option, } -impl HeapSizeOf for Verifying { - fn heap_size_of_children(&self) -> usize { - self.output.heap_size_of_children() - } -} - /// Status of items in the queue. pub enum Status { /// Currently queued. @@ -117,9 +115,8 @@ pub enum Status { Unknown, } -impl Into<::types::block_status::BlockStatus> for Status { - fn into(self) -> ::types::block_status::BlockStatus { - use ::types::block_status::BlockStatus; +impl Into for Status { + fn into(self) -> BlockStatus { match self { Status::Queued => BlockStatus::Queued, Status::Bad => BlockStatus::Bad, @@ -137,12 +134,12 @@ struct Sizes { /// A queue of items to be verified. Sits between network or other I/O and the `BlockChain`. /// Keeps them in the same order as inserted, minus invalid items. -pub struct VerificationQueue { - engine: Arc, +pub struct VerificationQueue { + engine: Arc, more_to_verify: Arc, verification: Arc>, deleting: Arc, - ready_signal: Arc, + ready_signal: Arc>, empty: Arc, processing: RwLock>, // hash to difficulty ticks_since_adjustment: AtomicUsize, @@ -154,13 +151,13 @@ pub struct VerificationQueue { total_difficulty: RwLock, } -struct QueueSignal { +struct QueueSignal { deleting: Arc, signalled: AtomicBool, - message_channel: Mutex>, + message_channel: Mutex>>, } -impl QueueSignal { +impl QueueSignal { fn set_sync(&self) { // Do not signal when we are about to close if self.deleting.load(AtomicOrdering::Relaxed) { @@ -204,9 +201,9 @@ struct Verification { check_seal: bool, } -impl VerificationQueue { +impl VerificationQueue { /// Creates a new queue instance. - pub fn new(config: Config, engine: Arc, message_channel: IoChannel, check_seal: bool) -> Self { + pub fn new(config: Config, engine: Arc, message_channel: IoChannel>, check_seal: bool) -> Self { let verification = Arc::new(Verification { unverified: LenCachingMutex::new(VecDeque::new()), verifying: LenCachingMutex::new(VecDeque::new()), @@ -217,7 +214,7 @@ impl VerificationQueue { verifying: AtomicUsize::new(0), verified: AtomicUsize::new(0), }, - check_seal: check_seal, + check_seal, }); let more_to_verify = Arc::new(Condvar::new()); let deleting = Arc::new(AtomicBool::new(false)); @@ -274,28 +271,28 @@ impl VerificationQueue { } VerificationQueue { - engine: engine, - ready_signal: ready_signal, - more_to_verify: more_to_verify, - verification: verification, - deleting: deleting, + engine, + ready_signal, + more_to_verify, + verification, + deleting, processing: RwLock::new(HashMap::new()), - empty: empty, + empty, ticks_since_adjustment: AtomicUsize::new(0), max_queue_size: cmp::max(config.max_queue_size, MIN_QUEUE_LIMIT), max_mem_use: cmp::max(config.max_mem_use, MIN_MEM_LIMIT), - scale_verifiers: scale_verifiers, - verifier_handles: verifier_handles, - state: state, + scale_verifiers, + verifier_handles, + state, total_difficulty: RwLock::new(0.into()), } } fn verify( verification: Arc>, - engine: Arc, + engine: Arc, wait: Arc, - ready: Arc, + ready: Arc>, empty: Arc, state: Arc<(Mutex, Condvar)>, id: usize, @@ -353,7 +350,7 @@ impl VerificationQueue { None => continue, }; - verification.sizes.unverified.fetch_sub(item.heap_size_of_children(), AtomicOrdering::SeqCst); + verification.sizes.unverified.fetch_sub(item.malloc_size_of(), AtomicOrdering::SeqCst); verifying.push_back(Verifying { hash: item.hash(), output: None }); item }; @@ -367,7 +364,7 @@ impl VerificationQueue { if e.hash == hash { idx = Some(i); - verification.sizes.verifying.fetch_add(verified.heap_size_of_children(), AtomicOrdering::SeqCst); + verification.sizes.verifying.fetch_add(verified.malloc_size_of(), AtomicOrdering::SeqCst); e.output = Some(verified); break; } @@ -377,7 +374,7 @@ impl VerificationQueue { // we're next! let mut verified = verification.verified.lock(); let mut bad = verification.bad.lock(); - VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes); + VerificationQueue::<_, C>::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes); true } else { false @@ -392,7 +389,7 @@ impl VerificationQueue { verifying.retain(|e| e.hash != hash); if verifying.front().map_or(false, |x| x.output.is_some()) { - VerificationQueue::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes); + VerificationQueue::<_, C>::drain_verifying(&mut verifying, &mut verified, &mut bad, &verification.sizes); true } else { false @@ -417,7 +414,7 @@ impl VerificationQueue { while let Some(output) = verifying.front_mut().and_then(|x| x.output.take()) { assert!(verifying.pop_front().is_some()); - let size = output.heap_size_of_children(); + let size = output.malloc_size_of(); removed_size += size; if bad.contains(&output.parent_hash()) { @@ -470,29 +467,33 @@ impl VerificationQueue { } /// Add a block to the queue. - pub fn import(&self, input: K::Input) -> Result { + // + // TODO: #11403 - rework `EthcoreError::Block` to include raw bytes of the error cause + pub fn import(&self, input: K::Input) -> Result)> { let hash = input.hash(); + let raw_hash = input.raw_hash(); { if self.processing.read().contains_key(&hash) { - bail!((input, ErrorKind::Import(ImportErrorKind::AlreadyQueued).into())); + return Err((Error::Import(ImportError::AlreadyQueued), Some(input))); } let mut bad = self.verification.bad.lock(); - if bad.contains(&hash) { - bail!((input, ErrorKind::Import(ImportErrorKind::KnownBad).into())); + if bad.contains(&hash) || bad.contains(&raw_hash) { + return Err((Error::Import(ImportError::KnownBad), Some(input))); } if bad.contains(&input.parent_hash()) { bad.insert(hash); - bail!((input, ErrorKind::Import(ImportErrorKind::KnownBad).into())); + return Err((Error::Import(ImportError::KnownBad), Some(input))); } } match K::create(input, &*self.engine, self.verification.check_seal) { Ok(item) => { - self.verification.sizes.unverified.fetch_add(item.heap_size_of_children(), AtomicOrdering::SeqCst); - - self.processing.write().insert(hash, item.difficulty()); + if self.processing.write().insert(hash, item.difficulty()).is_some() { + return Err((Error::Import(ImportError::AlreadyQueued), None)); + } + self.verification.sizes.unverified.fetch_add(item.malloc_size_of(), AtomicOrdering::SeqCst); { let mut td = self.total_difficulty.write(); *td = *td + item.difficulty(); @@ -501,15 +502,25 @@ impl VerificationQueue { self.more_to_verify.notify_all(); Ok(hash) }, - Err((input, err)) => { + Err((err, input)) => { match err { // Don't mark future blocks as bad. - Error(ErrorKind::Block(BlockError::TemporarilyInvalid(_)), _) => {}, + Error::Block(BlockError::TemporarilyInvalid(_)) => {}, + // If the transaction root or uncles hash is invalid, it doesn't necessarily mean + // that the header is invalid. We might have just received a malformed block body, + // so we shouldn't put the header hash to `bad`. + // + // We still put the entire `Item` hash to bad, so that we can early reject + // the items that are malformed. + Error::Block(BlockError::InvalidTransactionsRoot(_)) | + Error::Block(BlockError::InvalidUnclesHash(_)) => { + self.verification.bad.lock().insert(raw_hash); + }, _ => { self.verification.bad.lock().insert(hash); } } - Err((input, err)) + Err((err, input)) } } } @@ -537,7 +548,7 @@ impl VerificationQueue { let mut removed_size = 0; for output in verified.drain(..) { if bad.contains(&output.parent_hash()) { - removed_size += output.heap_size_of_children(); + removed_size += output.malloc_size_of(); bad.insert(output.hash()); if let Some(difficulty) = processing.remove(&output.hash()) { let mut td = self.total_difficulty.write(); @@ -574,7 +585,7 @@ impl VerificationQueue { let count = cmp::min(max, verified.len()); let result = verified.drain(..count).collect::>(); - let drained_size = result.iter().map(HeapSizeOf::heap_size_of_children).fold(0, |a, c| a + c); + let drained_size = result.iter().map(MallocSizeOfExt::malloc_size_of).sum(); self.verification.sizes.verified.fetch_sub(drained_size, AtomicOrdering::SeqCst); self.ready_signal.reset(); @@ -628,7 +639,7 @@ impl VerificationQueue { /// Get the total difficulty of all the blocks in the queue. pub fn total_difficulty(&self) -> U256 { - self.total_difficulty.read().clone() + *self.total_difficulty.read() } /// Get the current number of working verifiers. @@ -710,7 +721,7 @@ impl VerificationQueue { } } -impl Drop for VerificationQueue { +impl Drop for VerificationQueue { fn drop(&mut self) { trace!(target: "shutdown", "[VerificationQueue] Closing..."); self.clear(); @@ -738,20 +749,23 @@ impl Drop for VerificationQueue { #[cfg(test)] mod tests { - use io::*; - use spec::Spec; + use ethcore_io::*; use super::{BlockQueue, Config, State}; - use super::kind::blocks::Unverified; - use test_helpers::{get_good_dummy_block_seq, get_good_dummy_block}; - use error::*; - use bytes::Bytes; - use types::view; - use types::views::BlockView; + use ethcore::test_helpers::{get_good_dummy_block_seq, get_good_dummy_block}; + use ethcore::client::Client; + use parity_bytes::Bytes; + use common_types::{ + errors::{EthcoreError, ImportError}, + verification::Unverified, + view, + views::BlockView, + }; + use spec; // create a test block queue. // auto_scaling enables verifier adjustment. - fn get_test_queue(auto_scale: bool) -> BlockQueue { - let spec = Spec::new_test(); + fn get_test_queue(auto_scale: bool) -> BlockQueue { + let spec = spec::new_test(); let engine = spec.engine; let mut config = Config::default(); @@ -773,9 +787,9 @@ mod tests { #[test] fn can_be_created() { // TODO better test - let spec = Spec::new_test(); + let spec = spec::new_test(); let engine = spec.engine; - let _ = BlockQueue::new(Config::default(), engine, IoChannel::disconnected(), true); + let _ = BlockQueue::::new(Config::default(), engine, IoChannel::disconnected(), true); } #[test] @@ -795,9 +809,9 @@ mod tests { let duplicate_import = queue.import(new_unverified(get_good_dummy_block())); match duplicate_import { - Err((_, e)) => { + Err(e) => { match e { - Error(ErrorKind::Import(ImportErrorKind::AlreadyQueued), _) => {}, + (EthcoreError::Import(ImportError::AlreadyQueued), _) => {}, _ => { panic!("must return AlreadyQueued error"); } } } @@ -851,11 +865,11 @@ mod tests { #[test] fn test_mem_limit() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let engine = spec.engine; let mut config = Config::default(); config.max_mem_use = super::MIN_MEM_LIMIT; // empty queue uses about 15000 - let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + let queue = BlockQueue::::new(config, engine, IoChannel::disconnected(), true); assert!(!queue.queue_info().is_full()); let mut blocks = get_good_dummy_block_seq(50); for b in blocks.drain(..) { @@ -902,30 +916,30 @@ mod tests { #[test] fn worker_threads_honor_specified_number_without_scaling() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let engine = spec.engine; let config = get_test_config(1, false); - let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + let queue = BlockQueue::::new(config, engine, IoChannel::disconnected(), true); assert_eq!(queue.num_verifiers(), 1); } #[test] fn worker_threads_specified_to_zero_should_set_to_one() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let engine = spec.engine; let config = get_test_config(0, false); - let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + let queue = BlockQueue::::new(config, engine, IoChannel::disconnected(), true); assert_eq!(queue.num_verifiers(), 1); } #[test] fn worker_threads_should_only_accept_max_number_cpus() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let engine = spec.engine; let config = get_test_config(10_000, false); - let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + let queue = BlockQueue::::new(config, engine, IoChannel::disconnected(), true); let num_cpus = ::num_cpus::get(); assert_eq!(queue.num_verifiers(), num_cpus); @@ -936,10 +950,10 @@ mod tests { let num_cpus = ::num_cpus::get(); // only run the test with at least 2 CPUs if num_cpus > 1 { - let spec = Spec::new_test(); + let spec = spec::new_test(); let engine = spec.engine; let config = get_test_config(num_cpus - 1, true); - let queue = BlockQueue::new(config, engine, IoChannel::disconnected(), true); + let queue = BlockQueue::::new(config, engine, IoChannel::disconnected(), true); queue.scale_verifiers(num_cpus); assert_eq!(queue.num_verifiers(), num_cpus); diff --git a/ethcore/verification/src/test_helpers.rs b/ethcore/verification/src/test_helpers.rs new file mode 100644 index 0000000000..e514d63c4a --- /dev/null +++ b/ethcore/verification/src/test_helpers.rs @@ -0,0 +1,114 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Verification test helpers. + +use std::collections::HashMap; + +use blockchain::{BlockProvider, BlockChain, BlockDetails, TransactionAddress, BlockReceipts}; +use common_types::{ + BlockNumber, + encoded, + verification::Unverified, + log_entry::{LogEntry, LocalizedLogEntry}, +}; +use ethereum_types::{BloomRef, H256}; +use parity_bytes::Bytes; + +#[derive(Default)] +pub struct TestBlockChain { + blocks: HashMap, + numbers: HashMap, +} + +impl TestBlockChain { + pub fn new() -> Self { TestBlockChain::default() } + + pub fn insert(&mut self, bytes: Bytes) { + let header = Unverified::from_rlp(bytes.clone()).unwrap().header; + let hash = header.hash(); + self.blocks.insert(hash, bytes); + self.numbers.insert(header.number(), hash); + } +} + +impl BlockProvider for TestBlockChain { + fn is_known(&self, hash: &H256) -> bool { + self.blocks.contains_key(hash) + } + + fn first_block(&self) -> Option { + unimplemented!() + } + + fn best_ancient_block(&self) -> Option { + None + } + + /// Get raw block data + fn block(&self, hash: &H256) -> Option { + self.blocks.get(hash).cloned().map(encoded::Block::new) + } + + /// Get the familial details concerning a block. + fn block_details(&self, hash: &H256) -> Option { + self.blocks.get(hash).map(|bytes| { + let header = Unverified::from_rlp(bytes.to_vec()).unwrap().header; + BlockDetails { + number: header.number(), + total_difficulty: *header.difficulty(), + parent: *header.parent_hash(), + children: Vec::new(), + is_finalized: false, + } + }) + } + + /// Get the hash of given block's number. + fn block_hash(&self, index: BlockNumber) -> Option { + self.numbers.get(&index).cloned() + } + + fn transaction_address(&self, _hash: &H256) -> Option { + unimplemented!() + } + + fn block_receipts(&self, _hash: &H256) -> Option { + unimplemented!() + } + + fn block_header_data(&self, hash: &H256) -> Option { + self.block(hash) + .map(|b| b.header_view().rlp().as_raw().to_vec()) + .map(encoded::Header::new) + } + + fn block_body(&self, hash: &H256) -> Option { + self.block(hash) + .map(|b| BlockChain::block_to_body(&b.into_inner())) + .map(encoded::Body::new) + } + + fn blocks_with_bloom<'a, B, I, II>(&self, _blooms: II, _from_block: BlockNumber, _to_block: BlockNumber) -> Vec + where BloomRef<'a>: From, II: IntoIterator + Copy, I: Iterator, Self: Sized { + unimplemented!() + } + + fn logs(&self, _blocks: Vec, _matches: F, _limit: Option) -> Vec + where F: Fn(&LogEntry) -> bool, Self: Sized { + unimplemented!() + } +} diff --git a/ethcore/src/verification/verification.rs b/ethcore/verification/src/verification.rs similarity index 67% rename from ethcore/src/verification/verification.rs rename to ethcore/verification/src/verification.rs index 61d7119249..8685e4e1e9 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/verification/src/verification.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,47 +24,30 @@ use std::collections::HashSet; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use bytes::Bytes; -use hash::keccak; -use heapsize::HeapSizeOf; +use keccak_hash::keccak; use rlp::Rlp; use triehash::ordered_trie_root; use unexpected::{Mismatch, OutOfBounds}; -use blockchain::*; +use blockchain::BlockProvider; use call_contract::CallContract; -use client::BlockInfo; -use engines::{EthEngine, MAX_UNCLE_AGE}; -use error::{BlockError, Error}; -use types::{BlockNumber, header::Header}; -use types::transaction::SignedTransaction; -use verification::queue::kind::blocks::Unverified; +use client_traits::BlockInfo; +use engine::Engine; +use common_types::{ + BlockNumber, + header::Header, + errors::{EthcoreError as Error, BlockError}, + engines::MAX_UNCLE_AGE, + block::PreverifiedBlock, + verification::Unverified, +}; use time_utils::CheckedSystemTime; -/// Preprocessed block data gathered in `verify_block_unordered` call -pub struct PreverifiedBlock { - /// Populated block header - pub header: Header, - /// Populated block transactions - pub transactions: Vec, - /// Populated block uncles - pub uncles: Vec
, - /// Block bytes - pub bytes: Bytes, -} - -impl HeapSizeOf for PreverifiedBlock { - fn heap_size_of_children(&self) -> usize { - self.header.heap_size_of_children() - + self.transactions.heap_size_of_children() - + self.bytes.heap_size_of_children() - } -} - /// Phase 1 quick block verification. Only does checks that are cheap. Operates on a single block -pub fn verify_block_basic(block: &Unverified, engine: &EthEngine, check_seal: bool) -> Result<(), Error> { - verify_header_params(&block.header, engine, true, check_seal)?; +pub fn verify_block_basic(block: &Unverified, engine: &dyn Engine, check_seal: bool) -> Result<(), Error> { + verify_header_params(&block.header, engine, check_seal)?; + verify_header_time(&block.header)?; verify_block_integrity(block)?; if check_seal { @@ -72,12 +55,20 @@ pub fn verify_block_basic(block: &Unverified, engine: &EthEngine, check_seal: bo } for uncle in &block.uncles { - verify_header_params(uncle, engine, false, check_seal)?; + verify_header_params(uncle, engine, check_seal)?; if check_seal { engine.verify_block_basic(uncle)?; } } + if let Some(gas_limit) = engine.gas_limit_override(&block.header) { + if *block.header.gas_limit() != gas_limit { + return Err(From::from(BlockError::InvalidGasLimit( + OutOfBounds { min: Some(gas_limit), max: Some(gas_limit), found: *block.header.gas_limit() } + ))); + } + } + for t in &block.transactions { engine.verify_transaction_basic(t, &block.header)?; } @@ -88,7 +79,7 @@ pub fn verify_block_basic(block: &Unverified, engine: &EthEngine, check_seal: bo /// Phase 2 verification. Perform costly checks such as transaction signatures and block nonce for ethash. /// Still operates on a individual block /// Returns a `PreverifiedBlock` structure populated with transactions -pub fn verify_block_unordered(block: Unverified, engine: &EthEngine, check_seal: bool) -> Result { +pub fn verify_block_unordered(block: Unverified, engine: &dyn Engine, check_seal: bool) -> Result { let header = block.header; if check_seal { engine.verify_block_unordered(&header)?; @@ -106,7 +97,7 @@ pub fn verify_block_unordered(block: Unverified, engine: &EthEngine, check_seal: let transactions = block.transactions .into_iter() .map(|t| { - let t = engine.verify_transaction_unordered(t, &header)?; + let t = t.verify_unordered()?; if let Some(max_nonce) = nonce_cap { if t.nonce >= max_nonce { return Err(BlockError::TooManyTransactions(t.sender()).into()); @@ -130,23 +121,22 @@ pub struct FullFamilyParams<'a, C: BlockInfo + CallContract + 'a> { pub block: &'a PreverifiedBlock, /// Block provider to use during verification - pub block_provider: &'a BlockProvider, + pub block_provider: &'a dyn BlockProvider, /// Engine client to use during verification pub client: &'a C, } /// 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> { +pub fn verify_block_family( + header: &Header, + parent: &Header, + engine: &dyn Engine, + params: FullFamilyParams +) -> Result<(), Error> { // TODO: verify timestamp verify_parent(&header, &parent, engine)?; engine.verify_block_family(&header, &parent)?; - - let params = match do_full { - Some(x) => x, - None => return Ok(()), - }; - verify_uncles(params.block, params.block_provider, engine)?; for tx in ¶ms.block.transactions { @@ -158,7 +148,7 @@ pub fn verify_block_family(header: &Header, parent: Ok(()) } -fn verify_uncles(block: &PreverifiedBlock, bc: &BlockProvider, engine: &EthEngine) -> Result<(), Error> { +fn verify_uncles(block: &PreverifiedBlock, bc: &dyn BlockProvider, engine: &dyn Engine) -> Result<(), Error> { let header = &block.header; let num_uncles = block.uncles.len(); let max_uncles = engine.maximum_uncle_count(header.number()); @@ -179,8 +169,7 @@ fn verify_uncles(block: &PreverifiedBlock, bc: &BlockProvider, engine: &EthEngin match bc.block_details(&hash) { Some(details) => { excluded.insert(details.parent); - let b = bc.block(&hash) - .expect("parent already known to be stored; qed"); + let b = bc.block(&hash).expect("parent already known to be stored; qed"); excluded.extend(b.uncle_hashes()); hash = details.parent; } @@ -198,22 +187,22 @@ fn verify_uncles(block: &PreverifiedBlock, bc: &BlockProvider, engine: &EthEngin return Err(From::from(BlockError::DuplicateUncle(uncle.hash()))) } - // m_currentBlock.number() - uncle.number() m_cB.n - uP.n() - // 1 2 - // 2 - // 3 - // 4 - // 5 - // 6 7 - // (8 Invalid) - - let depth = if header.number() > uncle.number() { header.number() - uncle.number() } else { 0 }; - if depth > MAX_UNCLE_AGE as u64 { - return Err(From::from(BlockError::UncleTooOld(OutOfBounds { min: Some(header.number() - depth), max: Some(header.number() - 1), found: uncle.number() }))); - } - else if depth < 1 { - return Err(From::from(BlockError::UncleIsBrother(OutOfBounds { min: Some(header.number() - depth), max: Some(header.number() - 1), found: uncle.number() }))); - } + // uncle.number() needs to be within specific number range which is + // [header.number() - MAX_UNCLE_AGE, header.number() - 1] + // + // depth is the difference between uncle.number() and header.number() + // and the previous condition implies that it is always in range + // [1, MAX_UNCLE_AGE] + let depth = if header.number() > uncle.number() && + uncle.number() + MAX_UNCLE_AGE >= header.number() { + header.number() - uncle.number() + } else { + return Err(BlockError::UncleOutOfBounds(OutOfBounds { + min: Some(header.number() - MAX_UNCLE_AGE), + max: Some(header.number() - 1), + found: uncle.number() + }).into()); + }; // cB // cB.p^1 1 depth, valid uncle @@ -225,7 +214,8 @@ fn verify_uncles(block: &PreverifiedBlock, bc: &BlockProvider, engine: &EthEngin // cB.p^7 -------------/ // cB.p^8 let mut expected_uncle_parent = header.parent_hash().clone(); - let uncle_parent = bc.block_header_data(&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(|| BlockError::UnknownUncleParent(*uncle.parent_hash()))?; for _ in 0..depth { match bc.block_details(&expected_uncle_parent) { Some(details) => { @@ -251,22 +241,34 @@ fn verify_uncles(block: &PreverifiedBlock, bc: &BlockProvider, engine: &EthEngin /// Phase 4 verification. Check block information against transaction enactment results, pub fn verify_block_final(expected: &Header, got: &Header) -> Result<(), Error> { if expected.state_root() != got.state_root() { - return Err(From::from(BlockError::InvalidStateRoot(Mismatch { expected: *expected.state_root(), found: *got.state_root() }))) + return Err(From::from(BlockError::InvalidStateRoot(Mismatch { + expected: *expected.state_root(), + found: *got.state_root() + }))) } if expected.gas_used() != got.gas_used() { - return Err(From::from(BlockError::InvalidGasUsed(Mismatch { expected: *expected.gas_used(), found: *got.gas_used() }))) + return Err(From::from(BlockError::InvalidGasUsed(Mismatch { + expected: *expected.gas_used(), + found: *got.gas_used() + }))) } if expected.log_bloom() != got.log_bloom() { - return Err(From::from(BlockError::InvalidLogBloom(Box::new(Mismatch { expected: *expected.log_bloom(), found: *got.log_bloom() })))) + return Err(From::from(BlockError::InvalidLogBloom(Box::new(Mismatch { + expected: *expected.log_bloom(), + found: *got.log_bloom() + })))) } if expected.receipts_root() != got.receipts_root() { - return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch { expected: *expected.receipts_root(), found: *got.receipts_root() }))) + return Err(From::from(BlockError::InvalidReceiptsRoot(Mismatch { + expected: *expected.receipts_root(), + found: *got.receipts_root() + }))) } Ok(()) } /// Check basic header parameters. -pub fn verify_header_params(header: &Header, engine: &EthEngine, is_full: bool, check_seal: bool) -> Result<(), Error> { +pub(crate) fn verify_header_params(header: &Header, engine: &dyn Engine, check_seal: bool) -> Result<(), Error> { if check_seal { let expected_seal_fields = engine.seal_fields(header); if header.seal().len() != expected_seal_fields { @@ -277,81 +279,129 @@ pub fn verify_header_params(header: &Header, engine: &EthEngine, is_full: bool, } if header.number() >= From::from(BlockNumber::max_value()) { - return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { max: Some(From::from(BlockNumber::max_value())), min: None, found: header.number() }))) + return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { + max: Some(From::from(BlockNumber::max_value())), + min: None, + found: header.number() + }))) } if header.gas_used() > header.gas_limit() { - return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { max: Some(*header.gas_limit()), min: None, found: *header.gas_used() }))); - } - let min_gas_limit = engine.params().min_gas_limit; - if header.gas_limit() < &min_gas_limit { - return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas_limit), max: None, found: *header.gas_limit() }))); - } - if let Some(limit) = engine.maximum_gas_limit() { - if header.gas_limit() > &limit { - return Err(From::from(::error::BlockError::InvalidGasLimit(OutOfBounds { min: None, max: Some(limit), found: *header.gas_limit() }))); + return Err(From::from(BlockError::TooMuchGasUsed(OutOfBounds { + max: Some(*header.gas_limit()), + min: None, + found: *header.gas_used() + }))); + } + if engine.gas_limit_override(header).is_none() { + let min_gas_limit = engine.min_gas_limit(); + if header.gas_limit() < &min_gas_limit { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { + min: Some(min_gas_limit), + max: None, + found: *header.gas_limit() + }))); + } + if let Some(limit) = engine.maximum_gas_limit() { + if header.gas_limit() > &limit { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { + min: None, + max: Some(limit), + found: *header.gas_limit() + }))); + } } } let maximum_extra_data_size = engine.maximum_extra_data_size(); if header.number() != 0 && header.extra_data().len() > maximum_extra_data_size { - return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: Some(maximum_extra_data_size), found: header.extra_data().len() }))); + return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { + min: None, + max: Some(maximum_extra_data_size), + found: header.extra_data().len() + }))); } if let Some(ref ext) = engine.machine().ethash_extensions() { if header.number() >= ext.dao_hardfork_transition && header.number() <= ext.dao_hardfork_transition + 9 && header.extra_data()[..] != b"dao-hard-fork"[..] { - return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: None, max: None, found: 0 }))); + return Err(From::from(BlockError::ExtraDataOutOfBounds(OutOfBounds { + min: None, + max: None, + found: 0 + }))); } } - if is_full { - const ACCEPTABLE_DRIFT: Duration = Duration::from_secs(15); - // this will resist overflow until `year 2037` - let max_time = SystemTime::now() + ACCEPTABLE_DRIFT; - let invalid_threshold = max_time + ACCEPTABLE_DRIFT * 9; - let timestamp = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp())) - .ok_or(BlockError::TimestampOverflow)?; + Ok(()) +} - if timestamp > invalid_threshold { - return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: Some(max_time), min: None, found: timestamp }))) - } +/// A header verification step that should be done for new block headers, but not for uncles. +pub(crate) fn verify_header_time(header: &Header) -> Result<(), Error> { + const ACCEPTABLE_DRIFT: Duration = Duration::from_secs(15); + // this will resist overflow until `year 2037` + let max_time = SystemTime::now() + ACCEPTABLE_DRIFT; + let invalid_threshold = max_time + ACCEPTABLE_DRIFT * 9; + let timestamp = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(header.timestamp())) + .ok_or(BlockError::TimestampOverflow)?; - if timestamp > max_time { - return Err(From::from(BlockError::TemporarilyInvalid(OutOfBounds { max: Some(max_time), min: None, found: timestamp }))) - } + if timestamp > invalid_threshold { + return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { + max: Some(max_time), + min: None, + found: timestamp + }.into()))) + } + + if timestamp > max_time { + return Err(From::from(BlockError::TemporarilyInvalid(OutOfBounds { + max: Some(max_time), + min: None, + found: timestamp + }.into()))) } Ok(()) } -/// Check header parameters agains parent header. -fn verify_parent(header: &Header, parent: &Header, engine: &EthEngine) -> Result<(), Error> { +/// Check header parameters against parent header. +fn verify_parent(header: &Header, parent: &Header, engine: &dyn Engine) -> 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 now = SystemTime::now(); let min = CheckedSystemTime::checked_add(now, Duration::from_secs(parent.timestamp().saturating_add(1))) .ok_or(BlockError::TimestampOverflow)?; let found = CheckedSystemTime::checked_add(now, Duration::from_secs(header.timestamp())) .ok_or(BlockError::TimestampOverflow)?; - return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(min), found }))) + return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(min), found }.into()))) } if header.number() != parent.number() + 1 { - return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number() + 1, found: header.number() }))); + return Err(From::from(BlockError::InvalidNumber(Mismatch { + expected: parent.number() + 1, + found: header.number() + }))); } if header.number() == 0 { - return Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }).into()); - } - - let parent_gas_limit = *parent.gas_limit(); - let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor; - let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor; - if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { - return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: *header.gas_limit() }))); + return Err(BlockError::RidiculousNumber(OutOfBounds { + min: Some(1), + max: None, + found: header.number() + }).into()); + } + if engine.gas_limit_override(header).is_none() { + let gas_limit_divisor = engine.params().gas_limit_bound_divisor; + let parent_gas_limit = *parent.gas_limit(); + let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor; + let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor; + if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { + min: Some(min_gas), + max: Some(max_gas), + found: *header.gas_limit() + }))); + } } Ok(()) @@ -363,17 +413,17 @@ fn verify_block_integrity(block: &Unverified) -> Result<(), Error> { let tx = block_rlp.at(1)?; let expected_root = ordered_trie_root(tx.iter().map(|r| r.as_raw())); if &expected_root != block.header.transactions_root() { - bail!(BlockError::InvalidTransactionsRoot(Mismatch { + return Err(BlockError::InvalidTransactionsRoot(Mismatch { expected: expected_root, found: *block.header.transactions_root(), - })); + }).into()); } let expected_uncles = keccak(block_rlp.at(2)?.as_raw()); if &expected_uncles != block.header.uncles_hash(){ - bail!(BlockError::InvalidUnclesHash(Mismatch { + return Err(BlockError::InvalidUnclesHash(Mismatch { expected: expected_uncles, found: *block.header.uncles_hash(), - })); + }).into()); } Ok(()) } @@ -382,22 +432,29 @@ fn verify_block_integrity(block: &Unverified) -> Result<(), Error> { mod tests { use super::*; - use std::collections::{BTreeMap, HashMap}; + use std::collections::BTreeMap; use std::time::{SystemTime, UNIX_EPOCH}; - use ethereum_types::{H256, BloomRef, U256}; - use blockchain::{BlockDetails, TransactionAddress, BlockReceipts}; - use types::encoded; - use hash::keccak; - use engines::EthEngine; - use error::BlockError::*; - use error::ErrorKind; - use ethkey::{Random, Generator}; - use spec::{CommonParams, Spec}; - use test_helpers::{create_test_block_with_data, create_test_block}; - use types::transaction::{SignedTransaction, Transaction, UnverifiedTransaction, Action}; - use types::log_entry::{LogEntry, LocalizedLogEntry}; + + use ethereum_types::{H256, U256, Address}; + use parity_bytes::Bytes; + use keccak_hash::keccak; + use engine::Engine; + use parity_crypto::publickey::{Random, Generator}; + use spec; + use ethcore::test_helpers::{ + create_test_block_with_data, create_test_block, TestBlockChainClient + }; + use common_types::{ + engines::params::CommonParams, + errors::BlockError::*, + transaction::{SignedTransaction, Transaction, UnverifiedTransaction, Action}, + }; use rlp; use triehash::ordered_trie_root; + use machine::Machine; + use null_engine::NullEngine; + + use crate::test_helpers::TestBlockChain; fn check_ok(result: Result<(), Error>) { result.unwrap_or_else(|e| panic!("Block verification failed: {:?}", e)); @@ -405,7 +462,7 @@ mod tests { fn check_fail(result: Result<(), Error>, e: BlockError) { match result { - Err(Error(ErrorKind::Block(ref error), _)) if *error == e => (), + Err(Error::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), } @@ -414,114 +471,19 @@ mod tests { fn check_fail_timestamp(result: Result<(), Error>, temp: bool) { let name = if temp { "TemporarilyInvalid" } else { "InvalidTimestamp" }; match result { - Err(Error(ErrorKind::Block(BlockError::InvalidTimestamp(_)), _)) if !temp => (), - Err(Error(ErrorKind::Block(BlockError::TemporarilyInvalid(_)), _)) if temp => (), + Err(Error::Block(BlockError::InvalidTimestamp(_))) if !temp => (), + Err(Error::Block(BlockError::TemporarilyInvalid(_))) if temp => (), Err(other) => panic!("Block verification failed.\nExpected: {}\nGot: {:?}", name, other), Ok(_) => panic!("Block verification failed.\nExpected: {}\nGot: Ok", name), } } - struct TestBlockChain { - blocks: HashMap, - numbers: HashMap, - } - - impl Default for TestBlockChain { - fn default() -> Self { - TestBlockChain::new() - } - } - - impl TestBlockChain { - pub fn new() -> Self { - TestBlockChain { - blocks: HashMap::new(), - numbers: HashMap::new(), - } - } - - pub fn insert(&mut self, bytes: Bytes) { - let header = Unverified::from_rlp(bytes.clone()).unwrap().header; - let hash = header.hash(); - self.blocks.insert(hash, bytes); - self.numbers.insert(header.number(), hash); - } - } - - impl BlockProvider for TestBlockChain { - fn is_known(&self, hash: &H256) -> bool { - self.blocks.contains_key(hash) - } - - fn first_block(&self) -> Option { - unimplemented!() - } - - /// Get raw block data - fn block(&self, hash: &H256) -> Option { - self.blocks.get(hash).cloned().map(encoded::Block::new) - } - - fn block_header_data(&self, hash: &H256) -> Option { - self.block(hash) - .map(|b| b.header_view().rlp().as_raw().to_vec()) - .map(encoded::Header::new) - } - - fn block_body(&self, hash: &H256) -> Option { - self.block(hash) - .map(|b| BlockChain::block_to_body(&b.into_inner())) - .map(encoded::Body::new) - } - - fn best_ancient_block(&self) -> Option { - None - } - - /// Get the familial details concerning a block. - fn block_details(&self, hash: &H256) -> Option { - self.blocks.get(hash).map(|bytes| { - let header = Unverified::from_rlp(bytes.to_vec()).unwrap().header; - BlockDetails { - number: header.number(), - total_difficulty: *header.difficulty(), - parent: *header.parent_hash(), - children: Vec::new(), - is_finalized: false, - } - }) - } - - fn transaction_address(&self, _hash: &H256) -> Option { - unimplemented!() - } - - /// Get the hash of given block's number. - fn block_hash(&self, index: BlockNumber) -> Option { - self.numbers.get(&index).cloned() - } - - fn block_receipts(&self, _hash: &H256) -> Option { - unimplemented!() - } - - fn blocks_with_bloom<'a, B, I, II>(&self, _blooms: II, _from_block: BlockNumber, _to_block: BlockNumber) -> Vec - where BloomRef<'a>: From, II: IntoIterator + Copy, I: Iterator, Self: Sized { - unimplemented!() - } - - 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> { + fn basic_test(bytes: &[u8], engine: &dyn Engine) -> Result<(), Error> { let unverified = Unverified::from_rlp(bytes.to_vec())?; verify_block_basic(&unverified, engine, true) } - fn family_test(bytes: &[u8], engine: &EthEngine, bc: &BC) -> Result<(), Error> where BC: BlockProvider { + fn family_test(bytes: &[u8], engine: &dyn Engine, bc: &BC) -> Result<(), Error> where BC: BlockProvider { let block = Unverified::from_rlp(bytes.to_vec()).unwrap(); let header = block.header; let transactions: Vec<_> = block.transactions @@ -533,7 +495,7 @@ mod tests { // additions that need access to state (tx filter in specific) // no existing tests need access to test, so having this not function // is fine. - let client = ::client::TestBlockChainClient::default(); + let client = TestBlockChainClient::default(); let parent = bc.block_header_data(header.parent_hash()) .ok_or(BlockError::UnknownParent(*header.parent_hash()))? .decode()?; @@ -547,13 +509,13 @@ mod tests { let full_params = FullFamilyParams { block: &block, - block_provider: bc as &BlockProvider, + block_provider: bc as &dyn BlockProvider, client: &client, }; - verify_block_family(&block.header, &parent, engine, Some(full_params)) + verify_block_family(&block.header, &parent, engine, full_params) } - fn unordered_test(bytes: &[u8], engine: &EthEngine) -> Result<(), Error> { + fn unordered_test(bytes: &[u8], engine: &dyn Engine) -> Result<(), Error> { let un = Unverified::from_rlp(bytes.to_vec())?; verify_block_unordered(un, engine, false)?; Ok(()) @@ -561,7 +523,7 @@ mod tests { #[test] fn test_verify_block_basic_with_invalid_transactions() { - let spec = Spec::new_test(); + let spec = spec::new_test(); let engine = &*spec.engine; let block = { @@ -570,7 +532,7 @@ mod tests { // that's an invalid transaction list rlp let invalid_transactions = vec![vec![0u8]]; header.set_transactions_root(ordered_trie_root(&invalid_transactions)); - header.set_gas_limit(engine.params().min_gas_limit); + header.set_gas_limit(engine.min_gas_limit()); rlp.append(&header); rlp.append_list::, _>(&invalid_transactions); rlp.append_raw(&rlp::EMPTY_LIST_RLP, 1); @@ -586,10 +548,10 @@ mod tests { // Test against morden let mut good = Header::new(); - let spec = Spec::new_test(); + let spec = spec::new_test(); let engine = &*spec.engine; - let min_gas_limit = engine.params().min_gas_limit; + let min_gas_limit = engine.min_gas_limit(); good.set_gas_limit(min_gas_limit); good.set_timestamp(40); good.set_number(10); @@ -615,7 +577,7 @@ mod tests { }.sign(keypair.secret(), None); let tr3 = Transaction { - action: Action::Call(0x0.into()), + action: Action::Call(Address::from_low_u64_be(0x0)), value: U256::from(0), data: Bytes::new(), gas: U256::from(30_000), @@ -689,7 +651,7 @@ mod tests { 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() => (), + Err(Error::Transaction(ref e)) if e == &parity_crypto::publickey::Error::InvalidSignature.into() => (), e => panic!("Block verification failed.\nExpected: Transaction Error (Invalid Signature)\nGot: {:?}", e), } @@ -784,7 +746,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(ErrorKind::Block(InvalidGasLimit(_)), _)) => {}, + Err(Error::Block(InvalidGasLimit(_))) => {}, Err(_) => { panic!("should be invalid difficulty fail"); }, _ => { panic!("Should be error, got Ok"); }, } @@ -794,11 +756,6 @@ mod tests { #[test] fn dust_protection() { - use ethkey::{Generator, Random}; - use types::transaction::{Transaction, Action}; - use machine::EthereumMachine; - use engines::NullEngine; - let mut params = CommonParams::default(); params.dust_protection_transition = 0; params.nonce_cap_increment = 2; @@ -818,7 +775,7 @@ mod tests { let good_transactions = [bad_transactions[0].clone(), bad_transactions[1].clone()]; - let machine = EthereumMachine::regular(params, BTreeMap::new()); + let machine = Machine::regular(params, BTreeMap::new()); let engine = NullEngine::new(Default::default(), machine); check_fail(unordered_test(&create_test_block_with_data(&header, &bad_transactions, &[]), &engine), TooManyTransactions(keypair.address())); unordered_test(&create_test_block_with_data(&header, &good_transactions, &[]), &engine).unwrap(); diff --git a/ethcore/vm/Cargo.toml b/ethcore/vm/Cargo.toml index 34b086bce0..fdf431edb7 100644 --- a/ethcore/vm/Cargo.toml +++ b/ethcore/vm/Cargo.toml @@ -6,8 +6,8 @@ authors = ["Parity Technologies "] [dependencies] parity-bytes = "0.1" -ethereum-types = "0.4" +ethereum-types = "0.8.0" patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" } ethjson = { path = "../../json" } -rlp = { version = "0.3.0", features = ["ethereum"] } -keccak-hash = "0.1" +rlp = "0.4.0" +keccak-hash = "0.4.0" diff --git a/ethcore/vm/src/action_params.rs b/ethcore/vm/src/action_params.rs index 0d4959c189..da06fb3f3f 100644 --- a/ethcore/vm/src/action_params.rs +++ b/ethcore/vm/src/action_params.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use bytes::Bytes; use hash::{keccak, KECCAK_EMPTY}; use ethjson; -use call_type::CallType; +use action_type::ActionType; use std::sync::Arc; @@ -84,10 +84,12 @@ pub struct ActionParams { pub value: ActionValue, /// Code being executed. pub code: Option>, + /// Code version being executed. + pub code_version: U256, /// Input data. pub data: Option, - /// Type of call - pub call_type: CallType, + /// Type of action (e.g. CALL, DELEGATECALL, CREATE, etc.) + pub action_type: ActionType, /// Param types encoding pub params_type: ParamsType, } @@ -96,17 +98,18 @@ impl Default for ActionParams { /// Returns default ActionParams initialized with zeros fn default() -> ActionParams { ActionParams { - code_address: Address::new(), + code_address: Address::zero(), code_hash: Some(KECCAK_EMPTY), - address: Address::new(), - sender: Address::new(), - origin: Address::new(), + address: Address::zero(), + sender: Address::zero(), + origin: Address::zero(), gas: U256::zero(), gas_price: U256::zero(), value: ActionValue::Transfer(U256::zero()), code: None, + code_version: U256::zero(), data: None, - call_type: CallType::None, + action_type: ActionType::Create, params_type: ParamsType::Separate, } } @@ -116,17 +119,18 @@ impl From for ActionParams { fn from(t: ethjson::vm::Transaction) -> Self { let address: Address = t.address.into(); ActionParams { - code_address: Address::new(), + code_address: Address::zero(), code_hash: Some(keccak(&*t.code)), address: address, sender: t.sender.into(), origin: t.origin.into(), code: Some(Arc::new(t.code.into())), + code_version: t.code_version.into(), data: Some(t.data.into()), gas: t.gas.into(), gas_price: t.gas_price.into(), value: ActionValue::Transfer(t.value.into()), - call_type: match address.is_zero() { true => CallType::None, false => CallType::Call }, // TODO @debris is this correct? + action_type: ActionType::Call, params_type: ParamsType::Separate, } } diff --git a/ethcore/vm/src/call_type.rs b/ethcore/vm/src/action_type.rs similarity index 63% rename from ethcore/vm/src/call_type.rs rename to ethcore/vm/src/action_type.rs index e5245c8c14..2696ba598a 100644 --- a/ethcore/vm/src/call_type.rs +++ b/ethcore/vm/src/action_type.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,47 +14,51 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! EVM call types. +//! EVM action types. use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp}; -/// The type of the call-like instruction. +/// The type of the instruction. #[derive(Debug, PartialEq, Clone)] -pub enum CallType { - /// Not a CALL. - None, +pub enum ActionType { + /// CREATE. + Create, /// CALL. Call, /// CALLCODE. CallCode, /// DELEGATECALL. DelegateCall, - /// STATICCALL + /// STATICCALL. StaticCall, + /// CREATE2. + Create2 } -impl Encodable for CallType { +impl Encodable for ActionType { fn rlp_append(&self, s: &mut RlpStream) { let v = match *self { - CallType::None => 0u32, - CallType::Call => 1, - CallType::CallCode => 2, - CallType::DelegateCall => 3, - CallType::StaticCall => 4, + ActionType::Create => 0u32, + ActionType::Call => 1, + ActionType::CallCode => 2, + ActionType::DelegateCall => 3, + ActionType::StaticCall => 4, + ActionType::Create2 => 5, }; Encodable::rlp_append(&v, s); } } -impl Decodable for CallType { +impl Decodable for ActionType { fn decode(rlp: &Rlp) -> Result { rlp.as_val().and_then(|v| Ok(match v { - 0u32 => CallType::None, - 1 => CallType::Call, - 2 => CallType::CallCode, - 3 => CallType::DelegateCall, - 4 => CallType::StaticCall, - _ => return Err(DecoderError::Custom("Invalid value of CallType item")), + 0u32 => ActionType::Create, + 1 => ActionType::Call, + 2 => ActionType::CallCode, + 3 => ActionType::DelegateCall, + 4 => ActionType::StaticCall, + 5 => ActionType::Create2, + _ => return Err(DecoderError::Custom("Invalid value of ActionType item")), })) } } @@ -62,11 +66,11 @@ impl Decodable for CallType { #[cfg(test)] mod tests { use rlp::*; - use super::CallType; + use super::ActionType; #[test] fn encode_call_type() { - let ct = CallType::Call; + let ct = ActionType::Call; let mut s = RlpStream::new_list(2); s.append(&ct); @@ -78,9 +82,9 @@ mod tests { #[test] fn should_encode_and_decode_call_type() { - let original = CallType::Call; + let original = ActionType::Call; let encoded = encode(&original); - let decoded = decode(&encoded).expect("failure decoding CallType"); + let decoded = decode(&encoded).expect("failure decoding ActionType"); assert_eq!(original, decoded); } } diff --git a/ethcore/vm/src/env_info.rs b/ethcore/vm/src/env_info.rs index addfa62898..f0a0d531b8 100644 --- a/ethcore/vm/src/env_info.rs +++ b/ethcore/vm/src/env_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -51,7 +51,7 @@ impl Default for EnvInfo { fn default() -> Self { EnvInfo { number: 0, - author: Address::default(), + author: Address::zero(), timestamp: 0, difficulty: 0.into(), gas_limit: 0.into(), diff --git a/ethcore/vm/src/error.rs b/ethcore/vm/src/error.rs index 3340056335..b475b9f799 100644 --- a/ethcore/vm/src/error.rs +++ b/ethcore/vm/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -119,5 +119,5 @@ impl fmt::Display for Error { pub type Result = ::std::result::Result; pub type TrapResult = ::std::result::Result, TrapError>; -pub type ExecTrapResult = TrapResult, Box>; -pub type ExecTrapError = TrapError, Box>; +pub type ExecTrapResult = TrapResult, Box>; +pub type ExecTrapError = TrapError, Box>; diff --git a/ethcore/vm/src/ext.rs b/ethcore/vm/src/ext.rs index 247758c1e5..656831cdcf 100644 --- a/ethcore/vm/src/ext.rs +++ b/ethcore/vm/src/ext.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use std::sync::Arc; use ethereum_types::{U256, H256, Address}; use bytes::Bytes; -use call_type::CallType; +use action_type::ActionType; use env_info::EnvInfo; use schedule::Schedule; use return_data::ReturnData; @@ -97,6 +97,7 @@ pub trait Ext { gas: &U256, value: &U256, code: &[u8], + parent_version: &U256, address: CreateContractAddress, trap: bool, ) -> ::std::result::Result; @@ -114,7 +115,7 @@ pub trait Ext { value: Option, data: &[u8], code_address: &Address, - call_type: CallType, + call_type: ActionType, trap: bool ) -> ::std::result::Result; diff --git a/ethcore/vm/src/lib.rs b/ethcore/vm/src/lib.rs index 4204ff92dc..6c5ed98fd5 100644 --- a/ethcore/vm/src/lib.rs +++ b/ethcore/vm/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,7 +24,7 @@ extern crate keccak_hash as hash; extern crate patricia_trie_ethereum as ethtrie; mod action_params; -mod call_type; +mod action_type; mod env_info; mod schedule; mod ext; @@ -34,9 +34,9 @@ mod error; pub mod tests; pub use action_params::{ActionParams, ActionValue, ParamsType}; -pub use call_type::CallType; +pub use action_type::ActionType; pub use env_info::{EnvInfo, LastHashes}; -pub use schedule::{Schedule, CleanDustMode, WasmCosts}; +pub use schedule::{Schedule, VersionedSchedule, CleanDustMode, WasmCosts}; pub use ext::{Ext, MessageCallResult, ContractCreateResult, CreateContractAddress}; pub use return_data::{ReturnData, GasLeft}; pub use error::{Error, Result, TrapResult, TrapError, TrapKind, ExecTrapResult, ExecTrapError}; @@ -46,17 +46,17 @@ pub trait Exec: Send { /// This function should be used to execute transaction. /// It returns either an error, a known amount of gas left, or parameters to be used /// to compute the final gas left. - fn exec(self: Box, ext: &mut Ext) -> ExecTrapResult; + fn exec(self: Box, ext: &mut dyn Ext) -> ExecTrapResult; } /// Resume call interface pub trait ResumeCall: Send { /// Resume an execution for call, returns back the Vm interface. - fn resume_call(self: Box, result: MessageCallResult) -> Box; + fn resume_call(self: Box, result: MessageCallResult) -> Box; } /// Resume create interface pub trait ResumeCreate: Send { /// Resume an execution from create, returns back the Vm interface. - fn resume_create(self: Box, result: ContractCreateResult) -> Box; + fn resume_create(self: Box, result: ContractCreateResult) -> Box; } diff --git a/ethcore/vm/src/return_data.rs b/ethcore/vm/src/return_data.rs index 38ac23ffdc..35420fbc90 100644 --- a/ethcore/vm/src/return_data.rs +++ b/ethcore/vm/src/return_data.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/vm/src/schedule.rs b/ethcore/vm/src/schedule.rs index 6fad8351e6..58d4b81525 100644 --- a/ethcore/vm/src/schedule.rs +++ b/ethcore/vm/src/schedule.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -53,6 +53,8 @@ pub struct Schedule { pub sha3_word_gas: usize, /// Gas price for loading from storage pub sload_gas: usize, + /// Special gas price for dirty gas of SSTORE, after net gas metering. + pub sstore_dirty_gas: Option, /// Gas price for setting new value to storage (`storage==0`, `new!=0`) pub sstore_set_gas: usize, /// Gas price for altering value in storage @@ -136,6 +138,10 @@ pub struct Schedule { pub eip1706: bool, /// VM execution does not increase null signed address nonce if this field is true. pub keep_unsigned_nonce: bool, + /// Latest VM version for contract creation transaction. + pub latest_version: U256, + /// All supported non-legacy VM versions. + pub versions: HashMap, /// Wasm extra schedule settings, if wasm activated pub wasm: Option, } @@ -236,6 +242,7 @@ impl Schedule { sha3_gas: 30, sha3_word_gas: 6, sload_gas: 200, + sstore_dirty_gas: None, sstore_set_gas: 20000, sstore_reset_gas: 5000, sstore_refund_gas: 15000, @@ -273,6 +280,8 @@ impl Schedule { eip1283: false, eip1706: false, keep_unsigned_nonce: false, + latest_version: U256::zero(), + versions: HashMap::new(), wasm: None, } } @@ -325,6 +334,7 @@ impl Schedule { sha3_gas: 30, sha3_word_gas: 6, sload_gas: 50, + sstore_dirty_gas: None, sstore_set_gas: 20000, sstore_reset_gas: 5000, sstore_refund_gas: 15000, @@ -362,6 +372,8 @@ impl Schedule { eip1283: false, eip1706: false, keep_unsigned_nonce: false, + latest_version: U256::zero(), + versions: HashMap::new(), wasm: None, } } diff --git a/ethcore/vm/src/tests.rs b/ethcore/vm/src/tests.rs index 5684a234ac..bdca9bbf3a 100644 --- a/ethcore/vm/src/tests.rs +++ b/ethcore/vm/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use std::collections::{HashMap, HashSet}; use ethereum_types::{U256, H256, Address}; use bytes::Bytes; use { - CallType, Schedule, EnvInfo, + ActionType, Schedule, EnvInfo, ReturnData, Ext, ContractCreateResult, MessageCallResult, CreateContractAddress, Result, GasLeft, }; @@ -122,11 +122,11 @@ impl FakeExt { impl Ext for FakeExt { fn initial_storage_at(&self, _key: &H256) -> Result { - Ok(H256::new()) + Ok(H256::zero()) } fn storage_at(&self, key: &H256) -> Result { - Ok(self.store.get(key).unwrap_or(&H256::new()).clone()) + Ok(self.store.get(key).unwrap_or(&H256::zero()).clone()) } fn set_storage(&mut self, key: H256, value: H256) -> Result<()> { @@ -151,7 +151,7 @@ impl Ext for FakeExt { } fn blockhash(&mut self, number: &U256) -> H256 { - self.blockhashes.get(number).unwrap_or(&H256::new()).clone() + self.blockhashes.get(number).unwrap_or(&H256::zero()).clone() } fn create( @@ -159,6 +159,7 @@ impl Ext for FakeExt { gas: &U256, value: &U256, code: &[u8], + _parent_version: &U256, address: CreateContractAddress, _trap: bool, ) -> ::std::result::Result { @@ -184,7 +185,7 @@ impl Ext for FakeExt { value: Option, data: &[u8], code_address: &Address, - _call_type: CallType, + _call_type: ActionType, _trap: bool, ) -> ::std::result::Result { self.calls.insert(FakeCall { diff --git a/ethcore/wasm/Cargo.toml b/ethcore/wasm/Cargo.toml index e724a6a4cf..1feb9b31c0 100644 --- a/ethcore/wasm/Cargo.toml +++ b/ethcore/wasm/Cargo.toml @@ -6,7 +6,7 @@ authors = ["Parity Technologies "] [dependencies] byteorder = "1.0" -ethereum-types = "0.4" +ethereum-types = "0.8.0" log = "0.4" parity-wasm = "0.31" libc = "0.2" diff --git a/ethcore/wasm/run/Cargo.toml b/ethcore/wasm/run/Cargo.toml index eee0d5fa32..1cd5536744 100644 --- a/ethcore/wasm/run/Cargo.toml +++ b/ethcore/wasm/run/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Parity Technologies "] serde = "1" serde_json = "1" serde_derive = "1" -ethereum-types = "0.4" +ethereum-types = "0.8.0" 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 42117d6dfd..13f9936812 100644 --- a/ethcore/wasm/run/src/fixture.rs +++ b/ethcore/wasm/run/src/fixture.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/wasm/run/src/main.rs b/ethcore/wasm/run/src/main.rs index 0773f9b42a..1fd52df3c1 100644 --- a/ethcore/wasm/run/src/main.rs +++ b/ethcore/wasm/run/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/wasm/run/src/runner.rs b/ethcore/wasm/run/src/runner.rs index a69fe6bf67..fe80415dd8 100644 --- a/ethcore/wasm/run/src/runner.rs +++ b/ethcore/wasm/run/src/runner.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use vm::tests::FakeExt; use std::io::{self, Read}; use std::{fs, path, fmt}; use std::sync::Arc; -use ethereum_types::{U256, H256, H160}; +use ethereum_types::{U256, H256, H160, BigEndianHash}; use rustc_hex::ToHex; fn load_code>(p: P) -> io::Result> { @@ -95,17 +95,17 @@ impl fmt::Display for Fail { write!( f, "Storage key {} value mismatch, expected {}, got: {}", - key.to_vec().to_hex(), - expected.to_vec().to_hex(), - actual.to_vec().to_hex(), + key.as_bytes().to_vec().to_hex(), + expected.as_bytes().to_vec().to_hex(), + actual.as_bytes().to_vec().to_hex(), ), StorageMismatch { ref key, ref expected, actual: None} => write!( f, "No expected storage value for key {} found, expected {}", - key.to_vec().to_hex(), - expected.to_vec().to_hex(), + key.as_bytes().to_vec().to_hex(), + expected.as_bytes().to_vec().to_hex(), ), Nonconformity(SpecNonconformity::Address) => @@ -115,7 +115,7 @@ impl fmt::Display for Fail { } pub fn construct( - ext: &mut vm::Ext, + ext: &mut dyn vm::Ext, source: Vec, arguments: Vec, sender: H160, @@ -188,7 +188,7 @@ pub fn run_fixture(fixture: &Fixture) -> Vec { for storage_entry in storage.iter() { let key: U256 = storage_entry.key.into(); let val: U256 = storage_entry.value.into(); - ext.store.insert(key.into(), val.into()); + ext.store.insert(BigEndianHash::from_uint(&key), BigEndianHash::from_uint(&val)); } } diff --git a/ethcore/wasm/src/env.rs b/ethcore/wasm/src/env.rs index b996ea000a..5211e79235 100644 --- a/ethcore/wasm/src/env.rs +++ b/ethcore/wasm/src/env.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/wasm/src/lib.rs b/ethcore/wasm/src/lib.rs index 1e6129ba13..8d3aa79e81 100644 --- a/ethcore/wasm/src/lib.rs +++ b/ethcore/wasm/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -96,7 +96,7 @@ enum ExecutionOutcome { } impl WasmInterpreter { - pub fn run(self: Box, ext: &mut vm::Ext) -> vm::Result { + pub fn run(self: Box, ext: &mut dyn vm::Ext) -> vm::Result { let (module, data) = parser::payload(&self.params, ext.schedule().wasm())?; let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?; @@ -131,6 +131,7 @@ impl WasmInterpreter { sender: self.params.sender, origin: self.params.origin, code_address: self.params.code_address, + code_version: self.params.code_version, value: self.params.value.value(), }, ); @@ -195,7 +196,7 @@ impl WasmInterpreter { } impl vm::Exec for WasmInterpreter { - fn exec(self: Box, ext: &mut vm::Ext) -> vm::ExecTrapResult { + fn exec(self: Box, ext: &mut dyn vm::Ext) -> vm::ExecTrapResult { Ok(self.run(ext)) } } diff --git a/ethcore/wasm/src/panic_payload.rs b/ethcore/wasm/src/panic_payload.rs index a484daf712..695c19847e 100644 --- a/ethcore/wasm/src/panic_payload.rs +++ b/ethcore/wasm/src/panic_payload.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/wasm/src/parser.rs b/ethcore/wasm/src/parser.rs index ca730e7188..76410a5e5c 100644 --- a/ethcore/wasm/src/parser.rs +++ b/ethcore/wasm/src/parser.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/ethcore/wasm/src/runtime.rs b/ethcore/wasm/src/runtime.rs index 8466c3b8d9..3350e15bd8 100644 --- a/ethcore/wasm/src/runtime.rs +++ b/ethcore/wasm/src/runtime.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,8 +15,8 @@ // along with Parity Ethereum. If not, see . use std::cmp; -use ethereum_types::{U256, H256, Address}; -use vm::{self, CallType}; +use ethereum_types::{BigEndianHash, U256, H256, Address}; +use vm::{self, ActionType}; use wasmi::{self, MemoryRef, RuntimeArgs, RuntimeValue, Error as InterpreterError, Trap, TrapKind}; use super::panic_payload; @@ -25,13 +25,14 @@ pub struct RuntimeContext { pub sender: Address, pub origin: Address, pub code_address: Address, + pub code_version: U256, pub value: U256, } pub struct Runtime<'a> { gas_counter: u64, gas_limit: u64, - ext: &'a mut vm::Ext, + ext: &'a mut dyn vm::Ext, context: RuntimeContext, memory: MemoryRef, args: Vec, @@ -127,7 +128,7 @@ impl ::std::fmt::Display for Error { Error::AllocationFailed => write!(f, "Memory allocation failed (OOM)"), Error::BadUtf8 => write!(f, "String encoding is bad utf-8 sequence"), Error::GasLimit => write!(f, "Invocation resulted in gas limit violated"), - Error::Log => write!(f, "Error occured while logging an event"), + Error::Log => write!(f, "Error occurred while logging an event"), Error::InvalidSyscall => write!(f, "Invalid syscall signature encountered at runtime"), Error::Other => write!(f, "Other unspecified error"), Error::Unreachable => write!(f, "Unreachable instruction encountered"), @@ -146,7 +147,7 @@ impl<'a> Runtime<'a> { /// New runtime for wasm contract with specified params pub fn with_params( - ext: &mut vm::Ext, + ext: &mut dyn vm::Ext, memory: MemoryRef, gas_limit: u64, args: Vec, @@ -168,7 +169,7 @@ impl<'a> Runtime<'a> { let mut buf = [0u8; 32]; self.memory.get_into(ptr, &mut buf[..])?; - Ok(H256::from(&buf[..])) + Ok(H256::from_slice(&buf[..])) } /// Loads 160-bit hash (Ethereum address) from the specified sandboxed memory pointer @@ -176,7 +177,7 @@ impl<'a> Runtime<'a> { let mut buf = [0u8; 20]; self.memory.get_into(ptr, &mut buf[..])?; - Ok(Address::from(&buf[..])) + Ok(Address::from_slice(&buf[..])) } /// Loads 256-bit integer represented with bigendian from the specified sandboxed memory pointer @@ -262,7 +263,7 @@ impl<'a> Runtime<'a> { self.adjusted_charge(|schedule| schedule.sload_gas as u64)?; - self.memory.set(val_ptr as u32, &*val)?; + self.memory.set(val_ptr as u32, val.as_bytes())?; Ok(()) } @@ -383,7 +384,7 @@ impl<'a> Runtime<'a> { fn do_call( &mut self, use_val: bool, - call_type: CallType, + call_type: ActionType, args: RuntimeArgs, ) -> Result @@ -444,8 +445,8 @@ impl<'a> Runtime<'a> { let call_result = self.ext.call( &gas.into(), - match call_type { CallType::DelegateCall => &self.context.sender, _ => &self.context.address }, - match call_type { CallType::Call | CallType::StaticCall => &address, _ => &self.context.address }, + match call_type { ActionType::DelegateCall => &self.context.sender, _ => &self.context.address }, + match call_type { ActionType::Call | ActionType::StaticCall => &address, _ => &self.context.address }, val, &payload, &address, @@ -486,30 +487,30 @@ impl<'a> Runtime<'a> { /// Message call fn ccall(&mut self, args: RuntimeArgs) -> Result { - self.do_call(true, CallType::Call, args) + self.do_call(true, ActionType::Call, args) } /// Delegate call fn dcall(&mut self, args: RuntimeArgs) -> Result { - self.do_call(false, CallType::DelegateCall, args) + self.do_call(false, ActionType::DelegateCall, args) } /// Static call fn scall(&mut self, args: RuntimeArgs) -> Result { - self.do_call(false, CallType::StaticCall, args) + self.do_call(false, ActionType::StaticCall, args) } fn return_address_ptr(&mut self, ptr: u32, val: Address) -> Result<()> { self.charge(|schedule| schedule.wasm().static_address as u64)?; - self.memory.set(ptr, &*val)?; + self.memory.set(ptr, val.as_bytes())?; Ok(()) } fn return_u256_ptr(&mut self, ptr: u32, val: U256) -> Result<()> { - let value: H256 = val.into(); + let value: H256 = BigEndianHash::from_uint(&val); self.charge(|schedule| schedule.wasm().static_u256 as u64)?; - self.memory.set(ptr, &*value)?; + self.memory.set(ptr, value.as_bytes())?; Ok(()) } @@ -529,9 +530,9 @@ impl<'a> Runtime<'a> { * U256::from(self.ext.schedule().wasm().opcodes_mul) / U256::from(self.ext.schedule().wasm().opcodes_div); - match self.ext.create(&gas_left, &endowment, &code, scheme, false).ok().expect("Trap is false; trap error will not happen; qed") { + match self.ext.create(&gas_left, &endowment, &code, &self.context.code_version, scheme, false).ok().expect("Trap is false; trap error will not happen; qed") { vm::ContractCreateResult::Created(address, gas_left) => { - self.memory.set(result_ptr, &*address)?; + self.memory.set(result_ptr, address.as_bytes())?; self.gas_counter = self.gas_limit - // this cannot overflow, since initial gas is in [0..u64::max) range, // and gas_left cannot be bigger @@ -598,7 +599,7 @@ impl<'a> Runtime<'a> { trace!(target: "wasm", "runtime: CREATE2"); let endowment = self.u256_at(args.nth_checked(0)?)?; trace!(target: "wasm", " val: {:?}", endowment); - let salt: H256 = self.u256_at(args.nth_checked(1)?)?.into(); + let salt: H256 = BigEndianHash::from_uint(&self.u256_at(args.nth_checked(1)?)?); trace!(target: "wasm", " salt: {:?}", salt); let code_ptr: u32 = args.nth_checked(2)?; trace!(target: "wasm", " code_ptr: {:?}", code_ptr); @@ -646,7 +647,7 @@ impl<'a> Runtime<'a> { pub fn blockhash(&mut self, args: RuntimeArgs) -> Result<()> { self.adjusted_charge(|schedule| schedule.blockhash_gas as u64)?; let hash = self.ext.blockhash(&U256::from(args.nth_checked::(0)?)); - self.memory.set(args.nth_checked(1)?, &*hash)?; + self.memory.set(args.nth_checked(1)?, hash.as_bytes())?; Ok(()) } @@ -736,7 +737,7 @@ impl<'a> Runtime<'a> { *topics.get_mut(i as usize) .expect("topics is resized to `topic_count`, i is in 0..topic count iterator, get_mut uses i as an indexer, get_mut cannot fail; qed") - = H256::from(&self.memory.get(offset, 32)?[..]); + = H256::from_slice(&self.memory.get(offset, 32)?[..]); } self.ext.log(topics, &self.memory.get(data_ptr, data_len as usize)?).map_err(|_| Error::Log)?; diff --git a/ethcore/wasm/src/tests.rs b/ethcore/wasm/src/tests.rs index 9ed7053da1..544e7b6a14 100644 --- a/ethcore/wasm/src/tests.rs +++ b/ethcore/wasm/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,8 +16,9 @@ use std::sync::Arc; use std::collections::HashMap; +use std::str::FromStr; use byteorder::{LittleEndian, ByteOrder}; -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{H256, U256, Address, BigEndianHash as _}; use super::WasmInterpreter; use vm::{self, Exec, GasLeft, ActionParams, ActionValue, CreateContractAddress}; @@ -47,7 +48,7 @@ macro_rules! reqrep_test { fake_ext.info = $info; fake_ext.blockhashes = $block_hashes; - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); interpreter.exec(&mut fake_ext).ok().unwrap() .map(|result| match result { GasLeft::Known(_) => { panic!("Test is expected to return payload to check"); }, @@ -82,7 +83,7 @@ fn empty() { let mut ext = FakeExt::new().with_wasm(); let gas_left = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -111,7 +112,7 @@ fn logger() { let mut ext = FakeExt::new().with_wasm(); let gas_left = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -134,7 +135,10 @@ fn logger() { "Logger sets 0x03 key to the provided origin" ); assert_eq!( - U256::from(ext.store.get(&"0400000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist")), + ext.store + .get(&"0400000000000000000000000000000000000000000000000000000000000000".parse().unwrap()) + .expect("storage key to exist") + .into_uint(), U256::from(1_000_000_000), "Logger sets 0x04 key to the trasferred value" ); @@ -160,7 +164,7 @@ fn identity() { let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Identity contract should return payload"); }, @@ -195,7 +199,7 @@ fn dispersion() { let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Dispersion routine should return payload"); }, @@ -223,7 +227,7 @@ fn suicide_not() { let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Suicidal contract should return payload when had not actualy killed himself"); }, @@ -250,13 +254,13 @@ fn suicide() { params.code = Some(Arc::new(code)); let mut args = vec![127u8]; - args.extend(refund.to_vec()); + args.extend(refund.as_bytes().to_vec()); params.data = Some(args); let mut ext = FakeExt::new().with_wasm(); let gas_left = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(gas) => gas, @@ -284,7 +288,7 @@ fn create() { ext.schedule.wasm.as_mut().unwrap().have_create2 = true; let gas_left = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { @@ -311,10 +315,13 @@ fn create() { code_address: None, } )); + let mut salt = [0u8; 32]; + salt[0] = 5; + let salt = H256::from_slice(salt.as_ref()); assert!(ext.calls.contains( &FakeCall { call_type: FakeCallType::Create, - create_scheme: Some(CreateContractAddress::FromSenderSaltAndCodeHash(H256::from([5u8].as_ref()))), + create_scheme: Some(CreateContractAddress::FromSenderSaltAndCodeHash(salt)), gas: U256::from(6039), sender_address: None, receive_address: None, @@ -346,7 +353,7 @@ fn call_msg() { ext.balances.insert(receiver.clone(), U256::from(10000000000u64)); let gas_left = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(gas_left) => gas_left, @@ -394,7 +401,7 @@ fn call_msg_gasleft() { ext.balances.insert(receiver.clone(), U256::from(10000000000u64)); let gas_left = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(gas_left) => gas_left, @@ -437,7 +444,7 @@ fn call_code() { let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Call test should return payload"); }, @@ -485,7 +492,7 @@ fn call_static() { let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Static call test should return payload"); }, @@ -526,7 +533,7 @@ fn realloc() { let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("Realloc should return payload"); }, @@ -548,7 +555,7 @@ fn alloc() { let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("alloc test should return payload"); }, @@ -572,10 +579,11 @@ fn storage_read() { params.gas = U256::from(100_000); params.code = Some(Arc::new(code)); let mut ext = FakeExt::new().with_wasm(); - ext.store.insert("0100000000000000000000000000000000000000000000000000000000000000".into(), address.into()); + let hash = H256::from_str("0100000000000000000000000000000000000000000000000000000000000000").unwrap(); + ext.store.insert(hash, address.into()); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("storage_read should return payload"); }, @@ -583,7 +591,7 @@ fn storage_read() { } }; - assert_eq!(Address::from(&result[12..32]), address); + assert_eq!(Address::from_slice(&result[12..32]), address); assert_eq!(gas_left, U256::from(98_369)); } @@ -601,7 +609,7 @@ fn keccak() { let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("keccak should return payload"); }, @@ -609,7 +617,10 @@ fn keccak() { } }; - assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); + assert_eq!( + H256::from_slice(&result), + H256::from_str("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87").unwrap(), + ); assert_eq!(gas_left, U256::from(85_949)); } @@ -748,7 +759,7 @@ fn storage_metering() { ]); let gas_left = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -767,7 +778,7 @@ fn storage_metering() { ]); let gas_left = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap() }; @@ -786,7 +797,7 @@ fn externs() { number: 0x9999999999u64.into(), author: "efefefefefefefefefefefefefefefefefefefef".parse().unwrap(), timestamp: 0x8888888888u64.into(), - difficulty: H256::from("0f1f2f3f4f5f6f7f8f9fafbfcfdfefff0d1d2d3d4d5d6d7d8d9dadbdcdddedfd").into(), + difficulty: U256::from_str("0f1f2f3f4f5f6f7f8f9fafbfcfdfefff0d1d2d3d4d5d6d7d8d9dadbdcdddedfd").unwrap(), gas_limit: 0x777777777777u64.into(), last_hashes: Default::default(), gas_used: 0.into(), @@ -795,11 +806,11 @@ fn externs() { let mut hashes = HashMap::new(); hashes.insert( U256::from(0), - H256::from("9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d") + H256::from_str("9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d").unwrap(), ); hashes.insert( U256::from(1), - H256::from("7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b") + H256::from_str("7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b").unwrap(), ); hashes } @@ -918,7 +929,7 @@ fn embedded_keccak() { let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("keccak should return payload"); }, @@ -926,7 +937,10 @@ fn embedded_keccak() { } }; - assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); + assert_eq!( + H256::from_slice(&result), + H256::from_str("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87").unwrap(), + ); assert_eq!(gas_left, U256::from(85_949)); } @@ -946,7 +960,7 @@ fn events() { let mut ext = FakeExt::new().with_wasm(); let (gas_left, result) = { - let mut interpreter = wasm_interpreter(params); + let interpreter = wasm_interpreter(params); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors"); match result { GasLeft::Known(_) => { panic!("events should return payload"); }, @@ -957,9 +971,15 @@ fn events() { assert_eq!(ext.logs.len(), 1); let log_entry = &ext.logs[0]; assert_eq!(log_entry.topics.len(), 2); - assert_eq!(&log_entry.topics[0], &H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87")); - assert_eq!(&log_entry.topics[1], &H256::from("871d5ea37430753faab7dff7a7187783517d83bd822c02e28a164c887e1d3768")); - assert_eq!(&log_entry.data, b"gnihtemos"); + assert_eq!( + log_entry.topics[0], + H256::from_str("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87").unwrap(), + ); + assert_eq!( + log_entry.topics[1], + H256::from_str("871d5ea37430753faab7dff7a7187783517d83bd822c02e28a164c887e1d3768").unwrap(), + ); + assert_eq!(log_entry.data, b"gnihtemos"); assert_eq!(&result, b"gnihtemos"); assert_eq!(gas_left, U256::from(83_161)); diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index dd6a3aad04..92ada4ef64 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -3,30 +3,38 @@ description = "Parity EVM Implementation" name = "evmbin" version = "0.1.0" authors = ["Parity Technologies "] +edition = "2018" [[bin]] name = "parity-evm" path = "./src/main.rs" [dependencies] -common-types = { path = "../ethcore/types" } +account-state = { path = "../ethcore/account-state" } +common-types = { path = "../ethcore/types", features = ["test-helpers"] } docopt = "1.0" env_logger = "0.5" -ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests", "to-pod-full"] } -ethereum-types = "0.4" -ethjson = { path = "../json" } +ethcore = { path = "../ethcore", features = ["test-helpers", "json-tests"] } +ethereum-types = "0.8.0" +ethjson = { path = "../json", features = ["test-helpers"] } evm = { path = "../ethcore/evm" } panic_hook = { path = "../util/panic-hook" } parity-bytes = "0.1" +pod = { path = "../ethcore/pod" } rustc-hex = "1.0" -serde = "1.0" -serde_derive = "1.0" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +spec = { path = "../ethcore/spec" } +trace = { path = "../ethcore/trace" } vm = { path = "../ethcore/vm" } [dev-dependencies] -pretty_assertions = "0.1" +criterion = "0.3" tempdir = "0.3" [features] evm-debug = ["ethcore/evm-debug-tests"] + +[[bench]] +name = "mod" +harness = false diff --git a/evmbin/README.md b/evmbin/README.md index e0a8c9d966..8fff22e8aa 100644 --- a/evmbin/README.md +++ b/evmbin/README.md @@ -6,7 +6,7 @@ EVM implementation for Parity. ``` EVM implementation for Parity. - Copyright 2015-2019 Parity Technologies (UK) Ltd. + Copyright 2015-2020 Parity Technologies (UK) Ltd. Usage: parity-evm state-test [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only] @@ -30,7 +30,7 @@ Transaction options: --gas-price WEI Supplied gas price as hex (without 0x). State test options: - --only NAME Runs only a single test matching the name. + --only NAME Runs only a single state test matching the name. --chain CHAIN Run only tests from specific chain. General options: @@ -52,4 +52,3 @@ _This project is a part of the Parity Ethereum toolchain._ - [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding. - [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management. - [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator. -- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC. diff --git a/evmbin/benches/mod.rs b/evmbin/benches/mod.rs index d8872b5100..593abcd6b4 100644 --- a/evmbin/benches/mod.rs +++ b/evmbin/benches/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,69 +17,83 @@ //! benchmarking for EVM //! should be started with: //! ```bash -//! multirust run nightly cargo bench +//! cargo bench //! ``` -#![feature(test)] - -extern crate test; +#[macro_use] +extern crate criterion; extern crate ethcore; extern crate evm; -extern crate ethcore_util; -extern crate ethcore_bigint; +extern crate ethereum_types; extern crate rustc_hex; +extern crate vm; -use self::test::{Bencher, black_box}; +use std::sync::Arc; +use criterion::{Criterion, black_box}; -use evm::run_vm; -use ethcore::vm::ActionParams; -use ethcore_bigint::prelude::U256; +use ethereum_types::U256; +use evm::Factory; use rustc_hex::FromHex; - -#[bench] -fn simple_loop_usize(b: &mut Bencher) { - simple_loop(U256::from(::std::usize::MAX), b) +use vm::tests::FakeExt; +use vm::{ActionParams, Ext}; + +criterion_group!( + evmbin, + bench_simple_loop_usize, + bench_simple_loop_u256, + bench_rng_usize, + bench_rng_u256 +); +criterion_main!(evmbin); + +fn bench_simple_loop_usize(c: &mut Criterion) { + simple_loop(U256::from(::std::usize::MAX), c, "simple_loop_usize") } -#[bench] -fn simple_loop_u256(b: &mut Bencher) { - simple_loop(!U256::zero(), b) +fn bench_simple_loop_u256(c: &mut Criterion) { + simple_loop(!U256::zero(), c, "simple_loop_u256") } -fn simple_loop(gas: U256, b: &mut Bencher) { +fn simple_loop(gas: U256, c: &mut Criterion, bench_id: &str) { let code = black_box( "606060405260005b620042408112156019575b6001016007565b600081905550600680602b6000396000f3606060405200".from_hex().unwrap() ); - b.iter(|| { - let mut params = ActionParams::default(); - params.gas = gas; - params.code = Some(code.clone()); + c.bench_function(bench_id, move |b| { + b.iter(|| { + let mut params = ActionParams::default(); + params.gas = gas; + params.code = Some(Arc::new(code.clone())); - run_vm(params) + let mut ext = FakeExt::new(); + let evm = Factory::default().create(params, ext.schedule(), ext.depth()); + let _ = evm.exec(&mut ext); + }) }); } -#[bench] -fn rng_usize(b: &mut Bencher) { - rng(U256::from(::std::usize::MAX), b) +fn bench_rng_usize(c: &mut Criterion) { + rng(U256::from(::std::usize::MAX), c, "rng_usize") } -#[bench] -fn rng_u256(b: &mut Bencher) { - rng(!U256::zero(), b) +fn bench_rng_u256(c: &mut Criterion) { + rng(!U256::zero(), c, "rng_u256") } -fn rng(gas: U256, b: &mut Bencher) { +fn rng(gas: U256, c: &mut Criterion, bench_id: &str) { let code = black_box( "6060604052600360056007600b60005b62004240811215607f5767ffe7649d5eca84179490940267f47ed85c4b9a6379019367f8e5dd9a5c994bba9390930267f91d87e4b8b74e55019267ff97f6f3b29cda529290920267f393ada8dd75c938019167fe8d437c45bb3735830267f47d9a7b5428ffec019150600101600f565b838518831882186000555050505050600680609a6000396000f3606060405200".from_hex().unwrap() ); - b.iter(|| { - let mut params = ActionParams::default(); - params.gas = gas; - params.code = Some(code.clone()); + c.bench_function(bench_id, move |b| { + b.iter(|| { + let mut params = ActionParams::default(); + params.gas = gas; + params.code = Some(Arc::new(code.clone())); - run_vm(params) + let mut ext = FakeExt::new(); + let evm = Factory::default().create(params, ext.schedule(), ext.depth()); + let _ = evm.exec(&mut ext); + }) }); } diff --git a/evmbin/res/create2callPrecompiles.json b/evmbin/res/create2callPrecompiles.json new file mode 100644 index 0000000000..eb7487625d --- /dev/null +++ b/evmbin/res/create2callPrecompiles.json @@ -0,0 +1,140 @@ +{ + "create2callPrecompiles": { + "_info": { + "comment": "CALL precompiles during init code of CREATE2 contract ", + "filledwith": "testeth 1.5.0.dev2-73+commit.1bcd29e5", + "lllcversion": "Version: 0.4.26-develop.2018.9.19+commit.785cbf40.Linux.g++", + "source": "src/GeneralStateTestsFiller/stCreate2/create2callPrecompilesFiller.json", + "sourceHash": "0dcd6d7b5819f61399ecfba9a67b7518c92c426866bd5b599516c184d194e51c" + }, + "env": { + "currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty": "0x20000", + "currentGasLimit": "0xe8d4a51000", + "currentNumber": "0x01", + "currentTimestamp": "0x03e8", + "previousHash": "0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "post": { + "Constantinople": [ + { + "hash": "0x3dfdcd1d19badbbba8b0c953504e8b4685270ee5b86e155350b6ef1042c9ce43", + "indexes": { + "data": 0, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + { + "hash": "0x88803085d3420aec76078e215f67fc5f7b6f297fbe19d85c2236ad685d0fc7fc", + "indexes": { + "data": 1, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + { + "hash": "0x57181dda5c067cb31f084c4118791b40d5028c39071e83e60e7f7403d683527e", + "indexes": { + "data": 2, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + { + "hash": "0xf04c1039893eb6959354c3c16e9fe025d4b9dc3981362f79c56cc427dca0d544", + "indexes": { + "data": 3, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + { + "hash": "0x5d5db3d6c4377b34b74ecf8638f684acb220cc7ce286ae5f000ffa74faf38bae", + "indexes": { + "data": 4, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + { + "hash": "0xf8343b2e05ae120bf25947de840cedf1ca2c1bcda1cdb89d218427d8a84d4798", + "indexes": { + "data": 5, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + { + "hash": "0x305a8a8a7d9da97d14ed2259503d9373d803ea4b7fbf8c360f50b1b30a3d04ed", + "indexes": { + "data": 6, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + }, + { + "hash": "0xde1d3953b508913c6e3e9bd412cd50daf60bb177517e5d1e8ccb0dab193aed03", + "indexes": { + "data": 7, + "gas": 0, + "value": 0 + }, + "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" + } + ] + }, + "pre": { + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x0de0b6b3a7640000", + "code": "", + "nonce": "0x00", + "storage": { + } + }, + "0xaddf5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x00", + "code": "0x600035600052602035602052604035604052606035606052604060c860806000600060066207a120f260005560c85160015560e851600255", + "nonce": "0x00", + "storage": { + } + }, + "0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x00", + "code": "0x60003560005260203560205260403560405260603560605260803560805260a03560a05260c03560c052604061012c60806000600060066207a120f2600055604061019060606080600060076207a120f260015561012c51600a5561014c51600b55610190516014556101b051601555601454600a5414600255601554600b5414600355", + "nonce": "0x00", + "storage": { + } + } + }, + "transaction": { + "data": [ + "0x6000609a80601260003960006000f55000fe7f18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c600052601c6020527f73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f6040527feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549606052602060806080600060006001620493e0f160025560a060020a60805106600055600054321460015500", + "0x6000602380601260003960006000f55000fe64f34578907f6005526020600060256000600060026101f4f160025560005160005500", + "0x6000601a80601260003960006000f55000fe602060006000600060006003610258f160025560005160005500", + "0x6000602380601260003960006000f55000fe64f34578907f6000526020600060256000600060046101f4f160025560005160005500", + "0x6000609580601260003960006000f55000fe6001600052602060205260206040527f03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc6060527f2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc6080527f2f0000000000000000000000000000000000000000000000000000000000000060965260206103e860976000600060055af26001556103e85160025500", + "0x6000602180601260003960006000f55000fe600160005260206000610100600060006006620927c0f160025560005160005500", + "0x600060b680601260003960006000f55000fe7f0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd26000527f16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba6020527f1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2866040527f0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d46060526000600060806000600073addf5374fce5edbc8e2a8697c15331677e6ebf0b6207a120f25000", + "0x600060c580601260003960006000f55000fe7f1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2866000527f0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4602052600060405260006060527f1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc2866080527f0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d460a052600160c0526000600060e06000600073b94f5374fce5edbc8e2a8697c15331677e6ebf0b6207a120f25000" + ], + "gasLimit": [ + "0xe4e1c0" + ], + "gasPrice": "0x01", + "nonce": "0x00", + "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to": "", + "value": [ + "0x01" + ] + } + } +} diff --git a/evmbin/res/teststate.json b/evmbin/res/teststate.json new file mode 100644 index 0000000000..4e774112f9 --- /dev/null +++ b/evmbin/res/teststate.json @@ -0,0 +1,144 @@ +{ + "add11": { + "env": { + "currentCoinbase": "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty": "0x0100", + "currentGasLimit": "0x01c9c380", + "currentNumber": "0x00", + "currentTimestamp": "0x01", + "previousHash": "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "post": { + "EIP150": [ + { + "hash": "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", + "indexes": { "data": 0, "gas": 0, "value": 0 } + }, + { + "hash": "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", + "indexes": { "data": 0, "gas": 0, "value": 1 } + } + ], + "EIP158": [ + { + "hash": "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", + "indexes": { "data": 0, "gas": 0, "value": 0 } + }, + { + "hash": "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", + "indexes": { "data": 0, "gas": 0, "value": 1 } + } + ] + }, + "pre": { + "1000000000000000000000000000000000000000": { + "balance": "0x0de0b6b3a7640000", + "code": "0x6040600060406000600173100000000000000000000000000000000000000162055730f1600055", + "nonce": "0x00", + "storage": { + } + }, + "1000000000000000000000000000000000000001": { + "balance": "0x0de0b6b3a7640000", + "code": "0x604060006040600060027310000000000000000000000000000000000000026203d090f1600155", + "nonce": "0x00", + "storage": { + } + }, + "1000000000000000000000000000000000000002": { + "balance": "0x00", + "code": "0x600160025533600455346007553060e6553260e8553660ec553860ee553a60f055", + "nonce": "0x00", + "storage": { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x0de0b6b3a7640000", + "code": "0x", + "nonce": "0x00", + "storage": { + } + } + }, + "transaction": { + "data": [ "" ], + "gasLimit": [ "285000", "100000", "6000" ], + "gasPrice": "0x01", + "nonce": "0x00", + "secretKey": "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to": "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value": [ "10", "0" ] + } + }, + "add12": { + "env": { + "currentCoinbase": "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty": "0x0100", + "currentGasLimit": "0x01c9c380", + "currentNumber": "0x00", + "currentTimestamp": "0x01", + "previousHash": "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "post": { + "EIP150": [ + { + "hash": "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", + "indexes": { "data": 0, "gas": 0, "value": 0 } + }, + { + "hash": "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", + "indexes": { "data": 0, "gas": 0, "value": 1 } + } + ], + "EIP158": [ + { + "hash": "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", + "indexes": { "data": 0, "gas": 0, "value": 0 } + }, + { + "hash": "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", + "indexes": { "data": 0, "gas": 0, "value": 1 } + } + ] + }, + "pre": { + "1000000000000000000000000000000000000000": { + "balance": "0x0de0b6b3a7640000", + "code": "0x6040600060406000600173100000000000000000000000000000000000000162055730f1600055", + "nonce": "0x00", + "storage": { + } + }, + "1000000000000000000000000000000000000001": { + "balance": "0x0de0b6b3a7640000", + "code": "0x604060006040600060027310000000000000000000000000000000000000026203d090f1600155", + "nonce": "0x00", + "storage": { + } + }, + "1000000000000000000000000000000000000002": { + "balance": "0x00", + "code": "0x600160025533600455346007553060e6553260e8553660ec553860ee553a60f055", + "nonce": "0x00", + "storage": { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x0de0b6b3a7640000", + "code": "0x", + "nonce": "0x00", + "storage": { + } + } + }, + "transaction": { + "data": [ "" ], + "gasLimit": [ "285000", "100000", "6000" ], + "gasPrice": "0x01", + "nonce": "0x00", + "secretKey": "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to": "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value": [ "10", "0" ] + } + } +} diff --git a/evmbin/src/display/json.rs b/evmbin/src/display/json.rs index eec5131c7b..239874d2c9 100644 --- a/evmbin/src/display/json.rs +++ b/evmbin/src/display/json.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,17 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! JSON VM output. +//! Log EVM instruction output data traces from a JSON formatting informant. use std::collections::HashMap; use std::mem; -use ethereum_types::{U256, H256}; -use bytes::ToPretty; -use ethcore::trace; +use ethereum_types::{U256, H256, BigEndianHash}; +use parity_bytes::ToPretty; +use serde::Serialize; +use trace; -use display; -use info as vm; +use crate::{ + display, + info as vm, +}; /// JSON formatting informant. #[derive(Default)] @@ -47,6 +50,42 @@ pub struct Informant { unmatched: bool, } +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct TraceData<'a> { + pc: usize, + op: u8, + op_name: &'a str, + gas: &'a str, + gas_cost: &'a str, + memory: &'a str, + stack: &'a [U256], + storage: &'a HashMap, + depth: usize, +} + +#[derive(Serialize, Debug)] +pub struct MessageInitial<'a> { + action: &'a str, + test: &'a str, +} + +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct MessageSuccess<'a> { + output: &'a str, + gas_used: &'a str, + time: &'a u64, +} + +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct MessageFailure<'a> { + error: &'a str, + gas_used: &'a str, + time: &'a u64, +} + impl Informant { fn with_informant_in_depth(informant: &mut Informant, depth: usize, f: F) { if depth == 0 { @@ -59,17 +98,21 @@ impl Informant { fn informant_trace(informant: &Informant, gas_used: U256) -> String { let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info()); - json!({ - "pc": informant.pc, - "op": informant.instruction, - "opName": info.map(|i| i.name).unwrap_or(""), - "gas": format!("{:#x}", gas_used.saturating_add(informant.gas_cost)), - "gasCost": format!("{:#x}", informant.gas_cost), - "memory": format!("0x{}", informant.memory.to_hex()), - "stack": informant.stack, - "storage": informant.storage, - "depth": informant.depth, - }).to_string() + let trace_data = + TraceData { + pc: informant.pc, + op: informant.instruction, + op_name: info.map(|i| i.name).unwrap_or(""), + gas: &format!("{:#x}", gas_used.saturating_add(informant.gas_cost)), + gas_cost: &format!("{:#x}", informant.gas_cost), + memory: &format!("0x{}", informant.memory.to_hex()), + stack: &informant.stack, + storage: &informant.storage, + depth: informant.depth, + } + ; + + serde_json::to_string(&trace_data).expect("Serialization cannot fail; qed") } } @@ -77,7 +120,15 @@ impl vm::Informant for Informant { type Sink = (); fn before_test(&mut self, name: &str, action: &str) { - println!("{}", json!({"action": action, "test": name})); + let message_init = + MessageInitial { + action, + test: &name, + } + ; + + let s = serde_json::to_string(&message_init).expect("Serialization cannot fail; qed"); + println!("{}", s); } fn set_gas(&mut self, gas: U256) { @@ -93,26 +144,32 @@ impl vm::Informant for Informant { println!("{}", trace); } - let success_msg = json!({ - "output": format!("0x{}", success.output.to_hex()), - "gasUsed": format!("{:#x}", success.gas_used), - "time": display::as_micros(&success.time), - }); + let message_success = + MessageSuccess { + output: &format!("0x{}", success.output.to_hex()), + gas_used: &format!("{:#x}", success.gas_used), + time: &display::as_micros(&success.time), + } + ; - println!("{}", success_msg) + let s = serde_json::to_string(&message_success).expect("Serialization cannot fail; qed"); + println!("{}", s); }, Err(failure) => { for trace in failure.traces.unwrap_or_else(Vec::new) { println!("{}", trace); } - let failure_msg = json!({ - "error": &failure.error.to_string(), - "gasUsed": format!("{:#x}", failure.gas_used), - "time": display::as_micros(&failure.time), - }); + let message_failure = + MessageFailure { + error: &failure.error.to_string(), + gas_used: &format!("{:#x}", failure.gas_used), + time: &display::as_micros(&failure.time), + } + ; - println!("{}", failure_msg) + let s = serde_json::to_string(&message_failure).expect("Serialization cannot fail; qed"); + println!("{}", s); }, } } @@ -168,7 +225,7 @@ impl trace::VMTracer for Informant { } if let Some((pos, val)) = store_diff { - informant.storage.insert(pos.into(), val.into()); + informant.storage.insert(BigEndianHash::from_uint(&pos), BigEndianHash::from_uint(&val)); } if !informant.subtraces.is_empty() { @@ -219,10 +276,12 @@ impl trace::VMTracer for Informant { #[cfg(test)] mod tests { - use super::*; - use info::tests::run_test; + use serde::{Deserialize, Serialize}; use serde_json; + use super::*; + use crate::info::tests::run_test; + #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename_all = "camelCase")] struct TestTrace { diff --git a/evmbin/src/display/mod.rs b/evmbin/src/display/mod.rs index 32b45c5692..846c94ef25 100644 --- a/evmbin/src/display/mod.rs +++ b/evmbin/src/display/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! VM Output display utils. +//! EVM output display utils. use std::time::Duration; @@ -24,7 +24,7 @@ pub mod simple; /// Formats duration into human readable format. pub fn format_time(time: &Duration) -> String { - format!("{}.{:.9}s", time.as_secs(), time.subsec_nanos()) + format!("{}.{:09}s", time.as_secs(), time.subsec_nanos()) } /// Formats the time as microseconds. diff --git a/evmbin/src/display/simple.rs b/evmbin/src/display/simple.rs index 58d4a70458..2e3863e2d0 100644 --- a/evmbin/src/display/simple.rs +++ b/evmbin/src/display/simple.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,13 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Simple VM output. +//! Log EVM instruction output data traces from a simple formatting informant. -use ethcore::trace; -use bytes::ToPretty; +use trace; +use parity_bytes::ToPretty; -use display; -use info as vm; +use crate::{ + display, + info as vm, +}; /// Simple formatting informant. #[derive(Default)] diff --git a/evmbin/src/display/std_json.rs b/evmbin/src/display/std_json.rs index a0071b174f..96ccb83870 100644 --- a/evmbin/src/display/std_json.rs +++ b/evmbin/src/display/std_json.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,17 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Standardized JSON VM output. +//! Log EVM instruction output data traces from a standardized JSON formatting informant. use std::collections::HashMap; use std::io; -use ethereum_types::{H256, U256}; -use bytes::ToPretty; -use ethcore::{trace, pod_state}; +use ethereum_types::{H256, U256, BigEndianHash}; +use parity_bytes::ToPretty; +use pod::PodState; +use serde::Serialize; +use trace; -use display; -use info as vm; +use crate::{ + display, + info as vm, +}; pub trait Writer: io::Write + Send + Sized { fn clone(&self) -> Self; @@ -64,6 +68,52 @@ pub struct Informant { out_sink: Out, } +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct TraceData<'a> { + pc: usize, + op: u8, + op_name: &'a str, + gas: &'a str, + stack: &'a [U256], + storage: &'a HashMap, + depth: usize, +} + +#[derive(Serialize, Debug)] +pub struct MessageInitial<'a> { + action: &'a str, + test: &'a str, +} + +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct MessageSuccess<'a> { + output: &'a str, + gas_used: &'a str, + time: &'a u64, +} + +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct MessageFailure<'a> { + error: &'a str, + gas_used: &'a str, + time: &'a u64, +} + +#[derive(Serialize, Debug)] +pub struct DumpData<'a> { + root: &'a H256, + accounts: &'a PodState, +} + +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct TraceDataStateRoot<'a> { + state_root: &'a H256, +} + impl Default for Informant { fn default() -> Self { Self::new(io::stderr(), io::stdout()) @@ -71,14 +121,14 @@ impl Default for Informant { } impl Informant { - /// std json informant using out only. + /// Standardized JSON formatting informant using stdout only. pub fn out_only() -> Self { Self::new(io::stdout(), io::stdout()) } } impl Informant { - /// std json informant using err only. + /// Standardized JSON formatting informant using stderr only. pub fn err_only() -> Self { Self::new(io::stderr(), io::stderr()) } @@ -95,7 +145,8 @@ impl Informant { storage: Default::default(), subinfos: Default::default(), subdepth: 0, - trace_sink, out_sink + trace_sink, + out_sink, } } @@ -107,13 +158,17 @@ impl Informant { } } - fn dump_state_into(trace_sink: &mut Trace, root: H256, end_state: &Option) { - if let Some(ref end_state) = end_state { - let dump_data = json!({ - "root": root, - "accounts": end_state, - }); - writeln!(trace_sink, "{}", dump_data).expect("The sink must be writeable."); + fn dump_state_into(trace_sink: &mut Trace, root: H256, end_state: &Option) { + if let Some(ref end_state) = end_state { + let dump_data = + DumpData { + root: &root, + accounts: end_state, + } + ; + + let s = serde_json::to_string(&dump_data).expect("Serialization cannot fail; qed"); + writeln!(trace_sink, "{}", s).expect("The sink must be writeable."); } } @@ -124,12 +179,15 @@ impl vm::Informant for Informant { type Sink = (Trace, Out); fn before_test(&mut self, name: &str, action: &str) { - let out_data = json!({ - "action": action, - "test": name, - }); + let message_init = + MessageInitial { + action, + test: &name, + } + ; - writeln!(&mut self.out_sink, "{}", out_data).expect("The sink must be writeable."); + let s = serde_json::to_string(&message_init).expect("Serialization cannot fail; qed"); + writeln!(&mut self.out_sink, "{}", s).expect("The sink must be writeable."); } fn set_gas(&mut self, _gas: U256) {} @@ -137,34 +195,46 @@ impl vm::Informant for Informant { fn clone_sink(&self) -> Self::Sink { (self.trace_sink.clone(), self.out_sink.clone()) } + fn finish(result: vm::RunResult<::Output>, (ref mut trace_sink, ref mut out_sink): &mut Self::Sink) { match result { Ok(success) => { - let trace_data = json!({"stateRoot": success.state_root}); - writeln!(trace_sink, "{}", trace_data) - .expect("The sink must be writeable."); + let state_root_data = + TraceDataStateRoot { + state_root: &success.state_root, + } + ; + + let s = serde_json::to_string(&state_root_data).expect("Serialization cannot fail; qed"); + writeln!(trace_sink, "{}", s).expect("The sink must be writeable."); Self::dump_state_into(trace_sink, success.state_root, &success.end_state); - let out_data = json!({ - "output": format!("0x{}", success.output.to_hex()), - "gasUsed": format!("{:#x}", success.gas_used), - "time": display::as_micros(&success.time), - }); + let message_success = + MessageSuccess { + output: &format!("0x{}", success.output.to_hex()), + gas_used: &format!("{:#x}", success.gas_used), + time: &display::as_micros(&success.time), + } + ; - writeln!(out_sink, "{}", out_data).expect("The sink must be writeable."); + let s = serde_json::to_string(&message_success).expect("Serialization cannot fail; qed"); + writeln!(out_sink, "{}", s).expect("The sink must be writeable."); }, Err(failure) => { - let out_data = json!({ - "error": &failure.error.to_string(), - "gasUsed": format!("{:#x}", failure.gas_used), - "time": display::as_micros(&failure.time), - }); + let message_failure = + MessageFailure { + error: &failure.error.to_string(), + gas_used: &format!("{:#x}", failure.gas_used), + time: &display::as_micros(&failure.time), + } + ; Self::dump_state_into(trace_sink, failure.state_root, &failure.end_state); - writeln!(out_sink, "{}", out_data).expect("The sink must be writeable."); + let s = serde_json::to_string(&message_failure).expect("Serialization cannot fail; qed"); + writeln!(out_sink, "{}", s).expect("The sink must be writeable."); }, } } @@ -178,17 +248,22 @@ impl trace::VMTracer for Informant { Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info()); informant.instruction = instruction; - let trace_data = json!({ - "pc": pc, - "op": instruction, - "opName": info.map(|i| i.name).unwrap_or(""), - "gas": format!("{:#x}", current_gas), - "stack": informant.stack, - "storage": informant.storage, - "depth": informant.depth, - }); - - writeln!(&mut informant.trace_sink, "{}", trace_data).expect("The sink must be writeable."); + + let trace_data = + TraceData { + pc: pc, + op: instruction, + op_name: info.map(|i| i.name).unwrap_or(""), + gas: &format!("{:#x}", current_gas), + stack: &informant.stack, + storage: &informant.storage, + depth: informant.depth, + } + ; + + let s = serde_json::to_string(&trace_data).expect("Serialization cannot fail; qed"); + + writeln!(&mut informant.trace_sink, "{}", s).expect("The sink must be writeable."); }); true } @@ -197,7 +272,7 @@ impl trace::VMTracer for Informant { let subdepth = self.subdepth; Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| { if let Some((pos, val)) = store_written { - informant.storage.insert(pos.into(), val.into()); + informant.storage.insert(BigEndianHash::from_uint(&pos), BigEndianHash::from_uint(&val)); } }); } @@ -241,7 +316,7 @@ impl trace::VMTracer for Informant { pub mod tests { use std::sync::{Arc, Mutex}; use super::*; - use info::tests::run_test; + use crate::info::tests::run_test; #[derive(Debug, Clone, Default)] pub struct TestWriter(pub Arc>>); @@ -261,78 +336,79 @@ pub mod tests { } } - pub fn informant() -> (Informant, Arc>>) { + pub fn informant() -> (Informant, TestWriter, TestWriter) { let trace_writer: TestWriter = Default::default(); let out_writer: TestWriter = Default::default(); - let res = trace_writer.0.clone(); - (Informant::new(trace_writer, out_writer), res) + let trace_copy = Clone::clone(&trace_writer); + let out_copy = Clone::clone(&out_writer); + (Informant::new(trace_writer, out_writer), trace_copy, out_copy) } #[test] fn should_trace_failure() { - let (inf, res) = informant(); + let (inf, res, _) = informant(); run_test( inf, move |_, expected| { - let bytes = res.lock().unwrap(); + let bytes = res.0.lock().unwrap(); assert_eq!(expected, &String::from_utf8_lossy(&**bytes)) }, "60F8d6", 0xffff, - r#"{"depth":1,"gas":"0xffff","op":96,"opName":"PUSH1","pc":0,"stack":[],"storage":{}} -{"depth":1,"gas":"0xfffc","op":214,"opName":"","pc":2,"stack":["0xf8"],"storage":{}} + r#"{"pc":0,"op":96,"opName":"PUSH1","gas":"0xffff","stack":[],"storage":{},"depth":1} +{"pc":2,"op":214,"opName":"","gas":"0xfffc","stack":["0xf8"],"storage":{},"depth":1} "#, ); - let (inf, res) = informant(); + let (inf, res, _) = informant(); run_test( inf, move |_, expected| { - let bytes = res.lock().unwrap(); + let bytes = res.0.lock().unwrap(); assert_eq!(expected, &String::from_utf8_lossy(&**bytes)) }, "F8d6", 0xffff, - r#"{"depth":1,"gas":"0xffff","op":248,"opName":"","pc":0,"stack":[],"storage":{}} + r#"{"pc":0,"op":248,"opName":"","gas":"0xffff","stack":[],"storage":{},"depth":1} "#, ); } #[test] fn should_trace_create_correctly() { - let (informant, res) = informant(); + let (informant, res, _) = informant(); run_test( informant, move |_, expected| { - let bytes = res.lock().unwrap(); + let bytes = res.0.lock().unwrap(); assert_eq!(expected, &String::from_utf8_lossy(&**bytes)) }, "32343434345830f138343438323439f0", 0xffff, - r#"{"depth":1,"gas":"0xffff","op":50,"opName":"ORIGIN","pc":0,"stack":[],"storage":{}} -{"depth":1,"gas":"0xfffd","op":52,"opName":"CALLVALUE","pc":1,"stack":["0x0"],"storage":{}} -{"depth":1,"gas":"0xfffb","op":52,"opName":"CALLVALUE","pc":2,"stack":["0x0","0x0"],"storage":{}} -{"depth":1,"gas":"0xfff9","op":52,"opName":"CALLVALUE","pc":3,"stack":["0x0","0x0","0x0"],"storage":{}} -{"depth":1,"gas":"0xfff7","op":52,"opName":"CALLVALUE","pc":4,"stack":["0x0","0x0","0x0","0x0"],"storage":{}} -{"depth":1,"gas":"0xfff5","op":88,"opName":"PC","pc":5,"stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{}} -{"depth":1,"gas":"0xfff3","op":48,"opName":"ADDRESS","pc":6,"stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{}} -{"depth":1,"gas":"0xfff1","op":241,"opName":"CALL","pc":7,"stack":["0x0","0x0","0x0","0x0","0x0","0x5","0x0"],"storage":{}} -{"depth":1,"gas":"0x9e21","op":56,"opName":"CODESIZE","pc":8,"stack":["0x1"],"storage":{}} -{"depth":1,"gas":"0x9e1f","op":52,"opName":"CALLVALUE","pc":9,"stack":["0x1","0x10"],"storage":{}} -{"depth":1,"gas":"0x9e1d","op":52,"opName":"CALLVALUE","pc":10,"stack":["0x1","0x10","0x0"],"storage":{}} -{"depth":1,"gas":"0x9e1b","op":56,"opName":"CODESIZE","pc":11,"stack":["0x1","0x10","0x0","0x0"],"storage":{}} -{"depth":1,"gas":"0x9e19","op":50,"opName":"ORIGIN","pc":12,"stack":["0x1","0x10","0x0","0x0","0x10"],"storage":{}} -{"depth":1,"gas":"0x9e17","op":52,"opName":"CALLVALUE","pc":13,"stack":["0x1","0x10","0x0","0x0","0x10","0x0"],"storage":{}} -{"depth":1,"gas":"0x9e15","op":57,"opName":"CODECOPY","pc":14,"stack":["0x1","0x10","0x0","0x0","0x10","0x0","0x0"],"storage":{}} -{"depth":1,"gas":"0x9e0c","op":240,"opName":"CREATE","pc":15,"stack":["0x1","0x10","0x0","0x0"],"storage":{}} -{"depth":2,"gas":"0x210c","op":50,"opName":"ORIGIN","pc":0,"stack":[],"storage":{}} -{"depth":2,"gas":"0x210a","op":52,"opName":"CALLVALUE","pc":1,"stack":["0x0"],"storage":{}} -{"depth":2,"gas":"0x2108","op":52,"opName":"CALLVALUE","pc":2,"stack":["0x0","0x0"],"storage":{}} -{"depth":2,"gas":"0x2106","op":52,"opName":"CALLVALUE","pc":3,"stack":["0x0","0x0","0x0"],"storage":{}} -{"depth":2,"gas":"0x2104","op":52,"opName":"CALLVALUE","pc":4,"stack":["0x0","0x0","0x0","0x0"],"storage":{}} -{"depth":2,"gas":"0x2102","op":88,"opName":"PC","pc":5,"stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{}} -{"depth":2,"gas":"0x2100","op":48,"opName":"ADDRESS","pc":6,"stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{}} -{"depth":2,"gas":"0x20fe","op":241,"opName":"CALL","pc":7,"stack":["0x0","0x0","0x0","0x0","0x0","0x5","0xbd770416a3345f91e4b34576cb804a576fa48eb1"],"storage":{}} + r#"{"pc":0,"op":50,"opName":"ORIGIN","gas":"0xffff","stack":[],"storage":{},"depth":1} +{"pc":1,"op":52,"opName":"CALLVALUE","gas":"0xfffd","stack":["0x0"],"storage":{},"depth":1} +{"pc":2,"op":52,"opName":"CALLVALUE","gas":"0xfffb","stack":["0x0","0x0"],"storage":{},"depth":1} +{"pc":3,"op":52,"opName":"CALLVALUE","gas":"0xfff9","stack":["0x0","0x0","0x0"],"storage":{},"depth":1} +{"pc":4,"op":52,"opName":"CALLVALUE","gas":"0xfff7","stack":["0x0","0x0","0x0","0x0"],"storage":{},"depth":1} +{"pc":5,"op":88,"opName":"PC","gas":"0xfff5","stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{},"depth":1} +{"pc":6,"op":48,"opName":"ADDRESS","gas":"0xfff3","stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{},"depth":1} +{"pc":7,"op":241,"opName":"CALL","gas":"0xfff1","stack":["0x0","0x0","0x0","0x0","0x0","0x5","0x0"],"storage":{},"depth":1} +{"pc":8,"op":56,"opName":"CODESIZE","gas":"0x9e21","stack":["0x1"],"storage":{},"depth":1} +{"pc":9,"op":52,"opName":"CALLVALUE","gas":"0x9e1f","stack":["0x1","0x10"],"storage":{},"depth":1} +{"pc":10,"op":52,"opName":"CALLVALUE","gas":"0x9e1d","stack":["0x1","0x10","0x0"],"storage":{},"depth":1} +{"pc":11,"op":56,"opName":"CODESIZE","gas":"0x9e1b","stack":["0x1","0x10","0x0","0x0"],"storage":{},"depth":1} +{"pc":12,"op":50,"opName":"ORIGIN","gas":"0x9e19","stack":["0x1","0x10","0x0","0x0","0x10"],"storage":{},"depth":1} +{"pc":13,"op":52,"opName":"CALLVALUE","gas":"0x9e17","stack":["0x1","0x10","0x0","0x0","0x10","0x0"],"storage":{},"depth":1} +{"pc":14,"op":57,"opName":"CODECOPY","gas":"0x9e15","stack":["0x1","0x10","0x0","0x0","0x10","0x0","0x0"],"storage":{},"depth":1} +{"pc":15,"op":240,"opName":"CREATE","gas":"0x9e0c","stack":["0x1","0x10","0x0","0x0"],"storage":{},"depth":1} +{"pc":0,"op":50,"opName":"ORIGIN","gas":"0x210c","stack":[],"storage":{},"depth":2} +{"pc":1,"op":52,"opName":"CALLVALUE","gas":"0x210a","stack":["0x0"],"storage":{},"depth":2} +{"pc":2,"op":52,"opName":"CALLVALUE","gas":"0x2108","stack":["0x0","0x0"],"storage":{},"depth":2} +{"pc":3,"op":52,"opName":"CALLVALUE","gas":"0x2106","stack":["0x0","0x0","0x0"],"storage":{},"depth":2} +{"pc":4,"op":52,"opName":"CALLVALUE","gas":"0x2104","stack":["0x0","0x0","0x0","0x0"],"storage":{},"depth":2} +{"pc":5,"op":88,"opName":"PC","gas":"0x2102","stack":["0x0","0x0","0x0","0x0","0x0"],"storage":{},"depth":2} +{"pc":6,"op":48,"opName":"ADDRESS","gas":"0x2100","stack":["0x0","0x0","0x0","0x0","0x0","0x5"],"storage":{},"depth":2} +{"pc":7,"op":241,"opName":"CALL","gas":"0x20fe","stack":["0x0","0x0","0x0","0x0","0x0","0x5","0xbd770416a3345f91e4b34576cb804a576fa48eb1"],"storage":{},"depth":2} "#, ) } diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index 74ea3175a6..1509c8cefe 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,17 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! VM runner. +//! EVM runner. use std::time::{Instant, Duration}; + +use common_types::transaction; +use ethcore::test_helpers::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess, TrieSpec}; use ethereum_types::{H256, U256}; -use ethcore::client::{self, EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; -use ethcore::{state, state_db, trace, spec, pod_state, TrieSpec}; use ethjson; -use types::transaction; +use pod::PodState; +use spec; +use trace; use vm::ActionParams; -/// VM execution informant +/// EVM execution informant. pub trait Informant: trace::VMTracer { /// Sink to use with finish type Sink; @@ -35,44 +38,44 @@ pub trait Informant: trace::VMTracer { /// Clone sink. fn clone_sink(&self) -> Self::Sink; /// Display final result. - fn finish(result: RunResult, &mut Self::Sink); + fn finish(result: RunResult, _: &mut Self::Sink); } -/// Execution finished correctly +/// Execution finished correctly. #[derive(Debug)] pub struct Success { - /// State root + /// State root. pub state_root: H256, - /// Used gas + /// Used gas. pub gas_used: U256, - /// Output as bytes + /// Output as bytes. pub output: Vec, - /// Time Taken + /// Time taken. pub time: Duration, - /// Traces + /// Traces. pub traces: Option, /// Optional end state dump - pub end_state: Option, + pub end_state: Option, } -/// Execution failed +/// Execution failed. #[derive(Debug)] pub struct Failure { - /// State root + /// State root. pub state_root: H256, - /// Used gas + /// Used gas. pub gas_used: U256, - /// Internal error + /// Internal error. pub error: EvmTestError, - /// Duration + /// Duration. pub time: Duration, - /// Traces + /// Traces. pub traces: Option, /// Optional end state dump - pub end_state: Option, + pub end_state: Option, } -/// EVM Execution result +/// EVM execution result. pub type RunResult = Result, Failure>; /// Execute given `ActionParams` and return the result. @@ -86,49 +89,76 @@ pub fn run_action( // if the code is not overwritten from CLI, use code from spec file. if params.code.is_none() { - if let Some(acc) = spec.genesis_state().get().get(¶ms.code_address) { + if let Some(acc) = spec.genesis_state.get().get(¶ms.code_address) { params.code = acc.code.clone().map(::std::sync::Arc::new); params.code_hash = None; } } - run(spec, trie_spec, params.gas, spec.genesis_state(), |mut client| { + run(spec, trie_spec, params.gas, &spec.genesis_state, |mut client| { let result = match client.call(params, &mut trace::NoopTracer, &mut informant) { Ok(r) => (Ok(r.return_data.to_vec()), Some(r.gas_left)), Err(err) => (Err(err), None), }; - (result.0, 0.into(), None, result.1, informant.drain()) + (result.0, H256::from_low_u64_be(0), None, result.1, informant.drain()) }) } -/// Execute given Transaction and verify resulting state root. +/// Input data to run transaction. +#[derive(Debug)] +pub struct TxInput<'a, T> { + /// State test name associated with the transaction. + pub state_test_name: &'a str, + /// Transaction index from list of transactions within a state root hash corresponding to a chain. + pub tx_index: usize, + /// Fork specification (i.e. Constantinople, EIP150, EIP158, etc). + pub fork_spec_name: &'a ethjson::spec::ForkSpec, + /// State of all accounts in the system that is a binary tree mapping of each account address to account data + /// that is expressed as Plain Old Data containing the account balance, account nonce, account code in bytes, + /// and the account storage binary tree map. + pub pre_state: &'a PodState, + /// State root hash associated with the transaction. + pub post_root: H256, + /// Client environment information associated with the transaction's chain specification. + pub env_info: &'a vm::EnvInfo, + /// Signed transaction accompanied by a signature that may be unverified and a successfully recovered + /// sender address. The unverified transaction contains a recoverable ECDSA signature that has been encoded + /// as RSV components and includes replay protection for the specified chain. Verification of the signed transaction + /// with a valid secret of an account's keypair and a specific chain may be used to recover the sender's public key + /// and their associated address by applying the Keccak-256 hash function. + pub transaction: transaction::SignedTransaction, + /// JSON formatting informant. + pub informant: T, + /// Trie specification (i.e. Generic trie, Secure trie, Secure with fat database). + pub trie_spec: TrieSpec, +} + +/// Execute given transaction and verify resulting state root. +/// Returns true if the transaction executes successfully. pub fn run_transaction( - name: &str, - idx: usize, - spec: ðjson::spec::ForkSpec, - pre_state: &pod_state::PodState, - post_root: H256, - env_info: &client::EnvInfo, - transaction: transaction::SignedTransaction, - mut informant: T, - trie_spec: TrieSpec, -) { - let spec_name = format!("{:?}", spec).to_lowercase(); - let spec = match EvmTestClient::spec_from_json(spec) { + tx_input: TxInput +) -> bool { + let TxInput { + state_test_name, tx_index, fork_spec_name, pre_state, post_root, env_info, transaction, mut informant, trie_spec, .. + } = tx_input; + let fork_spec_name_formatted = format!("{:?}", fork_spec_name).to_lowercase(); + let fork_spec = match EvmTestClient::fork_spec_from_json(&fork_spec_name) { Some(spec) => { - informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "starting"); + informant.before_test( + &format!("{}:{}:{}", &state_test_name, &fork_spec_name_formatted, tx_index), "starting"); spec }, None => { - informant.before_test(&format!("{}:{}:{}", name, spec_name, idx), "skipping because of missing spec"); - return; + informant.before_test(&format!("{}:{}:{}", + &state_test_name, fork_spec_name_formatted, &tx_index), "skipping because of missing fork specification"); + return false; }, }; informant.set_gas(env_info.gas_limit); let mut sink = informant.clone_sink(); - let result = run(&spec, trie_spec, transaction.gas, pre_state, |mut client| { - let result = client.transact(env_info, transaction, trace::NoopTracer, informant); + let result = run(&fork_spec, trie_spec, transaction.gas, &pre_state, |mut client| { + let result = client.transact(&env_info, transaction, trace::NoopTracer, informant); match result { Ok(TransactSuccess { state_root, gas_left, output, vm_trace, end_state, .. }) => { if state_root != post_root { @@ -149,22 +179,20 @@ pub fn run_transaction( } }); - T::finish(result, &mut sink) -} - -fn dump_state(state: &state::State) -> Option { - state.to_pod_full().ok() + let ok = result.is_ok(); + T::finish(result, &mut sink); + ok } -/// Execute VM with given `ActionParams` +/// Execute EVM with given `ActionParams`. pub fn run<'a, F, X>( spec: &'a spec::Spec, trie_spec: TrieSpec, initial_gas: U256, - pre_state: &'a pod_state::PodState, + pre_state: &'a PodState, run: F, ) -> RunResult where - F: FnOnce(EvmTestClient) -> (Result, EvmTestError>, H256, Option, Option, Option), + F: FnOnce(EvmTestClient) -> (Result, EvmTestError>, H256, Option, Option, Option), { let do_dump = trie_spec == TrieSpec::Fat; @@ -174,12 +202,12 @@ pub fn run<'a, F, X>( error, time: Duration::from_secs(0), traces: None, - state_root: H256::default(), + state_root: H256::zero(), end_state: None, })?; if do_dump { - test_client.set_dump_state_fn(dump_state); + test_client.set_dump_state(); } let start = Instant::now(); @@ -212,6 +240,8 @@ pub mod tests { use rustc_hex::FromHex; use super::*; use tempdir::TempDir; + use ethereum_types::Address; + use spec::{self, Spec}; pub fn run_test( informant: I, @@ -229,7 +259,7 @@ pub mod tests { params.gas = gas.into(); let tempdir = TempDir::new("").unwrap(); - let spec = ::ethcore::ethereum::new_foundation(&tempdir.path()); + let spec = spec::new_foundation(&tempdir.path()); let result = run_action(&spec, params, informant, TrieSpec::Secure); match result { Ok(Success { traces, .. }) => { @@ -243,28 +273,29 @@ pub mod tests { #[test] fn should_call_account_from_spec() { - use display::std_json::tests::informant; + use crate::display::std_json::tests::informant; - let (inf, res) = informant(); + let (inf, res, _) = informant(); let mut params = ActionParams::default(); - params.code_address = 0x20.into(); + params.code_address = Address::from_low_u64_be(0x20); params.gas = 0xffff.into(); - let spec = ::ethcore::ethereum::load(None, include_bytes!("../res/testchain.json")); + let tempdir = TempDir::new("").unwrap(); + let spec = Spec::load(&tempdir.path(), include_bytes!("../res/testchain.json") as &[u8]).unwrap(); let _result = run_action(&spec, params, inf, TrieSpec::Secure); assert_eq!( - &String::from_utf8_lossy(&**res.lock().unwrap()), -r#"{"depth":1,"gas":"0xffff","op":98,"opName":"PUSH3","pc":0,"stack":[],"storage":{}} -{"depth":1,"gas":"0xfffc","op":96,"opName":"PUSH1","pc":4,"stack":["0xaaaaaa"],"storage":{}} -{"depth":1,"gas":"0xfff9","op":96,"opName":"PUSH1","pc":6,"stack":["0xaaaaaa","0xaa"],"storage":{}} -{"depth":1,"gas":"0xfff6","op":80,"opName":"POP","pc":8,"stack":["0xaaaaaa","0xaa","0xaa"],"storage":{}} -{"depth":1,"gas":"0xfff4","op":96,"opName":"PUSH1","pc":9,"stack":["0xaaaaaa","0xaa"],"storage":{}} -{"depth":1,"gas":"0xfff1","op":96,"opName":"PUSH1","pc":11,"stack":["0xaaaaaa","0xaa","0xaa"],"storage":{}} -{"depth":1,"gas":"0xffee","op":96,"opName":"PUSH1","pc":13,"stack":["0xaaaaaa","0xaa","0xaa","0xaa"],"storage":{}} -{"depth":1,"gas":"0xffeb","op":96,"opName":"PUSH1","pc":15,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}} -{"depth":1,"gas":"0xffe8","op":96,"opName":"PUSH1","pc":17,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}} -{"depth":1,"gas":"0xffe5","op":96,"opName":"PUSH1","pc":19,"stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{}} + &String::from_utf8_lossy(&**res.0.lock().unwrap()), +r#"{"pc":0,"op":98,"opName":"PUSH3","gas":"0xffff","stack":[],"storage":{},"depth":1} +{"pc":4,"op":96,"opName":"PUSH1","gas":"0xfffc","stack":["0xaaaaaa"],"storage":{},"depth":1} +{"pc":6,"op":96,"opName":"PUSH1","gas":"0xfff9","stack":["0xaaaaaa","0xaa"],"storage":{},"depth":1} +{"pc":8,"op":80,"opName":"POP","gas":"0xfff6","stack":["0xaaaaaa","0xaa","0xaa"],"storage":{},"depth":1} +{"pc":9,"op":96,"opName":"PUSH1","gas":"0xfff4","stack":["0xaaaaaa","0xaa"],"storage":{},"depth":1} +{"pc":11,"op":96,"opName":"PUSH1","gas":"0xfff1","stack":["0xaaaaaa","0xaa","0xaa"],"storage":{},"depth":1} +{"pc":13,"op":96,"opName":"PUSH1","gas":"0xffee","stack":["0xaaaaaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1} +{"pc":15,"op":96,"opName":"PUSH1","gas":"0xffeb","stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1} +{"pc":17,"op":96,"opName":"PUSH1","gas":"0xffe8","stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1} +{"pc":19,"op":96,"opName":"PUSH1","gas":"0xffe5","stack":["0xaaaaaa","0xaa","0xaa","0xaa","0xaa","0xaa","0xaa"],"storage":{},"depth":1} "#); } } diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index 48c1e85817..4e6f637d4e 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,65 +14,61 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Parity EVM interpreter binary. +//! Parity EVM Interpreter Binary. +//! +//! ## Overview +//! +//! The Parity EVM interpreter binary is a tool in the Parity +//! Ethereum toolchain. It is an EVM implementation for Parity Ethereum that +//! is used to run a standalone version of the EVM interpreter. +//! +//! ## Usage +//! +//! The evmbin tool is not distributed with regular Parity Ethereum releases +//! so you need to build it from source and run it like so: +//! +//! ```bash +//! cargo build -p evmbin --release +//! ./target/release/parity-evm --help +//! ``` #![warn(missing_docs)] -extern crate common_types as types; -extern crate ethcore; -extern crate ethjson; -extern crate rustc_hex; -extern crate serde; -#[macro_use] -extern crate serde_derive; -#[macro_use] -extern crate serde_json; -extern crate docopt; -extern crate parity_bytes as bytes; -extern crate ethereum_types; -extern crate vm; -extern crate evm; -extern crate panic_hook; -extern crate env_logger; - -#[cfg(test)] -#[macro_use] -extern crate pretty_assertions; - -#[cfg(test)] -extern crate tempdir; - use std::sync::Arc; use std::{fmt, fs}; use std::path::PathBuf; + +use parity_bytes::Bytes; use docopt::Docopt; use rustc_hex::FromHex; use ethereum_types::{U256, Address}; -use bytes::Bytes; -use ethcore::{spec, json_tests, TrieSpec}; -use vm::{ActionParams, CallType}; +use ethcore::{json_tests, test_helpers::TrieSpec}; +use spec; +use serde::Deserialize; +use vm::{ActionParams, ActionType}; mod info; mod display; -use info::Informant; +use crate::info::{Informant, TxInput}; const USAGE: &'static str = r#" EVM implementation for Parity. - Copyright 2015-2019 Parity Technologies (UK) Ltd. + Copyright 2015-2020 Parity Technologies (UK) Ltd. Usage: - parity-evm state-test [--json --std-json --std-dump-json --only NAME --chain CHAIN --std-out-only --std-err-only] + parity-evm state-test [--chain CHAIN --only NAME --json --std-json --std-dump-json --std-out-only --std-err-only] parity-evm stats [options] parity-evm stats-jsontests-vm parity-evm [options] parity-evm [-h | --help] Commands: - state-test Run a state test from a json file. + state-test Run a state test on a provided state test JSON file. stats Execute EVM runtime code and return the statistics. - stats-jsontests-vm Execute standard json-tests format VMTests and return - timing statistics in tsv format. + stats-jsontests-vm Execute standard json-tests on a provided state test JSON + file path, format VMTests, and return timing statistics + in tsv format. Transaction options: --code CODE Contract code as hex (without 0x). @@ -90,14 +86,13 @@ State test options: --only NAME Runs only a single test matching the name. General options: + --chain PATH Path to chain spec file. --json Display verbose results in JSON. --std-json Display results in standardized JSON format. - --std-err-only With --std-json redirect to err output only. - --std-out-only With --std-json redirect to out output only. --std-dump-json Display results in standardized JSON format with additional state dump. -Display result state dump in standardized JSON format. - --chain CHAIN Chain spec file path. + --std-err-only With --std-json redirect to err output only. + --std-out-only With --std-json redirect to out output only. -h, --help Display this message and exit. "#; @@ -126,119 +121,208 @@ fn main() { } } -fn run_stats_jsontests_vm(args: Args) { - use json_tests::HookType; - use std::collections::HashMap; - use std::time::{Instant, Duration}; - - let file = args.arg_file.expect("FILE (or PATH) is required"); - - let mut timings: HashMap)> = HashMap::new(); - - { - let mut record_time = |name: &str, typ: HookType| { - match typ { - HookType::OnStart => { - timings.insert(name.to_string(), (Instant::now(), None)); - }, - HookType::OnStop => { - timings.entry(name.to_string()).and_modify(|v| { - v.1 = Some(v.0.elapsed()); - }); - }, - } - }; - if !file.is_file() { - json_tests::run_executive_test_path(&file, &[], &mut record_time); - } else { - json_tests::run_executive_test_file(&file, &mut record_time); - } - } - - for (name, v) in timings { - println!("{}\t{}", name, display::as_micros(&v.1.expect("All hooks are called with OnStop; qed"))); - } -} - fn run_state_test(args: Args) { - use ethjson::state::test::Test; + use ethjson::test_helpers::state::Test; - let file = args.arg_file.expect("FILE is required"); + // Parse the specified state test JSON file provided to the command `state-test `. + let file = args.arg_file.expect("PATH to a state test JSON file is required"); let mut file = match fs::File::open(&file) { - Err(err) => die(format!("Unable to open: {:?}: {}", file, err)), + Err(err) => die(format!("Unable to open path: {:?}: {}", file, err)), Ok(file) => file, }; let state_test = match Test::load(&mut file) { Err(err) => die(format!("Unable to load the test file: {}", err)), Ok(test) => test, }; + // Parse the name CLI option `--only NAME`. let only_test = args.flag_only.map(|s| s.to_lowercase()); + // Parse the chain `--chain CHAIN` let only_chain = args.flag_chain.map(|s| s.to_lowercase()); - for (name, test) in state_test { - if let Some(false) = only_test.as_ref().map(|only_test| &name.to_lowercase() == only_test) { + // Iterate over 1st level (outer) key-value pair of the state test JSON file. + // Skip to next iteration if CLI option `--only NAME` was parsed into `only_test` and does not match + // the current key `state_test_name` (i.e. add11, create2callPrecompiles). + for (state_test_name, test) in state_test { + if let Some(false) = only_test.as_ref().map(|only_test| { + &state_test_name.to_lowercase() == only_test + }) { continue; } + // Assign from 2nd level key-value pairs of the state test JSON file (i.e. env, post, pre, transaction). let multitransaction = test.transaction; let env_info = test.env.into(); let pre = test.pre_state.into(); - for (spec, states) in test.post_states { - if let Some(false) = only_chain.as_ref().map(|only_chain| &format!("{:?}", spec).to_lowercase() == only_chain) { + // Iterate over remaining "post" key of the 2nd level key-value pairs in the state test JSON file. + // Skip to next iteration if CLI option `--chain CHAIN` was parsed into `only_chain` and does not match + // the current key `fork_spec_name` (i.e. Constantinople, EIP150, EIP158). + for (fork_spec_name, states) in test.post_states { + if let Some(false) = only_chain.as_ref().map(|only_chain| { + &format!("{:?}", fork_spec_name).to_lowercase() == only_chain + }) { continue; } - for (idx, state) in states.into_iter().enumerate() { + // Iterate over the 3rd level key-value pairs of the state test JSON file + // (i.e. list of transactions and associated state roots hashes corresponding each chain). + for (tx_index, state) in states.into_iter().enumerate() { let post_root = state.hash.into(); let transaction = multitransaction.select(&state.indexes).into(); + // Determine the type of trie with state root to create in the database. + // The database is a key-value datastore implemented as a database-backend + // modified Merkle tree. + // Use a secure trie database specification when CLI option `--std-dump-json` + // is specified, otherwise use secure trie with fat trie database. let trie_spec = if args.flag_std_dump_json { TrieSpec::Fat } else { TrieSpec::Secure }; - if args.flag_json { - info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::json::Informant::default(), trie_spec) - } else if args.flag_std_dump_json || args.flag_std_json { + + // Execute the given transaction and verify resulting state root + // for CLI option `--std-dump-json` or `--std-json`. + if args.flag_std_dump_json || args.flag_std_json { if args.flag_std_err_only { - info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::std_json::Informant::err_only(), trie_spec) + let tx_input = TxInput { + state_test_name: &state_test_name, + tx_index, + fork_spec_name: &fork_spec_name, + pre_state: &pre, + post_root, + env_info: &env_info, + transaction, + informant: display::std_json::Informant::err_only(), + trie_spec, + }; + // Use Standard JSON informant with err only + info::run_transaction(tx_input); } else if args.flag_std_out_only { - info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::std_json::Informant::out_only(), trie_spec) + let tx_input = TxInput { + state_test_name: &state_test_name, + tx_index, + fork_spec_name: &fork_spec_name, + pre_state: &pre, + post_root, + env_info: &env_info, + transaction, + informant: display::std_json::Informant::out_only(), + trie_spec, + }; + // Use Standard JSON informant with out only + info::run_transaction(tx_input); } else { - info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::std_json::Informant::default(), trie_spec) + let tx_input = TxInput { + state_test_name: &state_test_name, + tx_index, + fork_spec_name: &fork_spec_name, + pre_state: &pre, + post_root, + env_info: &env_info, + transaction, + informant: display::std_json::Informant::default(), + trie_spec, + }; + // Use Standard JSON informant default + info::run_transaction(tx_input); } } else { - info::run_transaction(&name, idx, &spec, &pre, post_root, &env_info, transaction, display::simple::Informant::default(), trie_spec) + // Execute the given transaction and verify resulting state root + // for CLI option `--json`. + if args.flag_json { + let tx_input = TxInput { + state_test_name: &state_test_name, + tx_index, + fork_spec_name: &fork_spec_name, + pre_state: &pre, + post_root, + env_info: &env_info, + transaction, + informant: display::json::Informant::default(), + trie_spec, + }; + // Use JSON informant + info::run_transaction(tx_input); + } else { + let tx_input = TxInput { + state_test_name: &state_test_name, + tx_index, + fork_spec_name: &fork_spec_name, + pre_state: &pre, + post_root, + env_info: &env_info, + transaction, + informant: display::simple::Informant::default(), + trie_spec, + }; + // Use Simple informant + info::run_transaction(tx_input); + } } } } } } +fn run_stats_jsontests_vm(args: Args) { + use crate::json_tests::HookType; + use std::collections::HashMap; + use std::time::{Instant, Duration}; + + let file = args.arg_file.expect("PATH to a state test JSON file is required"); + + let mut timings: HashMap)> = HashMap::new(); + + { + let mut record_time = |name: &str, typ: HookType| { + match typ { + HookType::OnStart => { + timings.insert(name.to_string(), (Instant::now(), None)); + }, + HookType::OnStop => { + timings.entry(name.to_string()).and_modify(|v| { + v.1 = Some(v.0.elapsed()); + }); + }, + } + }; + if !file.is_file() { + json_tests::run_executive_test_path(&file, &[], &mut record_time); + } else { + json_tests::run_executive_test_file(&file, &mut record_time); + } + } + + for (name, v) in timings { + println!("{}\t{}", name, display::as_micros(&v.1.expect("All hooks are called with OnStop; qed"))); + } +} + +// CLI command `stats` fn run_call(args: Args, informant: T) { - let from = arg(args.from(), "--from"); - let to = arg(args.to(), "--to"); let code = arg(args.code(), "--code"); - let spec = arg(args.spec(), "--chain"); + let to = arg(args.to(), "--to"); + let from = arg(args.from(), "--from"); + let data = arg(args.data(), "--input"); let gas = arg(args.gas(), "--gas"); let gas_price = arg(args.gas_price(), "--gas-price"); - let data = arg(args.data(), "--input"); + let spec = arg(args.spec(), "--chain"); - if code.is_none() && to == Address::default() { + if code.is_none() && to == Address::zero() { die("Either --code or --to is required."); } let mut params = ActionParams::default(); - params.call_type = if code.is_none() { CallType::Call } else { CallType::None }; + params.action_type = if code.is_none() { ActionType::Call } else { ActionType::Create }; + params.code = code.map(Arc::new); params.code_address = to; params.address = to; params.sender = from; params.origin = from; + params.data = data; params.gas = gas; params.gas_price = gas_price; - params.code = code.map(Arc::new); - params.data = data; let mut sink = informant.clone_sink(); let result = if args.flag_std_dump_json { @@ -255,13 +339,13 @@ struct Args { cmd_state_test: bool, cmd_stats_jsontests_vm: bool, arg_file: Option, - flag_only: Option, - flag_from: Option, - flag_to: Option, flag_code: Option, + flag_to: Option, + flag_from: Option, + flag_input: Option, flag_gas: Option, flag_gas_price: Option, - flag_input: Option, + flag_only: Option, flag_chain: Option, flag_json: bool, flag_std_json: bool, @@ -271,56 +355,72 @@ struct Args { } impl Args { - pub fn gas(&self) -> Result { - match self.flag_gas { - Some(ref gas) => gas.parse().map_err(to_string), - None => Ok(U256::from(u64::max_value())), + // CLI option `--code CODE` + /// Set the contract code in hex. Only send to either a contract code or a recipient address. + pub fn code(&self) -> Result, String> { + match self.flag_code { + Some(ref code) => code.from_hex().map(Some).map_err(to_string), + None => Ok(None), } } - pub fn gas_price(&self) -> Result { - match self.flag_gas_price { - Some(ref gas_price) => gas_price.parse().map_err(to_string), - None => Ok(U256::zero()), + // CLI option `--to ADDRESS` + /// Set the recipient address in hex. Only send to either a contract code or a recipient address. + pub fn to(&self) -> Result { + match self.flag_to { + Some(ref to) => to.parse().map_err(to_string), + None => Ok(Address::zero()), } } + // CLI option `--from ADDRESS` + /// Set the sender address. pub fn from(&self) -> Result { match self.flag_from { Some(ref from) => from.parse().map_err(to_string), - None => Ok(Address::default()), + None => Ok(Address::zero()), } } - pub fn to(&self) -> Result { - match self.flag_to { - Some(ref to) => to.parse().map_err(to_string), - None => Ok(Address::default()), + // CLI option `--input DATA` + /// Set the input data in hex. + pub fn data(&self) -> Result, String> { + match self.flag_input { + Some(ref input) => input.from_hex().map_err(to_string).map(Some), + None => Ok(None), } } - pub fn code(&self) -> Result, String> { - match self.flag_code { - Some(ref code) => code.from_hex().map(Some).map_err(to_string), - None => Ok(None), + // CLI option `--gas GAS` + /// Set the gas limit in units of gas. Defaults to max value to allow code to run for whatever time is required. + pub fn gas(&self) -> Result { + match self.flag_gas { + Some(ref gas) => gas.parse().map_err(to_string), + None => Ok(U256::from(u64::max_value())), } } - pub fn data(&self) -> Result, String> { - match self.flag_input { - Some(ref input) => input.from_hex().map_err(to_string).map(Some), - None => Ok(None), + // CLI option `--gas-price WEI` + /// Set the gas price. Defaults to zero to allow the code to run even if an account with no balance + /// is used, otherwise such accounts would not have sufficient funds to pay the transaction fee. + /// Defaulting to zero also makes testing easier since it is not necessary to specify a special configuration file. + pub fn gas_price(&self) -> Result { + match self.flag_gas_price { + Some(ref gas_price) => gas_price.parse().map_err(to_string), + None => Ok(U256::zero()), } } + // CLI option `--chain PATH` + /// Set the path of the chain specification JSON file. pub fn spec(&self) -> Result { Ok(match self.flag_chain { Some(ref filename) => { - let file = fs::File::open(filename).map_err(|e| format!("{}", e))?; - spec::Spec::load(&::std::env::temp_dir(), file)? + let file = fs::File::open(filename).map_err(|e| e.to_string())?; + spec::Spec::load(&::std::env::temp_dir(), file).map_err(|e| e.to_string())? }, None => { - ethcore::ethereum::new_foundation(&::std::env::temp_dir()) + spec::new_foundation(&::std::env::temp_dir()) }, }) } @@ -341,8 +441,29 @@ fn die(msg: T) -> ! { #[cfg(test)] mod tests { + use common_types::transaction; use docopt::Docopt; - use super::{Args, USAGE}; + use ethcore::test_helpers::TrieSpec; + use ethjson::test_helpers::state::State; + use serde::Deserialize; + + use super::{Args, USAGE, Address, run_call}; + use crate::{ + display::std_json::tests::informant, + info::{self, TxInput} + }; + + #[derive(Debug, PartialEq, Deserialize)] + pub struct SampleStateTests { + pub add11: State, + pub add12: State, + } + + #[derive(Debug, PartialEq, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct ConstantinopleStateTests { + pub create2call_precompiles: State, + } fn run>(args: &[T]) -> Args { Docopt::new(USAGE).and_then(|d| d.argv(args.into_iter()).deserialize()).unwrap() @@ -352,30 +473,32 @@ mod tests { fn should_parse_all_the_options() { let args = run(&[ "parity-evm", + "--code", "05", + "--to", "0000000000000000000000000000000000000004", + "--from", "0000000000000000000000000000000000000003", + "--input", "06", + "--gas", "1", + "--gas-price", "2", + "--chain", "./testfile.json", "--json", "--std-json", "--std-dump-json", - "--gas", "1", - "--gas-price", "2", - "--from", "0000000000000000000000000000000000000003", - "--to", "0000000000000000000000000000000000000004", - "--code", "05", - "--input", "06", - "--chain", "./testfile", "--std-err-only", "--std-out-only" + "--std-err-only", + "--std-out-only", ]); + assert_eq!(args.code(), Ok(Some(vec![05]))); + assert_eq!(args.to(), Ok(Address::from_low_u64_be(4))); + assert_eq!(args.from(), Ok(Address::from_low_u64_be(3))); + assert_eq!(args.data(), Ok(Some(vec![06]))); // input data + assert_eq!(args.gas(), Ok(1.into())); + assert_eq!(args.gas_price(), Ok(2.into())); + assert_eq!(args.flag_chain, Some("./testfile.json".to_owned())); assert_eq!(args.flag_json, true); assert_eq!(args.flag_std_json, true); assert_eq!(args.flag_std_dump_json, true); assert_eq!(args.flag_std_err_only, true); assert_eq!(args.flag_std_out_only, true); - assert_eq!(args.gas(), Ok(1.into())); - assert_eq!(args.gas_price(), Ok(2.into())); - assert_eq!(args.from(), Ok(3.into())); - assert_eq!(args.to(), Ok(4.into())); - assert_eq!(args.code(), Ok(Some(vec![05]))); - assert_eq!(args.data(), Ok(Some(vec![06]))); - assert_eq!(args.flag_chain, Some("./testfile".to_owned())); } #[test] @@ -388,15 +511,164 @@ mod tests { "--only=add11", "--json", "--std-json", - "--std-dump-json" + "--std-dump-json", + "--std-out-only", + "--std-err-only", ]); assert_eq!(args.cmd_state_test, true); assert!(args.arg_file.is_some()); + assert_eq!(args.flag_chain, Some("homestead".to_owned())); + assert_eq!(args.flag_only, Some("add11".to_owned())); assert_eq!(args.flag_json, true); assert_eq!(args.flag_std_json, true); assert_eq!(args.flag_std_dump_json, true); - assert_eq!(args.flag_chain, Some("homestead".to_owned())); - assert_eq!(args.flag_only, Some("add11".to_owned())); + assert_eq!(args.flag_std_out_only, true); + assert_eq!(args.flag_std_err_only, true); + } + + #[test] + #[should_panic] + fn should_not_parse_only_flag_without_state_test() { + let _ = run(&[ + "parity-evm", + "./file.json", + "--chain", "homestead", + "--only=add11", + "--json", + ]); + } + + #[test] + #[should_panic] + fn should_not_parse_only_flag_with_stats() { + let _ = run(&[ + "parity-evm", + "stats", + "./file.json", + "--chain", "homestead", + "--only=add11", + "--json", + ]); + } + + #[test] + fn should_not_verify_state_root_using_sample_state_test_json_file() { + let state_tests = include_str!("../res/teststate.json"); + // Parse the specified state test JSON file to simulate the CLI command `state-test `. + let deserialized_state_tests: SampleStateTests = serde_json::from_str(state_tests) + .expect("Serialization cannot fail; qed"); + + // Simulate the name CLI option `--only NAME` + let state_test_name = "add11"; + let pre = deserialized_state_tests.add11.pre_state.into(); + let env_info = deserialized_state_tests.add11.env.into(); + let multitransaction = deserialized_state_tests.add11.transaction; + + for (fork_spec_name, tx_states) in deserialized_state_tests.add11.post_states.iter() { + for (tx_index, tx_state) in tx_states.into_iter().enumerate() { + let (informant, _, res) = informant(); + let trie_spec = TrieSpec::Secure; + let transaction: transaction::SignedTransaction = multitransaction.select(&tx_state.indexes).into(); + let tx_input = TxInput { + state_test_name: &state_test_name, + tx_index, + fork_spec_name: &fork_spec_name, + pre_state: &pre, + post_root: tx_states[tx_index].hash.0, + env_info: &env_info, + transaction, + informant, + trie_spec, + }; + assert!(!info::run_transaction(tx_input)); + assert!( + &String::from_utf8_lossy(&**res.0.lock().unwrap()).contains("State root mismatch") + ); + } + } + } + + #[test] + fn should_verify_state_root_using_constantinople_state_test_json_file() { + let state_tests = include_str!("../res/create2callPrecompiles.json"); + // Parse the specified state test JSON file to simulate the CLI command `state-test `. + let deserialized_state_tests: ConstantinopleStateTests = serde_json::from_str(state_tests) + .expect("Serialization cannot fail; qed"); + + // Simulate the name CLI option `--only NAME` + let state_test_name = "create2callPrecompiles"; + let pre = deserialized_state_tests.create2call_precompiles.pre_state.into(); + let env_info = deserialized_state_tests.create2call_precompiles.env.into(); + let multitransaction = deserialized_state_tests.create2call_precompiles.transaction; + for (fork_spec_name, tx_states) in deserialized_state_tests.create2call_precompiles.post_states.iter() { + for (tx_index, tx_state) in tx_states.into_iter().enumerate() { + let (informant, _, _) = informant(); + let trie_spec = TrieSpec::Secure; // TrieSpec::Fat for --std_dump_json + let transaction: transaction::SignedTransaction = multitransaction.select(&tx_state.indexes).into(); + let tx_input = TxInput { + state_test_name: &state_test_name, + tx_index, + fork_spec_name: &fork_spec_name, + pre_state: &pre, + post_root: tx_states[tx_index].hash.0, + env_info: &env_info, + transaction, + informant, + trie_spec, + }; + assert!(info::run_transaction(tx_input)); + } + } + } + + #[test] + fn should_error_out_of_gas() { + let args = run(&[ + "parity-evm", + "stats", + "--to", "0000000000000000000000000000000000000004", + "--from", "0000000000000000000000000000000000000003", + "--code", "05", + "--input", "06", + "--gas", "1", + "--gas-price", "2", + "--only=add11", + "--std-json", + "--std-out-only", + ]); + + let (inf, _, res) = informant(); + run_call(args, inf); + + assert!( + &String::from_utf8_lossy(&**res.0.lock().unwrap()) + .starts_with(r#"{"error":"EVM: Out of gas","gasUsed":"0x1","#), + ); + } + + #[test] + fn should_not_error_out_of_gas() { + let args = run(&[ + "parity-evm", + "stats", + "--to", "0000000000000000000000000000000000000004", + "--from", "0000000000000000000000000000000000000003", + "--code", "05", + "--input", "06", + "--gas", "21", + "--gas-price", "2", + "--only=add11", + "--std-json", + "--std-out-only", + ]); + + let (inf, _, res) = informant(); + run_call(args, inf); + + assert!( + &String::from_utf8_lossy(&**res.0.lock().unwrap()) + .starts_with(r#"{"output":"0x06","gasUsed":"0x12","#), + ); } } diff --git a/ipfs/Cargo.toml b/ipfs/Cargo.toml index 3c0fcd7947..4d35547aa1 100644 --- a/ipfs/Cargo.toml +++ b/ipfs/Cargo.toml @@ -4,14 +4,16 @@ name = "parity-ipfs-api" version = "1.12.0" license = "GPL-3.0" authors = ["Parity Technologies "] +edition = "2018" [dependencies] -ethcore = { path = "../ethcore" } -parity-bytes = "0.1" -ethereum-types = "0.4" -jsonrpc-core = "10.0.1" -jsonrpc-http-server = "10.0.1" -rlp = { version = "0.3.0", features = ["ethereum"] } +client-traits = { path = "../ethcore/client-traits" } +common-types = { path = "../ethcore/types" } +bytes = { package = "parity-bytes", version = "0.1"} +ethereum-types = "0.8.0" +jsonrpc-core = "14.0.3" +http = { package = "jsonrpc-http-server", version = "14.0.3"} +rlp = "0.4.0" cid = "0.3" multihash = "0.8" unicase = "2.0" diff --git a/ipfs/src/error.rs b/ipfs/src/error.rs index a9a584985a..7229ab5ea6 100644 --- a/ipfs/src/error.rs +++ b/ipfs/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,16 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use {multihash, cid, http}; -use route::Out; +use crate::route::Out; -pub type Result = ::std::result::Result; +pub type Result = std::result::Result; /// IPFS server error #[derive(Debug)] pub enum ServerError { /// Wrapped `std::io::Error` - IoError(::std::io::Error), + IoError(std::io::Error), /// Other `hyper` error Other(http::hyper::error::Error), /// Invalid --ipfs-api-interface @@ -31,8 +30,8 @@ pub enum ServerError { } /// Handle IO errors (ports taken when starting the server). -impl From<::std::io::Error> for ServerError { - fn from(err: ::std::io::Error) -> ServerError { +impl From for ServerError { + fn from(err: std::io::Error) -> ServerError { ServerError::IoError(err) } } @@ -53,17 +52,17 @@ impl From for String { } } -impl ::std::fmt::Display for ServerError { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match self { - ServerError::IoError(err) => write!(f, "Io Error: {}", err), - ServerError::Other(err) => write!(f, "Other error: {}", err), - ServerError::InvalidInterface => write!(f, "Invalid interface"), - } - } +impl std::fmt::Display for ServerError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + ServerError::IoError(err) => write!(f, "Io Error: {}", err), + ServerError::Other(err) => write!(f, "Other error: {}", err), + ServerError::InvalidInterface => write!(f, "Invalid interface"), + } + } } -impl ::std::error::Error for ServerError {} +impl std::error::Error for ServerError {} #[derive(Debug, PartialEq)] pub enum Error { diff --git a/ipfs/src/lib.rs b/ipfs/src/lib.rs index 0a3d83432a..143d6084ed 100644 --- a/ipfs/src/lib.rs +++ b/ipfs/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,17 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -extern crate multihash; -extern crate cid; -extern crate unicase; - -extern crate rlp; -extern crate ethcore; -extern crate parity_bytes as bytes; -extern crate ethereum_types; -extern crate jsonrpc_core; -extern crate jsonrpc_http_server as http; - pub mod error; mod route; @@ -34,7 +23,7 @@ use std::net::{SocketAddr, IpAddr}; use jsonrpc_core::futures::future::{self, FutureResult}; use jsonrpc_core::futures::{self, Future}; -use ethcore::client::BlockChainClient; +use client_traits::BlockChainClient; use http::hyper::{self, server, Method, StatusCode, Body, header::{self, HeaderValue}, }; diff --git a/ipfs/src/route.rs b/ipfs/src/route.rs index f4a730338b..724a9d7a88 100644 --- a/ipfs/src/route.rs +++ b/ipfs/src/route.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,14 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use {rlp, multihash, IpfsHandler}; -use error::{Error, Result}; -use cid::{ToCid, Codec}; +use crate::{ + IpfsHandler, + error::{Error, Result}, +}; -use multihash::Hash; -use ethereum_types::H256; use bytes::Bytes; -use ethcore::client::{BlockId, TransactionId}; +use cid::{ToCid, Codec}; +use common_types::ids::{BlockId, TransactionId}; +use ethereum_types::H256; +use multihash::{self, Hash}; +use rlp; type Reason = &'static str; @@ -56,7 +59,7 @@ impl IpfsHandler { if mh.alg != Hash::Keccak256 { return Err(Error::UnsupportedHash); } - let hash: H256 = mh.digest.into(); + let hash = H256::from_slice(&mh.digest); match cid.codec { Codec::EthereumBlock => self.block(hash), @@ -117,7 +120,7 @@ fn get_param<'a>(query: &'a str, name: &str) -> Option<&'a str> { mod tests { use std::sync::Arc; use super::*; - use ethcore::client::TestBlockChainClient; + use ethcore::test_helpers::TestBlockChainClient; fn get_mocked_handler() -> IpfsHandler { IpfsHandler::new(None.into(), None.into(), Arc::new(TestBlockChainClient::new())) diff --git a/json/Cargo.toml b/json/Cargo.toml index 164ee6bce4..5c7ee10727 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -3,11 +3,16 @@ description = "Parity Ethereum JSON Deserialization" name = "ethjson" version = "0.1.0" authors = ["Parity Technologies "] +edition = "2018" [dependencies] -ethereum-types = "0.4" +ethereum-types = "0.8.0" rustc-hex = "1.0" -serde = "1.0" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -serde_derive = "1.0" +[dev-dependencies] +macros = { path = "../util/macros" } + +[features] +test-helpers = [] diff --git a/json/src/blockchain/account.rs b/json/src/blockchain/account.rs deleted file mode 100644 index aa6f6f8bf2..0000000000 --- a/json/src/blockchain/account.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Blockchain test account deserializer. - -use std::collections::BTreeMap; -use uint::Uint; -use bytes::Bytes; - -/// Blockchain test account deserializer. -#[derive(Debug, PartialEq, Deserialize, Clone)] -pub struct Account { - /// Balance. - pub balance: Uint, - /// Code. - pub code: Bytes, - /// Nonce. - pub nonce: Uint, - /// Storage. - pub storage: BTreeMap, -} - -#[cfg(test)] -mod tests { - use serde_json; - use blockchain::account::Account; - - #[test] - fn account_deserialization() { - let s = r#"{ - "balance" : "0x09184e72a078", - "code" : "0x600140600155", - "nonce" : "0x00", - "storage" : { - "0x01" : "0x9a10c2b5bb8f3c602e674006d9b21f09167df57c87a78a5ce96d4159ecb76520" - } - }"#; - let _deserialized: Account = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/json/src/blockchain/state.rs b/json/src/blockchain/state.rs deleted file mode 100644 index e108c937f8..0000000000 --- a/json/src/blockchain/state.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Blockchain test state deserializer. - -use std::collections::BTreeMap; -use hash::Address; -use blockchain::account::Account; - -/// Blockchain test state deserializer. -#[derive(Debug, PartialEq, Deserialize, Clone)] -pub struct State(BTreeMap); - -impl IntoIterator for State { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} diff --git a/json/src/blockchain/test.rs b/json/src/blockchain/test.rs deleted file mode 100644 index d773aa3b51..0000000000 --- a/json/src/blockchain/test.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Blockchain test deserializer. - -use std::collections::BTreeMap; -use std::io::Read; -use serde_json; -use serde_json::Error; -use blockchain::blockchain::BlockChain; - -/// Blockchain test deserializer. -#[derive(Debug, PartialEq, Deserialize)] -pub struct Test(BTreeMap); - -impl IntoIterator for Test { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Test { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } -} diff --git a/json/src/bytes.rs b/json/src/bytes.rs index 3fbdee2380..699b9e2cfc 100644 --- a/json/src/bytes.rs +++ b/json/src/bytes.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -34,9 +34,15 @@ impl Bytes { } } -impl Into> for Bytes { - fn into(self) -> Vec { - self.0 +impl From for Vec { + fn from(bytes: Bytes) -> Self { + bytes.0 + } +} + +impl From> for Bytes { + fn from(bytes: Vec) -> Self { + Bytes(bytes) } } @@ -57,10 +63,10 @@ impl FromStr for Bytes { 2 if value.starts_with("0x") => vec![], _ if value.starts_with("0x") && value.len() % 2 == 1 => { let v = "0".to_owned() + &value[2..]; - FromHex::from_hex(v.as_str()).unwrap_or(vec![]) + FromHex::from_hex(v.as_str()).unwrap_or_default() }, - _ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or(vec![]), - _ => FromHex::from_hex(value).unwrap_or(vec![]), + _ if value.starts_with("0x") => FromHex::from_hex(&value[2..]).unwrap_or_default(), + _ => FromHex::from_hex(value).unwrap_or_default(), }; Ok(Bytes(v)) @@ -94,8 +100,7 @@ impl<'a> Visitor<'a> for BytesVisitor { #[cfg(test)] mod test { - use serde_json; - use bytes::Bytes; + use super::Bytes; #[test] fn bytes_deserialization() { @@ -112,8 +117,7 @@ mod test { #[test] fn bytes_into() { - let bytes = Bytes(vec![0xff, 0x11]); - let v: Vec = bytes.into(); + let v: Vec = Bytes(vec![0xff, 0x11]).into(); assert_eq!(vec![0xff, 0x11], v); } } diff --git a/json/src/hash.rs b/json/src/hash.rs index a025dc4541..bf8bcbb4fc 100644 --- a/json/src/hash.rs +++ b/json/src/hash.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,7 +20,6 @@ use std::str::FromStr; use std::fmt; use serde::{Deserialize, Deserializer, Serialize, Serializer}; 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 { @@ -56,8 +55,8 @@ macro_rules! impl_hash { fn visit_str(self, value: &str) -> Result where E: Error { let value = match value.len() { - 0 => $inner::from(0), - 2 if value == "0x" => $inner::from(0), + 0 => $inner::from_low_u64_be(0), + 2 if value == "0x" => $inner::from_low_u64_be(0), _ if value.starts_with("0x") => $inner::from_str(&value[2..]).map_err(|e| { Error::custom(format!("Invalid hex value {}: {}", value, e).as_str()) })?, @@ -80,14 +79,13 @@ macro_rules! impl_hash { impl Serialize for $name { fn serialize(&self, serializer: S) -> Result where S: Serializer { - let mut hex = "0x".to_owned(); - hex.push_str(&self.0.to_hex()); - serializer.serialize_str(&hex) + serializer.serialize_str(&format!("{:#x}", self.0)) } } } } + impl_hash!(H64, Hash64); impl_hash!(Address, Hash160); impl_hash!(H256, Hash256); @@ -96,23 +94,21 @@ impl_hash!(Bloom, Hash2048); #[cfg(test)] mod test { + use super::H256; use std::str::FromStr; - use serde_json; - use ethereum_types; - use hash::H256; #[test] fn hash_deserialization() { let s = r#"["", "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"]"#; let deserialized: Vec = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, vec![ - H256(ethereum_types::H256::from(0)), + H256(ethereum_types::H256::zero()), H256(ethereum_types::H256::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae").unwrap()) ]); } #[test] fn hash_into() { - assert_eq!(ethereum_types::H256::from(0), H256(ethereum_types::H256::from(0)).into()); + assert_eq!(ethereum_types::H256::zero(), H256(ethereum_types::H256::zero()).into()); } } diff --git a/json/src/lib.rs b/json/src/lib.rs index af5d93edfa..e66ce0dc9c 100644 --- a/json/src/lib.rs +++ b/json/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,20 +14,18 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -extern crate rustc_hex; -extern crate serde; -extern crate serde_json; -extern crate ethereum_types; -#[macro_use] extern crate serde_derive; +//! JSON deserialization library + +#![warn(missing_docs)] -pub mod hash; -pub mod uint; pub mod bytes; -pub mod blockchain; +pub mod hash; +pub mod maybe; pub mod spec; -pub mod trie; +pub mod uint; pub mod vm; -pub mod maybe; -pub mod state; pub mod transaction; -pub mod test; +pub mod state; + +#[cfg(any(test, feature = "test-helpers"))] +pub mod test_helpers; diff --git a/json/src/maybe.rs b/json/src/maybe.rs index 4fd2ca60e4..f232236475 100644 --- a/json/src/maybe.rs +++ b/json/src/maybe.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,9 +18,13 @@ use std::fmt; use std::marker::PhantomData; + +use ethereum_types::U256; use serde::{Deserialize, Deserializer}; use serde::de::{Error, Visitor, IntoDeserializer}; +use crate::uint::Uint; + /// Deserializer of empty string values into optionals. #[derive(Debug, PartialEq, Clone)] pub enum MaybeEmpty { @@ -32,7 +36,8 @@ pub enum MaybeEmpty { impl<'a, T> Deserialize<'a> for MaybeEmpty where T: Deserialize<'a> { fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> { + where D: Deserializer<'a> + { deserializer.deserialize_any(MaybeEmptyVisitor::new()) } } @@ -61,11 +66,10 @@ impl<'a, T> Visitor<'a> for MaybeEmptyVisitor where T: Deserialize<'a> { } fn visit_string(self, value: String) -> Result where E: Error { - match value.is_empty() { - true => Ok(MaybeEmpty::None), - false => { - T::deserialize(value.into_deserializer()).map(MaybeEmpty::Some) - } + if value.is_empty() { + Ok(MaybeEmpty::None) + } else { + T::deserialize(value.into_deserializer()).map(MaybeEmpty::Some) } } } @@ -79,13 +83,42 @@ impl Into> for MaybeEmpty { } } +#[cfg(test)] +impl From for MaybeEmpty { + fn from(uint: Uint) -> Self { + MaybeEmpty::Some(uint) + } +} + +impl From> for U256 { + fn from(maybe: MaybeEmpty) -> U256 { + match maybe { + MaybeEmpty::Some(v) => v.0, + MaybeEmpty::None => U256::zero(), + } + } +} + +impl From> for u64 { + fn from(maybe: MaybeEmpty) -> u64 { + match maybe { + MaybeEmpty::Some(v) => v.0.low_u64(), + MaybeEmpty::None => 0u64, + } + } +} + +impl Default for MaybeEmpty { + fn default() -> Self { + MaybeEmpty::Some(Uint::default()) + } +} + #[cfg(test)] mod tests { use std::str::FromStr; - use serde_json; - use ethereum_types; - use hash::H256; - use maybe::MaybeEmpty; + use super::MaybeEmpty; + use crate::hash::H256; #[test] fn maybe_deserialization() { diff --git a/json/src/spec/account.rs b/json/src/spec/account.rs index 3d18ecd6c2..466aa99231 100644 --- a/json/src/spec/account.rs +++ b/json/src/spec/account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,22 +17,26 @@ //! Spec account deserialization. use std::collections::BTreeMap; -use uint::Uint; -use bytes::Bytes; -use spec::builtin::Builtin; + +use crate::{bytes::Bytes, spec::builtin::BuiltinCompat, uint::Uint}; +use serde::Deserialize; /// Spec account. +#[cfg_attr(any(test, feature = "test-helpers"), derive(Clone))] #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] +#[serde(rename_all = "camelCase")] pub struct Account { /// Builtin contract. - pub builtin: Option, + pub builtin: Option, /// Balance. pub balance: Option, /// Nonce. pub nonce: Option, /// Code. pub code: Option, + /// Version. + pub version: Option, /// Storage. pub storage: Option>, /// Constructor. @@ -48,12 +52,8 @@ impl Account { #[cfg(test)] mod tests { - use std::collections::BTreeMap; - use serde_json; - use spec::account::Account; + use super::{Account, Bytes, BTreeMap, Uint}; use ethereum_types::U256; - use uint::Uint; - use bytes::Bytes; #[test] fn account_balance_missing_not_empty() { @@ -102,7 +102,15 @@ mod tests { #[test] fn account_empty() { let s = r#"{ - "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } + "builtin": { + "name": "ecrecover", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } }"#; let deserialized: Account = serde_json::from_str(s).unwrap(); assert!(deserialized.is_empty()); @@ -113,8 +121,16 @@ mod tests { let s = r#"{ "balance": "1", "nonce": "0", - "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } }, - "code": "1234" + "code": "1234", + "builtin": { + "name": "ecrecover", + "pricing": { + "linear": { + "base": 3000, + "word": 0 + } + } + } }"#; let deserialized: Account = serde_json::from_str(s).unwrap(); assert!(!deserialized.is_empty()); diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index 61936a5f9a..b4d3166dac 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,12 +14,34 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Authority params deserialization. +//! Authority Round parameter deserialization. +//! +//! Here is an example of input parameters where the step duration is constant at 5 seconds, the set +//! of validators is decided by the contract at address `0x10..01` starting from block 0, and where +//! the address of the contract that computes block rewards is set to `0x20..02` for blocks 0 +//! through 41 and to `0x30.03` for all blocks starting from block 42. +//! +//! ```ignore +//! "params": { +//! "stepDuration": "5", +//! "validators": { +//! "multi": { +//! "0": { +//! "contract": "0x1000000000000000000000000000000000000001" +//! } +//! } +//! }, +//! "blockRewardContractTransitions": { +//! "0": "0x2000000000000000000000000000000000000002", +//! "42": "0x3000000000000000000000000000000000000003" +//! } +//! } +//! ``` -use hash::Address; -use uint::Uint; -use bytes::Bytes; -use super::ValidatorSet; +use std::collections::BTreeMap; +use crate::{bytes::Bytes, hash::Address, uint::Uint}; +use serde::Deserialize; +use super::{StepDuration, ValidatorSet}; /// Authority params deserialization. #[derive(Debug, PartialEq, Deserialize)] @@ -27,7 +49,7 @@ use super::ValidatorSet; #[serde(rename_all = "camelCase")] pub struct AuthorityRoundParams { /// Block duration, in seconds. - pub step_duration: Uint, + pub step_duration: StepDuration, /// Valid authorities pub validators: ValidatorSet, /// Starting step. Determined automatically if not specified. @@ -41,11 +63,24 @@ pub struct AuthorityRoundParams { pub immediate_transitions: Option, /// Reward per block in wei. pub block_reward: Option, - /// Block at which the block reward contract should start being used. + /// Block at which the block reward contract should start being used. This option allows one to + /// add a single block reward contract transition and is compatible with the multiple address + /// option `block_reward_contract_transitions` below. pub block_reward_contract_transition: Option, - /// Block reward contract address (setting the block reward contract - /// overrides the static block reward definition). + /// Block reward contract address which overrides the `block_reward` setting. This option allows + /// one to add a single block reward contract address and is compatible with the multiple + /// address option `block_reward_contract_transitions` below. pub block_reward_contract_address: Option
, + /// Block reward contract addresses with their associated starting block numbers. + /// + /// Setting the block reward contract overrides `block_reward`. If the single block reward + /// contract address is also present then it is added into the map at the block number stored in + /// `block_reward_contract_transition` or 0 if that block number is not provided. Therefore both + /// a single block reward contract transition and a map of reward contract transitions can be + /// used simulataneously in the same configuration. In such a case the code requires that the + /// block number of the single transition is strictly less than any of the block numbers in the + /// map. + pub block_reward_contract_transitions: Option>, /// Block reward code. This overrides the block reward contract address. pub block_reward_contract_code: Option, /// Block at which maximum uncle count should be considered. @@ -58,24 +93,32 @@ pub struct AuthorityRoundParams { pub maximum_empty_steps: Option, /// Strict validation of empty steps transition block. pub strict_empty_steps_transition: Option, + /// First block for which a 2/3 quorum (instead of 1/2) is required. + pub two_thirds_majority_transition: Option, + /// The random number contract's address, or a map of contract transitions. + pub randomness_contract_address: Option>, + /// The addresses of contracts that determine the block gas limit starting from the block number + /// associated with each of those contracts. + pub block_gas_limit_contract_transitions: Option>, } /// Authority engine deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] pub struct AuthorityRound { - /// Ethash params. + /// Authority Round parameters. pub params: AuthorityRoundParams, } #[cfg(test)] mod tests { + use std::str::FromStr; + use ethereum_types::{U256, H160}; - use uint::Uint; use serde_json; - use hash::Address; - use spec::validator_set::ValidatorSet; - use spec::authority_round::AuthorityRound; + + use super::{Address, Uint, StepDuration}; + use crate::{spec::{validator_set::ValidatorSet, authority_round::AuthorityRound}}; #[test] fn authority_round_deserialization() { @@ -89,17 +132,37 @@ mod tests { "validateStepTransition": 150, "blockReward": 5000000, "maximumUncleCountTransition": 10000000, - "maximumUncleCount": 5 + "maximumUncleCount": 5, + "randomnessContractAddress": { + "10": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "20": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "blockGasLimitContractTransitions": { + "10": "0x1000000000000000000000000000000000000001", + "20": "0x2000000000000000000000000000000000000002" + } } }"#; let deserialized: AuthorityRound = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.params.step_duration, Uint(U256::from(0x02))); - assert_eq!(deserialized.params.validators, ValidatorSet::List(vec![Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b"))])); + assert_eq!(deserialized.params.step_duration, StepDuration::Single(Uint(U256::from(2)))); + assert_eq!( + deserialized.params.validators, + ValidatorSet::List(vec![Address(H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap())]), + ); assert_eq!(deserialized.params.start_step, Some(Uint(U256::from(24)))); assert_eq!(deserialized.params.immediate_transitions, None); assert_eq!(deserialized.params.maximum_uncle_count_transition, Some(Uint(10_000_000.into()))); assert_eq!(deserialized.params.maximum_uncle_count, Some(Uint(5.into()))); - + assert_eq!(deserialized.params.randomness_contract_address.unwrap(), + vec![ + (Uint(10.into()), Address(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap())), + (Uint(20.into()), Address(H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap())), + ].into_iter().collect()); + let expected_bglc = + [(Uint(10.into()), Address(H160::from_str("1000000000000000000000000000000000000001").unwrap())), + (Uint(20.into()), Address(H160::from_str("2000000000000000000000000000000000000002").unwrap()))]; + assert_eq!(deserialized.params.block_gas_limit_contract_transitions, + Some(expected_bglc.to_vec().into_iter().collect())); } } diff --git a/json/src/spec/basic_authority.rs b/json/src/spec/basic_authority.rs index 195b89bebb..45c0004429 100644 --- a/json/src/spec/basic_authority.rs +++ b/json/src/spec/basic_authority.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,8 +16,9 @@ //! Authority params deserialization. -use uint::Uint; +use crate::uint::Uint; use super::ValidatorSet; +use serde::Deserialize; /// Authority params deserialization. #[derive(Debug, PartialEq, Deserialize)] @@ -40,12 +41,10 @@ pub struct BasicAuthority { #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; + use std::str::FromStr; + use super::{BasicAuthority, Uint}; use ethereum_types::{U256, H160}; - use hash::Address; - use spec::basic_authority::BasicAuthority; - use spec::validator_set::ValidatorSet; + use crate::{hash::Address, spec::validator_set::ValidatorSet}; #[test] fn basic_authority_deserialization() { @@ -61,7 +60,7 @@ mod tests { let deserialized: BasicAuthority = serde_json::from_str(s).unwrap(); assert_eq!(deserialized.params.duration_limit, Uint(U256::from(0x0d))); - let vs = ValidatorSet::List(vec![Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b"))]); + let vs = ValidatorSet::List(vec![Address(H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap())]); assert_eq!(deserialized.params.validators, vs); } } diff --git a/json/src/spec/builtin.rs b/json/src/spec/builtin.rs index c4f9f4e1ec..ad9a8fb159 100644 --- a/json/src/spec/builtin.rs +++ b/json/src/spec/builtin.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,17 +16,19 @@ //! Spec builtin deserialization. -use uint::Uint; +use std::collections::BTreeMap; +use crate::uint::Uint; +use serde::Deserialize; /// Linear pricing. #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub struct Linear { /// Base price. - pub base: usize, + pub base: u64, /// Price for word. - pub word: usize, + pub word: u64, } /// Pricing for modular exponentiation. @@ -34,7 +36,7 @@ pub struct Linear { #[serde(deny_unknown_fields)] pub struct Modexp { /// Price divisor. - pub divisor: usize, + pub divisor: u64, } /// Pricing for constant alt_bn128 operations (ECADD and ECMUL) @@ -42,9 +44,7 @@ pub struct Modexp { #[serde(deny_unknown_fields)] pub struct AltBn128ConstOperations { /// price - pub price: usize, - /// EIP 1108 transition price - pub eip1108_transition_price: usize, + pub price: u64, } /// Pricing for alt_bn128_pairing. @@ -52,13 +52,9 @@ pub struct AltBn128ConstOperations { #[serde(deny_unknown_fields)] pub struct AltBn128Pairing { /// Base price. - pub base: usize, + pub base: u64, /// Price per point pair. - pub pair: usize, - /// EIP 1108 transition base price - pub eip1108_transition_base: usize, - /// EIP 1108 transition price per point pair - pub eip1108_transition_pair: usize, + pub pair: u64, } /// Pricing variants. @@ -81,25 +77,70 @@ pub enum Pricing { AltBn128ConstOperations(AltBn128ConstOperations), } -/// Spec builtin. +/// Builtin compability layer #[derive(Debug, PartialEq, Deserialize, Clone)] #[serde(deny_unknown_fields)] +pub struct BuiltinCompat { + /// Builtin name. + name: String, + /// Builtin pricing. + pricing: PricingCompat, + /// Activation block. + activate_at: Option, +} + +/// Spec builtin. +#[derive(Debug, PartialEq, Clone)] pub struct Builtin { /// Builtin name. pub name: String, /// Builtin pricing. - pub pricing: Pricing, - /// Activation block. - pub activate_at: Option, - /// EIP 1108 - pub eip1108_transition: Option, + pub pricing: BTreeMap, +} + +impl From for Builtin { + fn from(legacy: BuiltinCompat) -> Self { + let pricing = match legacy.pricing { + PricingCompat::Single(pricing) => { + let mut map = BTreeMap::new(); + let activate_at: u64 = legacy.activate_at.map_or(0, Into::into); + map.insert(activate_at, PricingAt { info: None, price: pricing }); + map + } + PricingCompat::Multi(pricings) => { + pricings.into_iter().map(|(a, p)| (a.into(), p)).collect() + } + }; + Self { name: legacy.name, pricing } + } +} + +/// Compability layer for different pricings +#[derive(Debug, PartialEq, Deserialize, Clone)] +#[serde(rename_all = "snake_case")] +#[serde(deny_unknown_fields)] +#[serde(untagged)] +enum PricingCompat { + /// Single builtin + Single(Pricing), + /// Multiple builtins + Multi(BTreeMap), +} + +/// Price for a builtin, with the block number to activate it on +#[derive(Debug, PartialEq, Deserialize, Clone)] +#[serde(deny_unknown_fields)] +pub struct PricingAt { + /// Description of the activation, e.g. "PunyPony HF, March 12, 2025". + pub info: Option, + /// Builtin pricing. + pub price: Pricing, } #[cfg(test)] mod tests { - use serde_json; - use spec::builtin::{Builtin, Pricing, Linear, Modexp}; - use uint::Uint; + use super::{Builtin, BuiltinCompat, BTreeMap, Pricing, PricingAt, Linear, Modexp, AltBn128ConstOperations}; + use macros::map; #[test] fn builtin_deserialization() { @@ -107,10 +148,42 @@ mod tests { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } }"#; - let deserialized: Builtin = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.name, "ecrecover"); - assert_eq!(deserialized.pricing, Pricing::Linear(Linear { base: 3000, word: 0 })); - assert!(deserialized.activate_at.is_none()); + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "ecrecover"); + assert_eq!(builtin.pricing, map![ + 0 => PricingAt { + info: None, + price: Pricing::Linear(Linear { base: 3000, word: 0 }) + } + ]); + } + + #[test] + fn deserialize_multiple_pricings() { + let s = r#"{ + "name": "ecrecover", + "pricing": { + "0": { + "price": {"linear": { "base": 3000, "word": 0 }} + }, + "500": { + "info": "enable fake EIP at block 500", + "price": {"linear": { "base": 10, "word": 0 }} + } + } + }"#; + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "ecrecover"); + assert_eq!(builtin.pricing, map![ + 0 => PricingAt { + info: None, + price: Pricing::Linear(Linear { base: 3000, word: 0 }) + }, + 500 => PricingAt { + info: Some(String::from("enable fake EIP at block 500")), + price: Pricing::Linear(Linear { base: 10, word: 0 }) + } + ]); } #[test] @@ -120,10 +193,36 @@ mod tests { "activate_at": "0xffffff", "pricing": { "blake2_f": { "gas_per_round": 123 } } }"#; - let deserialized: Builtin = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.name, "blake2_f"); - assert_eq!(deserialized.pricing, Pricing::Blake2F { gas_per_round: 123 }); - assert!(deserialized.activate_at.is_some()); + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "blake2_f"); + assert_eq!(builtin.pricing, map![ + 0xffffff => PricingAt { + info: None, + price: Pricing::Blake2F { gas_per_round: 123 } + } + ]); + } + + #[test] + fn deserialization_alt_bn128_const_operations() { + let s = r#"{ + "name": "alt_bn128_mul", + "pricing": { + "100500": { + "price": { "alt_bn128_const_operations": { "price": 123 }} + } + } + }"#; + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "alt_bn128_mul"); + assert_eq!(builtin.pricing, map![ + 100500 => PricingAt { + info: None, + price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { + price: 123, + }), + } + ]); } #[test] @@ -134,9 +233,13 @@ mod tests { "pricing": { "modexp": { "divisor": 5 } } }"#; - let deserialized: Builtin = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized.name, "late_start"); - assert_eq!(deserialized.pricing, Pricing::Modexp(Modexp { divisor: 5 })); - assert_eq!(deserialized.activate_at, Some(Uint(100000.into()))); + let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); + assert_eq!(builtin.name, "late_start"); + assert_eq!(builtin.pricing, map![ + 100_000 => PricingAt { + info: None, + price: Pricing::Modexp(Modexp { divisor: 5 }) + } + ]); } } diff --git a/json/src/spec/clique.rs b/json/src/spec/clique.rs index 64be9c569a..ca6c0893da 100644 --- a/json/src/spec/clique.rs +++ b/json/src/spec/clique.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,13 +17,14 @@ //! Clique params deserialization. use std::num::NonZeroU64; +use serde::Deserialize; /// Clique params deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct CliqueParams { - /// period as defined in EIP + /// period as defined in EIP 225 pub period: Option, - /// epoch length as defined in EIP + /// epoch length as defined in EIP 225 pub epoch: Option } @@ -36,10 +37,7 @@ pub struct Clique { #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; - use ethereum_types::U256; - use super::*; + use super::{Clique, NonZeroU64}; #[test] fn clique_deserialization() { diff --git a/json/src/spec/engine.rs b/json/src/spec/engine.rs index cfa1d8cafd..7577eadbd4 100644 --- a/json/src/spec/engine.rs +++ b/json/src/spec/engine.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,6 +17,7 @@ //! Engine deserialization. use super::{Ethash, BasicAuthority, AuthorityRound, NullEngine, InstantSeal, Clique}; +use serde::Deserialize; /// Engine deserialization. #[derive(Debug, PartialEq, Deserialize)] @@ -40,8 +41,7 @@ pub enum Engine { #[cfg(test)] mod tests { - use serde_json; - use spec::Engine; + use super::Engine; #[test] fn engine_deserialization() { diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index 6051ac90d8..0addd933f4 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,16 +17,21 @@ //! Ethash params deserialization. use std::collections::BTreeMap; -use uint::{self, Uint}; -use bytes::Bytes; -use hash::Address; +use crate::{ + bytes::Bytes, + uint::{self, Uint}, + hash::Address +}; +use serde::Deserialize; /// Deserializable doppelganger of block rewards for EthashParams #[derive(Clone, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(untagged)] pub enum BlockReward { + /// Single block reward Single(Uint), + /// Several block rewards Multi(BTreeMap), } @@ -86,6 +91,7 @@ pub struct EthashParams { pub ecip1010_continue_transition: Option, /// See main EthashParams docs. + #[serde(default, deserialize_with="uint::validate_optional_non_zero")] pub ecip1017_era_rounds: Option, /// Delays of difficulty bombs. @@ -96,7 +102,6 @@ pub struct EthashParams { /// EXPIP-2 duration limit pub expip2_duration_limit: Option, /// Block to transition to progpow - #[serde(rename="progpowTransition")] pub progpow_transition: Option, } @@ -110,11 +115,9 @@ pub struct Ethash { #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; + use std::str::FromStr; + use super::{Address, BlockReward, Ethash, EthashParams, Uint}; use ethereum_types::{H160, U256}; - use hash::Address; - use spec::ethash::{Ethash, EthashParams, BlockReward}; #[test] fn ethash_deserialization() { @@ -171,28 +174,28 @@ mod tests { block_reward_contract_code: None, block_reward_contract_transition: None, dao_hardfork_transition: Some(Uint(U256::from(0x08))), - dao_hardfork_beneficiary: Some(Address(H160::from("0xabcabcabcabcabcabcabcabcabcabcabcabcabca"))), + dao_hardfork_beneficiary: Some(Address(H160::from_str("abcabcabcabcabcabcabcabcabcabcabcabcabca").unwrap())), dao_hardfork_accounts: Some(vec![ - Address(H160::from("0x304a554a310c7e546dfe434669c62820b7d83490")), - Address(H160::from("0x914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79")), - Address(H160::from("0xfe24cdd8648121a43a7c86d289be4dd2951ed49f")), - Address(H160::from("0x17802f43a0137c506ba92291391a8a8f207f487d")), - Address(H160::from("0xb136707642a4ea12fb4bae820f03d2562ebff487")), - Address(H160::from("0xdbe9b615a3ae8709af8b93336ce9b477e4ac0940")), - Address(H160::from("0xf14c14075d6c4ed84b86798af0956deef67365b5")), - Address(H160::from("0xca544e5c4687d109611d0f8f928b53a25af72448")), - Address(H160::from("0xaeeb8ff27288bdabc0fa5ebb731b6f409507516c")), - Address(H160::from("0xcbb9d3703e651b0d496cdefb8b92c25aeb2171f7")), - Address(H160::from("0xaccc230e8a6e5be9160b8cdf2864dd2a001c28b6")), - Address(H160::from("0x2b3455ec7fedf16e646268bf88846bd7a2319bb2")), - Address(H160::from("0x4613f3bca5c44ea06337a9e439fbc6d42e501d0a")), - Address(H160::from("0xd343b217de44030afaa275f54d31a9317c7f441e")), - Address(H160::from("0x84ef4b2357079cd7a7c69fd7a37cd0609a679106")), - Address(H160::from("0xda2fef9e4a3230988ff17df2165440f37e8b1708")), - Address(H160::from("0xf4c64518ea10f995918a454158c6b61407ea345c")), - Address(H160::from("0x7602b46df5390e432ef1c307d4f2c9ff6d65cc97")), - Address(H160::from("0xbb9bc244d798123fde783fcc1c72d3bb8c189413")), - Address(H160::from("0x807640a13483f8ac783c557fcdf27be11ea4ac7a")), + Address(H160::from_str("304a554a310c7e546dfe434669c62820b7d83490").unwrap()), + Address(H160::from_str("914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79").unwrap()), + Address(H160::from_str("fe24cdd8648121a43a7c86d289be4dd2951ed49f").unwrap()), + Address(H160::from_str("17802f43a0137c506ba92291391a8a8f207f487d").unwrap()), + Address(H160::from_str("b136707642a4ea12fb4bae820f03d2562ebff487").unwrap()), + Address(H160::from_str("dbe9b615a3ae8709af8b93336ce9b477e4ac0940").unwrap()), + Address(H160::from_str("f14c14075d6c4ed84b86798af0956deef67365b5").unwrap()), + Address(H160::from_str("ca544e5c4687d109611d0f8f928b53a25af72448").unwrap()), + Address(H160::from_str("aeeb8ff27288bdabc0fa5ebb731b6f409507516c").unwrap()), + Address(H160::from_str("cbb9d3703e651b0d496cdefb8b92c25aeb2171f7").unwrap()), + Address(H160::from_str("accc230e8a6e5be9160b8cdf2864dd2a001c28b6").unwrap()), + Address(H160::from_str("2b3455ec7fedf16e646268bf88846bd7a2319bb2").unwrap()), + Address(H160::from_str("4613f3bca5c44ea06337a9e439fbc6d42e501d0a").unwrap()), + Address(H160::from_str("d343b217de44030afaa275f54d31a9317c7f441e").unwrap()), + Address(H160::from_str("84ef4b2357079cd7a7c69fd7a37cd0609a679106").unwrap()), + Address(H160::from_str("da2fef9e4a3230988ff17df2165440f37e8b1708").unwrap()), + Address(H160::from_str("f4c64518ea10f995918a454158c6b61407ea345c").unwrap()), + Address(H160::from_str("7602b46df5390e432ef1c307d4f2c9ff6d65cc97").unwrap()), + Address(H160::from_str("bb9bc244d798123fde783fcc1c72d3bb8c189413").unwrap()), + Address(H160::from_str("807640a13483f8ac783c557fcdf27be11ea4ac7a").unwrap()), ]), difficulty_hardfork_transition: Some(Uint(U256::from(0x59d9))), difficulty_hardfork_bound_divisor: Some(Uint(U256::from(0x0200))), diff --git a/json/src/spec/genesis.rs b/json/src/spec/genesis.rs index 1452bea0c1..00e1cddeb6 100644 --- a/json/src/spec/genesis.rs +++ b/json/src/spec/genesis.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,10 +16,13 @@ //! Spec genesis deserialization. -use uint::{Uint, self}; -use hash::{Address, H256}; -use bytes::Bytes; -use spec::Seal; +use crate::{ + bytes::Bytes, + hash::{Address, H256}, + spec::Seal, + uint::{self, Uint}, +}; +use serde::Deserialize; /// Spec genesis. #[derive(Debug, PartialEq, Deserialize)] @@ -53,14 +56,13 @@ pub struct Genesis { #[cfg(test)] mod tests { - use serde_json; - use bytes::Bytes; - use uint::Uint; - use ethereum_types::{U256, H160, H64 as Eth64, H256 as Eth256}; - use hash::{H64, H256, Address}; - use spec::genesis::Genesis; - use spec::{Ethereum, Seal}; use std::str::FromStr; + use super::{Address, Bytes, Genesis, H256, Uint}; + use crate::{ + hash::H64, + spec::{Ethereum, Seal} + }; + use ethereum_types::{U256, H160, H64 as Eth64, H256 as Eth256}; #[test] fn genesis_deserialization() { @@ -82,19 +84,19 @@ mod tests { let deserialized: Genesis = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, Genesis { seal: Seal::Ethereum(Ethereum { - nonce: H64(Eth64::from("0x00006d6f7264656e")), - mix_hash: H256(Eth256::from("0x0000000000000000000000000000000000000000000000000000000000000000")) + nonce: H64(Eth64::from_str("00006d6f7264656e").unwrap()), + mix_hash: H256(Eth256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap()) }), difficulty: Uint(U256::from(0x400000000u64)), - author: Some(Address(H160::from("0x1000000000000000000000000000000000000001"))), + author: Some(Address(H160::from_str("1000000000000000000000000000000000000001").unwrap())), timestamp: Some(Uint(U256::from(0x07))), - parent_hash: Some(H256(Eth256::from("0x9000000000000000000000000000000000000000000000000000000000000000"))), + parent_hash: Some(H256(Eth256::from_str("9000000000000000000000000000000000000000000000000000000000000000").unwrap())), gas_limit: Uint(U256::from(0x1388)), transactions_root: None, receipts_root: None, - state_root: Some(H256(Eth256::from("0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"))), + state_root: Some(H256(Eth256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap())), gas_used: None, - extra_data: Some(Bytes::from_str("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa").unwrap()), + extra_data: Some(Bytes::from_str("11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa").unwrap()), }); } } diff --git a/json/src/spec/hardcoded_sync.rs b/json/src/spec/hardcoded_sync.rs index 381cd1f1ac..e5393de7bc 100644 --- a/json/src/spec/hardcoded_sync.rs +++ b/json/src/spec/hardcoded_sync.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,16 +16,16 @@ //! Spec hardcoded synchronization deserialization for the light client. -use hash::H256; -use uint::Uint; +use crate::{bytes::Bytes, hash::H256, uint::Uint}; +use serde::Deserialize; /// Spec hardcoded sync. -#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct HardcodedSync { /// Hexadecimal of the RLP encoding of the header of the block to start synchronization from. - pub header: String, + pub header: Bytes, /// Total difficulty including the block of `header`. pub total_difficulty: Uint, /// Ordered trie roots of blocks before and including `header`. @@ -35,11 +35,9 @@ pub struct HardcodedSync { #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; + use std::str::FromStr; + use super::{H256, HardcodedSync, Uint}; use ethereum_types::{U256, H256 as Eth256}; - use hash::H256; - use spec::hardcoded_sync::HardcodedSync; #[test] fn hardcoded_sync_deserialization() { @@ -53,11 +51,11 @@ mod tests { }"#; let deserialized: HardcodedSync = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, HardcodedSync { - header: String::from("f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23"), + header: "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".parse().unwrap(), total_difficulty: Uint(U256::from(0x400000000u64)), chts: vec![ - H256(Eth256::from("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa")), - H256(Eth256::from("0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544")), + H256(Eth256::from_str("11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa").unwrap()), + H256(Eth256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap()), ] }); } diff --git a/json/src/spec/instant_seal.rs b/json/src/spec/instant_seal.rs index 2f1ad33e9b..dbd5cd4357 100644 --- a/json/src/spec/instant_seal.rs +++ b/json/src/spec/instant_seal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,6 +16,8 @@ //! Instant seal engine params deserialization. +use serde::Deserialize; + /// Instant seal engine params deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] diff --git a/json/src/spec/mod.rs b/json/src/spec/mod.rs index f1145be2e9..f220bbd731 100644 --- a/json/src/spec/mod.rs +++ b/json/src/spec/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -32,6 +32,7 @@ pub mod null_engine; pub mod instant_seal; pub mod hardcoded_sync; pub mod clique; +pub mod step_duration; pub use self::account::Account; pub use self::builtin::{Builtin, Pricing, Linear}; @@ -40,7 +41,7 @@ pub use self::params::Params; pub use self::spec::{Spec, ForkSpec}; pub use self::seal::{Seal, Ethereum, AuthorityRoundSeal, TendermintSeal}; pub use self::engine::Engine; -pub use self::state::State; +pub use self::state::{State, HashOrMap}; pub use self::ethash::{Ethash, EthashParams, BlockReward}; pub use self::validator_set::ValidatorSet; pub use self::basic_authority::{BasicAuthority, BasicAuthorityParams}; @@ -49,3 +50,4 @@ pub use self::clique::{Clique, CliqueParams}; pub use self::null_engine::{NullEngine, NullEngineParams}; pub use self::instant_seal::{InstantSeal, InstantSealParams}; pub use self::hardcoded_sync::HardcodedSync; +pub use self::step_duration::StepDuration; diff --git a/json/src/spec/null_engine.rs b/json/src/spec/null_engine.rs index bf75749808..14c6fa2229 100644 --- a/json/src/spec/null_engine.rs +++ b/json/src/spec/null_engine.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,8 @@ //! Null engine params deserialization. -use uint::Uint; +use crate::uint::Uint; +use serde::Deserialize; /// Authority params deserialization. #[derive(Debug, PartialEq, Deserialize)] @@ -25,6 +26,8 @@ use uint::Uint; pub struct NullEngineParams { /// Block reward. pub block_reward: Option, + /// Immediate finalization. + pub immediate_finalization: Option } /// Null engine descriptor @@ -37,10 +40,8 @@ pub struct NullEngine { #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; + use super::{NullEngine, Uint}; use ethereum_types::U256; - use super::*; #[test] fn null_engine_deserialization() { diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs index e8b3ded8a9..e1d8e5c02d 100644 --- a/json/src/spec/params.rs +++ b/json/src/spec/params.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,9 +16,12 @@ //! Spec params deserialization. -use uint::{self, Uint}; -use hash::{H256, Address}; -use bytes::Bytes; +use crate::{ + bytes::Bytes, + hash::{H256, Address}, + uint::{self, Uint} +}; +use serde::Deserialize; /// Spec params. #[derive(Debug, PartialEq, Deserialize)] @@ -104,6 +107,8 @@ pub struct Params { /// See `CommonParams` docs. pub eip2028_transition: Option, /// See `CommonParams` docs. + pub eip2200_advance_transition: Option, + /// See `CommonParams` docs. pub dust_protection_transition: Option, /// See `CommonParams` docs. pub nonce_cap_increment: Option, @@ -128,8 +133,10 @@ pub struct Params { pub transaction_permission_contract: Option
, /// Block at which the transaction permission contract should start being used. pub transaction_permission_contract_transition: Option, - /// Wasm activation block height, if not activated from start + /// Wasm activation block height, if not activated from start. pub wasm_activation_transition: Option, + /// Define a separate wasm version instead of using the prefix. + pub wasm_version: Option, /// KIP4 activiation block height. pub kip4_transition: Option, /// KIP6 activiation block height. @@ -138,18 +145,16 @@ pub struct Params { #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; + use super::{Params, Uint}; use ethereum_types::U256; - use spec::params::Params; #[test] fn params_deserialization() { let s = r#"{ "maximumExtraDataSize": "0x20", - "networkID" : "0x1", - "chainID" : "0x15", - "subprotocolName" : "exp", + "networkID": "0x1", + "chainID": "0x15", + "subprotocolName": "exp", "minGasLimit": "0x1388", "accountStartNonce": "0x01", "gasLimitBoundDivisor": "0x20", diff --git a/json/src/spec/seal.rs b/json/src/spec/seal.rs index e716a05bc2..8d03f6e0e7 100644 --- a/json/src/spec/seal.rs +++ b/json/src/spec/seal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,9 +16,8 @@ //! Spec seal deserialization. -use hash::*; -use uint::Uint; -use bytes::Bytes; +use crate::{bytes::Bytes, hash::{H64, H256, H520}, uint::Uint}; +use serde::Deserialize; /// Ethereum seal. #[derive(Debug, PartialEq, Deserialize)] @@ -70,12 +69,9 @@ pub enum Seal { #[cfg(test)] mod tests { - use serde_json; - use hash::*; - use bytes::Bytes; - use uint::Uint; + use std::str::FromStr; + use super::{AuthorityRoundSeal, Bytes, Ethereum, H64, H256, H520, TendermintSeal, Seal, Uint}; use ethereum_types::{U256, H64 as Eth64, H256 as Eth256, H520 as Eth520}; - use spec::{Ethereum, AuthorityRoundSeal, TendermintSeal, Seal}; #[test] fn seal_deserialization() { @@ -106,8 +102,8 @@ mod tests { // [0] assert_eq!(deserialized[0], Seal::Ethereum(Ethereum { - nonce: H64(Eth64::from("0x0000000000000042")), - mix_hash: H256(Eth256::from("0x1000000000000000000000000000000000000000000000000000000000000001")) + nonce: H64(Eth64::from_str("0000000000000042").unwrap()), + mix_hash: H256(Eth256::from_str("1000000000000000000000000000000000000000000000000000000000000001").unwrap()) })); // [1] @@ -118,14 +114,14 @@ mod tests { // [2] assert_eq!(deserialized[2], Seal::AuthorityRound(AuthorityRoundSeal { step: Uint(U256::from(0x0)), - signature: H520(Eth520::from("0x2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002")) + signature: H520(Eth520::from_str("2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002").unwrap()) })); // [3] assert_eq!(deserialized[3], Seal::Tendermint(TendermintSeal { round: Uint(U256::from(0x3)), - proposal: H520(Eth520::from("0x3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003")), - precommits: vec![H520(Eth520::from("0x4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004"))] + proposal: H520(Eth520::from_str("3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003").unwrap()), + precommits: vec![H520(Eth520::from_str("4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004").unwrap())] })); } } diff --git a/json/src/spec/spec.rs b/json/src/spec/spec.rs index 013320c27d..5da53e59e5 100644 --- a/json/src/spec/spec.rs +++ b/json/src/spec/spec.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,24 +17,36 @@ //! Spec deserialization. use std::io::Read; -use serde_json; +use crate::spec::{Params, Genesis, Engine, State, HardcodedSync}; +use serde::Deserialize; use serde_json::Error; -use spec::{Params, Genesis, Engine, State, HardcodedSync}; /// Fork spec definition #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize)] pub enum ForkSpec { + /// EIP 150 Tangerine Whistle: Gas cost changes for IO-heavy operations (#2,463,000, 2016-10-18) EIP150, + /// EIP 158/EIP 161 Spurious Dragon: State trie clearing (#2,675,000, 2016-11-22) EIP158, + /// Frontier (#1, 2015-07-30) Frontier, + /// Homestead (#1,150,000, 2016-03-14) Homestead, + /// Byzantium Metropolis phase 1 (#4,370,000, 2017-10-16) Byzantium, + /// Constantinople Metropolis phase 2 (#7,280,000, 2019-02-28) Constantinople, + /// Constantinople transition test-net ConstantinopleFix, + /// Istanbul (To be announced) Istanbul, + /// Byzantium transition test-net EIP158ToByzantiumAt5, + /// Homestead transition test-net FrontierToHomesteadAt5, + /// Homestead transition test-net HomesteadToDaoAt5, + /// EIP158/EIP161 transition test-net HomesteadToEIP150At5, } @@ -70,69 +82,68 @@ impl Spec { #[cfg(test)] mod tests { - use serde_json; - use spec::spec::Spec; + use super::Spec; #[test] fn should_error_on_unknown_fields() { let s = r#"{ - "name": "Morden", - "dataDir": "morden", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "homesteadTransition" : "0x", - "daoHardforkTransition": "0xffffffffffffffff", - "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", - "daoHardforkAccounts": [] - } - } - }, - "params": { - "accountStartNonce": "0x0100000", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x2", - "forkBlock": "0xffffffffffffffff", - "forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "gasLimitBoundDivisor": "0x20", - "unknownField": "0x0" - }, - "genesis": { - "seal": { - "ethereum": { - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x00006d6f7264656e" + "name": "Morden", + "dataDir": "morden", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "homesteadTransition" : "0x", + "daoHardforkTransition": "0xffffffffffffffff", + "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", + "daoHardforkAccounts": [] + } } }, - "difficulty": "0x20000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x", - "gasLimit": "0x2fefd8" - }, - "nodes": [ - "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" - ], - "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 } } } }, - "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } - }, - "hardcodedSync": { - "header": "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23", - "totalDifficulty": "0x400000000", - "CHTs": [ + "params": { + "accountStartNonce": "0x0100000", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2", + "forkBlock": "0xffffffffffffffff", + "forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimitBoundDivisor": "0x20", + "unknownField": "0x0" + }, + "genesis": { + "seal": { + "ethereum": { + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x00006d6f7264656e" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" + }, + "nodes": [ + "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" + ], + "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 } } } }, + "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } + }, + "hardcodedSync": { + "header": "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23", + "totalDifficulty": "0x400000000", + "CHTs": [ "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - ] - } + ] + } }"#; let result: Result = serde_json::from_str(s); assert!(result.is_err()); @@ -141,62 +152,110 @@ mod tests { #[test] fn spec_deserialization() { let s = r#"{ - "name": "Morden", - "dataDir": "morden", - "engine": { - "Ethash": { - "params": { - "minimumDifficulty": "0x020000", - "difficultyBoundDivisor": "0x0800", - "durationLimit": "0x0d", - "homesteadTransition" : "0x", - "daoHardforkTransition": "0xffffffffffffffff", - "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", - "daoHardforkAccounts": [] - } - } - }, - "params": { - "accountStartNonce": "0x0100000", - "maximumExtraDataSize": "0x20", - "minGasLimit": "0x1388", - "networkID" : "0x2", - "forkBlock": "0xffffffffffffffff", - "forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "gasLimitBoundDivisor": "0x20" - }, - "genesis": { - "seal": { - "ethereum": { - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "nonce": "0x00006d6f7264656e" + "name": "Morden", + "dataDir": "morden", + "engine": { + "Ethash": { + "params": { + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "homesteadTransition" : "0x", + "daoHardforkTransition": "0xffffffffffffffff", + "daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000", + "daoHardforkAccounts": [] + } } }, - "difficulty": "0x20000", - "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x00", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0x", - "gasLimit": "0x2fefd8" - }, - "nodes": [ - "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" - ], - "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 } } } }, - "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } - }, - "hardcodedSync": { - "header": "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23", - "totalDifficulty": "0x400000000", - "CHTs": [ - "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", - "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" - ] - } + "params": { + "accountStartNonce": "0x0100000", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x2", + "forkBlock": "0xffffffffffffffff", + "forkCanonHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "gasLimitBoundDivisor": "0x20" + }, + "genesis": { + "seal": { + "ethereum": { + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x00006d6f7264656e" + } + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" + }, + "nodes": [ + "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" + ], + "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 + } + } + } + }, + "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } + }, + "hardcodedSync": { + "header": "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23", + "totalDifficulty": "0x400000000", + "CHTs": [ + "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" + ] + } }"#; let _deserialized: Spec = serde_json::from_str(s).unwrap(); // TODO: validate all fields diff --git a/json/src/spec/state.rs b/json/src/spec/state.rs index e8a8663672..3b0095e274 100644 --- a/json/src/spec/state.rs +++ b/json/src/spec/state.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,33 +14,60 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Blockchain test state deserializer. +//! Blockchain state deserializer. use std::collections::BTreeMap; -use hash::Address; -use bytes::Bytes; -use spec::{Account, Builtin}; +use crate::{ + bytes::Bytes, + hash::{Address, H256}, + spec::{Account, Builtin} +}; +use serde::Deserialize; -/// Blockchain test state deserializer. +/// Recent JSON tests can be either a map or a hash (represented by a string). +/// See https://github.com/ethereum/tests/issues/637 +#[cfg_attr(any(test, feature = "test-helpers"), derive(Clone))] +#[derive(Debug, PartialEq, Deserialize)] +#[serde(untagged)] +pub enum HashOrMap { + /// When the `postState` is large, tests sometimes just include the state root of the last + /// successful block here. + Hash(H256), + /// The expected `postState` of a test + Map(BTreeMap), +} + +/// Blockchain state deserializer. +#[cfg_attr(any(test, feature = "test-helpers"), derive(Clone))] #[derive(Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] -pub struct State(BTreeMap); +pub struct State(pub HashOrMap); impl State { /// Returns all builtins. pub fn builtins(&self) -> BTreeMap { - self.0 - .iter() - .filter_map(|(add, ref acc)| acc.builtin.clone().map(|b| (add.clone(), b))) - .collect() + match &self.0 { + HashOrMap::Hash(_) => BTreeMap::default(), + HashOrMap::Map(map) => { + map.iter().filter_map(|(add, ref acc)| { + acc.builtin.clone().map(|b| (add.clone(), b.into())) + }).collect() + } + + } } /// Returns all constructors. pub fn constructors(&self) -> BTreeMap { - self.0 - .iter() - .filter_map(|(add, ref acc)| acc.constructor.clone().map(|b| (add.clone(), b))) - .collect() + match &self.0 { + HashOrMap::Hash(_) => BTreeMap::default(), + HashOrMap::Map(map) => { + map.iter().filter_map(|(add, ref acc)| { + acc.constructor.clone().map(|b| (add.clone(), b)) + }).collect() + } + + } } } @@ -49,6 +76,10 @@ impl IntoIterator for State { type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() + if let HashOrMap::Map(m) = self.0 { + m.into_iter() + } else { + BTreeMap::default().into_iter() + } } } diff --git a/json/src/spec/step_duration.rs b/json/src/spec/step_duration.rs new file mode 100644 index 0000000000..ec16c4b7c3 --- /dev/null +++ b/json/src/spec/step_duration.rs @@ -0,0 +1,36 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Step duration configuration parameter + +use std::collections::BTreeMap; + +use serde::Deserialize; + +use crate::uint::Uint; + +/// Step duration can be specified either as a `Uint` (in seconds), in which case it will be +/// constant, or as a list of pairs consisting of a timestamp of type `Uint` and a duration, in +/// which case the duration of a step will be determined by a mapping arising from that list. +#[derive(Debug, PartialEq, Deserialize)] +#[serde(deny_unknown_fields)] +#[serde(untagged)] +pub enum StepDuration { + /// Duration of all steps. + Single(Uint), + /// Step duration transitions: a mapping of timestamp to step durations. + Transitions(BTreeMap), +} diff --git a/json/src/spec/validator_set.rs b/json/src/spec/validator_set.rs index e7e82282c7..d9aee4476e 100644 --- a/json/src/spec/validator_set.rs +++ b/json/src/spec/validator_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,8 +17,8 @@ //! Validator set deserialization. use std::collections::BTreeMap; -use uint::Uint; -use hash::Address; +use crate::{hash::Address, uint::Uint}; +use serde::Deserialize; /// Different ways of specifying validators. #[derive(Debug, PartialEq, Deserialize)] @@ -37,11 +37,9 @@ pub enum ValidatorSet { #[cfg(test)] mod tests { - use serde_json; - use uint::Uint; + use std::str::FromStr; + use super::{Address, Uint, ValidatorSet}; use ethereum_types::{H160, U256}; - use hash::Address; - use spec::validator_set::ValidatorSet; #[test] fn validator_set_deserialization() { @@ -62,9 +60,9 @@ mod tests { let deserialized: Vec = serde_json::from_str(s).unwrap(); assert_eq!(deserialized.len(), 4); - assert_eq!(deserialized[0], ValidatorSet::List(vec![Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b"))])); - assert_eq!(deserialized[1], ValidatorSet::SafeContract(Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b")))); - assert_eq!(deserialized[2], ValidatorSet::Contract(Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b")))); + assert_eq!(deserialized[0], ValidatorSet::List(vec![Address(H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap())])); + assert_eq!(deserialized[1], ValidatorSet::SafeContract(Address(H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap()))); + assert_eq!(deserialized[2], ValidatorSet::Contract(Address(H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap()))); match deserialized[3] { ValidatorSet::Multi(ref map) => { assert_eq!(map.len(), 3); diff --git a/json/src/state/log.rs b/json/src/state.rs similarity index 89% rename from json/src/state/log.rs rename to json/src/state.rs index 1a0dda5295..91b3d5ff6a 100644 --- a/json/src/state/log.rs +++ b/json/src/state.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,11 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! State test log deserialization. -use hash::{Address, H256, Bloom}; -use bytes::Bytes; +//! State deserialization types -/// State test log deserialization. +use crate::{ + bytes::Bytes, + hash::{Address, H256, Bloom}, +}; +use serde::Deserialize; + +/// State log deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct Log { /// Address. @@ -33,8 +37,7 @@ pub struct Log { #[cfg(test)] mod tests { - use serde_json; - use state::Log; + use super::Log; #[test] fn log_deserialization() { diff --git a/json/src/state/state.rs b/json/src/state/state.rs deleted file mode 100644 index f25dabec79..0000000000 --- a/json/src/state/state.rs +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! State test deserialization. - -use bytes::Bytes; -use hash::H256; -use state::{Env, AccountState, Transaction, Log}; - -/// State test deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct State { - /// Environment. - pub env: Env, - /// Output. - #[serde(rename = "out")] - pub output: Bytes, - /// Pre state. - #[serde(rename = "pre")] - pub pre_state: AccountState, - /// Post state. - #[serde(rename = "post")] - pub post_state: AccountState, - /// Post state root. - pub post_state_root: H256, - /// Transaction. - pub transaction: Transaction, - /// Logs. - pub logs: Vec -} - -#[cfg(test)] -mod tests { - use serde_json; - use state::State; - - #[test] - fn state_deserialization() { - let s = r#"{ - "env" : { - "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentDifficulty" : "0x0100", - "currentGasLimit" : "0x01c9c380", - "currentNumber" : "0x00", - "currentTimestamp" : "0x01", - "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" - }, - "logs" : [ - ], - "out" : "0x", - "post" : { - "1000000000000000000000000000000000000000" : { - "balance" : "0x0de0b6b3a763ffff", - "code" : "0x6040600060406000600173100000000000000000000000000000000000000162055730f1600055", - "nonce" : "0x00", - "storage" : { - "0x00" : "0x01" - } - }, - "1000000000000000000000000000000000000001" : { - "balance" : "0x0de0b6b3a763ffff", - "code" : "0x604060006040600060027310000000000000000000000000000000000000026203d090f1600155", - "nonce" : "0x00", - "storage" : { - "0x01" : "0x01" - } - }, - "1000000000000000000000000000000000000002" : { - "balance" : "0x02", - "code" : "0x600160025533600455346007553060e6553260e8553660ec553860ee553a60f055", - "nonce" : "0x00", - "storage" : { - "0x02" : "0x01", - "0x04" : "0x1000000000000000000000000000000000000001", - "0x07" : "0x02", - "0xe6" : "0x1000000000000000000000000000000000000002", - "0xe8" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", - "0xec" : "0x40", - "0xee" : "0x21", - "0xf0" : "0x01" - } - }, - "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : { - "balance" : "0x039455", - "code" : "0x", - "nonce" : "0x00", - "storage" : { - } - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "0x0de0b6b3a7606bab", - "code" : "0x", - "nonce" : "0x01", - "storage" : { - } - } - }, - "postStateRoot" : "8f8ed2aed2973e159fa5486f47c6ebf15c5058f8e2350286b84b569bc6ce2d25", - "pre" : { - "1000000000000000000000000000000000000000" : { - "balance" : "0x0de0b6b3a7640000", - "code" : "0x6040600060406000600173100000000000000000000000000000000000000162055730f1600055", - "nonce" : "0x00", - "storage" : { - } - }, - "1000000000000000000000000000000000000001" : { - "balance" : "0x0de0b6b3a7640000", - "code" : "0x604060006040600060027310000000000000000000000000000000000000026203d090f1600155", - "nonce" : "0x00", - "storage" : { - } - }, - "1000000000000000000000000000000000000002" : { - "balance" : "0x00", - "code" : "0x600160025533600455346007553060e6553260e8553660ec553860ee553a60f055", - "nonce" : "0x00", - "storage" : { - } - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "0x0de0b6b3a7640000", - "code" : "0x", - "nonce" : "0x00", - "storage" : { - } - } - }, - "transaction" : { - "data" : "", - "gasLimit" : "0x2dc6c0", - "gasPrice" : "0x01", - "nonce" : "0x00", - "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to" : "1000000000000000000000000000000000000000", - "value" : "0x00" - } - }"#; - let _deserialized: State = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/json/src/state/test.rs b/json/src/state/test.rs deleted file mode 100644 index 3521c79778..0000000000 --- a/json/src/state/test.rs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! General test deserialization. - -use std::io::Read; -use std::collections::BTreeMap; -use uint::Uint; -use bytes::Bytes; -use hash::{Address, H256}; -use spec::ForkSpec; -use state::{Env, AccountState, Transaction}; -use maybe::MaybeEmpty; -use serde_json::{self, Error}; - -/// State test deserializer. -#[derive(Debug, PartialEq, Deserialize)] -pub struct Test(BTreeMap); - -impl IntoIterator for Test { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Test { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } -} - -/// State test deserialization. -#[derive(Debug, PartialEq, Deserialize)] -pub struct State { - /// Environment. - pub env: Env, - /// Pre state. - #[serde(rename = "pre")] - pub pre_state: AccountState, - /// Post state. - #[serde(rename = "post")] - pub post_states: BTreeMap>, - /// Transaction. - pub transaction: MultiTransaction, -} - -/// State test transaction deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct MultiTransaction { - /// Transaction data set. - pub data: Vec, - /// Gas limit set. - pub gas_limit: Vec, - /// Gas price. - pub gas_price: Uint, - /// Nonce. - pub nonce: Uint, - /// Secret key. - #[serde(rename = "secretKey")] - pub secret: Option, - /// To. - pub to: MaybeEmpty
, - /// Value set. - pub value: Vec, -} - -impl MultiTransaction { - /// Build transaction with given indexes. - pub fn select(&self, indexes: &PostStateIndexes) -> Transaction { - Transaction { - data: self.data[indexes.data as usize].clone(), - gas_limit: self.gas_limit[indexes.gas as usize].clone(), - gas_price: self.gas_price.clone(), - nonce: self.nonce.clone(), - secret: self.secret.clone(), - to: self.to.clone(), - value: self.value[indexes.value as usize].clone(), - } - } -} - -/// State test indexes deserialization. -#[derive(Debug, PartialEq, Deserialize)] -pub struct PostStateIndexes { - /// Index into transaction data set. - pub data: u64, - /// Index into transaction gas limit set. - pub gas: u64, - /// Index into transaction value set. - pub value: u64, -} - -/// State test indexed state result deserialization. -#[derive(Debug, PartialEq, Deserialize)] -pub struct PostStateResult { - /// Post state hash - pub hash: H256, - /// Indexes - pub indexes: PostStateIndexes, -} - -#[cfg(test)] -mod tests { - use serde_json; - use super::{MultiTransaction, State}; - - #[test] - fn multi_transaction_deserialization() { - let s = r#"{ - "data" : [ "" ], - "gasLimit" : [ "0x2dc6c0", "0x222222" ], - "gasPrice" : "0x01", - "nonce" : "0x00", - "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to" : "1000000000000000000000000000000000000000", - "value" : [ "0x00", "0x01", "0x02" ] - }"#; - let _deserialized: MultiTransaction = serde_json::from_str(s).unwrap(); - } - - #[test] - fn state_deserialization() { - let s = r#"{ - "env" : { - "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentDifficulty" : "0x0100", - "currentGasLimit" : "0x01c9c380", - "currentNumber" : "0x00", - "currentTimestamp" : "0x01", - "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" - }, - "post" : { - "EIP150" : [ - { - "hash" : "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", - "indexes" : { "data" : 0, "gas" : 0, "value" : 0 } - }, - { - "hash" : "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", - "indexes" : { "data" : 0, "gas" : 0, "value" : 1 } - } - ], - "EIP158" : [ - { - "hash" : "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", - "indexes" : { "data" : 0, "gas" : 0, "value" : 0 } - }, - { - "hash" : "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", - "indexes" : { "data" : 0, "gas" : 0, "value" : 1 } - } - ] - }, - "pre" : { - "1000000000000000000000000000000000000000" : { - "balance" : "0x0de0b6b3a7640000", - "code" : "0x6040600060406000600173100000000000000000000000000000000000000162055730f1600055", - "nonce" : "0x00", - "storage" : { - } - }, - "1000000000000000000000000000000000000001" : { - "balance" : "0x0de0b6b3a7640000", - "code" : "0x604060006040600060027310000000000000000000000000000000000000026203d090f1600155", - "nonce" : "0x00", - "storage" : { - } - }, - "1000000000000000000000000000000000000002" : { - "balance" : "0x00", - "code" : "0x600160025533600455346007553060e6553260e8553660ec553860ee553a60f055", - "nonce" : "0x00", - "storage" : { - } - }, - "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "0x0de0b6b3a7640000", - "code" : "0x", - "nonce" : "0x00", - "storage" : { - } - } - }, - "transaction" : { - "data" : [ "" ], - "gasLimit" : [ "285000", "100000", "6000" ], - "gasPrice" : "0x01", - "nonce" : "0x00", - "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", - "value" : [ "10", "0" ] - } - }"#; - let _deserialized: State = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/json/src/state/transaction.rs b/json/src/state/transaction.rs deleted file mode 100644 index 693b976994..0000000000 --- a/json/src/state/transaction.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! State test transaction deserialization. - -use uint::Uint; -use bytes::Bytes; -use hash::{Address, H256}; -use maybe::MaybeEmpty; - -/// State test transaction deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Transaction { - /// Transaction data. - pub data: Bytes, - /// Gas limit. - pub gas_limit: Uint, - /// Gas price. - pub gas_price: Uint, - /// Nonce. - pub nonce: Uint, - /// Secret key. - #[serde(rename = "secretKey")] - pub secret: Option, - /// To. - pub to: MaybeEmpty
, - /// Value. - pub value: Uint, -} - -#[cfg(test)] -mod tests { - use serde_json; - use state::Transaction; - - #[test] - fn transaction_deserialization() { - let s = r#"{ - "data" : "", - "gasLimit" : "0x2dc6c0", - "gasPrice" : "0x01", - "nonce" : "0x00", - "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - "to" : "1000000000000000000000000000000000000000", - "value" : "0x00" - }"#; - let _deserialized: Transaction = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/json/src/test/mod.rs b/json/src/test/mod.rs deleted file mode 100644 index 355d30d69f..0000000000 --- a/json/src/test/mod.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Additional test structures deserialization. - -use std::collections::BTreeMap; -use std::io::Read; -use serde_json; -use serde_json::Error; -use hash::H256; -use uint::Uint; - -/// Blockchain test header deserializer. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DifficultyTestCase { - /// Parent timestamp. - pub parent_timestamp: Uint, - /// Parent difficulty. - pub parent_difficulty: Uint, - /// Parent uncle hash. - pub parent_uncles: H256, - /// Current timestamp. - pub current_timestamp: Uint, - /// Current difficulty. - pub current_difficulty: Uint, - /// Current block number. - pub current_block_number: Uint, -} - -/// Blockchain test deserializer. -#[derive(Debug, PartialEq, Deserialize)] -pub struct DifficultyTest(BTreeMap); - -impl IntoIterator for DifficultyTest { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl DifficultyTest { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } -} - -/// Test to skip (only if issue ongoing) -#[derive(Debug, PartialEq, Deserialize)] -pub struct SkipStates { - /// Block tests - pub block: Vec, - /// State tests - pub state: Vec, - -} - -/// Block test to skip. -#[derive(Debug, PartialEq, Deserialize)] -pub struct BlockSkipStates { - /// Issue reference. - pub reference: String, - /// Test failing name. - pub failing: String, - /// Items failing for the test. - pub subtests: Vec, -} - -/// State test to skip. -#[derive(Debug, PartialEq, Deserialize)] -pub struct StateSkipStates { - /// Issue reference. - pub reference: String, - /// Test failing name. - pub failing: String, - /// Items failing for the test. - pub subtests: BTreeMap -} - -/// State subtest to skip. -#[derive(Debug, PartialEq, Deserialize)] -pub struct StateSkipSubStates { - /// State test number of this item. Or '*' for all state. - pub subnumbers: Vec, - /// Chain for this items. - pub chain: String, -} - -impl SkipStates { - /// Loads skip states from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } - - /// Empty skip states. - pub fn empty() -> Self { - SkipStates { - block: Vec::new(), - state: Vec::new(), - } - } -} diff --git a/json/src/blockchain/block.rs b/json/src/test_helpers/blockchain/block.rs similarity index 94% rename from json/src/blockchain/block.rs rename to json/src/test_helpers/blockchain/block.rs index 23ba5300dc..0fab3a2b23 100644 --- a/json/src/blockchain/block.rs +++ b/json/src/test_helpers/blockchain/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,9 +16,9 @@ //! Blockchain test block deserializer. -use bytes::Bytes; -use blockchain::header::Header; -use blockchain::transaction::Transaction; +use crate::{bytes::Bytes, transaction::Transaction}; +use super::header::Header; +use serde::Deserialize; /// Blockchain test block deserializer. #[derive(Debug, PartialEq, Deserialize)] @@ -40,8 +40,7 @@ impl Block { #[cfg(test)] mod tests { - use serde_json; - use blockchain::block::Block; + use super::Block; #[test] fn block_deserialization() { @@ -66,7 +65,7 @@ mod tests { }, "blocknumber" : "1", "rlp" : "0xf901fcf901f7a05a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c5c83ff43741f573a0c9b31d0e56fdd745f4e37d193c4e78544f302777aafcf3a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefba808456850b7b80a013735ab4156c9b36327224d92e1692fab8fc362f8e0f868c94d421848ef7cd0688931dcc53e5edc514c0c0", - "transactions" : [], + "transaction" : [], "uncleHeaders" : [] }"#; let _deserialized: Block = serde_json::from_str(s).unwrap(); diff --git a/json/src/blockchain/header.rs b/json/src/test_helpers/blockchain/header.rs similarity index 94% rename from json/src/blockchain/header.rs rename to json/src/test_helpers/blockchain/header.rs index 8de5b16edb..8818a4b906 100644 --- a/json/src/blockchain/header.rs +++ b/json/src/test_helpers/blockchain/header.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,9 +16,12 @@ //! Blockchain test header deserializer. -use hash::{H64, Address, H256, Bloom}; -use uint::Uint; -use bytes::Bytes; +use crate::{ + bytes::Bytes, + hash::{H64, Address, H256, Bloom}, + uint::Uint +}; +use serde::Deserialize; /// Blockchain test header deserializer. #[derive(Debug, PartialEq, Deserialize)] @@ -64,8 +67,7 @@ pub struct Header { #[cfg(test)] mod tests { - use serde_json; - use blockchain::header::Header; + use super::Header; #[test] fn header_deserialization() { diff --git a/json/src/blockchain/blockchain.rs b/json/src/test_helpers/blockchain/mod.rs similarity index 95% rename from json/src/blockchain/blockchain.rs rename to json/src/test_helpers/blockchain/mod.rs index b92336f793..49a6352b77 100644 --- a/json/src/blockchain/blockchain.rs +++ b/json/src/test_helpers/blockchain/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,14 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Blockchain deserialization. +//! Blockchain test deserialization. -use bytes::Bytes; -use hash::H256; -use blockchain::state::State; -use blockchain::header::Header; -use blockchain::block::Block; -use spec::{ForkSpec, Genesis, Seal, Ethereum}; +use crate::{ + bytes::Bytes, + hash::H256, + spec::{Ethereum, ForkSpec, Genesis, Seal, State} + +}; +use serde::Deserialize; + +pub mod block; +pub mod header; + +pub use self::block::Block; +pub use self::header::Header; + +/// Type for running `Blockchain` tests +pub type Test = super::tester::GenericTester; /// Json Block test possible engine kind. #[derive(Debug, PartialEq, Deserialize)] @@ -95,8 +105,7 @@ impl BlockChain { #[cfg(test)] mod tests { - use serde_json; - use blockchain::blockchain::BlockChain; + use super::BlockChain; #[test] fn blockchain_deserialization() { diff --git a/json/src/test_helpers/difficulty.rs b/json/src/test_helpers/difficulty.rs new file mode 100644 index 0000000000..023966c6a2 --- /dev/null +++ b/json/src/test_helpers/difficulty.rs @@ -0,0 +1,23 @@ +use crate::{hash::H256, uint::Uint}; +use serde::Deserialize; + +/// Blockchain test header deserializer. +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DifficultyTestCase { + /// Parent timestamp. + pub parent_timestamp: Uint, + /// Parent difficulty. + pub parent_difficulty: Uint, + /// Parent uncle hash. + pub parent_uncles: H256, + /// Current timestamp. + pub current_timestamp: Uint, + /// Current difficulty. + pub current_difficulty: Uint, + /// Current block number. + pub current_block_number: Uint, +} + +/// Type for running `Difficulty` tests +pub type DifficultyTest = super::tester::GenericTester; diff --git a/json/src/blockchain/mod.rs b/json/src/test_helpers/mod.rs similarity index 61% rename from json/src/blockchain/mod.rs rename to json/src/test_helpers/mod.rs index 0a3b162e95..dfe30328a0 100644 --- a/json/src/blockchain/mod.rs +++ b/json/src/test_helpers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,21 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Blockchain test deserialization. +//! Test structures for JSON deserialization. -pub mod account; -pub mod block; +/// Blockchain test helpers pub mod blockchain; -pub mod header; +/// Difficulty test helpers +pub mod difficulty; +/// Tests to skip helpers +pub mod skip; +/// State test helpers pub mod state; +/// Test primitives +pub mod tester; +/// Transaction test helpers pub mod transaction; -pub mod test; - -pub use self::account::Account; -pub use self::block::Block; -pub use self::blockchain::BlockChain; -pub use self::blockchain::Engine; -pub use self::header::Header; -pub use self::state::State; -pub use self::test::Test; -pub use self::transaction::Transaction; +/// Trie test helpers +pub mod trie; +/// Vm test helpers +pub mod vm { + /// Type for running `vm` tests + pub type Test = super::tester::GenericTester; +} diff --git a/json/src/test_helpers/skip.rs b/json/src/test_helpers/skip.rs new file mode 100644 index 0000000000..91067dd36b --- /dev/null +++ b/json/src/test_helpers/skip.rs @@ -0,0 +1,58 @@ +use std::collections::BTreeMap; +use serde::Deserialize; + +/// Test to skip (only if issue ongoing) +#[derive(Debug, PartialEq, Deserialize)] +pub struct SkipTests { + /// Block tests + pub block: Vec, + /// State tests + pub state: Vec, + +} + +/// Block test to skip. +#[derive(Debug, PartialEq, Deserialize)] +pub struct SkipBlockchainTest { + /// Issue reference. + pub reference: String, + /// Test failing name. + pub failing: String, + /// Items failing for the test. + pub subtests: Vec, +} + +/// State test to skip. +#[derive(Debug, PartialEq, Deserialize)] +pub struct SkipStateTest { + /// Issue reference. + pub reference: String, + /// Test failing name. + pub failing: String, + /// Items failing for the test. + pub subtests: BTreeMap +} + +/// State subtest to skip. +#[derive(Debug, PartialEq, Deserialize)] +pub struct StateSkipSubStates { + /// State test number of this item. Or '*' for all state. + pub subnumbers: Vec, + /// Chain for this items. + pub chain: String, +} + +impl SkipTests { + /// Empty skip states. + pub fn empty() -> Self { + SkipTests { + block: Vec::new(), + state: Vec::new(), + } + } + + /// Loads test from json. + pub fn load(reader: R) -> Result where R: std::io::Read { + serde_json::from_reader(reader) + } +} diff --git a/json/src/test_helpers/state.rs b/json/src/test_helpers/state.rs new file mode 100644 index 0000000000..aca26b8068 --- /dev/null +++ b/json/src/test_helpers/state.rs @@ -0,0 +1,203 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! State test deserialization. + +/// Type for running `State` tests +pub type Test = super::tester::GenericTester; + +use std::collections::BTreeMap; +use serde::Deserialize; +use crate::{ + bytes::Bytes, + hash::{Address, H256}, + maybe::MaybeEmpty, + uint::Uint, + spec::{ForkSpec, State as AccountState}, + transaction::Transaction, + vm::Env +}; + +/// State test deserialization. +#[derive(Debug, PartialEq, Deserialize)] +pub struct State { + /// Environment. + pub env: Env, + /// Pre state. + #[serde(rename = "pre")] + pub pre_state: AccountState, + /// Post state. + #[serde(rename = "post")] + pub post_states: BTreeMap>, + /// Transaction. + pub transaction: MultiTransaction, +} + +/// State test transaction deserialization. +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MultiTransaction { + /// Transaction data set. + pub data: Vec, + /// Gas limit set. + pub gas_limit: Vec, + /// Gas price. + pub gas_price: Uint, + /// Nonce. + pub nonce: Uint, + /// Secret key. + #[serde(rename = "secretKey")] + pub secret: Option, + /// To. + pub to: MaybeEmpty
, + /// Value set. + pub value: Vec, +} + +impl MultiTransaction { + /// Build transaction with given indexes. + pub fn select(&self, indexes: &PostStateIndexes) -> Transaction { + Transaction { + data: self.data[indexes.data as usize].clone(), + gas_limit: self.gas_limit[indexes.gas as usize], + gas_price: self.gas_price, + nonce: self.nonce, + to: self.to.clone(), + value: self.value[indexes.value as usize], + r: Default::default(), + s: Default::default(), + v: Default::default(), + secret: self.secret.clone(), + } + } +} + +/// State test indexes deserialization. +#[derive(Debug, PartialEq, Deserialize)] +pub struct PostStateIndexes { + /// Index into transaction data set. + pub data: u64, + /// Index into transaction gas limit set. + pub gas: u64, + /// Index into transaction value set. + pub value: u64, +} + +/// State test indexed state result deserialization. +#[derive(Debug, PartialEq, Deserialize)] +pub struct PostStateResult { + /// Post state hash + pub hash: H256, + /// Indexes + pub indexes: PostStateIndexes, +} + +#[cfg(test)] +mod tests { + use serde_json; + use super::{MultiTransaction, State}; + + #[test] + fn multi_transaction_deserialization() { + let s = r#"{ + "data": [ "" ], + "gasLimit": [ "0x2dc6c0", "0x222222" ], + "gasPrice": "0x01", + "nonce": "0x00", + "secretKey": "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to": "1000000000000000000000000000000000000000", + "value": [ "0x00", "0x01", "0x02" ] + }"#; + let _deserialized: MultiTransaction = serde_json::from_str(s).unwrap(); + } + + #[test] + fn state_deserialization() { + let s = r#"{ + "env": { + "currentCoinbase": "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty": "0x0100", + "currentGasLimit": "0x01c9c380", + "currentNumber": "0x00", + "currentTimestamp": "0x01", + "previousHash": "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "post": { + "EIP150": [ + { + "hash": "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", + "indexes": { "data": 0, "gas": 0, "value": 0 } + }, + { + "hash": "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", + "indexes": { "data": 0, "gas": 0, "value": 1 } + } + ], + "EIP158": [ + { + "hash": "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", + "indexes": { "data": 0, "gas": 0, "value": 0 } + }, + { + "hash": "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", + "indexes": { "data": 0, "gas": 0, "value": 1 } + } + ] + }, + "pre": { + "1000000000000000000000000000000000000000": { + "balance": "0x0de0b6b3a7640000", + "code": "0x6040600060406000600173100000000000000000000000000000000000000162055730f1600055", + "nonce": "0x00", + "storage": { + } + }, + "1000000000000000000000000000000000000001": { + "balance": "0x0de0b6b3a7640000", + "code": "0x604060006040600060027310000000000000000000000000000000000000026203d090f1600155", + "nonce": "0x00", + "storage": { + } + }, + "1000000000000000000000000000000000000002": { + "balance": "0x00", + "code": "0x600160025533600455346007553060e6553260e8553660ec553860ee553a60f055", + "nonce": "0x00", + "storage": { + } + }, + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "balance": "0x0de0b6b3a7640000", + "code": "0x", + "nonce": "0x00", + "storage": { + } + } + }, + "transaction": { + "data": [ "" ], + "gasLimit": [ "285000", "100000", "6000" ], + "gasPrice": "0x01", + "nonce": "0x00", + "secretKey": "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to": "095e7baea6a6c7c4c2dfeb977efac326af552d87", + "value": [ "10", "0" ] + } + }"#; + let _deserialized: State = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } +} diff --git a/json/src/test_helpers/tester.rs b/json/src/test_helpers/tester.rs new file mode 100644 index 0000000000..610f597230 --- /dev/null +++ b/json/src/test_helpers/tester.rs @@ -0,0 +1,27 @@ +use std::collections::BTreeMap; +use serde::Deserialize; +use serde::de::DeserializeOwned; + +/// A genric wrapper over a `BTreeMap` for tests +#[derive(Deserialize)] +pub struct GenericTester(BTreeMap); + +impl IntoIterator for GenericTester { + type Item = as IntoIterator>::Item; + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl GenericTester +where + T: DeserializeOwned + Ord, + U: DeserializeOwned +{ + /// Loads test from json. + pub fn load(reader: R) -> Result where R: std::io::Read { + serde_json::from_reader(reader) + } +} diff --git a/json/src/transaction/txtest.rs b/json/src/test_helpers/transaction.rs similarity index 85% rename from json/src/transaction/txtest.rs rename to json/src/test_helpers/transaction.rs index e72e5f60bf..7c5c621f34 100644 --- a/json/src/transaction/txtest.rs +++ b/json/src/test_helpers/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,16 +17,20 @@ //! Transaction test deserialization. use std::collections::BTreeMap; -use bytes::Bytes; -use hash::Address; -use hash::H256; -use spec::ForkSpec; +use crate::{bytes::Bytes, hash::{Address, H256}, spec::ForkSpec}; +use serde::Deserialize; + +/// Type for running `Transaction` tests +pub type Test = super::tester::GenericTester; /// Transaction test deserialization. #[derive(Debug, Deserialize)] pub struct TransactionTest { + /// RLP of the transaction pub rlp: Bytes, - pub _info: ::serde::de::IgnoredAny, + #[allow(missing_docs)] + pub _info: serde::de::IgnoredAny, + /// State of the transaction after the test runs #[serde(flatten)] pub post_state: BTreeMap, } @@ -43,8 +47,7 @@ pub struct PostState { #[cfg(test)] mod tests { - use serde_json; - use transaction::TransactionTest; + use super::TransactionTest; #[test] fn transaction_deserialization() { diff --git a/json/src/trie/input.rs b/json/src/test_helpers/trie/input.rs similarity index 94% rename from json/src/trie/input.rs rename to json/src/test_helpers/trie/input.rs index 4c56c10d7c..d32fc80e62 100644 --- a/json/src/trie/input.rs +++ b/json/src/test_helpers/trie/input.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use std::fmt; use std::collections::BTreeMap; use std::str::FromStr; -use bytes::Bytes; +use crate::bytes::Bytes; use serde::{Deserialize, Deserializer}; use serde::de::{Error as ErrorTrait, Visitor, MapAccess, SeqAccess}; @@ -89,8 +89,8 @@ impl<'a> Visitor<'a> for InputVisitor { return Err(V::Error::custom("Invalid key value pair.")); } - let ref key_str: Option = keyval[0]; - let ref val_str: Option = keyval[1]; + let key_str = &keyval[0]; + let val_str = &keyval[1]; let key = match *key_str { Some(ref k) if k.starts_with("0x") => Bytes::from_str(k).map_err(V::Error::custom)?, @@ -117,10 +117,7 @@ impl<'a> Visitor<'a> for InputVisitor { #[cfg(test)] mod tests { - use std::collections::BTreeMap; - use serde_json; - use bytes::Bytes; - use super::Input; + use super::{BTreeMap, Bytes, Input}; #[test] fn input_deserialization_from_map() { diff --git a/json/src/trie/trie.rs b/json/src/test_helpers/trie/mod.rs similarity index 79% rename from json/src/trie/trie.rs rename to json/src/test_helpers/trie/mod.rs index 756e54f435..250e08b2f4 100644 --- a/json/src/trie/trie.rs +++ b/json/src/test_helpers/trie/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,8 +16,15 @@ //! Trie test deserialization. -use hash::H256; -use trie::Input; +mod input; + +pub use self::input::Input; + +/// Type used by `trie` tests +pub type Test = super::tester::GenericTester; + +use serde::Deserialize; +use crate::hash::H256; /// Trie test deserialization. #[derive(Debug, Deserialize, PartialEq)] diff --git a/json/src/transaction.rs b/json/src/transaction.rs new file mode 100644 index 0000000000..1a4f16276e --- /dev/null +++ b/json/src/transaction.rs @@ -0,0 +1,83 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Transaction deserialization. + +use crate::{bytes::Bytes, hash::{Address, H256}, maybe::MaybeEmpty, uint::Uint}; +use serde::Deserialize; + +/// Unsigned transaction with signing information deserialization. +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Transaction { + /// Transaction data. + pub data: Bytes, + /// Gas limit. + pub gas_limit: Uint, + /// Gas price. + pub gas_price: Uint, + /// Nonce. + pub nonce: Uint, + /// To. + pub to: MaybeEmpty
, + /// Value. + pub value: Uint, + /// R. + #[serde(default)] + pub r: MaybeEmpty, + /// S. + #[serde(default)] + pub s: MaybeEmpty, + /// V. + #[serde(default)] + pub v: MaybeEmpty, + /// Secret + #[serde(rename = "secretKey")] + pub secret: Option, +} + +#[cfg(test)] +mod tests { + use super::{Bytes, H256, MaybeEmpty, Transaction, Uint}; + use ethereum_types::{H256 as Eth256, U256}; + + #[test] + fn transaction_deserialization() { + let s = r#"{ + "data" : "0x", + "gasLimit" : "0xf388", + "gasPrice" : "0x09184e72a000", + "nonce" : "0x00", + "to" : "", + "value" : "0x00", + "r": "0", + "s": "1", + "v": "2", + "secretKey": "0x0000000000000000000000000000000000000000000000000000000000000000" + }"#; + let tx: Transaction = serde_json::from_str(s).expect("JSON string is valid"); + assert_eq!(tx.data, Bytes::new(Vec::new())); + assert_eq!(tx.gas_limit, Uint(U256::from(0xf388))); + assert_eq!(tx.gas_price, Uint(U256::from(0x09184e72a000_u64))); + assert_eq!(tx.nonce, Uint(U256::zero())); + assert_eq!(tx.to, MaybeEmpty::None); + assert_eq!(tx.value, Uint(U256::zero())); + assert_eq!(tx.r, Uint(U256::zero()).into()); + assert_eq!(tx.s, Uint(U256::one()).into()); + assert_eq!(tx.v, Uint(U256::from(2)).into()); + assert_eq!(tx.secret, Some(H256(Eth256::zero()))); + } +} diff --git a/json/src/transaction/mod.rs b/json/src/transaction/mod.rs deleted file mode 100644 index f845fe3340..0000000000 --- a/json/src/transaction/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Transaction test deserialization. - -mod transaction; -mod txtest; -mod test; - -pub use self::transaction::Transaction; -pub use self::txtest::TransactionTest; -pub use self::test::Test; diff --git a/json/src/transaction/test.rs b/json/src/transaction/test.rs deleted file mode 100644 index ee9d4110f1..0000000000 --- a/json/src/transaction/test.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! TransactionTest test deserializer. - -use std::collections::BTreeMap; -use std::io::Read; -use serde_json; -use serde_json::Error; -use transaction::TransactionTest; - -/// TransactionTest test deserializer. -#[derive(Debug, Deserialize)] -pub struct Test(BTreeMap); - -impl IntoIterator for Test { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Test { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } -} diff --git a/json/src/transaction/transaction.rs b/json/src/transaction/transaction.rs deleted file mode 100644 index 718d3080bd..0000000000 --- a/json/src/transaction/transaction.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Transaction test transaction deserialization. - -use uint::Uint; -use bytes::Bytes; -use hash::Address; -use maybe::MaybeEmpty; - -/// Transaction test transaction deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Transaction { - /// Transaction data. - pub data: Bytes, - /// Gas limit. - pub gas_limit: Uint, - /// Gas price. - pub gas_price: Uint, - /// Nonce. - pub nonce: Uint, - /// To. - pub to: MaybeEmpty
, - /// Value. - pub value: Uint, - /// R. - pub r: Uint, - /// S. - pub s: Uint, - /// V. - pub v: Uint, -} - -#[cfg(test)] -mod tests { - use serde_json; - use transaction::Transaction; - - #[test] - fn transaction_deserialization() { - let s = r#"{ - "data" : "0x", - "gasLimit" : "0xf388", - "gasPrice" : "0x09184e72a000", - "nonce" : "0x00", - "r" : "0x2c", - "s" : "0x04", - "to" : "", - "v" : "0x1b", - "value" : "0x00" - }"#; - let _deserialized: Transaction = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/json/src/trie/test.rs b/json/src/trie/test.rs deleted file mode 100644 index 39876da0a1..0000000000 --- a/json/src/trie/test.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! TransactionTest test deserializer. - -use std::collections::BTreeMap; -use std::io::Read; -use serde_json; -use serde_json::Error; -use trie::Trie; - -/// TransactionTest test deserializer. -#[derive(Debug, PartialEq, Deserialize)] -pub struct Test(BTreeMap); - -impl IntoIterator for Test { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Test { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } -} diff --git a/json/src/uint.rs b/json/src/uint.rs index 3f1d02f03e..ed9b568379 100644 --- a/json/src/uint.rs +++ b/json/src/uint.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -34,22 +34,17 @@ impl Into for Uint { impl Into for Uint { fn into(self) -> u64 { - u64::from(self.0) + self.0.low_u64() } } impl Into for Uint { fn into(self) -> usize { - // TODO: clean it after util conversions refactored. - u64::from(self.0) as usize - } -} -impl Into for Uint { - fn into(self) -> u8 { - u64::from(self.0) as u8 + self.0.low_u64() as usize } } + impl Serialize for Uint { fn serialize(&self, serializer: S) -> Result where S: Serializer { @@ -81,9 +76,14 @@ impl<'a> Visitor<'a> for UintVisitor { let value = match value.len() { 0 => U256::from(0), 2 if value.starts_with("0x") => U256::from(0), - _ if value.starts_with("0x") => U256::from_str(&value[2..]).map_err(|e| { - Error::custom(format!("Invalid hex value {}: {}", value, e).as_str()) - })?, + _ if value.starts_with("0x") => { + if value.len() > 66 { + return Err(Error::custom(format!("Invalid hex value {}: value too big", value).as_str())); + } + U256::from_str(&value[2..]).map_err(|e| { + Error::custom(format!("Invalid hex value {}: {}", value, e).as_str()) + })? + }, _ => U256::from_dec_str(value).map_err(|e| { Error::custom(format!("Invalid decimal value {}: {:?}", value, e).as_str()) })? @@ -97,22 +97,24 @@ impl<'a> Visitor<'a> for UintVisitor { } } +/// Deserialize and validate that the value is non-zero pub fn validate_non_zero<'de, D>(d: D) -> Result where D: Deserializer<'de> { let value = Uint::deserialize(d)?; if value == Uint(U256::from(0)) { - return Err(Error::invalid_value(Unexpected::Unsigned(value.into()), &"a non-zero value")) + return Err(Error::invalid_value(Unexpected::Unsigned(0), &"a non-zero value")) } Ok(value) } +/// Deserialize and validate that the value is non-zero pub fn validate_optional_non_zero<'de, D>(d: D) -> Result, D::Error> where D: Deserializer<'de> { let value: Option = Option::deserialize(d)?; if let Some(value) = value { if value == Uint(U256::from(0)) { - return Err(Error::invalid_value(Unexpected::Unsigned(value.into()), &"a non-zero value")) + return Err(Error::invalid_value(Unexpected::Unsigned(0), &"a non-zero value")) } } @@ -121,9 +123,8 @@ pub fn validate_optional_non_zero<'de, D>(d: D) -> Result, D::Error #[cfg(test)] mod test { - use serde_json; + use super::Uint; use ethereum_types::U256; - use uint::Uint; #[test] fn uint_deserialization() { @@ -138,6 +139,18 @@ mod test { ]); } + #[test] + fn uint_deserialization_error_for_hex_too_large() { + let hex = format!("0x{}", "1".repeat(65)); + let result: Result = serde_json::from_str(&format!(r#""{}""#, hex)); + let err = result.unwrap_err(); + assert!(err.is_data()); + assert_eq!( + err.to_string(), + format!("Invalid hex value {}: value too big at line 1 column 69", hex) + ); + } + #[test] fn uint_into() { assert_eq!(U256::from(10), Uint(U256::from(10)).into()); diff --git a/json/src/vm.rs b/json/src/vm.rs new file mode 100644 index 0000000000..d6150b0d95 --- /dev/null +++ b/json/src/vm.rs @@ -0,0 +1,277 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Vm json deserialization + +use crate::{ + bytes::Bytes, + hash::{Address, H256}, + maybe::MaybeEmpty, + spec::State, + uint::Uint, +}; +use serde::Deserialize; + +/// Represents vm execution environment before and after execution of transaction. +#[derive(Debug, PartialEq, Deserialize)] +pub struct Vm { + /// Contract calls made internaly by executed transaction. + #[serde(rename = "callcreates")] + pub calls: Option>, + /// Env info. + pub env: Env, + /// Executed transaction + #[serde(rename = "exec")] + pub transaction: Transaction, + /// Gas left after transaction execution. + #[serde(rename = "gas")] + pub gas_left: Option, + /// Hash of logs created during execution of transaction. + pub logs: Option, + /// Transaction output. + #[serde(rename = "out")] + pub output: Option, + /// Post execution vm state. + #[serde(rename = "post")] + pub post_state: Option, + /// Pre execution vm state. + #[serde(rename = "pre")] + pub pre_state: State, +} + +impl Vm { + /// Returns true if transaction execution run out of gas. + pub fn out_of_gas(&self) -> bool { + self.calls.is_none() + } +} + +/// Call deserialization. +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Call { + /// Call data. + pub data: Bytes, + /// Call destination. + pub destination: MaybeEmpty
, + /// Gas limit. + pub gas_limit: Uint, + /// Call value. + pub value: Uint, +} + +/// Executed transaction. +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Transaction { + /// Contract address. + pub address: Address, + /// Transaction sender. + #[serde(rename = "caller")] + pub sender: Address, + /// Contract code. + pub code: Bytes, + /// Input data. + pub data: Bytes, + /// Gas. + pub gas: Uint, + /// Gas price. + pub gas_price: Uint, + /// Transaction origin. + pub origin: Address, + /// Sent value. + pub value: Uint, + /// Contract code version. + #[serde(default)] + pub code_version: Uint, +} + +/// Environment. +#[derive(Debug, PartialEq, Deserialize)] +pub struct Env { + /// Address. + #[serde(rename = "currentCoinbase")] + pub author: Address, + /// Difficulty + #[serde(rename = "currentDifficulty")] + pub difficulty: Uint, + /// Gas limit. + #[serde(rename = "currentGasLimit")] + pub gas_limit: Uint, + /// Number. + #[serde(rename = "currentNumber")] + pub number: Uint, + /// Timestamp. + #[serde(rename = "currentTimestamp")] + pub timestamp: Uint, +} + +#[cfg(test)] +mod tests { + use std::{ + collections::BTreeMap, + str::FromStr + }; + use super::{Address, Bytes, Call, Env, H256, MaybeEmpty, State, Transaction, Uint, Vm}; + + use crate::spec::{Account, HashOrMap}; + use ethereum_types::{U256, H160 as Hash160, H256 as Hash256}; + use macros::map; + use rustc_hex::FromHex; + + const TEST_CODE: &str = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055"; + + #[test] + fn vm_deserialization() { + let s = r#"{ + "callcreates" : [ + ], + "env" : { + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "currentDifficulty" : "0x0100", + "currentGasLimit" : "0x0f4240", + "currentNumber" : "0x00", + "currentTimestamp" : "0x01" + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", + "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", + "data" : "0x", + "gas" : "0x0186a0", + "gasPrice" : "0x5af3107a4000", + "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", + "value" : "0x0de0b6b3a7640000" + }, + "gas" : "0x013874", + "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "out" : "0x", + "post" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "0x0de0b6b3a7640000", + "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", + "nonce" : "0x00", + "storage" : { + "0x00" : "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" + } + } + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "0x0de0b6b3a7640000", + "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", + "nonce" : "0x00", + "storage" : { + } + } + } + }"#; + let vm: Vm = serde_json::from_str(s).expect("JSON is valid"); + assert_eq!(vm.calls, Some(Vec::new())); + assert_eq!(vm.env, Env { + author: Address(Hash160::from_str("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap()), + difficulty: Uint(0x0100.into()), + gas_limit: Uint(0x0f4240.into()), + number: Uint(0.into()), + timestamp: Uint(1.into()) + }); + assert_eq!(vm.transaction, Transaction { + address: Address(Hash160::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap()), + sender: Address(Hash160::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap()), + code: Bytes::new(TEST_CODE.from_hex().unwrap()), + code_version: Uint(0.into()), + data: Bytes::new(Vec::new()), + gas: Uint(0x0186a0.into()), + gas_price: Uint(0x5af3107a4000_u64.into()), + origin: Address(Hash160::from_str("cd1722f2947def4cf144679da39c4c32bdc35681").unwrap()), + value: Uint(0x0de0b6b3a7640000_u64.into()) + }); + assert_eq!(vm.gas_left, Some(Uint(0x013874.into()))); + assert_eq!( + vm.logs, + Some(H256(Hash256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap())) + ); + assert_eq!(vm.output, Some(Bytes::new(Vec::new()))); + assert_eq!(vm.pre_state, State( + HashOrMap::Map( + map![ + Address(Hash160::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap()) => Account { + builtin: None, + balance: Some(Uint(0x0de0b6b3a7640000_u64.into())), + code: Some(Bytes::new(TEST_CODE.from_hex().unwrap())), + constructor: None, + nonce: Some(Uint(0.into())), + storage: Some(map![]), + version: None, + } + ])) + ); + assert_eq!(vm.post_state, Some( + State( + HashOrMap::Map( + map![ + Address(Hash160::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap()) => Account { + builtin: None, + balance: Some(Uint(0x0de0b6b3a7640000_u64.into())), + code: Some(Bytes::new(TEST_CODE.from_hex().unwrap())), + constructor: None, + nonce: Some(Uint(0.into())), + storage: Some(map![ + Uint(0.into()) => Uint(U256::from_str("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe").unwrap()) + ]), + version: None, + }])) + ) + ); + } + + #[test] + fn call_deserialization_empty_dest() { + let s = r#"{ + "data" : "0x1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff", + "destination" : "", + "gasLimit" : "0x1748766aa5", + "value" : "0x00" + }"#; + let call: Call = serde_json::from_str(s).unwrap(); + + assert_eq!(&call.data[..], + &[0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77, + 0x88, 0x88, 0x99, 0x99, 0x00, 0x00, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, + 0xee, 0xee, 0xff, 0xff]); + + assert_eq!(call.destination, MaybeEmpty::None); + assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); + assert_eq!(call.value, Uint(U256::from(0))); + } + + #[test] + fn call_deserialization_full_dest() { + let s = r#"{ + "data" : "0x1234", + "destination" : "5a39ed1020c04d4d84539975b893a4e7c53eab6c", + "gasLimit" : "0x1748766aa5", + "value" : "0x00" + }"#; + + let call: Call = serde_json::from_str(s).unwrap(); + + assert_eq!(&call.data[..], &[0x12, 0x34]); + assert_eq!(call.destination, MaybeEmpty::Some(Address(Hash160::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c").unwrap()))); + assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); + assert_eq!(call.value, Uint(U256::from(0))); + } +} diff --git a/json/src/vm/call.rs b/json/src/vm/call.rs deleted file mode 100644 index aa75862f0d..0000000000 --- a/json/src/vm/call.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Vm call deserialization. - -use bytes::Bytes; -use hash::Address; -use uint::Uint; -use maybe::MaybeEmpty; - -/// Vm call deserialization. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Call { - /// Call data. - pub data: Bytes, - /// Call destination. - pub destination: MaybeEmpty
, - /// Gas limit. - pub gas_limit: Uint, - /// Call value. - pub value: Uint, -} - -#[cfg(test)] -mod tests { - use serde_json; - use vm::Call; - use ethereum_types::{U256, H160 as Hash160}; - use uint::Uint; - use hash::Address; - use maybe::MaybeEmpty; - use std::str::FromStr; - - #[test] - fn call_deserialization_empty_dest() { - let s = r#"{ - "data" : "0x1111222233334444555566667777888899990000aaaabbbbccccddddeeeeffff", - "destination" : "", - "gasLimit" : "0x1748766aa5", - "value" : "0x00" - }"#; - let call: Call = serde_json::from_str(s).unwrap(); - - assert_eq!(&call.data[..], - &[0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77, - 0x88, 0x88, 0x99, 0x99, 0x00, 0x00, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, - 0xee, 0xee, 0xff, 0xff]); - - assert_eq!(call.destination, MaybeEmpty::None); - assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); - assert_eq!(call.value, Uint(U256::from(0))); - } - - #[test] - fn call_deserialization_full_dest() { - let s = r#"{ - "data" : "0x1234", - "destination" : "5a39ed1020c04d4d84539975b893a4e7c53eab6c", - "gasLimit" : "0x1748766aa5", - "value" : "0x00" - }"#; - - let call: Call = serde_json::from_str(s).unwrap(); - - assert_eq!(&call.data[..], &[0x12, 0x34]); - assert_eq!(call.destination, MaybeEmpty::Some(Address(Hash160::from_str("5a39ed1020c04d4d84539975b893a4e7c53eab6c").unwrap()))); - assert_eq!(call.gas_limit, Uint(U256::from(0x1748766aa5u64))); - assert_eq!(call.value, Uint(U256::from(0))); - } -} diff --git a/json/src/vm/env.rs b/json/src/vm/env.rs deleted file mode 100644 index e06812c0a8..0000000000 --- a/json/src/vm/env.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Vm environment. -use hash::Address; -use uint::Uint; - -/// Vm environment. -#[derive(Debug, PartialEq, Deserialize)] -pub struct Env { - /// Address. - #[serde(rename = "currentCoinbase")] - pub author: Address, - /// Difficulty - #[serde(rename = "currentDifficulty")] - pub difficulty: Uint, - /// Gas limit. - #[serde(rename = "currentGasLimit")] - pub gas_limit: Uint, - /// Number. - #[serde(rename = "currentNumber")] - pub number: Uint, - /// Timestamp. - #[serde(rename = "currentTimestamp")] - pub timestamp: Uint, -} - -#[cfg(test)] -mod tests { - use serde_json; - use vm::Env; - - #[test] - fn env_deserialization() { - let s = r#"{ - "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentDifficulty" : "0x0100", - "currentGasLimit" : "0x0f4240", - "currentNumber" : "0x00", - "currentTimestamp" : "0x01" - }"#; - let _deserialized: Env = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/json/src/vm/mod.rs b/json/src/vm/mod.rs deleted file mode 100644 index d8f99e2002..0000000000 --- a/json/src/vm/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Vm test loader. - -pub mod env; -pub mod transaction; -pub mod vm; -pub mod call; -pub mod test; - -pub use self::env::Env; -pub use self::transaction::Transaction; -pub use self::vm::Vm; -pub use self::call::Call; -pub use self::test::Test; diff --git a/json/src/vm/test.rs b/json/src/vm/test.rs deleted file mode 100644 index 9dfe814ae8..0000000000 --- a/json/src/vm/test.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Vm test deserializer. - -use std::collections::BTreeMap; -use std::io::Read; -use serde_json; -use serde_json::Error; -use vm::Vm; - -/// Vm test deserializer. -#[derive(Debug, PartialEq, Deserialize)] -pub struct Test(BTreeMap); - -impl IntoIterator for Test { - type Item = as IntoIterator>::Item; - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Test { - /// Loads test from json. - pub fn load(reader: R) -> Result where R: Read { - serde_json::from_reader(reader) - } -} diff --git a/json/src/vm/transaction.rs b/json/src/vm/transaction.rs deleted file mode 100644 index 4a9374531d..0000000000 --- a/json/src/vm/transaction.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Executed transaction. -use hash::Address; -use uint::Uint; -use bytes::Bytes; - -/// Executed transaction. -#[derive(Debug, PartialEq, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Transaction { - /// Contract address. - pub address: Address, - /// Transaction sender. - #[serde(rename = "caller")] - pub sender: Address, - /// Contract code. - pub code: Bytes, - /// Input data. - pub data: Bytes, - /// Gas. - pub gas: Uint, - /// Gas price. - pub gas_price: Uint, - /// Transaction origin. - pub origin: Address, - /// Sent value. - pub value: Uint, -} - -#[cfg(test)] -mod tests { - use serde_json; - use vm::Transaction; - - #[test] - fn transaction_deserialization() { - let s = r#"{ - "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", - "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", - "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", - "data" : "0x", - "gas" : "0x0186a0", - "gasPrice" : "0x5af3107a4000", - "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", - "value" : "0x0de0b6b3a7640000" - }"#; - let _deserialized: Transaction = serde_json::from_str(s).unwrap(); - } -} diff --git a/json/src/vm/vm.rs b/json/src/vm/vm.rs deleted file mode 100644 index 1fbb937cb7..0000000000 --- a/json/src/vm/vm.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Vm execution env. - -use bytes::Bytes; -use uint::Uint; -use hash::H256; -use blockchain::State; -use vm::{Transaction, Call, Env}; - -/// Represents vm execution environment before and after execution of transaction. -#[derive(Debug, PartialEq, Deserialize)] -pub struct Vm { - /// Contract calls made internaly by executed transaction. - #[serde(rename = "callcreates")] - pub calls: Option>, - /// Env info. - pub env: Env, - /// Executed transaction - #[serde(rename = "exec")] - pub transaction: Transaction, - /// Gas left after transaction execution. - #[serde(rename = "gas")] - pub gas_left: Option, - /// Hash of logs created during execution of transaction. - pub logs: Option, - /// Transaction output. - #[serde(rename = "out")] - pub output: Option, - /// Post execution vm state. - #[serde(rename = "post")] - pub post_state: Option, - /// Pre execution vm state. - #[serde(rename = "pre")] - pub pre_state: State, -} - -impl Vm { - /// Returns true if transaction execution run out of gas. - pub fn out_of_gas(&self) -> bool { - self.calls.is_none() - } -} - -#[cfg(test)] -mod tests { - use serde_json; - use vm::Vm; - - #[test] - fn vm_deserialization() { - let s = r#"{ - "callcreates" : [ - ], - "env" : { - "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentDifficulty" : "0x0100", - "currentGasLimit" : "0x0f4240", - "currentNumber" : "0x00", - "currentTimestamp" : "0x01" - }, - "exec" : { - "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", - "caller" : "cd1722f2947def4cf144679da39c4c32bdc35681", - "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", - "data" : "0x", - "gas" : "0x0186a0", - "gasPrice" : "0x5af3107a4000", - "origin" : "cd1722f2947def4cf144679da39c4c32bdc35681", - "value" : "0x0de0b6b3a7640000" - }, - "gas" : "0x013874", - "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - "out" : "0x", - "network" : "Frontier", - "post" : { - "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { - "balance" : "0x0de0b6b3a7640000", - "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", - "nonce" : "0x00", - "storage" : { - "0x00" : "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" - } - } - }, - "pre" : { - "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { - "balance" : "0x0de0b6b3a7640000", - "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055", - "nonce" : "0x00", - "storage" : { - } - } - } - }"#; - let _deserialized: Vm = serde_json::from_str(s).unwrap(); - // TODO: validate all fields - } -} diff --git a/license_header b/license_header index 2fc672e4f1..0c290dd8f7 100644 --- a/license_header +++ b/license_header @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/miner/Cargo.toml b/miner/Cargo.toml index 7432b10512..43fc1e4d4a 100644 --- a/miner/Cargo.toml +++ b/miner/Cargo.toml @@ -11,32 +11,35 @@ authors = ["Parity Technologies "] ethash = { path = "../ethash", optional = true } fetch = { path = "../util/fetch", optional = true } hyper = { version = "0.12", optional = true } -url = { version = "1", optional = true } +url = { version = "2", optional = true } # Miner -ansi_term = "0.10" +ansi_term = "0.11" common-types = { path = "../ethcore/types" } -error-chain = "0.12" -ethabi = "6.0" -ethabi-derive = "6.0" -ethabi-contract = "6.0" +ethabi = "9.0.1" +ethabi-derive = "9.0.1" +ethabi-contract = "9.0.0" ethcore-call-contract = { path = "../ethcore/call-contract" } -ethereum-types = "0.4" +ethereum-types = "0.8.0" futures = "0.1" -heapsize = "0.4" -keccak-hash = "0.1" +parity-util-mem = "0.3.0" +keccak-hash = "0.4.0" linked-hash-map = "0.5" log = "0.4" parity-runtime = { path = "../util/runtime" } -parking_lot = "0.7" +parking_lot = "0.9" price-info = { path = "./price-info", optional = true } -rlp = { version = "0.3.0", features = ["ethereum"] } +registrar = { path = "../util/registrar" } +rlp = "0.4.0" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" trace-time = "0.1" transaction-pool = "2.0.1" [dev-dependencies] env_logger = "0.5" -ethkey = { path = "../accounts/ethkey" } +parity-crypto = { version = "0.4.2", features = ["publickey"] } rustc-hex = "1.0" [features] diff --git a/miner/local-store/Cargo.toml b/miner/local-store/Cargo.toml index d16c644071..10ce145175 100644 --- a/miner/local-store/Cargo.toml +++ b/miner/local-store/Cargo.toml @@ -3,17 +3,19 @@ name = "parity-local-store" description = "Manages persistent local node data." version = "0.1.0" authors = ["Parity Technologies "] +edition = "2018" [dependencies] common-types = { path = "../../ethcore/types" } ethcore-io = { path = "../../util/io" } -kvdb = "0.1" +kvdb = "0.3.1" log = "0.4" -rlp = { version = "0.3.0", features = ["ethereum"] } +rlp = "0.4.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" [dev-dependencies] ethkey = { path = "../../accounts/ethkey" } -kvdb-memorydb = "0.1" +parity-crypto = { version = "0.4.2", features = ["publickey"] } +kvdb-memorydb = "0.3.1" diff --git a/miner/local-store/src/lib.rs b/miner/local-store/src/lib.rs index 56c573b06f..b9f6e79295 100644 --- a/miner/local-store/src/lib.rs +++ b/miner/local-store/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,62 +16,32 @@ //! Manages local node data: pending local transactions, sync security level +use std::io; use std::sync::Arc; -use std::fmt; use std::time::Duration; -use types::transaction::{ - SignedTransaction, PendingTransaction, UnverifiedTransaction, - Condition as TransactionCondition +use common_types::{ + BlockNumber, + transaction::{ + SignedTransaction, PendingTransaction, UnverifiedTransaction, + Condition as TransactionCondition + } }; -use io::IoHandler; -use rlp::Rlp; +use ethcore_io::{IoHandler, TimerToken, IoContext}; use kvdb::KeyValueDB; - -extern crate common_types as types; -extern crate ethcore_io as io; -extern crate rlp; -extern crate serde_json; -extern crate serde; -extern crate kvdb; - -#[macro_use] -extern crate serde_derive; - -#[macro_use] -extern crate log; - -#[cfg(test)] -extern crate ethkey; -#[cfg(test)] -extern crate kvdb_memorydb; +use log::{debug, trace, warn}; +use rlp::Rlp; +use serde_derive::{Serialize, Deserialize}; +use serde_json; const LOCAL_TRANSACTIONS_KEY: &'static [u8] = &*b"LOCAL_TXS"; -const UPDATE_TIMER: ::io::TimerToken = 0; +const UPDATE_TIMER: TimerToken = 0; 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)] -pub enum Error { - /// Io and database errors: these manifest as `String`s. - Io(::std::io::Error), - /// JSON errors. - Json(::serde_json::Error), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Io(ref val) => write!(f, "{}", val), - Error::Json(ref err) => write!(f, "{}", err), - } - } -} - #[derive(Serialize, Deserialize)] enum Condition { - Number(types::BlockNumber), + Number(BlockNumber), Timestamp(u64), } @@ -137,11 +107,11 @@ pub trait NodeInfo: Send + Sync { /// Create a new local data store, given a database, a column to write to, and a node. /// Attempts to read data out of the store, and move it into the node. -pub fn create(db: Arc, col: Option, node: T) -> LocalDataStore { +pub fn create(db: Arc, col: u32, node: T) -> LocalDataStore { LocalDataStore { - db: db, - col: col, - node: node, + db, + col, + node, } } @@ -150,17 +120,16 @@ pub fn create(db: Arc, col: Option, node: T) -> Lo /// In specific, this will be used to store things like unpropagated local transactions /// and the node security level. pub struct LocalDataStore { - db: Arc, - col: Option, + db: Arc, + col: u32, node: T, } impl LocalDataStore { /// Attempt to read pending transactions out of the local store. - pub fn pending_transactions(&self) -> Result, Error> { - if let Some(val) = self.db.get(self.col, LOCAL_TRANSACTIONS_KEY).map_err(Error::Io)? { - let local_txs: Vec<_> = ::serde_json::from_slice::>(&val) - .map_err(Error::Json)? + pub fn pending_transactions(&self) -> io::Result> { + if let Some(val) = self.db.get(self.col, LOCAL_TRANSACTIONS_KEY)? { + let local_txs: Vec<_> = serde_json::from_slice::>(&val)? .into_iter() .filter_map(TransactionEntry::into_pending) .collect(); @@ -172,7 +141,7 @@ impl LocalDataStore { } /// Update the entries in the database. - pub fn update(&self) -> Result<(), Error> { + pub fn update(&self) -> io::Result<()> { trace!(target: "local_store", "Updating local store entries."); let local_entries: Vec = self.node.pending_transactions() @@ -184,32 +153,32 @@ impl LocalDataStore { } /// Clear data in this column. - pub fn clear(&self) -> Result<(), Error> { + pub fn clear(&self) -> io::Result<()> { trace!(target: "local_store", "Clearing local store entries."); self.write_txs(&[]) } // helper for writing a vector of transaction entries to disk. - fn write_txs(&self, txs: &[TransactionEntry]) -> Result<(), Error> { + fn write_txs(&self, txs: &[TransactionEntry]) -> io::Result<()> { let mut batch = self.db.transaction(); - let local_json = ::serde_json::to_value(txs).map_err(Error::Json)?; + let local_json = serde_json::to_value(txs)?; let json_str = format!("{}", local_json); batch.put_vec(self.col, LOCAL_TRANSACTIONS_KEY, json_str.into_bytes()); - self.db.write(batch).map_err(Error::Io) + self.db.write(batch) } } impl IoHandler for LocalDataStore { - fn initialize(&self, io: &::io::IoContext) { + fn initialize(&self, io: &IoContext) { if let Err(e) = io.register_timer(UPDATE_TIMER, UPDATE_TIMEOUT) { warn!(target: "local_store", "Error registering local store update timer: {}", e); } } - fn timeout(&self, _io: &::io::IoContext, timer: ::io::TimerToken) { + fn timeout(&self, _io: &IoContext, timer: TimerToken) { if let UPDATE_TIMER = timer { if let Err(e) = self.update() { debug!(target: "local_store", "Error updating local store: {}", e); @@ -231,8 +200,9 @@ mod tests { use super::NodeInfo; use std::sync::Arc; - use types::transaction::{Transaction, Condition, PendingTransaction}; - use ethkey::{Brain, Generator}; + use common_types::transaction::{Transaction, Condition, PendingTransaction}; + use ethkey::Brain; + use parity_crypto::publickey::Generator; // we want to test: round-trip of good transactions. // failure to roundtrip bad transactions (but that it doesn't panic) @@ -244,15 +214,15 @@ mod tests { #[test] fn twice_empty() { - let db = Arc::new(::kvdb_memorydb::create(0)); + let db = Arc::new(::kvdb_memorydb::create(1)); { - let store = super::create(db.clone(), None, Dummy(vec![])); + let store = super::create(db.clone(), 0, Dummy(vec![])); assert_eq!(store.pending_transactions().unwrap(), vec![]) } { - let store = super::create(db.clone(), None, Dummy(vec![])); + let store = super::create(db.clone(), 0, Dummy(vec![])); assert_eq!(store.pending_transactions().unwrap(), vec![]) } } @@ -273,21 +243,21 @@ mod tests { PendingTransaction::new(signed, condition) }).collect(); - let db = Arc::new(::kvdb_memorydb::create(0)); + let db = Arc::new(::kvdb_memorydb::create(1)); { // nothing written yet, will write pending. - let store = super::create(db.clone(), None, Dummy(transactions.clone())); + let store = super::create(db.clone(), 0, Dummy(transactions.clone())); assert_eq!(store.pending_transactions().unwrap(), vec![]) } { // pending written, will write nothing. - let store = super::create(db.clone(), None, Dummy(vec![])); + let store = super::create(db.clone(), 0, Dummy(vec![])); assert_eq!(store.pending_transactions().unwrap(), transactions) } { // pending removed, will write nothing. - let store = super::create(db.clone(), None, Dummy(vec![])); + let store = super::create(db.clone(), 0, Dummy(vec![])); assert_eq!(store.pending_transactions().unwrap(), vec![]) } } @@ -312,15 +282,15 @@ mod tests { PendingTransaction::new(signed, None) }); - let db = Arc::new(::kvdb_memorydb::create(0)); + let db = Arc::new(::kvdb_memorydb::create(1)); { // nothing written, will write bad. - let store = super::create(db.clone(), None, Dummy(transactions.clone())); + let store = super::create(db.clone(), 0, Dummy(transactions.clone())); assert_eq!(store.pending_transactions().unwrap(), vec![]) } { // try to load transactions. The last transaction, which is invalid, will be skipped. - let store = super::create(db.clone(), None, Dummy(vec![])); + let store = super::create(db.clone(), 0, Dummy(vec![])); let loaded = store.pending_transactions().unwrap(); transactions.pop(); assert_eq!(loaded, transactions); diff --git a/miner/price-info/Cargo.toml b/miner/price-info/Cargo.toml index 0b72fd37b3..23f69e37c2 100644 --- a/miner/price-info/Cargo.toml +++ b/miner/price-info/Cargo.toml @@ -5,14 +5,14 @@ license = "GPL-3.0" name = "price-info" version = "1.12.0" authors = ["Parity Technologies "] +edition = "2018" [dependencies] fetch = { path = "../../util/fetch" } futures = "0.1" -parity-runtime = { path = "../../util/runtime" } log = "0.4" +parity-runtime = { path = "../../util/runtime" } serde_json = "1.0" [dev-dependencies] -parking_lot = "0.7" fake-fetch = { path = "../../util/fake-fetch" } diff --git a/miner/price-info/src/lib.rs b/miner/price-info/src/lib.rs index 4117f84c52..e590b61a5e 100644 --- a/miner/price-info/src/lib.rs +++ b/miner/price-info/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,28 +18,14 @@ //! A simple client to get the current ETH price using an external API. -extern crate futures; -extern crate serde_json; -extern crate parity_runtime; - -#[macro_use] -extern crate log; - -#[cfg(test)] -extern crate fake_fetch; - -pub extern crate fetch; - -use std::cmp; -use std::fmt; -use std::io; -use std::str; - +use std::{cmp, fmt, io, str}; use fetch::{Client as FetchClient, Fetch}; use futures::{Future, Stream}; -use futures::future::{self, Either}; -use serde_json::Value; +use log::warn; use parity_runtime::Executor; +use serde_json::Value; + +pub use fetch; /// Current ETH price information. #[derive(Debug)] @@ -48,27 +34,6 @@ pub struct PriceInfo { pub ethusd: f32, } -/// Price info error. -#[derive(Debug)] -pub enum Error { - /// The API returned an unexpected status code. - StatusCode(&'static str), - /// The API returned an unexpected status content. - UnexpectedResponse(Option), - /// There was an error when trying to reach the API. - Fetch(fetch::Error), - /// IO error when reading API response. - Io(io::Error), -} - -impl From for Error { - fn from(err: io::Error) -> Self { Error::Io(err) } -} - -impl From for Error { - fn from(err: fetch::Error) -> Self { Error::Fetch(err) } -} - /// A client to get the current ETH price using an external API. pub struct Client { pool: Executor, @@ -92,22 +57,14 @@ impl cmp::PartialEq for Client { impl Client { /// Creates a new instance of the `Client` given a `fetch::Client`. - pub fn new(fetch: F, pool: Executor) -> Client { - let api_endpoint = "https://api.etherscan.io/api?module=stats&action=ethprice".to_owned(); + pub fn new(fetch: F, pool: Executor, api_endpoint: String) -> Client { Client { pool, api_endpoint, fetch } } /// Gets the current ETH price and calls `set_price` with the result. 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() { - let s = Error::StatusCode(response.status().canonical_reason().unwrap_or("unknown")); - return Either::A(future::err(s)); - } - Either::B(response.concat2().from_err()) - }) + .and_then(|response| response.concat2()) .and_then(move |body| { let body_str = str::from_utf8(&body).ok(); let value: Option = body_str.and_then(|s| serde_json::from_str(s).ok()); @@ -123,7 +80,11 @@ impl Client { set_price(PriceInfo { ethusd }); Ok(()) }, - None => Err(Error::UnexpectedResponse(body_str.map(From::from))), + None => { + let msg = format!("Unexpected response: {}", body_str.unwrap_or_default()); + let err = io::Error::new(io::ErrorKind::Other, msg); + Err(fetch::Error::Io(err)) + } } }) .map_err(|err| { @@ -135,18 +96,19 @@ impl Client { #[cfg(test)] mod test { - use std::sync::Arc; - use parity_runtime::{Runtime, Executor}; - use Client; - use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::{ + Arc, atomic::{AtomicBool, Ordering} + }; use fake_fetch::FakeFetch; + use parity_runtime::{Runtime, Executor}; + use super::Client; fn price_info_ok(response: &str, executor: Executor) -> Client> { - Client::new(FakeFetch::new(Some(response.to_owned())), executor) + Client::new(FakeFetch::new(Some(response.to_owned())), executor, "fake_endpoint".to_owned()) } fn price_info_not_found(executor: Executor) -> Client> { - Client::new(FakeFetch::new(None::), executor) + Client::new(FakeFetch::new(None::), executor, "fake_endpoint".to_owned()) } #[test] diff --git a/miner/src/external.rs b/miner/src/external.rs index f68e065abd..6e38868e18 100644 --- a/miner/src/external.rs +++ b/miner/src/external.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -84,7 +84,7 @@ mod tests { // given let m = miner(); assert_eq!(m.hashrate(), U256::from(0)); - m.submit_hashrate(U256::from(10), H256::from(1)); + m.submit_hashrate(U256::from(10), H256::from_low_u64_be(1)); assert_eq!(m.hashrate(), U256::from(10)); // when @@ -99,12 +99,12 @@ mod tests { // given let m = miner(); assert_eq!(m.hashrate(), U256::from(0)); - m.submit_hashrate(U256::from(10), H256::from(1)); + m.submit_hashrate(U256::from(10), H256::from_low_u64_be(1)); assert_eq!(m.hashrate(), U256::from(10)); // when - m.submit_hashrate(U256::from(15), H256::from(1)); - m.submit_hashrate(U256::from(20), H256::from(2)); + m.submit_hashrate(U256::from(15), H256::from_low_u64_be(1)); + m.submit_hashrate(U256::from(20), H256::from_low_u64_be(2)); // then assert_eq!(m.hashrate(), U256::from(35)); diff --git a/miner/src/gas_price_calibrator.rs b/miner/src/gas_price_calibrator.rs index 7a0943640c..c176d996ab 100644 --- a/miner/src/gas_price_calibrator.rs +++ b/miner/src/gas_price_calibrator.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -43,11 +43,11 @@ pub struct GasPriceCalibrator { impl GasPriceCalibrator { /// Create a new gas price calibrator. - pub fn new(options: GasPriceCalibratorOptions, fetch: FetchClient, p: Executor) -> GasPriceCalibrator { + pub fn new(options: GasPriceCalibratorOptions, fetch: FetchClient, p: Executor, api_endpoint: String) -> GasPriceCalibrator { GasPriceCalibrator { options: options, next_calibration: Instant::now(), - price_info: PriceInfoClient::new(fetch, p), + price_info: PriceInfoClient::new(fetch, p, api_endpoint), } } diff --git a/miner/src/gas_pricer.rs b/miner/src/gas_pricer.rs index c4e04442f1..1e48cb692d 100644 --- a/miner/src/gas_pricer.rs +++ b/miner/src/gas_pricer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/miner/src/lib.rs b/miner/src/lib.rs index 55091093ac..9ac9f9ad7f 100644 --- a/miner/src/lib.rs +++ b/miner/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -25,31 +25,32 @@ extern crate ethabi; extern crate ethcore_call_contract as call_contract; extern crate ethereum_types; extern crate futures; -extern crate heapsize; + +extern crate parity_util_mem; extern crate keccak_hash as hash; extern crate linked_hash_map; extern crate parity_runtime; extern crate parking_lot; #[cfg(feature = "price-info")] extern crate price_info; +extern crate registrar; extern crate rlp; extern crate transaction_pool as txpool; +extern crate serde; #[macro_use] extern crate ethabi_contract; #[macro_use] -extern crate ethabi_derive; -#[macro_use] -extern crate error_chain; -#[macro_use] extern crate log; #[macro_use] +extern crate serde_derive; +#[macro_use] extern crate trace_time; #[cfg(test)] extern crate rustc_hex; #[cfg(test)] -extern crate ethkey; +extern crate parity_crypto; #[cfg(test)] extern crate env_logger; diff --git a/miner/src/local_accounts.rs b/miner/src/local_accounts.rs index 23bcf81442..f2725476b2 100644 --- a/miner/src/local_accounts.rs +++ b/miner/src/local_accounts.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/miner/src/pool/client.rs b/miner/src/pool/client.rs index bcd2b96895..d374914a0b 100644 --- a/miner/src/pool/client.rs +++ b/miner/src/pool/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -50,6 +50,15 @@ pub trait Client: fmt::Debug + Sync { /// Is transaction with given hash already in the blockchain? fn transaction_already_included(&self, hash: &H256) -> bool; + /// Perform basic/cheap transaction verification. + /// + /// This should include all cheap checks that can be done before + /// actually checking the signature, like chain-replay protection. + /// + /// This method is currently used only for verifying local transactions. + fn verify_transaction_basic(&self, t: &transaction::UnverifiedTransaction) + -> Result<(), transaction::Error>; + /// Structurarily verify given transaction. fn verify_transaction(&self, tx: transaction::UnverifiedTransaction) -> Result; diff --git a/miner/src/pool/listener.rs b/miner/src/pool/listener.rs index 67034aa523..4a7e23ec55 100644 --- a/miner/src/pool/listener.rs +++ b/miner/src/pool/listener.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,53 +20,11 @@ use std::fmt; use std::sync::Arc; use ethereum_types::H256; +use futures::sync::mpsc; 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) { - if self.pending.is_empty() { - return; - } - - 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()); - } -} +use pool::TxStatus; /// Transaction pool logger. #[derive(Default, Debug)] @@ -116,35 +74,119 @@ impl txpool::Listener for Logger { } } +/// Transactions pool notifier +#[derive(Default)] +pub struct TransactionsPoolNotifier { + full_listeners: Vec>>>, + pending_listeners: Vec>>>, + tx_statuses: Vec<(H256, TxStatus)>, +} + +impl TransactionsPoolNotifier { + /// Add new full listener to receive notifications. + pub fn add_full_listener(&mut self, f: mpsc::UnboundedSender>>) { + self.full_listeners.push(f); + } + + /// Add new pending listener to receive notifications. + pub fn add_pending_listener(&mut self, f: mpsc::UnboundedSender>>) { + self.pending_listeners.push(f); + } + + /// Notify listeners about all currently transactions. + pub fn notify(&mut self) { + if self.tx_statuses.is_empty() { + return; + } + + let to_pending_send: Arc> = Arc::new( + self.tx_statuses.clone() + .into_iter() + .map(|(hash, _)| hash) + .collect() + ); + self.pending_listeners.retain(|listener| { + listener.unbounded_send(to_pending_send.clone()).is_ok() + }); + + let to_full_send = Arc::new( + std::mem::replace(&mut self.tx_statuses, Vec::new()) + ); + self.full_listeners + .retain(|listener| { + listener.unbounded_send(to_full_send.clone()).is_ok() + }); + } +} + +impl fmt::Debug for TransactionsPoolNotifier { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("TransactionsPoolNotifier") + .field("full_listeners", &self.full_listeners.len()) + .field("pending_listeners", &self.pending_listeners.len()) + .finish() + } +} + +impl txpool::Listener for TransactionsPoolNotifier { + fn added(&mut self, tx: &Arc, _old: Option<&Arc>) { + self.tx_statuses.push((tx.hash.clone(), TxStatus::Added)); + } + + fn rejected(&mut self, tx: &Arc, _reason: &txpool::Error) { + self.tx_statuses.push((tx.hash.clone(), TxStatus::Rejected)); + } + + fn dropped(&mut self, tx: &Arc, _new: Option<&Transaction>) { + self.tx_statuses.push((tx.hash.clone(), TxStatus::Dropped)); + } + + fn invalid(&mut self, tx: &Arc) { + self.tx_statuses.push((tx.hash.clone(), TxStatus::Invalid)); + } + + fn canceled(&mut self, tx: &Arc) { + self.tx_statuses.push((tx.hash.clone(), TxStatus::Canceled)); + } + + fn culled(&mut self, tx: &Arc) { + self.tx_statuses.push((tx.hash.clone(), TxStatus::Culled)); + } +} + #[cfg(test)] mod tests { use super::*; - use parking_lot::Mutex; use types::transaction; use txpool::Listener; + use futures::{Stream, Future}; + use ethereum_types::Address; #[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 (full_sender, full_receiver) = mpsc::unbounded(); + let (pending_sender, pending_receiver) = mpsc::unbounded(); - let mut tx_listener = Notifier::default(); - tx_listener.add(listener); + let mut tx_listener = TransactionsPoolNotifier::default(); + tx_listener.add_full_listener(full_sender); + tx_listener.add_pending_listener(pending_sender); // when let tx = new_tx(); tx_listener.added(&tx, None); - assert_eq!(*received.lock(), vec![]); // then tx_listener.notify(); + let (full_res , _full_receiver)= full_receiver.into_future().wait().unwrap(); + let (pending_res , _pending_receiver)= pending_receiver.into_future().wait().unwrap(); + assert_eq!( + full_res, + Some(Arc::new(vec![(serde_json::from_str::("\"0x13aff4201ac1dc49daf6a7cf07b558ed956511acbaabf9502bdacc353953766d\"").unwrap(), TxStatus::Added)])) + ); assert_eq!( - *received.lock(), - vec!["13aff4201ac1dc49daf6a7cf07b558ed956511acbaabf9502bdacc353953766d".parse().unwrap()] + pending_res, + Some(Arc::new(vec![serde_json::from_str::("\"0x13aff4201ac1dc49daf6a7cf07b558ed956511acbaabf9502bdacc353953766d\"").unwrap()])) ); } @@ -156,7 +198,7 @@ mod tests { gas: 21_000.into(), gas_price: 5.into(), value: 0.into(), - }.fake_sign(5.into()); + }.fake_sign(Address::from_low_u64_be(5)); Arc::new(Transaction::from_pending_block_transaction(signed)) } diff --git a/miner/src/pool/local_transactions.rs b/miner/src/pool/local_transactions.rs index 346877d030..811023f2e1 100644 --- a/miner/src/pool/local_transactions.rs +++ b/miner/src/pool/local_transactions.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -66,7 +66,7 @@ pub struct LocalTransactionsList { max_old: usize, transactions: LinkedHashMap, pending: usize, - in_chain: Option bool + Send + Sync>>, + in_chain: Option bool + Send + Sync>>, } impl fmt::Debug for LocalTransactionsList { @@ -235,7 +235,7 @@ impl txpool::Listener for LocalTransactionsList { mod tests { use super::*; use ethereum_types::U256; - use ethkey::{Random, Generator}; + use parity_crypto::publickey::{Random, Generator}; use types::transaction; use txpool::Listener; diff --git a/miner/src/pool/mod.rs b/miner/src/pool/mod.rs index 40a226d9fc..6c3f85e510 100644 --- a/miner/src/pool/mod.rs +++ b/miner/src/pool/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ //! Transaction Pool use ethereum_types::{U256, H256, Address}; -use heapsize::HeapSizeOf; +use parity_util_mem::MallocSizeOfExt; use types::transaction; use txpool; @@ -176,7 +176,7 @@ impl txpool::VerifiedTransaction for VerifiedTransaction { } fn mem_usage(&self) -> usize { - self.transaction.heap_size_of_children() + self.transaction.malloc_size_of() } fn sender(&self) -> &Address { @@ -199,3 +199,21 @@ impl ScoredTransaction for VerifiedTransaction { self.transaction.nonce } } + +/// Pool transactions status +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum TxStatus { + /// Added transaction + Added, + /// Rejected transaction + Rejected, + /// Dropped transaction + Dropped, + /// Invalid transaction + Invalid, + /// Canceled transaction + Canceled, + /// Culled transaction + Culled, +} diff --git a/miner/src/pool/queue.rs b/miner/src/pool/queue.rs index 58966df859..90afdc812c 100644 --- a/miner/src/pool/queue.rs +++ b/miner/src/pool/queue.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,17 +22,18 @@ use std::sync::atomic::{self, AtomicUsize}; use std::collections::{BTreeMap, BTreeSet, HashMap}; use ethereum_types::{H256, U256, Address}; +use futures::sync::mpsc; use parking_lot::RwLock; use txpool::{self, Verifier}; use types::transaction; use pool::{ self, replace, scoring, verifier, client, ready, listener, - PrioritizationStrategy, PendingOrdering, PendingSettings, + PrioritizationStrategy, PendingOrdering, PendingSettings, TxStatus }; use pool::local_transactions::LocalTransactionsList; -type Listener = (LocalTransactionsList, (listener::Notifier, listener::Logger)); +type Listener = (LocalTransactionsList, (listener::TransactionsPoolNotifier, listener::Logger)); type Pool = txpool::Pool; /// Max cache time in milliseconds for pending transactions. @@ -240,10 +241,10 @@ impl TransactionQueue { /// /// Given blockchain and state access (Client) /// verifies and imports transactions to the pool. - pub fn import( + pub fn import, C: client::Client + client::NonceClient + Clone>( &self, client: C, - transactions: Vec, + transactions: T, ) -> Vec> { // Run verification trace_time!("pool::verify_and_import"); @@ -496,7 +497,7 @@ impl TransactionQueue { /// 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>( + pub fn remove<'a, T: IntoIterator>( &self, hashes: T, is_invalid: bool, @@ -568,10 +569,16 @@ impl TransactionQueue { 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) { + /// Add a listener to be notified about all transactions the pool + pub fn add_pending_listener(&self, f: mpsc::UnboundedSender>>) { let mut pool = self.pool.write(); - (pool.listener_mut().1).0.add(f); + (pool.listener_mut().1).0.add_pending_listener(f); + } + + /// Add a listener to be notified about all transactions the pool + pub fn add_full_listener(&self, f: mpsc::UnboundedSender>>) { + let mut pool = self.pool.write(); + (pool.listener_mut().1).0.add_full_listener(f); } /// Check if pending set is cached. diff --git a/miner/src/pool/ready.rs b/miner/src/pool/ready.rs index 3accba1390..b023faa079 100644 --- a/miner/src/pool/ready.rs +++ b/miner/src/pool/ready.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/miner/src/pool/replace.rs b/miner/src/pool/replace.rs index 0655af5999..853f228b75 100644 --- a/miner/src/pool/replace.rs +++ b/miner/src/pool/replace.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -119,7 +119,7 @@ mod tests { use super::*; use std::sync::Arc; - use ethkey::{Random, Generator, KeyPair}; + use parity_crypto::publickey::{Random, Generator, KeyPair}; use pool::tests::tx::{Tx, TxExt}; use pool::tests::client::TestClient; use pool::scoring::*; @@ -133,7 +133,7 @@ mod tests { verified_tx } - fn should_replace(replace: &ShouldReplace, old: VerifiedTransaction, new: VerifiedTransaction) -> Choice { + fn should_replace(replace: &dyn ShouldReplace, old: VerifiedTransaction, new: VerifiedTransaction) -> Choice { let old_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(old) }; let new_tx = txpool::Transaction { insertion_id: 0, transaction: Arc::new(new) }; let old = ReplaceTransaction::new(&old_tx, Default::default()); diff --git a/miner/src/pool/scoring.rs b/miner/src/pool/scoring.rs index 0360bec354..48812d59cd 100644 --- a/miner/src/pool/scoring.rs +++ b/miner/src/pool/scoring.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -34,7 +34,7 @@ use txpool::{self, scoring}; use super::{verifier, PrioritizationStrategy, VerifiedTransaction, ScoredTransaction}; /// Transaction with the same (sender, nonce) can be replaced only if -/// `new_gas_price > old_gas_price + old_gas_price >> SHIFT` +/// `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% /// Calculate minimal gas price requirement. diff --git a/miner/src/pool/tests/client.rs b/miner/src/pool/tests/client.rs index 4735fbd64d..5928c9ee27 100644 --- a/miner/src/pool/tests/client.rs +++ b/miner/src/pool/tests/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -103,6 +103,12 @@ impl pool::client::Client for TestClient { false } + fn verify_transaction_basic(&self, _tx: &UnverifiedTransaction) + -> Result<(), transaction::Error> + { + Ok(()) + } + fn verify_transaction(&self, tx: UnverifiedTransaction) -> Result { diff --git a/miner/src/pool/tests/mod.rs b/miner/src/pool/tests/mod.rs index 1df1be4ce7..dd9ff16308 100644 --- a/miner/src/pool/tests/mod.rs +++ b/miner/src/pool/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/miner/src/pool/tests/tx.rs b/miner/src/pool/tests/tx.rs index b8f6dca676..4425aafd9c 100644 --- a/miner/src/pool/tests/tx.rs +++ b/miner/src/pool/tests/tx.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . use ethereum_types::{U256, H256}; -use ethkey::{Random, Generator}; +use parity_crypto::publickey::{Random, Generator}; use rustc_hex::FromHex; use types::transaction::{self, Transaction, SignedTransaction, UnverifiedTransaction}; diff --git a/miner/src/pool/verifier.rs b/miner/src/pool/verifier.rs index 51232e967a..fff62ea4ff 100644 --- a/miner/src/pool/verifier.rs +++ b/miner/src/pool/verifier.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -168,7 +168,7 @@ impl txpool::Verifier for Verifier txpool::Verifier for Verifier txpool::Verifier for Verifier txpool::Verifier for Verifier txpool::Verifier for Verifier signed.into(), Err(err) => { debug!(target: "txqueue", "[{:?}] Rejected tx {:?}", hash, err); - bail!(err) + return Err(err) }, }, - Transaction::Local(tx) => tx, + Transaction::Local(tx) => match self.client.verify_transaction_basic(&**tx) { + Ok(()) => tx, + Err(err) => { + warn!(target: "txqueue", "[{:?}] Rejected local tx {:?}", hash, err); + return Err(err) + } + }, }; // 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) + return Err(err) } let sender = transaction.sender(); @@ -276,7 +282,7 @@ impl txpool::Verifier for Verifier txpool::Verifier for Verifier txpool::Verifier for Verifier txpool::Verifier for Verifier(&self, client: &C, tx: &SignedTransaction) -> Result { - let sender = tx.sender(); + pub fn check( + &self, + client: &C, + tx: &SignedTransaction + ) -> Result { // Skip checking the contract if the transaction does not have zero gas price if !tx.gas_price.is_zero() { return Ok(false) } + let sender = tx.sender(); self.check_address(client, sender) } /// Checks if given address is whitelisted to send service transactions. - pub fn check_address(&self, client: &C, sender: Address) -> Result { + pub fn check_address(&self, client: &C, sender: Address) -> Result + where C: CallContract + RegistrarClient + { trace!(target: "txqueue", "Checking service transaction checker contract from {}", sender); - if let Some(allowed) = self.certified_addresses_cache.try_read().as_ref().and_then(|c| c.get(&sender)) { + if let Some(allowed) = self + .certified_addresses_cache + .try_read() + .as_ref() + .and_then(|c| c.get(&sender)) + { return Ok(*allowed); } - let contract_address = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest) - .ok_or_else(|| "contract is not configured")?; + + let contract_address = match client.get_address( + SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME, + BlockId::Latest + ) { + Ok(Some(addr)) => addr, + Ok(None) => return Err("contract is not configured".to_owned()), + Err(e) => return Err(e) + }; + self.call_contract(client, contract_address, sender).and_then(|allowed| { if let Some(mut cache) = self.certified_addresses_cache.try_write() { cache.insert(sender, allowed); @@ -66,13 +85,24 @@ impl ServiceTransactionChecker { } /// Refresh certified addresses cache - pub fn refresh_cache(&self, client: &C) -> Result { + pub fn refresh_cache(&self, client: &C) -> Result + where C: CallContract + RegistrarClient + { trace!(target: "txqueue", "Refreshing certified addresses cache"); // replace the cache with an empty list, // since it's not recent it won't be used anyway. let cache = mem::replace(&mut *self.certified_addresses_cache.write(), HashMap::default()); - if let Some(contract_address) = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest) { + if client.registrar_address().is_none() { + return Ok(false); + } + + let contract_address_fetch = client.get_address( + SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME, + BlockId::Latest + )?; + + if let Some(contract_address) = contract_address_fetch { let addresses: Vec<_> = cache.keys().collect(); let mut cache: HashMap = HashMap::default(); for address in addresses { @@ -86,7 +116,14 @@ impl ServiceTransactionChecker { } } - fn call_contract(&self, client: &C, contract_address: Address, sender: Address) -> Result { + fn call_contract( + &self, + client: &C, + contract_address: Address, + sender: Address + ) -> Result + where C: CallContract + RegistrarClient + { let (data, decoder) = service_transaction::functions::certified::call(sender); let value = client.call_contract(BlockId::Latest, contract_address, data)?; decoder.decode(&value).map_err(|e| e.to_string()) diff --git a/miner/src/work_notify.rs b/miner/src/work_notify.rs index 367990f225..0752f34924 100644 --- a/miner/src/work_notify.rs +++ b/miner/src/work_notify.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/miner/stratum/Cargo.toml b/miner/stratum/Cargo.toml index a7e13ef681..971dd44004 100644 --- a/miner/stratum/Cargo.toml +++ b/miner/stratum/Cargo.toml @@ -6,12 +6,12 @@ license = "GPL-3.0" authors = ["Parity Technologies "] [dependencies] -ethereum-types = "0.4" -keccak-hash = "0.1" -jsonrpc-core = "10.0.1" -jsonrpc-tcp-server = "10.0.1" +ethereum-types = "0.8.0" +keccak-hash = "0.4.0" +jsonrpc-core = "14.0.3" +jsonrpc-tcp-server = "14.0.3" log = "0.4" -parking_lot = "0.7" +parking_lot = "0.9" [dev-dependencies] env_logger = "0.5" diff --git a/miner/stratum/src/lib.rs b/miner/stratum/src/lib.rs index 5ee0296dac..c664058632 100644 --- a/miner/stratum/src/lib.rs +++ b/miner/stratum/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -70,13 +70,13 @@ pub struct Stratum { impl Stratum { pub fn start( addr: &SocketAddr, - dispatcher: Arc, + dispatcher: Arc, secret: Option, ) -> Result, Error> { let implementation = Arc::new(StratumImpl { subscribers: RwLock::default(), - job_que: RwLock::default(), + job_queue: RwLock::default(), dispatcher, workers: Arc::new(RwLock::default()), secret, @@ -106,13 +106,9 @@ impl Stratum { } impl PushWorkHandler for Stratum { - fn push_work_all(&self, payload: String) -> Result<(), Error> { + fn push_work_all(&self, payload: String) { self.implementation.push_work_all(payload, &self.tcp_dispatcher) } - - fn push_work(&self, payloads: Vec) -> Result<(), Error> { - self.implementation.push_work(payloads, &self.tcp_dispatcher) - } } impl Drop for Stratum { @@ -126,14 +122,14 @@ struct StratumImpl { /// Subscribed clients subscribers: RwLock>, /// List of workers supposed to receive job update - job_que: RwLock>, + job_queue: RwLock>, /// Payload manager - dispatcher: Arc, + dispatcher: Arc, /// Authorized workers (socket - worker_id) workers: Arc>>, /// Secret if any secret: Option, - /// Dispatch notify couinter + /// Dispatch notify counter notify_counter: RwLock, } @@ -143,7 +139,7 @@ impl StratumImpl { use std::str::FromStr; self.subscribers.write().push(meta.addr().clone()); - self.job_que.write().insert(meta.addr().clone()); + self.job_queue.write().insert(meta.addr().clone()); trace!(target: "stratum", "Subscription request from {:?}", meta.addr()); Ok(match self.dispatcher.initial() { @@ -160,7 +156,7 @@ impl StratumImpl { /// rpc method `mining.authorize` fn authorize(&self, params: Params, meta: SocketMetadata) -> RpcResult { - params.parse::<(String, String)>().map(|(worker_id, secret)|{ + params.parse::<(String, String)>().map(|(worker_id, secret)| { if let Some(valid_secret) = self.secret { let hash = keccak(secret); if hash != valid_secret { @@ -184,15 +180,15 @@ impl StratumImpl { _ => None }) .collect::>()) { - Ok(()) => { - self.update_peers(&meta.tcp_dispatcher.expect("tcp_dispatcher is always initialized; qed")); - to_value(true) - }, - Err(submit_err) => { - warn!("Error while submitting share: {:?}", submit_err); - to_value(false) - } + Ok(()) => { + self.update_peers(&meta.tcp_dispatcher.expect("tcp_dispatcher is always initialized; qed")); + to_value(true) + }, + Err(submit_err) => { + warn!("Error while submitting share: {:?}", submit_err); + to_value(false) } + } }, _ => { trace!(target: "stratum", "Invalid submit work format {:?}", params); @@ -204,36 +200,37 @@ impl StratumImpl { /// Helper method fn update_peers(&self, tcp_dispatcher: &Dispatcher) { if let Some(job) = self.dispatcher.job() { - if let Err(e) = self.push_work_all(job, tcp_dispatcher) { - warn!("Failed to update some of the peers: {:?}", e); - } + self.push_work_all(job, tcp_dispatcher) } } - fn push_work_all(&self, payload: String, tcp_dispatcher: &Dispatcher) -> Result<(), Error> { + fn push_work_all(&self, payload: String, tcp_dispatcher: &Dispatcher) { let hup_peers = { let workers = self.workers.read(); let next_request_id = { let mut counter = self.notify_counter.write(); - if *counter == ::std::u32::MAX { *counter = NOTIFY_COUNTER_INITIAL; } - else { *counter = *counter + 1 } + if *counter == ::std::u32::MAX { + *counter = NOTIFY_COUNTER_INITIAL; + } else { + *counter = *counter + 1 + } *counter }; - let mut hup_peers = HashSet::with_capacity(0); // most of the cases won't be needed, hence avoid allocation + let mut hup_peers = HashSet::new(); let workers_msg = format!("{{ \"id\": {}, \"method\": \"mining.notify\", \"params\": {} }}", next_request_id, payload); trace!(target: "stratum", "pushing work for {} workers (payload: '{}')", workers.len(), &workers_msg); - for (ref addr, _) in workers.iter() { - trace!(target: "stratum", "pusing work to {}", addr); + for (addr, _) in workers.iter() { + trace!(target: "stratum", "pushing work to {}", addr); match tcp_dispatcher.push_message(addr, workers_msg.clone()) { Err(PushMessageError::NoSuchPeer) => { - trace!(target: "stratum", "Worker no longer connected: {}", &addr); - hup_peers.insert(*addr.clone()); + trace!(target: "stratum", "Worker no longer connected: {}", addr); + hup_peers.insert(addr.clone()); }, Err(e) => { warn!(target: "stratum", "Unexpected transport error: {:?}", e); }, - Ok(_) => { }, + Ok(_) => {}, } } hup_peers @@ -241,33 +238,10 @@ impl StratumImpl { if !hup_peers.is_empty() { let mut workers = self.workers.write(); - for hup_peer in hup_peers { workers.remove(&hup_peer); } - } - - Ok(()) - } - - fn push_work(&self, payloads: Vec, tcp_dispatcher: &Dispatcher) -> Result<(), Error> { - if !payloads.len() > 0 { - return Err(Error::NoWork); - } - let workers = self.workers.read(); - let addrs = workers.keys().collect::>(); - if !workers.len() > 0 { - return Err(Error::NoWorkers); - } - let mut que = payloads; - let mut addr_index = 0; - while que.len() > 0 { - let next_worker = addrs[addr_index]; - let mut next_payload = que.drain(0..1); - tcp_dispatcher.push_message( - next_worker, - next_payload.nth(0).expect("drained successfully of 0..1, so 0-th element should exist") - )?; - addr_index = addr_index + 1; + for hup_peer in hup_peers { + workers.remove(&hup_peer); + } } - Ok(()) } } @@ -475,8 +449,7 @@ mod tests { .map_err(|err: timeout::Error<()>| panic!("Timeout: {:?}", err)) .and_then(move |stream| { trace!(target: "stratum", "Pusing work to peers"); - stratum.push_work_all(r#"{ "00040008", "100500" }"#.to_owned()) - .expect("Pushing work should produce no errors"); + stratum.push_work_all(r#"{ "00040008", "100500" }"#.to_owned()); Timeout::new(future::ok(stream), ::std::time::Duration::from_millis(100)) }) .map_err(|err: timeout::Error<()>| panic!("Timeout: {:?}", err)) @@ -497,4 +470,11 @@ mod tests { "{ \"id\": 17, \"method\": \"mining.notify\", \"params\": { \"00040008\", \"100500\" } }\n", response); } + + #[test] + fn jsonprc_server_is_send_and_sync() { + fn is_send_and_sync() {} + + is_send_and_sync::(); + } } diff --git a/miner/stratum/src/traits.rs b/miner/stratum/src/traits.rs index 36b95a0169..29bd790104 100644 --- a/miner/stratum/src/traits.rs +++ b/miner/stratum/src/traits.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std; -use std::error::Error as StdError; use ethereum_types::H256; use jsonrpc_tcp_server::PushMessageError; @@ -30,7 +28,7 @@ pub enum Error { impl From for Error { fn from(err: std::io::Error) -> Self { - Error::Io(err.description().to_owned()) + Error::Io(err.to_string()) } } @@ -55,10 +53,7 @@ pub trait JobDispatcher: Send + Sync { /// Interface that can handle requests to push job for workers pub trait PushWorkHandler: Send + Sync { /// push the same work package for all workers (`payload`: json of pow-specific set of work specification) - fn push_work_all(&self, payload: String) -> Result<(), Error>; - - /// push the work packages worker-wise (`payload`: json of pow-specific set of work specification) - fn push_work(&self, payloads: Vec) -> Result<(), Error>; + fn push_work_all(&self, payload: String); } pub struct ServiceConfiguration { diff --git a/miner/using-queue/src/lib.rs b/miner/using-queue/src/lib.rs index 56e99879db..59347a6a50 100644 --- a/miner/using-queue/src/lib.rs +++ b/miner/using-queue/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,9 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Queue-like datastructure including notion of usage. +//! Queue-like data structure including notion of usage. -/// Special queue-like datastructure that includes the notion of +/// Special queue-like data structure that includes the notion of /// usage to avoid items that were queued but never used from making it into /// the queue. pub struct UsingQueue { @@ -42,7 +42,7 @@ impl UsingQueue { UsingQueue { pending: None, in_use: vec![], - max_size: max_size, + max_size, } } diff --git a/parity-clib/Cargo.toml b/parity-clib/Cargo.toml deleted file mode 100644 index f57e503dea..0000000000 --- a/parity-clib/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[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] -futures = "0.1.6" -jni = { version = "0.11", optional = true } -panic_hook = { path = "../util/panic-hook" } -parity-ethereum = { path = "../", default-features = false } -tokio = "0.1.11" -tokio-current-thread = "0.1.3" - -[features] -default = [] -final = ["parity-ethereum/final"] diff --git a/parity-clib/Parity.java b/parity-clib/Parity.java deleted file mode 100644 index 3885cfb1e6..0000000000 --- a/parity-clib/Parity.java +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2018-2019 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 . - -package io.parity.ethereum; - -/** - * Interface to the Parity client. - */ -public class Parity { - /** - * Starts the Parity client with the CLI options passed as an array of strings. - * - * Each space-delimited option corresponds to an array entry. - * For example: `["--port", "12345"]` - * - * @param options The CLI options to start Parity with - */ - public Parity(String[] options, String loggerMode, String loggerFile) { - long config = configFromCli(options); - inner = build(config, loggerMode, loggerFile); - } - - /** Performs an asynchronous RPC query by spawning a background thread that is executed until - * either a response is received or the timeout has been expired. - * - * @param query The JSON-encoded RPC query to perform - * @param timeoutMillis The maximum time in milliseconds that the query will run - * @param callback An instance of class which must have a instance method named `callback` that will be - * invoke when the result is ready - */ - public void rpcQuery(String query, long timeoutMillis, Object callback) { - rpcQueryNative(inner, query, timeoutMillis, callback); - } - - /** Subscribes to a specific WebSocket event that will run in a background thread until it is canceled. - * - * @param query The JSON-encoded RPC query to perform - * @param callback An instance of class which must have a instance method named `callback` that will be invoked - * when the result is ready - * - * @return A pointer to the current sessions which can be used to terminate the session later - */ - public long subscribeWebSocket(String query, Object callback) { - return subscribeWebSocketNative(inner, query, callback); - } - - /** Unsubscribes to a specific WebSocket event - * - * @param session Pointer the the session to terminate - */ - public void unsubscribeWebSocket(long session) { - unsubscribeWebSocketNative(session); - } - - // FIXME: `finalize` is deprecated - https://github.com/paritytech/parity-ethereum/issues/10066 - @Override - protected void finalize​() { - destroy(inner); - } - - static { - System.loadLibrary("parity"); - } - - private static native long configFromCli(String[] cliOptions); - private static native long build(long config, String loggerMode, String loggerFile); - private static native void destroy(long inner); - private static native void rpcQueryNative(long inner, String rpc, long timeoutMillis, Object callback); - private static native long subscribeWebSocketNative(long inner, String rpc, Object callback); - private static native void unsubscribeWebSocketNative(long session); - - private long inner; -} diff --git a/parity-clib/examples/cpp/CMakeLists.txt b/parity-clib/examples/cpp/CMakeLists.txt deleted file mode 100644 index 8cc6aef8f5..0000000000 --- a/parity-clib/examples/cpp/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -include(ExternalProject) -include_directories("${CMAKE_SOURCE_DIR}/../..") -set (CMAKE_CXX_STANDARD 11) # Enfore C++11 -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/${CMAKE_SHARED_LIBRARY_PREFIX}parity${CMAKE_SHARED_LIBRARY_SUFFIX}") diff --git a/parity-clib/examples/cpp/main.cpp b/parity-clib/examples/cpp/main.cpp deleted file mode 100644 index 43b31d793f..0000000000 --- a/parity-clib/examples/cpp/main.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -#include -#include -#include -#include -#include - -void* parity_run(std::vector); -int parity_subscribe_to_websocket(void*); -int parity_rpc_queries(void*); - -const int SUBSCRIPTION_ID_LEN = 18; -const size_t TIMEOUT_ONE_MIN_AS_MILLIS = 60 * 1000; -const unsigned int CALLBACK_RPC = 1; -const unsigned int CALLBACK_WS = 2; - -struct Callback { - unsigned int type; - long unsigned int counter; -}; - -// list of rpc queries -const std::vector rpc_queries { - "{\"method\":\"parity_versionInfo\",\"params\":[],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_getTransactionReceipt\",\"params\":[\"0x444172bef57ad978655171a8af2cfd89baa02a97fcb773067aef7794d6913fff\"],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"0x0066Dc48bb833d2B59f730F33952B3c29fE926F5\"}],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_getBalance\",\"params\":[\"0x0066Dc48bb833d2B59f730F33952B3c29fE926F5\"],\"id\":1,\"jsonrpc\":\"2.0\"}" -}; - -// list of subscriptions -const std::vector ws_subscriptions { - "{\"method\":\"parity_subscribe\",\"params\":[\"eth_getBalance\",[\"0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826\",\"latest\"]],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"parity_subscribe\",\"params\":[\"parity_netPeers\"],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_subscribe\",\"params\":[\"newHeads\"],\"id\":1,\"jsonrpc\":\"2.0\"}" -}; - -// callback that gets invoked upon an event -void callback(void* user_data, const char* response, size_t _len) { - Callback* cb = static_cast(user_data); - if (cb->type == CALLBACK_RPC) { - cb->counter -= 1; - } else if (cb->type == CALLBACK_WS) { - std::regex is_subscription ("\\{\"jsonrpc\":\"2.0\",\"result\":\"0[xX][a-fA-F0-9]{16}\",\"id\":1\\}"); - if (std::regex_match(response, is_subscription) == true) { - cb->counter -= 1; - } - } -} - -int main() { - // run full-client - { - std::vector config = {"--no-ipc" , "--jsonrpc-apis=all", "--chain", "kovan"}; - void* parity = parity_run(config); - if (parity_rpc_queries(parity)) { - printf("rpc_queries failed\r\n"); - return 1; - } - - if (parity_subscribe_to_websocket(parity)) { - printf("ws_queries failed\r\n"); - return 1; - } - - if (parity != nullptr) { - parity_destroy(parity); - } - } - - // run light-client - { - std::vector light_config = {"--no-ipc", "--light", "--jsonrpc-apis=all", "--chain", "kovan"}; - void* parity = parity_run(light_config); - - if (parity_rpc_queries(parity)) { - printf("rpc_queries failed\r\n"); - return 1; - } - - if (parity_subscribe_to_websocket(parity)) { - printf("ws_queries failed\r\n"); - return 1; - } - - if (parity != nullptr) { - parity_destroy(parity); - } - } - return 0; -} - -int parity_rpc_queries(void* parity) { - if (!parity) { - return 1; - } - - Callback cb { .type = CALLBACK_RPC, .counter = rpc_queries.size() }; - - for (auto query : rpc_queries) { - if (parity_rpc(parity, query.c_str(), query.length(), TIMEOUT_ONE_MIN_AS_MILLIS, callback, &cb) != 0) { - return 1; - } - } - - while(cb.counter != 0); - return 0; -} - - -int parity_subscribe_to_websocket(void* parity) { - if (!parity) { - return 1; - } - - std::vector sessions; - - Callback cb { .type = CALLBACK_WS, .counter = ws_subscriptions.size() }; - - for (auto sub : ws_subscriptions) { - void *const session = parity_subscribe_ws(parity, sub.c_str(), sub.length(), callback, &cb); - if (!session) { - return 1; - } - sessions.push_back(session); - } - - while(cb.counter != 0); - std::this_thread::sleep_for(std::chrono::seconds(60)); - for (auto session : sessions) { - parity_unsubscribe_ws(session); - } - return 0; -} - -void* parity_run(std::vector args) { - ParityParams cfg = { - .configuration = nullptr, - .on_client_restart_cb = callback, - .on_client_restart_cb_custom = nullptr, - .logger = nullptr - }; - - std::vector str_lens; - - for (auto arg: args) { - str_lens.push_back(std::strlen(arg)); - } - - // make sure no out-of-range access happens here - if (args.empty()) { - if (parity_config_from_cli(nullptr, nullptr, 0, &cfg.configuration) != 0) { - return nullptr; - } - } else { - if (parity_config_from_cli(&args[0], &str_lens[0], args.size(), &cfg.configuration) != 0) { - return nullptr; - } - } - - // enable logging but only the `rpc module` and don't write it to a file - char log_mode [] = "rpc=trace"; - parity_set_logger(log_mode, strlen(log_mode), nullptr, 0, &cfg.logger); - - void *parity = nullptr; - if (parity_start(&cfg, &parity) != 0) { - return nullptr; - } - - return parity; -} diff --git a/parity-clib/examples/java/Main.java b/parity-clib/examples/java/Main.java deleted file mode 100644 index c20b9e34d6..0000000000 --- a/parity-clib/examples/java/Main.java +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2018-2019 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 java.util.Vector; -import java.util.concurrent.atomic.AtomicInteger; -import io.parity.ethereum.Parity; - -class Main { - public static final int ONE_MINUTE_AS_MILLIS = 60 * 1000; - - public static final String[] rpc_queries = { - "{\"method\":\"parity_versionInfo\",\"params\":[],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_getTransactionReceipt\",\"params\":[\"0x444172bef57ad978655171a8af2cfd89baa02a97fcb773067aef7794d6913fff\"],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"0x0066Dc48bb833d2B59f730F33952B3c29fE926F5\"}],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_getBalance\",\"params\":[\"0x0066Dc48bb833d2B59f730F33952B3c29fE926F5\"],\"id\":1,\"jsonrpc\":\"2.0\"}" - }; - - public static final String[] ws_queries = { - "{\"method\":\"parity_subscribe\",\"params\":[\"eth_getBalance\",[\"0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826\",\"latest\"]],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"parity_subscribe\",\"params\":[\"parity_netPeers\"],\"id\":1,\"jsonrpc\":\"2.0\"}", - "{\"method\":\"eth_subscribe\",\"params\":[\"newHeads\"],\"id\":1,\"jsonrpc\":\"2.0\"}" - }; - - public static void runParity(String[] config) { - String loggerMode = "rpc=trace"; - String loggerFile = "foo.log"; - Parity parity = new Parity(config, loggerMode, loggerFile); - - Callback rpcCallback = new Callback(1); - Callback webSocketCallback = new Callback(2); - - for (String query : rpc_queries) { - parity.rpcQuery(query, ONE_MINUTE_AS_MILLIS, rpcCallback); - } - - while (rpcCallback.getNumCallbacks() != 4); - - Vector sessions = new Vector(); - - for (String ws : ws_queries) { - long session = parity.subscribeWebSocket(ws, webSocketCallback); - sessions.add(session); - } - - try { - Thread.sleep(ONE_MINUTE_AS_MILLIS); - } catch (Exception e) { - System.out.println(e); - } - - for (long session : sessions) { - parity.unsubscribeWebSocket(session); - } - - // Force GC to destroy parity - parity = null; - System.gc(); - } - - public static void main(String[] args) { - String[] full = {"--no-ipc" , "--jsonrpc-apis=all", "--chain", "kovan"}; - String[] light = {"--no-ipc", "--light", "--jsonrpc-apis=all", "--chain", "kovan"}; - - runParity(full); - - try { - Thread.sleep(ONE_MINUTE_AS_MILLIS); - } catch (Exception e) { - System.out.println(e); - } - - runParity(light); - } -} - -class Callback { - private AtomicInteger counter; - private final int callbackType; - - public Callback(int type) { - counter = new AtomicInteger(); - callbackType = type; - } - - public void callback(Object response) { - counter.getAndIncrement(); - } - - public int getNumCallbacks() { - return counter.intValue(); - } -} diff --git a/parity-clib/examples/java/README.md b/parity-clib/examples/java/README.md deleted file mode 100644 index ec83905bf2..0000000000 --- a/parity-clib/examples/java/README.md +++ /dev/null @@ -1,9 +0,0 @@ -parity-clib: Java example -=================================== - -An example Java application to demonstrate how to use `jni` bindings to parity-ethereum. Note, that the example is built in debug-mode to reduce the build time. If you want to use it in real project use release-mode instead to facilitate all compiler optimizations. - -## How to compile and run - -1. Make sure you have installed [JDK](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) -2. Run `run.sh` \ No newline at end of file diff --git a/parity-clib/examples/java/run.sh b/parity-clib/examples/java/run.sh deleted file mode 100755 index 428a7dc751..0000000000 --- a/parity-clib/examples/java/run.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -FLAGS="-Xlint:deprecation" -PARITY_JAVA="../../Parity.java" -# parity-clib must be built with feature `jni` in debug-mode to work -PARITY_LIB=".:../../../target/debug/" - -# build -cd .. -cargo build --features jni -cd - -javac $FLAGS -d $PWD $PARITY_JAVA -javac $FLAGS *.java -# Setup the path `libparity.so` and run -java -Djava.library.path=$PARITY_LIB Main diff --git a/parity-clib/parity.h b/parity-clib/parity.h deleted file mode 100644 index 7bba08e433..0000000000 --- a/parity-clib/parity.h +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2018-2019 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; - - /// Logger object which must be created by the `parity_config_logger` function - void *logger; -}; - -#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); - -/// Builds a new logger object which should be a member of the `ParityParams struct` -/// -/// - log_mode : String representing the log mode according to `Rust LOG` or nullptr to disable logging. -/// See module documentation for `ethcore-logger` for more info. -/// - log_mode_len : Length of the log_mode or zero to disable logging -/// - log_file : String respresenting the file name to write to log to or nullptr to disable logging to a file -/// - log_mode_len : Length of the log_file or zero to disable logging to a file -/// - logger : Pointer to point to the created `Logger` object - -/// **Important**: This function must only be called exactly once otherwise it will panic. If you want to disable a -/// logging mode or logging to a file make sure that you pass the `length` as zero -/// -/// # 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; -/// } -/// char[] logger_mode = "rpc=trace"; -/// parity_set_logger(logger_mode, strlen(logger_mode), nullptr, 0, &cfg.logger); -/// ``` -/// -int parity_set_logger(const char* log_mode, size_t log_mode_len, const char* log_file, size_t log_file_len, void** logger); - -/// 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 asynchronous RPC request running in a background thread for at most X milliseconds -/// -/// - parity : Reference to the running parity client -/// - rpc_query : JSON encoded string representing the RPC request. -/// - len : Length of the RPC query -/// - timeout_ms : Maximum time that request is waiting for a response -/// - response : Callback to invoke when the query gets answered. It will respond with a JSON encoded the string -/// with the result both on success and error. -/// - ud : Specific user defined data that can used in the callback -/// -/// - On success : The function returns 0 -/// - On error : The function returns 1 -/// -int parity_rpc(const void *const parity, const char* rpc_query, size_t rpc_len, size_t timeout_ms, - void (*subscribe)(void* ud, const char* response, size_t len), void* ud); - - -/// Subscribes to a specific websocket event that will run until it is canceled -/// -/// - parity : Reference to the running parity client -/// - ws_query : JSON encoded string representing the websocket event to subscribe to -/// - len : Length of the query -/// - response : Callback to invoke when a websocket event occurs -/// - ud : Specific user defined data that can used in the callback -/// -/// - On success : The function returns an object to the current session -/// which can be used cancel the subscription -/// - On error : The function returns a null pointer -/// -void* parity_subscribe_ws(const void *const parity, const char* ws_query, size_t len, - void (*subscribe)(void* ud, const char* response, size_t len), void* ud); - -/// Unsubscribes from a websocket subscription. Caution this function consumes the session object and must only be -/// used exactly once per session. -/// -/// - session : Pointer to the session to unsubscribe from -/// -int parity_unsubscribe_ws(const void *const session); - -/// Sets a callback to call when a panic happens in the Rust code. -/// -/// The callback takes as parameter the custom param (the one passed to this function), plus the -/// panic message. You are expected to log the panic message somehow, in order to communicate it to -/// the user. A panic always indicates a bug in Parity. -/// -/// Note that this method sets the panic hook for the whole program, and not just for Parity. In -/// other words, if you use multiple Rust libraries at once (and not just Parity), then a panic -/// in any Rust code will call this callback as well. -/// -/// ## Thread safety -/// -/// The callback can be called from any thread and multiple times simultaneously. Make sure that -/// your code is thread safe. -/// -int parity_set_panic_hook(void (*cb)(void* param, const char* msg, size_t msg_len), void* param); - -#ifdef __cplusplus -} -#endif - -#endif // include guard diff --git a/parity-clib/src/java.rs b/parity-clib/src/java.rs deleted file mode 100644 index 98969b1d10..0000000000 --- a/parity-clib/src/java.rs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::{mem, ptr}; -use std::ffi::c_void; -use std::sync::Arc; - -use {Callback, parity_config_from_cli, parity_destroy, parity_rpc_worker, parity_start, parity_set_logger, - parity_unsubscribe_ws, parity_ws_worker, ParityParams}; - -use jni::{JavaVM, JNIEnv}; -use jni::objects::{JClass, JString, JObject, JValue, GlobalRef}; -use jni::sys::{jlong, jobjectArray, va_list}; -use parity_ethereum::RunningClient; - -type CheckedQuery<'a> = (&'a RunningClient, String, JavaVM, GlobalRef); - -// Creates a Java callback to a static method named `void callback(Object)` -struct JavaCallback<'a> { - jvm: JavaVM, - callback: GlobalRef, - method_name: &'a str, - method_descriptor: &'a str, -} - -impl<'a> JavaCallback<'a> { - fn new(jvm: JavaVM, callback: GlobalRef) -> Self { - Self { - jvm, - callback, - method_name: "callback", - method_descriptor: "(Ljava/lang/Object;)V", - } - } -} - -impl<'a> Callback for JavaCallback<'a> { - fn call(&self, msg: &str) { - let env = self.jvm.attach_current_thread().expect("JavaVM should have an environment; qed"); - let java_str = env.new_string(msg.to_string()).expect("Rust String is valid JString; qed"); - let val = &[JValue::Object(JObject::from(java_str))]; - env.call_method(self.callback.as_obj(), self.method_name, self.method_descriptor, val).expect( - "The callback must be an instance method and be named \"void callback(Object)\"; qed)"); - } -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_configFromCli(env: JNIEnv, _: JClass, cli: jobjectArray) -> jlong { - let cli_len = env.get_array_length(cli).expect("invalid Java bindings") as usize; - - let mut jni_strings = Vec::with_capacity(cli_len); - let mut opts = Vec::with_capacity(cli_len); - let mut opts_lens = Vec::with_capacity(cli_len); - - for n in 0..cli_len as i32 { - let elem = env.get_object_array_element(cli, n).expect("invalid Java bindings"); - let elem_str: JString = elem.into(); - match env.get_string(elem_str) { - Ok(s) => { - opts.push(s.as_ptr()); - opts_lens.push(s.to_bytes().len()); - jni_strings.push(s); - } - Err(err) => { - let _ = env.throw_new("java/lang/Exception", err.to_string()); - return 0 - } - }; - } - - let mut out = ptr::null_mut(); - match parity_config_from_cli(opts.as_ptr(), opts_lens.as_ptr(), cli_len, &mut out) { - 0 => out as jlong, - _ => { - let _ = env.throw_new("java/lang/Exception", "failed to create config object"); - 0 - }, - } -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_build( - env: JNIEnv, - _: JClass, - config: va_list, - logger_mode: JString, - logger_file: JString -) -> jlong { - let mut params = ParityParams { - configuration: config, - .. mem::zeroed() - }; - - let logger_mode: String = env.get_string(logger_mode).expect("valid JString; qed").into(); - let logger_file: String = env.get_string(logger_file).expect("valid JString; qed").into(); - - parity_set_logger(logger_mode.as_ptr(), logger_mode.as_bytes().len(), logger_file.as_ptr(), - logger_file.as_bytes().len(), &mut params.logger); - - let mut out = ptr::null_mut(); - match parity_start(¶ms, &mut out) { - 0 => out as jlong, - _ => { - let _ = env.throw_new("java/lang/Exception", "failed to start Parity"); - 0 - } - } -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_destroy(_env: JNIEnv, _: JClass, parity: va_list) { - parity_destroy(parity); -} - -unsafe fn java_query_checker<'a>(client: va_list, rpc: JString, callback: JObject, env: &JNIEnv<'a>) --> Result, String> { - let query: String = env.get_string(rpc) - .map(Into::into) - .map_err(|e| e.to_string())?; - - let client: &RunningClient = &*(client as *const RunningClient); - let jvm = env.get_java_vm().map_err(|e| e.to_string())?; - let global_ref = env.new_global_ref(callback).map_err(|e| e.to_string())?; - Ok((client, query, jvm, global_ref)) -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_rpcQueryNative( - env: JNIEnv, - _: JClass, - parity: va_list, - rpc: JString, - timeout_ms: jlong, - callback: JObject, - ) -{ - let _ = java_query_checker(parity, rpc, callback, &env) - .map(|(client, query, jvm, global_ref)| { - let callback = Arc::new(JavaCallback::new(jvm, global_ref)); - parity_rpc_worker(client, &query, callback, timeout_ms as u64); - }) - .map_err(|e| { - let _ = env.throw_new("java/lang/Exception", e); - }); -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_subscribeWebSocketNative( - env: JNIEnv, - _: JClass, - parity: va_list, - rpc: JString, - callback: JObject, - ) -> va_list { - - java_query_checker(parity, rpc, callback, &env) - .map(move |(client, query, jvm, global_ref)| { - let callback = Arc::new(JavaCallback::new(jvm, global_ref)); - parity_ws_worker(client, &query, callback) as va_list - }) - .unwrap_or_else(|e| { - let _ = env.throw_new("java/lang/Exception", e); - ptr::null_mut() - }) -} - -#[no_mangle] -pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_unsubscribeWebSocketNative( - _: JNIEnv, - _: JClass, - session: va_list) { - parity_unsubscribe_ws(session as *const c_void); -} diff --git a/parity-clib/src/lib.rs b/parity-clib/src/lib.rs deleted file mode 100644 index bbb60ec2d2..0000000000 --- a/parity-clib/src/lib.rs +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Note that all the structs and functions here are documented in `parity.h`, to avoid -//! duplicating documentation. - -extern crate futures; -extern crate panic_hook; -extern crate parity_ethereum; -extern crate tokio; -extern crate tokio_current_thread; - -#[cfg(feature = "jni")] -extern crate jni; - -#[cfg(feature = "jni")] -mod java; - -use std::ffi::CString; -use std::os::raw::{c_char, c_void, c_int}; -use std::{panic, ptr, slice, str, thread}; -use std::sync::Arc; -use std::time::Duration; - -use futures::{Future, Stream}; -use futures::sync::mpsc; -use parity_ethereum::{PubSubSession, RunningClient}; -use tokio_current_thread::CurrentThread; - -type CCallback = Option; -type CheckedQuery<'a> = (&'a RunningClient, &'static str); - -pub mod error { - pub const EMPTY: &str = r#"{"jsonrpc":"2.0","result":"null","id":1}"#; - pub const TIMEOUT: &str = r#"{"jsonrpc":"2.0","result":"timeout","id":1}"#; - pub const SUBSCRIBE: &str = r#"{"jsonrpc":"2.0","result":"subcribe_fail","id":1}"#; -} - -#[repr(C)] -pub struct ParityParams { - pub configuration: *mut c_void, - pub on_client_restart_cb: CCallback, - pub on_client_restart_cb_custom: *mut c_void, - pub logger: *mut c_void -} - -/// Trait representing a callback that passes a string -pub(crate) trait Callback: Send + Sync { - fn call(&self, msg: &str); -} - -// Internal structure for handling callbacks that get passed a string. -struct CallbackStr { - user_data: *mut c_void, - function: CCallback, -} - -unsafe impl Send for CallbackStr {} -unsafe impl Sync for CallbackStr {} -impl Callback for CallbackStr { - fn call(&self, msg: &str) { - if let Some(ref cb) = self.function { - let cstr = CString::new(msg).expect("valid string with no nul bytes in the middle; qed").into_raw(); - cb(self.user_data, cstr, msg.len()) - } - } -} - -#[no_mangle] -pub unsafe extern fn parity_config_from_cli( - args: *const *const c_char, - args_lens: *const usize, - len: usize, - output: *mut *mut c_void -) -> c_int { - 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_ethereum::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 unsafe extern fn parity_config_destroy(cfg: *mut c_void) { - let _ = panic::catch_unwind(|| { - let _cfg = Box::from_raw(cfg as *mut parity_ethereum::Configuration); - }); -} - -#[no_mangle] -pub unsafe extern fn parity_start(cfg: *const ParityParams, output: *mut *mut c_void) -> c_int { - panic::catch_unwind(|| { - *output = ptr::null_mut(); - let cfg: &ParityParams = &*cfg; - let logger = Arc::from_raw(cfg.logger as *mut parity_ethereum::RotatingLogger); - let config = Box::from_raw(cfg.configuration as *mut parity_ethereum::Configuration); - - let on_client_restart_cb = { - let cb = CallbackStr { - user_data: cfg.on_client_restart_cb_custom, - function: cfg.on_client_restart_cb, - }; - move |new_chain: String| { cb.call(&new_chain); } - }; - - let action = match parity_ethereum::start(*config, logger, on_client_restart_cb, || {}) { - Ok(action) => action, - Err(_) => return 1, - }; - - match action { - parity_ethereum::ExecutionAction::Instant(Some(s)) => { println!("{}", s); 0 }, - parity_ethereum::ExecutionAction::Instant(None) => 0, - parity_ethereum::ExecutionAction::Running(client) => { - *output = Box::into_raw(Box::new(client)) as *mut c_void; - 0 - } - } - }).unwrap_or(1) -} - -#[no_mangle] -pub unsafe extern fn parity_destroy(client: *mut c_void) { - let _ = panic::catch_unwind(|| { - let client = Box::from_raw(client as *mut RunningClient); - client.shutdown(); - }); -} - -#[no_mangle] -pub unsafe extern fn parity_rpc( - client: *const c_void, - query: *const c_char, - len: usize, - timeout_ms: usize, - callback: CCallback, - user_data: *mut c_void, -) -> c_int { - panic::catch_unwind(|| { - if let Some((client, query)) = parity_rpc_query_checker(client, query, len) { - let callback = Arc::new(CallbackStr {user_data, function: callback} ); - parity_rpc_worker(client, query, callback, timeout_ms as u64); - 0 - } else { - 1 - } - }).unwrap_or(1) -} - -#[no_mangle] -pub unsafe extern fn parity_subscribe_ws( - client: *const c_void, - query: *const c_char, - len: usize, - callback: CCallback, - user_data: *mut c_void, -) -> *const c_void { - panic::catch_unwind(|| { - if let Some((client, query)) = parity_rpc_query_checker(client, query, len) { - let callback = Arc::new(CallbackStr { user_data, function: callback}); - parity_ws_worker(client, query, callback) - } else { - ptr::null() - } - }) - .unwrap_or(ptr::null()) -} - -#[no_mangle] -pub unsafe extern fn parity_unsubscribe_ws(session: *const c_void) { - let _ = panic::catch_unwind(|| { - let _session = Arc::from_raw(session as *const PubSubSession); - }); -} - -#[no_mangle] -pub extern fn parity_set_panic_hook(callback: CCallback, param: *mut c_void) { - let cb = CallbackStr {user_data: param, function: callback}; - panic_hook::set_with(move |panic_msg| { - cb.call(panic_msg); - }); -} - -#[no_mangle] -pub unsafe extern fn parity_set_logger( - logger_mode: *const u8, - logger_mode_len: usize, - log_file: *const u8, - log_file_len: usize, - logger: *mut *mut c_void) { - - let mut logger_cfg = parity_ethereum::LoggerConfig::default(); - logger_cfg.mode = String::from_utf8(slice::from_raw_parts(logger_mode, logger_mode_len).to_owned()).ok(); - - // Make sure an empty string is not constructed as file name (to prevent panic) - if log_file_len != 0 && !log_file.is_null() { - logger_cfg.file = String::from_utf8(slice::from_raw_parts(log_file, log_file_len).to_owned()).ok(); - } - - *logger = Arc::into_raw(parity_ethereum::setup_log(&logger_cfg).expect("Logger initialized only once; qed")) as *mut _; -} - -// WebSocket event loop -fn parity_ws_worker(client: &RunningClient, query: &str, callback: Arc) -> *const c_void { - let (tx, mut rx) = mpsc::channel(1); - let session = Arc::new(PubSubSession::new(tx)); - let query_future = client.rpc_query(query, Some(session.clone())); - let weak_session = Arc::downgrade(&session); - let _handle = thread::Builder::new() - .name("ws-subscriber".into()) - .spawn(move || { - // Wait for subscription ID - // Note this may block forever and be can't destroyed using the session object - // However, this will likely timeout or be catched the RPC layer - if let Ok(Some(response)) = query_future.wait() { - callback.call(&response); - } else { - callback.call(error::SUBSCRIBE); - return; - } - - while weak_session.upgrade().map_or(0, |session| Arc::strong_count(&session)) > 1 { - for response in rx.by_ref().wait() { - if let Ok(r) = response { - callback.call(&r); - } - } - } - }) - .expect("rpc-subscriber thread shouldn't fail; qed"); - Arc::into_raw(session) as *const c_void -} - -// RPC event loop that runs for at most `timeout_ms` -fn parity_rpc_worker(client: &RunningClient, query: &str, callback: Arc, timeout_ms: u64) { - let cb = callback.clone(); - let query = client.rpc_query(query, None).map(move |response| { - let response = response.unwrap_or_else(|| error::EMPTY.to_string()); - callback.call(&response); - }); - - let _handle = thread::Builder::new() - .name("rpc_query".to_string()) - .spawn(move || { - let mut current_thread = CurrentThread::new(); - current_thread.spawn(query); - let _ = current_thread - .run_timeout(Duration::from_millis(timeout_ms)) - .map_err(|_e| { - cb.call(error::TIMEOUT); - }); - }) - .expect("rpc-query thread shouldn't fail; qed"); -} - -unsafe fn parity_rpc_query_checker<'a>(client: *const c_void, query: *const c_char, len: usize) - -> Option> -{ - let query_str = str::from_utf8(slice::from_raw_parts(query as *const u8, len)).ok()?; - let client: &RunningClient = &*(client as *const RunningClient); - Some((client, query_str)) -} diff --git a/parity/account.rs b/parity/account.rs index 118c06fdda..e3eaa55ce1 100644 --- a/parity/account.rs +++ b/parity/account.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::num::NonZeroU32; use params::SpecType; #[derive(Debug, PartialEq)] @@ -33,7 +32,7 @@ pub struct ListAccounts { #[derive(Debug, PartialEq)] pub struct NewAccount { - pub iterations: NonZeroU32, + pub iterations: u32, pub path: String, pub spec: SpecType, pub password_file: Option, @@ -87,7 +86,7 @@ mod command { RootDiskDirectory::create(path).map_err(|e| format!("Could not open keys directory: {}", e)) } - fn secret_store(dir: Box, iterations: Option) -> Result { + fn secret_store(dir: Box, iterations: Option) -> Result { match iterations { Some(i) => EthStore::open_with_iterations(dir, i), _ => EthStore::open(dir) diff --git a/parity/account_utils.rs b/parity/account_utils.rs index 6c11ae23b3..13588d6f3b 100644 --- a/parity/account_utils.rs +++ b/parity/account_utils.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -61,6 +61,8 @@ mod accounts { mod accounts { use super::*; use upgrade::upgrade_key_location; + use ethereum_types::H160; + use std::str::FromStr; pub use accounts::AccountProvider; @@ -77,13 +79,11 @@ mod accounts { upgrade_key_location(&dirs.legacy_keys_path(cfg.testnet), &path); let dir = Box::new(RootDiskDirectory::create(&path).map_err(|e| format!("Could not open keys directory: {}", e))?); let account_settings = AccountProviderSettings { - enable_hardware_wallets: cfg.enable_hardware_wallets, - hardware_wallet_classic_key: spec == &SpecType::Classic, unlock_keep_secret: cfg.enable_fast_unlock, blacklisted_accounts: match *spec { - SpecType::Morden | SpecType::Ropsten | SpecType::Kovan | SpecType::Sokol | SpecType::Dev => vec![], + SpecType::Morden | SpecType::Mordor | SpecType::Ropsten | SpecType::Kovan | SpecType::Goerli | SpecType::Kotti | SpecType::Sokol | SpecType::Dev => vec![], _ => vec![ - "00a329c0648769a73afac7f9381e08fb43dbea72".into() + H160::from_str("00a329c0648769a73afac7f9381e08fb43dbea72").expect("the string is valid hex; qed"), ], }, }; @@ -133,7 +133,7 @@ mod accounts { } pub fn miner_author(spec: &SpecType, dirs: &Directories, account_provider: &Arc, engine_signer: Address, passwords: &[Password]) -> Result, String> { - use ethcore::engines::EngineSigner; + use engine::signer::EngineSigner; // Check if engine signer exists if !account_provider.has_account(engine_signer) { @@ -166,7 +166,7 @@ mod accounts { mod private_tx { use super::*; - use ethkey::{Signature, Message}; + use parity_crypto::publickey::{Signature, Message}; use ethcore_private_tx::{Error}; pub struct AccountSigner { @@ -199,20 +199,20 @@ mod accounts { } } - pub fn private_tx_signer(accounts: Arc, passwords: &[Password]) -> Result, String> { + pub fn private_tx_signer(accounts: Arc, passwords: &[Password]) -> Result, String> { Ok(Arc::new(self::private_tx::AccountSigner { accounts, passwords: passwords.to_vec(), })) } - pub fn accounts_list(account_provider: Arc) -> Arc Vec
+ Send + Sync> { + pub fn accounts_list(account_provider: Arc) -> Arc Vec
+ Send + Sync> { Arc::new(move || account_provider.accounts().unwrap_or_default()) } 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"); + let secret = parity_crypto::publickey::Secret::from_str("4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7".into()).expect("Valid account;qed"); + let dev_account = parity_crypto::publickey::KeyPair::from_secret(secret.clone()).expect("Valid secret produces valid key;qed"); if !account_provider.has_account(dev_account.address()) { match account_provider.insert_account(secret, &Password::from(String::new())) { Err(e) => warn!("Unable to add development account: {}", e), @@ -241,4 +241,3 @@ pub use self::accounts::{ private_tx_signer, accounts_list, }; - diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 262b98ae2c..4e593f8dc1 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,27 +14,26 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::str::{FromStr, from_utf8}; +use std::str::from_utf8; use std::{io, fs}; use std::io::{BufReader, BufRead}; use std::time::{Instant, Duration}; use std::thread::sleep; use std::sync::Arc; + use rustc_hex::FromHex; use hash::{keccak, KECCAK_NULL_RLP}; use ethereum_types::{U256, H256, Address}; use bytes::ToPretty; use rlp::PayloadInfo; -use ethcore::client::{ - Mode, DatabaseCompactionProfile, VMType, Nonce, Balance, BlockChainClient, BlockId, BlockInfo, ImportBlock, BlockChainReset +use client_traits::{BlockChainReset, Nonce, Balance, BlockChainClient, ImportExportBlocks}; +use ethcore::{ + client::{DatabaseCompactionProfile}, + miner::Miner, }; -use ethcore::error::{ImportErrorKind, ErrorKind as EthcoreErrorKind, Error as EthcoreError}; -use ethcore::miner::Miner; -use ethcore::verification::queue::VerifierSettings; -use ethcore::verification::queue::kind::blocks::Unverified; use ethcore_service::ClientService; use cache::CacheConfig; -use informant::{Informant, FullNodeInformantData, MillisecondDuration}; +use informant::{Informant, FullNodeInformantData}; use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_bool}; use helpers::{to_client_config, execute_upgrades}; use dir::Directories; @@ -42,30 +41,13 @@ use user_defaults::UserDefaults; use ethcore_private_tx; use db; use ansi_term::Colour; - -#[derive(Debug, PartialEq)] -pub enum DataFormat { - Hex, - Binary, -} - -impl Default for DataFormat { - fn default() -> Self { - DataFormat::Binary - } -} - -impl FromStr for DataFormat { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "binary" | "bin" => Ok(DataFormat::Binary), - "hex" => Ok(DataFormat::Hex), - x => Err(format!("Invalid format: {}", x)) - } - } -} +use types::{ + ids::BlockId, + errors::{ImportError, EthcoreError}, + client_types::{Mode, StateResult}, +}; +use types::data_format::DataFormat; +use verification::queue::VerifierSettings; #[derive(Debug, PartialEq)] pub enum BlockchainCmd { @@ -110,7 +92,6 @@ pub struct ImportBlockchain { pub compaction: DatabaseCompactionProfile, pub tracing: Switch, pub fat_db: Switch, - pub vm_type: VMType, pub check_seal: bool, pub with_color: bool, pub verifier_settings: VerifierSettings, @@ -224,11 +205,13 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { config.queue.verifier_settings = cmd.verifier_settings; // initialize database. - let db = db::open_db(&client_path.to_str().expect("DB path could not be converted to string."), - &cmd.cache_config, - &cmd.compaction).map_err(|e| format!("Failed to open database: {:?}", e))?; + let db = db::open_db_light( + &client_path.to_str().expect("DB path could not be converted to string."), + &cmd.cache_config, + &cmd.compaction, + ).map_err(|e| format!("Failed to open database: {:?}", e))?; - // TODO: could epoch signals be avilable at the end of the file? + // TODO: could epoch signals be available at the end of the file? let fetch = ::light::client::fetch::unavailable(); let service = LightClientService::start(config, &spec, fetch, db, cache) .map_err(|e| format!("Failed to start client: {}", e))?; @@ -238,7 +221,7 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { let client = service.client(); - let mut instream: Box = match cmd.file_path { + let mut instream: Box = match cmd.file_path { Some(f) => Box::new(fs::File::open(&f).map_err(|_| format!("Cannot open given file: {}", f))?), None => Box::new(io::stdin()), }; @@ -272,7 +255,7 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { } match client.import_header(header) { - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { + Err(EthcoreError::Import(ImportError::AlreadyInChain)) => { trace!("Skipping block already in chain."); } Err(e) => { @@ -312,13 +295,13 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { } client.flush_queue(); - let ms = timer.elapsed().as_milliseconds(); + let elapsed = timer.elapsed(); let report = client.report(); info!("Import completed in {} seconds, {} headers, {} hdr/s", - ms / 1000, + elapsed.as_secs(), report.blocks_imported, - (report.blocks_imported * 1000) as u64 / ms, + (report.blocks_imported as u128 * 1000) / elapsed.as_millis(), ); Ok(()) @@ -369,7 +352,6 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { tracing, fat_db, cmd.compaction, - cmd.vm_type, "".into(), algorithm, cmd.pruning_history, @@ -406,27 +388,11 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { let client = service.client(); - let mut instream: Box = match cmd.file_path { + let instream: Box = match cmd.file_path { Some(f) => Box::new(fs::File::open(&f).map_err(|_| format!("Cannot open given file: {}", f))?), None => Box::new(io::stdin()), }; - const READAHEAD_BYTES: usize = 8; - - let mut first_bytes: Vec = vec![0; READAHEAD_BYTES]; - let mut first_read = 0; - - let format = match cmd.format { - Some(format) => format, - None => { - first_read = instream.read(&mut first_bytes).map_err(|_| "Error reading from the file/stream.")?; - match first_bytes[0] { - 0xf9 => DataFormat::Binary, - _ => DataFormat::Hex, - } - } - }; - let informant = Arc::new(Informant::new( FullNodeInformantData { client: client.clone(), @@ -440,49 +406,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { service.register_io_handler(informant).map_err(|_| "Unable to register informant handler".to_owned())?; - let do_import = |bytes| { - let block = Unverified::from_rlp(bytes).map_err(|_| "Invalid block rlp")?; - while client.queue_info().is_full() { sleep(Duration::from_secs(1)); } - match client.import_block(block) { - Err(EthcoreError(EthcoreErrorKind::Import(ImportErrorKind::AlreadyInChain), _)) => { - trace!("Skipping block already in chain."); - } - Err(e) => { - return Err(format!("Cannot import block: {:?}", e)); - }, - Ok(_) => {}, - } - Ok(()) - }; - - match format { - DataFormat::Binary => { - loop { - let mut bytes = if first_read > 0 {first_bytes.clone()} else {vec![0; READAHEAD_BYTES]}; - let n = if first_read > 0 { - first_read - } else { - instream.read(&mut bytes).map_err(|_| "Error reading from the file/stream.")? - }; - if n == 0 { break; } - first_read = 0; - let s = PayloadInfo::from(&bytes).map_err(|e| format!("Invalid RLP in the file/stream: {:?}", e))?.total(); - bytes.resize(s, 0); - instream.read_exact(&mut bytes[n..]).map_err(|_| "Error reading from the file/stream.")?; - do_import(bytes)?; - } - } - DataFormat::Hex => { - for line in BufReader::new(instream).lines() { - let s = line.map_err(|_| "Error reading from the file/stream.")?; - let s = if first_read > 0 {from_utf8(&first_bytes).unwrap().to_owned() + &(s[..])} else {s}; - first_read = 0; - let bytes = s.from_hex().map_err(|_| "Invalid hex in file/stream.")?; - do_import(bytes)?; - } - } - } - client.flush_queue(); + client.import_blocks(instream, cmd.format)?; // save user defaults user_defaults.pruning = algorithm; @@ -491,16 +415,16 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { user_defaults.save(&user_defaults_path)?; let report = client.report(); - - let ms = timer.elapsed().as_milliseconds(); + let elapsed = timer.elapsed(); + let ms = timer.elapsed().as_millis(); info!("Import completed in {} seconds, {} blocks, {} blk/s, {} transactions, {} tx/s, {} Mgas, {} Mgas/s", - ms / 1000, + elapsed.as_secs(), report.blocks_imported, - (report.blocks_imported * 1000) as u64 / ms, + (report.blocks_imported as u128 * 1000) / ms, report.transactions_applied, - (report.transactions_applied * 1000) as u64 / ms, + (report.transactions_applied as u128 * 1000) / ms, report.gas_processed / 1_000_000, - (report.gas_processed / (ms * 1000)).low_u64(), + report.gas_processed / (ms * 1000), ); Ok(()) } @@ -564,7 +488,6 @@ fn start_client( tracing, fat_db, compaction, - VMType::default(), "".into(), algorithm, pruning_history, @@ -611,32 +534,14 @@ fn execute_export(cmd: ExportBlockchain) -> Result<(), String> { false, cmd.max_round_blocks_to_import, )?; - let format = cmd.format.unwrap_or_default(); - let client = service.client(); - let mut out: Box = match cmd.file_path { + let out: Box = match cmd.file_path { Some(f) => Box::new(fs::File::create(&f).map_err(|_| format!("Cannot write to file given: {}", f))?), None => Box::new(io::stdout()), }; - let from = client.block_number(cmd.from_block).ok_or("From block could not be found")?; - let to = client.block_number(cmd.to_block).ok_or("To block could not be found")?; - - for i in from..(to + 1) { - if i % 10000 == 0 { - info!("#{}", i); - } - let b = client.block(BlockId::Number(i)).ok_or("Error exporting incomplete chain")?.into_inner(); - match format { - DataFormat::Binary => { - out.write(&b).map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?; - } - DataFormat::Hex => { - out.write_fmt(format_args!("{}", b.pretty())).map_err(|e| format!("Couldn't write to stream. Cause: {}", e))?; - } - } - } + client.export_blocks(out, cmd.from_block, cmd.to_block, cmd.format)?; info!("Export completed."); Ok(()) @@ -659,7 +564,7 @@ fn execute_export_state(cmd: ExportState) -> Result<(), String> { let client = service.client(); - let mut out: Box = match cmd.file_path { + let mut out: Box = match cmd.file_path { Some(f) => Box::new(fs::File::create(&f).map_err(|_| format!("Cannot write to file given: {}", f))?), None => Box::new(io::stdout()), }; @@ -686,7 +591,10 @@ fn execute_export_state(cmd: ExportState) -> Result<(), String> { out.write(b",").expect("Write error"); } out.write_fmt(format_args!("\n\"0x{:x}\": {{\"balance\": \"{:x}\", \"nonce\": \"{:x}\"", account, balance, client.nonce(&account, at).unwrap_or_else(U256::zero))).expect("Write error"); - let code = client.code(&account, at.into()).unwrap_or(None).unwrap_or_else(Vec::new); + let code = match client.code(&account, at.into()) { + StateResult::Missing => Vec::new(), + StateResult::Some(t) => t.unwrap_or_else(Vec::new), + }; if !code.is_empty() { out.write_fmt(format_args!(", \"code_hash\": \"0x{:x}\"", keccak(&code))).expect("Write error"); if cmd.code { @@ -700,7 +608,7 @@ fn execute_export_state(cmd: ExportState) -> Result<(), String> { out.write_fmt(format_args!(", \"storage\": {{")).expect("Write error"); let mut last_storage: Option = None; loop { - let keys = client.list_storage(at, &account, last_storage.as_ref(), 1000).ok_or("Specified block not found")?; + let keys = client.list_storage(at, &account, last_storage.as_ref(), Some(1000)).ok_or("Specified block not found")?; if keys.is_empty() { break; } diff --git a/parity/cache.rs b/parity/cache.rs index d6487221b3..5b1ee048c2 100644 --- a/parity/cache.rs +++ b/parity/cache.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index bbbb16c97d..b49ac6cebb 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -296,11 +296,11 @@ usage! { 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.", + "Set which release track we should use for updates. TRACK can be one of: stable - Stable 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 ethereum, classic, poacore, xdai, volta, ewc, expanse, musicoin, ellaism, mix, callisto, morden, ropsten, kovan, rinkeby, goerli, kotti, poasokol, testnet, or dev.", + "Specify the blockchain type. CHAIN may be either a JSON chain specification file or ethereum, classic, poacore, xdai, volta, ewc, musicoin, ellaism, mix, callisto, ethercore, morden, mordor, ropsten, kovan, rinkeby, goerli, kotti, poasokol, testnet, evantestcore, evancore or dev.", ARG arg_keys_path: (String) = "$BASE/keys", or |c: &Config| c.parity.as_ref()?.keys_path.clone(), "--keys-path=[PATH]", @@ -332,10 +332,6 @@ usage! { "Add SHIFT to all port numbers Parity is listening on. Includes network port and all servers (HTTP JSON-RPC, WebSockets JSON-RPC, IPFS, SecretStore).", ["Account Options"] - FLAG flag_no_hardware_wallets: (bool) = false, or |c: &Config| c.account.as_ref()?.disable_hardware.clone(), - "--no-hardware-wallets", - "Disables hardware wallet support.", - FLAG flag_fast_unlock: (bool) = false, or |c: &Config| c.account.as_ref()?.fast_unlock.clone(), "--fast-unlock", "Use drastically faster unlocking mode. This setting causes raw secrets to be stored unprotected in memory, so use with care.", @@ -352,6 +348,10 @@ usage! { "--unlock=[ACCOUNTS]", "Unlock ACCOUNTS for the duration of the execution. ACCOUNTS is a comma-delimited list of addresses.", + ARG arg_enable_signing_queue: (bool) = false, or |c: &Config| c.account.as_ref()?.enable_signing_queue, + "--enable-signing-queue=[BOOLEAN]", + "Enables the signing queue for external transaction signing either via CLI or personal_unlockAccount, turned off by default.", + ARG arg_password: (Vec) = Vec::new(), or |c: &Config| c.account.as_ref()?.password.clone(), "--password=[FILE]...", "Provide a file containing a password for unlocking an account. Leading and trailing whitespace is trimmed.", @@ -361,6 +361,10 @@ usage! { "--private-tx-enabled", "Enable private transactions.", + FLAG flag_private_state_offchain: (bool) = false, or |c: &Config| c.private_tx.as_ref()?.state_offchain, + "--private-state-offchain", + "Store private state offchain (in the local DB).", + 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.", @@ -498,26 +502,26 @@ usage! { "--jsonrpc-interface=[IP]", "Specify the hostname portion of the HTTP JSON-RPC 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,private,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,parity_transactions_pool", or |c: &Config| c.rpc.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), "--jsonrpc-apis=[APIS]", - "Specify the APIs available through the HTTP JSON-RPC interface using a comma-delimited list of API names. Possible names are: all, safe, debug, 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' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", + "Specify the APIs available through the HTTP JSON-RPC interface using a comma-delimited list of API names. Possible names are: all, safe, debug, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc", ARG arg_jsonrpc_hosts: (String) = "none", or |c: &Config| c.rpc.as_ref()?.hosts.as_ref().map(|vec| vec.join(",")), "--jsonrpc-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_jsonrpc_threads: (usize) = 4usize, or |c: &Config| c.rpc.as_ref()?.processing_threads, - "--jsonrpc-threads=[THREADS]", - "Turn on additional processing threads for JSON-RPC servers (all transports). Setting this to a non-zero value allows parallel execution of cpu-heavy queries.", + ARG arg_jsonrpc_threads: (Option) = None, or |_| None, + "--jsonrpc-threads=[NUM]", + "DEPRECATED, DOES NOTHING", + + ARG arg_jsonrpc_server_threads: (Option) = Some(4), or |c: &Config| c.rpc.as_ref()?.server_threads, + "--jsonrpc-server-threads=[NUM]", + "Enables multiple threads handling incoming connections for HTTP JSON-RPC server.", ARG arg_jsonrpc_cors: (String) = "none", or |c: &Config| c.rpc.as_ref()?.cors.as_ref().map(|vec| vec.join(",")), "--jsonrpc-cors=[URL]", "Specify CORS header for HTTP JSON-RPC API responses. Special options: \"all\", \"none\".", - ARG arg_jsonrpc_server_threads: (Option) = None, or |c: &Config| c.rpc.as_ref()?.server_threads, - "--jsonrpc-server-threads=[NUM]", - "Enables multiple threads handling incoming connections for HTTP JSON-RPC server.", - ARG arg_jsonrpc_max_payload: (Option) = None, or |c: &Config| c.rpc.as_ref()?.max_payload, "--jsonrpc-max-payload=[MB]", "Specify maximum size for HTTP JSON-RPC requests in megabytes.", @@ -539,9 +543,9 @@ usage! { "--ws-interface=[IP]", "Specify the hostname portion of the WebSockets JSON-RPC 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,private,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,parity_transactions_pool", or |c: &Config| c.websockets.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), "--ws-apis=[APIS]", - "Specify the JSON-RPC 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' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", + "Specify the JSON-RPC 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. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc", 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]", @@ -564,23 +568,13 @@ 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,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 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' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", + ARG arg_ipc_chmod: (String) = "660", or |c: &Config| c.ipc.as_ref()?.chmod.clone(), + "--ipc-chmod=[NUM]", + "Specify octal value for ipc socket permissions (unix/bsd only)", - ["API and Console Options – RabbitMQ"] - ARG arg_rabbitmq_uri: (String) = "amqp://localhost:5672", or |c: &Config| c.rabbitmq.as_ref()?.uri.clone(), - "--rabbitmq-uri=[URI]", - "Specify the RabbitMQ server uri", - - ["API and Console Options – Prometheus"] - ARG arg_prometheus_export_service: (bool) = false, or |c: &Config| c.prometheus_export_service.as_ref()?.prometheus_export_service.clone(), - "--prometheus-export-service=[BOOL]", - "Enable prometheus export service", - - ARG arg_prometheus_export_service_port: (u16) = 9898u16, or |c: &Config| c.prometheus_export_service.as_ref()?.prometheus_export_service_port.clone(), - "--prometheus-export-service-port=[PORT]", - "Specify the port to run the export service", + ARG arg_ipc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,private,traces,rpc,parity_transactions_pool", 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 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. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc", ["API and Console Options – IPFS"] FLAG flag_ipfs_api: (bool) = false, or |c: &Config| c.ipfs.as_ref()?.enable.clone(), @@ -637,6 +631,10 @@ usage! { "--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_http_cors: (String) = "none", or |c: &Config| c.secretstore.as_ref()?.cors.as_ref().map(|vec| vec.join(",")), + "--secretstore-http-cors=[URL]", + "Specify CORS header for Secret Store HTTP API responses. Special options: \"all\", \"none\".", + 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.", @@ -730,7 +728,9 @@ usage! { "--no-persistent-txqueue", "Don't save pending local transactions to disk to be restored whenever the node restarts.", - FLAG flag_stratum: (bool) = false, or |c: &Config| Some(c.stratum.is_some()), + // For backward compatibility; Stratum should be enabled if the config file + // contains a `[stratum]` section and it is not explicitly disabled (disable = true) + FLAG flag_stratum: (bool) = false, or |c: &Config| Some(c.stratum.as_ref().map(|s| s.disable != Some(true)).unwrap_or(false)), "--stratum", "Run Stratum server for miner push notification.", @@ -938,11 +938,11 @@ usage! { ["Whisper Options"] FLAG flag_whisper: (bool) = false, or |c: &Config| c.whisper.as_ref()?.enabled, "--whisper", - "Enable the Whisper network.", + "Does nothing. Whisper has been moved to https://github.com/paritytech/whisper", - ARG arg_whisper_pool_size: (usize) = 10usize, or |c: &Config| c.whisper.as_ref()?.pool_size.clone(), + ARG arg_whisper_pool_size: (Option) = None, or |c: &Config| c.whisper.as_ref()?.pool_size.clone(), "--whisper-pool-size=[MB]", - "Target size of the whisper message pool in megabytes.", + "Does nothing. Whisper has been moved to https://github.com/paritytech/whisper", ["Legacy Options"] // Options that are hidden from config, but are still unique for its functionality. @@ -1160,8 +1160,6 @@ struct Config { rpc: Option, websockets: Option, ipc: Option, - rabbitmq: Option, - prometheus_export_service: Option, dapps: Option, secretstore: Option, private_tx: Option, @@ -1204,10 +1202,10 @@ struct Operating { #[serde(deny_unknown_fields)] struct Account { unlock: Option>, + enable_signing_queue: Option, password: Option>, keys_iterations: Option, refresh_time: Option, - disable_hardware: Option, fast_unlock: Option, } @@ -1215,6 +1213,7 @@ struct Account { #[serde(deny_unknown_fields)] struct PrivateTransactions { enabled: Option, + state_offchain: Option, signer: Option, validators: Option>, account: Option, @@ -1272,7 +1271,6 @@ struct Rpc { apis: Option>, hosts: Option>, server_threads: Option, - processing_threads: Option, max_payload: Option, keep_alive: Option, experimental_rpcs: Option, @@ -1295,25 +1293,12 @@ struct Ws { #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Ipc { + chmod: Option, disable: Option, path: Option, apis: Option>, } -#[derive(Default, Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -struct RabbitMQ { - uri: Option, -} - -#[derive(Default, Debug, PartialEq, Deserialize)] -#[serde(deny_unknown_fields)] -struct PrometheusExportService { - prometheus_export_service: Option, - prometheus_export_service_port: Option -} - - #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Dapps { @@ -1356,6 +1341,7 @@ struct SecretStore { http_interface: Option, http_port: Option, path: Option, + cors: Option> } #[derive(Default, Debug, PartialEq, Deserialize)] @@ -1409,6 +1395,7 @@ struct Mining { #[derive(Default, Debug, PartialEq, Deserialize)] #[serde(deny_unknown_fields)] struct Stratum { + disable: Option, interface: Option, port: Option, secret: Option, @@ -1471,8 +1458,8 @@ struct Light { mod tests { use super::{ Args, ArgsError, - Config, Operating, Account, Ui, Network, Ws, Rpc, Ipc, RabbitMQ, Dapps, Ipfs, Mining, Footprint, - Snapshots, Misc, Whisper, SecretStore, Light, PrometheusExportService + Config, Operating, Account, Ui, Network, Ws, Rpc, Ipc, Dapps, Ipfs, Mining, Footprint, + Snapshots, Misc, Whisper, SecretStore, Light, }; use toml; use clap::{ErrorKind as ClapErrorKind}; @@ -1586,14 +1573,14 @@ mod tests { // given let mut config = Config::default(); let mut operating = Operating::default(); - operating.chain = Some("morden".into()); + operating.chain = Some("mordor".into()); config.parity = Some(operating); // when let args = Args::parse_with_config(&["parity"], config).unwrap(); // then - assert_eq!(args.arg_chain, "morden".to_owned()); + assert_eq!(args.arg_chain, "mordor".to_owned()); } #[test] @@ -1601,7 +1588,7 @@ mod tests { // given let mut config = Config::default(); let mut operating = Operating::default(); - operating.chain = Some("morden".into()); + operating.chain = Some("mordor".into()); config.parity = Some(operating); // when @@ -1625,6 +1612,83 @@ mod tests { assert_eq!(args.arg_pruning_history, 128); } + #[test] + fn should_disable_stratum() { + // given + let config = toml::from_str(include_str!("./tests/config.stratum_disabled.toml")).unwrap(); + + // when + let args = Args::parse_with_config(&["parity"], config).unwrap(); + + // then + assert_eq!(args.flag_stratum, false); + assert_eq!(args.arg_stratum_interface, "local".to_owned()); + assert_eq!(args.arg_stratum_port, 8008u16); + assert_eq!(args.arg_stratum_secret, None); + } + + #[test] + fn should_disable_stratum_when_missing_section() { + // given + let config = toml::from_str(include_str!("./tests/config.stratum_missing_section.toml")).unwrap(); + + // when + let args = Args::parse_with_config(&["parity"], config).unwrap(); + + // then + assert_eq!(args.flag_stratum, false); + assert_eq!(args.arg_stratum_interface, "local".to_owned()); + assert_eq!(args.arg_stratum_port, 8008u16); + assert_eq!(args.arg_stratum_secret, None); + } + + #[test] + fn should_enable_stratum() { + // given + let config = toml::from_str(include_str!("./tests/config.stratum_enabled.toml")).unwrap(); + + // when + let args = Args::parse_with_config(&["parity"], config).unwrap(); + + // then (with custom configurations) + assert_eq!(args.flag_stratum, true); + assert_eq!(args.arg_stratum_interface, "some_interface".to_owned()); + assert_eq!(args.arg_stratum_port, 8007u16); + assert_eq!(args.arg_stratum_secret, Some("Yellow".to_owned())); + } + + #[test] + fn should_enable_stratum_by_param() { + // given + let config = toml::from_str(include_str!("./tests/config.full.toml")).unwrap(); + + // when + let args = Args::parse_with_config(&["parity", "--stratum"], config).unwrap(); + + // then + assert_eq!(args.flag_stratum, true); + assert_eq!(args.arg_stratum_interface, "local".to_owned()); + assert_eq!(args.arg_stratum_port, 8008u16); + assert_eq!(args.arg_stratum_secret, None); + } + + #[test] + // For backward compatibility; Stratum should be enabled if the config file + // contains a `[stratum]` section and it is not explicitly disabled (disable = true) + fn should_enable_stratum_when_missing_field() { + // given + let config = toml::from_str(include_str!("./tests/config.stratum_missing_field.toml")).unwrap(); + + // when + let args = Args::parse_with_config(&["parity"], config).unwrap(); + + // then + assert_eq!(args.flag_stratum, true); + assert_eq!(args.arg_stratum_interface, "local".to_owned()); + assert_eq!(args.arg_stratum_port, 8008u16); + assert_eq!(args.arg_stratum_secret, None); + } + #[test] fn should_parse_full_config() { // given @@ -1674,6 +1738,7 @@ mod tests { arg_restore_file: None, arg_tools_hash_file: None, + arg_enable_signing_queue: false, arg_signer_sign_id: None, arg_signer_reject_id: None, arg_dapp_path: None, @@ -1712,11 +1777,11 @@ mod tests { arg_password: vec!["~/.safe/password.file".into()], arg_keys_iterations: 10240u32, arg_accounts_refresh: 5u64, - flag_no_hardware_wallets: false, flag_fast_unlock: false, // -- Private Transactions Options flag_private_enabled: true, + flag_private_state_offchain: false, arg_private_signer: Some("0xdeadbeefcafe0000000000000000000000000000".into()), arg_private_validators: Some("0xdeadbeefcafe0000000000000000000000000000".into()), arg_private_passwords: Some("~/.safe/password.file".into()), @@ -1761,8 +1826,8 @@ mod tests { arg_jsonrpc_cors: "null".into(), arg_jsonrpc_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(), arg_jsonrpc_hosts: "none".into(), - arg_jsonrpc_server_threads: None, - arg_jsonrpc_threads: 4, + arg_jsonrpc_server_threads: Some(4), + arg_jsonrpc_threads: None, // DEPRECATED, does nothing arg_jsonrpc_max_payload: None, arg_poll_lifetime: 60u32, flag_jsonrpc_allow_missing_blocks: false, @@ -1780,14 +1845,7 @@ mod tests { flag_no_ipc: false, arg_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), arg_ipc_apis: "web3,eth,net,parity,parity_accounts,personal,traces,rpc,secretstore".into(), - - // RabbitMQ - arg_rabbitmq_uri: "amqp://localhost:5672".into(), - - // Prometheus - arg_prometheus_export_service: false, - arg_prometheus_export_service_port: 9898, - + arg_ipc_chmod: "660".into(), // DAPPS arg_dapps_path: Some("$HOME/.parity/dapps".into()), flag_no_dapps: false, @@ -1811,6 +1869,7 @@ mod tests { arg_secretstore_http_interface: "local".into(), arg_secretstore_http_port: 8082u16, arg_secretstore_path: "$HOME/.parity/secretstore".into(), + arg_secretstore_http_cors: "null".into(), // IPFS flag_ipfs_api: false, @@ -1899,7 +1958,7 @@ mod tests { // -- Whisper options. flag_whisper: false, - arg_whisper_pool_size: 20, + arg_whisper_pool_size: Some(20), // -- Legacy Options flag_warp: false, @@ -1997,11 +2056,11 @@ mod tests { _legacy_public_node: None, }), account: Some(Account { + enable_signing_queue: None, unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]), password: Some(vec!["passwdfile path".into()]), keys_iterations: None, refresh_time: None, - disable_hardware: None, fast_unlock: None, }), ui: Some(Ui { @@ -2047,8 +2106,7 @@ mod tests { cors: None, apis: None, hosts: None, - server_threads: None, - processing_threads: None, + server_threads: Some(13), max_payload: None, keep_alive: None, experimental_rpcs: None, @@ -2058,15 +2116,9 @@ mod tests { ipc: Some(Ipc { disable: None, path: None, + chmod: None, apis: Some(vec!["rpc".into(), "eth".into()]), }), - rabbitmq: Some(RabbitMQ { - uri: Some("amqp://localhost:5672".into()), - }), - prometheus_export_service: Some(PrometheusExportService { - prometheus_export_service: Some(true), - prometheus_export_service_port: Some(9999), - }), dapps: Some(Dapps { _legacy_disable: None, _legacy_port: Some(8080), @@ -2096,6 +2148,7 @@ mod tests { http_interface: None, http_port: Some(8082), path: None, + cors: None, }), private_tx: None, ipfs: Some(Ipfs { diff --git a/parity/cli/presets/mod.rs b/parity/cli/presets/mod.rs index 25bccf41b8..53fdb2606e 100644 --- a/parity/cli/presets/mod.rs +++ b/parity/cli/presets/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/parity/cli/tests/config.full.toml b/parity/cli/tests/config.full.toml index 34dd39058b..a1b5b29049 100644 --- a/parity/cli/tests/config.full.toml +++ b/parity/cli/tests/config.full.toml @@ -75,6 +75,7 @@ apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"] hosts = ["none"] [ipc] +chmod = "660" disable = false path = "$HOME/.parity/jsonrpc.ipc" apis = ["web3", "eth", "net", "parity", "parity_accounts", "personal", "traces", "rpc", "secretstore"] @@ -105,6 +106,7 @@ http_port = 8082 interface = "local" port = 8083 path = "$HOME/.parity/secretstore" +cors = ["null"] [ipfs] enable = false diff --git a/parity/cli/tests/config.stratum_disabled.toml b/parity/cli/tests/config.stratum_disabled.toml new file mode 100644 index 0000000000..b7f99af7e3 --- /dev/null +++ b/parity/cli/tests/config.stratum_disabled.toml @@ -0,0 +1,2 @@ +[stratum] +disable = true \ No newline at end of file diff --git a/parity/cli/tests/config.stratum_enabled.toml b/parity/cli/tests/config.stratum_enabled.toml new file mode 100644 index 0000000000..5b46c0c0cc --- /dev/null +++ b/parity/cli/tests/config.stratum_enabled.toml @@ -0,0 +1,5 @@ +[stratum] +disable = false +interface = "some_interface" +port = 8007 +secret = "Yellow" \ No newline at end of file diff --git a/parity/cli/tests/config.stratum_missing_field.toml b/parity/cli/tests/config.stratum_missing_field.toml new file mode 100644 index 0000000000..7ceed18150 --- /dev/null +++ b/parity/cli/tests/config.stratum_missing_field.toml @@ -0,0 +1 @@ +[stratum] \ No newline at end of file diff --git a/parity/cli/tests/config.stratum_missing_section.toml b/parity/cli/tests/config.stratum_missing_section.toml new file mode 100644 index 0000000000..65efa412c8 --- /dev/null +++ b/parity/cli/tests/config.stratum_missing_section.toml @@ -0,0 +1 @@ +# No `[stratum]` section \ No newline at end of file diff --git a/parity/cli/tests/config.toml b/parity/cli/tests/config.toml index c94f4ea295..8c39f4c25f 100644 --- a/parity/cli/tests/config.toml +++ b/parity/cli/tests/config.toml @@ -31,6 +31,7 @@ origins = ["none"] [rpc] disable = true port = 8180 +server_threads = 13 [ipc] apis = ["rpc", "eth"] diff --git a/parity/cli/usage.rs b/parity/cli/usage.rs index 8b06f4f1f6..4939e1f9c4 100644 --- a/parity/cli/usage.rs +++ b/parity/cli/usage.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/parity/cli/usage_header.txt b/parity/cli/usage_header.txt index ec9f7a2216..77c64a702c 100644 --- a/parity/cli/usage_header.txt +++ b/parity/cli/usage_header.txt @@ -2,5 +2,5 @@ Parity Ethereum Client. By Wood/Paronyan/Kotewicz/Drwięga/Volf/Greeff Habermeier/Czaban/Gotchac/Redman/Nikolsky Schoedon/Tang/Adolfsson/Silva/Palm/Hirsz et al. - Copyright 2015-2019 Parity Technologies (UK) Ltd. + Copyright 2015-2020 Parity Technologies (UK) Ltd. License GPLv3+: GNU GPL version 3 or later . diff --git a/parity/cli/version.txt b/parity/cli/version.txt index 26eae78ddf..8c4dffbe72 100644 --- a/parity/cli/version.txt +++ b/parity/cli/version.txt @@ -1,6 +1,6 @@ Parity Ethereum Client. version {} -Copyright 2015-2019 Parity Technologies (UK) Ltd. +Copyright 2015-2020 Parity Technologies (UK) Ltd. License GPLv3+: GNU GPL version 3 or later . This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. diff --git a/parity/configuration.rs b/parity/configuration.rs index 62a315917d..f1b5749f78 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,6 @@ use std::time::Duration; use std::io::Read; use std::net::{SocketAddr, ToSocketAddrs}; -use std::num::NonZeroU32; use std::path::PathBuf; use std::collections::{HashSet, BTreeMap}; use std::iter::FromIterator; @@ -29,16 +28,13 @@ use parity_version::{version_data, version}; use bytes::Bytes; use ansi_term::Colour; use sync::{NetworkConfiguration, validate_node_url, self}; -use ethkey::{Secret, Public}; -use ethcore::client::{VMType}; +use parity_crypto::publickey::{Secret, Public}; use ethcore::miner::{stratum, MinerOptions}; -use ethcore::snapshot::SnapshotConfiguration; -use ethcore::verification::queue::VerifierSettings; +use snapshot::SnapshotConfiguration; use miner::pool; -use num_cpus; +use verification::queue::VerifierSettings; use rpc::{IpcConfiguration, HttpConfiguration, WsConfiguration}; -use parity_rabbitmq::client::{RabbitMqConfig, PrometheusExportServiceConfig}; 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_queue_strategy, to_queue_penalization}; @@ -51,15 +47,17 @@ use ethcore_private_tx::{ProviderConfig, EncryptorConfig}; use secretstore::{NodeSecretKey, Configuration as SecretStoreConfiguration, ContractAddress as SecretStoreContractAddress}; use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack}; use run::RunCmd; -use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, DataFormat, ResetBlockchain}; +use types::data_format::DataFormat; +use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, ResetBlockchain}; use export_hardcoded_sync::ExportHsyncCmd; use presale::ImportWallet; use account::{AccountCmd, NewAccount, ListAccounts, ImportAccounts, ImportFromGethAccounts}; -use snapshot::{self, SnapshotCommand}; -use network::{IpFilter}; +use snapshot_cmd::{self, SnapshotCommand}; +use network::{IpFilter, NatType}; const DEFAULT_MAX_PEERS: u16 = 50; const DEFAULT_MIN_PEERS: u16 = 25; +pub const ETHERSCAN_ETH_PRICE_ENDPOINT: &str = "https://api.etherscan.io/api?module=stats&action=ethprice"; #[derive(Debug, PartialEq)] pub enum Cmd { @@ -121,7 +119,6 @@ impl Configuration { let dirs = self.directories(); let pruning = self.args.arg_pruning.parse()?; let pruning_history = self.args.arg_pruning_history; - let vm_type = self.vm_type()?; let spec = self.chain()?; let mode = match self.args.arg_mode.as_ref() { "last" => None, @@ -134,8 +131,6 @@ impl Configuration { let http_conf = self.http_config()?; let ipc_conf = self.ipc_config()?; let net_conf = self.net_config()?; - let rabbitmq_conf = self.rabbitmq_config(); - let prometheus_export_service_config = self.prometheus_export_service_config(); let network_id = self.network_id(); let cache_config = self.cache_config(); let tracing = self.args.arg_tracing.parse()?; @@ -147,8 +142,11 @@ impl Configuration { let ipfs_conf = self.ipfs_config(); let secretstore_conf = self.secretstore_config()?; let format = self.format()?; - let keys_iterations = NonZeroU32::new(self.args.arg_keys_iterations) - .ok_or_else(|| "--keys-iterations must be non-zero")?; + + let key_iterations = self.args.arg_keys_iterations; + if key_iterations == 0 { + return Err("--key-iterations must be non-zero".into()); + } let cmd = if self.args.flag_version { Cmd::Version @@ -205,7 +203,7 @@ impl Configuration { } else if self.args.cmd_account { let account_cmd = if self.args.cmd_account_new { let new_acc = NewAccount { - iterations: keys_iterations, + iterations: key_iterations, path: dirs.keys, spec: spec, password_file: self.accounts_config()?.password_files.first().map(|x| x.to_owned()), @@ -239,7 +237,7 @@ impl Configuration { Cmd::Account(account_cmd) } else if self.args.cmd_wallet { let presale_cmd = ImportWallet { - iterations: keys_iterations, + iterations: key_iterations, path: dirs.keys, spec: spec, wallet_path: self.args.arg_wallet_import_path.clone().unwrap(), @@ -259,7 +257,6 @@ impl Configuration { compaction: compaction, tracing: tracing, fat_db: fat_db, - vm_type: vm_type, check_seal: !self.args.flag_no_seal_check, with_color: logger_config.color, verifier_settings: self.verifier_settings(), @@ -323,7 +320,7 @@ impl Configuration { fat_db: fat_db, compaction: compaction, file_path: self.args.arg_snapshot_file.clone(), - kind: snapshot::Kind::Take, + kind: snapshot_cmd::Kind::Take, block_at: to_block_id(&self.args.arg_snapshot_at)?, max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, snapshot_conf: snapshot_conf, @@ -341,7 +338,7 @@ impl Configuration { fat_db: fat_db, compaction: compaction, file_path: self.args.arg_restore_file.clone(), - kind: snapshot::Kind::Restore, + kind: snapshot_cmd::Kind::Restore, block_at: to_block_id("latest")?, // unimportant. max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, snapshot_conf: snapshot_conf, @@ -364,59 +361,54 @@ 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, - dirs: dirs, - spec: spec, - pruning: pruning, - pruning_history: pruning_history, + cache_config, + dirs, + spec, + pruning, + pruning_history, pruning_memory: self.args.arg_pruning_memory, - daemon: daemon, + daemon, 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, - ws_conf: ws_conf, - snapshot_conf: snapshot_conf, - http_conf: http_conf, - ipc_conf: ipc_conf, - net_conf: net_conf, - rabbitmq_conf: rabbitmq_conf, - prometheus_export_service_conf: prometheus_export_service_config, - network_id: network_id, + ws_conf, + snapshot_conf, + http_conf, + ipc_conf, + net_conf, + network_id, acc_conf: self.accounts_config()?, gas_pricer_conf: self.gas_pricer_config()?, miner_extras: self.miner_extras()?, stratum: self.stratum_options()?, - update_policy: update_policy, + update_policy, allow_missing_blocks: self.args.flag_jsonrpc_allow_missing_blocks, - mode: mode, - tracing: tracing, - fat_db: fat_db, - compaction: compaction, - vm_type: vm_type, - warp_sync: warp_sync, + mode, + tracing, + fat_db, + compaction, + warp_sync, warp_barrier: self.args.arg_warp_barrier, - geth_compatibility: geth_compatibility, + geth_compatibility, experimental_rpcs, net_settings: self.network_settings()?, - ipfs_conf: ipfs_conf, - secretstore_conf: secretstore_conf, - private_provider_conf: private_provider_conf, + ipfs_conf, + secretstore_conf, + private_provider_conf, private_encryptor_conf: private_enc_conf, private_tx_enabled, name: self.args.arg_identity, custom_bootnodes: self.args.arg_bootnodes.is_some(), check_seal: !self.args.flag_no_seal_check, download_old_blocks: !self.args.flag_no_ancient_blocks, - verifier_settings: verifier_settings, + verifier_settings, serve_light: !self.args.flag_no_serve_light, light: self.args.flag_light, no_persistent_txqueue: self.args.flag_no_persistent_txqueue, - whisper: whisper_config, no_hardcoded_sync: self.args.flag_no_hardcoded_sync, max_round_blocks_to_import: self.args.arg_max_round_blocks_to_import, on_demand_response_time_window: self.args.arg_on_demand_response_time_window, @@ -430,14 +422,10 @@ impl Configuration { Ok(Execute { logger: logger_config, - cmd: cmd, + cmd, }) } - fn vm_type(&self) -> Result { - 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)?; @@ -537,15 +525,12 @@ impl Configuration { } fn accounts_config(&self) -> Result { - let keys_iterations = NonZeroU32::new(self.args.arg_keys_iterations) - .ok_or_else(|| "--keys-iterations must be non-zero")?; let cfg = AccountsConfig { - iterations: keys_iterations, + iterations: self.args.arg_keys_iterations, refresh_time: self.args.arg_accounts_refresh, testnet: self.args.flag_testnet, 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, }; @@ -643,6 +628,7 @@ impl Configuration { http_port: self.args.arg_ports_shift + self.args.arg_secretstore_http_port, data_path: self.directories().secretstore, admin_public: self.secretstore_admin_public()?, + cors: self.secretstore_cors() }) } @@ -673,23 +659,30 @@ impl Configuration { } let usd_per_tx = to_price(&self.args.arg_usd_per_tx)?; - if "auto" == self.args.arg_usd_per_eth.as_str() { - return Ok(GasPricerConfig::Calibrated { + + if "auto" == self.args.arg_usd_per_eth { + Ok(GasPricerConfig::Calibrated { usd_per_tx: usd_per_tx, recalibration_period: to_duration(self.args.arg_price_update_period.as_str())?, - }); - } - - let usd_per_eth = to_price(&self.args.arg_usd_per_eth)?; - let wei_per_gas = wei_per_gas(usd_per_tx, usd_per_eth); + api_endpoint: ETHERSCAN_ETH_PRICE_ENDPOINT.to_string(), + }) + } else if let Ok(usd_per_eth_parsed) = to_price(&self.args.arg_usd_per_eth) { + let wei_per_gas = wei_per_gas(usd_per_tx, usd_per_eth_parsed); - info!( - "Using a fixed conversion rate of Ξ1 = {} ({} wei/gas)", - Colour::White.bold().paint(format!("US${:.2}", usd_per_eth)), - Colour::Yellow.bold().paint(format!("{}", wei_per_gas)) - ); + info!( + "Using a fixed conversion rate of Ξ1 = {} ({} wei/gas)", + Colour::White.bold().paint(format!("US${:.2}", usd_per_eth_parsed)), + Colour::Yellow.bold().paint(format!("{}", wei_per_gas)) + ); - Ok(GasPricerConfig::Fixed(wei_per_gas)) + Ok(GasPricerConfig::Fixed(wei_per_gas)) + } else { + Ok(GasPricerConfig::Calibrated { + usd_per_tx: usd_per_tx, + recalibration_period: to_duration(self.args.arg_price_update_period.as_str())?, + api_endpoint: self.args.arg_usd_per_eth.clone(), + }) + } } fn extra_data(&self) -> Result { @@ -715,7 +708,7 @@ impl Configuration { for line in &lines { match validate_node_url(line).map(Into::into) { None => continue, - Some(sync::ErrorKind::AddressResolve(_)) => return Err(format!("Failed to resolve hostname of a boot node: {}", line)), + Some(sync::Error::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)), } } @@ -750,13 +743,19 @@ impl Configuration { fn net_config(&self) -> Result { let mut ret = NetworkConfiguration::new(); - ret.nat_enabled = self.args.arg_nat == "any" || self.args.arg_nat == "upnp"; + ret.nat_enabled = self.args.arg_nat == "any" || self.args.arg_nat == "upnp" || self.args.arg_nat == "natpmp"; + ret.nat_type = match &self.args.arg_nat[..] { + "any" => NatType::Any, + "upnp" => NatType::UPnP, + "natpmp" => NatType::NatPMP, + _ => NatType::Nothing, + }; ret.boot_nodes = to_bootnodes(&self.args.arg_bootnodes)?; let (listen, public) = self.net_addresses()?; ret.listen_address = Some(format!("{}", listen)); ret.public_address = public.map(|p| format!("{}", p)); ret.use_secret = match self.args.arg_node_key.as_ref() - .map(|s| s.parse::().or_else(|_| Secret::from_unsafe_slice(&keccak(s))).map_err(|e| format!("Invalid key: {:?}", e)) + .map(|s| s.parse::().or_else(|_| Secret::import_key(keccak(s).as_bytes())).map_err(|e| format!("Invalid key: {:?}", e)) ) { None => None, Some(Ok(key)) => Some(key), @@ -785,20 +784,6 @@ impl Configuration { Ok(ret) } - fn rabbitmq_config(&self) -> RabbitMqConfig { - RabbitMqConfig { - uri: self.args.arg_rabbitmq_uri.clone() - } - } - - fn prometheus_export_service_config(&self) -> PrometheusExportServiceConfig { - PrometheusExportServiceConfig { - prometheus_export_service: self.args.arg_prometheus_export_service.clone(), - prometheus_export_service_port: self.args.arg_prometheus_export_service_port.clone() - } - } - - fn network_id(&self) -> Option { self.args.arg_network_id.or(self.args.arg_networkid) } @@ -880,6 +865,7 @@ impl Configuration { fn ipc_config(&self) -> Result { let conf = IpcConfiguration { + chmod: self.args.arg_ipc_chmod.clone(), enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc), socket_addr: self.ipc_path(), apis: { @@ -898,24 +884,20 @@ impl Configuration { } fn http_config(&self) -> Result { - let conf = HttpConfiguration { - 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: self.rpc_apis().parse()?, - hosts: self.rpc_hosts(), - cors: self.rpc_cors(), - server_threads: match self.args.arg_jsonrpc_server_threads { - Some(threads) if threads > 0 => threads, - _ => 1, - }, - processing_threads: self.args.arg_jsonrpc_threads, - max_payload: match self.args.arg_jsonrpc_max_payload { - Some(max) if max > 0 => max as usize, - _ => 5usize, - }, - keep_alive: !self.args.flag_jsonrpc_no_keep_alive, - }; + let mut conf = HttpConfiguration::default(); + conf.enabled = self.rpc_enabled(); + conf.interface = self.rpc_interface(); + conf.port = self.args.arg_ports_shift + self.args.arg_rpcport.unwrap_or(self.args.arg_jsonrpc_port); + conf.apis = self.rpc_apis().parse()?; + conf.hosts = self.rpc_hosts(); + conf.cors = self.rpc_cors(); + if let Some(threads) = self.args.arg_jsonrpc_server_threads { + conf.server_threads = std::cmp::max(1, threads); + } + if let Some(max_payload) = self.args.arg_jsonrpc_max_payload { + conf.max_payload = std::cmp::max(1, max_payload); + } + conf.keep_alive = !self.args.flag_jsonrpc_no_keep_alive; Ok(conf) } @@ -923,7 +905,7 @@ impl Configuration { fn ws_config(&self) -> Result { let support_token_api = // enabled when not unlocking - self.args.arg_unlock.is_none(); + self.args.arg_unlock.is_none() && self.args.arg_enable_signing_queue; let conf = WsConfiguration { enabled: self.ws_enabled(), @@ -941,9 +923,15 @@ impl Configuration { } fn private_provider_config(&self) -> Result<(ProviderConfig, EncryptorConfig, bool), String> { + let dirs = self.directories(); 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()), + logs_path: match self.args.flag_private_enabled { + true => Some(dirs.base), + false => None, + }, + use_offchain_storage: self.args.flag_private_state_offchain, }; let encryptor_conf = EncryptorConfig { @@ -956,13 +944,13 @@ impl Configuration { } fn snapshot_config(&self) -> Result { - let conf = SnapshotConfiguration { - no_periodic: self.args.flag_no_periodic_snapshot, - processing_threads: match self.args.arg_snapshot_threads { - Some(threads) if threads > 0 => threads, - _ => ::std::cmp::max(1, num_cpus::get_physical() / 2), - }, - }; + let mut conf = SnapshotConfiguration::default(); + conf.no_periodic = self.args.flag_no_periodic_snapshot; + if let Some(threads) = self.args.arg_snapshot_threads { + if threads > 0 { + conf.processing_threads = threads; + } + } Ok(conf) } @@ -993,9 +981,7 @@ impl Configuration { }, track: match self.args.arg_release_track.as_ref() { "stable" => ReleaseTrack::Stable, - "beta" => ReleaseTrack::Beta, "nightly" => ReleaseTrack::Nightly, - "testing" => ReleaseTrack::Testing, "current" => ReleaseTrack::Unknown, _ => return Err("Invalid value for `--releases-track`. See `--help` for more information.".into()), }, @@ -1086,6 +1072,10 @@ impl Configuration { self.interface(&self.args.arg_secretstore_http_interface) } + fn secretstore_cors(&self) -> Option> { + Self::cors(self.args.arg_secretstore_http_cors.as_ref()) + } + fn secretstore_self_secret(&self) -> Result, String> { match self.args.arg_secretstore_secret { Some(ref s) if s.len() == 64 => Ok(Some(NodeSecretKey::Plain(s.parse() @@ -1190,13 +1180,6 @@ impl Configuration { settings } - - fn whisper_config(&self) -> ::whisper::Config { - ::whisper::Config { - enabled: self.args.flag_whisper, - target_message_pool_size: self.args.arg_whisper_pool_size * 1024 * 1024, - } - } } fn into_secretstore_service_contract_address(s: Option<&String>) -> Result, String> { @@ -1214,14 +1197,14 @@ mod tests { use std::str::FromStr; use tempdir::TempDir; - use ethcore::client::{VMType, BlockId}; use ethcore::miner::MinerOptions; use miner::pool::PrioritizationStrategy; use parity_rpc::NetworkSettings; use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack}; - + use types::ids::BlockId; + use types::data_format::DataFormat; use account::{AccountCmd, NewAccount, ImportAccounts, ListAccounts}; - use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat, ExportState}; + use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, ExportState}; use cli::Args; use dir::{Directories, default_hypervisor_path}; use helpers::{default_network_config}; @@ -1238,10 +1221,6 @@ mod tests { use super::*; - lazy_static! { - static ref ITERATIONS: NonZeroU32 = NonZeroU32::new(10240).expect("10240 > 0; qed"); - } - #[derive(Debug, PartialEq)] struct TestPasswordReader(&'static str); @@ -1263,7 +1242,7 @@ mod tests { let args = vec!["parity", "account", "new"]; let conf = parse(&args); assert_eq!(conf.into_command().unwrap().cmd, Cmd::Account(AccountCmd::New(NewAccount { - iterations: *ITERATIONS, + iterations: 10240, path: Directories::default().keys, password_file: None, spec: SpecType::default(), @@ -1298,7 +1277,7 @@ mod tests { let args = vec!["parity", "wallet", "import", "my_wallet.json", "--password", "pwd"]; let conf = parse(&args); assert_eq!(conf.into_command().unwrap().cmd, Cmd::ImportPresaleWallet(ImportWallet { - iterations: *ITERATIONS, + iterations: 10240, path: Directories::default().keys, wallet_path: "my_wallet.json".into(), password_file: Some("pwd".into()), @@ -1322,7 +1301,6 @@ mod tests { compaction: Default::default(), tracing: Default::default(), fat_db: Default::default(), - vm_type: VMType::Interpreter, check_seal: true, with_color: !cfg!(windows), verifier_settings: Default::default(), @@ -1415,7 +1393,7 @@ mod tests { origins: Some(vec!["parity://*".into(),"chrome-extension://*".into(), "moz-extension://*".into()]), hosts: Some(vec![]), signer_path: expected.into(), - support_token_api: true, + support_token_api: false, max_connections: 100, }, LogConfig { color: !cfg!(windows), @@ -1456,13 +1434,6 @@ mod tests { http_conf: Default::default(), ipc_conf: Default::default(), net_conf: default_network_config(), - rabbitmq_conf: RabbitMqConfig { - uri: "amqp://localhost:5672".into(), - }, - prometheus_export_service_conf: PrometheusExportServiceConfig { - prometheus_export_service: false, - prometheus_export_service_port: 9898, - }, network_id: None, warp_sync: true, warp_barrier: None, @@ -1482,7 +1453,6 @@ mod tests { mode: Default::default(), tracing: Default::default(), compaction: Default::default(), - vm_type: Default::default(), geth_compatibility: false, experimental_rpcs: false, net_settings: Default::default(), @@ -1503,7 +1473,6 @@ mod tests { light: false, no_hardcoded_sync: false, no_persistent_txqueue: false, - whisper: Default::default(), max_round_blocks_to_import: 12, on_demand_response_time_window: None, on_demand_request_backoff_start: None, @@ -1541,23 +1510,11 @@ mod tests { #[test] fn should_parse_updater_options() { // when - let conf0 = parse(&["parity", "--release-track=testing"]); - 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"]); + let conf0 = parse(&["parity", "--auto-update", "all", "--no-consensus", "--auto-update-delay", "300"]); + let conf1 = parse(&["parity", "--auto-update=xxx"]); // then assert_eq!(conf0.update_policy().unwrap(), UpdatePolicy { - enable_downloading: true, - require_consensus: true, - filter: UpdateFilter::Critical, - 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, require_consensus: false, filter: UpdateFilter::All, @@ -1567,17 +1524,7 @@ mod tests { max_delay: 300, frequency: 20, }); - assert_eq!(conf2.update_policy().unwrap(), UpdatePolicy { - enable_downloading: false, - require_consensus: true, - filter: UpdateFilter::All, - track: ReleaseTrack::Beta, - path: default_hypervisor_path(), - max_size: 128 * 1024 * 1024, - max_delay: 300, - frequency: 100, - }); - assert!(conf3.update_policy().is_err()); + assert!(conf1.update_policy().is_err()); } #[test] @@ -1590,7 +1537,7 @@ mod tests { // then assert_eq!(conf.network_settings(), Ok(NetworkSettings { name: "testname".to_owned(), - chain: "kovan".to_owned(), + chain: "goerli".to_owned(), is_dev_chain: false, network_port: 30303, rpc_enabled: true, @@ -1647,6 +1594,28 @@ mod tests { assert_eq!(conf3.rpc_hosts(), Some(vec!["parity.io".into(), "something.io".into()])); } + #[test] + fn ensures_sane_http_settings() { + // given incorrect settings + let conf = parse(&["parity", + "--jsonrpc-server-threads=0", + "--jsonrpc-max-payload=0", + ]); + + // then things are adjusted to Just Work. + let http_conf = conf.http_config().unwrap(); + assert_eq!(http_conf.server_threads, 1); + assert_eq!(http_conf.max_payload, 1); + } + + #[test] + fn jsonrpc_threading_defaults() { + let conf = parse(&["parity"]); + let http_conf = conf.http_config().unwrap(); + assert_eq!(http_conf.server_threads, 4); + assert_eq!(http_conf.max_payload, 5); + } + #[test] fn should_parse_ipfs_hosts() { // given @@ -2031,4 +2000,19 @@ mod tests { _ => panic!("Should be Cmd::Run"), } } + + #[test] + fn should_parse_secretstore_cors() { + // given + + // when + let conf0 = parse(&["parity"]); + let conf1 = parse(&["parity", "--secretstore-http-cors", "*"]); + let conf2 = parse(&["parity", "--secretstore-http-cors", "http://parity.io,http://something.io"]); + + // then + assert_eq!(conf0.secretstore_cors(), Some(vec![])); + assert_eq!(conf1.secretstore_cors(), None); + assert_eq!(conf2.secretstore_cors(), Some(vec!["http://parity.io".into(),"http://something.io".into()])); + } } diff --git a/parity/db/mod.rs b/parity/db/mod.rs index 9b46624425..0d3858ea50 100644 --- a/parity/db/mod.rs +++ b/parity/db/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,4 @@ #[path="rocksdb/mod.rs"] mod impls; -pub use self::impls::{open_db, restoration_db_handler, migrate}; - -#[cfg(feature = "secretstore")] -pub use self::impls::open_secretstore_db; +pub use self::impls::{open_db_light, restoration_db_handler, migrate}; diff --git a/parity/db/rocksdb/blooms.rs b/parity/db/rocksdb/blooms.rs index eea913bea4..ef42fbaf14 100644 --- a/parity/db/rocksdb/blooms.rs +++ b/parity/db/rocksdb/blooms.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use std::path::Path; use ethereum_types::Bloom; -use ethcore::error::Error; +use types::errors::EthcoreError as Error; use rlp; use super::kvdb_rocksdb::DatabaseConfig; use super::open_database; @@ -37,7 +37,7 @@ pub fn migrate_blooms>(path: P, config: &DatabaseConfig) -> Resul // 3u8 -> ExtrasIndex::BlocksBlooms // 0u8 -> level 0 let blooms_iterator = db.key_value() - .iter_from_prefix(Some(3), &[3u8, 0u8]) + .iter_from_prefix(3, &[3u8, 0u8]) .filter(|(key, _)| key.len() == 6) .take_while(|(key, _)| { key[0] == 3u8 && key[1] == 0u8 @@ -63,7 +63,7 @@ pub fn migrate_blooms>(path: P, config: &DatabaseConfig) -> Resul // 1u8 -> TraceDBIndex::BloomGroups // 0u8 -> level 0 let trace_blooms_iterator = db.key_value() - .iter_from_prefix(Some(4), &[1u8, 0u8]) + .iter_from_prefix(4, &[1u8, 0u8]) .filter(|(key, _)| key.len() == 6) .take_while(|(key, _)| { key[0] == 1u8 && key[1] == 0u8 diff --git a/parity/db/rocksdb/helpers.rs b/parity/db/rocksdb/helpers.rs index 9829cb5a62..09b4fa3baf 100644 --- a/parity/db/rocksdb/helpers.rs +++ b/parity/db/rocksdb/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +use std::collections::HashMap; use std::path::Path; -use ethcore_db::NUM_COLUMNS; use ethcore::client::{ClientConfig, DatabaseCompactionProfile}; use super::kvdb_rocksdb::{CompactionProfile, DatabaseConfig}; @@ -27,10 +27,46 @@ pub fn compaction_profile(profile: &DatabaseCompactionProfile, db_path: &Path) - } } +/// Spreads the `total` (in MiB) memory budget across the db columns. +/// If it's `None`, the default memory budget will be used for each column. +/// 90% of the memory budget is assigned to the first column, `col0`, which is where we store the +/// state. +pub fn memory_per_column(total: Option) -> HashMap { + let mut memory_per_column = HashMap::new(); + if let Some(budget) = total { + // spend 90% of the memory budget on the state column, but at least 256 MiB + memory_per_column.insert(ethcore_db::COL_STATE, std::cmp::max(budget * 9 / 10, 256)); + // spread the remaining 10% evenly across columns + let rest_budget = budget / 10 / (ethcore_db::NUM_COLUMNS as usize - 1); + + for i in 1..ethcore_db::NUM_COLUMNS { + // but at least 16 MiB for each column + memory_per_column.insert(i, std::cmp::max(rest_budget, 16)); + } + } + memory_per_column +} + +/// Spreads the `total` (in MiB) memory budget across the light db columns. +pub fn memory_per_column_light(total: usize) -> HashMap { + let mut memory_per_column = HashMap::new(); + // spread the memory budget evenly across columns + // light client doesn't use the state column + let per_column = total / (ethcore_db::NUM_COLUMNS as usize - 1); + + // Note: `col0` (State) is not used for the light client so setting it to a low value. + memory_per_column.insert(0, 1); + for i in 1..ethcore_db::NUM_COLUMNS { + // but at least 4 MiB for each column + memory_per_column.insert(i, std::cmp::max(per_column, 4)); + } + memory_per_column +} + pub fn client_db_config(client_path: &Path, client_config: &ClientConfig) -> DatabaseConfig { - let mut client_db_config = DatabaseConfig::with_columns(NUM_COLUMNS); + let mut client_db_config = DatabaseConfig::with_columns(ethcore_db::NUM_COLUMNS); - client_db_config.memory_budget = client_config.db_cache_size; + client_db_config.memory_budget = memory_per_column(client_config.db_cache_size); client_db_config.compaction = compaction_profile(&client_config.db_compaction, &client_path); client_db_config diff --git a/parity/db/rocksdb/migration.rs b/parity/db/rocksdb/migration.rs index eec43d2336..66225f18f4 100644 --- a/parity/db/rocksdb/migration.rs +++ b/parity/db/rocksdb/migration.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ use std::fmt::{Display, Formatter, Error as FmtError}; use super::migration_rocksdb::{Manager as MigrationManager, Config as MigrationConfig, ChangeColumns}; use super::kvdb_rocksdb::{CompactionProfile, DatabaseConfig}; use ethcore::client::DatabaseCompactionProfile; -use ethcore; +use types::errors::EthcoreError; use super::helpers; use super::blooms::migrate_blooms; @@ -29,23 +29,31 @@ use super::blooms::migrate_blooms; /// 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), + pre_columns: 6, + post_columns: 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), + pre_columns: 7, + post_columns: 8, version: 12, }; +/// The migration from v12 to v14. +/// Adds a column for private transactions state storage. +pub const TO_V14: ChangeColumns = ChangeColumns { + pre_columns: 8, + post_columns: 9, + version: 14, +}; + /// Database is assumed to be at default version, when no version file is found. const DEFAULT_VERSION: u32 = 5; /// Current version of database models. -const CURRENT_VERSION: u32 = 13; +const CURRENT_VERSION: u32 = 14; /// A version of database at which blooms-db was introduced const BLOOMS_DB_VERSION: u32 = 13; /// Defines how many items are migrated to the new version of database at once. @@ -63,7 +71,7 @@ pub enum Error { /// Migration is not possible. MigrationImpossible, /// Blooms-db migration error. - BloomsDB(ethcore::error::Error), + BloomsDB(EthcoreError), /// Migration was completed succesfully, /// but there was a problem with io. Io(IoError), @@ -147,6 +155,7 @@ fn consolidated_database_migrations(compaction_profile: &CompactionProfile) -> R let mut manager = MigrationManager::new(default_migration_settings(compaction_profile)); manager.add_migration(TO_V11).map_err(|_| Error::MigrationImpossible)?; manager.add_migration(TO_V12).map_err(|_| Error::MigrationImpossible)?; + manager.add_migration(TO_V14).map_err(|_| Error::MigrationImpossible)?; Ok(manager) } @@ -208,22 +217,22 @@ pub fn migrate(path: &Path, compaction_profile: &DatabaseCompactionProfile) -> R // Further migrations if version < CURRENT_VERSION && exists(&db_path) { - println!("Migrating database from version {} to {}", version, CURRENT_VERSION); + info!(target: "migration", "Migrating database from version {} to {}", version, CURRENT_VERSION); migrate_database(version, &db_path, consolidated_database_migrations(&compaction_profile)?)?; if version < BLOOMS_DB_VERSION { - println!("Migrating blooms to blooms-db..."); + info!(target: "migration", "Migrating blooms to blooms-db..."); let db_config = DatabaseConfig { max_open_files: 64, - memory_budget: None, compaction: compaction_profile, columns: ethcore_db::NUM_COLUMNS, + ..Default::default() }; migrate_blooms(&db_path, &db_config).map_err(Error::BloomsDB)?; } - println!("Migration finished"); + info!(target: "migration", "Migration finished"); } // update version file. diff --git a/parity/db/rocksdb/mod.rs b/parity/db/rocksdb/mod.rs index c7aa0a5344..541194df22 100644 --- a/parity/db/rocksdb/mod.rs +++ b/parity/db/rocksdb/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,6 +18,9 @@ extern crate kvdb_rocksdb; extern crate migration_rocksdb; extern crate ethcore_blockchain; +#[cfg(test)] +extern crate tempdir; + use std::{io, fs}; use std::sync::Arc; use std::path::Path; @@ -37,13 +40,13 @@ mod helpers; pub use self::migration::migrate; struct AppDB { - key_value: Arc, + key_value: Arc, blooms: blooms_db::Database, trace_blooms: blooms_db::Database, } impl BlockChainDB for AppDB { - fn key_value(&self) -> &Arc { + fn key_value(&self) -> &Arc { &self.key_value } @@ -56,19 +59,8 @@ impl BlockChainDB for AppDB { } } -/// 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))?)) -} - /// 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 { +pub fn restoration_db_handler(client_path: &Path, client_config: &ClientConfig) -> Box { let client_db_config = helpers::client_db_config(client_path, client_config); struct RestorationDBHandler { @@ -76,7 +68,7 @@ pub fn restoration_db_handler(client_path: &Path, client_config: &ClientConfig) } impl BlockChainDBHandler for RestorationDBHandler { - fn open(&self, db_path: &Path) -> io::Result> { + fn open(&self, db_path: &Path) -> io::Result> { open_database(&db_path.to_string_lossy(), &self.config) } } @@ -86,12 +78,16 @@ pub fn restoration_db_handler(client_path: &Path, client_config: &ClientConfig) }) } -/// Open a new main DB. -pub fn open_db(client_path: &str, cache_config: &CacheConfig, compaction: &DatabaseCompactionProfile) -> io::Result> { +/// Open a new light client DB. +pub fn open_db_light( + client_path: &str, + cache_config: &CacheConfig, + compaction: &DatabaseCompactionProfile +) -> io::Result> { let path = Path::new(client_path); let db_config = DatabaseConfig { - memory_budget: Some(cache_config.blockchain() as usize * 1024 * 1024), + memory_budget: helpers::memory_per_column_light(cache_config.blockchain() as usize), compaction: helpers::compaction_profile(&compaction, path), .. DatabaseConfig::with_columns(NUM_COLUMNS) }; @@ -99,7 +95,7 @@ pub fn open_db(client_path: &str, cache_config: &CacheConfig, compaction: &Datab open_database(client_path, &db_config) } -pub fn open_database(client_path: &str, config: &DatabaseConfig) -> io::Result> { +pub fn open_database(client_path: &str, config: &DatabaseConfig) -> io::Result> { let path = Path::new(client_path); let blooms_path = path.join("blooms"); diff --git a/parity/deprecated.rs b/parity/deprecated.rs index 49155225aa..7fce526547 100644 --- a/parity/deprecated.rs +++ b/parity/deprecated.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -229,6 +229,20 @@ pub fn find_deprecated(args: &Args) -> Vec { result.push(Deprecated::Removed("--ntp-servers")); } + // Removed in 2.7. + + if args.flag_whisper { + result.push(Deprecated::Removed("--whisper")); + } + + if args.arg_whisper_pool_size.is_some() { + result.push(Deprecated::Removed("--whisper-pool-size")); + } + + if args.arg_jsonrpc_threads.is_some() { + result.push(Deprecated::Removed("--jsonrpc--threads (aka processing_threads)")); + } + result } @@ -261,6 +275,8 @@ mod tests { args.flag_dapps_apis_all = true; args.flag_fast_and_loose = true; args.arg_ntp_servers = Some(Default::default()); + args.flag_whisper = true; + args.arg_whisper_pool_size = Some(Default::default()); args }), vec![ Deprecated::DoesNothing("--warp"), @@ -282,6 +298,8 @@ mod tests { Deprecated::Replaced("--dapps-apis-all", "--jsonrpc-apis"), Deprecated::Removed("--fast-and-loose"), Deprecated::Removed("--ntp-servers"), + Deprecated::Removed("--whisper"), + Deprecated::Removed("--whisper-pool-size"), ]); } } diff --git a/parity/export_hardcoded_sync.rs b/parity/export_hardcoded_sync.rs index 0e527b3413..c55f2daf23 100644 --- a/parity/export_hardcoded_sync.rs +++ b/parity/export_hardcoded_sync.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,10 +18,10 @@ use std::sync::Arc; use std::time::Duration; use ethcore::client::DatabaseCompactionProfile; -use ethcore::spec::{SpecParams, OptimizeFor}; +use spec::SpecParams; use light::client::fetch::Unavailable as UnavailableDataFetcher; use light::Cache as LightDataCache; - +use types::engines::OptimizeFor; use params::{SpecType, Pruning}; use helpers::execute_upgrades; use dir::Directories; @@ -86,9 +86,11 @@ pub fn execute(cmd: ExportHsyncCmd) -> Result { config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024; // initialize database. - 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).map_err(|e| format!("Failed to open database {:?}", e))?; + let db = db::open_db_light( + &db_dirs.client_path(algorithm).to_str().expect("DB path could not be converted to string."), + &cmd.cache_config, + &cmd.compaction, + ).map_err(|e| format!("Failed to open database {:?}", e))?; let service = light_client::Service::start(config, &spec, UnavailableDataFetcher, db, cache) .map_err(|e| format!("Error starting light client: {}", e))?; @@ -96,7 +98,7 @@ pub fn execute(cmd: ExportHsyncCmd) -> Result { let hs = service.client().read_hardcoded_sync() .map_err(|e| format!("Error reading hardcoded sync: {}", e))?; if let Some(hs) = hs { - Ok(::serde_json::to_string_pretty(&hs.to_json()).expect("generated JSON is always valid")) + Ok(hs.to_string()) } else { Err("Error: cannot generate hardcoded sync because the database is empty.".into()) } diff --git a/parity/helpers.rs b/parity/helpers.rs index b68d854d1a..53c520ea3e 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,10 +19,11 @@ use std::io::{Write, BufReader, BufRead}; use std::time::Duration; use std::fs::File; use std::collections::HashSet; -use ethereum_types::{U256, clean_0x, Address}; +use ethereum_types::{U256, Address}; use journaldb::Algorithm; -use ethcore::client::{Mode, BlockId, VMType, DatabaseCompactionProfile, ClientConfig, VerifierType}; +use ethcore::client::{DatabaseCompactionProfile, ClientConfig}; use ethcore::miner::{PendingSet, Penalization}; +use verification::VerifierType; use miner::pool::PrioritizationStrategy; use cache::CacheConfig; use dir::DatabaseDirectories; @@ -32,11 +33,24 @@ use sync::{validate_node_url, self}; use db::migrate; use path; use ethkey::Password; +use types::{ + ids::BlockId, + client_types::Mode, +}; pub fn to_duration(s: &str) -> Result { to_seconds(s).map(Duration::from_secs) } +// TODO: should we bring it back to ethereum-types? +fn clean_0x(s: &str) -> &str { + if s.starts_with("0x") { + &s[2..] + } else { + s + } +} + fn to_seconds(s: &str) -> Result { let bad = |_| { format!("{}: Invalid duration given. See parity --help for more information.", s) @@ -117,7 +131,7 @@ pub fn to_queue_penalization(time: Option) -> Result pub fn to_address(s: Option) -> Result { match s { Some(ref a) => clean_0x(a).parse().map_err(|_| format!("Invalid address: {:?}", a)), - None => Ok(Address::default()) + None => Ok(Address::zero()) } } @@ -132,7 +146,7 @@ pub fn to_addresses(s: &Option) -> Result, String> { /// Tries to parse string as a price. pub fn to_price(s: &str) -> Result { - s.parse::().map_err(|_| format!("Invalid transaciton price 's' given. Must be a decimal number.")) + s.parse::().map_err(|_| format!("Invalid transaction price {:?} given. Must be a decimal number.", s)) } pub fn join_set(set: Option<&HashSet>) -> Option { @@ -177,7 +191,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(sync::ErrorKind::AddressResolve(_)) => Err(format!("Failed to resolve hostname of a boot node: {}", s)), + Some(sync::Error::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(), @@ -188,6 +202,7 @@ pub fn to_bootnodes(bootnodes: &Option) -> Result, String> { #[cfg(test)] pub fn default_network_config() -> ::sync::NetworkConfiguration { + use network::NatType; use sync::{NetworkConfiguration}; use super::network::IpFilter; NetworkConfiguration { @@ -197,6 +212,7 @@ pub fn default_network_config() -> ::sync::NetworkConfiguration { public_address: None, udp_port: None, nat_enabled: true, + nat_type: NatType::Any, discovery_enabled: true, boot_nodes: Vec::new(), use_secret: None, @@ -218,7 +234,6 @@ pub fn to_client_config( tracing: bool, fat_db: bool, compaction: DatabaseCompactionProfile, - vm_type: VMType, name: String, pruning: Algorithm, pruning_history: u64, @@ -254,7 +269,6 @@ pub fn to_client_config( client_config.pruning = pruning; client_config.history = pruning_history; client_config.db_compaction = compaction; - client_config.vm_type = vm_type; client_config.name = name; client_config.verifier_type = if check_seal { VerifierType::Canon } else { VerifierType::CanonNoSeal }; client_config.spec_name = spec_name; @@ -338,9 +352,12 @@ mod tests { use std::collections::HashSet; use tempdir::TempDir; use ethereum_types::U256; - use ethcore::client::{Mode, BlockId}; use ethcore::miner::PendingSet; use ethkey::Password; + use types::{ + ids::BlockId, + client_types::Mode, + }; use super::{to_duration, to_mode, to_block_id, to_u256, to_pending_set, to_address, to_addresses, to_price, geth_ipc_path, to_bootnodes, join_set, password_from_file}; #[test] diff --git a/parity/informant.rs b/parity/informant.rs index 78d055686b..cc60b5c9ab 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,22 +14,27 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -extern crate ansi_term; -use self::ansi_term::Colour::{White, Yellow, Green, Cyan, Blue}; -use self::ansi_term::{Colour, Style}; - -use std::sync::{Arc}; +use std::sync::Arc; use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; use std::time::{Instant, Duration}; +use ansi_term::Colour::{White, Yellow, Green, Cyan, Blue}; +use ansi_term::{Colour, Style}; use atty; -use ethcore::client::{ - BlockId, BlockChainClient, ChainInfo, BlockInfo, BlockChainInfo, - BlockQueueInfo, ChainNotify, NewBlocks, ClientReport, Client, ClientIoMessage +use ethcore::client::Client; +use client_traits::{BlockInfo, ChainInfo, BlockChainClient, ChainNotify}; +use types::{ + BlockNumber, + chain_notify::NewBlocks, + client_types::ClientReport, + ids::BlockId, + io_message::ClientIoMessage, + blockchain_info::BlockChainInfo, + verification::VerificationQueueInfo as BlockQueueInfo, + snapshot::RestorationStatus, }; -use types::BlockNumber; -use ethcore::snapshot::{RestorationStatus, SnapshotService as SS}; -use ethcore::snapshot::service::Service as SnapshotService; +use snapshot::SnapshotService as SS; +use snapshot::service::Service as SnapshotService; use sync::{LightSyncProvider, LightSync, SyncProvider, ManageNetwork}; use io::{TimerToken, IoContext, IoHandler}; use light::Cache as LightDataCache; @@ -41,25 +46,13 @@ use ethereum_types::H256; use parking_lot::{RwLock, Mutex}; /// Format byte counts to standard denominations. -pub fn format_bytes(b: usize) -> String { +pub fn format_bytes(b: u64) -> String { match binary_prefix(b as f64) { Standalone(bytes) => format!("{} bytes", bytes), Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix), } } -/// Something that can be converted to milliseconds. -pub trait MillisecondDuration { - /// Get the value in milliseconds. - fn as_milliseconds(&self) -> u64; -} - -impl MillisecondDuration for Duration { - fn as_milliseconds(&self) -> u64 { - self.as_secs() * 1000 + self.subsec_nanos() as u64 / 1_000_000 - } -} - #[derive(Default)] struct CacheSizes { sizes: ::std::collections::BTreeMap<&'static str, usize>, @@ -76,9 +69,8 @@ impl CacheSizes { use std::fmt::Write; let mut buf = String::new(); - for (name, &size) in &self.sizes { - - write!(buf, " {:>8} {}", paint(style, format_bytes(size)), name) + for (name, size) in &self.sizes { + write!(buf, " {:>8} {}", paint(style, format_bytes(*size as u64)), name) .expect("writing to string won't fail unless OOM; qed") } @@ -118,8 +110,8 @@ pub trait InformantData: Send + Sync { /// Informant data for a full node. pub struct FullNodeInformantData { pub client: Arc, - pub sync: Option>, - pub net: Option>, + pub sync: Option>, + pub net: Option>, } impl InformantData for FullNodeInformantData { @@ -174,7 +166,7 @@ impl InformantData for FullNodeInformantData { /// Informant data for a light node -- note that the network is required. pub struct LightNodeInformantData { - pub client: Arc, + pub client: Arc, pub sync: Arc, pub cache: Arc>, } @@ -218,7 +210,7 @@ pub struct Informant { last_tick: RwLock, with_color: bool, target: T, - snapshot: Option>, + snapshot: Option>>, rpc_stats: Option>, last_import: Mutex, skipped: AtomicUsize, @@ -231,16 +223,16 @@ impl Informant { /// Make a new instance potentially `with_color` output. pub fn new( target: T, - snapshot: Option>, + snapshot: Option>>, rpc_stats: Option>, with_color: bool, ) -> Self { Informant { last_tick: RwLock::new(Instant::now()), - with_color: with_color, - target: target, - snapshot: snapshot, - rpc_stats: rpc_stats, + with_color, + target, + snapshot, + rpc_stats, last_import: Mutex::new(Instant::now()), skipped: AtomicUsize::new(0), skipped_txs: AtomicUsize::new(0), @@ -259,7 +251,7 @@ impl Informant { let elapsed = now.duration_since(*self.last_tick.read()); let (client_report, full_report) = { - let mut last_report = self.last_report.lock(); + let last_report = self.last_report.lock(); let full_report = self.target.report(); let diffed = full_report.client_report.clone() - &*last_report; (diffed, full_report) @@ -301,13 +293,13 @@ 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!("{: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!("{:6.1}", (client_report.gas_processed / 1000).low_u64() as f64 / elapsed.as_milliseconds() as f64)) + paint(Yellow.bold(), format!("{:7.2}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_millis() as f64)), + paint(Yellow.bold(), format!("{:6.1}", (client_report.transactions_applied * 1000) as f64 / elapsed.as_millis() as f64)), + paint(Yellow.bold(), format!("{:6.1}", (client_report.gas_processed / 1000).low_u64() as f64 / elapsed.as_millis() as f64)) ) } else { format!("{} hdr/s", - paint(Yellow.bold(), format!("{:6.1}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_milliseconds() as f64)) + paint(Yellow.bold(), format!("{:6.1}", (client_report.blocks_imported * 1000) as f64 / elapsed.as_millis() as f64)) ) }, paint(Green.bold(), format!("{:5}", queue_info.unverified_queue_size)), @@ -319,9 +311,16 @@ impl Informant { 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) + RestorationStatus::Initializing { chunks_done, state_chunks, block_chunks } => { + let total_chunks = state_chunks + block_chunks; + // Note that the percentage here can be slightly misleading when + // they have chunks already on disk: we'll import the local + // chunks first and then download the rest. + format!("Snapshot initializing ({}/{} chunks restored, {:.0}%)", chunks_done, total_chunks, (chunks_done as f32 / total_chunks as f32) * 100.0) }, + RestorationStatus::Finalizing => { + format!("Snapshot finalization under way") + } _ => String::new(), } ) @@ -387,7 +386,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!("{}", new_blocks.duration.as_milliseconds())), + Colour::Purple.bold().paint(format!("{}", new_blocks.duration.as_millis())), Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)), if skipped > 0 { format!(" + another {} block(s) containing {} tx(s)", @@ -438,12 +437,16 @@ impl LightChainNotify for Informant { const INFO_TIMER: TimerToken = 0; -impl IoHandler for Informant { - fn initialize(&self, io: &IoContext) { +impl IoHandler> for Informant +where + T: InformantData, + C: client_traits::Tick + 'static, +{ + fn initialize(&self, io: &IoContext>) { io.register_timer(INFO_TIMER, Duration::from_secs(5)).expect("Error registering timer"); } - fn timeout(&self, _io: &IoContext, timer: TimerToken) { + fn timeout(&self, _io: &IoContext>, timer: TimerToken) { if timer == INFO_TIMER && !self.in_shutdown.load(AtomicOrdering::SeqCst) { self.tick(); } diff --git a/parity/ipfs.rs b/parity/ipfs.rs index 0923a1e7d4..5dbc4b615a 100644 --- a/parity/ipfs.rs +++ b/parity/ipfs.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ use std::sync::Arc; use parity_ipfs_api::{self, AccessControlAllowOrigin, Host, Listening}; use parity_ipfs_api::error::ServerError; -use ethcore::client::BlockChainClient; +use client_traits::BlockChainClient; #[derive(Debug, PartialEq, Clone)] pub struct Configuration { @@ -40,7 +40,7 @@ impl Default for Configuration { } } -pub fn start_server(conf: Configuration, client: Arc) -> Result, ServerError> { +pub fn start_server(conf: Configuration, client: Arc) -> Result, ServerError> { if !conf.enabled { return Ok(None); } diff --git a/parity/lib.rs b/parity/lib.rs index b3ca3baf7f..c3876fa9be 100644 --- a/parity/lib.rs +++ b/parity/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -41,9 +41,11 @@ extern crate toml; extern crate blooms_db; extern crate cli_signer; + +extern crate client_traits; extern crate common_types as types; +extern crate engine; extern crate ethcore; -extern crate ethcore_call_contract as call_contract; extern crate ethcore_db; extern crate ethcore_io as io; extern crate ethcore_light as light; @@ -61,17 +63,19 @@ extern crate keccak_hash as hash; extern crate kvdb; extern crate node_filter; extern crate parity_bytes as bytes; +extern crate parity_crypto; extern crate parity_hash_fetch as hash_fetch; extern crate parity_ipfs_api; extern crate parity_local_store as local_store; extern crate parity_path as path; -extern crate parity_rabbitmq; extern crate parity_rpc; extern crate parity_runtime; extern crate parity_updater as updater; extern crate parity_version; -extern crate parity_whisper; extern crate registrar; +extern crate snapshot; +extern crate spec; +extern crate verification; #[macro_use] extern crate log as rlog; @@ -82,6 +86,12 @@ extern crate ethcore_accounts as accounts; #[cfg(feature = "secretstore")] extern crate ethcore_secretstore; +#[cfg(feature = "secretstore")] +extern crate ethabi; + +#[cfg(feature = "secretstore")] +extern crate ethcore_call_contract as call_contract; + #[cfg(test)] #[macro_use] extern crate pretty_assertions; @@ -89,10 +99,6 @@ extern crate pretty_assertions; #[cfg(test)] extern crate tempdir; -#[cfg(test)] -#[macro_use] -extern crate lazy_static; - mod account; mod account_utils; mod blockchain; @@ -113,10 +119,9 @@ mod rpc_apis; mod run; mod secretstore; mod signer; -mod snapshot; +mod snapshot_cmd; mod upgrade; mod user_defaults; -mod whisper; mod db; use std::fs::File; @@ -194,9 +199,12 @@ pub enum ExecutionAction { fn execute( command: Execute, logger: Arc, - on_client_rq: Cr, on_updater_rq: Rr) -> Result - where Cr: Fn(String) + 'static + Send, - Rr: Fn() + 'static + Send + on_client_rq: Cr, + on_updater_rq: Rr +) -> Result + where + Cr: Fn(String) + 'static + Send, + Rr: Fn() + 'static + Send { #[cfg(feature = "deadlock_detection")] run_deadlock_detection_thread(); @@ -215,7 +223,7 @@ fn execute( Cmd::SignerSign { id, pwfile, port, authfile } => cli_signer::signer_sign(id, pwfile, port, authfile).map(|s| ExecutionAction::Instant(Some(s))), Cmd::SignerList { port, authfile } => cli_signer::signer_list(port, authfile).map(|s| ExecutionAction::Instant(Some(s))), Cmd::SignerReject { id, port, authfile } => cli_signer::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::Snapshot(snapshot_cmd) => snapshot_cmd::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))), } } diff --git a/parity/light_helpers/epoch_fetch.rs b/parity/light_helpers/epoch_fetch.rs index 9c7fd6a8ee..89a68d2df3 100644 --- a/parity/light_helpers/epoch_fetch.rs +++ b/parity/light_helpers/epoch_fetch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,12 +16,13 @@ use std::sync::{Arc, Weak}; -use ethcore::engines::{EthEngine, StateDependentProof}; -use ethcore::machine::EthereumMachine; +use engine::{Engine, StateDependentProof}; use sync::{LightSync, LightNetworkDispatcher}; -use types::encoded; -use types::header::Header; -use types::receipt::Receipt; +use types::{ + header::Header, + encoded, + receipt::Receipt, +}; use futures::{future, Future}; use futures::future::Either; @@ -34,7 +35,7 @@ use ethereum_types::H256; const ALL_VALID_BACKREFS: &str = "no back-references, therefore all back-references valid; qed"; -type BoxFuture = Box>; +type BoxFuture = Box>; /// Allows on-demand fetch of data useful for the light client. pub struct EpochFetch { @@ -82,7 +83,7 @@ impl ChainDataFetcher for EpochFetch { } /// Fetch epoch transition proof at given header. - fn epoch_transition(&self, hash: H256, engine: Arc, checker: Arc>) + fn epoch_transition(&self, hash: H256, engine: Arc, checker: Arc) -> Self::Transition { self.request(request::Signal { diff --git a/parity/light_helpers/mod.rs b/parity/light_helpers/mod.rs index 843dd419d4..656e969af5 100644 --- a/parity/light_helpers/mod.rs +++ b/parity/light_helpers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/parity/logger/Cargo.toml b/parity/logger/Cargo.toml index 217bcf48a6..62fc3f3d5f 100644 --- a/parity/logger/Cargo.toml +++ b/parity/logger/Cargo.toml @@ -12,6 +12,6 @@ atty = "0.2" lazy_static = "1.0" regex = "1.0" time = "0.1" -parking_lot = "0.7" +parking_lot = "0.9" arrayvec = "0.4" -ansi_term = "0.10" +ansi_term = "0.11" diff --git a/parity/logger/src/lib.rs b/parity/logger/src/lib.rs index a2e3de176a..d8afc347ab 100644 --- a/parity/logger/src/lib.rs +++ b/parity/logger/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/parity/logger/src/rotating.rs b/parity/logger/src/rotating.rs index 2745e95bf6..52854b3e16 100644 --- a/parity/logger/src/rotating.rs +++ b/parity/logger/src/rotating.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/parity/main.rs b/parity/main.rs index 066061cbaf..0a6af8a076 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -114,7 +114,7 @@ fn take_spec_name_override() -> Option { #[cfg(windows)] fn global_cleanup() { - // We need to cleanup all sockets before spawning another Parity process. This makes sure everything is cleaned up. + // We need to clean up 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 { @@ -285,6 +285,7 @@ fn main_direct(force_can_restart: bool) -> i32 { let e = exit.clone(); let exiting = exiting.clone(); move |panic_msg| { + warn!("Panic occurred, see stderr for details"); eprintln!("{}", panic_msg); if !exiting.swap(true, Ordering::SeqCst) { *e.0.lock() = ExitStatus { diff --git a/parity/modules.rs b/parity/modules.rs index 9f5d25a11e..264f79202d 100644 --- a/parity/modules.rs +++ b/parity/modules.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,48 +16,51 @@ use std::sync::{Arc, mpsc}; -use ethcore::client::BlockChainClient; -use sync::{self, AttachedProtocol, SyncConfig, NetworkConfiguration, Params, ConnectionFilter}; -use ethcore::snapshot::SnapshotService; +use client_traits::{BlockChainClient, ChainNotify}; +use sync::{self, SyncConfig, NetworkConfiguration, Params, ConnectionFilter}; +use snapshot::SnapshotService; +use ethcore_private_tx::PrivateStateDB; use light::Provider; +use parity_runtime::Executor; pub use sync::{EthSync, SyncProvider, ManageNetwork, PrivateTxHandler}; -pub use ethcore::client::ChainNotify; use ethcore_logger::Config as LogConfig; pub type SyncModules = ( - Arc, - Arc, - Arc, + Arc, + Arc, + Arc, mpsc::Sender, ); pub fn sync( config: SyncConfig, + executor: Executor, network_config: NetworkConfiguration, - chain: Arc, - snapshot_service: Arc, - private_tx_handler: Option>, - provider: Arc, + chain: Arc, + snapshot_service: Arc, + private_tx_handler: Option>, + private_state: Option>, + provider: Arc, _log_settings: &LogConfig, - attached_protos: Vec, - connection_filter: Option>, + connection_filter: Option>, ) -> Result { let eth_sync = EthSync::new(Params { config, + executor, chain, provider, snapshot_service, private_tx_handler, + private_state, network_config, - attached_protos, }, connection_filter)?; Ok(( - eth_sync.clone() as Arc, - eth_sync.clone() as Arc, - eth_sync.clone() as Arc, + eth_sync.clone() as Arc, + eth_sync.clone() as Arc, + eth_sync.clone() as Arc, eth_sync.priority_tasks() )) } diff --git a/parity/params.rs b/parity/params.rs index f17cae2c57..4526def3b5 100644 --- a/parity/params.rs +++ b/parity/params.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,11 +17,8 @@ use std::collections::HashSet; use std::time::Duration; use std::{str, fs, fmt}; -use std::num::NonZeroU32; -use ethcore::client::Mode; -use ethcore::ethereum; -use ethcore::spec::{Spec, SpecParams}; +use spec::{Spec, SpecParams, self}; use ethereum_types::{U256, Address}; use parity_runtime::Executor; use hash_fetch::fetch::Client as FetchClient; @@ -30,6 +27,9 @@ use miner::gas_pricer::GasPricer; use miner::gas_price_calibrator::{GasPriceCalibratorOptions, GasPriceCalibrator}; use parity_version::version_data; use user_defaults::UserDefaults; +use types::client_types::Mode; + +use crate::configuration; #[derive(Debug, PartialEq)] pub enum SpecType { @@ -39,18 +39,21 @@ pub enum SpecType { Xdai, Volta, Ewc, - Expanse, Musicoin, Ellaism, Mix, Callisto, + EtherCore, Morden, + Mordor, Ropsten, Kovan, Rinkeby, Goerli, Kotti, Sokol, + Evantestcore, + Evancore, Dev, Custom(String), } @@ -66,24 +69,27 @@ impl str::FromStr for SpecType { fn from_str(s: &str) -> Result { let spec = match s { - "ethereum" | "frontier" | "homestead" | "byzantium" | "foundation" | "mainnet" => SpecType::Foundation, - "classic" | "frontier-dogmatic" | "homestead-dogmatic" => SpecType::Classic, + "eth" | "ethereum" | "foundation" | "mainnet" => SpecType::Foundation, + "etc" | "classic" => SpecType::Classic, "poanet" | "poacore" => SpecType::Poanet, "xdai" => SpecType::Xdai, "volta" => SpecType::Volta, "ewc" | "energyweb" => SpecType::Ewc, - "expanse" => SpecType::Expanse, "musicoin" => SpecType::Musicoin, "ellaism" => SpecType::Ellaism, "mix" => SpecType::Mix, "callisto" => SpecType::Callisto, - "morden" | "classic-testnet" => SpecType::Morden, + "ethercore" => SpecType::EtherCore, + "morden" => SpecType::Morden, + "mordor" | "classic-testnet" => SpecType::Mordor, "ropsten" => SpecType::Ropsten, - "kovan" | "testnet" => SpecType::Kovan, + "kovan" => SpecType::Kovan, "rinkeby" => SpecType::Rinkeby, - "goerli" | "görli" => SpecType::Goerli, + "goerli" | "görli" | "testnet" => SpecType::Goerli, "kotti" => SpecType::Kotti, "sokol" | "poasokol" => SpecType::Sokol, + "evantestcore" => SpecType::Evantestcore, + "evancore" => SpecType::Evancore, "dev" => SpecType::Dev, other => SpecType::Custom(other.into()), }; @@ -100,18 +106,21 @@ impl fmt::Display for SpecType { SpecType::Xdai => "xdai", SpecType::Volta => "volta", SpecType::Ewc => "energyweb", - SpecType::Expanse => "expanse", SpecType::Musicoin => "musicoin", SpecType::Ellaism => "ellaism", SpecType::Mix => "mix", SpecType::Callisto => "callisto", + SpecType::EtherCore => "ethercore", SpecType::Morden => "morden", + SpecType::Mordor => "mordor", SpecType::Ropsten => "ropsten", SpecType::Kovan => "kovan", SpecType::Rinkeby => "rinkeby", SpecType::Goerli => "goerli", SpecType::Kotti => "kotti", SpecType::Sokol => "sokol", + SpecType::Evantestcore => "evantestcore", + SpecType::Evancore => "evancore", SpecType::Dev => "dev", SpecType::Custom(ref custom) => custom, }) @@ -122,28 +131,31 @@ impl SpecType { pub fn spec<'a, T: Into>>(&self, params: T) -> Result { let params = params.into(); match *self { - SpecType::Foundation => Ok(ethereum::new_foundation(params)), - SpecType::Classic => Ok(ethereum::new_classic(params)), - SpecType::Poanet => Ok(ethereum::new_poanet(params)), - SpecType::Xdai => Ok(ethereum::new_xdai(params)), - SpecType::Volta => Ok(ethereum::new_volta(params)), - SpecType::Ewc => Ok(ethereum::new_ewc(params)), - SpecType::Expanse => Ok(ethereum::new_expanse(params)), - SpecType::Musicoin => Ok(ethereum::new_musicoin(params)), - SpecType::Ellaism => Ok(ethereum::new_ellaism(params)), - SpecType::Mix => Ok(ethereum::new_mix(params)), - SpecType::Callisto => Ok(ethereum::new_callisto(params)), - SpecType::Morden => Ok(ethereum::new_morden(params)), - SpecType::Ropsten => Ok(ethereum::new_ropsten(params)), - SpecType::Kovan => Ok(ethereum::new_kovan(params)), - SpecType::Rinkeby => Ok(ethereum::new_rinkeby(params)), - SpecType::Goerli => Ok(ethereum::new_goerli(params)), - SpecType::Kotti => Ok(ethereum::new_kotti(params)), - SpecType::Sokol => Ok(ethereum::new_sokol(params)), - SpecType::Dev => Ok(Spec::new_instant()), + SpecType::Foundation => Ok(spec::new_foundation(params)), + SpecType::Classic => Ok(spec::new_classic(params)), + SpecType::Poanet => Ok(spec::new_poanet(params)), + SpecType::Xdai => Ok(spec::new_xdai(params)), + SpecType::Volta => Ok(spec::new_volta(params)), + SpecType::Ewc => Ok(spec::new_ewc(params)), + SpecType::Musicoin => Ok(spec::new_musicoin(params)), + SpecType::Ellaism => Ok(spec::new_ellaism(params)), + SpecType::Mix => Ok(spec::new_mix(params)), + SpecType::Callisto => Ok(spec::new_callisto(params)), + SpecType::EtherCore => Ok(spec::new_ethercore(params)), + SpecType::Morden => Ok(spec::new_morden(params)), + SpecType::Mordor => Ok(spec::new_mordor(params)), + SpecType::Ropsten => Ok(spec::new_ropsten(params)), + SpecType::Kovan => Ok(spec::new_kovan(params)), + SpecType::Rinkeby => Ok(spec::new_rinkeby(params)), + SpecType::Goerli => Ok(spec::new_goerli(params)), + SpecType::Kotti => Ok(spec::new_kotti(params)), + SpecType::Sokol => Ok(spec::new_sokol(params)), + SpecType::Evantestcore => Ok(spec::new_evantestcore(params)), + SpecType::Evancore => Ok(spec::new_evancore(params)), + SpecType::Dev => Ok(spec::new_instant()), SpecType::Custom(ref filename) => { let file = fs::File::open(filename).map_err(|e| format!("Could not load specification file at {}: {}", filename, e))?; - Spec::load(params, file) + Spec::load(params, file).map_err(|e| e.to_string()) } } } @@ -151,7 +163,6 @@ impl SpecType { pub fn legacy_fork_name(&self) -> Option { match *self { SpecType::Classic => Some("classic".to_owned()), - SpecType::Expanse => Some("expanse".to_owned()), SpecType::Musicoin => Some("musicoin".to_owned()), _ => None, } @@ -228,24 +239,22 @@ impl str::FromStr for ResealPolicy { #[derive(Debug, PartialEq)] pub struct AccountsConfig { - pub iterations: NonZeroU32, + pub iterations: u32, pub refresh_time: u64, pub testnet: bool, pub password_files: Vec, pub unlocked_accounts: Vec
, - pub enable_hardware_wallets: bool, pub enable_fast_unlock: bool, } impl Default for AccountsConfig { fn default() -> Self { AccountsConfig { - iterations: NonZeroU32::new(10240).expect("10240 > 0; qed"), + iterations: 10240, refresh_time: 5, testnet: false, password_files: Vec::new(), unlocked_accounts: Vec::new(), - enable_hardware_wallets: true, enable_fast_unlock: false, } } @@ -257,6 +266,7 @@ pub enum GasPricerConfig { Calibrated { usd_per_tx: f32, recalibration_period: Duration, + api_endpoint: String } } @@ -265,6 +275,7 @@ impl Default for GasPricerConfig { GasPricerConfig::Calibrated { usd_per_tx: 0.0001f32, recalibration_period: Duration::from_secs(3600), + api_endpoint: configuration::ETHERSCAN_ETH_PRICE_ENDPOINT.to_string(), } } } @@ -273,7 +284,7 @@ impl GasPricerConfig { pub fn to_gas_pricer(&self, fetch: FetchClient, p: Executor) -> GasPricer { match *self { GasPricerConfig::Fixed(u) => GasPricer::Fixed(u), - GasPricerConfig::Calibrated { usd_per_tx, recalibration_period, .. } => { + GasPricerConfig::Calibrated { usd_per_tx, recalibration_period, ref api_endpoint } => { GasPricer::new_calibrated( GasPriceCalibrator::new( GasPriceCalibratorOptions { @@ -282,6 +293,7 @@ impl GasPricerConfig { }, fetch, p, + api_endpoint.clone(), ) ) } @@ -373,37 +385,37 @@ mod tests { #[test] fn test_spec_type_parsing() { + assert_eq!(SpecType::Foundation, "eth".parse().unwrap()); + assert_eq!(SpecType::Foundation, "ethereum".parse().unwrap()); assert_eq!(SpecType::Foundation, "foundation".parse().unwrap()); - assert_eq!(SpecType::Foundation, "frontier".parse().unwrap()); - assert_eq!(SpecType::Foundation, "homestead".parse().unwrap()); - assert_eq!(SpecType::Foundation, "byzantium".parse().unwrap()); assert_eq!(SpecType::Foundation, "mainnet".parse().unwrap()); - assert_eq!(SpecType::Foundation, "ethereum".parse().unwrap()); + assert_eq!(SpecType::Classic, "etc".parse().unwrap()); assert_eq!(SpecType::Classic, "classic".parse().unwrap()); - assert_eq!(SpecType::Classic, "frontier-dogmatic".parse().unwrap()); - assert_eq!(SpecType::Classic, "homestead-dogmatic".parse().unwrap()); assert_eq!(SpecType::Poanet, "poanet".parse().unwrap()); assert_eq!(SpecType::Poanet, "poacore".parse().unwrap()); assert_eq!(SpecType::Xdai, "xdai".parse().unwrap()); assert_eq!(SpecType::Volta, "volta".parse().unwrap()); assert_eq!(SpecType::Ewc, "ewc".parse().unwrap()); assert_eq!(SpecType::Ewc, "energyweb".parse().unwrap()); - assert_eq!(SpecType::Expanse, "expanse".parse().unwrap()); assert_eq!(SpecType::Musicoin, "musicoin".parse().unwrap()); assert_eq!(SpecType::Ellaism, "ellaism".parse().unwrap()); assert_eq!(SpecType::Mix, "mix".parse().unwrap()); assert_eq!(SpecType::Callisto, "callisto".parse().unwrap()); + assert_eq!(SpecType::EtherCore, "ethercore".parse().unwrap()); assert_eq!(SpecType::Morden, "morden".parse().unwrap()); - assert_eq!(SpecType::Morden, "classic-testnet".parse().unwrap()); + assert_eq!(SpecType::Mordor, "mordor".parse().unwrap()); + assert_eq!(SpecType::Mordor, "classic-testnet".parse().unwrap()); assert_eq!(SpecType::Ropsten, "ropsten".parse().unwrap()); assert_eq!(SpecType::Kovan, "kovan".parse().unwrap()); - assert_eq!(SpecType::Kovan, "testnet".parse().unwrap()); assert_eq!(SpecType::Rinkeby, "rinkeby".parse().unwrap()); assert_eq!(SpecType::Goerli, "goerli".parse().unwrap()); assert_eq!(SpecType::Goerli, "görli".parse().unwrap()); + assert_eq!(SpecType::Goerli, "testnet".parse().unwrap()); assert_eq!(SpecType::Kotti, "kotti".parse().unwrap()); assert_eq!(SpecType::Sokol, "sokol".parse().unwrap()); assert_eq!(SpecType::Sokol, "poasokol".parse().unwrap()); + assert_eq!(SpecType::Evantestcore, "evantestcore".parse().unwrap()); + assert_eq!(SpecType::Evancore, "evancore".parse().unwrap()); } #[test] @@ -419,18 +431,21 @@ mod tests { assert_eq!(format!("{}", SpecType::Xdai), "xdai"); assert_eq!(format!("{}", SpecType::Volta), "volta"); assert_eq!(format!("{}", SpecType::Ewc), "energyweb"); - assert_eq!(format!("{}", SpecType::Expanse), "expanse"); assert_eq!(format!("{}", SpecType::Musicoin), "musicoin"); assert_eq!(format!("{}", SpecType::Ellaism), "ellaism"); assert_eq!(format!("{}", SpecType::Mix), "mix"); assert_eq!(format!("{}", SpecType::Callisto), "callisto"); + assert_eq!(format!("{}", SpecType::EtherCore), "ethercore"); assert_eq!(format!("{}", SpecType::Morden), "morden"); + assert_eq!(format!("{}", SpecType::Mordor), "mordor"); assert_eq!(format!("{}", SpecType::Ropsten), "ropsten"); assert_eq!(format!("{}", SpecType::Kovan), "kovan"); assert_eq!(format!("{}", SpecType::Rinkeby), "rinkeby"); assert_eq!(format!("{}", SpecType::Goerli), "goerli"); assert_eq!(format!("{}", SpecType::Kotti), "kotti"); assert_eq!(format!("{}", SpecType::Sokol), "sokol"); + assert_eq!(format!("{}", SpecType::Evantestcore), "evantestcore"); + assert_eq!(format!("{}", SpecType::Evancore), "evancore"); assert_eq!(format!("{}", SpecType::Dev), "dev"); assert_eq!(format!("{}", SpecType::Custom("foo/bar".into())), "foo/bar"); } diff --git a/parity/presale.rs b/parity/presale.rs index 162d149b54..ed9feac0a4 100644 --- a/parity/presale.rs +++ b/parity/presale.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,11 +19,10 @@ use ethkey::Password; use ethstore::PresaleWallet; use helpers::{password_prompt, password_from_file}; use params::SpecType; -use std::num::NonZeroU32; #[derive(Debug, PartialEq)] pub struct ImportWallet { - pub iterations: NonZeroU32, + pub iterations: u32, pub path: String, pub spec: SpecType, pub wallet_path: String, @@ -44,7 +43,7 @@ pub fn execute(cmd: ImportWallet) -> Result { } #[cfg(feature = "accounts")] -pub fn import_account(cmd: &ImportWallet, kp: ethkey::KeyPair, password: Password) { +pub fn import_account(cmd: &ImportWallet, kp: parity_crypto::publickey::KeyPair, password: Password) { use accounts::{AccountProvider, AccountProviderSettings}; use ethstore::EthStore; use ethstore::accounts_dir::RootDiskDirectory; @@ -56,4 +55,4 @@ pub fn import_account(cmd: &ImportWallet, kp: ethkey::KeyPair, password: Passwor } #[cfg(not(feature = "accounts"))] -pub fn import_account(_cmd: &ImportWallet, _kp: ethkey::KeyPair, _password: Password) {} +pub fn import_account(_cmd: &ImportWallet, _kp: parity_crypto::publickey::KeyPair, _password: Password) {} diff --git a/parity/rpc.rs b/parity/rpc.rs index b07ca3f3e4..6f76f8f50b 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -29,21 +29,30 @@ use parity_rpc::{self as rpc, Metadata, DomainsValidation}; use rpc_apis::{self, ApiSet}; pub use parity_rpc::{IpcServer, HttpServer, RequestMiddleware}; -pub use parity_rpc::ws::Server as WsServer; +pub use parity_rpc::ws::{Server as WsServer, ws}; pub const DAPPS_DOMAIN: &'static str = "web3.site"; #[derive(Debug, Clone, PartialEq)] pub struct HttpConfiguration { + /// Is RPC over HTTP enabled (default is true)? pub enabled: bool, + /// The IP of the network interface used (default is 127.0.0.1). pub interface: String, + /// The network port (default is 8545). pub port: u16, + /// The categories of RPC calls enabled. pub apis: ApiSet, + /// CORS headers pub cors: Option>, + /// Specify a list of valid hosts we accept requests from. pub hosts: Option>, + /// Number of HTTP server threads to use to handle incoming requests (default is 4). pub server_threads: usize, - pub processing_threads: usize, + /// Sets the maximum size of a request body in megabytes (default is 5 MiB). pub max_payload: usize, + /// Use keepalive messages on the underlying socket: SO_KEEPALIVE as well as the TCP_KEEPALIVE + /// or TCP_KEEPIDLE options depending on your platform (default is true). pub keep_alive: bool, } @@ -56,8 +65,7 @@ impl Default for HttpConfiguration { apis: ApiSet::UnsafeContext, cors: Some(vec![]), hosts: Some(vec![]), - server_threads: 1, - processing_threads: 4, + server_threads: 4, max_payload: 5, keep_alive: true, } @@ -68,6 +76,7 @@ impl Default for HttpConfiguration { pub struct IpcConfiguration { pub enabled: bool, pub socket_addr: String, + pub chmod: String, pub apis: ApiSet, } @@ -81,6 +90,7 @@ impl Default for IpcConfiguration { let data_dir = ::dir::default_data_path(); parity_ipc_path(&data_dir, "$BASE/jsonrpc.ipc", 0) }, + chmod: "660".into(), apis: ApiSet::IpcContext, } } @@ -111,7 +121,7 @@ impl Default for WsConfiguration { 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, + support_token_api: false, } } } @@ -187,7 +197,9 @@ pub fn new_ws( match start_result { Ok(server) => Ok(Some(server)), - Err(rpc::ws::Error(rpc::ws::ErrorKind::Io(ref err), _)) if err.kind() == io::ErrorKind::AddrInUse => Err( + Err(rpc::ws::Error::WsError(ws::Error { + kind: ws::ErrorKind::Io(ref err), .. + })) if err.kind() == io::ErrorKind::AddrInUse => Err( format!("WebSockets address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --ws-port and --ws-interface options.", url) ), Err(e) => Err(format!("WebSockets error: {:?}", e)), @@ -251,7 +263,16 @@ pub fn new_ipc( } } - match rpc::start_ipc(&conf.socket_addr, handler, rpc::RpcExtractor) { + // some validations .. + let chmod = conf.chmod; + let chmod = u16::from_str_radix(&chmod, 8) + .map_err(|e| format!("Invalid octal value: {}", e))?; + + if chmod == 0 || chmod > 0o7777 { + return Err("Valid octal permissions are within the range 1 to 7777".into()) + } + + match rpc::start_ipc(&conf.socket_addr, handler, rpc::RpcExtractor, chmod) { Ok(server) => Ok(Some(server)), Err(io_error) => Err(format!("IPC error: {}", io_error)), } diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 668206174b..025173049d 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,7 +24,9 @@ pub use parity_rpc::signer::SignerService; use account_utils::{self, AccountProvider}; use ethcore::client::Client; use ethcore::miner::Miner; -use ethcore::snapshot::SnapshotService; +use snapshot::SnapshotService; +use client_traits::BlockChainClient; +use sync::SyncState; use ethcore_logger::RotatingLogger; use ethcore_private_tx::Provider as PrivateTransactionManager; use ethcore_service::PrivateTxService; @@ -36,6 +38,7 @@ use miner::external::ExternalMiner; use parity_rpc::dispatch::{FullDispatcher, LightDispatcher}; use parity_rpc::informant::{ActivityNotifier, ClientNotifier}; use parity_rpc::{Host, Metadata, NetworkSettings}; +use parity_rpc::v1::traits::TransactionsPool; use parity_runtime::Executor; use parking_lot::{Mutex, RwLock}; use sync::{LightSync, ManageNetwork, SyncProvider}; @@ -63,12 +66,6 @@ pub enum Api { Rpc, /// 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. - Whisper, - /// Whisper Pub-Sub (Safe but same concerns as above). - WhisperPubSub, /// Parity PubSub - Generic Publish-Subscriber (Safety depends on other APIs exposed). ParityPubSub, /// Parity Accounts extensions (UNSAFE: Passwords, Side Effects (new account)) @@ -80,6 +77,10 @@ pub enum Api { /// Geth-compatible (best-effort) debug API (Potentially UNSAFE) /// NOTE We don't aim to support all methods, only the ones that are useful. Debug, + /// Parity Transactions pool PubSub + ParityTransactionsPool, + /// Deprecated api + Deprecated, } impl FromStr for Api { @@ -101,11 +102,11 @@ impl FromStr for Api { "pubsub" => Ok(EthPubSub), "rpc" => Ok(Rpc), "secretstore" => Ok(SecretStore), - "shh" => Ok(Whisper), - "shh_pubsub" => Ok(WhisperPubSub), "signer" => Ok(Signer), "traces" => Ok(Traces), "web3" => Ok(Web3), + "parity_transactions_pool" => Ok(ParityTransactionsPool), + "shh" | "shh_pubsub" => Ok(Deprecated), api => Err(format!("Unknown api: {}", api)), } } @@ -187,8 +188,10 @@ fn to_modules(apis: &HashSet) -> BTreeMap { Api::Signer => ("signer", "1.0"), Api::Traces => ("traces", "1.0"), Api::Web3 => ("web3", "1.0"), - Api::Whisper => ("shh", "1.0"), - Api::WhisperPubSub => ("shh_pubsub", "1.0"), + Api::ParityTransactionsPool => ("parity_transactions_pool", "1.0"), + Api::Deprecated => { + continue; + } }; modules.insert(name.into(), version.into()); } @@ -232,23 +235,22 @@ pub trait Dependencies { pub struct FullDependencies { pub signer_service: Arc, pub client: Arc, - pub snapshot: Arc, - pub sync: Arc, - pub net: Arc, + pub snapshot: Arc, + pub sync: Arc, + pub net: Arc, pub accounts: Arc, pub private_tx_service: Option>, pub miner: Arc, pub external_miner: Arc, pub logger: Arc, pub settings: Arc, - pub net_service: Arc, + pub net_service: Arc, pub updater: Arc, pub geth_compatibility: bool, pub experimental_rpcs: bool, pub ws_address: Option, pub fetch: FetchClient, pub executor: Executor, - pub whisper_rpc: Option<::whisper::RpcFactory>, pub gas_price_percentile: usize, pub poll_lifetime: u32, pub allow_missing_blocks: bool, @@ -322,15 +324,22 @@ impl FullDependencies { } Api::EthPubSub => { if !for_generic_pubsub { - let client = - EthPubSubClient::new(self.client.clone(), self.executor.clone()); - let h = client.handler(); - self.miner - .add_transactions_listener(Box::new(move |hashes| { - if let Some(h) = h.upgrade() { - h.notify_new_transactions(hashes); - } - })); + let pool_receiver = self.miner.pending_transactions_receiver(); + let mut client = + EthPubSubClient::new(self.client.clone(), self.executor.clone(), pool_receiver); + let weak_client = Arc::downgrade(&self.client); + + client.add_sync_notifier(self.sync.sync_notification(), move |state| { + let client = weak_client.upgrade()?; + let queue_info = client.queue_info(); + + let is_syncing_state = match state { SyncState::Idle | SyncState::NewBlocks => false, _ => true }; + let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3; + + Some(PubSubSyncStatus { + syncing: is_verifying || is_syncing_state, + }) + }); if let Some(h) = client.handler().upgrade() { self.client.add_notify(h); @@ -338,6 +347,13 @@ impl FullDependencies { handler.extend_with(client.to_delegate()); } } + Api::ParityTransactionsPool => { + if !for_generic_pubsub { + let receiver = self.miner.full_transactions_receiver(); + let client = TransactionsPoolClient::new(self.executor.clone(), receiver); + handler.extend_with(TransactionsPoolClient::to_delegate(client)); + } + } Api::Personal => { #[cfg(feature = "accounts")] handler.extend_with( @@ -428,28 +444,13 @@ impl FullDependencies { #[cfg(feature = "accounts")] handler.extend_with(SecretStoreClient::new(&self.accounts).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 !for_generic_pubsub { - 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 => { handler.extend_with( PrivateClient::new(self.private_tx_service.as_ref().map(|p| p.provider())) .to_delegate(), ); } + Api::Deprecated => {}, } } } @@ -484,7 +485,7 @@ pub struct LightDependencies { pub signer_service: Arc, pub client: Arc, pub sync: Arc, - pub net: Arc, + pub net: Arc, pub accounts: Arc, pub logger: Arc, pub settings: Arc, @@ -496,7 +497,6 @@ pub struct LightDependencies { pub geth_compatibility: bool, pub experimental_rpcs: bool, pub executor: Executor, - pub whisper_rpc: Option<::whisper::RpcFactory>, pub private_tx_service: Option>, pub gas_price_percentile: usize, pub poll_lifetime: u32, @@ -555,25 +555,42 @@ impl LightDependencies { } } Api::EthPubSub => { - let client = EthPubSubClient::light( + let receiver = self.transaction_queue.write().pending_transactions_receiver(); + + let mut client = EthPubSubClient::light( self.client.clone(), self.on_demand.clone(), self.sync.clone(), self.cache.clone(), self.executor.clone(), self.gas_price_percentile, + receiver ); + + let weak_client = Arc::downgrade(&self.client); + + client.add_sync_notifier(self.sync.sync_notification(), move |state| { + let client = weak_client.upgrade()?; + let queue_info = client.queue_info(); + + let is_syncing_state = match state { SyncState::Idle | SyncState::NewBlocks => false, _ => true }; + let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3; + + Some(PubSubSyncStatus { + syncing: is_verifying || is_syncing_state, + }) + }); + self.client.add_listener(client.handler() as Weak<_>); - let h = client.handler(); - self.transaction_queue - .write() - .add_listener(Box::new(move |transactions| { - if let Some(h) = h.upgrade() { - h.notify_new_transactions(transactions); - } - })); handler.extend_with(EthPubSub::to_delegate(client)); } + Api::ParityTransactionsPool => { + if !for_generic_pubsub { + let receiver = self.transaction_queue.write().full_transactions_receiver(); + let client = TransactionsPoolClient::new(self.executor.clone(), receiver); + handler.extend_with(TransactionsPoolClient::to_delegate(client)); + } + } Api::Personal => { #[cfg(feature = "accounts")] handler.extend_with( @@ -648,26 +665,13 @@ impl LightDependencies { #[cfg(feature = "accounts")] handler.extend_with(SecretStoreClient::new(&self.accounts).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()); } } + Api::Deprecated => {}, } } } @@ -702,25 +706,28 @@ impl ApiSet { Api::EthPubSub, Api::Parity, Api::Rpc, - Api::Whisper, - Api::WhisperPubSub, Api::Private, ] - .into_iter() + .iter() .cloned() .collect(); match *self { - ApiSet::List(ref apis) => apis.clone(), + ApiSet::List(ref apis) => apis.into_iter() + .filter(|api| *api != &Api::Deprecated) + .cloned() + .collect(), ApiSet::UnsafeContext => { public_list.insert(Api::Traces); public_list.insert(Api::ParityPubSub); + public_list.insert(Api::ParityTransactionsPool); public_list } ApiSet::IpcContext => { public_list.insert(Api::Traces); public_list.insert(Api::ParityPubSub); public_list.insert(Api::ParityAccounts); + public_list.insert(Api::ParityTransactionsPool); public_list } ApiSet::All => { @@ -732,6 +739,7 @@ impl ApiSet { public_list.insert(Api::Signer); public_list.insert(Api::Personal); public_list.insert(Api::SecretStore); + public_list.insert(Api::ParityTransactionsPool); public_list } ApiSet::PubSub => [ @@ -740,8 +748,9 @@ impl ApiSet { Api::ParityAccounts, Api::ParitySet, Api::Traces, + Api::ParityTransactionsPool, ] - .into_iter() + .iter() .cloned() .collect(), } @@ -768,8 +777,7 @@ mod test { 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_eq!(Api::ParityTransactionsPool, "parity_transactions_pool".parse().unwrap()); assert!("rp".parse::().is_err()); } @@ -798,9 +806,8 @@ mod test { Api::ParityPubSub, Api::Traces, Api::Rpc, - Api::Whisper, - Api::WhisperPubSub, Api::Private, + Api::ParityTransactionsPool, ].into_iter() .collect(); assert_eq!(ApiSet::UnsafeContext.list_apis(), expected); @@ -818,9 +825,8 @@ mod test { Api::ParityPubSub, Api::Traces, Api::Rpc, - Api::Whisper, - Api::WhisperPubSub, Api::Private, + Api::ParityTransactionsPool, // semi-safe Api::ParityAccounts, ].into_iter() @@ -843,14 +849,13 @@ mod test { Api::Traces, Api::Rpc, Api::SecretStore, - Api::Whisper, - Api::WhisperPubSub, Api::ParityAccounts, Api::ParitySet, Api::Signer, Api::Personal, Api::Private, Api::Debug, + Api::ParityTransactionsPool, ].into_iter() .collect() ) @@ -872,13 +877,12 @@ mod test { Api::Traces, Api::Rpc, Api::SecretStore, - Api::Whisper, - Api::WhisperPubSub, Api::ParityAccounts, Api::ParitySet, Api::Signer, Api::Private, Api::Debug, + Api::ParityTransactionsPool, ].into_iter() .collect() ) @@ -899,9 +903,8 @@ mod test { Api::ParityPubSub, Api::Traces, Api::Rpc, - Api::Whisper, - Api::WhisperPubSub, Api::Private, + Api::ParityTransactionsPool, ].into_iter() .collect() ) diff --git a/parity/run.rs b/parity/run.rs index 3cb42fbad0..3ab5a67b2b 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,17 +20,15 @@ use std::time::{Duration, Instant}; use std::thread; use ansi_term::Colour; -use bytes::Bytes; -use call_contract::CallContract; -use ethcore::client::{BlockId, Client, Mode, DatabaseCompactionProfile, VMType, BlockChainClient, BlockInfo}; +use client_traits::{BlockInfo, BlockChainClient}; +use ethcore::client::{Client, DatabaseCompactionProfile}; use ethcore::miner::{self, stratum, Miner, MinerService, MinerOptions}; -use ethcore::snapshot::{self, SnapshotConfiguration}; -use ethcore::spec::{SpecParams, OptimizeFor}; -use ethcore::verification::queue::VerifierSettings; +use snapshot::{self, SnapshotConfiguration}; +use spec::SpecParams; +use verification::queue::VerifierSettings; use ethcore_logger::{Config as LogConfig, RotatingLogger}; use ethcore_service::ClientService; -use ethereum_types::Address; -use futures::IntoFuture; +use futures::Stream; use hash_fetch::{self, fetch}; use informant::{Informant, LightNodeInformantData, FullNodeInformantData}; use journaldb::Algorithm; @@ -38,11 +36,15 @@ use light::Cache as LightDataCache; use miner::external::ExternalMiner; use miner::work_notify::WorkPoster; use node_filter::NodeFilter; -use parity_rabbitmq::client::{PubSubClient, RabbitMqConfig, PrometheusExportServiceConfig}; use parity_runtime::Runtime; use sync::{self, SyncConfig, PrivateTxHandler}; +use types::{ + client_types::Mode, + engines::OptimizeFor, + snapshot::Snapshotting, +}; use parity_rpc::{ - Origin, Metadata, NetworkSettings, informant, is_major_importing, PubSubSession, FutureResult, FutureResponse, FutureOutput + Origin, Metadata, NetworkSettings, informant, PubSubSession, FutureResult, FutureResponse, FutureOutput }; use updater::{UpdatePolicy, Updater}; use parity_version::version; @@ -59,17 +61,17 @@ use user_defaults::UserDefaults; use ipfs; use jsonrpc_core; use modules; -use registrar::{RegistrarClient, Asynchronous}; use rpc; use rpc_apis; use secretstore; use signer; use db; +use registrar::RegistrarClient; -// how often to take periodic snapshots. +// How often we attempt to take a snapshot: only snapshot on blocknumbers that are multiples of this. const SNAPSHOT_PERIOD: u64 = 5000; -// how many blocks to wait before starting a periodic snapshot. +// Start snapshots `history` blocks from the tip. Should be smaller than `SNAPSHOT_HISTORY`. const SNAPSHOT_HISTORY: u64 = 100; // Number of minutes before a given gas price corpus should expire. @@ -100,8 +102,6 @@ pub struct RunCmd { pub http_conf: rpc::HttpConfiguration, pub ipc_conf: rpc::IpcConfiguration, pub net_conf: sync::NetworkConfiguration, - pub rabbitmq_conf: RabbitMqConfig, - pub prometheus_export_service_conf: PrometheusExportServiceConfig, pub network_id: Option, pub warp_sync: bool, pub warp_barrier: Option, @@ -113,7 +113,6 @@ pub struct RunCmd { pub tracing: Switch, pub fat_db: Switch, pub compaction: DatabaseCompactionProfile, - pub vm_type: VMType, pub geth_compatibility: bool, pub experimental_rpcs: bool, pub net_settings: NetworkSettings, @@ -133,7 +132,6 @@ pub struct RunCmd { pub serve_light: bool, pub light: bool, pub no_persistent_txqueue: bool, - pub whisper: ::whisper::Config, pub no_hardcoded_sync: bool, pub max_round_blocks_to_import: usize, pub on_demand_response_time_window: Option, @@ -255,9 +253,11 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc, on_client_rq }; // initialize database. - 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).map_err(|e| format!("Failed to open database {:?}", e))?; + let db = db::open_db_light( + &db_dirs.client_path(algorithm).to_str().expect("DB path could not be converted to string."), + &cmd.cache_config, + &cmd.compaction, + ).map_err(|e| format!("Failed to open database {:?}", e))?; let service = light_client::Service::start(config, &spec, fetch, db, cache.clone()) .map_err(|e| format!("Error starting light client: {}", e))?; @@ -272,15 +272,6 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc, on_client_rq net_conf.boot_nodes = spec.nodes.clone(); } - let mut attached_protos = Vec::new(); - let whisper_factory = if cmd.whisper.enabled { - let whisper_factory = ::whisper::setup(cmd.whisper.target_message_pool_size, &mut attached_protos) - .map_err(|e| format!("Failed to initialize whisper: {}", e))?; - whisper_factory - } else { - None - }; - // set network path. net_conf.net_config_path = Some(db_dirs.network_path().to_string_lossy().into_owned()); let sync_params = LightSyncParams { @@ -289,13 +280,13 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc, on_client_rq network_id: cmd.network_id.unwrap_or(spec.network_id()), subprotocol_name: sync::LIGHT_PROTOCOL, handlers: vec![on_demand.clone()], - attached_protos: attached_protos, }; let light_sync = LightSync::new(sync_params).map_err(|e| format!("Error starting network: {}", e))?; let light_sync = Arc::new(light_sync); *sync_handle.write() = Arc::downgrade(&light_sync); - // spin up event loop + // Spin up the Tokio event loop with core_threads = number of logical cores on the machine. + // This runtime is shared among many subsystems: sync, rpc processing, tx broadcasting, price fetcher etc let runtime = Runtime::with_default_thread_count(); // start the network. @@ -314,22 +305,21 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc, on_client_rq // start RPCs let deps_for_rpc_apis = Arc::new(rpc_apis::LightDependencies { - signer_service: signer_service, + signer_service, client: client.clone(), sync: light_sync.clone(), net: light_sync.clone(), accounts: account_provider, - logger: logger, + logger, settings: Arc::new(cmd.net_settings), - on_demand: on_demand, + on_demand, cache: cache.clone(), transaction_queue: txq, ws_address: cmd.ws_conf.address(), - fetch: fetch, + fetch, geth_compatibility: cmd.geth_compatibility, experimental_rpcs: cmd.experimental_rpcs, executor: runtime.executor(), - whisper_rpc: whisper_factory, private_tx_service: None, //TODO: add this to client. gas_price_percentile: cmd.gas_price_percentile, poll_lifetime: cmd.poll_lifetime @@ -352,7 +342,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc, on_client_rq LightNodeInformantData { client: client.clone(), sync: light_sync.clone(), - cache: cache, + cache, }, None, Some(rpc_stats), @@ -373,9 +363,14 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc, on_client_rq }) } -fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: Cr, - on_updater_rq: Rr) -> Result - where Cr: Fn(String) + 'static + Send, +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 @@ -455,7 +450,14 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: } sync_config.fork_block = spec.fork_block(); - let mut warp_sync = spec.engine.supports_warp() && cmd.warp_sync; + let snapshot_supported = + if let Snapshotting::Unsupported = spec.engine.snapshot_mode() { + false + } else { + true + }; + + let mut warp_sync = snapshot_supported && cmd.warp_sync; if warp_sync { // Logging is not initialized yet, so we print directly to stderr if fat_db { @@ -482,7 +484,8 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: // prepare account provider let account_provider = Arc::new(account_utils::prepare_account_provider(&cmd.spec, &cmd.dirs, &spec.data_dir, cmd.acc_conf, &passwords)?); - // spin up event loop + // Spin up the Tokio event loop with core_threads = number of logical cores on the machine. + // This runtime is shared among many subsystems: sync, rpc processing, tx broadcasting, price fetcher etc let runtime = Runtime::with_default_thread_count(); // fetch service @@ -529,7 +532,6 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: tracing, fat_db, cmd.compaction, - cmd.vm_type, cmd.name, algorithm, cmd.pruning_history, @@ -584,9 +586,11 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: // 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 connection_filter = connection_filter_address.map(|a| Arc::new(NodeFilter::new(Arc::downgrade(&client) as Weak, a))); let snapshot_service = service.snapshot_service(); - + if let Some(filter) = connection_filter.clone() { + service.add_notify(filter.clone()); + } // initialize the local node information store. let store = { let db = service.db(); @@ -634,33 +638,23 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: .map_err(|e| format!("Stratum start error: {:?}", e))?; } - let mut attached_protos = Vec::new(); - - let whisper_factory = if cmd.whisper.enabled { - let whisper_factory = ::whisper::setup(cmd.whisper.target_message_pool_size, &mut attached_protos) - .map_err(|e| format!("Failed to initialize whisper: {}", e))?; - - whisper_factory - } else { - None - }; - - let private_tx_sync: Option> = match cmd.private_tx_enabled { - true => Some(private_tx_service.clone() as Arc), - false => None, + let (private_tx_sync, private_state) = match cmd.private_tx_enabled { + true => (Some(private_tx_service.clone() as Arc), Some(private_tx_provider.private_state_db())), + false => (None, None), }; // create sync object let (sync_provider, manage_network, chain_notify, priority_tasks) = modules::sync( sync_config, + runtime.executor(), net_conf.clone().into(), client.clone(), snapshot_service.clone(), private_tx_sync, + private_state, client.clone(), &cmd.logger_config, - attached_protos, - connection_filter.clone().map(|f| f as Arc<::sync::ConnectionFilter + 'static>), + connection_filter.clone().map(|f| f as Arc), ).map_err(|e| format!("Sync error: {}", e))?; service.add_notify(chain_notify.clone()); @@ -668,14 +662,19 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: // Propagate transactions as soon as they are imported. let tx = ::parking_lot::Mutex::new(priority_tasks); let is_ready = Arc::new(atomic::AtomicBool::new(true)); - miner.add_transactions_listener(Box::new(move |_hashes| { - // we want to have only one PendingTransactions task in the queue. - if is_ready.compare_and_swap(true, false, atomic::Ordering::SeqCst) { - let task = ::sync::PriorityTask::PropagateTransactions(Instant::now(), is_ready.clone()); - // we ignore error cause it means that we are closing - let _ = tx.lock().send(task); - } - })); + let executor = runtime.executor(); + let pool_receiver = miner.pending_transactions_receiver(); + executor.spawn( + pool_receiver.for_each(move |_hashes| { + // we want to have only one PendingTransactions task in the queue. + if is_ready.compare_and_swap(true, false, atomic::Ordering::SeqCst) { + let task = ::sync::PriorityTask::PropagateTransactions(Instant::now(), is_ready.clone()); + // we ignore error cause it means that we are closing + let _ = tx.lock().send(task); + } + Ok(()) + }) + ); // provider not added to a notification center is effectively disabled // TODO [debris] refactor it later on @@ -691,29 +690,18 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: chain_notify.start(); } - let contract_client = { - struct FullRegistrar { client: Arc } - impl RegistrarClient for FullRegistrar { - type Call = Asynchronous; - fn registrar_address(&self) -> Result { - self.client.registrar_address() - .ok_or_else(|| "Registrar not defined.".into()) - } - fn call_contract(&self, address: Address, data: Bytes) -> Self::Call { - Box::new(self.client.call_contract(BlockId::Latest, address, data).into_future()) - } - } - - Arc::new(FullRegistrar { client: client.clone() }) - }; + let fetcher = hash_fetch::Client::with_fetch( + Arc::downgrade(&(service.client() as Arc)), + fetch.clone(), + runtime.executor() + ); // the updater service - let updater_fetch = fetch.clone(); let updater = Updater::new( - &Arc::downgrade(&(service.client() as Arc)), + &Arc::downgrade(&(service.client() as Arc)), &Arc::downgrade(&sync_provider), update_policy, - hash_fetch::Client::with_fetch(contract_client.clone(), updater_fetch, runtime.executor()) + fetcher ); service.add_notify(updater.clone()); @@ -740,7 +728,6 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: ws_address: cmd.ws_conf.address(), fetch: fetch.clone(), executor: runtime.executor(), - whisper_rpc: whisper_factory, private_tx_service: Some(private_tx_service.clone()), gas_price_percentile: cmd.gas_price_percentile, poll_lifetime: cmd.poll_lifetime, @@ -760,18 +747,6 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: 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)?; - // if a chain specification was customized by the user, pass the path of the chainfile to the rabbitmq_client. - let chainfile_path = match &cmd.spec { - SpecType::Custom(filename) => Some(filename), - _ => None - }; - - let rabbitmq_client = match PubSubClient::new(client.clone(), miner.clone(), sync_provider.clone(), runtime.executor(), client_path.to_str(), cmd.rabbitmq_conf, cmd.prometheus_export_service_conf, chainfile_path) { - Ok(client) => Arc::new(client), - Err(e) => return Err(format!("Failed to start the Blockchain RabbitMQ Interface: {}", e)), - }; - service.add_notify(rabbitmq_client.clone()); - // secret store key server let secretstore_deps = secretstore::Dependencies { client: client.clone(), @@ -820,10 +795,9 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: true => None, false => { let sync = sync_provider.clone(); - let client = client.clone(); let watcher = Arc::new(snapshot::Watcher::new( service.client(), - move || is_major_importing(Some(sync.status().state), client.queue_info()), + move || sync.is_major_syncing(), service.io().channel(), SNAPSHOT_PERIOD, SNAPSHOT_HISTORY, @@ -843,7 +817,7 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: informant, client, client_service: Arc::new(service), - keep_alive: Box::new((watcher, updater, ws_server, http_server, ipc_server, secretstore_key_server, ipfs_server, runtime, rabbitmq_client)), + keep_alive: Box::new((watcher, updater, ws_server, http_server, ipc_server, secretstore_key_server, ipfs_server, runtime)), } }) } @@ -861,14 +835,14 @@ enum RunningClientInner { rpc: jsonrpc_core::MetaIoHandler>, informant: Arc>, client: Arc, - keep_alive: Box, + keep_alive: Box, }, Full { rpc: jsonrpc_core::MetaIoHandler>, informant: Arc>, client: Arc, client_service: Arc, - keep_alive: Box, + keep_alive: Box, }, } @@ -943,9 +917,14 @@ impl RunningClient { /// `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, +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 { diff --git a/parity/secretstore/blockchain.rs b/parity/secretstore/blockchain.rs new file mode 100644 index 0000000000..4d628de197 --- /dev/null +++ b/parity/secretstore/blockchain.rs @@ -0,0 +1,246 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! SecretStoreChain implementation with information about blockchain, retrieved from the client + +use std::sync::{Arc, Weak}; +use ethereum_types::{H256, Address}; +use parking_lot::RwLock; +use types::{ + ids::BlockId as EthcoreBlockId, + transaction::{Transaction, SignedTransaction, Action}, + chain_notify::NewBlocks, + tree_route::TreeRoute, + filter::Filter as BlockchainFilter, + log_entry::LocalizedLogEntry, +}; +use ethcore::client::Client; +use bytes::Bytes; +use ethabi::RawLog; +use client_traits::BlockChainClient; +use call_contract::CallContract; +use client_traits::{ChainInfo, Nonce, ChainNotify}; +use ethcore::miner::{Miner, MinerService}; +use parity_crypto::publickey::Error as EthKeyError; +use sync::SyncProvider; +use registrar::RegistrarClient; +use ethcore_secretstore::{BlockId, BlockNumber, SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, Filter}; + +// TODO: Instead of a constant, make this based on consensus finality. +/// Number of confirmations required before request can be processed. +const REQUEST_CONFIRMATIONS_REQUIRED: u64 = 3; + +fn into_ethcore_block_id(id: BlockId) -> EthcoreBlockId { + match id { + BlockId::Hash(hash) => EthcoreBlockId::Hash(hash), + BlockId::Number(number) => EthcoreBlockId::Number(number), + BlockId::Earliest => EthcoreBlockId::Earliest, + BlockId::Latest => EthcoreBlockId::Latest, + } +} + +/// SecretStore blockchain implementation (client's wrapper) +/// This implementation is trusted, when underlying client is synced and chain's security level is full +/// This trust is guaranteed by return result in get_trusted method (if it's not trusted, None is returned) +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, + /// Chain new blocks listeners + listeners: RwLock>>, +} + +impl TrustedClient { + /// Create new trusted client. + pub fn new(self_key_pair: Arc, client: Arc, sync: Arc, miner: Arc) -> Arc { + let trusted_client = Arc::new(TrustedClient { + self_key_pair, + client: Arc::downgrade(&client), + sync: Arc::downgrade(&sync), + miner: Arc::downgrade(&miner), + listeners: RwLock::default(), + }); + client.add_notify(trusted_client.clone()); + trusted_client + } + + fn notify_listeners(&self, new_enacted_len: usize) { + for listener_pointer in self.listeners.read().iter() { + if let Some(listener) = listener_pointer.upgrade() { + listener.new_blocks(new_enacted_len); + } + } + } + + /// Get 'trusted' `Client` reference only if it is synchronized && trusted. + fn get_trusted(&self) -> Option> { + self.client.upgrade() + .and_then(|client| self.sync.upgrade().map(|sync| (client, sync))) + .and_then(|(client, sync)| { + let is_synced = !sync.is_major_syncing(); + let is_trusted = client.chain_info().security_level().is_full(); + match is_synced && is_trusted { + true => Some(client), + false => None, + } + }) + } + + fn tree_route(&self, from: &H256, to: &H256) -> Option { + if let Some(client) = self.get_trusted() { + client.tree_route(from, to) + } else { + None + } + } + + fn logs(&self, filter: BlockchainFilter) -> Option> { + if let Some(client) = self.get_trusted() { + client.logs(filter).ok() + } else { + None + } + } + +} + +impl SecretStoreChain for TrustedClient { + fn add_listener(&self, target: Arc) { + self.listeners.write().push(Arc::downgrade(&target)); + } + + fn is_trusted(&self) -> bool { + self.get_trusted().is_some() + } + + fn transact_contract(&self, contract: Address, tx_data: Bytes) -> Result<(), EthKeyError> { + let client = self.client.upgrade().ok_or_else(|| EthKeyError::Custom("cannot submit tx when client is offline".into()))?; + let miner = self.miner.upgrade().ok_or_else(|| EthKeyError::Custom("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| EthKeyError::Custom(format!("failed to import tx: {}", e))) + } + + fn read_contract_address( + &self, + registry_name: &str, + address: &ContractAddress + ) -> Option
{ + match *address { + ContractAddress::Address(ref address) => Some(address.clone()), + ContractAddress::Registry => self.get_trusted().and_then(|client| + self.get_confirmed_block_hash() + .and_then(|block| { + client.get_address(registry_name, EthcoreBlockId::Hash(block)) + .unwrap_or(None) + }) + ), + } + } + + fn call_contract(&self, block_id: BlockId, contract_address: Address, data: Bytes) -> Result { + if let Some(client) = self.get_trusted() { + client.call_contract(into_ethcore_block_id(block_id), contract_address, data) + } else { + Err("Calling ACL contract without trusted blockchain client".into()) + } + } + + fn block_hash(&self, id: BlockId) -> Option { + if let Some(client) = self.get_trusted() { + client.block_hash(into_ethcore_block_id(id)) + } else { + None + } + } + + fn block_number(&self, id: BlockId) -> Option { + if let Some(client) = self.get_trusted() { + client.block_number(into_ethcore_block_id(id)) + } else { + None + } + } + + fn retrieve_last_logs(&self, filter: Filter) -> Option> { + let confirmed_block = match self.get_confirmed_block_hash() { + Some(confirmed_block) => confirmed_block, + None => return None, // no block with enough confirmations + }; + + let from_block = self.block_hash(filter.from_block).unwrap_or_else(|| confirmed_block); + let first_block = match self.tree_route(&from_block, &confirmed_block) { + // if we have a route from last_log_block to confirmed_block => search for logs on this route + // + // potentially this could lead us to reading same logs twice when reorganizing to the fork, which + // already has been canonical previosuly + // the worst thing that can happen in this case is spending some time reading unneeded data from SS db + Some(ref route) if route.index < route.blocks.len() => route.blocks[route.index], + // else we care only about confirmed block + _ => confirmed_block.clone(), + }; + + self.logs(BlockchainFilter { + from_block: EthcoreBlockId::Hash(first_block), + to_block: EthcoreBlockId::Hash(confirmed_block), + address: filter.address, + topics: filter.topics, + limit: None, + }) + .map(|blockchain_logs| { + blockchain_logs + .into_iter() + .map(|log| { + let raw_log: RawLog = (log.entry.topics.into_iter().map(|t| t.0.into()).collect(), log.entry.data).into(); + raw_log + }) + .collect::>() + }) + } + + fn get_confirmed_block_hash(&self) -> Option { + self.block_number(BlockId::Latest) + .map(|b| b.saturating_sub(REQUEST_CONFIRMATIONS_REQUIRED)) + .and_then(|b| self.block_hash(BlockId::Number(b))) + } +} + +impl ChainNotify for TrustedClient { + fn new_blocks(&self, new_blocks: NewBlocks) { + if new_blocks.has_more_blocks_to_import { return } + if !new_blocks.route.enacted().is_empty() || !new_blocks.route.retracted().is_empty() { + let enacted_len = new_blocks.route.enacted().len(); + self.notify_listeners(enacted_len); + } + } +} + diff --git a/ethcore/src/ethereum/denominations.rs b/parity/secretstore/mod.rs similarity index 58% rename from ethcore/src/ethereum/denominations.rs rename to parity/secretstore/mod.rs index 3bf4878c7c..e1026e69bd 100644 --- a/ethcore/src/ethereum/denominations.rs +++ b/parity/secretstore/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,24 +14,18 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use ethereum_types::U256; +//! Secret store related components. -#[inline] -/// 1 Ether in Wei -pub fn ether() -> U256 { U256::exp10(18) } +mod server; -#[inline] -/// 1 Finney in Wei -pub fn finney() -> U256 { U256::exp10(15) } +#[cfg(feature = "secretstore")] +mod blockchain; -#[inline] -/// 1 Szabo in Wei -pub fn szabo() -> U256 { U256::exp10(12) } +#[cfg(all(feature = "accounts", feature = "secretstore"))] +mod nodekeypair; -#[inline] -/// 1 Shannon in Wei -pub fn shannon() -> U256 { U256::exp10(9) } - -#[inline] -/// 1 Wei in Wei -pub fn wei() -> U256 { U256::exp10(0) } +pub use self::server::{Configuration, NodeSecretKey, ContractAddress, Dependencies, start}; +#[cfg(feature = "secretstore")] +use self::blockchain::TrustedClient; +#[cfg(all(feature = "accounts", feature = "secretstore"))] +use self::nodekeypair::KeyStoreNodeKeyPair; \ No newline at end of file diff --git a/parity/secretstore/nodekeypair.rs b/parity/secretstore/nodekeypair.rs new file mode 100644 index 0000000000..ef1b3cce6f --- /dev/null +++ b/parity/secretstore/nodekeypair.rs @@ -0,0 +1,59 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Key pair with signing ability + +use std::sync::Arc; +use accounts::AccountProvider; +use ethkey::Password; +use parity_crypto::publickey::public_to_address; +use ethereum_types::{H256, Address, Public}; +use parity_crypto::publickey::{Signature, Error as EthKeyError}; +use ethcore_secretstore::SigningKeyPair; + +pub struct KeyStoreNodeKeyPair { + account_provider: Arc, + address: Address, + public: Public, + password: Password, +} + +impl KeyStoreNodeKeyPair { + pub fn new(account_provider: Arc, address: Address, password: Password) -> Result { + let public = account_provider.account_public(address.clone(), &password).map_err(|e| EthKeyError::Custom(format!("{}", e)))?; + Ok(KeyStoreNodeKeyPair { + account_provider, + address, + public, + password, + }) + } +} + +impl SigningKeyPair for KeyStoreNodeKeyPair { + fn public(&self) -> &Public { + &self.public + } + + fn address(&self) -> Address { + public_to_address(&self.public) + } + + fn sign(&self, data: &H256) -> Result { + self.account_provider.sign(self.address.clone(), Some(self.password.clone()), data.clone()) + .map_err(|e| EthKeyError::Custom(format!("{}", e))) + } +} diff --git a/parity/secretstore.rs b/parity/secretstore/server.rs similarity index 89% rename from parity/secretstore.rs rename to parity/secretstore/server.rs index d9075edec8..450a9d7876 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore/server.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +//! Secret store server's launcher, contains required configuration parameters and launch method + use std::collections::BTreeMap; use std::sync::Arc; use account_utils::AccountProvider; @@ -21,7 +23,8 @@ use dir::default_data_path; use dir::helpers::replace_home; use ethcore::client::Client; use ethcore::miner::Miner; -use ethkey::{Secret, Public, Password}; +use ethkey::Password; +use parity_crypto::publickey::{Secret, Public}; use sync::SyncProvider; use ethereum_types::Address; use parity_runtime::Executor; @@ -84,6 +87,8 @@ pub struct Configuration { pub data_path: String, /// Administrator public key. pub admin_public: Option, + // Allowed CORS domains + pub cors: Option>, } /// Secret store dependencies @@ -91,7 +96,7 @@ pub struct Dependencies<'a> { /// Blockchain client. pub client: Arc, /// Sync provider. - pub sync: Arc, + pub sync: Arc, /// Miner service. pub miner: Arc, /// Account provider. @@ -119,10 +124,12 @@ mod server { mod server { use std::sync::Arc; use ethcore_secretstore; - use ethkey::KeyPair; + use parity_crypto::publickey::KeyPair; use ansi_term::Colour::{Red, White}; - use db; use super::{Configuration, Dependencies, NodeSecretKey, ContractAddress, Executor}; + use super::super::TrustedClient; + #[cfg(feature = "accounts")] + use super::super::KeyStoreNodeKeyPair; fn into_service_contract_address(address: ContractAddress) -> ethcore_secretstore::ContractAddress { match address { @@ -133,13 +140,13 @@ mod server { /// Key server pub struct KeyServer { - _key_server: Box, + _key_server: Box, } impl KeyServer { /// Create new key server pub fn new(mut conf: Configuration, deps: Dependencies, executor: Executor) -> Result { - let self_secret: Arc = match conf.self_secret.take() { + let self_secret: Arc = match conf.self_secret.take() { Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), #[cfg(feature = "accounts")] @@ -158,7 +165,7 @@ mod server { let password = deps.accounts_passwords.iter() .find(|p| deps.account_provider.sign(account.clone(), Some((*p).clone()), Default::default()).is_ok()) .ok_or_else(|| format!("No valid password for the secret store node account {}", account))?; - Arc::new(ethcore_secretstore::KeyStoreNodeKeyPair::new(deps.account_provider, account, password.clone()) + Arc::new(KeyStoreNodeKeyPair::new(deps.account_provider, account, password.clone()) .map_err(|e| format!("{}", e))?) }, None => return Err("self secret is required when using secretstore".into()), @@ -195,12 +202,14 @@ mod server { admin_public: conf.admin_public, auto_migrate_enabled: conf.auto_migrate_enabled, }, + cors: conf.cors }; cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone()); - 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, executor) + let db = ethcore_secretstore::open_secretstore_db(&conf.data_path)?; + let trusted_client = TrustedClient::new(self_secret.clone(), deps.client, deps.sync, deps.miner); + let key_server = ethcore_secretstore::start(trusted_client, self_secret, cconf, db, executor) .map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?; Ok(KeyServer { @@ -234,6 +243,7 @@ impl Default for Configuration { http_interface: "127.0.0.1".to_owned(), http_port: 8082, data_path: replace_home(&data_dir, "$BASE/secretstore"), + cors: Some(vec![]), } } } diff --git a/parity/signer.rs b/parity/signer.rs index 98f2b8cc6b..f451eb642c 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/parity/snapshot.rs b/parity/snapshot_cmd.rs similarity index 89% rename from parity/snapshot.rs rename to parity/snapshot_cmd.rs index 269965c335..3ce24bb52a 100644 --- a/parity/snapshot.rs +++ b/parity/snapshot_cmd.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,13 +21,19 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use hash::keccak; -use ethcore::snapshot::{Progress, RestorationStatus, SnapshotConfiguration, SnapshotService as SS}; -use ethcore::snapshot::io::{SnapshotReader, PackedReader, PackedWriter}; -use ethcore::snapshot::service::Service as SnapshotService; -use ethcore::client::{Mode, DatabaseCompactionProfile, VMType}; +use snapshot::{SnapshotConfiguration, SnapshotService as SS, SnapshotClient}; +use snapshot::io::{SnapshotReader, PackedReader, PackedWriter}; +use snapshot::service::Service as SnapshotService; +use ethcore::client::{Client, DatabaseCompactionProfile}; use ethcore::miner::Miner; use ethcore_service::ClientService; -use types::ids::BlockId; +use parking_lot::RwLock; +use types::{ + ids::BlockId, + snapshot::Progress, + client_types::Mode, + snapshot::RestorationStatus, +}; use cache::CacheConfig; use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_bool}; @@ -67,7 +73,7 @@ pub struct SnapshotCommand { // helper for reading chunks from arbitrary reader and feeding them into the // service. -fn restore_using(snapshot: Arc, reader: &R, recover: bool) -> Result<(), String> { +fn restore_using(snapshot: Arc>, reader: &R, recover: bool) -> Result<(), String> { let manifest = reader.manifest(); info!("Restoring to block #{} (0x{:?})", manifest.block_number, manifest.block_hash); @@ -123,6 +129,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::Finalizing => Err("Snapshot restoration is still finalizing.".into()), RestorationStatus::Failed => Err("Snapshot restoration failed.".into()), RestorationStatus::Inactive => { info!("Restoration complete."); @@ -173,7 +180,6 @@ impl SnapshotCommand { tracing, fat_db, self.compaction, - VMType::default(), "".into(), algorithm, self.pruning_history, @@ -251,20 +257,25 @@ impl SnapshotCommand { let writer = PackedWriter::new(&file_path) .map_err(|e| format!("Failed to open snapshot writer: {}", e))?; - let progress = Arc::new(Progress::default()); + let progress = Arc::new(RwLock::new(Progress::new())); let p = progress.clone(); let informant_handle = ::std::thread::spawn(move || { ::std::thread::sleep(Duration::from_secs(5)); - let mut last_size = 0; - while !p.done() { - let cur_size = p.size(); - if cur_size != last_size { - last_size = cur_size; - let bytes = ::informant::format_bytes(cur_size as usize); - info!("Snapshot: {} accounts {} blocks {}", p.accounts(), p.blocks(), bytes); + loop { + { + let progress = p.read(); + if !progress.done() { + let cur_size = progress.bytes(); + if cur_size != last_size { + last_size = cur_size; + let bytes = ::informant::format_bytes(cur_size); + info!("Snapshot: {} accounts (state), {} blocks, {} bytes", progress.accounts(), progress.blocks(), bytes); + } + } else { + break; + } } - ::std::thread::sleep(Duration::from_secs(5)); } }); @@ -276,7 +287,7 @@ impl SnapshotCommand { info!("snapshot creation complete"); - assert!(progress.done()); + assert!(progress.read().done()); informant_handle.join().map_err(|_| "failed to join logger thread")?; Ok(()) diff --git a/parity/stratum.rs b/parity/stratum.rs index a74f60d852..f39ebdb5c7 100644 --- a/parity/stratum.rs +++ b/parity/stratum.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/parity/upgrade.rs b/parity/upgrade.rs index ecd9beff16..7480766360 100644 --- a/parity/upgrade.rs +++ b/parity/upgrade.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/parity/user_defaults.rs b/parity/user_defaults.rs index bdd5d9efb3..491162dcb3 100644 --- a/parity/user_defaults.rs +++ b/parity/user_defaults.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::de::from_reader; use serde_json::ser::to_string; use journaldb::Algorithm; -use ethcore::client::{Mode as ClientMode}; +use types::client_types::Mode as ClientMode; #[derive(Clone)] pub struct Seconds(Duration); diff --git a/parity/whisper.rs b/parity/whisper.rs deleted file mode 100644 index e9a744b546..0000000000 --- a/parity/whisper.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::io; - -use sync::{AttachedProtocol, ManageNetwork}; -use parity_rpc::Metadata; -use parity_whisper::message::Message; -use parity_whisper::net::{self as whisper_net, Network as WhisperNetwork}; -use parity_whisper::rpc::{WhisperClient, PoolHandle, FilterManager}; - -/// Whisper config. -#[derive(Debug, PartialEq, Eq)] -pub struct Config { - pub enabled: bool, - pub target_message_pool_size: usize, -} - -impl Default for Config { - fn default() -> Self { - Config { - enabled: false, - target_message_pool_size: 10 * 1024 * 1024, - } - } -} - -/// Standard pool handle. -pub struct NetPoolHandle { - /// Pool handle. - handle: Arc>>, - /// Network manager. - net: Arc, -} - -impl PoolHandle for NetPoolHandle { - fn relay(&self, message: Message) -> bool { - let mut res = false; - let mut message = Some(message); - self.net.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() - } -} - -/// Factory for standard whisper RPC. -pub struct RpcFactory { - net: Arc>>, - manager: Arc, -} - -impl RpcFactory { - pub fn make_handler(&self, net: Arc) -> WhisperClient { - let handle = NetPoolHandle { handle: self.net.clone(), net: net }; - WhisperClient::new(handle, self.manager.clone()) - } -} - -/// Sets up whisper protocol and RPC handler. -/// -/// Will target the given pool size. -#[cfg(not(feature = "ipc"))] -pub fn setup(target_pool_size: usize, protos: &mut Vec) - -> io::Result> -{ - let manager = Arc::new(FilterManager::new()?); - let net = Arc::new(WhisperNetwork::new(target_pool_size, manager.clone())); - - protos.push(AttachedProtocol { - handler: net.clone() as Arc<_>, - versions: whisper_net::SUPPORTED_VERSIONS, - protocol_id: whisper_net::PROTOCOL_ID, - }); - - // parity-only extensions to whisper. - protos.push(AttachedProtocol { - handler: Arc::new(whisper_net::ParityExtensions), - versions: whisper_net::SUPPORTED_VERSIONS, - protocol_id: whisper_net::PARITY_PROTOCOL_ID, - }); - - let factory = RpcFactory { net: net, manager: manager }; - - Ok(Some(factory)) -} - -// TODO: make it possible to attach generic protocols in IPC. -#[cfg(feature = "ipc")] -pub fn setup(_target_pool_size: usize, _protos: &mut Vec) - -> io::Result> -{ - Ok(None) -} diff --git a/rabbitmq/src/types/hash.rs b/rabbitmq/src/types/hash.rs deleted file mode 100644 index 6cdaccf43b..0000000000 --- a/rabbitmq/src/types/hash.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::fmt; -use std::str::FromStr; -use std::cmp::Ordering; -use std::hash::{Hash, Hasher}; -use serde; -use rustc_hex::{ToHex, FromHex}; -use ethereum_types::{H64 as Eth64, H160 as Eth160, H256 as Eth256, H520 as Eth520, H512 as Eth512, Bloom as Eth2048}; - -macro_rules! impl_hash { - ($name: ident, $other: ident, $size: expr) => { - /// Hash serialization - pub struct $name(pub [u8; $size]); - - impl Eq for $name { } - - impl Default for $name { - fn default() -> Self { - $name([0; $size]) - } - } - - impl fmt::Debug for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0.to_hex()) - } - } - - impl fmt::Display for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let hex = self.0.to_hex(); - write!(f, "{}..{}", &hex[0..2], &hex[$size-2..$size]) - } - } - - impl From for $name where $other: From { - fn from(o: T) -> Self { - $name($other::from(o).0) - } - } - - impl FromStr for $name { - type Err = <$other as FromStr>::Err; - - fn from_str(s: &str) -> Result { - $other::from_str(s).map(|x| $name(x.0)) - } - } - - impl Into<$other> for $name { - fn into(self) -> $other { - $other(self.0) - } - } - - impl PartialEq for $name { - fn eq(&self, other: &Self) -> bool { - let self_ref: &[u8] = &self.0; - let other_ref: &[u8] = &other.0; - self_ref == other_ref - } - } - - impl PartialOrd for $name { - fn partial_cmp(&self, other: &Self) -> Option { - let self_ref: &[u8] = &self.0; - let other_ref: &[u8] = &other.0; - self_ref.partial_cmp(other_ref) - } - } - - impl Ord for $name { - fn cmp(&self, other: &Self) -> Ordering { - let self_ref: &[u8] = &self.0; - let other_ref: &[u8] = &other.0; - self_ref.cmp(other_ref) - } - } - - impl Hash for $name { - fn hash(&self, state: &mut H) where H: Hasher { - let self_ref: &[u8] = &self.0; - Hash::hash(self_ref, state) - } - } - - impl Clone for $name { - fn clone(&self) -> Self { - let mut r = [0; $size]; - r.copy_from_slice(&self.0); - $name(r) - } - } - - impl serde::Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer { - let mut hex = "0x".to_owned(); - hex.push_str(&self.0.to_hex()); - serializer.serialize_str(&hex) - } - } - - impl<'a> serde::Deserialize<'a> for $name { - fn deserialize(deserializer: D) -> Result<$name, D::Error> where D: serde::Deserializer<'a> { - struct HashVisitor; - - impl<'b> serde::de::Visitor<'b> for HashVisitor { - type Value = $name; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed, padded, hex-encoded hash with length {}", $size * 2) - } - - fn visit_str(self, value: &str) -> Result where E: serde::de::Error { - - if value.len() < 2 || !value.starts_with("0x") { - return Err(E::custom("expected a hex-encoded hash with 0x prefix")); - } - if value.len() != 2 + $size * 2 { - return Err(E::invalid_length(value.len() - 2, &self)); - } - - match value[2..].from_hex() { - Ok(ref v) => { - let mut result = [0u8; $size]; - result.copy_from_slice(v); - Ok($name(result)) - }, - Err(e) => Err(E::custom(format!("invalid hex value: {:?}", e))), - } - } - - fn visit_string(self, value: String) -> Result where E: serde::de::Error { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize_any(HashVisitor) - } - } - } -} - -impl_hash!(H64, Eth64, 8); -impl_hash!(H160, Eth160, 20); -impl_hash!(H256, Eth256, 32); -impl_hash!(H512, Eth512, 64); -impl_hash!(H520, Eth520, 65); -impl_hash!(H2048, Eth2048, 256); diff --git a/rabbitmq/src/types/uint.rs b/rabbitmq/src/types/uint.rs deleted file mode 100644 index 3c5801c07c..0000000000 --- a/rabbitmq/src/types/uint.rs +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::str::FromStr; -use std::fmt; -use serde; -use ethereum_types::{U256 as EthU256, U128 as EthU128}; - -macro_rules! impl_uint { - ($name: ident, $other: ident, $size: expr) => { - /// Uint serialization. - #[derive(Debug, Default, Clone, Copy, PartialEq, Hash)] - pub struct $name($other); - - impl Eq for $name { } - - impl From for $name where $other: From { - fn from(o: T) -> Self { - $name($other::from(o)) - } - } - - impl FromStr for $name { - type Err = <$other as FromStr>::Err; - - fn from_str(s: &str) -> Result { - $other::from_str(s).map($name) - } - } - - impl Into<$other> for $name { - fn into(self) -> $other { - self.0 - } - } - - impl fmt::Display for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } - } - - impl fmt::LowerHex for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::LowerHex::fmt(&self.0, f) - } - } - - impl<'a> serde::Deserialize<'a> for $name { - fn deserialize(deserializer: D) -> Result<$name, D::Error> - where D: serde::Deserializer<'a> { - struct UintVisitor; - - impl<'b> serde::de::Visitor<'b> for UintVisitor { - type Value = $name; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed, hex-encoded number of length {}", $size*16) - } - - fn visit_str(self, value: &str) -> Result where E: serde::de::Error { - if value.len() < 2 || !value.starts_with("0x") { - return Err(E::custom("expected a hex-encoded numbers with 0x prefix")) - } - - // 0x + len - if value.len() > 2 + $size * 16 { - return Err(E::invalid_length(value.len() - 2, &self)); - } - - $other::from_str(&value[2..]).map($name).map_err(|e| E::custom(&format!("invalid hex value: {:?}", e))) - } - - fn visit_string(self, value: String) -> Result where E: serde::de::Error { - self.visit_str(&value) - } - } - - deserializer.deserialize_any(UintVisitor) - } - } - - } -} - -impl_uint!(U128, EthU128, 2); -impl_uint!(U256, EthU256, 4); -impl_uint!(U64, u64, 1); - -impl serde::Serialize for U128 { - fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - 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!("{:#x}", self)) - } -} - -impl serde::Serialize for U64 { - fn serialize(&self, serializer: S) -> Result where S: serde::Serializer { - serializer.serialize_str(&format!("{:#x}", self)) - } -} - -#[cfg(test)] -mod tests { - use super::U256; - use serde_json; - - type Res = Result; - - #[test] - fn should_serialize_u256() { - let serialized1 = serde_json::to_string(&U256(0.into())).unwrap(); - let serialized2 = serde_json::to_string(&U256(1.into())).unwrap(); - let serialized3 = serde_json::to_string(&U256(16.into())).unwrap(); - let serialized4 = serde_json::to_string(&U256(256.into())).unwrap(); - - assert_eq!(serialized1, r#""0x0""#); - assert_eq!(serialized2, r#""0x1""#); - assert_eq!(serialized3, r#""0x10""#); - assert_eq!(serialized4, r#""0x100""#); - } - - #[test] - fn should_fail_to_deserialize_decimals() { - let deserialized0: Res = serde_json::from_str(r#""∀∂""#); - let deserialized1: Res = serde_json::from_str(r#""""#); - let deserialized2: Res = serde_json::from_str(r#""0""#); - let deserialized3: Res = serde_json::from_str(r#""10""#); - let deserialized4: Res = serde_json::from_str(r#""1000000""#); - let deserialized5: Res = serde_json::from_str(r#""1000000000000000000""#); - - assert!(deserialized0.is_err()); - assert!(deserialized1.is_err()); - assert!(deserialized2.is_err()); - assert!(deserialized3.is_err()); - assert!(deserialized4.is_err()); - assert!(deserialized5.is_err()); - } - - #[test] - fn should_deserialize_u256() { - let deserialized1: U256 = serde_json::from_str(r#""0x""#).unwrap(); - let deserialized2: U256 = serde_json::from_str(r#""0x0""#).unwrap(); - let deserialized3: U256 = serde_json::from_str(r#""0x1""#).unwrap(); - let deserialized4: U256 = serde_json::from_str(r#""0x01""#).unwrap(); - let deserialized5: U256 = serde_json::from_str(r#""0x100""#).unwrap(); - - assert_eq!(deserialized1, U256(0.into())); - assert_eq!(deserialized2, U256(0.into())); - assert_eq!(deserialized3, U256(1.into())); - assert_eq!(deserialized4, U256(1.into())); - assert_eq!(deserialized5, U256(256.into())); - } -} diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index e95c48cb96..492e06467d 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -8,33 +8,36 @@ authors = ["Parity Technologies "] [lib] [dependencies] -ansi_term = "0.10" +ansi_term = "0.11" cid = "0.3" futures = "0.1.6" log = "0.4" multihash = "0.8" order-stat = "0.1" -parking_lot = "0.7" -rand = "0.4" +rand = "0.7" +rand_xorshift = "0.2" rustc-hex = "1.0" semver = "0.9" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +parking_lot = "0.9" tempdir = "0.3" tiny-keccak = "1.4" tokio-timer = "0.1" transient-hashmap = "0.4" itertools = "0.5" -jsonrpc-core = "10.0.1" -jsonrpc-derive = "10.0.2" -jsonrpc-http-server = "10.0.1" -jsonrpc-ws-server = "10.0.1" -jsonrpc-ipc-server = "10.0.1" -jsonrpc-pubsub = "10.0.1" +jsonrpc-core = "14.0.5" +jsonrpc-derive = "14.0.5" +jsonrpc-http-server = "14.0.5" +jsonrpc-ws-server = "14.0.5" +jsonrpc-ipc-server = "14.0.6" +jsonrpc-pubsub = "14.0.5" +client-traits = { path = "../ethcore/client-traits" } common-types = { path = "../ethcore/types" } +engine = { path = "../ethcore/engine" } ethash = { path = "../ethash" } ethcore = { path = "../ethcore" } ethcore-accounts = { path = "../accounts", optional = true } @@ -44,33 +47,41 @@ ethcore-miner = { path = "../miner" } ethcore-network = { path = "../util/network" } ethcore-private-tx = { path = "../ethcore/private-tx" } ethcore-sync = { path = "../ethcore/sync" } -ethereum-types = "0.4" +ethereum-types = "0.8.0" fastmap = { path = "../util/fastmap" } +machine = { path = "../ethcore/machine" } parity-bytes = "0.1" -parity-crypto = "0.3.0" +parity-crypto = { version = "0.4.2", features = ["publickey"] } eip-712 = { path = "../util/EIP-712" } ethjson = { path = "../json" } ethkey = { path = "../accounts/ethkey" } ethstore = { path = "../accounts/ethstore" } fetch = { path = "../util/fetch" } -keccak-hash = "0.1.2" +keccak-hash = "0.4.0" parity-runtime = { path = "../util/runtime" } parity-updater = { path = "../updater" } parity-version = { path = "../util/version" } -rlp = { version = "0.3.0", features = ["ethereum"] } +rlp = "0.4.0" +account-state = { path = "../ethcore/account-state" } +snapshot = { path = "../ethcore/snapshot" } stats = { path = "../util/stats" } +trace = { path = "../ethcore/trace" } vm = { path = "../ethcore/vm" } [dev-dependencies] +client-traits = { path = "../ethcore/client-traits" } ethcore = { path = "../ethcore", features = ["test-helpers"] } ethcore-accounts = { path = "../accounts" } ethcore-io = { path = "../util/io" } ethcore-network = { path = "../util/network" } +ethjson = { path = "../json", features = ["test-helpers"] } fake-fetch = { path = "../util/fake-fetch" } macros = { path = "../util/macros" } +spec = { path = "../ethcore/spec" } pretty_assertions = "0.1" transaction-pool = "2.0.1" +verification = { path = "../ethcore/verification" } [features] accounts = ["ethcore-accounts"] diff --git a/rpc/src/authcodes.rs b/rpc/src/authcodes.rs index b348dfd729..0f1ab4e195 100644 --- a/rpc/src/authcodes.rs +++ b/rpc/src/authcodes.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,8 +19,7 @@ use std::path::Path; use std::{fs, time, mem}; use itertools::Itertools; -use rand::Rng; -use rand::os::OsRng; +use rand::{Rng, rngs::OsRng, distributions::Alphanumeric}; use hash::keccak; use ethereum_types::H256; @@ -174,8 +173,8 @@ impl AuthCodes { /// Generates and returns a new code that can be used by `SignerUIs` pub fn generate_new(&mut self) -> io::Result { - let mut rng = OsRng::new()?; - let code = rng.gen_ascii_chars().take(TOKEN_LENGTH).collect::(); + let rng = OsRng; + let code = rng.sample_iter(&Alphanumeric).take(TOKEN_LENGTH).collect::(); let readable_code = code.as_bytes() .chunks(4) .filter_map(|f| String::from_utf8(f.to_vec()).ok()) diff --git a/rpc/src/http_common.rs b/rpc/src/http_common.rs index 99bd392f35..5d4898a310 100644 --- a/rpc/src/http_common.rs +++ b/rpc/src/http_common.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 6c0ce2a011..45911ad674 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -43,6 +43,7 @@ extern crate futures; extern crate ansi_term; extern crate cid; extern crate itertools; +extern crate machine; extern crate multihash; extern crate order_stat; extern crate parking_lot; @@ -60,6 +61,7 @@ extern crate jsonrpc_http_server as http; extern crate jsonrpc_ipc_server as ipc; extern crate jsonrpc_pubsub; +extern crate client_traits; extern crate common_types as types; extern crate ethash; extern crate ethcore; @@ -82,8 +84,12 @@ extern crate parity_updater as updater; extern crate parity_version as version; extern crate eip_712; extern crate rlp; +extern crate account_state; + extern crate stats; +extern crate snapshot; extern crate tempdir; +extern crate trace; extern crate vm; #[cfg(any(test, feature = "ethcore-accounts"))] @@ -97,6 +103,12 @@ extern crate log; #[macro_use] extern crate serde_derive; +#[cfg(test)] +extern crate rand_xorshift; + +#[cfg(test)] +extern crate engine; + #[cfg(test)] extern crate ethjson; #[cfg(test)] @@ -116,6 +128,11 @@ extern crate fake_fetch; #[cfg(test)] extern crate ethcore_io as io; +#[cfg(test)] +extern crate spec; +#[cfg(test)] +extern crate verification; + pub extern crate jsonrpc_ws_server as ws; mod authcodes; @@ -126,7 +143,12 @@ pub mod tests; pub use jsonrpc_core::{FutureOutput, FutureResult, FutureResponse, FutureRpcResult}; pub use jsonrpc_pubsub::Session as PubSubSession; -pub use ipc::{Server as IpcServer, MetaExtractor as IpcMetaExtractor, RequestContext as IpcRequestContext}; +pub use ipc::{ + MetaExtractor as IpcMetaExtractor, + RequestContext as IpcRequestContext, + SecurityAttributes, + Server as IpcServer, +}; pub use http::{ hyper, RequestMiddleware, RequestMiddlewareAction, @@ -134,7 +156,8 @@ pub use http::{ }; pub use v1::{NetworkSettings, Metadata, Origin, informant, dispatch, signer}; -pub use v1::block_import::{is_major_importing, is_major_importing_or_waiting}; +pub use v1::block_import::{is_major_importing_or_waiting}; +pub use v1::PubSubSyncStatus; pub use v1::extractors::{RpcExtractor, WsExtractor, WsStats, WsDispatcher}; pub use authcodes::{AuthCodes, TimeProvider}; pub use http_common::HttpMetaExtractor; @@ -208,13 +231,18 @@ pub fn start_ipc( addr: &str, handler: H, extractor: T, + chmod: u16 ) -> ::std::io::Result where M: jsonrpc_core::Metadata, S: jsonrpc_core::Middleware, H: Into>, T: IpcMetaExtractor, { + let attr = SecurityAttributes::empty() + .set_mode(chmod as _)?; + ipc::ServerBuilder::with_meta_extractor(handler, extractor) + .set_security_attributes(attr) .start(addr) } diff --git a/rpc/src/tests/helpers.rs b/rpc/src/tests/helpers.rs index 301d77e91c..57a405365d 100644 --- a/rpc/src/tests/helpers.rs +++ b/rpc/src/tests/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/tests/http_client.rs b/rpc/src/tests/http_client.rs index 0588c791e7..08639ff4a2 100644 --- a/rpc/src/tests/http_client.rs +++ b/rpc/src/tests/http_client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -113,20 +113,20 @@ pub fn request(address: &SocketAddr, request: &str) -> Response { pub fn assert_security_headers_present(headers: &[String], port: Option) { if port.is_none() { assert!( - headers.iter().any(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN") + headers.iter().any(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN"), "X-Frame-Options: SAMEORIGIN missing: {:?}", headers ); } assert!( - headers.iter().any(|header| header.as_str() == "X-XSS-Protection: 1; mode=block") + headers.iter().any(|header| header.as_str() == "X-XSS-Protection: 1; mode=block"), "X-XSS-Protection missing: {:?}", headers ); assert!( - headers.iter().any(|header| header.as_str() == "X-Content-Type-Options: nosniff") + headers.iter().any(|header| header.as_str() == "X-Content-Type-Options: nosniff"), "X-Content-Type-Options missing: {:?}", headers ); assert!( - headers.iter().any(|header| header.starts_with("Content-Security-Policy: ")) + headers.iter().any(|header| header.starts_with("Content-Security-Policy: ")), "Content-Security-Policy missing: {:?}", headers ) } diff --git a/rpc/src/tests/mod.rs b/rpc/src/tests/mod.rs index a73d69096d..0d4ef1e330 100644 --- a/rpc/src/tests/mod.rs +++ b/rpc/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/tests/rpc.rs b/rpc/src/tests/rpc.rs index 99498c3e5d..28aabab66a 100644 --- a/rpc/src/tests/rpc.rs +++ b/rpc/src/tests/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/tests/ws.rs b/rpc/src/tests/ws.rs index 3b60788820..4a66cc9875 100644 --- a/rpc/src/tests/ws.rs +++ b/rpc/src/tests/ws.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/extractors.rs b/rpc/src/v1/extractors.rs index d3384c2c1d..9cb0626899 100644 --- a/rpc/src/v1/extractors.rs +++ b/rpc/src/v1/extractors.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -52,7 +52,7 @@ impl HttpMetaExtractor for RpcExtractor { impl ipc::MetaExtractor for RpcExtractor { fn extract(&self, req: &ipc::RequestContext) -> Metadata { Metadata { - origin: Origin::Ipc(req.session_id.into()), + origin: Origin::Ipc(H256::from_low_u64_be(req.session_id)), session: Some(Arc::new(Session::new(req.sender.clone()))), } } @@ -81,10 +81,10 @@ impl ws::MetaExtractor for WsExtractor { let authorization = req.protocols.get(0).and_then(|p| auth_token_hash(&path, p, true)); match authorization { Some(id) => Origin::Signer { session: id }, - None => Origin::Ws { session: id.into() }, + None => Origin::Ws { session: H256::from_low_u64_be(id) }, } }, - None => Origin::Ws { session: id.into() }, + None => Origin::Ws { session: H256::from_low_u64_be(id) }, }; let session = Some(Arc::new(Session::new(req.sender()))); Metadata { diff --git a/rpc/src/v1/helpers/block_import.rs b/rpc/src/v1/helpers/block_import.rs index 3fd5d9fff7..e0d55803d8 100644 --- a/rpc/src/v1/helpers/block_import.rs +++ b/rpc/src/v1/helpers/block_import.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ //! Block import analysis functions. -use ethcore::client::BlockQueueInfo; +use types::verification::VerificationQueueInfo as BlockQueueInfo; use sync::SyncState; /// Check if client is during major sync or during block import and allows defining whether 'waiting for peers' should @@ -30,38 +30,3 @@ pub fn is_major_importing_or_waiting(sync_state: Option, queue_info: let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > 3; is_verifying || is_syncing_state } - -/// Check if client is during major sync or during block import. -pub fn is_major_importing(sync_state: Option, queue_info: BlockQueueInfo) -> bool { - is_major_importing_or_waiting(sync_state, queue_info, true) -} - -#[cfg(test)] -mod tests { - use ethcore::client::BlockQueueInfo; - use sync::SyncState; - use super::is_major_importing; - - 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 - } - } - - #[test] - fn is_still_verifying() { - assert!(!is_major_importing(None, queue_info(2, 1))); - assert!(is_major_importing(None, queue_info(2, 2))); - } - - #[test] - fn is_synced_state() { - assert!(is_major_importing(Some(SyncState::Blocks), queue_info(0, 0))); - assert!(!is_major_importing(Some(SyncState::Idle), queue_info(0, 0))); - } -} diff --git a/rpc/src/v1/helpers/deprecated.rs b/rpc/src/v1/helpers/deprecated.rs index 49e9d8b074..ba0fd35471 100644 --- a/rpc/src/v1/helpers/deprecated.rs +++ b/rpc/src/v1/helpers/deprecated.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -38,7 +38,7 @@ const PRINT_INTERVAL: Duration = Duration::from_secs(60); pub struct DeprecationNotice Instant> { now: T, next_warning_at: RwLock>, - printer: Box) + Send + Sync>, + printer: Box) + Send + Sync>, } impl Default for DeprecationNotice { diff --git a/rpc/src/v1/helpers/dispatch/full.rs b/rpc/src/v1/helpers/dispatch/full.rs index d958416cbf..4fb936e308 100644 --- a/rpc/src/v1/helpers/dispatch/full.rs +++ b/rpc/src/v1/helpers/dispatch/full.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ use std::sync::Arc; -use ethcore::client::BlockChainClient; +use client_traits::BlockChainClient; use ethcore::miner::{self, MinerService}; use ethereum_types::{H256, U256, Address}; use types::transaction::{SignedTransaction, PendingTransaction}; @@ -118,7 +118,7 @@ impl Dispatcher fn sign

( &self, filled: FilledTransactionRequest, - signer: &Arc, + signer: &Arc, password: SignWith, post_sign: P, ) -> BoxFuture diff --git a/rpc/src/v1/helpers/dispatch/light.rs b/rpc/src/v1/helpers/dispatch/light.rs index 88f9fafcf1..2159670c42 100644 --- a/rpc/src/v1/helpers/dispatch/light.rs +++ b/rpc/src/v1/helpers/dispatch/light.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -45,7 +45,7 @@ where /// Sync service. pub sync: Arc, /// Header chain client. - pub client: Arc, + pub client: Arc, /// On-demand request service. pub on_demand: Arc, /// Data cache. @@ -68,7 +68,7 @@ where /// For correct operation, the OnDemand service is assumed to be registered as a network handler, pub fn new( sync: Arc, - client: Arc, + client: Arc, on_demand: Arc, cache: Arc>, transaction_queue: Arc>, @@ -215,7 +215,7 @@ where fn sign

( &self, filled: FilledTransactionRequest, - signer: &Arc, + signer: &Arc, password: SignWith, post_sign: P ) -> BoxFuture @@ -248,7 +248,7 @@ where // TODO: this could be `impl Trait`. pub fn fetch_gas_price_corpus( sync: Arc, - client: Arc, + client: Arc, on_demand: Arc, cache: Arc>, ) -> BoxFuture> diff --git a/rpc/src/v1/helpers/dispatch/mod.rs b/rpc/src/v1/helpers/dispatch/mod.rs index 3f247f0c6c..4d0612cff1 100644 --- a/rpc/src/v1/helpers/dispatch/mod.rs +++ b/rpc/src/v1/helpers/dispatch/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -75,10 +75,11 @@ use std::ops::Deref; use std::sync::Arc; use bytes::Bytes; -use ethcore::client::BlockChainClient; +use client_traits::BlockChainClient; use ethcore::miner::MinerService; use ethereum_types::{H520, H256, U256, Address}; -use ethkey::{Password, Signature}; +use ethkey::Password; +use crypto::publickey::Signature; use hash::keccak; use types::transaction::{SignedTransaction, PendingTransaction}; @@ -111,7 +112,7 @@ pub trait Dispatcher: Send + Sync + Clone { fn sign

( &self, filled: FilledTransactionRequest, - signer: &Arc, + signer: &Arc, password: SignWith, post_sign: P, ) -> BoxFuture where @@ -277,7 +278,7 @@ impl From<(T, Option)> for WithToken { /// Execute a confirmation payload. pub fn execute( dispatcher: D, - signer: &Arc, + signer: &Arc, payload: ConfirmationPayload, pass: SignWith ) -> BoxFuture> { diff --git a/rpc/src/v1/helpers/dispatch/prospective_signer.rs b/rpc/src/v1/helpers/dispatch/prospective_signer.rs index 034d19dc65..90f1693516 100644 --- a/rpc/src/v1/helpers/dispatch/prospective_signer.rs +++ b/rpc/src/v1/helpers/dispatch/prospective_signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -32,7 +32,7 @@ enum ProspectiveSignerState { } pub struct ProspectiveSigner { - signer: Arc, + signer: Arc, filled: FilledTransactionRequest, chain_id: Option, reserved: nonce::Reserved, @@ -46,7 +46,7 @@ pub struct ProspectiveSigner { impl ProspectiveSigner

{ pub fn new( - signer: Arc, + signer: Arc, filled: FilledTransactionRequest, chain_id: Option, reserved: nonce::Reserved, @@ -129,7 +129,7 @@ impl Future for ProspectiveSigner

{ .into_future()); }, WaitForPostSign => { - if let Some(mut fut) = self.post_sign_future.as_mut() { + if let Some(fut) = self.post_sign_future.as_mut() { match fut.poll()? { Async::Ready(item) => { let nonce = self.ready diff --git a/rpc/src/v1/helpers/dispatch/signing.rs b/rpc/src/v1/helpers/dispatch/signing.rs index 8243dcbdf8..0d14c79e29 100644 --- a/rpc/src/v1/helpers/dispatch/signing.rs +++ b/rpc/src/v1/helpers/dispatch/signing.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use accounts::AccountProvider; use bytes::Bytes; use crypto::DEFAULT_MAC; use ethereum_types::{H256, U256, Address}; -use ethkey::{Signature}; +use crypto::publickey::Signature; use types::transaction::{Transaction, Action, SignedTransaction}; use jsonrpc_core::Result; @@ -51,10 +51,6 @@ impl super::Accounts for Signer { data: filled.data, }; - if self.accounts.is_hardware_address(&filled.from) { - return hardware_signature(&*self.accounts, filled.from, t, chain_id).map(WithToken::No) - } - let hash = t.hash(chain_id); let signature = signature(&*self.accounts, filled.from, hash, password)?; @@ -65,19 +61,6 @@ impl super::Accounts for Signer { } fn sign_message(&self, address: Address, password: SignWith, hash: SignMessage) -> Result> { - if self.accounts.is_hardware_address(&address) { - return if let SignMessage::Data(data) = hash { - let signature = self.accounts.sign_message_with_hardware(&address, &data) - // 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)); - - signature - } else { - Err(errors::account("Error signing message with hardware_wallet", "Message signing is unsupported")) - } - } - match hash { SignMessage::Data(data) => { let hash = eth_data_hash(data); @@ -90,10 +73,6 @@ impl super::Accounts for Signer { } fn decrypt(&self, address: Address, password: SignWith, data: Bytes) -> Result> { - if self.accounts.is_hardware_address(&address) { - return Err(errors::unsupported("Decrypting via hardware wallets is not supported.", None)); - } - match password.clone() { SignWith::Nothing => self.accounts.decrypt(address, None, &DEFAULT_MAC, &data).map(WithToken::No), SignWith::Password(pass) => self.accounts.decrypt(address, Some(pass), &DEFAULT_MAC, &data).map(WithToken::No), @@ -134,23 +113,3 @@ fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: }) } -// obtain a hardware signature from the given account. -fn hardware_signature(accounts: &AccountProvider, address: Address, t: Transaction, chain_id: Option) - -> Result -{ - debug_assert!(accounts.is_hardware_address(&address)); - - let mut stream = rlp::RlpStream::new(); - t.rlp_append_unsigned_transaction(&mut stream, chain_id); - 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) - })?; - - SignedTransaction::new(t.with_signature(signature, chain_id)) - .map_err(|e| { - debug!(target: "miner", "Hardware wallet has produced invalid signature: {}", e); - errors::account("Invalid signature generated", e) - }) -} diff --git a/rpc/src/v1/helpers/eip191.rs b/rpc/src/v1/helpers/eip191.rs index 938ab81dc4..58a990333c 100644 --- a/rpc/src/v1/helpers/eip191.rs +++ b/rpc/src/v1/helpers/eip191.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/engine_signer.rs b/rpc/src/v1/helpers/engine_signer.rs index 56cead696c..7602673fbf 100644 --- a/rpc/src/v1/helpers/engine_signer.rs +++ b/rpc/src/v1/helpers/engine_signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -17,7 +17,8 @@ use std::sync::Arc; use accounts::AccountProvider; -use ethkey::{self, Address, Password}; +use ethkey::Password; +use crypto::publickey::{Address, Message, Public, Signature, Error}; /// An implementation of EngineSigner using internal account management. pub struct EngineSigner { @@ -33,16 +34,27 @@ impl EngineSigner { } } -impl ethcore::engines::EngineSigner for EngineSigner { - fn sign(&self, message: ethkey::Message) -> Result { +impl engine::signer::EngineSigner for EngineSigner { + fn sign(&self, message: Message) -> Result { match self.accounts.sign(self.address, Some(self.password.clone()), message) { Ok(ok) => Ok(ok), - Err(e) => Err(ethkey::Error::InvalidSecret), + Err(_) => Err(Error::InvalidSecretKey), } } + fn decrypt(&self, auth_data: &[u8], cipher: &[u8]) -> Result, Error> { + self.accounts.decrypt(self.address, None, auth_data, cipher).map_err(|e| { + warn!("Unable to decrypt message: {:?}", e); + Error::InvalidMessage + }) + } + fn address(&self) -> Address { self.address } + + fn public(&self) -> Option { + self.accounts.account_public(self.address, &self.password).ok() + } } diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 6adb114f36..6651a67312 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,16 +18,19 @@ use std::fmt; -use ethcore::error::{Error as EthcoreError, ErrorKind, CallError}; -use ethcore::client::BlockId; use jsonrpc_core::{futures, Result as RpcResult, Error, ErrorCode, Value}; use rlp::DecoderError; use types::transaction::Error as TransactionError; use ethcore_private_tx::Error as PrivateTransactionError; use vm::Error as VMError; -use light::on_demand::error::{Error as OnDemandError, ErrorKind as OnDemandErrorKind}; -use ethcore::client::BlockChainClient; -use types::blockchain_info::BlockChainInfo; +use light::on_demand::error::{Error as OnDemandError}; +use client_traits::BlockChainClient; +use types::{ + ids::BlockId, + blockchain_info::BlockChainInfo, + errors::{EthcoreError}, + transaction::CallError, +}; use v1::types::BlockNumber; use v1::impls::EthClientOptions; @@ -39,6 +42,7 @@ mod codes { pub const NO_NEW_WORK: i64 = -32003; pub const NO_WORK_REQUIRED: i64 = -32004; pub const CANNOT_SUBMIT_WORK: i64 = -32005; + pub const CANNOT_SUBMIT_BLOCK: i64 = -32006; pub const UNKNOWN_ERROR: i64 = -32009; pub const TRANSACTION_ERROR: i64 = -32010; pub const EXECUTION_ERROR: i64 = -32015; @@ -246,6 +250,14 @@ pub fn unavailable_block(no_ancient_block: bool, by_hash: bool) -> Error { } } +pub fn cannot_submit_block(err: EthcoreError) -> Error { + Error { + code: ErrorCode::ServerError(codes::CANNOT_SUBMIT_BLOCK), + message: "Cannot submit block.".into(), + data: Some(Value::String(err.to_string())), + } +} + pub fn check_block_number_existence<'a, T, C>( client: &'a C, num: BlockNumber, @@ -368,6 +380,16 @@ pub fn invalid_call_data(error: T) -> Error { } } +pub fn signing_queue_disabled() -> Error { + Error { + code: ErrorCode::ServerError(-32020), + message: "Your account is locked and the signing queue is disabled. \ + You can either Unlock the account via CLI, personal_unlockAccount or \ + enable the signing queue to use Trusted Signer.".into(), + data: None, + } +} + #[cfg(any(test, feature = "accounts"))] pub fn signing(error: ::accounts::SignError) -> Error { Error { @@ -443,7 +465,7 @@ pub fn transaction_message(error: &TransactionError) -> String { pub fn transaction>(error: T) -> Error { let error = error.into(); - if let ErrorKind::Transaction(ref e) = *error.kind() { + if let EthcoreError::Transaction(ref e) = error { Error { code: ErrorCode::ServerError(codes::TRANSACTION_ERROR), message: transaction_message(e), @@ -459,9 +481,8 @@ 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()), + match error.into() { + EthcoreError::Decoder(ref dec_err) => rlp(dec_err.clone()), _ => Error { code: ErrorCode::InternalError, message: "decoding error".into(), @@ -550,10 +571,9 @@ pub fn filter_block_not_found(id: BlockId) -> Error { pub fn on_demand_error(err: OnDemandError) -> Error { match err { - OnDemandError(OnDemandErrorKind::ChannelCanceled(e), _) => on_demand_cancel(e), - OnDemandError(OnDemandErrorKind::RequestLimit, _) => timeout_new_peer(&err), - OnDemandError(OnDemandErrorKind::BadResponse(_), _) => max_attempts_reached(&err), - _ => on_demand_others(&err), + OnDemandError::ChannelCanceled(e) => on_demand_cancel(e), + OnDemandError::RequestLimit => timeout_new_peer(&err), + OnDemandError::BadResponse(_) => max_attempts_reached(&err), } } @@ -578,14 +598,6 @@ pub fn timeout_new_peer(err: &OnDemandError) -> Error { } } -pub fn on_demand_others(err: &OnDemandError) -> Error { - Error { - code: ErrorCode::ServerError(codes::UNKNOWN_ERROR), - message: err.to_string(), - data: None, - } -} - pub fn status_error(has_peers: bool) -> Error { if has_peers { no_work() @@ -610,3 +622,13 @@ pub fn require_experimental(allow_experimental_rpcs: bool, eip: &str) -> Result< }) } } + +/// returns an error for when require_canonical was specified and +pub fn invalid_input() -> Error { + Error { + // UNSUPPORTED_REQUEST shares the same error code for EIP-1898 + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "Invalid input".into(), + data: None + } +} diff --git a/rpc/src/v1/helpers/external_signer/mod.rs b/rpc/src/v1/helpers/external_signer/mod.rs index 0797929cbd..4123333503 100644 --- a/rpc/src/v1/helpers/external_signer/mod.rs +++ b/rpc/src/v1/helpers/external_signer/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -30,7 +30,7 @@ pub use self::signing_queue::QueueEvent; pub struct SignerService { is_enabled: bool, queue: Arc, - generate_new_token: Box Result + Send + Sync + 'static>, + generate_new_token: Box Result + Send + Sync + 'static>, } impl SignerService { diff --git a/rpc/src/v1/helpers/external_signer/oneshot.rs b/rpc/src/v1/helpers/external_signer/oneshot.rs index eac3dca7f8..9b93348061 100644 --- a/rpc/src/v1/helpers/external_signer/oneshot.rs +++ b/rpc/src/v1/helpers/external_signer/oneshot.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/external_signer/signing_queue.rs b/rpc/src/v1/helpers/external_signer/signing_queue.rs index 00a459a869..f79549e5b7 100644 --- a/rpc/src/v1/helpers/external_signer/signing_queue.rs +++ b/rpc/src/v1/helpers/external_signer/signing_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -96,7 +96,7 @@ pub type ConfirmationReceiver = oneshot::Receiver; pub struct ConfirmationsQueue { id: Mutex, queue: RwLock>, - on_event: RwLock () + Send + Sync>>>, + on_event: RwLock () + Send + Sync>>>, } impl ConfirmationsQueue { @@ -213,7 +213,7 @@ impl SigningQueue for ConfirmationsQueue { #[cfg(test)] mod test { use std::sync::Arc; - use ethereum_types::{U256, Address}; + use ethereum_types::{U256, Address, H256}; use parking_lot::Mutex; use jsonrpc_core::futures::Future; use v1::helpers::external_signer::{SigningQueue, ConfirmationsQueue, QueueEvent}; @@ -222,9 +222,9 @@ mod test { fn request() -> ConfirmationPayload { ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: Address::from(1), + from: Address::from_low_u64_be(1), used_default_from: false, - to: Some(Address::from(2)), + to: Some(Address::from_low_u64_be(2)), gas_price: 0.into(), gas: 10_000.into(), value: 10_000_000.into(), @@ -243,11 +243,11 @@ mod test { // when let (id, future) = queue.add_request(request, Default::default()).unwrap(); let sender = queue.take(&id).unwrap(); - queue.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(1.into()))); + queue.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(H256::from_low_u64_be(1)))); // then let confirmation = future.wait().unwrap(); - assert_eq!(confirmation, Ok(ConfirmationResponse::SendTransaction(1.into()))); + assert_eq!(confirmation, Ok(ConfirmationResponse::SendTransaction(H256::from_low_u64_be(1)))); } #[test] diff --git a/rpc/src/v1/helpers/fake_sign.rs b/rpc/src/v1/helpers/fake_sign.rs index d93408b89a..13f7b4e796 100644 --- a/rpc/src/v1/helpers/fake_sign.rs +++ b/rpc/src/v1/helpers/fake_sign.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/ipfs.rs b/rpc/src/v1/helpers/ipfs.rs index 93110dbf34..8ecbd731da 100644 --- a/rpc/src/v1/helpers/ipfs.rs +++ b/rpc/src/v1/helpers/ipfs.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/light_fetch.rs b/rpc/src/v1/helpers/light_fetch.rs index 819f566455..365ec6d765 100644 --- a/rpc/src/v1/helpers/light_fetch.rs +++ b/rpc/src/v1/helpers/light_fetch.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,12 +21,14 @@ use std::cmp; use std::collections::BTreeMap; use std::sync::Arc; -use types::basic_account::BasicAccount; -use types::encoded; -use types::filter::Filter as EthcoreFilter; -use types::ids::BlockId; -use types::receipt::Receipt; -use ethcore::executed::ExecutionError; +use types::{ + basic_account::BasicAccount, + encoded, + errors::ExecutionError, + filter::Filter as EthcoreFilter, + ids::BlockId, + receipt::Receipt, +}; use jsonrpc_core::{Result, Error}; use jsonrpc_core::futures::{future, Future}; @@ -37,11 +39,12 @@ use light::client::LightChainClient; use light::{cht, MAX_HEADERS_PER_REQUEST}; use light::on_demand::{ request, OnDemandRequester, HeaderRef, Request as OnDemandRequest, - Response as OnDemandResponse, ExecutionResult, + Response as OnDemandResponse, }; use light::on_demand::error::Error as OnDemandError; use light::request::Field; use light::TransactionQueue; +use machine::executed::ExecutionResult; use sync::{LightNetworkDispatcher, ManageNetwork, LightSyncProvider}; @@ -79,7 +82,7 @@ where OD: OnDemandRequester + 'static { /// The light client. - pub client: Arc, + pub client: Arc, /// The on-demand request service. pub on_demand: Arc, /// Handle to the network. @@ -106,7 +109,6 @@ where } } - /// Extract a transaction at given index. pub fn extract_transaction_at_index(block: encoded::Block, index: usize) -> Option { block.transactions().into_iter().nth(index) @@ -262,6 +264,7 @@ where // (they don't have state) we can safely fallback to `Latest`. let id = match num.unwrap_or_default() { BlockNumber::Num(n) => BlockId::Number(n), + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, BlockNumber::Pending => { @@ -582,7 +585,7 @@ where match maybe_future { Some(recv) => recv, - None => Box::new(future::err(errors::network_disabled())) as Box + Send> + None => Box::new(future::err(errors::network_disabled())) as Box + Send> } } @@ -738,7 +741,7 @@ where tx: EthTransaction, hdr: encoded::Header, env_info: ::vm::EnvInfo, - engine: Arc<::ethcore::engines::EthEngine>, + engine: Arc, on_demand: Arc, sync: Arc, } @@ -803,7 +806,7 @@ where failed => Ok(future::Loop::Break(failed)), } }) - })) as Box + Send> + })) as Box + Send> } else { trace!(target: "light_fetch", "Placing execution request for {} gas in on_demand", params.tx.gas); @@ -824,8 +827,8 @@ where }); match proved_future { - Some(fut) => Box::new(fut) as Box + Send>, - None => Box::new(future::err(errors::network_disabled())) as Box + Send>, + 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 8a25f93056..ac517eab77 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/network_settings.rs b/rpc/src/v1/helpers/network_settings.rs index ed515e471a..1d5ae4a262 100644 --- a/rpc/src/v1/helpers/network_settings.rs +++ b/rpc/src/v1/helpers/network_settings.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/nonce.rs b/rpc/src/v1/helpers/nonce.rs index 25ec89f01b..d82227f912 100644 --- a/rpc/src/v1/helpers/nonce.rs +++ b/rpc/src/v1/helpers/nonce.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/poll_filter.rs b/rpc/src/v1/helpers/poll_filter.rs index 2d7eb95668..a0537794b4 100644 --- a/rpc/src/v1/helpers/poll_filter.rs +++ b/rpc/src/v1/helpers/poll_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/poll_manager.rs b/rpc/src/v1/helpers/poll_manager.rs index a0f1684395..45df1e32dd 100644 --- a/rpc/src/v1/helpers/poll_manager.rs +++ b/rpc/src/v1/helpers/poll_manager.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/requests.rs b/rpc/src/v1/helpers/requests.rs index e71d104449..1d0d920d3d 100644 --- a/rpc/src/v1/helpers/requests.rs +++ b/rpc/src/v1/helpers/requests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/helpers/secretstore.rs b/rpc/src/v1/helpers/secretstore.rs index 6e1cbca45d..d13e015469 100644 --- a/rpc/src/v1/helpers/secretstore.rs +++ b/rpc/src/v1/helpers/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,9 +15,9 @@ // along with Parity Ethereum. If not, see . use std::collections::BTreeSet; -use rand::{Rng, OsRng}; +use rand::{RngCore, rngs::OsRng}; use ethereum_types::{H256, H512}; -use ethkey::{self, Public, Secret, Random, Generator, math}; +use crypto::publickey::{Public, Secret, Random, Generator, ec_math_utils}; use crypto; use bytes::Bytes; use jsonrpc_core::Error; @@ -37,8 +37,11 @@ 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 = ethkey::crypto::ecies::encrypt(&account_public, &crypto::DEFAULT_MAC, document_key.public()) - .map_err(errors::encryption)?; + let encrypted_key = crypto::publickey::ecies::encrypt( + &account_public, + &crypto::DEFAULT_MAC, + document_key.public().as_bytes(), + ).map_err(errors::encryption)?; Ok(EncryptedDocumentKey { common_point: common_point.into(), @@ -87,7 +90,7 @@ pub fn decrypt_document(key: Bytes, mut encrypted_document: Bytes) -> Result, encrypted_document: Bytes) -> Result { let key = decrypt_with_shadow_coefficients(decrypted_secret, common_point, shadows)?; - decrypt_document(key.to_vec(), encrypted_document) + decrypt_document(key.as_bytes().to_vec(), encrypted_document) } /// Calculate Keccak(ordered servers set) @@ -115,7 +118,7 @@ fn into_document_key(key: Bytes) -> Result { fn initialization_vector() -> [u8; INIT_VEC_LEN] { let mut result = [0u8; INIT_VEC_LEN]; - let mut rng = OsRng::new().unwrap(); + let mut rng = OsRng; rng.fill_bytes(&mut result); result } @@ -127,9 +130,9 @@ fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common_sha .map_err(errors::encryption)?; } - math::public_mul_secret(&mut common_shadow_point, &shadow_coefficients_sum) + ec_math_utils::public_mul_secret(&mut common_shadow_point, &shadow_coefficients_sum) .map_err(errors::encryption)?; - math::public_add(&mut decrypted_shadow, &common_shadow_point) + ec_math_utils::public_add(&mut decrypted_shadow, &common_shadow_point) .map_err(errors::encryption)?; Ok(decrypted_shadow) } @@ -142,15 +145,15 @@ fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result<(Public, Pub .map_err(errors::encryption)?; // k * T - let mut common_point = math::generation_point(); - math::public_mul_secret(&mut common_point, key_pair.secret()) + let mut common_point = ec_math_utils::generation_point(); + ec_math_utils::public_mul_secret(&mut common_point, key_pair.secret()) .map_err(errors::encryption)?; // M + k * y let mut encrypted_point = joint_public.clone(); - math::public_mul_secret(&mut encrypted_point, key_pair.secret()) + ec_math_utils::public_mul_secret(&mut encrypted_point, key_pair.secret()) .map_err(errors::encryption)?; - math::public_add(&mut encrypted_point, secret) + ec_math_utils::public_add(&mut encrypted_point, secret) .map_err(errors::encryption)?; Ok((common_point, encrypted_point)) diff --git a/rpc/src/v1/helpers/signature.rs b/rpc/src/v1/helpers/signature.rs index b191a3737e..a271d8e4a8 100644 --- a/rpc/src/v1/helpers/signature.rs +++ b/rpc/src/v1/helpers/signature.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use ethkey::{recover, public_to_address, Signature}; +use crypto::publickey::{recover, public_to_address, Signature}; use ethereum_types::{H256, U64}; use jsonrpc_core::Result; use v1::types::{Bytes, RecoveredAccount}; @@ -54,7 +54,7 @@ pub fn verify_signature( #[cfg(test)] mod tests { use super::*; - use ethkey::Generator; + use crypto::publickey::{Generator, Random}; use ethereum_types::{H160, U64}; pub fn add_chain_replay_protection(v: u64, chain_id: Option) -> u64 { @@ -71,9 +71,9 @@ mod tests { /// mocked signer fn sign(should_prefix: bool, data: Vec, signing_chain_id: Option) -> (H160, [u8; 32], [u8; 32], U64) { let hash = if should_prefix { eth_data_hash(data) } else { keccak(data) }; - let account = ethkey::Random.generate().unwrap(); + let account = Random.generate().unwrap(); let address = account.address(); - let sig = ethkey::sign(account.secret(), &hash).unwrap(); + let sig = crypto::publickey::sign(account.secret(), &hash).unwrap(); let (r, s, v) = (sig.r(), sig.s(), sig.v()); let v = add_chain_replay_protection(v as u64, signing_chain_id); let (r_buf, s_buf) = { diff --git a/rpc/src/v1/helpers/subscribers.rs b/rpc/src/v1/helpers/subscribers.rs index 9483d8e321..a75839c0fb 100644 --- a/rpc/src/v1/helpers/subscribers.rs +++ b/rpc/src/v1/helpers/subscribers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,9 +18,9 @@ use std::{ops, str}; use std::collections::HashMap; + use jsonrpc_pubsub::{typed::{Subscriber, Sink}, SubscriptionId}; use ethereum_types::H64; -use rand::{Rng, StdRng}; #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct Id(H64); @@ -42,34 +42,44 @@ impl Id { } } -#[derive(Clone)] +#[cfg(not(test))] +mod random { + use rand::rngs::OsRng; + + pub type Rng = rand::rngs::OsRng; + + pub fn new() -> Rng { OsRng } +} + +#[cfg(test)] +mod random { + use rand::SeedableRng; + use rand_xorshift::XorShiftRng; + + const RNG_SEED: [u8; 16] = [0u8; 16]; + + pub type Rng = XorShiftRng; + + pub fn new() -> Rng { Rng::from_seed(RNG_SEED) } +} + pub struct Subscribers { - rand: StdRng, + rand: random::Rng, subscriptions: HashMap, } impl Default for Subscribers { fn default() -> Self { Subscribers { - rand: StdRng::new().expect("Valid random source is required."), + rand: random::new(), subscriptions: HashMap::new(), } } } impl Subscribers { - /// Create a new Subscribers with given random source. - #[cfg(test)] - pub fn new_test() -> Self { - Subscribers { - rand: ::rand::SeedableRng::from_seed([0usize].as_ref()), - subscriptions: HashMap::new(), - } - } - fn next_id(&mut self) -> Id { - let mut data = H64::default(); - self.rand.fill_bytes(&mut data.0); + let data = H64::random_using(&mut self.rand); Id(data) } diff --git a/rpc/src/v1/helpers/subscription_manager.rs b/rpc/src/v1/helpers/subscription_manager.rs index d83beb397f..6517e74e4c 100644 --- a/rpc/src/v1/helpers/subscription_manager.rs +++ b/rpc/src/v1/helpers/subscription_manager.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -60,7 +60,7 @@ impl> GenericPollManager { #[cfg(test)] pub fn new_test(rpc: MetaIoHandler) -> Self { let mut manager = Self::new(rpc); - manager.subscribers = Subscribers::new_test(); + manager.subscribers = Subscribers::default(); manager } @@ -87,7 +87,7 @@ impl> GenericPollManager { }).is_some() } - pub fn tick(&self) -> Box + Send> { + pub fn tick(&self) -> Box + Send> { let mut futures = Vec::new(); // poll all subscriptions for (id, subscription) in self.subscribers.iter() { @@ -165,7 +165,7 @@ mod tests { let mut el = Runtime::new().unwrap(); let mut poll_manager = poll_manager(); let (id, rx) = poll_manager.subscribe(Default::default(), "hello".into(), Params::None); - assert_eq!(id, SubscriptionId::String("0x416d77337e24399d".into())); + assert_eq!(id, SubscriptionId::String("0x43ca64edf03768e1".into())); // then poll_manager.tick().wait().unwrap(); diff --git a/rpc/src/v1/helpers/work.rs b/rpc/src/v1/helpers/work.rs index b52cb70c5f..91134934cf 100644 --- a/rpc/src/v1/helpers/work.rs +++ b/rpc/src/v1/helpers/work.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/debug.rs b/rpc/src/v1/impls/debug.rs index e46dd628d1..60853fedea 100644 --- a/rpc/src/v1/impls/debug.rs +++ b/rpc/src/v1/impls/debug.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use std::sync::Arc; -use ethcore::client::BlockChainClient; +use client_traits::BlockChainClient; use types::header::Header; use types::transaction::LocalizedTransaction; diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 734e70a61c..b98d33808e 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,20 +21,28 @@ use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; use std::sync::Arc; use rlp::Rlp; -use ethereum_types::{Address, H64, H160, H256, U64, U256}; +use ethereum_types::{Address, H64, H160, H256, U64, U256, BigEndianHash}; use parking_lot::Mutex; +use account_state::state::StateInfo; +use client_traits::{BlockChainClient, StateClient, ProvingBlockChainClient, StateOrBlock}; use ethash::{self, SeedHashCompute}; -use ethcore::client::{BlockChainClient, BlockId, TransactionId, UncleId, StateOrBlock, StateClient, StateInfo, Call, EngineInfo, ProvingBlockChainClient}; +use ethcore::client::{Call, EngineInfo}; use ethcore::miner::{self, MinerService}; -use ethcore::snapshot::SnapshotService; +use snapshot::SnapshotService; use hash::keccak; use miner::external::ExternalMinerService; use sync::SyncProvider; -use types::transaction::{SignedTransaction, LocalizedTransaction}; -use types::BlockNumber as EthBlockNumber; -use types::encoded; -use types::filter::Filter as EthcoreFilter; +use types::{ + BlockNumber as EthBlockNumber, + client_types::StateResult, + encoded, + header::Header, + ids::{BlockId, TransactionId, UncleId}, + filter::Filter as EthcoreFilter, + transaction::{SignedTransaction, LocalizedTransaction}, + snapshot::RestorationStatus, +}; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_core::futures::future; @@ -42,7 +50,6 @@ use jsonrpc_core::futures::future; use v1::helpers::{self, errors, limit_logs, fake_sign}; use v1::helpers::deprecated::{self, DeprecationNotice}; use v1::helpers::dispatch::{FullDispatcher, default_gas_price}; -use v1::helpers::block_import::is_major_importing; use v1::traits::Eth; use v1::types::{ RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo, @@ -108,7 +115,7 @@ pub struct EthClient where client: Arc, snapshot: Arc, sync: Arc, - accounts: Arc Vec

+ Send + Sync>, + accounts: Arc Vec
+ Send + Sync>, miner: Arc, external_miner: Arc, seed_compute: Mutex, @@ -176,10 +183,11 @@ pub fn base_logs (client: &C, miner: &M, filter: F Box::new(future::ok(logs)) } -impl EthClient where +impl EthClient where C: miner::BlockChainClient + BlockChainClient + StateClient + Call + EngineInfo, SN: SnapshotService, S: SyncProvider, + T: StateInfo + 'static, M: MinerService, EM: ExternalMinerService { @@ -188,7 +196,7 @@ impl EthClient, snapshot: &Arc, sync: &Arc, - accounts: &Arc Vec
+ Send + Sync>, + accounts: &Arc Vec
+ Send + Sync>, miner: &Arc, em: &Arc, options: EthClientOptions @@ -242,6 +250,7 @@ impl EthClient { let id = match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Latest => BlockId::Latest, BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Num(n) => BlockId::Number(n), @@ -432,23 +441,49 @@ impl EthClient StateOrBlock { match number { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash).into(), BlockNumber::Num(num) => BlockId::Number(num).into(), BlockNumber::Earliest => BlockId::Earliest.into(), BlockNumber::Latest => BlockId::Latest.into(), - BlockNumber::Pending => { let info = self.client.chain_info(); self.miner .pending_state(info.best_block_number) - .map(|s| Box::new(s) as Box) - .unwrap_or(Box::new(self.client.latest_state()) as Box) + .map(|s| Box::new(s) as Box) + .unwrap_or_else(|| { + warn!("Asked for best pending state, but none found. Falling back to latest state"); + let (state, _) = self.client.latest_state_and_header(); + Box::new(state) as Box + }) .into() } } } + + /// Get the state and header of best pending block. On failure, fall back to the best imported + /// blocks state&header. + fn pending_state_and_header_with_fallback(&self) -> (T, Header) { + let best_block_number = self.client.chain_info().best_block_number; + let (maybe_state, maybe_header) = + self.miner.pending_state(best_block_number).map_or_else(|| (None, None),|s| { + (Some(s), self.miner.pending_block_header(best_block_number)) + }); + + match (maybe_state, maybe_header) { + (Some(state), Some(header)) => (state, header), + _ => { + warn!("Falling back to \"Latest\""); + self.client.latest_state_and_header() + } + } + } } pub fn pending_logs(miner: &M, best_block: EthBlockNumber, filter: &EthcoreFilter) -> Vec where M: MinerService { @@ -473,10 +508,22 @@ fn check_known(client: &C, number: BlockNumber) -> Result<()> where C: BlockC let id = match number { BlockNumber::Pending => return Ok(()), - BlockNumber::Num(n) => BlockId::Number(n), BlockNumber::Latest => BlockId::Latest, BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Hash { hash, require_canonical } => { + // block check takes precedence over canon check. + match client.block_status(BlockId::Hash(hash.clone())) { + BlockStatus::InChain => {}, + _ => return Err(errors::unknown_block()), + }; + + if require_canonical && !client.chain().is_canon(&hash) { + return Err(errors::invalid_input()) + } + + return Ok(()) + } }; match client.block_status(id) { @@ -502,8 +549,6 @@ impl Eth for EthClient< } fn syncing(&self) -> Result { - use ethcore::snapshot::RestorationStatus; - let status = self.sync.status(); let client = &self.client; let snapshot_status = self.snapshot.status(); @@ -514,7 +559,7 @@ impl Eth for EthClient< _ => (false, None, None), }; - if warping || is_major_importing(Some(status.state), client.queue_info()) { + if warping || self.sync.is_major_syncing() { let chain_info = client.chain_info(); let current_block = U256::from(chain_info.best_block_number); let highest_block = U256::from(status.highest_block_number.unwrap_or(status.start_block_number)); @@ -534,7 +579,7 @@ impl Eth for EthClient< fn author(&self) -> Result { let miner = self.miner.authoring_params().author; - if miner == 0.into() { + if miner.is_zero() { (self.accounts)() .first() .cloned() @@ -590,6 +635,7 @@ impl Eth for EthClient< let num = num.unwrap_or_default(); let id = match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(n) => BlockId::Number(n), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, @@ -612,8 +658,8 @@ impl Eth for EthClient< let key2: H256 = storage_index; self.client.prove_storage(key1, keccak(key2), id) .map(|(storage_proof, storage_value)| StorageProof { - key: key2.into(), - value: storage_value.into(), + key: key2.into_uint(), + value: storage_value.into_uint(), proof: storage_proof.into_iter().map(Bytes::new).collect() }) }) @@ -629,12 +675,13 @@ impl Eth for EthClient< let num = num.unwrap_or_default(); try_bf!(check_known(&*self.client, num.clone())); - let res = match self.client.storage_at(&address, &H256::from(position), self.get_state(num)) { - Some(s) => Ok(s), - None => Err(errors::state_pruned()), - }; + let storage = self.client.storage_at( + &address, + &BigEndianHash::from_uint(&position), + self.get_state(num) + ).ok_or_else(errors::state_pruned); - Box::new(future::done(res)) + Box::new(future::done(storage)) } fn transaction_count(&self, address: H160, num: Option) -> BoxFuture { @@ -725,8 +772,8 @@ impl Eth for EthClient< try_bf!(check_known(&*self.client, num.clone())); let res = match self.client.code(&address, self.get_state(num)) { - Some(code) => Ok(code.map_or_else(Bytes::default, Bytes::new)), - None => Err(errors::state_pruned()), + StateResult::Some(code) => Ok(code.map_or_else(Bytes::default, Bytes::new)), + StateResult::Missing => Err(errors::state_pruned()), }; Box::new(future::done(res)) @@ -763,6 +810,7 @@ impl Eth for EthClient< fn transaction_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture> { let block_id = match num { + BlockNumber::Hash { hash, .. } => PendingOrBlock::Block(BlockId::Hash(hash)), BlockNumber::Latest => PendingOrBlock::Block(BlockId::Latest), BlockNumber::Earliest => PendingOrBlock::Block(BlockId::Earliest), BlockNumber::Num(num) => PendingOrBlock::Block(BlockId::Number(num)), @@ -799,6 +847,7 @@ impl Eth for EthClient< fn uncle_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture> { let id = match num { + BlockNumber::Hash { hash, .. } => PendingUncleId { id: PendingOrBlock::Block(BlockId::Hash(hash)), position: index.value() }, BlockNumber::Latest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Latest), position: index.value() }, BlockNumber::Earliest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Earliest), position: index.value() }, BlockNumber::Num(num) => PendingUncleId { id: PendingOrBlock::Block(BlockId::Number(num)), position: index.value() }, @@ -915,26 +964,28 @@ impl Eth for EthClient< let signed = try_bf!(fake_sign::sign_call(request)); let num = num.unwrap_or_default(); + try_bf!(check_known(&*self.client, num.clone())); - let (mut state, header) = if num == BlockNumber::Pending { - let info = self.client.chain_info(); - let state = try_bf!(self.miner.pending_state(info.best_block_number).ok_or_else(errors::state_pruned)); - let header = try_bf!(self.miner.pending_block_header(info.best_block_number).ok_or_else(errors::state_pruned)); - - (state, header) - } else { - let id = match num { - BlockNumber::Num(num) => BlockId::Number(num), - BlockNumber::Earliest => BlockId::Earliest, - BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => unreachable!(), // Already covered - }; + let (mut state, header) = + if num == BlockNumber::Pending { + self.pending_state_and_header_with_fallback() + } else { + let id = match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), + BlockNumber::Num(num) => BlockId::Number(num), + BlockNumber::Earliest => BlockId::Earliest, + BlockNumber::Latest => BlockId::Latest, + BlockNumber::Pending => unreachable!(), // Already covered + }; - let state = try_bf!(self.client.state_at(id).ok_or_else(errors::state_pruned)); - let header = try_bf!(self.client.block_header(id).ok_or_else(errors::state_pruned).and_then(|h| h.decode().map_err(errors::decode))); + let state = try_bf!(self.client.state_at(id).ok_or_else(errors::state_pruned)); + let header = try_bf!( + self.client.block_header(id).ok_or_else(errors::state_pruned) + .and_then(|h| h.decode().map_err(errors::decode)) + ); - (state, header) - }; + (state, header) + }; let result = self.client.call(&signed, Default::default(), &mut state, &header); @@ -956,15 +1007,10 @@ impl Eth for EthClient< let num = num.unwrap_or_default(); let (state, header) = if num == BlockNumber::Pending { - let info = self.client.chain_info(); - let state = try_bf!(self.miner.pending_state(info.best_block_number) - .ok_or_else(errors::state_pruned)); - let header = try_bf!(self.miner.pending_block_header(info.best_block_number) - .ok_or_else(errors::state_pruned)); - - (state, header) + self.pending_state_and_header_with_fallback() } else { let id = match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, diff --git a/rpc/src/v1/impls/eth_filter.rs b/rpc/src/v1/impls/eth_filter.rs index c51c85fb62..bf5decabd7 100644 --- a/rpc/src/v1/impls/eth_filter.rs +++ b/rpc/src/v1/impls/eth_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,11 +19,14 @@ use std::sync::Arc; use std::collections::{BTreeSet, VecDeque}; -use ethcore::client::{BlockChainClient, BlockId}; +use client_traits::BlockChainClient; use ethcore::miner::{self, MinerService}; use ethereum_types::{H256, U256}; use parking_lot::Mutex; -use types::filter::Filter as EthcoreFilter; +use types::{ + ids::BlockId, + filter::Filter as EthcoreFilter +}; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_core::futures::{future, Future}; diff --git a/rpc/src/v1/impls/eth_pubsub.rs b/rpc/src/v1/impls/eth_pubsub.rs index 4507281578..645d53c348 100644 --- a/rpc/src/v1/impls/eth_pubsub.rs +++ b/rpc/src/v1/impls/eth_pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,16 +20,18 @@ use std::sync::{Arc, Weak}; use std::collections::BTreeMap; use jsonrpc_core::{BoxFuture, Result, Error}; -use jsonrpc_core::futures::{self, Future, IntoFuture}; -use jsonrpc_pubsub::{SubscriptionId, typed::{Sink, Subscriber}}; +use jsonrpc_core::futures::{self, Future, IntoFuture, Stream, sync::mpsc}; +use jsonrpc_pubsub::typed::{Sink, Subscriber}; +use jsonrpc_pubsub::SubscriptionId; -use v1::helpers::{errors, limit_logs, Subscribers}; +use v1::helpers::{errors, limit_logs, Subscribers, }; use v1::helpers::light_fetch::LightFetch; use v1::metadata::Metadata; use v1::traits::EthPubSub; use v1::types::{pubsub, RichHeader, Log}; -use ethcore::client::{BlockChainClient, ChainNotify, NewBlocks, ChainRouteType, BlockId}; +use sync::{SyncState, Notification}; +use client_traits::{BlockChainClient, ChainNotify}; use ethereum_types::H256; use light::cache::Cache; use light::client::{LightChainClient, LightChainNotify}; @@ -39,8 +41,12 @@ use parking_lot::{RwLock, Mutex}; use sync::{LightSyncProvider, LightNetworkDispatcher, ManageNetwork}; -use types::encoded; -use types::filter::Filter as EthFilter; +use types::{ + chain_notify::{NewBlocks, ChainRouteType}, + ids::BlockId, + encoded, + filter::Filter as EthFilter, +}; type Client = Sink; @@ -50,39 +56,74 @@ pub struct EthPubSubClient { heads_subscribers: Arc>>, logs_subscribers: Arc>>, transactions_subscribers: Arc>>, + sync_subscribers: Arc>>, } -impl EthPubSubClient { +impl EthPubSubClient + where + C: 'static + Send + Sync +{ + /// adds a sync notification channel to the pubsub client + pub fn add_sync_notifier(&mut self, receiver: Notification, f: F) + where + F: 'static + Fn(SyncState) -> Option + Send + { + let weak_handler = Arc::downgrade(&self.handler); + + self.handler.executor.spawn( + receiver.for_each(move |state| { + if let Some(status) = f(state) { + if let Some(handler) = weak_handler.upgrade() { + handler.notify_syncing(status); + return Ok(()) + } + } + Err(()) + }) + ) + } +} + +impl EthPubSubClient + where + C: 'static + Send + Sync { + /// Creates new `EthPubSubClient`. - pub fn new(client: Arc, executor: Executor) -> Self { + pub fn new(client: Arc, executor: Executor, pool_receiver: mpsc::UnboundedReceiver>>) -> Self { let heads_subscribers = Arc::new(RwLock::new(Subscribers::default())); let logs_subscribers = Arc::new(RwLock::new(Subscribers::default())); let transactions_subscribers = Arc::new(RwLock::new(Subscribers::default())); + let sync_subscribers = Arc::new(RwLock::new(Subscribers::default())); + + let handler = Arc::new(ChainNotificationHandler { + client, + executor, + heads_subscribers: heads_subscribers.clone(), + logs_subscribers: logs_subscribers.clone(), + transactions_subscribers: transactions_subscribers.clone(), + sync_subscribers: sync_subscribers.clone(), + }); + let handler2 = Arc::downgrade(&handler); + + handler.executor.spawn(pool_receiver + .for_each(move |hashes| { + if let Some(handler2) = handler2.upgrade() { + handler2.notify_new_transactions(&hashes.to_vec()); + return Ok(()) + } + Err(()) + }) + ); EthPubSubClient { - handler: Arc::new(ChainNotificationHandler { - client, - executor, - heads_subscribers: heads_subscribers.clone(), - logs_subscribers: logs_subscribers.clone(), - transactions_subscribers: transactions_subscribers.clone(), - }), + handler, + sync_subscribers, heads_subscribers, logs_subscribers, transactions_subscribers, } } - /// Creates new `EthPubSubCient` with deterministic subscription ids. - #[cfg(test)] - pub fn new_test(client: Arc, executor: Executor) -> Self { - let client = Self::new(client, executor); - *client.heads_subscribers.write() = Subscribers::new_test(); - *client.logs_subscribers.write() = Subscribers::new_test(); - *client.transactions_subscribers.write() = Subscribers::new_test(); - client - } - /// Returns a chain notification handler. pub fn handler(&self) -> Weak> { Arc::downgrade(&self.handler) @@ -96,12 +137,13 @@ where { /// Creates a new `EthPubSubClient` for `LightClient`. pub fn light( - client: Arc, + client: Arc, on_demand: Arc, sync: Arc, cache: Arc>, executor: Executor, gas_price_percentile: usize, + pool_receiver: mpsc::UnboundedReceiver>> ) -> Self { let fetch = LightFetch { client, @@ -110,7 +152,7 @@ where cache, gas_price_percentile, }; - EthPubSubClient::new(Arc::new(fetch), executor) + EthPubSubClient::new(Arc::new(fetch), executor, pool_receiver) } } @@ -121,6 +163,7 @@ pub struct ChainNotificationHandler { heads_subscribers: Arc>>, logs_subscribers: Arc>>, transactions_subscribers: Arc>>, + sync_subscribers: Arc>>, } impl ChainNotificationHandler { @@ -143,6 +186,12 @@ impl ChainNotificationHandler { } } + fn notify_syncing(&self, sync_status: pubsub::PubSubSyncStatus) { + for subscriber in self.sync_subscribers.read().values() { + Self::notify(&self.executor, subscriber, pubsub::Result::SyncState(sync_status.clone())); + } + } + fn notify_logs(&self, enacted: &[(H256, Ex)], logs: F) where F: Fn(EthFilter, &Ex) -> T, Ex: Send, @@ -177,7 +226,7 @@ impl ChainNotificationHandler { } /// Notify all subscribers about new transaction hashes. - pub fn notify_new_transactions(&self, hashes: &[H256]) { + fn notify_new_transactions(&self, hashes: &[H256]) { for subscriber in self.transactions_subscribers.read().values() { for hash in hashes { Self::notify(&self.executor, subscriber, pubsub::Result::TransactionHash(*hash)); @@ -274,6 +323,10 @@ impl EthPubSub for EthPubSubClient { self.heads_subscribers.write().push(subscriber); return; }, + (pubsub::Kind::Syncing, None) => { + self.sync_subscribers.write().push(subscriber); + return; + }, (pubsub::Kind::NewHeads, _) => { errors::invalid_params("newHeads", "Expected no parameters.") }, @@ -308,7 +361,8 @@ impl EthPubSub for EthPubSubClient { let res = self.heads_subscribers.write().remove(&id).is_some(); let res2 = self.logs_subscribers.write().remove(&id).is_some(); let res3 = self.transactions_subscribers.write().remove(&id).is_some(); + let res4 = self.sync_subscribers.write().remove(&id).is_some(); - Ok(res || res2 || res3) + Ok(res || res2 || res3 || res4) } } diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index 6467bfbc78..25b3c114f9 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -58,7 +58,7 @@ pub struct EthClient, on_demand: Arc, transaction_queue: Arc>, - accounts: Arc Vec
+ Send + Sync>, + accounts: Arc Vec
+ Send + Sync>, cache: Arc>, polls: Mutex>, poll_lifetime: u32, @@ -101,7 +101,7 @@ where client: Arc, on_demand: Arc, transaction_queue: Arc>, - accounts: Arc Vec
+ Send + Sync>, + accounts: Arc Vec
+ Send + Sync>, cache: Arc>, gas_price_percentile: usize, poll_lifetime: u32 diff --git a/rpc/src/v1/impls/light/mod.rs b/rpc/src/v1/impls/light/mod.rs index c159514582..9359e305bf 100644 --- a/rpc/src/v1/impls/light/mod.rs +++ b/rpc/src/v1/impls/light/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/light/net.rs b/rpc/src/v1/impls/light/net.rs index a9ab012e5a..6f4f2cfdc5 100644 --- a/rpc/src/v1/impls/light/net.rs +++ b/rpc/src/v1/impls/light/net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 0486366de6..9a01a7a3b5 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,11 +21,13 @@ use std::collections::BTreeMap; use version::version_data; use crypto::DEFAULT_MAC; -use ethkey::{crypto::ecies, Brain, Generator}; +use ethkey::Brain; +use crypto::publickey::{Generator, ecies}; use ethstore::random_phrase; use sync::{LightSyncInfo, LightSyncProvider, LightNetworkDispatcher, ManageNetwork}; use updater::VersionInfo as UpdaterVersionInfo; use ethereum_types::{H64, H160, H256, H512, U64, U256}; +use ethcore::miner::FilterOptions; use ethcore_logger::RotatingLogger; use jsonrpc_core::{Result, BoxFuture}; @@ -47,6 +49,8 @@ use v1::types::{ Log, Filter, }; use Host; +use v1::helpers::errors::light_unimplemented; +use v1::types::block_number_to_id; /// Parity implementation for light client. pub struct ParityClient @@ -160,12 +164,7 @@ where } fn registry_address(&self) -> Result> { - let reg = self.light_dispatch.client.engine().params().registrar; - if reg == Default::default() { - Ok(None) - } else { - Ok(Some(reg)) - } + Ok(self.light_dispatch.client.engine().params().registrar) } fn rpc_settings(&self) -> Result { @@ -205,7 +204,7 @@ where Err(errors::light_unimplemented(None)) } - fn list_storage_keys(&self, _: H160, _: u64, _: Option, _: Option) -> Result>> { + fn list_storage_keys(&self, _: H160, _: Option, _: Option, _: Option) -> Result>> { Err(errors::light_unimplemented(None)) } @@ -215,7 +214,7 @@ where .map(Into::into) } - fn pending_transactions(&self, limit: Option) -> Result> { + fn pending_transactions(&self, limit: Option, _filter: Option) -> Result> { let txq = self.light_dispatch.transaction_queue.read(); let chain_info = self.light_dispatch.client.chain_info(); Ok( @@ -407,4 +406,16 @@ where fn verify_signature(&self, is_prefixed: bool, message: Bytes, r: H256, s: H256, v: U64) -> Result { verify_signature(is_prefixed, message, r, s, v, self.light_dispatch.client.signing_chain_id()) } + + fn get_raw_block_by_number(&self, block: BlockNumber) -> BoxFuture> { + Box::new( + self.fetcher() + .block(block_number_to_id(block)) + .map(|block| Some(Bytes::from(block.raw().to_vec()))) + ) + } + + fn submit_raw_block(&self, _block: Bytes) -> Result { + Err(light_unimplemented(None)) + } } diff --git a/rpc/src/v1/impls/light/parity_set.rs b/rpc/src/v1/impls/light/parity_set.rs index 68fc212b2f..acd9d17ef6 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -34,14 +34,14 @@ use v1::types::{Bytes, ReleaseInfo, Transaction}; /// Parity-specific rpc interface for operations altering the settings. pub struct ParitySetClient { - client: Arc, - net: Arc, + client: Arc, + net: Arc, fetch: F, } impl ParitySetClient { /// Creates new `ParitySetClient` with given `Fetch`. - pub fn new(client: Arc, net: Arc, fetch: F) -> Self { + pub fn new(client: Arc, net: Arc, fetch: F) -> Self { ParitySetClient { client, net, @@ -75,6 +75,10 @@ impl ParitySet for ParitySetClient { Err(errors::light_unimplemented(None)) } + fn clear_engine_signer(&self) -> Result { + Err(errors::light_unimplemented(None)) + } + fn set_transactions_limit(&self, _limit: usize) -> Result { Err(errors::light_unimplemented(None)) } diff --git a/rpc/src/v1/impls/light/trace.rs b/rpc/src/v1/impls/light/trace.rs index a560f980e7..9ab438eeb9 100644 --- a/rpc/src/v1/impls/light/trace.rs +++ b/rpc/src/v1/impls/light/trace.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index ba1cc100e8..a83f29760a 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -36,6 +36,7 @@ mod signer; mod signing; mod signing_unsafe; mod traces; +mod transactions_pool; mod web3; pub mod light; @@ -44,6 +45,7 @@ pub use self::debug::DebugClient; pub use self::eth::{EthClient, EthClientOptions}; pub use self::eth_filter::EthFilterClient; pub use self::eth_pubsub::EthPubSubClient; +pub use self::transactions_pool::TransactionsPoolClient; pub use self::net::NetClient; pub use self::parity::ParityClient; #[cfg(any(test, feature = "accounts"))] diff --git a/rpc/src/v1/impls/net.rs b/rpc/src/v1/impls/net.rs index 188d67cd5a..4f3d7f7bd3 100644 --- a/rpc/src/v1/impls/net.rs +++ b/rpc/src/v1/impls/net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 27a7037958..acf28b092d 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,26 +16,30 @@ //! Parity-specific rpc implementation. use std::sync::Arc; -use std::str::FromStr; use std::collections::BTreeMap; use crypto::DEFAULT_MAC; -use ethereum_types::{Address, H64, H160, H256, H512, U64, U256}; -use ethcore::client::{BlockChainClient, StateClient, Call}; -use ethcore::miner::{self, MinerService}; -use ethcore::snapshot::{SnapshotService, RestorationStatus}; -use ethcore::state::StateInfo; +use ethereum_types::{H64, H160, H256, H512, U64, U256}; +use ethcore::client::Call; +use client_traits::{BlockChainClient, StateClient}; +use ethcore::miner::{self, MinerService, FilterOptions}; +use snapshot::SnapshotService; +use account_state::state::StateInfo; use ethcore_logger::RotatingLogger; -use ethkey::{crypto::ecies, Brain, Generator}; +use ethkey::Brain; +use crypto::publickey::{ecies, Generator}; use ethstore::random_phrase; use jsonrpc_core::futures::future; use jsonrpc_core::{BoxFuture, Result}; use sync::{SyncProvider, ManageNetwork}; -use types::ids::BlockId; +use types::{ + ids::BlockId, + verification::Unverified, + snapshot::RestorationStatus, +}; use updater::{Service as UpdateService}; use version::version_data; -use v1::helpers::block_import::is_major_importing; use v1::helpers::{self, errors, fake_sign, ipfs, NetworkSettings, verify_signature}; use v1::helpers::external_signer::{SigningQueue, SignerService}; use v1::metadata::Metadata; @@ -56,13 +60,13 @@ pub struct ParityClient { client: Arc, miner: Arc, updater: Arc, - sync: Arc, - net: Arc, + sync: Arc, + net: Arc, logger: Arc, settings: Arc, signer: Option>, ws_address: Option, - snapshot: Option>, + snapshot: Option>, } impl ParityClient where @@ -72,14 +76,14 @@ impl ParityClient where pub fn new( client: Arc, miner: Arc, - sync: Arc, + sync: Arc, updater: Arc, - net: Arc, + net: Arc, logger: Arc, settings: Arc, signer: Option>, ws_address: Option, - snapshot: Option>, + snapshot: Option>, ) -> Self { ParityClient { client, @@ -165,12 +169,7 @@ impl Parity for ParityClient where } fn registry_address(&self) -> Result> { - Ok( - self.client - .additional_params() - .get("registrar") - .and_then(|s| Address::from_str(s).ok()) - ) + Ok(self.client.registrar_address()) } fn rpc_settings(&self) -> Result { @@ -224,7 +223,7 @@ impl Parity for ParityClient where .map(|a| a.into_iter().map(Into::into).collect())) } - fn list_storage_keys(&self, address: H160, count: u64, after: Option, block_number: Option) -> Result>> { + fn list_storage_keys(&self, address: H160, count: Option, after: Option, block_number: Option) -> Result>> { let number = match block_number.unwrap_or_default() { BlockNumber::Pending => { warn!("BlockNumber::Pending is unsupported"); @@ -245,10 +244,11 @@ impl Parity for ParityClient where .map(Into::into) } - fn pending_transactions(&self, limit: Option) -> Result> { - let ready_transactions = self.miner.ready_transactions( + fn pending_transactions(&self, limit: Option, filter: Option) -> Result> { + let ready_transactions = self.miner.ready_transactions_filtered( &*self.client, limit.unwrap_or_else(usize::max_value), + filter, miner::PendingOrdering::Priority, ); @@ -355,6 +355,7 @@ impl Parity for ParityClient where (header.encoded(), None) } else { let id = match number { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, @@ -386,6 +387,7 @@ impl Parity for ParityClient where .collect() )) }, + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, @@ -417,6 +419,7 @@ impl Parity for ParityClient where (state, header) } else { let id = match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, @@ -445,8 +448,7 @@ impl Parity for ParityClient where _ => false, }; let is_not_syncing = - !is_warping && - !is_major_importing(Some(self.sync.status().state), self.client.queue_info()); + !is_warping && !self.sync.is_major_syncing(); if has_peers && is_not_syncing { Ok(()) @@ -464,4 +466,22 @@ impl Parity for ParityClient where fn verify_signature(&self, is_prefixed: bool, message: Bytes, r: H256, s: H256, v: U64) -> Result { verify_signature(is_prefixed, message, r, s, v, self.client.signing_chain_id()) } + + fn get_raw_block_by_number(&self, block_number: BlockNumber) -> BoxFuture> { + Box::new(futures::done( + Ok( + self.client + .block(block_number_to_id(block_number)) + .map(|block| Bytes::from(block.raw().to_vec())) + ) + )) + } + + + fn submit_raw_block(&self, block: Bytes) -> Result { + let result = self.client.import_block( + Unverified::from_rlp(block.into_vec()).map_err(errors::rlp)? + ); + Ok(result.map_err(errors::cannot_submit_block)?) + } } diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index e52f8b7ac0..e1e0f33ac7 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,15 +22,15 @@ use std::collections::{ }; use ethereum_types::{Address, H160, H256, H520}; -use ethkey::{Brain, Generator, Secret}; +use ethkey::{Brain, Password}; +use crypto::publickey::{Generator, Secret}; use ethstore::KeyFile; use accounts::AccountProvider; use jsonrpc_core::Result; use v1::helpers::deprecated::{self, DeprecationNotice}; use v1::helpers::errors; use v1::traits::{ParityAccounts, ParityAccountsInfo}; -use v1::types::{Derive, DeriveHierarchical, DeriveHash,ExtAccountInfo, AccountInfo, HwAccountInfo}; -use ethkey::Password; +use v1::types::{Derive, DeriveHierarchical, DeriveHash, ExtAccountInfo, AccountInfo}; /// Account management (personal) rpc implementation. pub struct ParityAccountsClient { @@ -74,23 +74,6 @@ impl ParityAccountsInfo for ParityAccountsClient { ) } - fn hardware_accounts_info(&self) -> Result> { - self.deprecation_notice("parity_hardwareAccountsInfo"); - - 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 })) - .collect() - ) - } - - fn locked_hardware_accounts_info(&self) -> Result> { - self.deprecation_notice("parity_lockedHardwareAccountsInfo"); - - self.accounts.locked_hardware_accounts().map_err(|e| errors::account("Error communicating with hardware wallet.", e)) - } - fn default_account(&self) -> Result { self.deprecation_notice("parity_defaultAccount"); @@ -151,7 +134,7 @@ impl ParityAccounts for ParityAccountsClient { fn new_account_from_secret(&self, secret: H256, pass: Password) -> Result { self.deprecation_notice("parity_newAccountFromSecret"); - let secret = Secret::from_unsafe_slice(&secret.0) + let secret = Secret::import_key(&secret.0) .map_err(|e| errors::account("Could not create account.", e))?; self.accounts.insert_account(secret, &pass) .map(Into::into) @@ -352,12 +335,6 @@ impl ParityAccounts for ParityAccountsClient { .map(Into::into) .map_err(|e| errors::account("Could not sign message.", e)) } - - fn hardware_pin_matrix_ack(&self, path: String, pin: String) -> Result { - self.deprecation_notice("parity_hardwarePinMatrixAck"); - - self.accounts.hardware_pin_matrix_ack(&path, &pin).map_err(|e| errors::account("Error communicating with hardware wallet.", e)) - } } fn into_vec(a: Vec) -> Vec where diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs index b7cef6c6b8..dafb46b85e 100644 --- a/rpc/src/v1/impls/parity_set.rs +++ b/rpc/src/v1/impls/parity_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,10 +19,11 @@ use std::io; use std::sync::Arc; use std::time::Duration; -use ethcore::client::{BlockChainClient, Mode}; +use client_traits::BlockChainClient; +use types::client_types::Mode; use ethcore::miner::{self, MinerService}; use ethereum_types::{H160, H256, U256}; -use ethkey; +use crypto::publickey::KeyPair; use fetch::{self, Fetch}; use hash::keccak_buffer; use sync::ManageNetwork; @@ -86,7 +87,7 @@ pub struct ParitySetClient { client: Arc, miner: Arc, updater: Arc, - net: Arc, + net: Arc, fetch: F, } @@ -98,7 +99,7 @@ impl ParitySetClient client: &Arc, miner: &Arc, updater: &Arc, - net: &Arc, + net: &Arc, fetch: F, ) -> Self { ParitySetClient { @@ -160,8 +161,13 @@ impl ParitySet for ParitySetClient where } fn set_engine_signer_secret(&self, secret: H256) -> Result { - let keypair = ethkey::KeyPair::from_secret(secret.into()).map_err(|e| errors::account("Invalid secret", e))?; - self.miner.set_author(miner::Author::Sealer(ethcore::engines::signer::from_keypair(keypair))); + let keypair = KeyPair::from_secret(secret.into()).map_err(|e| errors::account("Invalid secret", e))?; + self.miner.set_author(miner::Author::Sealer(engine::signer::from_keypair(keypair))); + Ok(true) + } + + fn clear_engine_signer(&self) -> Result { + self.miner.set_author(None); Ok(true) } diff --git a/rpc/src/v1/impls/personal.rs b/rpc/src/v1/impls/personal.rs index b6af1f81e5..fc74448f26 100644 --- a/rpc/src/v1/impls/personal.rs +++ b/rpc/src/v1/impls/personal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use accounts::AccountProvider; use bytes::Bytes; use eip_712::{EIP712, hash_structured_data}; use ethereum_types::{H160, H256, H520, U128, Address}; -use ethkey::{public_to_address, recover, Signature}; +use crypto::publickey::{public_to_address, recover, Signature}; use types::transaction::{PendingTransaction, SignedTransaction}; use jsonrpc_core::futures::{future, Future}; @@ -223,7 +223,7 @@ impl Personal for PersonalClient { fn ec_recover(&self, data: RpcBytes, signature: H520) -> BoxFuture { let signature: H520 = signature.into(); - let signature = Signature::from_electrum(&signature); + let signature = Signature::from_electrum(signature.as_bytes()); let data: Bytes = data.into(); let hash = eth_data_hash(data); diff --git a/rpc/src/v1/impls/private.rs b/rpc/src/v1/impls/private.rs index c3be3f9150..f0becfc1c9 100644 --- a/rpc/src/v1/impls/private.rs +++ b/rpc/src/v1/impls/private.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -26,7 +26,8 @@ use types::transaction::SignedTransaction; use jsonrpc_core::{Error}; use v1::types::{Bytes, PrivateTransactionReceipt, TransactionRequest, - BlockNumber, PrivateTransactionReceiptAndTransaction, CallRequest, block_number_to_id}; + BlockNumber, PrivateTransactionReceiptAndTransaction, CallRequest, + block_number_to_id, PrivateTransactionLog}; use v1::traits::Private; use v1::metadata::Metadata; use v1::helpers::{errors, fake_sign}; @@ -119,4 +120,11 @@ impl Private for PrivateClient { let key = client.contract_key_id(&contract_address).map_err(errors::private_message)?; Ok(key) } + + fn private_log(&self, tx_hash: H256) -> Result { + self.unwrap_manager()? + .private_log(tx_hash) + .map_err(errors::private_message) + .map(Into::into) + } } diff --git a/rpc/src/v1/impls/pubsub.rs b/rpc/src/v1/impls/pubsub.rs index 1575aacdd6..46a626ab20 100644 --- a/rpc/src/v1/impls/pubsub.rs +++ b/rpc/src/v1/impls/pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/rpc.rs b/rpc/src/v1/impls/rpc.rs index 0c2afd57ca..e7a6d74fe4 100644 --- a/rpc/src/v1/impls/rpc.rs +++ b/rpc/src/v1/impls/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/impls/secretstore.rs b/rpc/src/v1/impls/secretstore.rs index b6526b85d5..c09a5e4425 100644 --- a/rpc/src/v1/impls/secretstore.rs +++ b/rpc/src/v1/impls/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use std::sync::Arc; use accounts::AccountProvider; use crypto::DEFAULT_MAC; use ethereum_types::{H160, H256, H512}; -use ethkey::Secret; +use crypto::publickey::Secret; use jsonrpc_core::Result; use v1::helpers::errors; @@ -54,7 +54,7 @@ impl SecretStoreClient { /// Decrypt secret key using account' private key fn decrypt_secret(&self, address: H160, password: Password, key: Bytes) -> Result { self.decrypt_key(address, password, key) - .and_then(|s| Secret::from_unsafe_slice(&s).map_err(|e| errors::account("invalid secret", e))) + .and_then(|s| Secret::import_key(&s).map_err(|e| errors::account("invalid secret", e))) } } diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index 4edac1144a..8dd4da1af0 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,8 +18,7 @@ use std::sync::Arc; -use ethereum_types::U256; -use ethkey; +use ethereum_types::{U256, H520}; use parity_runtime::Executor; use parking_lot::Mutex; use rlp::Rlp; @@ -40,7 +39,7 @@ use v1::types::{TransactionModification, ConfirmationRequest, ConfirmationRespon /// Transactions confirmation (personal) rpc implementation. pub struct SignerClient { signer: Arc, - accounts: Arc, + accounts: Arc, dispatcher: D, subscribers: Arc>>>>, deprecation_notice: DeprecationNotice, @@ -49,7 +48,7 @@ pub struct SignerClient { impl SignerClient { /// Create new instance of signer client. pub fn new( - accounts: Arc, + accounts: Arc, dispatcher: D, signer: &Arc, executor: Executor, @@ -81,7 +80,7 @@ impl SignerClient { } fn confirm_internal(&self, id: U256, modification: TransactionModification, f: F) -> BoxFuture> where - F: FnOnce(D, &Arc, ConfirmationPayload) -> T, + F: FnOnce(D, &Arc, ConfirmationPayload) -> T, T: IntoFuture, Error=Error>, T::Future: Send + 'static { @@ -216,17 +215,17 @@ impl Signer for SignerClient { }, ConfirmationPayload::EthSignMessage(address, data) => { let expected_hash = eth_data_hash(data); - let signature = ethkey::Signature::from_electrum(&bytes.0); - match ethkey::verify_address(&address, &signature, &expected_hash) { - Ok(true) => Ok(ConfirmationResponse::Signature(bytes.0.as_slice().into())), + let signature = crypto::publickey::Signature::from_electrum(&bytes.0); + match crypto::publickey::verify_address(&address, &signature, &expected_hash) { + Ok(true) => Ok(ConfirmationResponse::Signature(H520::from_slice(bytes.0.as_slice()))), Ok(false) => Err(errors::invalid_params("Sender address does not match the signature.", ())), Err(err) => Err(errors::invalid_params("Invalid signature received.", err)), } }, ConfirmationPayload::SignMessage(address, hash) => { - let signature = ethkey::Signature::from_electrum(&bytes.0); - match ethkey::verify_address(&address, &signature, &hash) { - Ok(true) => Ok(ConfirmationResponse::Signature(bytes.0.as_slice().into())), + let signature = crypto::publickey::Signature::from_electrum(&bytes.0); + match crypto::publickey::verify_address(&address, &signature, &hash) { + Ok(true) => Ok(ConfirmationResponse::Signature(H520::from_slice(bytes.0.as_slice()))), Ok(false) => Err(errors::invalid_params("Sender address does not match the signature.", ())), Err(err) => Err(errors::invalid_params("Invalid signature received.", err)), } diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs index 38ca6d59c9..8e545eda1c 100644 --- a/rpc/src/v1/impls/signing.rs +++ b/rpc/src/v1/impls/signing.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -91,7 +91,7 @@ fn schedule(executor: Executor, /// Implementation of functions that require signing when no trusted signer is used. pub struct SigningQueueClient { signer: Arc, - accounts: Arc, + accounts: Arc, dispatcher: D, executor: Executor, // None here means that the request hasn't yet been confirmed @@ -101,7 +101,7 @@ pub struct SigningQueueClient { impl SigningQueueClient { /// Creates a new signing queue client given shared signing queue. - pub fn new(signer: &Arc, dispatcher: D, executor: Executor, accounts: &Arc) -> Self { + pub fn new(signer: &Arc, dispatcher: D, executor: Executor, accounts: &Arc) -> Self { SigningQueueClient { signer: signer.clone(), accounts: accounts.clone(), @@ -114,6 +114,12 @@ impl SigningQueueClient { fn dispatch(&self, payload: RpcConfirmationPayload, origin: Origin) -> BoxFuture { let default_account = self.accounts.default_account(); + let from = &payload.sender().unwrap_or(&default_account); + // bail early if the account isn't unlocked + if !self.accounts.is_unlocked(from) && !self.signer.is_enabled() { + return Box::new(future::done(Err(errors::signing_queue_disabled()))) + } + let accounts = self.accounts.clone(); let dispatcher = self.dispatcher.clone(); let signer = self.signer.clone(); @@ -127,7 +133,9 @@ impl SigningQueueClient { } else { Either::B(future::done( signer.add_request(payload, origin) - .map(|(id, future)| DispatchResult::Future(id, future)) + .map(|(id, future)| { + DispatchResult::Future(id, future) + }) .map_err(|_| errors::request_rejected_limit()) )) } diff --git a/rpc/src/v1/impls/signing_unsafe.rs b/rpc/src/v1/impls/signing_unsafe.rs index f08a9ffbe6..bae82da2cc 100644 --- a/rpc/src/v1/impls/signing_unsafe.rs +++ b/rpc/src/v1/impls/signing_unsafe.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -37,14 +37,14 @@ use v1::types::{ /// Implementation of functions that require signing when no trusted signer is used. pub struct SigningUnsafeClient { - accounts: Arc, + accounts: Arc, dispatcher: D, deprecation_notice: DeprecationNotice, } impl SigningUnsafeClient { /// Creates new SigningUnsafeClient. - pub fn new(accounts: &Arc, dispatcher: D) -> Self { + pub fn new(accounts: &Arc, dispatcher: D) -> Self { SigningUnsafeClient { accounts: accounts.clone(), dispatcher, diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index a6301eda53..5518d831a5 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,10 +18,16 @@ use std::sync::Arc; -use ethcore::client::{BlockChainClient, CallAnalytics, TransactionId, TraceId, StateClient, StateInfo, Call, BlockId}; +use account_state::state::StateInfo; +use ethcore::client::Call; +use client_traits::{BlockChainClient, StateClient}; use ethereum_types::H256; use rlp::Rlp; -use types::transaction::SignedTransaction; +use types::{ + call_analytics::CallAnalytics, + ids::{BlockId, TransactionId, TraceId}, + transaction::SignedTransaction, +}; use jsonrpc_core::Result; use v1::Metadata; @@ -95,6 +101,7 @@ impl Traces for TracesClient where let signed = fake_sign::sign_call(request)?; let id = match block { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, @@ -122,6 +129,7 @@ impl Traces for TracesClient where .collect::>>()?; let id = match block { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, @@ -144,6 +152,7 @@ impl Traces for TracesClient where let signed = SignedTransaction::new(tx).map_err(errors::transaction)?; let id = match block { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, @@ -167,6 +176,7 @@ impl Traces for TracesClient where fn replay_block_transactions(&self, block_number: BlockNumber, flags: TraceOptions) -> Result> { let id = match block_number { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, diff --git a/rpc/src/v1/impls/transactions_pool.rs b/rpc/src/v1/impls/transactions_pool.rs new file mode 100644 index 0000000000..789395d7e3 --- /dev/null +++ b/rpc/src/v1/impls/transactions_pool.rs @@ -0,0 +1,101 @@ +use std::sync::{Arc, Weak}; + +use jsonrpc_core::Result; +use jsonrpc_core::futures::Future; +use jsonrpc_pubsub::{SubscriptionId, typed::{Sink, Subscriber}}; + +use v1::helpers::Subscribers; +use v1::metadata::Metadata; +use v1::traits::TransactionsPool; + +use miner::pool::TxStatus; +use parity_runtime::Executor; +use parking_lot::RwLock; +use ethereum_types::H256; +use futures::{Stream, sync::mpsc}; + +type Client = Sink<(H256, TxStatus)>; + +/// Transactions pool PubSub implementation. +pub struct TransactionsPoolClient { + handler: Arc, + transactions_pool_subscribers: Arc>>, +} + +impl TransactionsPoolClient { + /// Creates new `TransactionsPoolClient`. + pub fn new(executor: Executor, pool_receiver: mpsc::UnboundedReceiver>>) -> Self { + let transactions_pool_subscribers = Arc::new(RwLock::new(Subscribers::default())); + let handler = Arc::new( + TransactionsNotificationHandler::new( + executor.clone(), + transactions_pool_subscribers.clone(), + ) + ); + let handler2 = Arc::downgrade(&handler); + + executor.spawn(pool_receiver + .for_each(move |tx_status| { + if let Some(handler2) = handler2.upgrade() { + handler2.notify_transaction(tx_status); + } + Ok(()) + }) + .map_err(|e| warn!("Key server listener error: {:?}", e)) + ); + + TransactionsPoolClient { + handler, + transactions_pool_subscribers, + } + } + + /// Returns a chain notification handler. + pub fn handler(&self) -> Weak { + Arc::downgrade(&self.handler) + } +} + +/// Transactions pool PubSub Notification handler. +pub struct TransactionsNotificationHandler { + executor: Executor, + transactions_pool_subscribers: Arc>>, +} + +impl TransactionsNotificationHandler { + fn new(executor: Executor, transactions_pool_subscribers: Arc>>) -> Self { + TransactionsNotificationHandler { + executor, + transactions_pool_subscribers, + } + } + + fn notify(executor: &Executor, subscriber: &Client, result: (H256, TxStatus)) { + executor.spawn(subscriber + .notify(Ok(result)) + .map(|_| ()) + .map_err(|e| warn!(target: "rpc", "Unable to send notification: {}", e)) + ); + } + + pub fn notify_transaction(&self, tx_statuses: Arc>) { + for subscriber in self.transactions_pool_subscribers.read().values() { + for tx_status in tx_statuses.to_vec() { + Self::notify(&self.executor, subscriber, tx_status.clone()); + } + } + } +} + +impl TransactionsPool for TransactionsPoolClient { + type Metadata = Metadata; + + fn subscribe(&self, _meta: Metadata, subscriber: Subscriber<(H256, TxStatus)>) { + self.transactions_pool_subscribers.write().push(subscriber); + } + + fn unsubscribe(&self, _meta: Option, id: SubscriptionId) -> Result { + let res = self.transactions_pool_subscribers.write().remove(&id).is_some(); + Ok(res) + } +} diff --git a/rpc/src/v1/impls/web3.rs b/rpc/src/v1/impls/web3.rs index 5ffda51b66..93203aca87 100644 --- a/rpc/src/v1/impls/web3.rs +++ b/rpc/src/v1/impls/web3.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/informant.rs b/rpc/src/v1/informant.rs index 945378390b..dea0e37534 100644 --- a/rpc/src/v1/informant.rs +++ b/rpc/src/v1/informant.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/metadata.rs b/rpc/src/v1/metadata.rs index 3224bd2c03..c4171e35b6 100644 --- a/rpc/src/v1/metadata.rs +++ b/rpc/src/v1/metadata.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index 8b8afacdb1..1019c0f498 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -46,6 +46,7 @@ pub use self::impls::*; pub use self::helpers::{NetworkSettings, block_import, dispatch}; pub use self::metadata::Metadata; pub use self::types::Origin; +pub use self::types::pubsub::PubSubSyncStatus; pub use self::extractors::{RpcExtractor, WsExtractor, WsStats, WsDispatcher}; /// Signer utilities diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index 9c2273746f..f11822624c 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,25 +15,27 @@ // along with Parity Ethereum. If not, see . //! rpc integration tests. -use std::env; -use std::sync::Arc; + +use std::{env, sync::Arc}; use accounts::AccountProvider; -use ethcore::client::{BlockChainClient, Client, ClientConfig, ChainInfo, ImportBlock}; -use ethcore::ethereum; +use client_traits::{BlockChainClient, ChainInfo, ImportBlock}; +use ethcore::client::{Client, ClientConfig}; use ethcore::miner::Miner; -use ethcore::spec::{Genesis, Spec}; +use spec::{Genesis, Spec, self}; use ethcore::test_helpers; -use ethcore::verification::VerifierType; -use ethcore::verification::queue::kind::blocks::Unverified; +use verification::VerifierType; use ethereum_types::{Address, H256, U256}; -use ethjson::blockchain::BlockChain; +use ethjson::test_helpers::blockchain::BlockChain; use ethjson::spec::ForkSpec; use io::IoChannel; use miner::external::ExternalMiner; use parity_runtime::Runtime; use parking_lot::Mutex; -use types::ids::BlockId; +use types::{ + ids::BlockId, + verification::Unverified, +}; use jsonrpc_core::IoHandler; use v1::helpers::dispatch::{self, FullDispatcher}; @@ -64,11 +66,10 @@ fn snapshot_service() -> Arc { fn make_spec(chain: &BlockChain) -> Spec { let genesis = Genesis::from(chain.genesis()); - let mut spec = ethereum::new_frontier_test(); + let mut spec = spec::new_frontier_test(); let state = chain.pre_state.clone().into(); spec.set_genesis_state(state).expect("unable to set genesis state"); spec.overwrite_genesis_params(genesis); - assert!(spec.is_state_root_valid()); spec } @@ -84,7 +85,7 @@ struct EthTester { impl EthTester { fn from_chain(chain: &BlockChain) -> Self { - let tester = if ::ethjson::blockchain::Engine::NoProof == chain.engine { + let tester = if ethjson::test_helpers::blockchain::Engine::NoProof == chain.engine { let mut config = ClientConfig::default(); config.verifier_type = VerifierType::CanonNoSeal; config.check_seal = false; @@ -97,7 +98,6 @@ impl EthTester { if let Ok(block) = Unverified::from_rlp(b) { let _ = tester.client.import_block(block); tester.client.flush_queue(); - tester.client.import_verified_blocks(); } } @@ -116,7 +116,7 @@ impl EthTester { let runtime = Runtime::with_thread_count(1); let account_provider = account_provider(); let ap = account_provider.clone(); - let accounts = Arc::new(move || ap.accounts().unwrap_or_default()) as _; + let accounts = Arc::new(move || ap.accounts().unwrap_or_default()) as _; let miner_service = miner_service(&spec); let snapshot_service = snapshot_service(); @@ -174,13 +174,13 @@ impl EthTester { #[test] fn harness_works() { - let chain: BlockChain = extract_chain!("BlockchainTests/bcWalletTest/wallet2outOf3txs"); + let chain: BlockChain = extract_chain!("BlockchainTests/ValidBlocks/bcWalletTest/wallet2outOf3txs"); let _ = EthTester::from_chain(&chain); } #[test] fn eth_get_balance() { - let chain = extract_chain!("BlockchainTests/bcWalletTest/wallet2outOf3txs"); + let chain = extract_chain!("BlockchainTests/ValidBlocks/bcWalletTest/wallet2outOf3txs"); let tester = EthTester::from_chain(&chain); // final account state let req_latest = r#"{ @@ -206,7 +206,7 @@ fn eth_get_balance() { #[test] fn eth_get_proof() { - let chain = extract_chain!("BlockchainTests/bcWalletTest/wallet2outOf3txs"); + let chain = extract_chain!("BlockchainTests/ValidBlocks/bcWalletTest/wallet2outOf3txs"); let tester = EthTester::from_chain(&chain); // final account state let req_latest = r#"{ @@ -217,8 +217,7 @@ fn eth_get_proof() { }"#; let res_latest = r#","address":"0xaaaf5374fce5edbc8e2a8697c15331677e6ebaaa","balance":"0x9","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","nonce":"0x0","storageHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","storageProof":[]},"id":1}"#.to_owned(); - assert!(tester.handler.handle_request_sync(req_latest).unwrap().to_string().ends_with(res_latest.as_str())); - + assert!(tester.handler.handle_request_sync(req_latest).unwrap().to_string().ends_with(res_latest.as_str())); // non-existant account let req_new_acc = r#"{ "jsonrpc": "2.0", @@ -228,12 +227,12 @@ fn eth_get_proof() { }"#; let res_new_acc = r#","address":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","balance":"0x0","codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","nonce":"0x0","storageHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","storageProof":[]},"id":3}"#.to_owned(); - assert!(tester.handler.handle_request_sync(req_new_acc).unwrap().to_string().ends_with(res_new_acc.as_str())); + assert!(tester.handler.handle_request_sync(req_new_acc).unwrap().to_string().ends_with(res_new_acc.as_str())); } #[test] fn eth_block_number() { - let chain = extract_chain!("BlockchainTests/bcGasPricerTest/RPC_API_Test"); + let chain = extract_chain!("BlockchainTests/ValidBlocks/bcGasPricerTest/RPC_API_Test"); let tester = EthTester::from_chain(&chain); let req_number = r#"{ "jsonrpc": "2.0", @@ -248,7 +247,7 @@ fn eth_block_number() { #[test] fn eth_get_block() { - let chain = extract_chain!("BlockchainTests/bcGasPricerTest/RPC_API_Test"); + let chain = extract_chain!("BlockchainTests/ValidBlocks/bcGasPricerTest/RPC_API_Test"); let tester = EthTester::from_chain(&chain); let req_block = r#"{"method":"eth_getBlockByNumber","params":["0x0",false],"id":1,"jsonrpc":"2.0"}"#; @@ -258,13 +257,13 @@ fn eth_get_block() { #[test] fn eth_get_block_by_hash() { - let chain = extract_chain!("BlockchainTests/bcGasPricerTest/RPC_API_Test"); + let chain = extract_chain!("BlockchainTests/ValidBlocks/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":["0xaddb9e39795e9e041c936b88a2577802569f34afded0948707b074caa3163a87",false],"id":1,"jsonrpc":"2.0"}"#; + let req_block = r#"{"method":"eth_getBlockByHash","params":["0x75e65fb3bbf5f53afe26dcc72df6a95b0e8ca5f1c450145d8c3915bd0308b75b",false],"id":1,"jsonrpc":"2.0"}"#; - let res_block = r#"{"jsonrpc":"2.0","result":{"author":"0x8888f1f195afa192cfee860698584c030f4c9db1","difficulty":"0x20080","extraData":"0x","gasLimit":"0x1dd7ea0","gasUsed":"0x5458","hash":"0xaddb9e39795e9e041c936b88a2577802569f34afded0948707b074caa3163a87","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x8888f1f195afa192cfee860698584c030f4c9db1","mixHash":"0x713b0b31f6e72d8cb7367eaf59447ea531f209fc80e6379edd9f8d3bb73931c4","nonce":"0x4534b406bc23b86d","number":"0x4","parentHash":"0x17567aa5995b703736e32972289d68af50543acc4d56d37e8ad1fea7252cac4a","receiptsRoot":"0x7ed8026cf72ed0e98e6fd53ab406e51ffd34397d9da0052494ff41376fda7b5f","sealFields":["0xa0713b0b31f6e72d8cb7367eaf59447ea531f209fc80e6379edd9f8d3bb73931c4","0x884534b406bc23b86d"],"sha3Uncles":"0xe588a44b3e320e72e70b32b531f3ac0d432e756120135ae8fe5fa10895196b40","size":"0x661","stateRoot":"0x68805721294e365020aca15ed56c360d9dc2cf03cbeff84c9b84b8aed023bfb5","timestamp":"0x5bbdf772","totalDifficulty":"0xa00c0","transactions":["0xb094b9dc356dbb8b256402c6d5709288066ad6a372c90c9c516f14277545fd58"],"transactionsRoot":"0x97a593d8d7e15b57f5c6bb25bc6c325463ef99f874bc08a78656c3ab5cb23262","uncles":["0x86b48f5186c4b0882d3dca7977aa37840008832ef092f8ef797019dc74bfa8c7","0x2da9d062c11d536f0f1cc2a4e0111597c79926958d0fc26ae1a2d07d1a3bf47d"]},"id":1}"#; + let res_block = r#"{"jsonrpc":"2.0","result":{"author":"0x8888f1f195afa192cfee860698584c030f4c9db1","difficulty":"0x20000","extraData":"0x","gasLimit":"0x1dd7ea0","gasUsed":"0x5458","hash":"0x75e65fb3bbf5f53afe26dcc72df6a95b0e8ca5f1c450145d8c3915bd0308b75b","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x8888f1f195afa192cfee860698584c030f4c9db1","mixHash":"0x55553aaef7ee28e3aea539eb784e8cc26646911a19126c242ac682c3fcf22041","nonce":"0xca2904e50ca47ace","number":"0x4","parentHash":"0x58849f66c0ca60054468725cf173b72a2769807152c625aa02e71d67ab2eaed5","receiptsRoot":"0x7ed8026cf72ed0e98e6fd53ab406e51ffd34397d9da0052494ff41376fda7b5f","sealFields":["0xa055553aaef7ee28e3aea539eb784e8cc26646911a19126c242ac682c3fcf22041","0x88ca2904e50ca47ace"],"sha3Uncles":"0x0dbc9711185574f2eee337af18d08c0afe85490304c6bb16b443991b552c5e2c","size":"0x661","stateRoot":"0x68805721294e365020aca15ed56c360d9dc2cf03cbeff84c9b84b8aed023bfb5","timestamp":"0x5c477134","totalDifficulty":"0xa0000","transactions":["0xb094b9dc356dbb8b256402c6d5709288066ad6a372c90c9c516f14277545fd58"],"transactionsRoot":"0x97a593d8d7e15b57f5c6bb25bc6c325463ef99f874bc08a78656c3ab5cb23262","uncles":["0x51b0d7366382926a4f83191af19cb4aa894f6fd9bd1bda6c04de3d5af70eddba","0x9263e0be8311eb79db96171fad3fdd70317bbbdc4081ad6b04c60335db65a3bb"]},"id":1}"#; assert_eq!(tester.handler.handle_request_sync(req_block).unwrap(), res_block); } @@ -495,7 +494,7 @@ fn verify_transaction_counts(name: String, chain: BlockChain) { #[test] fn starting_nonce_test() { let tester = EthTester::from_spec(Spec::load(&env::temp_dir(), POSITIVE_NONCE_SPEC).expect("invalid chain spec")); - let address = Address::from(10); + let address = Address::from_low_u64_be(10); let sample = tester.handler.handle_request_sync(&(r#" { @@ -510,6 +509,6 @@ fn starting_nonce_test() { assert_eq!(r#"{"jsonrpc":"2.0","result":"0x100","id":15}"#, &sample); } -register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/bcWalletTest/wallet2outOf3txs"); -register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/bcTotalDifficultyTest/sideChainWithMoreTransactions"); -register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/bcGasPricerTest/RPC_API_Test"); +register_test!(eth_transaction_count_1, verify_transaction_counts, "BlockchainTests/ValidBlocks/bcWalletTest/wallet2outOf3txs"); +register_test!(eth_transaction_count_2, verify_transaction_counts, "BlockchainTests/ValidBlocks/bcTotalDifficultyTest/sideChainWithMoreTransactions"); +register_test!(eth_transaction_count_3, verify_transaction_counts, "BlockchainTests/ValidBlocks/bcGasPricerTest/RPC_API_Test"); diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index b16651bd9c..f1e4dd6cc3 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,22 +20,26 @@ use std::sync::Arc; use std::collections::{BTreeMap, BTreeSet, HashMap}; use bytes::Bytes; +use client_traits::{Nonce, StateClient, ForceUpdateSealing}; +use engine::{Engine, signer::EngineSigner}; use ethcore::block::SealedBlock; -use ethcore::client::{Nonce, PrepareOpenBlock, StateClient, EngineInfo}; -use ethcore::engines::{EthEngine, signer::EngineSigner}; -use ethcore::error::Error; -use ethcore::miner::{self, MinerService, AuthoringParams}; +use ethcore::client::{PrepareOpenBlock, EngineInfo}; +use ethcore::miner::{self, MinerService, AuthoringParams, FilterOptions}; +use ethcore::test_helpers::TestState; use ethereum_types::{H256, U256, Address}; use miner::pool::local_transactions::Status as LocalTransactionStatus; use miner::pool::{verifier, VerifiedTransaction, QueueStatus}; use parking_lot::{RwLock, Mutex}; -use types::transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}; use txpool; -use types::BlockNumber; -use types::block::Block; -use types::header::Header; -use types::ids::BlockId; -use types::receipt::RichReceipt; +use types::{ + BlockNumber, + block::Block, + header::Header, + errors::EthcoreError as Error, + ids::BlockId, + receipt::RichReceipt, + transaction::{self, UnverifiedTransaction, SignedTransaction, PendingTransaction}, +}; /// Test miner service. pub struct TestMinerService { @@ -52,7 +56,7 @@ pub struct TestMinerService { /// Minimum gas price pub min_gas_price: RwLock>, /// Signer (if any) - pub signer: RwLock>>, + pub signer: RwLock>>, authoring_params: RwLock, } @@ -87,25 +91,25 @@ impl TestMinerService { impl StateClient for TestMinerService { // State will not be used by test client anyway, since all methods that accept state are mocked - type State = (); + type State = TestState; - fn latest_state(&self) -> Self::State { - () + fn latest_state_and_header(&self) -> (Self::State, Header) { + (TestState, Header::default()) } fn state_at(&self, _id: BlockId) -> Option { - Some(()) + Some(TestState) } } impl EngineInfo for TestMinerService { - fn engine(&self) -> &EthEngine { + fn engine(&self) -> &dyn Engine { unimplemented!() } } impl MinerService for TestMinerService { - type State = (); + type State = TestState; fn pending_state(&self, _latest_block_number: BlockNumber) -> Option { None @@ -123,10 +127,13 @@ impl MinerService for TestMinerService { self.authoring_params.read().clone() } - fn set_author(&self, author: miner::Author) { - self.authoring_params.write().author = author.address(); - if let miner::Author::Sealer(signer) = author { - *self.signer.write() = Some(signer); + fn set_author>>(&self, author: T) { + let author_opt = author.into(); + self.authoring_params.write().author = author_opt.as_ref().map(miner::Author::address).unwrap_or_default(); + match author_opt { + Some(miner::Author::Sealer(signer)) => *self.signer.write() = Some(signer), + Some(miner::Author::External(_addr)) => (), + None => *self.signer.write() = None, } } @@ -185,7 +192,7 @@ impl MinerService for TestMinerService { } /// New chain head event. Restart mining operation. - fn update_sealing(&self, _chain: &C) { + fn update_sealing(&self, _chain: &C, _force: ForceUpdateSealing) { unimplemented!(); } @@ -222,6 +229,25 @@ impl MinerService for TestMinerService { self.queued_transactions() } + fn ready_transactions_filtered( + &self, + _chain: &C, + max_len: usize, + filter: Option, + _ordering: miner::PendingOrdering + ) -> Vec> { + self.queued_transactions() + .iter() + .cloned() + .filter(|tx| { + filter.as_ref().map_or(true, |filter| { + filter.matches(tx.signed()) + }) + }) + .take(max_len) + .collect() + } + fn pending_transaction_hashes(&self, _chain: &C) -> BTreeSet { self.queued_transactions().into_iter().map(|tx| tx.signed().hash()).collect() } diff --git a/rpc/src/v1/tests/helpers/mod.rs b/rpc/src/v1/tests/helpers/mod.rs index 0cecd271c0..271c0bec49 100644 --- a/rpc/src/v1/tests/helpers/mod.rs +++ b/rpc/src/v1/tests/helpers/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/tests/helpers/snapshot_service.rs b/rpc/src/v1/tests/helpers/snapshot_service.rs index 881c434e17..ca22d332dd 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,11 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use ethcore::snapshot::{ManifestData, RestorationStatus, SnapshotService}; +use snapshot::SnapshotService; use bytes::Bytes; use ethereum_types::H256; use parking_lot::Mutex; +use types::snapshot::{ManifestData, RestorationStatus}; /// Mocked snapshot service (used for sync info extensions). pub struct TestSnapshotService { @@ -51,5 +52,6 @@ impl SnapshotService for TestSnapshotService { fn abort_snapshot(&self) {} fn restore_state_chunk(&self, _hash: H256, _chunk: Bytes) { } fn restore_block_chunk(&self, _hash: H256, _chunk: Bytes) { } + fn abort_snapshot(&self) {} fn shutdown(&self) { } } diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index 37c2f93553..81e50c3b5b 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,10 +17,11 @@ //! Test implementation of SyncProvider. use std::collections::BTreeMap; -use ethereum_types::H256; +use ethereum_types::{H256, H512}; use parking_lot::RwLock; -use sync::{SyncProvider, EthProtocolInfo, SyncStatus, SyncState, PeerInfo, TransactionStats}; use network::client_version::ClientVersion; +use futures::sync::mpsc; +use sync::{SyncProvider, EthProtocolInfo, SyncStatus, PeerInfo, TransactionStats, SyncState}; /// TestSyncProvider config. pub struct Config { @@ -34,6 +35,8 @@ pub struct Config { pub struct TestSyncProvider { /// Sync status. pub status: RwLock, + /// is major importing? + is_importing: RwLock, } impl TestSyncProvider { @@ -56,12 +59,14 @@ impl TestSyncProvider { snapshot_chunks_done: 0, last_imported_old_block_number: None, }), + is_importing: RwLock::new(false) } } /// Simulate importing blocks. pub fn increase_imported_block_number(&self, count: u64) { let mut status = self.status.write(); + *self.is_importing.write() = true; let current_number = status.last_imported_block_number.unwrap_or(0); status.last_imported_block_number = Some(current_number + count); } @@ -83,7 +88,7 @@ impl SyncProvider for TestSyncProvider { eth_info: Some(EthProtocolInfo { version: 62, difficulty: Some(40.into()), - head: 50.into(), + head: H256::from_low_u64_be(50), }), pip_info: None, }, @@ -96,7 +101,7 @@ impl SyncProvider for TestSyncProvider { eth_info: Some(EthProtocolInfo { version: 64, difficulty: None, - head: 60.into() + head: H256::from_low_u64_be(60), }), pip_info: None, } @@ -109,18 +114,31 @@ impl SyncProvider for TestSyncProvider { fn transactions_stats(&self) -> BTreeMap { map![ - 1.into() => TransactionStats { + H256::from_low_u64_be(1) => TransactionStats { first_seen: 10, propagated_to: map![ - 128.into() => 16 + H512::from_low_u64_be(128) => 16 ], }, - 5.into() => TransactionStats { + H256::from_low_u64_be(5) => TransactionStats { first_seen: 16, propagated_to: map![ - 16.into() => 1 + H512::from_low_u64_be(16) => 1 ], } ] } + + fn sync_notification(&self) -> mpsc::UnboundedReceiver { + unimplemented!() + } + + fn is_major_syncing(&self) -> bool { + match (self.status.read().state, *self.is_importing.read()) { + (SyncState::Idle, _) => false, + (SyncState::Blocks, _) => true, + (_, true) => true, + _ => false + } + } } diff --git a/rpc/src/v1/tests/helpers/update_service.rs b/rpc/src/v1/tests/helpers/update_service.rs index ccf3315c47..b1dd758831 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,6 +19,7 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use semver::Version; use updater::{Service as UpdateService, CapState, ReleaseInfo, VersionInfo, OperationsInfo, ReleaseTrack}; +use ethereum_types::{H160, H256}; /// Test implementation of fetcher. Will always return the same file. #[derive(Default)] @@ -71,9 +72,9 @@ impl UpdateService for TestUpdater { fn version_info(&self) -> VersionInfo { VersionInfo { - track: ReleaseTrack::Beta, + track: ReleaseTrack::Stable, version: Version{major: 1, minor: 5, patch: 0, build: vec![], pre: vec![]}, - hash: 150.into(), + hash: H160::from_low_u64_be(150), } } @@ -83,13 +84,13 @@ impl UpdateService for TestUpdater { this_fork: Some(15000), track: ReleaseInfo { version: VersionInfo { - track: ReleaseTrack::Beta, + track: ReleaseTrack::Stable, version: Version{major: 1, minor: 5, patch: 1, build: vec![], pre: vec![]}, - hash: 151.into(), + hash: H160::from_low_u64_be(151), }, is_critical: true, fork: 15100, - binary: Some(1510.into()), + binary: Some(H256::from_low_u64_be(1510)), }, minor: None, }) diff --git a/rpc/src/v1/tests/mocked/debug.rs b/rpc/src/v1/tests/mocked/debug.rs index ffbe8d4d1c..0883ecc1bd 100644 --- a/rpc/src/v1/tests/mocked/debug.rs +++ b/rpc/src/v1/tests/mocked/debug.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ use std::sync::Arc; -use ethcore::client::TestBlockChainClient; +use ethcore::test_helpers::TestBlockChainClient; use jsonrpc_core::IoHandler; use v1::{Debug, DebugClient}; diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 75e05ce467..8282190194 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,18 +20,24 @@ use std::sync::Arc; use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; use accounts::AccountProvider; -use ethcore::client::{BlockChainClient, BlockId, EachBlockWith, Executed, TestBlockChainClient, TransactionId}; +use client_traits::BlockChainClient; +use ethcore::test_helpers::{EachBlockWith, TestBlockChainClient}; use ethcore::miner::{self, MinerService}; -use ethereum_types::{H160, H256, U256, Address}; +use ethereum_types::{H160, H256, U256, Address, Bloom}; +use machine::executed::Executed; use miner::external::ExternalMiner; use parity_runtime::Runtime; use parking_lot::Mutex; use rlp; use rustc_hex::{FromHex, ToHex}; use sync::SyncState; -use types::transaction::{Transaction, Action}; -use types::log_entry::{LocalizedLogEntry, LogEntry}; -use types::receipt::{LocalizedReceipt, TransactionOutcome}; +use types::{ + ids::{BlockId, TransactionId}, + transaction::{Transaction, Action}, + log_entry::{LocalizedLogEntry, LogEntry}, + receipt::{LocalizedReceipt, RichReceipt, TransactionOutcome}, + snapshot::RestorationStatus, +}; use jsonrpc_core::IoHandler; use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient}; @@ -100,13 +106,13 @@ impl EthTester { EthTester { runtime, - client: client, - sync: sync, + client, + sync, accounts_provider: ap, - miner: miner, - snapshot: snapshot, - io: io, - hashrates: hashrates, + miner, + snapshot, + io, + hashrates, } } @@ -126,8 +132,6 @@ fn rpc_eth_protocol_version() { #[test] fn rpc_eth_syncing() { - use ethcore::snapshot::RestorationStatus; - let request = r#"{"jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1}"#; let tester = EthTester::default(); @@ -186,9 +190,9 @@ fn rpc_eth_chain_id() { #[test] fn rpc_eth_hashrate() { let tester = EthTester::default(); - tester.hashrates.lock().insert(H256::from(0), (Instant::now() + Duration::from_secs(2), U256::from(0xfffa))); - tester.hashrates.lock().insert(H256::from(0), (Instant::now() + Duration::from_secs(2), U256::from(0xfffb))); - tester.hashrates.lock().insert(H256::from(1), (Instant::now() + Duration::from_secs(2), U256::from(0x1))); + tester.hashrates.lock().insert(H256::from_low_u64_be(0), (Instant::now() + Duration::from_secs(2), U256::from(0xfffa))); + tester.hashrates.lock().insert(H256::from_low_u64_be(0), (Instant::now() + Duration::from_secs(2), U256::from(0xfffb))); + tester.hashrates.lock().insert(H256::from_low_u64_be(1), (Instant::now() + Duration::from_secs(2), U256::from(0x1))); let request = r#"{"jsonrpc": "2.0", "method": "eth_hashrate", "params": [], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":"0xfffc","id":1}"#; @@ -201,27 +205,27 @@ fn rpc_eth_logs() { let tester = EthTester::default(); tester.client.set_logs(vec![LocalizedLogEntry { block_number: 1, - block_hash: H256::default(), + block_hash: H256::zero(), entry: LogEntry { - address: Address::default(), + address: Address::zero(), topics: vec![], data: vec![1,2,3], }, transaction_index: 0, transaction_log_index: 0, - transaction_hash: H256::default(), + transaction_hash: H256::zero(), log_index: 0, }, LocalizedLogEntry { block_number: 1, - block_hash: H256::default(), + block_hash: H256::zero(), entry: LogEntry { - address: Address::default(), + address: Address::zero(), topics: vec![], data: vec![1,2,3], }, transaction_index: 0, transaction_log_index: 1, - transaction_hash: H256::default(), + transaction_hash: H256::zero(), log_index: 1, }]); @@ -240,8 +244,14 @@ fn rpc_eth_logs() { #[test] fn rpc_eth_logs_error() { + fn h256_from_digit_be(d: u8) -> H256 { + let mut bytes = [0u8; 32]; + bytes[0] = d; + H256(bytes) + } + let tester = EthTester::default(); - tester.client.set_error_on_logs(Some(BlockId::Hash(H256::from([5u8].as_ref())))); + tester.client.set_error_on_logs(Some(BlockId::Hash(h256_from_digit_be(5)))); let request = r#"{"jsonrpc": "2.0", "method": "eth_getLogs", "params": [{"limit":1,"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"One of the blocks specified in filter (fromBlock, toBlock or blockHash) cannot be found","data":"0x0500000000000000000000000000000000000000000000000000000000000000"},"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); @@ -253,27 +263,27 @@ fn rpc_logs_filter() { // Set some logs tester.client.set_logs(vec![LocalizedLogEntry { block_number: 1, - block_hash: H256::default(), + block_hash: H256::zero(), entry: LogEntry { - address: Address::default(), + address: Address::zero(), topics: vec![], data: vec![1,2,3], }, transaction_index: 0, transaction_log_index: 0, - transaction_hash: H256::default(), + transaction_hash: H256::zero(), log_index: 0, }, LocalizedLogEntry { block_number: 1, - block_hash: H256::default(), + block_hash: H256::zero(), entry: LogEntry { - address: Address::default(), + address: Address::zero(), topics: vec![], data: vec![1,2,3], }, transaction_index: 0, transaction_log_index: 1, - transaction_hash: H256::default(), + transaction_hash: H256::zero(), log_index: 1, }]); @@ -349,7 +359,7 @@ fn rpc_eth_submit_hashrate() { let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(tester.hashrates.lock().get(&H256::from("0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c")).cloned().unwrap().1, + assert_eq!(tester.hashrates.lock().get(&H256::from_str("59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c").unwrap()).cloned().unwrap().1, U256::from(0x500_000)); } @@ -404,8 +414,8 @@ fn rpc_eth_gas_price() { fn rpc_eth_accounts() { let tester = EthTester::default(); let address = tester.accounts_provider.new_account(&"".into()).unwrap(); - tester.accounts_provider.set_address_name(1.into(), "1".into()); - tester.accounts_provider.set_address_name(10.into(), "10".into()); + tester.accounts_provider.set_address_name(Address::from_low_u64_be(1), "1".into()); + tester.accounts_provider.set_address_name(Address::from_low_u64_be(10), "10".into()); // with current policy it should return the account let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#; @@ -427,7 +437,7 @@ fn rpc_eth_block_number() { #[test] fn rpc_eth_balance() { let tester = EthTester::default(); - tester.client.set_balance(Address::from(1), U256::from(5)); + tester.client.set_balance(Address::from_low_u64_be(1), U256::from(5)); let request = r#"{ "jsonrpc": "2.0", @@ -443,7 +453,7 @@ fn rpc_eth_balance() { #[test] fn rpc_eth_balance_pending() { let tester = EthTester::default(); - tester.client.set_balance(Address::from(1), U256::from(5)); + tester.client.set_balance(Address::from_low_u64_be(1), U256::from(5)); let request = r#"{ "jsonrpc": "2.0", @@ -460,7 +470,7 @@ fn rpc_eth_balance_pending() { #[test] fn rpc_eth_storage_at() { let tester = EthTester::default(); - tester.client.set_storage(Address::from(1), H256::from(4), H256::from(7)); + tester.client.set_storage(Address::from_low_u64_be(1), H256::from_low_u64_be(4), H256::from_low_u64_be(7)); let request = r#"{ "jsonrpc": "2.0", @@ -491,7 +501,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_nonce(&1.into()); + tester.miner.increment_nonce(&H160::from_low_u64_be(1)); let request1 = r#"{ "jsonrpc": "2.0", @@ -604,7 +614,7 @@ fn rpc_eth_uncle_count_by_block_number() { #[test] fn rpc_eth_code() { let tester = EthTester::default(); - tester.client.set_code(Address::from(1), vec![0xff, 0x21]); + tester.client.set_code(Address::from_low_u64_be(1), vec![0xff, 0x21]); let request = r#"{ "jsonrpc": "2.0", @@ -653,6 +663,43 @@ fn rpc_eth_call_latest() { assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } +#[test] +fn rpc_eth_call_pending() { + let tester = EthTester::default(); + tester.client.set_execution_result(Ok(Executed { + exception: None, + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_call", + "params": [{ + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }, + "pending"], + "id": 1 + }"#; + // Falls back to "Latest" and gives the same result. + let response = r#"{"jsonrpc":"2.0","result":"0x1234ff","id":1}"#; + + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); +} + #[test] fn rpc_eth_call() { let tester = EthTester::default(); @@ -760,6 +807,43 @@ fn rpc_eth_estimate_gas() { assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } +#[test] +fn rpc_eth_estimate_gas_pending() { + let tester = EthTester::default(); + tester.client.set_execution_result(Ok(Executed { + exception: None, + gas: U256::zero(), + gas_used: U256::from(0xff30), + refunded: U256::from(0x5), + cumulative_gas_used: U256::zero(), + logs: vec![], + contracts_created: vec![], + output: vec![0x12, 0x34, 0xff], + trace: vec![], + vm_trace: None, + state_diff: None, + })); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_estimateGas", + "params": [{ + "from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x76c0", + "gasPrice": "0x9184e72a000", + "value": "0x9184e72a", + "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + }, + "pending"], + "id": 1 + }"#; + // Falls back to "Latest" so the result is the same + let response = r#"{"jsonrpc":"2.0","result":"0x5208","id":1}"#; + + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); +} + #[test] fn rpc_eth_estimate_gas_default_block() { let tester = EthTester::default(); @@ -868,13 +952,13 @@ fn rpc_eth_transaction_receipt() { }, block_hash: H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap(), block_number: 0x4510c, - transaction_hash: H256::new(), + transaction_hash: H256::zero(), transaction_index: 0, transaction_log_index: 0, log_index: 1, }], - log_bloom: 0.into(), - outcome: TransactionOutcome::StateRoot(0.into()), + log_bloom: Bloom::zero(), + outcome: TransactionOutcome::StateRoot(H256::zero()), }; let hash = H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap(); @@ -887,7 +971,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","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}"#; + 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","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionIndex":"0x0"},"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } @@ -907,6 +991,34 @@ fn rpc_eth_transaction_receipt_null() { assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } +#[test] +fn rpc_eth_pending_receipt() { + let pending = RichReceipt { + from: H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap(), + to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), + transaction_hash: H256::from_str("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238").unwrap(), + transaction_index: 0, + cumulative_gas_used: U256::from(0x20), + gas_used: U256::from(0x10), + contract_address: None, + logs: Vec::new(), + log_bloom: Bloom::zero(), + outcome: TransactionOutcome::Unknown, + }; + let tester = EthTester::default(); + + tester.miner.pending_receipts.lock().push(pending); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "eth_getTransactionReceipt", + "params": ["0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238"], + "id": 1 + }"#; + let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"contractAddress":null,"cumulativeGasUsed":"0x20","from":"0xb60e8dd61c5d32be8058bb8eb970870f07233155","gasUsed":"0x10","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionHash":"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238","transactionIndex":"0x0"},"id":1}"#; + assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); +} + // These tests are incorrect: their output is undefined as long as eth_getCompilers is []. // Will ignore for now, but should probably be replaced by more substantial tests which check // the output of eth_getCompilers to determine whether to test. CI systems can then be preinstalled diff --git a/rpc/src/v1/tests/mocked/eth_pubsub.rs b/rpc/src/v1/tests/mocked/eth_pubsub.rs index 6fd7394a4b..67438f9f46 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,15 +17,22 @@ use std::sync::Arc; use jsonrpc_core::MetaIoHandler; -use jsonrpc_core::futures::{self, Stream, Future}; +use jsonrpc_core::futures::{self, Stream, Future, sync::mpsc}; use jsonrpc_pubsub::Session; use std::time::Duration; use v1::{EthPubSub, EthPubSubClient, Metadata}; - -use ethcore::client::{TestBlockChainClient, EachBlockWith, ChainNotify, NewBlocks, ChainRoute, ChainRouteType}; +use ethcore::test_helpers::{TestBlockChainClient, EachBlockWith}; use parity_runtime::Runtime; +use ethereum_types::{Address, H256}; +use client_traits::{BlockInfo, ChainNotify}; +use types::{ + chain_notify::{NewBlocks, ChainRoute, ChainRouteType}, + log_entry::{LocalizedLogEntry, LogEntry}, + ids::BlockId, +}; + const DURATION_ZERO: Duration = Duration::from_millis(0); @@ -40,7 +47,9 @@ fn should_subscribe_to_new_heads() { let h2 = client.block_hash_delta_minus(2); let h1 = client.block_hash_delta_minus(3); - let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); + let (_, pool_receiver) = mpsc::unbounded(); + + let pubsub = EthPubSubClient::new(Arc::new(client), el.executor(), pool_receiver); let handler = pubsub.handler().upgrade().unwrap(); let pubsub = pubsub.to_delegate(); @@ -53,13 +62,13 @@ fn should_subscribe_to_new_heads() { // Subscribe let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newHeads"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x43ca64edf03768e1","id":1}"#; assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); // Check notifications handler.new_blocks(NewBlocks::new(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO, true)); 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"}}"#; + 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":"0x43ca64edf03768e1"}}"#; assert_eq!(res, Some(response.into())); // Notify about two blocks @@ -67,14 +76,14 @@ fn should_subscribe_to_new_heads() { // Receive both let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x2","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0x44e5ecf454ea99af9d8a8f2ca0daba96964c90de05db7a78f59b84ae9e749706","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x2","parentHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#; + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x2","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0x44e5ecf454ea99af9d8a8f2ca0daba96964c90de05db7a78f59b84ae9e749706","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x2","parentHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x43ca64edf03768e1"}}"#; assert_eq!(res, Some(response.into())); let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x3","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0xdf04a98bb0c6fa8441bd429822f65a46d0cb553f6bcef602b973e65c81497f8e","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x3","parentHash":"0x44e5ecf454ea99af9d8a8f2ca0daba96964c90de05db7a78f59b84ae9e749706","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#; + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x3","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0xdf04a98bb0c6fa8441bd429822f65a46d0cb553f6bcef602b973e65c81497f8e","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x3","parentHash":"0x44e5ecf454ea99af9d8a8f2ca0daba96964c90de05db7a78f59b84ae9e749706","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x43ca64edf03768e1"}}"#; assert_eq!(res, Some(response.into())); // And unsubscribe - let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x43ca64edf03768e1"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(io.handle_request_sync(request, metadata), Some(response.to_owned())); @@ -84,10 +93,6 @@ fn should_subscribe_to_new_heads() { #[test] fn should_subscribe_to_logs() { - use ethcore::client::BlockInfo; - use types::log_entry::{LocalizedLogEntry, LogEntry}; - use types::ids::BlockId; - // given let el = Runtime::with_thread_count(1); let mut client = TestBlockChainClient::new(); @@ -99,8 +104,8 @@ fn should_subscribe_to_logs() { client.set_logs(vec![ LocalizedLogEntry { entry: LogEntry { - address: 5.into(), - topics: vec![1.into(), 2.into(), 0.into(), 0.into()], + address: Address::from_low_u64_be(5), + topics: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2), H256::from_low_u64_be(0), H256::from_low_u64_be(0)], data: vec![], }, block_hash: h1, @@ -112,7 +117,9 @@ fn should_subscribe_to_logs() { } ]); - let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); + let (_, pool_receiver) = mpsc::unbounded(); + + let pubsub = EthPubSubClient::new(Arc::new(client), el.executor(), pool_receiver); let handler = pubsub.handler().upgrade().unwrap(); let pubsub = pubsub.to_delegate(); @@ -125,7 +132,7 @@ fn should_subscribe_to_logs() { // Subscribe let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["logs", {}], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x43ca64edf03768e1","id":1}"#; assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); // Check notifications (enacted) @@ -133,7 +140,7 @@ fn should_subscribe_to_logs() { 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","removed":false,"topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + &format!("0x{:x}", tx_hash) - + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},"subscription":"0x416d77337e24399d"}}"#; + + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},"subscription":"0x43ca64edf03768e1"}}"#; assert_eq!(res, Some(response.into())); // Check notifications (retracted) @@ -141,11 +148,11 @@ fn should_subscribe_to_logs() { 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","removed":true,"topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned() + &format!("0x{:x}", tx_hash) - + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"removed"},"subscription":"0x416d77337e24399d"}}"#; + + r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"removed"},"subscription":"0x43ca64edf03768e1"}}"#; assert_eq!(res, Some(response.into())); // And unsubscribe - let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x43ca64edf03768e1"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(io.handle_request_sync(request, metadata), Some(response.to_owned())); @@ -159,8 +166,9 @@ fn should_subscribe_to_pending_transactions() { let el = Runtime::with_thread_count(1); let client = TestBlockChainClient::new(); - let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); - let handler = pubsub.handler().upgrade().unwrap(); + let (pool_sender, pool_receiver) = mpsc::unbounded(); + + let pubsub = EthPubSubClient::new(Arc::new(client), el.executor(), pool_receiver); let pubsub = pubsub.to_delegate(); let mut io = MetaIoHandler::default(); @@ -177,22 +185,22 @@ fn should_subscribe_to_pending_transactions() { // Subscribe let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newPendingTransactions"], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x43ca64edf03768e1","id":1}"#; assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); // Send new transactions - handler.notify_new_transactions(&[5.into(), 7.into()]); + pool_sender.unbounded_send(Arc::new(vec![H256::from_low_u64_be(5), H256::from_low_u64_be(7)])).unwrap(); let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000005","subscription":"0x416d77337e24399d"}}"#; + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000005","subscription":"0x43ca64edf03768e1"}}"#; assert_eq!(res, Some(response.into())); let (res, receiver) = receiver.into_future().wait().unwrap(); - let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000007","subscription":"0x416d77337e24399d"}}"#; + let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000007","subscription":"0x43ca64edf03768e1"}}"#; assert_eq!(res, Some(response.into())); // And unsubscribe - let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x43ca64edf03768e1"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(io.handle_request_sync(request, metadata), Some(response.to_owned())); @@ -201,11 +209,12 @@ fn should_subscribe_to_pending_transactions() { } #[test] -fn should_return_unimplemented() { +fn eth_subscribe_syncing() { // given let el = Runtime::with_thread_count(1); let client = TestBlockChainClient::new(); - let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor()); + let (_, pool_receiver) = mpsc::unbounded(); + let pubsub = EthPubSubClient::new(Arc::new(client), el.executor(), pool_receiver); let pubsub = pubsub.to_delegate(); let mut io = MetaIoHandler::default(); @@ -215,8 +224,7 @@ fn should_return_unimplemented() { let (sender, _receiver) = futures::sync::mpsc::channel(8); metadata.session = Some(Arc::new(Session::new(sender))); - // Subscribe - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not implemented yet. Please create an issue on Github repo."},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x43ca64edf03768e1","id":1}"#; let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["syncing"], "id": 1}"#; assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/manage_network.rs b/rpc/src/v1/tests/mocked/manage_network.rs index d327a8743c..dfd7c7799d 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -31,5 +31,5 @@ impl ManageNetwork for TestManageNetwork { fn start_network(&self) {} fn stop_network(&self) {} fn num_peers_range(&self) -> RangeInclusive { 25..=50 } - fn with_proto_context(&self, _: ProtocolId, _: &mut FnMut(&NetworkContext)) { } + fn with_proto_context(&self, _: ProtocolId, _: &mut dyn FnMut(&dyn NetworkContext)) { } } diff --git a/rpc/src/v1/tests/mocked/mod.rs b/rpc/src/v1/tests/mocked/mod.rs index 35d109b171..41ca1f8614 100644 --- a/rpc/src/v1/tests/mocked/mod.rs +++ b/rpc/src/v1/tests/mocked/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/tests/mocked/net.rs b/rpc/src/v1/tests/mocked/net.rs index ff6d152d82..c7f862bff3 100644 --- a/rpc/src/v1/tests/mocked/net.rs +++ b/rpc/src/v1/tests/mocked/net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 6608589f55..a9c023f68c 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,13 +15,17 @@ // along with Parity Ethereum. If not, see . use std::sync::Arc; -use ethcore::client::{TestBlockChainClient, Executed, TransactionId}; +use ethcore::test_helpers::TestBlockChainClient; use ethcore_logger::RotatingLogger; -use ethereum_types::{Address, U256, H256}; -use ethstore::ethkey::{Generator, Random}; +use ethereum_types::{Address, U256, H256, BigEndianHash, Bloom}; +use crypto::publickey::{Generator, Random}; +use machine::executed::Executed; use miner::pool::local_transactions::Status as LocalTransactionStatus; use sync::ManageNetwork; -use types::receipt::{LocalizedReceipt, TransactionOutcome}; +use types::{ + ids::TransactionId, + receipt::{LocalizedReceipt, TransactionOutcome}, +}; use jsonrpc_core::IoHandler; use v1::{Parity, ParityClient}; @@ -41,7 +45,7 @@ pub struct Dependencies { pub updater: Arc, pub logger: Arc, pub settings: Arc, - pub network: Arc, + pub network: Arc, pub ws_address: Option, } @@ -126,7 +130,7 @@ fn rpc_parity_version_info() { let io = deps.default_client(); let request = r#"{"jsonrpc": "2.0", "method": "parity_versionInfo", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"hash":"0x0000000000000000000000000000000000000096","track":"beta","version":{"major":1,"minor":5,"patch":0}},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"hash":"0x0000000000000000000000000000000000000096","track":"stable","version":{"major":1,"minor":5,"patch":0}},"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } @@ -136,7 +140,7 @@ fn rpc_parity_releases_info() { let io = deps.default_client(); let request = r#"{"jsonrpc": "2.0", "method": "parity_releasesInfo", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"fork":15100,"minor":null,"this_fork":15000,"track":{"binary":"0x00000000000000000000000000000000000000000000000000000000000005e6","fork":15100,"is_critical":true,"version":{"hash":"0x0000000000000000000000000000000000000097","track":"beta","version":{"major":1,"minor":5,"patch":1}}}},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"fork":15100,"minor":null,"this_fork":15000,"track":{"binary":"0x00000000000000000000000000000000000000000000000000000000000005e6","fork":15100,"is_critical":true,"version":{"hash":"0x0000000000000000000000000000000000000097","track":"stable","version":{"major":1,"minor":5,"patch":1}}}},"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } @@ -312,7 +316,7 @@ fn rpc_parity_unsigned_transactions_count_when_signer_disabled() { } #[test] -fn rpc_parity_pending_transactions() { +fn rpc_parity_pending_transactions_without_limit_without_filter() { let deps = Dependencies::new(); let io = deps.default_client(); @@ -322,6 +326,39 @@ fn rpc_parity_pending_transactions() { assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } +#[test] +fn rpc_parity_pending_transactions_with_limit_without_filter() { + let deps = Dependencies::new(); + let io = deps.default_client(); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_pendingTransactions", "params":[5], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); +} + +#[test] +fn rpc_parity_pending_transactions_without_limit_with_filter() { + let deps = Dependencies::new(); + let io = deps.default_client(); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_pendingTransactions", "params":[null,{"to":{"eq":"0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6"},"gas":{"gt":"0x493e0"}}], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); +} + +#[test] +fn rpc_parity_pending_transactions_with_limit_with_filter() { + let deps = Dependencies::new(); + let io = deps.default_client(); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_pendingTransactions", "params":[5,{"to":{"eq":"0xe8b2d01ffa0a15736b2370b6e5064f9702c891b6"},"gas":{"gt":"0x493e0"}}], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); +} + #[test] fn rpc_parity_encrypt() { let deps = Dependencies::new(); @@ -353,7 +390,7 @@ fn rpc_parity_ws_address() { #[test] fn rpc_parity_next_nonce() { let deps = Dependencies::new(); - let address = Address::default(); + let address = Address::zero(); let io1 = deps.default_client(); let deps = Dependencies::new(); deps.miner.increment_nonce(&address); @@ -396,10 +433,10 @@ fn rpc_parity_local_transactions() { action: ::types::transaction::Action::Create, data: vec![1, 2, 3], nonce: 0.into(), - }.fake_sign(3.into()); + }.fake_sign(Address::from_low_u64_be(3)); 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())); + deps.miner.local_transactions.lock().insert(H256::from_low_u64_be(10), LocalTransactionStatus::Pending(tx.clone())); + deps.miner.local_transactions.lock().insert(H256::from_low_u64_be(15), 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":"pending"}},"id":1}"#; @@ -412,8 +449,8 @@ fn rpc_parity_chain_status() { let deps = Dependencies::new(); let io = deps.default_client(); - *deps.client.ancient_block.write() = Some((H256::default(), 5)); - *deps.client.first_block.write() = Some((H256::from(U256::from(1234)), 3333)); + *deps.client.ancient_block.write() = Some((H256::zero(), 5)); + *deps.client.first_block.write() = Some((BigEndianHash::from_uint(&U256::from(1234)), 3333)); let request = r#"{"jsonrpc": "2.0", "method": "parity_chainStatus", "params":[], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":{"blockGap":["0x6","0xd05"]},"id":1}"#; @@ -484,19 +521,19 @@ fn rpc_parity_call() { fn rpc_parity_block_receipts() { let deps = Dependencies::new(); deps.client.receipts.write() - .insert(TransactionId::Hash(1.into()), LocalizedReceipt { - transaction_hash: 1.into(), + .insert(TransactionId::Hash(H256::from_low_u64_be(1)), LocalizedReceipt { + transaction_hash: H256::from_low_u64_be(1), transaction_index: 0, - block_hash: 3.into(), + block_hash: H256::from_low_u64_be(3), block_number: 0, cumulative_gas_used: 21_000.into(), gas_used: 21_000.into(), contract_address: None, logs: vec![], - log_bloom: 1.into(), + log_bloom: Bloom::from_low_u64_be(1), outcome: TransactionOutcome::Unknown, to: None, - from: 9.into(), + from: Address::from_low_u64_be(9), }); let io = deps.default_client(); @@ -506,7 +543,7 @@ fn rpc_parity_block_receipts() { "params": [], "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","result":[{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000003","blockNumber":"0x0","contractAddress":null,"cumulativeGasUsed":"0x5208","from":"0x0000000000000000000000000000000000000009","gasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","root":null,"status":null,"to":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000001","transactionIndex":"0x0"}],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":[{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000003","blockNumber":"0x0","contractAddress":null,"cumulativeGasUsed":"0x5208","from":"0x0000000000000000000000000000000000000009","gasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","to":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000001","transactionIndex":"0x0"}],"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 5b2e0762b1..09807afadf 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,6 +15,7 @@ // along with Parity Ethereum. If not, see . use std::sync::Arc; +use std::str::FromStr; use accounts::{AccountProvider, AccountProviderSettings}; use ethereum_types::Address; @@ -74,7 +75,7 @@ fn rpc_parity_accounts_info() { assert_eq!(accounts.len(), 1); let address = accounts[0]; - tester.accounts.set_address_name(1.into(), "XX".into()); + tester.accounts.set_address_name(Address::from_low_u64_be(1), "XX".into()); tester.accounts.set_account_name(address.clone(), "Test".into()).unwrap(); tester.accounts.set_account_meta(address.clone(), "{foo: 69}".into()).unwrap(); @@ -89,7 +90,7 @@ fn rpc_parity_default_account() { let io = tester.io; // Check empty - let address = Address::default(); + let address = Address::zero(); let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultAccount", "params": [], "id": 1}"#; let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":\"0x{:x}\",\"id\":1}}", address); assert_eq!(io.handle_request_sync(request), Some(response)); @@ -456,7 +457,7 @@ fn should_import_wallet() { assert_eq!(res, response); - let account_meta = tester.accounts.account_meta("0x00bac56a8a27232baa044c03f43bf3648c961735".into()).unwrap(); + let account_meta = tester.accounts.account_meta(Address::from_str("00bac56a8a27232baa044c03f43bf3648c961735").unwrap()).unwrap(); let account_uuid: String = account_meta.uuid.unwrap().into(); // the RPC should import the account with a new id diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index 25c13fb1cb..249d2806fe 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use rustc_hex::FromHex; use ethereum_types::{U256, Address}; use ethcore::miner::MinerService; -use ethcore::client::TestBlockChainClient; +use ethcore::test_helpers::TestBlockChainClient; use sync::ManageNetwork; use jsonrpc_core::IoHandler; @@ -58,7 +58,7 @@ fn parity_set_client( client, miner, updater, - &(net.clone() as Arc), + &(net.clone() as Arc), FakeFetch::new(Some(1)), ) } @@ -91,7 +91,7 @@ fn rpc_parity_upgrade_ready() { io.extend_with(parity_set_client(&client, &miner, &updater, &network).to_delegate()); let request = r#"{"jsonrpc": "2.0", "method": "parity_upgradeReady", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"binary":"0x00000000000000000000000000000000000000000000000000000000000005e6","fork":15100,"is_critical":true,"version":{"hash":"0x0000000000000000000000000000000000000097","track":"beta","version":{"major":1,"minor":5,"patch":1}}},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"binary":"0x00000000000000000000000000000000000000000000000000000000000005e6","fork":15100,"is_critical":true,"version":{"hash":"0x0000000000000000000000000000000000000097","track":"stable","version":{"major":1,"minor":5,"patch":1}}},"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); updater.set_updated(true); @@ -230,11 +230,11 @@ fn rpc_parity_remove_transaction() { nonce: 1.into(), gas_price: 0x9184e72a000u64.into(), gas: 0x76c0.into(), - action: Action::Call(5.into()), + action: Action::Call(Address::from_low_u64_be(5)), value: 0x9184e72au64.into(), data: vec![] }; - let signed = tx.fake_sign(2.into()); + let signed = tx.fake_sign(Address::from_low_u64_be(2)); let hash = signed.hash(); let request = r#"{"jsonrpc": "2.0", "method": "parity_removeTransaction", "params":[""#.to_owned() + &format!("0x{:x}", hash) + r#""], "id": 1}"#; @@ -268,4 +268,3 @@ fn rpc_parity_set_engine_signer() { let signature = miner.signer.read().as_ref().unwrap().sign(::hash::keccak("x")).unwrap().to_vec(); assert_eq!(&format!("{}", signature.pretty()), "6f46069ded2154af6e806706e4f7f6fd310ac45f3c6dccb85f11c0059ee20a09245df0a0008bb84a10882b1298284bc93058e7bc5938ea728e77620061687a6401"); } - diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index a2d6b87ce2..54a09c7a1b 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use std::str::FromStr; use bytes::ToPretty; use accounts::AccountProvider; use ethereum_types::{Address, H520, U256}; -use ethcore::client::TestBlockChainClient; +use ethcore::test_helpers::TestBlockChainClient; use jsonrpc_core::IoHandler; use parking_lot::Mutex; use types::transaction::{Action, Transaction}; @@ -34,7 +34,7 @@ use v1::tests::helpers::TestMinerService; use v1::types::{EIP191Version, PresignedTransaction}; use rustc_hex::ToHex; use serde_json::to_value; -use ethkey::Secret; +use crypto::publickey::Secret; struct PersonalTester { _runtime: Runtime, @@ -363,7 +363,7 @@ fn sign_eip191_with_validator() { }"#; let with_validator = to_value(PresignedTransaction { validator: address.into(), - data: keccak("hello world").to_vec().into() + data: keccak("hello world").as_bytes().to_vec().into() }).unwrap(); let result = eip191::hash_message(EIP191Version::PresignedTransaction, with_validator).unwrap(); let result = tester.accounts.sign(address, Some("password123".into()), result).unwrap().into_electrum(); diff --git a/rpc/src/v1/tests/mocked/pubsub.rs b/rpc/src/v1/tests/mocked/pubsub.rs index c0f664d5fc..30dc11df57 100644 --- a/rpc/src/v1/tests/mocked/pubsub.rs +++ b/rpc/src/v1/tests/mocked/pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -53,22 +53,22 @@ fn should_subscribe_to_a_method() { // Subscribe let request = r#"{"jsonrpc": "2.0", "method": "parity_subscribe", "params": ["hello", []], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"0x43ca64edf03768e1","id":1}"#; assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned())); // Check notifications let (res, receiver) = receiver.into_future().wait().unwrap(); let response = - r#"{"jsonrpc":"2.0","method":"parity_subscription","params":{"result":"hello","subscription":"0x416d77337e24399d"}}"#; + r#"{"jsonrpc":"2.0","method":"parity_subscription","params":{"result":"hello","subscription":"0x43ca64edf03768e1"}}"#; assert_eq!(res, Some(response.into())); let (res, receiver) = receiver.into_future().wait().unwrap(); let response = - r#"{"jsonrpc":"2.0","method":"parity_subscription","params":{"result":"world","subscription":"0x416d77337e24399d"}}"#; + r#"{"jsonrpc":"2.0","method":"parity_subscription","params":{"result":"world","subscription":"0x43ca64edf03768e1"}}"#; assert_eq!(res, Some(response.into())); // And unsubscribe - let request = r#"{"jsonrpc": "2.0", "method": "parity_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#; + let request = r#"{"jsonrpc": "2.0", "method": "parity_unsubscribe", "params": ["0x43ca64edf03768e1"], "id": 1}"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(io.handle_request_sync(request, metadata), Some(response.to_owned())); diff --git a/rpc/src/v1/tests/mocked/rpc.rs b/rpc/src/v1/tests/mocked/rpc.rs index d4634ac90e..939880c199 100644 --- a/rpc/src/v1/tests/mocked/rpc.rs +++ b/rpc/src/v1/tests/mocked/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/tests/mocked/secretstore.rs b/rpc/src/v1/tests/mocked/secretstore.rs index 96e20d0028..b7cf32317a 100644 --- a/rpc/src/v1/tests/mocked/secretstore.rs +++ b/rpc/src/v1/tests/mocked/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use std::sync::Arc; use crypto::DEFAULT_MAC; use accounts::AccountProvider; use ethereum_types::H256; -use ethkey::{KeyPair, Signature, verify_public}; +use crypto::publickey::{KeyPair, Signature, verify_public}; use serde_json; use jsonrpc_core::{IoHandler, Success}; diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index e22c5b8d24..29b8e57279 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,7 +20,7 @@ use ethereum_types::{H520, U256, Address}; use bytes::ToPretty; use accounts::AccountProvider; -use ethcore::client::TestBlockChainClient; +use ethcore::test_helpers::TestBlockChainClient; use parity_runtime::Runtime; use parking_lot::Mutex; use rlp::encode; @@ -84,7 +84,7 @@ fn should_return_list_of_items_to_confirm() { // given let tester = signer_tester(); let _send_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: Address::from(1), + from: Address::from_low_u64_be(1), used_default_from: false, to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: U256::from(10_000), @@ -94,7 +94,7 @@ fn should_return_list_of_items_to_confirm() { nonce: None, condition: None, }), Origin::Unknown).unwrap(); - let _sign_future = tester.signer.add_request(ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()), Origin::Unknown).unwrap(); + let _sign_future = tester.signer.add_request(ConfirmationPayload::EthSignMessage(Address::from_low_u64_be(1), vec![5].into()), Origin::Unknown).unwrap(); // when let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#; @@ -114,7 +114,7 @@ fn should_reject_transaction_from_queue_without_dispatching() { // given let tester = signer_tester(); let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: Address::from(1), + from: Address::from_low_u64_be(1), used_default_from: false, to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: U256::from(10_000), @@ -141,7 +141,7 @@ fn should_not_remove_transaction_if_password_is_invalid() { // given let tester = signer_tester(); let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: Address::from(1), + from: Address::from_low_u64_be(1), used_default_from: false, to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), gas_price: U256::from(10_000), @@ -166,7 +166,7 @@ fn should_not_remove_transaction_if_password_is_invalid() { fn should_not_remove_sign_if_password_is_invalid() { // given let tester = signer_tester(); - let _confirmation_future = tester.signer.add_request(ConfirmationPayload::EthSignMessage(0.into(), vec![5].into()), Origin::Unknown).unwrap(); + let _confirmation_future = tester.signer.add_request(ConfirmationPayload::EthSignMessage(Address::zero(), vec![5].into()), Origin::Unknown).unwrap(); assert_eq!(tester.signer.requests().len(), 1); // when @@ -231,7 +231,7 @@ fn should_alter_the_sender_and_nonce() { let tester = signer_tester(); let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: 0.into(), + from: Address::zero(), used_default_from: false, to: Some(recipient), gas_price: U256::from(10_000), @@ -377,7 +377,7 @@ fn should_return_error_when_sender_does_not_match() { let address = tester.accounts.new_account(&"test".into()).unwrap(); let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(); let _confirmation_future = tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest { - from: Address::default(), + from: Address::zero(), used_default_from: false, to: Some(recipient), gas_price: U256::from(10_000), diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 39385d19bd..99645f8e49 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -33,14 +33,13 @@ use v1::tests::mocked::parity; use accounts::AccountProvider; use bytes::ToPretty; -use ethereum_types::{U256, Address}; -use ethcore::client::TestBlockChainClient; -use ethkey::Secret; -use ethstore::ethkey::{Generator, Random}; +use ethcore::test_helpers::TestBlockChainClient; +use ethereum_types::{U256, Address, Signature, H256}; +use crypto::publickey::{Generator, Random, Secret}; +use parity_runtime::{Runtime, Executor}; use parking_lot::Mutex; use serde_json; use types::transaction::{Transaction, Action, SignedTransaction}; -use parity_runtime::{Runtime, Executor}; struct SigningTester { pub runtime: Runtime, @@ -51,10 +50,10 @@ struct SigningTester { pub io: IoHandler, } -impl Default for SigningTester { - fn default() -> Self { +impl SigningTester { + fn new(signing_queue_enabled: bool) ->Self { let runtime = Runtime::with_thread_count(1); - let signer = Arc::new(SignerService::new_test(false)); + let signer = Arc::new(SignerService::new_test(signing_queue_enabled)); let client = Arc::new(TestBlockChainClient::default()); let miner = Arc::new(TestMinerService::default()); let accounts = Arc::new(AccountProvider::transient_provider()); @@ -82,15 +81,15 @@ impl Default for SigningTester { } } -fn eth_signing() -> SigningTester { - SigningTester::default() +fn eth_signing(signing_queue_enabled: bool) -> SigningTester { + SigningTester::new(signing_queue_enabled) } #[test] fn rpc_eth_sign() { use rustc_hex::FromHex; - let tester = eth_signing(); + let tester = eth_signing(true); let account = tester.accounts.insert_account(Secret::from([69u8; 32]), &"abcd".into()).unwrap(); tester.accounts.unlock_account_permanently(account, "abcd".into()).unwrap(); @@ -113,7 +112,7 @@ fn rpc_eth_sign() { #[test] fn should_add_sign_to_queue() { // given - let tester = eth_signing(); + let tester = eth_signing(true); let address = Address::random(); assert_eq!(tester.signer.requests().len(), 0); @@ -138,7 +137,7 @@ fn should_add_sign_to_queue() { if signer.requests().len() == 1 { // respond let sender = signer.take(&1.into()).unwrap(); - signer.request_confirmed(sender, Ok(ConfirmationResponse::Signature(0.into()))); + signer.request_confirmed(sender, Ok(ConfirmationResponse::Signature(Signature::zero()))); break } ::std::thread::sleep(Duration::from_millis(100)) @@ -151,7 +150,7 @@ fn should_add_sign_to_queue() { #[test] fn should_post_sign_to_queue() { // given - let tester = eth_signing(); + let tester = eth_signing(true); let address = Address::random(); assert_eq!(tester.signer.requests().len(), 0); @@ -175,7 +174,7 @@ fn should_post_sign_to_queue() { #[test] fn should_check_status_of_request() { // given - let tester = eth_signing(); + let tester = eth_signing(true); let address = Address::random(); let request = r#"{ "jsonrpc": "2.0", @@ -204,7 +203,7 @@ fn should_check_status_of_request() { #[test] fn should_check_status_of_request_when_its_resolved() { // given - let tester = eth_signing(); + let tester = eth_signing(true); let address = Address::random(); let request = r#"{ "jsonrpc": "2.0", @@ -217,10 +216,10 @@ fn should_check_status_of_request_when_its_resolved() { }"#; tester.io.handle_request_sync(&request).expect("Sent"); let sender = tester.signer.take(&1.into()).unwrap(); - tester.signer.request_confirmed(sender, Ok(ConfirmationResponse::Signature(1.into()))); + tester.signer.request_confirmed(sender, Ok(ConfirmationResponse::Signature(Signature::from_low_u64_be(1)))); // 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)); + thread::sleep(Duration::from_millis(100)); // when let request = r#"{ @@ -235,10 +234,56 @@ fn should_check_status_of_request_when_its_resolved() { assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); } +#[test] +fn eth_sign_locked_account() { + let secret = "8a283037bb19c4fed7b1c569e40c7dcff366165eb869110a1b11532963eb9cb2".parse().unwrap(); + let tester = eth_signing(false); + let address = tester.accounts.insert_account(secret, &"".into()).unwrap(); + + let req_send_trans = r#"{ + "jsonrpc": "2.0", + "method": "eth_sendTransaction", + "params": [{ + "from": ""#.to_owned() + format!("0x{:x}", address).as_ref() + r#"", + "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567", + "gas": "0x30000", + "gasPrice": "0x1", + "value": "0x9184e72a" + }], + "id": 16 + }"#; + + // expected error response + let error_res = r#"{ + "jsonrpc":"2.0", + "error": + { + "code":-32020, + "message":"Your account is locked and the signing queue is disabled. + You can either Unlock the account via CLI, personal_unlockAccount or + enable the signing queue to use Trusted Signer." + }, + "id":16 + }"#; + // dispatch the transaction, without unlocking the account. + assert_eq!( + error_res.replace("\t", "").replace("\n", ""), + tester.io.handle_request_sync(&req_send_trans).unwrap() + ); + // unlock the account + tester.accounts.unlock_account_permanently(address, "".into()).unwrap(); + + // try again, this time account is unlocked. + assert_eq!( + r#"{"jsonrpc":"2.0","result":"0x70df76392fba654351714803d99a90be1d58d165352e0008e376427284314c88","id":16}"#, + tester.io.handle_request_sync(&req_send_trans).unwrap() + ); +} + #[test] fn should_sign_if_account_is_unlocked() { // given - let tester = eth_signing(); + let tester = eth_signing(true); let data = vec![5u8]; let acc = tester.accounts.insert_account(Secret::from([69u8; 32]), &"test".into()).unwrap(); tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap(); @@ -261,7 +306,7 @@ fn should_sign_if_account_is_unlocked() { #[test] fn should_add_transaction_to_queue() { // given - let tester = eth_signing(); + let tester = eth_signing(true); let address = Address::random(); assert_eq!(tester.signer.requests().len(), 0); @@ -289,7 +334,7 @@ fn should_add_transaction_to_queue() { if signer.requests().len() == 1 { // respond let sender = signer.take(&1.into()).unwrap(); - signer.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(0.into()))); + signer.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(H256::zero()))); break } ::std::thread::sleep(Duration::from_millis(100)) @@ -302,7 +347,7 @@ fn should_add_transaction_to_queue() { #[test] fn should_add_sign_transaction_to_the_queue() { // given - let tester = eth_signing(); + let tester = eth_signing(true); let address = tester.accounts.new_account(&"test".into()).unwrap(); assert_eq!(tester.signer.requests().len(), 0); @@ -381,7 +426,7 @@ fn should_add_sign_transaction_to_the_queue() { #[test] fn should_dispatch_transaction_if_account_is_unlock() { // given - let tester = eth_signing(); + let tester = eth_signing(true); let acc = tester.accounts.new_account(&"test".into()).unwrap(); tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap(); @@ -418,7 +463,7 @@ fn should_dispatch_transaction_if_account_is_unlock() { #[test] fn should_decrypt_message_if_account_is_unlocked() { // given - let mut tester = eth_signing(); + let mut tester = eth_signing(true); let parity = parity::Dependencies::new(); tester.io.extend_with(parity.client(None).to_delegate()); let (address, public) = tester.accounts.new_account_and_public(&"test".into()).unwrap(); @@ -450,7 +495,7 @@ fn should_decrypt_message_if_account_is_unlocked() { #[test] fn should_add_decryption_to_the_queue() { // given - let tester = eth_signing(); + let tester = eth_signing(true); let acc = Random.generate().unwrap(); assert_eq!(tester.signer.requests().len(), 0); @@ -487,7 +532,7 @@ fn should_add_decryption_to_the_queue() { #[test] fn should_compose_transaction() { // given - let tester = eth_signing(); + let tester = eth_signing(true); let acc = Random.generate().unwrap(); assert_eq!(tester.signer.requests().len(), 0); let from = format!("{:x}", acc.address()); diff --git a/rpc/src/v1/tests/mocked/signing_unsafe.rs b/rpc/src/v1/tests/mocked/signing_unsafe.rs index a91a85ea1f..11d5f60551 100644 --- a/rpc/src/v1/tests/mocked/signing_unsafe.rs +++ b/rpc/src/v1/tests/mocked/signing_unsafe.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 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::str::FromStr; use std::sync::Arc; use accounts::AccountProvider; -use ethcore::client::TestBlockChainClient; +use ethcore::test_helpers::TestBlockChainClient; use ethereum_types::{U256, Address}; use parity_runtime::Runtime; use parking_lot::Mutex; @@ -30,7 +30,7 @@ use jsonrpc_core::IoHandler; use v1::{EthClientOptions, EthSigning, SigningUnsafeClient}; use v1::helpers::nonce; use v1::helpers::dispatch::{self, FullDispatcher}; -use v1::tests::helpers::{TestMinerService}; +use v1::tests::helpers::TestMinerService; use v1::metadata::Metadata; fn blockchain_client() -> Arc { diff --git a/rpc/src/v1/tests/mocked/traces.rs b/rpc/src/v1/tests/mocked/traces.rs index 89cf198aa1..0ae9e61954 100644 --- a/rpc/src/v1/tests/mocked/traces.rs +++ b/rpc/src/v1/tests/mocked/traces.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,12 +16,14 @@ use std::sync::Arc; -use ethcore::executed::{Executed, CallError}; -use ethcore::trace::trace::{Action, Res, Call}; -use ethcore::trace::LocalizedTrace; -use ethcore::client::TestBlockChainClient; +use machine::executed::Executed; +use trace::trace::{Action, Res, Call}; +use trace::LocalizedTrace; +use ethcore::test_helpers::TestBlockChainClient; +use ethereum_types::{Address, H256}; -use vm::CallType; +use types::transaction::CallError; +use trace::trace::CallType; use jsonrpc_core::IoHandler; use v1::tests::helpers::{TestMinerService}; @@ -37,20 +39,20 @@ fn io() -> Tester { let client = Arc::new(TestBlockChainClient::new()); *client.traces.write() = Some(vec![LocalizedTrace { action: Action::Call(Call { - from: 0xf.into(), - to: 0x10.into(), + from: Address::from_low_u64_be(0xf), + to: Address::from_low_u64_be(0x10), value: 0x1.into(), gas: 0x100.into(), input: vec![1, 2, 3], - call_type: CallType::Call, + call_type: Some(CallType::Call).into(), }), result: Res::None, subtraces: 0, trace_address: vec![0], transaction_number: Some(0), - transaction_hash: Some(5.into()), + transaction_hash: Some(H256::from_low_u64_be(5)), block_number: 10, - block_hash: 10.into(), + block_hash: H256::from_low_u64_be(10), }]); *client.execution_result.write() = Some(Ok(Executed { exception: None, diff --git a/rpc/src/v1/tests/mocked/web3.rs b/rpc/src/v1/tests/mocked/web3.rs index 5590d5d283..7c729d011f 100644 --- a/rpc/src/v1/tests/mocked/web3.rs +++ b/rpc/src/v1/tests/mocked/web3.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/tests/mod.rs b/rpc/src/v1/tests/mod.rs index 83f9dca905..b3996b179d 100644 --- a/rpc/src/v1/tests/mod.rs +++ b/rpc/src/v1/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -32,7 +32,7 @@ macro_rules! extract_chain { (iter $file:expr) => {{ const RAW_DATA: &'static [u8] = include_bytes!(concat!("../../../../ethcore/res/ethereum/tests/", $file, ".json")); - ::ethjson::blockchain::Test::load(RAW_DATA).unwrap().into_iter() + ethjson::test_helpers::blockchain::Test::load(RAW_DATA).unwrap().into_iter() }}; ($file:expr) => {{ diff --git a/rpc/src/v1/traits/debug.rs b/rpc/src/v1/traits/debug.rs index 5d332d434a..573ee5a3da 100644 --- a/rpc/src/v1/traits/debug.rs +++ b/rpc/src/v1/traits/debug.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use jsonrpc_derive::rpc; use v1::types::RichBlock; /// Debug RPC interface. -#[rpc] +#[rpc(server)] pub trait Debug { /// Returns recently seen bad blocks. #[rpc(name = "debug_getBadBlocks")] diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index 69a37ae526..07bba3f6b8 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -23,7 +23,7 @@ use v1::types::{RichBlock, BlockNumber, Bytes, CallRequest, Filter, FilterChange use v1::types::{Log, Receipt, SyncStatus, Transaction, Work}; /// Eth rpc interface. -#[rpc] +#[rpc(server)] pub trait Eth { /// RPC Metadata type Metadata; @@ -68,87 +68,87 @@ pub trait Eth { /// Returns balance of the given account. #[rpc(name = "eth_getBalance")] - fn balance(&self, H160, Option) -> BoxFuture; + fn balance(&self, _: H160, _: Option) -> BoxFuture; /// Returns the account- and storage-values of the specified account including the Merkle-proof #[rpc(name = "eth_getProof")] - fn proof(&self, H160, Vec, Option) -> BoxFuture; + fn proof(&self, _: H160, _: Vec, _: Option) -> BoxFuture; /// Returns content of the storage at given address. #[rpc(name = "eth_getStorageAt")] - fn storage_at(&self, H160, U256, Option) -> BoxFuture; + fn storage_at(&self, _: H160, _: U256, _: Option) -> BoxFuture; /// Returns block with given hash. #[rpc(name = "eth_getBlockByHash")] - fn block_by_hash(&self, H256, bool) -> BoxFuture>; + fn block_by_hash(&self, _: H256, _: bool) -> BoxFuture>; /// Returns block with given number. #[rpc(name = "eth_getBlockByNumber")] - fn block_by_number(&self, BlockNumber, bool) -> BoxFuture>; + fn block_by_number(&self, _: BlockNumber, _: bool) -> BoxFuture>; /// Returns the number of transactions sent from given address at given time (block number). #[rpc(name = "eth_getTransactionCount")] - fn transaction_count(&self, H160, Option) -> BoxFuture; + fn transaction_count(&self, _: H160, _: Option) -> BoxFuture; /// Returns the number of transactions in a block with given hash. #[rpc(name = "eth_getBlockTransactionCountByHash")] - fn block_transaction_count_by_hash(&self, H256) -> BoxFuture>; + fn block_transaction_count_by_hash(&self, _: H256) -> BoxFuture>; /// Returns the number of transactions in a block with given block number. #[rpc(name = "eth_getBlockTransactionCountByNumber")] - fn block_transaction_count_by_number(&self, BlockNumber) -> BoxFuture>; + fn block_transaction_count_by_number(&self, _: BlockNumber) -> BoxFuture>; /// Returns the number of uncles in a block with given hash. #[rpc(name = "eth_getUncleCountByBlockHash")] - fn block_uncles_count_by_hash(&self, H256) -> BoxFuture>; + fn block_uncles_count_by_hash(&self, _: H256) -> BoxFuture>; /// Returns the number of uncles in a block with given block number. #[rpc(name = "eth_getUncleCountByBlockNumber")] - fn block_uncles_count_by_number(&self, BlockNumber) -> BoxFuture>; + fn block_uncles_count_by_number(&self, _: BlockNumber) -> BoxFuture>; /// Returns the code at given address at given time (block number). #[rpc(name = "eth_getCode")] - fn code_at(&self, H160, Option) -> BoxFuture; + fn code_at(&self, _: H160, _: Option) -> BoxFuture; /// Sends signed transaction, returning its hash. #[rpc(name = "eth_sendRawTransaction")] - fn send_raw_transaction(&self, Bytes) -> Result; + fn send_raw_transaction(&self, _: Bytes) -> Result; /// @alias of `eth_sendRawTransaction`. #[rpc(name = "eth_submitTransaction")] - fn submit_transaction(&self, Bytes) -> Result; + fn submit_transaction(&self, _: Bytes) -> Result; /// Call contract, returning the output data. #[rpc(name = "eth_call")] - fn call(&self, CallRequest, Option) -> BoxFuture; + fn call(&self, _: CallRequest, _: Option) -> BoxFuture; /// Estimate gas needed for execution of given contract. #[rpc(name = "eth_estimateGas")] - fn estimate_gas(&self, CallRequest, Option) -> BoxFuture; + fn estimate_gas(&self, _: CallRequest, _: Option) -> BoxFuture; /// Get transaction by its hash. #[rpc(name = "eth_getTransactionByHash")] - fn transaction_by_hash(&self, H256) -> BoxFuture>; + fn transaction_by_hash(&self, _: H256) -> BoxFuture>; /// Returns transaction at given block hash and index. #[rpc(name = "eth_getTransactionByBlockHashAndIndex")] - fn transaction_by_block_hash_and_index(&self, H256, Index) -> BoxFuture>; + fn transaction_by_block_hash_and_index(&self, _: H256, _: Index) -> BoxFuture>; /// Returns transaction by given block number and index. #[rpc(name = "eth_getTransactionByBlockNumberAndIndex")] - fn transaction_by_block_number_and_index(&self, BlockNumber, Index) -> BoxFuture>; + fn transaction_by_block_number_and_index(&self, _: BlockNumber, _: Index) -> BoxFuture>; /// Returns transaction receipt by transaction hash. #[rpc(name = "eth_getTransactionReceipt")] - fn transaction_receipt(&self, H256) -> BoxFuture>; + fn transaction_receipt(&self, _: H256) -> BoxFuture>; /// Returns an uncles at given block and index. #[rpc(name = "eth_getUncleByBlockHashAndIndex")] - fn uncle_by_block_hash_and_index(&self, H256, Index) -> BoxFuture>; + fn uncle_by_block_hash_and_index(&self, _: H256, _: Index) -> BoxFuture>; /// Returns an uncles at given block and index. #[rpc(name = "eth_getUncleByBlockNumberAndIndex")] - fn uncle_by_block_number_and_index(&self, BlockNumber, Index) -> BoxFuture>; + fn uncle_by_block_number_and_index(&self, _: BlockNumber, _: Index) -> BoxFuture>; /// Returns available compilers. /// @deprecated @@ -158,42 +158,42 @@ pub trait Eth { /// Compiles lll code. /// @deprecated #[rpc(name = "eth_compileLLL")] - fn compile_lll(&self, String) -> Result; + fn compile_lll(&self, _: String) -> Result; /// Compiles solidity. /// @deprecated #[rpc(name = "eth_compileSolidity")] - fn compile_solidity(&self, String) -> Result; + fn compile_solidity(&self, _: String) -> Result; /// Compiles serpent. /// @deprecated #[rpc(name = "eth_compileSerpent")] - fn compile_serpent(&self, String) -> Result; + fn compile_serpent(&self, _: String) -> Result; /// Returns logs matching given filter object. #[rpc(name = "eth_getLogs")] - fn logs(&self, Filter) -> BoxFuture>; + fn logs(&self, _: Filter) -> BoxFuture>; /// Returns the hash of the current block, the seedHash, and the boundary condition to be met. #[rpc(name = "eth_getWork")] - fn work(&self, Option) -> Result; + fn work(&self, _: Option) -> Result; /// Used for submitting a proof-of-work solution. #[rpc(name = "eth_submitWork")] - fn submit_work(&self, H64, H256, H256) -> Result; + fn submit_work(&self, _: H64, _: H256, _: H256) -> Result; /// Used for submitting mining hashrate. #[rpc(name = "eth_submitHashrate")] - fn submit_hashrate(&self, U256, H256) -> Result; + fn submit_hashrate(&self, _: U256, _: H256) -> Result; } /// Eth filters rpc api (polling). // TODO: do filters api properly -#[rpc] +#[rpc(server)] pub trait EthFilter { /// Returns id of new filter. #[rpc(name = "eth_newFilter")] - fn new_filter(&self, Filter) -> Result; + fn new_filter(&self, _: Filter) -> Result; /// Returns id of new block filter. #[rpc(name = "eth_newBlockFilter")] @@ -205,13 +205,13 @@ pub trait EthFilter { /// Returns filter changes since last poll. #[rpc(name = "eth_getFilterChanges")] - fn filter_changes(&self, Index) -> BoxFuture; + fn filter_changes(&self, _: Index) -> BoxFuture; /// Returns all logs matching given filter (in a range 'from' - 'to'). #[rpc(name = "eth_getFilterLogs")] - fn filter_logs(&self, Index) -> BoxFuture>; + fn filter_logs(&self, _: Index) -> BoxFuture>; /// Uninstalls filter. #[rpc(name = "eth_uninstallFilter")] - fn uninstall_filter(&self, Index) -> Result; + fn uninstall_filter(&self, _: Index) -> Result; } diff --git a/rpc/src/v1/traits/eth_pubsub.rs b/rpc/src/v1/traits/eth_pubsub.rs index 0628781392..e06e410d37 100644 --- a/rpc/src/v1/traits/eth_pubsub.rs +++ b/rpc/src/v1/traits/eth_pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -23,16 +23,22 @@ use jsonrpc_pubsub::{typed, SubscriptionId}; use v1::types::pubsub; /// Eth PUB-SUB rpc interface. -#[rpc] +#[rpc(server)] pub trait EthPubSub { /// RPC Metadata type Metadata; /// Subscribe to Eth subscription. #[pubsub(subscription = "eth_subscription", subscribe, name = "eth_subscribe")] - fn subscribe(&self, Self::Metadata, typed::Subscriber, pubsub::Kind, Option); + fn subscribe( + &self, + _: Self::Metadata, + _: typed::Subscriber, + _: pubsub::Kind, + _: Option, + ); /// Unsubscribe from existing Eth subscription. #[pubsub(subscription = "eth_subscription", unsubscribe, name = "eth_unsubscribe")] - fn unsubscribe(&self, Option, SubscriptionId) -> Result; + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; } diff --git a/rpc/src/v1/traits/eth_signing.rs b/rpc/src/v1/traits/eth_signing.rs index 72e13ddabe..0e65d7804c 100644 --- a/rpc/src/v1/traits/eth_signing.rs +++ b/rpc/src/v1/traits/eth_signing.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -23,24 +23,24 @@ use ethereum_types::{H160, H256, H520}; use v1::types::{Bytes, TransactionRequest, RichRawTransaction}; /// Signing methods implementation relying on unlocked accounts. -#[rpc] +#[rpc(server)] pub trait EthSigning { /// RPC Metadata type Metadata; /// Signs the hash of data with given address signature. #[rpc(meta, name = "eth_sign")] - fn sign(&self, Self::Metadata, H160, Bytes) -> BoxFuture; + fn sign(&self, _: Self::Metadata, _: H160, _: Bytes) -> BoxFuture; /// Sends transaction; will block waiting for signer to return the /// transaction hash. /// If Signer is disable it will require the account to be unlocked. #[rpc(meta, name = "eth_sendTransaction")] - fn send_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture; + fn send_transaction(&self, _: Self::Metadata, _: TransactionRequest) -> BoxFuture; /// Signs transactions without dispatching it to the network. /// Returns signed transaction RLP representation and the transaction itself. /// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`. #[rpc(meta, name = "eth_signTransaction")] - fn sign_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture; + fn sign_transaction(&self, _: Self::Metadata, _: TransactionRequest) -> BoxFuture; } diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index e25ca76ac4..5aee5c0cc9 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -32,6 +32,7 @@ pub mod rpc; pub mod secretstore; pub mod signer; pub mod traces; +pub mod transactions_pool; pub mod web3; pub use self::debug::Debug; @@ -50,4 +51,5 @@ pub use self::rpc::Rpc; pub use self::secretstore::SecretStore; pub use self::signer::Signer; pub use self::traces::Traces; +pub use self::transactions_pool::TransactionsPool; pub use self::web3::Web3; diff --git a/rpc/src/v1/traits/net.rs b/rpc/src/v1/traits/net.rs index a16729294d..b947e63876 100644 --- a/rpc/src/v1/traits/net.rs +++ b/rpc/src/v1/traits/net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use jsonrpc_core::Result; use jsonrpc_derive::rpc; /// Net rpc interface. -#[rpc] +#[rpc(server)] pub trait Net { /// Returns protocol version. #[rpc(name = "net_version")] diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index e3821355ee..728dc31e71 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,6 +19,7 @@ use std::collections::BTreeMap; use ethereum_types::{H64, H160, H256, H512, U64, U256}; +use ethcore::miner::FilterOptions; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_derive::rpc; use v1::types::{ @@ -31,7 +32,7 @@ use v1::types::{ }; /// Parity-specific rpc interface. -#[rpc] +#[rpc(server)] pub trait Parity { /// RPC Metadata type Metadata; @@ -103,7 +104,7 @@ pub trait Parity { /// Returns whatever address would be derived from the given phrase if it were to seed a brainwallet. #[rpc(name = "parity_phraseToAddress")] - fn phrase_to_address(&self, String) -> Result; + fn phrase_to_address(&self, _: String) -> Result; /// Returns the value of the registrar for this network. #[rpc(name = "parity_registryAddress")] @@ -111,21 +112,27 @@ pub trait Parity { /// Returns all addresses if Fat DB is enabled (`--fat-db`), or null if not. #[rpc(name = "parity_listAccounts")] - fn list_accounts(&self, u64, Option, Option) -> Result>>; + fn list_accounts(&self, _: u64, _: Option, _: Option) -> Result>>; /// Returns all storage keys of the given address (first parameter) if Fat DB is enabled (`--fat-db`), /// or null if not. #[rpc(name = "parity_listStorageKeys")] - fn list_storage_keys(&self, H160, u64, Option, Option) -> Result>>; + fn list_storage_keys( + &self, + _: H160, + _: Option, + _: Option, + _: Option, + ) -> Result>>; /// Encrypt some data with a public key under ECIES. /// First parameter is the 512-byte destination public key, second is the message. #[rpc(name = "parity_encryptMessage")] - fn encrypt_message(&self, H512, Bytes) -> Result; + fn encrypt_message(&self, _: H512, _: Bytes) -> Result; /// Returns all pending transactions from transaction queue. #[rpc(name = "parity_pendingTransactions")] - fn pending_transactions(&self, Option) -> Result>; + fn pending_transactions(&self, _: Option, _: Option) -> Result>; /// Returns all transactions from transaction queue. /// @@ -155,7 +162,7 @@ pub trait Parity { /// Returns next nonce for particular sender. Should include all transactions in the queue. #[rpc(name = "parity_nextNonce")] - fn next_nonce(&self, H160) -> BoxFuture; + fn next_nonce(&self, _: H160) -> BoxFuture; /// Get the mode. Returns one of: "active", "passive", "dark", "offline". #[rpc(name = "parity_mode")] @@ -192,26 +199,26 @@ pub trait Parity { /// Get block header. /// Same as `eth_getBlockByNumber` but without uncles and transactions. #[rpc(name = "parity_getBlockHeaderByNumber")] - fn block_header(&self, Option) -> BoxFuture; + fn block_header(&self, _: Option) -> BoxFuture; /// Get block receipts. /// Allows you to fetch receipts from the entire block at once. /// If no parameter is provided defaults to `latest`. #[rpc(name = "parity_getBlockReceipts")] - fn block_receipts(&self, Option) -> BoxFuture>; + fn block_receipts(&self, _: Option) -> BoxFuture>; /// Get IPFS CIDv0 given protobuf encoded bytes. #[rpc(name = "parity_cidV0")] - fn ipfs_cid(&self, Bytes) -> Result; + fn ipfs_cid(&self, _: Bytes) -> Result; /// Call contract, returning the output data. #[rpc(name = "parity_call")] - fn call(&self, Vec, Option) -> Result>; + fn call(&self, _: Vec, _: Option) -> Result>; /// Used for submitting a proof-of-work solution (similar to `eth_submitWork`, /// but returns block hash on success, and returns an explicit error message on failure). #[rpc(name = "parity_submitWorkDetail")] - fn submit_work_detail(&self, H64, H256, H256) -> Result; + fn submit_work_detail(&self, _: H64, _: H256, _: H256) -> Result; /// Returns the status of the node. Used as the health endpoint. /// @@ -226,10 +233,18 @@ pub trait Parity { /// Extracts Address and public key from signature using the r, s and v params. Equivalent to Solidity erecover /// as well as checks the signature for chain replay protection #[rpc(name = "parity_verifySignature")] - fn verify_signature(&self, bool, Bytes, H256, H256, U64) -> Result; + fn verify_signature(&self, _: bool, _: Bytes, _: H256, _: H256, _: U64) -> Result; /// Returns logs matching given filter object. /// Is allowed to skip filling transaction hash for faster query. #[rpc(name = "parity_getLogsNoTransactionHash")] - fn logs_no_tx_hash(&self, Filter) -> BoxFuture>; + fn logs_no_tx_hash(&self, _: Filter) -> BoxFuture>; + + /// Returns raw block RLP with given number. + #[rpc(name = "parity_getRawBlockByNumber")] + fn get_raw_block_by_number(&self, _: BlockNumber) -> BoxFuture>; + + /// Submit raw block to be published to the network + #[rpc(name = "parity_submitRawBlock")] + fn submit_raw_block(&self, _: Bytes) -> Result; } diff --git a/rpc/src/v1/traits/parity_accounts.rs b/rpc/src/v1/traits/parity_accounts.rs index eaffac7885..32226ff58e 100644 --- a/rpc/src/v1/traits/parity_accounts.rs +++ b/rpc/src/v1/traits/parity_accounts.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -23,30 +23,22 @@ use ethereum_types::{H160, H256, H520}; use ethkey::Password; use ethstore::KeyFile; use v1::types::{DeriveHash, DeriveHierarchical, ExtAccountInfo}; -use v1::types::{AccountInfo, HwAccountInfo}; +use v1::types::AccountInfo; /// Parity-specific read-only accounts rpc interface. -#[rpc] +#[rpc(server)] pub trait ParityAccountsInfo { /// Returns accounts information. #[rpc(name = "parity_accountsInfo")] fn accounts_info(&self) -> Result>; - /// Returns hardware accounts information. - #[rpc(name = "parity_hardwareAccountsInfo")] - fn hardware_accounts_info(&self) -> Result>; - - /// Get a list of paths to locked hardware wallets - #[rpc(name = "parity_lockedHardwareAccountsInfo")] - fn locked_hardware_accounts_info(&self) -> Result>; - /// Returns default account for dapp. #[rpc(name = "parity_defaultAccount")] fn default_account(&self) -> Result; } /// Personal Parity rpc interface. -#[rpc] +#[rpc(server)] pub trait ParityAccounts { /// Returns accounts information. #[rpc(name = "parity_allAccountsInfo")] @@ -55,49 +47,49 @@ pub trait ParityAccounts { /// Creates new account from the given phrase using standard brainwallet mechanism. /// Second parameter is password for the new account. #[rpc(name = "parity_newAccountFromPhrase")] - fn new_account_from_phrase(&self, String, Password) -> Result; + fn new_account_from_phrase(&self, _: String, _: Password) -> Result; /// Creates new account from the given JSON wallet. /// Second parameter is password for the wallet and the new account. #[rpc(name = "parity_newAccountFromWallet")] - fn new_account_from_wallet(&self, String, Password) -> Result; + fn new_account_from_wallet(&self, _: String, _: Password) -> Result; /// Creates new account from the given raw secret. /// Second parameter is password for the new account. #[rpc(name = "parity_newAccountFromSecret")] - fn new_account_from_secret(&self, H256, Password) -> Result; + fn new_account_from_secret(&self, _: H256, _: Password) -> Result; /// Returns true if given `password` would unlock given `account`. /// Arguments: `account`, `password`. #[rpc(name = "parity_testPassword")] - fn test_password(&self, H160, Password) -> Result; + fn test_password(&self, _: H160, _: Password) -> Result; /// Changes an account's password. /// Arguments: `account`, `password`, `new_password`. #[rpc(name = "parity_changePassword")] - fn change_password(&self, H160, Password, Password) -> Result; + fn change_password(&self, _: H160, _: Password, _: Password) -> Result; /// Permanently deletes an account. /// Arguments: `account`, `password`. #[rpc(name = "parity_killAccount")] - fn kill_account(&self, H160, Password) -> Result; + fn kill_account(&self, _: H160, _: Password) -> Result; /// Permanently deletes an address from the addressbook /// Arguments: `address` #[rpc(name = "parity_removeAddress")] - fn remove_address(&self, H160) -> Result; + fn remove_address(&self, _: H160) -> Result; /// Set an account's name. #[rpc(name = "parity_setAccountName")] - fn set_account_name(&self, H160, String) -> Result; + fn set_account_name(&self, _: H160, _: String) -> Result; /// Set an account's metadata string. #[rpc(name = "parity_setAccountMeta")] - fn set_account_meta(&self, H160, String) -> Result; + fn set_account_meta(&self, _: H160, _: String) -> Result; /// Imports a number of Geth accounts, with the list provided as the argument. #[rpc(name = "parity_importGethAccounts")] - fn import_geth_accounts(&self, Vec) -> Result>; + fn import_geth_accounts(&self, _: Vec) -> Result>; /// Returns the accounts available for importing from Geth. #[rpc(name = "parity_listGethAccounts")] @@ -105,15 +97,15 @@ pub trait ParityAccounts { /// Create new vault. #[rpc(name = "parity_newVault")] - fn create_vault(&self, String, Password) -> Result; + fn create_vault(&self, _: String, _: Password) -> Result; /// Open existing vault. #[rpc(name = "parity_openVault")] - fn open_vault(&self, String, Password) -> Result; + fn open_vault(&self, _: String, _: Password) -> Result; /// Close previously opened vault. #[rpc(name = "parity_closeVault")] - fn close_vault(&self, String) -> Result; + fn close_vault(&self, _: String) -> Result; /// List all vaults. #[rpc(name = "parity_listVaults")] @@ -125,40 +117,36 @@ pub trait ParityAccounts { /// Change vault password. #[rpc(name = "parity_changeVaultPassword")] - fn change_vault_password(&self, String, Password) -> Result; + fn change_vault_password(&self, _: String, _: Password) -> Result; /// Change vault of the given address. #[rpc(name = "parity_changeVault")] - fn change_vault(&self, H160, String) -> Result; + fn change_vault(&self, _: H160, _: String) -> Result; /// Get vault metadata string. #[rpc(name = "parity_getVaultMeta")] - fn get_vault_meta(&self, String) -> Result; + fn get_vault_meta(&self, _: String) -> Result; /// Set vault metadata string. #[rpc(name = "parity_setVaultMeta")] - fn set_vault_meta(&self, String, String) -> Result; + fn set_vault_meta(&self, _: String, _: String) -> Result; /// Derive new address from given account address using specific hash. /// Resulting address can be either saved as a new account (with the same password). #[rpc(name = "parity_deriveAddressHash")] - fn derive_key_hash(&self, H160, Password, DeriveHash, bool) -> Result; + fn derive_key_hash(&self, _: H160, _: Password, _: DeriveHash, _: bool) -> Result; /// Derive new address from given account address using /// hierarchical derivation (sequence of 32-bit integer indices). /// Resulting address can be either saved as a new account (with the same password). #[rpc(name = "parity_deriveAddressIndex")] - fn derive_key_index(&self, H160, Password, DeriveHierarchical, bool) -> Result; + fn derive_key_index(&self, _: H160, _: Password, _: DeriveHierarchical, _: bool) -> Result; /// Exports an account with given address if provided password matches. #[rpc(name = "parity_exportAccount")] - fn export_account(&self, H160, Password) -> Result; + fn export_account(&self, _: H160, _: Password) -> Result; /// Sign raw hash with the key corresponding to address and password. #[rpc(name = "parity_signMessage")] - fn sign_message(&self, H160, Password, H256) -> Result; - - /// Send a PinMatrixAck to a hardware wallet, unlocking it - #[rpc(name = "parity_hardwarePinMatrixAck")] - fn hardware_pin_matrix_ack(&self, String, String) -> Result; + fn sign_message(&self, _: H160, _: Password, _: H256) -> Result; } diff --git a/rpc/src/v1/traits/parity_set.rs b/rpc/src/v1/traits/parity_set.rs index c7c2338790..16dac93ea7 100644 --- a/rpc/src/v1/traits/parity_set.rs +++ b/rpc/src/v1/traits/parity_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -23,55 +23,59 @@ use jsonrpc_derive::rpc; use v1::types::{Bytes, ReleaseInfo, Transaction}; /// Parity-specific rpc interface for operations altering the account-related settings. -#[rpc] +#[rpc(server)] pub trait ParitySetAccounts { /// Sets account for signing consensus messages. #[rpc(name = "parity_setEngineSigner")] - fn set_engine_signer(&self, H160, String) -> Result; + fn set_engine_signer(&self, _: H160, _: String) -> Result; } /// Parity-specific rpc interface for operations altering the settings. -#[rpc] +#[rpc(server)] pub trait ParitySet { /// Sets new minimal gas price for mined blocks. #[rpc(name = "parity_setMinGasPrice")] - fn set_min_gas_price(&self, U256) -> Result; + fn set_min_gas_price(&self, _: U256) -> Result; /// Sets new gas floor target for mined blocks. #[rpc(name = "parity_setGasFloorTarget")] - fn set_gas_floor_target(&self, U256) -> Result; + fn set_gas_floor_target(&self, _: U256) -> Result; /// Sets new gas ceiling target for mined blocks. #[rpc(name = "parity_setGasCeilTarget")] - fn set_gas_ceil_target(&self, U256) -> Result; + fn set_gas_ceil_target(&self, _: U256) -> Result; /// Sets new extra data for mined blocks. #[rpc(name = "parity_setExtraData")] - fn set_extra_data(&self, Bytes) -> Result; + fn set_extra_data(&self, _: Bytes) -> Result; /// Sets new author for mined block. #[rpc(name = "parity_setAuthor")] - fn set_author(&self, H160) -> Result; + fn set_author(&self, _: H160) -> Result; /// Sets the secret of engine signer account. #[rpc(name = "parity_setEngineSignerSecret")] - fn set_engine_signer_secret(&self, H256) -> Result; + fn set_engine_signer_secret(&self, _: H256) -> Result; + + /// Unsets the engine signer account address. + #[rpc(name = "parity_clearEngineSigner")] + fn clear_engine_signer(&self) -> Result; /// Sets the limits for transaction queue. #[rpc(name = "parity_setTransactionsLimit")] - fn set_transactions_limit(&self, usize) -> Result; + fn set_transactions_limit(&self, _: usize) -> Result; /// Sets the maximum amount of gas a single transaction may consume. #[rpc(name = "parity_setMaxTransactionGas")] - fn set_tx_gas_limit(&self, U256) -> Result; + fn set_tx_gas_limit(&self, _: U256) -> Result; /// Add a reserved peer. #[rpc(name = "parity_addReservedPeer")] - fn add_reserved_peer(&self, String) -> Result; + fn add_reserved_peer(&self, _: String) -> Result; /// Remove a reserved peer. #[rpc(name = "parity_removeReservedPeer")] - fn remove_reserved_peer(&self, String) -> Result; + fn remove_reserved_peer(&self, _: String) -> Result; /// Drop all non-reserved peers. #[rpc(name = "parity_dropNonReservedPeers")] @@ -95,15 +99,15 @@ pub trait ParitySet { /// Set the mode. Argument must be one of: "active", "passive", "dark", "offline". #[rpc(name = "parity_setMode")] - fn set_mode(&self, String) -> Result; + fn set_mode(&self, _: String) -> Result; /// Set the network spec. Argument must be one of pre-configured chains or a filename. #[rpc(name = "parity_setChain")] - fn set_spec_name(&self, String) -> Result; + fn set_spec_name(&self, _: String) -> Result; /// Hash a file content under given URL. #[rpc(name = "parity_hashContent")] - fn hash_content(&self, String) -> BoxFuture; + fn hash_content(&self, _: String) -> BoxFuture; /// Is there a release ready for install? #[rpc(name = "parity_upgradeReady")] @@ -120,5 +124,5 @@ pub trait ParitySet { /// or excessive gas limit that are not accepted by other peers whp. /// Returns `true` when transaction was removed, `false` if it was not found. #[rpc(name = "parity_removeTransaction")] - fn remove_transaction(&self, H256) -> Result>; + fn remove_transaction(&self, _: H256) -> Result>; } diff --git a/rpc/src/v1/traits/parity_signing.rs b/rpc/src/v1/traits/parity_signing.rs index acd9e8cd68..b4fc507462 100644 --- a/rpc/src/v1/traits/parity_signing.rs +++ b/rpc/src/v1/traits/parity_signing.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use ethereum_types::{H160, U256}; use v1::types::{Bytes, ConfirmationResponse, TransactionRequest, Either}; /// Signing methods implementation. -#[rpc] +#[rpc(server)] pub trait ParitySigning { /// RPC Metadata type Metadata; @@ -30,25 +30,25 @@ pub trait ParitySigning { /// Given partial transaction request produces transaction with all fields filled in. /// Such transaction can be then signed externally. #[rpc(meta, name = "parity_composeTransaction")] - fn compose_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture; + fn compose_transaction(&self, _: Self::Metadata, _: TransactionRequest) -> BoxFuture; /// Posts sign request asynchronously. /// Will return a confirmation ID for later use with check_transaction. #[rpc(meta, name = "parity_postSign")] - fn post_sign(&self, Self::Metadata, H160, Bytes) -> BoxFuture>; + fn post_sign(&self, _: Self::Metadata, _: H160, _: Bytes) -> BoxFuture>; /// Posts transaction asynchronously. /// Will return a transaction ID for later use with check_transaction. #[rpc(meta, name = "parity_postTransaction")] - fn post_transaction(&self, Self::Metadata, TransactionRequest) -> BoxFuture>; + fn post_transaction(&self, _: Self::Metadata, _: TransactionRequest) -> BoxFuture>; /// Checks the progress of a previously posted request (transaction/sign). /// Should be given a valid send_transaction ID. #[rpc(name = "parity_checkRequest")] - fn check_request(&self, U256) -> Result>; + fn check_request(&self, _: U256) -> Result>; /// Decrypt some ECIES-encrypted message. /// First parameter is the address with which it is encrypted, second is the ciphertext. #[rpc(meta, name = "parity_decryptMessage")] - fn decrypt_message(&self, Self::Metadata, H160, Bytes) -> BoxFuture; + fn decrypt_message(&self, _: Self::Metadata, _: H160, _: Bytes) -> BoxFuture; } diff --git a/rpc/src/v1/traits/personal.rs b/rpc/src/v1/traits/personal.rs index e3632731fa..349a598204 100644 --- a/rpc/src/v1/traits/personal.rs +++ b/rpc/src/v1/traits/personal.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -23,7 +23,7 @@ use jsonrpc_derive::rpc; use v1::types::{Bytes, TransactionRequest, RichRawTransaction as RpcRichRawTransaction, EIP191Version}; /// Personal rpc interface. Safe (read-only) functions. -#[rpc] +#[rpc(server)] pub trait Personal { /// RPC Metadata type Metadata; @@ -35,40 +35,40 @@ pub trait Personal { /// Creates new account (it becomes new current unlocked account) /// Param is the password for the account. #[rpc(name = "personal_newAccount")] - fn new_account(&self, String) -> Result; + fn new_account(&self, _: String) -> Result; /// Unlocks specified account for use (can only be one unlocked account at one moment) #[rpc(name = "personal_unlockAccount")] - fn unlock_account(&self, H160, String, Option) -> Result; + fn unlock_account(&self, _: H160, _: String, _: Option) -> Result; /// Signs the hash of data with given account signature using the given password to unlock the account during /// the request. #[rpc(name = "personal_sign")] - fn sign(&self, Bytes, H160, String) -> BoxFuture; + fn sign(&self, _: Bytes, _: H160, _: String) -> BoxFuture; /// Produces an EIP-712 compliant signature with given account using the given password to unlock the /// account during the request. #[rpc(name = "personal_signTypedData")] - fn sign_typed_data(&self, EIP712, H160, String) -> BoxFuture; + fn sign_typed_data(&self, _: EIP712, _: H160, _: String) -> BoxFuture; /// Signs an arbitrary message based on the version specified #[rpc(name = "personal_sign191")] - fn sign_191(&self, EIP191Version, Value, H160, String) -> BoxFuture; + fn sign_191(&self, _: EIP191Version, _: Value, _: H160, _: String) -> BoxFuture; /// Returns the account associated with the private key that was used to calculate the signature in /// `personal_sign`. #[rpc(name = "personal_ecRecover")] - fn ec_recover(&self, Bytes, H520) -> BoxFuture; + fn ec_recover(&self, _: Bytes, _: H520) -> BoxFuture; /// Signs transaction. The account is not unlocked in such case. #[rpc(meta, name = "personal_signTransaction")] - fn sign_transaction(&self, Self::Metadata, TransactionRequest, String) -> BoxFuture; + fn sign_transaction(&self, _: Self::Metadata, _: TransactionRequest, _: String) -> BoxFuture; /// Sends transaction and signs it in single call. The account is not unlocked in such case. #[rpc(meta, name = "personal_sendTransaction")] - fn send_transaction(&self, Self::Metadata, TransactionRequest, String) -> BoxFuture; + fn send_transaction(&self, _: Self::Metadata, _: TransactionRequest, _: String) -> BoxFuture; /// @deprecated alias for `personal_sendTransaction`. #[rpc(meta, name = "personal_signAndSendTransaction")] - fn sign_and_send_transaction(&self, Self::Metadata, TransactionRequest, String) -> BoxFuture; + fn sign_and_send_transaction(&self, _: Self::Metadata, _: TransactionRequest, _: String) -> BoxFuture; } diff --git a/rpc/src/v1/traits/private.rs b/rpc/src/v1/traits/private.rs index 732e3914bd..42e92aa3b5 100644 --- a/rpc/src/v1/traits/private.rs +++ b/rpc/src/v1/traits/private.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,27 +21,37 @@ use jsonrpc_core::Error; use jsonrpc_derive::rpc; use v1::types::{Bytes, PrivateTransactionReceipt, BlockNumber, - PrivateTransactionReceiptAndTransaction, CallRequest}; + PrivateTransactionReceiptAndTransaction, CallRequest, PrivateTransactionLog}; /// Private transaction management RPC interface. -#[rpc] +#[rpc(server)] pub trait Private { /// RPC Metadata 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; + 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; + fn compose_deployment_transaction( + &self, + _: BlockNumber, + _: Bytes, + _: Vec, + _: U256 + ) -> Result; /// Make a call to the private contract #[rpc(name = "private_call")] - fn private_call(&self, BlockNumber, CallRequest) -> Result; + fn private_call(&self, _: BlockNumber, _: CallRequest) -> Result; /// Retrieve the id of the key associated with the contract #[rpc(name = "private_contractKey")] - fn private_contract_key(&self, H160) -> Result; + fn private_contract_key(&self, _: H160) -> Result; + + /// Retrieve log information about private transaction + #[rpc(name = "private_log")] + fn private_log(&self, _: H256) -> Result; } diff --git a/rpc/src/v1/traits/pubsub.rs b/rpc/src/v1/traits/pubsub.rs index c91b335613..019f5fd6a9 100644 --- a/rpc/src/v1/traits/pubsub.rs +++ b/rpc/src/v1/traits/pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,16 +21,16 @@ use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; use jsonrpc_derive::rpc; /// Parity-specific PUB-SUB rpc interface. -#[rpc] +#[rpc(server)] pub trait PubSub { /// Pub/Sub Metadata type Metadata; /// Subscribe to changes of any RPC method in Parity. #[pubsub(subscription = "parity_subscription", subscribe, name = "parity_subscribe")] - fn parity_subscribe(&self, Self::Metadata, Subscriber, String, Option); + fn parity_subscribe(&self, _: Self::Metadata, _: Subscriber, _: String, _: Option); /// Unsubscribe from existing Parity subscription. #[pubsub(subscription = "parity_subscription", unsubscribe, name = "parity_unsubscribe")] - fn parity_unsubscribe(&self, Option, SubscriptionId) -> Result; + fn parity_unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; } diff --git a/rpc/src/v1/traits/rpc.rs b/rpc/src/v1/traits/rpc.rs index 9600630fec..d647403f4c 100644 --- a/rpc/src/v1/traits/rpc.rs +++ b/rpc/src/v1/traits/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use jsonrpc_core::Result; use jsonrpc_derive::rpc; /// RPC Interface. -#[rpc] +#[rpc(server)] pub trait Rpc { /// Returns supported modules for Geth 1.3.6 /// @ignore diff --git a/rpc/src/v1/traits/secretstore.rs b/rpc/src/v1/traits/secretstore.rs index 6883753b4f..7d67cb3fd5 100644 --- a/rpc/src/v1/traits/secretstore.rs +++ b/rpc/src/v1/traits/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -25,37 +25,37 @@ use ethkey::Password; use v1::types::{Bytes, EncryptedDocumentKey}; /// Parity-specific rpc interface. -#[rpc] +#[rpc(server)] pub trait SecretStore { /// Generate document key to store in secret store. /// Arguments: `account`, `password`, `server_key_public`. #[rpc(name = "secretstore_generateDocumentKey")] - fn generate_document_key(&self, H160, Password, H512) -> Result; + fn generate_document_key(&self, _: H160, _: Password, _: H512) -> Result; /// Encrypt data with key, received from secret store. /// Arguments: `account`, `password`, `key`, `data`. #[rpc(name = "secretstore_encrypt")] - fn encrypt(&self, H160, Password, Bytes, Bytes) -> Result; + fn encrypt(&self, _: H160, _: Password, _: Bytes, _: Bytes) -> Result; /// Decrypt data with key, received from secret store. /// Arguments: `account`, `password`, `key`, `data`. #[rpc(name = "secretstore_decrypt")] - fn decrypt(&self, H160, Password, Bytes, Bytes) -> Result; + fn decrypt(&self, _: H160, _: Password, _: Bytes, _: Bytes) -> Result; /// Decrypt data with shadow key, received from secret store. /// Arguments: `account`, `password`, `decrypted_secret`, `common_point`, `decrypt_shadows`, `data`. #[rpc(name = "secretstore_shadowDecrypt")] - fn shadow_decrypt(&self, H160, Password, H512, H512, Vec, Bytes) -> Result; + fn shadow_decrypt(&self, _: H160, _: Password, _: H512, _: H512, _: Vec, _: Bytes) -> Result; /// Calculates the hash (keccak256) of servers set for using in ServersSetChange session. /// Returned hash must be signed later by using `secretstore_signRawHash` method. /// Arguments: `servers_set`. #[rpc(name = "secretstore_serversSetHash")] - fn servers_set_hash(&self, BTreeSet) -> Result; + fn servers_set_hash(&self, _: BTreeSet) -> Result; /// Generate recoverable ECDSA signature of raw hash. /// Passed hash is treated as an input to the `sign` function (no prefixes added, no hash function is applied). /// Arguments: `account`, `password`, `raw_hash`. #[rpc(name = "secretstore_signRawHash")] - fn sign_raw_hash(&self, H160, Password, H256) -> Result; + fn sign_raw_hash(&self, _: H160, _: Password, _: H256) -> Result; } diff --git a/rpc/src/v1/traits/signer.rs b/rpc/src/v1/traits/signer.rs index b5653eba63..0c00c36414 100644 --- a/rpc/src/v1/traits/signer.rs +++ b/rpc/src/v1/traits/signer.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,7 +24,7 @@ use jsonrpc_derive::rpc; use v1::types::{Bytes, TransactionModification, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken}; /// Signer extension for confirmations rpc interface. -#[rpc] +#[rpc(server)] pub trait Signer { /// RPC Metadata type Metadata; @@ -35,19 +35,24 @@ pub trait Signer { /// Confirm specific request. #[rpc(name = "signer_confirmRequest")] - fn confirm_request(&self, U256, TransactionModification, String) -> BoxFuture; + fn confirm_request(&self, _: U256, _: TransactionModification, _: String) -> BoxFuture; /// Confirm specific request with token. #[rpc(name = "signer_confirmRequestWithToken")] - fn confirm_request_with_token(&self, U256, TransactionModification, String) -> BoxFuture; + fn confirm_request_with_token( + &self, + _: U256, + _: TransactionModification, + _: String + ) -> BoxFuture; /// Confirm specific request with already signed data. #[rpc(name = "signer_confirmRequestRaw")] - fn confirm_request_raw(&self, U256, Bytes) -> Result; + fn confirm_request_raw(&self, _: U256, _: Bytes) -> Result; /// Reject the confirmation request. #[rpc(name = "signer_rejectRequest")] - fn reject_request(&self, U256) -> Result; + fn reject_request(&self, _: U256) -> Result; /// Generates new authorization token. #[rpc(name = "signer_generateAuthorizationToken")] @@ -55,9 +60,9 @@ pub trait Signer { /// Subscribe to new pending requests on signer interface. #[pubsub(subscription = "signer_pending", subscribe, name = "signer_subscribePending")] - fn subscribe_pending(&self, Self::Metadata, Subscriber>); + fn subscribe_pending(&self, _: Self::Metadata, _: Subscriber>); /// Unsubscribe from pending requests subscription. #[pubsub(subscription = "signer_pending", unsubscribe, name = "signer_unsubscribePending")] - fn unsubscribe_pending(&self, Option, SubscriptionId) -> Result; + fn unsubscribe_pending(&self, _: Option, _: SubscriptionId) -> Result; } diff --git a/rpc/src/v1/traits/traces.rs b/rpc/src/v1/traits/traces.rs index 5308ed4c6c..679ed912b2 100644 --- a/rpc/src/v1/traits/traces.rs +++ b/rpc/src/v1/traits/traces.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -23,44 +23,48 @@ use v1::types::{TraceFilter, LocalizedTrace, BlockNumber, Index, CallRequest, By TraceResultsWithTransactionHash, TraceOptions}; /// Traces specific rpc interface. -#[rpc] +#[rpc(server)] pub trait Traces { /// RPC Metadata type Metadata; /// Returns traces matching given filter. #[rpc(name = "trace_filter")] - fn filter(&self, TraceFilter) -> Result>>; + fn filter(&self, _: TraceFilter) -> Result>>; /// Returns transaction trace at given index. #[rpc(name = "trace_get")] - fn trace(&self, H256, Vec) -> Result>; + fn trace(&self, _: H256, _: Vec) -> Result>; /// Returns all traces of given transaction. #[rpc(name = "trace_transaction")] - fn transaction_traces(&self, H256) -> Result>>; + fn transaction_traces(&self, _: H256) -> Result>>; /// Returns all traces produced at given block. #[rpc(name = "trace_block")] - fn block_traces(&self, BlockNumber) -> Result>>; + fn block_traces(&self, _: BlockNumber) -> Result>>; /// Executes the given call and returns a number of possible traces for it. #[rpc(name = "trace_call")] - fn call(&self, CallRequest, TraceOptions, Option) -> Result; + fn call(&self, _: CallRequest, _: TraceOptions, _: Option) -> Result; /// Executes all given calls and returns a number of possible traces for each of it. #[rpc(name = "trace_callMany")] - fn call_many(&self, Vec<(CallRequest, TraceOptions)>, Option) -> Result>; + fn call_many(&self, _: Vec<(CallRequest, TraceOptions)>, _: Option) -> Result>; /// Executes the given raw transaction and returns a number of possible traces for it. #[rpc(name = "trace_rawTransaction")] - fn raw_transaction(&self, Bytes, TraceOptions, Option) -> Result; + fn raw_transaction(&self, _: Bytes, _: TraceOptions, _: Option) -> Result; /// Executes the transaction with the given hash and returns a number of possible traces for it. #[rpc(name = "trace_replayTransaction")] - fn replay_transaction(&self, H256, TraceOptions) -> Result; + fn replay_transaction(&self, _: H256, _: TraceOptions) -> Result; /// Executes all the transactions at the given block and returns a number of possible traces for each transaction. #[rpc(name = "trace_replayBlockTransactions")] - fn replay_block_transactions(&self, BlockNumber, TraceOptions) -> Result>; + fn replay_block_transactions( + &self, + _: BlockNumber, + _: TraceOptions + ) -> Result>; } diff --git a/rpc/src/v1/traits/transactions_pool.rs b/rpc/src/v1/traits/transactions_pool.rs new file mode 100644 index 0000000000..1eb19ca162 --- /dev/null +++ b/rpc/src/v1/traits/transactions_pool.rs @@ -0,0 +1,23 @@ +//! Transactions pool PUB-SUB rpc interface. + +use jsonrpc_core::Result; +use jsonrpc_pubsub::{typed, SubscriptionId}; +use jsonrpc_derive::rpc; +use miner::pool::TxStatus; + +use ethereum_types::H256; + +/// Transactions Pool PUB-SUB rpc interface. +#[rpc(server)] +pub trait TransactionsPool { + /// Pub/Sub Metadata + type Metadata; + + /// Subscribe to Transactions Pool subscription. + #[pubsub(subscription = "parity_watchTransactionsPool", subscribe, name = "parity_watchTransactionsPool")] + fn subscribe(&self, _: Self::Metadata, _: typed::Subscriber<(H256, TxStatus)>); + + /// Unsubscribe from existing Transactions Pool subscription. + #[pubsub(subscription = "parity_watchTransactionsPool", unsubscribe, name = "parity_unwatchTransactionsPool")] + fn unsubscribe(&self, _: Option, _: SubscriptionId) -> Result; +} diff --git a/rpc/src/v1/traits/web3.rs b/rpc/src/v1/traits/web3.rs index dd464ee1c2..1815eb28a7 100644 --- a/rpc/src/v1/traits/web3.rs +++ b/rpc/src/v1/traits/web3.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,7 +22,7 @@ use jsonrpc_derive::rpc; use v1::types::Bytes; /// Web3 rpc interface. -#[rpc] +#[rpc(server)] pub trait Web3 { /// Returns current client version. #[rpc(name = "web3_clientVersion")] @@ -30,5 +30,5 @@ pub trait Web3 { /// Returns sha3 of the given data #[rpc(name = "web3_sha3")] - fn sha3(&self, Bytes) -> Result; + fn sha3(&self, _: Bytes) -> Result; } diff --git a/rpc/src/v1/types/account_info.rs b/rpc/src/v1/types/account_info.rs index 6d7585f87f..d2335035fe 100644 --- a/rpc/src/v1/types/account_info.rs +++ b/rpc/src/v1/types/account_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -26,7 +26,7 @@ pub struct AccountInfo { pub name: String, } -/// Datastructure with proof for one single storage-entry +/// Data structure with proof for one single storage-entry #[derive(Debug, Default, Clone, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub struct StorageProof { @@ -60,15 +60,6 @@ pub struct ExtAccountInfo { pub uuid: Option, } -/// Hardware wallet information. -#[derive(Debug, Default, Clone, PartialEq, Serialize)] -pub struct HwAccountInfo { - /// Device name. - pub name: String, - /// Device manufacturer. - pub manufacturer: String, -} - /// account derived from a signature /// as well as information that tells if it is valid for /// the current chain diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index 61f4402af0..deb59b3d1d 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -216,7 +216,7 @@ mod tests { let serialized = serde_json::to_string(&t).unwrap(); assert_eq!(serialized, r#"[{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}]"#); - let t = BlockTransactions::Hashes(vec![H256::default().into()]); + let t = BlockTransactions::Hashes(vec![H256::zero().into()]); let serialized = serde_json::to_string(&t).unwrap(); assert_eq!(serialized, r#"["0x0000000000000000000000000000000000000000000000000000000000000000"]"#); } @@ -224,14 +224,14 @@ mod tests { #[test] fn test_serialize_block() { let block = Block { - hash: Some(H256::default()), - parent_hash: H256::default(), - uncles_hash: H256::default(), + hash: Some(H256::zero()), + parent_hash: H256::zero(), + uncles_hash: H256::zero(), author: H160::default(), miner: H160::default(), - state_root: H256::default(), - transactions_root: H256::default(), - receipts_root: H256::default(), + state_root: H256::zero(), + transactions_root: H256::zero(), + receipts_root: H256::zero(), number: Some(U256::default()), gas_used: U256::default(), gas_limit: U256::default(), @@ -249,7 +249,7 @@ mod tests { let rich_block = RichBlock { inner: block, extra_info: map![ - "mixHash".into() => format!("{:?}", H256::default()), + "mixHash".into() => format!("{:?}", H256::zero()), "nonce".into() => format!("{:?}", H64::default()) ], }; @@ -262,14 +262,14 @@ mod tests { #[test] fn none_size_null() { let block = Block { - hash: Some(H256::default()), - parent_hash: H256::default(), - uncles_hash: H256::default(), + hash: Some(H256::zero()), + parent_hash: H256::zero(), + uncles_hash: H256::zero(), author: H160::default(), miner: H160::default(), - state_root: H256::default(), - transactions_root: H256::default(), - receipts_root: H256::default(), + state_root: H256::zero(), + transactions_root: H256::zero(), + receipts_root: H256::zero(), number: Some(U256::default()), gas_used: U256::default(), gas_limit: U256::default(), @@ -287,7 +287,7 @@ mod tests { let rich_block = RichBlock { inner: block, extra_info: map![ - "mixHash".into() => format!("{:?}", H256::default()), + "mixHash".into() => format!("{:?}", H256::zero()), "nonce".into() => format!("{:?}", H64::default()) ], }; @@ -300,14 +300,14 @@ mod tests { #[test] fn test_serialize_header() { let header = Header { - hash: Some(H256::default()), - parent_hash: H256::default(), - uncles_hash: H256::default(), + hash: Some(H256::zero()), + parent_hash: H256::zero(), + uncles_hash: H256::zero(), author: H160::default(), miner: H160::default(), - state_root: H256::default(), - transactions_root: H256::default(), - receipts_root: H256::default(), + state_root: H256::zero(), + transactions_root: H256::zero(), + receipts_root: H256::zero(), number: Some(U256::default()), gas_used: U256::default(), gas_limit: U256::default(), @@ -322,7 +322,7 @@ mod tests { let rich_header = RichHeader { inner: header, extra_info: map![ - "mixHash".into() => format!("{:?}", H256::default()), + "mixHash".into() => format!("{:?}", H256::zero()), "nonce".into() => format!("{:?}", H64::default()) ], }; diff --git a/rpc/src/v1/types/block_number.rs b/rpc/src/v1/types/block_number.rs index 7e19f2d3d9..e0a46e0d5c 100644 --- a/rpc/src/v1/types/block_number.rs +++ b/rpc/src/v1/types/block_number.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,12 +16,20 @@ use std::fmt; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde::de::{Error, Visitor}; -use ethcore::client::BlockId; +use serde::de::{Error, Visitor, MapAccess}; +use types::ids::BlockId; +use ethereum_types::H256; /// Represents rpc api block number param. #[derive(Debug, PartialEq, Clone, Hash, Eq)] pub enum BlockNumber { + /// Hash + Hash { + /// block hash + hash: H256, + /// only return blocks part of the canon chain + require_canonical: bool, + }, /// Number Num(u64), /// Latest block @@ -68,6 +76,7 @@ impl LightBlockNumber for BlockNumber { // Since light clients don't produce pending blocks // (they don't have state) we can safely fallback to `Latest`. match self { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(n) => BlockId::Number(n), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, @@ -82,6 +91,9 @@ impl LightBlockNumber for BlockNumber { impl Serialize for BlockNumber { fn serialize(&self, serializer: S) -> Result where S: Serializer { match *self { + BlockNumber::Hash{ hash, require_canonical } => serializer.serialize_str( + &format!("{{ 'hash': '{}', 'requireCanonical': '{}' }}", hash, require_canonical) + ), BlockNumber::Num(ref x) => serializer.serialize_str(&format!("0x{:x}", x)), BlockNumber::Latest => serializer.serialize_str("latest"), BlockNumber::Earliest => serializer.serialize_str("earliest"), @@ -99,6 +111,54 @@ impl<'a> Visitor<'a> for BlockNumberVisitor { write!(formatter, "a block number or 'latest', 'earliest' or 'pending'") } + fn visit_map(self, mut visitor: V) -> Result where V: MapAccess<'a> { + let (mut require_canonical, mut block_number, mut block_hash) = (false, None::, None::); + + loop { + let key_str: Option = visitor.next_key()?; + + match key_str { + Some(key) => match key.as_str() { + "blockNumber" => { + let value: String = visitor.next_value()?; + if value.starts_with("0x") { + let number = u64::from_str_radix(&value[2..], 16).map_err(|e| { + Error::custom(format!("Invalid block number: {}", e)) + })?; + + block_number = Some(number); + break; + } else { + return Err(Error::custom("Invalid block number: missing 0x prefix".to_string())) + } + } + "blockHash" => { + block_hash = Some(visitor.next_value()?); + } + "requireCanonical" => { + require_canonical = visitor.next_value()?; + } + key => { + return Err(Error::custom(format!("Unknown key: {}", key))) + } + } + None => { + break + } + }; + } + + if let Some(number) = block_number { + return Ok(BlockNumber::Num(number)) + } + + if let Some(hash) = block_hash { + return Ok(BlockNumber::Hash { hash, require_canonical }) + } + + return Err(Error::custom("Invalid input")) + } + fn visit_str(self, value: &str) -> Result where E: Error { match value { "latest" => Ok(BlockNumber::Latest), @@ -107,7 +167,9 @@ impl<'a> Visitor<'a> for BlockNumberVisitor { _ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16).map(BlockNumber::Num).map_err(|e| { Error::custom(format!("Invalid block number: {}", e)) }), - _ => Err(Error::custom("Invalid block number: missing 0x prefix".to_string())), + _ => { + Err(Error::custom("Invalid block number: missing 0x prefix".to_string())) + }, } } @@ -119,31 +181,52 @@ impl<'a> Visitor<'a> for BlockNumberVisitor { /// Converts `BlockNumber` to `BlockId`, panics on `BlockNumber::Pending` pub fn block_number_to_id(number: BlockNumber) -> BlockId { match number { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(num) => BlockId::Number(num), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, - BlockNumber::Pending => panic!("`BlockNumber::Pending` should be handled manually") } } #[cfg(test)] mod tests { - use ethcore::client::BlockId; + use types::ids::BlockId; use super::*; + use std::str::FromStr; use serde_json; #[test] fn block_number_deserialization() { - let s = r#"["0xa", "latest", "earliest", "pending"]"#; + let s = r#"[ + "0xa", + "latest", + "earliest", + "pending", + {"blockNumber": "0xa"}, + {"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"}, + {"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "requireCanonical": true} + ]"#; let deserialized: Vec = serde_json::from_str(s).unwrap(); - assert_eq!(deserialized, vec![BlockNumber::Num(10), BlockNumber::Latest, BlockNumber::Earliest, BlockNumber::Pending]) + + assert_eq!( + deserialized, + vec![ + BlockNumber::Num(10), + BlockNumber::Latest, + BlockNumber::Earliest, + BlockNumber::Pending, + BlockNumber::Num(10), + BlockNumber::Hash { hash: H256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(), require_canonical: false }, + BlockNumber::Hash { hash: H256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(), require_canonical: true } + ] + ) } #[test] - fn should_not_deserialize_decimal() { - let s = r#""10""#; - assert!(serde_json::from_str::(s).is_err()); + fn should_not_deserialize() { + let s = r#"[{}, "10"]"#; + assert!(serde_json::from_str::>(s).is_err()); } #[test] diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index 837f3b5f9a..f0891e22a3 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/call_request.rs b/rpc/src/v1/types/call_request.rs index d75e4b1a2d..59658dc07e 100644 --- a/rpc/src/v1/types/call_request.rs +++ b/rpc/src/v1/types/call_request.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -75,8 +75,8 @@ mod tests { let deserialized: CallRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, CallRequest { - from: Some(H160::from(1)), - to: Some(H160::from(2)), + from: Some(H160::from_low_u64_be(1)), + to: Some(H160::from_low_u64_be(2)), gas_price: Some(U256::from(1)), gas: Some(U256::from(2)), value: Some(U256::from(3)), @@ -114,7 +114,7 @@ mod tests { let deserialized: CallRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, CallRequest { - from: Some(H160::from(1)), + from: Some(H160::from_low_u64_be(1)), to: None, gas_price: None, gas: None, diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index dedd0ba253..22b60c88c6 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -229,6 +229,18 @@ impl From for ConfirmationPayload { } } +impl ConfirmationPayload { + pub fn sender(&self) -> Option<&H160> { + match *self { + ConfirmationPayload::SendTransaction(ref request) => request.from.as_ref(), + ConfirmationPayload::SignTransaction(ref request) => request.from.as_ref(), + ConfirmationPayload::EthSignMessage(ref request) => Some(&request.address), + ConfirmationPayload::EIP191SignMessage(ref request) => Some(&request.address), + ConfirmationPayload::Decrypt(ref request) => Some(&request.address), + } + } +} + /// Possible modifications to the confirmed transaction sent by `Trusted Signer` #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -282,7 +294,7 @@ impl Serialize for Either where #[cfg(test)] mod tests { use std::str::FromStr; - use ethereum_types::{H256, U256}; + use ethereum_types::{H256, U256, Address}; use serde_json; use v1::types::TransactionCondition; use v1::helpers; @@ -293,7 +305,7 @@ mod tests { // given let request = helpers::ConfirmationRequest { id: 15.into(), - payload: helpers::ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()), + payload: helpers::ConfirmationPayload::EthSignMessage(Address::from_low_u64_be(1), vec![5].into()), origin: Origin::Rpc("test service".into()), }; @@ -311,7 +323,7 @@ mod tests { let request = helpers::ConfirmationRequest { id: 15.into(), payload: helpers::ConfirmationPayload::SendTransaction(helpers::FilledTransactionRequest { - from: 0.into(), + from: Address::zero(), used_default_from: false, to: None, gas: 15_000.into(), @@ -322,7 +334,7 @@ mod tests { condition: None, }), origin: Origin::Signer { - session: 5.into(), + session: H256::from_low_u64_be(5), } }; @@ -340,7 +352,7 @@ mod tests { let request = helpers::ConfirmationRequest { id: 15.into(), payload: helpers::ConfirmationPayload::SignTransaction(helpers::FilledTransactionRequest { - from: 0.into(), + from: Address::zero(), used_default_from: false, to: None, gas: 15_000.into(), @@ -367,7 +379,7 @@ mod tests { let request = helpers::ConfirmationRequest { id: 15.into(), payload: helpers::ConfirmationPayload::Decrypt( - 10.into(), vec![1, 2, 3].into(), + Address::from_low_u64_be(10), vec![1, 2, 3].into(), ), origin: Default::default(), }; @@ -398,7 +410,7 @@ mod tests { // then assert_eq!(res1, TransactionModification { - sender: Some(10.into()), + sender: Some(Address::from_low_u64_be(10)), gas_price: Some(U256::from_str("0ba43b7400").unwrap()), gas: None, condition: Some(Some(TransactionCondition::Number(0x42))), @@ -421,7 +433,7 @@ mod tests { fn should_serialize_confirmation_response_with_token() { // given let response = ConfirmationResponseWithToken { - result: ConfirmationResponse::SendTransaction(H256::default()), + result: ConfirmationResponse::SendTransaction(H256::zero()), token: "test-token".into(), }; diff --git a/rpc/src/v1/types/consensus_status.rs b/rpc/src/v1/types/consensus_status.rs index da2aa26a1d..50dfc5f49d 100644 --- a/rpc/src/v1/types/consensus_status.rs +++ b/rpc/src/v1/types/consensus_status.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -49,8 +49,6 @@ impl Into for CapState { pub enum ReleaseTrack { /// Stable track. Stable, - /// Beta track. - Beta, /// Nightly track. Nightly, /// Testing track. @@ -64,9 +62,7 @@ impl Into for updater::ReleaseTrack { fn into(self) -> ReleaseTrack { match self { updater::ReleaseTrack::Stable => ReleaseTrack::Stable, - updater::ReleaseTrack::Beta => ReleaseTrack::Beta, updater::ReleaseTrack::Nightly => ReleaseTrack::Nightly, - updater::ReleaseTrack::Testing => ReleaseTrack::Testing, updater::ReleaseTrack::Unknown => ReleaseTrack::Unknown, } } diff --git a/rpc/src/v1/types/derivation.rs b/rpc/src/v1/types/derivation.rs index 1f2764d9ff..6966d973e0 100644 --- a/rpc/src/v1/types/derivation.rs +++ b/rpc/src/v1/types/derivation.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use serde::{Deserialize, Deserializer}; use serde::de::{Error, Visitor}; use ethereum_types::H256; -use ethstore; + /// Type of derivation pub enum DerivationType { diff --git a/rpc/src/v1/types/eip191.rs b/rpc/src/v1/types/eip191.rs index fe3aab4c51..5bff702ca5 100644 --- a/rpc/src/v1/types/eip191.rs +++ b/rpc/src/v1/types/eip191.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/eth_types.rs b/rpc/src/v1/types/eth_types.rs index 606e759248..baa56fc1e4 100644 --- a/rpc/src/v1/types/eth_types.rs +++ b/rpc/src/v1/types/eth_types.rs @@ -18,10 +18,10 @@ fn should_serialize_u256() { #[test] fn should_serialize_h256() { - let serialized1 = serde_json::to_string(&H256::from(0)).unwrap(); - let serialized2 = serde_json::to_string(&H256::from(1)).unwrap(); - let serialized3 = serde_json::to_string(&H256::from(16)).unwrap(); - let serialized4 = serde_json::to_string(&H256::from(256)).unwrap(); + let serialized1 = serde_json::to_string(&H256::from_low_u64_be(0)).unwrap(); + let serialized2 = serde_json::to_string(&H256::from_low_u64_be(1)).unwrap(); + let serialized3 = serde_json::to_string(&H256::from_low_u64_be(16)).unwrap(); + let serialized4 = serde_json::to_string(&H256::from_low_u64_be(256)).unwrap(); assert_eq!(serialized1, r#""0x0000000000000000000000000000000000000000000000000000000000000000""#); assert_eq!(serialized2, r#""0x0000000000000000000000000000000000000000000000000000000000000001""#); @@ -78,7 +78,7 @@ fn should_deserialize_h256() { let deserialized2: H256 = serde_json::from_str(r#""0x0000000000000000000000000000000000000000000000000000000000000001""#).unwrap(); let deserialized3: H256 = serde_json::from_str(r#""0x0000000000000000000000000000000000000000000000000000000000000100""#).unwrap(); - assert_eq!(deserialized1, 0.into()); - assert_eq!(deserialized2, 1.into()); - assert_eq!(deserialized3, 256.into()); + assert_eq!(deserialized1, H256::from_low_u64_be(0)); + assert_eq!(deserialized2, H256::from_low_u64_be(1)); + assert_eq!(deserialized3, H256::from_low_u64_be(256)); } diff --git a/rpc/src/v1/types/filter.rs b/rpc/src/v1/types/filter.rs index ec9f541797..28cea0c79d 100644 --- a/rpc/src/v1/types/filter.rs +++ b/rpc/src/v1/types/filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -82,6 +82,7 @@ impl Filter { } let num_to_id = |num| match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(n) => BlockId::Number(n), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest, @@ -188,7 +189,7 @@ mod tests { address: Some(VariadicValue::Multiple(vec![])), topics: Some(vec![ VariadicValue::Null, - VariadicValue::Single("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".into()), + VariadicValue::Single(H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap()), VariadicValue::Null, ]), limit: None, @@ -201,7 +202,7 @@ mod tests { address: Some(vec![]), topics: vec![ None, - Some(vec!["000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b".into()]), + Some(vec![H256::from_str("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap()]), None, None, ], diff --git a/rpc/src/v1/types/histogram.rs b/rpc/src/v1/types/histogram.rs index d7f14c514e..b35de129e5 100644 --- a/rpc/src/v1/types/histogram.rs +++ b/rpc/src/v1/types/histogram.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,6 +17,7 @@ //! Gas prices histogram. use ethereum_types::U256; +use stats; /// Values of RPC settings. #[derive(Serialize, Deserialize)] @@ -25,12 +26,12 @@ use ethereum_types::U256; pub struct Histogram { /// Gas prices for bucket edges. pub bucket_bounds: Vec, - /// Transacion counts for each bucket. + /// Transaction counts for each bucket. pub counts: Vec, } -impl From<::stats::Histogram<::ethereum_types::U256>> for Histogram { - fn from(h: ::stats::Histogram<::ethereum_types::U256>) -> Self { +impl From> for Histogram { + fn from(h: stats::Histogram) -> Self { Histogram { bucket_bounds: h.bucket_bounds.into_iter().map(Into::into).collect(), counts: h.counts diff --git a/rpc/src/v1/types/index.rs b/rpc/src/v1/types/index.rs index 3f4b4e3170..8e6fab18ed 100644 --- a/rpc/src/v1/types/index.rs +++ b/rpc/src/v1/types/index.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/log.rs b/rpc/src/v1/types/log.rs index 57b2cdd5dc..55654f6e35 100644 --- a/rpc/src/v1/types/log.rs +++ b/rpc/src/v1/types/log.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -104,7 +104,7 @@ mod tests { data: vec![].into(), block_hash: Some(H256::from_str("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5").unwrap()), block_number: Some(U256::from(0x4510c)), - transaction_hash: Some(H256::default()), + transaction_hash: Some(H256::zero()), transaction_index: Some(U256::default()), transaction_log_index: Some(1.into()), log_index: Some(U256::from(1)), diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index a41f49fab1..93739467d1 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -32,6 +32,8 @@ mod histogram; mod index; mod log; mod node_kind; +mod private_receipt; +mod private_log; mod provenance; mod receipt; mod rpc_settings; @@ -43,13 +45,12 @@ mod transaction; mod transaction_request; mod transaction_condition; mod work; -mod private_receipt; mod eip191; pub mod pubsub; pub use self::eip191::{EIP191Version, PresignedTransaction}; -pub use self::account_info::{AccountInfo, ExtAccountInfo, HwAccountInfo, EthAccount, StorageProof, RecoveredAccount}; +pub use self::account_info::{AccountInfo, ExtAccountInfo, EthAccount, StorageProof, RecoveredAccount}; pub use self::bytes::Bytes; pub use self::block::{RichBlock, Block, BlockTransactions, Header, RichHeader, Rich}; pub use self::block_number::{BlockNumber, LightBlockNumber, block_number_to_id}; @@ -65,6 +66,8 @@ pub use self::histogram::Histogram; pub use self::index::Index; pub use self::log::Log; pub use self::node_kind::{NodeKind, Availability, Capability}; +pub use self::private_receipt::{PrivateTransactionReceipt, PrivateTransactionReceiptAndTransaction}; +pub use self::private_log::PrivateTransactionLog; pub use self::provenance::Origin; pub use self::receipt::Receipt; pub use self::rpc_settings::RpcSettings; @@ -79,7 +82,6 @@ pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionSta pub use self::transaction_request::TransactionRequest; pub use self::transaction_condition::TransactionCondition; 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 f02f21939d..c34d906185 100644 --- a/rpc/src/v1/types/node_kind.rs +++ b/rpc/src/v1/types/node_kind.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/private_log.rs b/rpc/src/v1/types/private_log.rs new file mode 100644 index 0000000000..66e8294eea --- /dev/null +++ b/rpc/src/v1/types/private_log.rs @@ -0,0 +1,99 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::time::SystemTime; +use ethereum_types::{H160, H256}; +use ethcore_private_tx::{TransactionLog as EthTransactionLog, ValidatorLog as EthValidatorLog, PrivateTxStatus as EthStatus}; + +/// Current status of the private transaction +#[derive(Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub enum Status { + /// Private tx was created but no validation received yet + Created, + /// Private state not found locally and being retrived from peers + PrivateStateSync, + /// Retrieval of the private state failed, transaction not created + PrivateStateSyncFailed, + /// Several validators (but not all) validated the transaction + Validating, + /// All validators validated the private tx + /// Corresponding public tx was created and added into the pool + Deployed, +} + +impl From for Status { + fn from(c: EthStatus) -> Self { + match c { + EthStatus::Created => Status::Created, + EthStatus::PrivateStateSync => Status::PrivateStateSync, + EthStatus::PrivateStateSyncFailed => Status::PrivateStateSyncFailed, + EthStatus::Validating => Status::Validating, + EthStatus::Deployed => Status::Deployed, + } + } +} + +/// Information about private tx validation +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ValidatorLog { + /// Account of the validator + pub account: H160, + /// Validation timestamp, None, if the transaction is not validated yet + pub validation_timestamp: Option, +} + +impl From for ValidatorLog { + fn from(r: EthValidatorLog) -> Self { + ValidatorLog { + account: r.account, + validation_timestamp: r.validation_timestamp.map(|t| t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs()), + } + } +} + +/// Information about the private transaction +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PrivateTransactionLog { + /// Original signed transaction hash (used as a source for private tx) + pub tx_hash: H256, + /// Current status of the private transaction + pub status: Status, + /// Creation timestamp + pub creation_timestamp: u64, + /// List of validations + pub validators: Vec, + /// Timestamp of the resulting public tx deployment + pub deployment_timestamp: Option, + /// Hash of the resulting public tx + pub public_tx_hash: Option, +} + +impl From for PrivateTransactionLog { + fn from(r: EthTransactionLog) -> Self { + PrivateTransactionLog { + tx_hash: r.tx_hash, + status: r.status.into(), + creation_timestamp: r.creation_timestamp.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs(), + validators: r.validators.into_iter().map(Into::into).collect(), + deployment_timestamp: r.deployment_timestamp.map(|t| t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default().as_secs()), + public_tx_hash: r.public_tx_hash, + } + } +} + diff --git a/rpc/src/v1/types/private_receipt.rs b/rpc/src/v1/types/private_receipt.rs index 68e9e716f9..68dc949ca3 100644 --- a/rpc/src/v1/types/private_receipt.rs +++ b/rpc/src/v1/types/private_receipt.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/provenance.rs b/rpc/src/v1/types/provenance.rs index dcdd2408fe..acf65291c8 100644 --- a/rpc/src/v1/types/provenance.rs +++ b/rpc/src/v1/types/provenance.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -67,18 +67,19 @@ impl fmt::Display for Origin { mod tests { use serde_json; use super::Origin; + use ethereum_types::H256; #[test] fn should_serialize_origin() { // given let o1 = Origin::Rpc("test service".into()); - let o3 = Origin::Ipc(5.into()); + let o3 = Origin::Ipc(H256::from_low_u64_be(5)); let o4 = Origin::Signer { - session: 10.into(), + session: H256::from_low_u64_be(10), }; let o5 = Origin::Unknown; let o6 = Origin::Ws { - session: 5.into(), + session: H256::from_low_u64_be(5), }; // when diff --git a/rpc/src/v1/types/pubsub.rs b/rpc/src/v1/types/pubsub.rs index 1586b115c3..25908c0674 100644 --- a/rpc/src/v1/types/pubsub.rs +++ b/rpc/src/v1/types/pubsub.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -31,6 +31,16 @@ pub enum Result { Log(Box), /// Transaction hash TransactionHash(H256), + /// SyncStatus + SyncState(PubSubSyncStatus) +} + +/// PubSbub sync status +#[derive(Debug, Serialize, Eq, PartialEq, Clone)] +#[serde(rename_all="camelCase")] +pub struct PubSubSyncStatus { + /// is_major_syncing? + pub syncing: bool, } impl Serialize for Result { @@ -41,6 +51,7 @@ impl Serialize for Result { Result::Header(ref header) => header.serialize(serializer), Result::Log(ref log) => log.serialize(serializer), Result::TransactionHash(ref hash) => hash.serialize(serializer), + Result::SyncState(ref sync) => sync.serialize(serializer), } } } diff --git a/rpc/src/v1/types/receipt.rs b/rpc/src/v1/types/receipt.rs index f492ca98c5..afa2dcf54a 100644 --- a/rpc/src/v1/types/receipt.rs +++ b/rpc/src/v1/types/receipt.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -43,12 +43,14 @@ pub struct Receipt { /// Logs pub logs: Vec, /// State Root - #[serde(rename = "root")] + // NOTE(niklasad1): EIP98 makes this optional field, if it's missing then skip serializing it + #[serde(skip_serializing_if = "Option::is_none", rename = "root")] pub state_root: Option, /// Logs bloom pub logs_bloom: H2048, /// Status code - #[serde(rename = "status")] + // NOTE(niklasad1): Unknown after EIP98 rules, if it's missing then skip serializing it + #[serde(skip_serializing_if = "Option::is_none", rename = "status")] pub status_code: Option, } @@ -91,8 +93,8 @@ impl From for Receipt { impl From for Receipt { fn from(r: RichReceipt) -> Self { Receipt { - from: None, - to: None, + from: Some(r.from), + to: r.to.map(Into::into), transaction_hash: Some(r.transaction_hash), transaction_index: Some(r.transaction_index.into()), block_hash: None, @@ -132,6 +134,7 @@ impl From for Receipt { mod tests { use serde_json; use v1::types::{Log, Receipt}; + use ethereum_types::{H256, Bloom}; #[test] fn receipt_serialization() { @@ -140,7 +143,7 @@ mod tests { let receipt = Receipt { from: None, to: None, - transaction_hash: Some(0.into()), + transaction_hash: Some(H256::zero()), transaction_index: Some(0.into()), block_hash: Some("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5".parse().unwrap()), block_number: Some(0x4510c.into()), @@ -156,15 +159,15 @@ mod tests { data: vec![].into(), block_hash: Some("ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5".parse().unwrap()), block_number: Some(0x4510c.into()), - transaction_hash: Some(0.into()), + transaction_hash: Some(H256::zero()), transaction_index: Some(0.into()), transaction_log_index: None, log_index: Some(1.into()), log_type: "mined".into(), removed: false, }], - logs_bloom: 15.into(), - state_root: Some(10.into()), + logs_bloom: Bloom::from_low_u64_be(15), + state_root: Some(H256::from_low_u64_be(10)), status_code: Some(1u64.into()), }; diff --git a/rpc/src/v1/types/rpc_settings.rs b/rpc/src/v1/types/rpc_settings.rs index 63dfba7a8e..271049884b 100644 --- a/rpc/src/v1/types/rpc_settings.rs +++ b/rpc/src/v1/types/rpc_settings.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/secretstore.rs b/rpc/src/v1/types/secretstore.rs index ef76ec5b46..e3b1c681a3 100644 --- a/rpc/src/v1/types/secretstore.rs +++ b/rpc/src/v1/types/secretstore.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -32,13 +32,13 @@ pub struct EncryptedDocumentKey { #[cfg(test)] mod tests { use serde_json; - use super::EncryptedDocumentKey; + use super::{EncryptedDocumentKey, H512}; #[test] fn test_serialize_encrypted_document_key() { let initial = EncryptedDocumentKey { - common_point: 1.into(), - encrypted_point: 2.into(), + common_point: H512::from_low_u64_be(1), + encrypted_point: H512::from_low_u64_be(2), encrypted_key: vec![3].into(), }; @@ -46,8 +46,8 @@ mod tests { assert_eq!(serialized, r#"{"common_point":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","encrypted_point":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002","encrypted_key":"0x03"}"#); let deserialized: EncryptedDocumentKey = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized.common_point, 1.into()); - assert_eq!(deserialized.encrypted_point, 2.into()); + assert_eq!(deserialized.common_point, H512::from_low_u64_be(1)); + assert_eq!(deserialized.encrypted_point, H512::from_low_u64_be(2)); assert_eq!(deserialized.encrypted_key, vec![3].into()); } } diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs index 901611fea2..f54303beb8 100644 --- a/rpc/src/v1/types/sync.rs +++ b/rpc/src/v1/types/sync.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -197,7 +197,7 @@ pub struct ChainStatus { mod tests { use serde_json; use std::collections::BTreeMap; - use super::{SyncInfo, SyncStatus, Peers, TransactionStats, ChainStatus}; + use super::{SyncInfo, SyncStatus, Peers, TransactionStats, ChainStatus, H512}; #[test] fn test_serialize_sync_info() { @@ -241,7 +241,7 @@ mod tests { let stats = TransactionStats { first_seen: 100, propagated_to: map![ - 10.into() => 50 + H512::from_low_u64_be(10) => 50 ], }; diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index 58a5ee5962..b59a07b6ef 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,15 +16,14 @@ use std::collections::BTreeMap; -use ethcore::client::Executed; -use ethcore::trace as et; -use ethcore::trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace, trace, TraceError}; +use machine::executed::Executed; +use trace as et; +use trace::{FlatTrace, LocalizedTrace as EthLocalizedTrace, trace, TraceError}; use ethereum_types::{H160, H256, U256}; use serde::ser::SerializeStruct; use serde::{Serialize, Serializer}; use types::account_diff; use types::state_diff; -use vm; use v1::types::Bytes; @@ -214,6 +213,7 @@ impl From for StateDiff { /// Create response #[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] pub struct Create { /// Sender from: H160, @@ -223,6 +223,9 @@ pub struct Create { gas: U256, /// Initialization code init: Bytes, + // Create Type + #[serde(skip_serializing_if="Option::is_none")] + creation_method: Option, } impl From for Create { @@ -232,6 +235,7 @@ impl From for Create { value: c.value, gas: c.gas, init: Bytes::new(c.init), + creation_method: c.creation_method.map(|c| c.into()), } } } @@ -240,8 +244,6 @@ impl From for Create { #[derive(Debug, Serialize)] #[serde(rename_all = "lowercase")] pub enum CallType { - /// None - None, /// Call Call, /// Call code @@ -252,14 +254,32 @@ pub enum CallType { StaticCall, } -impl From for CallType { - fn from(c: vm::CallType) -> Self { +impl From for CallType { + fn from(c: trace::CallType) -> Self { + match c { + trace::CallType::Call => CallType::Call, + trace::CallType::CallCode => CallType::CallCode, + trace::CallType::DelegateCall => CallType::DelegateCall, + trace::CallType::StaticCall => CallType::StaticCall, + } + } +} + +/// Create type. +#[derive(Debug, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum CreationMethod { + /// Create + Create, + /// Create2 + Create2, +} + +impl From for CreationMethod { + fn from(c: trace::CreationMethod) -> Self { match c { - vm::CallType::None => CallType::None, - vm::CallType::Call => CallType::Call, - vm::CallType::CallCode => CallType::CallCode, - vm::CallType::DelegateCall => CallType::DelegateCall, - vm::CallType::StaticCall => CallType::StaticCall, + trace::CreationMethod::Create => CreationMethod::Create, + trace::CreationMethod::Create2 => CreationMethod::Create2, } } } @@ -279,18 +299,19 @@ pub struct Call { /// Input data input: Bytes, /// The type of the call. - call_type: CallType, + call_type: Option, } impl From for Call { fn from(c: trace::Call) -> Self { + let optional: Option = c.call_type.0; Call { from: c.from, to: c.to, value: c.value, gas: c.gas, input: c.input.into(), - call_type: c.call_type.into(), + call_type: optional.map(|c| c.into()), } } } @@ -656,7 +677,8 @@ mod tests { use serde_json; use std::collections::BTreeMap; use v1::types::Bytes; - use ethcore::trace::TraceError; + use trace::TraceError; + use ethereum_types::Address; use super::*; #[test] @@ -675,12 +697,12 @@ mod tests { fn test_trace_call_serialize() { let t = LocalizedTrace { action: Action::Call(Call { - from: 4.into(), - to: 5.into(), + from: Address::from_low_u64_be(4), + to: Address::from_low_u64_be(5), value: 6.into(), gas: 7.into(), input: Bytes::new(vec![0x12, 0x34]), - call_type: CallType::Call, + call_type: Some(CallType::Call), }), result: Res::Call(CallResult { gas_used: 8.into(), @@ -689,9 +711,9 @@ mod tests { trace_address: vec![10], subtraces: 1, transaction_position: Some(11), - transaction_hash: Some(12.into()), + transaction_hash: Some(H256::from_low_u64_be(12)), block_number: 13, - block_hash: 14.into(), + block_hash: H256::from_low_u64_be(14), }; let serialized = serde_json::to_string(&t).unwrap(); assert_eq!(serialized, r#"{"type":"call","action":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"},"result":{"gasUsed":"0x8","output":"0x5678"},"traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); @@ -701,20 +723,20 @@ mod tests { fn test_trace_failed_call_serialize() { let t = LocalizedTrace { action: Action::Call(Call { - from: 4.into(), - to: 5.into(), + from: Address::from_low_u64_be(4), + to: Address::from_low_u64_be(5), value: 6.into(), gas: 7.into(), input: Bytes::new(vec![0x12, 0x34]), - call_type: CallType::Call, + call_type: Some(CallType::Call), }), result: Res::FailedCall(TraceError::OutOfGas), trace_address: vec![10], subtraces: 1, transaction_position: Some(11), - transaction_hash: Some(12.into()), + transaction_hash: Some(H256::from_low_u64_be(12)), block_number: 13, - block_hash: 14.into(), + block_hash: H256::from_low_u64_be(14), }; let serialized = serde_json::to_string(&t).unwrap(); assert_eq!(serialized, r#"{"type":"call","action":{"from":"0x0000000000000000000000000000000000000004","to":"0x0000000000000000000000000000000000000005","value":"0x6","gas":"0x7","input":"0x1234","callType":"call"},"error":"Out of gas","traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); @@ -724,63 +746,65 @@ mod tests { fn test_trace_create_serialize() { let t = LocalizedTrace { action: Action::Create(Create { - from: 4.into(), + from: Address::from_low_u64_be(4), value: 6.into(), gas: 7.into(), init: Bytes::new(vec![0x12, 0x34]), + creation_method: Some(CreationMethod::Create).into(), }), result: Res::Create(CreateResult { gas_used: 8.into(), code: vec![0x56, 0x78].into(), - address: 0xff.into(), + address: Address::from_low_u64_be(0xff), }), trace_address: vec![10], subtraces: 1, transaction_position: Some(11), - transaction_hash: Some(12.into()), + transaction_hash: Some(H256::from_low_u64_be(12)), block_number: 13, - block_hash: 14.into(), + block_hash: H256::from_low_u64_be(14), }; let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234"},"result":{"gasUsed":"0x8","code":"0x5678","address":"0x00000000000000000000000000000000000000ff"},"traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); + assert_eq!(serialized, r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234","creationMethod":"create"},"result":{"gasUsed":"0x8","code":"0x5678","address":"0x00000000000000000000000000000000000000ff"},"traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); } #[test] fn test_trace_failed_create_serialize() { let t = LocalizedTrace { action: Action::Create(Create { - from: 4.into(), + from: Address::from_low_u64_be(4), value: 6.into(), gas: 7.into(), init: Bytes::new(vec![0x12, 0x34]), + creation_method: Some(CreationMethod::Create).into(), }), result: Res::FailedCreate(TraceError::OutOfGas), trace_address: vec![10], subtraces: 1, transaction_position: Some(11), - transaction_hash: Some(12.into()), + transaction_hash: Some(H256::from_low_u64_be(12)), block_number: 13, - block_hash: 14.into(), + block_hash: H256::from_low_u64_be(14), }; let serialized = serde_json::to_string(&t).unwrap(); - assert_eq!(serialized, r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234"},"error":"Out of gas","traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); + assert_eq!(serialized, r#"{"type":"create","action":{"from":"0x0000000000000000000000000000000000000004","value":"0x6","gas":"0x7","init":"0x1234","creationMethod":"create"},"error":"Out of gas","traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); } #[test] fn test_trace_suicide_serialize() { let t = LocalizedTrace { action: Action::Suicide(Suicide { - address: 4.into(), - refund_address: 6.into(), + address: Address::from_low_u64_be(4), + refund_address: Address::from_low_u64_be(6), balance: 7.into(), }), result: Res::None, trace_address: vec![10], subtraces: 1, transaction_position: Some(11), - transaction_hash: Some(12.into()), + transaction_hash: Some(H256::from_low_u64_be(12)), block_number: 13, - block_hash: 14.into(), + block_hash: H256::from_low_u64_be(14), }; let serialized = serde_json::to_string(&t).unwrap(); assert_eq!(serialized, r#"{"type":"suicide","action":{"address":"0x0000000000000000000000000000000000000004","refundAddress":"0x0000000000000000000000000000000000000006","balance":"0x7"},"result":null,"traceAddress":[10],"subtraces":1,"transactionPosition":11,"transactionHash":"0x000000000000000000000000000000000000000000000000000000000000000c","blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); @@ -790,7 +814,7 @@ mod tests { fn test_trace_reward_serialize() { let t = LocalizedTrace { action: Action::Reward(Reward { - author: 4.into(), + author: Address::from_low_u64_be(4), value: 6.into(), reward_type: RewardType::Block, }), @@ -800,7 +824,7 @@ mod tests { transaction_position: None, transaction_hash: None, block_number: 13, - block_hash: 14.into(), + block_hash: H256::from_low_u64_be(14), }; let serialized = serde_json::to_string(&t).unwrap(); assert_eq!(serialized, r#"{"type":"reward","action":{"author":"0x0000000000000000000000000000000000000004","value":"0x6","rewardType":"block"},"result":null,"traceAddress":[10],"subtraces":1,"transactionPosition":null,"transactionHash":null,"blockNumber":13,"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000e"}"#); @@ -852,15 +876,15 @@ mod tests { #[test] fn test_statediff_serialize() { let t = StateDiff(map![ - 42.into() => AccountDiff { + Address::from_low_u64_be(42) => AccountDiff { balance: Diff::Same, nonce: Diff::Born(1.into()), code: Diff::Same, storage: map![ - 42.into() => Diff::Same + H256::from_low_u64_be(42) => Diff::Same ] }, - 69.into() => AccountDiff { + Address::from_low_u64_be(69) => AccountDiff { balance: Diff::Same, nonce: Diff::Changed(ChangedType { from: 1.into(), to: 0.into() }), code: Diff::Died(vec![96].into()), diff --git a/rpc/src/v1/types/trace_filter.rs b/rpc/src/v1/types/trace_filter.rs index a455c3d1cc..3053d1db73 100644 --- a/rpc/src/v1/types/trace_filter.rs +++ b/rpc/src/v1/types/trace_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,9 +16,11 @@ //! Trace filter deserialization. -use ethcore::client::BlockId; -use ethcore::client; use ethereum_types::H160; +use types::{ + ids::BlockId, + trace_filter::Filter, +}; use v1::types::BlockNumber; /// Trace filter @@ -40,9 +42,10 @@ pub struct TraceFilter { pub count: Option, } -impl Into for TraceFilter { - fn into(self) -> client::TraceFilter { +impl Into for TraceFilter { + fn into(self) -> Filter { let num_to_id = |num| match num { + BlockNumber::Hash { hash, .. } => BlockId::Hash(hash), BlockNumber::Num(n) => BlockId::Number(n), BlockNumber::Earliest => BlockId::Earliest, BlockNumber::Latest => BlockId::Latest, @@ -53,7 +56,7 @@ impl Into for TraceFilter { }; let start = self.from_block.map_or(BlockId::Latest, &num_to_id); let end = self.to_block.map_or(BlockId::Latest, &num_to_id); - client::TraceFilter { + Filter { range: start..end, from_address: self.from_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), to_address: self.to_address.map_or_else(Vec::new, |x| x.into_iter().map(Into::into).collect()), @@ -97,8 +100,8 @@ mod tests { assert_eq!(deserialized, TraceFilter { from_block: Some(BlockNumber::Latest), to_block: Some(BlockNumber::Latest), - from_address: Some(vec![Address::from(3).into()]), - to_address: Some(vec![Address::from(5).into()]), + from_address: Some(vec![Address::from_low_u64_be(3).into()]), + to_address: Some(vec![Address::from_low_u64_be(5).into()]), after: 50.into(), count: 100.into(), }); diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 931e038669..9e416026e9 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,7 +18,8 @@ use std::sync::Arc; use serde::{Serialize, Serializer}; use serde::ser::SerializeStruct; -use ethcore::{contract_address, CreateContractAddress}; +use machine::executive::{contract_address}; +use vm::CreateContractAddress; use ethereum_types::{H160, H256, H512, U64, U256}; use miner; use types::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; @@ -286,6 +287,8 @@ mod tests { #[test] fn test_local_transaction_status_serialize() { + use ethereum_types::H256; + let tx_ser = serde_json::to_string(&Transaction::default()).unwrap(); let status1 = LocalTransactionStatus::Pending; let status2 = LocalTransactionStatus::Future; @@ -293,7 +296,7 @@ mod tests { let status4 = LocalTransactionStatus::Dropped(Transaction::default()); let status5 = LocalTransactionStatus::Invalid(Transaction::default()); let status6 = LocalTransactionStatus::Rejected(Transaction::default(), "Just because".into()); - let status7 = LocalTransactionStatus::Replaced(Transaction::default(), 5.into(), 10.into()); + let status7 = LocalTransactionStatus::Replaced(Transaction::default(), 5.into(), H256::from_low_u64_be(10)); assert_eq!( serde_json::to_string(&status1).unwrap(), diff --git a/rpc/src/v1/types/transaction_condition.rs b/rpc/src/v1/types/transaction_condition.rs index 589848fe54..69e8ae6b0a 100644 --- a/rpc/src/v1/types/transaction_condition.rs +++ b/rpc/src/v1/types/transaction_condition.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 944ee11148..b91d3eb2b7 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -157,8 +157,8 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: Some(H160::from(1)), - to: Some(H160::from(2)), + from: Some(H160::from_low_u64_be(1)), + to: Some(H160::from_low_u64_be(2)), gas_price: Some(U256::from(1)), gas: Some(U256::from(2)), value: Some(U256::from(3)), @@ -198,7 +198,7 @@ mod tests { let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); assert_eq!(deserialized, TransactionRequest { - from: Some(H160::from(1).into()), + from: Some(H160::from_low_u64_be(1)), to: None, gas_price: None, gas: None, diff --git a/rpc/src/v1/types/work.rs b/rpc/src/v1/types/work.rs index ed6c7c8e91..5be6cb1e49 100644 --- a/rpc/src/v1/types/work.rs +++ b/rpc/src/v1/types/work.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/scripts/docker/README.md b/scripts/docker/README.md index 86b2fcb6aa..1b970699d6 100644 --- a/scripts/docker/README.md +++ b/scripts/docker/README.md @@ -8,7 +8,7 @@ Builds a lightweight non-root Parity docker image: ``` git clone https://github.com/paritytech/parity-ethereum.git cd parity-ethereum -./docker/centos/build.sh +./scripts/docker/centos/build.sh ``` Fully customised build: @@ -16,7 +16,7 @@ Fully customised build: PARITY_IMAGE_REPO=my-personal/parity \ PARITY_BUILDER_IMAGE_TAG=build-latest \ PARITY_RUNNER_IMAGE_TAG=centos-parity-experimental \ -./docker/centos/build.sh +./scripts/docker/centos/build.sh ``` Default values: diff --git a/scripts/docker/centos/build.sh b/scripts/docker/centos/build.sh index 7215e745f0..df4796b913 100755 --- a/scripts/docker/centos/build.sh +++ b/scripts/docker/centos/build.sh @@ -8,18 +8,18 @@ PARITY_BUILDER_IMAGE_TAG=${PARITY_BUILDER_IMAGE_TAG:-build} PARITY_RUNNER_IMAGE_TAG=${PARITY_RUNNER_IMAGE_TAG:-latest} echo Building $PARITY_IMAGE_REPO:$PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") -docker build --no-cache -t $PARITY_IMAGE_REPO:$PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") . -f docker/centos/Dockerfile.build +docker build --no-cache -t $PARITY_IMAGE_REPO:$PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") . -f scripts/docker/centos/Dockerfile.build echo Creating $PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H"), extracting binary docker create --name extract $PARITY_IMAGE_REPO:$PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") -mkdir docker/centos/parity -docker cp extract:/build/parity-ethereum/target/release/parity docker/centos/parity +mkdir scripts/docker/centos/parity +docker cp extract:/build/parity-ethereum/target/release/parity scripts/docker/centos/parity echo Building $PARITY_IMAGE_REPO:$PARITY_RUNNER_IMAGE_TAG -docker build --no-cache -t $PARITY_IMAGE_REPO:$PARITY_RUNNER_IMAGE_TAG docker/centos/ -f docker/centos/Dockerfile +docker build --no-cache -t $PARITY_IMAGE_REPO:$PARITY_RUNNER_IMAGE_TAG scripts/docker/centos/ -f scripts/docker/centos/Dockerfile echo Cleaning up ... -rm -rf docker/centos/parity +rm -rf scripts/docker/centos/parity docker rm -f extract docker rmi -f $PARITY_IMAGE_REPO:$PARITY_BUILDER_IMAGE_TAG-$(git log -1 --format="%H") diff --git a/scripts/docker/debian/Dockerfile b/scripts/docker/debian/Dockerfile index 39fa77161f..ebbde4a6ae 100644 --- a/scripts/docker/debian/Dockerfile +++ b/scripts/docker/debian/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.39 AS builder +FROM rust:1.44 AS builder # show backtraces ENV RUST_BACKTRACE 1 @@ -19,7 +19,7 @@ ENV RUST_BACKTRACE 1 RUN addgroup --gid 1000 parity && \ adduser --uid 1000 --ingroup parity --shell /bin/sh parity RUN apt-get update -y && \ - apt-get install -y libssl1.1 + apt-get install -y libssl1.1 USER parity diff --git a/scripts/docker/hub/Dockerfile b/scripts/docker/hub/Dockerfile index ac1dd32ee1..5c47464b1b 100644 --- a/scripts/docker/hub/Dockerfile +++ b/scripts/docker/hub/Dockerfile @@ -21,10 +21,11 @@ ENV RUST_BACKTRACE 1 RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends \ - file curl jq; \ + file curl jq ca-certificates; \ # apt cleanup apt-get autoremove -y; \ apt-get clean; \ + update-ca-certificates; \ rm -rf /tmp/* /var/tmp/* /var/lib/apt/lists/*; \ # add user groupadd -g 1000 parity; \ @@ -45,4 +46,3 @@ RUN parity --version EXPOSE 5001 8080 8082 8083 8545 8546 8180 30303/tcp 30303/udp ENTRYPOINT ["/bin/parity"] - diff --git a/scripts/docker/hub/publish-docker.sh b/scripts/docker/hub/publish-docker.sh index 84feedb281..a341ccc128 100755 --- a/scripts/docker/hub/publish-docker.sh +++ b/scripts/docker/hub/publish-docker.sh @@ -23,29 +23,18 @@ case "${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}" in --tag "parity/parity:${SCHEDULE_TAG}" \ --file tools/Dockerfile .; docker push "parity/parity:${SCHEDULE_TAG}";; - "beta") - echo "Docker TAGs - 'parity/parity:beta', 'parity/parity:latest', \ - 'parity/parity:${VERSION}-${CI_COMMIT_REF_NAME}'"; - docker build --no-cache \ - --build-arg VCS_REF="${CI_COMMIT_SHA}" \ - --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ - --tag "parity/parity:beta" \ - --tag "parity/parity:latest" \ - --tag "parity/parity:${VERSION}-${CI_COMMIT_REF_NAME}" \ - --file tools/Dockerfile .; - docker push "parity/parity:beta"; - docker push "parity/parity:latest"; - docker push "parity/parity:${VERSION}-${CI_COMMIT_REF_NAME}";; "stable") echo "Docker TAGs - 'parity/parity:${VERSION}-${CI_COMMIT_REF_NAME}', 'parity/parity:stable'"; docker build --no-cache \ --build-arg VCS_REF="${CI_COMMIT_SHA}" \ --build-arg BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ --tag "parity/parity:${VERSION}-${CI_COMMIT_REF_NAME}" \ + --tag "parity/parity:latest" \ --tag "parity/parity:stable" \ --file tools/Dockerfile .; docker push "parity/parity:${VERSION}-${CI_COMMIT_REF_NAME}"; - docker push "parity/parity:stable";; + docker push "parity/parity:stable"; + docker push "parity/parity:latest";; v[0-9]*.[0-9]*) echo "Docker TAG - 'parity/parity:${VERSION}-${TRACK}'" docker build --no-cache \ diff --git a/scripts/docker/ubuntu-aarch64/Dockerfile b/scripts/docker/ubuntu-aarch64/Dockerfile index 53eb325acf..5837893396 100644 --- a/scripts/docker/ubuntu-aarch64/Dockerfile +++ b/scripts/docker/ubuntu-aarch64/Dockerfile @@ -22,7 +22,6 @@ RUN apt-get -y update && \ curl make cmake file ca-certificates \ g++ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \ libc6-dev-arm64-cross binutils-aarch64-linux-gnu \ - libudev-dev libudev-dev:arm64 \ && \ apt-get clean diff --git a/scripts/docker/ubuntu-arm/Dockerfile b/scripts/docker/ubuntu-arm/Dockerfile index bbdc280d51..56daa637a8 100644 --- a/scripts/docker/ubuntu-arm/Dockerfile +++ b/scripts/docker/ubuntu-arm/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get -y update && \ apt-get install -y --force-yes --no-install-recommends \ curl git make g++ gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \ libc6-dev-armhf-cross wget file ca-certificates \ - binutils-arm-linux-gnueabihf cmake3 libudev-dev \ + binutils-arm-linux-gnueabihf cmake3 \ && \ apt-get clean diff --git a/scripts/gitlab/build-linux.sh b/scripts/gitlab/build-linux.sh index ebd65cd9b3..1552d9ec43 100755 --- a/scripts/gitlab/build-linux.sh +++ b/scripts/gitlab/build-linux.sh @@ -3,45 +3,36 @@ set -e # fail on any error set -u # treat unset variables as error +export CC="sccache "$CC +export CXX="sccache "$CXX echo "__________Show ENVIROMENT__________" -echo "CI_SERVER_NAME: " $CI_SERVER_NAME -echo "CARGO_HOME: " $CARGO_HOME echo "CARGO_TARGET: " $CARGO_TARGET echo "CC: " $CC echo "CXX: " $CXX #strip ON -export RUSTFLAGS=" -C link-arg=-s" -# Linker for crosscomile -echo "_____ Linker _____" -cat .cargo/config +export RUSTFLAGS+=" -C link-arg=-s" echo "_____ Building target: "$CARGO_TARGET" _____" -if [ "${CARGO_TARGET}" = "armv7-linux-androideabi" ] +if [ "${CARGO_TARGET}" = "x86_64-unknown-linux-gnu" ] || [ "${CARGO_TARGET}" = "x86_64-apple-darwin" ] then - time cargo build --target $CARGO_TARGET --verbose --color=always --release -p parity-clib --features final -else - time cargo build --target $CARGO_TARGET --verbose --color=always --release --features final - time cargo build --target $CARGO_TARGET --verbose --color=always --release -p evmbin - time cargo build --target $CARGO_TARGET --verbose --color=always --release -p ethstore-cli - time cargo build --target $CARGO_TARGET --verbose --color=always --release -p ethkey-cli - time cargo build --target $CARGO_TARGET --verbose --color=always --release -p whisper-cli + # NOTE: Enables the aes-ni instructions for RustCrypto dependency. + # If you change this please remember to also update .cargo/config + export RUSTFLAGS+=" -C target-feature=+aes,+sse2,+ssse3" fi +time cargo build --target $CARGO_TARGET --verbose --color=always --release --features final +time cargo build --target $CARGO_TARGET --verbose --color=always --release -p evmbin +time cargo build --target $CARGO_TARGET --verbose --color=always --release -p ethstore-cli +time cargo build --target $CARGO_TARGET --verbose --color=always --release -p ethkey-cli echo "_____ Post-processing binaries _____" rm -rf artifacts/* mkdir -p artifacts/$CARGO_TARGET cd artifacts/$CARGO_TARGET -if [ "${CARGO_TARGET}" = "armv7-linux-androideabi" ] -then - cp -v ../../target/$CARGO_TARGET/release/libparity.so ./libparity.so -else - cp -v ../../target/$CARGO_TARGET/release/parity ./parity - cp -v ../../target/$CARGO_TARGET/release/parity-evm ./parity-evm - cp -v ../../target/$CARGO_TARGET/release/ethstore ./ethstore - cp -v ../../target/$CARGO_TARGET/release/ethkey ./ethkey - cp -v ../../target/$CARGO_TARGET/release/whisper ./whisper -fi +cp -v ../../target/$CARGO_TARGET/release/parity ./parity +cp -v ../../target/$CARGO_TARGET/release/parity-evm ./parity-evm +cp -v ../../target/$CARGO_TARGET/release/ethstore ./ethstore +cp -v ../../target/$CARGO_TARGET/release/ethkey ./ethkey echo "_____ Calculating checksums _____" for binary in $(ls) @@ -51,6 +42,8 @@ do then ./parity tools hash $binary > $binary.sha3 else - echo "> ${binary} cannot be hashed with cross-compiled binary (keccak256)" + echo ">[WARN] ${binary} cannot be hashed with cross-compiled binary (keccak256)" fi done +#show sccache statistics +sccache --show-stats diff --git a/scripts/gitlab/build-unix.sh b/scripts/gitlab/build-unix.sh deleted file mode 100755 index f74825f1ef..0000000000 --- a/scripts/gitlab/build-unix.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash - -set -e # fail on any error -set -u # treat unset variables as error - -echo "__________Show ENVIROMENT__________" -echo "CI_SERVER_NAME: " $CI_SERVER_NAME -echo "CARGO_HOME: " $CARGO_HOME -echo "CARGO_TARGET: " $CARGO_TARGET -echo "CC: " $CC -echo "CXX: " $CXX -#strip ON -export RUSTFLAGS=" -C link-arg=-s" -# Linker for crosscomile -echo "_____ Linker _____" -cat .cargo/config - -echo "_____ Building target: "$CARGO_TARGET" _____" -if [ "${CARGO_TARGET}" = "armv7-linux-androideabi" ] -then - time cargo build --target $CARGO_TARGET --release -p parity-clib --features final -else - time cargo build --target $CARGO_TARGET --release --features final - time cargo build --target $CARGO_TARGET --release -p evmbin - time cargo build --target $CARGO_TARGET --release -p ethstore-cli - time cargo build --target $CARGO_TARGET --release -p ethkey-cli - time cargo build --target $CARGO_TARGET --release -p whisper-cli -fi - -echo "_____ Post-processing binaries _____" -rm -rf artifacts/* -mkdir -p artifacts/$CARGO_TARGET -cd artifacts/$CARGO_TARGET - -if [ "${CARGO_TARGET}" = "armv7-linux-androideabi" ] -then - cp -v ../../target/$CARGO_TARGET/release/libparity.so ./libparity.so -else - cp -v ../../target/$CARGO_TARGET/release/parity ./parity - cp -v ../../target/$CARGO_TARGET/release/parity-evm ./parity-evm - cp -v ../../target/$CARGO_TARGET/release/ethstore ./ethstore - cp -v ../../target/$CARGO_TARGET/release/ethkey ./ethkey - cp -v ../../target/$CARGO_TARGET/release/whisper ./whisper -fi - -echo "_____ Calculating checksums _____" -for binary in $(ls) -do - rhash --sha256 $binary -o $binary.sha256 #do we still need this hash (SHA2)? - rhash --sha3-256 $binary -o $binary.sha3 -done - diff --git a/scripts/gitlab/build-windows.sh b/scripts/gitlab/build-windows.sh index 76332124d1..477a8edde7 100755 --- a/scripts/gitlab/build-windows.sh +++ b/scripts/gitlab/build-windows.sh @@ -14,18 +14,20 @@ echo "RUSTC_WRAPPER: " $RUSTC_WRAPPER echo "SCCACHE_DIR: " $SCCACHE_DIR echo "_____ Building target: "$CARGO_TARGET" _____" + # NOTE: Enables the aes-ni instructions for RustCrypto dependency. + # If you change this please remember to also update .cargo/config +export RUSTFLAGS=" -Ctarget-feature=+aes,+sse2,+ssse3 -Ctarget-feature=+crt-static" + time cargo build --target $CARGO_TARGET --verbose --release --features final time cargo build --target $CARGO_TARGET --verbose --release -p evmbin time cargo build --target $CARGO_TARGET --verbose --release -p ethstore-cli time cargo build --target $CARGO_TARGET --verbose --release -p ethkey-cli -time cargo build --target $CARGO_TARGET --verbose --release -p whisper-cli echo "__________Sign binaries__________" scripts/gitlab/sign-win.cmd $keyfile $certpass target/$CARGO_TARGET/release/parity.exe scripts/gitlab/sign-win.cmd $keyfile $certpass target/$CARGO_TARGET/release/parity-evm.exe scripts/gitlab/sign-win.cmd $keyfile $certpass target/$CARGO_TARGET/release/ethstore.exe scripts/gitlab/sign-win.cmd $keyfile $certpass target/$CARGO_TARGET/release/ethkey.exe -scripts/gitlab/sign-win.cmd $keyfile $certpass target/$CARGO_TARGET/release/whisper.exe echo "_____ Post-processing binaries _____" rm -rf artifacts @@ -37,7 +39,6 @@ cp --verbose ../../target/$CARGO_TARGET/release/parity.exe ./parity.exe cp --verbose ../../target/$CARGO_TARGET/release/parity-evm.exe ./parity-evm.exe cp --verbose ../../target/$CARGO_TARGET/release/ethstore.exe ./ethstore.exe cp --verbose ../../target/$CARGO_TARGET/release/ethkey.exe ./ethkey.exe -cp --verbose ../../target/$CARGO_TARGET/release/whisper.exe ./whisper.exe echo "_____ Calculating checksums _____" for binary in $(ls) diff --git a/scripts/gitlab/publish-onchain.sh b/scripts/gitlab/publish-onchain.sh deleted file mode 100755 index 588cbdfb57..0000000000 --- a/scripts/gitlab/publish-onchain.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -set -e # fail on any error -set -u # treat unset variables as error - -echo "__________Register Release__________" -DATA="secret=$RELEASES_SECRET" - -echo "Pushing release to Mainnet" -./scripts/gitlab/safe-curl.sh $DATA "http://update.parity.io:1337/push-release/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/$CI_COMMIT_SHA" - -echo "Pushing release to Kovan" -./scripts/gitlab/safe-curl.sh $DATA "http://update.parity.io:1338/push-release/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/$CI_COMMIT_SHA" - -cd artifacts -ls -l | sort -k9 -filetest=( * ) -echo ${filetest[*]} -for DIR in "${filetest[@]}"; -do - cd $DIR - if [[ $DIR =~ "windows" ]]; - then - WIN=".exe"; - else - WIN=""; - fi - sha3=$(cat parity.sha3 | awk '{print $1}') - case $DIR in - x86_64* ) - DATA="commit=$CI_COMMIT_SHA&sha3=$sha3&filename=parity$WIN&secret=$RELEASES_SECRET" - ../../scripts/gitlab/safe-curl.sh $DATA "http://update.parity.io:1337/push-build/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/$DIR" - # Kovan - ../../scripts/gitlab/safe-curl.sh $DATA "http://update.parity.io:1338/push-build/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}/$DIR" - ;; - esac - cd .. -done diff --git a/scripts/gitlab/publish-snap.sh b/scripts/gitlab/publish-snap.sh index d9f10ce3ee..f33be15b65 100755 --- a/scripts/gitlab/publish-snap.sh +++ b/scripts/gitlab/publish-snap.sh @@ -11,7 +11,6 @@ SNAP_PACKAGE="parity_"$VERSION"_"$BUILD_ARCH".snap" # Choose snap release channel based on parity ethereum version track case ${TRACK} in nightly) export GRADE="devel" CHANNEL="edge";; - beta) export GRADE="stable" CHANNEL="beta";; stable) export GRADE="stable" CHANNEL="stable";; *) echo "No release" && exit 0;; esac diff --git a/scripts/gitlab/rust-changes.sh b/scripts/gitlab/rust-changes.sh index 236a20d59e..f0283728c6 100755 --- a/scripts/gitlab/rust-changes.sh +++ b/scripts/gitlab/rust-changes.sh @@ -7,7 +7,7 @@ echo "__________Checking if Rust files were changed__________" git log --graph --oneline --decorate=short -n 10 case ${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}} in - (beta|stable) + (stable) export GIT_COMPARE=origin/${SCHEDULE_TAG:-${CI_COMMIT_REF_NAME}}~ ;; (master|nightly) diff --git a/scripts/gitlab/test-linux.sh b/scripts/gitlab/test-linux.sh index d7e6da0bb6..808c741836 100755 --- a/scripts/gitlab/test-linux.sh +++ b/scripts/gitlab/test-linux.sh @@ -5,8 +5,11 @@ echo "________Running test-linux.sh________" set -e # fail on any error set -u # treat unset variables as error -FEATURES="json-tests,ci-skip-tests" -OPTIONS="" +export CC="sccache "$CC +export CXX="sccache "$CXX +FEATURES="json-tests" + +OPTIONS="--release" #use nproc `linux only THREADS=$(nproc) @@ -15,4 +18,7 @@ rustup show echo "________Running Parity Full Test Suite________" # Why are we using RUSTFLAGS? See https://github.com/paritytech/parity-ethereum/pull/10719 -CARGO_INCREMENTAL=0 RUSTFLAGS="-C opt-level=3 -C overflow-checks=on -C debuginfo=2" time cargo test $OPTIONS --features "$FEATURES" --locked --all --target $CARGO_TARGET --verbose --color=never -- --test-threads $THREADS +CARGO_INCREMENTAL=0 RUSTFLAGS="-C opt-level=3 -C overflow-checks=on -C debuginfo=2 -Ctarget-feature=+aes,+sse2,+ssse3" time cargo test $OPTIONS --features "$FEATURES" --locked --all --target $CARGO_TARGET --verbose --color=never -- --test-threads $THREADS + +#show sccache statistics +sccache --show-stats diff --git a/scripts/gitlab/validate-chainspecs.sh b/scripts/gitlab/validate-chainspecs.sh index 58391e1312..fd0f73926c 100755 --- a/scripts/gitlab/validate-chainspecs.sh +++ b/scripts/gitlab/validate-chainspecs.sh @@ -16,4 +16,14 @@ for spec in ethcore/res/ethereum/*.json; do if ! ./target/release/chainspec "$spec"; then ERR=1; fi done +echo "________Mainnet contains Istanbul EIPs________" +for eip in $(grep --only-matching "eip.*Transition" ethcore/res/ethereum/istanbul_test.json); do + if ! grep -q $eip ethcore/res/ethereum/foundation.json; then + echo "ERROR: $eip is missing in the foundation json spec" + ERR=1 + fi +done + +#show sccache statistics +sccache --show-stats exit $ERR diff --git a/scripts/snap/snapcraft.template.yaml b/scripts/snap/snapcraft.template.yaml index 7307c4ff6c..eba7c7f253 100644 --- a/scripts/snap/snapcraft.template.yaml +++ b/scripts/snap/snapcraft.template.yaml @@ -25,9 +25,6 @@ apps: ethstore: command: ethstore plugs: [home, removable-media] - whisper: - command: whisper - plugs: [home, network-bind, removable-media] icon: ./scripts/snap/icon.png @@ -49,5 +46,4 @@ parts: cp -v parity-evm $SNAPCRAFT_PART_INSTALL/usr/bin/parity-evm cp -v ethkey $SNAPCRAFT_PART_INSTALL/usr/bin/ethkey cp -v ethstore $SNAPCRAFT_PART_INSTALL/usr/bin/ethstore - cp -v whisper $SNAPCRAFT_PART_INSTALL/usr/bin/whisper - stage-packages: [libudev1, libstdc++6, cmake, libdb5.3] + stage-packages: [libstdc++6, cmake, libdb5.3] diff --git a/secret-store/Cargo.toml b/secret-store/Cargo.toml index 6f5e16d7a5..2c19907a58 100644 --- a/secret-store/Cargo.toml +++ b/secret-store/Cargo.toml @@ -7,41 +7,35 @@ authors = ["Parity Technologies "] [dependencies] byteorder = "1.0" -common-types = { path = "../ethcore/types" } -ethabi = "6.0" -ethabi-contract = "6.0" -ethabi-derive = "6.0" -ethcore = { path = "../ethcore" } -ethcore-accounts = { path = "../accounts", optional = true} -ethcore-call-contract = { path = "../ethcore/call-contract" } -ethcore-sync = { path = "../ethcore/sync" } -ethereum-types = "0.4" -ethkey = { path = "../accounts/ethkey" } +ethabi = "9.0.1" +ethabi-contract = "9.0.0" +ethabi-derive = "9.0.1" +ethereum-types = "0.8.0" +ethkey = { path = "../accounts/ethkey", optional = true } futures = "0.1" hyper = { version = "0.12", default-features = false } -keccak-hash = "0.1" -kvdb = "0.1" +keccak-hash = "0.4.0" +kvdb = "0.3.1" +kvdb-rocksdb = "0.4.1" lazy_static = "1.0" log = "0.4" parity-bytes = "0.1" -parity-crypto = "0.3" +parity-crypto = { version = "0.4.2", features = ["publickey"] } parity-runtime = { path = "../util/runtime" } -parking_lot = "0.7" +parking_lot = "0.9" +percent-encoding = "2.1.0" rustc-hex = "1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" tiny-keccak = "1.4" -tokio = "~0.1.11" +tokio = "0.1.22" tokio-io = "0.1" tokio-service = "0.1" -url = "1.0" +url = "2.1.0" +jsonrpc-server-utils = "14.0.3" [dev-dependencies] env_logger = "0.5" -ethcore = { path = "../ethcore", features = ["test-helpers"] } tempdir = "0.3" -kvdb-rocksdb = "0.1.3" - -[features] -accounts = ["ethcore-accounts"] +kvdb-rocksdb = "0.4.1" diff --git a/secret-store/src/acl_storage.rs b/secret-store/src/acl_storage.rs index 8c6656b108..2c9dbf2d20 100644 --- a/secret-store/src/acl_storage.rs +++ b/secret-store/src/acl_storage.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,12 +17,10 @@ use std::sync::Arc; use std::collections::{HashMap, HashSet}; use parking_lot::{Mutex, RwLock}; -use call_contract::CallContract; -use ethcore::client::{BlockId, ChainNotify, NewBlocks}; use ethereum_types::Address; use ethabi::FunctionOutputDecoder; -use trusted_client::TrustedClient; -use types::{Error, ServerKeyId, ContractAddress}; +use blockchain::{SecretStoreChain, NewBlocksNotify, ContractAddress, BlockId}; +use types::{Error, ServerKeyId}; use_contract!(acl_storage, "res/acl_storage.json"); @@ -30,7 +28,7 @@ const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checke /// ACL storage of Secret Store pub trait AclStorage: Send + Sync { - /// Check if requestor can access document with hash `document` + /// Check if requester can access document with hash `document` fn check(&self, requester: Address, document: &ServerKeyId) -> Result; } @@ -43,7 +41,7 @@ pub struct OnChainAclStorage { /// Cached on-chain ACL storage contract. struct CachedContract { /// Blockchain client. - client: TrustedClient, + client: Arc, /// Contract address source. address_source: ContractAddress, /// Current contract address. @@ -57,14 +55,11 @@ pub struct DummyAclStorage { } impl OnChainAclStorage { - pub fn new(trusted_client: TrustedClient, address_source: ContractAddress) -> Result, Error> { - let client = trusted_client.get_untrusted(); + pub fn new(trusted_client: Arc, address_source: ContractAddress) -> Result, Error> { let acl_storage = Arc::new(OnChainAclStorage { - contract: Mutex::new(CachedContract::new(trusted_client, address_source)), + contract: Mutex::new(CachedContract::new(trusted_client.clone(), address_source)), }); - client - .ok_or_else(|| Error::Internal("Constructing OnChainAclStorage without active Client".into()))? - .add_notify(acl_storage.clone()); + trusted_client.add_listener(acl_storage.clone()); Ok(acl_storage) } } @@ -75,17 +70,14 @@ impl AclStorage for OnChainAclStorage { } } -impl ChainNotify for OnChainAclStorage { - fn new_blocks(&self, new_blocks: NewBlocks) { - if new_blocks.has_more_blocks_to_import { return } - if !new_blocks.route.enacted().is_empty() || !new_blocks.route.retracted().is_empty() { - self.contract.lock().update_contract_address() - } +impl NewBlocksNotify for OnChainAclStorage { + fn new_blocks(&self, _new_enacted_len: usize) { + self.contract.lock().update_contract_address() } } impl CachedContract { - pub fn new(client: TrustedClient, address_source: ContractAddress) -> Self { + pub fn new(client: Arc, address_source: ContractAddress) -> Self { let mut contract = CachedContract { client, address_source, @@ -96,7 +88,10 @@ impl CachedContract { } pub fn update_contract_address(&mut self) { - let contract_address = self.client.read_contract_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.into(), &self.address_source); + let contract_address = self.client.read_contract_address( + ACL_CHECKER_CONTRACT_REGISTRY_NAME, + &self.address_source + ); if contract_address != self.contract_address { trace!(target: "secretstore", "Configuring for ACL checker contract from address {:?}", contract_address); @@ -106,12 +101,12 @@ impl CachedContract { } pub fn check(&mut self, requester: Address, document: &ServerKeyId) -> Result { - if let Some(client) = self.client.get() { + if self.client.is_trusted() { // call contract to check accesss match self.contract_address { Some(contract_address) => { let (encoded, decoder) = acl_storage::functions::check_permissions::call(requester, document.clone()); - let d = client.call_contract(BlockId::Latest, contract_address, encoded) + let d = self.client.call_contract(BlockId::Latest, contract_address, encoded) .map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string())))?; decoder.decode(&d) .map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string()))) @@ -125,7 +120,7 @@ impl CachedContract { } impl DummyAclStorage { - /// Prohibit given requestor access to given documents + /// Prohibit given requester access to given documents #[cfg(test)] pub fn prohibit(&self, requester: Address, document: ServerKeyId) { self.prohibited.write() diff --git a/secret-store/src/blockchain.rs b/secret-store/src/blockchain.rs new file mode 100644 index 0000000000..bed3eb1a77 --- /dev/null +++ b/secret-store/src/blockchain.rs @@ -0,0 +1,119 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +use std::sync::Arc; +use bytes::Bytes; +use ethereum_types::{H256, Address, Public}; +use ethabi::RawLog; +use crypto::publickey::{Signature, Error as EthKeyError}; + +/// Type for block number. +/// Duplicated from ethcore types +pub type BlockNumber = u64; + +/// Uniquely identifies block. +/// Duplicated from ethcore types +#[derive(Debug, PartialEq, Copy, Clone, Hash, Eq)] +pub enum BlockId { + /// Block's sha3. + /// Querying by hash is always faster. + Hash(H256), + /// Block number within canon blockchain. + Number(BlockNumber), + /// Earliest block (genesis). + Earliest, + /// Latest mined block. + Latest, +} + +/// Contract address. +#[derive(Debug, Clone)] +pub enum ContractAddress { + /// Address is read from registry. + Registry, + /// Address is specified. + Address(ethereum_types::Address), +} + +/// Key pair with signing ability. +pub trait SigningKeyPair: Send + Sync { + /// Public portion of key. + fn public(&self) -> &Public; + /// Address of key owner. + fn address(&self) -> Address; + /// Sign data with the key. + fn sign(&self, data: &H256) -> Result; +} + +/// Wrapps client ChainNotify in order to send signal about new blocks +pub trait NewBlocksNotify: Send + Sync { + /// Fires when chain has new blocks. + /// Sends this signal only, if contracts' update required + fn new_blocks(&self, _new_enacted_len: usize) { + // does nothing by default + } +} + +/// Blockchain logs Filter. +#[derive(Debug, PartialEq)] +pub struct Filter { + /// Blockchain will be searched from this block. + pub from_block: BlockId, + + /// Search addresses. + /// + /// If None, match all. + /// If specified, log must be produced by one of these addresses. + pub address: Option>, + + /// Search topics. + /// + /// If None, match all. + /// If specified, log must contain one of these topics. + pub topics: Vec>>, +} + +/// Blockchain representation for Secret Store +pub trait SecretStoreChain: Send + Sync + 'static { + /// Adds listener for chain's NewBlocks event + fn add_listener(&self, target: Arc); + + /// Check if the underlying chain is in the trusted state + fn is_trusted(&self) -> bool; + + /// Transact contract. + fn transact_contract(&self, contract: Address, tx_data: Bytes) -> Result<(), EthKeyError>; + + /// 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. + fn read_contract_address(&self, registry_name: &str, address: &ContractAddress) -> Option
; + + /// Call contract in the blockchain + fn call_contract(&self, block_id: BlockId, contract_address: Address, data: Bytes) -> Result; + + /// Returns blockhash for block id + fn block_hash(&self, id: BlockId) -> Option; + + /// Returns block number for block id + fn block_number(&self, id: BlockId) -> Option; + + /// Retrieve last blockchain logs for the filter + fn retrieve_last_logs(&self, filter: Filter) -> Option>; + + /// Get hash of the last block with predefined number of confirmations (depends on the chain). + fn get_confirmed_block_hash(&self) -> Option; +} diff --git a/secret-store/src/helpers.rs b/secret-store/src/helpers.rs deleted file mode 100644 index 19b19d1ba1..0000000000 --- a/secret-store/src/helpers.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use ethcore::client::{Client, BlockChainClient, BlockId}; -use ethereum_types::H256; - -// 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; - -/// 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))) -} diff --git a/secret-store/src/key_server.rs b/secret-store/src/key_server.rs index 3f03e7e259..abc2a75f51 100644 --- a/secret-store/src/key_server.rs +++ b/secret-store/src/key_server.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,15 +16,17 @@ use std::collections::BTreeSet; use std::sync::Arc; +use futures::{future::{err, result}, Future}; use parking_lot::Mutex; use crypto::DEFAULT_MAC; -use ethkey::crypto; +use crypto::publickey::public_to_address; use parity_runtime::Executor; use super::acl_storage::AclStorage; use super::key_storage::KeyStorage; use super::key_server_set::KeyServerSet; -use key_server_cluster::{math, new_network_cluster}; -use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{math, new_network_cluster, ClusterSession, WaitableSession}; +use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; use types::{Error, Public, RequestSignature, Requester, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, ClusterConfiguration, MessageHash, EncryptedMessageSignature, NodeId}; use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration, NetConnectionsManagerConfig}; @@ -36,13 +38,13 @@ pub struct KeyServerImpl { /// Secret store key server data. pub struct KeyServerCore { - cluster: Arc, + cluster: Arc, } impl KeyServerImpl { /// Create new key server instance - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, - acl_storage: Arc, key_storage: Arc, executor: Executor) -> Result + pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, + acl_storage: Arc, key_storage: Arc, executor: Executor) -> Result { Ok(KeyServerImpl { data: Arc::new(Mutex::new(KeyServerCore::new(config, key_server_set, self_key_pair, acl_storage, key_storage, executor)?)), @@ -50,7 +52,7 @@ impl KeyServerImpl { } /// Get cluster client reference. - pub fn cluster(&self) -> Arc { + pub fn cluster(&self) -> Arc { self.data.lock().cluster.clone() } } @@ -58,122 +60,218 @@ impl KeyServerImpl { impl KeyServer for KeyServerImpl {} impl AdminSessionsServer for KeyServerImpl { - fn change_servers_set(&self, old_set_signature: RequestSignature, new_set_signature: RequestSignature, new_servers_set: BTreeSet) -> Result<(), Error> { - let servers_set_change_session = self.data.lock().cluster - .new_servers_set_change_session(None, None, new_servers_set, old_set_signature, new_set_signature)?; - servers_set_change_session.as_servers_set_change() - .expect("new_servers_set_change_session creates servers_set_change_session; qed") - .wait().map_err(Into::into) + fn change_servers_set( + &self, + old_set_signature: RequestSignature, + new_set_signature: RequestSignature, + new_servers_set: BTreeSet, + ) -> Box + Send> { + return_session(self.data.lock().cluster + .new_servers_set_change_session(None, None, new_servers_set, old_set_signature, new_set_signature)) } } impl ServerKeyGenerator for KeyServerImpl { - fn generate_key(&self, key_id: &ServerKeyId, author: &Requester, threshold: usize) -> Result { - // recover requestor' public key from signature - let address = author.address(key_id).map_err(Error::InsufficientRequesterData)?; + fn generate_key( + &self, + key_id: ServerKeyId, + author: Requester, + threshold: usize, + ) -> Box + Send> { + // recover requestor' address key from signature + 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(), None, address, threshold)?; - generation_session.wait(None) - .expect("when wait is called without timeout it always returns Some; qed") - .map_err(Into::into) + return_session(address.and_then(|address| self.data.lock().cluster + .new_generation_session(key_id, None, address, threshold))) + } + + fn restore_key_public( + &self, + key_id: ServerKeyId, + author: Requester, + ) -> Box + Send> { + // recover requestor' public key from signature + let session_and_address = author + .address(&key_id) + .map_err(Error::InsufficientRequesterData) + .and_then(|address| self.data.lock().cluster.new_key_version_negotiation_session(key_id) + .map(|session| (session, address))); + let (session, address) = match session_and_address { + Ok((session, address)) => (session, address), + Err(error) => return Box::new(err(error)), + }; + + // negotiate key version && retrieve common key data + let core_session = session.session.clone(); + Box::new(session.into_wait_future() + .and_then(move |_| core_session.common_key_data() + .map(|key_share| (key_share, address))) + .and_then(|(key_share, address)| if key_share.author == address { + Ok(key_share.public) + } else { + Err(Error::AccessDenied) + })) } } impl DocumentKeyServer for KeyServerImpl { - fn store_document_key(&self, key_id: &ServerKeyId, author: &Requester, 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, + ) -> Box + Send> { // store encrypted key - let encryption_session = self.data.lock().cluster.new_encryption_session(key_id.clone(), - author.clone(), common_point, encrypted_document_key)?; - encryption_session.wait(None).map_err(Into::into) + return_session(self.data.lock().cluster.new_encryption_session(key_id, + author.clone(), common_point, encrypted_document_key)) } - fn generate_document_key(&self, key_id: &ServerKeyId, author: &Requester, threshold: usize) -> Result { + fn generate_document_key( + &self, + key_id: ServerKeyId, + author: Requester, + threshold: usize, + ) -> Box + Send> { // recover requestor' public key from signature - let public = author.public(key_id).map_err(Error::InsufficientRequesterData)?; + let public = result(author.public(&key_id).map_err(Error::InsufficientRequesterData)); // generate server key - let server_key = self.generate_key(key_id, author, threshold)?; + let data = self.data.clone(); + let server_key = public.and_then(move |public| { + let data = data.lock(); + let session = data.cluster.new_generation_session(key_id, None, public_to_address(&public), threshold); + result(session.map(|session| (public, session))) + }) + .and_then(|(public, session)| session.into_wait_future().map(move |server_key| (public, server_key))); // generate random document key - let document_key = math::generate_random_point()?; - let encrypted_document_key = math::encrypt_secret(&document_key, &server_key)?; + let document_key = server_key.and_then(|(public, server_key)| + result(math::generate_random_point() + .and_then(|document_key| math::encrypt_secret(&document_key, &server_key) + .map(|encrypted_document_key| (public, document_key, encrypted_document_key)))) + ); // store document key in the storage - self.store_document_key(key_id, author, encrypted_document_key.common_point, encrypted_document_key.encrypted_point)?; + let data = self.data.clone(); + let stored_document_key = document_key.and_then(move |(public, document_key, encrypted_document_key)| { + let data = data.lock(); + let session = data.cluster.new_encryption_session(key_id, + author.clone(), encrypted_document_key.common_point, encrypted_document_key.encrypted_point); + result(session.map(|session| (public, document_key, session))) + }) + .and_then(|(public, document_key, session)| session.into_wait_future().map(move |_| (public, document_key))); // encrypt document key with requestor public 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) + let encrypted_document_key = stored_document_key + .and_then(|(public, document_key)| crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, document_key.as_bytes()) + .map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))); + + Box::new(encrypted_document_key) } - fn restore_document_key(&self, key_id: &ServerKeyId, requester: &Requester) -> Result { + fn restore_document_key( + &self, + key_id: ServerKeyId, + requester: Requester, + ) -> Box + Send> { // recover requestor' public key from signature - let public = requester.public(key_id).map_err(Error::InsufficientRequesterData)?; + let public = result(requester.public(&key_id).map_err(Error::InsufficientRequesterData)); // decrypt document key - let decryption_session = self.data.lock().cluster.new_decryption_session(key_id.clone(), - 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; + let data = self.data.clone(); + let stored_document_key = public.and_then(move |public| { + let data = data.lock(); + let session = data.cluster.new_decryption_session(key_id, None, requester.clone(), None, false, false); + result(session.map(|session| (public, session))) + }) + .and_then(|(public, session)| session.into_wait_future().map(move |document_key| (public, document_key))); // encrypt document key with requestor public 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) + let encrypted_document_key = stored_document_key + .and_then(|(public, document_key)| + crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, document_key.decrypted_secret.as_bytes()) + .map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))); + + Box::new(encrypted_document_key) } - 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(), - 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) + fn restore_document_key_shadow( + &self, + key_id: ServerKeyId, + requester: Requester, + ) -> Box + Send> { + return_session(self.data.lock().cluster.new_decryption_session(key_id, + None, requester.clone(), None, true, false)) } } impl MessageSigner for KeyServerImpl { - fn sign_message_schnorr(&self, key_id: &ServerKeyId, requester: &Requester, message: MessageHash) -> Result { + fn sign_message_schnorr( + &self, + key_id: ServerKeyId, + requester: Requester, + message: MessageHash, + ) -> Box + Send> { // recover requestor' public key from signature - let public = requester.public(key_id).map_err(Error::InsufficientRequesterData)?; + let public = result(requester.public(&key_id).map_err(Error::InsufficientRequesterData)); // sign message - let signing_session = self.data.lock().cluster.new_schnorr_signing_session(key_id.clone(), - requester.clone().into(), None, message)?; - let message_signature = signing_session.wait()?; + let data = self.data.clone(); + let signature = public.and_then(move |public| { + let data = data.lock(); + let session = data.cluster.new_schnorr_signing_session(key_id, requester.clone().into(), None, message); + result(session.map(|session| (public, session))) + }) + .and_then(|(public, session)| session.into_wait_future().map(move |signature| (public, signature))); // compose two message signature components into single one - let mut combined_signature = [0; 64]; - combined_signature[..32].clone_from_slice(&**message_signature.0); - combined_signature[32..].clone_from_slice(&**message_signature.1); - - // encrypt combined signature with requestor public key - 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) + let combined_signature = signature.map(|(public, signature)| { + let mut combined_signature = [0; 64]; + combined_signature[..32].clone_from_slice(signature.0.as_bytes()); + combined_signature[32..].clone_from_slice(signature.1.as_bytes()); + (public, combined_signature) + }); + + // encrypt signature with requestor public key + let encrypted_signature = combined_signature + .and_then(|(public, combined_signature)| crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, &combined_signature) + .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))); + + Box::new(encrypted_signature) } - fn sign_message_ecdsa(&self, key_id: &ServerKeyId, requester: &Requester, message: MessageHash) -> Result { + fn sign_message_ecdsa( + &self, + key_id: ServerKeyId, + requester: Requester, + message: MessageHash, + ) -> Box + Send> { // recover requestor' public key from signature - let public = requester.public(key_id).map_err(Error::InsufficientRequesterData)?; + let public = result(requester.public(&key_id).map_err(Error::InsufficientRequesterData)); // sign message - let signing_session = self.data.lock().cluster.new_ecdsa_signing_session(key_id.clone(), - requester.clone().into(), None, message)?; - let message_signature = signing_session.wait()?; + let data = self.data.clone(); + let signature = public.and_then(move |public| { + let data = data.lock(); + let session = data.cluster.new_ecdsa_signing_session(key_id, requester.clone().into(), None, message); + result(session.map(|session| (public, session))) + }) + .and_then(|(public, session)| session.into_wait_future().map(move |signature| (public, signature))); // encrypt combined signature with requestor public key - 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) + let encrypted_signature = signature + .and_then(|(public, signature)| crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, &*signature) + .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))); + + Box::new(encrypted_signature) } } impl KeyServerCore { - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, - acl_storage: Arc, key_storage: Arc, executor: Executor) -> Result + pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, + acl_storage: Arc, key_storage: Arc, executor: Executor) -> Result { let cconfig = NetClusterConfiguration { self_key_pair: self_key_pair.clone(), @@ -199,6 +297,15 @@ impl KeyServerCore { } } +fn return_session( + session: Result, Error>, +) -> Box + Send> { + match session { + Ok(session) => Box::new(session.into_wait_future()), + Err(error) => Box::new(err(error)) + } +} + #[cfg(test)] pub mod tests { use std::collections::BTreeSet; @@ -206,8 +313,9 @@ pub mod tests { use std::sync::Arc; use std::net::SocketAddr; use std::collections::BTreeMap; + use futures::Future; use crypto::DEFAULT_MAC; - use ethkey::{self, crypto, Secret, Random, Generator, verify_public}; + use crypto::publickey::{Secret, Random, Generator, verify_public}; use acl_storage::DummyAclStorage; use key_storage::KeyStorage; use key_storage::tests::DummyKeyStorage; @@ -228,41 +336,88 @@ pub mod tests { impl KeyServer for DummyKeyServer {} impl AdminSessionsServer for DummyKeyServer { - fn change_servers_set(&self, _old_set_signature: RequestSignature, _new_set_signature: RequestSignature, _new_servers_set: BTreeSet) -> Result<(), Error> { + fn change_servers_set( + &self, + _old_set_signature: RequestSignature, + _new_set_signature: RequestSignature, + _new_servers_set: BTreeSet, + ) -> Box + Send> { unimplemented!("test-only") } } impl ServerKeyGenerator for DummyKeyServer { - fn generate_key(&self, _key_id: &ServerKeyId, _author: &Requester, _threshold: usize) -> Result { + fn generate_key( + &self, + _key_id: ServerKeyId, + _author: Requester, + _threshold: usize, + ) -> Box + Send> { + unimplemented!("test-only") + } + + fn restore_key_public( + &self, + _key_id: ServerKeyId, + _author: Requester, + ) -> Box + Send> { unimplemented!("test-only") } } impl DocumentKeyServer for DummyKeyServer { - fn store_document_key(&self, _key_id: &ServerKeyId, _author: &Requester, _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, + ) -> Box + Send> { unimplemented!("test-only") } - fn generate_document_key(&self, _key_id: &ServerKeyId, _author: &Requester, _threshold: usize) -> Result { + fn generate_document_key( + &self, + _key_id: ServerKeyId, + _author: Requester, + _threshold: usize, + ) -> Box + Send> { unimplemented!("test-only") } - fn restore_document_key(&self, _key_id: &ServerKeyId, _requester: &Requester) -> Result { + fn restore_document_key( + &self, + _key_id: ServerKeyId, + _requester: Requester, + ) -> Box + Send> { unimplemented!("test-only") } - fn restore_document_key_shadow(&self, _key_id: &ServerKeyId, _requester: &Requester) -> Result { + fn restore_document_key_shadow( + &self, + _key_id: ServerKeyId, + _requester: Requester, + ) -> Box + Send> { unimplemented!("test-only") } } impl MessageSigner for DummyKeyServer { - fn sign_message_schnorr(&self, _key_id: &ServerKeyId, _requester: &Requester, _message: MessageHash) -> Result { + fn sign_message_schnorr( + &self, + _key_id: ServerKeyId, + _requester: Requester, + _message: MessageHash, + ) -> Box + Send> { unimplemented!("test-only") } - fn sign_message_ecdsa(&self, _key_id: &ServerKeyId, _requester: &Requester, _message: MessageHash) -> Result { + fn sign_message_ecdsa( + &self, + _key_id: ServerKeyId, + _requester: Requester, + _message: MessageHash, + ) -> Box + Send> { unimplemented!("test-only") } } @@ -335,14 +490,21 @@ pub mod tests { 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.clone().into(), threshold).unwrap(); - let generated_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); + let signature: Requester = crypto::publickey::sign(&secret, &document).unwrap().into(); + let generated_key = key_servers[0].generate_document_key( + *document, + signature.clone(), + threshold, + ).wait().unwrap(); + let generated_key = crypto::publickey::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.clone().into()).unwrap(); - let retrieved_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); + let retrieved_key = key_server.restore_document_key( + *document, + signature.clone(), + ).wait().unwrap(); + let retrieved_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); assert_eq!(retrieved_key, generated_key); } drop(runtime); @@ -358,14 +520,21 @@ pub mod tests { // generate document key 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.clone().into(), *threshold).unwrap(); - let generated_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); + let signature: Requester = crypto::publickey::sign(&secret, &document).unwrap().into(); + let generated_key = key_servers[0].generate_document_key( + *document, + signature.clone(), + *threshold, + ).wait().unwrap(); + let generated_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); // now let's try to retrieve key back 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(); + let retrieved_key = key_server.restore_document_key( + *document, + signature.clone(), + ).wait().unwrap(); + let retrieved_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); assert_eq!(retrieved_key, generated_key); let key_share = key_storages[i].get(&document).unwrap().unwrap(); @@ -386,21 +555,25 @@ pub mod tests { // 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.clone().into(), *threshold).unwrap(); + let signature: Requester = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap().into(); + let server_public = key_servers[0].generate_key( + *server_key_id, + signature.clone(), + *threshold, + ).wait().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.clone().into(), - encrypted_document_key.common_point, encrypted_document_key.encrypted_point).unwrap(); + key_servers[0].store_document_key(*server_key_id, signature.clone(), + encrypted_document_key.common_point, encrypted_document_key.encrypted_point).wait().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.clone().into()).unwrap(); - let retrieved_key = crypto::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &retrieved_key).unwrap(); + let retrieved_key = key_server.restore_document_key(*server_key_id, signature.clone()).wait().unwrap(); + let retrieved_key = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &retrieved_key).unwrap(); let retrieved_key = Public::from_slice(&retrieved_key); assert_eq!(retrieved_key, generated_key); } @@ -418,15 +591,23 @@ pub mod tests { // 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.clone().into(), *threshold).unwrap(); + let signature: Requester = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap().into(); + let server_public = key_servers[0].generate_key( + *server_key_id, + signature.clone(), + *threshold, + ).wait().unwrap(); // sign message - let message_hash = H256::from(42); - 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(); + let message_hash = H256::from_low_u64_be(42); + let combined_signature = key_servers[0].sign_message_schnorr( + *server_key_id, + signature, + message_hash, + ).wait().unwrap(); + let combined_signature = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &combined_signature).unwrap(); + let signature_c = Secret::copy_from_slice(&combined_signature[..32]).unwrap(); + let signature_s = Secret::copy_from_slice(&combined_signature[32..]).unwrap(); // check signature assert_eq!(math::verify_schnorr_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); @@ -443,16 +624,20 @@ pub mod tests { 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.clone().into(), threshold).unwrap(); - let generated_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); + let signature: Requester = crypto::publickey::sign(&secret, &document).unwrap().into(); + let generated_key = key_servers[0].generate_document_key( + *document, + signature.clone(), + threshold, + ).wait().unwrap(); + let generated_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); // remove key from node0 key_storages[0].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.into()).unwrap(); - let retrieved_key = crypto::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); + let retrieved_key = key_servers[0].restore_document_key(*document, signature).wait().unwrap(); + let retrieved_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); assert_eq!(retrieved_key, generated_key); drop(runtime); } @@ -466,18 +651,22 @@ pub mod tests { // 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.clone().into(), threshold).unwrap(); + let signature: Requester = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap().into(); + let server_public = key_servers[0].generate_key(*server_key_id, signature.clone(), threshold).wait().unwrap(); // remove key from node0 key_storages[0].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.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(); + let message_hash = H256::from_low_u64_be(42); + let combined_signature = key_servers[0].sign_message_schnorr( + *server_key_id, + signature, + message_hash, + ).wait().unwrap(); + let combined_signature = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &combined_signature).unwrap(); + let signature_c = Secret::copy_from_slice(&combined_signature[..32]).unwrap(); + let signature_s = Secret::copy_from_slice(&combined_signature[32..]).unwrap(); // check signature assert_eq!(math::verify_schnorr_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); @@ -493,17 +682,25 @@ pub mod tests { // 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.clone().into(), threshold).unwrap(); + let signature = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap(); + let server_public = key_servers[0].generate_key( + *server_key_id, + signature.clone().into(), + threshold, + ).wait().unwrap(); // remove key from node0 key_storages[0].remove(&server_key_id).unwrap(); // sign message let message_hash = H256::random(); - 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(); + let signature = key_servers[0].sign_message_ecdsa( + *server_key_id, + signature.clone().into(), + message_hash, + ).wait().unwrap(); + let signature = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &signature).unwrap(); + let signature = H520::from_slice(&signature[0..65]); // check signature assert!(verify_public(&server_public, &signature.into(), &message_hash).unwrap()); 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 5880194866..b90d850ff0 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,16 +17,17 @@ use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap}; use ethereum_types::{Address, H256}; -use ethkey::Secret; -use parking_lot::{Mutex, Condvar}; +use crypto::publickey::Secret; +use futures::Oneshot; +use parking_lot::Mutex; use key_server_cluster::{Error, SessionId, NodeId, DocumentKeyShare}; use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession}; +use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; 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, KeyVersionsError, FailedKeyVersionContinueAction}; + KeyVersions, KeyVersionsError, FailedKeyVersionContinueAction, CommonKeyData}; use key_server_cluster::admin_sessions::ShareChangeSessionMeta; // TODO [Opt]: change sessions so that versions are sent by chunks. @@ -82,13 +83,13 @@ struct SessionCore { /// Key share. pub key_share: Option, /// Session result computer. - pub result_computer: Arc, + pub result_computer: Arc, /// Session transport. pub transport: T, /// Session nonce. pub nonce: u64, - /// SessionImpl completion condvar. - pub completed: Condvar, + /// Session completion signal. + pub completed: CompletionSignal>, } /// Mutable session data. @@ -97,8 +98,8 @@ struct SessionData { pub state: SessionState, /// Initialization confirmations. pub confirmations: Option>, - /// Key threshold. - pub threshold: Option, + /// Common key data that nodes have agreed upon. + pub key_share: Option, /// { Version => Nodes } pub versions: Option>>, /// Session result. @@ -118,7 +119,7 @@ pub struct SessionParams { /// Key share. pub key_share: Option, /// Session result computer. - pub result_computer: Arc, + pub result_computer: Arc, /// Session transport to communicate to other cluster nodes. pub transport: T, /// Session nonce. @@ -139,7 +140,7 @@ enum SessionState { /// Isolated session transport. pub struct IsolatedSessionTransport { /// Cluster. - pub cluster: Arc, + pub cluster: Arc, /// Key id. pub key_id: SessionId, /// Sub session id. @@ -166,28 +167,33 @@ 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 { + pub fn new(params: SessionParams) -> (Self, Oneshot, Error>>) { + let (completed, oneshot) = CompletionSignal::new(); + (SessionImpl { core: SessionCore { meta: params.meta, sub_session: params.sub_session, - key_share: params.key_share, + key_share: params.key_share.clone(), result_computer: params.result_computer, transport: params.transport, nonce: params.nonce, - completed: Condvar::new(), + completed, }, data: Mutex::new(SessionData { state: SessionState::WaitingForInitialization, confirmations: None, - threshold: threshold, + key_share: params.key_share.map(|key_share| DocumentKeyShare { + threshold: key_share.threshold, + author: key_share.author, + public: key_share.public, + ..Default::default() + }), versions: None, result: None, continue_with: None, failed_continue_with: None, }) - } + }, oneshot) } /// Return session meta. @@ -195,12 +201,6 @@ impl SessionImpl where T: SessionTransport { &self.core.meta } - /// Return key threshold. - pub fn key_threshold(&self) -> Result { - 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)? @@ -223,10 +223,15 @@ impl SessionImpl where T: SessionTransport { self.data.lock().failed_continue_with.take() } - /// 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") + /// Return session completion result (if available). + pub fn result(&self) -> Option, Error>> { + self.data.lock().result.clone() + } + + /// Retrieve common key data (author, threshold, public), if available. + pub fn common_key_data(&self) -> Result { + self.data.lock().key_share.clone() + .ok_or(Error::InvalidStateForRequest) } /// Initialize session. @@ -322,7 +327,11 @@ impl SessionImpl where T: SessionTransport { session: self.core.meta.id.clone().into(), sub_session: self.core.sub_session.clone().into(), session_nonce: self.core.nonce, - threshold: self.core.key_share.as_ref().map(|key_share| key_share.threshold), + key_common: self.core.key_share.as_ref().map(|key_share| CommonKeyData { + threshold: key_share.threshold, + author: key_share.author.into(), + public: key_share.public.into(), + }), versions: self.core.key_share.as_ref().map(|key_share| key_share.versions.iter().rev() .filter(|v| v.id_numbers.contains_key(sender)) @@ -336,7 +345,7 @@ impl SessionImpl where T: SessionTransport { // update state data.state = SessionState::Finished; data.result = Some(Ok(None)); - self.core.completed.notify_all(); + self.core.completed.send(Ok(None)); Ok(()) } @@ -357,12 +366,25 @@ impl SessionImpl where T: SessionTransport { // remember versions that sender have { - match message.threshold.clone() { - Some(threshold) if data.threshold.is_none() => { - data.threshold = Some(threshold); + match message.key_common.as_ref() { + Some(key_common) if data.key_share.is_none() => { + data.key_share = Some(DocumentKeyShare { + threshold: key_common.threshold, + author: key_common.author.clone().into(), + public: key_common.public.clone().into(), + ..Default::default() + }); + }, + Some(key_common) => { + let prev_key_share = data.key_share.as_ref() + .expect("data.key_share.is_none() is matched by previous branch; qed"); + if prev_key_share.threshold != key_common.threshold || + prev_key_share.author.as_bytes() != key_common.author.as_bytes() || + prev_key_share.public.as_bytes() != key_common.public.as_bytes() + { + return Err(Error::InvalidMessage); + } }, - Some(threshold) if data.threshold.as_ref() == Some(&threshold) => (), - Some(_) => return Err(Error::InvalidMessage), None if message.versions.is_empty() => (), None => return Err(Error::InvalidMessage), } @@ -388,7 +410,8 @@ impl SessionImpl where T: SessionTransport { let reason = "this field is filled on master node when initializing; try_complete is only called on initialized master node; qed"; 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) { + let threshold = data.key_share.as_ref().map(|key_share| key_share.threshold); + if let Some(result) = core.result_computer.compute_result(threshold, 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 @@ -428,15 +451,18 @@ impl SessionImpl where T: SessionTransport { } } + let result = result.map(Some); data.state = SessionState::Finished; - data.result = Some(result.map(Some)); - core.completed.notify_all(); + data.result = Some(result.clone()); + core.completed.send(result); } } } impl ClusterSession for SessionImpl where T: SessionTransport { type Id = SessionIdWithSubSession; + type CreationData = (); + type SuccessfulResult = Option<(H256, NodeId)>; fn type_name() -> &'static str { "version negotiation" @@ -460,7 +486,7 @@ impl ClusterSession for SessionImpl where T: SessionTransport { warn!(target: "secretstore_net", "{}: key version negotiation session failed with timeout", self.core.meta.self_node_id); data.result = Some(Err(Error::ConsensusTemporaryUnreachable)); - self.core.completed.notify_all(); + self.core.completed.send(Err(Error::ConsensusTemporaryUnreachable)); } } } @@ -488,8 +514,8 @@ impl ClusterSession for SessionImpl where T: SessionTransport { self.core.meta.self_node_id, error, node); data.state = SessionState::Finished; - data.result = Some(Err(error)); - self.core.completed.notify_all(); + data.result = Some(Err(error.clone())); + self.core.completed.send(Err(error)); } fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { @@ -590,7 +616,8 @@ impl SessionResultComputer for LargestSupportResultComputer { mod tests { use std::sync::Arc; use std::collections::{VecDeque, BTreeMap, BTreeSet}; - use ethkey::public_to_address; + use ethereum_types::{H512, H160, Address}; + use crypto::publickey::public_to_address; use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, DummyKeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; use key_server_cluster::math; @@ -599,9 +626,14 @@ mod tests { 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, ContinueAction, FailedContinueAction}; + use key_server_cluster::message::{ + Message, KeyVersionNegotiationMessage, RequestKeyVersions, + CommonKeyData, KeyVersions, + }; + use super::{ + SessionImpl, SessionTransport, SessionParams, FastestResultComputer, LargestSupportResultComputer, + SessionResultComputer, SessionState, ContinueAction, FailedContinueAction, + }; struct DummyTransport { cluster: Arc, @@ -670,7 +702,7 @@ mod tests { cluster: cluster, }, nonce: 0, - }), + }).0, }) }).collect(), queue: VecDeque::new(), @@ -756,7 +788,11 @@ mod tests { session: Default::default(), sub_session: math::generate_random_scalar().unwrap().into(), session_nonce: 0, - threshold: Some(10), + key_common: Some(CommonKeyData { + threshold: 10, + author: Default::default(), + public: Default::default(), + }), versions: Vec::new(), })), Err(Error::InvalidStateForRequest)); } @@ -772,7 +808,12 @@ mod tests { session: Default::default(), sub_session: math::generate_random_scalar().unwrap().into(), session_nonce: 0, - threshold: Some(0), + key_common: Some(CommonKeyData { + threshold: 0, + author: Default::default(), + public: Default::default(), + }), + versions: vec![version_id.clone().into()] })), Ok(())); assert_eq!(ml.session(0).data.lock().state, SessionState::Finished); @@ -781,32 +822,61 @@ mod tests { session: Default::default(), sub_session: math::generate_random_scalar().unwrap().into(), session_nonce: 0, - threshold: Some(0), + key_common: Some(CommonKeyData { + threshold: 0, + author: Default::default(), + public: Default::default(), + }), + versions: vec![version_id.clone().into()] })), Ok(())); assert_eq!(ml.session(0).data.lock().state, SessionState::Finished); } #[test] - fn negotiation_fails_if_wrong_threshold_sent() { - let ml = MessageLoop::empty(3); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); + fn negotiation_fails_if_wrong_common_data_sent() { + fn run_test(key_common: CommonKeyData) { + let ml = MessageLoop::empty(3); + ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); + + let version_id = (*math::generate_random_scalar().unwrap()).clone(); + assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { + session: Default::default(), + sub_session: math::generate_random_scalar().unwrap().into(), + session_nonce: 0, + key_common: Some(CommonKeyData { + threshold: 1, + author: Default::default(), + public: Default::default(), + }), + versions: vec![version_id.clone().into()] + })), Ok(())); + assert_eq!(ml.session(0).process_message(ml.node_id(2), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { + session: Default::default(), + sub_session: math::generate_random_scalar().unwrap().into(), + session_nonce: 0, + key_common: Some(key_common), + versions: vec![version_id.clone().into()] + })), Err(Error::InvalidMessage)); + } - let version_id = (*math::generate_random_scalar().unwrap()).clone(); - assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - threshold: Some(1), - versions: vec![version_id.clone().into()] - })), Ok(())); - assert_eq!(ml.session(0).process_message(ml.node_id(2), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - threshold: Some(2), - versions: vec![version_id.clone().into()] - })), Err(Error::InvalidMessage)); + run_test(CommonKeyData { + threshold: 2, + author: Default::default(), + public: Default::default(), + }); + + run_test(CommonKeyData { + threshold: 1, + author: H160::from_low_u64_be(1).into(), + public: Default::default(), + }); + + run_test(CommonKeyData { + threshold: 1, + author: H160::from_low_u64_be(2).into(), + public: Default::default(), + }); } #[test] @@ -819,7 +889,7 @@ mod tests { session: Default::default(), sub_session: math::generate_random_scalar().unwrap().into(), session_nonce: 0, - threshold: None, + key_common: None, versions: vec![version_id.clone().into()] })), Err(Error::InvalidMessage)); } @@ -829,9 +899,9 @@ mod tests { let nodes = MessageLoop::prepare_nodes(2); let version_id = (*math::generate_random_scalar().unwrap()).clone(); nodes.values().nth(0).unwrap().insert(Default::default(), DocumentKeyShare { - author: Default::default(), + author: H160::from_low_u64_be(2), threshold: 1, - public: Default::default(), + public: H512::from_low_u64_be(3), common_point: None, encrypted_point: None, versions: vec![DocumentKeyShareVersion { @@ -845,8 +915,13 @@ mod tests { // 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)); + // check that upon completion, commmon key data is known + assert_eq!(ml.session(0).common_key_data(), Ok(DocumentKeyShare { + author: H160::from_low_u64_be(2), + threshold: 1, + public: H512::from_low_u64_be(3), + ..Default::default() + })); } #[test] @@ -880,7 +955,7 @@ mod tests { #[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).set_continue_action(ContinueAction::Decrypt(create_default_decryption_session(), Some(Address::from_low_u64_be(1)), true, true)); ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); ml.run(); @@ -889,6 +964,6 @@ mod tests { // 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()))))); + == Some(FailedContinueAction::Decrypt(Some(Address::from_low_u64_be(1)), public_to_address(&H512::from_low_u64_be(2)))))); } } 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 2509a76c4a..c253f4436f 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify 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 18c6358795..1e1f71918b 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,13 +17,14 @@ use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap}; use std::collections::btree_map::Entry; -use parking_lot::{Mutex, Condvar}; +use futures::Oneshot; +use parking_lot::Mutex; use ethereum_types::H256; -use ethkey::{Public, Signature}; +use crypto::publickey::{Public, Signature}; use key_server_cluster::{Error, NodeId, SessionId, KeyStorage}; use key_server_cluster::math; use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::ClusterSession; +use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; use key_server_cluster::message::{Message, ServersSetChangeMessage, ConsensusMessageWithServersSet, InitializeConsensusSessionWithServersSet, ServersSetChangeConsensusMessage, ConfirmConsensusInitialization, UnknownSessionsRequest, UnknownSessions, @@ -82,9 +83,9 @@ struct SessionCore { /// Servers set change session meta (id is computed from new_nodes_set). pub meta: ShareChangeSessionMeta, /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, + pub cluster: Arc, /// Keys storage. - pub key_storage: Arc, + pub key_storage: Arc, /// Session-level nonce. pub nonce: u64, /// All known nodes. @@ -93,8 +94,8 @@ struct SessionCore { pub admin_public: Public, /// Migration id (if this session is a part of auto-migration process). pub migration_id: Option, - /// SessionImpl completion condvar. - pub completed: Condvar, + /// Session completion signal. + pub completed: CompletionSignal<()>, } /// Servers set change consensus session type. @@ -135,9 +136,9 @@ pub struct SessionParams { /// Session meta (artificial). pub meta: ShareChangeSessionMeta, /// Cluster. - pub cluster: Arc, + pub cluster: Arc, /// Keys storage. - pub key_storage: Arc, + pub key_storage: Arc, /// Session nonce. pub nonce: u64, /// All known nodes. @@ -157,7 +158,7 @@ struct ServersSetChangeConsensusTransport { /// Migration id (if part of auto-migration process). migration_id: Option, /// Cluster. - cluster: Arc, + cluster: Arc, } /// Unknown sessions job transport. @@ -167,7 +168,7 @@ struct UnknownSessionsJobTransport { /// Session-level nonce. nonce: u64, /// Cluster. - cluster: Arc, + cluster: Arc, } /// Key version negotiation transport. @@ -177,13 +178,14 @@ struct ServersSetChangeKeyVersionNegotiationTransport { /// Session-level nonce. nonce: u64, /// Cluster. - cluster: Arc, + cluster: Arc, } impl SessionImpl { /// Create new servers set change session. - pub fn new(params: SessionParams) -> Result { - Ok(SessionImpl { + pub fn new(params: SessionParams) -> Result<(Self, Oneshot>), Error> { + let (completed, oneshot) = CompletionSignal::new(); + Ok((SessionImpl { core: SessionCore { meta: params.meta, cluster: params.cluster, @@ -192,7 +194,7 @@ impl SessionImpl { all_nodes_set: params.all_nodes_set, admin_public: params.admin_public, migration_id: params.migration_id, - completed: Condvar::new(), + completed, }, data: Mutex::new(SessionData { state: SessionState::EstablishingConsensus, @@ -205,7 +207,7 @@ impl SessionImpl { active_key_sessions: BTreeMap::new(), result: None, }), - }) + }, oneshot)) } /// Get session id. @@ -218,10 +220,9 @@ impl SessionImpl { self.core.migration_id.as_ref() } - /// 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") + /// Return session completion result (if available). + pub fn result(&self) -> Option> { + self.data.lock().result.clone() } /// Initialize servers set change session on master node. @@ -291,7 +292,7 @@ impl SessionImpl { self.on_session_error(sender, message.error.clone()); Ok(()) }, - &ServersSetChangeMessage::ServersSetChangeCompleted(ref message) => + &ServersSetChangeMessage::ServersSetChangeCompleted(ref message) => self.on_session_completed(sender, message), } } @@ -423,7 +424,7 @@ impl SessionImpl { &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)?; - let negotiation_session = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { + let (negotiation_session, _) = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { meta: ShareChangeSessionMeta { id: key_id.clone(), self_node_id: self.core.meta.self_node_id.clone(), @@ -671,7 +672,7 @@ impl SessionImpl { } data.state = SessionState::Finished; - self.core.completed.notify_all(); + self.core.completed.send(Ok(())); Ok(()) } @@ -741,7 +742,7 @@ impl SessionImpl { }; let key_share = core.key_storage.get(&key_id)?; - let negotiation_session = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { + let (negotiation_session, _) = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { meta: ShareChangeSessionMeta { id: key_id, self_node_id: core.meta.self_node_id.clone(), @@ -797,10 +798,11 @@ impl SessionImpl { 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()? + .result() + .expect("share change session is only initialized when negotiation is completed; qed")? .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()?; + let selected_version_threshold = negotiation_session.common_key_data()?.threshold; // prepare session change plan && check if something needs to be changed let old_nodes_set = selected_version_holders; @@ -882,7 +884,7 @@ impl SessionImpl { if data.result.is_some() && data.active_key_sessions.len() == 0 { data.state = SessionState::Finished; - core.completed.notify_all(); + core.completed.send(Ok(())); } Ok(()) @@ -891,7 +893,7 @@ impl SessionImpl { /// Complete servers set change session. fn complete_session(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { debug_assert_eq!(core.meta.self_node_id, core.meta.master_node_id); - + // send completion notification core.cluster.broadcast(Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeCompleted(ServersSetChangeCompleted { session: core.meta.id.clone().into(), @@ -907,7 +909,7 @@ impl SessionImpl { data.state = SessionState::Finished; data.result = Some(Ok(())); - core.completed.notify_all(); + core.completed.send(Ok(())); Ok(()) } @@ -915,6 +917,8 @@ impl SessionImpl { impl ClusterSession for SessionImpl { type Id = SessionId; + type CreationData = (); // never used directly + type SuccessfulResult = (); fn type_name() -> &'static str { "servers set change" @@ -954,8 +958,8 @@ impl ClusterSession for SessionImpl { self.core.meta.self_node_id, error, node); data.state = SessionState::Finished; - data.result = Some(Err(error)); - self.core.completed.notify_all(); + data.result = Some(Err(error.clone())); + self.core.completed.send(Err(error)); } fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { @@ -1046,8 +1050,9 @@ pub mod tests { use std::sync::Arc; use std::collections::{VecDeque, BTreeMap, BTreeSet}; use ethereum_types::H256; - use ethkey::{Random, Generator, Public, Signature, KeyPair, sign}; - use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, NodeKeyPair, PlainNodeKeyPair}; + use crypto::publickey::{Random, Generator, Public, Signature, KeyPair, sign}; + use blockchain::SigningKeyPair; + use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, PlainNodeKeyPair}; use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; use key_server_cluster::generation_session::tests::{MessageLoop as GenerationMessageLoop}; @@ -1109,7 +1114,7 @@ pub mod tests { nonce: 1, admin_public: admin_public, migration_id: None, - }).unwrap() + }).unwrap().0 } } 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 91d3bc7b89..448f5bdd82 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -28,7 +28,7 @@ pub struct SessionsQueue { impl SessionsQueue { /// Create new sessions queue. - pub fn new(key_storage: &Arc, unknown_sessions: BTreeSet) -> Self { + pub fn new(key_storage: &Arc, unknown_sessions: BTreeSet) -> Self { // TODO [Opt]: // 1) known sessions - change to iter // 2) unknown sesions - request chunk-by-chunk 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 e2af7bc7fa..1386b8d39f 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,15 +17,16 @@ use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap}; use ethereum_types::{H256, Address}; -use ethkey::{Public, Secret, Signature}; -use parking_lot::{Mutex, Condvar}; +use crypto::publickey::{Public, Secret, Signature}; +use futures::Oneshot; +use parking_lot::Mutex; use key_server_cluster::{Error, SessionId, NodeId, DocumentKeyShare, DocumentKeyShareVersion, KeyStorage}; use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::ClusterSession; +use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; use key_server_cluster::math; use key_server_cluster::message::{Message, ShareAddMessage, ShareAddConsensusMessage, ConsensusMessageOfShareAdd, InitializeConsensusSessionOfShareAdd, KeyShareCommon, NewKeysDissemination, ShareAddError, - ConfirmConsensusInitialization}; + ConfirmConsensusInitialization, CommonKeyData}; use key_server_cluster::jobs::job_session::JobTransport; use key_server_cluster::jobs::dummy_job::{DummyJob, DummyJobTransport}; use key_server_cluster::jobs::servers_set_change_access_job::{ServersSetChangeAccessJob, ServersSetChangeAccessRequest}; @@ -68,11 +69,11 @@ struct SessionCore { /// Session transport to communicate to other cluster nodes. pub transport: T, /// Key storage. - pub key_storage: Arc, + pub key_storage: Arc, /// Administrator public key. pub admin_public: Option, - /// SessionImpl completion condvar. - pub completed: Condvar, + /// Session completion signal. + pub completed: CompletionSignal<()>, } /// Share add consensus session type. @@ -130,7 +131,7 @@ pub struct SessionParams { /// Session transport. pub transport: T, /// Key storage. - pub key_storage: Arc, + pub key_storage: Arc, /// Administrator public key. pub admin_public: Option, /// Session nonce. @@ -153,15 +154,15 @@ pub struct IsolatedSessionTransport { /// Id numbers of all new nodes. id_numbers: Option>>, /// Cluster. - cluster: Arc, + cluster: Arc, } impl SessionImpl where T: SessionTransport { /// Create new share addition session. - pub fn new(params: SessionParams) -> Result { + pub fn new(params: SessionParams) -> Result<(Self, Oneshot>), Error> { let key_share = params.key_storage.get(¶ms.meta.id)?; - - Ok(SessionImpl { + let (completed, oneshot) = CompletionSignal::new(); + Ok((SessionImpl { core: SessionCore { meta: params.meta, nonce: params.nonce, @@ -169,7 +170,7 @@ impl SessionImpl where T: SessionTransport { transport: params.transport, key_storage: params.key_storage, admin_public: params.admin_public, - completed: Condvar::new(), + completed, }, data: Mutex::new(SessionData { state: SessionState::ConsensusEstablishing, @@ -181,7 +182,7 @@ impl SessionImpl where T: SessionTransport { secret_subshares: None, result: None, }), - }) + }, oneshot)) } /// Set pre-established consensus data. @@ -469,9 +470,9 @@ impl SessionImpl where T: SessionTransport { // update data data.state = SessionState::WaitingForKeysDissemination; data.new_key_share = Some(NewKeyShare { - threshold: message.threshold, - author: message.author.clone().into(), - joint_public: message.joint_public.clone().into(), + threshold: message.key_common.threshold, + author: message.key_common.author.clone().into(), + joint_public: message.key_common.public.clone().into(), common_point: message.common_point.clone().map(Into::into), encrypted_point: message.encrypted_point.clone().map(Into::into), }); @@ -645,9 +646,11 @@ impl SessionImpl where T: SessionTransport { core.transport.send(new_node, ShareAddMessage::KeyShareCommon(KeyShareCommon { session: core.meta.id.clone().into(), session_nonce: core.nonce, - threshold: old_key_share.threshold, - author: old_key_share.author.clone().into(), - joint_public: old_key_share.public.clone().into(), + key_common: CommonKeyData { + threshold: old_key_share.threshold, + author: old_key_share.author.into(), + public: old_key_share.public.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() @@ -750,7 +753,7 @@ impl SessionImpl where T: SessionTransport { // signal session completion data.state = SessionState::Finished; data.result = Some(Ok(())); - core.completed.notify_all(); + core.completed.send(Ok(())); Ok(()) } @@ -758,6 +761,8 @@ impl SessionImpl where T: SessionTransport { impl ClusterSession for SessionImpl where T: SessionTransport { type Id = SessionId; + type CreationData = (); // never used directly + type SuccessfulResult = (); fn type_name() -> &'static str { "share add" @@ -799,8 +804,8 @@ impl ClusterSession for SessionImpl where T: SessionTransport { self.core.meta.self_node_id, error, node); data.state = SessionState::Finished; - data.result = Some(Err(error)); - self.core.completed.notify_all(); + data.result = Some(Err(error.clone())); + self.core.completed.send(Err(error)); } fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { @@ -812,7 +817,7 @@ impl ClusterSession for SessionImpl where T: SessionTransport { } impl IsolatedSessionTransport { - pub fn new(session_id: SessionId, version: Option, nonce: u64, cluster: Arc) -> Self { + pub fn new(session_id: SessionId, version: Option, nonce: u64, cluster: Arc) -> Self { IsolatedSessionTransport { session: session_id, version: version, @@ -883,8 +888,9 @@ impl SessionTransport for IsolatedSessionTransport { #[cfg(test)] pub mod tests { use std::collections::BTreeSet; - use ethkey::{Random, Generator, Public}; - use key_server_cluster::{NodeId, Error, KeyStorage, NodeKeyPair}; + use crypto::publickey::{Random, Generator, Public}; + use blockchain::SigningKeyPair; + use key_server_cluster::{NodeId, Error, KeyStorage}; use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; use key_server_cluster::servers_set_change_session::tests::{MessageLoop, AdminSessionAdapter, generate_key}; use key_server_cluster::admin_sessions::ShareChangeSessionMeta; @@ -912,7 +918,7 @@ pub mod tests { key_storage, admin_public: Some(admin_public), nonce: 1, - }).unwrap() + }).unwrap().0 } } 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 bd2bed2d61..e3d58d9658 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap}; use ethereum_types::H256; -use ethkey::Secret; +use crypto::publickey::Secret; use key_server_cluster::{Error, NodeId, SessionId, ServerKeyId, KeyStorage}; use key_server_cluster::cluster::Cluster; use key_server_cluster::cluster_sessions::ClusterSession; @@ -43,9 +43,9 @@ pub struct ShareChangeSession { /// Share change session meta. meta: ShareChangeSessionMeta, /// Cluster. - cluster: Arc, + cluster: Arc, /// Key storage. - key_storage: Arc, + key_storage: Arc, /// Key version. key_version: H256, /// Nodes that have reported version ownership. @@ -82,9 +82,9 @@ pub struct ShareChangeSessionParams { /// Share change session meta. pub meta: ShareChangeSessionMeta, /// Cluster. - pub cluster: Arc, + pub cluster: Arc, /// Keys storage. - pub key_storage: Arc, + pub key_storage: Arc, /// Session plan. pub plan: ShareChangeSessionPlan, } @@ -97,7 +97,7 @@ pub struct ShareChangeTransport { /// Session nonce. nonce: u64, /// Cluster. - cluster: Arc, + cluster: Arc, } impl ShareChangeSession { @@ -166,7 +166,7 @@ impl ShareChangeSession { 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 { + let (share_add_session, _) = ShareAddSessionImpl::new(ShareAddSessionParams { meta: self.meta.clone(), nonce: self.nonce, transport: ShareChangeTransport::new(self.session_id, self.nonce, self.cluster.clone()), @@ -201,7 +201,7 @@ impl ShareChangeSession { } impl ShareChangeTransport { - pub fn new(session_id: SessionId, nonce: u64, cluster: Arc) -> Self { + pub fn new(session_id: SessionId, nonce: u64, cluster: Arc) -> Self { ShareChangeTransport { session_id: session_id, nonce: nonce, 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 ccb34e9828..dba470e7ce 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,14 +16,14 @@ use std::collections::{BTreeSet, BTreeMap}; use std::sync::Arc; -use std::time; -use parking_lot::{Mutex, Condvar}; +use futures::Oneshot; +use parking_lot::Mutex; use ethereum_types::{Address, H256}; -use ethkey::Secret; +use crypto::publickey::Secret; use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, Requester, EncryptedDocumentKeyShadow, SessionMeta}; use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession}; +use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; use key_server_cluster::message::{Message, DecryptionMessage, DecryptionConsensusMessage, RequestPartialDecryption, PartialDecryption, DecryptionSessionError, DecryptionSessionCompleted, ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization, DecryptionSessionDelegation, DecryptionSessionDelegationCompleted}; @@ -56,11 +56,11 @@ struct SessionCore { /// Key share. pub key_share: Option, /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, + pub cluster: Arc, /// Session-level nonce. pub nonce: u64, - /// SessionImpl completion condvar. - pub completed: Condvar, + /// Session completion signal. + pub completed: CompletionSignal, } /// Decryption consensus session type. @@ -98,9 +98,9 @@ pub struct SessionParams { /// Key share. pub key_share: Option, /// ACL storage. - pub acl_storage: Arc, + pub acl_storage: Arc, /// Cluster. - pub cluster: Arc, + pub cluster: Arc, /// Session nonce. pub nonce: u64, } @@ -118,7 +118,7 @@ struct DecryptionConsensusTransport { /// Selected key version (on master node). version: Option, /// Cluster. - cluster: Arc, + cluster: Arc, } /// Decryption job transport @@ -134,7 +134,7 @@ struct DecryptionJobTransport { /// Master node id. master_node_id: NodeId, /// Cluster. - cluster: Arc, + cluster: Arc, } /// Session delegation status. @@ -147,7 +147,10 @@ enum DelegationStatus { impl SessionImpl { /// Create new decryption session. - pub fn new(params: SessionParams, requester: Option) -> Result { + pub fn new( + params: SessionParams, + requester: Option, + ) -> Result<(Self, Oneshot>), Error> { debug_assert_eq!(params.meta.threshold, params.key_share.as_ref().map(|ks| ks.threshold).unwrap_or_default()); // check that common_point and encrypted_point are already set @@ -175,14 +178,15 @@ impl SessionImpl { consensus_transport: consensus_transport, })?; - Ok(SessionImpl { + let (completed, oneshot) = CompletionSignal::new(); + Ok((SessionImpl { core: SessionCore { meta: params.meta, access_key: params.access_key, key_share: params.key_share, cluster: params.cluster, nonce: params.nonce, - completed: Condvar::new(), + completed, }, data: Mutex::new(SessionData { version: None, @@ -194,7 +198,7 @@ impl SessionImpl { delegation_status: None, result: None, }), - }) + }, oneshot)) } /// Get this node id. @@ -209,7 +213,7 @@ impl SessionImpl { &self.core.access_key } - /// Get session state. + /// Get session state (tests only). #[cfg(test)] pub fn state(&self) -> ConsensusSessionState { self.data.lock().consensus_session.state() @@ -231,9 +235,9 @@ impl SessionImpl { self.data.lock().origin.clone() } - /// Wait for session completion. - pub fn wait(&self, timeout: Option) -> Option> { - Self::wait_session(&self.core.completed, &self.data, timeout, |data| data.result.clone()) + /// Get session completion result (if available). + pub fn result(&self) -> Option> { + self.data.lock().result.clone() } /// Get broadcasted shadows. @@ -667,13 +671,15 @@ impl SessionImpl { }; } - data.result = Some(result); - core.completed.notify_all(); + data.result = Some(result.clone()); + core.completed.send(result); } } impl ClusterSession for SessionImpl { type Id = SessionIdWithSubSession; + type CreationData = Requester; + type SuccessfulResult = EncryptedDocumentKeyShadow; fn type_name() -> &'static str { "decryption" @@ -816,6 +822,7 @@ impl JobTransport for DecryptionJobTransport { pub fn create_default_decryption_session() -> Arc { use acl_storage::DummyAclStorage; use key_server_cluster::cluster::tests::DummyCluster; + use ethereum_types::H512; Arc::new(SessionImpl::new(SessionParams { meta: SessionMeta { @@ -831,7 +838,7 @@ pub fn create_default_decryption_session() -> Arc { acl_storage: Arc::new(DummyAclStorage::default()), cluster: Arc::new(DummyCluster::new(Default::default())), nonce: 0, - }, Some(Requester::Public(2.into()))).unwrap()) + }, Some(Requester::Public(H512::from_low_u64_be(2)))).unwrap().0) } #[cfg(test)] @@ -839,7 +846,7 @@ mod tests { use std::sync::Arc; use std::collections::{BTreeMap, VecDeque}; use acl_storage::DummyAclStorage; - use ethkey::{self, KeyPair, Random, Generator, Public, Secret, public_to_address}; + use crypto::publickey::{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; @@ -848,6 +855,8 @@ mod tests { use key_server_cluster::message::{self, Message, DecryptionMessage}; use key_server_cluster::math; use key_server_cluster::jobs::consensus_session::ConsensusSessionState; + use ethereum_types::{H512, Address}; + use std::str::FromStr; const SECRET_PLAIN: &'static str = "d2b57ae7619e070af0af6bc8c703c0cd27814c54d5d6a999cacac0da34ede279ca0d9216e85991029e54e2f0c92ee0bd30237725fa765cbdbfc4529489864c5f"; @@ -863,19 +872,19 @@ mod tests { "c06546b5669877ba579ca437a5602e89425c53808c708d44ccd6afcaa4610fad".parse().unwrap(), ]; let id_numbers: Vec<(NodeId, Secret)> = vec![ - ("b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".into(), + (H512::from_str("b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8").unwrap(), "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap()), - ("1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb".into(), + (H512::from_str("1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb").unwrap(), "00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse().unwrap()), - ("99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc".into(), + (H512::from_str("99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc").unwrap(), "f43ac0fba42a5b6ed95707d2244659e89ba877b1c9b82c0d0a9dcf834e80fc62".parse().unwrap()), - ("7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c".into(), + (H512::from_str("7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c").unwrap(), "5a324938dfb2516800487d25ab7289ba8ec38811f77c3df602e4e65e3c9acd9f".parse().unwrap()), - ("321977760d1d8e15b047a309e4c7fe6f355c10bb5a06c68472b676926427f69f229024fa2692c10da167d14cdc77eb95d0fce68af0a0f704f0d3db36baa83bb2".into(), + (H512::from_str("321977760d1d8e15b047a309e4c7fe6f355c10bb5a06c68472b676926427f69f229024fa2692c10da167d14cdc77eb95d0fce68af0a0f704f0d3db36baa83bb2").unwrap(), "12cf422d50002d04e52bd4906fd7f5f235f051ca36abfe37e061f8da248008d8".parse().unwrap()), ]; - let common_point: Public = "6962be696e1bcbba8e64cc7fddf140f854835354b5804f3bb95ae5a2799130371b589a131bd39699ac7174ccb35fc4342dab05331202209582fc8f3a40916ab0".into(); - let encrypted_point: Public = "b07031982bde9890e12eff154765f03c56c3ab646ad47431db5dd2d742a9297679c4c65b998557f8008469afd0c43d40b6c5f6c6a1c7354875da4115237ed87a".into(); + let common_point: Public = H512::from_str("6962be696e1bcbba8e64cc7fddf140f854835354b5804f3bb95ae5a2799130371b589a131bd39699ac7174ccb35fc4342dab05331202209582fc8f3a40916ab0").unwrap(); + let encrypted_point: Public = H512::from_str("b07031982bde9890e12eff154765f03c56c3ab646ad47431db5dd2d742a9297679c4c65b998557f8008469afd0c43d40b6c5f6c6a1c7354875da4115237ed87a").unwrap(); let encrypted_datas: Vec<_> = (0..5).map(|i| DocumentKeyShare { author: Default::default(), threshold: 3, @@ -897,7 +906,7 @@ mod tests { cluster }).collect(); let requester = Random.generate().unwrap(); - let signature = Some(ethkey::sign(requester.secret(), &SessionId::default()).unwrap()); + let signature = Some(crypto::publickey::sign(requester.secret(), &SessionId::default()).unwrap()); let sessions: Vec<_> = (0..5).map(|i| SessionImpl::new(SessionParams { meta: SessionMeta { id: session_id.clone(), @@ -912,7 +921,7 @@ mod tests { acl_storage: acl_storages[i].clone(), cluster: clusters[i].clone(), nonce: 0, - }, if i == 0 { signature.clone().map(Into::into) } else { None }).unwrap()).collect(); + }, if i == 0 { signature.clone().map(Into::into) } else { None }).unwrap().0).collect(); (requester, clusters, acl_storages, sessions) } @@ -988,7 +997,7 @@ mod tests { acl_storage: Arc::new(DummyAclStorage::default()), cluster: Arc::new(DummyCluster::new(self_node_id.clone())), nonce: 0, - }, Some(Requester::Signature(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()))) { + }, Some(Requester::Signature(crypto::publickey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()))) { Ok(_) => (), _ => panic!("unexpected"), } @@ -1011,7 +1020,9 @@ mod tests { acl_storage: Arc::new(DummyAclStorage::default()), cluster: Arc::new(DummyCluster::new(self_node_id.clone())), nonce: 0, - }, Some(Requester::Signature(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()))).unwrap(); + }, Some(Requester::Signature( + crypto::publickey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap() + ))).unwrap().0; assert_eq!(session.initialize(Default::default(), Default::default(), false, false), Err(Error::InvalidMessage)); } @@ -1046,7 +1057,9 @@ mod tests { acl_storage: Arc::new(DummyAclStorage::default()), cluster: Arc::new(DummyCluster::new(self_node_id.clone())), nonce: 0, - }, Some(Requester::Signature(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()))).unwrap(); + }, Some(Requester::Signature( + crypto::publickey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap() + ))).unwrap().0; assert_eq!(session.initialize(Default::default(), Default::default(), false, false), Err(Error::ConsensusUnreachable)); } @@ -1067,7 +1080,7 @@ mod tests { session_nonce: 0, origin: None, message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { - requester: Requester::Signature(ethkey::sign( + requester: Requester::Signature(crypto::publickey::sign( Random.generate().unwrap().secret(), &SessionId::default()).unwrap()).into(), version: Default::default(), }), @@ -1083,7 +1096,7 @@ mod tests { session_nonce: 0, origin: None, message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { - requester: Requester::Signature(ethkey::sign(Random.generate().unwrap().secret(), + requester: Requester::Signature(crypto::publickey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()).into(), version: Default::default(), }), @@ -1108,7 +1121,7 @@ mod tests { session_nonce: 0, origin: None, message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { - requester: Requester::Signature(ethkey::sign(Random.generate().unwrap().secret(), + requester: Requester::Signature(crypto::publickey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()).into(), version: Default::default(), }), @@ -1272,7 +1285,7 @@ mod tests { assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: SECRET_PLAIN.into(), + decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), common_point: None, decrypt_shadows: None, }); @@ -1295,18 +1308,18 @@ mod tests { let decrypted_secret = sessions[0].decrypted_secret().unwrap().unwrap(); // check that decrypted_secret != SECRET_PLAIN - assert!(decrypted_secret.decrypted_secret != SECRET_PLAIN.into()); + assert!(decrypted_secret.decrypted_secret != H512::from_str(SECRET_PLAIN).unwrap()); // check that common point && shadow coefficients are returned 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 crypto::DEFAULT_MAC; - use ethkey::crypto::ecies::decrypt; + use crypto::publickey::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()).unwrap()) + .map(|c| Secret::copy_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()); + assert_eq!(decrypted_secret, H512::from_str(SECRET_PLAIN).unwrap()); } #[test] @@ -1347,7 +1360,7 @@ mod tests { // 2) 1 session has decrypted key value assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: SECRET_PLAIN.into(), + decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), common_point: None, decrypt_shadows: None, }); @@ -1385,7 +1398,7 @@ mod tests { assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 4); // 2) 1 session has decrypted key value assert_eq!(sessions[1].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: SECRET_PLAIN.into(), + decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), common_point: None, decrypt_shadows: None, }); @@ -1407,7 +1420,7 @@ mod tests { do_messages_exchange(&clusters, &sessions).unwrap(); assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: SECRET_PLAIN.into(), + decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), common_point: None, decrypt_shadows: None, }); @@ -1423,7 +1436,7 @@ mod tests { let result = sessions[0].decrypted_secret(); assert!(result.clone().unwrap().is_ok()); assert_eq!(result.clone().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: SECRET_PLAIN.into(), + decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), common_point: None, decrypt_shadows: None, }); @@ -1445,23 +1458,23 @@ mod tests { // 4 nodes must be able to recover original secret use crypto::DEFAULT_MAC; - use ethkey::crypto::ecies::decrypt; + use crypto::publickey::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()) + .map(|c| Secret::copy_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()); + assert_eq!(decrypted_secret, H512::from_str(SECRET_PLAIN).unwrap()); } #[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(); + sessions[0].initialize(Some(Address::from_low_u64_be(1)), 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()); + assert_eq!(5, sessions.iter().filter(|s| s.origin() == Some(Address::from_low_u64_be(1))).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 a3eabc35c6..05713b1dfe 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,15 +16,15 @@ use std::collections::BTreeMap; use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::time; use std::sync::Arc; -use parking_lot::{Condvar, Mutex}; +use futures::Oneshot; +use parking_lot::Mutex; use ethereum_types::Address; -use ethkey::Public; +use crypto::publickey::Public; 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::cluster_sessions::{ClusterSession, CompletionSignal}; use key_server_cluster::message::{Message, EncryptionMessage, InitializeEncryptionSession, ConfirmEncryptionInitialization, EncryptionSessionError}; @@ -44,13 +44,13 @@ pub struct SessionImpl { /// Encrypted data. encrypted_data: Option, /// Key storage. - key_storage: Arc, + key_storage: Arc, /// Cluster which allows this node to send messages to other nodes in the cluster. - cluster: Arc, + cluster: Arc, /// Session nonce. nonce: u64, - /// SessionImpl completion condvar. - completed: Condvar, + /// Session completion signal. + completed: CompletionSignal<()>, /// Mutable session data. data: Mutex, } @@ -64,9 +64,9 @@ pub struct SessionParams { /// Encrypted data (result of running generation_session::SessionImpl). pub encrypted_data: Option, /// Key storage. - pub key_storage: Arc, + pub key_storage: Arc, /// Cluster - pub cluster: Arc, + pub cluster: Arc, /// Session nonce. pub nonce: u64, } @@ -108,23 +108,24 @@ pub enum SessionState { impl SessionImpl { /// Create new encryption session. - pub fn new(params: SessionParams) -> Result { + pub fn new(params: SessionParams) -> Result<(Self, Oneshot>), Error> { check_encrypted_data(params.encrypted_data.as_ref())?; - Ok(SessionImpl { + let (completed, oneshot) = CompletionSignal::new(); + Ok((SessionImpl { id: params.id, self_node_id: params.self_node_id, encrypted_data: params.encrypted_data, key_storage: params.key_storage, cluster: params.cluster, nonce: params.nonce, - completed: Condvar::new(), + completed, data: Mutex::new(SessionData { state: SessionState::WaitingForInitialization, nodes: BTreeMap::new(), result: None, }), - }) + }, oneshot)) } /// Get this node Id. @@ -132,12 +133,6 @@ impl SessionImpl { &self.self_node_id } - /// 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(); @@ -175,7 +170,7 @@ impl SessionImpl { } else { data.state = SessionState::Finished; data.result = Some(Ok(())); - self.completed.notify_all(); + self.completed.send(Ok(())); Ok(()) } @@ -230,7 +225,7 @@ impl SessionImpl { // update state data.state = SessionState::Finished; data.result = Some(Ok(())); - self.completed.notify_all(); + self.completed.send(Ok(())); Ok(()) } @@ -238,6 +233,8 @@ impl SessionImpl { impl ClusterSession for SessionImpl { type Id = SessionId; + type CreationData = (); + type SuccessfulResult = (); fn type_name() -> &'static str { "encryption" @@ -260,7 +257,7 @@ impl ClusterSession for SessionImpl { data.state = SessionState::Failed; data.result = Some(Err(Error::NodeDisconnected)); - self.completed.notify_all(); + self.completed.send(Err(Error::NodeDisconnected)); } fn on_session_timeout(&self) { @@ -270,7 +267,7 @@ impl ClusterSession for SessionImpl { data.state = SessionState::Failed; data.result = Some(Err(Error::NodeDisconnected)); - self.completed.notify_all(); + self.completed.send(Err(Error::NodeDisconnected)); } fn on_session_error(&self, node: &NodeId, error: Error) { @@ -290,8 +287,8 @@ impl ClusterSession for SessionImpl { warn!("{}: encryption session failed with error: {} from {}", self.node(), error, node); data.state = SessionState::Failed; - data.result = Some(Err(error)); - self.completed.notify_all(); + data.result = Some(Err(error.clone())); + self.completed.send(Err(error)); } fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { @@ -334,7 +331,7 @@ pub fn check_encrypted_data(key_share: Option<&DocumentKeyShare>) -> Result<(), } /// 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> { +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); 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 0fa805f571..6d65db70a5 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,15 +16,15 @@ use std::collections::{BTreeSet, BTreeMap, VecDeque}; use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::time::Duration; use std::sync::Arc; -use parking_lot::{Condvar, Mutex}; +use futures::Oneshot; +use parking_lot::Mutex; use ethereum_types::Address; -use ethkey::{Public, Secret}; +use crypto::publickey::{Public, Secret}; use key_server_cluster::{Error, NodeId, SessionId, KeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; use key_server_cluster::math; use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::ClusterSession; +use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; use key_server_cluster::message::{Message, GenerationMessage, InitializeSession, ConfirmInitialization, CompleteInitialization, KeysDissemination, PublicKeyShare, SessionError, SessionCompleted}; @@ -42,15 +42,15 @@ pub struct SessionImpl { /// Public identifier of this node. self_node_id: NodeId, /// Key storage. - key_storage: Option>, + key_storage: Option>, /// Cluster which allows this node to send messages to other nodes in the cluster. - cluster: Arc, + cluster: Arc, /// Session-level nonce. nonce: u64, - /// SessionImpl completion condvar. - completed: Condvar, /// Mutable session data. data: Mutex, + /// Session completion signal. + completed: CompletionSignal, } /// SessionImpl creation parameters @@ -60,9 +60,9 @@ pub struct SessionParams { /// Id of node, on which this session is running. pub self_node_id: Public, /// Key storage. - pub key_storage: Option>, + pub key_storage: Option>, /// Cluster - pub cluster: Arc, + pub cluster: Arc, /// Session nonce. pub nonce: Option, } @@ -204,8 +204,9 @@ impl From> for InitializationNodes { impl SessionImpl { /// Create new generation session. - pub fn new(params: SessionParams) -> Self { - SessionImpl { + pub fn new(params: SessionParams) -> (Self, Oneshot>) { + let (completed, oneshot) = CompletionSignal::new(); + (SessionImpl { id: params.id, self_node_id: params.self_node_id, key_storage: params.key_storage, @@ -213,7 +214,7 @@ impl SessionImpl { // when nonce.is_nonce(), generation session is wrapped // => nonce is checked somewhere else && we can pass any value nonce: params.nonce.unwrap_or_default(), - completed: Condvar::new(), + completed, data: Mutex::new(SessionData { state: SessionState::WaitingForInitialization, simulate_faulty_behaviour: false, @@ -230,7 +231,7 @@ impl SessionImpl { key_share: None, joint_public_and_secret: None, }), - } + }, oneshot) } /// Get this node Id. @@ -259,10 +260,10 @@ impl SessionImpl { self.data.lock().origin.clone() } - /// Wait for session completion. - 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()))) + /// Get session completion result (if available). + pub fn result(&self) -> Option> { + self.data.lock().joint_public_and_secret.clone() + .map(|r| r.map(|r| r.0.clone())) } /// Get generated public and secret (if any). @@ -328,8 +329,12 @@ impl SessionImpl { self.verify_keys()?; self.complete_generation()?; - self.data.lock().state = SessionState::Finished; - self.completed.notify_all(); + let mut data = self.data.lock(); + let result = data.joint_public_and_secret.clone() + .expect("session is instantly completed on a single node; qed") + .map(|(p, _, _)| p); + data.state = SessionState::Finished; + self.completed.send(result); Ok(()) } @@ -619,8 +624,11 @@ impl SessionImpl { } // we have received enough confirmations => complete session + let result = data.joint_public_and_secret.clone() + .expect("we're on master node; we have received last completion confirmation; qed") + .map(|(p, _, _)| p); data.state = SessionState::Finished; - self.completed.notify_all(); + self.completed.send(result); Ok(()) } @@ -813,6 +821,8 @@ impl SessionImpl { impl ClusterSession for SessionImpl { type Id = SessionId; + type CreationData = (); + type SuccessfulResult = Public; fn type_name() -> &'static str { "generation" @@ -838,7 +848,7 @@ impl ClusterSession for SessionImpl { data.state = SessionState::Failed; data.key_share = Some(Err(Error::NodeDisconnected)); data.joint_public_and_secret = Some(Err(Error::NodeDisconnected)); - self.completed.notify_all(); + self.completed.send(Err(Error::NodeDisconnected)); } fn on_session_timeout(&self) { @@ -849,7 +859,7 @@ impl ClusterSession for SessionImpl { data.state = SessionState::Failed; data.key_share = Some(Err(Error::NodeDisconnected)); data.joint_public_and_secret = Some(Err(Error::NodeDisconnected)); - self.completed.notify_all(); + self.completed.send(Err(Error::NodeDisconnected)); } fn on_session_error(&self, node: &NodeId, error: Error) { @@ -867,8 +877,8 @@ impl ClusterSession for SessionImpl { let mut data = self.data.lock(); data.state = SessionState::Failed; data.key_share = Some(Err(error.clone())); - data.joint_public_and_secret = Some(Err(error)); - self.completed.notify_all(); + data.joint_public_and_secret = Some(Err(error.clone())); + self.completed.send(Err(error)); } fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { @@ -941,7 +951,7 @@ fn check_threshold(threshold: usize, nodes: &BTreeSet) -> Result<(), Err pub mod tests { use std::sync::Arc; use ethereum_types::H256; - use ethkey::{Random, Generator, KeyPair, Secret}; + use crypto::publickey::{Random, Generator, KeyPair, Secret}; use key_server_cluster::{NodeId, Error, KeyStorage}; use key_server_cluster::message::{self, Message, GenerationMessage, KeysDissemination, PublicKeyShare, ConfirmInitialization}; 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 7815a74fbf..536a09a7ce 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify 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 fe3bd4f114..3532af7f0a 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,12 +17,13 @@ use std::collections::{BTreeSet, BTreeMap}; use std::collections::btree_map::Entry; use std::sync::Arc; -use parking_lot::{Mutex, Condvar}; -use ethkey::{Public, Secret, Signature, sign}; +use futures::Oneshot; +use parking_lot::Mutex; +use crypto::publickey::{Public, Secret, Signature, sign}; use ethereum_types::H256; use key_server_cluster::{Error, NodeId, SessionId, SessionMeta, AclStorage, DocumentKeyShare, Requester}; use key_server_cluster::cluster::{Cluster}; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession}; +use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, SessionState as GenerationSessionState}; use key_server_cluster::math; @@ -55,11 +56,11 @@ struct SessionCore { /// Key share. pub key_share: Option, /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, + pub cluster: Arc, /// Session-level nonce. pub nonce: u64, - /// SessionImpl completion condvar. - pub completed: Condvar, + /// Session completion signal. + pub completed: CompletionSignal, } /// Signing consensus session type. @@ -111,9 +112,9 @@ pub struct SessionParams { /// Key share. pub key_share: Option, /// ACL storage. - pub acl_storage: Arc, + pub acl_storage: Arc, /// Cluster - pub cluster: Arc, + pub cluster: Arc, /// Session nonce. pub nonce: u64, } @@ -129,7 +130,7 @@ struct SigningConsensusTransport { /// Selected key version (on master node). version: Option, /// Cluster. - cluster: Arc, + cluster: Arc, } /// Signing key generation transport. @@ -141,7 +142,7 @@ struct NonceGenerationTransport, + cluster: Arc, /// Other nodes ids. other_nodes_ids: BTreeSet, /// Message mapping function. @@ -157,7 +158,7 @@ struct SigningJobTransport { /// Session-level nonce. nonce: u64, /// Cluster. - cluster: Arc, + cluster: Arc, } /// Session delegation status. @@ -170,7 +171,10 @@ enum DelegationStatus { impl SessionImpl { /// Create new signing session. - pub fn new(params: SessionParams, requester: Option) -> Result { + pub fn new( + params: SessionParams, + requester: Option, + ) -> Result<(Self, Oneshot>), Error> { debug_assert_eq!(params.meta.threshold, params.key_share.as_ref().map(|ks| ks.threshold).unwrap_or_default()); let consensus_transport = SigningConsensusTransport { @@ -197,14 +201,15 @@ impl SessionImpl { consensus_transport: consensus_transport, })?; - Ok(SessionImpl { + let (completed, oneshot) = CompletionSignal::new(); + Ok((SessionImpl { core: SessionCore { meta: params.meta, access_key: params.access_key, key_share: params.key_share, cluster: params.cluster, nonce: params.nonce, - completed: Condvar::new(), + completed, }, data: Mutex::new(SessionData { state: SessionState::ConsensusEstablishing, @@ -218,10 +223,11 @@ impl SessionImpl { delegation_status: None, result: None, }), - }) + }, oneshot)) } /// Wait for session completion. + #[cfg(test)] 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") @@ -251,7 +257,6 @@ impl SessionImpl { })))?; data.delegation_status = Some(DelegationStatus::DelegatedTo(master)); Ok(()) - } /// Initialize signing session on master node. @@ -284,8 +289,9 @@ impl SessionImpl { // consensus established => threshold is 0 => we can generate signature on this node if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { - data.result = Some(sign(&key_version.secret_share, &message_hash).map_err(Into::into)); - self.core.completed.notify_all(); + let result = sign(&key_version.secret_share, &message_hash).map_err(Into::into); + data.result = Some(result.clone()); + self.core.completed.send(result); } Ok(()) @@ -797,7 +803,7 @@ impl SessionImpl { map: map_message, }), nonce: None, - }) + }).0 } /// Set signing session result. @@ -820,8 +826,8 @@ impl SessionImpl { }; } - data.result = Some(result); - core.completed.notify_all(); + data.result = Some(result.clone()); + core.completed.send(result); } /// Check if all nonces are generated. @@ -883,6 +889,8 @@ impl SessionImpl { impl ClusterSession for SessionImpl { type Id = SessionIdWithSubSession; + type CreationData = Requester; + type SuccessfulResult = Signature; fn type_name() -> &'static str { "ecdsa_signing" @@ -1062,7 +1070,7 @@ impl JobTransport for SigningJobTransport { mod tests { use std::sync::Arc; use ethereum_types::H256; - use ethkey::{self, Random, Generator, Public, verify_public, public_to_address}; + use crypto::publickey::{Random, Generator, Public, verify_public, public_to_address}; use key_server_cluster::{SessionId, Error, KeyStorage}; use key_server_cluster::cluster::tests::{MessageLoop as ClusterMessageLoop}; use key_server_cluster::signing_session_ecdsa::SessionImpl; @@ -1082,7 +1090,7 @@ mod tests { pub fn init_with_version(self, key_version: Option) -> Result<(Self, Public, H256), Error> { let message_hash = H256::random(); let requester = Random.generate().unwrap(); - let signature = ethkey::sign(requester.secret(), &SessionId::default()).unwrap(); + let signature = crypto::publickey::sign(requester.secret(), &SessionId::default()).unwrap(); self.0.cluster(0).client() .new_ecdsa_signing_session(Default::default(), signature.into(), key_version, message_hash) .map(|_| (self, *requester.public(), message_hash)) 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 0b0619f967..26ce3f3af9 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,12 +16,13 @@ use std::collections::BTreeSet; use std::sync::Arc; -use parking_lot::{Mutex, Condvar}; -use ethkey::{Public, Secret}; +use futures::Oneshot; +use parking_lot::Mutex; +use crypto::publickey::{Public, Secret}; use ethereum_types::H256; use key_server_cluster::{Error, NodeId, SessionId, Requester, SessionMeta, AclStorage, DocumentKeyShare}; use key_server_cluster::cluster::{Cluster}; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession}; +use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, SessionState as GenerationSessionState}; use key_server_cluster::message::{Message, SchnorrSigningMessage, SchnorrSigningConsensusMessage, SchnorrSigningGenerationMessage, @@ -56,11 +57,11 @@ struct SessionCore { /// Key share. pub key_share: Option, /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, + pub cluster: Arc, /// Session-level nonce. pub nonce: u64, - /// SessionImpl completion condvar. - pub completed: Condvar, + /// SessionImpl completion signal. + pub completed: CompletionSignal<(Secret, Secret)>, } /// Signing consensus session type. @@ -105,9 +106,9 @@ pub struct SessionParams { /// Key share. pub key_share: Option, /// ACL storage. - pub acl_storage: Arc, + pub acl_storage: Arc, /// Cluster - pub cluster: Arc, + pub cluster: Arc, /// Session nonce. pub nonce: u64, } @@ -123,7 +124,7 @@ struct SigningConsensusTransport { /// Selected key version (on master node). version: Option, /// Cluster. - cluster: Arc, + cluster: Arc, } /// Signing key generation transport. @@ -131,7 +132,7 @@ struct SessionKeyGenerationTransport { /// Session access key. access_key: Secret, /// Cluster. - cluster: Arc, + cluster: Arc, /// Session-level nonce. nonce: u64, /// Other nodes ids. @@ -147,7 +148,7 @@ struct SigningJobTransport { /// Session-level nonce. nonce: u64, /// Cluster. - cluster: Arc, + cluster: Arc, } /// Session delegation status. @@ -160,7 +161,10 @@ enum DelegationStatus { impl SessionImpl { /// Create new signing session. - pub fn new(params: SessionParams, requester: Option) -> Result { + pub fn new( + params: SessionParams, + requester: Option, + ) -> Result<(Self, Oneshot>), Error> { debug_assert_eq!(params.meta.threshold, params.key_share.as_ref().map(|ks| ks.threshold).unwrap_or_default()); let consensus_transport = SigningConsensusTransport { @@ -179,14 +183,15 @@ impl SessionImpl { consensus_transport: consensus_transport, })?; - Ok(SessionImpl { + let (completed, oneshot) = CompletionSignal::new(); + Ok((SessionImpl { core: SessionCore { meta: params.meta, access_key: params.access_key, key_share: params.key_share, cluster: params.cluster, nonce: params.nonce, - completed: Condvar::new(), + completed, }, data: Mutex::new(SessionData { state: SessionState::ConsensusEstablishing, @@ -197,21 +202,22 @@ impl SessionImpl { delegation_status: None, result: None, }), - }) - } - - /// Get session state. - #[cfg(test)] - pub fn state(&self) -> SessionState { - self.data.lock().state + }, oneshot)) } /// Wait for session completion. + #[cfg(test)] 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") } + /// Get session state (tests only). + #[cfg(test)] + pub fn state(&self) -> SessionState { + self.data.lock().state + } + /// Delegate session to other node. pub fn delegate(&self, master: NodeId, version: H256, message_hash: H256) -> Result<(), Error> { if self.core.meta.master_node_id != self.core.meta.self_node_id { @@ -277,7 +283,7 @@ impl SessionImpl { other_nodes_ids: BTreeSet::new() }), nonce: None, - }); + }).0; 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::Finished); @@ -405,7 +411,7 @@ impl SessionImpl { other_nodes_ids: other_consensus_group_nodes, }), nonce: None, - }); + }).0; generation_session.initialize(Default::default(), Default::default(), false, key_share.threshold, consensus_group.into())?; data.generation_session = Some(generation_session); @@ -445,7 +451,7 @@ impl SessionImpl { other_nodes_ids: other_consensus_group_nodes }), nonce: None, - }); + }).0; data.generation_session = Some(generation_session); data.state = SessionState::SessionKeyGeneration; } @@ -617,13 +623,15 @@ impl SessionImpl { }; } - data.result = Some(result); - core.completed.notify_all(); + data.result = Some(result.clone()); + core.completed.send(result); } } impl ClusterSession for SessionImpl { type Id = SessionIdWithSubSession; + type CreationData = Requester; + type SuccessfulResult = (Secret, Secret); fn type_name() -> &'static str { "signing" @@ -811,7 +819,7 @@ mod tests { use std::str::FromStr; use std::collections::BTreeMap; use ethereum_types::{Address, H256}; - use ethkey::{self, Random, Generator, Public, Secret, public_to_address}; + use crypto::publickey::{Random, Generator, Public, Secret, public_to_address}; use acl_storage::DummyAclStorage; use key_server_cluster::{SessionId, Requester, SessionMeta, Error, KeyStorage}; use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; @@ -834,7 +842,7 @@ mod tests { } pub fn into_session(&self, at_node: usize) -> SessionImpl { - let requester = Some(Requester::Signature(ethkey::sign(Random.generate().unwrap().secret(), + let requester = Some(Requester::Signature(crypto::publickey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())); SessionImpl::new(SessionParams { meta: SessionMeta { @@ -850,13 +858,13 @@ mod tests { acl_storage: Arc::new(DummyAclStorage::default()), cluster: self.0.cluster(0).view().unwrap(), nonce: 0, - }, requester).unwrap() + }, requester).unwrap().0 } pub fn init_with_version(self, key_version: Option) -> Result<(Self, Public, H256), Error> { let message_hash = H256::random(); let requester = Random.generate().unwrap(); - let signature = ethkey::sign(requester.secret(), &SessionId::default()).unwrap(); + let signature = crypto::publickey::sign(requester.secret(), &SessionId::default()).unwrap(); self.0.cluster(0).client().new_schnorr_signing_session( Default::default(), signature.into(), @@ -933,7 +941,7 @@ mod tests { #[test] fn schnorr_fails_to_initialize_when_already_initialized() { let (ml, _, _) = MessageLoop::new(1, 0).unwrap().init().unwrap(); - assert_eq!(ml.session_at(0).initialize(ml.key_version(), 777.into()), + assert_eq!(ml.session_at(0).initialize(ml.key_version(), H256::from_low_u64_be(777)), Err(Error::InvalidStateForRequest)); } @@ -1005,7 +1013,7 @@ mod tests { session: SessionId::default().into(), session_nonce: 0, origin: None, - author: Address::default().into(), + author: Address::zero().into(), nodes: BTreeMap::new(), is_zero: false, threshold: 1, @@ -1023,7 +1031,7 @@ mod tests { sub_session: session.core.access_key.clone().into(), session_nonce: 0, request_id: Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap().into(), - message_hash: H256::default().into(), + message_hash: H256::zero().into(), nodes: Default::default(), }), Err(Error::InvalidStateForRequest)); } @@ -1037,7 +1045,7 @@ mod tests { sub_session: session.core.access_key.clone().into(), session_nonce: 0, request_id: Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap().into(), - message_hash: H256::default().into(), + message_hash: H256::zero().into(), nodes: Default::default(), }), Err(Error::InvalidMessage)); } diff --git a/secret-store/src/key_server_cluster/cluster.rs b/secret-store/src/key_server_cluster/cluster.rs index a8416a8f70..18f8307532 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,12 +17,13 @@ use std::sync::Arc; use std::collections::{BTreeMap, BTreeSet}; use parking_lot::RwLock; -use ethkey::{Public, Signature, Random, Generator}; +use crypto::publickey::{Public, Signature, Random, Generator}; use ethereum_types::{Address, H256}; use parity_runtime::Executor; -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, +use blockchain::SigningKeyPair; +use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage, KeyStorage, KeyServerSet}; +use key_server_cluster::cluster_sessions::{WaitableSession, ClusterSession, AdminSession, ClusterSessions, + SessionIdWithSubSession, ClusterSessionsContainer, SERVERS_SET_CHANGE_SESSION_ID, create_cluster_view, AdminSessionCreationData, ClusterSessionsListener}; use key_server_cluster::cluster_sessions_creator::ClusterSessionCreator; use key_server_cluster::cluster_connections::{ConnectionProvider, ConnectionManager}; @@ -47,26 +48,68 @@ use key_server_cluster::cluster_connections::tests::{MessagesQueue, TestConnecti /// Cluster interface for external clients. pub trait ClusterClient: Send + Sync { /// Start new generation session. - fn new_generation_session(&self, session_id: SessionId, origin: Option
, author: Address, 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, author: 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, origin: Option
, requester: Requester, version: Option, is_shadow_decryption: bool, is_broadcast_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>; + fn new_schnorr_signing_session( + &self, + session_id: SessionId, + requester: Requester, + version: Option, + message_hash: H256, + ) -> Result, Error>; /// Start new ECDSA session. - fn new_ecdsa_signing_session(&self, session_id: SessionId, requester: Requester, version: Option, message_hash: H256) -> Result, Error>; + fn new_ecdsa_signing_session( + &self, + session_id: SessionId, + requester: Requester, + version: Option, + message_hash: H256, + ) -> Result, Error>; /// Start new key version negotiation session. - fn new_key_version_negotiation_session(&self, session_id: SessionId) -> Result>, Error>; + fn new_key_version_negotiation_session( + &self, + session_id: SessionId, + ) -> Result>, Error>; /// Start new servers set change session. - 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>; + 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>; /// Listen for new generation sessions. - fn add_generation_listener(&self, listener: Arc>); + fn add_generation_listener(&self, listener: Arc>); /// Listen for new decryption sessions. - fn add_decryption_listener(&self, listener: Arc>); + fn add_decryption_listener(&self, listener: Arc>); /// Listen for new key version negotiation sessions. - fn add_key_version_negotiation_listener(&self, listener: Arc>>); + fn add_key_version_negotiation_listener(&self, listener: Arc>>); /// Ask node to make 'faulty' generation sessions. #[cfg(test)] @@ -101,13 +144,13 @@ pub trait Cluster: Send + Sync { #[derive(Clone)] pub struct ClusterConfiguration { /// KeyPair this node holds. - pub self_key_pair: Arc, + pub self_key_pair: Arc, /// Cluster nodes set. - pub key_server_set: Arc, + pub key_server_set: Arc, /// Reference to key storage - pub key_storage: Arc, + pub key_storage: Arc, /// Reference to ACL storage - pub acl_storage: Arc, + pub acl_storage: Arc, /// Administrator public key. pub admin_public: Option, /// Do not remove sessions from container. @@ -130,8 +173,8 @@ pub struct ClusterClientImpl { pub struct ClusterView { configured_nodes_count: usize, connected_nodes: BTreeSet, - connections: Arc, - self_key_pair: Arc, + connections: Arc, + self_key_pair: Arc, } /// Cross-thread shareable cluster data. @@ -139,15 +182,15 @@ pub struct ClusterData { /// Cluster configuration. pub config: ClusterConfiguration, /// KeyPair this node holds. - pub self_key_pair: Arc, + pub self_key_pair: Arc, /// Connections data. pub connections: C, /// Active sessions data. pub sessions: Arc, // Messages processor. - pub message_processor: Arc, + pub message_processor: Arc, /// Link between servers set chnage session and the connections manager. - pub servers_set_change_creator_connector: Arc, + pub servers_set_change_creator_connector: Arc, } /// Create new network-backed cluster. @@ -164,7 +207,7 @@ pub fn new_network_cluster( connections: BTreeMap::new(), })); - let connection_trigger: Box = match net_config.auto_migrate_enabled { + let connection_trigger: Box = match net_config.auto_migrate_enabled { false => Box::new(SimpleConnectionTrigger::with_config(&config)), true if config.admin_public.is_none() => Box::new(ConnectionTriggerWithMigration::with_config(&config)), true => return Err(Error::Internal( @@ -222,9 +265,9 @@ pub fn new_test_cluster( impl ClusterCore { pub fn new( sessions: Arc, - message_processor: Arc, + message_processor: Arc, connections: C, - servers_set_change_creator_connector: Arc, + servers_set_change_creator_connector: Arc, config: ClusterConfiguration, ) -> Result, Error> { Ok(Arc::new(ClusterCore { @@ -240,7 +283,7 @@ impl ClusterCore { } /// Create new client interface. - pub fn client(&self) -> Arc { + pub fn client(&self) -> Arc { Arc::new(ClusterClientImpl::new(self.data.clone())) } @@ -251,7 +294,7 @@ impl ClusterCore { } #[cfg(test)] - pub fn view(&self) -> Result, Error> { + pub fn view(&self) -> Result, Error> { let connections = self.data.connections.provider(); let mut connected_nodes = connections.connected_nodes()?; let disconnected_nodes = connections.disconnected_nodes(); @@ -269,8 +312,8 @@ impl ClusterCore { impl ClusterView { pub fn new( - self_key_pair: Arc, - connections: Arc, + self_key_pair: Arc, + connections: Arc, nodes: BTreeSet, configured_nodes_count: usize ) -> Self { @@ -324,7 +367,10 @@ impl ClusterClientImpl { } } - fn create_key_version_negotiation_session(&self, session_id: SessionId) -> Result>, Error> { + fn create_key_version_negotiation_session( + &self, + session_id: SessionId, + ) -> Result>, Error> { let mut connected_nodes = self.data.connections.provider().connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); @@ -332,10 +378,10 @@ impl ClusterClientImpl { let session_id = SessionIdWithSubSession::new(session_id, access_key); let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), false)?; let session = self.data.sessions.negotiation_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, None)?; - match session.initialize(connected_nodes) { + match session.session.initialize(connected_nodes) { Ok(()) => Ok(session), Err(error) => { - self.data.sessions.negotiation_sessions.remove(&session.id()); + self.data.sessions.negotiation_sessions.remove(&session.session.id()); Err(error) } } @@ -343,29 +389,49 @@ impl ClusterClientImpl { } impl ClusterClient for ClusterClientImpl { - fn new_generation_session(&self, session_id: SessionId, origin: Option
, author: Address, threshold: usize) -> Result, Error> { + fn new_generation_session( + &self, + session_id: SessionId, + origin: Option
, + author: Address, + threshold: usize, + ) -> Result, Error> { let mut connected_nodes = self.data.connections.provider().connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), true)?; let session = self.data.sessions.generation_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, false, None)?; process_initialization_result( - session.initialize(origin, author, false, threshold, connected_nodes.into()), + session.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> { + 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.provider().connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), true)?; let session = self.data.sessions.encryption_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, false, None)?; process_initialization_result( - session.initialize(requester, common_point, encrypted_point), + session.session.initialize(requester, common_point, encrypted_point), session, &self.data.sessions.encryption_sessions) } - fn new_decryption_session(&self, session_id: SessionId, origin: Option
, requester: Requester, version: Option, is_shadow_decryption: bool, is_broadcast_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> { let mut connected_nodes = self.data.connections.provider().connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); @@ -376,12 +442,18 @@ impl ClusterClient for ClusterClientImpl { session_id.clone(), None, false, Some(requester))?; let initialization_result = match version { - Some(version) => session.initialize(origin, version, is_shadow_decryption, is_broadcast_decryption), + Some(version) => session.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(), origin, is_shadow_decryption, is_broadcast_decryption)); - self.data.message_processor.try_continue_session(Some(version_session)); + let continue_action = ContinueAction::Decrypt( + session.session.clone(), + origin, + is_shadow_decryption, + is_broadcast_decryption, + ); + version_session.session.set_continue_action(continue_action); + self.data.message_processor.try_continue_session(Some(version_session.session)); }) }, }; @@ -391,7 +463,13 @@ impl ClusterClient for ClusterClientImpl { session, &self.data.sessions.decryption_sessions) } - fn new_schnorr_signing_session(&self, session_id: SessionId, requester: Requester, version: Option, message_hash: H256) -> Result, Error> { + 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.provider().connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); @@ -401,12 +479,13 @@ impl ClusterClient for ClusterClientImpl { let session = self.data.sessions.schnorr_signing_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, Some(requester))?; let initialization_result = match version { - Some(version) => session.initialize(version, message_hash), + Some(version) => session.session.initialize(version, message_hash), None => { self.create_key_version_negotiation_session(session_id.id.clone()) .map(|version_session| { - version_session.set_continue_action(ContinueAction::SchnorrSign(session.clone(), message_hash)); - self.data.message_processor.try_continue_session(Some(version_session)); + let continue_action = ContinueAction::SchnorrSign(session.session.clone(), message_hash); + version_session.session.set_continue_action(continue_action); + self.data.message_processor.try_continue_session(Some(version_session.session)); }) }, }; @@ -416,7 +495,13 @@ impl ClusterClient for ClusterClientImpl { 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> { + 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.provider().connected_nodes()?; connected_nodes.insert(self.data.self_key_pair.public().clone()); @@ -426,12 +511,13 @@ impl ClusterClient for ClusterClientImpl { let session = self.data.sessions.ecdsa_signing_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, Some(requester))?; let initialization_result = match version { - Some(version) => session.initialize(version, message_hash), + Some(version) => session.session.initialize(version, message_hash), None => { self.create_key_version_negotiation_session(session_id.id.clone()) .map(|version_session| { - version_session.set_continue_action(ContinueAction::EcdsaSign(session.clone(), message_hash)); - self.data.message_processor.try_continue_session(Some(version_session)); + let continue_action = ContinueAction::EcdsaSign(session.session.clone(), message_hash); + version_session.session.set_continue_action(continue_action); + self.data.message_processor.try_continue_session(Some(version_session.session)); }) }, }; @@ -441,12 +527,21 @@ impl ClusterClient for ClusterClientImpl { session, &self.data.sessions.ecdsa_signing_sessions) } - fn new_key_version_negotiation_session(&self, session_id: SessionId) -> Result>, Error> { - let session = self.create_key_version_negotiation_session(session_id)?; - Ok(session) + fn new_key_version_negotiation_session( + &self, + session_id: SessionId, + ) -> Result>, Error> { + self.create_key_version_negotiation_session(session_id) } - 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> { + 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> { new_servers_set_change_session( self.data.self_key_pair.clone(), &self.data.sessions, @@ -461,15 +556,15 @@ impl ClusterClient for ClusterClientImpl { }) } - fn add_generation_listener(&self, listener: Arc>) { + fn add_generation_listener(&self, listener: Arc>) { self.data.sessions.generation_sessions.add_listener(listener); } - fn add_decryption_listener(&self, listener: Arc>) { + fn add_decryption_listener(&self, listener: Arc>) { self.data.sessions.decryption_sessions.add_listener(listener); } - fn add_key_version_negotiation_listener(&self, listener: Arc>>) { + fn add_key_version_negotiation_listener(&self, listener: Arc>>) { self.data.sessions.negotiation_sessions.add_listener(listener); } @@ -503,12 +598,12 @@ pub struct ServersSetChangeParams { } pub fn new_servers_set_change_session( - self_key_pair: Arc, + self_key_pair: Arc, sessions: &ClusterSessions, - connections: Arc, - servers_set_change_creator_connector: Arc, + connections: Arc, + servers_set_change_creator_connector: Arc, params: ServersSetChangeParams, -) -> Result, Error> { +) -> Result, Error> { let session_id = match params.session_id { Some(session_id) if session_id == *SERVERS_SET_CHANGE_SESSION_ID => session_id, Some(_) => return Err(Error::InvalidMessage), @@ -519,11 +614,11 @@ pub fn new_servers_set_change_session( let creation_data = AdminSessionCreationData::ServersSetChange(params.migration_id, params.new_nodes_set.clone()); let session = sessions.admin_sessions .insert(cluster, *self_key_pair.public(), session_id, None, true, Some(creation_data))?; - let initialization_result = session.as_servers_set_change().expect("servers set change session is created; qed") + let initialization_result = session.session.as_servers_set_change().expect("servers set change session is created; qed") .initialize(params.new_nodes_set, params.old_set_signature, params.new_set_signature); if initialization_result.is_ok() { - servers_set_change_creator_connector.set_key_servers_set_change_session(session.clone()); + servers_set_change_creator_connector.set_key_servers_set_change_session(session.session.clone()); } process_initialization_result( @@ -531,23 +626,23 @@ pub fn new_servers_set_change_session( session, &sessions.admin_sessions) } -fn process_initialization_result( +fn process_initialization_result( result: Result<(), Error>, - session: Arc, - sessions: &ClusterSessionsContainer -) -> Result, Error> + session: WaitableSession, + sessions: &ClusterSessionsContainer +) -> Result, Error> where S: ClusterSession, - SC: ClusterSessionCreator + SC: ClusterSessionCreator { match result { - Ok(()) if session.is_finished() => { - sessions.remove(&session.id()); + Ok(()) if session.session.is_finished() => { + sessions.remove(&session.session.id()); Ok(session) }, Ok(()) => Ok(session), Err(error) => { - sessions.remove(&session.id()); + sessions.remove(&session.session.id()); Err(error) }, } @@ -558,16 +653,19 @@ pub mod tests { use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use std::collections::{BTreeMap, BTreeSet, VecDeque}; + use futures::Future; use parking_lot::{Mutex, RwLock}; use ethereum_types::{Address, H256}; - use ethkey::{Random, Generator, Public, Signature, sign}; + use crypto::publickey::{Random, Generator, Public, Signature, sign}; + use blockchain::SigningKeyPair; use key_server_cluster::{NodeId, SessionId, Requester, Error, DummyAclStorage, DummyKeyStorage, - MapKeyServerSet, PlainNodeKeyPair, NodeKeyPair}; + MapKeyServerSet, PlainNodeKeyPair}; use key_server_cluster::message::Message; use key_server_cluster::cluster::{new_test_cluster, Cluster, ClusterCore, ClusterConfiguration, ClusterClient}; use key_server_cluster::cluster_connections::ConnectionManager; use key_server_cluster::cluster_connections::tests::{MessagesQueue, TestConnections}; - use key_server_cluster::cluster_sessions::{ClusterSession, ClusterSessions, AdminSession, ClusterSessionsListener}; + use key_server_cluster::cluster_sessions::{WaitableSession, ClusterSession, ClusterSessions, AdminSession, + ClusterSessionsListener}; use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionState as GenerationSessionState}; use key_server_cluster::decryption_session::{SessionImpl as DecryptionSession}; @@ -595,21 +693,75 @@ pub mod tests { } impl ClusterClient for DummyClusterClient { - fn new_generation_session(&self, _session_id: SessionId, _origin: Option
, _author: Address, _threshold: usize) -> Result, Error> { + 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, _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_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, + _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 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 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") } @@ -897,7 +1049,7 @@ pub mod tests { // start && wait for generation session to fail let session = ml.cluster(0).client() - .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap(); + .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap().session; ml.loop_until(|| session.joint_public_and_secret().is_some() && ml.cluster(0).client().generation_session(&SessionId::default()).is_none()); assert!(session.joint_public_and_secret().unwrap().is_err()); @@ -924,7 +1076,7 @@ pub mod tests { // start && wait for generation session to fail let session = ml.cluster(0).client() - .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap(); + .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap().session; ml.loop_until(|| session.joint_public_and_secret().is_some() && ml.cluster(0).client().generation_session(&SessionId::default()).is_none()); assert!(session.joint_public_and_secret().unwrap().is_err()); @@ -949,7 +1101,7 @@ pub mod tests { // start && wait for generation session to complete let session = ml.cluster(0).client() - .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap(); + .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap().session; ml.loop_until(|| (session.state() == GenerationSessionState::Finished || session.state() == GenerationSessionState::Failed) && ml.cluster(0).client().generation_session(&SessionId::default()).is_none()); @@ -1017,7 +1169,7 @@ pub mod tests { // start && wait for generation session to complete let session = ml.cluster(0).client(). - new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap(); + new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap().session; ml.loop_until(|| (session.state() == GenerationSessionState::Finished || session.state() == GenerationSessionState::Failed) && ml.cluster(0).client().generation_session(&SessionId::default()).is_none()); @@ -1035,7 +1187,7 @@ pub mod tests { ml.loop_until(|| session.is_finished() && (0..3).all(|i| ml.cluster(i).data.sessions.schnorr_signing_sessions.is_empty())); - session0.wait().unwrap(); + session0.into_wait_future().wait().unwrap(); // and try to sign message with generated key using node that has no key share let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); @@ -1045,7 +1197,7 @@ pub mod tests { ml.loop_until(|| session.is_finished() && (0..3).all(|i| ml.cluster(i).data.sessions.schnorr_signing_sessions.is_empty())); - session2.wait().unwrap(); + session2.into_wait_future().wait().unwrap(); // now remove share from node1 ml.cluster(1).data.config.key_storage.remove(&Default::default()).unwrap(); @@ -1057,7 +1209,7 @@ pub mod tests { let session = ml.cluster(0).data.sessions.schnorr_signing_sessions.first().unwrap(); ml.loop_until(|| session.is_finished()); - session1.wait().unwrap_err(); + session1.into_wait_future().wait().unwrap_err(); } #[test] @@ -1067,7 +1219,7 @@ pub mod tests { // start && wait for generation session to complete let session = ml.cluster(0).client() - .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap(); + .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap().session; ml.loop_until(|| (session.state() == GenerationSessionState::Finished || session.state() == GenerationSessionState::Failed) && ml.cluster(0).client().generation_session(&SessionId::default()).is_none()); @@ -1085,7 +1237,7 @@ pub mod tests { ml.loop_until(|| session.is_finished() && (0..3).all(|i| ml.cluster(i).data.sessions.ecdsa_signing_sessions.is_empty())); - session0.wait().unwrap(); + session0.into_wait_future().wait().unwrap(); // and try to sign message with generated key using node that has no key share let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); @@ -1094,7 +1246,7 @@ pub mod tests { let session = ml.cluster(2).data.sessions.ecdsa_signing_sessions.first().unwrap(); ml.loop_until(|| session.is_finished() && (0..3).all(|i| ml.cluster(i).data.sessions.ecdsa_signing_sessions.is_empty())); - session2.wait().unwrap(); + session2.into_wait_future().wait().unwrap(); // now remove share from node1 ml.cluster(1).data.config.key_storage.remove(&Default::default()).unwrap(); @@ -1105,6 +1257,6 @@ pub mod tests { .new_ecdsa_signing_session(Default::default(), signature.into(), None, H256::random()).unwrap(); let session = ml.cluster(0).data.sessions.ecdsa_signing_sessions.first().unwrap(); ml.loop_until(|| session.is_finished()); - session1.wait().unwrap_err(); + session1.into_wait_future().wait().unwrap_err(); } } diff --git a/secret-store/src/key_server_cluster/cluster_connections.rs b/secret-store/src/key_server_cluster/cluster_connections.rs index b484e6d8e0..6a5d417d65 100644 --- a/secret-store/src/key_server_cluster/cluster_connections.rs +++ b/secret-store/src/key_server_cluster/cluster_connections.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 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 trait Connection: Send + Sync { /// Connections manager. Responsible for keeping us connected to all required nodes. pub trait ConnectionManager: 'static + Send + Sync { /// Returns shared reference to connections provider. - fn provider(&self) -> Arc; + fn provider(&self) -> Arc; /// Try to reach all disconnected nodes immediately. This method is exposed mostly for /// tests, where all 'nodes' are starting listening for incoming connections first and /// only after this, they're actually start connecting to each other. @@ -55,7 +55,7 @@ pub trait ConnectionProvider: Send + Sync { /// Returns the set of currently disconnected nodes. fn disconnected_nodes(&self) -> BTreeSet; /// Returns the reference to the active node connection or None if the node is not connected. - fn connection(&self, node: &NodeId) -> Option>; + fn connection(&self, node: &NodeId) -> Option>; } #[cfg(test)] @@ -110,7 +110,7 @@ pub mod tests { } impl ConnectionManager for Arc { - fn provider(&self) -> Arc { + fn provider(&self) -> Arc { self.clone() } @@ -129,7 +129,7 @@ pub mod tests { self.disconnected_nodes.lock().clone() } - fn connection(&self, node: &NodeId) -> Option> { + fn connection(&self, node: &NodeId) -> Option> { match self.connected_nodes.lock().contains(node) { true => Some(Arc::new(TestConnection { from: self.node, diff --git a/secret-store/src/key_server_cluster/cluster_connections_net.rs b/secret-store/src/key_server_cluster/cluster_connections_net.rs index bda7f7dd28..cd46b11aab 100644 --- a/secret-store/src/key_server_cluster/cluster_connections_net.rs +++ b/secret-store/src/key_server_cluster/cluster_connections_net.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -25,9 +25,10 @@ use parking_lot::{Mutex, RwLock}; use tokio::net::{TcpListener, TcpStream}; use tokio::timer::{Interval, timeout::Error as TimeoutError}; use tokio_io::IoFuture; -use ethkey::KeyPair; +use crypto::publickey::KeyPair; use parity_runtime::Executor; -use key_server_cluster::{Error, NodeId, ClusterConfiguration, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{Error, NodeId, ClusterConfiguration}; use key_server_cluster::cluster_connections::{ConnectionProvider, Connection, ConnectionManager}; use key_server_cluster::connection_trigger::{Maintain, ConnectionTrigger}; use key_server_cluster::cluster_message_processor::MessageProcessor; @@ -38,7 +39,7 @@ use key_server_cluster::net::{accept_connection as io_accept_connection, connect as io_connect, Connection as IoConnection}; /// Empty future. -pub type BoxedEmptyFuture = Box + Send>; +pub type BoxedEmptyFuture = Box + Send>; /// Maintain interval (seconds). Every MAINTAIN_INTERVAL seconds node: /// 1) checks if connected nodes are responding to KeepAlive messages @@ -79,11 +80,11 @@ struct NetConnectionsData { /// Reference to tokio task executor. executor: Executor, /// Key pair of this node. - self_key_pair: Arc, + self_key_pair: Arc, /// Network messages processor. - message_processor: Arc, + message_processor: Arc, /// Connections trigger. - trigger: Mutex>, + trigger: Mutex>, /// Mutable connection data. container: Arc>, } @@ -121,8 +122,8 @@ impl NetConnectionsManager { /// Create new network connections manager. pub fn new( executor: Executor, - message_processor: Arc, - trigger: Box, + message_processor: Arc, + trigger: Box, container: Arc>, config: &ClusterConfiguration, net_config: NetConnectionsManagerConfig, @@ -153,7 +154,7 @@ impl NetConnectionsManager { } impl ConnectionManager for NetConnectionsManager { - fn provider(&self) -> Arc { + fn provider(&self) -> Arc { self.data.container.clone() } @@ -180,7 +181,7 @@ impl ConnectionProvider for RwLock { .collect() } - fn connection(&self, node: &NodeId) -> Option> { + fn connection(&self, node: &NodeId) -> Option> { match self.read().connections.get(node).cloned() { Some(connection) => Some(connection), None => None, @@ -302,7 +303,7 @@ impl NetConnectionsData { trace!(target: "secretstore_net", "{}: removing connection to {} at {}", self.self_key_pair.public(), node_id, entry.get().node_address()); entry.remove_entry(); - + true } else { false @@ -467,10 +468,13 @@ fn net_maintain(data: Arc) { /// Send keep alive messages to remote nodes. fn net_keep_alive(data: Arc) { - let now = Instant::now(); let active_connections = data.active_connections(); for connection in active_connections { - let last_message_diff = now - connection.last_message_time(); + // the last_message_time could change after active_connections() call + // => we always need to call Instant::now() after getting last_message_time + let last_message_time = connection.last_message_time(); + let now = Instant::now(); + let last_message_diff = now - 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()); diff --git a/secret-store/src/key_server_cluster/cluster_message_processor.rs b/secret-store/src/key_server_cluster/cluster_message_processor.rs index b4ba5ef03b..df1211f8a0 100644 --- a/secret-store/src/key_server_cluster/cluster_message_processor.rs +++ b/secret-store/src/key_server_cluster/cluster_message_processor.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -15,7 +15,8 @@ // along with Parity. If not, see . use std::sync::Arc; -use key_server_cluster::{Error, NodeId, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{Error, NodeId}; use key_server_cluster::cluster::{ServersSetChangeParams, new_servers_set_change_session}; use key_server_cluster::cluster_sessions::{AdminSession}; use key_server_cluster::cluster_connections::{ConnectionProvider, Connection}; @@ -32,7 +33,7 @@ pub trait MessageProcessor: Send + Sync { /// Process disconnect from the remote node. fn process_disconnect(&self, node: &NodeId); /// Process single message from the connection. - fn process_connection_message(&self, connection: Arc, message: Message); + fn process_connection_message(&self, connection: Arc, message: Message); /// Start servers set change session. This is typically used by ConnectionManager when /// it detects that auto-migration session needs to be started. @@ -49,19 +50,19 @@ pub trait MessageProcessor: Send + Sync { /// Bridge between ConnectionManager and ClusterSessions. pub struct SessionsMessageProcessor { - self_key_pair: Arc, - servers_set_change_creator_connector: Arc, + self_key_pair: Arc, + servers_set_change_creator_connector: Arc, sessions: Arc, - connections: Arc, + connections: Arc, } impl SessionsMessageProcessor { /// Create new instance of SessionsMessageProcessor. pub fn new( - self_key_pair: Arc, - servers_set_change_creator_connector: Arc, + self_key_pair: Arc, + servers_set_change_creator_connector: Arc, sessions: Arc, - connections: Arc, + connections: Arc, ) -> Self { SessionsMessageProcessor { self_key_pair, @@ -72,10 +73,10 @@ impl SessionsMessageProcessor { } /// Process single session message from connection. - fn process_message, D>( + fn process_message>( &self, - sessions: &ClusterSessionsContainer, - connection: Arc, + sessions: &ClusterSessionsContainer, + connection: Arc, mut message: Message, ) -> Option> where @@ -151,9 +152,9 @@ impl SessionsMessageProcessor { } /// Get or insert new session. - fn prepare_session, D>( + fn prepare_session>( &self, - sessions: &ClusterSessionsContainer, + sessions: &ClusterSessionsContainer, sender: &NodeId, message: &Message ) -> Result, Error> @@ -192,13 +193,13 @@ impl SessionsMessageProcessor { let nonce = Some(message.session_nonce().ok_or(Error::InvalidMessage)?); let exclusive = message.is_exclusive_session_message(); - sessions.insert(cluster, master, session_id, nonce, exclusive, creation_data) + sessions.insert(cluster, master, session_id, nonce, exclusive, creation_data).map(|s| s.session) }, } } /// Process single cluster message from the connection. - fn process_cluster_message(&self, connection: Arc, message: ClusterMessage) { + fn process_cluster_message(&self, connection: Arc, message: ClusterMessage) { match message { ClusterMessage::KeepAlive(_) => { let msg = Message::Cluster(ClusterMessage::KeepAliveResponse(message::KeepAliveResponse { @@ -220,7 +221,7 @@ impl MessageProcessor for SessionsMessageProcessor { self.sessions.on_connection_timeout(node); } - fn process_connection_message(&self, connection: Arc, message: Message) { + fn process_connection_message(&self, connection: Arc, message: Message) { trace!(target: "secretstore_net", "{}: received message {} from {}", self.self_key_pair.public(), message, connection.node_id()); @@ -273,8 +274,8 @@ impl MessageProcessor for SessionsMessageProcessor { let is_master_node = meta.self_node_id == meta.master_node_id; if is_master_node && session.is_finished() { self.sessions.negotiation_sessions.remove(&session.id()); - match session.wait() { - Ok(Some((version, master))) => match session.take_continue_action() { + match session.result() { + Some(Ok(Some((version, master)))) => match session.take_continue_action() { Some(ContinueAction::Decrypt( session, origin, is_shadow_decryption, is_broadcast_decryption )) => { @@ -317,10 +318,7 @@ impl MessageProcessor for SessionsMessageProcessor { }, 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(Err(error)) => match session.take_continue_action() { Some(ContinueAction::Decrypt(session, _, _, _)) => { session.on_session_error(&meta.self_node_id, error); self.sessions.decryption_sessions.remove(&session.id()); @@ -335,6 +333,9 @@ impl MessageProcessor for SessionsMessageProcessor { }, None => (), }, + None | Some(Ok(None)) => unreachable!("is_master_node; session is finished; + negotiation version always finished with result on master; + qed"), } } } @@ -352,6 +353,6 @@ impl MessageProcessor for SessionsMessageProcessor { self.connections.clone(), self.servers_set_change_creator_connector.clone(), params, - ) + ).map(|s| s.session) } } diff --git a/secret-store/src/key_server_cluster/cluster_sessions.rs b/secret-store/src/key_server_cluster/cluster_sessions.rs index 53eec13346..7311f0e102 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,10 +18,12 @@ use std::time::{Duration, Instant}; use std::sync::{Arc, Weak}; use std::sync::atomic::AtomicBool; use std::collections::{VecDeque, BTreeMap, BTreeSet}; +use futures::{oneshot, Oneshot, Complete, Future}; use parking_lot::{Mutex, RwLock, Condvar}; use ethereum_types::H256; -use ethkey::Secret; -use key_server_cluster::{Error, NodeId, SessionId, Requester, NodeKeyPair}; +use crypto::publickey::Secret; +use blockchain::SigningKeyPair; +use key_server_cluster::{Error, NodeId, SessionId}; use key_server_cluster::cluster::{Cluster, ClusterConfiguration, ClusterView}; use key_server_cluster::cluster_connections::ConnectionProvider; use key_server_cluster::connection_trigger::ServersSetChangeSessionCreatorConnector; @@ -68,6 +70,10 @@ pub struct SessionIdWithSubSession { pub trait ClusterSession { /// Session identifier type. type Id: ::std::fmt::Debug + Ord + Clone; + /// Session creation data type. + type CreationData; + /// Session (successful) result type. + type SuccessfulResult: Send + 'static; /// Session type name. fn type_name() -> &'static str; @@ -85,15 +91,22 @@ 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) -> Option> { + #[cfg(test)] + fn wait_session Option>>( + completion: &CompletionSignal, + session_data: &Mutex, + timeout: Option, + result_reader: F + ) -> Option> { let mut locked_data = session_data.lock(); match result_reader(&locked_data) { Some(result) => Some(result), None => { + let completion_condvar = completion.completion_condvar.as_ref().expect("created in test mode"); match timeout { - None => completion_event.wait(&mut locked_data), + None => completion_condvar.wait(&mut locked_data), Some(timeout) => { - completion_event.wait_for(&mut locked_data, timeout); + completion_condvar.wait_for(&mut locked_data, timeout); }, } @@ -103,6 +116,23 @@ pub trait ClusterSession { } } +/// Waitable cluster session. +pub struct WaitableSession { + /// Session handle. + pub session: Arc, + /// Session result oneshot. + pub oneshot: Oneshot>, +} + +/// Session completion signal. +pub struct CompletionSignal { + /// Completion future. + pub completion_future: Mutex>>>, + + /// Completion condvar. + pub completion_condvar: Option, +} + /// Administrative session. pub enum AdminSession { /// Share add session. @@ -122,19 +152,22 @@ pub enum AdminSessionCreationData { /// Active sessions on this cluster. pub struct ClusterSessions { /// Key generation sessions. - pub generation_sessions: ClusterSessionsContainer, + pub generation_sessions: ClusterSessionsContainer, /// Encryption sessions. - pub encryption_sessions: ClusterSessionsContainer, + pub encryption_sessions: ClusterSessionsContainer, /// Decryption sessions. - pub decryption_sessions: ClusterSessionsContainer, + pub decryption_sessions: ClusterSessionsContainer, /// Schnorr signing sessions. - pub schnorr_signing_sessions: ClusterSessionsContainer, + pub schnorr_signing_sessions: ClusterSessionsContainer, /// ECDSA signing sessions. - pub ecdsa_signing_sessions: ClusterSessionsContainer, + pub ecdsa_signing_sessions: ClusterSessionsContainer, /// Key version negotiation sessions. - pub negotiation_sessions: ClusterSessionsContainer, KeyVersionNegotiationSessionCreator, ()>, + pub negotiation_sessions: ClusterSessionsContainer< + KeyVersionNegotiationSessionImpl, + KeyVersionNegotiationSessionCreator + >, /// Administrative sessions. - pub admin_sessions: ClusterSessionsContainer, + pub admin_sessions: ClusterSessionsContainer, /// Self node id. self_node_id: NodeId, /// Creator core. @@ -150,19 +183,17 @@ pub trait ClusterSessionsListener: Send + Sync { } /// Active sessions container. -pub struct ClusterSessionsContainer, D> { +pub struct ClusterSessionsContainer> { /// Sessions creator. pub creator: SC, /// Active sessions. sessions: RwLock>>, /// Listeners. Lock order: sessions -> listeners. - listeners: Mutex>>>, + listeners: Mutex>>>, /// Sessions container state. container_state: Arc>, /// Do not actually remove sessions. preserve_sessions: bool, - /// Phantom data. - _pd: ::std::marker::PhantomData, } /// Session and its message queue. @@ -170,7 +201,7 @@ pub struct QueuedSession { /// Session master. pub master: NodeId, /// Cluster view. - pub cluster_view: Arc, + pub cluster_view: Arc, /// Last keep alive time. pub last_keep_alive_time: Instant, /// Last received message time. @@ -194,7 +225,7 @@ pub enum ClusterSessionsContainerState { impl ClusterSessions { /// Create new cluster sessions container. - pub fn new(config: &ClusterConfiguration, servers_set_change_session_creator_connector: Arc) -> Self { + pub fn new(config: &ClusterConfiguration, servers_set_change_session_creator_connector: Arc) -> Self { let container_state = Arc::new(Mutex::new(ClusterSessionsContainerState::Idle)); let creator_core = Arc::new(SessionCreatorCore::new(config)); ClusterSessions { @@ -279,7 +310,7 @@ impl ClusterSessions { } } -impl ClusterSessionsContainer where S: ClusterSession, SC: ClusterSessionCreator { +impl ClusterSessionsContainer where S: ClusterSession, SC: ClusterSessionCreator { pub fn new(creator: SC, container_state: Arc>) -> Self { ClusterSessionsContainer { creator: creator, @@ -287,11 +318,10 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C listeners: Mutex::new(Vec::new()), container_state: container_state, preserve_sessions: false, - _pd: Default::default(), } } - pub fn add_listener(&self, listener: Arc>) { + pub fn add_listener(&self, listener: Arc>) { self.listeners.lock().push(Arc::downgrade(&listener)); } @@ -316,7 +346,15 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C self.sessions.read().values().nth(0).map(|s| s.session.clone()) } - pub fn insert(&self, cluster: Arc, master: NodeId, session_id: S::Id, session_nonce: Option, is_exclusive_session: bool, creation_data: Option) -> Result, Error> { + pub fn insert( + &self, + cluster: Arc, + master: NodeId, + session_id: S::Id, + session_nonce: Option, + is_exclusive_session: bool, + creation_data: Option, + ) -> Result, Error> { let mut sessions = self.sessions.write(); if sessions.contains_key(&session_id) { return Err(Error::DuplicateSessionId); @@ -335,11 +373,11 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C cluster_view: cluster, last_keep_alive_time: Instant::now(), last_message_time: Instant::now(), - session: session.clone(), + session: session.session.clone(), queue: VecDeque::new(), }; sessions.insert(session_id, queued_session); - self.notify_listeners(|l| l.on_session_inserted(session.clone())); + self.notify_listeners(|l| l.on_session_inserted(session.session.clone())); Ok(session) } @@ -402,7 +440,7 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C } } - fn notify_listeners) -> ()>(&self, callback: F) { + fn notify_listeners) -> ()>(&self, callback: F) { let mut listeners = self.listeners.lock(); let mut listener_index = 0; while listener_index < listeners.len() { @@ -419,7 +457,12 @@ impl ClusterSessionsContainer where S: ClusterSession, SC: C } } -impl ClusterSessionsContainer where S: ClusterSession, SC: ClusterSessionCreator, SessionId: From { +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 = Instant::now(); @@ -521,6 +564,8 @@ impl AdminSession { impl ClusterSession for AdminSession { type Id = SessionId; + type CreationData = AdminSessionCreationData; + type SuccessfulResult = (); fn type_name() -> &'static str { "admin" @@ -569,7 +614,41 @@ impl ClusterSession for AdminSession { } } -pub fn create_cluster_view(self_key_pair: Arc, connections: Arc, requires_all_connections: bool) -> Result, Error> { +impl WaitableSession { + pub fn new(session: S, oneshot: Oneshot>) -> Self { + WaitableSession { + session: Arc::new(session), + oneshot, + } + } + + pub fn into_wait_future(self) -> Box + Send> { + Box::new(self.oneshot + .map_err(|e| Error::Internal(e.to_string())) + .and_then(|res| res)) + } +} + +impl CompletionSignal { + pub fn new() -> (Self, Oneshot>) { + let (complete, oneshot) = oneshot(); + let completion_condvar = if cfg!(test) { Some(Condvar::new()) } else { None }; + (CompletionSignal { + completion_future: Mutex::new(Some(complete)), + completion_condvar, + }, oneshot) + } + + pub fn send(&self, result: Result) { + let completion_future = ::std::mem::replace(&mut *self.completion_future.lock(), None); + completion_future.map(|c| c.send(result)); + if let Some(ref completion_condvar) = self.completion_condvar { + completion_condvar.notify_all(); + } + } +} + +pub fn create_cluster_view(self_key_pair: Arc, connections: Arc, requires_all_connections: bool) -> Result, Error> { let mut connected_nodes = connections.connected_nodes()?; let disconnected_nodes = connections.disconnected_nodes(); @@ -590,7 +669,7 @@ pub fn create_cluster_view(self_key_pair: Arc, connections: Arc { +pub trait ClusterSessionCreator { /// Get creation data from message. - fn creation_data_from_message(_message: &Message) -> Result, Error> { + fn creation_data_from_message(_message: &Message) -> Result, Error> { Ok(None) } @@ -53,7 +54,14 @@ pub trait ClusterSessionCreator { fn make_error_message(sid: S::Id, nonce: u64, err: Error) -> Message; /// Create cluster session. - fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: S::Id, creation_data: Option) -> Result, Error>; + fn create( + &self, + cluster: Arc, + master: NodeId, + nonce: Option, + id: S::Id, + creation_data: Option, + ) -> Result, Error>; } /// Message with session id. @@ -66,9 +74,9 @@ pub struct SessionCreatorCore { /// Self node id. self_node_id: NodeId, /// Reference to key storage - key_storage: Arc, + key_storage: Arc, /// Reference to ACL storage - acl_storage: Arc, + acl_storage: Arc, /// Always-increasing sessions counter. Is used as session nonce to prevent replay attacks: /// 1) during handshake, KeyServers generate new random key to encrypt messages /// => there's no way to use messages from previous connections for replay attacks @@ -134,7 +142,7 @@ impl GenerationSessionCreator { } } -impl ClusterSessionCreator for GenerationSessionCreator { +impl ClusterSessionCreator for GenerationSessionCreator { fn make_error_message(sid: SessionId, nonce: u64, err: Error) -> Message { message::Message::Generation(message::GenerationMessage::SessionError(message::SessionError { session: sid.into(), @@ -143,27 +151,33 @@ impl ClusterSessionCreator for GenerationSessionCreat })) } - fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionId, _creation_data: Option<()>) -> Result, Error> { + 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::ServerKeyAlreadyGenerated); } let nonce = self.core.check_session_nonce(&master, nonce)?; - Ok(GenerationSessionImpl::new(GenerationSessionParams { + let (session, oneshot) = GenerationSessionImpl::new(GenerationSessionParams { id: id.clone(), self_node_id: self.core.self_node_id.clone(), key_storage: Some(self.core.key_storage.clone()), cluster: cluster, nonce: Some(nonce), - })) - .map(|session| { - if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { - session.simulate_faulty_behaviour(); - } - session - }) - .map(Arc::new) + }); + + if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { + session.simulate_faulty_behaviour(); + } + + Ok(WaitableSession::new(session, oneshot)) } } @@ -173,7 +187,7 @@ pub struct EncryptionSessionCreator { pub core: Arc, } -impl ClusterSessionCreator for EncryptionSessionCreator { +impl ClusterSessionCreator for EncryptionSessionCreator { fn make_error_message(sid: SessionId, nonce: u64, err: Error) -> Message { message::Message::Encryption(message::EncryptionMessage::EncryptionSessionError(message::EncryptionSessionError { session: sid.into(), @@ -182,17 +196,26 @@ impl ClusterSessionCreator for EncryptionSessionCreat })) } - fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionId, _creation_data: Option<()>) -> Result, Error> { + fn create( + &self, + cluster: Arc, + master: NodeId, + nonce: Option, + id: SessionId, + _creation_data: Option<()>, + ) -> Result, Error> { let encrypted_data = self.core.read_key_share(&id)?; let nonce = self.core.check_session_nonce(&master, nonce)?; - Ok(Arc::new(EncryptionSessionImpl::new(EncryptionSessionParams { + let (session, oneshot) = EncryptionSessionImpl::new(EncryptionSessionParams { id: id, self_node_id: self.core.self_node_id.clone(), encrypted_data: encrypted_data, key_storage: self.core.key_storage.clone(), cluster: cluster, nonce: nonce, - })?)) + })?; + + Ok(WaitableSession::new(session, oneshot)) } } @@ -202,7 +225,7 @@ pub struct DecryptionSessionCreator { pub core: Arc, } -impl ClusterSessionCreator for DecryptionSessionCreator { +impl ClusterSessionCreator for DecryptionSessionCreator { fn creation_data_from_message(message: &Message) -> Result, Error> { match *message { Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(ref message)) => match &message.message { @@ -223,10 +246,17 @@ impl ClusterSessionCreator for DecryptionSessi })) } - fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionIdWithSubSession, requester: Option) -> Result, Error> { + fn create( + &self, + cluster: Arc, + master: NodeId, + nonce: Option, + id: SessionIdWithSubSession, + requester: Option, + ) -> Result, Error> { let encrypted_data = self.core.read_key_share(&id.id)?; let nonce = self.core.check_session_nonce(&master, nonce)?; - Ok(Arc::new(DecryptionSessionImpl::new(DecryptionSessionParams { + let (session, oneshot) = DecryptionSessionImpl::new(DecryptionSessionParams { meta: SessionMeta { id: id.id, self_node_id: self.core.self_node_id.clone(), @@ -240,7 +270,9 @@ impl ClusterSessionCreator for DecryptionSessi acl_storage: self.core.acl_storage.clone(), cluster: cluster, nonce: nonce, - }, requester)?)) + }, requester)?; + + Ok(WaitableSession::new(session, oneshot)) } } @@ -250,7 +282,7 @@ pub struct SchnorrSigningSessionCreator { pub core: Arc, } -impl ClusterSessionCreator for SchnorrSigningSessionCreator { +impl ClusterSessionCreator for SchnorrSigningSessionCreator { fn creation_data_from_message(message: &Message) -> Result, Error> { match *message { Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref message)) => match &message.message { @@ -271,10 +303,17 @@ impl ClusterSessionCreator for SchnorrSign })) } - fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionIdWithSubSession, requester: Option) -> Result, Error> { + fn create( + &self, + cluster: Arc, + master: NodeId, + nonce: Option, + id: SessionIdWithSubSession, + requester: Option, + ) -> Result, Error> { let encrypted_data = self.core.read_key_share(&id.id)?; let nonce = self.core.check_session_nonce(&master, nonce)?; - Ok(Arc::new(SchnorrSigningSessionImpl::new(SchnorrSigningSessionParams { + let (session, oneshot) = SchnorrSigningSessionImpl::new(SchnorrSigningSessionParams { meta: SessionMeta { id: id.id, self_node_id: self.core.self_node_id.clone(), @@ -288,7 +327,8 @@ impl ClusterSessionCreator for SchnorrSign acl_storage: self.core.acl_storage.clone(), cluster: cluster, nonce: nonce, - }, requester)?)) + }, requester)?; + Ok(WaitableSession::new(session, oneshot)) } } @@ -298,7 +338,7 @@ pub struct EcdsaSigningSessionCreator { pub core: Arc, } -impl ClusterSessionCreator for EcdsaSigningSessionCreator { +impl ClusterSessionCreator for EcdsaSigningSessionCreator { fn creation_data_from_message(message: &Message) -> Result, Error> { match *message { Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref message)) => match &message.message { @@ -319,10 +359,10 @@ impl ClusterSessionCreator for EcdsaSigningS })) } - fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionIdWithSubSession, requester: Option) -> Result, Error> { + fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionIdWithSubSession, requester: Option) -> Result, Error> { let encrypted_data = self.core.read_key_share(&id.id)?; let nonce = self.core.check_session_nonce(&master, nonce)?; - Ok(Arc::new(EcdsaSigningSessionImpl::new(EcdsaSigningSessionParams { + let (session, oneshot) = EcdsaSigningSessionImpl::new(EcdsaSigningSessionParams { meta: SessionMeta { id: id.id, self_node_id: self.core.self_node_id.clone(), @@ -336,7 +376,9 @@ impl ClusterSessionCreator for EcdsaSigningS acl_storage: self.core.acl_storage.clone(), cluster: cluster, nonce: nonce, - }, requester)?)) + }, requester)?; + + Ok(WaitableSession::new(session, oneshot)) } } @@ -346,7 +388,7 @@ pub struct KeyVersionNegotiationSessionCreator { pub core: Arc, } -impl ClusterSessionCreator, ()> for KeyVersionNegotiationSessionCreator { +impl ClusterSessionCreator> for KeyVersionNegotiationSessionCreator { fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { message::Message::KeyVersionNegotiation(message::KeyVersionNegotiationMessage::KeyVersionsError(message::KeyVersionsError { session: sid.id.into(), @@ -359,14 +401,21 @@ impl ClusterSessionCreator, master: NodeId, nonce: Option, id: SessionIdWithSubSession, _creation_data: Option<()>) -> Result>, Error> { + fn create( + &self, + cluster: Arc, + 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(), configured_nodes_count, configured_nodes_count)); - Ok(Arc::new(KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { + let (session, oneshot) = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { meta: ShareChangeSessionMeta { id: id.id.clone(), self_node_id: self.core.self_node_id.clone(), @@ -384,7 +433,8 @@ impl ClusterSessionCreator, /// Servers set change sessions creator connector. - pub servers_set_change_session_creator_connector: Arc, + pub servers_set_change_session_creator_connector: Arc, } -impl ClusterSessionCreator for AdminSessionCreator { +impl ClusterSessionCreator for AdminSessionCreator { fn creation_data_from_message(message: &Message) -> Result, Error> { match *message { Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref message)) => match &message.message { @@ -424,11 +474,18 @@ impl ClusterSessionCreator for AdminSess })) } - fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionId, creation_data: Option) -> Result, Error> { + fn create( + &self, + cluster: Arc, + master: NodeId, + nonce: Option, + id: SessionId, + creation_data: Option, + ) -> Result, Error> { let nonce = self.core.check_session_nonce(&master, nonce)?; - Ok(Arc::new(match creation_data { + match creation_data { Some(AdminSessionCreationData::ShareAdd(version)) => { - AdminSession::ShareAdd(ShareAddSessionImpl::new(ShareAddSessionParams { + let (session, oneshot) = ShareAddSessionImpl::new(ShareAddSessionParams { meta: ShareChangeSessionMeta { id: id.clone(), self_node_id: self.core.self_node_id.clone(), @@ -440,13 +497,14 @@ impl ClusterSessionCreator for AdminSess key_storage: self.core.key_storage.clone(), nonce: nonce, admin_public: Some(self.admin_public.clone().ok_or(Error::AccessDenied)?), - })?) + })?; + Ok(WaitableSession::new(AdminSession::ShareAdd(session), oneshot)) }, Some(AdminSessionCreationData::ServersSetChange(migration_id, new_nodes_set)) => { let admin_public = self.servers_set_change_session_creator_connector.admin_public(migration_id.as_ref(), new_nodes_set) .map_err(|_| Error::AccessDenied)?; - AdminSession::ServersSetChange(ServersSetChangeSessionImpl::new(ServersSetChangeSessionParams { + let (session, oneshot) = ServersSetChangeSessionImpl::new(ServersSetChangeSessionParams { meta: ShareChangeSessionMeta { id: id.clone(), self_node_id: self.core.self_node_id.clone(), @@ -460,10 +518,11 @@ impl ClusterSessionCreator for AdminSess all_nodes_set: cluster.nodes(), admin_public: admin_public, migration_id: migration_id, - })?) + })?; + Ok(WaitableSession::new(AdminSession::ServersSetChange(session), oneshot)) }, None => unreachable!("expected to call with non-empty creation data; qed"), - })) + } } } diff --git a/secret-store/src/key_server_cluster/connection_trigger.rs b/secret-store/src/key_server_cluster/connection_trigger.rs index 7b3649861f..db6ddab1e0 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,14 +19,14 @@ use std::collections::btree_map::Entry; use std::net::SocketAddr; use std::sync::Arc; use ethereum_types::H256; -use ethkey::Public; +use crypto::publickey::Public; use key_server_cluster::{KeyServerSet, KeyServerSetSnapshot}; use key_server_cluster::cluster::{ClusterConfiguration, ServersSetChangeParams}; use key_server_cluster::cluster_sessions::AdminSession; use key_server_cluster::cluster_connections::{Connection}; use key_server_cluster::cluster_connections_net::{NetConnectionsContainer}; use types::{Error, NodeId}; -use NodeKeyPair; +use blockchain::SigningKeyPair; #[derive(Debug, Clone, Copy, PartialEq)] /// Describes which maintain() call is required. @@ -52,7 +52,7 @@ pub trait ConnectionTrigger: Send + Sync { /// Maintain active connections. fn maintain_connections(&mut self, connections: &mut NetConnectionsContainer); /// Return connector for the servers set change session creator. - fn servers_set_change_creator_connector(&self) -> Arc; + fn servers_set_change_creator_connector(&self) -> Arc; } /// Servers set change session creator connector. @@ -67,11 +67,11 @@ pub trait ServersSetChangeSessionCreatorConnector: Send + Sync { /// Simple connection trigger, which only keeps connections to current_set. pub struct SimpleConnectionTrigger { /// Key server set cluster. - key_server_set: Arc, + key_server_set: Arc, /// Trigger connections. connections: TriggerConnections, /// Servers set change session creator connector. - connector: Arc, + connector: Arc, } /// Simple Servers set change session creator connector, which will just return @@ -93,7 +93,7 @@ pub enum ConnectionsAction { /// Trigger connections. pub struct TriggerConnections { /// This node key pair. - pub self_key_pair: Arc, + pub self_key_pair: Arc, } impl SimpleConnectionTrigger { @@ -103,7 +103,7 @@ impl SimpleConnectionTrigger { } /// Create new simple connection trigger. - pub fn new(key_server_set: Arc, self_key_pair: Arc, admin_public: Option) -> Self { + pub fn new(key_server_set: Arc, self_key_pair: Arc, admin_public: Option) -> Self { SimpleConnectionTrigger { key_server_set: key_server_set, connections: TriggerConnections { @@ -139,7 +139,7 @@ impl ConnectionTrigger for SimpleConnectionTrigger { self.connections.maintain(ConnectionsAction::ConnectToCurrentSet, connections, &self.key_server_set.snapshot()) } - fn servers_set_change_creator_connector(&self) -> Arc { + fn servers_set_change_creator_connector(&self) -> Arc { self.connector.clone() } } @@ -215,7 +215,7 @@ fn select_nodes_to_disconnect(current_set: &BTreeMap, new_se mod tests { use std::collections::BTreeSet; use std::sync::Arc; - use ethkey::{Random, Generator}; + use crypto::publickey::{Random, Generator}; use key_server_cluster::{MapKeyServerSet, PlainNodeKeyPair, KeyServerSetSnapshot, KeyServerSetMigration}; use key_server_cluster::cluster_connections_net::NetConnectionsContainer; use super::{Maintain, TriggerConnections, ConnectionsAction, ConnectionTrigger, SimpleConnectionTrigger, 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 559bab18c1..4c8bcc482f 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use std::collections::{BTreeSet, BTreeMap}; use std::net::SocketAddr; use std::sync::Arc; use ethereum_types::H256; -use ethkey::Public; +use crypto::publickey::Public; use parking_lot::Mutex; use key_server_cluster::{KeyServerSet, KeyServerSetSnapshot, KeyServerSetMigration, is_migration_required}; use key_server_cluster::cluster::{ClusterConfiguration, ServersSetChangeParams}; @@ -28,14 +28,14 @@ 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::{Error, NodeId}; -use {NodeKeyPair}; +use blockchain::SigningKeyPair; /// Key servers set change trigger with automated migration procedure. pub struct ConnectionTriggerWithMigration { /// This node key pair. - self_key_pair: Arc, + self_key_pair: Arc, /// Key server set. - key_server_set: Arc, + key_server_set: Arc, /// Last server set state. snapshot: KeyServerSetSnapshot, /// Required connections action. @@ -105,9 +105,9 @@ struct TriggerSession { /// Servers set change session creator connector. connector: Arc, /// This node key pair. - self_key_pair: Arc, + self_key_pair: Arc, /// Key server set. - key_server_set: Arc, + key_server_set: Arc, } impl ConnectionTriggerWithMigration { @@ -117,7 +117,7 @@ impl ConnectionTriggerWithMigration { } /// Create new trigge with migration. - pub fn new(key_server_set: Arc, self_key_pair: Arc) -> Self { + pub fn new(key_server_set: Arc, self_key_pair: Arc) -> Self { let snapshot = key_server_set.snapshot(); let migration = snapshot.migration.clone(); @@ -142,7 +142,7 @@ impl ConnectionTriggerWithMigration { session_action: None, } } - + /// Actually do mainteinance. fn do_maintain(&mut self) -> Option { loop { @@ -203,7 +203,7 @@ impl ConnectionTrigger for ConnectionTriggerWithMigration { } } - fn servers_set_change_creator_connector(&self) -> Arc { + fn servers_set_change_creator_connector(&self) -> Arc { self.session.connector.clone() } } @@ -324,9 +324,10 @@ fn session_state(session: Option>) -> SessionState { session .and_then(|s| match s.as_servers_set_change() { Some(s) if !s.is_finished() => Some(SessionState::Active(s.migration_id().cloned())), - Some(s) => match s.wait() { - Ok(_) => Some(SessionState::Finished(s.migration_id().cloned())), - Err(_) => Some(SessionState::Failed(s.migration_id().cloned())), + Some(s) => match s.result() { + Some(Ok(_)) => Some(SessionState::Finished(s.migration_id().cloned())), + Some(Err(_)) => Some(SessionState::Failed(s.migration_id().cloned())), + None => unreachable!("s.is_finished() == true; when session is finished, result is available; qed"), }, None => None, }) @@ -452,58 +453,59 @@ mod tests { use key_server_cluster::connection_trigger::ConnectionsAction; use super::{MigrationState, SessionState, SessionAction, migration_state, maintain_session, maintain_connections, select_master_node}; + use ethereum_types::{H256, H512}; #[test] fn migration_state_is_idle_when_required_but_this_node_is_not_on_the_list() { - assert_eq!(migration_state(&1.into(), &KeyServerSetSnapshot { - current_set: vec![(2.into(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - new_set: vec![(3.into(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), + assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(3), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), migration: None, }), MigrationState::Idle); } #[test] fn migration_state_is_idle_when_sets_are_equal() { - assert_eq!(migration_state(&1.into(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - new_set: vec![(1.into(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), + assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), migration: None, }), MigrationState::Idle); } #[test] fn migration_state_is_idle_when_only_address_changes() { - assert_eq!(migration_state(&1.into(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), - new_set: vec![(1.into(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), + assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), migration: None, }), MigrationState::Idle); } #[test] fn migration_state_is_required_when_node_is_added() { - assert_eq!(migration_state(&1.into(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), - new_set: vec![(1.into(), "127.0.0.1:8080".parse().unwrap()), - (2.into(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), + assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), migration: None, }), MigrationState::Required); } #[test] fn migration_state_is_required_when_node_is_removed() { - assert_eq!(migration_state(&1.into(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8080".parse().unwrap()), - (2.into(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - new_set: vec![(1.into(), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), + assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), migration: None, }), MigrationState::Required); } #[test] fn migration_state_is_started_when_migration_is_some() { - assert_eq!(migration_state(&1.into(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), + assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), new_set: Default::default(), migration: Some(KeyServerSetMigration { id: Default::default(), @@ -517,35 +519,35 @@ mod tests { #[test] fn existing_master_is_selected_when_migration_has_started() { assert_eq!(select_master_node(&KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), - new_set: vec![(2.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), migration: Some(KeyServerSetMigration { - master: 3.into(), + master: H512::from_low_u64_be(3), ..Default::default() }), - }), &3.into()); + }), &H512::from_low_u64_be(3)); } #[test] fn persistent_master_is_selected_when_migration_has_not_started_yet() { assert_eq!(select_master_node(&KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8180".parse().unwrap()), - (2.into(), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), - new_set: vec![(2.into(), "127.0.0.1:8181".parse().unwrap()), - (4.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8180".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap()), + (H512::from_low_u64_be(4), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), migration: None, - }), &2.into()); + }), &H512::from_low_u64_be(2)); } #[test] fn new_master_is_selected_in_worst_case() { assert_eq!(select_master_node(&KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8180".parse().unwrap()), - (2.into(), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), - new_set: vec![(3.into(), "127.0.0.1:8181".parse().unwrap()), - (4.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8180".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(3), "127.0.0.1:8181".parse().unwrap()), + (H512::from_low_u64_be(4), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), migration: None, - }), &3.into()); + }), &H512::from_low_u64_be(3)); } #[test] @@ -574,29 +576,29 @@ mod tests { #[test] fn maintain_sessions_does_nothing_if_no_session_and_no_migration() { - assert_eq!(maintain_session(&1.into(), &Default::default(), &Default::default(), + assert_eq!(maintain_session(&H512::from_low_u64_be(1), &Default::default(), &Default::default(), MigrationState::Idle, SessionState::Idle), None); } #[test] fn maintain_session_does_nothing_when_migration_required_on_slave_node_and_no_session() { - assert_eq!(maintain_session(&2.into(), &vec![2.into()].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap()), - (2.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + assert_eq!(maintain_session(&H512::from_low_u64_be(2), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), migration: None, }, MigrationState::Required, SessionState::Idle), None); } #[test] fn maintain_session_does_nothing_when_migration_started_on_slave_node_and_no_session() { - assert_eq!(maintain_session(&2.into(), &vec![2.into()].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + assert_eq!(maintain_session(&H512::from_low_u64_be(2), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), new_set: Default::default(), migration: Some(KeyServerSetMigration { - master: 1.into(), - set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap()), - (2.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + master: H512::from_low_u64_be(1), + set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), ..Default::default() }), }, MigrationState::Started, SessionState::Idle), None); @@ -604,13 +606,13 @@ mod tests { #[test] fn maintain_session_does_nothing_when_migration_started_on_master_node_and_no_session_and_not_connected_to_migration_nodes() { - assert_eq!(maintain_session(&1.into(), &Default::default(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + assert_eq!(maintain_session(&H512::from_low_u64_be(1), &Default::default(), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), new_set: Default::default(), migration: Some(KeyServerSetMigration { - master: 1.into(), - set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap()), - (2.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + master: H512::from_low_u64_be(1), + set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), ..Default::default() }), }, MigrationState::Started, SessionState::Idle), None); @@ -618,13 +620,13 @@ mod tests { #[test] fn maintain_session_starts_session_when_migration_started_on_master_node_and_no_session() { - assert_eq!(maintain_session(&1.into(), &vec![2.into()].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), new_set: Default::default(), migration: Some(KeyServerSetMigration { - master: 1.into(), - set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap()), - (2.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + master: H512::from_low_u64_be(1), + set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), ..Default::default() }), }, MigrationState::Started, SessionState::Idle), Some(SessionAction::Start)); @@ -632,13 +634,13 @@ mod tests { #[test] fn maintain_session_does_nothing_when_both_migration_and_session_are_started() { - assert_eq!(maintain_session(&1.into(), &vec![2.into()].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), new_set: Default::default(), migration: Some(KeyServerSetMigration { - master: 1.into(), - set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap()), - (2.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + master: H512::from_low_u64_be(1), + set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), ..Default::default() }), }, MigrationState::Started, SessionState::Active(Default::default())), None); @@ -646,13 +648,13 @@ mod tests { #[test] fn maintain_session_confirms_migration_when_active_and_session_has_finished_on_new_node() { - assert_eq!(maintain_session(&1.into(), &vec![2.into()].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), new_set: Default::default(), migration: Some(KeyServerSetMigration { - master: 1.into(), - set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap()), - (2.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + master: H512::from_low_u64_be(1), + set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), ..Default::default() }), }, MigrationState::Started, SessionState::Finished(Default::default())), Some(SessionAction::ConfirmAndDrop(Default::default()))); @@ -660,13 +662,13 @@ mod tests { #[test] fn maintain_session_drops_session_when_active_and_session_has_finished_on_removed_node() { - assert_eq!(maintain_session(&1.into(), &vec![2.into()].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap()), - (2.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), new_set: Default::default(), migration: Some(KeyServerSetMigration { - master: 2.into(), - set: vec![(2.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + master: H512::from_low_u64_be(2), + set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), ..Default::default() }), }, MigrationState::Started, SessionState::Finished(Default::default())), Some(SessionAction::Drop)); @@ -674,13 +676,13 @@ mod tests { #[test] fn maintain_session_drops_session_when_active_and_session_has_failed() { - assert_eq!(maintain_session(&1.into(), &vec![2.into()].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { + current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), new_set: Default::default(), migration: Some(KeyServerSetMigration { - master: 1.into(), - set: vec![(1.into(), "127.0.0.1:8181".parse().unwrap()), - (2.into(), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), + master: H512::from_low_u64_be(1), + set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), + (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), ..Default::default() }), }, MigrationState::Started, SessionState::Failed(Default::default())), Some(SessionAction::Drop)); @@ -726,32 +728,32 @@ mod tests { fn maintain_session_detects_abnormal_when_active_migration_and_active_session_with_different_id() { assert_eq!(maintain_session(&Default::default(), &Default::default(), &KeyServerSetSnapshot { migration: Some(KeyServerSetMigration { - id: 0.into(), + id: H256::zero(), ..Default::default() }), ..Default::default() - }, MigrationState::Started, SessionState::Active(Some(1.into()))), Some(SessionAction::DropAndRetry)); + }, MigrationState::Started, SessionState::Active(Some(H256::from_low_u64_be(1)))), Some(SessionAction::DropAndRetry)); } #[test] fn maintain_session_detects_abnormal_when_active_migration_and_finished_session_with_different_id() { assert_eq!(maintain_session(&Default::default(), &Default::default(), &KeyServerSetSnapshot { migration: Some(KeyServerSetMigration { - id: 0.into(), + id: H256::zero(), ..Default::default() }), ..Default::default() - }, MigrationState::Started, SessionState::Finished(Some(1.into()))), Some(SessionAction::DropAndRetry)); + }, MigrationState::Started, SessionState::Finished(Some(H256::from_low_u64_be(1)))), Some(SessionAction::DropAndRetry)); } #[test] fn maintain_session_detects_abnormal_when_active_migration_and_failed_session_with_different_id() { assert_eq!(maintain_session(&Default::default(), &Default::default(), &KeyServerSetSnapshot { migration: Some(KeyServerSetMigration { - id: 0.into(), + id: H256::zero(), ..Default::default() }), ..Default::default() - }, MigrationState::Started, SessionState::Failed(Some(1.into()))), Some(SessionAction::DropAndRetry)); + }, MigrationState::Started, SessionState::Failed(Some(H256::from_low_u64_be(1)))), Some(SessionAction::DropAndRetry)); } } diff --git a/secret-store/src/key_server_cluster/io/deadline.rs b/secret-store/src/key_server_cluster/io/deadline.rs index 7c08932578..cb960a514c 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ use std::time::Duration; use futures::{Future, Poll}; use tokio::timer::timeout::{Timeout, Error as TimeoutError}; -type DeadlineBox = Box = Box::Item>, Error = TimeoutError<::Error> > + Send>; diff --git a/secret-store/src/key_server_cluster/io/handshake.rs b/secret-store/src/key_server_cluster/io/handshake.rs index f378cba098..2e7cbd14e4 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -37,23 +37,24 @@ use std::sync::Arc; use std::collections::BTreeSet; use futures::{Future, Poll, Async}; use tokio_io::{AsyncRead, AsyncWrite}; -use ethkey::crypto::ecdh::agree; -use ethkey::{Random, Generator, KeyPair, Public, Signature, verify_public, sign, recover}; +use crypto::publickey::ecdh::agree; +use crypto::publickey::{Random, Generator, KeyPair, Public, Signature, verify_public, sign, recover}; use ethereum_types::H256; -use key_server_cluster::{NodeId, Error, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{NodeId, Error}; use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; use key_server_cluster::io::{write_message, write_encrypted_message, WriteMessage, ReadMessage, read_message, read_encrypted_message, fix_shared_key}; /// Start handshake procedure with another node from the cluster. -pub fn handshake(a: A, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn handshake(a: A, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { let init_data = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into) .and_then(|cp| Random.generate().map(|kp| (cp, kp)).map_err(Into::into)); handshake_with_init_data(a, init_data, self_key_pair, trusted_nodes) } /// Start handshake procedure with another node from the cluster and given plain confirmation + session key pair. -pub fn handshake_with_init_data(a: A, init_data: Result<(H256, KeyPair), Error>, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn handshake_with_init_data(a: A, init_data: Result<(H256, KeyPair), Error>, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { let handshake_input_data = init_data .and_then(|(cp, kp)| sign(kp.secret(), &cp).map(|sp| (cp, kp, sp)).map_err(Into::into)) .and_then(|(cp, kp, sp)| Handshake::::make_public_key_message(self_key_pair.public().clone(), cp.clone(), sp).map(|msg| (cp, kp, msg))); @@ -79,7 +80,7 @@ pub fn handshake_with_init_data(a: A, init_data: Result<(H256, KeyPair), Erro } /// Wait for handshake procedure to be started by another node from the cluster. -pub fn accept_handshake(a: A, self_key_pair: Arc) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn accept_handshake(a: A, self_key_pair: Arc) -> Handshake where A: AsyncWrite + AsyncRead { let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into); let handshake_input_data = self_confirmation_plain .and_then(|cp| Random.generate().map(|kp| (cp, kp)).map_err(Into::into)); @@ -118,7 +119,7 @@ pub struct Handshake { is_active: bool, error: Option<(A, Result)>, state: HandshakeState, - self_key_pair: Arc, + self_key_pair: Arc, self_session_key_pair: Option, self_confirmation_plain: H256, trusted_nodes: Option>, @@ -156,7 +157,7 @@ impl Handshake where A: AsyncRead + AsyncWrite { }))) } - fn make_private_key_signature_message(self_key_pair: &NodeKeyPair, confirmation_plain: &H256) -> Result { + fn make_private_key_signature_message(self_key_pair: &dyn SigningKeyPair, confirmation_plain: &H256) -> Result { Ok(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature { confirmation_signed: self_key_pair.sign(confirmation_plain)?.into(), }))) @@ -317,7 +318,7 @@ mod tests { use std::sync::Arc; use std::collections::BTreeSet; use futures::Future; - use ethkey::{Random, Generator, sign}; + use crypto::publickey::{Random, Generator, sign}; use ethereum_types::H256; use key_server_cluster::PlainNodeKeyPair; use key_server_cluster::io::message::tests::TestIo; diff --git a/secret-store/src/key_server_cluster/io/message.rs b/secret-store/src/key_server_cluster/io/message.rs index 0ce2b7c01f..34ed722fed 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,10 +19,10 @@ use std::u16; use std::ops::Deref; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use serde_json; -use ethkey::crypto::ecies; -use ethkey::{Secret, KeyPair}; -use ethkey::math::curve_order; -use ethereum_types::{H256, U256}; +use crypto::publickey::ecies; +use crypto::publickey::{Secret, KeyPair}; +use crypto::publickey::ec_math_utils::CURVE_ORDER; +use ethereum_types::{H256, U256, BigEndianHash}; use key_server_cluster::Error; use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, SchnorrSigningMessage, EcdsaSigningMessage, ServersSetChangeMessage, ShareAddMessage, KeyVersionNegotiationMessage}; @@ -257,9 +257,9 @@ pub fn fix_shared_key(shared_secret: &Secret) -> Result { // secret key created in agree function is invalid, as it is not calculated mod EC.field.n // => let's do it manually let shared_secret: H256 = (**shared_secret).into(); - let shared_secret: U256 = shared_secret.into(); - let shared_secret: H256 = (shared_secret % curve_order()).into(); - let shared_key_pair = KeyPair::from_secret_slice(&*shared_secret)?; + let shared_secret: U256 = shared_secret.into_uint(); + let shared_secret: H256 = BigEndianHash::from_uint(&(shared_secret % *CURVE_ORDER)); + let shared_key_pair = KeyPair::from_secret_slice(shared_secret.as_bytes())?; Ok(shared_key_pair) } @@ -305,8 +305,8 @@ pub mod tests { use std::io; use futures::Poll; use tokio_io::{AsyncRead, AsyncWrite}; - use ethkey::{Random, Generator, KeyPair}; - use ethkey::crypto::ecdh::agree; + use crypto::publickey::{Random, Generator, KeyPair}; + use crypto::publickey::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 c1cfe55665..0b5e71144e 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify 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 3806537ebf..c484921b7d 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify 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 e16de57a36..18402c27e9 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ use std::io; use futures::{Poll, Future, Async}; use tokio_io::AsyncRead; -use ethkey::KeyPair; +use crypto::publickey::KeyPair; use key_server_cluster::Error; use key_server_cluster::message::Message; use key_server_cluster::io::{read_header, ReadHeader, read_payload, read_encrypted_payload, ReadPayload}; 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 9f3a47f662..fee466c533 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use std::io; use futures::{Poll, Future}; use tokio_io::AsyncRead; use tokio_io::io::{read_exact, ReadExact}; -use ethkey::KeyPair; +use crypto::publickey::KeyPair; use key_server_cluster::Error; use key_server_cluster::message::Message; use key_server_cluster::io::message::{MessageHeader, deserialize_message, decrypt_message}; 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 99d6e4ca78..a6a533a87d 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify 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 15823730a2..05259eb3a9 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use std::io; use futures::{Future, Poll}; use tokio_io::AsyncWrite; use tokio_io::io::{WriteAll, write_all}; -use ethkey::KeyPair; +use crypto::publickey::KeyPair; use key_server_cluster::message::Message; use key_server_cluster::io::{serialize_message, encrypt_message}; 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 7daf05d3b4..258348edca 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -354,7 +354,7 @@ impl Result<(), Error> { let consensus_result = match message { - + &ConsensusMessage::InitializeConsensusSession(ref message) => self.consensus_job.on_partial_request(sender, message.requester.clone().into()).map(|_| ()), &ConsensusMessage::ConfirmConsensusInitialization(ref message) => @@ -367,7 +367,7 @@ impl None, - Some(decrypt_shadow) => Some(encrypt(&self.requester, &DEFAULT_MAC, &**decrypt_shadow)?), + Some(decrypt_shadow) => Some(encrypt(&self.requester, &DEFAULT_MAC, decrypt_shadow.as_bytes())?), }, })) } 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 122903eb60..834d0de7d8 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify 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 d76f8addaf..d711098121 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -392,7 +392,7 @@ fn consensus_unreachable(rejects: &BTreeMap) -> Error { pub mod tests { use std::collections::{VecDeque, BTreeMap, BTreeSet}; use parking_lot::Mutex; - use ethkey::Public; + use crypto::publickey::Public; use key_server_cluster::{Error, NodeId, SessionId, SessionMeta}; use super::{JobPartialResponseAction, JobPartialRequestAction, JobExecutor, JobTransport, JobSession, JobSessionState}; @@ -434,12 +434,12 @@ 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_low_u64_be(1), self_node_id: NodeId::from_low_u64_be(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_low_u64_be(1), self_node_id: NodeId::from_low_u64_be(2), threshold: threshold, configured_nodes_count: 5, connected_nodes_count: 5 } } @@ -447,27 +447,27 @@ pub mod tests { 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); + assert_eq!(job.initialize(vec![Public::from_low_u64_be(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); + assert_eq!(job.initialize(vec![Public::from_low_u64_be(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(), None, false).unwrap(); - assert_eq!(job.initialize(vec![Public::from(1)].into_iter().collect(), None, false).unwrap_err(), Error::InvalidStateForRequest); + job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap(); + assert_eq!(job.initialize(vec![Public::from_low_u64_be(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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Finished); assert!(job.is_result_ready()); assert_eq!(job.result(), Ok(4)); @@ -476,77 +476,77 @@ 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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); } #[test] fn job_request_fails_if_comes_from_non_master_node() { let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_partial_request(&NodeId::from(3), 2).unwrap_err(), Error::InvalidMessage); + assert_eq!(job.on_partial_request(&NodeId::from_low_u64_be(3), 2).unwrap_err(), Error::InvalidMessage); } #[test] fn job_request_fails_if_comes_to_master_node() { let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_partial_request(&NodeId::from(1), 2).unwrap_err(), Error::InvalidMessage); + assert_eq!(job.on_partial_request(&NodeId::from_low_u64_be(1), 2).unwrap_err(), Error::InvalidMessage); } #[test] fn job_request_fails_if_comes_to_failed_state() { let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); job.on_session_timeout().unwrap_err(); - assert_eq!(job.on_partial_request(&NodeId::from(1), 2).unwrap_err(), Error::InvalidStateForRequest); + assert_eq!(job.on_partial_request(&NodeId::from_low_u64_be(1), 2).unwrap_err(), Error::InvalidStateForRequest); } #[test] fn job_request_succeeds_if_comes_to_finished_state() { let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.on_partial_request(&NodeId::from(1), 2).unwrap(); - assert_eq!(job.transport().response(), (NodeId::from(1), 4)); + job.on_partial_request(&NodeId::from_low_u64_be(1), 2).unwrap(); + assert_eq!(job.transport().response(), (NodeId::from_low_u64_be(1), 4)); assert_eq!(job.state(), JobSessionState::Finished); - job.on_partial_request(&NodeId::from(1), 3).unwrap(); - assert_eq!(job.transport().response(), (NodeId::from(1), 9)); + job.on_partial_request(&NodeId::from_low_u64_be(1), 3).unwrap(); + assert_eq!(job.transport().response(), (NodeId::from_low_u64_be(1), 9)); assert_eq!(job.state(), JobSessionState::Finished); } #[test] fn job_response_fails_if_comes_to_slave_node() { let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_partial_response(&NodeId::from(1), 2).unwrap_err(), Error::InvalidMessage); + assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(1), 2).unwrap_err(), Error::InvalidMessage); } #[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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(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); + assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(2), 2).unwrap_err(), Error::InvalidStateForRequest); } #[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(), None, false).unwrap(); - assert_eq!(job.on_partial_response(&NodeId::from(3), 2).unwrap_err(), Error::InvalidNodeForRequest); + job.initialize(vec![Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); + assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(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.on_partial_response(&NodeId::from_low_u64_be(2), 3).unwrap_err(), Error::ConsensusUnreachable); assert_eq!(job.state(), JobSessionState::Failed); } #[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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(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(); + job.on_partial_response(&NodeId::from_low_u64_be(2), 2).unwrap(); assert_eq!(job.state(), JobSessionState::Active); assert!(!job.is_result_ready()); } @@ -554,9 +554,9 @@ 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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - job.on_partial_response(&NodeId::from(2), 2).unwrap(); + job.on_partial_response(&NodeId::from_low_u64_be(2), 2).unwrap(); assert_eq!(job.state(), JobSessionState::Finished); } @@ -564,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), Error::AccessDenied).unwrap(); + job.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap(); assert_eq!(job.state(), JobSessionState::Inactive); } @@ -572,59 +572,59 @@ 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), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); + assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(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), Error::AccessDenied).unwrap(); + job.on_partial_response(&NodeId::from_low_u64_be(2), 3).unwrap(); + job.on_node_error(&NodeId::from_low_u64_be(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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - job.on_node_error(&NodeId::from(3), Error::AccessDenied).unwrap(); + job.on_node_error(&NodeId::from_low_u64_be(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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - job.on_node_error(&NodeId::from(3), Error::AccessDenied).unwrap(); + job.on_node_error(&NodeId::from_low_u64_be(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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - assert_eq!(job.on_node_error(&NodeId::from(2), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); + assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(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(), None, true).unwrap(); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, true).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - assert_eq!(job.transport().response(), (NodeId::from(2), 4)); + assert_eq!(job.transport().response(), (NodeId::from_low_u64_be(2), 4)); } #[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(), None, false).unwrap(); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); assert_eq!(job.state(), JobSessionState::Active); assert!(job.transport().is_empty_response()); } @@ -632,30 +632,30 @@ pub mod tests { #[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); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3), Public::from_low_u64_be(4)].into_iter().collect(), None, false).unwrap(); + job.on_node_error(&NodeId::from_low_u64_be(2), Error::NodeDisconnected).unwrap(); + assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(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); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3), Public::from_low_u64_be(4)].into_iter().collect(), None, false).unwrap(); + job.on_node_error(&NodeId::from_low_u64_be(2), Error::NodeDisconnected).unwrap(); + assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(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); + job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3), Public::from_low_u64_be(4)].into_iter().collect(), None, false).unwrap(); + job.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied).unwrap(); + assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(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); + assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(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 075d7320f2..4a30dd79f6 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -26,13 +26,13 @@ pub struct KeyAccessJob { /// Has key share? has_key_share: bool, /// ACL storage. - acl_storage: Arc, + acl_storage: Arc, /// Requester data. requester: Option, } impl KeyAccessJob { - pub fn new_on_slave(id: SessionId, acl_storage: Arc) -> Self { + pub fn new_on_slave(id: SessionId, acl_storage: Arc) -> Self { KeyAccessJob { id: id, has_key_share: true, @@ -41,7 +41,7 @@ impl KeyAccessJob { } } - pub fn new_on_master(id: SessionId, acl_storage: Arc, requester: Requester) -> Self { + pub fn new_on_master(id: SessionId, acl_storage: Arc, requester: Requester) -> Self { KeyAccessJob { id: id, has_key_share: true, @@ -76,7 +76,7 @@ impl JobExecutor for KeyAccessJob { if !self.has_key_share { return Ok(JobPartialRequestAction::Reject(false)); } - + self.requester = Some(partial_request.clone()); 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 543f3e1bbb..4e3b8c0d96 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify 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 ace5021a05..52e8c95dfb 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . use std::collections::{BTreeSet, BTreeMap}; -use ethkey::{Public, Signature, recover}; +use crypto::publickey::{Public, Signature, recover}; use tiny_keccak::Keccak; use key_server_cluster::{Error, NodeId, SessionId}; use key_server_cluster::message::{InitializeConsensusSessionWithServersSet, InitializeConsensusSessionOfShareAdd}; @@ -139,7 +139,7 @@ impl JobExecutor for ServersSetChangeAccessJob { pub fn ordered_nodes_hash(nodes: &BTreeSet) -> SessionId { let mut nodes_keccak = Keccak::new_keccak256(); for node in nodes { - nodes_keccak.update(&*node); + nodes_keccak.update(node.as_bytes()); } let mut nodes_keccak_value = [0u8; 32]; 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 0628b1e75c..ecb509ad1b 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . use std::collections::{BTreeSet, BTreeMap}; -use ethkey::{Public, Secret, Signature}; +use crypto::publickey::{Public, Secret, Signature}; use ethereum_types::H256; use key_server_cluster::{Error, NodeId, DocumentKeyShare}; use key_server_cluster::math; @@ -33,7 +33,7 @@ pub struct EcdsaSigningJob { nonce_public: Public, /// Request id. request_id: Option, - /// + /// ECDSA reversed-nonce coefficient inversed_nonce_coeff: Option, /// Message hash. message_hash: Option, @@ -43,7 +43,7 @@ pub struct EcdsaSigningJob { pub struct EcdsaPartialSigningRequest { /// Request id. pub id: Secret, - /// + /// ECDSA reversed-nonce coefficient pub inversed_nonce_coeff: Secret, /// Message hash to sign. pub message_hash: H256, 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 7e41dce47c..678d9f32a8 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . use std::collections::{BTreeSet, BTreeMap}; -use ethkey::{Public, Secret}; +use crypto::publickey::{Public, Secret}; use ethereum_types::H256; use key_server_cluster::{Error, NodeId, DocumentKeyShare}; use key_server_cluster::math; 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 33eca6583d..b9a366ee20 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,18 +24,18 @@ pub struct UnknownSessionsJob { /// Target node id. target_node_id: Option, /// Keys storage. - key_storage: Arc, + key_storage: Arc, } impl UnknownSessionsJob { - pub fn new_on_slave(key_storage: Arc) -> Self { + pub fn new_on_slave(key_storage: Arc) -> Self { UnknownSessionsJob { target_node_id: None, key_storage: key_storage, } } - pub fn new_on_master(key_storage: Arc, self_node_id: NodeId) -> Self { + pub fn new_on_master(key_storage: Arc, self_node_id: NodeId) -> Self { UnknownSessionsJob { target_node_id: Some(self_node_id), key_storage: key_storage, diff --git a/secret-store/src/key_server_cluster/math.rs b/secret-store/src/key_server_cluster/math.rs index 60d48b7f3c..d48fb9e918 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use ethkey::{Public, Secret, Signature, Random, Generator, math}; -use ethereum_types::{H256, U256}; +use crypto::publickey::{Public, Secret, Signature, Random, Generator, ec_math_utils}; +use ethereum_types::{H256, U256, BigEndianHash}; use hash::keccak; use key_server_cluster::Error; @@ -35,8 +35,8 @@ pub fn zero_scalar() -> Secret { /// Convert hash to EC scalar (modulo curve order). pub fn to_scalar(hash: H256) -> Result { - let scalar: U256 = hash.into(); - let scalar: H256 = (scalar % math::curve_order()).into(); + let scalar: U256 = hash.into_uint(); + let scalar: H256 = BigEndianHash::from_uint(&(scalar % *ec_math_utils::CURVE_ORDER)); let scalar = Secret::from(scalar.0); scalar.check_validity()?; Ok(scalar) @@ -54,19 +54,19 @@ pub fn generate_random_point() -> Result { /// Get X coordinate of point. fn public_x(public: &Public) -> H256 { - public[0..32].into() + H256::from_slice(&public.as_bytes()[0..32]) } /// Get Y coordinate of point. fn public_y(public: &Public) -> H256 { - public[32..64].into() + H256::from_slice(&public.as_bytes()[32..64]) } /// Compute publics sum. pub fn compute_public_sum<'a, I>(mut publics: I) -> Result where I: Iterator { let mut sum = publics.next().expect("compute_public_sum is called when there's at least one public; qed").clone(); while let Some(public) = publics.next() { - math::public_add(&mut sum, &public)?; + ec_math_utils::public_add(&mut sum, &public)?; } Ok(sum) } @@ -113,7 +113,7 @@ pub fn compute_shadow_mul<'a, I>(coeff: &Secret, self_secret: &Secret, mut other /// Update point by multiplying to random scalar pub fn update_random_point(point: &mut Public) -> Result<(), Error> { - Ok(math::public_mul_secret(point, &generate_random_scalar()?)?) + Ok(ec_math_utils::public_mul_secret(point, &generate_random_scalar()?)?) } /// Generate random polynom of threshold degree @@ -153,14 +153,14 @@ pub fn public_values_generation(threshold: usize, derived_point: &Public, polyno for i in 0..threshold + 1 { let coeff1 = &polynom1[i]; - let mut multiplication1 = math::generation_point(); - math::public_mul_secret(&mut multiplication1, &coeff1)?; + let mut multiplication1 = ec_math_utils::generation_point(); + ec_math_utils::public_mul_secret(&mut multiplication1, &coeff1)?; let coeff2 = &polynom2[i]; let mut multiplication2 = derived_point.clone(); - math::public_mul_secret(&mut multiplication2, &coeff2)?; + ec_math_utils::public_mul_secret(&mut multiplication2, &coeff2)?; - math::public_add(&mut multiplication1, &multiplication2)?; + ec_math_utils::public_add(&mut multiplication1, &multiplication2)?; publics.push(multiplication1); } @@ -172,13 +172,13 @@ pub fn public_values_generation(threshold: usize, derived_point: &Public, polyno /// Check keys passed by other participants. pub fn keys_verification(threshold: usize, derived_point: &Public, number_id: &Secret, secret1: &Secret, secret2: &Secret, publics: &[Public]) -> Result { // calculate left part - let mut multiplication1 = math::generation_point(); - math::public_mul_secret(&mut multiplication1, secret1)?; + let mut multiplication1 = ec_math_utils::generation_point(); + ec_math_utils::public_mul_secret(&mut multiplication1, secret1)?; let mut multiplication2 = derived_point.clone(); - math::public_mul_secret(&mut multiplication2, secret2)?; + ec_math_utils::public_mul_secret(&mut multiplication2, secret2)?; - math::public_add(&mut multiplication1, &multiplication2)?; + ec_math_utils::public_add(&mut multiplication1, &multiplication2)?; let left = multiplication1; // calculate right part @@ -188,9 +188,9 @@ pub fn keys_verification(threshold: usize, derived_point: &Public, number_id: &S secret_pow.pow(i)?; let mut public_k = publics[i].clone(); - math::public_mul_secret(&mut public_k, &secret_pow)?; + ec_math_utils::public_mul_secret(&mut public_k, &secret_pow)?; - math::public_add(&mut right, &public_k)?; + ec_math_utils::public_add(&mut right, &public_k)?; } Ok(left == right) @@ -213,8 +213,8 @@ pub fn compute_secret_share<'a, I>(secret_values: I) -> Result wh /// Compute public key share. pub fn compute_public_share(self_secret_value: &Secret) -> Result { - let mut public_share = math::generation_point(); - math::public_mul_secret(&mut public_share, self_secret_value)?; + let mut public_share = ec_math_utils::generation_point(); + ec_math_utils::public_mul_secret(&mut public_share, self_secret_value)?; Ok(public_share) } @@ -256,13 +256,13 @@ pub fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result(access_key: &Secret, common_point: joint_shadow.mul(access_key)?; let mut joint_shadow_point = common_point.clone(); - math::public_mul_secret(&mut joint_shadow_point, &joint_shadow)?; + ec_math_utils::public_mul_secret(&mut joint_shadow_point, &joint_shadow)?; Ok(joint_shadow_point) } @@ -318,13 +318,13 @@ pub fn decrypt_with_joint_shadow(threshold: usize, access_key: &Secret, encrypte inv_access_key.inv()?; let mut mul = joint_shadow_point.clone(); - math::public_mul_secret(&mut mul, &inv_access_key)?; + ec_math_utils::public_mul_secret(&mut mul, &inv_access_key)?; let mut decrypted_point = encrypted_point.clone(); if threshold % 2 != 0 { - math::public_add(&mut decrypted_point, &mul)?; + ec_math_utils::public_add(&mut decrypted_point, &mul)?; } else { - math::public_sub(&mut decrypted_point, &mul)?; + ec_math_utils::public_sub(&mut decrypted_point, &mul)?; } Ok(decrypted_point) @@ -335,7 +335,7 @@ pub fn make_common_shadow_point(threshold: usize, mut common_point: Public) -> R if threshold % 2 != 1 { Ok(common_point) } else { - math::public_negate(&mut common_point)?; + ec_math_utils::public_negate(&mut common_point)?; Ok(common_point) } } @@ -344,8 +344,8 @@ pub fn make_common_shadow_point(threshold: usize, mut common_point: Public) -> R #[cfg(test)] pub fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common_shadow_point: Public, shadow_coefficients: Vec) -> Result { let shadow_coefficients_sum = compute_secret_sum(shadow_coefficients.iter())?; - math::public_mul_secret(&mut common_shadow_point, &shadow_coefficients_sum)?; - math::public_add(&mut decrypted_shadow, &common_shadow_point)?; + ec_math_utils::public_mul_secret(&mut common_shadow_point, &shadow_coefficients_sum)?; + ec_math_utils::public_add(&mut decrypted_shadow, &common_shadow_point)?; Ok(decrypted_shadow) } @@ -353,10 +353,10 @@ pub fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common #[cfg(test)] pub fn decrypt_with_joint_secret(encrypted_point: &Public, common_point: &Public, joint_secret: &Secret) -> Result { let mut common_point_mul = common_point.clone(); - math::public_mul_secret(&mut common_point_mul, joint_secret)?; + ec_math_utils::public_mul_secret(&mut common_point_mul, joint_secret)?; let mut decrypted_point = encrypted_point.clone(); - math::public_sub(&mut decrypted_point, &common_point_mul)?; + ec_math_utils::public_sub(&mut decrypted_point, &common_point_mul)?; Ok(decrypted_point) } @@ -417,8 +417,8 @@ pub fn compute_schnorr_signature<'a, I>(signature_shares: I) -> Result Result<(Secret, Secret), Error> { - let mut nonce_public = math::generation_point(); - math::public_mul_secret(&mut nonce_public, &nonce).unwrap(); + let mut nonce_public = ec_math_utils::generation_point(); + ec_math_utils::public_mul_secret(&mut nonce_public, &nonce).unwrap(); let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; @@ -433,11 +433,11 @@ pub fn local_compute_schnorr_signature(nonce: &Secret, secret: &Secret, message_ /// Verify Schnorr signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Verifying. #[cfg(test)] pub fn verify_schnorr_signature(public: &Public, signature: &(Secret, Secret), message_hash: &H256) -> Result { - let mut addendum = math::generation_point(); - math::public_mul_secret(&mut addendum, &signature.1)?; + let mut addendum = ec_math_utils::generation_point(); + ec_math_utils::public_mul_secret(&mut addendum, &signature.1)?; let mut nonce_public = public.clone(); - math::public_mul_secret(&mut nonce_public, &signature.0)?; - math::public_add(&mut nonce_public, &addendum)?; + ec_math_utils::public_mul_secret(&mut nonce_public, &signature.0)?; + ec_math_utils::public_add(&mut nonce_public, &addendum)?; let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; Ok(combined_hash == signature.0) @@ -478,7 +478,7 @@ pub fn serialize_ecdsa_signature(nonce_public: &Public, signature_r: Secret, mut // compute recovery param let mut signature_v = { let nonce_public_x = public_x(nonce_public); - let nonce_public_y: U256 = public_y(nonce_public).into(); + let nonce_public_y: U256 = public_y(nonce_public).into_uint(); let nonce_public_y_is_odd = !(nonce_public_y % 2).is_zero(); let bit0 = if nonce_public_y_is_odd { 1u8 } else { 0u8 }; let bit1 = if nonce_public_x != *signature_r { 2u8 } else { 0u8 }; @@ -486,19 +486,18 @@ pub fn serialize_ecdsa_signature(nonce_public: &Public, signature_r: Secret, mut }; // fix high S - let curve_order = math::curve_order(); - let curve_order_half = curve_order / 2; - let s_numeric: U256 = (*signature_s).into(); + let curve_order_half = *ec_math_utils::CURVE_ORDER / 2; + let s_numeric: U256 = (*signature_s).into_uint(); if s_numeric > curve_order_half { - let signature_s_hash: H256 = (curve_order - s_numeric).into(); + let signature_s_hash: H256 = BigEndianHash::from_uint(&(*ec_math_utils::CURVE_ORDER - s_numeric)); signature_s = signature_s_hash.into(); signature_v ^= 1; } // serialize as [r][s]v let mut signature = [0u8; 65]; - signature[..32].copy_from_slice(&**signature_r); - signature[32..64].copy_from_slice(&**signature_s); + signature[..32].copy_from_slice(signature_r.as_bytes()); + signature[32..64].copy_from_slice(signature_s.as_bytes()); signature[64] = signature_v; signature.into() @@ -534,7 +533,7 @@ pub fn compute_ecdsa_inversed_secret_coeff_from_shares(t: usize, id_numbers: &[S #[cfg(test)] pub mod tests { use std::iter::once; - use ethkey::{KeyPair, recover, verify_public}; + use crypto::publickey::{KeyPair, recover, verify_public}; use super::*; #[derive(Clone)] diff --git a/secret-store/src/key_server_cluster/message.rs b/secret-store/src/key_server_cluster/message.rs index 4b850ec3d3..396c5a9938 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ use std::fmt; use std::collections::{BTreeSet, BTreeMap}; -use ethkey::Secret; +use crypto::publickey::Secret; use key_server_cluster::SessionId; use super::{Error, SerializableH256, SerializablePublic, SerializableSecret, SerializableSignature, SerializableMessageHash, SerializableRequester, SerializableAddress}; @@ -240,7 +240,7 @@ pub enum KeyVersionNegotiationMessage { pub struct NodePublicKey { /// Node identifier (aka node public key). pub node_id: MessageNodeId, - /// Random data, which must be signed by peer to prove that he owns the corresponding private key. + /// Random data, which must be signed by peer to prove that he owns the corresponding private key. pub confirmation_plain: SerializableH256, /// The same random `confirmation_plain`, signed with one-time session key. pub confirmation_signed_session: SerializableSignature, @@ -633,7 +633,7 @@ pub struct EcdsaRequestPartialSignature { pub session_nonce: u64, /// Request id. pub request_id: SerializableSecret, - /// + /// ECDSA reversed-nonce coefficient pub inversed_nonce_coeff: SerializableSecret, /// Message hash. pub message_hash: SerializableMessageHash, @@ -970,12 +970,8 @@ pub struct KeyShareCommon { pub session: MessageSessionId, /// Session-level nonce. pub session_nonce: u64, - /// Key threshold. - pub threshold: usize, - /// Author of key share entry. - pub author: SerializableAddress, - /// Joint public. - pub joint_public: SerializablePublic, + /// Common key data. + pub key_common: CommonKeyData, /// Common (shared) encryption point. pub common_point: Option, /// Encrypted point. @@ -1026,12 +1022,23 @@ pub struct KeyVersions { pub sub_session: SerializableSecret, /// Session-level nonce. pub session_nonce: u64, - /// Key threshold. - pub threshold: Option, + /// Common key data, shared by all versions. + pub key_common: Option, /// Key versions. pub versions: Vec, } +/// Common key data. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CommonKeyData { + /// Key threshold. + pub threshold: usize, + /// Author of the key entry. + pub author: SerializableAddress, + /// Joint public. + pub public: SerializablePublic, +} + /// When key versions error has occured. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct KeyVersionsError { diff --git a/secret-store/src/key_server_cluster/mod.rs b/secret-store/src/key_server_cluster/mod.rs index fc46e10318..7a264b70ae 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,7 +16,7 @@ use super::types::ServerKeyId; -pub use super::traits::NodeKeyPair; +pub use super::blockchain::SigningKeyPair; pub use super::types::{Error, NodeId, Requester, EncryptedDocumentKeyShadow}; pub use super::acl_storage::AclStorage; pub use super::key_storage::{KeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; @@ -25,7 +25,7 @@ pub use super::serialization::{SerializableSignature, SerializableH256, Serializ SerializableRequester, SerializableMessageHash, SerializableAddress}; pub use self::cluster::{new_network_cluster, ClusterCore, ClusterConfiguration, ClusterClient}; pub use self::cluster_connections_net::NetConnectionsManagerConfig; -pub use self::cluster_sessions::{ClusterSession, ClusterSessionsListener}; +pub use self::cluster_sessions::{ClusterSession, ClusterSessionsListener, WaitableSession}; #[cfg(test)] pub use self::cluster::tests::DummyClusterClient; 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 3b66fe1d7f..8ad2e952a2 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -20,12 +20,13 @@ use std::net::SocketAddr; use std::time::Duration; use futures::{Future, Poll}; use tokio::net::TcpStream; -use key_server_cluster::{Error, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::Error; use key_server_cluster::io::{accept_handshake, Handshake, Deadline, deadline}; use key_server_cluster::net::Connection; /// Create future for accepting incoming connection. -pub fn accept_connection(stream: TcpStream, self_key_pair: Arc) -> Deadline { +pub fn accept_connection(stream: TcpStream, self_key_pair: Arc) -> Deadline { // TODO: This could fail so it would be better either to accept the // address as a separate argument or return a result. let address = stream.peer_addr().expect("Unable to determine tcp peer address"); diff --git a/secret-store/src/key_server_cluster/net/connect.rs b/secret-store/src/key_server_cluster/net/connect.rs index 3c2cbc2693..532f0105b4 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,12 +21,13 @@ use std::time::Duration; use std::net::SocketAddr; use futures::{Future, Poll, Async}; use tokio::net::{TcpStream, tcp::ConnectFuture}; -use key_server_cluster::{Error, NodeId, NodeKeyPair}; +use blockchain::SigningKeyPair; +use key_server_cluster::{Error, NodeId}; use key_server_cluster::io::{handshake, Handshake, Deadline, deadline}; use key_server_cluster::net::Connection; /// Create future for connecting to other node. -pub fn connect(address: &SocketAddr, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Deadline { +pub fn connect(address: &SocketAddr, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Deadline { let connect = Connect { state: ConnectState::TcpConnect(TcpStream::connect(address)), address: address.clone(), @@ -47,7 +48,7 @@ enum ConnectState { pub struct Connect { state: ConnectState, address: SocketAddr, - self_key_pair: Arc, + self_key_pair: Arc, trusted_nodes: BTreeSet, } diff --git a/secret-store/src/key_server_cluster/net/connection.rs b/secret-store/src/key_server_cluster/net/connection.rs index 8688db2897..cf3306a886 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . use std::net; -use ethkey::KeyPair; +use crypto::publickey::KeyPair; use key_server_cluster::NodeId; use key_server_cluster::io::SharedTcpStream; diff --git a/secret-store/src/key_server_cluster/net/mod.rs b/secret-store/src/key_server_cluster/net/mod.rs index a040596af8..2ba3201d7f 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/secret-store/src/key_server_set.rs b/secret-store/src/key_server_set.rs index 5b25641aee..b4c5622ec3 100644 --- a/secret-store/src/key_server_set.rs +++ b/secret-store/src/key_server_set.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,15 +18,12 @@ use std::sync::Arc; use std::net::SocketAddr; use std::collections::{BTreeMap, HashSet}; use parking_lot::Mutex; -use call_contract::CallContract; use ethabi::FunctionOutputDecoder; -use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify, NewBlocks}; use ethereum_types::{H256, Address}; -use ethkey::public_to_address; +use crypto::publickey::public_to_address; use bytes::Bytes; use types::{Error, Public, NodeAddress, NodeId}; -use trusted_client::TrustedClient; -use {NodeKeyPair, ContractAddress}; +use blockchain::{SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, BlockId}; use_contract!(key_server, "res/key_server_set.json"); @@ -100,7 +97,7 @@ struct PreviousMigrationTransaction { /// Cached on-chain Key Server set contract. struct CachedContract { /// Blockchain client. - client: TrustedClient, + client: Arc, /// Contract address source. contract_address_source: Option, /// Current contract address. @@ -116,18 +113,15 @@ struct CachedContract { /// Previous confirm migration transaction. confirm_migration_tx: Option, /// This node key pair. - self_key_pair: Arc, + self_key_pair: Arc, } impl OnChainKeyServerSet { - 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(); + pub fn new(trusted_client: Arc, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result, Error> { let key_server_set = Arc::new(OnChainKeyServerSet { - contract: Mutex::new(CachedContract::new(trusted_client, contract_address_source, self_key_pair, auto_migrate_enabled, key_servers)?), + contract: Mutex::new(CachedContract::new(trusted_client.clone(), contract_address_source, self_key_pair, auto_migrate_enabled, key_servers)?), }); - client - .ok_or_else(|| Error::Internal("Constructing OnChainKeyServerSet without active Client".into()))? - .add_notify(key_server_set.clone()); + trusted_client.add_listener(key_server_set.clone()); Ok(key_server_set) } } @@ -150,14 +144,9 @@ impl KeyServerSet for OnChainKeyServerSet { } } -impl ChainNotify for OnChainKeyServerSet { - fn new_blocks(&self, new_blocks: NewBlocks) { - if new_blocks.has_more_blocks_to_import { return } - let (enacted, retracted) = new_blocks.route.into_enacted_retracted(); - - if !enacted.is_empty() || !retracted.is_empty() { - self.contract.lock().update(enacted, retracted) - } +impl NewBlocksNotify for OnChainKeyServerSet { + fn new_blocks(&self, _new_enacted_len: usize) { + self.contract.lock().update() } } @@ -227,7 +216,7 @@ impl ) -> Result, String>> KeyServerSubset for NewKeySe } impl CachedContract { - pub fn new(client: TrustedClient, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result { + pub fn new(client: Arc, 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)| { @@ -261,7 +250,10 @@ impl CachedContract { 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); + let contract_address = self.client.read_contract_address( + KEY_SERVER_SET_CONTRACT_REGISTRY_NAME, + 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); @@ -271,21 +263,19 @@ impl CachedContract { } } - pub fn update(&mut self, enacted: Vec, retracted: Vec) { + pub fn update(&mut self) { // 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) - if !enacted.is_empty() || !retracted.is_empty() { - self.update_contract_address(); - self.read_from_registry(&*client); - } + if self.client.is_trusted() { + // read new snapshot from reqistry + self.update_contract_address(); + self.read_from_registry(); // update number of confirmations (if there's future new set) - self.update_number_of_confirmations_if_required(&*client); + self.update_number_of_confirmations_if_required(); } } @@ -299,9 +289,9 @@ impl CachedContract { fn start_migration(&mut self, migration_id: H256) { // trust is not needed here, because it is the reaction to the read of the trusted client - if let (Some(client), Some(contract_address)) = (self.client.get_untrusted(), self.contract_address.as_ref()) { + if let Some(contract_address) = self.contract_address.as_ref() { // check if we need to send start migration transaction - if !update_last_transaction_block(&*client, &migration_id, &mut self.start_migration_tx) { + if !update_last_transaction_block(&*self.client, &migration_id, &mut self.start_migration_tx) { return; } @@ -320,9 +310,9 @@ impl CachedContract { fn confirm_migration(&mut self, migration_id: H256) { // trust is not needed here, because we have already completed the action - if let (Some(client), Some(contract_address)) = (self.client.get(), self.contract_address) { + if let (true, Some(contract_address)) = (self.client.is_trusted(), self.contract_address) { // check if we need to send start migration transaction - if !update_last_transaction_block(&*client, &migration_id, &mut self.confirm_migration_tx) { + if !update_last_transaction_block(&*self.client, &migration_id, &mut self.confirm_migration_tx) { return; } @@ -339,7 +329,7 @@ impl CachedContract { } } - fn read_from_registry(&mut self, client: &Client) { + fn read_from_registry(&mut self) { let contract_address = match self.contract_address { Some(contract_address) => contract_address, None => { @@ -353,7 +343,7 @@ impl CachedContract { }, }; - let do_call = |data| client.call_contract(BlockId::Latest, contract_address, data); + let do_call = |data| self.client.call_contract(BlockId::Latest, contract_address, data); let current_set = Self::read_key_server_set(CurrentKeyServerSubset, &do_call); @@ -426,7 +416,7 @@ impl CachedContract { // we might want to adjust new_set if auto migration is enabled if self.auto_migrate_enabled { - let block = client.block_hash(BlockId::Latest).unwrap_or_default(); + let block = self.client.block_hash(BlockId::Latest).unwrap_or_default(); update_future_set(&mut self.future_new_set, &mut new_snapshot, block); } @@ -466,14 +456,14 @@ impl CachedContract { key_servers } - fn update_number_of_confirmations_if_required(&mut self, client: &BlockChainClient) { + fn update_number_of_confirmations_if_required(&mut self) { if !self.auto_migrate_enabled { return; } - + let client = &*self.client; update_number_of_confirmations( - &|| latest_block_hash(&*client), - &|block| block_confirmations(&*client, block), + &|| latest_block_hash(client), + &|block| block_confirmations(client, block), &mut self.future_new_set, &mut self.snapshot); } } @@ -541,7 +531,7 @@ fn update_number_of_confirmations H256, F2: Fn(H256) -> Option> snapshot.new_set = future_new_set.new_set; } -fn update_last_transaction_block(client: &Client, migration_id: &H256, previous_transaction: &mut Option) -> bool { +fn update_last_transaction_block(client: &dyn SecretStoreChain, migration_id: &H256, previous_transaction: &mut Option) -> bool { let last_block = client.block_number(BlockId::Latest).unwrap_or_default(); match previous_transaction.as_ref() { // no previous transaction => send immediately @@ -569,11 +559,11 @@ fn update_last_transaction_block(client: &Client, migration_id: &H256, previous_ true } -fn latest_block_hash(client: &BlockChainClient) -> H256 { +fn latest_block_hash(client: &dyn SecretStoreChain) -> H256 { client.block_hash(BlockId::Latest).unwrap_or_default() } -fn block_confirmations(client: &BlockChainClient, block: H256) -> Option { +fn block_confirmations(client: &dyn SecretStoreChain, block: H256) -> Option { client.block_number(BlockId::Hash(block)) .and_then(|block| client.block_number(BlockId::Latest).map(|last_block| (block, last_block))) .map(|(block, last_block)| last_block - block) @@ -583,8 +573,8 @@ fn block_confirmations(client: &BlockChainClient, block: H256) -> Option { pub mod tests { use std::collections::BTreeMap; use std::net::SocketAddr; - use ethereum_types::H256; - use ethkey::Public; + use ethereum_types::{H256, H512}; + use crypto::publickey::Public; use super::{update_future_set, update_number_of_confirmations, FutureNewSet, KeyServerSet, KeyServerSetSnapshot, MIGRATION_CONFIRMATIONS_REQUIRED}; @@ -675,18 +665,18 @@ pub mod tests { let mut future_new_set = None; let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(2.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), ..Default::default() }; update_future_set(&mut future_new_set, &mut new_snapshot, Default::default()); assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(2.into(), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), block: Default::default(), })); assert_eq!(new_snapshot, KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(1.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), ..Default::default() }); } @@ -696,22 +686,22 @@ pub mod tests { let address = "127.0.0.1:12000".parse().unwrap(); let mut future_new_set = Some(FutureNewSet { - new_set: vec![(2.into(), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), block: Default::default(), }); let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(3.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(3), address)].into_iter().collect(), ..Default::default() }; - update_future_set(&mut future_new_set, &mut new_snapshot, 1.into()); + update_future_set(&mut future_new_set, &mut new_snapshot, H256::from_low_u64_be(1)); assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(3.into(), address)].into_iter().collect(), - block: 1.into(), + new_set: vec![(H512::from_low_u64_be(3), address)].into_iter().collect(), + block: H256::from_low_u64_be(1), })); assert_eq!(new_snapshot, KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(1.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), ..Default::default() }); } @@ -721,22 +711,22 @@ pub mod tests { let address = "127.0.0.1:12000".parse().unwrap(); let mut future_new_set = Some(FutureNewSet { - new_set: vec![(2.into(), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), block: Default::default(), }); let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(2.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), ..Default::default() }; - update_future_set(&mut future_new_set, &mut new_snapshot, 1.into()); + update_future_set(&mut future_new_set, &mut new_snapshot, H256::from_low_u64_be(1)); assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(2.into(), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), block: Default::default(), })); assert_eq!(new_snapshot, KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(1.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), ..Default::default() }); } @@ -747,13 +737,13 @@ pub mod tests { let mut future_new_set = None; let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(1.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), ..Default::default() }; let snapshot_copy = snapshot.clone(); update_number_of_confirmations( - &|| 1.into(), + &|| H256::from_low_u64_be(1), &|_| Some(MIGRATION_CONFIRMATIONS_REQUIRED), &mut future_new_set, &mut snapshot); assert_eq!(future_new_set, None); @@ -765,22 +755,22 @@ pub mod tests { let address = "127.0.0.1:12000".parse().unwrap(); let mut future_new_set = Some(FutureNewSet { - new_set: vec![(2.into(), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), block: Default::default(), }); let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(1.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), ..Default::default() }; update_number_of_confirmations( - &|| 1.into(), + &|| H256::from_low_u64_be(1), &|_| Some(MIGRATION_CONFIRMATIONS_REQUIRED), &mut future_new_set, &mut snapshot); assert_eq!(future_new_set, None); assert_eq!(snapshot, KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(2.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), ..Default::default() }); } @@ -790,18 +780,18 @@ pub mod tests { let address = "127.0.0.1:12000".parse().unwrap(); let mut future_new_set = Some(FutureNewSet { - new_set: vec![(2.into(), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), block: Default::default(), }); let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(1.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), ..Default::default() }; let future_new_set_copy = future_new_set.clone(); let snapshot_copy = snapshot.clone(); update_number_of_confirmations( - &|| 1.into(), + &|| H256::from_low_u64_be(1), &|_| Some(MIGRATION_CONFIRMATIONS_REQUIRED - 1), &mut future_new_set, &mut snapshot); assert_eq!(future_new_set, future_new_set_copy); @@ -813,22 +803,22 @@ pub mod tests { let address = "127.0.0.1:12000".parse().unwrap(); let mut future_new_set = Some(FutureNewSet { - new_set: vec![(2.into(), address)].into_iter().collect(), - block: 1.into(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), + block: H256::from_low_u64_be(1), }); let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(1.into(), address)].into_iter().collect(), - new_set: vec![(1.into(), address)].into_iter().collect(), + current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), + new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), ..Default::default() }; let snapshot_copy = snapshot.clone(); update_number_of_confirmations( - &|| 2.into(), + &|| H256::from_low_u64_be(2), &|_| None, &mut future_new_set, &mut snapshot); assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(2.into(), address)].into_iter().collect(), - block: 2.into(), + new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), + block: H256::from_low_u64_be(2), })); assert_eq!(snapshot, snapshot_copy); } diff --git a/secret-store/src/key_storage.rs b/secret-store/src/key_storage.rs index 36edae43ed..c5d082b440 100644 --- a/secret-store/src/key_storage.rs +++ b/secret-store/src/key_storage.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,23 +19,13 @@ use std::sync::Arc; use serde_json; use tiny_keccak::Keccak; use ethereum_types::{H256, Address}; -use ethkey::{Secret, Public, public_to_address}; +use crypto::publickey::{Secret, Public}; use kvdb::KeyValueDB; use types::{Error, ServerKeyId, NodeId}; use serialization::{SerializablePublic, SerializableSecret, SerializableH256, SerializableAddress}; -/// Key of version value. -const DB_META_KEY_VERSION: &'static [u8; 7] = b"version"; -/// Current db version. -const CURRENT_VERSION: u8 = 3; -/// Current type of serialized key shares. -type CurrentSerializableDocumentKeyShare = SerializableDocumentKeyShareV3; -/// Current type of serialized key shares versions. -type CurrentSerializableDocumentKeyVersion = SerializableDocumentKeyShareVersionV3; - /// Encrypted key share, stored by key storage on the single key server. -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(test, derive(Default))] +#[derive(Debug, Default, Clone, PartialEq)] pub struct DocumentKeyShare { /// Author of the entry. pub author: Address, @@ -77,56 +67,24 @@ pub trait KeyStorage: Send + Sync { /// Check if storage contains document encryption key fn contains(&self, document: &ServerKeyId) -> bool; /// Iterate through storage - fn iter<'a>(&'a self) -> Box + 'a>; + fn iter<'a>(&'a self) -> Box + 'a>; } /// Persistent document encryption keys storage pub struct PersistentKeyStorage { - db: Arc, + db: Arc, } /// Persistent document encryption keys storage iterator pub struct PersistentKeyStorageIterator<'a> { - iter: Box, Box<[u8]>)> + 'a>, + 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)] -pub struct SerializableDocumentKeyShareV0 { - /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). - pub threshold: usize, - /// Nodes ids numbers. - pub id_numbers: BTreeMap, - /// Node secret share. - pub secret_share: SerializableSecret, - /// Common (shared) encryption point. - pub common_point: SerializablePublic, - /// Encrypted point. - pub encrypted_point: SerializablePublic, -} - -/// V1 of encrypted key share, as it is stored by key storage on the single key server. -#[derive(Serialize, Deserialize)] -struct SerializableDocumentKeyShareV1 { - /// Author of the entry. - pub author: SerializablePublic, - /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). - pub threshold: usize, - /// Nodes ids numbers. - pub id_numbers: BTreeMap, - /// Node secret share. - pub secret_share: SerializableSecret, - /// Common (shared) encryption point. - pub common_point: Option, - /// Encrypted point. - pub encrypted_point: Option, -} - -/// V2 of encrypted key share, as it is stored by key storage on the single key server. +/// V3 of encrypted key share, as it is stored by key storage on the single key server. #[derive(Serialize, Deserialize)] -struct SerializableDocumentKeyShareV2 { +struct SerializableDocumentKeyShareV3 { /// Author of the entry. - pub author: SerializablePublic, + pub author: SerializableAddress, /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). pub threshold: usize, /// Server public. @@ -136,12 +94,12 @@ struct SerializableDocumentKeyShareV2 { /// Encrypted point. pub encrypted_point: Option, /// Versions. - pub versions: Vec + pub versions: Vec } -/// V2 of encrypted key share version, as it is stored by key storage on the single key server. +/// V3 of encrypted key share version, as it is stored by key storage on the single key server. #[derive(Serialize, Deserialize)] -struct SerializableDocumentKeyShareVersionV2 { +struct SerializableDocumentKeyShareVersionV3 { /// Version hash. pub hash: SerializableH256, /// Nodes ids numbers. @@ -150,119 +108,19 @@ struct SerializableDocumentKeyShareVersionV2 { pub secret_share: SerializableSecret, } -/// V3 of encrypted key share, as it is stored by key storage on the single key server. -#[derive(Serialize, Deserialize)] -struct SerializableDocumentKeyShareV3 { - /// Author of the entry. - pub author: SerializableAddress, - /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). - pub threshold: usize, - /// Server public. - pub public: SerializablePublic, - /// Common (shared) encryption point. - pub common_point: Option, - /// Encrypted point. - pub encrypted_point: Option, - /// Versions. - pub versions: Vec -} - -/// V3 of encrypted key share version, as it is stored by key storage on the single key server. -type SerializableDocumentKeyShareVersionV3 = SerializableDocumentKeyShareVersionV2; - impl PersistentKeyStorage { /// Create new persistent document encryption keys storage - pub fn new(db: Arc) -> Result { - let db = upgrade_db(db)?; - - Ok(PersistentKeyStorage { - db: db, - }) - } -} - -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().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. - // in v0 there have been only simultaneous GenEnc sessions. - author: Address::default().into(), // added in v1 - threshold: v0_key.threshold, - public: Public::default().into(), // addded in v2 - common_point: Some(v0_key.common_point), - encrypted_point: Some(v0_key.encrypted_point), - versions: vec![CurrentSerializableDocumentKeyVersion { - hash: DocumentKeyShareVersion::data_hash(v0_key.id_numbers.iter().map(|(k, v)| (&***k, &****v))).into(), - id_numbers: v0_key.id_numbers, - secret_share: v0_key.secret_share, - }], - }; - let db_value = serde_json::to_vec(¤t_key).map_err(|e| Error::Database(e.to_string()))?; - batch.put(None, &*db_key, &*db_value); - } - db.write(batch)?; - Ok(db) - }, - 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().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 - threshold: v1_key.threshold, - public: Public::default().into(), // addded in v2 - common_point: v1_key.common_point, - encrypted_point: v1_key.encrypted_point, - versions: vec![CurrentSerializableDocumentKeyVersion { - hash: DocumentKeyShareVersion::data_hash(v1_key.id_numbers.iter().map(|(k, v)| (&***k, &****v))).into(), - id_numbers: v1_key.id_numbers, - secret_share: v1_key.secret_share, - }], - }; - let db_value = serde_json::to_vec(¤t_key).map_err(|e| Error::Database(e.to_string()))?; - batch.put(None, &*db_key, &*db_value); - } - db.write(batch)?; - Ok(db) - } - 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().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 - threshold: v2_key.threshold, - public: v2_key.public, - common_point: v2_key.common_point, - encrypted_point: v2_key.encrypted_point, - versions: v2_key.versions, - }; - let db_value = serde_json::to_vec(¤t_key).map_err(|e| Error::Database(e.to_string()))?; - batch.put(None, &*db_key, &*db_value); - } - db.write(batch)?; - Ok(db) - }, - 3 => Ok(db), - _ => Err(Error::Database(format!("unsupported SecretStore database version: {}", version))), + pub fn new(db: Arc) -> Result { + Ok(Self { db }) } } impl KeyStorage for PersistentKeyStorage { fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { - let key: CurrentSerializableDocumentKeyShare = key.into(); + let key: SerializableDocumentKeyShareV3 = key.into(); let key = serde_json::to_vec(&key).map_err(|e| Error::Database(e.to_string()))?; let mut batch = self.db.transaction(); - batch.put(None, &document, &key); + batch.put(0, document.as_bytes(), &key); self.db.write(batch).map_err(Into::into) } @@ -271,11 +129,11 @@ impl KeyStorage for PersistentKeyStorage { } fn get(&self, document: &ServerKeyId) -> Result, Error> { - self.db.get(None, document) + self.db.get(0, document.as_bytes()) .map_err(|e| Error::Database(e.to_string())) .and_then(|key| match key { None => Ok(None), - Some(key) => serde_json::from_slice::(&key) + Some(key) => serde_json::from_slice::(&key) .map_err(|e| Error::Database(e.to_string())) .map(Into::into) .map(Some), @@ -284,28 +142,28 @@ impl KeyStorage for PersistentKeyStorage { fn remove(&self, document: &ServerKeyId) -> Result<(), Error> { let mut batch = self.db.transaction(); - batch.delete(None, &document); + batch.delete(0, document.as_bytes()); self.db.write(batch).map_err(Into::into) } fn clear(&self) -> Result<(), Error> { let mut batch = self.db.transaction(); for (key, _) in self.iter() { - batch.delete(None, &key); + batch.delete(0, key.as_bytes()); } self.db.write(batch) .map_err(|e| Error::Database(e.to_string())) } fn contains(&self, document: &ServerKeyId) -> bool { - self.db.get(None, document) + self.db.get(0, document.as_bytes()) .map(|k| k.is_some()) .unwrap_or(false) } - fn iter<'a>(&'a self) -> Box + 'a> { + fn iter<'a>(&'a self) -> Box + 'a> { Box::new(PersistentKeyStorageIterator { - iter: self.db.iter(None), + iter: self.db.iter(0), }) } } @@ -315,9 +173,9 @@ impl<'a> Iterator for PersistentKeyStorageIterator<'a> { fn next(&mut self) -> Option<(ServerKeyId, DocumentKeyShare)> { self.iter.as_mut().next() - .and_then(|(db_key, db_val)| serde_json::from_slice::(&db_val) + .and_then(|(db_key, db_val)| serde_json::from_slice::(&db_val) .ok() - .map(|key| ((*db_key).into(), key.into()))) + .map(|key| (ServerKeyId::from_slice(&*db_key), key.into()))) } } @@ -342,7 +200,7 @@ impl DocumentKeyShareVersion { /// Create new version pub fn new(id_numbers: BTreeMap, secret_share: Secret) -> Self { DocumentKeyShareVersion { - hash: Self::data_hash(id_numbers.iter().map(|(k, v)| (&**k, &***v))), + hash: Self::data_hash(id_numbers.iter().map(|(k, v)| (k.as_bytes(), v.as_bytes()))), id_numbers: id_numbers, secret_share: secret_share, } @@ -408,20 +266,14 @@ impl From for DocumentKeyShare { #[cfg(test)] pub mod tests { - extern crate tempdir; - 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 tempdir::TempDir; + use crypto::publickey::{Random, Generator, Public}; + use kvdb_rocksdb::{Database, DatabaseConfig}; use types::{Error, ServerKeyId}; - use super::{DB_META_KEY_VERSION, CURRENT_VERSION, KeyStorage, PersistentKeyStorage, DocumentKeyShare, - DocumentKeyShareVersion, CurrentSerializableDocumentKeyShare, upgrade_db, SerializableDocumentKeyShareV0, - SerializableDocumentKeyShareV1, SerializableDocumentKeyShareV2, SerializableDocumentKeyShareVersionV2}; + use super::{KeyStorage, PersistentKeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; /// In-memory document encryption keys storage #[derive(Default)] @@ -458,7 +310,7 @@ pub mod tests { self.keys.read().contains_key(document) } - fn iter<'a>(&'a self) -> Box + 'a> { + fn iter<'a>(&'a self) -> Box + 'a> { Box::new(self.keys.read().clone().into_iter()) } } @@ -466,7 +318,7 @@ pub mod tests { #[test] fn persistent_key_storage() { let tempdir = TempDir::new("").unwrap(); - let key1 = ServerKeyId::from(1); + let key1 = ServerKeyId::from_low_u64_be(1); let value1 = DocumentKeyShare { author: Default::default(), threshold: 100, @@ -481,7 +333,7 @@ pub mod tests { secret_share: Random.generate().unwrap().secret().clone(), }], }; - let key2 = ServerKeyId::from(2); + let key2 = ServerKeyId::from_low_u64_be(2); let value2 = DocumentKeyShare { author: Default::default(), threshold: 200, @@ -496,9 +348,10 @@ pub mod tests { secret_share: Random.generate().unwrap().secret().clone(), }], }; - let key3 = ServerKeyId::from(3); + let key3 = ServerKeyId::from_low_u64_be(3); - let db = Database::open_default(&tempdir.path().display().to_string()).unwrap(); + let db_config = DatabaseConfig::with_columns(1); + let db = Database::open(&db_config, &tempdir.path().display().to_string()).unwrap(); let key_storage = PersistentKeyStorage::new(Arc::new(db)).unwrap(); key_storage.insert(key1.clone(), value1.clone()).unwrap(); @@ -508,144 +361,11 @@ pub mod tests { assert_eq!(key_storage.get(&key3), Ok(None)); drop(key_storage); - let db = Database::open_default(&tempdir.path().display().to_string()).unwrap(); + let db = Database::open(&db_config, &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)); } - - #[test] - fn upgrade_db_from_0() { - let tempdir = TempDir::new("").unwrap(); - let db = Database::open_default(&tempdir.path().display().to_string()).unwrap(); - - // prepare v0 database - { - let key = serde_json::to_vec(&SerializableDocumentKeyShareV0 { - threshold: 777, - id_numbers: vec![( - "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".into(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse::().unwrap().into(), - )].into_iter().collect(), - secret_share: "00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse::().unwrap().into(), - common_point: "99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc".into(), - encrypted_point: "7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c".into(), - }).unwrap(); - let mut batch = db.transaction(); - batch.put(None, &[7], &key); - db.write(batch).unwrap(); - } - - // upgrade database - let db = upgrade_db(Arc::new(db)).unwrap(); - - // check upgrade - assert_eq!(db.get(None, DB_META_KEY_VERSION).unwrap().unwrap()[0], CURRENT_VERSION); - let key = serde_json::from_slice::(&db.get(None, &[7]).unwrap().map(|key| key.to_vec()).unwrap()).unwrap(); - assert_eq!(Address::default(), key.author.clone().into()); - assert_eq!(777, key.threshold); - assert_eq!(Some("99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc".parse::().unwrap()), key.common_point.clone().map(Into::into)); - assert_eq!(Some("7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c".parse::().unwrap()), key.encrypted_point.clone().map(Into::into)); - - assert_eq!(key.versions.len(), 1); - assert_eq!(vec![( - "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse::().unwrap(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse::().unwrap(), - )], key.versions[0].id_numbers.clone().into_iter().map(|(k, v)| (k.into(), v.into())).collect::>()); - assert_eq!("00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse::().unwrap(), key.versions[0].secret_share.clone().into()); - } - - #[test] - fn upgrade_db_from_1() { - let tempdir = TempDir::new("").unwrap(); - let db = Database::open_default(&tempdir.path().display().to_string()).unwrap(); - - // prepare v1 database - { - let key = serde_json::to_vec(&SerializableDocumentKeyShareV1 { - author: "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".into(), - threshold: 777, - id_numbers: vec![( - "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".into(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse::().unwrap().into(), - )].into_iter().collect(), - secret_share: "00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse::().unwrap().into(), - common_point: Some("99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc".into()), - encrypted_point: Some("7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c".into()), - }).unwrap(); - let mut batch = db.transaction(); - batch.put(None, DB_META_KEY_VERSION, &[1]); - batch.put(None, &[7], &key); - db.write(batch).unwrap(); - } - - // upgrade database - let db = upgrade_db(Arc::new(db)).unwrap(); - - // check upgrade - assert_eq!(db.get(None, DB_META_KEY_VERSION).unwrap().unwrap()[0], CURRENT_VERSION); - let key = serde_json::from_slice::(&db.get(None, &[7]).unwrap().map(|key| key.to_vec()).unwrap()).unwrap(); - assert_eq!(777, key.threshold); - assert_eq!(Some("99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc".parse::().unwrap()), key.common_point.clone().map(Into::into)); - assert_eq!(Some("7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c".parse::().unwrap()), key.encrypted_point.clone().map(Into::into)); - assert_eq!(key.author.0, public_to_address(&"b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".into())); - - assert_eq!(key.versions.len(), 1); - assert_eq!(vec![( - "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse::().unwrap(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse::().unwrap(), - )], key.versions[0].id_numbers.clone().into_iter().map(|(k, v)| (k.into(), v.into())).collect::>()); - - assert_eq!("00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse::().unwrap(), key.versions[0].secret_share.clone().into()); - } - - #[test] - fn upgrade_db_from_2() { - let tempdir = TempDir::new("").unwrap(); - let db = Database::open_default(&tempdir.path().display().to_string()).unwrap(); - - // prepare v2 database - { - let key = serde_json::to_vec(&SerializableDocumentKeyShareV2 { - author: "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".into(), - threshold: 777, - common_point: Some("99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc".into()), - encrypted_point: Some("7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c".into()), - public: "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".into(), - versions: vec![SerializableDocumentKeyShareVersionV2 { - hash: "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse::().unwrap().into(), - id_numbers: vec![( - "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".into(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse::().unwrap().into(), - )].into_iter().collect(), - secret_share: "00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse::().unwrap().into(), - }], - }).unwrap(); - let mut batch = db.transaction(); - batch.put(None, DB_META_KEY_VERSION, &[2]); - batch.put(None, &[7], &key); - db.write(batch).unwrap(); - } - - // upgrade database - let db = upgrade_db(Arc::new(db)).unwrap(); - - // check upgrade - assert_eq!(db.get(None, DB_META_KEY_VERSION).unwrap().unwrap()[0], CURRENT_VERSION); - let key = serde_json::from_slice::(&db.get(None, &[7]).unwrap().map(|key| key.to_vec()).unwrap()).unwrap(); - assert_eq!(777, key.threshold); - assert_eq!(Some("99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc".parse::().unwrap()), key.common_point.clone().map(Into::into)); - assert_eq!(Some("7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c".parse::().unwrap()), key.encrypted_point.clone().map(Into::into)); - assert_eq!(key.author.0, public_to_address(&"b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse().unwrap())); - - assert_eq!(key.versions.len(), 1); - assert_eq!(vec![( - "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse::().unwrap(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse::().unwrap(), - )], key.versions[0].id_numbers.clone().into_iter().map(|(k, v)| (k.into(), v.into())).collect::>()); - - assert_eq!("00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse::().unwrap(), key.versions[0].secret_share.clone().into()); - } } diff --git a/secret-store/src/lib.rs b/secret-store/src/lib.rs index a3c82991b1..2b7ff592ff 100644 --- a/secret-store/src/lib.rs +++ b/secret-store/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,20 +15,17 @@ // along with Parity Ethereum. If not, see . extern crate byteorder; -extern crate common_types; extern crate ethabi; -extern crate ethcore; -extern crate ethcore_call_contract as call_contract; -extern crate ethcore_sync as sync; extern crate ethereum_types; -extern crate ethkey; extern crate hyper; extern crate keccak_hash as hash; extern crate kvdb; +extern crate kvdb_rocksdb; extern crate parity_bytes as bytes; extern crate parity_crypto as crypto; extern crate parity_runtime; extern crate parking_lot; +extern crate percent_encoding; extern crate rustc_hex; extern crate serde; extern crate serde_json; @@ -37,8 +34,8 @@ extern crate tokio; extern crate tokio_io; extern crate tokio_service; extern crate url; +extern crate jsonrpc_server_utils; -#[macro_use] extern crate ethabi_derive; #[macro_use] extern crate ethabi_contract; @@ -54,14 +51,10 @@ extern crate log; #[cfg(test)] extern crate env_logger; #[cfg(test)] -extern crate kvdb_rocksdb; - -#[cfg(feature = "accounts")] -extern crate ethcore_accounts as accounts; +extern crate tempdir; mod key_server_cluster; mod types; -mod helpers; mod traits; mod acl_storage; @@ -71,28 +64,39 @@ mod serialization; mod key_server_set; mod node_key_pair; mod listener; -mod trusted_client; +mod blockchain; +mod migration; use std::sync::Arc; use kvdb::KeyValueDB; -use ethcore::client::Client; -use ethcore::miner::Miner; -use sync::SyncProvider; +use kvdb_rocksdb::{Database, DatabaseConfig}; use parity_runtime::Executor; pub use types::{ServerKeyId, EncryptedDocumentKey, RequestSignature, Public, - Error, NodeAddress, ContractAddress, ServiceConfiguration, ClusterConfiguration}; -pub use traits::{NodeKeyPair, KeyServer}; + Error, NodeAddress, ServiceConfiguration, ClusterConfiguration}; +pub use traits::KeyServer; +pub use blockchain::{SecretStoreChain, SigningKeyPair, ContractAddress, BlockId, BlockNumber, NewBlocksNotify, Filter}; pub use self::node_key_pair::PlainNodeKeyPair; -#[cfg(feature = "accounts")] -pub use self::node_key_pair::KeyStoreNodeKeyPair; + +/// Open a secret store DB using the given secret store data path. The DB path is one level beneath the data path. +pub fn open_secretstore_db(data_path: &str) -> Result, String> { + use std::path::PathBuf; + + migration::upgrade_db(data_path).map_err(|e| e.to_string())?; + + 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())?; + + let config = DatabaseConfig::with_columns(1); + Ok(Arc::new(Database::open(&config, &db_path).map_err(|e| format!("Error opening database: {:?}", e))?)) +} /// Start new key server instance -pub fn start(client: Arc, sync: Arc, miner: Arc, self_key_pair: Arc, mut config: ServiceConfiguration, - db: Arc, executor: Executor) -> Result, Error> +pub fn start(trusted_client: Arc, self_key_pair: Arc, mut config: ServiceConfiguration, + db: Arc, executor: Executor) -> 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() { + 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()), }; @@ -103,11 +107,11 @@ pub fn start(client: Arc, sync: Arc, miner: Arc, se 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(), executor.clone())?); let cluster = key_server.cluster(); - let key_server: Arc = key_server; + let key_server: Arc = key_server; // prepare HTTP listener let http_listener = match config.listener_address { - Some(listener_address) => Some(listener::http_listener::KeyServerHttpListener::start(listener_address, Arc::downgrade(&key_server), executor)?), + Some(listener_address) => Some(listener::http_listener::KeyServerHttpListener::start(listener_address, config.cors, Arc::downgrade(&key_server), executor)?), None => None, }; @@ -120,7 +124,7 @@ pub fn start(client: Arc, sync: Arc, miner: Arc, se address, self_key_pair.clone())); - let mut contracts: Vec> = Vec::new(); + 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(), @@ -147,7 +151,7 @@ pub fn start(client: Arc, sync: Arc, miner: Arc, se listener::ApiMask { document_key_shadow_retrieval_requests: true, ..Default::default() })) .map(|l| contracts.push(l)); - let contract: Option> = match contracts.len() { + 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))), @@ -165,7 +169,7 @@ pub fn start(client: Arc, sync: Arc, miner: Arc, se key_storage: key_storage, } )?; - client.add_notify(listener.clone()); + trusted_client.add_listener(listener.clone()); listener }), None => None, diff --git a/secret-store/src/listener/http_listener.rs b/secret-store/src/listener/http_listener.rs index aa67ec5c66..663164fa00 100644 --- a/secret-store/src/listener/http_listener.rs +++ b/secret-store/src/listener/http_listener.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,6 +16,7 @@ use std::collections::BTreeSet; use std::sync::{Arc, Weak}; +use futures::future::{ok, result}; use hyper::{self, Uri, Request as HttpRequest, Response as HttpResponse, Method as HttpMethod, StatusCode as HttpStatusCode, Body, header::{self, HeaderValue}, @@ -28,23 +29,27 @@ use tokio; use tokio::net::TcpListener; use parity_runtime::Executor; use futures::{future, Future, Stream}; -use url::percent_encoding::percent_decode; +use percent_encoding::percent_decode; use traits::KeyServer; use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic}; use types::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; +use jsonrpc_server_utils::cors::{self, AllowCors, AccessControlAllowOrigin}; /// 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 get public portion of server key: GET /server/{server_key_id}/{signature} /// To get document key: GET /{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 +type CorsDomains = Option>; + pub struct KeyServerHttpListener { _executor: Executor, _handler: Arc, @@ -61,6 +66,8 @@ enum Request { StoreDocumentKey(ServerKeyId, RequestSignature, Public, Public), /// Generate encryption key. GenerateDocumentKey(ServerKeyId, RequestSignature, usize), + /// Request public portion of server key. + GetServerKey(ServerKeyId, RequestSignature), /// Request encryption key of given document for given requestor. GetDocumentKey(ServerKeyId, RequestSignature), /// Request shadow of encryption key of given document for given requestor. @@ -77,20 +84,22 @@ enum Request { #[derive(Clone)] struct KeyServerHttpHandler { handler: Arc, + cors: CorsDomains, } /// Shared http handler struct KeyServerSharedHttpHandler { - key_server: Weak, + key_server: Weak, } + impl KeyServerHttpListener { /// Start KeyServer http listener - pub fn start(listener_address: NodeAddress, key_server: Weak, executor: Executor) -> Result { + pub fn start(listener_address: NodeAddress, cors_domains: Option>, key_server: Weak, executor: Executor) -> Result { let shared_handler = Arc::new(KeyServerSharedHttpHandler { key_server: key_server, }); - + let cors: CorsDomains = cors_domains.map(|domains| domains.into_iter().map(AccessControlAllowOrigin::from).collect()); let listener_address = format!("{}:{}", listener_address.address, listener_address.port).parse()?; let listener = TcpListener::bind(&listener_address)?; @@ -101,7 +110,7 @@ impl KeyServerHttpListener { .for_each(move |socket| { let http = Http::new(); let serve = http.serve_connection(socket, - KeyServerHttpHandler { handler: shared_handler2.clone() } + KeyServerHttpHandler { handler: shared_handler2.clone(), cors: cors.clone() } ).map(|_| ()).map_err(|e| { warn!("Key server handler error: {:?}", e); }); @@ -121,86 +130,86 @@ impl KeyServerHttpListener { } impl KeyServerHttpHandler { - fn process(self, req_method: HttpMethod, req_uri: Uri, path: &str, req_body: &[u8]) -> HttpResponse { + fn key_server(&self) -> Result, Error> { + self.handler.key_server.upgrade() + .ok_or_else(|| Error::Internal("KeyServer is already destroyed".into())) + } + + fn process( + self, + req_method: HttpMethod, + req_uri: Uri, + path: &str, + req_body: &[u8], + cors: AllowCors, + ) -> Box, Error=hyper::Error> + Send> { 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::GenerateServerKey(document, signature, threshold) => + Box::new(result(self.key_server()) + .and_then(move |key_server| key_server.generate_key(document, signature.into(), threshold)) + .then(move |result| ok(return_server_public_key("GenerateServerKey", &req_uri, cors, result)))), + Request::StoreDocumentKey(document, signature, common_point, encrypted_document_key) => + Box::new(result(self.key_server()) + .and_then(move |key_server| key_server.store_document_key( + document, + signature.into(), + common_point, + encrypted_document_key, + )) + .then(move |result| ok(return_empty("StoreDocumentKey", &req_uri, cors, result)))), + Request::GenerateDocumentKey(document, signature, threshold) => + Box::new(result(self.key_server()) + .and_then(move |key_server| key_server.generate_document_key( + document, + signature.into(), + threshold, + )) + .then(move |result| ok(return_document_key("GenerateDocumentKey", &req_uri, cors, result)))), + Request::GetServerKey(document, signature) => + Box::new(result(self.key_server()) + .and_then(move |key_server| key_server.restore_key_public( + document, + signature.into(), + )) + .then(move |result| ok(return_server_public_key("GetServerKey", &req_uri, cors, result)))), + Request::GetDocumentKey(document, signature) => + Box::new(result(self.key_server()) + .and_then(move |key_server| key_server.restore_document_key(document, signature.into())) + .then(move |result| ok(return_document_key("GetDocumentKey", &req_uri, cors, result)))), + Request::GetDocumentKeyShadow(document, signature) => + Box::new(result(self.key_server()) + .and_then(move |key_server| key_server.restore_document_key_shadow(document, signature.into())) + .then(move |result| ok(return_document_key_shadow("GetDocumentKeyShadow", &req_uri, cors, result)))), + Request::SchnorrSignMessage(document, signature, message_hash) => + Box::new(result(self.key_server()) + .and_then(move |key_server| key_server.sign_message_schnorr( + document, + signature.into(), + message_hash, + )) + .then(move |result| ok(return_message_signature("SchnorrSignMessage", &req_uri, cors, result)))), + Request::EcdsaSignMessage(document, signature, message_hash) => + Box::new(result(self.key_server()) + .and_then(move |key_server| key_server.sign_message_ecdsa( + document, + signature.into(), + message_hash, + )) + .then(move |result| ok(return_message_signature("EcdsaSignMessage", &req_uri, cors, result)))), + Request::ChangeServersSet(old_set_signature, new_set_signature, new_servers_set) => + Box::new(result(self.key_server()) + .and_then(move |key_server| key_server.change_servers_set( + old_set_signature, + new_set_signature, + new_servers_set, + )) + .then(move |result| ok(return_empty("ChangeServersSet", &req_uri, cors, result)))), Request::Invalid => { warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri); - HttpResponse::builder() + Box::new(ok(HttpResponse::builder() .status(HttpStatusCode::BAD_REQUEST) .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed") + .expect("Nothing to parse, cannot fail; qed"))) }, } } @@ -210,72 +219,96 @@ impl Service for KeyServerHttpHandler { type ReqBody = Body; type ResBody = Body; type Error = hyper::Error; - type Future = Box, Error=Self::Error> + Send>; + type Future = Box, Error=Self::Error> + Send>; fn call(&mut self, req: HttpRequest) -> Self::Future { - if req.headers().contains_key(header::ORIGIN) { - warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method(), req.uri()); - return Box::new(future::ok(HttpResponse::builder() + let cors = cors::get_cors_allow_origin( + req.headers().get(header::ORIGIN).and_then(|value| value.to_str().ok()), + req.headers().get(header::HOST).and_then(|value| value.to_str().ok()), + &self.cors + ); + match cors { + AllowCors::Invalid => { + warn!(target: "secretstore", "Ignoring {}-request {} with unauthorized Origin header", req.method(), req.uri()); + Box::new(future::ok(HttpResponse::builder() .status(HttpStatusCode::NOT_FOUND) .body(Body::empty()) .expect("Nothing to parse, cannot fail; qed"))) - } - - 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.into_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); - HttpResponse::builder() - .status(HttpStatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed") + }, + _ => { + let req_method = req.method().clone(); + let req_uri = req.uri().clone(); + let path = req_uri.path().to_string(); + // We cannot consume Self because of the Service trait requirement. + let this = self.clone(); + + Box::new(req.into_body().concat2() + .and_then(move |body| this.process(req_method, req_uri, &path, &body, cors))) } - })) + } } } -fn return_empty(req_uri: &Uri, empty: Result<(), Error>) -> HttpResponse { - return_bytes::(req_uri, empty.map(|_| None)) +fn return_empty(req_type: &str, req_uri: &Uri, cors: AllowCors, empty: Result<(), Error>) -> HttpResponse { + return_bytes::(req_type, req_uri, cors, empty.map(|_| None)) } -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_server_public_key( + req_type: &str, + req_uri: &Uri, + cors: AllowCors, + server_public: Result, +) -> HttpResponse { + return_bytes(req_type, req_uri, cors, server_public.map(|k| Some(SerializablePublic(k)))) } -fn return_message_signature(req_uri: &Uri, signature: Result) -> HttpResponse { - return_bytes(req_uri, signature.map(|s| Some(SerializableBytes(s)))) +fn return_message_signature( + req_type: &str, + req_uri: &Uri, + cors: AllowCors, + signature: Result, +) -> HttpResponse { + return_bytes(req_type, req_uri, cors, signature.map(|s| Some(SerializableBytes(s)))) } -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( + req_type: &str, + req_uri: &Uri, + cors: AllowCors, + document_key: Result, +) -> HttpResponse { + return_bytes(req_type, req_uri, cors, document_key.map(|k| Some(SerializableBytes(k)))) } -fn return_document_key_shadow(req_uri: &Uri, document_key_shadow: Result) - -> HttpResponse -{ - return_bytes(req_uri, document_key_shadow.map(|k| Some(SerializableEncryptedDocumentKeyShadow { +fn return_document_key_shadow( + req_type: &str, + req_uri: &Uri, + cors: AllowCors, + document_key_shadow: Result, +) -> HttpResponse { + return_bytes(req_type, req_uri, cors, 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(), + decrypt_shadows: k.decrypt_shadows.expect("always filled when requesting document_key_shadow; qed").into_iter().map(Into::into).collect() }))) } -fn return_bytes(req_uri: &Uri, result: Result, Error>) -> HttpResponse { +fn return_bytes( + req_type: &str, + req_uri: &Uri, + cors: AllowCors, + result: Result, Error>, +) -> HttpResponse { match result { Ok(Some(result)) => match serde_json::to_vec(&result) { Ok(result) => { let body: Body = result.into(); - HttpResponse::builder() - .header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8")) - .body(body) - .expect("Error creating http response") + let mut builder = HttpResponse::builder(); + builder.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8")); + if let AllowCors::Ok(AccessControlAllowOrigin::Value(origin)) = cors { + builder.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.to_string()); + } + builder.body(body).expect("Error creating http response") }, Err(err) => { warn!(target: "secretstore", "response to request {} has failed with: {}", req_uri, err); @@ -286,12 +319,17 @@ fn return_bytes(req_uri: &Uri, result: Result, Error>) - } }, Ok(None) => { - HttpResponse::builder() - .status(HttpStatusCode::OK) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed") + let mut builder = HttpResponse::builder(); + builder.status(HttpStatusCode::OK); + if let AllowCors::Ok(AccessControlAllowOrigin::Value(origin)) = cors { + builder.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.to_string()); + } + builder.body(Body::empty()).expect("Nothing to parse, cannot fail; qed") + }, + Err(err) => { + warn!(target: "secretstore", "{} request {} has failed with: {}", req_type, req_uri, err); + return_error(err) }, - Err(err) => return_error(err), } } @@ -343,8 +381,8 @@ fn parse_request(method: &HttpMethod, uri_path: &str, body: &[u8]) -> Request { return parse_admin_request(method, path, body); } - let (prefix, args_offset) = if &path[0] == "shadow" || &path[0] == "schnorr" || &path[0] == "ecdsa" - { (&*path[0], 1) } else { ("", 0) }; + let is_known_prefix = &path[0] == "shadow" || &path[0] == "schnorr" || &path[0] == "ecdsa" || &path[0] == "server"; + let (prefix, args_offset) = if is_known_prefix { (&*path[0], 1) } else { ("", 0) }; let args_count = path.len() - args_offset; if args_count < 2 || path[args_offset].is_empty() || path[args_offset + 1].is_empty() { return Request::Invalid; @@ -370,6 +408,8 @@ fn parse_request(method: &HttpMethod, uri_path: &str, body: &[u8]) -> Request { Request::StoreDocumentKey(document, signature, common_point, encrypted_key), ("", 3, &HttpMethod::POST, Some(Ok(threshold)), _, _, _) => Request::GenerateDocumentKey(document, signature, threshold), + ("server", 2, &HttpMethod::GET, _, _, _, _) => + Request::GetServerKey(document, signature), ("", 2, &HttpMethod::GET, _, _, _, _) => Request::GetDocumentKey(document, signature), ("shadow", 2, &HttpMethod::GET, _, _, _, _) => @@ -410,20 +450,22 @@ fn parse_admin_request(method: &HttpMethod, path: Vec, body: &[u8]) -> R #[cfg(test)] mod tests { use std::sync::Arc; + use std::str::FromStr; use hyper::Method as HttpMethod; - use ethkey::Public; + use crypto::publickey::Public; use traits::KeyServer; use key_server::tests::DummyKeyServer; use types::NodeAddress; use parity_runtime::Runtime; + use ethereum_types::H256; use super::{parse_request, Request, KeyServerHttpListener}; #[test] fn http_listener_successfully_drops() { - let key_server: Arc = Arc::new(DummyKeyServer::default()); + let key_server: Arc = Arc::new(DummyKeyServer::default()); let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 }; let runtime = Runtime::with_thread_count(1); - let listener = KeyServerHttpListener::start(address, Arc::downgrade(&key_server), + let listener = KeyServerHttpListener::start(address, None, Arc::downgrade(&key_server), runtime.executor()).unwrap(); drop(listener); } @@ -432,39 +474,43 @@ mod tests { fn parse_request_successful() { // POST /shadow/{server_key_id}/{signature}/{threshold} => generate server key assert_eq!(parse_request(&HttpMethod::POST, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()), - Request::GenerateServerKey("0000000000000000000000000000000000000000000000000000000000000001".into(), + Request::GenerateServerKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), 2)); // POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key} => store encrypted document key assert_eq!(parse_request(&HttpMethod::POST, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8/1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb", Default::default()), - Request::StoreDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(), + Request::StoreDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse().unwrap(), "1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb".parse().unwrap())); // POST /{server_key_id}/{signature}/{threshold} => generate server && document key assert_eq!(parse_request(&HttpMethod::POST, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()), - Request::GenerateDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(), + Request::GenerateDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), 2)); + // GET /server/{server_key_id}/{signature} => get public portion of server key + assert_eq!(parse_request(&HttpMethod::GET, "/server/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), + Request::GetServerKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), + "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); // GET /{server_key_id}/{signature} => get document key assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(), + Request::GetDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); assert_eq!(parse_request(&HttpMethod::GET, "/%30000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(), + Request::GetDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); // GET /shadow/{server_key_id}/{signature} => get document key shadow assert_eq!(parse_request(&HttpMethod::GET, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetDocumentKeyShadow("0000000000000000000000000000000000000000000000000000000000000001".into(), + Request::GetDocumentKeyShadow(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); // GET /schnorr/{server_key_id}/{signature}/{message_hash} => schnorr-sign message with server key assert_eq!(parse_request(&HttpMethod::GET, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()), - Request::SchnorrSignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(), + Request::SchnorrSignMessage(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap())); // GET /ecdsa/{server_key_id}/{signature}/{message_hash} => ecdsa-sign message with server key assert_eq!(parse_request(&HttpMethod::GET, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()), - Request::EcdsaSignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(), + Request::EcdsaSignMessage(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap())); // POST /admin/servers_set_change/{old_set_signature}/{new_set_signature} + body diff --git a/secret-store/src/listener/mod.rs b/secret-store/src/listener/mod.rs index b28375d8ec..1911a4d59d 100644 --- a/secret-store/src/listener/mod.rs +++ b/secret-store/src/listener/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,6 +22,7 @@ mod tasks_queue; use std::collections::BTreeSet; use std::sync::Arc; +use futures::Future; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, AdminSessionsServer, KeyServer}; use types::{Error, Public, MessageHash, EncryptedMessageSignature, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId, Requester}; @@ -41,7 +42,7 @@ pub struct ApiMask { /// Combined HTTP + service contract listener. pub struct Listener { - key_server: Arc, + key_server: Arc, _http: Option, _contract: Option>, } @@ -60,7 +61,7 @@ impl ApiMask { impl Listener { /// Create new listener. - pub fn new(key_server: Arc, http: Option, contract: Option>) -> Self { + pub fn new(key_server: Arc, http: Option, contract: Option>) -> Self { Self { key_server: key_server, _http: http, @@ -72,41 +73,88 @@ impl Listener { impl KeyServer for Listener {} impl ServerKeyGenerator for Listener { - fn generate_key(&self, key_id: &ServerKeyId, author: &Requester, threshold: usize) -> Result { + fn generate_key( + &self, + key_id: ServerKeyId, + author: Requester, + threshold: usize, + ) -> Box + Send> { self.key_server.generate_key(key_id, author, threshold) } + + fn restore_key_public( + &self, + key_id: ServerKeyId, + author: Requester, + ) -> Box + Send> { + self.key_server.restore_key_public(key_id, author) + } } impl DocumentKeyServer for Listener { - fn store_document_key(&self, key_id: &ServerKeyId, author: &Requester, 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, + ) -> Box + Send> { self.key_server.store_document_key(key_id, author, common_point, encrypted_document_key) } - fn generate_document_key(&self, key_id: &ServerKeyId, author: &Requester, threshold: usize) -> Result { + fn generate_document_key( + &self, + key_id: ServerKeyId, + author: Requester, + threshold: usize, + ) -> Box + Send> { self.key_server.generate_document_key(key_id, author, threshold) } - fn restore_document_key(&self, key_id: &ServerKeyId, requester: &Requester) -> Result { + fn restore_document_key( + &self, + key_id: ServerKeyId, + requester: Requester, + ) -> Box + Send> { self.key_server.restore_document_key(key_id, requester) } - fn restore_document_key_shadow(&self, key_id: &ServerKeyId, requester: &Requester) -> Result { + fn restore_document_key_shadow( + &self, + key_id: ServerKeyId, + requester: Requester, + ) -> Box + Send> { self.key_server.restore_document_key_shadow(key_id, requester) } } impl MessageSigner for Listener { - fn sign_message_schnorr(&self, key_id: &ServerKeyId, requester: &Requester, message: MessageHash) -> Result { + fn sign_message_schnorr( + &self, + key_id: ServerKeyId, + requester: Requester, + message: MessageHash, + ) -> Box + Send> { self.key_server.sign_message_schnorr(key_id, requester, message) } - fn sign_message_ecdsa(&self, key_id: &ServerKeyId, requester: &Requester, message: MessageHash) -> Result { + fn sign_message_ecdsa( + &self, + key_id: ServerKeyId, + requester: Requester, + message: MessageHash, + ) -> Box + Send> { self.key_server.sign_message_ecdsa(key_id, requester, message) } } impl AdminSessionsServer for Listener { - fn change_servers_set(&self, old_set_signature: RequestSignature, new_set_signature: RequestSignature, new_servers_set: BTreeSet) -> Result<(), Error> { + fn change_servers_set( + &self, + old_set_signature: RequestSignature, + new_set_signature: RequestSignature, + new_servers_set: BTreeSet, + ) -> Box + Send> { self.key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set) } } diff --git a/secret-store/src/listener/service_contract.rs b/secret-store/src/listener/service_contract.rs index 795e75d2c6..6ebe1796a9 100644 --- a/secret-store/src/listener/service_contract.rs +++ b/secret-store/src/listener/service_contract.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,20 +16,16 @@ use std::sync::Arc; use parking_lot::RwLock; -use common_types::filter::Filter; use ethabi::RawLog; use ethabi::FunctionOutputDecoder; -use call_contract::CallContract; -use ethcore::client::{Client, BlockChainClient, BlockId}; -use ethkey::{Public, public_to_address}; +use crypto::publickey::{Public, public_to_address}; use hash::keccak; use bytes::Bytes; -use ethereum_types::{H256, U256, Address}; +use ethereum_types::{H256, U256, Address, H512}; 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 blockchain::{SecretStoreChain, Filter, SigningKeyPair, ContractAddress, BlockId}; +use ServerKeyId; use_contract!(service, "res/service.json"); @@ -68,9 +64,9 @@ 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>; + fn read_pending_requests(&self) -> Box>; /// 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. @@ -96,9 +92,9 @@ pub struct OnChainServiceContract { /// Requests mask. mask: ApiMask, /// Blockchain client. - client: TrustedClient, + client: Arc, /// This node key pair. - self_key_pair: Arc, + self_key_pair: Arc, /// Contract registry name (if any). name: String, /// Contract address source. @@ -136,7 +132,7 @@ struct DocumentKeyShadowRetrievalService; impl OnChainServiceContract { /// Create new on-chain service contract. - pub fn new(mask: ApiMask, client: TrustedClient, name: String, address_source: ContractAddress, self_key_pair: Arc) -> Self { + pub fn new(mask: ApiMask, client: Arc, name: String, address_source: ContractAddress, self_key_pair: Arc) -> Self { let contract = OnChainServiceContract { mask: mask, client: client, @@ -155,24 +151,23 @@ impl OnChainServiceContract { /// 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, &ServerKeyId, &Address) -> bool, - P: FnOnce(&Client, &Address) -> Result { + where C: FnOnce(&dyn SecretStoreChain, &Address, &ServerKeyId, &Address) -> bool, + P: FnOnce(&dyn SecretStoreChain, &Address) -> 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()), - }; + if !self.client.is_trusted() { + 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, server_key_id, &self_address) { + if !is_response_required(&*self.client, origin, server_key_id, &self_address) { return Ok(()); } // prepare transaction data - let transaction_data = prepare_tx(&*client, origin)?; + let transaction_data = prepare_tx(&*self.client, origin)?; // send transaction self.client.transact_contract( @@ -188,9 +183,9 @@ impl OnChainServiceContract { /// Create task-specific pending requests iterator. fn create_pending_requests_iterator< - C: 'static + Fn(&Client, &Address, &BlockId) -> Result, - R: 'static + Fn(&NodeKeyPair, &Client, &Address, &BlockId, U256) -> Result<(bool, ServiceTask), String> - >(&self, client: Arc, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box> { + C: 'static + Fn(&dyn SecretStoreChain, &Address, &BlockId) -> Result, + R: 'static + Fn(&dyn SigningKeyPair, &dyn SecretStoreChain, &Address, &BlockId, U256) -> Result<(bool, ServiceTask), String> + >(&self, client: Arc, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box> { get_count(&*client, contract_address, block) .map(|count| { let client = client.clone(); @@ -207,7 +202,7 @@ impl OnChainServiceContract { .ok(), index: 0.into(), length: count, - }) as Box> + }) as Box> }) .map_err(|error| { warn!(target: "secretstore", "{}: creating pending requests iterator failed: {}", @@ -220,7 +215,7 @@ impl OnChainServiceContract { /// 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 contract_address = self.client.read_contract_address(&self.name, &self.address_source); let mut data = self.data.write(); if contract_address != data.contract_address { trace!(target: "secretstore", "{}: installing {} service contract from address {:?}", @@ -235,67 +230,46 @@ impl OnChainServiceContract { impl ServiceContract for OnChainServiceContract { fn update(&self) -> bool { - self.update_contract_address() && self.client.get().is_some() + self.update_contract_address() && self.client.is_trusted() } - fn read_logs(&self) -> Box> { - let client = match self.client.get() { - Some(client) => client, - None => { - warn!(target: "secretstore", "{}: client is offline during read_logs call", - self.self_key_pair.public()); - return Box::new(::std::iter::empty()); - }, - }; + fn read_logs(&self) -> Box> { + if !self.client.is_trusted() { + warn!(target: "secretstore", "{}: client is offline during read_logs call", + self.self_key_pair.public()); + return Box::new(::std::iter::empty()); + } - // prepare range of blocks to read logs from - let (address, first_block, last_block) = { - let mut data = self.data.write(); - 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 - }; - let first_block = match data.last_log_block.take().and_then(|b| client.tree_route(&b, &confirmed_block)) { - // if we have a route from last_log_block to confirmed_block => search for logs on this route - // - // potentially this could lead us to reading same logs twice when reorganizing to the fork, which - // already has been canonical previosuly - // the worst thing that can happen in this case is spending some time reading unneeded data from SS db - Some(ref route) if route.index < route.blocks.len() => route.blocks[route.index], - // else we care only about confirmed block - _ => confirmed_block.clone(), - }; - - data.last_log_block = Some(confirmed_block.clone()); - (address, first_block, confirmed_block) + let address = match self.data.read().contract_address { + Some(address) => address, + None => return Box::new(::std::iter::empty()), // no contract installed + }; + let confirmed_block = match self.client.get_confirmed_block_hash() { + Some(confirmed_block) => confirmed_block, + None => return Box::new(::std::iter::empty()), // no block with enough confirmations }; - // read server key generation requests - let request_logs = client.logs(Filter { - from_block: BlockId::Hash(first_block), - to_block: BlockId::Hash(last_block), + let request_logs = self.client.retrieve_last_logs(Filter { + from_block: BlockId::Hash(self.data.read().last_log_block.unwrap_or_else(|| confirmed_block)), address: Some(vec![address]), topics: vec![Some(mask_topics(&self.mask))], - limit: None, }).unwrap_or_default(); + let mut data = self.data.write(); + data.last_log_block = Some(confirmed_block.clone()); + 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, raw_log) - } else if raw_log.topics[0] == *SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - ServerKeyRetrievalService::parse_log(&address, raw_log) - } else if raw_log.topics[0] == *DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH { - DocumentKeyStoreService::parse_log(&address, raw_log) - } else if raw_log.topics[0] == *DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - DocumentKeyShadowRetrievalService::parse_common_request_log(&address, raw_log) - } else if raw_log.topics[0] == *DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - DocumentKeyShadowRetrievalService::parse_personal_request_log(&address, raw_log) + if log.topics[0] == *SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH { + ServerKeyGenerationService::parse_log(&address, log) + } else if log.topics[0] == *SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { + ServerKeyRetrievalService::parse_log(&address, log) + } else if log.topics[0] == *DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH { + DocumentKeyStoreService::parse_log(&address, log) + } else if log.topics[0] == *DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { + DocumentKeyShadowRetrievalService::parse_common_request_log(&address, log) + } else if log.topics[0] == *DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { + DocumentKeyShadowRetrievalService::parse_personal_request_log(&address, log) } else { Err("unknown type of log entry".into()) } @@ -308,40 +282,38 @@ impl ServiceContract for OnChainServiceContract { }).collect::>().into_iter()) } - fn read_pending_requests(&self) -> Box> { - let client = match self.client.get() { - Some(client) => client, - None => return Box::new(::std::iter::empty()), - }; + fn read_pending_requests(&self) -> Box> { + if !self.client.is_trusted() { + return Box::new(::std::iter::empty()) + } - // we only need requests that are here for more than REQUEST_CONFIRMATIONS_REQUIRED blocks - // => we're reading from Latest - (REQUEST_CONFIRMATIONS_REQUIRED + 1) block + // we only need requests that are here from the last confirm block let data = self.data.read(); match data.contract_address { None => Box::new(::std::iter::empty()), - Some(contract_address) => get_confirmed_block_hash(&*client, REQUEST_CONFIRMATIONS_REQUIRED + 1) + Some(contract_address) => self.client.get_confirmed_block_hash() .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, + true => Box::new(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, &ServerKeyGenerationService::read_pending_requests_count, - &ServerKeyGenerationService::read_pending_request)) as Box>, + &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, + true => Box::new(iter.chain(self.create_pending_requests_iterator(self.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, + true => Box::new(iter.chain(self.create_pending_requests_iterator(self.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, + true => Box::new(iter.chain(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, &DocumentKeyShadowRetrievalService::read_pending_requests_count, &DocumentKeyShadowRetrievalService::read_pending_request))), false => iter @@ -455,7 +427,7 @@ impl ServerKeyGenerationService { } /// Check if response from key server is required. - pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { + pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, 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 (encoded, decoder) = service::functions::is_server_key_generation_response_required::call(*server_key_id, *key_server); match client.call_contract(BlockId::Latest, *contract_address, encoded) { @@ -466,7 +438,7 @@ impl ServerKeyGenerationService { /// Prepare publish key transaction data. pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId, server_key_public: &Public) -> Bytes { - service::functions::server_key_generated::encode_input(*server_key_id, server_key_public.to_vec()) + service::functions::server_key_generated::encode_input(*server_key_id, server_key_public.as_bytes().to_vec()) } /// Prepare error transaction data. @@ -475,14 +447,14 @@ impl ServerKeyGenerationService { } /// Read pending requests count. - fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result { + fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { let (encoded, decoder) = service::functions::server_key_generation_requests_count::call(); decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) .map_err(|e| e.to_string()) } /// Read pending request. - fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { let self_address = public_to_address(self_key_pair.public()); let (encoded, decoder) = service::functions::get_server_key_generation_request::call(index); @@ -515,7 +487,7 @@ impl ServerKeyRetrievalService { } /// Check if response from key server is required. - pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { + pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, 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 (encoded, decoder) = service::functions::is_server_key_retrieval_response_required::call(*server_key_id, *key_server); match client.call_contract(BlockId::Latest, *contract_address, encoded) { @@ -526,7 +498,7 @@ impl ServerKeyRetrievalService { /// Prepare publish key transaction data. pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId, server_key_public: Public, threshold: U256) -> Bytes { - service::functions::server_key_retrieved::encode_input(*server_key_id, server_key_public.to_vec(), threshold) + service::functions::server_key_retrieved::encode_input(*server_key_id, server_key_public.as_bytes().to_vec(), threshold) } /// Prepare error transaction data. @@ -535,14 +507,14 @@ impl ServerKeyRetrievalService { } /// Read pending requests count. - fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result { + fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { let (encoded, decoder) = service::functions::server_key_retrieval_requests_count::call(); decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) .map_err(|e| e.to_string()) } /// Read pending request. - fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { let self_address = public_to_address(self_key_pair.public()); let (encoded, decoder) = service::functions::get_server_key_retrieval_request::call(index); @@ -566,13 +538,19 @@ impl DocumentKeyStoreService { /// Parse request log entry. pub fn parse_log(origin: &Address, raw_log: RawLog) -> Result { match service::events::document_key_store_requested::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())), + Ok(l) => Ok(ServiceTask::StoreDocumentKey( + origin.clone(), + l.server_key_id, + l.author, + H512::from_slice(&*l.common_point), + H512::from_slice(&*l.encrypted_point), + )), Err(e) => Err(format!("{}", e)), } } /// Check if response from key server is required. - pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { + pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, 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 (encoded, decoder) = service::functions::is_document_key_store_response_required::call(*server_key_id, *key_server); match client.call_contract(BlockId::Latest, *contract_address, encoded) { @@ -592,14 +570,14 @@ impl DocumentKeyStoreService { } /// Read pending requests count. - fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result { + fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { let (encoded, decoder) = service::functions::document_key_store_requests_count::call(); decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) .map_err(|e| e.to_string()) } /// Read pending request. - fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { let self_address = public_to_address(self_key_pair.public()); let (encoded, decoder) = service::functions::get_document_key_store_request::call(index); let (server_key_id, author, common_point, encrypted_point) = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) @@ -633,13 +611,13 @@ impl DocumentKeyShadowRetrievalService { /// Parse personal request log entry. pub fn parse_personal_request_log(origin: &Address, raw_log: RawLog) -> Result { match service::events::document_key_personal_retrieval_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyPersonal(origin.clone(), l.server_key_id, (*l.requester_public).into())), + Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyPersonal(origin.clone(), l.server_key_id, H512::from_slice(&*l.requester_public))), Err(e) => Err(e.to_string()) } } /// Check if response from key server is required. - pub fn is_response_required(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, key_server: &Address) -> bool { + pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, 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 (encoded, decoder) = service::functions::is_document_key_shadow_retrieval_response_required::call(*server_key_id, *requester, *key_server); match client.call_contract(BlockId::Latest, *contract_address, encoded) { @@ -650,11 +628,11 @@ impl DocumentKeyShadowRetrievalService { /// Prepare publish common key transaction data. pub fn prepare_pubish_common_tx_data(server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: U256) -> Bytes { - service::functions::document_key_common_retrieved::encode_input(*server_key_id, *requester, common_point.to_vec(), threshold) + service::functions::document_key_common_retrieved::encode_input(*server_key_id, *requester, common_point.as_bytes().to_vec(), threshold) } /// Prepare publish personal key transaction data. - pub fn prepare_pubish_personal_tx_data(client: &Client, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result { + pub fn prepare_pubish_personal_tx_data(client: &dyn SecretStoreChain, contract_address: &Address, 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, participant.clone()) @@ -662,7 +640,7 @@ impl DocumentKeyShadowRetrievalService { participants_mask = participants_mask | (U256::one() << participant_index); } Ok(service::functions::document_key_personal_retrieved::encode_input( - *server_key_id, *requester, participants_mask, decrypted_secret.to_vec(), shadow + *server_key_id, *requester, participants_mask, decrypted_secret.as_bytes().to_vec(), shadow )) } @@ -672,14 +650,14 @@ impl DocumentKeyShadowRetrievalService { } /// Read pending requests count. - fn read_pending_requests_count(client: &Client, contract_address: &Address, block: &BlockId) -> Result { + fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { let (encoded, decoder) = service::functions::document_key_shadow_retrieval_requests_count::call(); decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) .map_err(|e| e.to_string()) } /// Read pending request. - fn read_pending_request(self_key_pair: &NodeKeyPair, client: &Client, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { + fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { let self_address = public_to_address(self_key_pair.public()); let (encoded, decoder) = service::functions::get_document_key_shadow_retrieval_request::call(index); @@ -709,7 +687,7 @@ impl DocumentKeyShadowRetrievalService { } /// Map from key server address to key server index. - fn map_key_server_address(client: &Client, contract_address: &Address, key_server: Address) -> Result { + fn map_key_server_address(client: &dyn SecretStoreChain, contract_address: &Address, key_server: Address) -> Result { // we're checking confirmation in Latest block, because tx ,ust be appended to the latest state let (encoded, decoder) = service::functions::require_key_server::call(key_server); let index = decoder.decode(&client.call_contract(BlockId::Latest, *contract_address, encoded)?) @@ -718,7 +696,7 @@ impl DocumentKeyShadowRetrievalService { if index > u8::max_value().into() { Err(format!("key server index is too big: {}", index)) } else { - let index: u32 = index.into(); + let index: u32 = index.low_u32(); Ok(index as u8) } } @@ -746,7 +724,7 @@ fn serialize_threshold(threshold: usize) -> Result { pub mod tests { use parking_lot::Mutex; use bytes::Bytes; - use ethkey::Public; + use crypto::publickey::Public; use ethereum_types::Address; use listener::service_contract_listener::ServiceTask; use {ServerKeyId}; @@ -773,11 +751,11 @@ pub mod tests { true } - fn read_logs(&self) -> Box> { + fn read_logs(&self) -> Box> { Box::new(self.logs.clone().into_iter()) } - fn read_pending_requests(&self) -> Box> { + fn read_pending_requests(&self) -> Box> { Box::new(self.pending_requests.clone().into_iter()) } diff --git a/secret-store/src/listener/service_contract_aggregate.rs b/secret-store/src/listener/service_contract_aggregate.rs index 29a4730e2b..9b2fb0e54b 100644 --- a/secret-store/src/listener/service_contract_aggregate.rs +++ b/secret-store/src/listener/service_contract_aggregate.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,7 @@ use std::sync::Arc; use bytes::Bytes; use ethereum_types::Address; -use ethkey::Public; +use crypto::publickey::Public; use listener::service_contract::ServiceContract; use listener::service_contract_listener::ServiceTask; use {ServerKeyId}; @@ -25,12 +25,12 @@ use {ServerKeyId}; /// Aggregated on-chain service contract. pub struct OnChainServiceContractAggregate { /// All hosted service contracts. - contracts: Vec>, + contracts: Vec>, } impl OnChainServiceContractAggregate { /// Create new aggregated service contract listener. - pub fn new(contracts: Vec>) -> Self { + pub fn new(contracts: Vec>) -> Self { debug_assert!(contracts.len() > 1); OnChainServiceContractAggregate { contracts: contracts, @@ -47,15 +47,15 @@ impl ServiceContract for OnChainServiceContractAggregate { result } - fn read_logs(&self) -> Box> { + fn read_logs(&self) -> Box> { self.contracts.iter() - .fold(Box::new(::std::iter::empty()) as Box>, |i, c| + .fold(Box::new(::std::iter::empty()) as Box>, |i, c| Box::new(i.chain(c.read_logs()))) } - fn read_pending_requests(&self) -> Box> { + fn read_pending_requests(&self) -> Box> { self.contracts.iter() - .fold(Box::new(::std::iter::empty()) as Box>, |i, c| + .fold(Box::new(::std::iter::empty()) as Box>, |i, c| Box::new(i.chain(c.read_pending_requests()))) } diff --git a/secret-store/src/listener/service_contract_listener.rs b/secret-store/src/listener/service_contract_listener.rs index 61dcda1766..8e1ffa66d1 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-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,11 +18,9 @@ use std::collections::HashSet; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; -use parking_lot::Mutex; -use ethcore::client::{ChainNotify, NewBlocks}; -use ethkey::{Public, public_to_address}; use bytes::Bytes; -use ethereum_types::{H256, U256, Address}; +use crypto::publickey::{Public, public_to_address}; +use ethereum_types::{H256, U256, Address, BigEndianHash as _}; use key_server_set::KeyServerSet; use key_server_cluster::{NodeId, ClusterClient, ClusterSessionsListener, ClusterSession}; use key_server_cluster::math; @@ -32,10 +30,12 @@ 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 parking_lot::Mutex; use acl_storage::AclStorage; use listener::service_contract::ServiceContract; use listener::tasks_queue::TasksQueue; -use {ServerKeyId, NodeKeyPair, Error}; +use {ServerKeyId, Error}; +use blockchain::{NewBlocksNotify, SigningKeyPair}; /// 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 @@ -61,17 +61,17 @@ pub struct ServiceContractListener { /// Service contract listener parameters. pub struct ServiceContractListenerParams { /// Service contract. - pub contract: Arc, + pub contract: Arc, /// This node key pair. - pub self_key_pair: Arc, + pub self_key_pair: Arc, /// Key servers set. - pub key_server_set: Arc, + pub key_server_set: Arc, /// ACL storage reference. - pub acl_storage: Arc, + pub acl_storage: Arc, /// Cluster reference. - pub cluster: Arc, + pub cluster: Arc, /// Key storage reference. - pub key_storage: Arc, + pub key_storage: Arc, } /// Service contract listener data. @@ -83,17 +83,17 @@ struct ServiceContractListenerData { /// Service tasks queue. pub tasks_queue: Arc>, /// Service contract. - pub contract: Arc, + pub contract: Arc, /// ACL storage reference. - pub acl_storage: Arc, + pub acl_storage: Arc, /// Cluster client reference. - pub cluster: Arc, + pub cluster: Arc, /// This node key pair. - pub self_key_pair: Arc, + pub self_key_pair: Arc, /// Key servers set. - pub key_server_set: Arc, + pub key_server_set: Arc, /// Key storage reference. - pub key_storage: Arc, + pub key_storage: Arc, } @@ -433,14 +433,8 @@ impl Drop for ServiceContractListener { } } -impl ChainNotify for ServiceContractListener { - fn new_blocks(&self, new_blocks: NewBlocks) { - if new_blocks.has_more_blocks_to_import { return } - let enacted_len = new_blocks.route.enacted().len(); - if enacted_len == 0 && new_blocks.route.retracted().is_empty() { - return; - } - +impl NewBlocksNotify for ServiceContractListener { + fn new_blocks(&self, new_enacted_len: usize) { if !self.data.contract.update() { return; } @@ -449,7 +443,7 @@ 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 { + if self.data.last_retry.fetch_add(new_enacted_len, Ordering::Relaxed) >= RETRY_INTERVAL_BLOCKS { // 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); @@ -467,7 +461,7 @@ impl ClusterSessionsListener for ServiceContractListener { // 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())) { + if let Some(generation_result) = session.result() { 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); } @@ -484,7 +478,7 @@ impl ClusterSessionsListener for ServiceContractListener { 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())) { + if let Some(retrieval_result) = session.result() { let retrieval_result = retrieval_result.map(|key_shadow| session.broadcast_shadows() .and_then(|broadcast_shadows| @@ -509,8 +503,8 @@ impl ClusterSessionsListener error.clone(), + let error = match session.result() { + Some(Err(ref error)) if !error.is_non_fatal() => error.clone(), _ => return, }; @@ -560,7 +554,7 @@ fn log_service_task_result(task: &ServiceTask, self_id: &Public, result: 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 { +fn is_processed_by_this_key_server(key_server_set: &dyn 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 { @@ -574,7 +568,7 @@ fn is_processed_by_this_key_server(key_server_set: &KeyServerSet, node: &NodeId, None => return false, }; - let server_key_id_value: U256 = server_key_id.into(); + let server_key_id_value: U256 = server_key_id.into_uint(); let range_interval = U256::max_value() / total_servers_count; let range_begin = (range_interval + 1) * this_server_index as u32; let range_end = range_begin.saturating_add(range_interval); @@ -586,7 +580,7 @@ fn is_processed_by_this_key_server(key_server_set: &KeyServerSet, node: &NodeId, mod tests { use std::sync::Arc; use std::sync::atomic::Ordering; - use ethkey::{Random, Generator, KeyPair}; + use crypto::publickey::{Random, Generator, KeyPair}; use listener::service_contract::ServiceContract; use listener::service_contract::tests::DummyServiceContract; use key_server_cluster::DummyClusterClient; @@ -595,8 +589,10 @@ mod tests { use key_storage::tests::DummyKeyStorage; use key_server_set::KeyServerSet; use key_server_set::tests::MapKeyServerSet; - use {NodeKeyPair, PlainNodeKeyPair, ServerKeyId}; + use blockchain::SigningKeyPair; + use {PlainNodeKeyPair, ServerKeyId}; use super::{ServiceTask, ServiceContractListener, ServiceContractListenerParams, is_processed_by_this_key_server}; + use ethereum_types::Address; fn create_non_empty_key_storage(has_doc_key: bool) -> Arc { let key_storage = Arc::new(DummyKeyStorage::default()); @@ -611,7 +607,7 @@ mod tests { key_storage } - fn make_servers_set(is_isolated: bool) -> Arc { + 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()), @@ -622,7 +618,7 @@ mod tests { ].into_iter().collect())) } - fn make_service_contract_listener(contract: Option>, cluster: Option>, key_storage: Option>, acl_storage: Option>, servers_set: Option>) -> Arc { + 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())); @@ -983,7 +979,7 @@ mod tests { 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(); + Default::default(), Default::default(), Address::from_low_u64_be(1), Default::default(), Default::default())).unwrap_err(); assert_eq!(*contract.document_keys_store_failures.lock(), vec![Default::default()]); } diff --git a/secret-store/src/listener/tasks_queue.rs b/secret-store/src/listener/tasks_queue.rs index 2d9bdba11f..08a4a316de 100644 --- a/secret-store/src/listener/tasks_queue.rs +++ b/secret-store/src/listener/tasks_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/secret-store/src/migration.rs b/secret-store/src/migration.rs new file mode 100644 index 0000000000..44ac519765 --- /dev/null +++ b/secret-store/src/migration.rs @@ -0,0 +1,101 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Secret Store DB migration module. + + +use std::fmt::{Display, Error as FmtError, Formatter}; +use std::fs; +use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read as _}; +use std::path::PathBuf; + +/// Current db version. +const CURRENT_VERSION: u8 = 4; +/// Database is assumed to be at the default version, when no version file is found. +const DEFAULT_VERSION: u8 = 3; +/// Version file name. +const VERSION_FILE_NAME: &str = "db_version"; + +/// Migration related errors. +#[derive(Debug)] +pub enum Error { + /// Returned when current version cannot be read or guessed. + UnknownDatabaseVersion, + /// Existing DB is newer than the known one. + FutureDBVersion, + /// Migration using parity-ethereum 2.6.7 is required. + MigrationWithLegacyVersionRequired, + /// Migration was completed successfully, + /// but there was a problem with io. + Io(IoError), +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + let out = match *self { + Error::UnknownDatabaseVersion => + "Current Secret Store database version cannot be read".into(), + Error::FutureDBVersion => + "Secret Store database was created with newer client version.\ + Upgrade your client or delete DB and resync.".into(), + Error::MigrationWithLegacyVersionRequired => + "Secret Store database was created with an older client version.\ + To migrate, use parity-ethereum v2.6.7, then retry using the latest.".into(), + Error::Io(ref err) => + format!("Unexpected io error on Secret Store database migration: {}.", err), + }; + write!(f, "{}", out) + } +} + +impl From for Error { + fn from(err: IoError) -> Self { + Error::Io(err) + } +} + +/// Apply all migrations if possible. +pub fn upgrade_db(db_path: &str) -> Result<(), Error> { + match current_version(db_path)? { + old_version if old_version < CURRENT_VERSION => { + Err(Error::MigrationWithLegacyVersionRequired) + }, + CURRENT_VERSION => Ok(()), + _ => Err(Error::FutureDBVersion), + } +} + +/// Returns the version file path. +fn version_file_path(path: &str) -> PathBuf { + let mut file_path = PathBuf::from(path); + file_path.push(VERSION_FILE_NAME); + file_path +} + +/// Reads current database version from the file at given path. +/// If the file does not exist returns `DEFAULT_VERSION`. +fn current_version(path: &str) -> Result { + match fs::File::open(version_file_path(path)) { + Err(ref err) if err.kind() == IoErrorKind::NotFound => Ok(DEFAULT_VERSION), + Err(err) => Err(err.into()), + Ok(mut file) => { + let mut s = String::new(); + file.read_to_string(&mut s)?; + u8::from_str_radix(&s, 10).map_err(|_| Error::UnknownDatabaseVersion) + }, + } +} + diff --git a/secret-store/src/node_key_pair.rs b/secret-store/src/node_key_pair.rs index f50f75ad1b..3b953a95f0 100644 --- a/secret-store/src/node_key_pair.rs +++ b/secret-store/src/node_key_pair.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,10 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use ethkey::crypto::ecdh::agree; -use ethkey::{KeyPair, Public, Signature, Error as EthKeyError, sign, public_to_address}; +use crypto::publickey::{KeyPair, Public, Signature, Error as EthKeyError, sign, public_to_address}; use ethereum_types::{H256, Address}; -use traits::NodeKeyPair; +use blockchain::SigningKeyPair; pub struct PlainNodeKeyPair { key_pair: KeyPair, @@ -36,7 +35,7 @@ impl PlainNodeKeyPair { } } -impl NodeKeyPair for PlainNodeKeyPair { +impl SigningKeyPair for PlainNodeKeyPair { fn public(&self) -> &Public { self.key_pair.public() } @@ -48,60 +47,4 @@ impl NodeKeyPair for PlainNodeKeyPair { fn sign(&self, data: &H256) -> Result { sign(self.key_pair.secret(), data) } - - fn compute_shared_key(&self, peer_public: &Public) -> Result { - agree(self.key_pair.secret(), peer_public) - .map_err(|e| EthKeyError::Custom(e.to_string())) - .and_then(KeyPair::from_secret) - } } - -#[cfg(feature = "accounts")] -mod accounts { - use super::*; - use std::sync::Arc; - use ethkey::Password; - use accounts::AccountProvider; - - pub struct KeyStoreNodeKeyPair { - account_provider: Arc, - address: Address, - public: Public, - password: Password, - } - - impl KeyStoreNodeKeyPair { - pub fn new(account_provider: Arc, address: Address, password: Password) -> Result { - let public = account_provider.account_public(address.clone(), &password).map_err(|e| EthKeyError::Custom(format!("{}", e)))?; - Ok(KeyStoreNodeKeyPair { - account_provider: account_provider, - address: address, - public: public, - password: password, - }) - } - } - - impl NodeKeyPair for KeyStoreNodeKeyPair { - fn public(&self) -> &Public { - &self.public - } - - fn address(&self) -> Address { - public_to_address(&self.public) - } - - fn sign(&self, data: &H256) -> Result { - self.account_provider.sign(self.address.clone(), Some(self.password.clone()), data.clone()) - .map_err(|e| EthKeyError::Custom(format!("{}", e))) - } - - fn compute_shared_key(&self, peer_public: &Public) -> Result { - KeyPair::from_secret(self.account_provider.agree(self.address.clone(), Some(self.password.clone()), peer_public) - .map_err(|e| EthKeyError::Custom(format!("{}", e)))?) - } - } -} - -#[cfg(feature = "accounts")] -pub use self::accounts::KeyStoreNodeKeyPair; diff --git a/secret-store/src/serialization.rs b/secret-store/src/serialization.rs index 64cc5f1ce3..90fe967812 100644 --- a/secret-store/src/serialization.rs +++ b/secret-store/src/serialization.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,14 +16,46 @@ use std::fmt; use std::ops::Deref; -use rustc_hex::{ToHex, FromHex}; +use rustc_hex::{self, FromHex}; use serde::{Serialize, Deserialize, Serializer, Deserializer}; use serde::de::{Visitor, Error as SerdeError}; -use ethkey::{Public, Secret, Signature}; +use crypto::publickey::{Public, Secret, Signature}; use ethereum_types::{H160, H256}; use bytes::Bytes; use types::Requester; +trait ToHex { + fn to_hex(&self) -> String; +} + +impl ToHex for Bytes { + fn to_hex(&self) -> String { + format!("0x{}", rustc_hex::ToHex::to_hex(&self[..])) + } +} + +impl ToHex for Signature { + fn to_hex(&self) -> String { + format!("0x{}", self) + } +} + +impl ToHex for Secret { + fn to_hex(&self) -> String { + format!("0x{}", self.to_hex()) + } +} + +macro_rules! impl_to_hex { + ($name: ident) => ( + impl ToHex for $name { + fn to_hex(&self) -> String { + format!("{:#x}", self) + } + } + ); +} + macro_rules! impl_bytes_deserialize { ($name: ident, $value: expr, true) => { $value[2..].from_hex().map($name).map_err(SerdeError::custom) @@ -60,9 +92,7 @@ macro_rules! impl_bytes { impl Serialize for $name { fn serialize(&self, serializer: S) -> Result where S: Serializer { - let mut serialized = "0x".to_owned(); - serialized.push_str(self.0.to_hex().as_ref()); - serializer.serialize_str(serialized.as_ref()) + serializer.serialize_str(<$other as ToHex>::to_hex(&self.0).as_ref()) } } @@ -101,17 +131,15 @@ pub type SerializableMessageHash = SerializableH256; /// Serializable address; pub type SerializableAddress = SerializableH160; -/// Serializable Bytes. +impl_to_hex!(H256); +impl_to_hex!(H160); +impl_to_hex!(Public); + impl_bytes!(SerializableBytes, Bytes, true, (Default)); -/// Serializable H256. impl_bytes!(SerializableH256, H256, false, (Default, PartialOrd, Ord)); -/// Serializable H160. impl_bytes!(SerializableH160, H160, false, (Default)); -/// Serializable H512 (aka Public). impl_bytes!(SerializablePublic, Public, false, (Default, PartialOrd, Ord)); -/// Serializable Secret. impl_bytes!(SerializableSecret, Secret, false, ()); -/// Serializable Signature. impl_bytes!(SerializableSignature, Signature, false, ()); /// Serializable shadow decryption result. @@ -159,23 +187,59 @@ impl From for SerializableRequester { #[cfg(test)] mod tests { use serde_json; - use super::{SerializableBytes, SerializablePublic}; + use super::*; + use std::str::FromStr; + + macro_rules! do_test { + ($value: expr, $expected: expr, $expected_type: ident) => ( + let serialized = serde_json::to_string(&$value).unwrap(); + assert_eq!(serialized, $expected); + let deserialized: $expected_type = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, $value); + ); + } #[test] fn serialize_and_deserialize_bytes() { - let bytes = SerializableBytes(vec![1, 2, 3, 4]); - let bytes_serialized = serde_json::to_string(&bytes).unwrap(); - assert_eq!(&bytes_serialized, r#""0x01020304""#); - let bytes_deserialized: SerializableBytes = serde_json::from_str(&bytes_serialized).unwrap(); - assert_eq!(bytes_deserialized, bytes); + do_test!(SerializableBytes(vec![1, 2, 3, 4]), "\"0x01020304\"".to_owned(), SerializableBytes); + } + + #[test] + fn serialize_and_deserialize_h256() { + let s = "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"; + let h256 = SerializableH256(H256::from_str(s).unwrap()); + do_test!(h256, format!("\"0x{}\"", s), SerializableH256); + } + + #[test] + fn serialize_and_deserialize_h160() { + let s = "c6d9d2cd449a754c494264e1809c50e34d64562b"; + let h160 = SerializableH160(H160::from_str(s).unwrap()); + do_test!(h160, format!("\"0x{}\"", s), SerializableH160); } #[test] fn serialize_and_deserialize_public() { - let public = SerializablePublic("cac6c205eb06c8308d65156ff6c862c62b000b8ead121a4455a8ddeff7248128d895692136f240d5d1614dc7cc4147b1bd584bd617e30560bb872064d09ea325".parse().unwrap()); - let public_serialized = serde_json::to_string(&public).unwrap(); - assert_eq!(&public_serialized, r#""0xcac6c205eb06c8308d65156ff6c862c62b000b8ead121a4455a8ddeff7248128d895692136f240d5d1614dc7cc4147b1bd584bd617e30560bb872064d09ea325""#); - let public_deserialized: SerializablePublic = serde_json::from_str(&public_serialized).unwrap(); - assert_eq!(public_deserialized, public); + let s = "cac6c205eb06c8308d65156ff6c862c62b000b8ead121a4455a8ddeff7248128d895692136f240d5d1614dc7cc4147b1bd584bd617e30560bb872064d09ea325"; + let public = SerializablePublic(s.parse().unwrap()); + do_test!(public, format!("\"0x{}\"", s), SerializablePublic); + } + + #[test] + fn serialize_and_deserialize_secret() { + let s = "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"; + let secret = SerializableSecret(Secret::from_str(s).unwrap()); + do_test!(secret, format!("\"0x{}\"", s), SerializableSecret); + } + + #[test] + fn serialize_and_deserialize_signature() { + let raw_r = "afafafafafafafafafafafbcbcbcbcbcbcbcbcbcbeeeeeeeeeeeeedddddddddd"; + let raw_s = "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"; + let r = H256::from_str(raw_r).unwrap(); + let s = H256::from_str(raw_s).unwrap(); + let v = 42u8; + let public = SerializableSignature(Signature::from_rsv(&r, &s, v)); + do_test!(public, format!("\"0x{}{}{:x}\"", raw_r, raw_s, v), SerializableSignature); } } diff --git a/secret-store/src/traits.rs b/secret-store/src/traits.rs index fdfa058979..441bd25a77 100644 --- a/secret-store/src/traits.rs +++ b/secret-store/src/traits.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,23 +15,10 @@ // along with Parity Ethereum. If not, see . use std::collections::BTreeSet; -use ethkey::{KeyPair, Signature, Error as EthKeyError}; -use ethereum_types::{H256, Address}; +use futures::Future; use types::{Error, Public, ServerKeyId, MessageHash, EncryptedMessageSignature, RequestSignature, Requester, EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; -/// Node key pair. -pub trait NodeKeyPair: Send + Sync { - /// Public portion of key. - fn public(&self) -> &Public; - /// Address of key owner. - fn address(&self) -> Address; - /// Sign data with node key. - fn sign(&self, data: &H256) -> Result; - /// Compute shared key to encrypt channel between two nodes. - fn compute_shared_key(&self, peer_public: &Public) -> Result; -} - /// Server key (SK) generator. pub trait ServerKeyGenerator { /// Generate new SK. @@ -39,7 +26,20 @@ pub trait ServerKeyGenerator { /// `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, author: &Requester, threshold: usize) -> Result; + fn generate_key( + &self, + key_id: ServerKeyId, + author: Requester, + threshold: usize, + ) -> Box + Send>; + /// Retrieve public portion of previously generated SK. + /// `key_id` is identifier of previously generated SK. + /// `author` is the same author, that has created the server key. + fn restore_key_public( + &self, + key_id: ServerKeyId, + author: Requester, + ) -> Box + Send>; } /// Document key (DK) server. @@ -50,20 +50,35 @@ pub trait DocumentKeyServer: ServerKeyGenerator { /// `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, author: &Requester, 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, + ) -> Box + Send>; /// 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. /// `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, author: &Requester, threshold: usize) -> Result; + fn generate_document_key( + &self, + key_id: ServerKeyId, + author: Requester, + threshold: usize, + ) -> Box + Send>; /// 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. /// `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, requester: &Requester) -> Result; + fn restore_document_key( + &self, + key_id: ServerKeyId, + requester: Requester, + ) -> Box + Send>; /// Restore previously stored DK. /// To decrypt DK on client: /// 1) use requestor secret key to decrypt secret coefficients from result.decrypt_shadows @@ -71,7 +86,11 @@ 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, requester: &Requester) -> Result; + fn restore_document_key_shadow( + &self, + key_id: ServerKeyId, + requester: Requester, + ) -> Box + Send>; } /// Message signer. @@ -81,14 +100,24 @@ pub trait MessageSigner: ServerKeyGenerator { /// `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, requester: &Requester, message: MessageHash) -> Result; + fn sign_message_schnorr( + &self, + key_id: ServerKeyId, + requester: Requester, + message: MessageHash, + ) -> Box + Send>; /// 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: &Requester, message: MessageHash) -> Result; + fn sign_message_ecdsa( + &self, + key_id: ServerKeyId, + signature: Requester, + message: MessageHash, + ) -> Box + Send>; } /// Administrative sessions server. @@ -97,7 +126,12 @@ pub trait AdminSessionsServer { /// 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>; + fn change_servers_set( + &self, + old_set_signature: RequestSignature, + new_set_signature: RequestSignature, + new_servers_set: BTreeSet, + ) -> Box + Send>; } /// Key server. diff --git a/secret-store/src/trusted_client.rs b/secret-store/src/trusted_client.rs deleted file mode 100644 index a20373ad0d..0000000000 --- a/secret-store/src/trusted_client.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::{Arc, Weak}; -use bytes::Bytes; -use call_contract::RegistryInfo; -use common_types::transaction::{Transaction, SignedTransaction, Action}; -use ethereum_types::Address; -use ethcore::client::{Client, BlockChainClient, ChainInfo, Nonce, BlockId}; -use ethcore::miner::{Miner, MinerService}; -use sync::SyncProvider; -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(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), - } - } - - /// Get 'trusted' `Client` reference only if it is synchronized && trusted. - pub fn get(&self) -> Option> { - self.client.upgrade() - .and_then(|client| self.sync.upgrade().map(|sync| (client, sync))) - .and_then(|(client, sync)| { - let is_synced = !sync.status().is_syncing(client.queue_info()); - let is_trusted = client.chain_info().security_level().is_full(); - match is_synced && is_trusted { - true => Some(client), - false => None, - } - }) - } - - /// Get untrusted `Client` reference. - 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 3a1e9df70d..e85285b266 100644 --- a/secret-store/src/types/all.rs +++ b/secret-store/src/types/all.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,10 +16,11 @@ use std::collections::BTreeMap; -use {ethkey, bytes, ethereum_types}; +use blockchain::ContractAddress; +use {bytes, ethereum_types}; /// Node id. -pub type NodeId = ethkey::Public; +pub type NodeId = crypto::publickey::Public; /// Server key id. When key is used to encrypt document, it could be document contents hash. pub type ServerKeyId = ethereum_types::H256; /// Encrypted document key type. @@ -29,9 +30,9 @@ pub type MessageHash = ethereum_types::H256; /// Message signature. pub type EncryptedMessageSignature = bytes::Bytes; /// Request signature type. -pub type RequestSignature = ethkey::Signature; +pub type RequestSignature = crypto::publickey::Signature; /// Public key type. -pub use ethkey::Public; +pub use crypto::publickey::Public; /// Secret store configuration #[derive(Debug, Clone)] @@ -42,15 +43,6 @@ pub struct NodeAddress { pub port: u16, } -/// Contract address. -#[derive(Debug, Clone)] -pub enum ContractAddress { - /// Address is read from registry. - Registry, - /// Address is specified. - Address(ethkey::Address), -} - /// Secret store configuration #[derive(Debug)] pub struct ServiceConfiguration { @@ -70,6 +62,8 @@ pub struct ServiceConfiguration { pub acl_check_contract_address: Option, /// Cluster configuration. pub cluster_config: ClusterConfiguration, + // Allowed CORS domains + pub cors: Option>, } /// Key server cluster configuration @@ -78,7 +72,7 @@ pub struct ClusterConfiguration { /// This node address. pub listener_address: NodeAddress, /// All cluster nodes addresses. - pub nodes: BTreeMap, + 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. @@ -95,9 +89,9 @@ pub struct ClusterConfiguration { #[derive(Clone, Debug, PartialEq)] pub struct EncryptedDocumentKeyShadow { /// Decrypted secret point. It is partially decrypted if shadow decryption was requested. - pub decrypted_secret: ethkey::Public, + pub decrypted_secret: crypto::publickey::Public, /// Shared common point. - pub common_point: Option, + pub common_point: Option, /// If shadow decryption was requested: shadow decryption coefficients, encrypted with requestor public. pub decrypt_shadows: Option>>, } @@ -106,9 +100,9 @@ pub struct EncryptedDocumentKeyShadow { #[derive(Debug, Clone)] pub enum Requester { /// Requested with server key id signature. - Signature(ethkey::Signature), + Signature(crypto::publickey::Signature), /// Requested with public key. - Public(ethkey::Public), + Public(crypto::publickey::Public), /// Requested with verified address. Address(ethereum_types::Address), } @@ -122,21 +116,21 @@ impl Default for Requester { impl Requester { pub fn public(&self, server_key_id: &ServerKeyId) -> Result { match *self { - Requester::Signature(ref signature) => ethkey::recover(signature, server_key_id) + Requester::Signature(ref signature) => crypto::publickey::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) -> Result { + pub fn address(&self, server_key_id: &ServerKeyId) -> Result { self.public(server_key_id) - .map(|p| ethkey::public_to_address(&p)) + .map(|p| crypto::publickey::public_to_address(&p)) } } -impl From for Requester { - fn from(signature: ethkey::Signature) -> Requester { +impl From for Requester { + fn from(signature: crypto::publickey::Signature) -> Requester { Requester::Signature(signature) } } diff --git a/secret-store/src/types/error.rs b/secret-store/src/types/error.rs index 72dfded78e..29a9262e6d 100644 --- a/secret-store/src/types/error.rs +++ b/secret-store/src/types/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use std::fmt; use std::net; use std::io::Error as IoError; -use {ethkey, crypto}; +use crypto; /// Secret store error. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -162,18 +162,12 @@ impl fmt::Display for Error { } } -impl From for Error { - fn from(err: ethkey::Error) -> Self { +impl From for Error { + fn from(err: crypto::publickey::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: crypto::Error) -> Self { Error::EthKey(err.to_string()) diff --git a/secret-store/src/types/mod.rs b/secret-store/src/types/mod.rs index 2001efc5a7..3a8511d233 100644 --- a/secret-store/src/types/mod.rs +++ b/secret-store/src/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/test.sh b/test.sh deleted file mode 100755 index a25f41d9a9..0000000000 --- a/test.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/sh -# Running Parity Full Test Suite -echo "________Running test.sh________" - -FEATURES="json-tests,ci-skip-issue" -OPTIONS="--release" -VALIDATE=1 -THREADS=8 - -set -e - - -validate () { - if [ "$VALIDATE" -eq "1" ] - then - echo "________Validate build________" - time cargo check $@ --locked --no-default-features - time cargo check $@ --locked --manifest-path util/io/Cargo.toml --no-default-features - time cargo check $@ --locked --manifest-path util/io/Cargo.toml --features "mio" - - # Validate chainspecs - echo "________Validate chainspecs________" - time ./scripts/validate_chainspecs.sh - else - echo "# not validating due to \$VALIDATE!=1" - fi -} - -cpp_test () { - case $CARGO_TARGET in - (x86_64-unknown-linux-gnu) - # Running the C++ example - echo "________Running the C++ example________" - DIR=parity-clib/examples/cpp/build - mkdir -p $DIR - cd $DIR - cmake .. - make -j $THREADS - # Note: we don't try to run the example because it tries to sync Kovan, and we don't want - # that to happen on CI - cd - - rm -rf $DIR - ;; - (*) - echo "________Skipping the C++ example________" - ;; - esac -} - -cargo_test () { - echo "________Running Parity Full Test Suite________" - git submodule update --init --recursive - time cargo test $OPTIONS --features "$FEATURES" --locked --all $@ -- --test-threads $THREADS -} - - -if [ "$CARGO_TARGET" ] -then - validate --target $CARGO_TARGET -else - validate -fi - -test "${RUN_TESTS}" = "all" && cpp_test - -if [ "$CARGO_TARGET" ] -then - - case "${RUN_TESTS}" in - (cargo|all) - cargo_test --target $CARGO_TARGET $@ - ;; - ('') - cargo_test --no-run --target $CARGO_TARGET $@ - ;; - esac -else - cargo_test $@ -fi diff --git a/updater/Cargo.toml b/updater/Cargo.toml index dd3417f880..90ff44c9af 100644 --- a/updater/Cargo.toml +++ b/updater/Cargo.toml @@ -6,22 +6,23 @@ license = "GPL-3.0" authors = ["Parity Technologies "] [dependencies] +client-traits = { path = "../ethcore/client-traits" } common-types = { path = "../ethcore/types" } -ethabi = "6.0" -ethabi-contract = "6.0" -ethabi-derive = "6.0" +ethabi = "9.0.1" +ethabi-contract = "9.0.0" +ethabi-derive = "9.0.1" ethcore = { path = "../ethcore" } ethcore-sync = { path = "../ethcore/sync" } -ethereum-types = "0.4" -keccak-hash = "0.1" +ethereum-types = "0.8.0" +keccak-hash = "0.4.0" lazy_static = "1.0" log = "0.4" parity-bytes = "0.1" parity-hash-fetch = { path = "hash-fetch" } parity-path = "0.1" parity-version = { path = "../util/version" } -parking_lot = "0.7" -rand = "0.4" +rand = "0.7" +parking_lot = "0.9" semver = "0.9" target_info = "0.1" diff --git a/updater/hash-fetch/Cargo.toml b/updater/hash-fetch/Cargo.toml index 1c5477c06e..0aaca33e77 100644 --- a/updater/hash-fetch/Cargo.toml +++ b/updater/hash-fetch/Cargo.toml @@ -7,23 +7,25 @@ version = "1.12.0" authors = ["Parity Technologies "] [dependencies] +call-contract = { package = "ethcore-call-contract", path = "../../ethcore/call-contract" } futures = "0.1" log = "0.4" mime = "0.3" mime_guess = "2.0.0-alpha.2" -rand = "0.4" +rand = "0.7" rustc-hex = "1.0" fetch = { path = "../../util/fetch" } parity-bytes = "0.1" -ethereum-types = "0.4" +ethereum-types = "0.8.0" parity-runtime = { path = "../../util/runtime" } -keccak-hash = "0.1" +keccak-hash = "0.4.0" registrar = { path = "../../util/registrar" } +types = { path = "../../ethcore/types", package = "common-types" } -ethabi = "6.0" -ethabi-derive = "6.0" -ethabi-contract = "6.0" +ethabi = "9.0.1" +ethabi-derive = "9.0.1" +ethabi-contract = "9.0.0" [dev-dependencies] -parking_lot = "0.7" +parking_lot = "0.9" fake-fetch = { path = "../../util/fake-fetch" } diff --git a/updater/hash-fetch/src/client.rs b/updater/hash-fetch/src/client.rs index 513b02f256..09a70fee0d 100644 --- a/updater/hash-fetch/src/client.rs +++ b/updater/hash-fetch/src/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,7 +18,7 @@ use std::{io, fs}; use std::io::Write; -use std::sync::Arc; +use std::sync::{Arc, Weak}; use std::path::PathBuf; use hash::keccak_buffer; @@ -26,7 +26,7 @@ use fetch::{self, Fetch}; use futures::{Future, IntoFuture}; use parity_runtime::Executor; use urlhint::{URLHintContract, URLHint, URLHintResult}; -use registrar::{RegistrarClient, Asynchronous}; +use registrar::RegistrarClient; use ethereum_types::H256; /// API for fetching by hash. @@ -37,7 +37,7 @@ pub trait HashFetch: Send + Sync + 'static { /// 2. `on_done` - callback function invoked when the content is ready (or there was error during fetch) /// /// This function may fail immediately when fetch cannot be initialized or content cannot be resolved. - fn fetch(&self, hash: H256, abort: fetch::Abort, on_done: Box) + Send>); + fn fetch(&self, hash: H256, abort: fetch::Abort, on_done: Box) + Send>); } /// Hash-fetching error. @@ -111,12 +111,12 @@ pub struct Client { contract: URLHintContract, fetch: F, executor: Executor, - random_path: Arc PathBuf + Sync + Send>, + random_path: Arc PathBuf + Sync + Send>, } impl Client { /// Creates new instance of the `Client` given on-chain contract client, fetch service and task runner. - pub fn with_fetch(contract: Arc>, fetch: F, executor: Executor) -> Self { + pub fn with_fetch(contract: Weak, fetch: F, executor: Executor) -> Self { Client { contract: URLHintContract::new(contract), fetch: fetch, @@ -127,12 +127,13 @@ impl Client { } impl HashFetch for Client { - fn fetch(&self, hash: H256, abort: fetch::Abort, on_done: Box) + Send>) { + fn fetch(&self, hash: H256, abort: fetch::Abort, on_done: Box) + Send>) { debug!(target: "fetch", "Fetching: {:?}", hash); let random_path = self.random_path.clone(); let remote_fetch = self.fetch.clone(); let future = self.contract.resolve(hash) + .into_future() .map_err(|e| { warn!("Error resolving URL: {}", e); Error::NoResolution }) .and_then(|maybe_url| maybe_url.ok_or(Error::NoResolution)) .map(|content| match content { @@ -176,11 +177,11 @@ impl HashFetch for Client { } fn random_temp_path() -> PathBuf { - use ::rand::Rng; - use ::std::env; + use rand::{Rng, rngs::OsRng, distributions::Alphanumeric}; + use std::env; - let mut rng = ::rand::OsRng::new().expect("Reliable random source is required to work."); - let file: String = rng.gen_ascii_chars().take(12).collect(); + let rng = OsRng; + let file: String = rng.sample_iter(&Alphanumeric).take(12).collect(); let mut path = env::temp_dir(); path.push(file); @@ -195,7 +196,9 @@ mod tests { use parking_lot::Mutex; use parity_runtime::Executor; use urlhint::tests::{FakeRegistrar, URLHINT}; - use super::{Error, Client, HashFetch, random_temp_path}; + use super::{Error, Client, HashFetch, random_temp_path, H256}; + use std::str::FromStr; + use registrar::RegistrarClient; fn registrar() -> FakeRegistrar { let mut registrar = FakeRegistrar::new(); @@ -209,13 +212,13 @@ mod tests { #[test] fn should_return_error_if_hash_not_found() { // given - let contract = Arc::new(FakeRegistrar::new()); + let contract = Arc::new(FakeRegistrar::new()) as Arc; let fetch = FakeFetch::new(None::); - let client = Client::with_fetch(contract.clone(), fetch, Executor::new_sync()); + let client = Client::with_fetch(Arc::downgrade(&contract), fetch, Executor::new_sync()); // when let (tx, rx) = mpsc::channel(); - client.fetch(2.into(), Default::default(), Box::new(move |result| { + client.fetch(H256::from_low_u64_be(2), Default::default(), Box::new(move |result| { tx.send(result).unwrap(); })); @@ -227,13 +230,13 @@ mod tests { #[test] fn should_return_error_if_response_is_not_successful() { // given - let registrar = Arc::new(registrar()); + let registrar = Arc::new(registrar()) as Arc; let fetch = FakeFetch::new(None::); - let client = Client::with_fetch(registrar.clone(), fetch, Executor::new_sync()); + let client = Client::with_fetch(Arc::downgrade(®istrar), fetch, Executor::new_sync()); // when let (tx, rx) = mpsc::channel(); - client.fetch(2.into(), Default::default(), Box::new(move |result| { + client.fetch(H256::from_low_u64_be(2), Default::default(), Box::new(move |result| { tx.send(result).unwrap(); })); @@ -245,36 +248,36 @@ mod tests { #[test] fn should_return_hash_mismatch() { // given - let registrar = Arc::new(registrar()); + let registrar = Arc::new(registrar()) as Arc; let fetch = FakeFetch::new(Some(1)); - let mut client = Client::with_fetch(registrar.clone(), fetch, Executor::new_sync()); + let mut client = Client::with_fetch(Arc::downgrade(®istrar), fetch, Executor::new_sync()); let path = random_temp_path(); let path2 = path.clone(); client.random_path = Arc::new(move || path2.clone()); // when let (tx, rx) = mpsc::channel(); - client.fetch(2.into(), Default::default(), Box::new(move |result| { + client.fetch(H256::from_low_u64_be(2), Default::default(), Box::new(move |result| { tx.send(result).unwrap(); })); // then let result = rx.recv().unwrap(); - let hash = "0x2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".into(); - assert_eq!(result.unwrap_err(), Error::HashMismatch { expected: 2.into(), got: hash }); + let hash = H256::from_str("2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e").unwrap(); + assert_eq!(result.unwrap_err(), Error::HashMismatch { expected: H256::from_low_u64_be(2), got: hash }); assert!(!path.exists(), "Temporary file should be removed."); } #[test] fn should_return_path_if_hash_matches() { // given - let registrar = Arc::new(registrar()); + let registrar = Arc::new(registrar()) as Arc; let fetch = FakeFetch::new(Some(1)); - let client = Client::with_fetch(registrar.clone(), fetch, Executor::new_sync()); + let client = Client::with_fetch(Arc::downgrade(®istrar), fetch, Executor::new_sync()); // when let (tx, rx) = mpsc::channel(); - client.fetch("0x2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".into(), + client.fetch(H256::from_str("2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e").unwrap(), Default::default(), Box::new(move |result| { tx.send(result).unwrap(); })); diff --git a/updater/hash-fetch/src/lib.rs b/updater/hash-fetch/src/lib.rs index a9ddc7363e..677a6df24a 100644 --- a/updater/hash-fetch/src/lib.rs +++ b/updater/hash-fetch/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -21,6 +21,7 @@ #[macro_use] extern crate log; +extern crate call_contract; extern crate ethabi; extern crate parity_bytes as bytes; extern crate ethereum_types; @@ -32,10 +33,11 @@ extern crate parity_runtime; extern crate rand; extern crate rustc_hex; extern crate registrar; +extern crate types; pub extern crate fetch; -#[macro_use] +// #[macro_use] extern crate ethabi_derive; #[macro_use] extern crate ethabi_contract; diff --git a/updater/hash-fetch/src/urlhint.rs b/updater/hash-fetch/src/urlhint.rs index 73520fd3b7..d60b602895 100644 --- a/updater/hash-fetch/src/urlhint.rs +++ b/updater/hash-fetch/src/urlhint.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,15 +16,14 @@ //! URLHint Contract -use std::sync::Arc; +use std::sync::Weak; use rustc_hex::ToHex; use mime::{self, Mime}; use mime_guess; -use futures::{future, Future}; -use futures::future::Either; use ethereum_types::{H256, Address}; -use registrar::{Registrar, RegistrarClient, Asynchronous}; +use registrar::RegistrarClient; +use types::ids::BlockId; use_contract!(urlhint, "res/urlhint.json"); @@ -95,20 +94,18 @@ pub enum URLHintResult { /// URLHint Contract interface pub trait URLHint: Send + Sync { /// Resolves given id to registrar entry. - fn resolve(&self, id: H256) -> Box, Error = String> + Send>; + fn resolve(&self, id: H256) -> Result, String>; } /// `URLHintContract` API pub struct URLHintContract { - registrar: Registrar, - client: Arc>, + client: Weak, } impl URLHintContract { /// Creates new `URLHintContract` - pub fn new(client: Arc>) -> Self { + pub fn new(client: Weak) -> Self { URLHintContract { - registrar: Registrar::new(client.clone()), client: client, } } @@ -123,10 +120,12 @@ fn get_urlhint_content(account_slash_repo: String, owner: Address) -> Content { } } -fn decode_urlhint_output(output: (String, [u8; 20], Address)) -> Option { - let (account_slash_repo, commit, owner) = output; - - if owner == Address::default() { +fn decode_urlhint_output( + account_slash_repo: String, + commit: [u8; 20], + owner: Address +) -> Option { + if owner == Address::zero() { return None; } @@ -159,20 +158,26 @@ fn decode_urlhint_output(output: (String, [u8; 20], Address)) -> Option Box, Error = String> + Send> { - let client = self.client.clone(); - - let future = self.registrar.get_address(GITHUB_HINT) - .and_then(move |addr| if !addr.is_zero() { - let data = urlhint::functions::entries::encode_input(id); - let result = client.call_contract(addr, data) - .and_then(move |output| urlhint::functions::entries::decode_output(&output).map_err(|e| e.to_string())) - .map(decode_urlhint_output); - Either::B(result) - } else { - Either::A(future::ok(None)) - }); - Box::new(future) + fn resolve(&self, id: H256) -> Result, String> { + use urlhint::urlhint::functions::entries::{encode_input, decode_output}; + + let client = self.client.clone().upgrade() + .ok_or_else(|| "Registrar/contract client unavailable".to_owned())?; + + let returned_address = client.get_address(GITHUB_HINT, BlockId::Latest)?; + + if let Some(address) = returned_address { + let data = encode_input(id); + let output_bytes = client.call_contract(BlockId::Latest, address, data)?; + let (account_slash_repo, commit, owner) = decode_output(&output_bytes) + .map_err(|e| e.to_string())?; + + let url_hint = decode_urlhint_output(account_slash_repo, commit, owner); + + Ok(url_hint) + } else { + Ok(None) + } } } @@ -205,13 +210,12 @@ pub mod tests { use std::str::FromStr; use rustc_hex::FromHex; - use futures::{Future, IntoFuture}; - use super::*; use super::guess_mime_type; use parking_lot::Mutex; use ethereum_types::Address; use bytes::{Bytes, ToPretty}; + use call_contract::CallContract; pub struct FakeRegistrar { pub calls: Arc>>, @@ -235,20 +239,32 @@ pub mod tests { } } - impl RegistrarClient for FakeRegistrar { - type Call = Asynchronous; + impl CallContract for FakeRegistrar { + fn call_contract( + &self, + _block: BlockId, + address: Address, + data: Bytes + ) -> Result { + self.calls.lock().push((address.to_hex(), data.to_hex())); + let res = self.responses.lock().remove(0); - fn registrar_address(&self) -> Result { - Ok(REGISTRAR.parse().unwrap()) + res } + } - fn call_contract(&self, address: Address, data: Bytes) -> Self::Call { - self.calls.lock().push((address.to_hex(), data.to_hex())); - let res = self.responses.lock().remove(0); - Box::new(res.into_future()) + impl RegistrarClient for FakeRegistrar { + fn registrar_address(&self) -> Option
{ + Some(REGISTRAR.parse().unwrap()) } } + fn h256_from_short_str(s: &str) -> H256 { + let mut bytes = s.as_bytes().to_vec(); + bytes.resize(32usize, 0u8); + H256::from_slice(bytes.as_ref()) + } + #[test] fn should_call_registrar_and_urlhint_contracts() { // given @@ -260,10 +276,12 @@ pub mod tests { registrar.responses.lock()[1] = Ok(resolve_result); let calls = registrar.calls.clone(); - let urlhint = URLHintContract::new(Arc::new(registrar)); + + let registrar = Arc::new(registrar) as Arc; + let urlhint = URLHintContract::new(Arc::downgrade(®istrar)); // when - let res = urlhint.resolve("test".as_bytes().into()).wait().unwrap(); + let res = urlhint.resolve(h256_from_short_str("test")).unwrap(); let calls = calls.lock(); let call0 = calls.get(0).expect("Registrar resolve called"); let call1 = calls.get(1).expect("URLHint Resolve called"); @@ -288,10 +306,12 @@ pub mod tests { Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()), Ok("0000000000000000000000000000000000000000000000000000000000000060ec4c1fe06c808fe3739858c347109b1f5f1ed4b5000000000000000000000000000000000000000000000000deadcafebeefbeefcafedeaddeedfeedffffffff0000000000000000000000000000000000000000000000000000000000000011657468636f72652f64616f2e636c61696d000000000000000000000000000000".from_hex().unwrap()), ]); - let urlhint = URLHintContract::new(Arc::new(registrar)); + + let registrar = Arc::new(registrar) as Arc; + let urlhint = URLHintContract::new(Arc::downgrade(®istrar)); // when - let res = urlhint.resolve("test".as_bytes().into()).wait().unwrap(); + let res = urlhint.resolve(h256_from_short_str("test")).unwrap(); // then assert_eq!(res, Some(URLHintResult::Dapp(GithubApp { @@ -310,10 +330,12 @@ pub mod tests { Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()), Ok("00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000deadcafebeefbeefcafedeaddeedfeedffffffff000000000000000000000000000000000000000000000000000000000000003c68747470733a2f2f7061726974792e696f2f6173736574732f696d616765732f657468636f72652d626c61636b2d686f72697a6f6e74616c2e706e6700000000".from_hex().unwrap()), ]); - let urlhint = URLHintContract::new(Arc::new(registrar)); + + let registrar = Arc::new(registrar) as Arc; + let urlhint = URLHintContract::new(Arc::downgrade(®istrar)); // when - let res = urlhint.resolve("test".as_bytes().into()).wait().unwrap(); + let res = urlhint.resolve(h256_from_short_str("test")).unwrap(); // then assert_eq!(res, Some(URLHintResult::Content(Content { @@ -330,7 +352,7 @@ pub mod tests { account: "test".into(), repo: "xyz".into(), commit: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], - owner: Address::default(), + owner: Address::zero(), }; // when diff --git a/updater/src/lib.rs b/updater/src/lib.rs index f6fad17199..0752d01a47 100644 --- a/updater/src/lib.rs +++ b/updater/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,8 +18,10 @@ #![warn(missing_docs)] +extern crate client_traits; extern crate common_types; extern crate ethabi; +extern crate ethabi_derive; extern crate ethcore; extern crate ethcore_sync as sync; extern crate ethereum_types; @@ -36,8 +38,6 @@ extern crate target_info; #[macro_use] extern crate ethabi_contract; #[macro_use] -extern crate ethabi_derive; -#[macro_use] extern crate lazy_static; #[macro_use] extern crate log; diff --git a/updater/src/service.rs b/updater/src/service.rs index b9ef2f9657..0af13af139 100644 --- a/updater/src/service.rs +++ b/updater/src/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/updater/src/types/all.rs b/updater/src/types/all.rs index f17480c8ad..1cb4d88d7a 100644 --- a/updater/src/types/all.rs +++ b/updater/src/types/all.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/updater/src/types/mod.rs b/updater/src/types/mod.rs index fab4ec4d0e..4bf0281514 100644 --- a/updater/src/types/mod.rs +++ b/updater/src/types/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/updater/src/types/release_track.rs b/updater/src/types/release_track.rs index 0648ebc47a..ab7160c059 100644 --- a/updater/src/types/release_track.rs +++ b/updater/src/types/release_track.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,12 +24,8 @@ use std::fmt; pub enum ReleaseTrack { /// Stable track. Stable = 1, - /// Beta track. - Beta = 2, /// Nightly track. - Nightly = 3, - /// Testing track. - Testing = 4, + Nightly = 2, /// No known track, also "current executable's track" when it's not yet known. Unknown = 0, } @@ -38,9 +34,7 @@ impl fmt::Display for ReleaseTrack { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", match *self { ReleaseTrack::Stable => "stable", - ReleaseTrack::Beta => "beta", ReleaseTrack::Nightly => "nightly", - ReleaseTrack::Testing => "testing", ReleaseTrack::Unknown => "unknown", }) } @@ -50,9 +44,7 @@ impl<'a> From<&'a str> for ReleaseTrack { fn from(s: &'a str) -> Self { match s { "stable" => ReleaseTrack::Stable, - "beta" => ReleaseTrack::Beta, "nightly" => ReleaseTrack::Nightly, - "testing" => ReleaseTrack::Testing, _ => ReleaseTrack::Unknown, } } @@ -62,9 +54,7 @@ impl From for ReleaseTrack { fn from(i: u8) -> Self { match i { 1 => ReleaseTrack::Stable, - 2 => ReleaseTrack::Beta, - 3 => ReleaseTrack::Nightly, - 4 => ReleaseTrack::Testing, + 2 => ReleaseTrack::Nightly, _ => ReleaseTrack::Unknown, } } @@ -83,36 +73,28 @@ mod tests { #[test] fn test_release_track_from() { assert_eq!(ReleaseTrack::Stable, 1u8.into()); - assert_eq!(ReleaseTrack::Beta, 2u8.into()); - assert_eq!(ReleaseTrack::Nightly, 3u8.into()); - assert_eq!(ReleaseTrack::Testing, 4u8.into()); + assert_eq!(ReleaseTrack::Nightly, 2u8.into()); assert_eq!(ReleaseTrack::Unknown, 0u8.into()); } #[test] fn test_release_track_into() { assert_eq!(1u8, u8::from(ReleaseTrack::Stable)); - assert_eq!(2u8, u8::from(ReleaseTrack::Beta)); - assert_eq!(3u8, u8::from(ReleaseTrack::Nightly)); - assert_eq!(4u8, u8::from(ReleaseTrack::Testing)); + assert_eq!(2u8, u8::from(ReleaseTrack::Nightly)); assert_eq!(0u8, u8::from(ReleaseTrack::Unknown)); } #[test] fn test_release_track_from_str() { assert_eq!(ReleaseTrack::Stable, "stable".into()); - assert_eq!(ReleaseTrack::Beta, "beta".into()); assert_eq!(ReleaseTrack::Nightly, "nightly".into()); - assert_eq!(ReleaseTrack::Testing, "testing".into()); assert_eq!(ReleaseTrack::Unknown, "unknown".into()); } #[test] fn test_release_track_into_str() { assert_eq!("stable", ReleaseTrack::Stable.to_string()); - assert_eq!("beta", ReleaseTrack::Beta.to_string()); assert_eq!("nightly", ReleaseTrack::Nightly.to_string()); - assert_eq!("testing", ReleaseTrack::Testing.to_string()); assert_eq!("unknown", ReleaseTrack::Unknown.to_string()); } } diff --git a/updater/src/types/version_info.rs b/updater/src/types/version_info.rs index b55e08cb19..586a12c95a 100644 --- a/updater/src/types/version_info.rs +++ b/updater/src/types/version_info.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/updater/src/updater.rs b/updater/src/updater.rs index 70adc9f3b3..625e8babd8 100644 --- a/updater/src/updater.rs +++ b/updater/src/updater.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -25,10 +25,14 @@ use parking_lot::{Mutex, MutexGuard}; use rand::{self, Rng}; use target_info::Target; -use common_types::BlockNumber; -use common_types::filter::Filter; -use ethcore::client::{BlockId, BlockChainClient, ChainNotify, NewBlocks}; -use ethereum_types::H256; +use common_types::{ + BlockNumber, + ids::BlockId, + filter::Filter, + chain_notify::NewBlocks, +}; +use client_traits::{BlockChainClient, ChainNotify}; +use ethereum_types::{H256, H160}; use hash_fetch::{self as fetch, HashFetch}; use parity_path::restrict_permissions_owner; use service::Service; @@ -140,11 +144,11 @@ pub struct Updater>>, - client: Weak, - sync: Option>, + client: Weak, + sync: Option>, fetcher: F, operations_client: O, - exit_handler: Mutex>>, + exit_handler: Mutex>>, time_provider: T, rng: R, @@ -159,7 +163,7 @@ pub struct Updater H256 { + let mut bytes = s.as_bytes().to_vec(); + bytes.resize(H256::len_bytes(), 0); + H256::from_slice(&bytes) } /// Client trait for getting latest release information from operations contract. @@ -192,11 +205,11 @@ pub trait OperationsClient: Send + Sync + 'static { /// `OperationsClient` that delegates calls to the operations contract. pub struct OperationsContractClient { - client: Weak, + client: Weak, } impl OperationsContractClient { - fn new(client: Weak) -> Self { + fn new(client: Weak) -> Self { OperationsContractClient { client } @@ -238,8 +251,11 @@ impl OperationsClient for OperationsContractClient { } 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)); + let address = client.get_address("operations", 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, this.hash); @@ -267,8 +283,7 @@ impl OperationsClient for OperationsContractClient { // 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 != this.version.minor { let track = match in_minor.as_ref().expect(PROOF).version.track { - ReleaseTrack::Beta => ReleaseTrack::Stable, - ReleaseTrack::Nightly => ReleaseTrack::Beta, + ReleaseTrack::Nightly => ReleaseTrack::Stable, _ => { in_minor = None; break; } }; @@ -291,7 +306,7 @@ impl OperationsClient for OperationsContractClient { 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 address = client.get_address("operations", BlockId::Latest).unwrap_or(None)?; let topics = operations::events::release_added::filter(Some(*CLIENT_ID_HASH), Some(release.fork.into()), Some(release.is_critical)); let topics = vec![topics.topic0, topics.topic1, topics.topic2, topics.topic3]; @@ -355,8 +370,8 @@ impl GenRange for ThreadRngGenRange { impl Updater { /// `Updater` constructor pub fn new( - client: &Weak, - sync: &Weak, + client: &Weak, + sync: &Weak, update_policy: UpdatePolicy, fetcher: fetch::Client, ) -> Arc { @@ -373,7 +388,7 @@ impl Updater { VersionInfo { track: ReleaseTrack::Stable, version: Version::new(1, 3, 7), - hash: 0.into(), + hash: H160::zero(), } } else { VersionInfo::this() @@ -670,8 +685,8 @@ impl Updater self.poll(), + match self.sync.as_ref().and_then(Weak::upgrade) { + Some(ref s) if !s.is_major_syncing() => self.poll(), _ => {}, } } @@ -710,7 +725,7 @@ pub mod tests { use std::sync::Arc; use semver::Version; use tempdir::TempDir; - use ethcore::client::{TestBlockChainClient, EachBlockWith}; + use ethcore::test_helpers::{TestBlockChainClient, EachBlockWith}; use self::fetch::Error; use super::*; @@ -743,7 +758,7 @@ pub mod tests { #[derive(Clone)] struct FakeFetch { - on_done: Arc) + Send>>>>, + on_done: Arc) + Send>>>>, } impl FakeFetch { @@ -759,7 +774,7 @@ pub mod tests { } impl HashFetch for FakeFetch { - fn fetch(&self, _hash: H256, _abort: fetch::Abort, on_done: Box) + Send>) { + fn fetch(&self, _hash: H256, _abort: fetch::Abort, on_done: Box) + Send>) { *self.on_done.lock() = Some(on_done); } } @@ -825,9 +840,9 @@ pub mod tests { let rng = FakeGenRange::new(); let this = VersionInfo { - track: ReleaseTrack::Beta, + track: ReleaseTrack::Nightly, version: Version::parse("1.0.0").unwrap(), - hash: 0.into(), + hash: H160::zero(), }; let updater = Arc::new(Updater { @@ -865,16 +880,16 @@ pub mod tests { fn new_upgrade(version: &str) -> (VersionInfo, ReleaseInfo, OperationsInfo) { let latest_version = VersionInfo { - track: ReleaseTrack::Beta, + track: ReleaseTrack::Nightly, version: Version::parse(version).unwrap(), - hash: 1.into(), + hash: H160::from_low_u64_be(1), }; let latest_release = ReleaseInfo { version: latest_version.clone(), is_critical: false, fork: 0, - binary: Some(0.into()), + binary: Some(H256::zero()), }; let latest = OperationsInfo { @@ -1253,4 +1268,11 @@ pub mod tests { // and since our update policy requires consensus, the client should be disabled assert!(client.is_disabled()); } + + #[test] + fn static_hashes_do_not_panic() { + let client_id_hash: H256 = *CLIENT_ID_HASH; + assert_eq!(&format!("{:x}", client_id_hash), "7061726974790000000000000000000000000000000000000000000000000000"); + let _: H256 = *PLATFORM_ID_HASH; + } } diff --git a/util/EIP-152/Cargo.toml b/util/EIP-152/Cargo.toml index fe65d01109..1f7131fe45 100644 --- a/util/EIP-152/Cargo.toml +++ b/util/EIP-152/Cargo.toml @@ -11,4 +11,12 @@ license = "GPL-3.0" edition = "2018" [dependencies] +arrayref = "0.3.5" + +[dev-dependencies] +criterion = "0.3" rustc-hex = "2.0.1" + +[[bench]] +name = "bench" +harness = false diff --git a/util/EIP-152/LICENSE b/util/EIP-152/LICENSE new file mode 100644 index 0000000000..09df722dc2 --- /dev/null +++ b/util/EIP-152/LICENSE @@ -0,0 +1,25 @@ +This program is copyright 2020 Parity Technologies Limited and its licensors. + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + +Some portions of the program (“the Software”) are Copyright (c) 2018 Jack O'Connor +and the following relates solely to such portions: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/util/EIP-152/benches/bench.rs b/util/EIP-152/benches/bench.rs new file mode 100644 index 0000000000..cb4126983b --- /dev/null +++ b/util/EIP-152/benches/bench.rs @@ -0,0 +1,191 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + + +use criterion::{Criterion, criterion_group, criterion_main, black_box, Throughput, BenchmarkId}; +use std::mem; +use std::sync::atomic::{AtomicPtr, Ordering}; +use eip_152::portable; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use eip_152::avx2; + +type FnRaw = *mut (); +type Blake2bF = fn(&mut [u64; 8], [u64; 16], [u64; 2], bool, usize); + +static FN: AtomicPtr<()> = AtomicPtr::new(detect as FnRaw); + +fn detect(state: &mut [u64; 8], message: [u64; 16], count: [u64; 2], f: bool, rounds: usize) { + let fun = if is_x86_feature_detected!("avx2") { + avx2::compress as FnRaw + } else { + portable::compress as FnRaw + }; + FN.store(fun as FnRaw, Ordering::Relaxed); + unsafe { + mem::transmute::(fun)(state, message, count, f, rounds) + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub fn avx_ifunc_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("avx2_ifunc"); + + for rounds in [12, 50, 100].iter() { + group.throughput(Throughput::Elements(*rounds as u64)); + group.bench_with_input( + BenchmarkId::new("rounds", rounds), + &rounds, + |b, rounds| { + let mut state = [ + 0x6a09e667f2bdc948_u64, 0xbb67ae8584caa73b_u64, + 0x3c6ef372fe94f82b_u64, 0xa54ff53a5f1d36f1_u64, + 0x510e527fade682d1_u64, 0x9b05688c2b3e6c1f_u64, + 0x1f83d9abfb41bd6b_u64, 0x5be0cd19137e2179_u64, + ]; + + let message = [ + 0x0000000000636261_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + ]; + let count = [3, 0]; + let f = true; + + b.iter(|| { + unsafe { + let fun = FN.load(Ordering::Relaxed); + mem::transmute:: + (fun) + ( + black_box(&mut state), + black_box(message), + black_box(count), + black_box(f), + black_box(**rounds as usize), + ); + } + }); + }, + ); + } + + group.finish(); +} + + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub fn avx_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("avx2"); + + for rounds in [12, 50, 100].iter() { + group.throughput(Throughput::Elements(*rounds as u64)); + group.bench_with_input( + BenchmarkId::new("rounds", rounds), + &rounds, + |b, rounds| { + let mut state = [ + 0x6a09e667f2bdc948_u64, 0xbb67ae8584caa73b_u64, + 0x3c6ef372fe94f82b_u64, 0xa54ff53a5f1d36f1_u64, + 0x510e527fade682d1_u64, 0x9b05688c2b3e6c1f_u64, + 0x1f83d9abfb41bd6b_u64, 0x5be0cd19137e2179_u64, + ]; + + let message = [ + 0x0000000000636261_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + ]; + let count = [3, 0]; + let f = true; + + b.iter(|| { + + unsafe { + avx2::compress( + black_box(&mut state), + black_box(message), + black_box(count), + black_box(f), + black_box(**rounds as usize), + ); + } + }); + }, + ); + } + + group.finish(); +} + + +pub fn portable_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("portable_impl"); + + for rounds in [12, 50, 100].iter() { + group.throughput(Throughput::Elements(*rounds as u64)); + group.bench_with_input( + BenchmarkId::new("rounds", rounds), + &rounds, + |b, rounds| { + let mut state = [ + 0x6a09e667f2bdc948_u64, 0xbb67ae8584caa73b_u64, + 0x3c6ef372fe94f82b_u64, 0xa54ff53a5f1d36f1_u64, + 0x510e527fade682d1_u64, 0x9b05688c2b3e6c1f_u64, + 0x1f83d9abfb41bd6b_u64, 0x5be0cd19137e2179_u64, + ]; + + let message = [ + 0x0000000000636261_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + 0x0000000000000000_u64, 0x0000000000000000_u64, + ]; + let count = [3, 0]; + let f = true; + + b.iter(|| { + portable::compress( + black_box(&mut state), + black_box(message), + black_box(count), + black_box(f), + black_box(**rounds as usize), + ); + }); + }, + ); + } + + group.finish(); +} + +criterion_group!(benches, avx_benchmark, avx_ifunc_benchmark, portable_benchmark); +criterion_main!(benches); \ No newline at end of file diff --git a/util/EIP-152/src/avx2.rs b/util/EIP-152/src/avx2.rs new file mode 100644 index 0000000000..5fa2fcb4f6 --- /dev/null +++ b/util/EIP-152/src/avx2.rs @@ -0,0 +1,471 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! AVX2 implementation of the blake2b compression function. +use crate::IV; + +#[cfg(target_arch = "x86")] +use core::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use core::arch::x86_64::*; +use arrayref::{array_refs, mut_array_refs}; + +// Adapted from https://github.com/rust-lang-nursery/stdsimd/pull/479. +macro_rules! _MM_SHUFFLE { + ($z:expr, $y:expr, $x:expr, $w:expr) => { + ($z << 6) | ($y << 4) | ($x << 2) | $w + }; +} + +/// The Blake2b compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 +/// Takes as an argument the state vector `state`, message block vector `message`, offset counter, final +/// block indicator flag `f`, and number of rounds `rounds`. The state vector provided as the first +/// parameter is modified by the function. +/// +/// `g1` only operates on `x` from the original g function. +/// ``` +/// fn portable_g1(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64) { +/// v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); +/// v[d] = (v[d] ^ v[a]).rotate_right(32); +/// v[c] = v[c].wrapping_add(v[d]); +/// v[b] = (v[b] ^ v[c]).rotate_right(24); +/// } +/// ``` +/// +/// `g2` only operates on `y` from the originial g function. +/// ``` +/// fn portable_g2(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, y: u64) { +/// v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); +/// v[d] = (v[d] ^ v[a]).rotate_right(16); +/// v[c] = v[c].wrapping_add(v[d]); +/// v[b] = (v[b] ^ v[c]).rotate_right(63); +/// } +/// ``` +/// +/// Message mixing is done based on sigma values, for a given round. +/// +/// # Example +/// +/// `SIGMA` for round 1 i.e `SIGMA[0]` = `[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]`; +/// ``` +/// let s = &SIGMA[0 % 10]; +/// // a, b, c, d, x +/// g(&mut v, 0, 4, 8 , 12, m[s[0]]); +/// g(&mut v, 1, 5, 9 , 13, m[s[2]]); +/// g(&mut v, 2, 6, 10, 14, m[s[4]]); +/// g(&mut v, 3, 7, 11, 15, m[s[6]]); +/// +/// let a = v[..4]; +/// let b = v[4..8]; +/// let c = v[8..12]; +/// let d = v[12..16]; +/// let mut b0 = [m[0], m[2], m[4], m[6]]; +/// +/// g1(&mut a, &mut b, &mut c, &mut d, &mut b0); +/// // ... then contruct b0 for `g2` etc. +/// ``` +/// +#[target_feature(enable = "avx2")] +pub unsafe fn compress(state: &mut [u64; 8], message: [u64; 16], count: [u64; 2], f: bool, rounds: usize) { + // get a mutable reference to state[0..4], state[4..] + let (state_low, state_high) = mut_array_refs!(state, 4, 4); + // get a reference to IV[0..4], IV[4..] + let (iv_low, iv_high) = array_refs!(&IV, 4, 4); + + // loads them into an __m256i + let mut a = loadu(state_low); + let mut b = loadu(state_high); + let mut c = loadu(iv_low); + + // !a = xor(a, xor(a, !a)) + let inverse = if f { + iv_high[3] ^ !iv_high[3] + } else { + 0 + }; + + let flags = set4( + count[0], + count[1], + inverse, + 0, + ); + + let mut d = xor(loadu(iv_high), flags); + + // get a reference to message[(0..2)+,] + let msg_chunks = array_refs!(&message, 2, 2, 2, 2, 2, 2, 2, 2); + // load each message [u64; 2] into an __m128i, broadcast it into both lanes of an __m256i. + + // m0 = __m256i([message[0], message[1], message[0], message[1]]) + let m0 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.0)); + // m1 = __m256i([message[2], message[3], message[2], message[3]]) + let m1 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.1)); + // m2 = __m256i([message[4], message[5], message[4], message[5]]) + let m2 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.2)); + // m3 = __m256i([message[6], message[7], message[6], message[7]]) + let m3 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.3)); + // m4 = __m256i([message[8], message[9], message[8], message[9]]) + let m4 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.4)); + // m5 = __m256i([message[10], message[11], message[10], message[11]]) + let m5 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.5)); + // m6 = __m256i([message[12], message[13], message[12], message[13]]) + let m6 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.6)); + // m7 = __m256i([message[14], message[15], message[14], message[15]]) + let m7 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.7)); + + let iv0 = a; + let iv1 = b; + + let mut t0; + let mut t1; + let mut b0; + + for i in 0..rounds { + match i % 10 { + 0 => { + t0 = _mm256_unpacklo_epi64(m0, m1); // ([0, 1, 0, 1], [2, 3, 2, 3]) = [0, 2, 0, 2] + t1 = _mm256_unpacklo_epi64(m2, m3); // ([4, 5, 4, 5], [6, 7, 6, 7]) = [4, 6, 4, 6] + b0 = _mm256_blend_epi32(t0, t1, 0xF0); // ([0, 2, 0, 2], [4, 6, 4, 6]) = [0, 2, 4, 6] + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpackhi_epi64(m0, m1); // ([0, 1, 0, 1], [2, 3, 2, 3]) = [1, 3, 1, 3] + t1 = _mm256_unpackhi_epi64(m2, m3); // ([4, 5, 4, 5], [6, 7, 6, 7]) = [5, 7, 5, 7] + b0 = _mm256_blend_epi32(t0, t1, 0xF0); // ([1, 3, 1, 3], [5, 7, 5, 7]) = [1, 3, 5, 7] + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + diagonalize(&mut a, &mut b, &mut c, &mut d); + t0 = _mm256_unpacklo_epi64(m7, m4); // ([14, 15, 14, 15], [8, 9, 8, 9]) = [14, 8, 14, 8] + t1 = _mm256_unpacklo_epi64(m5, m6); // ([10, 11, 10, 11], [12, 13, 12, 13]) = [10, 12, 10, 12] + b0 = _mm256_blend_epi32(t0, t1, 0xF0); // ([14, 8, 14, 8], [10, 12, 10, 12]) = [14, 8, 10, 12] + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpackhi_epi64(m7, m4); // ([14, 15, 14, 15], [8, 9, 8, 9]) = [15, 9, 15, 9] + t1 = _mm256_unpackhi_epi64(m5, m6); // ([10, 11, 10, 11], [12, 13, 12, 13]) = [11, 13, 11, 13] + b0 = _mm256_blend_epi32(t0, t1, 0xF0); // ([15, 9, 15, 9], [11, 13, 11, 13]) = [15, 9, 11, 13] + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + undiagonalize(&mut a, &mut b, &mut c, &mut d); + } + 1 => { + t0 = _mm256_unpacklo_epi64(m7, m2); + t1 = _mm256_unpackhi_epi64(m4, m6); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpacklo_epi64(m5, m4); + t1 = _mm256_alignr_epi8(m3, m7, 8); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + diagonalize(&mut a, &mut b, &mut c, &mut d); + t0 = _mm256_unpackhi_epi64(m2, m0); + t1 = _mm256_blend_epi32(m5, m0, 0x33); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_alignr_epi8(m6, m1, 8); + t1 = _mm256_blend_epi32(m3, m1, 0x33); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + undiagonalize(&mut a, &mut b, &mut c, &mut d); + } + 2 => { + // round 3 + t0 = _mm256_alignr_epi8(m6, m5, 8); + t1 = _mm256_unpackhi_epi64(m2, m7); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpacklo_epi64(m4, m0); + t1 = _mm256_blend_epi32(m6, m1, 0x33); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + diagonalize(&mut a, &mut b, &mut c, &mut d); + t0 = _mm256_alignr_epi8(m5, m4, 8); + t1 = _mm256_unpackhi_epi64(m1, m3); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpacklo_epi64(m2, m7); + t1 = _mm256_blend_epi32(m0, m3, 0x33); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + undiagonalize(&mut a, &mut b, &mut c, &mut d); + } + 3 => { + // round 4 + t0 = _mm256_unpackhi_epi64(m3, m1); + t1 = _mm256_unpackhi_epi64(m6, m5); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpackhi_epi64(m4, m0); + t1 = _mm256_unpacklo_epi64(m6, m7); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + diagonalize(&mut a, &mut b, &mut c, &mut d); + t0 = _mm256_alignr_epi8(m1, m7, 8); + t1 = _mm256_shuffle_epi32(m2, _MM_SHUFFLE!(1, 0, 3, 2)); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpacklo_epi64(m4, m3); + t1 = _mm256_unpacklo_epi64(m5, m0); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + undiagonalize(&mut a, &mut b, &mut c, &mut d); + } + 4 => { + // round 5 + t0 = _mm256_unpackhi_epi64(m4, m2); + t1 = _mm256_unpacklo_epi64(m1, m5); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_blend_epi32(m3, m0, 0x33); + t1 = _mm256_blend_epi32(m7, m2, 0x33); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + diagonalize(&mut a, &mut b, &mut c, &mut d); + t0 = _mm256_alignr_epi8(m7, m1, 8); + t1 = _mm256_alignr_epi8(m3, m5, 8); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpackhi_epi64(m6, m0); + t1 = _mm256_unpacklo_epi64(m6, m4); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + undiagonalize(&mut a, &mut b, &mut c, &mut d); + } + 5 => { + // round 6 + t0 = _mm256_unpacklo_epi64(m1, m3); + t1 = _mm256_unpacklo_epi64(m0, m4); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpacklo_epi64(m6, m5); + t1 = _mm256_unpackhi_epi64(m5, m1); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + diagonalize(&mut a, &mut b, &mut c, &mut d); + t0 = _mm256_alignr_epi8(m2, m0, 8); + t1 = _mm256_unpackhi_epi64(m3, m7); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpackhi_epi64(m4, m6); + t1 = _mm256_alignr_epi8(m7, m2, 8); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + undiagonalize(&mut a, &mut b, &mut c, &mut d); + } + 6 => { + // round 7 + t0 = _mm256_blend_epi32(m0, m6, 0x33); + t1 = _mm256_unpacklo_epi64(m7, m2); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpackhi_epi64(m2, m7); + t1 = _mm256_alignr_epi8(m5, m6, 8); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + diagonalize(&mut a, &mut b, &mut c, &mut d); + t0 = _mm256_unpacklo_epi64(m4, m0); + t1 = _mm256_blend_epi32(m4, m3, 0x33); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpackhi_epi64(m5, m3); + t1 = _mm256_shuffle_epi32(m1, _MM_SHUFFLE!(1, 0, 3, 2)); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + undiagonalize(&mut a, &mut b, &mut c, &mut d); + } + 7 => { + // round 8 + t0 = _mm256_unpackhi_epi64(m6, m3); + t1 = _mm256_blend_epi32(m1, m6, 0x33); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_alignr_epi8(m7, m5, 8); + t1 = _mm256_unpackhi_epi64(m0, m4); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + diagonalize(&mut a, &mut b, &mut c, &mut d); + t0 = _mm256_blend_epi32(m2, m1, 0x33); + t1 = _mm256_alignr_epi8(m4, m7, 8); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpacklo_epi64(m5, m0); + t1 = _mm256_unpacklo_epi64(m2, m3); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + undiagonalize(&mut a, &mut b, &mut c, &mut d); + } + 8 => { + // round 9 + t0 = _mm256_unpacklo_epi64(m3, m7); + t1 = _mm256_alignr_epi8(m0, m5, 8); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpackhi_epi64(m7, m4); + t1 = _mm256_alignr_epi8(m4, m1, 8); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + diagonalize(&mut a, &mut b, &mut c, &mut d); + t0 = _mm256_unpacklo_epi64(m5, m6); + t1 = _mm256_unpackhi_epi64(m6, m0); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_alignr_epi8(m1, m2, 8); + t1 = _mm256_alignr_epi8(m2, m3, 8); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + undiagonalize(&mut a, &mut b, &mut c, &mut d); + } + _ => { + // round 10 + t0 = _mm256_unpacklo_epi64(m5, m4); + t1 = _mm256_unpackhi_epi64(m3, m0); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_unpacklo_epi64(m1, m2); + t1 = _mm256_blend_epi32(m2, m3, 0x33); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + diagonalize(&mut a, &mut b, &mut c, &mut d); + t0 = _mm256_unpackhi_epi64(m6, m7); + t1 = _mm256_unpackhi_epi64(m4, m1); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g1(&mut a, &mut b, &mut c, &mut d, &mut b0); + t0 = _mm256_blend_epi32(m5, m0, 0x33); + t1 = _mm256_unpacklo_epi64(m7, m6); + b0 = _mm256_blend_epi32(t0, t1, 0xF0); + g2(&mut a, &mut b, &mut c, &mut d, &mut b0); + undiagonalize(&mut a, &mut b, &mut c, &mut d); + } + } + } + + a = xor(a, c); + b = xor(b, d); + a = xor(a, iv0); + b = xor(b, iv1); + + storeu(a, state_low); + storeu(b, state_high); +} + + +#[inline(always)] +unsafe fn loadu(src: *const [u64; 4]) -> __m256i { + // This is an unaligned load, so the pointer cast is allowed. + _mm256_loadu_si256(src as *const __m256i) +} + +#[inline(always)] +unsafe fn storeu(src: __m256i, dest: *mut [u64; 4]) { + // This is an unaligned store, so the pointer cast is allowed. + _mm256_storeu_si256(dest as *mut __m256i, src) +} + +#[inline(always)] +unsafe fn loadu_128(mem_addr: &[u64; 2]) -> __m128i { + _mm_loadu_si128(mem_addr.as_ptr() as *const __m128i) +} + +#[inline(always)] +unsafe fn add(a: __m256i, b: __m256i) -> __m256i { + _mm256_add_epi64(a, b) +} + +#[inline(always)] +unsafe fn xor(a: __m256i, b: __m256i) -> __m256i { + _mm256_xor_si256(a, b) +} + +#[inline(always)] +unsafe fn set4(a: u64, b: u64, c: u64, d: u64) -> __m256i { + _mm256_setr_epi64x(a as i64, b as i64, c as i64, d as i64) +} + +#[inline(always)] +unsafe fn rotate_right_32(x: __m256i) -> __m256i { + _mm256_shuffle_epi32(x, _MM_SHUFFLE!(2, 3, 0, 1)) +} + +#[inline(always)] +unsafe fn rotate_right_24(x: __m256i) -> __m256i { + let rotate24 = _mm256_setr_epi8( + 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10, 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, + 14, 15, 8, 9, 10, + ); + _mm256_shuffle_epi8(x, rotate24) +} + +#[inline(always)] +unsafe fn rotate_right_16(x: __m256i) -> __m256i { + let rotate16 = _mm256_setr_epi8( + 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9, 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, + 13, 14, 15, 8, 9, + ); + _mm256_shuffle_epi8(x, rotate16) +} + +#[inline(always)] +unsafe fn rotate_right_63(x: __m256i) -> __m256i { + _mm256_or_si256(_mm256_srli_epi64(x, 63), add(x, x)) +} + +#[inline(always)] +unsafe fn g1(a: &mut __m256i, b: &mut __m256i, c: &mut __m256i, d: &mut __m256i, m: &mut __m256i) { + *a = add(*a, *m); + *a = add(*a, *b); + *d = xor(*d, *a); + *d = rotate_right_32(*d); + *c = add(*c, *d); + *b = xor(*b, *c); + *b = rotate_right_24(*b); +} + +#[inline(always)] +unsafe fn g2(a: &mut __m256i, b: &mut __m256i, c: &mut __m256i, d: &mut __m256i, m: &mut __m256i) { + *a = add(*a, *m); + *a = add(*a, *b); + *d = xor(*d, *a); + *d = rotate_right_16(*d); + *c = add(*c, *d); + *b = xor(*b, *c); + *b = rotate_right_63(*b); +} + +// Note the optimization here of leaving b as the unrotated row, rather than a. +// All the message loads below are adjusted to compensate for this. See +// discussion at https://github.com/sneves/blake2-avx2/pull/4 +#[inline(always)] +unsafe fn diagonalize(a: &mut __m256i, _b: &mut __m256i, c: &mut __m256i, d: &mut __m256i) { + *a = _mm256_permute4x64_epi64(*a, _MM_SHUFFLE!(2, 1, 0, 3)); + *d = _mm256_permute4x64_epi64(*d, _MM_SHUFFLE!(1, 0, 3, 2)); + *c = _mm256_permute4x64_epi64(*c, _MM_SHUFFLE!(0, 3, 2, 1)); +} + +// Note the optimization here of leaving b as the unrotated row, rather than a. +// All the message loads below are adjusted to compensate for this. See +// discussion at https://github.com/sneves/blake2-avx2/pull/4 +#[inline(always)] +unsafe fn undiagonalize(a: &mut __m256i, _b: &mut __m256i, c: &mut __m256i, d: &mut __m256i) { + *a = _mm256_permute4x64_epi64(*a, _MM_SHUFFLE!(0, 3, 2, 1)); + *d = _mm256_permute4x64_epi64(*d, _MM_SHUFFLE!(1, 0, 3, 2)); + *c = _mm256_permute4x64_epi64(*c, _MM_SHUFFLE!(2, 1, 0, 3)); +} + + +#[cfg(test)] +mod tests { + #[test] + fn test_mm_shuffle() { + assert_eq!(_MM_SHUFFLE!(0, 1, 1, 3), 0b00_01_01_11); + assert_eq!(_MM_SHUFFLE!(3, 1, 1, 0), 0b11_01_01_00); + assert_eq!(_MM_SHUFFLE!(1, 2, 2, 1), 0b01_10_10_01); + } +} diff --git a/util/EIP-152/src/lib.rs b/util/EIP-152/src/lib.rs index fd68b9072d..32ce9bd1c9 100644 --- a/util/EIP-152/src/lib.rs +++ b/util/EIP-152/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,20 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +pub mod portable; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +pub mod avx2; + /// The precomputed values for BLAKE2b [from the spec](https://tools.ietf.org/html/rfc7693#section-2.7) /// There are 10 16-byte arrays - one for each round /// the entries are calculated from the sigma constants. const SIGMA: [[usize; 16]; 10] = [ - [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], - [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], - [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], - [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], - [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], - [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], - [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], - [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], - [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], ]; @@ -38,58 +42,30 @@ const IV: [u64; 8] = [ 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179, ]; - -#[inline(always)] -/// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1 -fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { - v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); - v[d] = (v[d] ^ v[a]).rotate_right(32); - v[c] = v[c].wrapping_add(v[d]); - v[b] = (v[b] ^ v[c]).rotate_right(24); - v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); - v[d] = (v[d] ^ v[a]).rotate_right(16); - v[c] = v[c].wrapping_add(v[d]); - v[b] = (v[b] ^ v[c]).rotate_right(63); -} - -/// The Blake2 compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 -/// Takes as an argument the state vector `h`, message block vector `m`, offset counter `t`, final -/// block indicator flag `f`, and number of rounds `rounds`. The state vector provided as the first -/// parameter is modified by the function. -pub fn compress(h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool, rounds: usize) { - let mut v = [0u64; 16]; - v[..h.len()].copy_from_slice(h); // First half from state. - v[h.len()..].copy_from_slice(&IV); // Second half from IV. - - v[12] ^= t[0]; - v[13] ^= t[1]; - - if f { - v[14] = !v[14] // Invert all bits if the last-block-flag is set. - } - for i in 0..rounds { - // Message word selection permutation for this round. - let s = &SIGMA[i % 10]; - g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]); - g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]); - g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]); - g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]); - - g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]); - g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]); - g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]); - g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]); +/// blake2b compression function +pub fn compress(state: &mut [u64; 8], message: [u64; 16], count: [u64; 2], f: bool, rounds: usize) { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + if is_x86_feature_detected!("avx2") { + unsafe { + return avx2::compress(state, message, count, f, rounds) + } + } else { + return portable::compress(state, message, count, f, rounds) + }; } - for i in 0..8 { - h[i] ^= v[i] ^ v[i + 8]; - } + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + portable::compress(state, message, count, f, rounds); } #[cfg(test)] mod tests { - use crate::compress; + use crate::portable; + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + use crate::avx2; use rustc_hex::FromHex; #[test] @@ -119,9 +95,27 @@ mod tests { 0x5A92F1DBA88AD318_u64, 0x239900D4ED8623B9_u64, ]; - compress(&mut h_in, m, c, f, rounds); - + // portable + portable::compress(&mut h_in, m, c, f, rounds); assert_eq!(h_in, h_out); + + let mut h_in = [ + 0x6a09e667f2bdc948_u64, 0xbb67ae8584caa73b_u64, + 0x3c6ef372fe94f82b_u64, 0xa54ff53a5f1d36f1_u64, + 0x510e527fade682d1_u64, 0x9b05688c2b3e6c1f_u64, + 0x1f83d9abfb41bd6b_u64, 0x5be0cd19137e2179_u64, + ]; + + // avx + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + if is_x86_feature_detected!("avx2") { + unsafe { + avx2::compress(&mut h_in, m, c, f, rounds); + assert_eq!(h_in, h_out); + } + } + } } fn to_u64_slice(vec: &[u8], slice: &mut [u64]) { @@ -130,6 +124,7 @@ mod tests { }) } + #[test] fn test_vectors_from_eip() { let vec = vec![ @@ -178,15 +173,27 @@ mod tests { to_u64_slice(&bytes[4..68], &mut h); to_u64_slice(&bytes[68..196], &mut m); to_u64_slice(&bytes[196..212], &mut t); - - compress(&mut h, m, t, f, rounds as usize); - let output: Vec = output.from_hex().unwrap(); - let mut out = [0u64; 8]; to_u64_slice(&output[..], &mut out); - assert_eq!(out, h); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + // avx + if is_x86_feature_detected!("avx2") { + unsafe { + avx2::compress(&mut h, m, t, f, rounds as usize); + assert_eq!(out, h); + } + } + } + + { + // portable + to_u64_slice(&bytes[4..68], &mut h); + portable::compress(&mut h, m, t, f, rounds as usize); + assert_eq!(out, h); + } } } } diff --git a/util/EIP-152/src/portable.rs b/util/EIP-152/src/portable.rs new file mode 100644 index 0000000000..283639475d --- /dev/null +++ b/util/EIP-152/src/portable.rs @@ -0,0 +1,67 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . +//! Portable implementation of the blake2b compress function + +use crate::{IV, SIGMA}; + +/// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1 +#[inline(always)] +fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { + v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); + v[d] = (v[d] ^ v[a]).rotate_right(32); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right(24); + + v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); + v[d] = (v[d] ^ v[a]).rotate_right(16); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right(63); +} + +/// The Blake2b compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 +/// Takes as an argument the state vector `h`, message block vector `m`, offset counter `t`, final +/// block indicator flag `f`, and number of rounds `rounds`. The state vector provided as the first +/// parameter is modified by the function. +pub fn compress(h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool, rounds: usize) { + let mut v = [0u64; 16]; + v[..8].copy_from_slice(h); // First half from state. + v[8..].copy_from_slice(&IV); // Second half from IV. + + v[12] ^= t[0]; + v[13] ^= t[1]; + + if f { + v[14] = !v[14]; // Invert all bits if the last-block-flag is set. + } + + for i in 0..rounds { + // Message word selection permutation for this round. + let s = &SIGMA[i % 10]; + g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]); + g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]); + g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]); + g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]); + + g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]); + g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]); + g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]); + g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + } + + for i in 0..8 { + h[i] ^= v[i] ^ v[i + 8]; + } +} diff --git a/util/EIP-712/Cargo.toml b/util/EIP-712/Cargo.toml index baf0bfc89b..28e3ee7cd4 100644 --- a/util/EIP-712/Cargo.toml +++ b/util/EIP-712/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "eip-712" -version = "0.1.0" +version = "0.1.1" authors = ["Parity Technologies "] repository = "https://github.com/paritytech/parity-ethereum" documentation = "https://docs.rs/eip-712" @@ -14,16 +14,15 @@ edition = "2018" serde_derive = "1.0" serde = "1.0" serde_json = "1.0" -ethabi = "6.0" -keccak-hash = "0.1" -ethereum-types = "0.4" +ethabi = "9.0.1" +keccak-hash = "0.4.0" +ethereum-types = "0.8.0" failure = "0.1" itertools = "0.7" lazy_static = "1.1" -toolshed = "0.4" regex = "1.0" validator = "0.8" validator_derive = "0.8" -lunarity-lexer = "0.1" +lunarity-lexer = "0.2" rustc-hex = "2.0" indexmap = "1.0.2" diff --git a/util/EIP-712/src/eip712.rs b/util/EIP-712/src/eip712.rs index 426303403d..d4ea232b9a 100644 --- a/util/EIP-712/src/eip712.rs +++ b/util/EIP-712/src/eip712.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,8 +19,7 @@ use serde_json::{Value}; use std::collections::HashMap; use ethereum_types::{U256, H256, Address}; use regex::Regex; -use validator::Validate; -use validator::ValidationErrors; +use validator::{Validate, ValidationError, ValidationErrors}; use lazy_static::lazy_static; pub(crate) type MessageTypes = HashMap>; @@ -32,16 +31,28 @@ lazy_static! { } #[serde(rename_all = "camelCase")] -#[serde(deny_unknown_fields)] #[derive(Deserialize, Serialize, Validate, Debug, Clone)] +#[validate(schema(function = "validate_domain"))] pub(crate) struct EIP712Domain { - pub(crate) name: String, - pub(crate) version: String, - pub(crate) chain_id: U256, - pub(crate) verifying_contract: Address, + #[serde(skip_serializing_if="Option::is_none")] + pub(crate) name: Option, + #[serde(skip_serializing_if="Option::is_none")] + pub(crate) version: Option, + #[serde(skip_serializing_if="Option::is_none")] + pub(crate) chain_id: Option, + #[serde(skip_serializing_if="Option::is_none")] + pub(crate) verifying_contract: Option
, #[serde(skip_serializing_if="Option::is_none")] pub(crate) salt: Option, } + +fn validate_domain(domain: &EIP712Domain) -> Result<(), ValidationError> { + match (domain.name.as_ref(), domain.version.as_ref(), domain.chain_id, domain.verifying_contract, domain.salt) { + (None, None, None, None, None) => Err(ValidationError::new("EIP712Domain must include at least one field")), + _ => Ok(()) + } +} + /// EIP-712 struct #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] @@ -55,6 +66,7 @@ pub struct EIP712 { impl Validate for EIP712 { fn validate(&self) -> Result<(), ValidationErrors> { + self.domain.validate()?; for field_types in self.types.values() { for field_type in field_types { field_type.validate()?; @@ -159,7 +171,8 @@ mod tests { { "name": "name", "type": "string" }, { "name": "version", "type": "string" }, { "name": "chainId", "type": "7uint256[x] Seun" }, - { "name": "verifyingContract", "type": "address" } + { "name": "verifyingContract", "type": "address" }, + { "name": "salt", "type": "bytes32" } ], "Person": [ { "name": "name", "type": "string" }, @@ -175,4 +188,59 @@ mod tests { let data = from_str::(string).unwrap(); assert_eq!(data.validate().is_err(), true); } + + #[test] + fn test_valid_domain() { + let string = r#"{ + "primaryType": "Test", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": "0x1", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "salt": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "message": { + "test": "It works!" + }, + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" }, + { "name": "salt", "type": "bytes32" } + ], + "Test": [ + { "name": "test", "type": "string" } + ] + } + }"#; + let data = from_str::(string).unwrap(); + assert_eq!(data.validate().is_err(), false); + } + + #[test] + fn domain_needs_at_least_one_field() { + let string = r#"{ + "primaryType": "Test", + "domain": {}, + "message": { + "test": "It works!" + }, + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Test": [ + { "name": "test", "type": "string" } + ] + } + }"#; + let data = from_str::(string).unwrap(); + assert_eq!(data.validate().is_err(), true); + } } diff --git a/util/EIP-712/src/encode.rs b/util/EIP-712/src/encode.rs index f3c89840d6..a9888d699f 100644 --- a/util/EIP-712/src/encode.rs +++ b/util/EIP-712/src/encode.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -23,7 +23,7 @@ use std::str::FromStr; use itertools::Itertools; use indexmap::IndexSet; use serde_json::to_value; -use crate::parser::{Parser, Type}; +use crate::parser::{parse_type, Type}; use crate::error::{Result, ErrorKind, serde_error}; use crate::eip712::{EIP712, MessageTypes}; use rustc_hex::FromHex; @@ -41,7 +41,7 @@ fn check_hex(string: &str) -> Result<()> { } /// given a type and HashMap> /// returns a HashSet of dependent types of the given type -fn build_dependencies<'a>(message_type: &'a str, message_types: &'a MessageTypes) -> Option<(HashSet<&'a str>)> +fn build_dependencies<'a>(message_type: &'a str, message_types: &'a MessageTypes) -> Option> { if message_types.get(message_type).is_none() { return None; @@ -56,11 +56,16 @@ fn build_dependencies<'a>(message_type: &'a str, message_types: &'a MessageTypes deps.insert(item); for field in fields { + // check if this field is an array type + let field_type = if let Some(index) = field.type_.find('[') { + &field.type_[..index] + } else { + &field.type_ + }; // seen this type before? or not a custom type skip - if deps.contains(&*field.type_) || !message_types.contains_key(&*field.type_) { - continue; + if !deps.contains(field_type) || message_types.contains_key(field_type) { + types.insert(field_type); } - types.insert(&*field.type_); } } }; @@ -70,7 +75,8 @@ fn build_dependencies<'a>(message_type: &'a str, message_types: &'a MessageTypes fn encode_type(message_type: &str, message_types: &MessageTypes) -> Result { let deps = { - let mut temp = build_dependencies(message_type, message_types).ok_or_else(|| ErrorKind::NonExistentType)?; + let mut temp = build_dependencies(message_type, message_types) + .ok_or(ErrorKind::NonExistentType)?; temp.remove(message_type); let mut temp = temp.into_iter().collect::>(); (&mut temp[..]).sort_unstable(); @@ -99,7 +105,6 @@ fn type_hash(message_type: &str, typed_data: &MessageTypes) -> Result { } fn encode_data( - parser: &Parser, message_type: &Type, message_types: &MessageTypes, value: &Value, @@ -112,52 +117,67 @@ fn encode_data( length } => { let mut items = vec![]; - let values = value.as_array().ok_or_else(|| serde_error("array", field_name))?; + let values = value.as_array() + .ok_or(serde_error("array", field_name))?; // check if the type definition actually matches // the length of items to be encoded if length.is_some() && Some(values.len() as u64) != *length { let array_type = format!("{}[{}]", *inner, length.unwrap()); - return Err(ErrorKind::UnequalArrayItems(length.unwrap(), array_type, values.len() as u64))? + return Err( + ErrorKind::UnequalArrayItems(length.unwrap(), array_type, values.len() as u64) + )? } for item in values { - let mut encoded = encode_data(parser, &*inner, &message_types, item, field_name)?; + let mut encoded = encode_data( + &*inner, + &message_types, + item, + field_name + )?; items.append(&mut encoded); } - keccak(items).to_vec() + keccak(items).as_ref().to_vec() } Type::Custom(ref ident) if message_types.get(&*ident).is_some() => { - let type_hash = (&type_hash(ident, &message_types)?).to_vec(); + let type_hash = (&type_hash(ident, &message_types)?).0.to_vec(); let mut tokens = encode(&[EthAbiToken::FixedBytes(type_hash)]); for field in message_types.get(ident).expect("Already checked in match guard; qed") { let value = &value[&field.name]; - let type_ = parser.parse_type(&*field.type_)?; - let mut encoded = encode_data(parser, &type_, &message_types, &value, Some(&*field.name))?; + let type_ = parse_type(&*field.type_)?; + let mut encoded = encode_data( + &type_, + &message_types, + &value, + Some(&*field.name) + )?; tokens.append(&mut encoded); } - keccak(tokens).to_vec() + keccak(tokens).as_ref().to_vec() } Type::Bytes => { - let string = value.as_str().ok_or_else(|| serde_error("string", field_name))?; + let string = value.as_str() + .ok_or(serde_error("string", field_name))?; check_hex(&string)?; let bytes = (&string[2..]) .from_hex::>() .map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?; - let bytes = keccak(&bytes).to_vec(); + let bytes = keccak(&bytes).as_ref().to_vec(); encode(&[EthAbiToken::FixedBytes(bytes)]) } Type::Byte(_) => { - let string = value.as_str().ok_or_else(|| serde_error("string", field_name))?; + let string = value.as_str() + .ok_or(serde_error("string", field_name))?; check_hex(&string)?; @@ -169,28 +189,34 @@ fn encode_data( } Type::String => { - let value = value.as_str().ok_or_else(|| serde_error("string", field_name))?; - let hash = keccak(value).to_vec(); + let value = value.as_str() + .ok_or(serde_error("string", field_name))?; + let hash = keccak(value).as_ref().to_vec(); encode(&[EthAbiToken::FixedBytes(hash)]) } - Type::Bool => encode(&[EthAbiToken::Bool(value.as_bool().ok_or_else(|| serde_error("bool", field_name))?)]), + Type::Bool => encode(&[EthAbiToken::Bool(value.as_bool() + .ok_or(serde_error("bool", field_name))?)]), Type::Address => { - let addr = value.as_str().ok_or_else(|| serde_error("string", field_name))?; + let addr = value.as_str() + .ok_or(serde_error("string", field_name))?; if addr.len() != 42 { return Err(ErrorKind::InvalidAddressLength(addr.len()))?; } - let address = EthAddress::from_str(&addr[2..]).map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?; + let address = EthAddress::from_str(&addr[2..]) + .map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?; encode(&[EthAbiToken::Address(address)]) } Type::Uint | Type::Int => { - let string = value.as_str().ok_or_else(|| serde_error("int/uint", field_name))?; + let string = value.as_str() + .ok_or(serde_error("int/uint", field_name))?; check_hex(&string)?; - let uint = U256::from_str(&string[2..]).map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?; + let uint = U256::from_str(&string[2..]) + .map_err(|err| ErrorKind::HexParseError(format!("{}", err)))?; let token = if *message_type == Type::Uint { EthAbiToken::Uint(uint) @@ -200,7 +226,12 @@ fn encode_data( encode(&[token]) } - _ => return Err(ErrorKind::UnknownType(format!("{}", field_name.unwrap_or("")), format!("{}", *message_type)))? + _ => return Err( + ErrorKind::UnknownType( + format!("{}", field_name.unwrap_or("")), + format!("{}", *message_type) + ).into() + ) }; Ok(encoded) @@ -213,10 +244,19 @@ pub fn hash_structured_data(typed_data: EIP712) -> Result { // EIP-191 compliant let prefix = (b"\x19\x01").to_vec(); let domain = to_value(&typed_data.domain).unwrap(); - let parser = Parser::new(); let (domain_hash, data_hash) = ( - encode_data(&parser, &Type::Custom("EIP712Domain".into()), &typed_data.types, &domain, None)?, - encode_data(&parser, &Type::Custom(typed_data.primary_type), &typed_data.types, &typed_data.message, None)? + encode_data( + &Type::Custom("EIP712Domain".into()), + &typed_data.types, + &domain, + None + )?, + encode_data( + &Type::Custom(typed_data.primary_type), + &typed_data.types, + &typed_data.message, + None + )? ); let concat = [&prefix[..], &domain_hash[..], &data_hash[..]].concat(); Ok(keccak(concat)) @@ -359,9 +399,10 @@ mod tests { #[test] fn test_hash_data() { let typed_data = from_str::(JSON).expect("alas error!"); + let hash = hash_structured_data(typed_data).expect("alas error!"); assert_eq!( - hash_structured_data(typed_data).expect("alas error!").to_hex::(), - "be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2" + &format!("{:x}", hash)[..], + "be609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2", ) } @@ -411,4 +452,196 @@ mod tests { ErrorKind::UnequalArrayItems(2, "Person[2]".into(), 1) ) } + + #[test] + fn test_typed_data_v4() { + let string = r#"{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallets", + "type": "address[]" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person[]" + }, + { + "name": "contents", + "type": "string" + } + ], + "Group": [ + { + "name": "name", + "type": "string" + }, + { + "name": "members", + "type": "Person[]" + } + ] + }, + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": "0x1", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "primaryType": "Mail", + "message": { + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": [ + { + "name": "Bob", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + } + ], + "contents": "Hello, Bob!" + } + }"#; + + let typed_data = from_str::(string).expect("alas error!"); + let hash = hash_structured_data(typed_data.clone()).expect("alas error!"); + assert_eq!( + &format!("{:x}", hash)[..], + + "a85c2e2b118698e88db68a8105b794a8cc7cec074e89ef991cb4f5f533819cc2", + ); + } + + #[test] + fn test_typed_data_v4_custom_array() { + let string = r#"{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallets", + "type": "address[]" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Group" + }, + { + "name": "contents", + "type": "string" + } + ], + "Group": [ + { + "name": "name", + "type": "string" + }, + { + "name": "members", + "type": "Person[]" + } + ] + }, + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": "0x1", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "primaryType": "Mail", + "message": { + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": { + "name": "Farmers", + "members": [ + { + "name": "Bob", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + } + ] + }, + "contents": "Hello, Bob!" + } + }"#; + let typed_data = from_str::(string).expect("alas error!"); + let hash = hash_structured_data(typed_data.clone()).expect("alas error!"); + + assert_eq!( + &format!("{:x}", hash)[..], + "cd8b34cd09c541cfc0a2fcd147e47809b98b335649c2aa700db0b0c4501a02a0", + ); + } } diff --git a/util/EIP-712/src/error.rs b/util/EIP-712/src/error.rs index 3ec1292bb5..ed7db1a9e9 100644 --- a/util/EIP-712/src/error.rs +++ b/util/EIP-712/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -67,7 +67,7 @@ pub(crate) fn serde_error(expected: &str, field: Option<&str>) -> ErrorKind { } impl Fail for Error { - fn cause(&self) -> Option<&Fail> { + fn cause(&self) -> Option<&dyn Fail> { self.inner.cause() } diff --git a/util/EIP-712/src/lib.rs b/util/EIP-712/src/lib.rs index 26ce9615c6..064e87755c 100644 --- a/util/EIP-712/src/lib.rs +++ b/util/EIP-712/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/EIP-712/src/parser.rs b/util/EIP-712/src/parser.rs index c2c16cd599..b87dd12f8e 100644 --- a/util/EIP-712/src/parser.rs +++ b/util/EIP-712/src/parser.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,6 @@ //! Solidity type-name parsing use lunarity_lexer::{Lexer, Token}; use crate::error::*; -use toolshed::Arena; use std::{fmt, result}; #[derive(Debug, Clone, PartialEq)] @@ -68,81 +67,70 @@ impl fmt::Display for Type { } } -pub struct Parser { - arena: Arena, -} - -impl Parser { - pub fn new() -> Self { - Parser { - arena: Arena::new() - } - } - /// the type string is being validated before it's parsed. - pub fn parse_type(&self, field_type: &str) -> Result { - #[derive(PartialEq)] - enum State { Open, Close } - - let mut lexer = Lexer::new(&self.arena, field_type); - let mut token = None; - let mut state = State::Close; - let mut array_depth = 0; - let mut current_array_length: Option = None; - - while lexer.token != Token::EndOfProgram { - let type_ = match lexer.token { - Token::Identifier => Type::Custom(lexer.token_as_str().to_owned()), - Token::TypeByte => Type::Byte(lexer.type_size.0), - Token::TypeBytes => Type::Bytes, - Token::TypeBool => Type::Bool, - Token::TypeUint => Type::Uint, - Token::TypeInt => Type::Int, - Token::TypeString => Type::String, - Token::TypeAddress => Type::Address, - Token::LiteralInteger => { - let length = lexer.token_as_str(); - current_array_length = Some(length - .parse() - .map_err(|_| - ErrorKind::InvalidArraySize(length.into()) - )? - ); - lexer.consume(); +/// the type string is being validated before it's parsed. +pub fn parse_type(field_type: &str) -> Result { + #[derive(PartialEq)] + enum State { Open, Close } + + let mut lexer = Lexer::new(field_type); + let mut token = None; + let mut state = State::Close; + let mut array_depth = 0; + let mut current_array_length: Option = None; + + while lexer.token != Token::EndOfProgram { + let type_ = match lexer.token { + Token::Identifier => Type::Custom(lexer.slice().to_owned()), + Token::TypeByte => Type::Byte(lexer.extras.0), + Token::TypeBytes => Type::Bytes, + Token::TypeBool => Type::Bool, + Token::TypeUint => Type::Uint, + Token::TypeInt => Type::Int, + Token::TypeString => Type::String, + Token::TypeAddress => Type::Address, + Token::LiteralInteger => { + let length = lexer.slice(); + current_array_length = Some(length + .parse() + .map_err(|_| + ErrorKind::InvalidArraySize(length.into()) + )? + ); + lexer.advance(); + continue; + } + Token::BracketOpen if token.is_some() && state == State::Close => { + state = State::Open; + lexer.advance(); + continue; + } + Token::BracketClose if array_depth < 10 => { + if state == State::Open && token.is_some() { + let length = current_array_length.take(); + state = State::Close; + token = Some(Type::Array { + inner: Box::new(token.expect("if statement checks for some; qed")), + length, + }); + lexer.advance(); + array_depth += 1; continue; - }, - Token::BracketOpen if token.is_some() && state == State::Close => { - state = State::Open; - lexer.consume(); - continue - } - Token::BracketClose if array_depth < 10 => { - if state == State::Open && token.is_some() { - let length = current_array_length.take(); - state = State::Close; - token = Some(Type::Array { - inner: Box::new(token.expect("if statement checks for some; qed")), - length - }); - lexer.consume(); - array_depth += 1; - continue - } else { - return Err(ErrorKind::UnexpectedToken(lexer.token_as_str().to_owned(), field_type.to_owned()))? - } + } else { + return Err(ErrorKind::UnexpectedToken(lexer.slice().to_owned(), field_type.to_owned()))?; } - Token::BracketClose if array_depth == 10 => { - return Err(ErrorKind::UnsupportedArrayDepth)? - } - _ => return Err(ErrorKind::UnexpectedToken(lexer.token_as_str().to_owned(), field_type.to_owned()))? - }; - - token = Some(type_); - lexer.consume(); - } + } + Token::BracketClose if array_depth == 10 => { + return Err(ErrorKind::UnsupportedArrayDepth)?; + } + _ => return Err(ErrorKind::UnexpectedToken(lexer.slice().to_owned(), field_type.to_owned()))? + }; - Ok(token.ok_or_else(|| ErrorKind::NonExistentType)?) + token = Some(type_); + lexer.advance(); } + + Ok(token.ok_or(ErrorKind::NonExistentType)?) } #[cfg(test)] @@ -151,22 +139,19 @@ mod tests { #[test] fn test_parser() { - let parser = Parser::new(); let source = "byte[][][7][][][][][][][]"; - parser.parse_type(source).unwrap(); + parse_type(source).unwrap(); } #[test] fn test_nested_array() { - let parser = Parser::new(); let source = "byte[][][7][][][][][][][][]"; - assert_eq!(parser.parse_type(source).is_err(), true); + assert_eq!(parse_type(source).is_err(), true); } #[test] fn test_malformed_array_type() { - let parser = Parser::new(); let source = "byte[7[]uint][]"; - assert_eq!(parser.parse_type(source).is_err(), true) + assert_eq!(parse_type(source).is_err(), true) } } diff --git a/util/bloom/Cargo.toml b/util/bloom/Cargo.toml index 8c366a8522..ba659add1f 100644 --- a/util/bloom/Cargo.toml +++ b/util/bloom/Cargo.toml @@ -4,9 +4,10 @@ version = "0.1.0" authors = ["Parity Technologies "] description = "Journaling bloom filter" license = "GPL3" +edition = "2018" [lib] path = "src/lib.rs" [dependencies] -siphasher = "0.1.1" +siphasher = "0.3" diff --git a/util/bloom/src/lib.rs b/util/bloom/src/lib.rs index 3dec641cd3..0c67944e98 100644 --- a/util/bloom/src/lib.rs +++ b/util/bloom/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,11 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -extern crate siphasher; - -use std::cmp; -use std::mem; -use std::f64; +use std::{cmp, mem, f64}; use std::hash::{Hash, Hasher}; use std::collections::HashSet; use siphasher::sip::SipHasher; diff --git a/util/blooms-db/Cargo.toml b/util/blooms-db/Cargo.toml index c64721e6f4..ff5141f41c 100644 --- a/util/blooms-db/Cargo.toml +++ b/util/blooms-db/Cargo.toml @@ -3,12 +3,16 @@ name = "blooms-db" version = "0.1.0" license = "GPL-3.0" authors = ["Parity Technologies "] +edition = "2018" [dependencies] -byteorder = "1.2" -ethbloom = "0.5" -parking_lot = "0.7" -tiny-keccak = "1.4" +ethbloom = "0.8.0" +parking_lot = "0.9" [dev-dependencies] +criterion = "0.3" tempdir = "0.3" + +[[bench]] +name = "blooms" +harness = false diff --git a/util/blooms-db/benches/blooms.rs b/util/blooms-db/benches/blooms.rs index 4a0c54fbdb..2b624ca784 100644 --- a/util/blooms-db/benches/blooms.rs +++ b/util/blooms-db/benches/blooms.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,21 +14,27 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -#![feature(test)] - -extern crate test; +#[macro_use] +extern crate criterion; extern crate tempdir; extern crate blooms_db; extern crate ethbloom; use std::iter; -use test::Bencher; +use criterion::Criterion; use tempdir::TempDir; use blooms_db::Database; use ethbloom::Bloom; -#[bench] -fn blooms_filter_1_million_ok(b: &mut Bencher) { +criterion_group!( + blooms, + bench_blooms_filter_1_million_ok, + bench_blooms_filter_1_million_miss, + bench_blooms_filter_1_million_miss_and_ok, +); +criterion_main!(blooms); + +fn bench_blooms_filter_1_million_ok(c: &mut Criterion) { let tempdir = TempDir::new("").unwrap(); let database = Database::open(tempdir.path()).unwrap(); database.insert_blooms(999_999, iter::once(&Bloom::zero())).unwrap(); @@ -38,14 +44,15 @@ fn blooms_filter_1_million_ok(b: &mut Bencher) { database.insert_blooms(600_000, iter::once(&bloom)).unwrap(); database.insert_blooms(800_000, iter::once(&bloom)).unwrap(); - b.iter(|| { - let matches = database.filter(0, 999_999, Some(&bloom)).unwrap(); - assert_eq!(matches, vec![200_000, 400_000, 600_000, 800_000]); + c.bench_function("blooms_filter_1_million_ok", move |b| { + b.iter(|| { + let matches = database.filter(0, 999_999, Some(&bloom)).unwrap(); + assert_eq!(matches, vec![200_000, 400_000, 600_000, 800_000]); + }) }); } -#[bench] -fn blooms_filter_1_million_miss(b: &mut Bencher) { +fn bench_blooms_filter_1_million_miss(c: &mut Criterion) { let tempdir = TempDir::new("").unwrap(); let database = Database::open(tempdir.path()).unwrap(); database.insert_blooms(999_999, iter::once(&Bloom::zero())).unwrap(); @@ -56,14 +63,15 @@ fn blooms_filter_1_million_miss(b: &mut Bencher) { database.insert_blooms(600_000, iter::once(&bloom)).unwrap(); database.insert_blooms(800_000, iter::once(&bloom)).unwrap(); - b.iter(|| { - let matches = database.filter(0, 999_999, Some(&bad_bloom)).unwrap(); - assert_eq!(matches, vec![200_000, 400_000, 600_000, 800_000]); + c.bench_function("blooms_filter_1_million_miss", move |b| { + b.iter(|| { + let matches = database.filter(0, 999_999, Some(&bad_bloom)).unwrap(); + assert_eq!(matches, vec![200_000, 400_000, 600_000, 800_000]); + }) }); } -#[bench] -fn blooms_filter_1_million_miss_and_ok(b: &mut Bencher) { +fn bench_blooms_filter_1_million_miss_and_ok(c: &mut Criterion) { let tempdir = TempDir::new("").unwrap(); let database = Database::open(tempdir.path()).unwrap(); database.insert_blooms(999_999, iter::once(&Bloom::zero())).unwrap(); @@ -74,8 +82,10 @@ fn blooms_filter_1_million_miss_and_ok(b: &mut Bencher) { database.insert_blooms(600_000, iter::once(&bloom)).unwrap(); database.insert_blooms(800_000, iter::once(&bloom)).unwrap(); - b.iter(|| { - let matches = database.filter(0, 999_999, &vec![bad_bloom, bloom]).unwrap(); - assert_eq!(matches, vec![200_000, 400_000, 600_000, 800_000]); + c.bench_function("blooms_filter_1_million_miss_and_ok", move |b| { + b.iter(|| { + let matches = database.filter(0, 999_999, &vec![bad_bloom, bloom]).unwrap(); + assert_eq!(matches, vec![200_000, 400_000, 600_000, 800_000]); + }) }); } diff --git a/util/blooms-db/src/db.rs b/util/blooms-db/src/db.rs index 2a05ec8123..d2ed13de2d 100644 --- a/util/blooms-db/src/db.rs +++ b/util/blooms-db/src/db.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,10 +16,8 @@ use std::{error, io, fmt}; use std::path::{Path, PathBuf}; - use ethbloom; - -use file::{File, FileIterator}; +use crate::file::{File, FileIterator}; fn other_io_err(e: E) -> io::Error where E: Into> { io::Error::new(io::ErrorKind::Other, e) @@ -303,27 +301,32 @@ mod tests { fn test_database() { let tempdir = TempDir::new("").unwrap(); let mut database = Database::open(tempdir.path()).unwrap(); - database.insert_blooms(0, vec![Bloom::from(0), Bloom::from(0x01), Bloom::from(0x10), Bloom::from(0x11)].iter()).unwrap(); - - let matches = database.iterate_matching(0, 3, Some(&Bloom::from(0))).unwrap().collect::, _>>().unwrap(); + database.insert_blooms(0, vec![ + Bloom::from_low_u64_be(0), + Bloom::from_low_u64_be(0x01), + Bloom::from_low_u64_be(0x10), + Bloom::from_low_u64_be(0x11), + ].iter()).unwrap(); + + let matches = database.iterate_matching(0, 3, Some(&Bloom::zero())).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![0, 1, 2, 3]); - let matches = database.iterate_matching(0, 4, Some(&Bloom::from(0))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(0, 4, Some(&Bloom::zero())).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![0, 1, 2, 3]); - let matches = database.iterate_matching(1, 3, Some(&Bloom::from(0))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(1, 3, Some(&Bloom::zero())).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![1, 2, 3]); - let matches = database.iterate_matching(1, 2, Some(&Bloom::from(0))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(1, 2, Some(&Bloom::zero())).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![1, 2]); - let matches = database.iterate_matching(0, 3, Some(&Bloom::from(0x01))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(0, 3, Some(&Bloom::from_low_u64_be(0x01))).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![1, 3]); - let matches = database.iterate_matching(0, 3, Some(&Bloom::from(0x10))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(0, 3, Some(&Bloom::from_low_u64_be(0x10))).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![2, 3]); - let matches = database.iterate_matching(2, 2, Some(&Bloom::from(0x10))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(2, 2, Some(&Bloom::from_low_u64_be(0x10))).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![2]); } @@ -331,31 +334,41 @@ mod tests { fn test_database2() { let tempdir = TempDir::new("").unwrap(); let mut database = Database::open(tempdir.path()).unwrap(); - database.insert_blooms(254, vec![Bloom::from(0x100), Bloom::from(0x01), Bloom::from(0x10), Bloom::from(0x11)].iter()).unwrap(); - - let matches = database.iterate_matching(0, 257, Some(&Bloom::from(0x01))).unwrap().collect::, _>>().unwrap(); + database.insert_blooms(254, vec![ + Bloom::from_low_u64_be(0x100), + Bloom::from_low_u64_be(0x01), + Bloom::from_low_u64_be(0x10), + Bloom::from_low_u64_be(0x11), + ].iter()).unwrap(); + + let matches = database.iterate_matching(0, 257, Some(&Bloom::from_low_u64_be(0x01))).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![255, 257]); - let matches = database.iterate_matching(0, 258, Some(&Bloom::from(0x100))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(0, 258, Some(&Bloom::from_low_u64_be(0x100))).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![254]); - let matches = database.iterate_matching(0, 256, Some(&Bloom::from(0x01))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(0, 256, Some(&Bloom::from_low_u64_be(0x01))).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![255]); - let matches = database.iterate_matching(255, 255, Some(&Bloom::from(0x01))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(255, 255, Some(&Bloom::from_low_u64_be(0x01))).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![255]); - let matches = database.iterate_matching(256, 256, Some(&Bloom::from(0x10))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(256, 256, Some(&Bloom::from_low_u64_be(0x10))).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![256]); - let matches = database.iterate_matching(256, 257, Some(&Bloom::from(0x10))).unwrap().collect::, _>>().unwrap(); + let matches = database.iterate_matching(256, 257, Some(&Bloom::from_low_u64_be(0x10))).unwrap().collect::, _>>().unwrap(); assert_eq!(matches, vec![256, 257]); } #[test] fn test_db_close() { let tempdir = TempDir::new("").unwrap(); - let blooms = vec![Bloom::from(0x100), Bloom::from(0x01), Bloom::from(0x10), Bloom::from(0x11)]; + let blooms = vec![ + Bloom::from_low_u64_be(0x100), + Bloom::from_low_u64_be(0x01), + Bloom::from_low_u64_be(0x10), + Bloom::from_low_u64_be(0x11), + ]; let mut database = Database::open(tempdir.path()).unwrap(); // Close the DB and ensure inserting blooms errors diff --git a/util/blooms-db/src/file.rs b/util/blooms-db/src/file.rs index 0362ce8849..a3f01e9b93 100644 --- a/util/blooms-db/src/file.rs +++ b/util/blooms-db/src/file.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,7 +17,6 @@ use std::io::{Seek, SeekFrom, Write, Read}; use std::path::Path; use std::{io, fs}; - use ethbloom; /// Autoresizable file containing blooms. @@ -65,7 +64,7 @@ impl File { let mut file_ref = &self.file; file_ref.seek(SeekFrom::Start(pos * 256))?; let mut bloom = ethbloom::Bloom::default(); - file_ref.read_exact(&mut bloom)?; + file_ref.read_exact(bloom.as_bytes_mut())?; Ok(bloom) } @@ -76,7 +75,7 @@ impl File { old_bloom.accrue_bloom(bloom); let mut file_ref = &self.file; file_ref.seek(SeekFrom::Start(pos * 256))?; - file_ref.write_all(&old_bloom) + file_ref.write_all(old_bloom.as_bytes()) } /// Replace bloom at given position with a new one. @@ -128,7 +127,7 @@ impl<'a> Iterator for FileIterator<'a> { fn next(&mut self) -> Option { let mut bloom = ethbloom::Bloom::default(); - match self.file.read_exact(&mut bloom) { + match self.file.read_exact(bloom.as_bytes_mut()) { Ok(_) => Some(Ok(bloom)), Err(ref err) if err.kind() == io::ErrorKind::UnexpectedEof => None, Err(err) => Some(Err(err)), @@ -146,9 +145,9 @@ mod tests { fn test_file() { let tempdir = TempDir::new("").unwrap(); let mut file = File::open(tempdir.path().join("file")).unwrap(); - file.accrue_bloom(0, &Bloom::from(1)).unwrap(); + file.accrue_bloom(0, &Bloom::from_low_u64_be(1)).unwrap(); file.flush().unwrap(); - assert_eq!(file.read_bloom(0).unwrap(), Bloom::from(1)); + assert_eq!(file.read_bloom(0).unwrap(), Bloom::from_low_u64_be(1)); } } diff --git a/util/blooms-db/src/lib.rs b/util/blooms-db/src/lib.rs index 83baaffae8..8c67bbca2c 100644 --- a/util/blooms-db/src/lib.rs +++ b/util/blooms-db/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,19 +16,12 @@ //! Ethereum blooms database -extern crate byteorder; -extern crate ethbloom; -extern crate parking_lot; -extern crate tiny_keccak; - -#[cfg(test)] -extern crate tempdir; - mod db; mod file; use std::io; use std::path::Path; +use ethbloom; use parking_lot::Mutex; /// Threadsafe API for blooms database. diff --git a/util/dir/Cargo.toml b/util/dir/Cargo.toml index 836eb3ecc6..48e90eb855 100644 --- a/util/dir/Cargo.toml +++ b/util/dir/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] license = "GPL3" [dependencies] -ethereum-types = "0.4" +ethereum-types = "0.8.0" journaldb = { path = "../journaldb" } app_dirs = { git = "https://github.com/paritytech/app-dirs-rs" } home = "0.3" diff --git a/util/dir/src/helpers.rs b/util/dir/src/helpers.rs index 39cc767cac..46ee069f24 100644 --- a/util/dir/src/helpers.rs +++ b/util/dir/src/helpers.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/dir/src/lib.rs b/util/dir/src/lib.rs index fa7882cdd3..461c976975 100644 --- a/util/dir/src/lib.rs +++ b/util/dir/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -157,7 +157,12 @@ 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!("{:x}{}", H64::from(self.genesis_hash), self.fork_name.as_ref().map(|f| format!("-{}", f)).unwrap_or_default())) + let gh = H64::from_slice(&self.genesis_hash.as_bytes()[20..28]); + Path::new(&self.legacy_path).join(format!( + "{:x}{}", + gh, + self.fork_name.as_ref().map(|f| format!("-{}", f)).unwrap_or_default() + )) } /// Spec root directory for the given fork. @@ -172,7 +177,8 @@ impl DatabaseDirectories { /// DB root path, named after genesis hash pub fn db_root_path(&self) -> PathBuf { - self.spec_root_path().join("db").join(format!("{:x}", H64::from(self.genesis_hash))) + let gh = H64::from_slice(&self.genesis_hash.as_bytes()[20..28]); + self.spec_root_path().join("db").join(format!("{:x}", gh)) } /// DB path diff --git a/util/fake-fetch/src/lib.rs b/util/fake-fetch/src/lib.rs index 9dc52fc93c..e7df95c633 100644 --- a/util/fake-fetch/src/lib.rs +++ b/util/fake-fetch/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/fastmap/Cargo.toml b/util/fastmap/Cargo.toml index 371eebe976..774d56b55d 100644 --- a/util/fastmap/Cargo.toml +++ b/util/fastmap/Cargo.toml @@ -6,5 +6,5 @@ description = "Specialized version of `HashMap` with H256 keys and fast hashing license = "GPL-3.0" [dependencies] -ethereum-types = "0.4" +ethereum-types = "0.8.0" plain_hasher = "0.2" diff --git a/util/fastmap/src/lib.rs b/util/fastmap/src/lib.rs index 97f7d3b24e..0cbe46fa8b 100644 --- a/util/fastmap/src/lib.rs +++ b/util/fastmap/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -36,6 +36,6 @@ mod tests { #[test] fn test_works() { let mut h = H256FastMap::default(); - h.insert(H256::from(123), "abc"); + h.insert(H256::from_low_u64_be(123), "abc"); } } diff --git a/util/fetch/Cargo.toml b/util/fetch/Cargo.toml index c45c2ca4f4..72039a1a4b 100644 --- a/util/fetch/Cargo.toml +++ b/util/fetch/Cargo.toml @@ -9,11 +9,11 @@ authors = ["Parity Technologies "] [dependencies] futures = "0.1" hyper = "~0.12.9" -hyper-rustls = "0.16.0" +hyper-rustls = "0.18" http = "0.1" log = "0.4" -tokio = "~0.1.8" -url = "1" +tokio = "0.1.22" +url = "2" bytes = "0.4" [features] diff --git a/util/fetch/src/client.rs b/util/fetch/src/client.rs index e4474fb793..cb6767e3f2 100644 --- a/util/fetch/src/client.rs +++ b/util/fetch/src/client.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -271,7 +271,7 @@ impl Client { } impl Fetch for Client { - type Result = Box + Send + 'static>; + type Result = Box + Send + 'static>; fn fetch(&self, request: Request, abort: Abort) -> Self::Result { debug!(target: "fetch", "fetching: {:?}", request.url()); @@ -608,7 +608,7 @@ impl fmt::Display for Error { impl ::std::error::Error for Error { fn description(&self) -> &str { "Fetch client error" } - fn cause(&self) -> Option<&::std::error::Error> { None } + fn cause(&self) -> Option<&dyn std::error::Error> { None } } impl From for Error { @@ -871,7 +871,7 @@ mod test { type ReqBody = hyper::Body; type ResBody = hyper::Body; type Error = Error; - type Future = Box, Error=Self::Error> + Send + 'static>; + type Future = Box, Error=Self::Error> + Send + 'static>; fn call(&mut self, req: hyper::Request) -> Self::Future { match req.uri().path() { diff --git a/util/fetch/src/lib.rs b/util/fetch/src/lib.rs index eb172d2c67..c91be14802 100644 --- a/util/fetch/src/lib.rs +++ b/util/fetch/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/io/Cargo.toml b/util/io/Cargo.toml index eff2dae9c8..9d0a7a0e8b 100644 --- a/util/io/Cargo.toml +++ b/util/io/Cargo.toml @@ -1,16 +1,17 @@ [package] +name = "ethcore-io" description = "Ethcore IO library" +version = "1.12.0" homepage = "http://parity.io" license = "GPL-3.0" -name = "ethcore-io" -version = "1.12.0" authors = ["Parity Technologies "] +edition = "2018" [dependencies] fnv = "1.0" -mio = { version = "0.6.8", optional = true } +mio = { version = "0.6.19", optional = true } crossbeam-deque = "0.6" -parking_lot = "0.7" +parking_lot = "0.9" log = "0.4" slab = "0.4" num_cpus = "1.8" @@ -18,3 +19,4 @@ timer = "0.2" time = "0.1" tokio = "0.1" futures = "0.1" + diff --git a/util/io/src/lib.rs b/util/io/src/lib.rs index 997edd7c4a..90e8d9fb02 100644 --- a/util/io/src/lib.rs +++ b/util/io/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -69,20 +69,6 @@ //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_deque as deque; -extern crate parking_lot; -extern crate num_cpus; -extern crate timer; -extern crate fnv; -extern crate time; -extern crate tokio; -extern crate futures; - #[cfg(feature = "mio")] mod service_mio; #[cfg(not(feature = "mio"))] @@ -170,7 +156,7 @@ pub trait IoHandler: Send + Sync where Message: Send + Sync + 'static { /// 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 + /// Deregister a stream. Called when a stream is removed from the event loop #[cfg(feature = "mio")] fn deregister_stream(&self, _stream: StreamToken, _event_loop: &mut EventLoop>) {} } @@ -178,14 +164,15 @@ pub trait IoHandler: Send + Sync where Message: Send + Sync + 'static { #[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}; +pub use crate::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 std::{ + sync::{Arc, atomic}, + thread, + time::Duration, + }; use super::*; // Mio's behaviour is too unstable for this test. Sometimes we have to wait a few milliseconds, diff --git a/util/io/src/service_mio.rs b/util/io/src/service_mio.rs index f1f19bfc28..02eeb7bb60 100644 --- a/util/io/src/service_mio.rs +++ b/util/io/src/service_mio.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,18 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . +use std::collections::HashMap; use std::sync::{Arc, Weak}; use std::thread::{self, JoinHandle}; -use std::collections::HashMap; +use std::time::Duration; + +use crossbeam_deque as deque; +use log::{trace, debug, warn}; use mio::*; -use mio::timer::{Timeout}; -use mio::deprecated::{EventLoop, Handler, Sender, EventLoopBuilder}; -use deque; +use mio::deprecated::{EventLoop, EventLoopBuilder, Handler, Sender}; +use mio::timer::Timeout; +use parking_lot::{Condvar, Mutex, RwLock}; use slab::Slab; -use {IoError, IoHandler}; -use worker::{Worker, Work, WorkType}; -use parking_lot::{Condvar, RwLock, Mutex}; -use std::time::Duration; + +use crate::{ + IoError, IoHandler, + worker::{Work, Worker, WorkType} +}; /// Timer ID pub type TimerToken = usize; @@ -45,7 +50,7 @@ pub enum IoMessage where Message: Send + Sized { Shutdown, /// Register a new protocol handler. AddHandler { - handler: Arc+Send>, + handler: Arc + Send>, }, RemoveHandler { handler_id: HandlerId, @@ -182,7 +187,7 @@ struct UserTimer { /// Root IO handler. Manages user handlers, messages and IO timers. pub struct IoManager where Message: Send + Sync { timers: Arc>>, - handlers: Arc>>>>, + handlers: Arc>>>>, workers: Vec, worker_channel: deque::Worker>, work_ready: Arc, @@ -192,7 +197,7 @@ 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>>>> + handlers: Arc>>>> ) -> Result<(), IoError> { let (worker, stealer) = deque::fifo(); let num_workers = 4; @@ -243,24 +248,6 @@ impl Handler for IoManager where Message: Send + Sync + 'stati } } - fn timeout(&mut self, event_loop: &mut EventLoop, token: Token) { - let handler_index = token.0 / TOKENS_PER_HANDLER; - let token_id = token.0 % TOKENS_PER_HANDLER; - if let Some(handler) = self.handlers.read().get(handler_index) { - let maybe_timer = self.timers.read().get(&token.0).cloned(); - if let Some(timer) = maybe_timer { - if timer.once { - self.timers.write().remove(&token_id); - event_loop.clear_timeout(&timer.timeout); - } else { - 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(); - } - } - } - fn notify(&mut self, event_loop: &mut EventLoop, msg: Self::Message) { match msg { IoMessage::Shutdown => { @@ -331,11 +318,34 @@ impl Handler for IoManager where Message: Send + Sync + 'stati } } } + + fn timeout(&mut self, event_loop: &mut EventLoop, token: Token) { + let handler_index = token.0 / TOKENS_PER_HANDLER; + let token_id = token.0 % TOKENS_PER_HANDLER; + if let Some(handler) = self.handlers.read().get(handler_index) { + let maybe_timer = self.timers.read().get(&token.0).cloned(); + if let Some(timer) = maybe_timer { + if timer.once { + self.timers.write().remove(&token_id); + event_loop.clear_timeout(&timer.timeout); + } else { + 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(); + } + } + } } enum Handlers where Message: Send { - SharedCollection(Weak>>>>), - Single(Weak>), + SharedCollection(Weak>>>>), + Single(Weak>), } impl Clone for Handlers { @@ -413,13 +423,13 @@ impl IoChannel where Message: Send + Sync + 'static { } /// Create a new synchronous channel to a given handler. - pub fn to_handler(handler: Weak>) -> IoChannel { + pub fn to_handler(handler: Weak>) -> IoChannel { IoChannel { channel: None, handlers: Handlers::Single(handler), } } - fn new(channel: Sender>, handlers: Weak>>>>) -> IoChannel { + fn new(channel: Sender>, handlers: Weak>>>>) -> IoChannel { IoChannel { channel: Some(channel), handlers: Handlers::SharedCollection(handlers), @@ -432,7 +442,7 @@ impl IoChannel where Message: Send + Sync + 'static { pub struct IoService where Message: Send + Sync + 'static { thread: Option>, host_channel: Mutex>>, - handlers: Arc>>>>, + handlers: Arc>>>>, } impl IoService where Message: Send + Sync + 'static { @@ -469,7 +479,7 @@ impl IoService where Message: Send + Sync + 'static { } /// Regiter an IO handler with the event loop. - pub fn register_handler(&self, handler: Arc+Send>) -> Result<(), IoError> { + pub fn register_handler(&self, handler: Arc+Send>) -> Result<(), IoError> { self.host_channel.lock().send(IoMessage::AddHandler { handler: handler, })?; diff --git a/util/io/src/service_non_mio.rs b/util/io/src/service_non_mio.rs index 1cef3574e9..1620e944cb 100644 --- a/util/io/src/service_non_mio.rs +++ b/util/io/src/service_non_mio.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,15 +16,18 @@ use std::sync::{Arc, Weak}; use std::thread; -use deque; -use slab::Slab; +use std::time::Duration; + +use crossbeam_deque as deque; use fnv::FnvHashMap; -use {IoError, IoHandler}; -use parking_lot::{RwLock, Mutex}; +use log::{trace, debug}; use num_cpus; -use std::time::Duration; -use timer::{Timer, Guard as TimerGuard}; +use parking_lot::{Mutex, RwLock}; +use slab::Slab; use time::Duration as TimeDuration; +use timer::{Guard as TimerGuard, Timer}; + +use crate::{IoError, IoHandler}; /// Timer ID pub type TimerToken = usize; @@ -48,7 +51,7 @@ impl IoContext where Message: Send + Sync + 'static { let msg = WorkTask::TimerTrigger { handler_id: self.handler, - token: token, + token, }; let delay = TimeDuration::from_std(delay) @@ -68,7 +71,7 @@ impl IoContext where Message: Send + Sync + 'static { let msg = WorkTask::TimerTrigger { handler_id: self.handler, - token: token, + token, }; let delay = TimeDuration::from_std(delay) @@ -189,7 +192,7 @@ pub struct IoService where Message: Send + Sync + 'static { // Struct shared throughout the whole implementation. struct Shared where Message: Send + Sync + 'static { // All the I/O handlers that have been registered. - handlers: RwLock>>>, + handlers: RwLock>>>, // All the background threads, so that we can unpark them. threads: RwLock>, // Used to create timeouts. @@ -273,7 +276,7 @@ impl IoService where Message: Send + Sync + 'static { } /// Register an IO handler with the event loop. - pub fn register_handler(&self, handler: Arc+Send>) -> Result<(), IoError> { + 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() }; diff --git a/util/io/src/worker.rs b/util/io/src/worker.rs index 458882a851..afab946b42 100644 --- a/util/io/src/worker.rs +++ b/util/io/src/worker.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,17 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use futures::future::{self, Loop}; -use std::sync::Arc; -use std::thread::{JoinHandle, self}; -use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; -use deque; -use service_mio::{HandlerId, IoChannel, IoContext}; -use tokio::{self}; -use IoHandler; -use LOCAL_STACK_SIZE; +use std::{ + sync::{Arc, atomic::{AtomicBool, Ordering as AtomicOrdering}}, + thread::{self, JoinHandle}, +}; +use crossbeam_deque as deque; +use futures::future::{self, Loop}; +use log::{trace, error}; use parking_lot::{Condvar, Mutex}; +use tokio; + +use crate::{ + IoHandler, + LOCAL_STACK_SIZE, + service_mio::{HandlerId, IoChannel, IoContext}, +}; const STACK_SIZE: usize = 16*1024*1024; @@ -40,7 +45,7 @@ pub struct Work { pub work_type: WorkType, pub token: usize, pub handler_id: HandlerId, - pub handler: Arc>, + pub handler: Arc>, } /// An IO worker thread @@ -54,13 +59,15 @@ pub struct Worker { impl Worker { /// Creates a new worker instance. - pub fn new(index: usize, - stealer: deque::Stealer>, - channel: IoChannel, - wait: Arc, - wait_mutex: Arc>, - ) -> Worker - where Message: Send + Sync + 'static { + pub fn new( + index: usize, + stealer: deque::Stealer>, + channel: IoChannel, + wait: Arc, + wait_mutex: Arc>, + ) -> Worker + where Message: Send + Sync + 'static + { let deleting = Arc::new(AtomicBool::new(false)); let mut worker = Worker { thread: None, diff --git a/util/journaldb/Cargo.toml b/util/journaldb/Cargo.toml index 045b42d662..a6b74952ba 100644 --- a/util/journaldb/Cargo.toml +++ b/util/journaldb/Cargo.toml @@ -4,21 +4,22 @@ version = "0.2.0" authors = ["Parity Technologies "] description = "A `HashDB` which can manage a short-term journal potentially containing many forks of mutually exclusive actions" license = "GPL3" +edition = "2018" [dependencies] parity-bytes = "0.1" -ethereum-types = "0.4" -hash-db = "0.11.0" -heapsize = "0.4" +ethereum-types = "0.8.0" +hash-db = "0.15.0" +malloc_size_of = { version = "0.3.0", package = "parity-util-mem" } keccak-hasher = { path = "../keccak-hasher" } -kvdb = "0.1" +kvdb = "0.3.1" log = "0.4" -memory-db = "0.11.0" -parking_lot = "0.7" +memory-db = "0.18.0" +parking_lot = "0.9" fastmap = { path = "../../util/fastmap" } -rlp = { version = "0.3.0", features = ["ethereum"] } +rlp = "0.4.0" [dev-dependencies] env_logger = "0.5" -keccak-hash = "0.1" -kvdb-memorydb = "0.1" +keccak-hash = "0.4.0" +kvdb-memorydb = "0.3.1" diff --git a/util/journaldb/src/archivedb.rs b/util/journaldb/src/archivedb.rs index a068919a78..1af45722a3 100644 --- a/util/journaldb/src/archivedb.rs +++ b/util/journaldb/src/archivedb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,20 +16,24 @@ //! Disk-backed `HashDB` implementation. -use std::collections::HashMap; -use std::collections::hash_map::Entry; -use std::io; -use std::sync::Arc; +use std::{ + collections::{HashMap, hash_map::Entry}, + io, + sync::Arc, +}; -use bytes::Bytes; use ethereum_types::H256; -use hash_db::{HashDB}; +use hash_db::{HashDB, Prefix}; use keccak_hasher::KeccakHasher; use kvdb::{KeyValueDB, DBTransaction, DBValue}; +use malloc_size_of::MallocSizeOfExt; +use parity_bytes::Bytes; use rlp::{encode, decode}; -use super::{DB_PREFIX_LEN, LATEST_ERA_KEY, error_key_already_exists, error_negatively_reference_hash}; -use super::memory_db::*; -use traits::JournalDB; + +use crate::{ + DB_PREFIX_LEN, LATEST_ERA_KEY, error_key_already_exists, error_negatively_reference_hash, + JournalDB, new_memory_db +}; /// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay /// and latent-removal semantics. @@ -39,20 +43,20 @@ use traits::JournalDB; /// immediately. As this is an "archive" database, nothing is ever removed. This means /// that the states of any block the node has ever processed will be accessible. pub struct ArchiveDB { - overlay: MemoryDB, - backing: Arc, + overlay: super::MemoryDB, + backing: Arc, latest_era: Option, - column: Option, + column: u32, } impl ArchiveDB { /// Create a new instance from a key-value db. - pub fn new(backing: Arc, column: Option) -> ArchiveDB { + pub fn new(backing: Arc, column: u32) -> 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: ::new_memory_db(), + overlay: new_memory_db(), backing, latest_era, column, @@ -60,14 +64,14 @@ impl ArchiveDB { } fn payload(&self, key: &H256) -> Option { - self.backing.get(self.column, key).expect("Low-level database error. Some issue with your hard disk?") + self.backing.get(self.column, key.as_bytes()).expect("Low-level database error. Some issue with your hard disk?") } } impl HashDB for ArchiveDB { - fn get(&self, key: &H256) -> Option { - if let Some((d, rc)) = self.overlay.raw(key) { + fn get(&self, key: &H256, prefix: Prefix) -> Option { + if let Some((d, rc)) = self.overlay.raw(key, prefix) { if rc > 0 { return Some(d.clone()); } @@ -75,46 +79,25 @@ impl HashDB for ArchiveDB { self.payload(key) } - fn contains(&self, key: &H256) -> bool { - self.get(key).is_some() + fn contains(&self, key: &H256, prefix: Prefix) -> bool { + self.get(key, prefix).is_some() } - fn insert(&mut self, value: &[u8]) -> H256 { - self.overlay.insert(value) + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { + self.overlay.insert(prefix, value) } - fn emplace(&mut self, key: H256, value: DBValue) { - self.overlay.emplace(key, value); + fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { + self.overlay.emplace(key, prefix, value); } - fn remove(&mut self, key: &H256) { - self.overlay.remove(key); - } -} - -impl ::traits::KeyedHashDB for ArchiveDB { - fn keys(&self) -> HashMap { - let mut ret: HashMap = self.backing.iter(self.column) - .map(|(key, _)| (H256::from_slice(&*key), 1)) - .collect(); - - for (key, refs) in self.overlay.keys() { - match ret.entry(key) { - Entry::Occupied(mut entry) => { - *entry.get_mut() += refs; - }, - Entry::Vacant(entry) => { - entry.insert(refs); - } - } - } - ret + fn remove(&mut self, key: &H256, prefix: Prefix) { + self.overlay.remove(key, prefix); } } impl JournalDB for ArchiveDB { - - fn boxed_clone(&self) -> Box { + fn boxed_clone(&self) -> Box { Box::new(ArchiveDB { overlay: self.overlay.clone(), backing: self.backing.clone(), @@ -124,8 +107,8 @@ impl JournalDB for ArchiveDB { } fn mem_used(&self) -> usize { - self.overlay.mem_used() - } + self.overlay.malloc_size_of() + } fn is_empty(&self) -> bool { self.latest_era.is_none() @@ -138,7 +121,7 @@ impl JournalDB for ArchiveDB { for i in self.overlay.drain() { let (key, (value, rc)) = i; if rc > 0 { - batch.put(self.column, &key, &value); + batch.put(self.column, key.as_bytes(), &value); inserts += 1; } if rc < 0 { @@ -166,18 +149,18 @@ impl JournalDB for ArchiveDB { for i in self.overlay.drain() { let (key, (value, rc)) = i; if rc > 0 { - if self.backing.get(self.column, &key)?.is_some() { + if self.backing.get(self.column, key.as_bytes())?.is_some() { return Err(error_key_already_exists(&key)); } - batch.put(self.column, &key, &value); + batch.put(self.column, key.as_bytes(), &value); inserts += 1; } if rc < 0 { assert!(rc == -1); - if self.backing.get(self.column, &key)?.is_none() { + if self.backing.get(self.column, key.as_bytes())?.is_none() { return Err(error_negatively_reference_hash(&key)); } - batch.delete(self.column, &key); + batch.delete(self.column, key.as_bytes()); deletes += 1; } } @@ -191,268 +174,286 @@ impl JournalDB for ArchiveDB { self.backing.get_by_prefix(self.column, &id[0..DB_PREFIX_LEN]).map(|b| b.into_vec()) } - fn is_pruned(&self) -> bool { false } + fn is_prunable(&self) -> bool { false } - fn backing(&self) -> &Arc { + fn backing(&self) -> &Arc { &self.backing } - fn consolidate(&mut self, with: MemoryDB) { + fn consolidate(&mut self, with: super::MemoryDB) { self.overlay.consolidate(with); } + + fn keys(&self) -> HashMap { + let mut ret: HashMap = self.backing.iter(self.column) + .map(|(key, _)| (H256::from_slice(&*key), 1)) + .collect(); + + for (key, refs) in self.overlay.keys() { + match ret.entry(key) { + Entry::Occupied(mut entry) => { + *entry.get_mut() += refs; + }, + Entry::Vacant(entry) => { + entry.insert(refs); + } + } + } + ret + } } #[cfg(test)] mod tests { - - use keccak::keccak; - use hash_db::HashDB; + use keccak_hash::keccak; + use hash_db::{HashDB, EMPTY_PREFIX}; use super::*; - use {kvdb_memorydb, JournalDB}; + use kvdb_memorydb; + use crate::{JournalDB, inject_batch, commit_batch}; #[test] fn insert_same_in_fork() { // history is 1 - let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(0)), None); + let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(1)), 0); - let x = jdb.insert(b"X"); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); - jdb.commit_batch(3, &keccak(b"1002a"), Some((1, keccak(b"1")))).unwrap(); - jdb.commit_batch(4, &keccak(b"1003a"), Some((2, keccak(b"2")))).unwrap(); + let x = jdb.insert(EMPTY_PREFIX, b"X"); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); + commit_batch(&mut jdb, 3, &keccak(b"1002a"), Some((1, keccak(b"1")))).unwrap(); + commit_batch(&mut jdb, 4, &keccak(b"1003a"), Some((2, keccak(b"2")))).unwrap(); - jdb.remove(&x); - jdb.commit_batch(3, &keccak(b"1002b"), Some((1, keccak(b"1")))).unwrap(); - let x = jdb.insert(b"X"); - jdb.commit_batch(4, &keccak(b"1003b"), Some((2, keccak(b"2")))).unwrap(); + jdb.remove(&x, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"1002b"), Some((1, keccak(b"1")))).unwrap(); + let x = jdb.insert(EMPTY_PREFIX, b"X"); + commit_batch(&mut jdb, 4, &keccak(b"1003b"), Some((2, keccak(b"2")))).unwrap(); - jdb.commit_batch(5, &keccak(b"1004a"), Some((3, keccak(b"1002a")))).unwrap(); - jdb.commit_batch(6, &keccak(b"1005a"), Some((4, keccak(b"1003a")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"1004a"), Some((3, keccak(b"1002a")))).unwrap(); + commit_batch(&mut jdb, 6, &keccak(b"1005a"), Some((4, keccak(b"1003a")))).unwrap(); - assert!(jdb.contains(&x)); + assert!(jdb.contains(&x, EMPTY_PREFIX)); } #[test] fn long_history() { // history is 3 - let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(0)), None); - let h = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); - assert!(jdb.contains(&h)); - jdb.remove(&h); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); - assert!(jdb.contains(&h)); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); - assert!(jdb.contains(&h)); - jdb.commit_batch(3, &keccak(b"3"), Some((0, keccak(b"0")))).unwrap(); - assert!(jdb.contains(&h)); - jdb.commit_batch(4, &keccak(b"4"), Some((1, keccak(b"1")))).unwrap(); - assert!(jdb.contains(&h)); + let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(1)), 0); + let h = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + jdb.remove(&h, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((0, keccak(b"0")))).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((1, keccak(b"1")))).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); } #[test] #[should_panic] fn multiple_owed_removal_not_allowed() { - let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(0)), None); - let h = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); - assert!(jdb.contains(&h)); - jdb.remove(&h); - jdb.remove(&h); + let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(1)), 0); + let h = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + jdb.remove(&h, EMPTY_PREFIX); + jdb.remove(&h, EMPTY_PREFIX); // commit_batch would call journal_under(), // and we don't allow multiple owned removals. - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); } #[test] fn complex() { // history is 1 - let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(0)), None); - - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - - jdb.remove(&foo); - jdb.remove(&bar); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - assert!(jdb.contains(&baz)); - - let foo = jdb.insert(b"foo"); - jdb.remove(&baz); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&baz)); - - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); - assert!(jdb.contains(&foo)); - - jdb.commit_batch(4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); + let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(1)), 0); + + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + + jdb.remove(&foo, EMPTY_PREFIX); + jdb.remove(&bar, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); + + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + jdb.remove(&baz, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); + + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); } #[test] fn fork() { // history is 1 - let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(0)), None); + let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(1)), 0); - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); - jdb.remove(&foo); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); - jdb.remove(&bar); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - assert!(jdb.contains(&baz)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); - jdb.commit_batch(2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); - assert!(jdb.contains(&foo)); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); } #[test] fn overwrite() { // history is 1 - let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(0)), None); - - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); - assert!(jdb.contains(&foo)); - - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); - jdb.insert(b"foo"); - assert!(jdb.contains(&foo)); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); - assert!(jdb.contains(&foo)); - jdb.commit_batch(3, &keccak(b"2"), Some((0, keccak(b"2")))).unwrap(); - assert!(jdb.contains(&foo)); + let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(1)), 0); + + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + commit_batch(&mut jdb, 3, &keccak(b"2"), Some((0, keccak(b"2")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); } #[test] fn fork_same_key() { // history is 1 - let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(0)), None); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(1)), 0); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); - jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); - assert!(jdb.contains(&foo)); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.commit_batch(2, &keccak(b"2a"), Some((1, keccak(b"1a")))).unwrap(); - assert!(jdb.contains(&foo)); + commit_batch(&mut jdb, 2, &keccak(b"2a"), Some((1, keccak(b"1a")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); } #[test] fn reopen() { - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); let bar = H256::random(); let foo = { - let mut jdb = ArchiveDB::new(shared_db.clone(), None); + let mut jdb = ArchiveDB::new(shared_db.clone(), 0); // history is 1 - let foo = jdb.insert(b"foo"); - jdb.emplace(bar.clone(), DBValue::from_slice(b"bar")); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + jdb.emplace(bar.clone(), EMPTY_PREFIX, b"bar".to_vec()); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); foo }; { - let mut jdb = ArchiveDB::new(shared_db.clone(), None); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + let mut jdb = ArchiveDB::new(shared_db.clone(), 0); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); } { - let mut jdb = ArchiveDB::new(shared_db, None); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + let mut jdb = ArchiveDB::new(shared_db, 0); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); } } #[test] fn reopen_remove() { - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); let foo = { - let mut jdb = ArchiveDB::new(shared_db.clone(), None); + let mut jdb = ArchiveDB::new(shared_db.clone(), 0); // history is 1 - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); // foo is ancient history. - jdb.insert(b"foo"); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); foo }; { - let mut jdb = ArchiveDB::new(shared_db, None); - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); - assert!(jdb.contains(&foo)); - jdb.remove(&foo); - jdb.commit_batch(4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); - jdb.commit_batch(5, &keccak(b"5"), Some((4, keccak(b"4")))).unwrap(); + let mut jdb = ArchiveDB::new(shared_db, 0); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((4, keccak(b"4")))).unwrap(); } } #[test] fn reopen_fork() { - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); let (foo, _, _) = { - let mut jdb = ArchiveDB::new(shared_db.clone(), None); + let mut jdb = ArchiveDB::new(shared_db.clone(), 0); // history is 1 - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); - jdb.remove(&foo); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); - - jdb.remove(&bar); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); (foo, bar, baz) }; { - let mut jdb = ArchiveDB::new(shared_db, None); - jdb.commit_batch(2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); - assert!(jdb.contains(&foo)); + let mut jdb = ArchiveDB::new(shared_db, 0); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); } } #[test] fn returns_state() { - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); let key = { - let mut jdb = ArchiveDB::new(shared_db.clone(), None); - let key = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let mut jdb = ArchiveDB::new(shared_db.clone(), 0); + let key = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); key }; { - let jdb = ArchiveDB::new(shared_db, None); + let jdb = ArchiveDB::new(shared_db, 0); let state = jdb.state(&key); assert!(state.is_some()); } @@ -460,14 +461,14 @@ mod tests { #[test] fn inject() { - let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(0)), None); - let key = jdb.insert(b"dog"); - jdb.inject_batch().unwrap(); + let mut jdb = ArchiveDB::new(Arc::new(kvdb_memorydb::create(1)), 0); + let key = jdb.insert(EMPTY_PREFIX, b"dog"); + inject_batch(&mut jdb).unwrap(); - assert_eq!(jdb.get(&key).unwrap(), DBValue::from_slice(b"dog")); - jdb.remove(&key); - jdb.inject_batch().unwrap(); + assert_eq!(jdb.get(&key, EMPTY_PREFIX).unwrap(), b"dog".to_vec()); + jdb.remove(&key, EMPTY_PREFIX); + inject_batch(&mut jdb).unwrap(); - assert!(jdb.get(&key).is_none()); + assert!(jdb.get(&key, EMPTY_PREFIX).is_none()); } } diff --git a/util/journaldb/src/as_hash_db_impls.rs b/util/journaldb/src/as_hash_db_impls.rs index 8a380ea56a..64f4b399fb 100644 --- a/util/journaldb/src/as_hash_db_impls.rs +++ b/util/journaldb/src/as_hash_db_impls.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -17,55 +17,38 @@ //! Impls of the `AsHashDB` upcast trait for all different variants of DB use hash_db::{HashDB, AsHashDB}; use keccak_hasher::KeccakHasher; -use archivedb::ArchiveDB; -use earlymergedb::EarlyMergeDB; -use overlayrecentdb::OverlayRecentDB; -use refcounteddb::RefCountedDB; -use overlaydb::OverlayDB; + use kvdb::DBValue; -use crate::{KeyedHashDB, AsKeyedHashDB}; + +use crate::{ + archivedb::ArchiveDB, + earlymergedb::EarlyMergeDB, + overlayrecentdb::OverlayRecentDB, + refcounteddb::RefCountedDB, + overlaydb::OverlayDB, +}; impl AsHashDB for ArchiveDB { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl AsHashDB for EarlyMergeDB { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl AsHashDB for OverlayRecentDB { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl AsHashDB for RefCountedDB { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } impl AsHashDB for OverlayDB { - fn as_hash_db(&self) -> &HashDB { self } - fn as_hash_db_mut(&mut self) -> &mut HashDB { self } -} - -impl AsKeyedHashDB for ArchiveDB { - fn as_keyed_hash_db(&self) -> &KeyedHashDB { self } -} - -impl AsKeyedHashDB for EarlyMergeDB { - fn as_keyed_hash_db(&self) -> &KeyedHashDB { self } -} - -impl AsKeyedHashDB for OverlayRecentDB { - fn as_keyed_hash_db(&self) -> &KeyedHashDB { self } -} - -impl AsKeyedHashDB for RefCountedDB { - fn as_keyed_hash_db(&self) -> &KeyedHashDB { self } -} - -impl AsKeyedHashDB for OverlayDB { - fn as_keyed_hash_db(&self) -> &KeyedHashDB { self } + fn as_hash_db(&self) -> &dyn HashDB { self } + fn as_hash_db_mut(&mut self) -> &mut dyn HashDB { self } } diff --git a/util/journaldb/src/earlymergedb.rs b/util/journaldb/src/earlymergedb.rs index 2a55200c49..d557563883 100644 --- a/util/journaldb/src/earlymergedb.rs +++ b/util/journaldb/src/earlymergedb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,34 +16,34 @@ //! Disk-backed `HashDB` implementation. -use std::collections::HashMap; -use std::collections::hash_map::Entry; -use std::io; -use std::sync::Arc; +use std::{ + collections::{HashMap, hash_map::Entry}, + io, + sync::Arc, +}; -use bytes::Bytes; use ethereum_types::H256; -use hash_db::{HashDB}; -use heapsize::HeapSizeOf; +use hash_db::{HashDB, Prefix}; use keccak_hasher::KeccakHasher; use kvdb::{KeyValueDB, DBTransaction, DBValue}; -use memory_db::*; +use log::{trace, warn}; +use malloc_size_of::{MallocSizeOf, allocators::new_malloc_size_ops}; +use parity_bytes::Bytes; use parking_lot::RwLock; use rlp::{encode, decode}; -use super::{DB_PREFIX_LEN, LATEST_ERA_KEY, error_negatively_reference_hash, error_key_already_exists}; -use super::traits::JournalDB; -use util::{DatabaseKey, DatabaseValueView, DatabaseValueRef}; -#[derive(Debug, Clone, PartialEq, Eq)] +use crate::{ + DB_PREFIX_LEN, LATEST_ERA_KEY, error_negatively_reference_hash, error_key_already_exists, + JournalDB, new_memory_db, + util::{DatabaseKey, DatabaseValueView, DatabaseValueRef}, +}; + +#[derive(Debug, Clone, PartialEq, Eq, MallocSizeOf)] struct RefInfo { queue_refs: usize, in_archive: bool, } -impl HeapSizeOf for RefInfo { - fn heap_size_of_children(&self) -> usize { 0 } -} - #[derive(Clone, PartialEq, Eq)] enum RemoveFrom { Queue, @@ -107,41 +107,41 @@ enum RemoveFrom { /// /// TODO: `store_reclaim_period` pub struct EarlyMergeDB { - overlay: MemoryDB, - backing: Arc, + overlay: super::MemoryDB, + backing: Arc, refs: Option>>>, latest_era: Option, - column: Option, + column: u32, } impl EarlyMergeDB { /// Create a new instance from file - pub fn new(backing: Arc, col: Option) -> EarlyMergeDB { - let (latest_era, refs) = EarlyMergeDB::read_refs(&*backing, col); + pub fn new(backing: Arc, column: u32) -> EarlyMergeDB { + let (latest_era, refs) = EarlyMergeDB::read_refs(&*backing, column); let refs = Some(Arc::new(RwLock::new(refs))); EarlyMergeDB { - overlay: ::new_memory_db(), - backing: backing, - refs: refs, - latest_era: latest_era, - column: col, + overlay: new_memory_db(), + backing, + refs, + latest_era, + column, } } fn morph_key(key: &H256, index: u8) -> Bytes { - let mut ret = (&**key).to_owned(); + let mut ret = key.as_bytes().to_owned(); ret.push(index); ret } // The next three are valid only as long as there is an insert operation of `key` in the journal. - fn set_already_in(batch: &mut DBTransaction, col: Option, key: &H256) { batch.put(col, &Self::morph_key(key, 0), &[1u8]); } - fn reset_already_in(batch: &mut DBTransaction, col: Option, key: &H256) { batch.delete(col, &Self::morph_key(key, 0)); } - fn is_already_in(backing: &KeyValueDB, col: Option, key: &H256) -> bool { + fn set_already_in(batch: &mut DBTransaction, col: u32, key: &H256) { batch.put(col, &Self::morph_key(key, 0), &[1u8]); } + fn reset_already_in(batch: &mut DBTransaction, col: u32, key: &H256) { batch.delete(col, &Self::morph_key(key, 0)); } + fn is_already_in(backing: &dyn KeyValueDB, col: u32, key: &H256) -> bool { backing.get(col, &Self::morph_key(key, 0)).expect("Low-level database error. Some issue with your hard disk?").is_some() } - fn insert_keys(inserts: &[(H256, DBValue)], backing: &KeyValueDB, col: Option, refs: &mut HashMap, batch: &mut DBTransaction) { + fn insert_keys(inserts: &[(H256, DBValue)], backing: &dyn KeyValueDB, col: u32, refs: &mut HashMap, batch: &mut DBTransaction) { for &(ref h, ref d) in inserts { match refs.entry(*h) { Entry::Occupied(mut entry) => { @@ -152,7 +152,8 @@ impl EarlyMergeDB { }, Entry::Vacant(entry) => { // this is the first entry for this node in the journal. - let in_archive = backing.get(col, h).expect("Low-level database error. Some issue with your hard disk?").is_some(); + let in_archive = backing.get(col, h.as_bytes()) + .expect("Low-level database error. Some issue with your hard disk?").is_some(); if in_archive { // already in the backing DB. start counting, and remember it was already in. Self::set_already_in(batch, col, h); @@ -162,7 +163,7 @@ impl EarlyMergeDB { //Self::reset_already_in(&h); assert!(!Self::is_already_in(backing, col, h)); trace!(target: "jdb.fine", " insert({}): New to queue, not in DB: Inserting into queue and DB", h); - batch.put(col, h, d); + batch.put(col, h.as_bytes(), d); } entry.insert(RefInfo { queue_refs: 1, @@ -173,7 +174,7 @@ impl EarlyMergeDB { } } - fn replay_keys(inserts: &[H256], backing: &KeyValueDB, col: Option, refs: &mut HashMap) { + fn replay_keys(inserts: &[H256], backing: &dyn KeyValueDB, col: u32, refs: &mut HashMap) { trace!(target: "jdb.fine", "replay_keys: inserts={:?}, refs={:?}", inserts, refs); for h in inserts { match refs.entry(*h) { @@ -194,7 +195,7 @@ impl EarlyMergeDB { trace!(target: "jdb.fine", "replay_keys: (end) refs={:?}", refs); } - fn remove_keys(deletes: &[H256], refs: &mut HashMap, batch: &mut DBTransaction, col: Option, from: RemoveFrom) { + fn remove_keys(deletes: &[H256], refs: &mut HashMap, batch: &mut DBTransaction, col: u32, from: RemoveFrom) { // with a remove on {queue_refs: 1, in_archive: true}, we have two options: // - convert to {queue_refs: 1, in_archive: false} (i.e. remove it from the conceptual archive) // - convert to {queue_refs: 0, in_archive: true} (i.e. remove it from the conceptual queue) @@ -227,7 +228,7 @@ impl EarlyMergeDB { }, (1, false) => { entry.remove(); - batch.delete(col, h); + batch.delete(col, h.as_bytes()); trace!(target: "jdb.fine", " remove({}): Not in archive, only 1 ref in queue: Removing from queue and DB", h); }, _ => panic!("Invalid value in refs: {:?}", entry.get()), @@ -236,7 +237,7 @@ impl EarlyMergeDB { Entry::Vacant(_entry) => { // Gets removed when moving from 1 to 0 additional refs. Should never be here at 0 additional refs. //assert!(!Self::is_already_in(db, &h)); - batch.delete(col, h); + batch.delete(col, h.as_bytes()); trace!(target: "jdb.fine", " remove({}): Not in queue - MUST BE IN ARCHIVE: Removing from DB", h); }, } @@ -258,10 +259,12 @@ impl EarlyMergeDB { } fn payload(&self, key: &H256) -> Option { - self.backing.get(self.column, key).expect("Low-level database error. Some issue with your hard disk?") + self.backing + .get(self.column, key.as_bytes()) + .expect("Low-level database error. Some issue with your hard disk?") } - fn read_refs(db: &KeyValueDB, col: Option) -> (Option, HashMap) { + fn read_refs(db: &dyn KeyValueDB, col: u32) -> (Option, HashMap) { 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.") { @@ -289,8 +292,8 @@ impl EarlyMergeDB { } impl HashDB for EarlyMergeDB { - fn get(&self, key: &H256) -> Option { - if let Some((d, rc)) = self.overlay.raw(key) { + fn get(&self, key: &H256, prefix: Prefix) -> Option { + if let Some((d, rc)) = self.overlay.raw(key, prefix) { if rc > 0 { return Some(d.clone()) } @@ -298,43 +301,23 @@ impl HashDB for EarlyMergeDB { self.payload(key) } - fn contains(&self, key: &H256) -> bool { - self.get(key).is_some() + fn contains(&self, key: &H256, prefix: Prefix) -> bool { + self.get(key, prefix).is_some() } - fn insert(&mut self, value: &[u8]) -> H256 { - self.overlay.insert(value) + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { + self.overlay.insert(prefix, value) } - fn emplace(&mut self, key: H256, value: DBValue) { - self.overlay.emplace(key, value); + fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { + self.overlay.emplace(key, prefix, value); } - fn remove(&mut self, key: &H256) { - self.overlay.remove(key); - } -} - -impl ::traits::KeyedHashDB for EarlyMergeDB { - fn keys(&self) -> HashMap { - let mut ret: HashMap = self.backing.iter(self.column) - .map(|(key, _)| (H256::from_slice(&*key), 1)) - .collect(); - - for (key, refs) in self.overlay.keys() { - match ret.entry(key) { - Entry::Occupied(mut entry) => { - *entry.get_mut() += refs; - }, - Entry::Vacant(entry) => { - entry.insert(refs); - } - } - } - ret + fn remove(&mut self, key: &H256, prefix: Prefix) { + self.overlay.remove(key, prefix); } } impl JournalDB for EarlyMergeDB { - fn boxed_clone(&self) -> Box { + fn boxed_clone(&self) -> Box { Box::new(EarlyMergeDB { overlay: self.overlay.clone(), backing: self.backing.clone(), @@ -348,18 +331,19 @@ impl JournalDB for EarlyMergeDB { self.backing.get(self.column, &LATEST_ERA_KEY).expect("Low level database error").is_none() } - fn backing(&self) -> &Arc { + fn backing(&self) -> &Arc { &self.backing } fn latest_era(&self) -> Option { self.latest_era } fn mem_used(&self) -> usize { - self.overlay.mem_used() + match self.refs { - Some(ref c) => c.read().heap_size_of_children(), + let mut ops = new_malloc_size_ops(); + self.overlay.size_of(&mut ops) + match self.refs { + Some(ref c) => c.read().size_of(&mut ops), None => 0 } - } + } fn state(&self, id: &H256) -> Option { self.backing.get_by_prefix(self.column, &id[0..DB_PREFIX_LEN]).map(|b| b.into_vec()) @@ -499,16 +483,16 @@ impl JournalDB for EarlyMergeDB { match rc { 0 => {} 1 => { - if self.backing.get(self.column, &key)?.is_some() { + if self.backing.get(self.column, key.as_bytes())?.is_some() { return Err(error_key_already_exists(&key)); } - batch.put(self.column, &key, &value) + batch.put(self.column, key.as_bytes(), &value) } -1 => { - if self.backing.get(self.column, &key)?.is_none() { + if self.backing.get(self.column, key.as_bytes())?.is_none() { return Err(error_negatively_reference_hash(&key)); } - batch.delete(self.column, &key) + batch.delete(self.column, key.as_bytes()) } _ => panic!("Attempted to inject invalid state."), } @@ -517,91 +501,108 @@ impl JournalDB for EarlyMergeDB { Ok(ops) } - fn consolidate(&mut self, with: MemoryDB) { + fn consolidate(&mut self, with: super::MemoryDB) { self.overlay.consolidate(with); } + + fn keys(&self) -> HashMap { + let mut ret: HashMap = self.backing.iter(self.column) + .map(|(key, _)| (H256::from_slice(&*key), 1)) + .collect(); + + for (key, refs) in self.overlay.keys() { + match ret.entry(key) { + Entry::Occupied(mut entry) => { + *entry.get_mut() += refs; + }, + Entry::Vacant(entry) => { + entry.insert(refs); + } + } + } + ret + } } #[cfg(test)] mod tests { - - use keccak::keccak; - use hash_db::HashDB; + use keccak_hash::keccak; + use hash_db::{HashDB, EMPTY_PREFIX}; use super::*; - use super::super::traits::JournalDB; use kvdb_memorydb; + use crate::{inject_batch, commit_batch}; #[test] fn insert_same_in_fork() { // history is 1 let mut jdb = new_db(); - let x = jdb.insert(b"X"); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + let x = jdb.insert(EMPTY_PREFIX, b"X"); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(3, &keccak(b"1002a"), Some((1, keccak(b"1")))).unwrap(); + commit_batch(&mut jdb, 3, &keccak(b"1002a"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(4, &keccak(b"1003a"), Some((2, keccak(b"2")))).unwrap(); + commit_batch(&mut jdb, 4, &keccak(b"1003a"), Some((2, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&x); - jdb.commit_batch(3, &keccak(b"1002b"), Some((1, keccak(b"1")))).unwrap(); + jdb.remove(&x, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"1002b"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - let x = jdb.insert(b"X"); - jdb.commit_batch(4, &keccak(b"1003b"), Some((2, keccak(b"2")))).unwrap(); + let x = jdb.insert(EMPTY_PREFIX, b"X"); + commit_batch(&mut jdb, 4, &keccak(b"1003b"), Some((2, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(5, &keccak(b"1004a"), Some((3, keccak(b"1002a")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"1004a"), Some((3, keccak(b"1002a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(6, &keccak(b"1005a"), Some((4, keccak(b"1003a")))).unwrap(); + commit_batch(&mut jdb, 6, &keccak(b"1005a"), Some((4, keccak(b"1003a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&x)); + assert!(jdb.contains(&x, EMPTY_PREFIX)); } #[test] fn insert_older_era() { let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0a"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0a"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0a")))).unwrap(); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&bar); - jdb.commit_batch(0, &keccak(b"0b"), None).unwrap(); + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 0, &keccak(b"0b"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); } #[test] fn long_history() { // history is 3 let mut jdb = new_db(); - let h = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let h = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&h)); - jdb.remove(&h); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + jdb.remove(&h, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&h)); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&h)); - jdb.commit_batch(3, &keccak(b"3"), Some((0, keccak(b"0")))).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&h)); - jdb.commit_batch(4, &keccak(b"4"), Some((1, keccak(b"1")))).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(!jdb.contains(&h)); + assert!(!jdb.contains(&h, EMPTY_PREFIX)); } #[test] @@ -609,42 +610,42 @@ mod tests { // history is 1 let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); - jdb.remove(&foo); - jdb.remove(&bar); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + jdb.remove(&bar, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - assert!(jdb.contains(&baz)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); - let foo = jdb.insert(b"foo"); - jdb.remove(&baz); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + jdb.remove(&baz, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&bar)); - assert!(jdb.contains(&baz)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&bar)); - assert!(!jdb.contains(&baz)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); - jdb.commit_batch(4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(!jdb.contains(&foo)); - assert!(!jdb.contains(&bar)); - assert!(!jdb.contains(&baz)); + assert!(!jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); } #[test] @@ -652,31 +653,31 @@ mod tests { // history is 1 let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); - jdb.remove(&foo); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&bar); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - assert!(jdb.contains(&baz)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); - jdb.commit_batch(2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&baz)); - assert!(!jdb.contains(&bar)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); } #[test] @@ -684,142 +685,142 @@ mod tests { // history is 1 let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - assert!(jdb.contains(&foo)); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - jdb.commit_batch(3, &keccak(b"2"), Some((0, keccak(b"2")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + commit_batch(&mut jdb, 3, &keccak(b"2"), Some((0, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); } #[test] fn fork_same_key_one() { let mut jdb = new_db(); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1c"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1c"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.commit_batch(2, &keccak(b"2a"), Some((1, keccak(b"1a")))).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2a"), Some((1, keccak(b"1a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); } #[test] fn fork_same_key_other() { let mut jdb = new_db(); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1c"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1c"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.commit_batch(2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); } #[test] fn fork_ins_del_ins() { let mut jdb = new_db(); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(2, &keccak(b"2a"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(2, &keccak(b"2b"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(3, &keccak(b"3a"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 3, &keccak(b"3a"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(3, &keccak(b"3b"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 3, &keccak(b"3b"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(4, &keccak(b"4a"), Some((2, keccak(b"2a")))).unwrap(); + commit_batch(&mut jdb, 4, &keccak(b"4a"), Some((2, keccak(b"2a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(5, &keccak(b"5a"), Some((3, keccak(b"3a")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5a"), Some((3, keccak(b"3a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); } fn new_db() -> EarlyMergeDB { - let backing = Arc::new(kvdb_memorydb::create(0)); - EarlyMergeDB::new(backing, None) + let backing = Arc::new(kvdb_memorydb::create(1)); + EarlyMergeDB::new(backing, 0) } #[test] fn reopen() { - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); let bar = H256::random(); let foo = { - let mut jdb = EarlyMergeDB::new(shared_db.clone(), None); + let mut jdb = EarlyMergeDB::new(shared_db.clone(), 0); // history is 1 - let foo = jdb.insert(b"foo"); - jdb.emplace(bar.clone(), DBValue::from_slice(b"bar")); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + jdb.emplace(bar.clone(), EMPTY_PREFIX, b"bar".to_vec()); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); foo }; { - let mut jdb = EarlyMergeDB::new(shared_db.clone(), None); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + let mut jdb = EarlyMergeDB::new(shared_db.clone(), 0); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); } { - let mut jdb = EarlyMergeDB::new(shared_db, None); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + let mut jdb = EarlyMergeDB::new(shared_db, 0); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(!jdb.contains(&foo)); + assert!(!jdb.contains(&foo, EMPTY_PREFIX)); } } @@ -830,23 +831,23 @@ mod tests { let mut jdb = new_db(); // history is 4 - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(4, &keccak(b"4"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); // expunge foo - jdb.commit_batch(5, &keccak(b"5"), Some((1, keccak(b"1")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); } @@ -856,44 +857,44 @@ mod tests { let mut jdb = new_db(); // history is 4 - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1a"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1a"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1b"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1b"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(2, &keccak(b"2a"), None).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 2, &keccak(b"2a"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(2, &keccak(b"2b"), None).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 2, &keccak(b"2b"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3a"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3a"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3b"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3b"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(4, &keccak(b"4a"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 4, &keccak(b"4a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(4, &keccak(b"4b"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 4, &keccak(b"4b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); // expunge foo - jdb.commit_batch(5, &keccak(b"5"), Some((1, keccak(b"1a")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((1, keccak(b"1a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); } @@ -902,28 +903,28 @@ mod tests { let mut jdb = new_db(); // history is 1 - let foo = jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); // foo is ancient history. - jdb.remove(&foo); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); // BROKEN + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); // BROKEN assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.remove(&foo); - jdb.commit_batch(4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(5, &keccak(b"5"), Some((4, keccak(b"4")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((4, keccak(b"4")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(!jdb.contains(&foo)); + assert!(!jdb.contains(&foo, EMPTY_PREFIX)); } #[test] @@ -931,31 +932,31 @@ mod tests { let mut jdb = new_db(); // history is 4 - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(3, &keccak(b"3"), None).unwrap(); + commit_batch(&mut jdb, 3, &keccak(b"3"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(4, &keccak(b"4"), Some((0, keccak(b"0")))).unwrap(); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); // foo is ancient history. - jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(5, &keccak(b"5"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.remove(&bar); - jdb.commit_batch(6, &keccak(b"6"), Some((2, keccak(b"2")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 6, &keccak(b"6"), Some((2, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.insert(b"bar"); - jdb.commit_batch(7, &keccak(b"7"), Some((3, keccak(b"3")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 7, &keccak(b"7"), Some((3, keccak(b"3")))).unwrap(); assert!(jdb.can_reconstruct_refs()); } @@ -963,99 +964,99 @@ mod tests { fn reopen_remove_three() { let _ = ::env_logger::try_init(); - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); let foo = keccak(b"foo"); { - let mut jdb = EarlyMergeDB::new(shared_db.clone(), None); + let mut jdb = EarlyMergeDB::new(shared_db.clone(), 0); // history is 1 - jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); // foo is ancient history. - jdb.remove(&foo); - jdb.commit_batch(2, &keccak(b"2"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.insert(b"foo"); - jdb.commit_batch(3, &keccak(b"3"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); // incantation to reopen the db }; { - let mut jdb = EarlyMergeDB::new(shared_db.clone(), None); + let mut jdb = EarlyMergeDB::new(shared_db.clone(), 0); - jdb.remove(&foo); - jdb.commit_batch(4, &keccak(b"4"), Some((2, keccak(b"2")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((2, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); // incantation to reopen the db }; { - let mut jdb = EarlyMergeDB::new(shared_db.clone(), None); + let mut jdb = EarlyMergeDB::new(shared_db.clone(), 0); - jdb.commit_batch(5, &keccak(b"5"), Some((3, keccak(b"3")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((3, keccak(b"3")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); // incantation to reopen the db }; { - let mut jdb = EarlyMergeDB::new(shared_db, None); + let mut jdb = EarlyMergeDB::new(shared_db, 0); - jdb.commit_batch(6, &keccak(b"6"), Some((4, keccak(b"4")))).unwrap(); + commit_batch(&mut jdb, 6, &keccak(b"6"), Some((4, keccak(b"4")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(!jdb.contains(&foo)); + assert!(!jdb.contains(&foo, EMPTY_PREFIX)); } } #[test] fn reopen_fork() { - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); let (foo, bar, baz) = { - let mut jdb = EarlyMergeDB::new(shared_db.clone(), None); + let mut jdb = EarlyMergeDB::new(shared_db.clone(), 0); // history is 1 - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&bar); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); (foo, bar, baz) }; { - let mut jdb = EarlyMergeDB::new(shared_db, None); - jdb.commit_batch(2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); + let mut jdb = EarlyMergeDB::new(shared_db, 0); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&baz)); - assert!(!jdb.contains(&bar)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); } } #[test] fn inject() { let mut jdb = new_db(); - let key = jdb.insert(b"dog"); - jdb.inject_batch().unwrap(); + let key = jdb.insert(EMPTY_PREFIX, b"dog"); + inject_batch(&mut jdb).unwrap(); - assert_eq!(jdb.get(&key).unwrap(), DBValue::from_slice(b"dog")); - jdb.remove(&key); - jdb.inject_batch().unwrap(); + assert_eq!(jdb.get(&key, EMPTY_PREFIX).unwrap(), b"dog".to_vec()); + jdb.remove(&key, EMPTY_PREFIX); + inject_batch(&mut jdb).unwrap(); - assert!(jdb.get(&key).is_none()); + assert!(jdb.get(&key, EMPTY_PREFIX).is_none()); } } diff --git a/util/journaldb/src/lib.rs b/util/journaldb/src/lib.rs index 3eb6ee38c8..7163d18cb4 100644 --- a/util/journaldb/src/lib.rs +++ b/util/journaldb/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,48 +16,91 @@ //! `JournalDB` interface and implementation. -extern crate heapsize; -#[macro_use] -extern crate log; +use std::{ + fmt, str, io, + sync::Arc, + collections::HashMap, +}; -extern crate ethereum_types; -extern crate parity_bytes as bytes; -extern crate hash_db; -extern crate keccak_hasher; -extern crate kvdb; -extern crate memory_db; -extern crate parking_lot; -extern crate fastmap; -extern crate rlp; +use ethereum_types::H256; +use hash_db::HashDB; +use keccak_hasher::KeccakHasher; +use kvdb::{self, DBTransaction, DBValue}; +use parity_bytes::Bytes; -#[cfg(test)] -extern crate env_logger; -#[cfg(test)] -extern crate keccak_hash as keccak; -#[cfg(test)] -extern crate kvdb_memorydb; - -use std::{fmt, str, io}; -use std::sync::Arc; - -/// Export the journaldb module. -mod traits; mod archivedb; mod earlymergedb; mod overlayrecentdb; mod refcounteddb; mod util; mod as_hash_db_impls; +mod overlaydb; + +/// A `HashDB` which can manage a short-term journal potentially containing many forks of mutually +/// exclusive actions. +pub trait JournalDB: HashDB { + /// Return a copy of ourself, in a box. + fn boxed_clone(&self) -> Box; + + /// Returns heap memory size used + fn mem_used(&self) -> usize; + + /// Returns the size of journalled state in memory. + /// This function has a considerable speed requirement -- + /// it must be fast enough to call several times per block imported. + fn journal_size(&self) -> usize { 0 } + + /// Check if this database has any commits + fn is_empty(&self) -> bool; + + /// Get the earliest era in the DB. None if there isn't yet any data in there. + fn earliest_era(&self) -> Option { None } + + /// Get the latest era in the DB. None if there isn't yet any data in there. + fn latest_era(&self) -> Option; + + /// Journal recent database operations as being associated with a given era and id. + // TODO: give the overlay to this function so journaldbs don't manage the overlays themselves. + fn journal_under(&mut self, batch: &mut DBTransaction, now: u64, id: &H256) -> io::Result; + + /// Mark a given block as canonical, indicating that competing blocks' states may be pruned out. + fn mark_canonical(&mut self, batch: &mut DBTransaction, era: u64, id: &H256) -> io::Result; + + /// Commit all queued insert and delete operations without affecting any journalling -- this requires that all insertions + /// and deletions are indeed canonical and will likely lead to an invalid database if that assumption is violated. + /// + /// Any keys or values inserted or deleted must be completely independent of those affected + /// by any previous `commit` operations. Essentially, this means that `inject` can be used + /// either to restore a state to a fresh database, or to insert data which may only be journalled + /// from this point onwards. + fn inject(&mut self, batch: &mut DBTransaction) -> io::Result; -pub mod overlaydb; + /// State data query + fn state(&self, _id: &H256) -> Option; -/// Export the `JournalDB` trait. -pub use self::traits::JournalDB; + /// Whether this database is pruned. + fn is_prunable(&self) -> bool { true } -/// Export keyed hash trait -pub use self::traits::KeyedHashDB; -/// Export as keyed hash trait -pub use self::traits::AsKeyedHashDB; + /// Get backing database. + fn backing(&self) -> &Arc; + + /// Clear internal structure. This should be called after changes have been written + /// to the backing storage. + fn flush(&self) {} + + /// Consolidate all the insertions and deletions in the given memory overlay. + fn consolidate(&mut self, overlay: MemoryDB); + + /// Primarily use for tests, highly inefficient. + fn keys(&self) -> HashMap; +} + +/// Alias to ethereum MemoryDB +type MemoryDB = memory_db::MemoryDB< + keccak_hasher::KeccakHasher, + memory_db::HashKey, + kvdb::DBValue, +>; /// Journal database operating strategy. #[derive(Debug, PartialEq, Clone, Copy)] @@ -142,7 +185,7 @@ impl fmt::Display for Algorithm { } /// Create a new `JournalDB` trait object over a generic key-value database. -pub fn new(backing: Arc<::kvdb::KeyValueDB>, algorithm: Algorithm, col: Option) -> Box { +pub fn new(backing: Arc, algorithm: Algorithm, col: u32) -> Box { match algorithm { Algorithm::Archive => Box::new(archivedb::ArchiveDB::new(backing, col)), Algorithm::EarlyMerge => Box::new(earlymergedb::EarlyMergeDB::new(backing, col)), @@ -163,8 +206,31 @@ fn error_negatively_reference_hash(hash: ðereum_types::H256) -> io::Error { io::Error::new(io::ErrorKind::Other, format!("Entry {} removed from database more times than it was added.", hash)) } -pub fn new_memory_db() -> memory_db::MemoryDB { - memory_db::MemoryDB::from_null_node(&rlp::NULL_RLP, rlp::NULL_RLP.as_ref().into()) +pub fn new_memory_db() -> MemoryDB { + MemoryDB::from_null_node(&rlp::NULL_RLP, rlp::NULL_RLP.as_ref().into()) +} + +#[cfg(test)] +/// Inject all changes in a single batch. +pub fn inject_batch(jdb: &mut dyn JournalDB) -> io::Result { + let mut batch = jdb.backing().transaction(); + let res = jdb.inject(&mut batch)?; + jdb.backing().write(batch).map(|_| res).map_err(Into::into) +} + +/// Commit all changes in a single batch +#[cfg(test)] +fn commit_batch(jdb: &mut dyn JournalDB, now: u64, id: &H256, end: Option<(u64, H256)>) -> io::Result { + let mut batch = jdb.backing().transaction(); + let mut ops = jdb.journal_under(&mut batch, now, id)?; + + if let Some((end_era, canon_id)) = end { + ops += jdb.mark_canonical(&mut batch, end_era, &canon_id)?; + } + + let result = jdb.backing().write(batch).map(|_| ops).map_err(Into::into); + jdb.flush(); + result } #[cfg(test)] diff --git a/util/journaldb/src/overlaydb.rs b/util/journaldb/src/overlaydb.rs index 757a92e621..203702a286 100644 --- a/util/journaldb/src/overlaydb.rs +++ b/util/journaldb/src/overlaydb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,18 +16,20 @@ //! Disk-backed `HashDB` implementation. -use std::collections::HashMap; -use std::collections::hash_map::Entry; -use std::io; -use std::sync::Arc; +use std::{ + collections::{HashMap, hash_map::Entry}, + io, + sync::Arc, +}; use ethereum_types::H256; -use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable, encode, decode}; -use hash_db::{HashDB}; +use hash_db::{HashDB, Prefix}; use keccak_hasher::KeccakHasher; -use memory_db::*; use kvdb::{KeyValueDB, DBTransaction, DBValue}; -use super::{error_negatively_reference_hash}; +use log::trace; +use rlp::{Rlp, RlpStream, Encodable, DecoderError, Decodable, encode, decode}; + +use crate::{error_negatively_reference_hash, new_memory_db}; /// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay. /// @@ -39,9 +41,9 @@ use super::{error_negatively_reference_hash}; /// queries have an immediate effect in terms of these functions. #[derive(Clone)] pub struct OverlayDB { - overlay: MemoryDB, - backing: Arc, - column: Option, + overlay: super::MemoryDB, + backing: Arc, + column: u32, } struct Payload { @@ -70,7 +72,7 @@ impl Decodable for Payload { fn decode(rlp: &Rlp) -> Result { let payload = Payload { count: rlp.val_at(0)?, - value: DBValue::from_slice(rlp.at(1)?.data()?), + value: rlp.at(1)?.data()?.to_vec(), }; Ok(payload) @@ -79,15 +81,19 @@ impl Decodable for Payload { impl OverlayDB { /// Create a new instance of OverlayDB given a `backing` database. - pub fn new(backing: Arc, col: Option) -> OverlayDB { - OverlayDB{ overlay: ::new_memory_db(), backing: backing, column: col } + pub fn new(backing: Arc, column: u32) -> OverlayDB { + OverlayDB { + overlay: new_memory_db(), + backing, + column, + } } /// Create a new instance of OverlayDB with an anonymous temporary database. #[cfg(test)] pub fn new_temp() -> OverlayDB { - let backing = Arc::new(::kvdb_memorydb::create(0)); - Self::new(backing, None) + let backing = Arc::new(::kvdb_memorydb::create(1)); + Self::new(backing, 0) } /// Commit all operations in a single batch. @@ -129,16 +135,9 @@ impl OverlayDB { Ok(ret) } - /// Revert all operations on this object (i.e. `insert()`s and `remove()`s) since the - /// last `commit()`. - pub fn revert(&mut self) { self.overlay.clear(); } - - /// Get the number of references that would be committed. - pub fn commit_refs(&self, key: &H256) -> i32 { self.overlay.raw(key).map_or(0, |(_, refs)| refs) } - /// Get the refs and value of the given key. fn payload(&self, key: &H256) -> Option { - self.backing.get(self.column, key) + self.backing.get(self.column, key.as_bytes()) .expect("Low-level database error. Some issue with your hard disk?") .map(|ref d| decode(d).expect("decoding db value failed") ) } @@ -146,18 +145,15 @@ impl OverlayDB { /// Put the refs and value of the given key, possibly deleting it from the db. fn put_payload_in_batch(&self, batch: &mut DBTransaction, key: &H256, payload: &Payload) -> bool { if payload.count > 0 { - batch.put(self.column, key, &encode(payload)); + batch.put(self.column, key.as_bytes(), &encode(payload)); false } else { - batch.delete(self.column, key); + batch.delete(self.column, key.as_bytes()); true } } -} - -impl crate::KeyedHashDB for OverlayDB { - fn keys(&self) -> HashMap { + pub fn keys(&self) -> HashMap { let mut ret: HashMap = self.backing.iter(self.column) .map(|(key, _)| { let h = H256::from_slice(&*key); @@ -178,14 +174,13 @@ impl crate::KeyedHashDB for OverlayDB { } ret } - } impl HashDB for OverlayDB { - fn get(&self, key: &H256) -> Option { + fn get(&self, key: &H256, prefix: Prefix) -> Option { // return ok if positive; if negative, check backing - might be enough references there to make // it positive again. - let k = self.overlay.raw(key); + let k = self.overlay.raw(key, prefix); let memrc = { if let Some((d, rc)) = k { if rc > 0 { return Some(d.clone()); } @@ -209,10 +204,10 @@ impl HashDB for OverlayDB { } } - fn contains(&self, key: &H256) -> bool { + fn contains(&self, key: &H256, prefix: Prefix) -> bool { // return ok if positive; if negative, check backing - might be enough references there to make // it positive again. - let k = self.overlay.raw(key); + let k = self.overlay.raw(key, prefix); match k { Some((_, rc)) if rc > 0 => true, _ => { @@ -229,111 +224,108 @@ impl HashDB for OverlayDB { } } - fn insert(&mut self, value: &[u8]) -> H256 { self.overlay.insert(value) } - fn emplace(&mut self, key: H256, value: DBValue) { self.overlay.emplace(key, value); } - fn remove(&mut self, key: &H256) { self.overlay.remove(key); } + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { self.overlay.insert(prefix, value) } + fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { self.overlay.emplace(key, prefix, value); } + fn remove(&mut self, key: &H256, prefix: Prefix) { self.overlay.remove(key, prefix); } } -#[test] -fn overlaydb_revert() { - let mut m = OverlayDB::new_temp(); - let foo = m.insert(b"foo"); // insert foo. - let mut batch = m.backing.transaction(); - m.commit_to_batch(&mut batch).unwrap(); // commit - new operations begin here... - m.backing.write(batch).unwrap(); - let bar = m.insert(b"bar"); // insert bar. - m.remove(&foo); // remove foo. - assert!(!m.contains(&foo)); // foo is gone. - assert!(m.contains(&bar)); // bar is here. - m.revert(); // revert the last two operations. - assert!(m.contains(&foo)); // foo is here. - assert!(!m.contains(&bar)); // bar is gone. -} +#[cfg(test)] +mod tests { + use hash_db::EMPTY_PREFIX; + use super::*; + + #[test] + fn overlaydb_revert() { + let mut m = OverlayDB::new_temp(); + let foo = m.insert(EMPTY_PREFIX, b"foo"); // insert foo. + let mut batch = m.backing.transaction(); + m.commit_to_batch(&mut batch).unwrap(); // commit - new operations begin here... + m.backing.write(batch).unwrap(); + let bar = m.insert(EMPTY_PREFIX, b"bar"); // insert bar. + m.remove(&foo, EMPTY_PREFIX); // remove foo. + assert!(!m.contains(&foo, EMPTY_PREFIX)); // foo is gone. + assert!(m.contains(&bar, EMPTY_PREFIX)); // bar is here. + } -#[test] -fn overlaydb_overlay_insert_and_remove() { - let mut trie = OverlayDB::new_temp(); - let h = trie.insert(b"hello world"); - assert_eq!(trie.get(&h).unwrap(), DBValue::from_slice(b"hello world")); - trie.remove(&h); - assert_eq!(trie.get(&h), None); -} + #[test] + fn overlaydb_overlay_insert_and_remove() { + let mut trie = OverlayDB::new_temp(); + let h = trie.insert(EMPTY_PREFIX, b"hello world"); + assert_eq!(trie.get(&h, EMPTY_PREFIX).unwrap(), b"hello world".to_vec()); + trie.remove(&h, EMPTY_PREFIX); + assert_eq!(trie.get(&h, EMPTY_PREFIX), None); + } -#[test] -fn overlaydb_backing_insert_revert() { - let mut trie = OverlayDB::new_temp(); - let h = trie.insert(b"hello world"); - assert_eq!(trie.get(&h).unwrap(), DBValue::from_slice(b"hello world")); - trie.commit().unwrap(); - assert_eq!(trie.get(&h).unwrap(), DBValue::from_slice(b"hello world")); - trie.revert(); - assert_eq!(trie.get(&h).unwrap(), DBValue::from_slice(b"hello world")); -} + #[test] + fn overlaydb_backing_insert_revert() { + let mut trie = OverlayDB::new_temp(); + let h = trie.insert(EMPTY_PREFIX, b"hello world"); + assert_eq!(trie.get(&h, EMPTY_PREFIX).unwrap(), b"hello world".to_vec()); + trie.commit().unwrap(); + assert_eq!(trie.get(&h, EMPTY_PREFIX).unwrap(), b"hello world".to_vec()); + } -#[test] -fn overlaydb_backing_remove() { - let mut trie = OverlayDB::new_temp(); - let h = trie.insert(b"hello world"); - trie.commit().unwrap(); - trie.remove(&h); - assert_eq!(trie.get(&h), None); - trie.commit().unwrap(); - assert_eq!(trie.get(&h), None); - trie.revert(); - assert_eq!(trie.get(&h), None); -} + #[test] + fn overlaydb_backing_remove() { + let mut trie = OverlayDB::new_temp(); + let h = trie.insert(EMPTY_PREFIX, b"hello world"); + trie.commit().unwrap(); + trie.remove(&h, EMPTY_PREFIX); + assert_eq!(trie.get(&h, EMPTY_PREFIX), None); + trie.commit().unwrap(); + assert_eq!(trie.get(&h, EMPTY_PREFIX), None); + } -#[test] -fn overlaydb_backing_remove_revert() { - let mut trie = OverlayDB::new_temp(); - let h = trie.insert(b"hello world"); - trie.commit().unwrap(); - trie.remove(&h); - assert_eq!(trie.get(&h), None); - trie.revert(); - assert_eq!(trie.get(&h).unwrap(), DBValue::from_slice(b"hello world")); -} + #[test] + fn overlaydb_backing_remove_revert() { + let mut trie = OverlayDB::new_temp(); + let h = trie.insert(EMPTY_PREFIX, b"hello world"); + trie.commit().unwrap(); + trie.remove(&h, EMPTY_PREFIX); + assert_eq!(trie.get(&h, EMPTY_PREFIX), None); + } -#[test] -fn overlaydb_negative() { - let mut trie = OverlayDB::new_temp(); - let h = trie.insert(b"hello world"); - trie.commit().unwrap(); - trie.remove(&h); - trie.remove(&h); //bad - sends us into negative refs. - assert_eq!(trie.get(&h), None); - assert!(trie.commit().is_err()); -} + #[test] + fn overlaydb_negative() { + let mut trie = OverlayDB::new_temp(); + let h = trie.insert(EMPTY_PREFIX, b"hello world"); + trie.commit().unwrap(); + trie.remove(&h, EMPTY_PREFIX); + trie.remove(&h, EMPTY_PREFIX); //bad - sends us into negative refs. + assert_eq!(trie.get(&h, EMPTY_PREFIX), None); + assert!(trie.commit().is_err()); + } -#[test] -fn overlaydb_complex() { - let mut trie = OverlayDB::new_temp(); - let hfoo = trie.insert(b"foo"); - assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); - let hbar = trie.insert(b"bar"); - assert_eq!(trie.get(&hbar).unwrap(), DBValue::from_slice(b"bar")); - trie.commit().unwrap(); - assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); - assert_eq!(trie.get(&hbar).unwrap(), DBValue::from_slice(b"bar")); - trie.insert(b"foo"); // two refs - assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); - trie.commit().unwrap(); - assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); - assert_eq!(trie.get(&hbar).unwrap(), DBValue::from_slice(b"bar")); - trie.remove(&hbar); // zero refs - delete - assert_eq!(trie.get(&hbar), None); - trie.remove(&hfoo); // one ref - keep - assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); - trie.commit().unwrap(); - assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); - trie.remove(&hfoo); // zero ref - would delete, but... - assert_eq!(trie.get(&hfoo), None); - trie.insert(b"foo"); // one ref - keep after all. - assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); - trie.commit().unwrap(); - assert_eq!(trie.get(&hfoo).unwrap(), DBValue::from_slice(b"foo")); - trie.remove(&hfoo); // zero ref - delete - assert_eq!(trie.get(&hfoo), None); - trie.commit().unwrap(); // - assert_eq!(trie.get(&hfoo), None); + #[test] + fn overlaydb_complex() { + let mut trie = OverlayDB::new_temp(); + let hfoo = trie.insert(EMPTY_PREFIX, b"foo"); + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX).unwrap(), b"foo".to_vec()); + let hbar = trie.insert(EMPTY_PREFIX, b"bar"); + assert_eq!(trie.get(&hbar, EMPTY_PREFIX).unwrap(), b"bar".to_vec()); + trie.commit().unwrap(); + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX).unwrap(), b"foo".to_vec()); + assert_eq!(trie.get(&hbar, EMPTY_PREFIX).unwrap(), b"bar".to_vec()); + trie.insert(EMPTY_PREFIX, b"foo"); // two refs + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX).unwrap(), b"foo".to_vec()); + trie.commit().unwrap(); + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX).unwrap(), b"foo".to_vec()); + assert_eq!(trie.get(&hbar, EMPTY_PREFIX).unwrap(), b"bar".to_vec()); + trie.remove(&hbar, EMPTY_PREFIX); // zero refs - delete + assert_eq!(trie.get(&hbar, EMPTY_PREFIX), None); + trie.remove(&hfoo, EMPTY_PREFIX); // one ref - keep + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX).unwrap(), b"foo".to_vec()); + trie.commit().unwrap(); + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX).unwrap(), b"foo".to_vec()); + trie.remove(&hfoo, EMPTY_PREFIX); // zero ref - would delete, but... + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX), None); + trie.insert(EMPTY_PREFIX, b"foo"); // one ref - keep after all. + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX).unwrap(), b"foo".to_vec()); + trie.commit().unwrap(); + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX).unwrap(), b"foo".to_vec()); + trie.remove(&hfoo, EMPTY_PREFIX); // zero ref - delete + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX), None); + trie.commit().unwrap(); // + assert_eq!(trie.get(&hfoo, EMPTY_PREFIX), None); + } } diff --git a/util/journaldb/src/overlayrecentdb.rs b/util/journaldb/src/overlayrecentdb.rs index a48e59d91f..8f17637708 100644 --- a/util/journaldb/src/overlayrecentdb.rs +++ b/util/journaldb/src/overlayrecentdb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,23 +16,29 @@ //! `JournalDB` over in-memory overlay -use std::collections::HashMap; -use std::collections::hash_map::Entry; -use std::io; -use std::sync::Arc; +use std::{ + collections::{HashMap, hash_map::Entry}, + io, + sync::Arc, + time::Duration, +}; -use bytes::Bytes; use ethereum_types::H256; -use hash_db::{HashDB}; -use heapsize::HeapSizeOf; +use fastmap::H256FastMap; +use hash_db::{HashDB, Prefix, EMPTY_PREFIX}; use keccak_hasher::KeccakHasher; use kvdb::{KeyValueDB, DBTransaction, DBValue}; -use memory_db::*; +use log::trace; +use malloc_size_of::{MallocSizeOf, allocators::new_malloc_size_ops}; +use parity_bytes::Bytes; use parking_lot::RwLock; -use fastmap::H256FastMap; use rlp::{Rlp, RlpStream, encode, decode, DecoderError, Decodable, Encodable}; -use super::{DB_PREFIX_LEN, LATEST_ERA_KEY, JournalDB, error_negatively_reference_hash}; -use util::DatabaseKey; + +use crate::{ + DB_PREFIX_LEN, LATEST_ERA_KEY, JournalDB, error_negatively_reference_hash, + new_memory_db, + util::DatabaseKey +}; /// Implementation of the `JournalDB` trait for a disk-backed database with a memory overlay /// and, possibly, latent-removal semantics. @@ -66,10 +72,10 @@ use util::DatabaseKey; /// 7. Delete ancient record from memory and disk. pub struct OverlayRecentDB { - transaction_overlay: MemoryDB, - backing: Arc, + transaction_overlay: super::MemoryDB, + backing: Arc, journal_overlay: Arc>, - column: Option, + column: u32, } struct DatabaseValue { @@ -83,7 +89,7 @@ impl Decodable for DatabaseValue { let id = rlp.val_at(0)?; let inserts = rlp.at(1)?.iter().map(|r| { let k = r.val_at(0)?; - let v = DBValue::from_slice(r.at(1)?.data()?); + let v = r.at(1)?.data()?.to_vec(); Ok((k, v)) }).collect::, _>>()?; let deletes = rlp.list_at(2)?; @@ -120,7 +126,7 @@ impl<'a> Encodable for DatabaseValueRef<'a> { #[derive(PartialEq)] struct JournalOverlay { - backing_overlay: MemoryDB, // Nodes added in the history period + backing_overlay: super::MemoryDB, // Nodes added in the history period pending_overlay: H256FastMap, // Nodes being transfered from backing_overlay to backing db journal: HashMap>, latest_era: Option, @@ -128,19 +134,13 @@ struct JournalOverlay { cumulative_size: usize, // cumulative size of all entries. } -#[derive(PartialEq)] +#[derive(PartialEq, MallocSizeOf)] struct JournalEntry { id: H256, insertions: Vec, deletions: Vec, } -impl HeapSizeOf for JournalEntry { - fn heap_size_of_children(&self) -> usize { - self.insertions.heap_size_of_children() + self.deletions.heap_size_of_children() - } -} - impl Clone for OverlayRecentDB { fn clone(&self) -> OverlayRecentDB { OverlayRecentDB { @@ -154,12 +154,12 @@ impl Clone for OverlayRecentDB { impl OverlayRecentDB { /// Create a new instance. - pub fn new(backing: Arc, col: Option) -> OverlayRecentDB { + pub fn new(backing: Arc, col: u32) -> OverlayRecentDB { let journal_overlay = Arc::new(RwLock::new(OverlayRecentDB::read_overlay(&*backing, col))); OverlayRecentDB { - transaction_overlay: ::new_memory_db(), - backing: backing, - journal_overlay: journal_overlay, + transaction_overlay: new_memory_db(), + backing, + journal_overlay, column: col, } } @@ -176,12 +176,14 @@ impl OverlayRecentDB { } fn payload(&self, key: &H256) -> Option { - self.backing.get(self.column, key).expect("Low-level database error. Some issue with your hard disk?") + self.backing + .get(self.column, key.as_bytes()) + .expect("Low-level database error. Some issue with your hard disk?") } - fn read_overlay(db: &KeyValueDB, col: Option) -> JournalOverlay { + fn read_overlay(db: &dyn KeyValueDB, col: u32) -> JournalOverlay { let mut journal = HashMap::new(); - let mut overlay = ::new_memory_db(); + let mut overlay = new_memory_db(); let mut count = 0; let mut latest_era = None; let mut earliest_era = None; @@ -202,11 +204,11 @@ impl OverlayRecentDB { for (k, v) in value.inserts { let short_key = to_short_key(&k); - if !overlay.contains(&short_key) { + if !overlay.contains(&short_key, EMPTY_PREFIX) { cumulative_size += v.len(); } - overlay.emplace(short_key, v); + overlay.emplace(short_key, EMPTY_PREFIX, v); inserted_keys.push(k); } journal.entry(era).or_insert_with(Vec::new).push(JournalEntry { @@ -227,10 +229,10 @@ impl OverlayRecentDB { JournalOverlay { backing_overlay: overlay, pending_overlay: HashMap::default(), - journal: journal, - latest_era: latest_era, - earliest_era: earliest_era, - cumulative_size: cumulative_size, + journal, + latest_era, + earliest_era, + cumulative_size, } } @@ -238,58 +240,38 @@ impl OverlayRecentDB { #[inline] fn to_short_key(key: &H256) -> H256 { - let mut k = H256::new(); + let mut k = H256::zero(); k[0..DB_PREFIX_LEN].copy_from_slice(&key[0..DB_PREFIX_LEN]); k } -impl ::traits::KeyedHashDB for OverlayRecentDB { - fn keys(&self) -> HashMap { - let mut ret: HashMap = self.backing.iter(self.column) - .map(|(key, _)| (H256::from_slice(&*key), 1)) - .collect(); - - for (key, refs) in self.transaction_overlay.keys() { - match ret.entry(key) { - Entry::Occupied(mut entry) => { - *entry.get_mut() += refs; - }, - Entry::Vacant(entry) => { - entry.insert(refs); - } - } - } - ret - } -} - impl JournalDB for OverlayRecentDB { - fn boxed_clone(&self) -> Box { + fn boxed_clone(&self) -> Box { Box::new(self.clone()) } fn mem_used(&self) -> usize { - let mut mem = self.transaction_overlay.mem_used(); + let mut ops = new_malloc_size_ops(); + let mut mem = self.transaction_overlay.size_of(&mut ops); let overlay = self.journal_overlay.read(); - mem += overlay.backing_overlay.mem_used(); - mem += overlay.pending_overlay.heap_size_of_children(); - mem += overlay.journal.heap_size_of_children(); + mem += overlay.backing_overlay.size_of(&mut ops); + mem += overlay.pending_overlay.size_of(&mut ops); + mem += overlay.journal.size_of(&mut ops); mem } fn journal_size(&self) -> usize { self.journal_overlay.read().cumulative_size - } fn is_empty(&self) -> bool { self.backing.get(self.column, &LATEST_ERA_KEY).expect("Low level database error").is_none() } - fn backing(&self) -> &Arc { + fn backing(&self) -> &Arc { &self.backing } @@ -298,11 +280,22 @@ impl JournalDB for OverlayRecentDB { fn earliest_era(&self) -> Option { self.journal_overlay.read().earliest_era } fn state(&self, key: &H256) -> Option { - let journal_overlay = self.journal_overlay.read(); let key = to_short_key(key); - journal_overlay.backing_overlay.get(&key).map(|v| v.into_vec()) - .or_else(|| journal_overlay.pending_overlay.get(&key).map(|d| d.clone().into_vec())) - .or_else(|| self.backing.get_by_prefix(self.column, &key[0..DB_PREFIX_LEN]).map(|b| b.into_vec())) + // Hold the read lock for shortest possible amount of time. + let maybe_state_data = { + let journal_overlay = self.journal_overlay.read(); + journal_overlay + .backing_overlay + .get(&key, EMPTY_PREFIX) + .or_else(|| journal_overlay.pending_overlay.get(&key).map(|d| d.clone())) + }; + + maybe_state_data.or_else(|| { + let pkey = &key[..DB_PREFIX_LEN]; + self.backing + .get_by_prefix(self.column, &pkey) + .map(|b| b.to_vec()) + }) } fn journal_under(&mut self, batch: &mut DBTransaction, now: u64, id: &H256) -> io::Result { @@ -332,11 +325,11 @@ impl JournalDB for OverlayRecentDB { for (k, v) in insertions { let short_key = to_short_key(&k); - if !journal_overlay.backing_overlay.contains(&short_key) { + if !journal_overlay.backing_overlay.contains(&short_key, EMPTY_PREFIX) { journal_overlay.cumulative_size += v.len(); } - journal_overlay.backing_overlay.emplace(short_key, v); + journal_overlay.backing_overlay.emplace(short_key, EMPTY_PREFIX, v); } let index = journal_overlay.journal.get(&now).map_or(0, |j| j.len()); @@ -369,7 +362,7 @@ impl JournalDB for OverlayRecentDB { let mut ops = 0; // apply old commits' details - if let Some(ref mut records) = journal_overlay.journal.get_mut(&end_era) { + if let Some(records) = journal_overlay.journal.get_mut(&end_era) { let mut canon_insertions: Vec<(H256, DBValue)> = Vec::new(); let mut canon_deletions: Vec = Vec::new(); let mut overlay_deletions: Vec = Vec::new(); @@ -385,7 +378,7 @@ impl JournalDB for OverlayRecentDB { { if *canon_id == journal.id { for h in &journal.insertions { - if let Some((d, rc)) = journal_overlay.backing_overlay.raw(&to_short_key(h)) { + if let Some((d, rc)) = journal_overlay.backing_overlay.raw(&to_short_key(h), EMPTY_PREFIX) { if rc > 0 { canon_insertions.push((h.clone(), d.clone())); //TODO: optimize this to avoid data copy } @@ -403,19 +396,19 @@ impl JournalDB for OverlayRecentDB { // apply canon inserts first for (k, v) in canon_insertions { - batch.put(self.column, &k, &v); + batch.put(self.column, k.as_bytes(), &v); journal_overlay.pending_overlay.insert(to_short_key(&k), v); } // update the overlay for k in overlay_deletions { - if let Some(val) = journal_overlay.backing_overlay.remove_and_purge(&to_short_key(&k)) { + if let Some(val) = journal_overlay.backing_overlay.remove_and_purge(&to_short_key(&k), EMPTY_PREFIX) { journal_overlay.cumulative_size -= val.len(); } } // apply canon deletions for k in canon_deletions { - if !journal_overlay.backing_overlay.contains(&to_short_key(&k)) { - batch.delete(self.column, &k); + if !journal_overlay.backing_overlay.contains(&to_short_key(&k), EMPTY_PREFIX) { + batch.delete(self.column, k.as_bytes()); } } } @@ -441,13 +434,13 @@ impl JournalDB for OverlayRecentDB { match rc { 0 => {} _ if rc > 0 => { - batch.put(self.column, &key, &value) + batch.put(self.column, key.as_bytes(), &value) } -1 => { - if cfg!(debug_assertions) && self.backing.get(self.column, &key)?.is_none() { + if cfg!(debug_assertions) && self.backing.get(self.column, key.as_bytes())?.is_none() { return Err(error_negatively_reference_hash(&key)); } - batch.delete(self.column, &key) + batch.delete(self.column, key.as_bytes()) } _ => panic!("Attempted to inject invalid state ({})", rc), } @@ -456,14 +449,32 @@ impl JournalDB for OverlayRecentDB { Ok(ops) } - fn consolidate(&mut self, with: MemoryDB) { + fn consolidate(&mut self, with: super::MemoryDB) { self.transaction_overlay.consolidate(with); } + + fn keys(&self) -> HashMap { + let mut ret: HashMap = self.backing.iter(self.column) + .map(|(key, _)| (H256::from_slice(&*key), 1)) + .collect(); + + for (key, refs) in self.transaction_overlay.keys() { + match ret.entry(key) { + Entry::Occupied(mut entry) => { + *entry.get_mut() += refs; + }, + Entry::Vacant(entry) => { + entry.insert(refs); + } + } + } + ret + } } impl HashDB for OverlayRecentDB { - fn get(&self, key: &H256) -> Option { - if let Some((d, rc)) = self.transaction_overlay.raw(key) { + fn get(&self, key: &H256, prefix: Prefix) -> Option { + if let Some((d, rc)) = self.transaction_overlay.raw(key, prefix) { if rc > 0 { return Some(d.clone()) } @@ -471,38 +482,38 @@ impl HashDB for OverlayRecentDB { let v = { let journal_overlay = self.journal_overlay.read(); let key = to_short_key(key); - journal_overlay.backing_overlay.get(&key) + journal_overlay.backing_overlay.get(&key, prefix) .or_else(|| journal_overlay.pending_overlay.get(&key).cloned()) }; v.or_else(|| self.payload(key)) } - fn contains(&self, key: &H256) -> bool { - self.get(key).is_some() + fn contains(&self, key: &H256, prefix: Prefix) -> bool { + self.get(key, prefix).is_some() } - fn insert(&mut self, value: &[u8]) -> H256 { - self.transaction_overlay.insert(value) + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { + self.transaction_overlay.insert(prefix, value) } - fn emplace(&mut self, key: H256, value: DBValue) { - self.transaction_overlay.emplace(key, value); + fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { + self.transaction_overlay.emplace(key, prefix, value); } - fn remove(&mut self, key: &H256) { - self.transaction_overlay.remove(key); + fn remove(&mut self, key: &H256, prefix: Prefix) { + self.transaction_overlay.remove(key, prefix); } } #[cfg(test)] mod tests { - - use keccak::keccak; + use keccak_hash::keccak; use super::*; - use hash_db::HashDB; - use {kvdb_memorydb, JournalDB}; + use hash_db::{HashDB, EMPTY_PREFIX}; + use kvdb_memorydb; + use crate::{JournalDB, inject_batch, commit_batch}; fn new_db() -> OverlayRecentDB { - let backing = Arc::new(kvdb_memorydb::create(0)); - OverlayRecentDB::new(backing, None) + let backing = Arc::new(kvdb_memorydb::create(1)); + OverlayRecentDB::new(backing, 0) } #[test] @@ -510,52 +521,52 @@ mod tests { // history is 1 let mut jdb = new_db(); - let x = jdb.insert(b"X"); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + let x = jdb.insert(EMPTY_PREFIX, b"X"); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(3, &keccak(b"1002a"), Some((1, keccak(b"1")))).unwrap(); + commit_batch(&mut jdb, 3, &keccak(b"1002a"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(4, &keccak(b"1003a"), Some((2, keccak(b"2")))).unwrap(); + commit_batch(&mut jdb, 4, &keccak(b"1003a"), Some((2, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&x); - jdb.commit_batch(3, &keccak(b"1002b"), Some((1, keccak(b"1")))).unwrap(); + jdb.remove(&x, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"1002b"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - let x = jdb.insert(b"X"); - jdb.commit_batch(4, &keccak(b"1003b"), Some((2, keccak(b"2")))).unwrap(); + let x = jdb.insert(EMPTY_PREFIX, b"X"); + commit_batch(&mut jdb, 4, &keccak(b"1003b"), Some((2, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(5, &keccak(b"1004a"), Some((3, keccak(b"1002a")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"1004a"), Some((3, keccak(b"1002a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(6, &keccak(b"1005a"), Some((4, keccak(b"1003a")))).unwrap(); + commit_batch(&mut jdb, 6, &keccak(b"1005a"), Some((4, keccak(b"1003a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&x)); + assert!(jdb.contains(&x, EMPTY_PREFIX)); } #[test] fn long_history() { // history is 3 let mut jdb = new_db(); - let h = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let h = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&h)); - jdb.remove(&h); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + jdb.remove(&h, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&h)); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&h)); - jdb.commit_batch(3, &keccak(b"3"), Some((0, keccak(b"0")))).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&h)); - jdb.commit_batch(4, &keccak(b"4"), Some((1, keccak(b"1")))).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(!jdb.contains(&h)); + assert!(!jdb.contains(&h, EMPTY_PREFIX)); } #[test] @@ -563,42 +574,42 @@ mod tests { // history is 1 let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); - jdb.remove(&foo); - jdb.remove(&bar); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + jdb.remove(&bar, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - assert!(jdb.contains(&baz)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); - let foo = jdb.insert(b"foo"); - jdb.remove(&baz); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + jdb.remove(&baz, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&bar)); - assert!(jdb.contains(&baz)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&bar)); - assert!(!jdb.contains(&baz)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); - jdb.commit_batch(4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(!jdb.contains(&foo)); - assert!(!jdb.contains(&bar)); - assert!(!jdb.contains(&baz)); + assert!(!jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); } #[test] @@ -606,31 +617,31 @@ mod tests { // history is 1 let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); - jdb.remove(&foo); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&bar); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - assert!(jdb.contains(&baz)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); - jdb.commit_batch(2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&baz)); - assert!(!jdb.contains(&bar)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); } #[test] @@ -638,138 +649,138 @@ mod tests { // history is 1 let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - assert!(jdb.contains(&foo)); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - jdb.commit_batch(3, &keccak(b"2"), Some((0, keccak(b"2")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + commit_batch(&mut jdb, 3, &keccak(b"2"), Some((0, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); } #[test] fn fork_same_key_one() { let mut jdb = new_db(); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1c"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1c"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.commit_batch(2, &keccak(b"2a"), Some((1, keccak(b"1a")))).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2a"), Some((1, keccak(b"1a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); } #[test] fn fork_same_key_other() { let mut jdb = new_db(); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1c"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1c"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.commit_batch(2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); } #[test] fn fork_ins_del_ins() { let mut jdb = new_db(); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(2, &keccak(b"2a"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(2, &keccak(b"2b"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(3, &keccak(b"3a"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 3, &keccak(b"3a"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(3, &keccak(b"3b"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 3, &keccak(b"3b"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(4, &keccak(b"4a"), Some((2, keccak(b"2a")))).unwrap(); + commit_batch(&mut jdb, 4, &keccak(b"4a"), Some((2, keccak(b"2a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(5, &keccak(b"5a"), Some((3, keccak(b"3a")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5a"), Some((3, keccak(b"3a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); } #[test] fn reopen() { - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); let bar = H256::random(); let foo = { - let mut jdb = OverlayRecentDB::new(shared_db.clone(), None); + let mut jdb = OverlayRecentDB::new(shared_db.clone(), 0); // history is 1 - let foo = jdb.insert(b"foo"); - jdb.emplace(bar.clone(), DBValue::from_slice(b"bar")); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + jdb.emplace(bar.clone(), EMPTY_PREFIX, b"bar".to_vec()); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); foo }; { - let mut jdb = OverlayRecentDB::new(shared_db.clone(), None); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + let mut jdb = OverlayRecentDB::new(shared_db.clone(), 0); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); } { - let mut jdb = OverlayRecentDB::new(shared_db.clone(), None); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + let mut jdb = OverlayRecentDB::new(shared_db.clone(), 0); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(!jdb.contains(&foo)); + assert!(!jdb.contains(&foo, EMPTY_PREFIX)); } } @@ -779,23 +790,23 @@ mod tests { let mut jdb = new_db(); // history is 4 - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(4, &keccak(b"4"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); // expunge foo - jdb.commit_batch(5, &keccak(b"5"), Some((1, keccak(b"1")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); } @@ -805,44 +816,44 @@ mod tests { let mut jdb = new_db(); // history is 4 - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1a"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1a"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(1, &keccak(b"1b"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1b"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(2, &keccak(b"2a"), None).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 2, &keccak(b"2a"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(2, &keccak(b"2b"), None).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 2, &keccak(b"2b"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3a"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3a"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3b"), None).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3b"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(4, &keccak(b"4a"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 4, &keccak(b"4a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(4, &keccak(b"4b"), Some((0, keccak(b"0")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 4, &keccak(b"4b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); // expunge foo - jdb.commit_batch(5, &keccak(b"5"), Some((1, keccak(b"1a")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((1, keccak(b"1a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); } @@ -850,59 +861,59 @@ mod tests { fn broken_assert() { let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); // foo is ancient history. - jdb.remove(&foo); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.commit_batch(3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); // BROKEN + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); // BROKEN assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.remove(&foo); - jdb.commit_batch(4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(5, &keccak(b"5"), Some((4, keccak(b"4")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((4, keccak(b"4")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(!jdb.contains(&foo)); + assert!(!jdb.contains(&foo, EMPTY_PREFIX)); } #[test] fn reopen_test() { let mut jdb = new_db(); // history is 4 - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(3, &keccak(b"3"), None).unwrap(); + commit_batch(&mut jdb, 3, &keccak(b"3"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(4, &keccak(b"4"), Some((0, keccak(b"0")))).unwrap(); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); // foo is ancient history. - jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(5, &keccak(b"5"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - jdb.remove(&bar); - jdb.commit_batch(6, &keccak(b"6"), Some((2, keccak(b"2")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 6, &keccak(b"6"), Some((2, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.insert(b"foo"); - jdb.insert(b"bar"); - jdb.commit_batch(7, &keccak(b"7"), Some((3, keccak(b"3")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 7, &keccak(b"7"), Some((3, keccak(b"3")))).unwrap(); assert!(jdb.can_reconstruct_refs()); } @@ -910,132 +921,132 @@ mod tests { fn reopen_remove_three() { let _ = ::env_logger::try_init(); - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); let foo = keccak(b"foo"); { - let mut jdb = OverlayRecentDB::new(shared_db.clone(), None); + let mut jdb = OverlayRecentDB::new(shared_db.clone(), 0); // history is 1 - jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); // foo is ancient history. - jdb.remove(&foo); - jdb.commit_batch(2, &keccak(b"2"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); - jdb.insert(b"foo"); - jdb.commit_batch(3, &keccak(b"3"), Some((1, keccak(b"1")))).unwrap(); + jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((1, keccak(b"1")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); // incantation to reopen the db }; { - let mut jdb = OverlayRecentDB::new(shared_db.clone(), None); + let mut jdb = OverlayRecentDB::new(shared_db.clone(), 0); - jdb.remove(&foo); - jdb.commit_batch(4, &keccak(b"4"), Some((2, keccak(b"2")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((2, keccak(b"2")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); // incantation to reopen the db }; { - let mut jdb = OverlayRecentDB::new(shared_db.clone(), None); + let mut jdb = OverlayRecentDB::new(shared_db.clone(), 0); - jdb.commit_batch(5, &keccak(b"5"), Some((3, keccak(b"3")))).unwrap(); + commit_batch(&mut jdb, 5, &keccak(b"5"), Some((3, keccak(b"3")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); // incantation to reopen the db }; { - let mut jdb = OverlayRecentDB::new(shared_db, None); + let mut jdb = OverlayRecentDB::new(shared_db, 0); - jdb.commit_batch(6, &keccak(b"6"), Some((4, keccak(b"4")))).unwrap(); + commit_batch(&mut jdb, 6, &keccak(b"6"), Some((4, keccak(b"4")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(!jdb.contains(&foo)); + assert!(!jdb.contains(&foo, EMPTY_PREFIX)); } } #[test] fn reopen_fork() { - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); let (foo, bar, baz) = { - let mut jdb = OverlayRecentDB::new(shared_db.clone(), None); + let mut jdb = OverlayRecentDB::new(shared_db.clone(), 0); // history is 1 - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&foo); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&bar); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); assert!(jdb.can_reconstruct_refs()); (foo, bar, baz) }; { - let mut jdb = OverlayRecentDB::new(shared_db, None); - jdb.commit_batch(2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); + let mut jdb = OverlayRecentDB::new(shared_db, 0); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&baz)); - assert!(!jdb.contains(&bar)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); } } #[test] fn insert_older_era() { let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0a"), None).unwrap(); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0a"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0a")))).unwrap(); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0a")))).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.remove(&bar); - jdb.commit_batch(0, &keccak(b"0b"), None).unwrap(); + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 0, &keccak(b"0b"), None).unwrap(); assert!(jdb.can_reconstruct_refs()); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); } #[test] fn inject() { let mut jdb = new_db(); - let key = jdb.insert(b"dog"); - jdb.inject_batch().unwrap(); + let key = jdb.insert(EMPTY_PREFIX, b"dog"); + inject_batch(&mut jdb).unwrap(); - assert_eq!(jdb.get(&key).unwrap(), DBValue::from_slice(b"dog")); - jdb.remove(&key); - jdb.inject_batch().unwrap(); + assert_eq!(jdb.get(&key, EMPTY_PREFIX).unwrap(), b"dog".to_vec()); + jdb.remove(&key, EMPTY_PREFIX); + inject_batch(&mut jdb).unwrap(); - assert!(jdb.get(&key).is_none()); + assert!(jdb.get(&key, EMPTY_PREFIX).is_none()); } #[test] fn earliest_era() { - let shared_db = Arc::new(kvdb_memorydb::create(0)); + let shared_db = Arc::new(kvdb_memorydb::create(1)); // empty DB - let mut jdb = OverlayRecentDB::new(shared_db.clone(), None); + let mut jdb = OverlayRecentDB::new(shared_db.clone(), 0); assert!(jdb.earliest_era().is_none()); // single journalled era. - let _key = jdb.insert(b"hello!"); + let _key = jdb.insert(EMPTY_PREFIX, b"hello!"); let mut batch = jdb.backing().transaction(); jdb.journal_under(&mut batch, 0, &keccak(b"0")).unwrap(); jdb.backing().write_buffered(batch); @@ -1065,7 +1076,7 @@ mod tests { // reconstructed: no journal entries. drop(jdb); - let jdb = OverlayRecentDB::new(shared_db, None); + let jdb = OverlayRecentDB::new(shared_db, 0); assert_eq!(jdb.earliest_era(), None); } } diff --git a/util/journaldb/src/refcounteddb.rs b/util/journaldb/src/refcounteddb.rs index bdd396a481..ca88aa048c 100644 --- a/util/journaldb/src/refcounteddb.rs +++ b/util/journaldb/src/refcounteddb.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,22 +16,26 @@ //! Disk-backed, ref-counted `JournalDB` implementation. -use std::collections::HashMap; -use std::io; -use std::sync::Arc; +use std::{ + io, + sync::Arc, + collections::HashMap, +}; -use bytes::Bytes; use ethereum_types::H256; -use hash_db::{HashDB}; -use heapsize::HeapSizeOf; +use hash_db::{HashDB, Prefix, EMPTY_PREFIX}; use keccak_hasher::KeccakHasher; use kvdb::{KeyValueDB, DBTransaction, DBValue}; -use memory_db::MemoryDB; -use overlaydb::OverlayDB; +use log::trace; +use malloc_size_of::{MallocSizeOf, allocators::new_malloc_size_ops}; +use parity_bytes::Bytes; use rlp::{encode, decode}; -use super::{DB_PREFIX_LEN, LATEST_ERA_KEY}; -use super::traits::JournalDB; -use util::{DatabaseKey, DatabaseValueView, DatabaseValueRef}; + +use crate::{ + overlaydb::OverlayDB, + JournalDB, DB_PREFIX_LEN, LATEST_ERA_KEY, + util::{DatabaseKey, DatabaseValueView, DatabaseValueRef}, +}; /// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay /// and latent-removal semantics. @@ -55,16 +59,16 @@ use util::{DatabaseKey, DatabaseValueView, DatabaseValueRef}; // TODO: store last_era, reclaim_period. pub struct RefCountedDB { forward: OverlayDB, - backing: Arc, + backing: Arc, latest_era: Option, inserts: Vec, removes: Vec, - column: Option, + column: u32, } impl RefCountedDB { /// Create a new instance given a `backing` database. - pub fn new(backing: Arc, column: Option) -> RefCountedDB { + pub fn new(backing: Arc, column: u32) -> RefCountedDB { let latest_era = backing.get(column, &LATEST_ERA_KEY) .expect("Low-level database error.") .map(|v| decode::(&v).expect("decoding db value failed")); @@ -81,19 +85,15 @@ impl RefCountedDB { } impl HashDB for RefCountedDB { - fn get(&self, key: &H256) -> Option { self.forward.get(key) } - fn contains(&self, key: &H256) -> bool { self.forward.contains(key) } - fn insert(&mut self, value: &[u8]) -> H256 { let r = self.forward.insert(value); self.inserts.push(r.clone()); r } - fn emplace(&mut self, key: H256, value: DBValue) { self.inserts.push(key.clone()); self.forward.emplace(key, value); } - fn remove(&mut self, key: &H256) { self.removes.push(key.clone()); } -} - -impl ::traits::KeyedHashDB for RefCountedDB { - fn keys(&self) -> HashMap { self.forward.keys() } + fn get(&self, key: &H256, prefix: Prefix) -> Option { self.forward.get(key, prefix) } + fn contains(&self, key: &H256, prefix: Prefix) -> bool { self.forward.contains(key, prefix) } + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H256 { let r = self.forward.insert(prefix, value); self.inserts.push(r.clone()); r } + fn emplace(&mut self, key: H256, prefix: Prefix, value: DBValue) { self.inserts.push(key.clone()); self.forward.emplace(key, prefix, value); } + fn remove(&mut self, key: &H256, _prefix: Prefix) { self.removes.push(key.clone()); } } impl JournalDB for RefCountedDB { - fn boxed_clone(&self) -> Box { + fn boxed_clone(&self) -> Box { Box::new(RefCountedDB { forward: self.forward.clone(), backing: self.backing.clone(), @@ -105,14 +105,15 @@ impl JournalDB for RefCountedDB { } fn mem_used(&self) -> usize { - self.inserts.heap_size_of_children() + self.removes.heap_size_of_children() - } + let mut ops = new_malloc_size_ops(); + self.inserts.size_of(&mut ops) + self.removes.size_of(&mut ops) + } fn is_empty(&self) -> bool { self.latest_era.is_none() } - fn backing(&self) -> &Arc { + fn backing(&self) -> &Arc { &self.backing } @@ -184,7 +185,7 @@ impl JournalDB for RefCountedDB { }.expect("rlp read from db; qed"); trace!(target: "rcdb", "delete journal for time #{}.{}=>{}, (canon was {}): deleting {:?}", end_era, db_key.index, our_id, canon_id, to_remove); for i in &to_remove { - self.forward.remove(i); + self.forward.remove(i, EMPTY_PREFIX); } batch.delete(self.column, &last); db_key.index += 1; @@ -197,53 +198,57 @@ impl JournalDB for RefCountedDB { fn inject(&mut self, batch: &mut DBTransaction) -> io::Result { self.inserts.clear(); for remove in self.removes.drain(..) { - self.forward.remove(&remove); + self.forward.remove(&remove, EMPTY_PREFIX); } self.forward.commit_to_batch(batch) } - fn consolidate(&mut self, mut with: MemoryDB) { + fn consolidate(&mut self, mut with: super::MemoryDB) { for (key, (value, rc)) in with.drain() { for _ in 0..rc { - self.emplace(key, value.clone()); + self.emplace(key, EMPTY_PREFIX, value.clone()); } for _ in rc..0 { - self.remove(&key); + self.remove(&key, EMPTY_PREFIX); } } } + + fn keys(&self) -> HashMap { + self.forward.keys() + } } #[cfg(test)] mod tests { - - use keccak::keccak; - use hash_db::HashDB; + use keccak_hash::keccak; + use hash_db::{HashDB, EMPTY_PREFIX}; use super::*; - use {JournalDB, kvdb_memorydb}; + use kvdb_memorydb; + use crate::{JournalDB, inject_batch, commit_batch}; fn new_db() -> RefCountedDB { - let backing = Arc::new(kvdb_memorydb::create(0)); - RefCountedDB::new(backing, None) + let backing = Arc::new(kvdb_memorydb::create(1)); + RefCountedDB::new(backing, 0) } #[test] fn long_history() { // history is 3 let mut jdb = new_db(); - let h = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); - assert!(jdb.contains(&h)); - jdb.remove(&h); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); - assert!(jdb.contains(&h)); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); - assert!(jdb.contains(&h)); - jdb.commit_batch(3, &keccak(b"3"), Some((0, keccak(b"0")))).unwrap(); - assert!(jdb.contains(&h)); - jdb.commit_batch(4, &keccak(b"4"), Some((1, keccak(b"1")))).unwrap(); - assert!(!jdb.contains(&h)); + let h = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + jdb.remove(&h, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((0, keccak(b"0")))).unwrap(); + assert!(jdb.contains(&h, EMPTY_PREFIX)); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((1, keccak(b"1")))).unwrap(); + assert!(!jdb.contains(&h, EMPTY_PREFIX)); } #[test] @@ -251,17 +256,17 @@ mod tests { // history is 3 let mut jdb = new_db(); assert_eq!(jdb.latest_era(), None); - let h = jdb.insert(b"foo"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); + let h = jdb.insert(EMPTY_PREFIX, b"foo"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); assert_eq!(jdb.latest_era(), Some(0)); - jdb.remove(&h); - jdb.commit_batch(1, &keccak(b"1"), None).unwrap(); + jdb.remove(&h, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1"), None).unwrap(); assert_eq!(jdb.latest_era(), Some(1)); - jdb.commit_batch(2, &keccak(b"2"), None).unwrap(); + commit_batch(&mut jdb, 2, &keccak(b"2"), None).unwrap(); assert_eq!(jdb.latest_era(), Some(2)); - jdb.commit_batch(3, &keccak(b"3"), Some((0, keccak(b"0")))).unwrap(); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((0, keccak(b"0")))).unwrap(); assert_eq!(jdb.latest_era(), Some(3)); - jdb.commit_batch(4, &keccak(b"4"), Some((1, keccak(b"1")))).unwrap(); + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((1, keccak(b"1")))).unwrap(); assert_eq!(jdb.latest_era(), Some(4)); } @@ -270,37 +275,37 @@ mod tests { // history is 1 let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - - jdb.remove(&foo); - jdb.remove(&bar); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - assert!(jdb.contains(&baz)); - - let foo = jdb.insert(b"foo"); - jdb.remove(&baz); - jdb.commit_batch(2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&bar)); - assert!(jdb.contains(&baz)); - - jdb.remove(&foo); - jdb.commit_batch(3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&bar)); - assert!(!jdb.contains(&baz)); - - jdb.commit_batch(4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); - assert!(!jdb.contains(&foo)); - assert!(!jdb.contains(&bar)); - assert!(!jdb.contains(&baz)); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + + jdb.remove(&foo, EMPTY_PREFIX); + jdb.remove(&bar, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1"), Some((0, keccak(b"0")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); + + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + jdb.remove(&baz, EMPTY_PREFIX); + commit_batch(&mut jdb, 2, &keccak(b"2"), Some((1, keccak(b"1")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); + + jdb.remove(&foo, EMPTY_PREFIX); + commit_batch(&mut jdb, 3, &keccak(b"3"), Some((2, keccak(b"2")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); + + commit_batch(&mut jdb, 4, &keccak(b"4"), Some((3, keccak(b"3")))).unwrap(); + assert!(!jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); } #[test] @@ -308,39 +313,39 @@ mod tests { // history is 1 let mut jdb = new_db(); - let foo = jdb.insert(b"foo"); - let bar = jdb.insert(b"bar"); - jdb.commit_batch(0, &keccak(b"0"), None).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); + let foo = jdb.insert(EMPTY_PREFIX, b"foo"); + let bar = jdb.insert(EMPTY_PREFIX, b"bar"); + commit_batch(&mut jdb, 0, &keccak(b"0"), None).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); - jdb.remove(&foo); - let baz = jdb.insert(b"baz"); - jdb.commit_batch(1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&foo, EMPTY_PREFIX); + let baz = jdb.insert(EMPTY_PREFIX, b"baz"); + commit_batch(&mut jdb, 1, &keccak(b"1a"), Some((0, keccak(b"0")))).unwrap(); - jdb.remove(&bar); - jdb.commit_batch(1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); + jdb.remove(&bar, EMPTY_PREFIX); + commit_batch(&mut jdb, 1, &keccak(b"1b"), Some((0, keccak(b"0")))).unwrap(); - assert!(jdb.contains(&foo)); - assert!(jdb.contains(&bar)); - assert!(jdb.contains(&baz)); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(jdb.contains(&bar, EMPTY_PREFIX)); + assert!(jdb.contains(&baz, EMPTY_PREFIX)); - jdb.commit_batch(2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); - assert!(jdb.contains(&foo)); - assert!(!jdb.contains(&baz)); - assert!(!jdb.contains(&bar)); + commit_batch(&mut jdb, 2, &keccak(b"2b"), Some((1, keccak(b"1b")))).unwrap(); + assert!(jdb.contains(&foo, EMPTY_PREFIX)); + assert!(!jdb.contains(&baz, EMPTY_PREFIX)); + assert!(!jdb.contains(&bar, EMPTY_PREFIX)); } #[test] fn inject() { let mut jdb = new_db(); - let key = jdb.insert(b"dog"); - jdb.inject_batch().unwrap(); + let key = jdb.insert(EMPTY_PREFIX, b"dog"); + inject_batch(&mut jdb).unwrap(); - assert_eq!(jdb.get(&key).unwrap(), DBValue::from_slice(b"dog")); - jdb.remove(&key); - jdb.inject_batch().unwrap(); + assert_eq!(jdb.get(&key, EMPTY_PREFIX).unwrap(), b"dog".to_vec()); + jdb.remove(&key, EMPTY_PREFIX); + inject_batch(&mut jdb).unwrap(); - assert!(jdb.get(&key).is_none()); + assert!(jdb.get(&key, EMPTY_PREFIX).is_none()); } } diff --git a/util/journaldb/src/traits.rs b/util/journaldb/src/traits.rs deleted file mode 100644 index e6ce8acb04..0000000000 --- a/util/journaldb/src/traits.rs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Disk-backed `HashDB` implementation. - -use std::io; -use std::sync::Arc; - -use bytes::Bytes; -use ethereum_types::H256; -use hash_db::{HashDB, AsHashDB}; -use keccak_hasher::KeccakHasher; -use kvdb::{self, DBTransaction, DBValue}; -use std::collections::HashMap; - - -/// expose keys of a hashDB for debugging or tests (slow). -pub trait KeyedHashDB: HashDB { - /// Primarily use for tests, highly inefficient. - fn keys(&self) -> HashMap; -} - -/// Upcast to `KeyedHashDB` -pub trait AsKeyedHashDB: AsHashDB { - /// Perform upcast to KeyedHashDB. - fn as_keyed_hash_db(&self) -> &KeyedHashDB; -} - -/// A `HashDB` which can manage a short-term journal potentially containing many forks of mutually -/// exclusive actions. -pub trait JournalDB: KeyedHashDB { - - /// Return a copy of ourself, in a box. - fn boxed_clone(&self) -> Box; - - /// Returns heap memory size used - fn mem_used(&self) -> usize; - - /// Returns the size of journalled state in memory. - /// This function has a considerable speed requirement -- - /// it must be fast enough to call several times per block imported. - fn journal_size(&self) -> usize { 0 } - - /// Check if this database has any commits - fn is_empty(&self) -> bool; - - /// Get the earliest era in the DB. None if there isn't yet any data in there. - fn earliest_era(&self) -> Option { None } - - /// Get the latest era in the DB. None if there isn't yet any data in there. - fn latest_era(&self) -> Option; - - /// Journal recent database operations as being associated with a given era and id. - // TODO: give the overlay to this function so journaldbs don't manage the overlays themeselves. - fn journal_under(&mut self, batch: &mut DBTransaction, now: u64, id: &H256) -> io::Result; - - /// Mark a given block as canonical, indicating that competing blocks' states may be pruned out. - fn mark_canonical(&mut self, batch: &mut DBTransaction, era: u64, id: &H256) -> io::Result; - - /// Commit all queued insert and delete operations without affecting any journalling -- this requires that all insertions - /// and deletions are indeed canonical and will likely lead to an invalid database if that assumption is violated. - /// - /// Any keys or values inserted or deleted must be completely independent of those affected - /// by any previous `commit` operations. Essentially, this means that `inject` can be used - /// either to restore a state to a fresh database, or to insert data which may only be journalled - /// from this point onwards. - fn inject(&mut self, batch: &mut DBTransaction) -> io::Result; - - /// State data query - fn state(&self, _id: &H256) -> Option; - - /// Whether this database is pruned. - fn is_pruned(&self) -> bool { true } - - /// Get backing database. - fn backing(&self) -> &Arc; - - /// Clear internal strucutres. This should called after changes have been written - /// to the backing strage - fn flush(&self) {} - - /// Consolidate all the insertions and deletions in the given memory overlay. - fn consolidate(&mut self, overlay: ::memory_db::MemoryDB); - - /// Commit all changes in a single batch - #[cfg(test)] - fn commit_batch(&mut self, now: u64, id: &H256, end: Option<(u64, H256)>) -> io::Result { - let mut batch = self.backing().transaction(); - let mut ops = self.journal_under(&mut batch, now, id)?; - - if let Some((end_era, canon_id)) = end { - ops += self.mark_canonical(&mut batch, end_era, &canon_id)?; - } - - let result = self.backing().write(batch).map(|_| ops).map_err(Into::into); - self.flush(); - result - } - - /// Inject all changes in a single batch. - #[cfg(test)] - fn inject_batch(&mut self) -> io::Result { - let mut batch = self.backing().transaction(); - let res = self.inject(&mut batch)?; - self.backing().write(batch).map(|_| res).map_err(Into::into) - } -} diff --git a/util/journaldb/src/util.rs b/util/journaldb/src/util.rs index 11d329595a..f615f41a47 100644 --- a/util/journaldb/src/util.rs +++ b/util/journaldb/src/util.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/keccak-hasher/Cargo.toml b/util/keccak-hasher/Cargo.toml index edeecda833..b10267f3a0 100644 --- a/util/keccak-hasher/Cargo.toml +++ b/util/keccak-hasher/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "keccak-hasher" version = "0.1.1" +edition = "2018" authors = ["Parity Technologies "] description = "Keccak-256 implementation of the Hasher trait" license = "GPL-3.0" [dependencies] -ethereum-types = "0.4" +ethereum-types = "0.8.0" tiny-keccak = "1.4.2" -hash-db = "0.11.0" +hash-db = "0.15.0" plain_hasher = "0.2" diff --git a/util/keccak-hasher/src/lib.rs b/util/keccak-hasher/src/lib.rs index d9c9be5454..f72876e736 100644 --- a/util/keccak-hasher/src/lib.rs +++ b/util/keccak-hasher/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,15 +15,14 @@ // along with Parity Ethereum. If not, see . //! Hasher implementation for the Keccak-256 hash -extern crate hash_db; -extern crate ethereum_types; -extern crate tiny_keccak; -extern crate plain_hasher; + +#![warn(missing_docs)] use hash_db::Hasher; use ethereum_types::H256; use tiny_keccak::Keccak; use plain_hasher::PlainHasher; + /// Concrete `Hasher` impl for the Keccak-256 hash #[derive(Default, Debug, Clone, PartialEq)] pub struct KeccakHasher; @@ -32,7 +31,7 @@ impl Hasher for KeccakHasher { type StdHasher = PlainHasher; const LENGTH: usize = 32; fn hash(x: &[u8]) -> Self::Out { - let mut out = [0;32]; + let mut out = [0; 32]; Keccak::keccak256(x, &mut out); out.into() } diff --git a/util/len-caching-lock/Cargo.toml b/util/len-caching-lock/Cargo.toml index 41d0e4e701..ac34ea827d 100644 --- a/util/len-caching-lock/Cargo.toml +++ b/util/len-caching-lock/Cargo.toml @@ -7,4 +7,4 @@ version = "0.1.1" authors = ["Parity Technologies "] [dependencies] -parking_lot = "0.7" +parking_lot = "0.9" diff --git a/util/len-caching-lock/src/lib.rs b/util/len-caching-lock/src/lib.rs index f2c04e454d..18118937f9 100644 --- a/util/len-caching-lock/src/lib.rs +++ b/util/len-caching-lock/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/len-caching-lock/src/mutex.rs b/util/len-caching-lock/src/mutex.rs index c9a2ea02a4..2d54b48dbc 100644 --- a/util/len-caching-lock/src/mutex.rs +++ b/util/len-caching-lock/src/mutex.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/len-caching-lock/src/rwlock.rs b/util/len-caching-lock/src/rwlock.rs index 3593cbcf79..6a4487d48d 100644 --- a/util/len-caching-lock/src/rwlock.rs +++ b/util/len-caching-lock/src/rwlock.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/macros/Cargo.toml b/util/macros/Cargo.toml index fd6a130f37..e0732b0b91 100644 --- a/util/macros/Cargo.toml +++ b/util/macros/Cargo.toml @@ -2,3 +2,4 @@ name = "macros" version = "0.1.0" authors = ["Parity Technologies "] +edition = "2018" diff --git a/util/macros/src/lib.rs b/util/macros/src/lib.rs index 999c5122ed..471ec1d6b5 100644 --- a/util/macros/src/lib.rs +++ b/util/macros/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -44,18 +44,6 @@ macro_rules! hash_map { }} } -#[macro_export] -macro_rules! hash_map_into { - () => { HashMap::new() }; - ( $( $x:expr => $y:expr ),* ) => {{ - let mut x = HashMap::new(); - $( - x.insert($x.into(), $y.into()); - )* - x - }} -} - #[macro_export] macro_rules! map { () => { BTreeMap::new() }; @@ -68,18 +56,6 @@ macro_rules! map { }} } -#[macro_export] -macro_rules! map_into { - () => { BTreeMap::new() }; - ( $( $x:expr => $y:expr ),* ) => {{ - let mut x = BTreeMap::new(); - $( - x.insert($x.into(), $y.into()); - )* - x - }} -} - #[macro_export] macro_rules! flush { ($arg:expr) => ($crate::flush($arg.into())); diff --git a/util/memory-cache/Cargo.toml b/util/memory-cache/Cargo.toml index f2b612463a..a5bc356f31 100644 --- a/util/memory-cache/Cargo.toml +++ b/util/memory-cache/Cargo.toml @@ -6,5 +6,5 @@ description = "An LRU-cache which operates on memory used" license = "GPL3" [dependencies] -heapsize = "0.4" +parity-util-mem = "0.3.0" lru-cache = "0.1" diff --git a/util/memory-cache/src/lib.rs b/util/memory-cache/src/lib.rs index 64d10336fe..3ea8cab180 100644 --- a/util/memory-cache/src/lib.rs +++ b/util/memory-cache/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -18,10 +18,10 @@ //! crate. // TODO: push changes upstream in a clean way. -extern crate heapsize; +extern crate parity_util_mem; extern crate lru_cache; -use heapsize::HeapSizeOf; +use parity_util_mem::{MallocSizeOf, MallocSizeOfExt}; use lru_cache::LruCache; use std::hash::Hash; @@ -29,18 +29,18 @@ use std::hash::Hash; const INITIAL_CAPACITY: usize = 4; /// An LRU-cache which operates on memory used. -pub struct MemoryLruCache { +pub struct MemoryLruCache { inner: LruCache, cur_size: usize, max_size: usize, } // amount of memory used when the item will be put on the heap. -fn heap_size_of(val: &T) -> usize { - ::std::mem::size_of::() + val.heap_size_of_children() +fn heap_size_of(val: &T) -> usize { + ::std::mem::size_of::() + val.malloc_size_of() } -impl MemoryLruCache { +impl MemoryLruCache { /// Create a new cache with a maximum size in bytes. pub fn new(max_size: usize) -> Self { MemoryLruCache { diff --git a/util/memzero/Cargo.toml b/util/memzero/Cargo.toml deleted file mode 100644 index 67d17d26d0..0000000000 --- a/util/memzero/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "memzero" -version = "0.1.0" -description = "A wrapper for zero-ing out memory when dropped" -license = "GPL-3.0" -homepage = "https://parity.io" -repository = "https://github.com/paritytech/parity-ethereum" -documentation = "https://docs.rs/crate/memzero" -authors = ["Parity Technologies "] -edition = "2018" diff --git a/util/memzero/src/lib.rs b/util/memzero/src/lib.rs deleted file mode 100644 index 827407a913..0000000000 --- a/util/memzero/src/lib.rs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -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) { - unsafe { - for byte_ref in self.mem.as_mut() { - ptr::write_volatile(byte_ref, 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/migration-rocksdb/Cargo.toml b/util/migration-rocksdb/Cargo.toml index 3cbfce09f5..3854f42443 100644 --- a/util/migration-rocksdb/Cargo.toml +++ b/util/migration-rocksdb/Cargo.toml @@ -6,8 +6,8 @@ authors = ["Parity Technologies "] [dependencies] log = "0.4" macros = { path = "../macros" } -kvdb = "0.1" -kvdb-rocksdb = "0.1.3" +kvdb = "0.3.1" +kvdb-rocksdb = "0.4.1" [dev-dependencies] tempdir = "0.3" diff --git a/util/migration-rocksdb/src/lib.rs b/util/migration-rocksdb/src/lib.rs index d63f2843ca..384909c426 100644 --- a/util/migration-rocksdb/src/lib.rs +++ b/util/migration-rocksdb/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -32,7 +32,7 @@ use std::{fs, io, error}; use kvdb::DBTransaction; use kvdb_rocksdb::{CompactionProfile, Database, DatabaseConfig}; -fn other_io_err(e: E) -> io::Error where E: Into> { +fn other_io_err(e: E) -> io::Error where E: Into> { io::Error::new(io::ErrorKind::Other, e) } @@ -58,16 +58,16 @@ impl Default for Config { pub struct Batch { inner: BTreeMap, Vec>, batch_size: usize, - column: Option, + column: u32, } impl Batch { /// Make a new batch with the given config. - pub fn new(config: &Config, col: Option) -> Self { + pub fn new(config: &Config, column: u32) -> Self { Batch { inner: BTreeMap::new(), batch_size: config.batch_size, - column: col, + column, } } @@ -96,50 +96,45 @@ impl Batch { } /// A generalized migration from the given db to a destination db. -pub trait Migration: 'static { +pub trait Migration { /// Number of columns in the database before the migration. - fn pre_columns(&self) -> Option { self.columns() } + fn pre_columns(&self) -> u32 { self.columns() } /// Number of columns in database after the migration. - fn columns(&self) -> Option; + fn columns(&self) -> u32; /// Whether this migration alters any existing columns. /// if not, then column families will simply be added and `migrate` will never be called. fn alters_existing(&self) -> bool { true } /// Version of the database after the migration. fn version(&self) -> u32; /// Migrate a source to a destination. - fn migrate(&mut self, source: Arc, config: &Config, destination: &mut Database, col: Option) -> io::Result<()>; + fn migrate(&mut self, source: Arc, config: &Config, destination: &mut Database, col: u32) -> io::Result<()>; } /// A simple migration over key-value pairs of a single column. -pub trait SimpleMigration: 'static { +pub trait SimpleMigration { /// Number of columns in database after the migration. - fn columns(&self) -> Option; + fn columns(&self) -> u32; /// Version of database after the migration. fn version(&self) -> u32; /// Index of column which should be migrated. - fn migrated_column_index(&self) -> Option; + fn migrated_column_index(&self) -> u32; /// Should migrate existing object to new database. /// Returns `None` if the object does not exist in new version of database. fn simple_migrate(&mut self, key: Vec, value: Vec) -> Option<(Vec, Vec)>; } impl Migration for T { - fn columns(&self) -> Option { SimpleMigration::columns(self) } - - fn version(&self) -> u32 { SimpleMigration::version(self) } + fn columns(&self) -> u32 { SimpleMigration::columns(self) } fn alters_existing(&self) -> bool { true } - fn migrate(&mut self, source: Arc, config: &Config, dest: &mut Database, col: Option) -> io::Result<()> { + fn version(&self) -> u32 { SimpleMigration::version(self) } + + fn migrate(&mut self, source: Arc, config: &Config, dest: &mut Database, col: u32) -> io::Result<()> { let migration_needed = col == SimpleMigration::migrated_column_index(self); let mut batch = Batch::new(config, col); - let iter = match source.iter(col) { - Some(iter) => iter, - None => return Ok(()), - }; - - for (key, value) in iter { + for (key, value) in source.iter(col) { if migration_needed { if let Some((key, value)) = self.simple_migrate(key.into_vec(), value.into_vec()) { batch.insert(key, value, dest)?; @@ -156,19 +151,19 @@ impl Migration for T { /// An even simpler migration which just changes the number of columns. pub struct ChangeColumns { /// The amount of columns before this migration. - pub pre_columns: Option, + pub pre_columns: u32, /// The amount of columns after this migration. - pub post_columns: Option, + pub post_columns: u32, /// The version after this migration. pub version: u32, } impl Migration for ChangeColumns { - fn pre_columns(&self) -> Option { self.pre_columns } - fn columns(&self) -> Option { self.post_columns } - fn version(&self) -> u32 { self.version } + fn pre_columns(&self) -> u32 { self.pre_columns } + fn columns(&self) -> u32 { self.post_columns } fn alters_existing(&self) -> bool { false } - fn migrate(&mut self, _: Arc, _: &Config, _: &mut Database, _: Option) -> io::Result<()> { + fn version(&self) -> u32 { self.version } + fn migrate(&mut self, _: Arc, _: &Config, _: &mut Database, _: u32) -> io::Result<()> { Ok(()) } } @@ -209,20 +204,20 @@ impl TempIndex { /// Manages database migration. pub struct Manager { config: Config, - migrations: Vec>, + migrations: Vec>, } impl Manager { /// Creates new migration manager with given configuration. pub fn new(config: Config) -> Self { Manager { - config: config, + config, migrations: vec![], } } /// Adds new migration rules. - pub fn add_migration(&mut self, migration: T) -> io::Result<()> where T: Migration { + pub fn add_migration(&mut self, migration: T) -> io::Result<()> where T: Migration { let is_new = match self.migrations.last() { Some(last) => migration.version() > last.version(), None => true, @@ -244,14 +239,13 @@ impl Manager { return Err(other_io_err("Migration impossible")); }; - let columns = migrations.get(0).and_then(|m| m.pre_columns()); - - trace!(target: "migration", "Expecting database to contain {:?} columns", columns); + let columns = migrations.first().expect("checked empty above; qed").pre_columns(); + trace!(target: "migration", "Expecting database to contain {} columns", columns); let mut db_config = DatabaseConfig { max_open_files: 64, - memory_budget: None, compaction: config.compaction_profile, - columns: columns, + columns, + ..Default::default() }; let db_root = database_path(old_path); @@ -276,16 +270,10 @@ impl Manager { let temp_path_str = temp_path.to_str().ok_or_else(|| other_io_err("Migration impossible."))?; let mut new_db = Database::open(&db_config, temp_path_str)?; - match current_columns { - // migrate only default column - None => migration.migrate(cur_db.clone(), &config, &mut new_db, None)?, - Some(v) => { - // Migrate all columns in previous DB - for col in 0..v { - migration.migrate(cur_db.clone(), &config, &mut new_db, Some(col))? - } - } + for col in 0..current_columns { + migration.migrate(cur_db.clone(), &config, &mut new_db, col)? } + // next iteration, we will migrate from this db into the other temp. cur_db = Arc::new(new_db); temp_idx.swap(); @@ -295,13 +283,13 @@ impl Manager { } else { // migrations which simply add or remove column families. // we can do this in-place. - let goal_columns = migration.columns().unwrap_or(0); + let goal_columns = migration.columns(); while cur_db.num_columns() < goal_columns { cur_db.add_column().map_err(other_io_err)?; } while cur_db.num_columns() > goal_columns { - cur_db.drop_column().map_err(other_io_err)?; + cur_db.remove_last_column().map_err(other_io_err)?; } } } @@ -317,7 +305,7 @@ impl Manager { } /// Find all needed migrations. - fn migrations_from(&mut self, version: u32) -> Vec<&mut Box> { + fn migrations_from(&mut self, version: u32) -> Vec<&mut Box> { self.migrations.iter_mut().filter(|m| m.version() > version).collect() } } diff --git a/util/migration-rocksdb/tests/tests.rs b/util/migration-rocksdb/tests/tests.rs index 7458c2d322..335082de0b 100644 --- a/util/migration-rocksdb/tests/tests.rs +++ b/util/migration-rocksdb/tests/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -29,7 +29,7 @@ use std::io; use std::path::{Path, PathBuf}; use std::sync::Arc; use tempdir::TempDir; -use kvdb_rocksdb::Database; +use kvdb_rocksdb::{Database, DatabaseConfig}; use migration::{Batch, Config, SimpleMigration, Migration, Manager, ChangeColumns}; #[inline] @@ -39,11 +39,11 @@ fn db_path(path: &Path) -> PathBuf { // initialize a database at the given directory with the given values. fn make_db(path: &Path, pairs: BTreeMap, Vec>) { - let db = Database::open_default(path.to_str().unwrap()).expect("failed to open temp database"); + let db = Database::open(&DatabaseConfig::default(), path.to_str().unwrap()).expect("failed to open temp database"); { let mut transaction = db.transaction(); for (k, v) in pairs { - transaction.put(None, &k, &v); + transaction.put(0, &k, &v); } db.write(transaction).expect("failed to write db transaction"); @@ -52,10 +52,12 @@ fn make_db(path: &Path, pairs: BTreeMap, Vec>) { // helper for verifying a migrated database. fn verify_migration(path: &Path, pairs: BTreeMap, Vec>) { - let db = Database::open_default(path.to_str().unwrap()).unwrap(); + let db = Database::open(&DatabaseConfig::default(), path.to_str().expect("valid path")).expect("database should be there"); for (k, v) in pairs { - let x = db.get(None, &k).unwrap().unwrap(); + let x = db.get(0, &k) + .expect("database IO should work") + .expect(&format!("key={:?} should be in column 0 in the db", &k)); assert_eq!(&x[..], &v[..]); } @@ -64,18 +66,9 @@ fn verify_migration(path: &Path, pairs: BTreeMap, Vec>) { struct Migration0; impl SimpleMigration for Migration0 { - fn columns(&self) -> Option { - None - } - - fn version(&self) -> u32 { - 1 - } - - fn migrated_column_index(&self) -> Option { - None - } - + fn columns(&self) -> u32 { 1 } + fn version(&self) -> u32 { 1 } + fn migrated_column_index(&self) -> u32 { 0 } fn simple_migrate(&mut self, mut key: Vec, mut value: Vec) -> Option<(Vec, Vec)> { key.push(0x11); value.push(0x22); @@ -87,18 +80,9 @@ impl SimpleMigration for Migration0 { struct Migration1; impl SimpleMigration for Migration1 { - fn columns(&self) -> Option { - None - } - - fn version(&self) -> u32 { - 2 - } - - fn migrated_column_index(&self) -> Option { - None - } - + fn columns(&self) -> u32 { 1 } + fn version(&self) -> u32 { 2 } + fn migrated_column_index(&self) -> u32 { 0 } fn simple_migrate(&mut self, key: Vec, _value: Vec) -> Option<(Vec, Vec)> { Some((key, vec![])) } @@ -107,20 +91,17 @@ impl SimpleMigration for Migration1 { struct AddsColumn; impl Migration for AddsColumn { - fn pre_columns(&self) -> Option { None } - - fn columns(&self) -> Option { Some(1) } - + fn pre_columns(&self) -> u32 { 1 } + fn columns(&self) -> u32 { 1 } fn version(&self) -> u32 { 1 } - - fn migrate(&mut self, source: Arc, config: &Config, dest: &mut Database, col: Option) -> io::Result<()> { + fn migrate(&mut self, source: Arc, config: &Config, dest: &mut Database, col: u32) -> io::Result<()> { let mut batch = Batch::new(config, col); - for (key, value) in source.iter(col).into_iter().flat_map(|inner| inner) { + for (key, value) in source.iter(col) { batch.insert(key.into_vec(), value.into_vec(), dest)?; } - if col == Some(1) { + if col == 1 { batch.insert(vec![1, 2, 3], vec![4, 5, 6], dest)?; } @@ -204,8 +185,8 @@ fn first_and_noop_migration() { make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); let expected = map![vec![0x11] => vec![0x22], vec![1, 0x11] => vec![1, 0x22]]; - manager.add_migration(Migration0).unwrap(); - let end_path = manager.execute(&db_path, 0).unwrap(); + manager.add_migration(Migration0).expect("Migration0 can be added"); + let end_path = manager.execute(&db_path, 0).expect("Migration0 runs clean"); verify_migration(&end_path, expected); } @@ -254,8 +235,8 @@ fn change_columns() { let mut manager = Manager::new(Config::default()); manager.add_migration(ChangeColumns { - pre_columns: None, - post_columns: Some(4), + pre_columns: 1, + post_columns: 4, version: 1, }).unwrap(); @@ -266,7 +247,7 @@ fn change_columns() { assert_eq!(db_path, new_path, "Changing columns is an in-place migration."); - let config = DatabaseConfig::with_columns(Some(4)); + let config = DatabaseConfig::with_columns(4); let db = Database::open(&config, new_path.to_str().unwrap()).unwrap(); assert_eq!(db.num_columns(), 4); } diff --git a/util/network-devp2p/Cargo.toml b/util/network-devp2p/Cargo.toml index 8bf402e206..e5d4921789 100644 --- a/util/network-devp2p/Cargo.toml +++ b/util/network-devp2p/Cargo.toml @@ -5,36 +5,34 @@ license = "GPL-3.0" name = "ethcore-network-devp2p" version = "1.12.0" authors = ["Parity Technologies "] +edition = "2018" [dependencies] log = "0.4" mio = "0.6.8" bytes = "0.4" -rand = "0.4" +rand = "0.7" tiny-keccak = "1.4" -rust-crypto = "0.2.34" slab = "0.2" -igd = "0.7" +igd = "0.9" libc = "0.2.7" -parking_lot = "0.7" -ansi_term = "0.10" +parking_lot = "0.9" +ansi_term = "0.11" rustc-hex = "1.0" ethcore-io = { path = "../io", features = ["mio"] } parity-bytes = "0.1" -parity-crypto = "0.3.0" -ethcore-network = { path = "../network" } -ethereum-types = "0.4" -ethkey = { path = "../../accounts/ethkey" } -rlp = { version = "0.3.0", features = ["ethereum"] } +parity-crypto = { version = "0.4.2", features = ["publickey"] } +network = { package = "ethcore-network", path = "../network" } +ethereum-types = "0.8.0" +rlp = "0.4.0" parity-path = "0.1" ipnetwork = "0.12.6" -keccak-hash = "0.1" +keccak-hash = "0.4.0" parity-snappy = "0.1" -serde = "1.0" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -serde_derive = "1.0" -error-chain = { version = "0.12", default-features = false } lru-cache = "0.1" +natpmp = "0.2" [dev-dependencies] env_logger = "0.5" diff --git a/util/network-devp2p/src/connection.rs b/util/network-devp2p/src/connection.rs index ae34ec44cf..2f88efb2d0 100644 --- a/util/network-devp2p/src/connection.rs +++ b/util/network-devp2p/src/connection.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,35 +15,36 @@ // along with Parity Ethereum. If not, see . use std::collections::VecDeque; +use std::io::{self, Cursor, Read, Write}; 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 bytes::{Buf, BufMut}; +use parity_crypto::aes::{AesCtr256, AesEcb256}; +use parity_crypto::publickey::Secret; use ethereum_types::{H128, H256, H512}; -use parity_bytes::*; +use keccak_hash::{keccak, write_keccak}; +use log::{debug, trace, warn}; +use mio::{PollOpt, Ready, Token}; +use mio::deprecated::{EventLoop, Handler, TryRead, TryWrite}; +use mio::tcp::TcpStream; +use parity_bytes::Bytes; use rlp::{Rlp, RlpStream}; -use std::io::{self, Cursor, Read, Write}; -use io::{IoContext, StreamToken}; -use handshake::Handshake; -use rcrypto::blockmodes::*; -use rcrypto::aessafe::*; -use rcrypto::symmetriccipher::*; -use rcrypto::buffer::*; use tiny_keccak::Keccak; -use bytes::{Buf, BufMut}; -use ethkey::crypto; -use network::{Error, ErrorKind}; + +use ethcore_io::{IoContext, StreamToken}; +use network::Error; + +use crate::handshake::Handshake; const ENCRYPTED_HEADER_LEN: usize = 32; const RECEIVE_PAYLOAD: Duration = Duration::from_secs(30); -pub const MAX_PAYLOAD_SIZE: usize = (1 << 24) - 1; +pub const MAX_PAYLOAD_SIZE: usize = (1 << 24) - 1; // 16Mb /// Network responses should try not to go over this limit. /// This should be lower than MAX_PAYLOAD_SIZE -pub const PAYLOAD_SOFT_LIMIT: usize = (1 << 22) - 1; +pub const PAYLOAD_SOFT_LIMIT: usize = (1 << 22) - 1; // 4Mb pub trait GenericSocket : Read + Write { } @@ -96,7 +97,7 @@ impl GenericConnection { else if self.rec_buf.len() > self.rec_size { warn!(target:"network", "Read past buffer {} bytes", self.rec_buf.len() - self.rec_size); return Ok(Some(::std::mem::replace(&mut self.rec_buf, Bytes::new()))) - } + } }, Ok(_) => return Ok(None), Err(e) => { @@ -104,7 +105,7 @@ impl GenericConnection { return Err(e) } } - } + } } /// Add a packet to send queue. @@ -193,7 +194,10 @@ impl Connection { /// Get remote peer address string pub fn remote_addr_str(&self) -> String { - self.socket.peer_addr().map(|a| a.to_string()).unwrap_or_else(|_| "Unknown".to_owned()) + self.socket.peer_addr().map(|a| a.to_string()).unwrap_or_else(|err| { + debug!("error occurred getting peer_addr: {}, connection token: {}", err, self.token); + "Unknown peer address".to_owned() + }) } /// Get local peer address string @@ -216,14 +220,13 @@ impl Connection { /// Register this connection with the IO event loop. pub fn register_socket(&self, reg: Token, event_loop: &mut EventLoop) -> io::Result<()> { - if self.registered.load(AtomicOrdering::SeqCst) { + if self.registered.compare_and_swap(false, true, AtomicOrdering::SeqCst) { return Ok(()); - } + } trace!(target: "network", "connection register; token={:?}", reg); if let Err(e) = event_loop.register(&self.socket, reg, self.interest, PollOpt::edge() /* | PollOpt::oneshot() */) { // TODO: oneshot is broken on windows trace!(target: "network", "Failed to register {:?}, {:?}", reg, e); } - self.registered.store(true, AtomicOrdering::SeqCst); Ok(()) } @@ -232,7 +235,7 @@ impl Connection { trace!(target: "network", "connection reregister; token={:?}", reg); if !self.registered.load(AtomicOrdering::SeqCst) { self.register_socket(reg, event_loop) - } else { + } else { event_loop.reregister(&self.socket, reg, self.interest, PollOpt::edge() /* | PollOpt::oneshot() */ ).unwrap_or_else(|e| { // TODO: oneshot is broken on windows trace!(target: "network", "Failed to reregister {:?}, {:?}", reg, e); }); @@ -277,11 +280,11 @@ pub struct EncryptedConnection { /// Underlying tcp connection pub connection: Connection, /// Egress data encryptor - encoder: CtrMode, + encoder: AesCtr256, /// Ingress data decryptor - decoder: CtrMode, + decoder: AesCtr256, /// Ingress data decryptor - mac_encoder: EcbEncryptor>, + mac_encoder_key: Secret, /// MAC for egress data egress_mac: Keccak, /// MAC for ingress data @@ -294,41 +297,46 @@ pub struct EncryptedConnection { payload_len: usize, } +const NULL_IV : [u8; 16] = [0;16]; impl EncryptedConnection { /// Create an encrypted connection out of the handshake. pub fn new(handshake: &mut Handshake) -> Result { - let shared = crypto::ecdh::agree(handshake.ecdhe.secret(), &handshake.remote_ephemeral)?; - let mut nonce_material = H512::new(); + let shared = parity_crypto::publickey::ecdh::agree(handshake.ecdhe.secret(), &handshake.remote_ephemeral)?; + let mut nonce_material = H512::default(); if handshake.originated { - handshake.remote_nonce.copy_to(&mut nonce_material[0..32]); - handshake.nonce.copy_to(&mut nonce_material[32..64]); + (&mut nonce_material[0..32]).copy_from_slice(handshake.remote_nonce.as_bytes()); + (&mut nonce_material[32..64]).copy_from_slice(handshake.nonce.as_bytes()); } else { - handshake.nonce.copy_to(&mut nonce_material[0..32]); - handshake.remote_nonce.copy_to(&mut nonce_material[32..64]); + (&mut nonce_material[0..32]).copy_from_slice(handshake.nonce.as_bytes()); + (&mut nonce_material[32..64]).copy_from_slice(handshake.remote_nonce.as_bytes()); } - let mut key_material = H512::new(); - shared.copy_to(&mut key_material[0..32]); + let mut key_material = H512::default(); + (&mut key_material[0..32]).copy_from_slice(shared.as_bytes()); write_keccak(&nonce_material, &mut key_material[32..64]); - keccak(&key_material).copy_to(&mut key_material[32..64]); - keccak(&key_material).copy_to(&mut key_material[32..64]); - - let iv = vec![0u8; 16]; - let encoder = CtrMode::new(AesSafe256Encryptor::new(&key_material[32..64]), iv); - let iv = vec![0u8; 16]; - let decoder = CtrMode::new(AesSafe256Encryptor::new(&key_material[32..64]), iv); - - keccak(&key_material).copy_to(&mut key_material[32..64]); - let mac_encoder = EcbEncryptor::new(AesSafe256Encryptor::new(&key_material[32..64]), NoPadding); + let key_material_keccak = keccak(&key_material); + (&mut key_material[32..64]).copy_from_slice(key_material_keccak.as_bytes()); + let key_material_keccak = keccak(&key_material); + (&mut key_material[32..64]).copy_from_slice(key_material_keccak.as_bytes()); + + // Using a 0 IV with CTR is fine as long as the same IV is never reused with the same key. + // This is the case here: ecdh creates a new secret which will be the symmetric key used + // only for this session the 0 IV is only use once with this secret, so we are in the case + // of same IV use for different key. + let encoder = AesCtr256::new(&key_material[32..64], &NULL_IV)?; + let decoder = AesCtr256::new(&key_material[32..64], &NULL_IV)?; + let key_material_keccak = keccak(&key_material); + (&mut key_material[32..64]).copy_from_slice(key_material_keccak.as_bytes()); + let mac_encoder_key: Secret = Secret::copy_from_slice(&key_material[32..64]).expect("can create Secret from 32 bytes; qed"); let mut egress_mac = Keccak::new_keccak256(); let mut mac_material = H256::from_slice(&key_material[32..64]) ^ handshake.remote_nonce; - egress_mac.update(&mac_material); + egress_mac.update(mac_material.as_bytes()); egress_mac.update(if handshake.originated { &handshake.auth_cipher } else { &handshake.ack_cipher }); let mut ingress_mac = Keccak::new_keccak256(); mac_material = H256::from_slice(&key_material[32..64]) ^ handshake.nonce; - ingress_mac.update(&mac_material); + ingress_mac.update(mac_material.as_bytes()); ingress_mac.update(if handshake.originated { &handshake.ack_cipher } else { &handshake.auth_cipher }); let old_connection = handshake.connection.try_clone()?; @@ -337,7 +345,7 @@ impl EncryptedConnection { connection, encoder, decoder, - mac_encoder, + mac_encoder_key, egress_mac, ingress_mac, read_state: EncryptedConnectionState::Header, @@ -350,52 +358,53 @@ impl EncryptedConnection { /// Send a packet pub fn send_packet(&mut self, io: &IoContext, payload: &[u8]) -> Result<(), Error> where Message: Send + Clone + Sync + 'static { + const HEADER_LEN: usize = 16; let mut header = RlpStream::new(); let len = payload.len(); if len > MAX_PAYLOAD_SIZE { - bail!(ErrorKind::OversizedPacket); + return Err(Error::OversizedPacket); } + header.append_raw(&[(len >> 16) as u8, (len >> 8) as u8, len as u8], 1); header.append_raw(&[0xc2u8, 0x80u8, 0x80u8], 1); - //TODO: get rid of vectors here + let padding = (16 - (len % 16)) % 16; + + let mut packet = vec![0u8; 16 + 16 + len + padding + 16]; let mut header = header.out(); - let padding = (16 - (payload.len() % 16)) % 16; - header.resize(16, 0u8); - - let mut packet = vec![0u8; 32 + payload.len() + padding + 16]; - self.encoder.encrypt(&mut RefReadBuffer::new(&header), &mut RefWriteBuffer::new(&mut packet), false).expect("Invalid length or padding"); - EncryptedConnection::update_mac(&mut self.egress_mac, &mut self.mac_encoder, &packet[0..16]); - self.egress_mac.clone().finalize(&mut packet[16..32]); - self.encoder.encrypt(&mut RefReadBuffer::new(payload), &mut RefWriteBuffer::new(&mut packet[32..(32 + len)]), padding == 0).expect("Invalid length or padding"); + header.resize(HEADER_LEN, 0u8); + &mut packet[..HEADER_LEN].copy_from_slice(&mut header); + self.encoder.encrypt(&mut packet[..HEADER_LEN])?; + EncryptedConnection::update_mac(&mut self.egress_mac, &self.mac_encoder_key, &packet[..HEADER_LEN])?; + self.egress_mac.clone().finalize(&mut packet[HEADER_LEN..32]); + &mut packet[32..32 + len].copy_from_slice(payload); + self.encoder.encrypt(&mut packet[32..32 + len])?; if padding != 0 { - let pad = [0u8; 16]; - self.encoder.encrypt(&mut RefReadBuffer::new(&pad[0..padding]), &mut RefWriteBuffer::new(&mut packet[(32 + len)..(32 + len + padding)]), true).expect("Invalid length or padding"); + self.encoder.encrypt(&mut packet[(32 + len)..(32 + len + padding)])?; } self.egress_mac.update(&packet[32..(32 + len + padding)]); - EncryptedConnection::update_mac(&mut self.egress_mac, &mut self.mac_encoder, &[0u8; 0]); + EncryptedConnection::update_mac(&mut self.egress_mac, &self.mac_encoder_key, &[0u8; 0])?; self.egress_mac.clone().finalize(&mut packet[(32 + len + padding)..]); self.connection.send(io, packet); + Ok(()) } /// Decrypt and authenticate an incoming packet header. Prepare for receiving payload. - fn read_header(&mut self, header: &[u8]) -> Result<(), Error> { + fn read_header(&mut self, mut header: Bytes) -> Result<(), Error> { if header.len() != ENCRYPTED_HEADER_LEN { - return Err(ErrorKind::Auth.into()); + return Err(Error::Auth); } - EncryptedConnection::update_mac(&mut self.ingress_mac, &mut self.mac_encoder, &header[0..16]); + EncryptedConnection::update_mac(&mut self.ingress_mac, &self.mac_encoder_key, &header[0..16])?; let mac = &header[16..]; - let mut expected = H256::new(); - self.ingress_mac.clone().finalize(&mut expected); + let mut expected = H256::zero(); + self.ingress_mac.clone().finalize(expected.as_bytes_mut()); if mac != &expected[0..16] { - return Err(ErrorKind::Auth.into()); + return Err(Error::Auth); } + self.decoder.decrypt(&mut header[..16])?; - let mut hdec = H128::new(); - 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 = Rlp::new(&hdec[3..6]); + let length = ((((header[0] as u32) << 8) + (header[1] as u32)) << 8) + (header[2] as u32); + let header_rlp = Rlp::new(&header[3..6]); let protocol_id = header_rlp.val_at::(0)?; self.payload_len = length as usize; @@ -404,46 +413,47 @@ impl EncryptedConnection { let padding = (16 - (length % 16)) % 16; let full_length = length + padding + 16; + self.connection.expect(full_length as usize); Ok(()) } /// Decrypt and authenticate packet payload. - fn read_payload(&mut self, payload: &[u8]) -> Result { + fn read_payload(&mut self, mut payload: Bytes) -> Result { let padding = (16 - (self.payload_len % 16)) % 16; let full_length = self.payload_len + padding + 16; if payload.len() != full_length { - return Err(ErrorKind::Auth.into()); + return Err(Error::Auth); } self.ingress_mac.update(&payload[0..payload.len() - 16]); - EncryptedConnection::update_mac(&mut self.ingress_mac, &mut self.mac_encoder, &[0u8; 0]); + EncryptedConnection::update_mac(&mut self.ingress_mac, &self.mac_encoder_key, &[0u8; 0])?; + let mac = &payload[(payload.len() - 16)..]; - let mut expected = H128::new(); - self.ingress_mac.clone().finalize(&mut expected); + let mut expected = H128::default(); + self.ingress_mac.clone().finalize(expected.as_bytes_mut()); if mac != &expected[..] { - return Err(ErrorKind::Auth.into()); + return Err(Error::Auth); } - - let mut packet = vec![0u8; self.payload_len]; - self.decoder.decrypt(&mut RefReadBuffer::new(&payload[0..self.payload_len]), &mut RefWriteBuffer::new(&mut packet), false).expect("Invalid length or padding"); - let mut pad_buf = [0u8; 16]; - self.decoder.decrypt(&mut RefReadBuffer::new(&payload[self.payload_len..(payload.len() - 16)]), &mut RefWriteBuffer::new(&mut pad_buf), false).expect("Invalid length or padding"); + self.decoder.decrypt(&mut payload[..self.payload_len + padding])?; + payload.truncate(self.payload_len); Ok(Packet { protocol: self.protocol_id, - data: packet + data: payload }) } /// Update MAC after reading or writing any data. - fn update_mac(mac: &mut Keccak, mac_encoder: &mut EcbEncryptor>, seed: &[u8]) { - let mut prev = H128::new(); - mac.clone().finalize(&mut prev); - let mut enc = H128::new(); - mac_encoder.encrypt(&mut RefReadBuffer::new(&prev), &mut RefWriteBuffer::new(&mut enc), true).expect("Error updating MAC"); - mac_encoder.reset(); + fn update_mac(mac: &mut Keccak, mac_encoder_key: &Secret, seed: &[u8]) -> Result<(), Error> { + let mut prev = H128::default(); + mac.clone().finalize(prev.as_bytes_mut()); + let mut enc = H128::default(); + &mut enc[..].copy_from_slice(prev.as_bytes()); + let mac_encoder = AesEcb256::new(mac_encoder_key.as_bytes())?; + mac_encoder.encrypt(enc.as_bytes_mut())?; enc = enc ^ if seed.is_empty() { prev } else { H128::from_slice(seed) }; - mac.update(&enc); + mac.update(enc.as_bytes()); + Ok(()) } /// Readable IO handler. Tracker receive status and returns decoded packet if available. @@ -451,7 +461,7 @@ impl EncryptedConnection { io.clear_timer(self.connection.token)?; if let EncryptedConnectionState::Header = self.read_state { if let Some(data) = self.connection.readable()? { - self.read_header(&data)?; + self.read_header(data)?; io.register_timer(self.connection.token, RECEIVE_PAYLOAD)?; } }; @@ -460,7 +470,7 @@ impl EncryptedConnection { Some(data) => { self.read_state = EncryptedConnectionState::Header; self.connection.expect(ENCRYPTED_HEADER_LEN); - Ok(Some(self.read_payload(&data)?)) + Ok(Some(self.read_payload(data)?)) }, None => Ok(None) } @@ -476,38 +486,18 @@ impl EncryptedConnection { } } -#[test] -pub fn test_encryption() { - use ethereum_types::{H256, H128}; - use std::str::FromStr; - let key = H256::from_str("2212767d793a7a3d66f869ae324dd11bd17044b82c9f463b8a541a4d089efec5").unwrap(); - let before = H128::from_str("12532abaec065082a3cf1da7d0136f15").unwrap(); - let before2 = H128::from_str("7e99f682356fdfbc6b67a9562787b18a").unwrap(); - let after = H128::from_str("89464c6b04e7c99e555c81d3f7266a05").unwrap(); - let after2 = H128::from_str("85c070030589ef9c7a2879b3a8489316").unwrap(); - - let mut got = H128::new(); - - let mut encoder = EcbEncryptor::new(AesSafe256Encryptor::new(&key), NoPadding); - encoder.encrypt(&mut RefReadBuffer::new(&before), &mut RefWriteBuffer::new(&mut got), true).unwrap(); - encoder.reset(); - assert_eq!(got, after); - got = H128::new(); - encoder.encrypt(&mut RefReadBuffer::new(&before2), &mut RefWriteBuffer::new(&mut got), true).unwrap(); - encoder.reset(); - assert_eq!(got, after2); -} - #[cfg(test)] mod tests { use std::cmp; use std::collections::VecDeque; - use std::io::{Read, Write, Cursor, ErrorKind, Result, Error}; + use std::io::{Cursor, Error, ErrorKind, Read, Result, Write}; use std::sync::atomic::AtomicBool; - use mio::{Ready}; + use mio::Ready; use parity_bytes::Bytes; - use io::*; + + use ethcore_io::*; + use super::*; pub struct TestSocket { @@ -650,6 +640,30 @@ mod tests { IoContext::new(IoChannel::disconnected(), 0) } + #[test] + pub fn test_encryption() { + use ethereum_types::{H256, H128}; + use std::str::FromStr; + let key = H256::from_str("2212767d793a7a3d66f869ae324dd11bd17044b82c9f463b8a541a4d089efec5").unwrap(); + let before = H128::from_str("12532abaec065082a3cf1da7d0136f15").unwrap(); + let before2 = H128::from_str("7e99f682356fdfbc6b67a9562787b18a").unwrap(); + let after = H128::from_str("89464c6b04e7c99e555c81d3f7266a05").unwrap(); + let after2 = H128::from_str("85c070030589ef9c7a2879b3a8489316").unwrap(); + + let mut got = H128::default(); + + let encoder = AesEcb256::new(key.as_bytes()).unwrap(); + got.as_bytes_mut().copy_from_slice(before.as_bytes()); + encoder.encrypt(got.as_bytes_mut()).unwrap(); + assert_eq!(got, after); + + let encoder = AesEcb256::new(key.as_bytes()).unwrap(); + got = H128::default(); + got.as_bytes_mut().copy_from_slice(&before2.as_bytes()); + encoder.encrypt(got.as_bytes_mut()).unwrap(); + assert_eq!(got, after2); + } + #[test] fn connection_expect() { let mut connection = TestConnection::new(); diff --git a/util/network-devp2p/src/discovery.rs b/util/network-devp2p/src/discovery.rs index f18469e164..7df0367300 100644 --- a/util/network-devp2p/src/discovery.rs +++ b/util/network-devp2p/src/discovery.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,22 +14,25 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use parity_bytes::Bytes; -use std::net::SocketAddr; -use std::collections::{HashSet, HashMap, VecDeque}; +use std::collections::{HashMap, HashSet, VecDeque}; use std::collections::hash_map::Entry; use std::default::Default; +use std::net::SocketAddr; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; -use lru_cache::LruCache; -use hash::keccak; + use ethereum_types::{H256, H520}; +use keccak_hash::keccak; +use log::{debug, trace, warn}; +use lru_cache::LruCache; +use parity_bytes::Bytes; use rlp::{Rlp, RlpStream}; -use node_table::*; -use network::{Error, ErrorKind}; -use ethkey::{Secret, KeyPair, sign, recover}; + +use parity_crypto::publickey::{KeyPair, recover, Secret, sign}; +use network::Error; use network::IpFilter; -use PROTOCOL_VERSION; +use crate::node_table::*; +use crate::PROTOCOL_VERSION; 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]. @@ -196,7 +199,7 @@ impl<'a> Discovery<'a> { public_endpoint: public, discovery_initiated: false, discovery_round: None, - discovery_id: NodeId::new(), + discovery_id: NodeId::default(), discovery_nodes: HashSet::new(), node_buckets: (0..ADDRESS_BITS).map(|_| NodeBucket::new()).collect(), other_observed_nodes: LruCache::new(OBSERVED_NODES_MAX_SIZE), @@ -418,7 +421,7 @@ impl<'a> Discovery<'a> { fn send_packet(&mut self, packet_id: u8, address: &SocketAddr, payload: &[u8]) -> Result { let packet = assemble_packet(packet_id, payload, &self.secret)?; - let hash = H256::from(&packet[0..32]); + let hash = H256::from_slice(&packet[0..32]); self.send_to(packet, address.clone()); Ok(hash) } @@ -481,12 +484,12 @@ impl<'a> Discovery<'a> { 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()); + return Err(Error::BadProtocol); } let hash_signed = keccak(&packet[32..]); if hash_signed[..] != packet[0..32] { - return Err(ErrorKind::BadProtocol.into()); + return Err(Error::BadProtocol); } let signed = &packet[(32 + 65)..]; @@ -495,7 +498,7 @@ impl<'a> Discovery<'a> { let packet_id = signed[0]; let rlp = Rlp::new(&signed[1..]); match packet_id { - PACKET_PING => self.on_ping(&rlp, &node_id, &from, &hash_signed), + PACKET_PING => self.on_ping(&rlp, &node_id, &from, hash_signed.as_bytes()), 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), @@ -511,7 +514,7 @@ impl<'a> Discovery<'a> { let secs_since_epoch = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(); if self.check_timestamps && timestamp < secs_since_epoch { debug!(target: "discovery", "Expired packet"); - return Err(ErrorKind::Expired.into()); + return Err(Error::Expired); } Ok(()) } @@ -875,7 +878,7 @@ fn assemble_packet(packet_id: u8, bytes: &[u8], secret: &Secret) -> Result. use std::time::Duration; -use rand::random; -use hash::write_keccak; -use mio::tcp::*; + use ethereum_types::{H256, H520}; +use keccak_hash::write_keccak; +use log::{debug, trace}; +use mio::tcp::*; use parity_bytes::Bytes; +use rand::random; use rlp::{Rlp, RlpStream}; -use connection::Connection; -use node_table::NodeId; -use io::{IoContext, StreamToken}; -use ethkey::{KeyPair, Public, Secret, recover, sign, Generator, Random}; -use ethkey::crypto::{ecdh, ecies}; -use network::{Error, ErrorKind}; -use host::HostInfo; + +use ethcore_io::{IoContext, StreamToken}; +use parity_crypto::publickey::{Generator, KeyPair, Public, Random, recover, Secret, sign, ecdh, ecies}; +use network::Error; + +use crate::connection::Connection; +use crate::host::HostInfo; +use crate::node_table::NodeId; #[derive(PartialEq, Eq, Debug)] enum HandshakeState { @@ -82,14 +85,14 @@ impl Handshake { /// Create a new handshake object pub fn new(token: StreamToken, id: Option<&NodeId>, socket: TcpStream, nonce: &H256) -> Result { Ok(Handshake { - id: if let Some(id) = id { *id } else { NodeId::new() }, + id: if let Some(id) = id { *id } else { NodeId::default() }, connection: Connection::new(token, socket), originated: false, state: HandshakeState::New, ecdhe: Random.generate()?, nonce: *nonce, - remote_ephemeral: Public::new(), - remote_nonce: H256::new(), + remote_ephemeral: Public::default(), + remote_nonce: H256::zero(), remote_version: PROTOCOL_VERSION, auth_cipher: Bytes::new(), ack_cipher: Bytes::new(), @@ -149,8 +152,9 @@ impl Handshake { } fn set_auth(&mut self, host_secret: &Secret, sig: &[u8], remote_public: &[u8], remote_nonce: &[u8], remote_version: u64) -> Result<(), Error> { - self.id.clone_from_slice(remote_public); - self.remote_nonce.clone_from_slice(remote_nonce); + // TODO: assign_from_slice will panic if sizes differ + self.id.assign_from_slice(remote_public); + self.remote_nonce.assign_from_slice(remote_nonce); self.remote_version = remote_version; let shared = *ecdh::agree(host_secret, &self.id)?; let signature = H520::from_slice(sig); @@ -163,7 +167,7 @@ impl Handshake { trace!(target: "network", "Received handshake auth from {:?}", self.connection.remote_addr_str()); if data.len() != V4_AUTH_PACKET_SIZE { debug!(target: "network", "Wrong auth packet size"); - return Err(ErrorKind::BadProtocol.into()); + return Err(Error::BadProtocol); } self.auth_cipher = data.to_vec(); match ecies::decrypt(secret, &[], data) { @@ -180,7 +184,7 @@ impl Handshake { 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()); + return Err(Error::BadProtocol); } let rest = total - data.len(); self.state = HandshakeState::ReadingAuthEip8; @@ -199,7 +203,7 @@ impl Handshake { let remote_public: Public = rlp.val_at(1)?; let remote_nonce: H256 = rlp.val_at(2)?; let remote_version: u64 = rlp.val_at(3)?; - self.set_auth(secret, &signature, &remote_public, &remote_nonce, remote_version)?; + self.set_auth(secret, signature.as_bytes(), remote_public.as_bytes(), remote_nonce.as_bytes(), remote_version)?; self.write_ack_eip8(io)?; Ok(()) } @@ -209,13 +213,13 @@ impl Handshake { trace!(target: "network", "Received handshake ack from {:?}", self.connection.remote_addr_str()); if data.len() != V4_ACK_PACKET_SIZE { debug!(target: "network", "Wrong ack packet size"); - return Err(ErrorKind::BadProtocol.into()); + return Err(Error::BadProtocol); } self.ack_cipher = data.to_vec(); match ecies::decrypt(secret, &[], data) { Ok(ack) => { - self.remote_ephemeral.clone_from_slice(&ack[0..64]); - self.remote_nonce.clone_from_slice(&ack[64..(64+32)]); + self.remote_ephemeral.assign_from_slice(&ack[0..64]); + self.remote_nonce.assign_from_slice(&ack[64..(64+32)]); self.state = HandshakeState::StartSession; } Err(_) => { @@ -223,7 +227,7 @@ impl Handshake { 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()); + return Err(Error::BadProtocol); } let rest = total - data.len(); self.state = HandshakeState::ReadingAckEip8; @@ -261,8 +265,8 @@ impl Handshake { let shared = *ecdh::agree(secret, &self.id)?; sig.copy_from_slice(&*sign(self.ecdhe.secret(), &(shared ^ self.nonce))?); write_keccak(self.ecdhe.public(), hepubk); - pubk.copy_from_slice(public); - nonce.copy_from_slice(&self.nonce); + pubk.copy_from_slice(public.as_bytes()); + nonce.copy_from_slice(self.nonce.as_bytes()); } let message = ecies::encrypt(&self.id, &[], &data)?; self.auth_cipher = message.clone(); @@ -281,8 +285,8 @@ impl Handshake { data[len - 1] = 0x0; let (epubk, rest) = data.split_at_mut(64); let (nonce, _) = rest.split_at_mut(32); - self.ecdhe.public().copy_to(epubk); - self.nonce.copy_to(nonce); + epubk.copy_from_slice(self.ecdhe.public().as_bytes()); + nonce.copy_from_slice(self.nonce.as_bytes()); } let message = ecies::encrypt(&self.id, &[], &data)?; self.ack_cipher = message.clone(); @@ -317,30 +321,43 @@ impl Handshake { #[cfg(test)] mod test { + use std::str::FromStr; + + use ethereum_types::{H256, H512}; + use mio::tcp::TcpStream; use rustc_hex::FromHex; + + use ethcore_io::*; + use parity_crypto::publickey::Public; + use super::*; - use ethereum_types::H256; - use io::*; - use mio::tcp::TcpStream; - use ethkey::Public; fn check_auth(h: &Handshake, version: u64) { - assert_eq!(h.id, "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into()); - assert_eq!(h.remote_nonce, "7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6".into()); - assert_eq!(h.remote_ephemeral, "654d1044b69c577a44e5f01a1209523adb4026e70c62d1c13a067acabc09d2667a49821a0ad4b634554d330a15a58fe61f8a8e0544b310c6de7b0c8da7528a8d".into()); + assert_eq!( + h.id, + H512::from_str("fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877").unwrap(), + ); + assert_eq!(h.remote_nonce, H256::from_str("7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6").unwrap()); + assert_eq!( + h.remote_ephemeral, + H512::from_str("654d1044b69c577a44e5f01a1209523adb4026e70c62d1c13a067acabc09d2667a49821a0ad4b634554d330a15a58fe61f8a8e0544b310c6de7b0c8da7528a8d").unwrap(), + ); assert_eq!(h.remote_version, version); } fn check_ack(h: &Handshake, version: u64) { - assert_eq!(h.remote_nonce, "559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd".into()); - assert_eq!(h.remote_ephemeral, "b6d82fa3409da933dbf9cb0140c5dde89f4e64aec88d476af648880f4a10e1e49fe35ef3e69e93dd300b4797765a747c6384a6ecf5db9c2690398607a86181e4".into()); + assert_eq!(h.remote_nonce, H256::from_str("559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd").unwrap()); + assert_eq!( + h.remote_ephemeral, + H512::from_str("b6d82fa3409da933dbf9cb0140c5dde89f4e64aec88d476af648880f4a10e1e49fe35ef3e69e93dd300b4797765a747c6384a6ecf5db9c2690398607a86181e4").unwrap(), + ); assert_eq!(h.remote_version, version); } fn create_handshake(to: Option<&Public>) -> Handshake { let addr = "127.0.0.1:50556".parse().unwrap(); let socket = TcpStream::connect(&addr).unwrap(); - let nonce = H256::new(); + let nonce = H256::zero(); Handshake::new(0, to, socket, &nonce).unwrap() } @@ -427,7 +444,7 @@ mod test { #[test] fn test_handshake_ack_plain() { - let remote = "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into(); + let remote = H512::from_str("fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877").unwrap(); let mut h = create_handshake(Some(&remote)); let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".parse().unwrap(); let ack = @@ -447,7 +464,7 @@ mod test { #[test] fn test_handshake_ack_eip8() { - let remote = "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into(); + let remote = H512::from_str("fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877").unwrap(); let mut h = create_handshake(Some(&remote)); let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".parse().unwrap(); let ack = @@ -476,7 +493,7 @@ mod test { #[test] fn test_handshake_ack_eip8_2() { - let remote = "fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877".into(); + let remote = H512::from_str("fda1cff674c90c9a197539fe3dfb53086ace64f83ed7c6eabec741f7f381cc803e52ab2cd55d5569bce4347107a310dfd5f88a010cd2ffd1005ca406f1842877").unwrap(); let mut h = create_handshake(Some(&remote)); let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".parse().unwrap(); let ack = diff --git a/util/network-devp2p/src/host.rs b/util/network-devp2p/src/host.rs index 572f073a0d..e61faa2510 100644 --- a/util/network-devp2p/src/host.rs +++ b/util/network-devp2p/src/host.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,40 +14,46 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr}; +use std::cmp::{max, min}; use std::collections::{HashMap, HashSet}; +use std::fs; +use std::io::{self, Read, Write}; +use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; +use std::ops::*; +use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; -use std::ops::*; -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::{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::{SessionInfo, Error, ErrorKind, DisconnectReason, NetworkProtocolHandler}; -use discovery::{Discovery, TableUpdates, NodeEntry, MAX_DATAGRAM_SIZE}; -use network::client_version::ClientVersion; -use ip_utils::{map_external_address, select_public_address}; +use keccak_hash::keccak; +use log::{debug, info, trace, warn}; +use mio::{ + deprecated::EventLoop, PollOpt, Ready, tcp::{TcpListener, TcpStream}, + Token, + udp::UdpSocket +}; use parity_path::restrict_permissions_owner; use parking_lot::{Mutex, RwLock}; -use network::{ConnectionFilter, ConnectionDirection}; -use connection::PAYLOAD_SOFT_LIMIT; +use rlp::{Encodable, RlpStream}; + +use ethcore_io::{IoContext, IoHandler, IoManager, StreamToken, TimerToken}; +use parity_crypto::publickey::{Generator, KeyPair, Random, Secret}; +use network::{ + client_version::ClientVersion, ConnectionDirection, ConnectionFilter, DisconnectReason, Error, + NetworkConfiguration, NetworkContext as NetworkContextTrait, NetworkIoMessage, NetworkProtocolHandler, + NonReservedPeerMode, PacketId, PeerId, ProtocolId, SessionInfo +}; + +use crate::{ + connection::PAYLOAD_SOFT_LIMIT, + discovery::{Discovery, MAX_DATAGRAM_SIZE, NodeEntry, TableUpdates}, + ip_utils::{map_external_address, select_public_address}, + node_table::*, + PROTOCOL_VERSION, + session::{Session, SessionData} +}; type Slab = ::slab::Slab; @@ -155,7 +161,7 @@ impl<'s> NetworkContextTrait for NetworkContext<'s> { fn respond(&self, packet_id: PacketId, data: Vec) -> Result<(), Error> { assert!(self.session.is_some(), "Respond called without network context"); - self.session_id.map_or_else(|| Err(ErrorKind::Expired.into()), |id| self.send(id, packet_id, data)) + self.session_id.map_or_else(|| Err(Error::Expired), |id| self.send(id, packet_id, data)) } fn disable_peer(&self, peer: PeerId) { @@ -263,17 +269,17 @@ pub struct Host { sessions: Arc>>, discovery: Mutex>>, nodes: RwLock, - handlers: RwLock>>, + handlers: RwLock>>, timers: RwLock>, timer_counter: RwLock, reserved_nodes: RwLock>, stopping: AtomicBool, - filter: Option>, + filter: Option>, } impl Host { /// Create a new instance - pub fn new(mut config: NetworkConfiguration, 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, @@ -455,7 +461,7 @@ impl Host { let public_address = select_public_address(local_endpoint.address.port()); let public_endpoint = NodeEndpoint { address: public_address, udp_port: local_endpoint.udp_port }; if self.info.read().config.nat_enabled { - match map_external_address(&local_endpoint) { + match map_external_address(&local_endpoint, &self.info.read().config.nat_type) { Some(endpoint) => { info!("NAT mapped to external address {}", endpoint.address); endpoint @@ -618,8 +624,8 @@ impl Host { let socket = { let address = { - let mut nodes = self.nodes.write(); - if let Some(node) = nodes.get_mut(id) { + let nodes = self.nodes.read(); + if let Some(node) = nodes.get(id) { node.endpoint.address } else { debug!(target: "network", "Connection to expired node aborted"); @@ -720,8 +726,8 @@ impl Host { let reserved_nodes = self.reserved_nodes.read(); let s = session.lock(); trace!(target: "network", "Session read error: {}:{:?} ({:?}) {:?}", token, s.id(), s.remote_addr(), e); - match *e.kind() { - ErrorKind::Disconnect(DisconnectReason::IncompatibleProtocol) | ErrorKind::Disconnect(DisconnectReason::UselessPeer) => { + match e { + Error::Disconnect(DisconnectReason::IncompatibleProtocol) | Error::Disconnect(DisconnectReason::UselessPeer) => { if let Some(id) = s.id() { if !reserved_nodes.contains(id) { let mut nodes = self.nodes.write(); @@ -835,6 +841,7 @@ impl Host { if duplicate { trace!(target: "network", "Rejected duplicate connection: {}", token); session.lock().disconnect(io, DisconnectReason::DuplicatePeer); + drop(handlers); self.kill_connection(token, io, false); return; } @@ -926,7 +933,7 @@ impl Host { let mut failure_id = None; let mut deregister = false; let mut expired_session = None; - if let FIRST_SESSION ... LAST_SESSION = token { + if let FIRST_SESSION ..= LAST_SESSION = token { let sessions = self.sessions.read(); if let Some(session) = sessions.get(token).cloned() { expired_session = Some(session.clone()); @@ -981,14 +988,14 @@ impl Host { self.nodes.write().update(node_changes, &*reserved_nodes); } - pub fn with_context(&self, protocol: ProtocolId, io: &IoContext, action: F) where F: FnOnce(&NetworkContextTrait) { + pub fn with_context(&self, protocol: ProtocolId, io: &IoContext, action: F) where F: FnOnce(&dyn NetworkContextTrait) { let reserved = { self.reserved_nodes.read() }; let context = NetworkContext::new(io, protocol, None, self.sessions.clone(), &reserved); action(&context); } - pub fn with_context_eval(&self, protocol: ProtocolId, io: &IoContext, action: F) -> T where F: FnOnce(&NetworkContextTrait) -> T { + pub fn with_context_eval(&self, protocol: ProtocolId, io: &IoContext, action: F) -> T where F: FnOnce(&dyn NetworkContextTrait) -> T { let reserved = { self.reserved_nodes.read() }; let context = NetworkContext::new(io, protocol, None, self.sessions.clone(), &reserved); @@ -1007,7 +1014,7 @@ impl IoHandler for Host { fn stream_hup(&self, io: &IoContext, stream: StreamToken) { trace!(target: "network", "Hup: {}", stream); match stream { - FIRST_SESSION ... LAST_SESSION => self.connection_closed(stream, io), + FIRST_SESSION ..= LAST_SESSION => self.connection_closed(stream, io), _ => warn!(target: "network", "Unexpected hup"), }; } @@ -1017,7 +1024,7 @@ impl IoHandler for Host { return; } match stream { - FIRST_SESSION ... LAST_SESSION => self.session_readable(stream, io), + FIRST_SESSION ..= LAST_SESSION => self.session_readable(stream, io), DISCOVERY => self.discovery_readable(io), TCP_ACCEPT => self.accept(io), _ => panic!("Received unknown readable token"), @@ -1029,7 +1036,7 @@ impl IoHandler for Host { return; } match stream { - FIRST_SESSION ... LAST_SESSION => self.session_writable(stream, io), + FIRST_SESSION ..= LAST_SESSION => self.session_writable(stream, io), DISCOVERY => self.discovery_writable(io), _ => panic!("Received unknown writable token"), } @@ -1041,7 +1048,7 @@ impl IoHandler for Host { } match token { IDLE => self.maintain_network(io), - FIRST_SESSION ... LAST_SESSION => self.connection_timeout(token, io), + FIRST_SESSION ..= LAST_SESSION => self.connection_timeout(token, io), DISCOVERY_REFRESH => { // Run the _slow_ discovery if enough peers are connected if !self.has_enough_peers() { @@ -1150,7 +1157,7 @@ impl IoHandler for Host { fn register_stream(&self, stream: StreamToken, reg: Token, event_loop: &mut EventLoop>) { match stream { - FIRST_SESSION ... LAST_SESSION => { + FIRST_SESSION ..= LAST_SESSION => { let session = { self.sessions.read().get(stream).cloned() }; if let Some(session) = session { session.lock().register_socket(reg, event_loop).expect("Error registering socket"); @@ -1170,7 +1177,7 @@ impl IoHandler for Host { fn deregister_stream(&self, stream: StreamToken, event_loop: &mut EventLoop>) { match stream { - FIRST_SESSION ... LAST_SESSION => { + FIRST_SESSION ..= LAST_SESSION => { let mut connections = self.sessions.write(); if let Some(connection) = connections.get(stream).cloned() { let c = connection.lock(); @@ -1187,7 +1194,7 @@ impl IoHandler for Host { fn update_stream(&self, stream: StreamToken, reg: Token, event_loop: &mut EventLoop>) { match stream { - FIRST_SESSION ... LAST_SESSION => { + FIRST_SESSION ..= LAST_SESSION => { let connection = { self.sessions.read().get(stream).cloned() }; if let Some(connection) = connection { connection.lock().update_socket(reg, event_loop).expect("Error updating socket"); @@ -1229,7 +1236,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()[2..]) { + if let Err(e) = file.write(&key.to_hex().into_bytes()) { warn!("Error writing key file: {:?}", e); } } diff --git a/util/network-devp2p/src/ip_utils.rs b/util/network-devp2p/src/ip_utils.rs index 4b8473ceca..579bf08413 100644 --- a/util/network-devp2p/src/ip_utils.rs +++ b/util/network-devp2p/src/ip_utils.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,12 +16,22 @@ // Based on original work by David Levy https://raw.githubusercontent.com/dlevy47/rust-interfaces -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::io; -use igd::{PortMappingProtocol, search_gateway_from_timeout}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::time::Duration; -use node_table::NodeEndpoint; + +use igd::{PortMappingProtocol, search_gateway, SearchOptions}; use ipnetwork::IpNetwork; +use log::{trace, debug}; +use natpmp::{Natpmp, Protocol, Response}; +use network::NatType; + +use crate::node_table::NodeEndpoint; + +const NAT_PMP_PORT_MAPPING_LIFETIME: u32 = 30; +// Waiting duration in milliseconds for response from router after sending port mapping request. +// 50 milliseconds might be enough for low RTT. +const NAT_PMP_PORT_MAPPING_WAITING_DURATION: u64 = 50; /// Socket address extension for rustc beta. To be replaces with now unstable API pub trait SocketAddrExt { @@ -94,12 +104,12 @@ impl SocketAddrExt for Ipv4Addr { self.is_multicast() || self.is_shared_space() || self.is_special_purpose() || - self.is_benchmarking() || + SocketAddrExt::is_benchmarking(self) || self.is_future_use() } fn is_usable_public(&self) -> bool { - !self.is_reserved() && + !SocketAddrExt::is_reserved(self) && !self.is_private() } @@ -183,7 +193,7 @@ impl SocketAddrExt for IpAddr { fn is_reserved(&self) -> bool { match *self { - IpAddr::V4(ref ip) => ip.is_reserved(), + IpAddr::V4(ref ip) => SocketAddrExt::is_reserved(ip), IpAddr::V6(ref ip) => ip.is_reserved(), } } @@ -212,10 +222,11 @@ impl SocketAddrExt for IpAddr { #[cfg(not(any(windows, target_os = "android")))] mod getinterfaces { - use std::{mem, io}; + use std::{io, mem}; + use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + use libc::{AF_INET, AF_INET6}; - use libc::{getifaddrs, freeifaddrs, ifaddrs, sockaddr, sockaddr_in, sockaddr_in6}; - use std::net::{Ipv4Addr, Ipv6Addr, IpAddr}; + use libc::{freeifaddrs, getifaddrs, ifaddrs, sockaddr, sockaddr_in, sockaddr_in6}; fn convert_sockaddr(sa: *mut sockaddr) -> Option { if sa.is_null() { return None; } @@ -286,7 +297,7 @@ 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() => { + IpAddr::V4(a) if !SocketAddrExt::is_reserved(a) => { return SocketAddr::V4(SocketAddrV4::new(*a, port)); }, _ => {}, @@ -306,14 +317,21 @@ pub fn select_public_address(port: u16) -> SocketAddr { SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) } -pub fn map_external_address(local: &NodeEndpoint) -> Option { +fn search_upnp(local: &NodeEndpoint) -> Option { if let SocketAddr::V4(ref local_addr) = local.address { let local_ip = *local_addr.ip(); let local_port = local_addr.port(); let local_udp_port = local.udp_port; + let search_options = SearchOptions { + timeout: Some(Duration::new(5, 0)), + // igd 0.7 used port 0 by default. + // Let's not change this behaviour + bind_addr: SocketAddr::V4(SocketAddrV4::new(local_ip, 0)), + ..Default::default() + }; let search_gateway_child = ::std::thread::spawn(move || { - match search_gateway_from_timeout(local_ip, Duration::new(5, 0)) { + match search_gateway(search_options) { Err(ref err) => debug!("Gateway search error: {}", err), Ok(gateway) => { match gateway.get_external_ip() { @@ -347,6 +365,82 @@ pub fn map_external_address(local: &NodeEndpoint) -> Option { None } +fn search_natpmp(local: &NodeEndpoint) -> Option { + if let SocketAddr::V4(ref local_addr) = local.address { + let local_port = local_addr.port(); + let local_udp_port = local.udp_port; + + let search_gateway_child = ::std::thread::spawn(move || { + let mut n = Natpmp::new()?; + + // this function call want to receive `Response::Gateway` response from router, if other then it is an Error. + n.send_public_address_request()?; + ::std::thread::sleep(Duration::from_millis(NAT_PMP_PORT_MAPPING_WAITING_DURATION)); + let gw = match n.read_response_or_retry() { + Ok(Response::Gateway(gw)) => Ok(gw), + Err(e) => { + debug!(target: "network", "IP request error: {}", e); + Err(e) + }, + _ => Err(natpmp::Error::NATPMP_ERR_UNDEFINEDERROR.into()) + }?; + + // this function call want to receive `Response::TCP` response from router, if other then it is an Error. + n.send_port_mapping_request(Protocol::TCP, local_port, local_port, NAT_PMP_PORT_MAPPING_LIFETIME)?; + ::std::thread::sleep(Duration::from_millis(NAT_PMP_PORT_MAPPING_WAITING_DURATION)); + let tcp_r = match n.read_response_or_retry() { + Ok(Response::TCP(tcp)) => Ok(tcp), + Err(e) => { + debug!(target: "network", "Port mapping for TCP error: {}", e); + Err(e) + }, + _ => Err(natpmp::Error::NATPMP_ERR_UNDEFINEDERROR.into()) + }?; + + // this function call want to receive `Response::UDP` response from router, if other then it is an Error. + n.send_port_mapping_request(Protocol::UDP, local_udp_port, local_udp_port, NAT_PMP_PORT_MAPPING_LIFETIME)?; + ::std::thread::sleep(Duration::from_millis(NAT_PMP_PORT_MAPPING_WAITING_DURATION)); + let udp_r = match n.read_response_or_retry() { + Ok(Response::UDP(udp)) => Ok(udp), + Err(e) => { + debug!(target: "network", "Port mapping for UDP error: {}", e); + Err(e) + }, + _ => Err(natpmp::Error::NATPMP_ERR_UNDEFINEDERROR.into()) + }?; + + Ok(NodeEndpoint { + address: SocketAddr::V4(SocketAddrV4::new(*gw.public_address(), tcp_r.public_port())), + udp_port: udp_r.public_port() + }) + }); + + return search_gateway_child.join().ok()? + .map_err(|e: natpmp::Error| debug!(target: "network", "NAT PMP port mapping error: {:?}", e)) + .ok(); + } + None +} + +/// Port mapping using ether UPnP or Nat-PMP. +/// NAT PMP has higher priority than UPnP. +pub fn map_external_address(local: &NodeEndpoint, nat_type: &NatType) -> Option { + match *nat_type { + NatType::Any => { + match search_natpmp(local) { + Some(end_point) => Some(end_point), + None => search_upnp(local), + } + }, + NatType::NatPMP => search_natpmp(local), + NatType::UPnP => search_upnp(local), + _ => { + trace!(target: "network", "Can't map external address using NAT"); + None + } + } +} + #[test] fn can_select_public_address() { let pub_address = select_public_address(40477); @@ -355,9 +449,16 @@ fn can_select_public_address() { #[ignore] #[test] -fn can_map_external_address_or_fail() { +fn can_map_external_address_upnp_or_fail() { let pub_address = select_public_address(40478); - let _ = map_external_address(&NodeEndpoint { address: pub_address, udp_port: 40478 }); + let _ = map_external_address(&NodeEndpoint { address: pub_address, udp_port: 40478 }, &NatType::UPnP); +} + +#[ignore] +#[test] +fn can_map_external_address_natpmp_or_fail() { + let pub_address = select_public_address(40479); + let _ = map_external_address(&NodeEndpoint { address: pub_address, udp_port: 40479 }, &NatType::NatPMP); } #[test] diff --git a/util/network-devp2p/src/lib.rs b/util/network-devp2p/src/lib.rs index 6082049e89..94bd0af610 100644 --- a/util/network-devp2p/src/lib.rs +++ b/util/network-devp2p/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,7 @@ //! Example usage for creating a network service and adding an IO handler: //! //! ```rust -//! extern crate ethcore_network as net; +//! extern crate network as net; //! extern crate ethcore_network_devp2p as devp2p; //! use net::*; //! use devp2p::NetworkService; @@ -60,45 +60,10 @@ //TODO: use Poll from mio #![allow(deprecated)] -extern crate ethcore_io as io; -extern crate parity_bytes; -extern crate parity_crypto as crypto; -extern crate ethereum_types; -extern crate parking_lot; -extern crate mio; -extern crate tiny_keccak; -extern crate crypto as rcrypto; -extern crate rand; -extern crate ansi_term; //TODO: remove this -extern crate rustc_hex; -extern crate igd; -extern crate libc; -extern crate slab; -extern crate ethkey; -extern crate rlp; -extern crate bytes; -extern crate parity_path; -extern crate ethcore_network as network; -extern crate ipnetwork; -extern crate keccak_hash as hash; -extern crate serde; -extern crate serde_json; -extern crate parity_snappy as snappy; -extern crate lru_cache; - -#[macro_use] -extern crate error_chain; -#[macro_use] -extern crate log; -#[macro_use] -extern crate serde_derive; - -#[cfg(test)] -extern crate env_logger; -#[cfg(test)] -extern crate tempdir; -#[cfg(test)] #[macro_use] -extern crate assert_matches; +pub use ethcore_io::TimerToken; +pub use host::NetworkContext; +pub use node_table::{MAX_NODES_IN_TABLE, NodeId, validate_node_url}; +pub use service::NetworkService; mod host; mod connection; @@ -109,10 +74,4 @@ mod service; mod node_table; mod ip_utils; -pub use service::NetworkService; -pub use host::NetworkContext; - -pub use io::TimerToken; -pub use node_table::{validate_node_url, NodeId}; - const PROTOCOL_VERSION: u32 = 5; diff --git a/util/network-devp2p/src/node_table.rs b/util/network-devp2p/src/node_table.rs index db001bfe76..905dc530ca 100644 --- a/util/network-devp2p/src/node_table.rs +++ b/util/network-devp2p/src/node_table.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,21 +14,29 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. 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::{fs, slice}; 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::iter::FromIterator; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; use std::path::PathBuf; use std::str::FromStr; -use std::{fs, slice}; use std::time::{self, Duration, SystemTime}; -use rand::{self, Rng}; + +use ethereum_types::H512; +use log::{debug, warn}; +use rand::seq::SliceRandom; +use rlp::{DecoderError, Rlp, RlpStream}; +use serde::{Deserialize, Serialize}; +use serde_json; + +use network::{AllowIP, Error, IpFilter}; + +use crate::{ + discovery::{NodeEntry, TableUpdates}, + ip_utils::*, +}; /// Node public key pub type NodeId = H512; @@ -130,8 +138,8 @@ impl FromStr for NodeEndpoint { address: a, udp_port: a.port() }), - Ok(None) => bail!(ErrorKind::AddressResolve(None)), - Err(_) => Err(ErrorKind::AddressParse.into()) // always an io::Error of InvalidInput kind + Ok(None) => return Err(Error::AddressResolve(None.into())), + Err(_) => Err(Error::AddressParse) // always an io::Error of InvalidInput kind } } } @@ -213,10 +221,10 @@ impl FromStr for Node { type Err = Error; fn from_str(s: &str) -> Result { let (id, endpoint) = if s.len() > 136 && &s[0..8] == "enode://" && &s[136..137] == "@" { - (s[8..136].parse().map_err(|_| ErrorKind::InvalidNodeId)?, NodeEndpoint::from_str(&s[137..])?) + (s[8..136].parse().map_err(|_| Error::InvalidNodeId)?, NodeEndpoint::from_str(&s[137..])?) } else { - (NodeId::new(), NodeEndpoint::from_str(s)?) + (NodeId::default(), NodeEndpoint::from_str(s)?) }; Ok(Node { @@ -241,22 +249,27 @@ impl Hash for Node { } } -const MAX_NODES: usize = 1024; +pub const MAX_NODES_IN_TABLE: usize = 4096; +const MAX_NODES_IN_FILE: usize = 1024; const NODES_FILE: &str = "nodes.json"; /// Node table backed by disk file. pub struct NodeTable { nodes: HashMap, + ordered_ids: Vec, useless_nodes: HashSet, path: Option, } impl NodeTable { pub fn new(path: Option) -> NodeTable { + let nodes = NodeTable::load(path.clone()); + let ordered_ids = NodeTable::make_ordered_entries(&nodes).iter().map(|m| m.id).collect(); NodeTable { - path: path.clone(), - nodes: NodeTable::load(path), + path, + nodes, useless_nodes: HashSet::new(), + ordered_ids } } @@ -264,24 +277,72 @@ impl NodeTable { pub fn add_node(&mut self, mut node: 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); + let id = node.id; + if self.ordered_ids.len() == MAX_NODES_IN_TABLE { + self.nodes.remove(&self.ordered_ids.pop().expect("ordered_ids is not empty; qed")); + }; + let index = self.get_index_to_insert(node.last_contact); + if self.nodes.insert(node.id, node).is_none() { + self.ordered_ids.insert(index, id); + }; + } + + /// Get index in the ordered entries vector to insert node based on its last contact value + fn get_index_to_insert(&self, last_contact: Option) -> usize { + let len = self.ordered_ids.len(); + let mut index = len; + match last_contact { + Some(NodeContact::Success(last_contact_time)) => { + if let Some(i) = self.ordered_ids.iter().position(|&item| { + match self.nodes.get(&item).expect("nodes and ordered_ids do not get out of sync; qed").last_contact { + Some(NodeContact::Success(last)) => last < last_contact_time, + _ => true + } + }) { index = i; }; + }, + None => { + if let Some(i) = self.ordered_ids.iter().position(|&item| { + match self.nodes.get(&item).expect("nodes and ordered_ids do not get out of sync; qed").last_contact { + Some(NodeContact::Success(_)) => false, + _ => true + } + }) { index = i; }; + }, + Some(NodeContact::Failure(last_contact_time)) => { + if let Some(i) = self.ordered_ids.iter().rev().position(|&item| { + match self.nodes.get(&item).expect("nodes and ordered_ids do not get out of sync; qed").last_contact { + Some(NodeContact::Failure(last)) => last < last_contact_time, + _ => true + } + }) { index = len - i; }; + } + }; + index } - /// Returns a list of ordered nodes according to their most recent contact - /// and filtering useless nodes. The algorithm for creating the sorted nodes - /// is: + /// Returns a list of ordered entries from table + fn ordered(&self) -> Vec<&Node> { + Vec::from_iter( + self.ordered_ids + .iter() + .filter(|id| !self.useless_nodes.contains(&id)) + .map(|id| self.nodes.get(&id).expect("nodes and ordered_ids do not get out of sync; qed")) + ) + } + + /// Makes a list of ordered nodes according to their most recent contact. + /// 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> { + fn make_ordered_entries(node_table: &HashMap) -> Vec<&Node> { 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)); + let nodes = node_table.values(); for node in nodes { // discard contact points older that aren't recent @@ -312,7 +373,8 @@ impl NodeTable { a.time().cmp(&b.time()) }); - rand::thread_rng().shuffle(&mut unknown); + let mut rng = rand::thread_rng(); + unknown.shuffle(&mut rng); success.append(&mut unknown); success.append(&mut failures); @@ -322,7 +384,7 @@ impl NodeTable { /// 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 { - self.ordered_entries().iter() + self.ordered().iter() .filter(|n| n.endpoint.is_allowed(&filter)) .map(|n| n.id) .collect() @@ -331,15 +393,15 @@ impl NodeTable { /// Ordered list of all entries by failure percentage, for nodes with the same failure percentage the absolute /// number of failures is considered. pub fn entries(&self) -> Vec { - self.ordered_entries().iter().map(|n| NodeEntry { + self.ordered().iter().map(|n| NodeEntry { endpoint: n.endpoint.clone(), id: n.id, }).collect() } /// Get particular node - pub fn get_mut(&mut self, id: &NodeId) -> Option<&mut Node> { - self.nodes.get_mut(id) + pub fn get(&self, id: &NodeId) -> Option<&Node> { + self.nodes.get(id) } /// Check if a node exists in the table. @@ -350,28 +412,49 @@ 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).or_insert_with(|| Node::new(node.id, node.endpoint.clone())); - entry.endpoint = node.endpoint; - } + let mut add = false; + { + let entry = self.nodes.entry(node.id).or_insert_with(|| { + add = true; + Node::new(node.id, node.endpoint.clone()) + }); + entry.endpoint = node.endpoint; + } + if add { + if self.ordered_ids.len() == MAX_NODES_IN_TABLE { + self.nodes.remove(&self.ordered_ids.pop().expect("ordered_ids is not empty; qed")); + }; + let index = self.get_index_to_insert(None); + self.ordered_ids.insert(index, node.id); + }; + }; for r in update.removed { if !reserved.contains(&r) { + self.ordered_ids.iter().position(|&i| r == i).map(|p| self.ordered_ids.remove(p)); self.nodes.remove(&r); } } } - /// Set last contact as failure for a node - pub fn note_failure(&mut self, id: &NodeId) { + fn update_ordered_ids(&mut self, id: &NodeId, last_contact: Option) { if let Some(node) = self.nodes.get_mut(id) { - node.last_contact = Some(NodeContact::failure()); + node.last_contact = last_contact; + } + if let Some(pos) = self.ordered_ids.iter().position(|i| id == i) { + self.ordered_ids.remove(pos); + let index = self.get_index_to_insert(last_contact); + self.ordered_ids.insert(index, *id); } } + /// Set last contact as failure for a node + pub fn note_failure(&mut self, id: &NodeId) { + self.update_ordered_ids(id, 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()); - } + self.update_ordered_ids(id, Some(NodeContact::success())); } /// Mark as useless, no further attempts to connect until next call to `clear_useless`. @@ -398,7 +481,7 @@ impl NodeTable { 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) + .take(MAX_NODES_IN_FILE) .map(Into::into) .collect(); let table = json::NodeTable { nodes }; @@ -526,12 +609,18 @@ mod json { #[cfg(test)] mod tests { - use super::*; - use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr}; - use ethereum_types::H512; + use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::str::FromStr; - use tempdir::TempDir; + use std::thread::sleep; + use std::time::Duration; + + use ethereum_types::H512; use ipnetwork::IpNetwork; + use tempdir::TempDir; + + use assert_matches::assert_matches; + + use super::*; #[test] fn endpoint_parse() { @@ -548,21 +637,21 @@ mod tests { 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); + assert_matches!(endpoint.unwrap_err(), Error::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); + assert_matches!(endpoint.unwrap_err(), Error::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); + assert_matches!(endpoint.unwrap_err(), Error::AddressParse); let endpoint = NodeEndpoint::from_str("123.123.123.123:123"); assert!(endpoint.is_ok()) } @@ -587,11 +676,11 @@ mod tests { 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); + assert_matches!(node.unwrap_err(), Error::AddressParse); let node = Node::from_str("enode://foo@bar"); assert!(node.is_err()); - assert_matches!(node.unwrap_err().kind(), &ErrorKind::AddressParse); + assert_matches!(node.unwrap_err(), Error::AddressParse); } #[test] @@ -610,49 +699,85 @@ mod tests { let id6 = H512::from_str("f979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(); let mut table = NodeTable::new(None); + assert_eq!(table.get_index_to_insert(Some(NodeContact::success())), 0); + assert_eq!(table.get_index_to_insert(Some(NodeContact::failure())), 0); + assert_eq!(table.get_index_to_insert(None), 0); + + // sleep 1 mcs is added because nanosecond precision was lost since mac os x high sierra update + // https://github.com/paritytech/parity-ethereum/issues/9632 + table.add_node(node1); + sleep(Duration::from_micros(1)); + + assert_eq!(table.get_index_to_insert(Some(NodeContact::success())), 0); + assert_eq!(table.get_index_to_insert(Some(NodeContact::failure())), 1); + assert_eq!(table.get_index_to_insert(None), 0); + table.add_node(node2); + sleep(Duration::from_micros(1)); + + assert_eq!(table.get_index_to_insert(Some(NodeContact::success())), 0); + assert_eq!(table.get_index_to_insert(Some(NodeContact::failure())), 2); + assert_eq!(table.get_index_to_insert(None), 0); + table.add_node(node3); + sleep(Duration::from_micros(1)); table.add_node(node4); + sleep(Duration::from_micros(1)); table.add_node(node5); + sleep(Duration::from_micros(1)); table.add_node(node6); + sleep(Duration::from_micros(1)); // failures - nodes 1 & 2 table.note_failure(&id1); + sleep(Duration::from_micros(1)); + let time_in_between = SystemTime::now(); + sleep(Duration::from_micros(1)); table.note_failure(&id2); + sleep(Duration::from_micros(1)); + + assert_eq!(table.get_index_to_insert(Some(NodeContact::success())), 0); + assert_eq!(table.get_index_to_insert(Some(NodeContact::failure())), 6); + assert_eq!(table.get_index_to_insert(Some(NodeContact::Failure(time_in_between))), 5); + assert_eq!(table.get_index_to_insert(Some(NodeContact::Failure(time::UNIX_EPOCH))), 4); + assert_eq!(table.get_index_to_insert(None), 0); - // success - nodes 3 & 4 + // success - nodes 3,4,5 (5 - the oldest) + table.note_success(&id5); + sleep(Duration::from_micros(1)); table.note_success(&id3); + sleep(Duration::from_micros(1)); + + assert_eq!(table.get_index_to_insert(Some(NodeContact::Success(time::UNIX_EPOCH))), 2); + assert_eq!(table.get_index_to_insert(None), 2); + + let time_in_between = SystemTime::now(); + sleep(Duration::from_micros(1)); table.note_success(&id4); + sleep(Duration::from_micros(1)); - // success - node 5 (old contact) - table.get_mut(&id5).unwrap().last_contact = Some(NodeContact::Success(time::UNIX_EPOCH)); + assert_eq!(table.get_index_to_insert(Some(NodeContact::success())), 0); + assert_eq!(table.get_index_to_insert(Some(NodeContact::Success(time_in_between))), 1); + assert_eq!(table.get_index_to_insert(Some(NodeContact::Success(time::UNIX_EPOCH))), 3); + assert_eq!(table.get_index_to_insert(None), 3); // unknown - node 6 // nodes are also ordered according to their addition time - // - // nanosecond precision lost since mac os x high sierra update so let's not compare their order - // https://github.com/paritytech/parity-ethereum/issues/9632 let r = table.nodes(&IpFilter::default()); - // most recent success - assert!( - (r[0] == id4 && r[1] == id3) || - (r[0] == id3 && r[1] == id4) - ); + assert_eq!(r[0][..], id4[..]); // most recent success + assert_eq!(r[1][..], id3[..]); // unknown (old contacts and new nodes), randomly shuffled assert!( - (r[2] == id5 && r[3] == id6) || - (r[2] == id6 && r[3] == id5) + r[2][..] == id5[..] && r[3][..] == id6[..] || + r[2][..] == id6[..] && r[3][..] == id5[..] ); - // oldest failure - assert!( - (r[4] == id1 && r[5] == id2) || - (r[4] == id2 && r[5] == id1) - ); + assert_eq!(r[4][..], id1[..]); // oldest failure + assert_eq!(r[5][..], id2[..]); } #[test] diff --git a/util/network-devp2p/src/service.rs b/util/network-devp2p/src/service.rs index dfacec4be3..7a67590fba 100644 --- a/util/network-devp2p/src/service.rs +++ b/util/network-devp2p/src/service.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,16 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use network::{Error, NetworkConfiguration, NetworkProtocolHandler, NonReservedPeerMode}; -use network::{NetworkContext, PeerId, ProtocolId, NetworkIoMessage}; -use host::Host; -use io::*; -use parking_lot::RwLock; use std::net::SocketAddr; use std::ops::RangeInclusive; use std::sync::Arc; + use ansi_term::Colour; -use network::ConnectionFilter; +use log::info; +use parking_lot::RwLock; + +use ethcore_io::{IoContext, IoHandler, IoService}; +use network::{ + ConnectionFilter, Error, NetworkConfiguration, NetworkContext, + NetworkIoMessage, NetworkProtocolHandler, NonReservedPeerMode, PeerId, ProtocolId, + +}; + +use crate::host::Host; struct HostHandler { public_url: RwLock> @@ -49,12 +55,12 @@ pub struct NetworkService { host: RwLock>>, host_handler: Arc, config: NetworkConfiguration, - filter: Option>, + filter: Option>, } impl NetworkService { /// Starts IO event loop - pub fn new(config: NetworkConfiguration, filter: Option>) -> Result { + pub fn new(config: NetworkConfiguration, filter: Option>) -> Result { let host_handler = Arc::new(HostHandler { public_url: RwLock::new(None) }); let io_service = IoService::::start()?; @@ -71,7 +77,7 @@ impl NetworkService { /// Register a new protocol handler with the event loop. pub fn register_protocol( &self, - handler: Arc, + handler: Arc, protocol: ProtocolId, // version id + packet count versions: &[(u8, u8)] @@ -178,7 +184,7 @@ impl NetworkService { } /// Executes action in the network context - pub fn with_context(&self, protocol: ProtocolId, action: F) where F: FnOnce(&NetworkContext) { + pub fn with_context(&self, protocol: ProtocolId, action: F) where F: FnOnce(&dyn NetworkContext) { let io = IoContext::new(self.io_service.channel(), 0); let host = self.host.read(); if let Some(ref host) = host.as_ref() { @@ -187,7 +193,7 @@ impl NetworkService { } /// Evaluates function in the network context - pub fn with_context_eval(&self, protocol: ProtocolId, action: F) -> Option where F: FnOnce(&NetworkContext) -> T { + pub fn with_context_eval(&self, protocol: ProtocolId, action: F) -> Option where F: FnOnce(&dyn NetworkContext) -> T { let io = IoContext::new(self.io_service.channel(), 0); let host = self.host.read(); host.as_ref().map(|ref host| host.with_context_eval(protocol, &io, action)) diff --git a/util/network-devp2p/src/session.rs b/util/network-devp2p/src/session.rs index 6cecaf3610..07259ecf8e 100644 --- a/util/network-devp2p/src/session.rs +++ b/util/network-devp2p/src/session.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,25 +14,30 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::{str, io}; -use std::net::SocketAddr; +use std::{io, str}; use std::collections::HashMap; +use std::net::SocketAddr; use std::time::{Duration, Instant}; +use ethereum_types::H256; +use log::{debug, trace, warn}; use mio::*; -use mio::deprecated::{Handler, EventLoop}; +use mio::deprecated::{EventLoop, Handler}; use mio::tcp::*; -use ethereum_types::H256; -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; +use parity_snappy as snappy; +use rlp::{EMPTY_LIST_RLP, Rlp, RlpStream}; + +use ethcore_io::{IoContext, StreamToken}; +use network::{DisconnectReason, Error, PeerCapabilityInfo, ProtocolId, SessionInfo}; use network::client_version::ClientVersion; -use host::*; -use node_table::NodeId; -use snappy; +use network::SessionCapabilityInfo; + +use crate::{ + connection::{Connection, EncryptedConnection, MAX_PAYLOAD_SIZE, Packet}, + handshake::Handshake, + host::HostInfo, + node_table::NodeId, +}; // Timeout must be less than (interval - 1). const PING_TIMEOUT: Duration = Duration::from_secs(60); @@ -255,10 +260,10 @@ impl Session { where Message: Send + Sync + Clone { if protocol.is_some() && (self.info.capabilities.is_empty() || !self.had_hello) { debug!(target: "network", "Sending to unconfirmed session {}, protocol: {:?}, packet: {}", self.token(), protocol.as_ref().map(|p| str::from_utf8(&p[..]).unwrap_or("??")), packet_id); - bail!(ErrorKind::BadProtocol); + return Err(Error::BadProtocol); } if self.expired() { - return Err(ErrorKind::Expired.into()); + return Err(Error::Expired); } let mut i = 0usize; let pid = match protocol { @@ -280,7 +285,7 @@ impl Session { let mut payload = data; // create a reference with local lifetime if self.compression { if payload.len() > MAX_PAYLOAD_SIZE { - bail!(ErrorKind::OversizedPacket); + return Err(Error::OversizedPacket); } let len = snappy::compress_into(&payload, &mut compressed); trace!(target: "network", "compressed {} to {}", payload.len(), len); @@ -330,16 +335,16 @@ impl Session { 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()); + return Err(Error::BadProtocol); } let packet_id = packet.data[0]; if packet_id != PACKET_HELLO && packet_id != PACKET_DISCONNECT && !self.had_hello { - return Err(ErrorKind::BadProtocol.into()); + return Err(Error::BadProtocol); } let data = if self.compression { let compressed = &packet.data[1..]; if snappy::decompressed_len(&compressed)? > MAX_PAYLOAD_SIZE { - bail!(ErrorKind::OversizedPacket); + return Err(Error::OversizedPacket); } snappy::decompress(&compressed)? } else { @@ -357,7 +362,7 @@ impl Session { if self.had_hello { debug!(target:"network", "Disconnected: {}: {:?}", self.token(), DisconnectReason::from_u8(reason)); } - Err(ErrorKind::Disconnect(DisconnectReason::from_u8(reason)).into()) + Err(Error::Disconnect(DisconnectReason::from_u8(reason))) } PACKET_PING => { self.send_pong(io)?; @@ -371,7 +376,7 @@ impl Session { }, PACKET_GET_PEERS => Ok(SessionData::None), //TODO; PACKET_PEERS => Ok(SessionData::None), - PACKET_USER ... PACKET_LAST => { + PACKET_USER ..= PACKET_LAST => { let mut i = 0usize; while packet_id >= self.info.capabilities[i].id_offset + self.info.capabilities[i].packet_count { i += 1; @@ -499,7 +504,7 @@ impl Session { rlp.append(&(reason as u32)); self.send_packet(io, None, PACKET_DISCONNECT, &rlp.drain()).ok(); } - ErrorKind::Disconnect(reason).into() + Error::Disconnect(reason) } fn send(&mut self, io: &IoContext, data: &[u8]) -> Result<(), Error> where Message: Send + Sync + Clone { diff --git a/util/network-devp2p/tests/tests.rs b/util/network-devp2p/tests/tests.rs index 00f811e463..55ee29d031 100644 --- a/util/network-devp2p/tests/tests.rs +++ b/util/network-devp2p/tests/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,24 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -extern crate env_logger; -extern crate ethcore_io as io; -extern crate ethcore_network; -extern crate ethcore_network_devp2p; -extern crate ethkey; -extern crate parity_bytes; -extern crate parking_lot; - -use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering}; -use std::sync::Arc; +use std::sync::{ + Arc, + atomic::{AtomicBool, Ordering as AtomicOrdering} +}; use std::thread; -use std::time::*; -use parking_lot::Mutex; +use std::time::Duration; + use parity_bytes::Bytes; -use ethcore_network::*; +use parking_lot::Mutex; + +use network::{PeerId, NetworkContext, NetworkProtocolHandler, NetworkConfiguration}; use ethcore_network_devp2p::NetworkService; -use ethkey::{Random, Generator}; -use io::TimerToken; +use parity_crypto::publickey::{Generator, Random}; +use ethcore_io::TimerToken; pub struct TestProtocol { drop_session: bool, @@ -46,7 +42,7 @@ impl TestProtocol { packet: Mutex::new(Vec::new()), got_timeout: AtomicBool::new(false), got_disconnect: AtomicBool::new(false), - drop_session: drop_session, + drop_session, } } /// Creates and register protocol with the network service @@ -70,16 +66,16 @@ impl TestProtocol { } impl NetworkProtocolHandler for TestProtocol { - fn initialize(&self, io: &NetworkContext) { + fn initialize(&self, io: &dyn NetworkContext) { io.register_timer(0, Duration::from_millis(10)).unwrap(); } - fn read(&self, _io: &NetworkContext, _peer: &PeerId, packet_id: u8, data: &[u8]) { + fn read(&self, _io: &dyn NetworkContext, _peer: &PeerId, packet_id: u8, data: &[u8]) { assert_eq!(packet_id, 33); self.packet.lock().extend(data); } - fn connected(&self, io: &NetworkContext, peer: &PeerId) { + fn connected(&self, io: &dyn NetworkContext, peer: &PeerId) { assert!(io.peer_client_version(*peer).to_string().contains("Parity")); if self.drop_session { io.disconnect_peer(*peer) @@ -88,12 +84,12 @@ impl NetworkProtocolHandler for TestProtocol { } } - fn disconnected(&self, _io: &NetworkContext, _peer: &PeerId) { + fn disconnected(&self, _io: &dyn NetworkContext, _peer: &PeerId) { self.got_disconnect.store(true, AtomicOrdering::Relaxed); } /// Timer function called after a timeout created with `NetworkContext::timeout`. - fn timeout(&self, _io: &NetworkContext, timer: TimerToken) { + fn timeout(&self, _io: &dyn NetworkContext, timer: TimerToken) { assert_eq!(timer, 0); self.got_timeout.store(true, AtomicOrdering::Relaxed); } diff --git a/util/network/Cargo.toml b/util/network/Cargo.toml index b7077f7aea..8b71a3e7e6 100644 --- a/util/network/Cargo.toml +++ b/util/network/Cargo.toml @@ -7,14 +7,13 @@ version = "1.12.0" authors = ["Parity Technologies "] [dependencies] -error-chain = { version = "0.12", default-features = false } -parity-crypto = "0.3.0" +derive_more = "0.14.0" +parity-crypto = { version = "0.4.2", features = ["publickey"] } ethcore-io = { path = "../io" } -ethereum-types = "0.4" -ethkey = { path = "../../accounts/ethkey" } +ethereum-types = "0.8.0" ipnetwork = "0.12.6" lazy_static = "1.0" -rlp = { version = "0.3.0", features = ["ethereum"] } +rlp = "0.4.0" libc = "0.2" parity-snappy = "0.1" semver = {version="0.9.0", features=["serde"]} diff --git a/util/network/src/client_version.rs b/util/network/src/client_version.rs index 47d81fad86..7b6f68be87 100644 --- a/util/network/src/client_version.rs +++ b/util/network/src/client_version.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/network/src/connection_filter.rs b/util/network/src/connection_filter.rs index 4efd1c503c..c89152c3cb 100644 --- a/util/network/src/connection_filter.rs +++ b/util/network/src/connection_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/network/src/error.rs b/util/network/src/error.rs index bd48830c1b..8b46ea4a21 100644 --- a/util/network/src/error.rs +++ b/util/network/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,14 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -// Silence: `use of deprecated item 'std::error::Error::cause': replaced by Error::source, which can support downcasting` -// https://github.com/paritytech/parity-ethereum/issues/10302 -#![allow(deprecated)] - -use std::{io, net, fmt}; +use std::{error, io, net, fmt}; use libc::{ENFILE, EMFILE}; use io::IoError; -use {rlp, ethkey, crypto, snappy}; +use {rlp, crypto, snappy}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum DisconnectReason @@ -85,118 +81,121 @@ impl fmt::Display for DisconnectReason { } } -error_chain! { - foreign_links { - SocketIo(IoError) #[doc = "Socket IO error."]; - Decompression(snappy::InvalidInput) #[doc = "Decompression error."]; - Rlp(rlp::DecoderError) #[doc = "Rlp decoder 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"), - display("Failed to resolve network address {}", err.as_ref().map_or("".to_string(), |e| e.to_string())), - } - - #[doc = "Authentication failure"] - Auth { - description("Authentication failure"), - display("Authentication failure"), - } - - #[doc = "Unrecognised protocol"] - BadProtocol { - description("Bad protocol"), - display("Bad protocol"), - } - - #[doc = "Expired message"] - Expired { - description("Expired message"), - display("Expired message"), - } +/// Queue error +#[derive(Debug, derive_more::Display)] +pub enum Error { + /// Socket IO error. + SocketIo(IoError), + /// Decompression error. + Decompression(snappy::InvalidInput), + /// Rlp decoder error. + Rlp(rlp::DecoderError), + /// Error concerning the network address parsing subsystem. + #[display(fmt = "Failed to parse network address")] + AddressParse, + /// Error concerning the network address resolution subsystem. + #[display(fmt = "Failed to resolve network address {}", _0)] + AddressResolve(AddressResolveError), + /// Authentication failure + #[display(fmt = "Authentication failure")] + Auth, + /// Unrecognised protocol + #[display(fmt = "Bad protocol")] + BadProtocol, + /// Expired message + #[display(fmt = "Expired message")] + Expired, + /// Peer not found + #[display(fmt = "Peer not found")] + PeerNotFound, + /// Peer is disconnected + #[display(fmt = "Peer disconnected: {}", _0)] + Disconnect(DisconnectReason), + /// Invalid node id + #[display(fmt = "Invalid node id")] + InvalidNodeId, + /// Packet size is over the protocol limit + #[display(fmt = "Packet is too large")] + OversizedPacket, + /// Reached system resource limits for this process + #[display(fmt = "Too many open files in this process. Check your resource limits and restart parity")] + ProcessTooManyFiles, + /// Reached system wide resource limits + #[display(fmt = "Too many open files on system. Consider closing some processes/release some file handlers or increas the system-wide resource limits and restart parity.")] + SystemTooManyFiles, + /// An unknown IO error occurred. + #[display(fmt = "Unexpected IO error: {}", _0)] + Io(io::Error), +} - #[doc = "Peer not found"] - PeerNotFound { - description("Peer not found"), - display("Peer not found"), - } +/// Wraps io::Error for Display impl +#[derive(Debug)] +pub struct AddressResolveError(Option); - #[doc = "Peer is disconnected"] - Disconnect(reason: DisconnectReason) { - description("Peer disconnected"), - display("Peer disconnected: {}", reason), - } +impl fmt::Display for AddressResolveError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", self.0.as_ref().map_or("".to_string(), |e| e.to_string())) + } +} - #[doc = "Invalid node id"] - InvalidNodeId { - description("Invalid node id"), - display("Invalid node id"), - } +impl From> for AddressResolveError { + fn from(err: Option) -> Self { + AddressResolveError(err) + } +} - #[doc = "Packet size is over the protocol limit"] - OversizedPacket { - description("Packet is too large"), - display("Packet is too large"), +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Error::Decompression(e) => Some(e), + Error::Rlp(e) => Some(e), + _ => None, } + } +} - #[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"), - } +impl From for Error { + fn from(err: IoError) -> Self { + Error::SocketIo(err) + } +} - #[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."), - } +impl From for Error { + fn from(err: snappy::InvalidInput) -> Self { + Error::Decompression(err) + } +} - #[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: rlp::DecoderError) -> Self { + Error::Rlp(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)) + Some(ENFILE) => Error::ProcessTooManyFiles, + Some(EMFILE) => Error::SystemTooManyFiles, + _ => Error::Io(err) } } } -impl From for Error { - fn from(_err: ethkey::Error) -> Self { - ErrorKind::Auth.into() - } -} - -impl From for Error { - fn from(_err: ethkey::crypto::Error) -> Self { - ErrorKind::Auth.into() +impl From for Error { + fn from(_err: crypto::publickey::Error) -> Self { + Error::Auth } } impl From for Error { fn from(_err: crypto::error::SymmError) -> Self { - ErrorKind::Auth.into() + Error::Auth } } impl From for Error { - fn from(_err: net::AddrParseError) -> Self { ErrorKind::AddressParse.into() } + fn from(_err: net::AddrParseError) -> Self { Error::AddressParse } } #[test] @@ -208,13 +207,13 @@ fn test_errors() { } assert_eq!(DisconnectReason::Unknown, r); - match *>::from(rlp::DecoderError::RlpIsTooBig).kind() { - ErrorKind::Rlp(_) => {}, + match >::from(rlp::DecoderError::RlpIsTooBig) { + Error::Rlp(_) => {}, _ => panic!("Unexpected error"), } - match *>::from(ethkey::crypto::Error::InvalidMessage).kind() { - ErrorKind::Auth => {}, + match >::from(crypto::publickey::Error::InvalidMessage) { + Error::Auth => {}, _ => panic!("Unexpected error"), } } @@ -226,18 +225,18 @@ fn test_io_errors() { assert_matches!( >::from( io::Error::from_raw_os_error(ENFILE) - ).kind(), - ErrorKind::ProcessTooManyFiles); + ), + Error::ProcessTooManyFiles); assert_matches!( >::from( io::Error::from_raw_os_error(EMFILE) - ).kind(), - ErrorKind::SystemTooManyFiles); + ), + Error::SystemTooManyFiles); assert_matches!( >::from( io::Error::from_raw_os_error(0) - ).kind(), - ErrorKind::Io(_)); + ), + Error::Io(_)); } diff --git a/util/network/src/lib.rs b/util/network/src/lib.rs index 9e6f71fdd1..40e2ec3a04 100644 --- a/util/network/src/lib.rs +++ b/util/network/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -19,7 +19,6 @@ extern crate parity_crypto as crypto; extern crate ethcore_io as io; extern crate ethereum_types; -extern crate ethkey; extern crate rlp; extern crate ipnetwork; extern crate parity_snappy as snappy; @@ -32,9 +31,7 @@ extern crate serde_derive; #[cfg(test)] #[macro_use] extern crate assert_matches; - -#[macro_use] -extern crate error_chain; +extern crate derive_more; #[macro_use] extern crate lazy_static; @@ -46,7 +43,7 @@ mod error; pub use connection_filter::{ConnectionFilter, ConnectionDirection}; pub use io::TimerToken; -pub use error::{Error, ErrorKind, DisconnectReason}; +pub use error::{Error, DisconnectReason}; use client_version::ClientVersion; use std::cmp::Ordering; @@ -56,7 +53,7 @@ use std::str::{self, FromStr}; use std::sync::Arc; use std::time::Duration; use ipnetwork::{IpNetwork, IpNetworkError}; -use ethkey::Secret; +use crypto::publickey::Secret; use ethereum_types::H512; use rlp::{Decodable, DecoderError, Rlp}; @@ -71,13 +68,13 @@ pub type NodeId = H512; /// Local (temporary) peer session ID. pub type PeerId = usize; -/// Messages used to communitate with the event loop from other threads. +/// Messages used to communicate with the event loop from other threads. #[derive(Clone)] pub enum NetworkIoMessage { /// Register a new protocol handler. AddHandler { /// Handler shared instance. - handler: Arc, + handler: Arc, /// Protocol Id. protocol: ProtocolId, /// Supported protocol versions and number of packet IDs reserved by the protocol (packet count). @@ -177,6 +174,15 @@ impl Ord for SessionCapabilityInfo { } } +/// Type of NAT resolving method +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum NatType { + Nothing, + Any, + UPnP, + NatPMP, +} + /// Network service configuration #[derive(Debug, PartialEq, Clone)] pub struct NetworkConfiguration { @@ -192,6 +198,8 @@ pub struct NetworkConfiguration { pub udp_port: Option, /// Enable NAT configuration pub nat_enabled: bool, + /// Nat type + pub nat_type: NatType, /// Enable discovery pub discovery_enabled: bool, /// List of initial node addresses @@ -232,6 +240,7 @@ impl NetworkConfiguration { public_address: None, udp_port: None, nat_enabled: true, + nat_type: NatType::Any, discovery_enabled: true, boot_nodes: Vec::new(), use_secret: None, @@ -363,15 +372,15 @@ impl<'a, T> NetworkContext for &'a T where T: ?Sized + NetworkContext { /// `Message` is the type for message data. pub trait NetworkProtocolHandler: Sync + Send { /// Initialize the handler - fn initialize(&self, _io: &NetworkContext) {} + fn initialize(&self, _io: &dyn NetworkContext) {} /// Called when new network packet received. - fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]); + fn read(&self, io: &dyn NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]); /// Called when new peer is connected. Only called when peer supports the same protocol. - fn connected(&self, io: &NetworkContext, peer: &PeerId); + fn connected(&self, io: &dyn NetworkContext, peer: &PeerId); /// Called when a previously connected peer disconnects. - fn disconnected(&self, io: &NetworkContext, peer: &PeerId); + fn disconnected(&self, io: &dyn NetworkContext, peer: &PeerId); /// Timer function called after a timeout created with `NetworkContext::timeout`. - fn timeout(&self, _io: &NetworkContext, _timer: TimerToken) {} + fn timeout(&self, _io: &dyn NetworkContext, _timer: TimerToken) {} } /// Non-reserved peer modes. @@ -396,42 +405,42 @@ impl NonReservedPeerMode { #[derive(Clone, Debug, PartialEq, Eq)] pub struct IpFilter { - pub predefined: AllowIP, - pub custom_allow: Vec, - pub custom_block: Vec, + pub predefined: AllowIP, + pub custom_allow: Vec, + pub custom_block: Vec, } impl Default for IpFilter { - fn default() -> Self { - IpFilter { - predefined: AllowIP::All, - custom_allow: vec![], - custom_block: vec![], - } - } + fn default() -> Self { + IpFilter { + predefined: AllowIP::All, + custom_allow: vec![], + custom_block: vec![], + } + } } impl IpFilter { - /// Attempt to parse the peer mode from a string. - pub fn parse(s: &str) -> Result { - let mut filter = IpFilter::default(); - for f in s.split_whitespace() { - match f { - "all" => filter.predefined = AllowIP::All, - "private" => filter.predefined = AllowIP::Private, - "public" => filter.predefined = AllowIP::Public, - "none" => filter.predefined = AllowIP::None, - custom => { - if custom.starts_with("-") { - filter.custom_block.push(IpNetwork::from_str(&custom.to_owned().split_off(1))?) - } else { - filter.custom_allow.push(IpNetwork::from_str(custom)?) - } - } - } - } - Ok(filter) - } + /// Attempt to parse the peer mode from a string. + pub fn parse(s: &str) -> Result { + let mut filter = IpFilter::default(); + for f in s.split_whitespace() { + match f { + "all" => filter.predefined = AllowIP::All, + "private" => filter.predefined = AllowIP::Private, + "public" => filter.predefined = AllowIP::Public, + "none" => filter.predefined = AllowIP::None, + custom => { + if custom.starts_with("-") { + filter.custom_block.push(IpNetwork::from_str(&custom.to_owned().split_off(1))?) + } else { + filter.custom_allow.push(IpNetwork::from_str(custom)?) + } + } + } + } + Ok(filter) + } } /// IP fiter @@ -443,6 +452,6 @@ pub enum AllowIP { Private, /// Connect to public network only Public, - /// Block all addresses - None, + /// Block all addresses + None, } diff --git a/util/panic-hook/src/lib.rs b/util/panic-hook/src/lib.rs index 03501a47fd..d031bf807c 100644 --- a/util/panic-hook/src/lib.rs +++ b/util/panic-hook/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/patricia-trie-ethereum/Cargo.toml b/util/patricia-trie-ethereum/Cargo.toml index 8cb8664a5e..6979a7bfc8 100644 --- a/util/patricia-trie-ethereum/Cargo.toml +++ b/util/patricia-trie-ethereum/Cargo.toml @@ -6,15 +6,20 @@ description = "Merkle-Patricia Trie (Ethereum Style)" license = "GPL-3.0" [dependencies] -trie-db = "0.11.0" +trie-db = "0.18.0" keccak-hasher = { version = "0.1.1", path = "../keccak-hasher" } -hash-db = "0.11.0" -rlp = "0.3.0" +hash-db = "0.15.0" +rlp = "0.4.4" parity-bytes = "0.1" -ethereum-types = "0.4" +ethereum-types = "0.8.0" elastic-array = "0.10" [dev-dependencies] -memory-db = "0.11.0" -keccak-hash = "0.1.2" +memory-db = "0.18.0" +keccak-hash = "0.4.0" journaldb = { path = "../journaldb" } +criterion = "0.3" + +[[bench]] +name = "rlp_node_codec" +harness = false diff --git a/util/patricia-trie-ethereum/benches/rlp_node_codec.rs b/util/patricia-trie-ethereum/benches/rlp_node_codec.rs new file mode 100644 index 0000000000..dcd27aa5e2 --- /dev/null +++ b/util/patricia-trie-ethereum/benches/rlp_node_codec.rs @@ -0,0 +1,83 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Benchmarking RlpNodeCodec decoding performance + +extern crate criterion; +extern crate patricia_trie_ethereum as ethtrie; +extern crate trie_db; +extern crate ethereum_types; +extern crate rlp; + +use criterion::{Criterion, criterion_group, criterion_main}; +use ethereum_types::H256; +use ethtrie::RlpNodeCodec; +use rlp::RlpStream; +use trie_db::NodeCodec; + +fn decoding(c: &mut Criterion) { + c.bench_function("decode leaf (inline)", |b| { + let mut stream = RlpStream::new_list(2); + stream.append(&"cat").append(&"dog"); + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data) }); + }); + + c.bench_function("decode extension (inline)", |b| { + let mut stream = RlpStream::new_list(2); + let payload = vec![0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9u8]; + stream.append(&"").append(&payload); + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data) }); + }); + + c.bench_function("decode extension (hash)", |b| { + let mut stream = RlpStream::new_list(2); + let payload = H256::random(); + stream.append(&"").append(&payload); + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data) }); + }); + + c.bench_function("decode branch (hash)", |b| { + let mut stream = RlpStream::new_list(17); + for _ in 0..17 { + stream.append(&H256::random()); + + } + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data) }); + }); + + c.bench_function("decode branch (inline)", |b| { + let mut stream = RlpStream::new_list(17); + for _ in 0..17 { + stream.append(&[&H256::random().as_bytes(), H256::random().as_bytes()].concat()); + } + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data) }); + }); + + c.bench_function("decode empty data", |b| { + let mut stream = RlpStream::new(); + stream.append_empty_data(); + let data = stream.out(); + b.iter(|| { RlpNodeCodec::decode(&data)}); + }); +} + +criterion_group!(benches, decoding); +criterion_main!(benches); diff --git a/util/patricia-trie-ethereum/src/lib.rs b/util/patricia-trie-ethereum/src/lib.rs index ab44f4e837..6c54434f3b 100644 --- a/util/patricia-trie-ethereum/src/lib.rs +++ b/util/patricia-trie-ethereum/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -35,6 +35,17 @@ use rlp::DecoderError; /// Convenience type alias to instantiate a Keccak-flavoured `RlpNodeCodec` pub type RlpCodec = RlpNodeCodec; +#[derive(Clone, Default)] +/// Defines the working of a particular flavour of trie: +/// how keys are hashed, how values are encoded, does it use extension nodes or not. +pub struct Layout; + +impl trie_db::TrieLayout for Layout { + const USE_EXTENSION: bool = true; + type Hash = keccak_hasher::KeccakHasher; + type Codec = RlpNodeCodec; +} + /// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieDB` /// /// Use it as a `Trie` trait object. You can use `db()` to get the backing database object. @@ -63,20 +74,20 @@ pub type RlpCodec = RlpNodeCodec; /// /// fn main() { /// let mut memdb = journaldb::new_memory_db(); -/// let mut root = H256::new(); +/// let mut root = H256::zero(); /// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap(); /// let t = TrieDB::new(&memdb, &root).unwrap(); /// assert!(t.contains(b"foo").unwrap()); -/// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar")); +/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar".to_vec()); /// } /// ``` -pub type TrieDB<'db> = trie::TrieDB<'db, KeccakHasher, RlpCodec>; +pub type TrieDB<'db> = trie::TrieDB<'db, Layout>; /// Convenience type alias to instantiate a Keccak/Rlp-flavoured `SecTrieDB` -pub type SecTrieDB<'db> = trie::SecTrieDB<'db, KeccakHasher, RlpCodec>; +pub type SecTrieDB<'db> = trie::SecTrieDB<'db, Layout>; /// Convenience type alias to instantiate a Keccak/Rlp-flavoured `FatDB` -pub type FatDB<'db> = trie::FatDB<'db, KeccakHasher, RlpCodec>; +pub type FatDB<'db> = trie::FatDB<'db, Layout>; /// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieDBMut` /// @@ -102,34 +113,78 @@ pub type FatDB<'db> = trie::FatDB<'db, KeccakHasher, RlpCodec>; /// use memory_db::*; /// use ethereum_types::H256; /// use elastic_array::ElasticArray128; +/// use trie::Trie; /// /// type DBValue = ElasticArray128; /// /// fn main() { /// let mut memdb = journaldb::new_memory_db(); -/// let mut root = H256::new(); +/// let mut root = H256::zero(); /// let mut t = TrieDBMut::new(&mut memdb, &mut root); /// assert!(t.is_empty()); /// assert_eq!(*t.root(), KECCAK_NULL_RLP); /// t.insert(b"foo", b"bar").unwrap(); /// assert!(t.contains(b"foo").unwrap()); -/// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar")); +/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar".to_vec()); /// t.remove(b"foo").unwrap(); /// assert!(!t.contains(b"foo").unwrap()); /// } /// ``` -pub type TrieDBMut<'db> = trie::TrieDBMut<'db, KeccakHasher, RlpCodec>; +pub type TrieDBMut<'db> = trie::TrieDBMut<'db, Layout>; /// Convenience type alias to instantiate a Keccak/Rlp-flavoured `SecTrieDBMut` -pub type SecTrieDBMut<'db> = trie::SecTrieDBMut<'db, KeccakHasher, RlpCodec>; +pub type SecTrieDBMut<'db> = trie::SecTrieDBMut<'db, Layout>; /// Convenience type alias to instantiate a Keccak/Rlp-flavoured `FatDBMut` -pub type FatDBMut<'db> = trie::FatDBMut<'db, KeccakHasher, RlpCodec>; +pub type FatDBMut<'db> = trie::FatDBMut<'db, Layout>; /// Convenience type alias to instantiate a Keccak/Rlp-flavoured `TrieFactory` -pub type TrieFactory = trie::TrieFactory; +pub type TrieFactory = trie::TrieFactory; /// Convenience type alias for Keccak/Rlp flavoured trie errors pub type TrieError = trie::TrieError; /// Convenience type alias for Keccak/Rlp flavoured trie results pub type Result = trie::Result; + +#[cfg(test)] +mod tests { + use ethereum_types::H256; + use trie::Trie; + + use crate::{TrieDB, TrieDBMut, trie::TrieMut}; + + #[test] + fn test_inline_encoding_branch() { + let mut memdb = journaldb::new_memory_db(); + let mut root = H256::zero(); + { + let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + triedbmut.insert(b"foo", b"bar").unwrap(); + triedbmut.insert(b"fog", b"b").unwrap(); + triedbmut.insert(b"fot", &vec![0u8;33][..]).unwrap(); + } + let t = TrieDB::new(&memdb, &root).unwrap(); + assert!(t.contains(b"foo").unwrap()); + assert!(t.contains(b"fog").unwrap()); + assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar".to_vec()); + assert_eq!(t.get(b"fog").unwrap().unwrap(), b"b".to_vec()); + assert_eq!(t.get(b"fot").unwrap().unwrap(), vec![0u8;33]); + } + + #[test] + fn test_inline_encoding_extension() { + let mut memdb = journaldb::new_memory_db(); + let mut root = H256::zero(); + { + let mut triedbmut = TrieDBMut::new(&mut memdb, &mut root); + triedbmut.insert(b"foo", b"b").unwrap(); + triedbmut.insert(b"fog", b"a").unwrap(); + } + let t = TrieDB::new(&memdb, &root).unwrap(); + assert!(t.contains(b"foo").unwrap()); + assert!(t.contains(b"fog").unwrap()); + assert_eq!(t.get(b"foo").unwrap().unwrap(), b"b".to_vec()); + assert_eq!(t.get(b"fog").unwrap().unwrap(), b"a".to_vec()); + } + +} diff --git a/util/patricia-trie-ethereum/src/rlp_node_codec.rs b/util/patricia-trie-ethereum/src/rlp_node_codec.rs index ea8aea8a77..639b852ed6 100644 --- a/util/patricia-trie-ethereum/src/rlp_node_codec.rs +++ b/util/patricia-trie-ethereum/src/rlp_node_codec.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -16,13 +16,17 @@ //! `NodeCodec` implementation for Rlp -use elastic_array::ElasticArray128; use ethereum_types::H256; use hash_db::Hasher; use keccak_hasher::KeccakHasher; use rlp::{DecoderError, RlpStream, Rlp, Prototype}; use std::marker::PhantomData; -use trie::{NibbleSlice, NodeCodec, node::Node, ChildReference}; +use std::borrow::Borrow; +use std::ops::Range; +use trie::{ + NodeCodec, ChildReference, Partial, + node::{NibbleSlicePlan, NodePlan, NodeHandlePlan}, +}; /// Concrete implementation of a `NodeCodec` with Rlp encoding, generic over the `Hasher` #[derive(Default, Clone)] @@ -30,16 +34,70 @@ pub struct RlpNodeCodec {mark: PhantomData} const HASHED_NULL_NODE_BYTES : [u8;32] = [0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21]; const HASHED_NULL_NODE : H256 = H256( HASHED_NULL_NODE_BYTES ); + +/// Encode a partial value with a partial tuple as input. +fn encode_partial_iter<'a>(partial: Partial<'a>, is_leaf: bool) -> impl Iterator + 'a { + encode_partial_inner_iter((partial.0).1, partial.1.iter().map(|v| *v), (partial.0).0 > 0, is_leaf) +} + +/// Encode a partial value with an iterator as input. +fn encode_partial_from_iterator_iter<'a>( + mut partial: impl Iterator + 'a, + odd: bool, + is_leaf: bool, +) -> impl Iterator + 'a { + let first = if odd { partial.next().unwrap_or(0) } else { 0 }; + encode_partial_inner_iter(first, partial, odd, is_leaf) +} + +/// Encode a partial value with an iterator as input. +fn encode_partial_inner_iter<'a>( + first_byte: u8, + partial_remaining: impl Iterator + 'a, + odd: bool, + is_leaf: bool, +) -> impl Iterator + 'a { + let encoded_type = if is_leaf {0x20} else {0}; + let first = if odd { + 0x10 + encoded_type + first_byte + } else { + encoded_type + }; + std::iter::once(first).chain(partial_remaining) +} + +fn decode_value_range(rlp: Rlp, mut offset: usize) -> Result, DecoderError> { + let payload = rlp.payload_info()?; + offset += payload.header_len; + Ok(offset..(offset + payload.value_len)) +} + +fn decode_child_handle_plan(child_rlp: Rlp, mut offset: usize) + -> Result +{ + Ok(if child_rlp.is_data() && child_rlp.size() == H::LENGTH { + let payload = child_rlp.payload_info()?; + offset += payload.header_len; + NodeHandlePlan::Hash(offset..(offset + payload.value_len)) + } else { + NodeHandlePlan::Inline(offset..(offset + child_rlp.as_raw().len())) + }) +} + // NOTE: what we'd really like here is: // `impl NodeCodec for RlpNodeCodec where H::Out: Decodable` // but due to the current limitations of Rust const evaluation we can't // do `const HASHED_NULL_NODE: H::Out = H::Out( … … )`. Perhaps one day soon? -impl NodeCodec for RlpNodeCodec { +impl NodeCodec for RlpNodeCodec { + type Error = DecoderError; + type HashOut = ::Out; + fn hashed_null_node() -> ::Out { HASHED_NULL_NODE } - fn decode(data: &[u8]) -> ::std::result::Result { + + fn decode_plan(data: &[u8]) -> Result { let r = Rlp::new(data); match r.prototype()? { // either leaf or extension - decode first item with NibbleSlice::??? @@ -47,88 +105,175 @@ impl NodeCodec for RlpNodeCodec { // if leaf, second item is a value (is_data()) // if extension, second item is a node (either SHA3 to be looked up and // fed back into this function or inline RLP which can be fed back into this function). - Prototype::List(2) => match NibbleSlice::from_encoded(r.at(0)?.data()?) { - (slice, true) => Ok(Node::Leaf(slice, r.at(1)?.data()?)), - (slice, false) => Ok(Node::Extension(slice, r.at(1)?.as_raw())), + Prototype::List(2) => { + let (partial_rlp, mut partial_offset) = r.at_with_offset(0)?; + let partial_payload = partial_rlp.payload_info()?; + partial_offset += partial_payload.header_len; + + let (partial, is_leaf) = if partial_rlp.is_empty() { + (NibbleSlicePlan::new(partial_offset..partial_offset, 0), false) + } else { + let partial_header = partial_rlp.data()?[0]; + // check leaf bit from header. + let is_leaf = partial_header & 32 == 32; + // Check the header bit to see if we're dealing with an odd partial (only a nibble of header info) + // or an even partial (skip a full byte). + let (start, byte_offset) = if partial_header & 16 == 16 { (0, 1) } else { (1, 0) }; + let range = (partial_offset + start)..(partial_offset + partial_payload.value_len); + (NibbleSlicePlan::new(range, byte_offset), is_leaf) + }; + + let (value_rlp, value_offset) = r.at_with_offset(1)?; + Ok(if is_leaf { + let value = decode_value_range(value_rlp, value_offset)?; + NodePlan::Leaf { partial, value } + } else { + let child = decode_child_handle_plan::(value_rlp, value_offset)?; + NodePlan::Extension { partial, child } + }) }, // branch - first 16 are nodes, 17th is a value (or empty). Prototype::List(17) => { - let mut nodes = [None as Option<&[u8]>; 16]; - for i in 0..16 { - let v = r.at(i)?; - if v.is_empty() { - nodes[i] = None; - } else { - nodes[i] = Some(v.as_raw()); + let mut children = [ + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + ]; + for (i, child) in children.iter_mut().enumerate() { + let (child_rlp, child_offset) = r.at_with_offset(i)?; + if !child_rlp.is_empty() { + *child = Some( + decode_child_handle_plan::(child_rlp, child_offset)? + ); } } - Ok(Node::Branch(nodes, if r.at(16)?.is_empty() { None } else { Some(r.at(16)?.data()?) })) + let (value_rlp, value_offset) = r.at_with_offset(16)?; + let value = if value_rlp.is_empty() { + None + } else { + Some(decode_value_range(value_rlp, value_offset)?) + }; + Ok(NodePlan::Branch { value, children }) }, // an empty branch index. - Prototype::Data(0) => Ok(Node::Empty), + Prototype::Data(0) => Ok(NodePlan::Empty), // something went wrong. _ => Err(DecoderError::Custom("Rlp is not valid.")) } } - fn try_decode_hash(data: &[u8]) -> Option<::Out> { - let r = Rlp::new(data); - if r.is_data() && r.size() == KeccakHasher::LENGTH { - Some(r.as_val().expect("Hash is the correct size; qed")) - } else { - None - } - } + fn is_empty_node(data: &[u8]) -> bool { Rlp::new(data).is_empty() } - fn empty_node() -> Vec { - let mut stream = RlpStream::new(); - stream.append_empty_data(); - stream.drain() + + fn empty_node() -> &'static[u8] { + &[0x80] } - fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { + fn leaf_node(partial: Partial, value: &[u8]) -> Vec { let mut stream = RlpStream::new_list(2); - stream.append(&partial); + stream.append_iter(encode_partial_iter(partial, true)); stream.append(&value); stream.drain() } - fn ext_node(partial: &[u8], child_ref: ChildReference<::Out>) -> Vec { + fn extension_node( + partial: impl Iterator, + number_nibble: usize, + child_ref: ChildReference<::Out>, + ) -> Vec { let mut stream = RlpStream::new_list(2); - stream.append(&partial); + stream.append_iter(encode_partial_from_iterator_iter(partial, number_nibble % 2 > 0, false)); match child_ref { - ChildReference::Hash(h) => stream.append(&h), - ChildReference::Inline(inline_data, len) => { - let bytes = &AsRef::<[u8]>::as_ref(&inline_data)[..len]; + ChildReference::Hash(hash) => stream.append(&hash), + ChildReference::Inline(inline_data, length) => { + let bytes = &AsRef::<[u8]>::as_ref(&inline_data)[..length]; stream.append_raw(bytes, 1) }, }; stream.drain() } - // fn branch_node(children: I, value: Option>) -> Vec - fn branch_node(children: I, value: Option>) -> Vec - where I: IntoIterator::Out>>> - { + fn branch_node( + children: impl Iterator::Out>>>>, + maybe_value: Option<&[u8]>, + ) -> Vec { let mut stream = RlpStream::new_list(17); for child_ref in children { - match child_ref { + match child_ref.borrow() { Some(c) => match c { - ChildReference::Hash(h) => stream.append(&h), - ChildReference::Inline(inline_data, len) => { - let bytes = &AsRef::<[u8]>::as_ref(&inline_data)[..len]; + ChildReference::Hash(h) => { + stream.append(h) + }, + ChildReference::Inline(inline_data, length) => { + let bytes = &AsRef::<[u8]>::as_ref(inline_data)[..*length]; stream.append_raw(bytes, 1) }, }, None => stream.append_empty_data() }; } - if let Some(value) = value { + if let Some(value) = maybe_value { stream.append(&&*value); } else { stream.append_empty_data(); } stream.drain() } + + fn branch_node_nibbled( + _partial: impl Iterator, + _number_nibble: usize, + _children: impl Iterator::Out>>>>, + _maybe_value: Option<&[u8]>) -> Vec { + unreachable!("This codec is only used with a trie Layout that uses extension node.") + } + +} + +#[cfg(test)] +mod tests { + use trie::{NodeCodec, node::{Node, NodeHandle}, NibbleSlice}; + use rlp::RlpStream; + use RlpNodeCodec; + + #[test] + fn decode_leaf() { + let mut stream = RlpStream::new_list(2); + stream.append(&"cat").append(&"dog"); + let data = stream.out(); + let r = RlpNodeCodec::decode(&data); + assert!(r.is_ok()); + // "c" & 16 != 16 => `start` == 1 + let cat_nib = NibbleSlice::new(&b"at"[..]); + assert_eq!(r.unwrap(), Node::Leaf(cat_nib, &b"dog"[..])); + } + + #[test] + fn decode_ext() { + let mut stream = RlpStream::new_list(2); + let payload = vec![0x1, 0x2, 0x3u8]; + stream.append(&"").append(&payload); + let data = stream.out(); + let decoded = RlpNodeCodec::decode(&data); + + assert!(decoded.is_ok()); + assert_eq!( + decoded.unwrap(), + Node::Extension( + NibbleSlice::new(&[]), + NodeHandle::Inline(&[0x80 + 0x3, 0x1, 0x2, 0x3]) + ) + ); + } + + #[test] + fn decode_empty_data() { + let mut stream = RlpStream::new(); + stream.append_empty_data(); + let data = stream.out(); + assert_eq!( + RlpNodeCodec::decode(&data), + Ok(Node::Empty), + ); + } } diff --git a/util/registrar/Cargo.toml b/util/registrar/Cargo.toml index 6f526af28c..1cbd5cbeca 100644 --- a/util/registrar/Cargo.toml +++ b/util/registrar/Cargo.toml @@ -6,8 +6,9 @@ license = "GPL-3.0" authors = ["Parity Technologies "] [dependencies] -futures = "0.1" -ethabi = "6.0" -ethabi-derive = "6.0" -ethabi-contract = "6.0" -keccak-hash = "0.1" +call-contract = { package = "ethcore-call-contract", path = "../../ethcore/call-contract" } +ethabi = "9.0.1" +ethabi-derive = "9.0.1" +ethabi-contract = "9.0.0" +keccak-hash = "0.4.0" +types = { path = "../../ethcore/types", package = "common-types" } diff --git a/util/registrar/src/lib.rs b/util/registrar/src/lib.rs index d07e17c1bc..ccdec154ef 100644 --- a/util/registrar/src/lib.rs +++ b/util/registrar/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,14 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -extern crate futures; +extern crate call_contract; extern crate ethabi; +extern crate ethabi_derive; extern crate keccak_hash; +extern crate types; -#[macro_use] -extern crate ethabi_derive; #[macro_use] extern crate ethabi_contract; mod registrar; -pub use registrar::{Registrar, RegistrarClient, Synchronous, Asynchronous}; +pub use registrar::RegistrarClient; diff --git a/util/registrar/src/registrar.rs b/util/registrar/src/registrar.rs index cd6d35660a..5d247b47c3 100644 --- a/util/registrar/src/registrar.rs +++ b/util/registrar/src/registrar.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,59 +14,42 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use futures::{Future, future, IntoFuture}; -use ethabi::{Address, Bytes}; -use std::sync::Arc; +use call_contract::CallContract; +use ethabi::Address; use keccak_hash::keccak; +use types::ids::BlockId; use_contract!(registrar, "res/registrar.json"); // Maps a domain name to an Ethereum address const DNS_A_RECORD: &'static str = "A"; -pub type Asynchronous = Box + Send>; -pub type Synchronous = Result; - -/// Registrar is dedicated interface to access the registrar contract -/// which in turn generates an address when a client requests one -pub struct Registrar { - client: Arc>, -} +/// Registrar contract interface +pub trait RegistrarClient: CallContract + Send + Sync { + /// Get address of the registrar itself + fn registrar_address(&self) -> Option
; -impl Registrar { - /// Registrar constructor - pub fn new(client: Arc>) -> Self { - Self { - client: client, - } - } + /// Get address from registrar for the specified key. + fn get_address(&self, key: &str, block: BlockId) -> Result, String> { + use registrar::registrar::functions::get_address::{encode_input, decode_output}; - /// Generate an address for the given key - pub fn get_address<'a>(&self, key: &'a str) -> Box + Send> { - // Address of the registrar itself - let registrar_address = match self.client.registrar_address() { - Ok(a) => a, - Err(e) => return Box::new(future::err(e)), + let registrar_address = match self.registrar_address() { + Some(address) => address, + None => return Err("Registrar address not defined.".to_owned()) }; let hashed_key: [u8; 32] = keccak(key).into(); - let id = registrar::functions::get_address::encode_input(hashed_key, DNS_A_RECORD); - - let future = self.client.call_contract(registrar_address, id) - .and_then(move |address| registrar::functions::get_address::decode_output(&address).map_err(|e| e.to_string())); + let id = encode_input(hashed_key, DNS_A_RECORD); - Box::new(future) + let address_bytes = self.call_contract(block, registrar_address, id)?; + if address_bytes.is_empty() { + return Ok(None) + } + let address = decode_output(&address_bytes).map_err(|e| e.to_string())?; + if address.is_zero() { + Ok(None) + } else { + Ok(Some(address)) + } } } - -/// Registrar contract interface -/// Should execute transaction using current blockchain state. -pub trait RegistrarClient: Send + Sync { - /// Specifies synchronous or asynchronous communication - type Call: IntoFuture; - - /// Get registrar address - fn registrar_address(&self) -> Result; - /// Call Contract - fn call_contract(&self, address: Address, data: Bytes) -> Self::Call; -} diff --git a/util/rlp-compress/Cargo.toml b/util/rlp-compress/Cargo.toml index 4bd34bce13..0bcb092a18 100644 --- a/util/rlp-compress/Cargo.toml +++ b/util/rlp-compress/Cargo.toml @@ -4,6 +4,6 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] -rlp = { version = "0.3.0", features = ["ethereum"] } +rlp = "0.4.0" elastic-array = "0.10" lazy_static = "1.0" diff --git a/util/rlp-compress/src/common.rs b/util/rlp-compress/src/common.rs index 81767955b6..b4a5264122 100644 --- a/util/rlp-compress/src/common.rs +++ b/util/rlp-compress/src/common.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/util/rlp-compress/src/lib.rs b/util/rlp-compress/src/lib.rs index 48620ed239..9bfc015c4c 100644 --- a/util/rlp-compress/src/lib.rs +++ b/util/rlp-compress/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 or the MIT license @@ -40,7 +40,7 @@ pub trait Decompressor { } /// Call this function to compress rlp. -pub fn compress(c: &[u8], swapper: &Compressor) -> ElasticArray1024 { +pub fn compress(c: &[u8], swapper: &dyn Compressor) -> ElasticArray1024 { let rlp = Rlp::new(c); if rlp.is_data() { ElasticArray1024::from_slice(swapper.compressed(rlp.as_raw()).unwrap_or_else(|| rlp.as_raw())) @@ -50,7 +50,7 @@ pub fn compress(c: &[u8], swapper: &Compressor) -> ElasticArray1024 { } /// Call this function to decompress rlp. -pub fn decompress(c: &[u8], swapper: &Decompressor) -> ElasticArray1024 { +pub fn decompress(c: &[u8], swapper: &dyn Decompressor) -> ElasticArray1024 { let rlp = Rlp::new(c); if rlp.is_data() { ElasticArray1024::from_slice(swapper.decompressed(rlp.as_raw()).unwrap_or_else(|| rlp.as_raw())) diff --git a/util/rlp-compress/tests/compress.rs b/util/rlp-compress/tests/compress.rs index 6c5a023746..a65350f326 100644 --- a/util/rlp-compress/tests/compress.rs +++ b/util/rlp-compress/tests/compress.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/rlp-derive/Cargo.toml b/util/rlp-derive/Cargo.toml index 2087f75243..878b801674 100644 --- a/util/rlp-derive/Cargo.toml +++ b/util/rlp-derive/Cargo.toml @@ -1,16 +1,17 @@ [package] name = "rlp_derive" version = "0.1.0" -authors = ["debris "] +authors = ["Parity Technologies "] +edition = "2018" [lib] name = "rlp_derive" proc-macro = true [dependencies] -syn = "0.15" -quote = "0.6" -proc-macro2 = "0.4" +syn = "1.0.14" +quote = "1.0.2" +proc-macro2 = "1.0.8" [dev-dependencies] -rlp = { version = "0.3.0", features = ["ethereum"] } +rlp = "0.4.0" diff --git a/util/rlp-derive/src/de.rs b/util/rlp-derive/src/de.rs index 234bcbcb84..3ae4b3aff2 100644 --- a/util/rlp-derive/src/de.rs +++ b/util/rlp-derive/src/de.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use syn; -use proc_macro2::{TokenStream, Span}; +use proc_macro2::TokenStream; +use quote::quote; struct ParseQuotes { single: TokenStream, @@ -45,10 +45,19 @@ pub fn impl_decodable(ast: &syn::DeriveInput) -> TokenStream { _ => panic!("#[derive(RlpDecodable)] is only defined for structs."), }; - let stmts: Vec<_> = body.fields.iter().enumerate().map(decodable_field_map).collect(); + let mut default_attribute_encountered = false; + let stmts: Vec<_> = body + .fields + .iter() + .enumerate() + .map(|(i, field)| decodable_field( + i, + field, + decodable_parse_quotes(), + &mut default_attribute_encountered, + )).collect(); let name = &ast.ident; - let dummy_const = syn::Ident::new(&format!("_IMPL_RLP_DECODABLE_FOR_{}", name), Span::call_site()); let impl_block = quote! { impl rlp::Decodable for #name { fn decode(rlp: &rlp::Rlp) -> Result { @@ -62,8 +71,7 @@ pub fn impl_decodable(ast: &syn::DeriveInput) -> TokenStream { }; quote! { - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const #dummy_const: () = { + const _: () = { extern crate rlp; #impl_block }; @@ -80,7 +88,13 @@ pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> TokenStream { let fields: Vec<_> = body.fields.iter().collect(); if fields.len() == 1 { let field = fields.first().expect("fields.len() == 1; qed"); - decodable_field(0, field, decodable_wrapper_parse_quotes()) + let mut default_attribute_encountered = false; + decodable_field( + 0, + field, + decodable_wrapper_parse_quotes(), + &mut default_attribute_encountered, + ) } else { panic!("#[derive(RlpEncodableWrapper)] is only defined for structs with one field.") } @@ -88,7 +102,6 @@ pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; - let dummy_const = syn::Ident::new(&format!("_IMPL_RLP_DECODABLE_FOR_{}", name), Span::call_site()); let impl_block = quote! { impl rlp::Decodable for #name { fn decode(rlp: &rlp::Rlp) -> Result { @@ -102,19 +115,19 @@ pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> TokenStream { }; quote! { - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const #dummy_const: () = { + const _: () = { extern crate rlp; #impl_block }; } } -fn decodable_field_map(tuple: (usize, &syn::Field)) -> TokenStream { - decodable_field(tuple.0, tuple.1, decodable_parse_quotes()) -} - -fn decodable_field(index: usize, field: &syn::Field, quotes: ParseQuotes) -> TokenStream { +fn decodable_field( + index: usize, + field: &syn::Field, + quotes: ParseQuotes, + default_attribute_encountered: &mut bool, +) -> TokenStream { let id = match field.ident { Some(ref ident) => quote! { #ident }, None => { @@ -123,28 +136,58 @@ fn decodable_field(index: usize, field: &syn::Field, quotes: ParseQuotes) -> Tok } }; + let index = index - *default_attribute_encountered as usize; let index = quote! { #index }; let single = quotes.single; let list = quotes.list; + let attributes = &field.attrs; + let default = if let Some(attr) = attributes.iter().find(|attr| attr.path.is_ident("rlp")) { + if *default_attribute_encountered { + panic!("only 1 #[rlp(default)] attribute is allowed in a struct") + } + match attr.parse_args() { + Ok(proc_macro2::TokenTree::Ident(ident)) if ident.to_string() == "default" => {}, + _ => panic!("only #[rlp(default)] attribute is supported"), + } + *default_attribute_encountered = true; + true + } else { + false + }; + match field.ty { syn::Type::Path(ref path) => { - let ident = &path.path.segments.first().expect("there must be at least 1 segment").value().ident; - if &ident.to_string() == "Vec" { + let ident = &path + .path + .segments + .first() + .expect("there must be at least 1 segment") + .ident; + let ident_type = ident.to_string(); + if &ident_type == "Vec" { if quotes.takes_index { - quote! { #id: #list(#index)?, } + if default { + quote! { #id: #list(#index).unwrap_or_default(), } + } else { + quote! { #id: #list(#index)?, } + } } else { quote! { #id: #list()?, } } } else { if quotes.takes_index { - quote! { #id: #single(#index)?, } + if default { + quote! { #id: #single(#index).unwrap_or_default(), } + } else { + quote! { #id: #single(#index)?, } + } } else { quote! { #id: #single()?, } } } - }, + } _ => panic!("rlp_derive not supported"), } } diff --git a/util/rlp-derive/src/en.rs b/util/rlp-derive/src/en.rs index 95e5b9142d..32905456a8 100644 --- a/util/rlp-derive/src/en.rs +++ b/util/rlp-derive/src/en.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use syn; -use proc_macro2::{TokenStream, Span}; +use proc_macro2::TokenStream; +use quote::quote; pub fn impl_encodable(ast: &syn::DeriveInput) -> TokenStream { let body = match ast.data { @@ -23,12 +23,16 @@ pub fn impl_encodable(ast: &syn::DeriveInput) -> TokenStream { _ => panic!("#[derive(RlpEncodable)] is only defined for structs."), }; - let stmts: Vec<_> = body.fields.iter().enumerate().map(encodable_field_map).collect(); + let stmts: Vec<_> = body + .fields + .iter() + .enumerate() + .map(|(i, field)| encodable_field(i, field)) + .collect(); let name = &ast.ident; let stmts_len = stmts.len(); let stmts_len = quote! { #stmts_len }; - let dummy_const = syn::Ident::new(&format!("_IMPL_RLP_ENCODABLE_FOR_{}", name), Span::call_site()); let impl_block = quote! { impl rlp::Encodable for #name { fn rlp_append(&self, stream: &mut rlp::RlpStream) { @@ -39,8 +43,7 @@ pub fn impl_encodable(ast: &syn::DeriveInput) -> TokenStream { }; quote! { - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const #dummy_const: () = { + const _: () = { extern crate rlp; #impl_block }; @@ -65,7 +68,6 @@ pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> TokenStream { let name = &ast.ident; - let dummy_const = syn::Ident::new(&format!("_IMPL_RLP_ENCODABLE_FOR_{}", name), Span::call_site()); let impl_block = quote! { impl rlp::Encodable for #name { fn rlp_append(&self, stream: &mut rlp::RlpStream) { @@ -75,18 +77,13 @@ pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> TokenStream { }; quote! { - #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const #dummy_const: () = { + const _: () = { extern crate rlp; #impl_block }; } } -fn encodable_field_map(tuple: (usize, &syn::Field)) -> TokenStream { - encodable_field(tuple.0, tuple.1) -} - fn encodable_field(index: usize, field: &syn::Field) -> TokenStream { let ident = match field.ident { Some(ref ident) => quote! { #ident }, @@ -100,24 +97,38 @@ fn encodable_field(index: usize, field: &syn::Field) -> TokenStream { match field.ty { syn::Type::Path(ref path) => { - let top_segment = path.path.segments.first().expect("there must be at least 1 segment"); - let ident = &top_segment.value().ident; + let top_segment = path + .path + .segments + .first() + .expect("there must be at least 1 segment"); + let ident = &top_segment.ident; if &ident.to_string() == "Vec" { - let inner_ident = match top_segment.value().arguments { + let inner_ident = match top_segment.arguments { syn::PathArguments::AngleBracketed(ref angle) => { - let ty = angle.args.first().expect("Vec has only one angle bracketed type; qed"); - match **ty.value() { - syn::GenericArgument::Type(syn::Type::Path(ref path)) => &path.path.segments.first().expect("there must be at least 1 segment").value().ident, + let ty = angle + .args + .first() + .expect("Vec has only one angle bracketed type; qed"); + match *ty { + syn::GenericArgument::Type(syn::Type::Path(ref path)) => { + &path + .path + .segments + .first() + .expect("there must be at least 1 segment") + .ident + } _ => panic!("rlp_derive not supported"), } - }, + } _ => unreachable!("Vec has only one angle bracketed type; qed"), }; quote! { stream.append_list::<#inner_ident, _>(&#id); } } else { quote! { stream.append(&#id); } } - }, + } _ => panic!("rlp_derive not supported"), } } diff --git a/util/rlp-derive/src/lib.rs b/util/rlp-derive/src/lib.rs index 0f5d442f44..3c9799e2ae 100644 --- a/util/rlp-derive/src/lib.rs +++ b/util/rlp-derive/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -15,19 +15,15 @@ // along with Parity Ethereum. If not, see . extern crate proc_macro; -extern crate proc_macro2; -extern crate syn; -#[macro_use] -extern crate quote; -mod en; mod de; +mod en; -use proc_macro::TokenStream; -use en::{impl_encodable, impl_encodable_wrapper}; use de::{impl_decodable, impl_decodable_wrapper}; +use en::{impl_encodable, impl_encodable_wrapper}; +use proc_macro::TokenStream; -#[proc_macro_derive(RlpEncodable)] +#[proc_macro_derive(RlpEncodable, attributes(rlp))] pub fn encodable(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); let gen = impl_encodable(&ast); @@ -41,7 +37,7 @@ pub fn encodable_wrapper(input: TokenStream) -> TokenStream { gen.into() } -#[proc_macro_derive(RlpDecodable)] +#[proc_macro_derive(RlpDecodable, attributes(rlp))] pub fn decodable(input: TokenStream) -> TokenStream { let ast = syn::parse(input).unwrap(); let gen = impl_decodable(&ast); diff --git a/util/rlp-derive/tests/rlp.rs b/util/rlp-derive/tests/rlp.rs index a6819ba4bd..adf7376675 100644 --- a/util/rlp-derive/tests/rlp.rs +++ b/util/rlp-derive/tests/rlp.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -14,11 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -extern crate rlp; -#[macro_use] -extern crate rlp_derive; - -use rlp::{encode, decode}; +use rlp::{decode, encode}; +use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper}; #[derive(Debug, PartialEq, RlpEncodable, RlpDecodable)] struct Foo { @@ -32,9 +29,7 @@ struct FooWrapper { #[test] fn test_encode_foo() { - let foo = Foo { - a: "cat".into(), - }; + let foo = Foo { a: "cat".into() }; let expected = vec![0xc4, 0x83, b'c', b'a', b't']; let out = encode(&foo); @@ -46,9 +41,7 @@ fn test_encode_foo() { #[test] fn test_encode_foo_wrapper() { - let foo = FooWrapper { - a: "cat".into(), - }; + let foo = FooWrapper { a: "cat".into() }; let expected = vec![0x83, b'c', b'a', b't']; let out = encode(&foo); @@ -57,3 +50,30 @@ fn test_encode_foo_wrapper() { let decoded = decode(&expected).expect("decode failure"); assert_eq!(foo, decoded); } + +#[test] +fn test_encode_foo_default() { + #[derive(Debug, PartialEq, RlpEncodable, RlpDecodable)] + struct FooDefault { + a: String, + /// It works with other attributes. + #[rlp(default)] + b: Option>, + } + + let attack_of = String::from("clones"); + let foo = Foo { a: attack_of.clone() }; + + let expected = vec![0xc7, 0x86, b'c', b'l', b'o', b'n', b'e', b's']; + let out = encode(&foo); + assert_eq!(out, expected); + + let foo_default = FooDefault { a: attack_of.clone(), b: None }; + + let decoded = decode(&expected).expect("default failure"); + assert_eq!(foo_default, decoded); + + let foo_some = FooDefault { a: attack_of.clone(), b: Some(vec![1, 2, 3]) }; + let out = encode(&foo_some); + assert_eq!(decode(&out), Ok(foo_some)); +} diff --git a/util/runtime/Cargo.toml b/util/runtime/Cargo.toml index 084fef55a7..d22106c492 100644 --- a/util/runtime/Cargo.toml +++ b/util/runtime/Cargo.toml @@ -8,4 +8,4 @@ authors = ["Parity Technologies "] [dependencies] futures = "0.1" -tokio = "~0.1.9" +tokio = "0.1.22" diff --git a/util/runtime/src/lib.rs b/util/runtime/src/lib.rs index 0c78c7f4a3..568b2771fd 100644 --- a/util/runtime/src/lib.rs +++ b/util/runtime/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/stats/src/lib.rs b/util/stats/src/lib.rs index e1ceb4fe84..9b42b3946f 100644 --- a/util/stats/src/lib.rs +++ b/util/stats/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/time-utils/src/lib.rs b/util/time-utils/src/lib.rs index 0bfc3bb982..85aad4afc0 100644 --- a/util/time-utils/src/lib.rs +++ b/util/time-utils/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/triehash-ethereum/Cargo.toml b/util/triehash-ethereum/Cargo.toml index 5b63642975..4fd01cbd0b 100644 --- a/util/triehash-ethereum/Cargo.toml +++ b/util/triehash-ethereum/Cargo.toml @@ -6,6 +6,6 @@ description = "Trie-root helpers, ethereum style" license = "GPL-3.0" [dependencies] -triehash = { version = "0.4.0", features = ["ethereum"] } -ethereum-types = "0.4" +triehash = "0.8.2" +ethereum-types = "0.8.0" keccak-hasher = { path = "../keccak-hasher" } diff --git a/util/triehash-ethereum/src/lib.rs b/util/triehash-ethereum/src/lib.rs index 696ed61aba..27b3781473 100644 --- a/util/triehash-ethereum/src/lib.rs +++ b/util/triehash-ethereum/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -54,15 +54,16 @@ where #[cfg(test)] mod tests { - use super::{trie_root, sec_trie_root, ordered_trie_root}; + use super::{trie_root, sec_trie_root, ordered_trie_root, H256}; use triehash; use keccak_hasher::KeccakHasher; + use std::str::FromStr; #[test] fn simple_test() { assert_eq!(trie_root(vec![ (b"A", b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" as &[u8]) - ]), "d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab".into()); + ]), H256::from_str("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab").unwrap()); } #[test] diff --git a/util/unexpected/src/lib.rs b/util/unexpected/src/lib.rs index 9a1a709be4..e720f1e1e8 100644 --- a/util/unexpected/src/lib.rs +++ b/util/unexpected/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify diff --git a/util/version/Cargo.toml b/util/version/Cargo.toml index b824894bfb..aae4bddbb7 100644 --- a/util/version/Cargo.toml +++ b/util/version/Cargo.toml @@ -3,12 +3,12 @@ [package] name = "parity-version" # NOTE: this value is used for Parity Ethereum version string (via env CARGO_PKG_VERSION) -version = "2.5.9" +version = "2.7.2" authors = ["Parity Technologies "] build = "build.rs" [package.metadata] -# This versions track. Should be changed to `stable` or `beta` when on respective branches. +# This versions track. Should be changed to `stable` when on respective branches. # Used by auto-updater and for Parity version string. track = "stable" @@ -16,18 +16,18 @@ track = "stable" # Latest supported fork blocks. # Indicates a critical release in this track (i.e. consensus issue). [package.metadata.networks] -foundation = { forkBlock = 7280000, critical = false } -ropsten = { forkBlock = 4939394, critical = false } -kovan = { forkBlock = 10255201, critical = false } -goerli = { forkBlock = 0, critical = false } +foundation = { forkBlock = 9069000, critical = false } +ropsten = { forkBlock = 6485846, critical = false } +kovan = { forkBlock = 14111141, critical = false } +goerli = { forkBlock = 1561651, critical = false } [dependencies] parity-bytes = "0.1" -rlp = { version = "0.3.0", features = ["ethereum"] } +rlp = "0.4.0" target_info = "0.1" [build-dependencies] -vergen = "0.1" +vergen = "3.0.4" rustc_version = "0.2" toml = "0.4" diff --git a/util/version/build.rs b/util/version/build.rs index 3ee37fbca6..5ee2ef46db 100644 --- a/util/version/build.rs +++ b/util/version/build.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -22,12 +22,17 @@ use std::env; use std::fs::File; use std::io::Write; use std::path::Path; -use vergen::{vergen, OutputFns}; +use vergen::{ConstantsFlags, generate_cargo_keys}; const ERROR_MSG: &'static str = "Failed to generate metadata files"; fn main() { - vergen(OutputFns::all()).expect(ERROR_MSG); + let vergen_flags = ConstantsFlags::COMMIT_DATE | + ConstantsFlags::SHA | + ConstantsFlags::SHA_SHORT | + ConstantsFlags::TARGET_TRIPLE | + ConstantsFlags::REBUILD_ON_HEAD_CHANGE; + generate_cargo_keys(vergen_flags).expect(ERROR_MSG); let version = rustc_version::version().expect(ERROR_MSG); diff --git a/util/version/src/lib.rs b/util/version/src/lib.rs index 00f9f4550c..6b0ac6b73d 100644 --- a/util/version/src/lib.rs +++ b/util/version/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// Copyright 2015-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Ethereum. // Parity Ethereum is free software: you can redistribute it and/or modify @@ -24,18 +24,13 @@ use target_info::Target; use bytes::Bytes; use rlp::RlpStream; -mod vergen { - #![allow(unused)] - include!(concat!(env!("OUT_DIR"), "/version.rs")); -} - mod generated { include!(concat!(env!("OUT_DIR"), "/meta.rs")); } #[cfg(feature = "final")] const THIS_TRACK: &'static str = generated::TRACK; -// ^^^ should be reset in Cargo.toml to "stable" or "beta" according to the release branch. +// ^^^ should be reset in Cargo.toml to "stable" according to the release branch. #[cfg(not(feature = "final"))] const THIS_TRACK: &'static str = "unstable"; @@ -43,18 +38,21 @@ const THIS_TRACK: &'static str = "unstable"; /// Get the platform identifier. pub fn platform() -> String { - let env = Target::env(); - let env_dash = if env.is_empty() { "" } else { "-" }; - format!("{}-{}{}{}", Target::arch(), Target::os(), env_dash, env) + format!("{}", env!("VERGEN_TARGET_TRIPLE")) } /// Get the standard version string for this software. pub fn version() -> String { - let sha3 = vergen::short_sha(); - let sha3_dash = if sha3.is_empty() { "" } else { "-" }; - let commit_date = vergen::commit_date().replace("-", ""); - let date_dash = if commit_date.is_empty() { "" } else { "-" }; - format!("Parity-Ethereum/v{}-{}{}{}{}{}/{}/rustc{}", env!("CARGO_PKG_VERSION"), THIS_TRACK, sha3_dash, sha3, date_dash, commit_date, platform(), generated::rustc_version()) + let commit_date = format!("{}", env!("VERGEN_COMMIT_DATE")).replace("-", ""); + format!( + "Parity-Ethereum/v{}-{}-{}-{}/{}/rustc{}", + env!("CARGO_PKG_VERSION"), + THIS_TRACK, + env!("VERGEN_SHA_SHORT"), + commit_date, + platform(), + generated::rustc_version(), + ) } /// Get the standard version data for this software. @@ -73,5 +71,5 @@ pub fn version_data() -> Bytes { /// Provide raw information on the package. pub fn raw_package_info() -> (&'static str, &'static str, &'static str) { - (THIS_TRACK, env!["CARGO_PKG_VERSION"], vergen::sha()) + (THIS_TRACK, env!["CARGO_PKG_VERSION"], env!["VERGEN_SHA"]) } diff --git a/whisper/Cargo.toml b/whisper/Cargo.toml deleted file mode 100644 index bc29051b23..0000000000 --- a/whisper/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -description = "Parity Ethereum Whisper Protocol Implementation" -name = "parity-whisper" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -bitflags = "0.9" -byteorder = "1.0.0" -ethereum-types = "0.4" -ethcore-network = { path = "../util/network" } -parity-crypto = "0.3.0" -ethkey = { path = "../accounts/ethkey" } -hex = "0.2" -log = "0.4" -memzero = { path = "../util/memzero" } -ordered-float = "0.5" -parking_lot = "0.7" -rand = "0.4" -rlp = { version = "0.3.0", features = ["ethereum"] } -serde = "1.0" -serde_derive = "1.0" -serde_json = "1.0" -slab = "0.3" -smallvec = "0.6" -tiny-keccak = "1.4" -time-utils = { path = "../util/time-utils" } - -jsonrpc-core = "10.0.1" -jsonrpc-derive = "10.0.2" -jsonrpc-pubsub = "10.0.1" diff --git a/whisper/README.md b/whisper/README.md deleted file mode 100644 index b64c0244f9..0000000000 --- a/whisper/README.md +++ /dev/null @@ -1,33 +0,0 @@ -## Whisper - -Implementation of Whisper based on the Whisper-v2 PoC. - -### Usage - -``` -Parity Whisper-v2 CLI. - Copyright 2015-2019 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 P2P port to use [default: random]. - -a, --address ADDRESS Specify which P2P address to use [default: 127.0.0.1]. - -s, --secret KEYFILE Specify which file contains the key to generate the enode. - -P, --rpc-port PORT Specify which RPC port to use [default: 8545]. - -A, --rpc-address ADDRESS Specify which RPC 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. -``` - -## Parity Ethereum toolchain -_This project is a part of the Parity Ethereum toolchain._ - -- [evmbin](https://github.com/paritytech/parity-ethereum/blob/master/evmbin/) - EVM implementation for Parity Ethereum. -- [ethabi](https://github.com/paritytech/ethabi) - Parity Ethereum function calls encoding. -- [ethstore](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethstore) - Parity Ethereum key management. -- [ethkey](https://github.com/paritytech/parity-ethereum/blob/master/accounts/ethkey) - Parity Ethereum keys generator. -- [whisper](https://github.com/paritytech/parity-ethereum/blob/master/whisper/) - Implementation of Whisper-v2 PoC. diff --git a/whisper/cli/Cargo.toml b/whisper/cli/Cargo.toml deleted file mode 100644 index 5ed38e47d7..0000000000 --- a/whisper/cli/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "whisper-cli" -description = "Whisper command line interface" -version = "0.1.0" -authors = ["Parity Technologies "] -license = "GPL-3.0" - -[dependencies] -docopt = "1.0" -env_logger = "0.5" -ethcore-network = { path = "../../util/network" } -ethcore-network-devp2p = { path = "../../util/network-devp2p" } -jsonrpc-core = "10.0.1" -jsonrpc-http-server = "10.0.1" -jsonrpc-pubsub = "10.0.1" -log = "0.4" -panic_hook = { path = "../../util/panic-hook" } -parity-whisper = { path = "../" } -serde = "1.0" -serde_derive = "1.0" -ethkey = { path = "../../accounts/ethkey" } -rustc-hex = "2.0" - -[[bin]] -name = "whisper" -path = "src/main.rs" diff --git a/whisper/cli/src/main.rs b/whisper/cli/src/main.rs deleted file mode 100644 index 41d9e80e42..0000000000 --- a/whisper/cli/src/main.rs +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! 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 env_logger; -extern crate ethcore_network as net; -extern crate ethcore_network_devp2p as devp2p; -extern crate panic_hook; -extern crate parity_whisper as whisper; -extern crate serde; - -extern crate jsonrpc_core; -extern crate jsonrpc_pubsub; -extern crate jsonrpc_http_server; -extern crate ethkey; -extern crate rustc_hex; - -#[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}; -use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr}; -use std::str::FromStr; -use ethkey::Secret; -use rustc_hex::FromHex; - -const POOL_UNIT: usize = 1024 * 1024; -const USAGE: &'static str = r#" -Parity Whisper-v2 CLI. - Copyright 2015-2019 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 P2P port to use [default: random]. - -a, --address ADDRESS Specify which P2P address to use [default: 127.0.0.1]. - -s, --secret KEYFILE Specify which file contains the key to generate the enode. - -P, --rpc-port PORT Specify which RPC port to use [default: 8545]. - -A, --rpc-address ADDRESS Specify which RPC 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_rpc_port: String, - flag_rpc_address: String, - flag_log: String, - flag_secret: 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), - FromHex(rustc_hex::FromHexError), - ParseInt(std::num::ParseIntError), -} - -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: rustc_hex::FromHexError) -> Self { - Error::FromHex(err) - } -} - -impl From for Error { - fn from(err: std::num::ParseIntError) -> Self { - Error::ParseInt(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::ParseInt(ref e) => write!(f, "Invalid port: {}", e), - Error::FromHex(ref e) => write!(f, "Error deciphering key: {}", e), - } - } -} - -fn main() { - panic_hook::set_abort(); - - 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 rpc_url = format!("{}:{}", args.flag_rpc_address, args.flag_rpc_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())); - - let network_config = { - let mut cfg = net::NetworkConfiguration::new(); - let port = match args.flag_port.as_str() { - "random" => 0 as u16, - port => port.parse::()?, - - }; - let addr = Ipv4Addr::from_str(&args.flag_address[..])?; - cfg.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(addr, port))); - cfg.use_secret = match args.flag_secret.as_str() { - "" => None, - fname => { - let key_text = std::fs::read_to_string(fname)?; - let key : Vec = FromHex::from_hex(key_text.as_str())?; - Secret::from_slice(key.as_slice()) - } - }; - cfg.nat_enabled = false; - cfg - }; - - // Create network service - let network = devp2p::NetworkService::new(network_config, 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(&rpc_url.parse()?)?; - - server.wait(); - - // This will never return if the http server runs without errors - Ok(()) -} - -fn initialize_logger(log_level: String) { - env_logger::Builder::from_env(env_logger::Env::default()) - .parse(&log_level) - .init(); -} - -#[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 deleted file mode 100644 index cdc88780d4..0000000000 --- a/whisper/src/lib.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Whisper P2P messaging system as a DevP2P subprotocol, with RPC and Rust -//! interface. - -#![cfg_attr(feature = "time_checked_add", feature(time_checked_add))] - -extern crate byteorder; -extern crate parity_crypto as crypto; -extern crate ethcore_network as network; -extern crate ethereum_types; -extern crate ethkey; -extern crate hex; -extern crate memzero; -extern crate ordered_float; -extern crate parking_lot; -extern crate rand; -extern crate rlp; -extern crate serde; -extern crate slab; -extern crate smallvec; -extern crate tiny_keccak; - -extern crate jsonrpc_core; -extern crate jsonrpc_derive; -extern crate jsonrpc_pubsub; - -#[macro_use] -extern crate bitflags; - -#[macro_use] -extern crate log; - -#[macro_use] -extern crate serde_derive; - -#[cfg(not(time_checked_add))] -extern crate time_utils; - -#[cfg(test)] -extern crate serde_json; - -pub use self::message::Message; -pub use self::net::{Network, MessageHandler}; - -pub mod message; -pub mod net; -pub mod rpc; diff --git a/whisper/src/message.rs b/whisper/src/message.rs deleted file mode 100644 index c10d39700f..0000000000 --- a/whisper/src/message.rs +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Whisper message parsing, handlers, and construction. - -use std::fmt; -use std::time::{self, SystemTime, Duration, Instant}; - -use ethereum_types::{H256, H512}; -use rlp::{self, DecoderError, RlpStream, Rlp}; -use smallvec::SmallVec; -use tiny_keccak::{keccak256, Keccak}; - -#[cfg(not(time_checked_add))] -use time_utils::CheckedSystemTime; - -/// Work-factor proved. Takes 3 parameters: size of message, time to live, -/// and hash. -/// -/// Panics if size or TTL is zero. -pub fn work_factor_proved(size: u64, ttl: u64, hash: H256) -> f64 { - assert!(size != 0 && ttl != 0); - - let leading_zeros = { - let leading_bytes = hash.iter().take_while(|&&x| x == 0).count(); - let remaining_leading_bits = hash.get(leading_bytes).map_or(0, |byte| byte.leading_zeros() as usize); - (leading_bytes * 8) + remaining_leading_bits - }; - let spacetime = size as f64 * ttl as f64; - - 2.0_f64.powi(leading_zeros as i32) / spacetime -} - -/// A topic of a message. -#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Topic(pub [u8; 4]); - -impl From<[u8; 4]> for Topic { - fn from(x: [u8; 4]) -> Self { - Topic(x) - } -} - -impl Topic { - /// set up to three bits in the 64-byte bloom passed. - /// - /// this takes 3 sets of 9 bits, treating each as an index in the range - /// 0..512 into the bloom and setting the corresponding bit in the bloom to 1. - pub fn bloom_into(&self, bloom: &mut H512) { - - let data = &self.0; - for i in 0..3 { - let mut idx = data[i] as usize; - - if data[3] & (1 << i) != 0 { - idx += 256; - } - - debug_assert!(idx <= 511); - bloom[idx / 8] |= 1 << (7 - idx % 8); - } - } - - /// Get bloom for single topic. - pub fn bloom(&self) -> H512 { - let mut bloom = Default::default(); - self.bloom_into(&mut bloom); - bloom - } -} - -impl rlp::Encodable for Topic { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_value(&self.0); - } -} - -impl rlp::Decodable for Topic { - fn decode(rlp: &Rlp) -> Result { - use std::cmp; - - rlp.decoder().decode_value(|bytes| match bytes.len().cmp(&4) { - cmp::Ordering::Less => Err(DecoderError::RlpIsTooShort), - cmp::Ordering::Greater => Err(DecoderError::RlpIsTooBig), - cmp::Ordering::Equal => { - let mut t = [0u8; 4]; - t.copy_from_slice(bytes); - Ok(Topic(t)) - } - }) - } -} - -/// Calculate union of blooms for given topics. -pub fn bloom_topics(topics: &[Topic]) -> H512 { - let mut bloom = H512::default(); - for topic in topics { - topic.bloom_into(&mut bloom); - } - bloom -} - -/// Message errors. -#[derive(Debug)] -pub enum Error { - Decoder(DecoderError), - EmptyTopics, - LivesTooLong, - IssuedInFuture, - TimestampOverflow, - ZeroTTL, -} - -impl From for Error { - fn from(err: DecoderError) -> Self { - Error::Decoder(err) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Decoder(ref err) => write!(f, "Failed to decode message: {}", err), - Error::LivesTooLong => write!(f, "Message claims to be issued before the unix epoch."), - Error::IssuedInFuture => write!(f, "Message issued in future."), - Error::ZeroTTL => write!(f, "Message live for zero time."), - Error::TimestampOverflow => write!(f, "Timestamp overflow"), - Error::EmptyTopics => write!(f, "Message has no topics."), - } - } -} - -fn append_topics<'a>(s: &'a mut RlpStream, topics: &[Topic]) -> &'a mut RlpStream { - if topics.len() == 1 { - s.append(&topics[0]) - } else { - s.append_list(&topics) - } -} - -fn decode_topics(rlp: Rlp) -> Result, DecoderError> { - if rlp.is_list() { - rlp.iter().map(|r| r.as_val::()).collect() - } else { - rlp.as_val().map(|t| SmallVec::from_slice(&[t])) - } -} - -// Raw envelope struct. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Envelope { - /// Expiry timestamp - pub expiry: u64, - /// Time-to-live in seconds - pub ttl: u64, - /// series of 4-byte topics. - pub topics: SmallVec<[Topic; 4]>, - /// The message contained within. - pub data: Vec, - /// Arbitrary value used to target lower PoW hash. - pub nonce: u64, -} - -impl Envelope { - /// Whether the message is multi-topic. Only relay these to Parity peers. - pub fn is_multitopic(&self) -> bool { - self.topics.len() != 1 - } - - fn proving_hash(&self) -> H256 { - use byteorder::{BigEndian, ByteOrder}; - - let mut buf = [0; 32]; - - let mut stream = RlpStream::new_list(4); - stream.append(&self.expiry).append(&self.ttl); - - append_topics(&mut stream, &self.topics) - .append(&self.data); - - let mut digest = Keccak::new_keccak256(); - digest.update(&*stream.drain()); - digest.update(&{ - let mut nonce_bytes = [0u8; 8]; - BigEndian::write_u64(&mut nonce_bytes, self.nonce); - - nonce_bytes - }); - - digest.finalize(&mut buf); - H256(buf) - } -} - -impl rlp::Encodable for Envelope { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(5) - .append(&self.expiry) - .append(&self.ttl); - - append_topics(s, &self.topics) - .append(&self.data) - .append(&self.nonce); - } -} - -impl rlp::Decodable for Envelope { - fn decode(rlp: &Rlp) -> Result { - if rlp.item_count()? != 5 { return Err(DecoderError::RlpIncorrectListLen) } - - Ok(Envelope { - expiry: rlp.val_at(0)?, - ttl: rlp.val_at(1)?, - topics: decode_topics(rlp.at(2)?)?, - data: rlp.val_at(3)?, - nonce: rlp.val_at(4)?, - }) - } -} - -/// Message creation parameters. -/// Pass this to `Message::create` to make a message. -pub struct CreateParams { - /// time-to-live in seconds. - pub ttl: u64, - /// payload data. - pub payload: Vec, - /// Topics. May not be empty. - pub topics: Vec, - /// How many milliseconds to spend proving work. - pub work: u64, -} - -/// A whisper message. This is a checked message carrying around metadata. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Message { - envelope: Envelope, - bloom: H512, - hash: H256, - encoded_size: usize, -} - -impl Message { - /// Create a message from creation parameters. - /// Panics if TTL is 0. - pub fn create(params: CreateParams) -> Result { - use byteorder::{BigEndian, ByteOrder}; - use rand::{Rng, SeedableRng, XorShiftRng}; - - if params.topics.is_empty() { return Err(Error::EmptyTopics) } - - let mut rng = { - let mut thread_rng = ::rand::thread_rng(); - XorShiftRng::from_seed(thread_rng.gen::<[u32; 4]>()) - }; - - assert!(params.ttl > 0); - - let expiry = { - let since_epoch = SystemTime::now() - .checked_add(Duration::from_secs(params.ttl)) - .and_then(|t| t.checked_add(Duration::from_millis(params.work))) - .ok_or(Error::TimestampOverflow)? - .duration_since(time::UNIX_EPOCH).expect("time after now is after unix epoch; qed"); - - // round up the sub-second to next whole second. - since_epoch.as_secs() + if since_epoch.subsec_nanos() == 0 { 0 } else { 1 } - }; - - let start_digest = { - let mut stream = RlpStream::new_list(4); - stream.append(&expiry).append(¶ms.ttl); - append_topics(&mut stream, ¶ms.topics).append(¶ms.payload); - - let mut digest = Keccak::new_keccak256(); - digest.update(&*stream.drain()); - digest - }; - - let mut buf = [0; 32]; - let mut try_nonce = move |nonce: &[u8; 8]| { - let mut digest = start_digest.clone(); - digest.update(&nonce[..]); - digest.finalize(&mut buf[..]); - - buf.clone() - }; - - let mut nonce: [u8; 8] = rng.gen(); - let mut best_found = try_nonce(&nonce); - - let start = Instant::now(); - - while start.elapsed() <= Duration::from_millis(params.work) { - let temp_nonce = rng.gen(); - let hash = try_nonce(&temp_nonce); - - if hash < best_found { - nonce = temp_nonce; - best_found = hash; - } - } - - let envelope = Envelope { - expiry: expiry, - ttl: params.ttl, - topics: params.topics.into_iter().collect(), - data: params.payload, - nonce: BigEndian::read_u64(&nonce[..]), - }; - - debug_assert_eq!(H256(best_found.clone()), envelope.proving_hash()); - - let encoded = ::rlp::encode(&envelope); - - Ok(Message::from_components( - envelope, - encoded.len(), - H256(keccak256(&encoded)), - SystemTime::now(), - ).expect("Message generated here known to be valid; qed")) - } - - /// Decode message from RLP and check for validity against system time. - 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())); - - Message::from_components(envelope, encoded_size, hash, now) - } - - // create message from envelope, hash, and encoded size. - // does checks for validity. - fn from_components(envelope: Envelope, size: usize, hash: H256, now: SystemTime) - -> Result - { - const LEEWAY_SECONDS: u64 = 2; - - if envelope.expiry <= envelope.ttl { return Err(Error::LivesTooLong) } - if envelope.ttl == 0 { return Err(Error::ZeroTTL) } - - if envelope.topics.is_empty() { return Err(Error::EmptyTopics) } - - let issue_time_adjusted = Duration::from_secs( - (envelope.expiry - envelope.ttl).saturating_sub(LEEWAY_SECONDS) - ); - - let issue_time_adjusted = time::UNIX_EPOCH.checked_add(issue_time_adjusted) - .ok_or(Error::TimestampOverflow)?; - - if issue_time_adjusted > now { - return Err(Error::IssuedInFuture); - } - - // other validity checks? - let bloom = bloom_topics(&envelope.topics); - - Ok(Message { - envelope: envelope, - bloom: bloom, - hash: hash, - encoded_size: size, - }) - } - - /// Get a reference to the envelope. - pub fn envelope(&self) -> &Envelope { - &self.envelope - } - - /// Get the encoded size of the envelope. - pub fn encoded_size(&self) -> usize { - self.encoded_size - } - - /// Get a uniquely identifying hash for the message. - pub fn hash(&self) -> &H256 { - &self.hash - } - - /// Get the bloom filter of the topics - pub fn bloom(&self) -> &H512 { - &self.bloom - } - - /// Get the work proved by the hash. - pub fn work_proved(&self) -> f64 { - let proving_hash = self.envelope.proving_hash(); - - work_factor_proved(self.encoded_size as _, self.envelope.ttl, proving_hash) - } - - /// Get the expiry time. - pub fn expiry(&self) -> Option { - time::UNIX_EPOCH.checked_add(Duration::from_secs(self.envelope.expiry)) - } - - /// Get the topics. - pub fn topics(&self) -> &[Topic] { - &self.envelope.topics - } - - /// Get the message data. - pub fn data(&self) -> &[u8] { - &self.envelope.data - } -} - -#[cfg(test)] -mod tests { - use ethereum_types::H256; - use super::*; - use std::time::{self, Duration, SystemTime}; - use rlp::Rlp; - use smallvec::SmallVec; - - fn unix_time(x: u64) -> SystemTime { - time::UNIX_EPOCH + Duration::from_secs(x) - } - - #[test] - fn create_message() { - assert!(Message::create(CreateParams { - ttl: 100, - payload: vec![1, 2, 3, 4], - topics: vec![Topic([1, 2, 1, 2])], - work: 50, - }).is_ok()); - } - - #[test] - fn round_trip() { - let envelope = Envelope { - expiry: 100_000, - ttl: 30, - data: vec![9; 256], - topics: SmallVec::from_slice(&[Default::default()]), - nonce: 1010101, - }; - - let encoded = ::rlp::encode(&envelope); - let decoded = ::rlp::decode(&encoded).expect("failure decoding Envelope"); - - assert_eq!(envelope, decoded) - } - - #[test] - fn round_trip_multitopic() { - let envelope = Envelope { - expiry: 100_000, - ttl: 30, - data: vec![9; 256], - topics: SmallVec::from_slice(&[Default::default(), Topic([1, 2, 3, 4])]), - nonce: 1010101, - }; - - let encoded = ::rlp::encode(&envelope); - let decoded = ::rlp::decode(&encoded).expect("failure decoding Envelope"); - - assert_eq!(envelope, decoded) - } - - #[test] - fn passes_checks() { - let envelope = Envelope { - expiry: 100_000, - ttl: 30, - data: vec![9; 256], - topics: SmallVec::from_slice(&[Default::default()]), - nonce: 1010101, - }; - - let encoded = ::rlp::encode(&envelope); - - for i in 0..30 { - let now = unix_time(100_000 - i); - Message::decode(Rlp::new(&*encoded), now).unwrap(); - } - } - - #[test] - #[should_panic] - fn future_message() { - let envelope = Envelope { - expiry: 100_000, - ttl: 30, - data: vec![9; 256], - topics: SmallVec::from_slice(&[Default::default()]), - nonce: 1010101, - }; - - let encoded = ::rlp::encode(&envelope); - - let now = unix_time(100_000 - 1_000); - Message::decode(Rlp::new(&*encoded), now).unwrap(); - } - - #[test] - #[should_panic] - fn pre_epoch() { - let envelope = Envelope { - expiry: 100_000, - ttl: 200_000, - data: vec![9; 256], - topics: SmallVec::from_slice(&[Default::default()]), - nonce: 1010101, - }; - - let encoded = ::rlp::encode(&envelope); - - let now = unix_time(95_000); - Message::decode(Rlp::new(&*encoded), now).unwrap(); - } - - #[test] - fn work_factor() { - // 256 leading zeros -> 2^256 / 1 - assert_eq!(work_factor_proved(1, 1, H256::from(0)), 115792089237316200000000000000000000000000000000000000000000000000000000000000.0); - // 255 leading zeros -> 2^255 / 1 - assert_eq!(work_factor_proved(1, 1, H256::from(1)), 57896044618658100000000000000000000000000000000000000000000000000000000000000.0); - // 0 leading zeros -> 2^0 / 1 - assert_eq!(work_factor_proved(1, 1, serde_json::from_str::("\"0xff00000000000000000000000000000000000000000000000000000000000000\"").unwrap()), 1.0); - } -} diff --git a/whisper/src/net/mod.rs b/whisper/src/net/mod.rs deleted file mode 100644 index d263f6cfbd..0000000000 --- a/whisper/src/net/mod.rs +++ /dev/null @@ -1,731 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Whisper messaging system as a DevP2P subprotocol. - -use std::collections::{HashMap, HashSet}; -use std::cmp::Ordering; -use std::fmt; -use std::time::{Duration, SystemTime}; -use std::sync::Arc; - -use ethereum_types::{H256, H512}; -use network::{self, NetworkContext, NodeId, PeerId, ProtocolId, TimerToken}; -use ordered_float::OrderedFloat; -use parking_lot::{Mutex, RwLock}; -use rlp::{DecoderError, RlpStream, Rlp}; - -use message::{Message, Error as MessageError}; - -#[cfg(test)] -mod tests; - -// how often periodic relays are. when messages are imported -// we directly broadcast. -const RALLY_TOKEN: TimerToken = 1; -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, u8)] = &[ - (PROTOCOL_VERSION as u8, PACKET_COUNT) -]; - -// maximum tolerated delay between messages packets. -const MAX_TOLERATED_DELAY: Duration = Duration::from_millis(5000); - -/// Whisper protocol ID -pub const PROTOCOL_ID: ::network::ProtocolId = *b"shh"; - -/// Parity-whisper protocol ID -/// Current parity-specific extensions: -/// - Multiple topics in packet. -pub const PARITY_PROTOCOL_ID: ::network::ProtocolId = *b"pwh"; - -mod packet { - pub const STATUS: u8 = 0; - pub const MESSAGES: u8 = 1; - pub const POW_REQUIREMENT: u8 = 2; - pub const TOPIC_FILTER: u8 = 3; - - // 126, 127 for mail server stuff we will never implement here. -} - -/// Handles messages within a single packet. -pub trait MessageHandler: Send + Sync { - /// Evaluate the message and handle it. - /// - /// The same message will not be passed twice. - /// Heavy handling should be done asynchronously. - /// If there is a significant overhead in this thread, then an attacker - /// can determine which kinds of messages we are listening for. - fn handle_messages(&self, message: &[Message]); -} - -// errors in importing a whisper message. -#[derive(Debug)] -enum Error { - Decoder(DecoderError), - Network(network::Error), - Message(MessageError), - UnknownPeer(PeerId), - UnexpectedMessage, - InvalidPowReq, -} - -impl From for Error { - fn from(err: DecoderError) -> Self { - Error::Decoder(err) - } -} - -impl From for Error { - fn from(err: network::Error) -> Self { - Error::Network(err) - } -} - -impl From for Error { - fn from(err: MessageError) -> Self { - Error::Message(err) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Decoder(ref err) => write!(f, "Failed to decode packet: {}", err), - Error::Network(ref err) => write!(f, "Network error: {}", err), - Error::Message(ref err) => write!(f, "Error decoding message: {}", err), - Error::UnknownPeer(ref id) => write!(f, "Message received from unknown peer: {}", id), - Error::UnexpectedMessage => write!(f, "Unexpected message."), - Error::InvalidPowReq => write!(f, "Peer sent invalid PoW requirement."), - } - } -} - -// sorts by work proved, descending. -#[derive(PartialEq, Eq)] -struct SortedEntry { - slab_id: usize, - work_proved: OrderedFloat, - expiry: SystemTime, -} - -impl Ord for SortedEntry { - fn cmp(&self, other: &SortedEntry) -> Ordering { - self.work_proved.cmp(&other.work_proved) - } -} - -impl PartialOrd for SortedEntry { - fn partial_cmp(&self, other: &SortedEntry) -> Option { - Some(self.cmp(other)) - } -} - -// stores messages by two metrics: expiry and PoW rating -// when full, will accept messages above the minimum stored. -struct Messages { - slab: ::slab::Slab, - sorted: Vec, - known: HashSet, - removed_hashes: Vec, - cumulative_size: usize, - ideal_size: usize, -} - -impl Messages { - fn new(ideal_size: usize) -> Self { - Messages { - slab: ::slab::Slab::with_capacity(0), - sorted: Vec::new(), - known: HashSet::new(), - removed_hashes: Vec::new(), - cumulative_size: 0, - ideal_size: ideal_size, - } - } - - // reserve space for additional elements. - fn reserve(&mut self, additional: usize) { - self.slab.reserve_exact(additional); - self.sorted.reserve(additional); - self.known.reserve(additional); - } - - // whether a message is not known and within the bounds of PoW. - fn may_accept(&self, message: &Message) -> bool { - !self.known.contains(message.hash()) && { - self.sorted.last().map_or(true, |entry| { - let work_proved = OrderedFloat(message.work_proved()); - OrderedFloat(self.slab[entry.slab_id].work_proved()) < work_proved - }) - } - } - - // insert a message into the store. for best performance, - // call `reserve` before inserting a bunch. - // - fn insert(&mut self, message: Message) -> bool { - if !self.known.insert(message.hash().clone()) { return false } - - let work_proved = OrderedFloat(message.work_proved()); - - // pop off entries by low PoW until we have enough space for the higher - // PoW message being inserted. - let size_upon_insertion = self.cumulative_size + message.encoded_size(); - if size_upon_insertion >= self.ideal_size { - let diff = size_upon_insertion - self.ideal_size; - let mut found_diff = 0; - for entry in self.sorted.iter().rev() { - if found_diff >= diff { break } - - // if we encounter a message with at least the PoW we're looking - // at, don't push that message out. - if entry.work_proved >= work_proved { return false } - found_diff += self.slab[entry.slab_id].encoded_size(); - } - - // message larger than ideal size. - if found_diff < diff { return false } - - while found_diff > 0 { - let entry = self.sorted.pop() - .expect("found_diff built by traversing entries; therefore that many entries exist; qed"); - - let message = self.slab.remove(entry.slab_id) - .expect("sorted entry slab IDs always filled; qed"); - - found_diff -= message.encoded_size(); - - self.cumulative_size -= message.encoded_size(); - self.known.remove(message.hash()); - self.removed_hashes.push(message.hash().clone()); - } - } - - let expiry = match message.expiry() { - Some(time) => time, - _ => return false, - }; - - self.cumulative_size += message.encoded_size(); - - if !self.slab.has_available() { self.slab.reserve_exact(1) } - let id = self.slab.insert(message).expect("just ensured enough space in slab; qed"); - - let sorted_entry = SortedEntry { - slab_id: id, - work_proved, - expiry, - }; - - match self.sorted.binary_search(&sorted_entry) { - Ok(idx) | Err(idx) => self.sorted.insert(idx, sorted_entry), - } - - true - } - - // prune expired messages, and then prune low proof-of-work messages - // until below ideal size. - fn prune(&mut self, now: SystemTime) -> Vec { - { - let slab = &mut self.slab; - let known = &mut self.known; - let cumulative_size = &mut self.cumulative_size; - let ideal_size = &self.ideal_size; - let removed = &mut self.removed_hashes; - - // first pass, we look just at expired entries. - let all_expired = self.sorted.iter() - .filter(|entry| entry.expiry <= now) - .map(|x| (true, x)); - - // second pass, we look at entries which aren't expired but in order - // by PoW - let low_proof = self.sorted.iter().rev() - .filter(|entry| entry.expiry > now) - .map(|x| (false, x)); - - for (is_expired, entry) in all_expired.chain(low_proof) { - // break once we've removed all expired entries - // or have taken enough low-work entries. - if !is_expired && *cumulative_size <= *ideal_size { - break - } - - let message = slab.remove(entry.slab_id) - .expect("references to ID kept upon creation; only destroyed upon removal; qed"); - - known.remove(message.hash()); - removed.push(message.hash().clone()); - - *cumulative_size -= message.encoded_size(); - } - } - - // clear all the sorted entries we removed from slab. - let slab = &self.slab; - self.sorted.retain(|entry| slab.contains(entry.slab_id)); - - ::std::mem::replace(&mut self.removed_hashes, Vec::new()) - } - - fn iter(&self) -> ::slab::Iter { - self.slab.iter() - } - - fn is_full(&self) -> bool { - self.cumulative_size >= self.ideal_size - } - - fn status(&self) -> PoolStatus { - PoolStatus { - required_pow: if self.is_full() { - self.sorted.last().map(|entry| entry.work_proved.0) - } else { - None - }, - message_count: self.sorted.len(), - cumulative_size: self.cumulative_size, - target_size: self.ideal_size, - } - } -} - -enum State { - Unconfirmed(SystemTime), // awaiting status packet. - Confirmed, -} - -#[allow(dead_code)] // for node key. this will be useful for topic routing. -struct Peer { - node_key: NodeId, - state: State, - known_messages: HashSet, - topic_filter: Option, - pow_requirement: f64, - is_parity: bool, - _protocol_version: usize, -} - -impl Peer { - // note that a message has been evicted from the queue. - fn note_evicted(&mut self, messages: &[H256]) { - for message_hash in messages { - self.known_messages.remove(message_hash); - } - } - - // whether this peer will accept the message. - fn will_accept(&self, message: &Message) -> bool { - if self.known_messages.contains(message.hash()) { return false } - - // only parity peers will accept multitopic messages. - if message.envelope().is_multitopic() && !self.is_parity { return false } - if message.work_proved() < self.pow_requirement { return false } - - self.topic_filter.as_ref() - .map_or(true, |filter| &(filter & message.bloom()) == message.bloom()) - } - - // note a message as known. returns false if it was already - // known, true otherwise. - fn note_known(&mut self, message: &Message) -> bool { - self.known_messages.insert(message.hash().clone()) - } - - fn set_topic_filter(&mut self, topic: H512) { - self.topic_filter = Some(topic); - } - - fn set_pow_requirement(&mut self, pow_requirement: f64) { - self.pow_requirement = pow_requirement; - } - - fn can_send_messages(&self) -> bool { - match self.state { - State::Unconfirmed(_) => false, - State::Confirmed => true, - } - } -} - -/// Pool status. -pub struct PoolStatus { - /// Required PoW to be accepted into the pool - pub required_pow: Option, - /// Number of messages in the pool. - pub message_count: usize, - /// Cumulative size of the messages in the pool - pub cumulative_size: usize, - /// Target size of the pool. - pub target_size: usize, -} - -/// Generic network context. -pub trait Context { - /// Disconnect a peer. - fn disconnect_peer(&self, PeerId); - /// Disable a peer. - fn disable_peer(&self, PeerId); - /// Get a peer's node key. - fn node_key(&self, PeerId) -> Option; - /// Get a peer's protocol version for given protocol. - fn protocol_version(&self, ProtocolId, PeerId) -> Option; - /// Send message to peer. - fn send(&self, PeerId, u8, Vec); -} - -impl Context for T where T: ?Sized + NetworkContext { - fn disconnect_peer(&self, peer: PeerId) { - NetworkContext::disconnect_peer(self, peer); - } - fn disable_peer(&self, peer: PeerId) { - NetworkContext::disable_peer(self, peer) - } - fn node_key(&self, peer: PeerId) -> Option { - self.session_info(peer).and_then(|info| info.id) - } - fn protocol_version(&self, proto_id: ProtocolId, peer: PeerId) -> Option { - NetworkContext::protocol_version(self, proto_id, peer) - } - - fn send(&self, peer: PeerId, packet_id: u8, message: Vec) { - if let Err(e) = NetworkContext::send(self, peer, packet_id, message) { - debug!(target: "whisper", "Failed to send packet {} to peer {}: {}", - packet_id, peer, e); - - self.disconnect_peer(peer) - } - } -} - -/// The whisper network protocol handler. -pub struct Network { - messages: Arc>, - handler: T, - peers: RwLock>>, -} - -// public API. -impl Network { - /// Create a new network handler. - pub fn new(messages_size_bytes: usize, handler: T) -> Self { - Network { - messages: Arc::new(RwLock::new(Messages::new(messages_size_bytes))), - handler: handler, - peers: RwLock::new(HashMap::new()), - } - } - - /// Post a message to the whisper network to be relayed. - pub fn post_message(&self, message: Message, context: &C) -> bool - where T: MessageHandler - { - let ok = self.messages.write().insert(message); - if ok { self.rally(context) } - ok - } - - /// Get number of messages and amount of memory used by them. - pub fn pool_status(&self) -> PoolStatus { - self.messages.read().status() - } -} - -impl Network { - fn rally(&self, io: &C) { - // cannot be greater than 16MB (protocol limitation) - const MAX_MESSAGES_PACKET_SIZE: usize = 8 * 1024 * 1024; - - // prune messages. - let now = SystemTime::now(); - let pruned_hashes = self.messages.write().prune(now); - - let messages = self.messages.read(); - let peers = self.peers.read(); - - // send each peer a packet with new messages it may find relevant. - for (peer_id, peer) in peers.iter() { - let mut peer_data = peer.lock(); - peer_data.note_evicted(&pruned_hashes); - - let punish_timeout = |last_activity: &SystemTime| { - if *last_activity + MAX_TOLERATED_DELAY <= now { - debug!(target: "whisper", "Disconnecting peer {} due to excessive timeout.", peer_id); - io.disconnect_peer(*peer_id); - } - }; - - // check timeouts and skip peers who we can't send a rally to. - match peer_data.state { - State::Unconfirmed(ref time) => { - punish_timeout(time); - continue; - } - State::Confirmed => {} - } - - // construct packet, skipping messages the peer won't accept. - let mut stream = RlpStream::new(); - stream.begin_unbounded_list(); - - for message in messages.iter() { - if !peer_data.will_accept(message) { continue } - - if stream.estimate_size(message.encoded_size()) > MAX_MESSAGES_PACKET_SIZE { - break; - } - - peer_data.note_known(message); - stream.append(message.envelope()); - } - - stream.complete_unbounded_list(); - - io.send(*peer_id, packet::MESSAGES, stream.out()); - } - } - - // handle status packet from peer. - fn on_status(&self, peer: &PeerId, _status: Rlp) - -> Result<(), Error> - { - let peers = self.peers.read(); - - match peers.get(peer) { - Some(peer) => { - peer.lock().state = State::Confirmed; - Ok(()) - } - None => { - debug!(target: "whisper", "Received message from unknown peer."); - Err(Error::UnknownPeer(*peer)) - } - } - } - - fn on_messages(&self, peer: &PeerId, message_packet: Rlp) - -> Result<(), Error> - { - let mut messages_vec = { - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "whisper", "Received message from unknown peer."); - return Err(Error::UnknownPeer(*peer)); - } - }; - - let mut peer = peer.lock(); - - if !peer.can_send_messages() { - return Err(Error::UnexpectedMessage); - } - - let now = SystemTime::now(); - let mut messages_vec = message_packet.iter().map(|rlp| Message::decode(rlp, now)) - .collect::, _>>()?; - - if messages_vec.is_empty() { return Ok(()) } - - // disallow duplicates in packet. - messages_vec.retain(|message| peer.note_known(&message)); - messages_vec - }; - - // import for relaying. - let mut messages = self.messages.write(); - - messages_vec.retain(|message| messages.may_accept(&message)); - messages.reserve(messages_vec.len()); - - self.handler.handle_messages(&messages_vec); - - for message in messages_vec { - messages.insert(message); - } - - Ok(()) - } - - fn on_pow_requirement(&self, peer: &PeerId, requirement: Rlp) - -> Result<(), Error> - { - use byteorder::{ByteOrder, BigEndian}; - - let peers = self.peers.read(); - match peers.get(peer) { - Some(peer) => { - let mut peer = peer.lock(); - - if let State::Unconfirmed(_) = peer.state { - return Err(Error::UnexpectedMessage); - } - let bytes: Vec = requirement.as_val()?; - if bytes.len() != ::std::mem::size_of::() { - return Err(Error::InvalidPowReq); - } - - // as of byteorder 1.1.0, this is always defined. - let req = BigEndian::read_f64(&bytes[..]); - - if !req.is_normal() { - return Err(Error::InvalidPowReq); - } - - peer.set_pow_requirement(req); - } - None => { - debug!(target: "whisper", "Received message from unknown peer."); - return Err(Error::UnknownPeer(*peer)); - } - } - - Ok(()) - } - - fn on_topic_filter(&self, peer: &PeerId, filter: Rlp) - -> Result<(), Error> - { - let peers = self.peers.read(); - match peers.get(peer) { - Some(peer) => { - let mut peer = peer.lock(); - - if let State::Unconfirmed(_) = peer.state { - return Err(Error::UnexpectedMessage); - } - - peer.set_topic_filter(filter.as_val()?) - } - None => { - debug!(target: "whisper", "Received message from unknown peer."); - return Err(Error::UnknownPeer(*peer)); - } - } - - Ok(()) - } - - fn on_connect(&self, io: &C, peer: &PeerId) { - trace!(target: "whisper", "Connecting peer {}", peer); - - let node_key = match io.node_key(*peer) { - Some(node_key) => node_key, - None => { - debug!(target: "whisper", "Disconnecting peer {}, who has no node key.", peer); - io.disable_peer(*peer); - return; - } - }; - - let version = match io.protocol_version(PROTOCOL_ID, *peer) { - Some(version) => version as usize, - None => { - io.disable_peer(*peer); - return - } - }; - - self.peers.write().insert(*peer, Mutex::new(Peer { - node_key: node_key, - state: State::Unconfirmed(SystemTime::now()), - known_messages: HashSet::new(), - topic_filter: None, - pow_requirement: 0f64, - is_parity: io.protocol_version(PARITY_PROTOCOL_ID, *peer).is_some(), - _protocol_version: version, - })); - - io.send(*peer, packet::STATUS, ::rlp::EMPTY_LIST_RLP.to_vec()); - } - - fn on_packet(&self, io: &C, peer: &PeerId, packet_id: u8, data: &[u8]) { - let rlp = Rlp::new(data); - let res = match packet_id { - packet::STATUS => self.on_status(peer, rlp), - packet::MESSAGES => self.on_messages(peer, rlp), - packet::POW_REQUIREMENT => self.on_pow_requirement(peer, rlp), - packet::TOPIC_FILTER => self.on_topic_filter(peer, rlp), - _ => Ok(()), // ignore unknown packets. - }; - - if let Err(e) = res { - trace!(target: "whisper", "Disabling peer due to misbehavior: {}", e); - io.disable_peer(*peer); - } - } - - fn on_disconnect(&self, peer: &PeerId) { - trace!(target: "whisper", "Disconnecting peer {}", peer); - let _ = self.peers.write().remove(peer); - } -} - -impl ::network::NetworkProtocolHandler for Network { - fn initialize(&self, io: &NetworkContext) { - // set up broadcast timer (< 1s) - io.register_timer(RALLY_TOKEN, RALLY_TIMEOUT) - .expect("Failed to initialize message rally timer"); - } - - fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) { - self.on_packet(io, peer, packet_id, data) - } - - fn connected(&self, io: &NetworkContext, peer: &PeerId) { - // peer with higher ID should begin rallying. - self.on_connect(io, peer) - } - - fn disconnected(&self, _io: &NetworkContext, peer: &PeerId) { - self.on_disconnect(peer) - } - - fn timeout(&self, io: &NetworkContext, timer: TimerToken) { - // rally with each peer and handle timeouts. - match timer { - RALLY_TOKEN => self.rally(io), - other => debug!(target: "whisper", "Timeout triggered on unknown token {}", other), - } - } -} - -/// Dummy subprotocol used for parity extensions. -#[derive(Debug, Copy, Clone)] -pub struct ParityExtensions; - -impl ::network::NetworkProtocolHandler for ParityExtensions { - fn initialize(&self, _io: &NetworkContext) { } - - fn read(&self, _io: &NetworkContext, _peer: &PeerId, _id: u8, _msg: &[u8]) { } - - fn connected(&self, _io: &NetworkContext, _peer: &PeerId) { } - - fn disconnected(&self, _io: &NetworkContext, _peer: &PeerId) { } - - fn timeout(&self, _io: &NetworkContext, _timer: TimerToken) { } -} diff --git a/whisper/src/net/tests.rs b/whisper/src/net/tests.rs deleted file mode 100644 index 7af0cb8d38..0000000000 --- a/whisper/src/net/tests.rs +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Tests for the whisper network module. - -use std::collections::HashSet; -use std::sync::mpsc; - -use parking_lot::Mutex; -use network::{NodeId, PeerId}; - -use message::{CreateParams, Message}; -use super::*; - -struct TestHandler(Mutex>); - -impl MessageHandler for TestHandler { - fn handle_messages(&self, messages: &[Message]) { - let tx = self.0.lock(); - for message in messages { - let _ = tx.send(message.clone()); - } - } -} - -struct TestPeer { - network: Network, - recv: mpsc::Receiver, - disconnected: Mutex>, -} - -impl TestPeer { - fn create() -> Self { - let (tx, rx) = mpsc::channel(); - - TestPeer { - network: Network::new(10 * 1024 * 1024, TestHandler(Mutex::new(tx))), - recv: rx, - disconnected: Mutex::new(HashSet::new()), - } - } -} - -struct TestNetwork { - peers: Vec, -} - -impl TestNetwork { - fn new(n_peers: usize) -> Self { - let unconnected_peers: Vec<_> = (0..n_peers).map(|_| TestPeer::create()).collect(); - for i in 0..n_peers { - for j in (i + 1)..n_peers { - let (peer1, peer2) = (&unconnected_peers[i], &unconnected_peers[j]); - let ctx1 = TestContext::new(&unconnected_peers, i); - let ctx2 = TestContext::new(&unconnected_peers, j); - - peer1.network.on_connect(&ctx1, &j); - peer2.network.on_connect(&ctx2, &i); - } - } - - TestNetwork { - peers: unconnected_peers, - } - } - - fn post_message_from(&self, id: PeerId, msg: Message) { - self.peers[id].network.post_message(msg, &TestContext::new(&self.peers, id)); - } -} - -enum Event { - Disconnect(PeerId, PeerId), - Send(PeerId, PeerId, u8, Vec), -} - -struct TestContext<'a> { - peers: &'a [TestPeer], - local_id: PeerId, - events: Mutex>, -} - -impl<'a> TestContext<'a> { - fn new(peers: &'a [TestPeer], local_id: PeerId) -> Self { - TestContext { - peers, - local_id, - events: Mutex::new(Vec::new()), - } - } -} - -impl<'a> Context for TestContext<'a> { - fn disconnect_peer(&self, id: PeerId) { - self.events.lock().push(Event::Disconnect(self.local_id, id)); - } - - fn disable_peer(&self, id: PeerId) { - self.events.lock().push(Event::Disconnect(self.local_id, id)); - } - - fn node_key(&self, peer: PeerId) -> Option { - let mut id = NodeId::default(); - id[0] = peer as _; - Some(id) - } - - fn protocol_version(&self, id: ::network::ProtocolId, _peer: PeerId) -> Option { - if &id == b"shh" || &id == b"pwh" { - Some(PROTOCOL_VERSION as _) - } else { - None - } - } - - fn send(&self, peer: PeerId, packet: u8, data: Vec) { - self.events.lock().push(Event::Send(self.local_id, peer, packet, data)); - } -} - -impl<'a> Drop for TestContext<'a> { - fn drop(&mut self) { - let events = self.events.get_mut(); - while !events.is_empty() { - let mut deferred = Vec::new(); - for event in events.drain(..) { - match event { - Event::Disconnect(from, target) => { - self.peers[from].network.on_disconnect(&target); - self.peers[target].network.on_disconnect(&from); - - self.peers[from].disconnected.lock().insert(target); - self.peers[target].disconnected.lock().insert(from); - } - Event::Send(from, target, packet, data) => { - if self.peers[from].disconnected.lock().contains(&target) { - continue; - } - - let mut inner_ctx = TestContext::new(self.peers, target); - - self.peers[target].network.on_packet( - &inner_ctx, - &from, - packet, - &data[..] - ); - - // don't recursively apply disconnects or new messages - // from the receiver's actions yet. - let inner_events = ::std::mem::replace(inner_ctx.events.get_mut(), Vec::new()); - deferred.extend(inner_events); - } - } - } - - events.extend(deferred); - } - } -} - -#[test] -fn message_gets_relayed() { - let network = TestNetwork::new(5); - let message = Message::create(CreateParams { - ttl: 500, - payload: b"this is my payload, pal".to_vec(), - topics: vec![[0, 1, 2, 3].into()], - work: 25, - }).unwrap(); - - network.post_message_from(0, message.clone()); - - assert!(network.peers[0].recv.try_recv().is_err()); - - for i in 1..5 { - assert_eq!(network.peers[i].recv.try_recv().unwrap(), message); - } -} diff --git a/whisper/src/rpc/crypto.rs b/whisper/src/rpc/crypto.rs deleted file mode 100644 index 8911634531..0000000000 --- a/whisper/src/rpc/crypto.rs +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! 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 memzero::Memzero; - -/// Length of AES key -pub const AES_KEY_LEN: usize = 32; -/// Length of AES nonce (IV) -pub const AES_NONCE_LEN: usize = 12; - -// nonce used for encryption when broadcasting -const BROADCAST_IV: [u8; AES_NONCE_LEN] = [0xff; AES_NONCE_LEN]; - -// how to encode aes key/nonce. -enum AesEncode { - AppendedNonce, // receiver known, random nonce appended. - OnTopics(Vec), // receiver knows topics but not key. nonce global. -} - -enum EncryptionInner { - AES(Memzero<[u8; AES_KEY_LEN]>, [u8; AES_NONCE_LEN], AesEncode), - ECIES(Public), -} - -/// Encryption good for single usage. -pub struct EncryptionInstance(EncryptionInner); - -impl EncryptionInstance { - /// ECIES encryption using public key. Fails if invalid public key. - pub fn ecies(public: Public) -> Result { - if !ethkey::public_is_valid(&public) { - return Err("Invalid public key"); - } - - Ok(EncryptionInstance(EncryptionInner::ECIES(public))) - } - - /// 256-bit AES GCM encryption with given nonce. - /// It is extremely insecure to reuse nonces. - /// - /// If generating nonces with a secure RNG, limit uses such that - /// the chance of collision is negligible. - pub fn aes(key: Memzero<[u8; AES_KEY_LEN]>, nonce: [u8; AES_NONCE_LEN]) -> Self { - EncryptionInstance(EncryptionInner::AES(key, nonce, AesEncode::AppendedNonce)) - } - - /// Broadcast encryption for the message based on the given topics. - /// - /// Key reuse here is extremely dangerous. It should be randomly generated - /// with a secure RNG. - 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]) -> Option> { - match self.0 { - EncryptionInner::AES(key, nonce, encode) => { - match encode { - AesEncode::AppendedNonce => { - let mut enc = Encryptor::aes_256_gcm(&*key).ok()?; - let mut buf = enc.encrypt(&nonce, plain.to_vec()).ok()?; - buf.extend(&nonce[..]); - Some(buf) - } - AesEncode::OnTopics(topics) => { - let mut buf = Vec::new(); - for mut t in topics { - xor(&mut t.0, &key); - buf.extend(&t.0); - } - 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) => { - 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(Memzero<[u8; AES_KEY_LEN]>), // extract appended nonce. - OnTopics(usize, usize, H256), // number of topics, index we know, topic we know. -} - -enum DecryptionInner { - AES(AesExtract), - ECIES(Secret), -} - -/// Decryption instance good for single usage. -pub struct DecryptionInstance(DecryptionInner); - -impl DecryptionInstance { - /// ECIES decryption using secret key. Fails if invalid secret. - pub fn ecies(secret: Secret) -> Result { - secret.check_validity().map_err(|_| "Invalid secret key")?; - - Ok(DecryptionInstance(DecryptionInner::ECIES(secret))) - } - - /// 256-bit AES GCM decryption with appended nonce. - pub fn aes(key: Memzero<[u8; AES_KEY_LEN]>) -> Self { - DecryptionInstance(DecryptionInner::AES(AesExtract::AppendedNonce(key))) - } - - /// Decode broadcast based on number of topics and known topic. - /// Known topic index may not be larger than num topics - 1. - pub fn broadcast(num_topics: usize, topic_idx: usize, known_topic: H256) -> Result { - if topic_idx >= num_topics { return Err("topic index out of bounds") } - - Ok(DecryptionInstance(DecryptionInner::AES(AesExtract::OnTopics(num_topics, topic_idx, known_topic)))) - } - - /// Decrypt ciphertext. Fails if it's an invalid message. - pub fn decrypt(self, ciphertext: &[u8]) -> Option> { - match self.0 { - DecryptionInner::AES(extract) => { - match extract { - AesExtract::AppendedNonce(key) => { - 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..]); - 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 - } - let mut salted_topic = H256::new(); - salted_topic.copy_from_slice(&ciphertext[(known_index * 32)..][..32]); - let key = Memzero::from((salted_topic ^ known_topic).0); - let offset = num_topics * 32; - 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. - ecies::decrypt(&secret, &[], ciphertext).ok() - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn encrypt_asymmetric() { - use ethkey::{Generator, Random}; - - 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).unwrap(); - - if !message.is_empty() { - assert!(&ciphertext[..message.len()] != message) - } - - let instance = DecryptionInstance::ecies(key_pair.secret().clone()).unwrap(); - let decrypted = instance.decrypt(&ciphertext).unwrap(); - - assert_eq!(message, &decrypted[..]) - }; - - test_message(&[1, 2, 3, 4, 5]); - test_message(&[]); - test_message(&[255; 512]); - } - - #[test] - fn encrypt_symmetric() { - use rand::{Rng, OsRng}; - - let mut rng = OsRng::new().unwrap(); - let mut test_message = move |message: &[u8]| { - let key = Memzero::from(rng.gen::<[u8; 32]>()); - - let instance = EncryptionInstance::aes(key.clone(), rng.gen()); - let ciphertext = instance.encrypt(message).unwrap(); - - if !message.is_empty() { - assert!(&ciphertext[..message.len()] != message) - } - - let instance = DecryptionInstance::aes(key); - let decrypted = instance.decrypt(&ciphertext).unwrap(); - - assert_eq!(message, &decrypted[..]) - }; - - test_message(&[1, 2, 3, 4, 5]); - test_message(&[]); - test_message(&[255; 512]); - } - - #[test] - fn encrypt_broadcast() { - use rand::{Rng, OsRng}; - - let mut rng = OsRng::new().unwrap(); - - let mut test_message = move |message: &[u8]| { - let all_topics = (0..5).map(|_| rng.gen()).collect::>(); - let known_idx = 2; - let known_topic = all_topics[2]; - let key = Memzero::from(rng.gen::<[u8; 32]>()); - - let instance = EncryptionInstance::broadcast(key, all_topics); - let ciphertext = instance.encrypt(message).unwrap(); - - if !message.is_empty() { - assert!(&ciphertext[..message.len()] != message) - } - - let instance = DecryptionInstance::broadcast(5, known_idx, known_topic).unwrap(); - - let decrypted = instance.decrypt(&ciphertext).unwrap(); - - assert_eq!(message, &decrypted[..]) - }; - - test_message(&[1, 2, 3, 4, 5]); - test_message(&[]); - test_message(&[255; 512]); - } -} diff --git a/whisper/src/rpc/filter.rs b/whisper/src/rpc/filter.rs deleted file mode 100644 index 46cefd61e0..0000000000 --- a/whisper/src/rpc/filter.rs +++ /dev/null @@ -1,438 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Abstraction over filters which works with polling and subscription. - -use std::collections::HashMap; -use std::{sync::{Arc, atomic, atomic::AtomicBool, mpsc}, thread}; - -use ethereum_types::{H256, H512}; -use ethkey::Public; -use jsonrpc_pubsub::typed::{Subscriber, Sink}; -use parking_lot::{Mutex, RwLock}; -use rand::{Rng, OsRng}; - -use message::{Message, Topic}; -use super::{key_store::KeyStore, types::{self, FilterItem, HexEncode}}; - -/// Kinds of filters, -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum Kind { - /// Polled filter only returns data upon request - Poll, - /// Subscription filter pushes data to subscriber immediately. - Subscription, -} - -pub type ItemBuffer = Arc>>; - -enum FilterEntry { - Poll(Arc, ItemBuffer), - Subscription(Arc, Sink), -} - -/// Filter manager. Handles filters as well as a thread for doing decryption -/// and payload decoding. -pub struct Manager { - key_store: Arc>, - filters: RwLock>, - tx: Mutex>>, - join: Option>, - exit: Arc, -} - -impl Manager { - /// Create a new filter manager that will dispatch decryption tasks onto - /// 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 || { - 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, - }) - } - - /// Get a handle to the key store. - pub fn key_store(&self) -> Arc> { - self.key_store.clone() - } - - /// Get filter kind if it's known. - pub fn kind(&self, id: &H256) -> Option { - self.filters.read().get(id).map(|filter| match *filter { - FilterEntry::Poll(_, _) => Kind::Poll, - FilterEntry::Subscription(_, _) => Kind::Subscription, - }) - } - - /// Remove filter by ID. - pub fn remove(&self, id: &H256) { - self.filters.write().remove(id); - } - - /// Add a new polled filter. - pub fn insert_polled(&self, filter: Filter) -> Result { - let buffer = Arc::new(Mutex::new(Vec::new())); - let entry = FilterEntry::Poll(Arc::new(filter), buffer); - let id = OsRng::new() - .map_err(|_| "unable to acquire secure randomness")? - .gen(); - - self.filters.write().insert(id, entry); - Ok(id) - } - - /// Insert new subscription filter. Generates a secure ID and sends it to - /// the subscriber - pub fn insert_subscription(&self, filter: Filter, sub: Subscriber) - -> Result<(), &'static str> - { - let id: H256 = OsRng::new() - .map_err(|_| "unable to acquire secure randomness")? - .gen(); - - sub.assign_id(::jsonrpc_pubsub::SubscriptionId::String(format!("{:x}", id))) - .map(move |sink| { - let entry = FilterEntry::Subscription(Arc::new(filter), sink); - self.filters.write().insert(id, entry); - }) - .map_err(|_| "subscriber disconnected") - } - - /// Poll changes on filter identified by ID. - pub fn poll_changes(&self, id: &H256) -> Option> { - self.filters.read().get(id).and_then(|filter| match *filter { - FilterEntry::Subscription(_, _) => None, - FilterEntry::Poll(_, ref changes) - => Some(::std::mem::replace(&mut *changes.lock(), Vec::new())), - }) - } -} - -// machinery for attaching the manager to the network instance. -impl ::net::MessageHandler for Arc { - fn handle_messages(&self, messages: &[Message]) { - let filters = self.filters.read(); - let filters_iter = filters - .values() - .flat_map(|filter| messages.iter().map(move |msg| (filter, msg))) ; - - for (filter, message) in filters_iter { - // if the message matches any of the possible bloom filters, - // send to thread pool to attempt decryption and avoid - // blocking the network thread for long. - let failed_send = match *filter { - FilterEntry::Poll(ref filter, _) | FilterEntry::Subscription(ref filter, _) - if !filter.basic_matches(message) => None, - FilterEntry::Poll(ref filter, ref buffer) => { - let (message, key_store) = (message.clone(), self.key_store.clone()); - let (filter, buffer) = (filter.clone(), buffer.clone()); - - self.tx.lock().send(Box::new(move || { - filter.handle_message( - &message, - &*key_store, - |matched| buffer.lock().push(matched), - ) - })).err().map(|x| x.0) - } - FilterEntry::Subscription(ref filter, ref sink) => { - let (message, key_store) = (message.clone(), self.key_store.clone()); - let (filter, sink) = (filter.clone(), sink.clone()); - - self.tx.lock().send(Box::new(move || { - filter.handle_message( - &message, - &*key_store, - |matched| { let _ = sink.notify(Ok(matched)); }, - ) - })).err().map(|x| x.0) - } - }; - - // if we failed to send work, no option but to do it locally. - if let Some(local_work) = failed_send { - (local_work)() - } - } - } -} - -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"); - } -} - -/// Filter incoming messages by critera. -pub struct Filter { - topics: Vec<(Vec, H512, Topic)>, - from: Option, - decrypt_with: Option, -} - -impl Filter { - /// Create a new filter from filter request. - /// - /// Fails if the topics vector is empty. - pub fn new(params: types::FilterRequest) -> Result { - if params.topics.is_empty() { - return Err("no topics for filter"); - } - - let topics: Vec<_> = params.topics.into_iter() - .map(|x| x.into_inner()) - .map(|topic| { - let abridged = super::abridge_topic(&topic); - (topic, abridged.bloom(), abridged) - }) - .collect(); - - Ok(Filter { - topics: topics, - from: params.from.map(|x| x.into_inner()), - decrypt_with: params.decrypt_with.map(|x| x.into_inner()), - }) - } - - // does basic matching: - // whether the given message matches at least one of the topics of the - // filter. - // TODO: minimum PoW heuristic. - fn basic_matches(&self, message: &Message) -> bool { - self.topics.iter().any(|&(_, ref bloom, _)| { - &(bloom & message.bloom()) == bloom - }) - } - - // handle a message that matches the bloom. - fn handle_message( - &self, - message: &Message, - store: &RwLock, - on_match: F, - ) { - use rpc::crypto::DecryptionInstance; - use tiny_keccak::keccak256; - - let matched_indices: Vec<_> = self.topics.iter() - .enumerate() - .filter_map(|(i, &(_, ref bloom, ref abridged))| { - let contains_topic = &(bloom & message.bloom()) == bloom - && message.topics().contains(abridged); - - if contains_topic { Some(i) } else { None } - }) - .collect(); - - if matched_indices.is_empty() { return } - - let decrypt = match self.decrypt_with { - Some(ref id) => match store.read().decryption_instance(id) { - Some(d) => d, - None => { - warn!(target: "whisper", "Filter attempted to decrypt with destroyed identity {}", - id); - - return - } - }, - None => { - let known_idx = matched_indices[0]; - let known_topic = H256(keccak256(&self.topics[0].0)); - - DecryptionInstance::broadcast(message.topics().len(), known_idx, known_topic) - .expect("known idx is within the range 0..message.topics.len(); qed") - } - }; - - let decrypted = match decrypt.decrypt(message.data()) { - Some(d) => d, - None => { - trace!(target: "whisper", "Failed to decrypt message with {} matching topics", - matched_indices.len()); - - return - } - }; - - match ::rpc::payload::decode(&decrypted) { - Ok(decoded) => { - if decoded.from != self.from { return } - - let matched_topics = matched_indices - .into_iter() - .map(|i| self.topics[i].0.clone()) - .map(HexEncode) - .collect(); - - on_match(FilterItem { - from: decoded.from.map(HexEncode), - recipient: self.decrypt_with.map(HexEncode), - ttl: message.envelope().ttl, - topics: matched_topics, - timestamp: message.envelope().expiry - message.envelope().ttl, - payload: HexEncode(decoded.message.to_vec()), - padding: decoded.padding.map(|pad| HexEncode(pad.to_vec())), - }) - } - Err(reason) => - trace!(target: "whisper", "Bad payload in decrypted message with {} topics: {}", - matched_indices.len(), reason), - } - } -} - -#[cfg(test)] -mod tests { - use message::{CreateParams, Message, Topic}; - use rpc::types::{FilterRequest, HexEncode}; - use rpc::abridge_topic; - use super::*; - - #[test] - fn rejects_empty_topics() { - let req = FilterRequest { - decrypt_with: Default::default(), - from: None, - topics: Vec::new(), - }; - - assert!(Filter::new(req).is_err()); - } - - #[test] - fn basic_match() { - let topics = vec![vec![1, 2, 3, 4], vec![5, 6, 7, 8]]; - let abridged_topics: Vec<_> = topics.iter().map(|x| abridge_topic(&x)).collect(); - - let req = FilterRequest { - decrypt_with: Default::default(), - from: None, - topics: topics.into_iter().map(HexEncode).collect(), - }; - - let filter = Filter::new(req).unwrap(); - let message = Message::create(CreateParams { - ttl: 100, - payload: vec![1, 3, 5, 7, 9], - topics: abridged_topics.clone(), - work: 0, - }).unwrap(); - - assert!(filter.basic_matches(&message)); - - let message = Message::create(CreateParams { - ttl: 100, - payload: vec![1, 3, 5, 7, 9], - topics: abridged_topics.clone(), - work: 0, - }).unwrap(); - - assert!(filter.basic_matches(&message)); - - let message = Message::create(CreateParams { - ttl: 100, - payload: vec![1, 3, 5, 7, 9], - topics: vec![Topic([1, 8, 3, 99])], - work: 0, - }).unwrap(); - - assert!(!filter.basic_matches(&message)); - } - - #[test] - fn decrypt_and_decode() { - use rpc::payload::{self, EncodeParams}; - use rpc::key_store::{Key, KeyStore}; - - let topics = vec![vec![1, 2, 3, 4], vec![5, 6, 7, 8]]; - let abridged_topics: Vec<_> = topics.iter().map(|x| abridge_topic(&x)).collect(); - - let mut store = KeyStore::new().unwrap(); - let signing_pair = Key::new_asymmetric(store.rng()); - let encrypting_key = Key::new_symmetric(store.rng()); - - let decrypt_id = store.insert(encrypting_key); - let encryption_instance = store.encryption_instance(&decrypt_id).unwrap(); - - let store = ::parking_lot::RwLock::new(store); - - let payload = payload::encode(EncodeParams { - message: &[1, 2, 3], - padding: Some(&[4, 5, 4, 5]), - sign_with: Some(signing_pair.secret().unwrap()) - }).unwrap(); - - let encrypted = encryption_instance.encrypt(&payload).unwrap(); - - let message = Message::create(CreateParams { - ttl: 100, - payload: encrypted, - topics: abridged_topics.clone(), - work: 0, - }).unwrap(); - - let message2 = Message::create(CreateParams { - ttl: 100, - payload: vec![3, 5, 7, 9], - topics: abridged_topics, - work: 0, - }).unwrap(); - - let filter = Filter::new(FilterRequest { - decrypt_with: Some(HexEncode(decrypt_id)), - from: Some(HexEncode(signing_pair.public().unwrap().clone())), - topics: topics.into_iter().map(HexEncode).collect(), - }).unwrap(); - - assert!(filter.basic_matches(&message)); - assert!(filter.basic_matches(&message2)); - - let items = ::std::cell::Cell::new(0); - let on_match = |_| { items.set(items.get() + 1); }; - - filter.handle_message(&message, &store, &on_match); - filter.handle_message(&message2, &store, &on_match); - - assert_eq!(items.get(), 1); - } -} diff --git a/whisper/src/rpc/key_store.rs b/whisper/src/rpc/key_store.rs deleted file mode 100644 index 081a8b374d..0000000000 --- a/whisper/src/rpc/key_store.rs +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Identity and keystore for Whisper sessions. -//! -//! Can handle symmetric and asymmetric keys. -//! Symmetric encryption is done via AES-256 in GCM mode. - -use std::collections::HashMap; - -use ethereum_types::H256; -use ethkey::{KeyPair, Public, Secret}; -use memzero::Memzero; -use rand::{Rng, OsRng}; - -use rpc::crypto::{AES_KEY_LEN, EncryptionInstance, DecryptionInstance}; - -/// A symmetric or asymmetric key used for encryption, decryption, and signing -/// of payloads. -pub enum Key { - /// ECIES key pair for Secp2561k curve. Suitable for encryption, decryption, - /// and signing. - Asymmetric(KeyPair), - /// AES-256 GCM mode. Suitable for encryption, decryption, but not signing. - Symmetric(Memzero<[u8; AES_KEY_LEN]>), -} - -impl Key { - /// Generate a random asymmetric key with the given cryptographic RNG. - pub fn new_asymmetric(rng: &mut OsRng) -> Self { - match ::ethkey::Generator::generate(rng) { - Ok(pair) => Key::Asymmetric(pair), - Err(void) => match void {}, - } - } - - /// Generate a random symmetric key with the given cryptographic RNG. - pub fn new_symmetric(rng: &mut OsRng) -> Self { - Key::Symmetric(Memzero::from(rng.gen::<[u8; 32]>())) - } - - /// From secret asymmetric key. Fails if secret is invalid. - 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(Memzero::from(key)) - } - - /// Get a handle to the public key if this is an asymmetric key. - pub fn public(&self) -> Option<&Public> { - match *self { - Key::Asymmetric(ref pair) => Some(pair.public()), - Key::Symmetric(_) => None, - } - } - - /// Get a handle to the secret key if this is an asymmetric key. - pub fn secret(&self) -> Option<&Secret> { - match *self { - Key::Asymmetric(ref pair) => Some(pair.secret()), - Key::Symmetric(_) => None, - } - } - - /// Get a handle to the symmetric key. - pub fn symmetric(&self) -> Option<&[u8; AES_KEY_LEN]> { - match *self { - Key::Asymmetric(_) => None, - Key::Symmetric(ref key) => Some(key), - } - } -} - -/// Key store. -pub struct KeyStore { - rng: OsRng, - identities: HashMap, -} - -impl KeyStore { - /// Create the key store. Returns any error in accessing the system's secure - /// RNG. - pub fn new() -> Result { - Ok(KeyStore { - rng: OsRng::new()?, - identities: HashMap::new(), - }) - } - - /// Import a key, generating a random identity for it. - pub fn insert(&mut self, key: Key) -> H256 { - let id = self.rng().gen(); - self.identities.insert(id, key); - - id - } - - /// Get a key by ID. - pub fn get<'a>(&'a self, id: &H256) -> Option<&'a Key> { - self.identities.get(id) - } - - /// Get asymmetric ID's public key. - pub fn public<'a>(&'a self, id: &H256) -> Option<&'a Public> { - self.get(id).and_then(Key::public) - } - - /// Get asymmetric ID's secret key. - pub fn secret<'a>(&'a self, id: &H256) -> Option<&'a Secret> { - self.get(id).and_then(Key::secret) - } - - /// Get symmetric ID's key. - pub fn symmetric<'a>(&'a self, id: &H256) -> Option<&'a [u8; AES_KEY_LEN]> { - self.get(id).and_then(Key::symmetric) - } - - /// Get encryption instance for identity. - pub fn encryption_instance(&self, id: &H256) -> Result { - self.get(id).ok_or("no such identity").and_then(|key| match *key { - Key::Asymmetric(ref pair) => EncryptionInstance::ecies(pair.public().clone()) - .map_err(|_| "could not create encryption instance for id"), - Key::Symmetric(ref key) => - OsRng::new() - .map(|mut rng| EncryptionInstance::aes(key.clone(), rng.gen())) - .map_err(|_| "unable to get secure randomness") - }) - } - - /// Get decryption instance for identity. - /// If the identity is known, always succeeds. - pub fn decryption_instance(&self, id: &H256) -> Option { - self.get(id).map(|key| match *key { - Key::Asymmetric(ref pair) => DecryptionInstance::ecies(pair.secret().clone()) - .expect("all keys stored are valid; qed"), - Key::Symmetric(ref key) => DecryptionInstance::aes(key.clone()), - }) - } - - /// Whether the store contains a key by this ID. - pub fn contains(&self, id: &H256) -> bool { - self.identities.contains_key(id) - } - - /// Remove a key by ID. - pub fn remove(&mut self, id: &H256) -> bool { - self.identities.remove(id).is_some() - } - - /// Get RNG. - pub fn rng(&mut self) -> &mut OsRng { - &mut self.rng - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn rejects_invalid_secret() { - let bad_secret = ::ethkey::Secret::from([0xff; 32]); - assert!(Key::from_secret(bad_secret).is_none()); - } - - #[test] - fn generated_key_should_exist() { - let mut store = KeyStore::new().unwrap(); - let key = Key::new_asymmetric(store.rng()); - - assert!(key.public().is_some()); - assert!(key.secret().is_some()); - - let id = store.insert(key); - - assert!(store.contains(&id)); - assert!(store.get(&id).is_some()); - } -} diff --git a/whisper/src/rpc/mod.rs b/whisper/src/rpc/mod.rs deleted file mode 100644 index 03d98b51ff..0000000000 --- a/whisper/src/rpc/mod.rs +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! JSONRPC interface for Whisper. -//! -//! Manages standard message format decoding, ephemeral identities, signing, -//! encryption, and decryption. -//! -//! Provides an interface for using whisper to transmit data securely. - -use std::sync::Arc; - -use jsonrpc_core::{Error, ErrorCode, Metadata}; -use jsonrpc_derive::rpc; -use jsonrpc_pubsub::{Session, PubSubMetadata, SubscriptionId, typed::Subscriber}; - -use ethereum_types::H256; -use memzero::Memzero; -use parking_lot::RwLock; - -use self::filter::Filter; -use self::key_store::{Key, KeyStore}; -use self::types::HexEncode; - -use message::{CreateParams, Message, Topic}; - -mod crypto; -mod filter; -mod key_store; -mod payload; -mod types; - -pub use self::filter::Manager as FilterManager; - -// create whisper RPC error. -fn whisper_error>(message: T) -> Error { - const ERROR_CODE: i64 = -32085; - - Error { - code: ErrorCode::ServerError(ERROR_CODE), - message: message.into(), - data: None, - } -} - -fn topic_hash(topic: &[u8]) -> H256 { - H256(::tiny_keccak::keccak256(topic)) -} - -// abridge topic using first four bytes of hash. -fn abridge_topic(topic: &[u8]) -> Topic { - let mut abridged = [0; 4]; - let hash = topic_hash(topic).0; - abridged.copy_from_slice(&hash[..4]); - abridged.into() -} - -/// Whisper RPC interface. -#[rpc] -pub trait Whisper { - /// Info about the node. - #[rpc(name = "shh_info")] - fn info(&self) -> Result; - - /// Generate a new asymmetric key pair and return an identity. - #[rpc(name = "shh_newKeyPair")] - fn new_key_pair(&self) -> Result; - - /// Import the given SECP2561k private key and return an identity. - #[rpc(name = "shh_addPrivateKey")] - fn add_private_key(&self, types::Private) -> Result; - - /// Generate a new symmetric key and return an identity. - #[rpc(name = "shh_newSymKey")] - fn new_sym_key(&self) -> Result; - - /// Import the given symmetric key and return an identity. - #[rpc(name = "shh_addSymKey")] - fn add_sym_key(&self, types::Symmetric) -> Result; - - /// Get public key. Succeeds if identity is stored and asymmetric. - #[rpc(name = "shh_getPublicKey")] - fn get_public(&self, types::Identity) -> Result; - - /// Get private key. Succeeds if identity is stored and asymmetric. - #[rpc(name = "shh_getPrivateKey")] - fn get_private(&self, types::Identity) -> Result; - - #[rpc(name = "shh_getSymKey")] - fn get_symmetric(&self, types::Identity) -> Result; - - /// Delete key pair denoted by given identity. - /// - /// Return true if successfully removed, false if unknown, - /// and error otherwise. - #[rpc(name = "shh_deleteKey")] - fn remove_key(&self, types::Identity) -> Result; - - /// Post a message to the network with given parameters. - #[rpc(name = "shh_post")] - fn post(&self, types::PostRequest) -> Result; - - /// Create a new polled filter. - #[rpc(name = "shh_newMessageFilter")] - fn new_filter(&self, types::FilterRequest) -> Result; - - /// Poll changes on a polled filter. - #[rpc(name = "shh_getFilterMessages")] - fn poll_changes(&self, types::Identity) -> Result, Error>; - - /// Delete polled filter. Return bool indicating success. - #[rpc(name = "shh_deleteMessageFilter")] - fn delete_filter(&self, types::Identity) -> Result; -} - -/// Whisper RPC pubsub. -#[rpc] -pub trait WhisperPubSub { - // RPC Metadata - type Metadata; - - /// Subscribe to messages matching the filter. - #[pubsub(subscription = "shh_subscription", subscribe, name = "shh_subscribe")] - fn subscribe(&self, Self::Metadata, Subscriber, types::FilterRequest); - - /// Unsubscribe from filter matching given ID. Return - /// true on success, error otherwise. - #[pubsub(subscription = "shh_subscription", unsubscribe, name = "shh_unsubscribe")] - fn unsubscribe(&self, Option, SubscriptionId) -> Result; -} - -/// Something which can send messages to the network. -pub trait PoolHandle: Send + Sync { - /// Give message to the whisper network for relay. - /// Returns false if PoW too low. - fn relay(&self, message: Message) -> bool; - - /// Number of messages and memory used by resident messages. - fn pool_status(&self) -> ::net::PoolStatus; -} - -/// Default, simple metadata implementation. -#[derive(Clone, Default)] -pub struct Meta { - session: Option>, -} - -impl Metadata for Meta {} -impl PubSubMetadata for Meta { - fn session(&self) -> Option> { - self.session.clone() - } -} - -/// Implementation of whisper RPC. -pub struct WhisperClient { - store: Arc>, - pool: P, - filter_manager: Arc, - _meta: ::std::marker::PhantomData, -} - -impl

WhisperClient

{ - /// Create a new whisper client with basic metadata. - pub fn with_simple_meta(pool: P, filter_manager: Arc) -> Self { - WhisperClient::new(pool, filter_manager) - } -} - -impl WhisperClient { - /// Create a new whisper client. - pub fn new(pool: P, filter_manager: Arc) -> Self { - WhisperClient { - store: filter_manager.key_store(), - pool: pool, - filter_manager: filter_manager, - _meta: ::std::marker::PhantomData, - } - } - - fn delete_filter_kind(&self, id: H256, kind: filter::Kind) -> bool { - match self.filter_manager.kind(&id) { - Some(k) if k == kind => { - self.filter_manager.remove(&id); - true - } - None | Some(_) => false, - } - } -} - -impl Whisper for WhisperClient { - fn info(&self) -> Result { - let status = self.pool.pool_status(); - - Ok(types::NodeInfo { - required_pow: status.required_pow, - messages: status.message_count, - memory: status.cumulative_size, - target_memory: status.target_size, - }) - } - - fn new_key_pair(&self) -> Result { - let mut store = self.store.write(); - let key_pair = Key::new_asymmetric(store.rng()); - - Ok(HexEncode(store.insert(key_pair))) - } - - fn add_private_key(&self, private: types::Private) -> Result { - let key_pair = Key::from_secret(private.into_inner().into()) - .ok_or_else(|| whisper_error("Invalid private key"))?; - - Ok(HexEncode(self.store.write().insert(key_pair))) - } - - fn new_sym_key(&self) -> Result { - let mut store = self.store.write(); - let key = Key::new_symmetric(store.rng()); - - Ok(HexEncode(store.insert(key))) - } - - fn add_sym_key(&self, raw_key: types::Symmetric) -> Result { - let raw_key = raw_key.into_inner().0; - let key = Key::from_raw_symmetric(raw_key); - - Ok(HexEncode(self.store.write().insert(key))) - } - - fn get_public(&self, id: types::Identity) -> Result { - self.store.read().public(&id.into_inner()) - .cloned() - .map(HexEncode) - .ok_or_else(|| whisper_error("Unknown identity")) - } - - fn get_private(&self, id: types::Identity) -> Result { - self.store.read().secret(&id.into_inner()) - .map(|x| (&**x).clone()) - .map(HexEncode) - .ok_or_else(|| whisper_error("Unknown identity")) - } - - fn get_symmetric(&self, id: types::Identity) -> Result { - self.store.read().symmetric(&id.into_inner()) - .cloned() - .map(H256) - .map(HexEncode) - .ok_or_else(|| whisper_error("Unknown identity")) - } - - fn remove_key(&self, id: types::Identity) -> Result { - Ok(self.store.write().remove(&id.into_inner())) - } - - fn post(&self, req: types::PostRequest) -> Result { - use self::crypto::EncryptionInstance; - - let encryption = match req.to { - Some(types::Receiver::Public(public)) => EncryptionInstance::ecies(public.into_inner()) - .map_err(whisper_error)?, - Some(types::Receiver::Identity(id)) => self.store.read().encryption_instance(&id.into_inner()) - .map_err(whisper_error)?, - None => { - use rand::{Rng, OsRng}; - - // broadcast mode: use fixed nonce and fresh key each time. - - let mut rng = OsRng::new() - .map_err(|_| whisper_error("unable to acquire secure randomness"))?; - - 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")); - } - - EncryptionInstance::broadcast( - key, - req.topics.iter().map(|x| topic_hash(&x)).collect() - ) - } - }; - - let sign_with = match req.from { - Some(from) => { - Some( - self.store.read().secret(&from.into_inner()) - .cloned() - .ok_or_else(|| whisper_error("Unknown identity `from`"))? - ) - } - None => None, - }; - - let encrypted = { - let payload = payload::encode(payload::EncodeParams { - message: &req.payload.into_inner(), - padding: req.padding.map(|p| p.into_inner()).as_ref().map(|x| &x[..]), - sign_with: sign_with.as_ref(), - }).map_err(whisper_error)?; - - encryption.encrypt(&payload).ok_or(whisper_error("encryption error"))? - }; - - // mining the packet is the heaviest item of work by far. - // there may be a benefit to dispatching this onto the CPU pool - // and returning a future. but then things get _less_ efficient - // if the server infrastructure has more threads than the CPU pool. - let message = Message::create(CreateParams { - ttl: req.ttl, - payload: encrypted, - topics: req.topics.into_iter().map(|x| abridge_topic(&x.into_inner())).collect(), - work: req.priority, - }).map_err(|_| whisper_error("Empty topics"))?; - - if !self.pool.relay(message) { - Err(whisper_error("PoW too low to compete with other messages")) - } else { - Ok(true) - } - } - - fn new_filter(&self, req: types::FilterRequest) -> Result { - let filter = Filter::new(req).map_err(whisper_error)?; - - self.filter_manager.insert_polled(filter) - .map(HexEncode) - .map_err(whisper_error) - } - - fn poll_changes(&self, id: types::Identity) -> Result, Error> { - match self.filter_manager.poll_changes(&id.into_inner()) { - None => Err(whisper_error("no such message filter")), - Some(items) => Ok(items), - } - } - - fn delete_filter(&self, id: types::Identity) -> Result { - Ok(self.delete_filter_kind(id.into_inner(), filter::Kind::Poll)) - } -} - -impl WhisperPubSub for WhisperClient { - type Metadata = M; - - fn subscribe( - &self, - _meta: Self::Metadata, - subscriber: Subscriber, - req: types::FilterRequest, - ) { - match Filter::new(req) { - Ok(filter) => { - if let Err(e) = self.filter_manager.insert_subscription(filter, subscriber) { - debug!(target: "whisper", "Failed to add subscription: {}", e); - } - } - Err(reason) => { let _ = subscriber.reject(whisper_error(reason)); } - } - } - - fn unsubscribe(&self, _: Option, id: SubscriptionId) -> Result { - use std::str::FromStr; - - let res = match id { - SubscriptionId::String(s) => H256::from_str(&s) - .map_err(|_| "unrecognized ID") - .map(|id| self.delete_filter_kind(id, filter::Kind::Subscription)), - SubscriptionId::Number(_) => Err("unrecognized ID"), - }; - - res.map_err(whisper_error) - } -} diff --git a/whisper/src/rpc/payload.rs b/whisper/src/rpc/payload.rs deleted file mode 100644 index 326a6b6e2f..0000000000 --- a/whisper/src/rpc/payload.rs +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Common payload format definition, construction, and decoding. -//! -//! Format: -//! flags: 1 byte -//! -//! payload size: 0..4 bytes, BE, determined by flags. -//! optional padding: byte array up to 2^24 bytes in length. encoded in payload size. -//! optional signature: 65 bytes (r, s, v) -//! -//! payload: byte array of length of arbitrary size. -//! -//! flag bits used: -//! 0, 1 => how many bytes indicate padding length (up to 3) -//! 2 => whether signature is present -//! -//! padding is used to mask information about size of message. -//! -//! AES-256-GCM will append 12 bytes of metadata to the front of the message. - -use ethereum_types::H256; -use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; -use ethkey::{Public, Secret}; -use tiny_keccak::keccak256; - -const SIGNATURE_LEN: usize = 65; - -const STANDARD_PAYLOAD_VERSION: u8 = 1; - -bitflags! { - struct Flags: u8 { - const FLAG_PAD_LEN_HIGH = 0b10000000; - const FLAG_PAD_LEN_LOW = 0b01000000; - const FLAG_SIGNED = 0b00100000; - } -} - -// number of bytes of padding length (in the range 0..4) -fn padding_length_bytes(flags: Flags) -> usize { - match (flags & FLAG_PAD_LEN_HIGH, flags & FLAG_PAD_LEN_LOW) { - (FLAG_PAD_LEN_HIGH, FLAG_PAD_LEN_LOW) => 3, - (FLAG_PAD_LEN_HIGH, _) => 2, - (_, FLAG_PAD_LEN_LOW) => 1, - (_, _) => 0, - } -} - -// how many bytes are necessary to encode the given length. Range 0..4. -// `None` if too large. -fn num_padding_length_bytes(padding_len: usize) -> Option { - let bits = 64 - (padding_len as u64).leading_zeros(); - match bits { - 0 => Some(0), - 0 ... 8 => Some(1), - 0 ... 16 => Some(2), - 0 ... 24 => Some(3), - _ => None, - } -} - -/// Parameters for encoding a standard payload. -pub struct EncodeParams<'a> { - /// Message to encode. - pub message: &'a [u8], - /// Padding bytes. Maximum padding allowed is 65536 bytes. - pub padding: Option<&'a [u8]>, - /// Private key to sign with. - pub sign_with: Option<&'a Secret>, -} - -impl<'a> Default for EncodeParams<'a> { - fn default() -> Self { - EncodeParams { - message: &[], - padding: None, - sign_with: None, - } - } -} - -/// Parameters for decoding a standard payload. -pub struct Decoded<'a> { - /// Decoded message. - pub message: &'a [u8], - /// optional padding. - pub padding: Option<&'a [u8]>, - /// Recovered signature. - pub from: Option, -} - -/// Encode using provided parameters. -pub fn encode(params: EncodeParams) -> Result, &'static str> { - const VEC_WRITE_INFALLIBLE: &'static str = "writing to a Vec can never fail; qed"; - - let padding_len = params.padding.map_or(0, |x| x.len()); - let padding_len_bytes = num_padding_length_bytes(padding_len) - .ok_or_else(|| "padding size too long")?; - - let signature = params.sign_with.map(|secret| { - let hash = H256(keccak256(params.message)); - ::ethkey::sign(secret, &hash) - }); - - let signature = match signature { - Some(Ok(sig)) => Some(sig), - Some(Err(_)) => return Err("invalid signing key provided"), - None => None, - }; - - let (flags, plaintext_size) = { - let mut flags = Flags::empty(); - - // 1 byte each for flags and version. - let mut plaintext_size = 2 - + padding_len_bytes - + padding_len - + params.message.len(); - - flags.bits = (padding_len_bytes << 6) as u8; - debug_assert_eq!(padding_length_bytes(flags), padding_len_bytes); - - if let Some(ref sig) = signature { - plaintext_size += sig.len(); - flags |= FLAG_SIGNED; - } - - (flags, plaintext_size) - }; - - let mut plaintext = Vec::with_capacity(plaintext_size); - - plaintext.push(STANDARD_PAYLOAD_VERSION); - plaintext.push(flags.bits); - - if let Some(padding) = params.padding { - plaintext.write_uint::(padding_len as u64, padding_len_bytes) - .expect(VEC_WRITE_INFALLIBLE); - - plaintext.extend(padding) - } - - if let Some(signature) = signature { - plaintext.extend(signature.r()); - plaintext.extend(signature.s()); - plaintext.push(signature.v()); - } - - plaintext.extend(params.message); - - Ok(plaintext) -} - -/// Decode using provided parameters -pub fn decode(payload: &[u8]) -> Result { - let mut offset = 0; - - let (padding, signature) = { - // use a closure for reading slices since std::io::Read would require - // us to copy. - let mut next_slice = |len| { - let end = offset + len; - if payload.len() >= end { - let slice = &payload[offset .. end]; - offset = end; - - Ok(slice) - } else { - return Err("unexpected end of payload") - } - }; - - if next_slice(1)?[0] != STANDARD_PAYLOAD_VERSION { - return Err("unknown payload version."); - } - - let flags = Flags::from_bits_truncate(next_slice(1)?[0]); - - let padding_len_bytes = padding_length_bytes(flags); - let padding = if padding_len_bytes != 0 { - let padding_len = BigEndian::read_uint( - next_slice(padding_len_bytes)?, - padding_len_bytes, - ); - - Some(next_slice(padding_len as usize)?) - } else { - None - }; - - let signature = if flags & FLAG_SIGNED == FLAG_SIGNED { - let slice = next_slice(SIGNATURE_LEN)?; - let mut arr = [0; SIGNATURE_LEN]; - - arr.copy_from_slice(slice); - let signature = ::ethkey::Signature::from(arr); - - let not_rsv = signature.r() != &slice[..32] - || signature.s() != &slice[32..64] - || signature.v() != slice[64]; - - if not_rsv { - return Err("signature not in RSV format"); - } else { - Some(signature) - } - } else { - None - }; - - (padding, signature) - }; - - // remaining data is the message. - let message = &payload[offset..]; - - let from = match signature { - None => None, - Some(sig) => { - let hash = H256(keccak256(message)); - Some(::ethkey::recover(&sig, &hash).map_err(|_| "invalid signature")?) - } - }; - - Ok(Decoded { - message: message, - padding: padding, - from: from, - }) -} - -#[cfg(test)] -mod tests { - use ethkey::{Generator, Random}; - use super::*; - - #[test] - fn padding_len_bytes_sanity() { - const U24_MAX: usize = (1 << 24) - 1; - - assert_eq!(padding_length_bytes(FLAG_PAD_LEN_HIGH | FLAG_PAD_LEN_LOW), 3); - assert_eq!(padding_length_bytes(FLAG_PAD_LEN_HIGH), 2); - assert_eq!(padding_length_bytes(FLAG_PAD_LEN_LOW), 1); - assert_eq!(padding_length_bytes(Flags::empty()), 0); - - assert!(num_padding_length_bytes(u32::max_value() as _).is_none()); - assert!(num_padding_length_bytes(U24_MAX + 1).is_none()); - - assert_eq!(num_padding_length_bytes(U24_MAX), Some(3)); - - assert_eq!(num_padding_length_bytes(u16::max_value() as usize + 1), Some(3)); - assert_eq!(num_padding_length_bytes(u16::max_value() as usize), Some(2)); - - assert_eq!(num_padding_length_bytes(u8::max_value() as usize + 1), Some(2)); - assert_eq!(num_padding_length_bytes(u8::max_value() as usize), Some(1)); - - assert_eq!(num_padding_length_bytes(1), Some(1)); - assert_eq!(num_padding_length_bytes(0), Some(0)); - } - - #[test] - fn encode_decode_roundtrip() { - let message = [1, 2, 3, 4, 5]; - let encoded = encode(EncodeParams { - message: &message, - padding: None, - sign_with: None, - }).unwrap(); - - let decoded = decode(&encoded).unwrap(); - - assert_eq!(message, decoded.message); - } - - #[test] - fn encode_empty() { - let encoded = encode(EncodeParams { - message: &[], - padding: None, - sign_with: None, - }).unwrap(); - - let decoded = decode(&encoded).unwrap(); - - assert!(decoded.message.is_empty()); - } - - #[test] - fn encode_with_signature() { - let key_pair = Random.generate().unwrap(); - let message = [1, 3, 5, 7, 9]; - - let encoded = encode(EncodeParams { - message: &message, - padding: None, - sign_with: Some(key_pair.secret()), - }).unwrap(); - - let decoded = decode(&encoded).unwrap(); - - assert_eq!(decoded.message, message); - assert_eq!(decoded.from, Some(key_pair.public().clone())); - assert!(decoded.padding.is_none()); - } - - #[test] - fn encode_with_padding() { - let message = [1, 3, 5, 7, 9]; - let padding = [0xff; 1024 - 5]; - - let encoded = encode(EncodeParams { - message: &message, - padding: Some(&padding), - sign_with: None, - }).unwrap(); - - let decoded = decode(&encoded).unwrap(); - - assert_eq!(decoded.message, message); - assert_eq!(decoded.padding, Some(&padding[..])); - assert!(decoded.from.is_none()); - } - - #[test] - fn encode_with_padding_and_signature() { - let key_pair = Random.generate().unwrap(); - let message = [1, 3, 5, 7, 9]; - let padding = [0xff; 1024 - 5]; - - let encoded = encode(EncodeParams { - message: &message, - padding: Some(&padding), - sign_with: Some(key_pair.secret()), - }).unwrap(); - - let decoded = decode(&encoded).unwrap(); - - assert_eq!(decoded.message, message); - assert_eq!(decoded.padding, Some(&padding[..])); - assert_eq!(decoded.from, Some(key_pair.public().clone())); - } -} diff --git a/whisper/src/rpc/types.rs b/whisper/src/rpc/types.rs deleted file mode 100644 index 40e440bf56..0000000000 --- a/whisper/src/rpc/types.rs +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Types for Whisper RPC. - -use std::fmt; -use std::ops::Deref; - -use ethereum_types::{H32, H64, H128, H256, H264, H512}; -use hex::{ToHex, FromHex}; - -use serde::{Serialize, Serializer, Deserialize, Deserializer}; -use serde::de::{Error, Visitor}; - -/// Helper trait for generic hex bytes encoding. -pub trait HexEncodable: Sized + ::std::ops::Deref { - fn from_bytes(bytes: Vec) -> Option; -} - -impl HexEncodable for Vec { - fn from_bytes(bytes: Vec) -> Option { Some(bytes) } -} - -macro_rules! impl_hex_for_hash { - ($($t: ident)*) => { - $( - impl HexEncodable for $t { - fn from_bytes(bytes: Vec) -> Option { - if bytes.len() != $t::len() { - None - } else { - Some($t::from_slice(&bytes)) - } - } - } - )* - } -} - -impl_hex_for_hash!( - H32 H64 H128 H256 H264 H512 -); - -/// Wrapper structure around hex-encoded data. -#[derive(Debug, PartialEq, Eq, Default, Hash, Clone)] -pub struct HexEncode(pub T); - -impl From for HexEncode { - fn from(x: T) -> Self { - HexEncode(x) - } -} - -impl HexEncode { - /// Create a new wrapper from the inner value. - pub fn new(x: T) -> Self { HexEncode(x) } - - /// Consume the wrapper, yielding the inner value. - pub fn into_inner(self) -> T { self.0 } -} - -impl Deref for HexEncode { - type Target = T; - - fn deref(&self) -> &T { &self.0 } -} - -/// Hex-encoded arbitrary-byte vector. -pub type Bytes = HexEncode>; - -/// 32-byte local identity -pub type Identity = HexEncode; - -/// Public key for ECIES, SECP256k1 -pub type Public = HexEncode<::ethkey::Public>; - -/// Unvalidated private key for ECIES, SECP256k1 -pub type Private = HexEncode; - -/// Abridged topic is four bytes. -// only used in tests for now. -#[cfg(test)] -pub type AbridgedTopic = HexEncode; - -/// 32-byte AES key. -pub type Symmetric = HexEncode; - -impl Serialize for HexEncode { - fn serialize(&self, serializer: S) -> Result { - let data = &self.0[..]; - let serialized = "0x".to_owned() + &data.to_hex(); - - serializer.serialize_str(serialized.as_ref()) - } -} - -impl<'a, T: 'a + HexEncodable> Deserialize<'a> for HexEncode { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'a> - { - deserializer.deserialize_any(HexEncodeVisitor::(::std::marker::PhantomData)) - } -} - -// helper type for decoding anything from hex. -struct HexEncodeVisitor(::std::marker::PhantomData); - -impl<'a, T: HexEncodable> Visitor<'a> for HexEncodeVisitor { - type Value = HexEncode; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed, hex-encoded vector of bytes") - } - - fn visit_str(self, value: &str) -> Result { - let decoded = if value.len() >= 2 && &value[0..2] == "0x" && value.len() & 1 == 0 { - Ok(Vec::from_hex(&value[2..]).map_err(|_| Error::custom("invalid hex"))?) - } else { - Err(Error::custom("invalid format")) - }; - - decoded - .and_then(|x| T::from_bytes(x).ok_or(Error::custom("invalid format"))) - .map(HexEncode) - } - - fn visit_string(self, value: String) -> Result where E: Error { - self.visit_str(value.as_ref()) - } -} - -/// Receiver of a message. Either a public key, identity (presumably symmetric), -/// or broadcast over the topics. -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum Receiver { - Public(Public), - Identity(Identity), -} - -/// A request to post a message to the whisper network. -#[derive(Deserialize)] -pub struct PostRequest { - /// Receiver of the message. Either a public key or - /// an identity. If the identity is symmetric, it will - /// encrypt to that identity. - /// - /// If the receiver is missing, this will be a broadcast message. - pub to: Option, - - /// Sender of the message. - /// - /// If present, the payload will be signed by this - /// identity. The call will fail if the whisper node doesn't store the - /// signing key for this identity. - #[serde(skip_serializing_if = "Option::is_none")] - pub from: Option, - - /// Full topics to identify a message by. - /// At least one topic must be specified if the receiver is - /// not specified. - pub topics: Vec, - - /// Payload of the message - pub payload: Bytes, - - /// Optional padding of the message. No larger than 2^24 - 1. - pub padding: Option, - - /// Priority of the message: how many milliseconds to spend doing PoW - pub priority: u64, - - /// Time-To-Live of the message in seconds. - pub ttl: u64, -} - -/// Request for filter or subscription creation. -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct FilterRequest { - /// ID of key used for decryption. - /// - /// If this identity is removed, then no further messages will be returned. - /// - /// If optional, this will listen for broadcast messages. - pub decrypt_with: Option, - - /// Accept only messages signed by given public key. - pub from: Option, - - /// Possible topics. Cannot be empty if the identity is `None` - pub topics: Vec, -} - -/// A message captured by a filter or subscription. -#[derive(Serialize, Clone)] -pub struct FilterItem { - /// Public key that signed this message. - #[serde(skip_serializing_if = "Option::is_none")] - pub from: Option, - - /// Identity of recipient. If the filter wasn't registered with a - /// recipient, this will be `None`. - #[serde(skip_serializing_if = "Option::is_none")] - pub recipient: Option, - - /// Time to live in seconds. - pub ttl: u64, - - /// Topics that matched the filter. - pub topics: Vec, - - /// Unix timestamp of the message generation. - pub timestamp: u64, - - /// Decrypted/Interpreted payload. - pub payload: Bytes, - - /// Optional padding data. - #[serde(skip_serializing_if = "Option::is_none")] - pub padding: Option, -} - -/// Whisper node info. -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct NodeInfo { - /// min PoW to be accepted into the local pool. - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(rename = "minPow")] - pub required_pow: Option, - - /// Number of messages in the pool. - pub messages: usize, - - /// Memory used by messages in the pool. - pub memory: usize, - - /// Target memory of the pool. - pub target_memory: usize, -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json; - use hex::FromHex; - - #[test] - fn test_bytes_serialize() { - let bytes = Bytes::new(Vec::from_hex("0123456789abcdef").unwrap()); - let serialized = serde_json::to_string(&bytes).unwrap(); - assert_eq!(serialized, r#""0x0123456789abcdef""#); - } - - #[test] - fn test_bytes_deserialize() { - let bytes2: Result = serde_json::from_str(r#""0x123""#); - let bytes3: Result = serde_json::from_str(r#""0xgg""#); - - let bytes4: Bytes = serde_json::from_str(r#""0x""#).unwrap(); - let bytes5: Bytes = serde_json::from_str(r#""0x12""#).unwrap(); - let bytes6: Bytes = serde_json::from_str(r#""0x0123""#).unwrap(); - - assert!(bytes2.is_err()); - assert!(bytes3.is_err()); - assert_eq!(bytes4, Bytes::new(vec![])); - assert_eq!(bytes5, Bytes::new(vec![0x12])); - assert_eq!(bytes6, Bytes::new(vec![0x1, 0x23])); - } - - #[test] - fn deserialize_topic() { - let topic = AbridgedTopic::new([1, 2, 3, 15].into()); - - let topic1: Result = serde_json::from_str(r#""0x010203""#); - let topic2: Result = serde_json::from_str(r#""0102030F""#); - let topic3: AbridgedTopic = serde_json::from_str(r#""0x0102030F""#).unwrap(); - - assert!(topic1.is_err()); - assert!(topic2.is_err()); - assert_eq!(topic3, topic); - } -}