Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9396e158b | ||
|
|
7fbcdfeed4 | ||
|
|
f44d885b6d | ||
|
|
6b0a280685 | ||
|
|
78ceec6c6e | ||
|
|
5c56fc5023 |
@@ -9,16 +9,8 @@ image: parity/rust:gitlab-ci
|
||||
variables:
|
||||
CI_SERVER_NAME: "GitLab CI"
|
||||
CARGO_HOME: "${CI_PROJECT_DIR}/.cargo"
|
||||
BUILD_TARGET: ubuntu
|
||||
BUILD_ARCH: amd64
|
||||
CARGO_TARGET: x86_64-unknown-linux-gnu
|
||||
|
||||
cache:
|
||||
key: "${CI_JOB_NAME}"
|
||||
paths:
|
||||
- ./target
|
||||
- ./.cargo
|
||||
|
||||
.releaseable_branches: # list of git refs for building GitLab artifacts (think "pre-release binaries")
|
||||
only: &releaseable_branches
|
||||
- stable
|
||||
@@ -52,11 +44,16 @@ test-linux:
|
||||
tags:
|
||||
- rust-stable
|
||||
|
||||
test-audit:
|
||||
stage: test
|
||||
script:
|
||||
- scripts/gitlab/cargo-audit.sh
|
||||
tags:
|
||||
- rust-stable
|
||||
|
||||
build-linux:
|
||||
stage: build
|
||||
only: *releaseable_branches
|
||||
variables:
|
||||
CARGO_TARGET: x86_64-unknown-linux-gnu
|
||||
script:
|
||||
- scripts/gitlab/build-unix.sh
|
||||
<<: *collect_artifacts
|
||||
@@ -98,6 +95,23 @@ publish-docker:
|
||||
script:
|
||||
- scripts/gitlab/publish-docker.sh parity
|
||||
|
||||
publish-snap:
|
||||
stage: publish
|
||||
only: *releaseable_branches
|
||||
image: parity/snapcraft:gitlab-ci
|
||||
variables:
|
||||
BUILD_ARCH: amd64
|
||||
cache: {}
|
||||
before_script: *determine_version
|
||||
dependencies:
|
||||
- build-linux
|
||||
tags:
|
||||
- rust-stable
|
||||
script:
|
||||
- scripts/gitlab/publish-snap.sh
|
||||
allow_failure: true
|
||||
<<: *collect_artifacts
|
||||
|
||||
publish-awss3:
|
||||
stage: publish
|
||||
only: *releaseable_branches
|
||||
@@ -112,24 +126,17 @@ publish-awss3:
|
||||
tags:
|
||||
- shell
|
||||
|
||||
docs-jsonrpc:
|
||||
stage: optional
|
||||
publish-docs:
|
||||
stage: publish
|
||||
only:
|
||||
- tags
|
||||
except:
|
||||
- nightly
|
||||
cache: {}
|
||||
script:
|
||||
- scripts/gitlab/docs-jsonrpc.sh
|
||||
- scripts/gitlab/publish-docs.sh
|
||||
tags:
|
||||
- shell
|
||||
|
||||
cargo-audit:
|
||||
stage: optional
|
||||
script:
|
||||
- scripts/gitlab/cargo-audit.sh
|
||||
tags:
|
||||
- rust-stable
|
||||
- linux-docker
|
||||
|
||||
build-android:
|
||||
stage: optional
|
||||
@@ -140,6 +147,7 @@ build-android:
|
||||
- scripts/gitlab/build-unix.sh
|
||||
tags:
|
||||
- rust-arm
|
||||
allow_failure: true
|
||||
|
||||
test-beta:
|
||||
stage: optional
|
||||
@@ -149,6 +157,7 @@ test-beta:
|
||||
- scripts/gitlab/test-all.sh beta
|
||||
tags:
|
||||
- rust-beta
|
||||
allow_failure: true
|
||||
|
||||
test-nightly:
|
||||
stage: optional
|
||||
@@ -158,3 +167,4 @@ test-nightly:
|
||||
- scripts/gitlab/test-all.sh nightly
|
||||
tags:
|
||||
- rust-nightly
|
||||
allow_failure: true
|
||||
|
||||
260
Cargo.lock
generated
260
Cargo.lock
generated
@@ -205,7 +205,6 @@ name = "chainspec"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethjson 0.1.0",
|
||||
"serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -287,8 +286,26 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.3.2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-channel 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-deque 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.5.0 (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.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
@@ -299,6 +316,15 @@ dependencies = [
|
||||
"crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.6.1"
|
||||
@@ -335,6 +361,19 @@ dependencies = [
|
||||
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.2.2"
|
||||
@@ -348,6 +387,14 @@ name = "crossbeam-utils"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.1.6"
|
||||
@@ -378,8 +425,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "daemonize"
|
||||
version = "0.2.3"
|
||||
source = "git+https://github.com/paritytech/daemonize#df00295f03450326613f2d616059fd44434a0d74"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -424,6 +471,29 @@ name = "edit-distance"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "eip712"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.7.8 (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.1.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.0.5 (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.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.32 (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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.5.0"
|
||||
@@ -437,6 +507,15 @@ dependencies = [
|
||||
"heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.5.13"
|
||||
@@ -460,10 +539,11 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "eth-secp256k1"
|
||||
version = "0.5.7"
|
||||
source = "git+https://github.com/paritytech/rust-secp256k1#db81cfea59014b4d176f10f86ed52e1a130b6822"
|
||||
source = "git+https://github.com/paritytech/rust-secp256k1#ccc06e7480148b723eb44ac56cf4d20eec380b6f"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -534,7 +614,8 @@ dependencies = [
|
||||
"bn 0.4.4 (git+https://github.com/paritytech/bn)",
|
||||
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"common-types 0.1.0",
|
||||
"crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.12.0 (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)",
|
||||
@@ -1061,6 +1142,26 @@ dependencies = [
|
||||
"vm 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-fetch"
|
||||
version = "0.0.1"
|
||||
@@ -1173,6 +1274,14 @@ dependencies = [
|
||||
"num_cpus 1.8.0 (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.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.3.55"
|
||||
@@ -1216,7 +1325,7 @@ dependencies = [
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1389,6 +1498,11 @@ dependencies = [
|
||||
"unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "if_chain"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "igd"
|
||||
version = "0.7.0"
|
||||
@@ -1407,7 +1521,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@@ -1442,6 +1556,14 @@ dependencies = [
|
||||
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.3"
|
||||
@@ -1488,7 +1610,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-core"
|
||||
version = "9.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b"
|
||||
dependencies = [
|
||||
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1500,7 +1622,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-http-server"
|
||||
version = "9.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b"
|
||||
dependencies = [
|
||||
"hyper 0.12.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)",
|
||||
@@ -1513,7 +1635,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-ipc-server"
|
||||
version = "9.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b"
|
||||
dependencies = [
|
||||
"jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)",
|
||||
"jsonrpc-server-utils 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)",
|
||||
@@ -1526,7 +1648,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-macros"
|
||||
version = "9.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b"
|
||||
dependencies = [
|
||||
"jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)",
|
||||
"jsonrpc-pubsub 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)",
|
||||
@@ -1536,7 +1658,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-pubsub"
|
||||
version = "9.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b"
|
||||
dependencies = [
|
||||
"jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)",
|
||||
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1546,7 +1668,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-server-utils"
|
||||
version = "9.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b"
|
||||
dependencies = [
|
||||
"bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -1562,7 +1684,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-tcp-server"
|
||||
version = "9.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b"
|
||||
dependencies = [
|
||||
"jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)",
|
||||
"jsonrpc-server-utils 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)",
|
||||
@@ -1574,7 +1696,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "jsonrpc-ws-server"
|
||||
version = "9.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b"
|
||||
dependencies = [
|
||||
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)",
|
||||
@@ -1751,6 +1873,14 @@ dependencies = [
|
||||
"linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lunarity-lexer"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"toolshed 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
@@ -2080,7 +2210,7 @@ version = "1.12.0"
|
||||
dependencies = [
|
||||
"jni 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"panic_hook 0.1.0",
|
||||
"parity-ethereum 2.2.0",
|
||||
"parity-ethereum 2.2.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2096,14 +2226,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parity-ethereum"
|
||||
version = "2.2.0"
|
||||
version = "2.2.6"
|
||||
dependencies = [
|
||||
"ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"blooms-db 0.1.0",
|
||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)",
|
||||
"daemonize 0.2.3 (git+https://github.com/paritytech/daemonize)",
|
||||
"daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dir 0.1.2",
|
||||
"docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2145,7 +2275,7 @@ dependencies = [
|
||||
"parity-rpc-client 1.4.0",
|
||||
"parity-runtime 0.1.0",
|
||||
"parity-updater 1.12.0",
|
||||
"parity-version 2.2.0",
|
||||
"parity-version 2.2.6",
|
||||
"parity-whisper 0.1.0",
|
||||
"parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2261,6 +2391,7 @@ version = "1.12.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cid 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"eip712 0.1.0",
|
||||
"ethash 1.12.0",
|
||||
"ethcore 1.12.0",
|
||||
"ethcore-devtools 1.12.0",
|
||||
@@ -2299,7 +2430,7 @@ dependencies = [
|
||||
"parity-crypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-runtime 0.1.0",
|
||||
"parity-updater 1.12.0",
|
||||
"parity-version 2.2.0",
|
||||
"parity-version 2.2.6",
|
||||
"parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"patricia-trie 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2396,7 +2527,7 @@ dependencies = [
|
||||
"parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-hash-fetch 1.12.0",
|
||||
"parity-path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-version 2.2.0",
|
||||
"parity-version 2.2.6",
|
||||
"parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2406,7 +2537,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parity-version"
|
||||
version = "2.2.0"
|
||||
version = "2.2.6"
|
||||
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)",
|
||||
@@ -2672,7 +2803,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pwasm-utils"
|
||||
version = "0.2.2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -3034,14 +3165,6 @@ dependencies = [
|
||||
"syn 0.15.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_ignored"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.32"
|
||||
@@ -3176,6 +3299,17 @@ dependencies = [
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target_info"
|
||||
version = "0.1.0"
|
||||
@@ -3515,6 +3649,14 @@ dependencies = [
|
||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toolshed"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trace-time"
|
||||
version = "0.1.1"
|
||||
@@ -3687,6 +3829,34 @@ name = "utf8-ranges"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "validator"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator_derive"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"validator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.1"
|
||||
@@ -3767,7 +3937,7 @@ dependencies = [
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.5 (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.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pwasm-utils 0.6.1 (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)",
|
||||
]
|
||||
@@ -3945,24 +4115,29 @@ dependencies = [
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec65ee4f9c9d16f335091d23693457ed4928657ba4982289d7fafee03bc614a"
|
||||
"checksum combine 3.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc1d011beeed29187b8db2ac3925c8dd4d3e87db463dc9d2d2833985388fc5bc"
|
||||
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
|
||||
"checksum crossbeam 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7408247b1b87f480890f28b670c5f8d9a8a4274833433fe74dc0dfd46d33650"
|
||||
"checksum crossbeam-channel 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7b85741761b7f160bc5e7e0c14986ef685b7f8bf9b7ad081c60c604bb4649827"
|
||||
"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
|
||||
"checksum crossbeam-deque 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7792c4a9b5a4222f654e3728a3dd945aacc24d2c3a1a096ed265d80e4929cb9a"
|
||||
"checksum crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3486aefc4c0487b9cb52372c97df0a48b8c249514af1ee99703bf70d2f2ceda1"
|
||||
"checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150"
|
||||
"checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9"
|
||||
"checksum crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2449aaa4ec7ef96e5fb24db16024b935df718e9ae1cec0a1e68feeca2efca7b8"
|
||||
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
|
||||
"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015"
|
||||
"checksum crossbeam-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e07fc155212827475223f0bcfae57e945e694fc90950ddf3f6695bbfd5555c72"
|
||||
"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
|
||||
"checksum crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c240f247c278fa08a6d4820a6a222bfc6e0d999e51ba67be94f44c905b2161f2"
|
||||
"checksum ct-logs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95a4bf5107667e12bf6ce31a3a5066d67acc88942b6742117a41198734aaccaa"
|
||||
"checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "<none>"
|
||||
"checksum daemonize 0.2.3 (git+https://github.com/paritytech/daemonize)" = "<none>"
|
||||
"checksum daemonize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4093d27eb267d617f03c2ee25d4c3ca525b89a76154001954a11984508ffbde5"
|
||||
"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 docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d8acd393692c503b168471874953a2531df0e9ab77d0b6bbc582395743300a4a"
|
||||
"checksum edit-distance 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3bd26878c3d921f89797a4e1a1711919f999a9f6946bb6f5a4ffda126d297b7e"
|
||||
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
|
||||
"checksum elastic-array 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "88d4851b005ef16de812ea9acdb7bece2f0a40dd86c07b85631d7dafa54537bb"
|
||||
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
|
||||
"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38"
|
||||
"checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02"
|
||||
"checksum eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
|
||||
@@ -3972,6 +4147,8 @@ dependencies = [
|
||||
"checksum ethbloom 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a93a43ce2e9f09071449da36bfa7a1b20b950ee344b6904ff23de493b03b386"
|
||||
"checksum ethereum-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35b3c5a18bc5e73a32a110ac743ec04b02bbbcd3b71d3118d40a6113d509378a"
|
||||
"checksum ethereum-types-serialize 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ac59a21a9ce98e188f3dace9eb67a6c4a3c67ec7fbc7218cb827852679dc002"
|
||||
"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
|
||||
"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
|
||||
"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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de"
|
||||
@@ -3982,6 +4159,7 @@ dependencies = [
|
||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b"
|
||||
"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4"
|
||||
"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.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
|
||||
"checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797"
|
||||
@@ -4001,13 +4179,15 @@ dependencies = [
|
||||
"checksum hyper 0.12.11 (registry+https://github.com/rust-lang/crates.io-index)" = "78d50abbd1790e0f4c74cb1d4a2211b439bac661d54107ad5564c55e77906762"
|
||||
"checksum hyper-rustls 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "68f2aa6b1681795bf4da8063f718cd23145aa0c9a5143d9787b345aa60d38ee4"
|
||||
"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
|
||||
"checksum if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4bac95d9aa0624e7b78187d6fb8ab012b41d9f6f54b1bcb61e61c4845f8357ec"
|
||||
"checksum igd 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a254e265e8810deb357a9de757f784787ec415d056ededf410c0aa460afee9e"
|
||||
"checksum indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08173ba1e906efb6538785a8844dd496f5d34f0a2d88038e95195172fc667220"
|
||||
"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
|
||||
"checksum integer-encoding 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26746cbc2e680af687e88d717f20ff90079bd10fc984ad57d277cd0e37309fa5"
|
||||
"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.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450"
|
||||
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
|
||||
"checksum jni 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1ecfa3b81afc64d9a6539c4eece96ac9a93c551c713a313800dade8e33d7b5c1"
|
||||
"checksum jni-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
@@ -4038,6 +4218,7 @@ dependencies = [
|
||||
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
|
||||
"checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f"
|
||||
"checksum lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d06ff7ff06f729ce5f4e227876cb88d10bc59cd4ae1e09fbb2bde15c850dc21"
|
||||
"checksum lunarity-lexer 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a1670671f305792567116d4660e6e5bd785d6fa973e817c3445c0a7a54cecb6"
|
||||
"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
"checksum memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b3629fe9fdbff6daa6c33b90f7c08355c1aca05a3d01fa8063b822fcf185f3b"
|
||||
"checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff"
|
||||
@@ -4098,7 +4279,7 @@ dependencies = [
|
||||
"checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee"
|
||||
"checksum protobuf 1.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "52fbc45bf6709565e44ef31847eb7407b3c3c80af811ee884a04da071dcca12b"
|
||||
"checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07"
|
||||
"checksum pwasm-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "90d2b3c5bf24275fc77db6b14ec00a7a085d8ff9d1c4215fb6f6263e8d7b01bc"
|
||||
"checksum pwasm-utils 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e9135bed7b452e20dbb395a2d519abaf0c46d60e7ecc02daeeab447d29bada1"
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5"
|
||||
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
|
||||
@@ -4138,7 +4319,6 @@ dependencies = [
|
||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
"checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef"
|
||||
"checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c"
|
||||
"checksum serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "190e9765dcedb56be63b6e0993a006c7e3b071a016a304736e4a315dc01fb142"
|
||||
"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce"
|
||||
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
|
||||
"checksum sha1 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "171698ce4ec7cbb93babeb3190021b4d72e96ccb98e33d277ae4ea959d6f2d9e"
|
||||
@@ -4157,6 +4337,7 @@ dependencies = [
|
||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum syn 0.15.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b036b7b35e846707c0e55c2c9441fa47867c0f87fca416921db3261b1d8c741a"
|
||||
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
|
||||
"checksum target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c63f48baada5c52e65a29eef93ab4f8982681b67f9e8d29c7b05abcfec2b9ffe"
|
||||
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
|
||||
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
|
||||
@@ -4189,6 +4370,7 @@ dependencies = [
|
||||
"checksum tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "da941144b816d0dcda4db3a1ba87596e4df5e860a72b70783fe435891f80601c"
|
||||
"checksum tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22e3aa6d1fcc19e635418dc0a30ab5bd65d347973d6f43f1a37bf8d9d1335fc9"
|
||||
"checksum toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4a2ecc31b0351ea18b3fe11274b8db6e4d82bce861bbb22e6dbed40417902c65"
|
||||
"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 1.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e5866e5126b14358f1d7af4bf51a0be677a363799b90e655edcec8254edef1d2"
|
||||
"checksum transient-hashmap 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aeb4b191d033a35edfce392a38cdcf9790b6cebcb30fa690c312c29da4dc433e"
|
||||
@@ -4211,6 +4393,8 @@ dependencies = [
|
||||
"checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f"
|
||||
"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6"
|
||||
"checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4"
|
||||
"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 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"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "Parity Ethereum client"
|
||||
name = "parity-ethereum"
|
||||
# NOTE Make sure to update util/version/Cargo.toml as well
|
||||
version = "2.2.0"
|
||||
version = "2.2.6"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
@@ -83,7 +83,7 @@ fake-fetch = { path = "util/fake-fetch" }
|
||||
winapi = { version = "0.3.4", features = ["winsock2", "winuser", "shellapi"] }
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
daemonize = { git = "https://github.com/paritytech/daemonize" }
|
||||
daemonize = "0.3"
|
||||
|
||||
[features]
|
||||
miner-debug = ["ethcore/miner-debug"]
|
||||
@@ -136,4 +136,4 @@ members = [
|
||||
"util/keccak-hasher",
|
||||
"util/patricia-trie-ethereum",
|
||||
"util/fastmap",
|
||||
]
|
||||
]
|
||||
|
||||
@@ -6,4 +6,3 @@ authors = ["Marek Kotewicz <marek@parity.io>"]
|
||||
[dependencies]
|
||||
ethjson = { path = "../json" }
|
||||
serde_json = "1.0"
|
||||
serde_ignored = "0.0.4"
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate serde_json;
|
||||
extern crate serde_ignored;
|
||||
extern crate ethjson;
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::{fs, env, process};
|
||||
use ethjson::spec::Spec;
|
||||
|
||||
@@ -41,24 +39,11 @@ fn main() {
|
||||
Err(_) => quit(&format!("{} could not be opened", path)),
|
||||
};
|
||||
|
||||
let mut unused = BTreeSet::new();
|
||||
let mut deserializer = serde_json::Deserializer::from_reader(file);
|
||||
|
||||
let spec: Result<Spec, _> = serde_ignored::deserialize(&mut deserializer, |field| {
|
||||
unused.insert(field.to_string());
|
||||
});
|
||||
let spec: Result<Spec, _> = serde_json::from_reader(file);
|
||||
|
||||
if let Err(err) = spec {
|
||||
quit(&format!("{} {}", path, err.to_string()));
|
||||
}
|
||||
|
||||
if !unused.is_empty() {
|
||||
let err = unused.into_iter()
|
||||
.map(|field| format!("{} unexpected field `{}`", path, field))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
quit(&err);
|
||||
}
|
||||
|
||||
println!("{} is valid", path);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ blooms-db = { path = "../util/blooms-db" }
|
||||
bn = { git = "https://github.com/paritytech/bn", default-features = false }
|
||||
byteorder = "1.0"
|
||||
common-types = { path = "types" }
|
||||
crossbeam = "0.3"
|
||||
crossbeam = "0.4"
|
||||
ethash = { path = "../ethash" }
|
||||
ethcore-bloom-journal = { path = "../util/bloom" }
|
||||
parity-bytes = "0.1"
|
||||
@@ -76,6 +76,7 @@ hardware-wallet = { path = "../hw" }
|
||||
fake-hardware-wallet = { path = "../util/fake-hardware-wallet" }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.4"
|
||||
tempdir = "0.3"
|
||||
trie-standardmap = "0.1"
|
||||
|
||||
|
||||
@@ -179,14 +179,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn corpus_inaccessible() {
|
||||
let mut cache = Cache::new(Default::default(), Duration::from_secs(5 * 3600));
|
||||
let duration = Duration::from_secs(20);
|
||||
let mut cache = Cache::new(Default::default(), duration.clone());
|
||||
|
||||
cache.set_gas_price_corpus(vec![].into());
|
||||
assert_eq!(cache.gas_price_corpus(), Some(vec![].into()));
|
||||
|
||||
{
|
||||
let corpus_time = &mut cache.corpus.as_mut().unwrap().1;
|
||||
*corpus_time = *corpus_time - Duration::from_secs(6 * 3600);
|
||||
*corpus_time = *corpus_time - duration;
|
||||
}
|
||||
assert!(cache.gas_price_corpus().is_none());
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ use parking_lot::{Mutex, RwLock};
|
||||
use provider::Provider;
|
||||
use request::{Request, NetworkRequests as Requests, Response};
|
||||
use rlp::{RlpStream, Rlp};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::fmt;
|
||||
use std::ops::{BitOr, BitAnd, Not};
|
||||
use std::sync::Arc;
|
||||
@@ -38,7 +38,7 @@ use std::time::{Duration, Instant};
|
||||
use self::request_credits::{Credits, FlowParams};
|
||||
use self::context::{Ctx, TickCtx};
|
||||
use self::error::Punishment;
|
||||
use self::load_timer::{LoadDistribution, NullStore};
|
||||
use self::load_timer::{LoadDistribution, NullStore, MOVING_SAMPLE_SIZE};
|
||||
use self::request_set::RequestSet;
|
||||
use self::id_guard::IdGuard;
|
||||
|
||||
@@ -70,6 +70,16 @@ const PROPAGATE_TIMEOUT_INTERVAL: Duration = Duration::from_secs(5);
|
||||
const RECALCULATE_COSTS_TIMEOUT: TimerToken = 3;
|
||||
const RECALCULATE_COSTS_INTERVAL: Duration = Duration::from_secs(60 * 60);
|
||||
|
||||
const STATISTICS_TIMEOUT: TimerToken = 4;
|
||||
const STATISTICS_INTERVAL: Duration = Duration::from_secs(15);
|
||||
|
||||
/// Maximum load share for the light server
|
||||
pub const MAX_LIGHTSERV_LOAD: f64 = 0.5;
|
||||
|
||||
/// Factor to multiply leecher count to cater for
|
||||
/// extra sudden connections (should be >= 1.0)
|
||||
pub const LEECHER_COUNT_FACTOR: f64 = 1.25;
|
||||
|
||||
// minimum interval between updates.
|
||||
const UPDATE_INTERVAL: Duration = Duration::from_millis(5000);
|
||||
|
||||
@@ -256,18 +266,18 @@ pub trait Handler: Send + Sync {
|
||||
pub struct Config {
|
||||
/// How many stored seconds of credits peers should be able to accumulate.
|
||||
pub max_stored_seconds: u64,
|
||||
/// How much of the total load capacity each peer should be allowed to take.
|
||||
pub load_share: f64,
|
||||
/// The network config median peers (used as default peer count)
|
||||
pub median_peers: f64,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
const LOAD_SHARE: f64 = 1.0 / 25.0;
|
||||
const MEDIAN_PEERS: f64 = 25.0;
|
||||
const MAX_ACCUMULATED: u64 = 60 * 5; // only charge for 5 minutes.
|
||||
|
||||
Config {
|
||||
max_stored_seconds: MAX_ACCUMULATED,
|
||||
load_share: LOAD_SHARE,
|
||||
median_peers: MEDIAN_PEERS,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,6 +345,42 @@ mod id_guard {
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides various statistics that could
|
||||
/// be used to compute costs
|
||||
pub struct Statistics {
|
||||
/// Samples of peer count
|
||||
peer_counts: VecDeque<usize>,
|
||||
}
|
||||
|
||||
impl Statistics {
|
||||
/// Create a new Statistics instance
|
||||
pub fn new() -> Self {
|
||||
Statistics {
|
||||
peer_counts: VecDeque::with_capacity(MOVING_SAMPLE_SIZE),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a new peer_count sample
|
||||
pub fn add_peer_count(&mut self, peer_count: usize) {
|
||||
while self.peer_counts.len() >= MOVING_SAMPLE_SIZE {
|
||||
self.peer_counts.pop_front();
|
||||
}
|
||||
self.peer_counts.push_back(peer_count);
|
||||
}
|
||||
|
||||
/// Get the average peer count from previous samples. Is always >= 1.0
|
||||
pub fn avg_peer_count(&self) -> f64 {
|
||||
let len = self.peer_counts.len();
|
||||
if len == 0 {
|
||||
return 1.0;
|
||||
}
|
||||
let avg = self.peer_counts.iter()
|
||||
.fold(0, |sum: u32, &v| sum.saturating_add(v as u32)) as f64
|
||||
/ len as f64;
|
||||
avg.max(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// This is an implementation of the light ethereum network protocol, abstracted
|
||||
/// over a `Provider` of data and a p2p network.
|
||||
///
|
||||
@@ -359,6 +405,7 @@ pub struct LightProtocol {
|
||||
req_id: AtomicUsize,
|
||||
sample_store: Box<SampleStore>,
|
||||
load_distribution: LoadDistribution,
|
||||
statistics: RwLock<Statistics>,
|
||||
}
|
||||
|
||||
impl LightProtocol {
|
||||
@@ -369,9 +416,11 @@ impl LightProtocol {
|
||||
let genesis_hash = provider.chain_info().genesis_hash;
|
||||
let sample_store = params.sample_store.unwrap_or_else(|| Box::new(NullStore));
|
||||
let load_distribution = LoadDistribution::load(&*sample_store);
|
||||
// Default load share relative to median peers
|
||||
let load_share = MAX_LIGHTSERV_LOAD / params.config.median_peers;
|
||||
let flow_params = FlowParams::from_request_times(
|
||||
|kind| load_distribution.expected_time(kind),
|
||||
params.config.load_share,
|
||||
load_share,
|
||||
Duration::from_secs(params.config.max_stored_seconds),
|
||||
);
|
||||
|
||||
@@ -389,6 +438,7 @@ impl LightProtocol {
|
||||
req_id: AtomicUsize::new(0),
|
||||
sample_store,
|
||||
load_distribution,
|
||||
statistics: RwLock::new(Statistics::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,6 +458,16 @@ impl LightProtocol {
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the number of active light peers downloading from the
|
||||
/// node
|
||||
pub fn leecher_count(&self) -> usize {
|
||||
let credit_limit = *self.flow_params.read().limit();
|
||||
// Count the number of peers that used some credit
|
||||
self.peers.read().iter()
|
||||
.filter(|(_, p)| p.lock().local_credits.current() < credit_limit)
|
||||
.count()
|
||||
}
|
||||
|
||||
/// Make a request to a peer.
|
||||
///
|
||||
/// Fails on: nonexistent peer, network error, peer not server,
|
||||
@@ -772,12 +832,16 @@ impl LightProtocol {
|
||||
fn begin_new_cost_period(&self, io: &IoContext) {
|
||||
self.load_distribution.end_period(&*self.sample_store);
|
||||
|
||||
let avg_peer_count = self.statistics.read().avg_peer_count();
|
||||
// Load share relative to average peer count +LEECHER_COUNT_FACTOR%
|
||||
let load_share = MAX_LIGHTSERV_LOAD / (avg_peer_count * LEECHER_COUNT_FACTOR);
|
||||
let new_params = Arc::new(FlowParams::from_request_times(
|
||||
|kind| self.load_distribution.expected_time(kind),
|
||||
self.config.load_share,
|
||||
load_share,
|
||||
Duration::from_secs(self.config.max_stored_seconds),
|
||||
));
|
||||
*self.flow_params.write() = new_params.clone();
|
||||
trace!(target: "pip", "New cost period: avg_peers={} ; cost_table:{:?}", avg_peer_count, new_params.cost_table());
|
||||
|
||||
let peers = self.peers.read();
|
||||
let now = Instant::now();
|
||||
@@ -797,6 +861,11 @@ impl LightProtocol {
|
||||
peer_info.awaiting_acknowledge = Some((now, new_params.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
fn tick_statistics(&self) {
|
||||
let leecher_count = self.leecher_count();
|
||||
self.statistics.write().add_peer_count(leecher_count);
|
||||
}
|
||||
}
|
||||
|
||||
impl LightProtocol {
|
||||
@@ -1099,6 +1168,8 @@ impl NetworkProtocolHandler for LightProtocol {
|
||||
.expect("Error registering sync timer.");
|
||||
io.register_timer(RECALCULATE_COSTS_TIMEOUT, RECALCULATE_COSTS_INTERVAL)
|
||||
.expect("Error registering request timer interval token.");
|
||||
io.register_timer(STATISTICS_TIMEOUT, STATISTICS_INTERVAL)
|
||||
.expect("Error registering statistics timer.");
|
||||
}
|
||||
|
||||
fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) {
|
||||
@@ -1119,6 +1190,7 @@ impl NetworkProtocolHandler for LightProtocol {
|
||||
TICK_TIMEOUT => self.tick_handlers(&io),
|
||||
PROPAGATE_TIMEOUT => self.propagate_transactions(&io),
|
||||
RECALCULATE_COSTS_TIMEOUT => self.begin_new_cost_period(&io),
|
||||
STATISTICS_TIMEOUT => self.tick_statistics(),
|
||||
_ => warn!(target: "pip", "received timeout on unknown token {}", timer),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,10 @@ use ethcore::client::{EachBlockWith, TestBlockChainClient};
|
||||
use ethcore::encoded;
|
||||
use ethcore::ids::BlockId;
|
||||
use ethereum_types::{H256, U256, Address};
|
||||
use net::{LightProtocol, Params, packet, Peer};
|
||||
use net::{LightProtocol, Params, packet, Peer, Statistics};
|
||||
use net::context::IoContext;
|
||||
use net::status::{Capabilities, Status};
|
||||
use net::load_timer::MOVING_SAMPLE_SIZE;
|
||||
use network::{PeerId, NodeId};
|
||||
use provider::Provider;
|
||||
use request;
|
||||
@@ -780,3 +781,34 @@ fn get_transaction_index() {
|
||||
let expected = Expect::Respond(packet::RESPONSE, response);
|
||||
proto.handle_packet(&expected, 1, packet::REQUEST, &request_body);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_statistics() {
|
||||
let mut stats = Statistics::new();
|
||||
|
||||
// Empty set should return 1.0
|
||||
assert_eq!(stats.avg_peer_count(), 1.0);
|
||||
|
||||
// Average < 1.0 should return 1.0
|
||||
stats.add_peer_count(0);
|
||||
assert_eq!(stats.avg_peer_count(), 1.0);
|
||||
|
||||
stats = Statistics::new();
|
||||
|
||||
const N: f64 = 50.0;
|
||||
|
||||
for i in 1..(N as usize + 1) {
|
||||
stats.add_peer_count(i);
|
||||
}
|
||||
|
||||
// Compute the average for the sum 1..N
|
||||
assert_eq!(stats.avg_peer_count(), N * (N + 1.0) / 2.0 / N);
|
||||
|
||||
for _ in 1..(MOVING_SAMPLE_SIZE + 1) {
|
||||
stats.add_peer_count(40);
|
||||
}
|
||||
|
||||
// Test that it returns the average of the last
|
||||
// `MOVING_SAMPLE_SIZE` values
|
||||
assert_eq!(stats.avg_peer_count(), 40.0);
|
||||
}
|
||||
|
||||
@@ -176,8 +176,8 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
|
||||
}
|
||||
|
||||
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
|
||||
BlockChainClient::encoded_block_receipts(self, &req.hash)
|
||||
.map(|x| ::request::ReceiptsResponse { receipts: ::rlp::decode_list(&x) })
|
||||
BlockChainClient::block_receipts(self, &req.hash)
|
||||
.map(|x| ::request::ReceiptsResponse { receipts: x.receipts })
|
||||
}
|
||||
|
||||
fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse> {
|
||||
|
||||
@@ -69,7 +69,6 @@ pub use error::{Error, ErrorKind};
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::time::Duration;
|
||||
use ethereum_types::{H128, H256, U256, Address};
|
||||
use hash::keccak;
|
||||
use rlp::*;
|
||||
@@ -82,7 +81,7 @@ use ethcore::executed::{Executed};
|
||||
use transaction::{SignedTransaction, Transaction, Action, UnverifiedTransaction};
|
||||
use ethcore::{contract_address as ethcore_contract_address};
|
||||
use ethcore::client::{
|
||||
Client, ChainNotify, ChainRoute, ChainMessageType, ClientIoMessage, BlockId, CallContract
|
||||
Client, ChainNotify, NewBlocks, ChainMessageType, ClientIoMessage, BlockId, CallContract
|
||||
};
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use ethcore::miner::{self, Miner, MinerService, pool_client::NonceCache};
|
||||
@@ -102,6 +101,12 @@ const INIT_VEC_LEN: usize = 16;
|
||||
/// Size of nonce cache
|
||||
const NONCE_CACHE_SIZE: usize = 128;
|
||||
|
||||
/// Version for the initial private contract wrapper
|
||||
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;
|
||||
|
||||
/// Configurtion for private transaction provider
|
||||
#[derive(Default, PartialEq, Debug, Clone)]
|
||||
pub struct ProviderConfig {
|
||||
@@ -203,29 +208,29 @@ impl Provider where {
|
||||
bail!(ErrorKind::BadTransactonType);
|
||||
}
|
||||
Action::Call(contract) => {
|
||||
let data = signed_transaction.rlp_bytes();
|
||||
let encrypted_transaction = self.encrypt(&contract, &Self::iv_from_transaction(&signed_transaction), &data)?;
|
||||
let private = PrivateTransaction::new(encrypted_transaction, contract);
|
||||
let data = signed_transaction.rlp_bytes();
|
||||
let encrypted_transaction = self.encrypt(&contract, &Self::iv_from_transaction(&signed_transaction), &data)?;
|
||||
let private = PrivateTransaction::new(encrypted_transaction, contract);
|
||||
// TODO [ToDr] Using BlockId::Latest is bad here,
|
||||
// the block may change in the middle of execution
|
||||
// causing really weird stuff to happen.
|
||||
// We should retrieve hash and stick to that. IMHO
|
||||
// best would be to change the API and only allow H256 instead of BlockID
|
||||
// in private-tx to avoid such mistakes.
|
||||
let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?;
|
||||
let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction)?;
|
||||
trace!(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: Some(contract),
|
||||
status_code: 0,
|
||||
})
|
||||
// the block may change in the middle of execution
|
||||
// causing really weird stuff to happen.
|
||||
// We should retrieve hash and stick to that. IMHO
|
||||
// best would be to change the API and only allow H256 instead of BlockID
|
||||
// in private-tx to avoid such mistakes.
|
||||
let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest)?;
|
||||
let private_state = self.execute_private_transaction(BlockId::Latest, &signed_transaction)?;
|
||||
trace!(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: Some(contract),
|
||||
status_code: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,27 +276,27 @@ impl Provider where {
|
||||
let tx_action = transaction.transaction.action.clone();
|
||||
if let Action::Call(contract) = tx_action {
|
||||
// TODO [ToDr] Usage of BlockId::Latest
|
||||
let contract_nonce = self.get_contract_nonce(&contract, BlockId::Latest);
|
||||
if let Err(e) = contract_nonce {
|
||||
bail!("Cannot retrieve contract nonce: {:?}", e);
|
||||
}
|
||||
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 {
|
||||
bail!("Cannot retrieve private state: {:?}", e);
|
||||
}
|
||||
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 password = find_account_password(&self.passwords, &*self.accounts, &validator_account);
|
||||
let signed_state = self.accounts.sign(validator_account, password, private_state_hash);
|
||||
if let Err(e) = signed_state {
|
||||
bail!("Cannot sign the state: {:?}", e);
|
||||
}
|
||||
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_nonce = self.get_contract_nonce(&contract, BlockId::Latest);
|
||||
if let Err(e) = contract_nonce {
|
||||
bail!("Cannot retrieve contract nonce: {:?}", e);
|
||||
}
|
||||
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 {
|
||||
bail!("Cannot retrieve private state: {:?}", e);
|
||||
}
|
||||
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 password = find_account_password(&self.passwords, &*self.accounts, &validator_account);
|
||||
let signed_state = self.accounts.sign(validator_account, password, private_state_hash);
|
||||
if let Err(e) = signed_state {
|
||||
bail!("Cannot sign the state: {:?}", e);
|
||||
}
|
||||
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());
|
||||
} else {
|
||||
bail!("Incorrect type of action for the transaction");
|
||||
}
|
||||
@@ -327,7 +332,7 @@ impl Provider where {
|
||||
let mut signatures = desc.received_signatures.clone();
|
||||
signatures.push(signed_tx.signature());
|
||||
let rsv: Vec<Signature> = signatures.into_iter().map(|sign| sign.into_electrum().into()).collect();
|
||||
//Create public transaction
|
||||
// Create public transaction
|
||||
let public_tx = self.public_transaction(
|
||||
desc.state.clone(),
|
||||
&desc.original_transaction,
|
||||
@@ -336,7 +341,7 @@ impl Provider where {
|
||||
desc.original_transaction.gas_price
|
||||
)?;
|
||||
trace!(target: "privatetx", "Last required signature received, public transaction created: {:?}", public_tx);
|
||||
//Sign and add it to the queue
|
||||
// Sign and add it to the queue
|
||||
let chain_id = desc.original_transaction.chain_id();
|
||||
let hash = public_tx.hash(chain_id);
|
||||
let signer_account = self.signer_account.ok_or_else(|| ErrorKind::SignerAccountNotSet)?;
|
||||
@@ -350,13 +355,13 @@ impl Provider where {
|
||||
bail!(err);
|
||||
}
|
||||
}
|
||||
//Remove from store for signing
|
||||
// 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);
|
||||
bail!(err);
|
||||
}
|
||||
} else {
|
||||
//Add signature to the store
|
||||
// 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"),
|
||||
Err(err) => {
|
||||
@@ -684,12 +689,11 @@ fn find_account_password(passwords: &Vec<Password>, account_provider: &AccountPr
|
||||
}
|
||||
|
||||
impl ChainNotify for Provider {
|
||||
fn new_blocks(&self, imported: Vec<H256>, _invalid: Vec<H256>, _route: ChainRoute, _sealed: Vec<H256>, _proposed: Vec<Bytes>, _duration: Duration) {
|
||||
if !imported.is_empty() {
|
||||
fn new_blocks(&self, new_blocks: NewBlocks) {
|
||||
if new_blocks.imported.is_empty() || new_blocks.has_more_blocks_to_import { return }
|
||||
trace!(target: "privatetx", "New blocks imported, try to prune the queue");
|
||||
if let Err(err) = self.process_verification_queue() {
|
||||
warn!(target: "privatetx", "Cannot prune private transactions queue. error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "Kovan",
|
||||
"name": "Kovan Testnet",
|
||||
"dataDir": "kovan",
|
||||
"engine": {
|
||||
"authorityRound": {
|
||||
"params": {
|
||||
"stepDuration": "4",
|
||||
"stepDuration": "0x4",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"validators" : {
|
||||
"list": [
|
||||
@@ -19,10 +19,10 @@
|
||||
"0x00a0a24b9f0e5ec7aa4c7389b8302fd0123194de"
|
||||
]
|
||||
},
|
||||
"validateScoreTransition": 4301764,
|
||||
"validateStepTransition": 1500000,
|
||||
"maximumUncleCountTransition": 5067000,
|
||||
"maximumUncleCount": 0
|
||||
"validateScoreTransition": "0x41a3c4",
|
||||
"validateStepTransition": "0x16e360",
|
||||
"maximumUncleCountTransition": "0x4d50f8",
|
||||
"maximumUncleCount": "0x0"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -32,24 +32,25 @@
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2A",
|
||||
"forkBlock": 4297256,
|
||||
"forkCanonHash": "0x0a66d93c2f727dca618fabaf70c39b37018c73d78b939d8b11efbbd09034778f",
|
||||
"eip155Transition": 1000000,
|
||||
"maxCodeSize": 24576,
|
||||
"maxCodeSizeTransition": 6600000,
|
||||
"validateChainIdTransition": 1000000,
|
||||
"validateReceiptsTransition" : 1000000,
|
||||
"eip140Transition": 5067000,
|
||||
"eip211Transition": 5067000,
|
||||
"eip214Transition": 5067000,
|
||||
"eip658Transition": 5067000,
|
||||
"wasmActivationTransition": 6600000,
|
||||
"eip145Transition": 9200000,
|
||||
"eip1014Transition": 9200000,
|
||||
"eip1052Transition": 9200000,
|
||||
"eip1283Transition": 9200000,
|
||||
"kip4Transition": 9200000,
|
||||
"kip6Transition": 9200000
|
||||
"forkBlock": "0x9766dc",
|
||||
"forkCanonHash": "0xf2fa4bcc417ad374100c2035aa865ff60fb568a83db1b6f6cb8fb52cfebc28b5",
|
||||
"eip155Transition": "0xf4240",
|
||||
"maxCodeSize": "0x6000",
|
||||
"maxCodeSizeTransition": "0x64b540",
|
||||
"validateChainIdTransition": "0xf4240",
|
||||
"validateReceiptsTransition": "0xf4240",
|
||||
"eip98Transition": "0x0",
|
||||
"eip140Transition": "0x4d50f8",
|
||||
"eip211Transition": "0x4d50f8",
|
||||
"eip214Transition": "0x4d50f8",
|
||||
"eip658Transition": "0x4d50f8",
|
||||
"wasmActivationTransition": "0x64b540",
|
||||
"eip145Transition": "0x8c6180",
|
||||
"eip1014Transition": "0x8c6180",
|
||||
"eip1052Transition": "0x8c6180",
|
||||
"eip1283Transition": "0x8c6180",
|
||||
"kip4Transition": "0x8c6180",
|
||||
"kip6Transition": "0x8c6180"
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
@@ -62,8 +63,8 @@
|
||||
"gasLimit": "0x5B8D80"
|
||||
},
|
||||
"hardcodedSync": {
|
||||
"header": "f90247a01865856fb6e4118598117560df31734c74cf725c8edae4db941055ac0afeb207a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479400e6d2b931f55a3f1701c7389d592a7778897879a054563efd593e9682943065880710af9187131127148575efc8bb51d80dfed41aa0a568a1653a6c7d559711be0b91a8e75db76c678dbdd286c75b88e4f0c0d31171a0dab32c5cbe9b9244a7af00afa7f6042a4ac923573e8f2f025b107abe1e3da999b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000004000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000090fffffffffffffffffffffffffffffffd8389b001837a120083046540845bbd803c9fde830200048f5061726974792d457468657265756d86312e32382e30826c698416ef600fb841387b51dae8bc8daa6cde190d3f44797690b4da1ce5fcfcd54bdbb2a6ee6d8c1f7649081ca28b5cd70067ee9f61e27d8184db83705102d5e1a269f2b631b4d5db01",
|
||||
"totalDifficulty": "3020091077015059097853315484608800838133866777",
|
||||
"header": "f90247a0017c9292a6abf6bb9aa48c30f8854dc0e9649bceaf19079b1aac50ebea4fa316a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794007733a1fe69cf3f2cf989f81c7b4cac1693387aa06b81f6633226562f466201f0d097877c903a650f7427aefa7433fb971f601287a0098141509099d91ed2c2e503d43db5b5412d51880994e8e03a86e1d7a9ca8e8ca0495f318f8bfdd4beb517d115ad5d5330aebafe70eb2b1cfb0ab029221a0f48c1b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090fffffffffffffffffffffffffffffffe83975801837a120083035108845c2c49e49fde830202058f5061726974792d457468657265756d86312e33312e30826c6984170b1279b8413ee0655a59d1f7f8db39c22055c614346450e99965fef62efa43b21b20f0966e3e41eb65fcaf6f9ce3a34359dc064827e06f3a29d2c717f287234785d369e56f00",
|
||||
"totalDifficulty": "3324635628632492920129912637269655024947201711",
|
||||
"CHTs": [
|
||||
"0xdb9557458495268ddd69409fc1f66631ed5ff9bf6c479be6eabe5d83a460acac",
|
||||
"0xd413800c22172be6e0b7a36348c90098955991f119ddad32c5b928e8db4deb02",
|
||||
@@ -4470,19 +4471,545 @@
|
||||
"0xbe48c727fb6a32242229eaa09146c76522dcf6bed6d1c6fc1bebf86b5e4ccdb4",
|
||||
"0x8487b971e383272df82cd812a0bf3a2026b85bc3897b4ce9ce48afa00849fe00",
|
||||
"0x60d18b465172f59c0d71594b5273a90cb41db24a5d4c9fc37020f9d8c467a4a2",
|
||||
"0xab4e36d9f17c748c87d89c23b667e3f4e3265e77b62dbd9c92659026f8a53d12"
|
||||
"0xab4e36d9f17c748c87d89c23b667e3f4e3265e77b62dbd9c92659026f8a53d12",
|
||||
"0x2c711e8eec1be3caef6e16a03dcca83dac3a565c93327c67f4e8ea9f2697d9e9",
|
||||
"0x21d7a9a3f22a163767075aa693d82a962e4458b074bd8f3485c4ded1e47c1172",
|
||||
"0x1509cac83867db4b888107974d5c8547dc4aa1db3b8e886291f0f9eb6bd7af58",
|
||||
"0xa555eb6001982080479c9bdd3da9e8668bf90ca9538f16f27a45ae698ec85fb7",
|
||||
"0x1d1f4019fa3ea7ec85ee0a411560738791f2d45fe5fe6242d07267183a852b96",
|
||||
"0x2a8994caa5a27eb3081cc749cda5694da7ed3fbb8a1b4c67d7e92306cbd3c6ab",
|
||||
"0x8389736ba114022ea97a6b3e755d75e74c3c370e9eb916c0f2d73a46a6f6c396",
|
||||
"0x6a2e5d08ea64195a4153fc7bf26b7a99dcc9d31d8f58faa07510a1e87fb1ade4",
|
||||
"0x78b66b38fd9b9cd3a5bc9c91c6f816153c2c28c1055f7ac9ad12ab61f9464850",
|
||||
"0xb2f77834f7a88c4354763a28080d95be44dcb380d01f09292e679a6ed274b179",
|
||||
"0x2070456b2bff0c30ae26b2435af82e153094aeea8102a75541e02a39e9ebe717",
|
||||
"0x4e3bb2fbdc71602a62f0c423aa45398af09cf3ad24f0437027b4590e7056b882",
|
||||
"0xe877208750e7569ce78215659ef78c1656e98b63ab6cc3e1381d7581afbea99f",
|
||||
"0x8c9d8132d01b83cffd1a1fd9d19a3d2fb3a58a0a28602018db44f8b6dd5fe1aa",
|
||||
"0xcece8a371b72873a660f4077f98b04d6a3cde1a150852db3a194a293e6a08b72",
|
||||
"0xe72c8c7211acf2d67bf904cb3428c6fd69c8bd679d52deb3b2e749d5a3124b86",
|
||||
"0x504f53693e747fbba7475dff1aad887ad0124d48cc7892ec488fda56e31e0de5",
|
||||
"0xddb3ce7ac0c7e2d141df857e9e5c083d66f4a62ead742bf6631756a72fb643e3",
|
||||
"0xdc1c6a45dd8308ea319671732de7578a65a025852a3cbe88aa9c5d770d662990",
|
||||
"0xb6a9df772bdb5e7cdff569bb9d3078c081ed53e870f9171a5b0369d84fe6ce50",
|
||||
"0x1b436a7d8c9af357c8175430e1d0d3057096c92bd8bd24fe285fa55673c68b72",
|
||||
"0xa57ce7fed7521e26a908ead89253bc57939459b4c34fba583eececd0f9104e2a",
|
||||
"0xb16c4f6bdce8de6657d049e2e41f91a1cef8623671eacb9ab02fff45cee7c0c3",
|
||||
"0x8d23f3ea29767da41a2090cfceb56a65236a405031b69e614c78fb79c1847651",
|
||||
"0x33149d4876fa24dea4e872ad1da120e01b14e8f9f8270611fced328a36df25ca",
|
||||
"0x266c558486a85de2536e8849d49443e7d39797a6ed817ad7217633c28166bf06",
|
||||
"0x2073a22a1f157025228f8fc82bdaff48d3cb1c9b004ac2cb867c2cbbe7cafd95",
|
||||
"0xcf643d70f96101c99cedcc2d169b0196c564e7a8b235ae093ad25b1ba8981d01",
|
||||
"0x1489101bebc1b60e4c0fa37a94bb946f1b51ba284c667e4b2f3270ef9d264d41",
|
||||
"0xd2eceef739e2601390d872bdbb58f8d67864b000f7fbab07b0354c44279379e8",
|
||||
"0xf67a02d7e7e6da127015e5137f90e30ab43c2e828e62c3f8fe68ad5a0a3011fb",
|
||||
"0x01fe94220a07efc2bde2404c584f67e9b06e47eba05e50e71ad59710d9d6a9fd",
|
||||
"0x509bc80d1a464116b4f234001c3f2d7aac7772cd6ec5fa6ef9606ab8ab273127",
|
||||
"0xea497db1233691b3b86d8fc80ebffe28e6863f445a0b352a1286412a94ad2f6f",
|
||||
"0x80074f0e2cb668b6a8e5b454db4ae5cf2482894f3ccb7648cfa26b3365a0a54b",
|
||||
"0x56c8cd15e5e1a21c6f32f7211d18e1e56c89da688cf11b80c9e55718514eab24",
|
||||
"0x6705989758f03a8c4ee93d44f53b4114a10337694197186b62ac2220188a6879",
|
||||
"0x7581fbe2b0ae6e0a61d073fd1700c3111d2e3e8d9342d50800ba05d026c9edd6",
|
||||
"0x254548091369ad8e0abbd2eb4e31e2c08c8e9dc0256fd0097e50e75831946924",
|
||||
"0xac3119a5ba40f303a9a70ecdef9b6d4722c6adb6592a0cf52e5a312a1a0819c2",
|
||||
"0xbdf30bc3cd852b882c5d410e6d4419e0f36ed17b761b58fe58ded980829d1cf8",
|
||||
"0xebaeb5965563f6fa71006d8c2d285e093a167630c319cee2dfa961c4a583b60b",
|
||||
"0x6cd6717885bd50263a97574e8a56a3ccbca88229bce595b38a454c1184ea94d5",
|
||||
"0xc9bba3a4a72f0f08663e3af36e2bac2d4c8422b2e3c3bd29705e5b86e7c42a20",
|
||||
"0xcc442230a4b6aed5a1eb2b8744a09aefe3cfb9e477816edfa829dfbd3f7c2bc7",
|
||||
"0x4ee48471e3326144c7a48b15fa5f39691ad9f24b82430ca281eb961b18019e02",
|
||||
"0x35016779dd877530f99b1d3372a0a77eff7ae831208d2bdfcd0979b838190acd",
|
||||
"0xe3e9b2a4adc4b4e167376b33b8bf2a00bd64d52f66f4e7b291ade123e8c04402",
|
||||
"0x5a7d1466e2538b62ceada9577079248788e912e23c4cbc52d7b82a1afbf28ec7",
|
||||
"0xb8529c5a2b2dbfb72e70165b9c9291e7c02b43f157092a9b59f3b4cad85fb587",
|
||||
"0x55e90fb666a950f38ae41732269cb69afca8643b75a99bce508a16e06685fc03",
|
||||
"0x651f45996ed080d8a8dcb78550089331ad67c2df33330d1871dff956463aed3e",
|
||||
"0x9c6b4a961663c59dde3a4f0fdf7b68b0b5f049719ba0d10d841855cffc7ee166",
|
||||
"0x15c4d98e0ee4fa1535f83625a20bf1a491852884f8fe608eae6b9bde45986779",
|
||||
"0xc7891cf34fcc61935a43f37bd7f23b674c785ef3b2718358fb627364f3d8b09b",
|
||||
"0x8105fb76c9e1281f0f0d7c520dc6cd1d546d1b4b29cfa398eecd7965af57f408",
|
||||
"0x2a141f1c4e1f1e7be5d10c444dde3d1b5fe1a69f23506d5ed1842957375a1208",
|
||||
"0x9d2313d34a5a6873f2ae1e2684db00e3b693ba962796fe7e78d26a2c49104471",
|
||||
"0x84d0245b1537ec1fae89fd39741d3b6427ceda3933f750befe4e2d8ec022fc1b",
|
||||
"0x93d281ff6bfefc7f94058a0d87cb826b97728c9593ef4876b88c35d26b2163bf",
|
||||
"0x9018a352b09cb36311d0b54bd83a33dcd5dca5ae7032f1d399fec3a0e5a9cb7a",
|
||||
"0xf01486ea17dca0bb45c245c9ba76d20d1a2f4eb718b7d07fb5183736e5cd08c5",
|
||||
"0xf797fec0a8cddb31ace66223e1355211e41211c4bfd33a416c8a1a86e497f630",
|
||||
"0xf24aef69daf4e2b90b4f3bdedddbabbae86a7fd1182947da7306c8145f82cc88",
|
||||
"0xdd14e34cd000ddb6ae6426dcd78cabbf6bfbe5b3ecd7553c4989b3891e506257",
|
||||
"0xc01ece645c7430801ee26b20ad215e221f7de0e060fe004b450b372a6c4438a9",
|
||||
"0x64c836653383bcb165207b80ee8f9377ad25ef1bcd6cf7ba166cca78419898ed",
|
||||
"0x6b1cdfa195df272d7e35ccd843191d5725919c0d8b71f1abf4b950a954770503",
|
||||
"0xebe51f94a9f445d83180c4f075d944fa2f05b5b784b7e3cb4bc1544020bd35cf",
|
||||
"0x7007f6b2c04420d390fbdb6a9fc21edd3249c1a098f8a9383131b8be50c6f975",
|
||||
"0x6b7e93d96f74fc2b068104a017fa5fa74836e8be5599865309361ad9305d9bb9",
|
||||
"0xcccf2dc238c68464f3b0446e06fc48fedf202e366ace5734cdac5a7fe7c43342",
|
||||
"0xfec77750c277cd6bb0937d25d7f598d07cbffb1117b6232e932a296f32b45ce5",
|
||||
"0xb04e6883c11ba3f2b7f0301936c3dcb622f6f78b3d3d57e48c6dcba063038d6e",
|
||||
"0x23fe83ea2f197cb97a3ce5f2afd75652cae993f30bc627e8b9572a0069ae088b",
|
||||
"0x44cc6bf2cee513c826ecc0c130a32b61b0153129721ad2a70d4503b71c128247",
|
||||
"0xb1a11be5f9b37faf1901b1405494af32084641dc02685ec33be7f6e82e360a42",
|
||||
"0x284bdf9a6aa0ac964b0f5e72d0d47c713c919d72fa9e24f42b65d749efb6e2c3",
|
||||
"0x96a0a0e92ed87946a322c9bca6c0ebf70e51e0e825e2ede85b975349f9bb2caa",
|
||||
"0xd73becb6a3d9a155d46a96d17412cf015724a79b166d2961d24125d92e355947",
|
||||
"0xd87f142746611ce081945999f42b8da49cbda03a10bf953ba043417861e27c77",
|
||||
"0x4af010485b5385579a534693bd21ef3768c8920c5b5dc6f9ddf722cc68fd6550",
|
||||
"0x65a2f4ec5cf3b37530fe0672418214618393db47ad1185f285b445cf5f53cf26",
|
||||
"0x1d208df2a2785599565a40d934eb2c0cc1673c1dd43d11685d698122ecf24d6f",
|
||||
"0x046b7211427f0027ed4b69b06cb43986d3cd53d1579a2e078f6904a06ee6587d",
|
||||
"0x053fb3ccc35edc1a1623f8652dd8c239045b02b58ffe7d51337950ae43365990",
|
||||
"0x0721f82b52ff87272e1b0af5f4dc16b90dfaaaf3ba56b91bef555d283c98a8ae",
|
||||
"0x26e93e4e2d0df49a857bfcede7e6ac234d75b71617b561ce39c3db130449e4fb",
|
||||
"0x5ed6d53c35a0aa52d9dd0a244f31be00426292c11676737c7283f567ba485dbc",
|
||||
"0x047add17bd1601a06a32b721165ecb2afb55290e025a828696f234b95c52eae3",
|
||||
"0x6f9e6884745cad7091646f7767a6b17ebffb6482ee4726f5c1f3b9e02d26b77f",
|
||||
"0xab29ed5be2938fb19614da621ea9828eb338514309b3d56efc6f19b57b8f4f6a",
|
||||
"0xd5f14043479e8e37c6545527b2e93a76763a654661af9a25adae2b37dac40672",
|
||||
"0xc43635c1489c3cf3aa231419bfcf7bcb2455a812e4781e5947bb76cf0219d1e4",
|
||||
"0x72117c92f8bab83ccc7fbe5fc6cd931c765238ebd67ad8c4fcf1353eb78c8df2",
|
||||
"0x5deed7df3559ed75d7c2388a6e583067222684741143350f85d236bc5fb4075f",
|
||||
"0x655bbc0590587c030902e32d88cc9113fcbdd5c3371820868768abaeacecb79c",
|
||||
"0x1641c875e8e44b1fa71badcbb4f724a71e2f916d9cd212f09e9039ef41b1242f",
|
||||
"0x40bacdc3f0b53522cdf2aefafa2bbd9c8e9f8fd25b4b33bc19a87d72a5de293d",
|
||||
"0xc35c6cffaf93170662a6f3d4a0152c988b0f4916a33d037801f6f589a6140d08",
|
||||
"0xd4e37125c0ca7844e048bac4e4c6069ec24b0493e4547f53679fc8df2a1e0d66",
|
||||
"0x34c6bcf0e01dd55a1d3a82df0a2edcb2caccbc4be93fdfde32c7718276e59bbd",
|
||||
"0x78e50d394d9d42ca8aae3f4dfccfb32fc3923073a6643c010fd49ec3897c4fe5",
|
||||
"0xeba75c237fb213e893083c5ed0e28dde3ec1f5f1b60edfdddc17a54de9cdfaf3",
|
||||
"0x4f35b9a67fc66492fea331378c479eba921fe0a6f0589b5ac07a44b1ea9b4995",
|
||||
"0xbb4d09d98b27d672259b608f5075575cca49f87cce0519813c1ba4a614d369ee",
|
||||
"0x805550ff80be2d3a543d5076961442f70dcd86e82762149c125d2ce371a32f69",
|
||||
"0x7d81d766cc2d6c66c7bf07be0a416f2a4c0dd2bd94026270a410ea6a051dd4aa",
|
||||
"0x30edcef101041c0e0acecb8d6c15a28c70b8894e56196cddefb5fb6b39658ceb",
|
||||
"0xe5e8d21a00ccabb5cb50b4d04ee1dcc217fb9908e72bf0efab69d4ce7a0b7eab",
|
||||
"0x3793f78e2e582b56fb3d24e3bd4aa69db4741e768795f9681285b0fd34ebd602",
|
||||
"0x4a9378b4ad76df084739b2a63459ac67abf8eff7499210936fb9e6644265054a",
|
||||
"0x2e47b62375a267e104ad2f8bfaf5c985fa7c6f4c438739810517e41141a825bd",
|
||||
"0x73dd638affe08c104b37d53e6cb7884b8a9b4cd4afa4bbec6ef608bab4559dd9",
|
||||
"0xc566dce73da1cc0d069a032045c44ac4ac86b145efd2cb1c363a2d403fd8d7cd",
|
||||
"0xa1a04d193c5408d0edf85e771c6976c5dc8de08c87d906b6c9de1efa4b4fcbca",
|
||||
"0x3245d987f205b01c54448d9a9d76bc7e4174c1021726a68f5feb6748ac373f8d",
|
||||
"0x1f963274b936dde92281f2acd47d3685261b03845efc7d10ab8b02500c239af0",
|
||||
"0xa1e6d69b28aea5d24c3dc301dfb0d6df8ee8fe693d1d0f419cde3b4d20d22a89",
|
||||
"0x31b0711795c77cae9e1b62b87244a5eafa4b1764aa1620d53be7e779db060984",
|
||||
"0xea141234dd00c54ec7912c80baca591b53a058b81fed1673317fa4ec37c2c715",
|
||||
"0xc5902f3941d647da304e94062951b09f4d6da5bcff71517c7a35e8b49c6ac569",
|
||||
"0x54c5677af7e31b84010f8b2941ffed5080e9b4d18b608fcbd230f163a5e04ba5",
|
||||
"0x1a12c56542a01bc58288a58e8e526f2b2c81a5238c83bbfe4808d206b3b9d0e4",
|
||||
"0xa398984bab81b7640674893496b2346eaa8257aad4cc8bb52474f7162fb6fa2d",
|
||||
"0xf454a592314f1b7459d21eeb6ffe54c96ae2e25a7051dab1f8129518d3788cd2",
|
||||
"0x12964f85bcc410fb40e89f518d749c7dc2be7546a960849dcf560eea2ec3d333",
|
||||
"0xb176775c2ff94aedef2c099b936468986874d17eb563086215525acbee2fdbd1",
|
||||
"0x250de6e94d60125bb680e862fcb70823d06e4f27308ea1ecea8a465d5febc860",
|
||||
"0x731cc6817fec665290d7637c67fdbc72bbebae70a13f9346f47f3b368eb1e4a5",
|
||||
"0x489e955cc77f16d8aa16bada320127380315eac978f0fd1033baa04911123332",
|
||||
"0xa88623d9f89221dce46b55ca7248581b4e3ace94a7cccc9fd44a3b03412fa729",
|
||||
"0x4a65c1808d79577b36dfb5fa74d0deb96e90d0105ee373d552685cc4f73d35d8",
|
||||
"0x7aa8228e6f8fb98a23e1599e12ecd84c879475a23f10660e763e338a347c9002",
|
||||
"0xa825d02023ab76478b83a2a61dcc5cf6c05ca741b960043adba3f13d1c99612a",
|
||||
"0xd3c90e414048510d9594ae7a593e25641b6baf93f9625cc4a3caade094f02e07",
|
||||
"0x4d631d9024a4e75111189835b4ec0007a6951d710512dd7b91a8d56da95e57b2",
|
||||
"0x0158c7f49affa675f04aa4ebdf11e88bab8c6dc0abfecc0100d32489b0d59f1a",
|
||||
"0x4ec96496694ef53e17b11b3122cc45cce3a4fe1a252fe9c0d379b302db1318ac",
|
||||
"0xeec44b94d9f6c7359047d257ffd5f1f5cee87b3ebd6845dc1fe48be686a9ba69",
|
||||
"0xe24c7b667382186196e5da605ce75a5cd9c7e4803fb2e268b4de5848737db026",
|
||||
"0x14a551850f945f02fa2ab84c6ffe3d17930c81f5b15e94ce92342c3646b8cd9a",
|
||||
"0x7e9a72469a52662bbcacd449dc7df8fa75e5cd1480dac3b635148a34e0f9e375",
|
||||
"0x2dd4fd0394574c5d8c9272c42175e3a88d8501753bb25a77fa937123d0c2538d",
|
||||
"0x6f044eb54fd2b8e51d89f6db2ee6e654b144d9e69cddf79b7899e06af5bf39f5",
|
||||
"0x091d894513fca5d2239f6b5ce73737aa2657c90c2af196b2b893ba0b0205859c",
|
||||
"0x78b9ca580e3eca0808e60420ead000365eeafa60c6394e055a653a7f536c8b6d",
|
||||
"0x595cfe325aa600ef0d7de6b2eb717b33c92dfbb1e3ecb9487fe332c878e9f121",
|
||||
"0xe1dcc22aa44a0243f67dcb0aef7e0eb1ceee269b0faa67633698f8ee29155115",
|
||||
"0xddd3354d4167161c6b18173ae24fffac2cff12a8bcb3ddf1d964907716685cb3",
|
||||
"0x55c02c02eb758f8c9aab60a2432d29e73b567e3e3d0148611ce4569fa074c5d9",
|
||||
"0x5d07d5a7d45fe16a0521c55b3dd898b922c619e5b938535e2f974f11b4edfdc7",
|
||||
"0xf75100a1a2f8d6daf0dc36569cbf579ed6e52838b163fe50b5deaad8fe7bd07d",
|
||||
"0xb6391d028fbc88825eabf1dc4566e58112e6d3b7cfe6d387d0fa58df387df78f",
|
||||
"0x8a58fab34ada4d30eecbad64a001015c91e446cf14e95c9d7a3d894530787b03",
|
||||
"0xac13b36d0eb0483ab8cf21f49c78f9e32092c74c878ff520ed3a3c0080fdf5fd",
|
||||
"0x358c1021fbdf028ec82b811cdeb504adc2611a1ab477e8a191f780592d598361",
|
||||
"0x6a7af25eb6d089ad63712187d1414e8bd04fb6bfbac26e6086b1a3e2d66f6f7e",
|
||||
"0x6bebf617fe66129dc8c9dadef1a74df05567ed2fc89f4e5c4dd36327d6b0a5e5",
|
||||
"0xc658d76eb9f3fb91327c64668b08d3e9bfbbb2099bf5d9aeb6a61b8e4f893c39",
|
||||
"0xfe68ecc66eaa625842068f8b5ad4bdf5cbce0c82ecfafa4bf48b08de49c58595",
|
||||
"0x26eba0a168f2e5f3ffcb37955839d37432b39f2f447497854d42d802826fb1ee",
|
||||
"0x8b4f8964062d0f00e40a41d21852a2e22abba1f4cf75f594797e368e4df21f7d",
|
||||
"0x916993123d91b181d145e2668efed6eae3845fd76d4765df3a04d8adde8c7142",
|
||||
"0x21b91051b5c5fbd22164a655de8485cfc2a9eff58ed09d6fbbb454560898daed",
|
||||
"0xb52ca76065bcf43f06335977ab87d6c809dcf4e6cdfc452ac2018b77c64089a5",
|
||||
"0x9437d47e7f7fb4c95aacd5e1322a9c7b457888dbfb1e485142cb16ba805b5ded",
|
||||
"0xb7cb3f7394fef7de5884e165c8441d441c98c1080865b604dd21766759f7fffa",
|
||||
"0xfaff33c1aae8882f3a9d84678b55b8945b037b1f05785f2874079da71caf4aa5",
|
||||
"0xe032d77db9e882bfb3d5adc89d9a9812fb9775f98c03765ffdf0c40d0dbf11f8",
|
||||
"0xc9c4ded5821040df911b23d92d8270fc83164531c9d631c4d8e228a71e189ba1",
|
||||
"0xa27f4dd47059fdd4afe78ce8c9029c9a7c0916d10cc7dba1ce011c886d2275bb",
|
||||
"0x6dd0d3b0e07535d8350cb888c75ca355c4abb07bfc6ceba3eae02095324679a0",
|
||||
"0xee4a3c6125a5e69848457d35f9614705b651c68857b03968bc5420a809d1d9b8",
|
||||
"0xc14947239d0a7061d796313aca233659059b13740e63f967f8bd4806feb895ff",
|
||||
"0x36d9ad9d6ad2e1a1aca489524303aaa8534b137c5e9ef708d589b7a75cbf6500",
|
||||
"0x497c4cf7509b12ebd34d5c0520a24ae1b6e1c5c050bb3412d7d1a702c48d07d3",
|
||||
"0xac81f9bcce95283b7f188f16eb68d22adb22dc1cff0853739c46f27523a653b7",
|
||||
"0x2f0818ee594d2d1fbfe9a6a03cf4ae08dee69fbb9c289c2789d792f14a3e4e7d",
|
||||
"0x9beb888d86489512a9d1d8d6461490e23599c1595aa5b4d4b113d18c0eb99697",
|
||||
"0x7708aa2a88f9b41cde9f338da6d2f9dde2b57f1538f7aeefb66a0e9dce488ff3",
|
||||
"0x548a59ae0782fc5202431a472a8dc5e21ac83b68dc88afb2b0afd56cd3e880dc",
|
||||
"0x271397f861034f73fe228e3921d835a313041c260a6c2ca3d0c28d407d17ba89",
|
||||
"0x732b95d3ebdf035182863a540bc002cef70f98b91f90ee8dcef2285a970ade6b",
|
||||
"0x605d419ecbcdaa05d6f09c9734469847f52164df26417d7807f959986b843869",
|
||||
"0xdc56e2800a637475662632ac37e9eec90d9f3962a454d6f32ee7f56426ab9f42",
|
||||
"0xdcd35988aa6982f14b3a2834f841c755ad42dcc61eb0dc6122f2aa9db65fcf92",
|
||||
"0x659b06e2aa0359e3743f59d2b128cf85bfabc20ac9d8c42a89d9cdda2bef7697",
|
||||
"0x45377222c3910f95b3f5d0adef823dc4184189cab9eed54b9997d2b30fdbae43",
|
||||
"0x15d66d0b9347f12bf9c408910edf41623f460244f309076d55cf969dc158b36e",
|
||||
"0xe5de2158113351c0ea314eb12b28f9502c307decf883ed480c53c1310c9f7c11",
|
||||
"0xc7342c08dd73fbe5801cc6c8764c53f924d6a77cd5a5b8bdbb22d2b6d3e0336e",
|
||||
"0xf10486dc44a10f7eec6739dd56e0f7519507777e78e3974d974ee0bd931f03d0",
|
||||
"0x5985752f0b3849e9cc6a5d38b65617be10b52b8fd0c2642c328b864d59a23f9f",
|
||||
"0xce899213bc8a178cbf2a8eb86f045f59895530b0af5e209b60553869000070f4",
|
||||
"0x724dd7b5e5d0e93017a62faffd534e423610c494ef0b5689304bc80a68df0e46",
|
||||
"0x318073daf925850c412d6a1f2db2d2093268896a88c49e822ccae4070b406408",
|
||||
"0x74fd03921ea801d4618fa7c2375c4369a15c7774564dfb0373b5913bdc9a6df8",
|
||||
"0xb10896436461c2660cfe36a60c433b32ef8a1e772f3f9c16ae243840a1aa3aa8",
|
||||
"0xecfc10478a368b186e4299447ec9d3ce27ca8386274c545f3edac3608ad6cd0d",
|
||||
"0x288c697b29a0d2c5d45b9507aaf62bc4bfecca1b34e1a5ce13a17711af0df3cd",
|
||||
"0x429fc79b89ad00887096417282a571de77fe73dbfd1564c13bec5830b38890cd",
|
||||
"0x4d859982815d597e31d0e5374d7077b59a4b9beec01040608bad05c6a9d0be2a",
|
||||
"0x1addc6e300b6120cd196bcb7340ca163879ac55f7c9069be77947aa01e778cf9",
|
||||
"0x2e648a4eefda9d35e26b148028a0b336ef7d75809454e36795edb9d3fdeff550",
|
||||
"0x814e65df8c720e8c9c7d64c76b6b9554328d6c780b5f3dff96aa3e29506e7e49",
|
||||
"0x4841c38f2a920bb1a072b9a7853dd7d2e44dcbc35e2737aa2f3cf6652dabfdd2",
|
||||
"0xdc63afe7974211db82b2602c26d6ace8d93f34736c0fa68eac3bd5d2c1af7484",
|
||||
"0x2052d12d3534c5d7693d420c8e080bcee99ce10ccebc7451272b7ef08f8a3dc2",
|
||||
"0x4985769df508ccc2add583d187095a8af4ca73a5be2506a506146e23dbc67d55",
|
||||
"0x4a6744d48fc2d85cc99e419483ec1111ccb6fc7ab13e4eba28a29269acb30952",
|
||||
"0x527b0514dd83336c0524add1f01799512517378b2045c4ed43ccc0e977553bac",
|
||||
"0xfc33e6a20033fff41a9b885dc5bbaa848a0f244fe89b4a1af261d382c544fbaa",
|
||||
"0xadce691a1b4224bd56682784ec106dc93af89185721db8b427eab6b0ff22776c",
|
||||
"0x0fdef2a9525541c1e96172c8a33ccc08ed759dc024d437b82126ff03b65e1684",
|
||||
"0x7aa9e8c1e9da3c68080f099281b367de26838ec7bcd474590ae101fcd68a7116",
|
||||
"0x7eb70f0a4c42726f5b3a67ab12bdf5b4e6bf5e4c347b6c32f6fd3a04c4a003e5",
|
||||
"0x546f5f3c2e44b7a61d6d0dafd802fcc114db7acb7f887f0dd0b1878979fd3eac",
|
||||
"0x5c5294fd77081b1a23037c34170b568358011be2fdc0e3ffa2230b1a017ec5b9",
|
||||
"0x6c854d3de1f2a28dd8f6320a534b915b032078377e3b880eaa8e9a3775768e2c",
|
||||
"0xc2be1a4c7a5351063084c5907f19e63f25e048f3e27648a6d9056d3d4b635c19",
|
||||
"0x5d0673ecaecd00cc8847827bbda1dd7bb21f537e5e10f10d8b5944496b55e9e6",
|
||||
"0x8a1bfbf9dd51b727c17e8920019caae599f53d37563935c584c84cbdebf942e2",
|
||||
"0x75649056bcd2a520811d8f9d5c0c629f492e93fcdcd298c0657a8c857fdf2cc6",
|
||||
"0x71421a3eb42eeba485fb90918511b0a430311856c27a968e52096c6cdaf0cce1",
|
||||
"0xdf19c2d595ec81bacaabd39217e100e6731f8bd7c3d6c5bfd987b27f89be146d",
|
||||
"0x573839677ae138670609c499107217fcae5de5fa9905c78a21b07f7a906a0a53",
|
||||
"0x1e8db73865049c1cde4004c2a60945edd9ce9847c360a2feb36205e1f1cbd5da",
|
||||
"0x52a602e7c2f462a39288465c733d2133b7f77a7a77d7e60e214d4d4eff917258",
|
||||
"0xdc30b1767c78f2ac953c5046e6440b7de488933cdece18fa3566f00d8a96c92f",
|
||||
"0xfc0baf76004e7d87b36ea52de7d886c9b3be44bb8063549bba31b63b87ac3f92",
|
||||
"0x3426c56db720ccd184f2e49385e96e5a37fd378cd486de8fadcc7d5e727aebb8",
|
||||
"0x39fc1cbf20cab093c75b42931fc6371e9851cc4357badb6961359e07b9d97fca",
|
||||
"0x6b3725ada81d881bbe245d4b55c7be12926b42d0e58dfc8fa38d54f68ff79ee4",
|
||||
"0x69bbab0d123bbf03499984698c162f9dfb81375f6bf9f2e643badcc8e0281ef8",
|
||||
"0x101cf365b31d4b37910433ffa265e1a978d4f80c478936809e1bcca7baff59b1",
|
||||
"0x65d26a27a34ca4ca98c2b5f609e4596344514698f2137547e815dfcf91f5bb36",
|
||||
"0x3b8598589a6ef43efe0b7a64832360ab02e150c4b4ec02f2a028d2522f4c0ba7",
|
||||
"0xd8776563bb522b42a25cb2714f9ea294fffa4c4fb5ad040a9458e10637861f34",
|
||||
"0xb1b85b8c9bea99a2bde16ab99b2d7e42571ba15dfeaf28a2897322bb444171b1",
|
||||
"0x803cc4e5c23bd3842736b36a425364416dc69a3efb95f3c7fdbf7782d6de7edc",
|
||||
"0xf5803ed2a130a19600ca887ad06813482b678ac0eee049cbb2e56e39a90d9867",
|
||||
"0xd3f6125b061023531cbd3d625c9382631e2fecc75945dd6f78f883353ec98962",
|
||||
"0x7b1d59f8c9869a9c73a09385c82762b86fcc995e75db00bda388e1423be19df4",
|
||||
"0x2b7a8289bf9f93c589866af1a224c34943433b6a5a571df7dc4524bcf2adde42",
|
||||
"0x9199d73dd306fc9038b36212c3ed7706d8b0f30cfcf3359408a8e6fd8ebab0a7",
|
||||
"0x0cc8446d21ccc31bee04addd48b50f394390ef3340745555f9d7ccc3c13eefbe",
|
||||
"0x80052b2097d7f836090ba9f94ad7c33066453d8ee62a2e215a82003ed92568f1",
|
||||
"0x231c2d4e6a6039342ac03f766fac9f0a587ef6828d071e63a1e3f660fff13263",
|
||||
"0xcfa0cae426e4b6b9422d72a687d50139654fc3351993b5d38ea4fc6f8944e662",
|
||||
"0x9dc837a60a2c18493fd3b40083996f18a44f37a7a762ca649bc5d8c8b272aaca",
|
||||
"0x30b4651c3496efed5997116b7a12f9cdc24040a4e110478f1a2330fbba48fbec",
|
||||
"0x99567d98d73fe92c81c69aa465bd62b02492839519f0380686349da64caaf758",
|
||||
"0x9618fd333f89357a22bb3101cc386003c2745f8a24b5affd2be9cc3c26a93e34",
|
||||
"0xe539d1a4de2a57dd65cea42302fac83aed570f2fd7a318c6d226dea58d5f94c1",
|
||||
"0xe69e3ef5d9d8cf08d3a354da7179d5bab9c68b4b6ea6a7e5a58cae9d901b5f35",
|
||||
"0x97cd995441161fe4f50a339c6323020db6f56b9221b549194a93b9895a043e59",
|
||||
"0x803e04ee8a22299e779491a6749b7d57973a59fe714d080f03749bccde30d5c6",
|
||||
"0xace95a4dc00b3fef757aa76445b1edc582246eeebb7c2bdc71fa648e06428485",
|
||||
"0x1c8bd27b857dd8dd5a671fb4aa6cec3c41ca1c747c9f86edca8f389ac637e09f",
|
||||
"0x554b4d1bbe92718dd1aba2a9d10f48f803287d2c26108bf3cab2574a91375511",
|
||||
"0xd5856bc0794ddc23049639fc438c360d66e4b3eb1f96516ba0c02c46cda8fb49",
|
||||
"0x25a4c78e490c1d5fc6c1252b2ba13ca9ad5113a5c64e02220969146c5bbe9730",
|
||||
"0xa8dd024efc7f03b61ff9926421107b54c42f5f9c936d956e9b6c6eb36f9b242b",
|
||||
"0x26d488bff06eff3fe1912ce985ced65325e09e1c01a95bc16d0df22481690640",
|
||||
"0x01b89e65f1a82ea7e456e96e2ddf29be42dced7c7ea9b12e841bd973506dcc25",
|
||||
"0xf0f5d3392e3fb73697e44755d38c84bd99394ec2ef9dec841049baf8166e6d55",
|
||||
"0x91c4f65d698130b132d0a33e44c27ea55f477cfb134224139ba0264b5737ed42",
|
||||
"0x1d2295b21e1bb810d35471fe17adc80e59c6c57b5037de90c88e99021ac78b24",
|
||||
"0x667cc89337d382967727f0740819a2f6298dd5f2b7b5e82dc45e3f1c15c5e998",
|
||||
"0x908ac480019af2d3c3017c413077a73905e27ec01721aa499c24fa7e13bf8ad9",
|
||||
"0xbeb7ebf8ffae71221e5ff149e3333fdefe6b11780d439b45a874129b2c25d71d",
|
||||
"0x235c2ed0e3fd286df5cabca5f0f61bfe356ad0d7c8bb9c642814d01f2fd26699",
|
||||
"0x22fc5189fb1945ae98d89e50b258514baaf525e8137e9581c99339e1097e33c1",
|
||||
"0x148256253b74711fc61f8af7d4efcf8694752426715a0a4c788556feb68c1a2d",
|
||||
"0x26ad59543cfd91bbe37cd8fd1995b81d0f095ac4e02cda0b4f5f40c9db6d320f",
|
||||
"0xee340b0f25450c23a5c9b16cd5aa59657a9992c1c341172bcc8c9038eb6789fe",
|
||||
"0x5da22a0b434f041d76be1d53ef63ae24031861dce1ff4baa3511d67664060bbd",
|
||||
"0x6009815cd4da0162884d0be4cfe6936f1b251f50e9c89d9fdc2f0af430012e11",
|
||||
"0x9342474f10ce871dfeb70fde89b181075b18e973d4806364d52f83f6be4d69f1",
|
||||
"0x3c4c598faaf3c9fa8b9c1bf856872e65e3d53435177992b29a7674198e7df000",
|
||||
"0xdf0d952cca28797dbb503cb8c7192e811a7dfc0797379391f839341e210078ee",
|
||||
"0xdd48052c8e9a9ec62a58a580abe1c420b89393bfffe53fbe6b7f9079b5ec4d3c",
|
||||
"0x028846c41592546d1d11375d00c1d10a18c06ee5f968b2ce8e8c42bee7aa263d",
|
||||
"0x4aa08dc77484d15f8423a1fb2df0fb0c67729de1d48e61831884e49d7697674d",
|
||||
"0x084c72f0f0fb1cdd4ff80ebc9379c9d29c8d1e35cc45901a67b231ac201ff9b4",
|
||||
"0xe904eec627d475fcb3e61b2db0366a24ebcc286ccda2b1614049fa17ae9a9d85",
|
||||
"0xc3fb5ed46f30f566b1d5547d5e00c4c999960dcbd1b60f0b337bff8da30c054d",
|
||||
"0x1503b399ce9dd9f1cd8005036006e4bae2b3f05fbb1393b8f5d868332605c98b",
|
||||
"0x70de442d35565513b92d8cdb95c6537f72d7e099e217af7c260fdf92499ecfae",
|
||||
"0xc6b86174c627fdb002d187feada8c72b9bc6b1fa5d11785988c72b06480ef1c8",
|
||||
"0x06212692407b9b701ed1b115a8ae1cf07668cd7498cbd7c5c58e9b52a0a0093e",
|
||||
"0xfdc722f761f07d5c52d36c147ab8e80a666af5aa77fdefa37e86c7b2518aca12",
|
||||
"0x7396bd40c5677c5f4388d956ec437071cd4352cac44b7b97eecebb6c5c99b507",
|
||||
"0xf634b80145415b2086d4a20746bef5c7d34034ac6601487792889be39bac54c8",
|
||||
"0xfb8cbee41b441d5424cbd12b064b04f5618f0b360b1c2ae5ffb90a0194e61106",
|
||||
"0xf77c56810755fe69822c1250295092fb8e7c88d789706d6769d0cbdeb386137b",
|
||||
"0x9e0f084e1184804e850784a4e376b8acbf58f4235a3829d49217c888e4e5d535",
|
||||
"0x6b39d6de64090febb49dc6f6a31eb606d8e278b7ff4fa9e1a9774d3d9dc71f81",
|
||||
"0xb5d9e67fd6cdd93c74536e5a74dac8eb8ea6914c2315c22e079d61eaecb18c29",
|
||||
"0xac3dbb4fa1da752bff5884154e02ab975e993201e69ef9ca532c8b1e00b1628b",
|
||||
"0x747b42a7005c41ae222f8c2ecd4bf7000c274366237e68b2e95a64cef3799ef2",
|
||||
"0x8f06b121d921274a0d8da269d19ffd6a417cec988f0018647966960ac822fd60",
|
||||
"0x13451d3ecba978d3c0d88c2dd4068cf060e949889ea514306c23a7a1ec574ca2",
|
||||
"0x737f8bd85cce3a392da15c8e68c3ea9748a6523ab92dba8d1c6c8081311c3ef7",
|
||||
"0x83cca9d97f8c4b2776b96d887c5ab1e8e6172970d6d237da98dd5c6ce07db7f3",
|
||||
"0xb5bb3dd88ecfe2e4ac4e4be2b605cc7c5e79024782628a95df33c4967dc8251a",
|
||||
"0x640c6050e63c965742cdfc6d3217eca459a24b3acf3a5ec64eed9bd7bbcefaad",
|
||||
"0xe74478890f186d41b5157dbcef1f2754a35a5af332095dd25d34caf01580062c",
|
||||
"0xfaabbc9703e049e49d1d22da051cfe6cae0697e7e728e954bd5abc40d0e2014c",
|
||||
"0x8aa4a19c4ec50a68a6541ecc11d4473cf02773a0ba7bb4012ccd76ae11ce8bf9",
|
||||
"0x01abdedb1886ec8220a4f6ce6a43863ab77215f03e8116b03f3efe44bee658b7",
|
||||
"0x18e44060426a05d3d9500b7d6ae6d99133109bac39942ab825ae81c551c0ef39",
|
||||
"0x19a60001bc92289e73d3a8320f7601eae1bcf70f65c3a43ff6af1b2c2c17355f",
|
||||
"0x2dbc82c8ba30fe69aa00c7abf129cd1a65e8fb9398b2ebca0843e526e001ce1b",
|
||||
"0xec773bb4cf7d35feb70ea19cb53cd64af2cd34c1e4f1e282bd3cd0b39720bd2e",
|
||||
"0xc966e10e5568661e0930bbff1561e2de413f9f89d984b46224ced75d57213401",
|
||||
"0x29658e4a7e4dec3e9a449902af34ab93fe760c6a1695c1a97e0a093e3d187782",
|
||||
"0x98b0b147d3f6a8a21dd9e3fe01082dc9031c193dec4def76e53ad1c13269c3d7",
|
||||
"0x288b646ad44d5f4ea299917a9b69c9c07b5ddc50e93d03fc1c1ed653599d7a7d",
|
||||
"0x87c26362fb801ef77354375eadedc1489a4fc39401521cb06b604fd9288e4d82",
|
||||
"0xa5cf4098a62d2dcc87e589c8269dc712e2ac2be5adf07ca33c520556d4da0cc1",
|
||||
"0x4f253c49f87e135e4a0941409fe70ec6b29f6b0d9aed3f654f5eee7f4eb0a486",
|
||||
"0x5370ae8dfa2a982dab86dfb45950a50ffd29039781b2b065ef5057f7b43b84d2",
|
||||
"0x044f950f82d97f0fa164ec4174c238203d15f4cf32dd739931c3e291e0902ed4",
|
||||
"0x792c3af8c8808b95f38d6e21979450e3ebeae129f0a9a9d0cf2951252801dd7d",
|
||||
"0x141e09e2ca0a96d50e7c12aae794ed0f22c0441fcc51d1c4e179f891cb06ee1e",
|
||||
"0xc475b7c5293efed9c9f0a5e1d8800e4dd10810bc7dd33c406da131b15524ee48",
|
||||
"0x0018d2c2e814bff6fa8f5e162de402db71dd9eea45d320f60e280a00aadafc2a",
|
||||
"0xc68336fc8904777bf8ec4d5a61f79f2e847c028889bce39ae0f307d11efad818",
|
||||
"0xd6439bee30ae288496a022e04f5c21b9b047edbb370ea3e33ed236a217c6ca0c",
|
||||
"0x03e096d6ce43823e4543ddbfd8f55528d49c38b5c701c520e95a51547b5b193b",
|
||||
"0xfb80f18124198c12b6359ea7805f110f9f25a6686e7a917c110ad511554e634b",
|
||||
"0xcf9a0aa896f5c54982a827e07314a81b58beb3de97fdf112ee1539ee47158f59",
|
||||
"0x948cb5dcc88a5502d82a292e7337d5d010055ae4964c09b2c6d55b0401e1781b",
|
||||
"0xb91614b49ef967aef3b113983cea703a60097e0cd4c0d9cf1ad6f36d594750a9",
|
||||
"0xe29abd32597fbfcffb8d185931986a8519fcabca73abfc5be6e42bc8b6439374",
|
||||
"0x9ebbe1b1037327c4f2f3c9b6191b6f28aaf9b04dc58f3314ec2c16ac1eaba9b7",
|
||||
"0x098529aee4331ef9ee00ee624912ed1a7cd8d63ebf27a96059f56e60ab9a95b2",
|
||||
"0xcab9578772277f3e1f7a6920b96df6ce3e6d7f60c42925b7bb1ac1d71d733718",
|
||||
"0x43cb7ebd27e7207228ecee10cfb11aefe077bf03053877b53c42a41e4f39f607",
|
||||
"0x12ef0a09e0f5dc9d89b01cfd66b34182e80821f0f9d86c80327086a9960ab9f8",
|
||||
"0x30bafa474c54d2912897a13911cd1df20988be102333bcb9fc54905789f87718",
|
||||
"0x383205a3e0b07e359127b28c3ad1b451564091893edc87d3d92e081111dd20e7",
|
||||
"0x74382d6b160ac8b1e7b86abf22cb943b43afc4480be4b09c87c8b796f22eafe9",
|
||||
"0xe84cbe2ed5a30d7b25ec540b1934577387626fb894e4248c719d417ff1b0f171",
|
||||
"0x068dd7e31af946d3427dd6822c22577886c32a7f1e3a3bc61ee061be7c456751",
|
||||
"0xff5309cf09f24f3666205ae09e542634bb848c47031ebc10079d96819095b60a",
|
||||
"0x82991fe8d442572aafde5e2f6d2c0ba0823ae72110c2f4b65a0d39244db460a0",
|
||||
"0xb86d9c8e378420595feae0f728e00736f5b72ffe37e289088c03235b3ae8f556",
|
||||
"0xd678cbfb25960efc5a64d48f2ada784a5b43123da5db8636804c2ab7d1a6cc36",
|
||||
"0x1ceacf6ae9acb6e7438e6971b133aa7a8ba7b648c130962c3f60b7449266f3ff",
|
||||
"0xe81d2bbf5efd047428040f86caa351a8785a0ee864e74760569318c997b25116",
|
||||
"0xde530285d36c4a50d0f91d48a42e9b85de919e51246b6aa54a55eb2041b17289",
|
||||
"0xd5b86ba5c24729d199007133a8000314476135cc5b84dbc49de41a7d9f3060c7",
|
||||
"0x95b2fc8a95f67d4d67cba712a98bb3b35fc4b92581f5eab9e737f2555592bf3d",
|
||||
"0x9808b933e2c732a2b4d6229467430cd73715ac93d1000c470dbe403d0f14b6ce",
|
||||
"0x8a708bd8ec41b6aeb1f822ed4892c4d685ae3fd1a752a0b79a10a6cf288c8a26",
|
||||
"0x689c7f32af0a20de771b9c03efeb6304b08a421cbc2f178b63cd284f3d74407f",
|
||||
"0x0375180a004265f4a38946353455c221785c3f6da1dcbde2c09c373fa42c434c",
|
||||
"0x764f8f063124e8226a65af4823b260354924781b339a93f6eeb0622e10333741",
|
||||
"0xd0a28ce8c50017ad2e0bf1c1f8193792228b879688f01354552e656272bd7936",
|
||||
"0x20b12fa78b655cce826e87bd18cf938917d37c306cb620afef671b0548b1e1d0",
|
||||
"0xc94540910d93236aff53413af46597dcb51c02508f14bf81817966ef32799c00",
|
||||
"0xeeda2d6df1baa5dd3cf904c4402e4e99aaaaa16b6f3a0832bffad5492fd9e725",
|
||||
"0x74de84a1158dfb1af0c1478cf0b048ae8b0ae00139588e91d37a6874b524522b",
|
||||
"0xe9c92a1b7b298d7222890fffbd5ae4d2f8a8e0c5519cd06c3e34fd94b1f12767",
|
||||
"0x18261f839aab59220f72e4e4bfa7a8dde2d3c5cd8c0179d2ff32a151f7af92c2",
|
||||
"0x35fd53473534a3bdbf0ada29529991bfe6ac1865cc6786306b92dc26a6aa8116",
|
||||
"0x6f40f093a08a00eefae4cb848462ba663b62f089005acb49a8f5ee3f1a767375",
|
||||
"0xb0d49e32ac2ddef6bf0729ce9212c4bcc053aaa7cf8dd6649247749bab68cfac",
|
||||
"0xb616c68f680b5cc9be3fc5bca40621514109dbc1316b848f1f3ce18b05e76d33",
|
||||
"0x3ac8fe2f016f98eaa25451e36ce4fcfcbd160c0306c57f2edea049cdf541538a",
|
||||
"0xb45e54eca70d4943eecaebfef3dc2219e79bdc71bddaec6adf53a1d058a65a30",
|
||||
"0x74284120c5fa1ddcb7c294533eedd0b1ca2703626b7361543cb8e2a922177aae",
|
||||
"0x8b15208a8b5d6ddfbe8b09148d497a5e3fc267b8f1d24272814d72f4826f8e88",
|
||||
"0x3f9d4dcd96d5c902d4ef5655c10620c3e5a25edfa6fa093b0546d05c7c724d8c",
|
||||
"0xf91ef06068659865a7c94e955f9c01ba4794214ebab33c9ec7030b2dfa47604a",
|
||||
"0xadd5bb3d2f06ada2edb35754833e1bf1e317edff3e33535e135e67b4e4c5fdca",
|
||||
"0x5aeb51beab2c98d3f77f2f04a39c2dfaed3ff759de9f5c16d4d88692a95eca60",
|
||||
"0xeab1ab2c49609df8e29041ffefea082f92941aefae697fd915b9aeee8665c57b",
|
||||
"0xbcf847e97f7f6fa5a16075ea54a67254e21fadeaa38d7aa42d16f223fd179faa",
|
||||
"0x07f25413e56e9dfbd9bedf4b0676755f0cdbbf10b8f13ac7ef360bef7ac905d1",
|
||||
"0x373e2f4dca323c9c4b4cd0cf4a1eb04ce7fd4d7548078200d9ffde2e3fa4cee7",
|
||||
"0x5b9fd47a0d9cb386ccdcb2755934bfb8b27be896e52afaf18a0d130bcdff5432",
|
||||
"0x1050a77797e0fc3a98c1d172496b8a3bbb2b045ebaa6b8b4af4e1542180f464c",
|
||||
"0xea29629a5b08050c92412a25506d4d6dd8ea0f6bd8c962422d8f4913090a6d2c",
|
||||
"0x1cc76b2b9f505bc358df15e285ac7cdde0009a2a19de9fcb5a44156ff76c5585",
|
||||
"0x03cbbc18173700023643a8d6d7ff57cb0503336794c5fbe0b705a95034b7af80",
|
||||
"0x9e2a5b70615a5c057d2956ac21838616ffd5cfb26fdf7398c4ad988a8cbc27ed",
|
||||
"0xb2ef5e7c15e169ad2ec00e254415f4fa6c1f0635f728a98724dca4335e1c7801",
|
||||
"0x50f9e432ed8c0c999685735ad95a4c2b3e0c0002d1159056392ed541ddca9b11",
|
||||
"0x40ecd3140c4c8b02ce66489b39842d92ca64d05c30a9eddbfe675dded3fb6f3f",
|
||||
"0xa1417d5ef7d185323ae3452fd99d54d5336f69ff866cfba06838da5efe273ebc",
|
||||
"0x93b51cb9e7c9ca4e5f67fae55992ef6b0869c8c2effc8d244791cd2275f5f136",
|
||||
"0xd9889fbd8c71e14316b5dfd46a6d049ec05127e5d3db6f0a31a935cb86b42d0f",
|
||||
"0x384e1275612d71431d80d155015010e22b8b14a0648328bf1b32f92b1673308b",
|
||||
"0x5eeac9b703836463fc0019c1b6f4be32282f4ade6778f897f6cf9d4c33e16085",
|
||||
"0x2bdd040915ac28a0a604b727f062cf49fd413c44bcfaa2cc5eaaa9224248ef51",
|
||||
"0xd7c85280a461c43df6c82f6ef60a973f2581c428394996191eeaa0d06a4b6473",
|
||||
"0xe57d7c71a989601a066f8447a5be928ad0ce47d5413eceb709761654a30667e4",
|
||||
"0xe1514ee44d1db8307cc08177a343887b5bd843cc510c5153b7f251aa10704f87",
|
||||
"0x2898a69c885bb4cfcffa45a8aa63a8e81a40d4e3a7e1ff218ca3b4c70b836e08",
|
||||
"0x27b66b51bb80e11582319b2fe176490daff05f0ce72a9dbfe79691676a455e0c",
|
||||
"0x321d7bade5b8a1adee64bb0362620d99a5528831cdc0f1bd0258c9d66e4515f1",
|
||||
"0x0a5443109753ff0845752dc998e82148d1de44d8347e51accd03cdbdcf405acd",
|
||||
"0x1ef3a558361ee68b83346d40624b13d3170519e10dd18f60b6c5d7804f3c2d1b",
|
||||
"0xf81fadde2aa798e6c151b35f764009d754f01aa0d8dacdd1a8f3109bd5d711d7",
|
||||
"0x1af5b4d637429fab034b33b5fb3add567b5896608bcdc9e8d86a7d7ce762b4c2",
|
||||
"0x8dcae50013fcabdc708c7b99c46fb4a68c75971775c135b7f7727bdf8db645a2",
|
||||
"0x74dd3719b6229c2f675e41bae47aa0de9587608e8ad76e9dd911d695cbe3727a",
|
||||
"0xca02fb569c275518e1cb95018a3b67dde118e82ade5cc9a43e118afa8da5e3d4",
|
||||
"0xd5a890bef2beb2920a7ac22be90d0ed203dcf6b37ed0b3d2cea1fcc8f68c6d98",
|
||||
"0x917c9cc04ac1f8afae96f0dc9b49161c352ed4e1da970e17d52fca31b8c1978a",
|
||||
"0x009f16eb012717beb87fcc2b4417c8096a621fddc2421d02d84d0bfe92cd3587",
|
||||
"0xe5ff9e2a1ffbcf1f53cf716b7caaa1317151ed8b81bd641bcf41eeadcdb0790f",
|
||||
"0x2dfd67dc149772d4aa52588315df62edbc8a9340f2b3afb367f7f1564c1b8503",
|
||||
"0x830df571a176933f6a72ac634933d61e602f744d1e3a046b7b639c9d2110580c",
|
||||
"0xb2a030fd70ad2290c69e9b41220c14800d9adc33d7f8af049120fc6526a654d4",
|
||||
"0x8a013c2432c60aa6315b1adc7155dd022df24129106cccbdf463273051ee0efc",
|
||||
"0x55040dc6b554877d752f2f0bf0d97f71b34583b3d939587106affdacaf5d7c0a",
|
||||
"0x251817e5c81aba6d675d47d688f19d5cb6957cea13ef3e15c66a6573b84af432",
|
||||
"0x708cc1d141308870ea3ac36e5781cbfa3b5af327c565231123496354b5a9e74b",
|
||||
"0x4efb2006e58b45c648d27654d45f1f797d4798217b2d0b54958fc576f115d81c",
|
||||
"0x7d1bc87fd32a1b2ce74fe3784be5c0fe2e625ceac523ad55da7ebcd3cd1cb94f",
|
||||
"0x575cc8bf8070ea53a54d027cb7d42eec9dffb85489013634350a96a434cb498b",
|
||||
"0xc69812969e85bcd2e7e048b4323a726691c78d1902c1f1dbdd3893c11b9df8f6",
|
||||
"0x6193928c0a4e47c9409c1af5d945a8c92c9e28a76570dfaaecfdda8071d6558d",
|
||||
"0xb8982114b6b5da8849c27ee7d23280e15aa780248ebd36e5d364f2a05ae1fe45",
|
||||
"0x49577950e5a3d4a06629686f9353adecdad6670fc9f4e6d3eaf6735296cc6775",
|
||||
"0x6444155176ebe3b62f028ad79f058271d5b317360c0b97b7fe8c2a642da88229",
|
||||
"0x9a3f0adf9c7b9380f60baf1d524590bec23b4d3790b3909ec8da3e79303252c7",
|
||||
"0x591f122777ae7a313b6c4ba8c052d483f2aa3a687b5d85ed39e19d7e0c0e6de2",
|
||||
"0xe3d68466d0747ab6245e5d8620eaae080a0f3c2aa34a56defe8adaef56d5474a",
|
||||
"0x8d0f53bb5f7610133f04a763263815e092e43b0016369866e959d22eca466d85",
|
||||
"0x8c8f0fff76977593112a64826f15ec0e909d9a1f1589e121a37f73d881e6b696",
|
||||
"0x9ab1411e7ef5b295ab1a9dfe4ee1cd1a740af5b7e4b0a0e9c4a5f37e803c7c94"
|
||||
]
|
||||
},
|
||||
"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": 5067000, "pricing": { "modexp": { "divisor": 20 } } } },
|
||||
"0x0000000000000000000000000000000000000006": { "builtin": { "name": "alt_bn128_add", "activate_at": 5067000, "pricing": { "linear": { "base": 500, "word": 0 } } } },
|
||||
"0x0000000000000000000000000000000000000007": { "builtin": { "name": "alt_bn128_mul", "activate_at": 5067000, "pricing": { "linear": { "base": 40000, "word": 0 } } } },
|
||||
"0x0000000000000000000000000000000000000008": { "builtin": { "name": "alt_bn128_pairing", "activate_at": 5067000, "pricing": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 } } } },
|
||||
"0x00521965e7bd230323c423d96c657db5b79d099f": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
|
||||
"0x0000000000000000000000000000000000000001": {
|
||||
"balance": "0x1",
|
||||
"builtin": {
|
||||
"name": "ecrecover",
|
||||
"pricing": {
|
||||
"linear": {
|
||||
"base": 3000,
|
||||
"word": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0x0000000000000000000000000000000000000002": {
|
||||
"balance": "0x1",
|
||||
"builtin": {
|
||||
"name": "sha256",
|
||||
"pricing": {
|
||||
"linear": {
|
||||
"base": 60,
|
||||
"word": 12
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0x0000000000000000000000000000000000000003": {
|
||||
"balance": "0x1",
|
||||
"builtin": {
|
||||
"name": "ripemd160",
|
||||
"pricing": {
|
||||
"linear": {
|
||||
"base": 600,
|
||||
"word": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0x0000000000000000000000000000000000000004": {
|
||||
"balance": "0x1",
|
||||
"builtin": {
|
||||
"name": "identity",
|
||||
"pricing": {
|
||||
"linear": {
|
||||
"base": 15,
|
||||
"word": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0x0000000000000000000000000000000000000005": {
|
||||
"builtin": {
|
||||
"name": "modexp",
|
||||
"activate_at": "0x4d50f8",
|
||||
"pricing": {
|
||||
"modexp": {
|
||||
"divisor": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0x0000000000000000000000000000000000000006": {
|
||||
"builtin": {
|
||||
"name": "alt_bn128_add",
|
||||
"activate_at": "0x4d50f8",
|
||||
"pricing": {
|
||||
"linear": {
|
||||
"base": 500,
|
||||
"word": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0x0000000000000000000000000000000000000007": {
|
||||
"builtin": {
|
||||
"name": "alt_bn128_mul",
|
||||
"activate_at": "0x4d50f8",
|
||||
"pricing": {
|
||||
"linear": {
|
||||
"base": 40000,
|
||||
"word": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0x0000000000000000000000000000000000000008": {
|
||||
"builtin": {
|
||||
"name": "alt_bn128_pairing",
|
||||
"activate_at": "0x4d50f8",
|
||||
"pricing": {
|
||||
"alt_bn128_pairing": {
|
||||
"base": 100000,
|
||||
"pair": 80000
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0x00521965e7bd230323c423d96c657db5b79d099f": {
|
||||
"balance": "1606938044258990275541962092341162602522202993782792835301376"
|
||||
}
|
||||
},
|
||||
"nodes": [
|
||||
"enode://56abaf065581a5985b8c5f4f88bd202526482761ba10be9bfdcd14846dd01f652ec33fde0f8c0fd1db19b59a4c04465681fcef50e11380ca88d25996191c52de@40.71.221.215:30303",
|
||||
|
||||
@@ -34,7 +34,11 @@
|
||||
"eip140Transition": "0x0",
|
||||
"eip211Transition": "0x0",
|
||||
"eip214Transition": "0x0",
|
||||
"eip658Transition": "0x0"
|
||||
"eip658Transition": "0x0",
|
||||
"eip145Transition": 6843780,
|
||||
"eip1014Transition": 6843780,
|
||||
"eip1052Transition": 6843780,
|
||||
"eip1283Transition": 6843780
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
@@ -50,12 +54,10 @@
|
||||
"enode://6e3d1b39cbd2a9c4f053a27e68fd90d0bac83691dfdc4a13c59f2555078a71e63c5daaee5a82aa6db500512760a5456f86076bf8bbe8011c27c82ed7d6f5fb26@45.77.140.210:30303",
|
||||
"enode://f4698ad485a027497e1cc992bb5f7cecee2b32a44c47202738d8d0eecfab719541988d0cbcbc5ea94c6c959e5cddeb85fc6ae75fb63dc3bf87cdbe9e6f615e9d@206.156.242.64:30303",
|
||||
"enode://31dffed97f8fed1f34fe66453280a89cbeeda60cf28f6fbb212ebbefd7c7566a02c1c7d5c00bbbb49b9fa8a49f157e0f786f379ca9bcbf2fea24de70d70a22b6@206.156.242.61:30303",
|
||||
"enode://efe1a701595b7bfc20cc71a70dcdf1d731a5ca618cbc1bd0059edc6658d5958a2b243cc88112c7ded48f3d331a26a6a76b4d3f4a51260b5fb5d13f798e5786f0@206.156.242.62:30303",
|
||||
"enode://6bdc7553ab2e4914cb47774c1e6d8c8f47ac7c3981891f85f65d06f208ea1bc4d3bf982b330950e0a0cd127efd7145c4df7113159a1d4a06ed722e6c16d0ac6c@45.32.215.190:30303",
|
||||
"enode://872d82a24144bc007658fb6fac0dcdfb9b63aeb05ef563a06d0186f2d1e5ffbfc5c4f1244891a8a86ef70682b9d24382e654b305224883698862e2df647a4d23@45.76.236.247:30303",
|
||||
"enode://b11fbc6cde81c80be69508aca8ffea8460680a25a9c151b683293f8617282062b8e8e139bf91e88cedf60068a3cf927b0d48832fda5169b58a8f7ce442de6fb4@206.189.76.132:30303",
|
||||
"enode://96678da10ac83769ab3f63114a41b57b700476c5ac02719b878fa89909a936551bb7609aa09b068bf89903206fa03f23e1b5b9117ca278de304c2570b87dcb27@18.208.191.114:30303",
|
||||
"enode://47ee6bc6312210caa0087a5754221f7666e37ab2d5c10cfe386ecdf9e5f935fc55726b37d10f0eaf493f762a270445d3e6d569891c328759b8baeb39f6f6ed2d@34.224.215.151:30303"
|
||||
"enode://96678da10ac83769ab3f63114a41b57b700476c5ac02719b878fa89909a936551bb7609aa09b068bf89903206fa03f23e1b5b9117ca278de304c2570b87dcb27@35.175.15.164:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } },
|
||||
|
||||
@@ -37,7 +37,11 @@
|
||||
"eip140Transition": "0x0",
|
||||
"eip211Transition": "0x0",
|
||||
"eip214Transition": "0x0",
|
||||
"eip658Transition": "0x0"
|
||||
"eip658Transition": "0x0",
|
||||
"eip145Transition": 6464300,
|
||||
"eip1014Transition": 6464300,
|
||||
"eip1052Transition": 6464300,
|
||||
"eip1283Transition": 6464300
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
@@ -51,19 +55,12 @@
|
||||
},
|
||||
"nodes": [
|
||||
"enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303",
|
||||
"enode://0171db265a569636372566e86cb7a69306fe5c15a8e2ed5bed4010012fa1d146ae4918a688cf1bd3fd98e8c2d5c3705d68ff941c88ab974ff52c7fc8606ce2f8@35.168.201.160:30303",
|
||||
"enode://9a4a3788471af17a8346da7fc41057f037da692d4fad0ac5023fc0c88cf64666d74ee9f6396012eec98cd6bc772418b42dddd823a38e359bbfdc4fc93ba38de8@34.229.128.189:30303",
|
||||
"enode://881e56dc71472376b65ddaad5e42ee73084c96eb368c1e219b4fbcd73ffa339703cca168b6620cb1eabb7b8dcfe72e563117670c7ce94298c20d04b709c65697@165.227.111.36:30303",
|
||||
"enode://8e3733fe1606b17dd743e7d4fdb7ec1e0b909aab3c5289beabcb36e9d3175ca05bb36a52589090d4899c85a9219622ea4cbb9541057b722be65241db557b51a8@52.201.214.63:30303",
|
||||
"enode://f1a5100a81cb73163ae450c584d06b1f644aa4fad4486c6aeb4c384b343c54bb66c744aa5f133af66ea1b25f0f4a454f04878f3e96ee4cd2390c047396d6357b@209.97.158.4:30303",
|
||||
"enode://0d1e0372f63a3f0b82d66635ea101ecc0f6797788a078805cc933dd93e6a22f7c9fa51ab4e2d21da02d04480ef19f3bbb9a2b41dd1c262085d295a354bb8b0f9@18.217.47.209:30303",
|
||||
"enode://ab083db73da15b3995ac9c68035cdb32901835a823cb848fccb672e43dd21f14428706118d6fe5b921d8e741f122f35aad0255bc86807b1d17bcfa1e86e40a14@165.227.37.104:30303",
|
||||
"enode://a2a8545ccceaa1152721a213e6aca912c6d89a37bc6eea73999fdb08a95c6f07a79057f7f41c75c128f58ed77a13985e5c33c8c50d6a3d60b50c43f5f69c1fcd@52.170.16.34:30303",
|
||||
"enode://8e0af07c86ec36590bb6368e7ad0c45b6dc658f5fb66ec68889a614affddda5e021bd513bcf4fb2fae4a3bbe08cf0de84f037cd58478a89665dfce1ded2595c7@34.236.37.74:30303",
|
||||
"enode://182ee200ca134dc4d6390f3d5aadbcd80df0f7f24335830335d142573eacce4eeb919d30e82c5df588034e167e6ba6dd11187502ac9264a71005127f6b146a99@159.203.95.241:30303",
|
||||
"enode://b022ff70b5fcaf9596ae5efed99a8198b4ae0578ee9d17b733609d803a75cef95d3a2a18e50dca9a7c3b26139f158c59eaf8b5fb8d1d331c9a46934a78acabe8@206.189.76.128:30303",
|
||||
"enode://4a16c9a597dad00c000523fc2f4302bb8bb7f05949ec0ea4a516f0c6de9e191662f650ab55ea2a05f5af0f0eeee879800074f7263328f09181c7156059b7b6b7@18.221.62.190:30303",
|
||||
"enode://6527003feb6b534a7fac297b92d2f368bdf679d8f489639006c78640bf3ead3cdbf88a88a7bec29e6e29510122f279adf094d1b1b6e9e5005c4584aeb482c4a0@35.172.215.190:30303",
|
||||
"enode://99f335f9dffe978f7a925590f011909f2db1a2b28b05a0dc10da47e285e3d20b2d827caee258607c707c5261d9ba5f7f5b899dd025315c0afbfd5536a91ccf73@18.209.18.86:30303"
|
||||
"enode://b022ff70b5fcaf9596ae5efed99a8198b4ae0578ee9d17b733609d803a75cef95d3a2a18e50dca9a7c3b26139f158c59eaf8b5fb8d1d331c9a46934a78acabe8@206.189.76.128:30303"
|
||||
],
|
||||
"accounts": {
|
||||
"0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x0", "pricing": { "modexp": { "divisor": 20 } } } },
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -106,7 +106,13 @@ impl ClientService {
|
||||
info!("Configured for {} using {} engine", Colour::White.bold().paint(spec.name.clone()), Colour::Yellow.bold().paint(spec.engine.name()));
|
||||
|
||||
let pruning = config.pruning;
|
||||
let client = Client::new(config, &spec, blockchain_db.clone(), miner.clone(), io_service.channel())?;
|
||||
let client = Client::new(
|
||||
config,
|
||||
&spec,
|
||||
blockchain_db.clone(),
|
||||
miner.clone(),
|
||||
io_service.channel(),
|
||||
)?;
|
||||
miner.set_io_channel(io_service.channel());
|
||||
miner.set_in_chain_checker(&client.clone());
|
||||
|
||||
@@ -117,7 +123,7 @@ impl ClientService {
|
||||
pruning: pruning,
|
||||
channel: io_service.channel(),
|
||||
snapshot_root: snapshot_path.into(),
|
||||
db_restore: client.clone(),
|
||||
client: client.clone(),
|
||||
};
|
||||
let snapshot = Arc::new(SnapshotService::new(snapshot_params)?);
|
||||
|
||||
|
||||
@@ -229,6 +229,7 @@ pub struct BlockChain {
|
||||
|
||||
cache_man: Mutex<CacheManager<CacheId>>,
|
||||
|
||||
pending_best_ancient_block: RwLock<Option<Option<BestAncientBlock>>>,
|
||||
pending_best_block: RwLock<Option<BestBlock>>,
|
||||
pending_block_hashes: RwLock<HashMap<BlockNumber, H256>>,
|
||||
pending_block_details: RwLock<HashMap<H256, BlockDetails>>,
|
||||
@@ -538,6 +539,7 @@ impl BlockChain {
|
||||
block_receipts: RwLock::new(HashMap::new()),
|
||||
db: db.clone(),
|
||||
cache_man: Mutex::new(cache_man),
|
||||
pending_best_ancient_block: RwLock::new(None),
|
||||
pending_best_block: RwLock::new(None),
|
||||
pending_block_hashes: RwLock::new(HashMap::new()),
|
||||
pending_block_details: RwLock::new(HashMap::new()),
|
||||
@@ -808,18 +810,7 @@ impl BlockChain {
|
||||
}, is_best);
|
||||
|
||||
if is_ancient {
|
||||
let mut best_ancient_block = self.best_ancient_block.write();
|
||||
let ancient_number = best_ancient_block.as_ref().map_or(0, |b| b.number);
|
||||
if self.block_hash(block_number + 1).is_some() {
|
||||
batch.delete(db::COL_EXTRA, b"ancient");
|
||||
*best_ancient_block = None;
|
||||
} else if block_number > ancient_number {
|
||||
batch.put(db::COL_EXTRA, b"ancient", &hash);
|
||||
*best_ancient_block = Some(BestAncientBlock {
|
||||
hash: hash,
|
||||
number: block_number,
|
||||
});
|
||||
}
|
||||
self.set_best_ancient_block(block_number, &hash, batch);
|
||||
}
|
||||
|
||||
false
|
||||
@@ -860,6 +851,84 @@ impl BlockChain {
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the best ancient block to the given hash, after checking that
|
||||
/// it's directly linked to the currently known best ancient block
|
||||
pub fn update_best_ancient_block(&self, hash: &H256) {
|
||||
// Get the block view of the next ancient block (it must
|
||||
// be in DB at this point)
|
||||
let block_view = match self.block(hash) {
|
||||
Some(v) => v,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// So that `best_ancient_block` gets unlocked before calling
|
||||
// `set_best_ancient_block`
|
||||
{
|
||||
// Get the target hash ; if there are no ancient block,
|
||||
// it means that the chain is already fully linked
|
||||
// Release the `best_ancient_block` RwLock
|
||||
let target_hash = {
|
||||
let best_ancient_block = self.best_ancient_block.read();
|
||||
let cur_ancient_block = match *best_ancient_block {
|
||||
Some(ref b) => b,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Ensure that the new best ancient block is after the current one
|
||||
if block_view.number() <= cur_ancient_block.number {
|
||||
return;
|
||||
}
|
||||
|
||||
cur_ancient_block.hash.clone()
|
||||
};
|
||||
|
||||
let mut block_hash = *hash;
|
||||
let mut is_linked = false;
|
||||
|
||||
loop {
|
||||
if block_hash == target_hash {
|
||||
is_linked = true;
|
||||
break;
|
||||
}
|
||||
|
||||
match self.block_details(&block_hash) {
|
||||
Some(block_details) => {
|
||||
block_hash = block_details.parent;
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
if !is_linked {
|
||||
trace!(target: "blockchain", "The given block {:x} is not linked to the known ancient block {:x}", hash, target_hash);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut batch = self.db.key_value().transaction();
|
||||
self.set_best_ancient_block(block_view.number(), hash, &mut batch);
|
||||
self.db.key_value().write(batch).expect("Low level database error.");
|
||||
}
|
||||
|
||||
/// Set the best ancient block with the given value: private method
|
||||
/// `best_ancient_block` must not be locked, otherwise a DeadLock would occur
|
||||
fn set_best_ancient_block(&self, block_number: BlockNumber, block_hash: &H256, batch: &mut DBTransaction) {
|
||||
let mut pending_best_ancient_block = self.pending_best_ancient_block.write();
|
||||
let ancient_number = self.best_ancient_block.read().as_ref().map_or(0, |b| b.number);
|
||||
if self.block_hash(block_number + 1).is_some() {
|
||||
trace!(target: "blockchain", "The two ends of the chain have met.");
|
||||
batch.delete(db::COL_EXTRA, b"ancient");
|
||||
*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);
|
||||
*pending_best_ancient_block = Some(Some(BestAncientBlock {
|
||||
hash: *block_hash,
|
||||
number: block_number,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert an epoch transition. Provide an epoch number being transitioned to
|
||||
/// and epoch transition object.
|
||||
///
|
||||
@@ -1112,15 +1181,21 @@ impl BlockChain {
|
||||
|
||||
/// Apply pending insertion updates
|
||||
pub fn commit(&self) {
|
||||
let mut pending_best_ancient_block = self.pending_best_ancient_block.write();
|
||||
let mut pending_best_block = self.pending_best_block.write();
|
||||
let mut pending_write_hashes = self.pending_block_hashes.write();
|
||||
let mut pending_block_details = self.pending_block_details.write();
|
||||
let mut pending_write_txs = self.pending_transaction_addresses.write();
|
||||
|
||||
let mut best_block = self.best_block.write();
|
||||
let mut best_ancient_block = self.best_ancient_block.write();
|
||||
let mut write_block_details = self.block_details.write();
|
||||
let mut write_hashes = self.block_hashes.write();
|
||||
let mut write_txs = self.transaction_addresses.write();
|
||||
// update best ancient block
|
||||
if let Some(block_option) = pending_best_ancient_block.take() {
|
||||
*best_ancient_block = block_option;
|
||||
}
|
||||
// update best block
|
||||
if let Some(block) = pending_best_block.take() {
|
||||
*best_block = block;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use bytes::Bytes;
|
||||
use ethereum_types::H256;
|
||||
use ethereum_types::{H256, U256};
|
||||
use transaction::UnverifiedTransaction;
|
||||
use blockchain::ImportRoute;
|
||||
use std::time::Duration;
|
||||
@@ -114,19 +114,51 @@ impl ChainRoute {
|
||||
}
|
||||
}
|
||||
|
||||
/// Used by `ChainNotify` `new_blocks()`
|
||||
pub struct NewBlocks {
|
||||
/// Imported blocks
|
||||
pub imported: Vec<H256>,
|
||||
/// Invalid blocks
|
||||
pub invalid: Vec<H256>,
|
||||
/// Route
|
||||
pub route: ChainRoute,
|
||||
/// Sealed
|
||||
pub sealed: Vec<H256>,
|
||||
/// Block bytes.
|
||||
pub proposed: Vec<Bytes>,
|
||||
/// Duration
|
||||
pub duration: Duration,
|
||||
/// Has more blocks to import
|
||||
pub has_more_blocks_to_import: bool,
|
||||
}
|
||||
|
||||
impl NewBlocks {
|
||||
/// Constructor
|
||||
pub fn new (
|
||||
imported: Vec<H256>,
|
||||
invalid: Vec<H256>,
|
||||
route: ChainRoute,
|
||||
sealed: Vec<H256>,
|
||||
proposed: Vec<Bytes>,
|
||||
duration: Duration,
|
||||
has_more_blocks_to_import: bool,
|
||||
) -> NewBlocks {
|
||||
NewBlocks {
|
||||
imported,
|
||||
invalid,
|
||||
route,
|
||||
sealed,
|
||||
proposed,
|
||||
duration,
|
||||
has_more_blocks_to_import,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
_imported: Vec<H256>,
|
||||
_invalid: Vec<H256>,
|
||||
_route: ChainRoute,
|
||||
_sealed: Vec<H256>,
|
||||
// Block bytes.
|
||||
_proposed: Vec<Bytes>,
|
||||
_duration: Duration,
|
||||
) {
|
||||
fn new_blocks( &self, _new_blocks: NewBlocks) {
|
||||
// does nothing by default
|
||||
}
|
||||
|
||||
@@ -141,7 +173,15 @@ pub trait ChainNotify : Send + Sync {
|
||||
}
|
||||
|
||||
/// fires when chain broadcasts a message
|
||||
fn broadcast(&self, _message_type: ChainMessageType) {}
|
||||
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,
|
||||
|
||||
@@ -32,7 +32,7 @@ use kvdb::{DBValue, KeyValueDB, DBTransaction};
|
||||
// other
|
||||
use ethereum_types::{H256, Address, U256};
|
||||
use block::{IsBlock, LockedBlock, Drain, ClosedBlock, OpenBlock, enact_verified, SealedBlock};
|
||||
use blockchain::{BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert};
|
||||
use blockchain::{BlockReceipts, BlockChain, BlockChainDB, BlockProvider, TreeRoute, ImportRoute, TransactionAddress, ExtrasInsert};
|
||||
use client::ancient_import::AncientVerifier;
|
||||
use client::{
|
||||
Nonce, Balance, ChainInfo, BlockInfo, CallContract, TransactionInfo,
|
||||
@@ -44,7 +44,7 @@ use client::{
|
||||
use client::{
|
||||
BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
|
||||
TraceFilter, CallAnalytics, Mode,
|
||||
ChainNotify, ChainRoute, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType,
|
||||
ChainNotify, NewBlocks, ChainRoute, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType,
|
||||
IoClient, BadBlocks,
|
||||
};
|
||||
use client::bad_blocks;
|
||||
@@ -66,7 +66,7 @@ use ethcore_miner::pool::VerifiedTransaction;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use rand::OsRng;
|
||||
use receipt::{Receipt, LocalizedReceipt};
|
||||
use snapshot::{self, io as snapshot_io};
|
||||
use snapshot::{self, io as snapshot_io, SnapshotClient};
|
||||
use spec::Spec;
|
||||
use state_db::StateDB;
|
||||
use state::{self, State};
|
||||
@@ -268,7 +268,7 @@ impl Importer {
|
||||
}
|
||||
|
||||
let max_blocks_to_import = client.config.max_round_blocks_to_import;
|
||||
let (imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, duration, is_empty) = {
|
||||
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);
|
||||
@@ -322,26 +322,29 @@ impl Importer {
|
||||
if !invalid_blocks.is_empty() {
|
||||
self.block_queue.mark_as_bad(&invalid_blocks);
|
||||
}
|
||||
let is_empty = self.block_queue.mark_as_good(&imported_blocks);
|
||||
(imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, start.elapsed(), is_empty)
|
||||
let has_more_blocks_to_import = !self.block_queue.mark_as_good(&imported_blocks);
|
||||
(imported_blocks, import_results, invalid_blocks, imported, proposed_blocks, start.elapsed(), has_more_blocks_to_import)
|
||||
};
|
||||
|
||||
{
|
||||
if !imported_blocks.is_empty() && is_empty {
|
||||
if !imported_blocks.is_empty() {
|
||||
let route = ChainRoute::from(import_results.as_ref());
|
||||
|
||||
if is_empty {
|
||||
if !has_more_blocks_to_import {
|
||||
self.miner.chain_new_blocks(client, &imported_blocks, &invalid_blocks, route.enacted(), route.retracted(), false);
|
||||
}
|
||||
|
||||
client.notify(|notify| {
|
||||
notify.new_blocks(
|
||||
NewBlocks::new(
|
||||
imported_blocks.clone(),
|
||||
invalid_blocks.clone(),
|
||||
route.clone(),
|
||||
Vec::new(),
|
||||
proposed_blocks.clone(),
|
||||
duration,
|
||||
has_more_blocks_to_import,
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -481,7 +484,7 @@ impl Importer {
|
||||
|
||||
let mut batch = DBTransaction::new();
|
||||
|
||||
let ancestry_actions = self.engine.ancestry_actions(&block, &mut chain.ancestry_with_metadata_iter(*parent));
|
||||
let ancestry_actions = self.engine.ancestry_actions(&header, &mut chain.ancestry_with_metadata_iter(*parent));
|
||||
|
||||
let receipts = block.receipts;
|
||||
let traces = block.traces.drain();
|
||||
@@ -881,7 +884,7 @@ impl Client {
|
||||
/// Flush the block import queue.
|
||||
pub fn flush_queue(&self) {
|
||||
self.importer.block_queue.flush();
|
||||
while !self.importer.block_queue.queue_info().is_empty() {
|
||||
while !self.importer.block_queue.is_empty() {
|
||||
self.import_verified_blocks();
|
||||
}
|
||||
}
|
||||
@@ -1005,6 +1008,16 @@ impl Client {
|
||||
self.importer.miner.clone()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn state_db(&self) -> ::parking_lot::RwLockReadGuard<StateDB> {
|
||||
self.state_db.read()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn chain(&self) -> Arc<BlockChain> {
|
||||
self.chain.read().clone()
|
||||
}
|
||||
|
||||
/// Replace io channel. Useful for testing.
|
||||
pub fn set_io_channel(&self, io_channel: IoChannel<ClientIoMessage>) {
|
||||
*self.io_channel.write() = io_channel;
|
||||
@@ -1413,8 +1426,21 @@ impl ImportBlock for Client {
|
||||
bail!(EthcoreErrorKind::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 };
|
||||
|
||||
match self.importer.block_queue.import(unverified) {
|
||||
Ok(res) => Ok(res),
|
||||
Ok(hash) => {
|
||||
if let Some((raw, hash, difficulty)) = raw {
|
||||
self.notify(move |n| n.block_pre_import(&raw, &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));
|
||||
@@ -1817,7 +1843,7 @@ impl BlockChainClient for Client {
|
||||
Some(receipt)
|
||||
}
|
||||
|
||||
fn block_receipts(&self, id: BlockId) -> Option<Vec<LocalizedReceipt>> {
|
||||
fn localized_block_receipts(&self, id: BlockId) -> Option<Vec<LocalizedReceipt>> {
|
||||
let hash = self.block_hash(id)?;
|
||||
|
||||
let chain = self.chain.read();
|
||||
@@ -1860,14 +1886,18 @@ impl BlockChainClient for Client {
|
||||
self.state_db.read().journal_db().state(hash)
|
||||
}
|
||||
|
||||
fn encoded_block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
||||
self.chain.read().block_receipts(hash).map(|receipts| ::rlp::encode(&receipts))
|
||||
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts> {
|
||||
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()
|
||||
}
|
||||
|
||||
fn clear_queue(&self) {
|
||||
self.importer.block_queue.clear();
|
||||
}
|
||||
@@ -2137,11 +2167,8 @@ impl IoClient for Client {
|
||||
// NOTE To prevent race condition with import, make sure to check queued blocks first
|
||||
// (and attempt to acquire lock)
|
||||
let is_parent_pending = self.queued_ancient_blocks.read().0.contains(&parent_hash);
|
||||
if !is_parent_pending {
|
||||
let status = self.block_status(BlockId::Hash(parent_hash));
|
||||
if status == BlockStatus::Unknown {
|
||||
bail!(EthcoreErrorKind::Block(BlockError::UnknownParent(parent_hash)));
|
||||
}
|
||||
if !is_parent_pending && !self.chain.read().is_known(&parent_hash) {
|
||||
bail!(EthcoreErrorKind::Block(BlockError::UnknownParent(parent_hash)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2171,6 +2198,10 @@ impl IoClient for Client {
|
||||
);
|
||||
if let Err(e) = result {
|
||||
error!(target: "client", "Error importing ancient block: {}", e);
|
||||
|
||||
let mut queued = queued.write();
|
||||
queued.0.clear();
|
||||
queued.1.clear();
|
||||
}
|
||||
// remove from pending
|
||||
queued.write().0.remove(&hash);
|
||||
@@ -2277,26 +2308,37 @@ impl ScheduleInfo for Client {
|
||||
|
||||
impl ImportSealedBlock for Client {
|
||||
fn import_sealed_block(&self, block: SealedBlock) -> EthcoreResult<H256> {
|
||||
let h = block.header().hash();
|
||||
let start = Instant::now();
|
||||
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()));
|
||||
|
||||
let route = {
|
||||
// Do a super duper basic verification to detect potential bugs
|
||||
if let Err(e) = self.engine.verify_block_basic(&header) {
|
||||
self.importer.bad_blocks.report(
|
||||
block.rlp_bytes(),
|
||||
format!("Detected an issue with locally sealed block: {}", e),
|
||||
);
|
||||
return Err(e.into());
|
||||
}
|
||||
|
||||
// scope for self.import_lock
|
||||
let _import_lock = self.importer.import_lock.lock();
|
||||
trace_time!("import_sealed_block");
|
||||
|
||||
let number = block.header().number();
|
||||
let block_data = block.rlp_bytes();
|
||||
let header = block.header().clone();
|
||||
|
||||
let route = self.importer.commit_block(block, &header, encoded::Block::new(block_data), self);
|
||||
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
||||
trace!(target: "client", "Imported sealed block #{} ({})", header.number(), hash);
|
||||
self.state_db.write().sync_cache(&route.enacted, &route.retracted, false);
|
||||
route
|
||||
};
|
||||
let route = ChainRoute::from([route].as_ref());
|
||||
self.importer.miner.chain_new_blocks(
|
||||
self,
|
||||
&[h.clone()],
|
||||
&[hash],
|
||||
&[],
|
||||
route.enacted(),
|
||||
route.retracted(),
|
||||
@@ -2304,16 +2346,19 @@ impl ImportSealedBlock for Client {
|
||||
);
|
||||
self.notify(|notify| {
|
||||
notify.new_blocks(
|
||||
vec![h.clone()],
|
||||
vec![],
|
||||
route.clone(),
|
||||
vec![h.clone()],
|
||||
vec![],
|
||||
start.elapsed(),
|
||||
NewBlocks::new(
|
||||
vec![hash],
|
||||
vec![],
|
||||
route.clone(),
|
||||
vec![hash],
|
||||
vec![],
|
||||
start.elapsed(),
|
||||
false
|
||||
)
|
||||
);
|
||||
});
|
||||
self.db.read().key_value().flush().expect("DB flush failed.");
|
||||
Ok(h)
|
||||
Ok(hash)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2322,12 +2367,15 @@ impl BroadcastProposalBlock for Client {
|
||||
const DURATION_ZERO: Duration = Duration::from_millis(0);
|
||||
self.notify(|notify| {
|
||||
notify.new_blocks(
|
||||
vec![],
|
||||
vec![],
|
||||
ChainRoute::default(),
|
||||
vec![],
|
||||
vec![block.rlp_bytes()],
|
||||
DURATION_ZERO,
|
||||
NewBlocks::new(
|
||||
vec![],
|
||||
vec![],
|
||||
ChainRoute::default(),
|
||||
vec![],
|
||||
vec![block.rlp_bytes()],
|
||||
DURATION_ZERO,
|
||||
false
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -2406,6 +2454,8 @@ impl ProvingBlockChainClient for Client {
|
||||
}
|
||||
}
|
||||
|
||||
impl SnapshotClient for Client {}
|
||||
|
||||
impl Drop for Client {
|
||||
fn drop(&mut self) {
|
||||
self.engine.stop();
|
||||
@@ -2504,7 +2554,7 @@ mod tests {
|
||||
use test_helpers::{generate_dummy_client_with_data};
|
||||
|
||||
let client = generate_dummy_client_with_data(2, 2, &[1.into(), 1.into()]);
|
||||
let receipts = client.block_receipts(BlockId::Latest).unwrap();
|
||||
let receipts = client.localized_block_receipts(BlockId::Latest).unwrap();
|
||||
|
||||
assert_eq!(receipts.len(), 2);
|
||||
assert_eq!(receipts[0].transaction_index, 0);
|
||||
|
||||
@@ -34,7 +34,7 @@ pub use self::evm_test_client::{EvmTestClient, EvmTestError, TransactResult};
|
||||
pub use self::io_message::ClientIoMessage;
|
||||
#[cfg(any(test, feature = "test-helpers"))]
|
||||
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
|
||||
pub use self::chain_notify::{ChainNotify, ChainRoute, ChainRouteType, ChainMessageType};
|
||||
pub use self::chain_notify::{ChainNotify, NewBlocks, ChainRoute, ChainRouteType, ChainMessageType};
|
||||
pub use self::traits::{
|
||||
Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, CallContract, TransactionInfo, RegistryInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock,
|
||||
StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks,
|
||||
|
||||
@@ -686,7 +686,7 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
self.receipts.read().get(&id).cloned()
|
||||
}
|
||||
|
||||
fn block_receipts(&self, _id: BlockId) -> Option<Vec<LocalizedReceipt>> {
|
||||
fn localized_block_receipts(&self, _id: BlockId) -> Option<Vec<LocalizedReceipt>> {
|
||||
Some(self.receipts.read().values().cloned().collect())
|
||||
}
|
||||
|
||||
@@ -789,16 +789,14 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
None
|
||||
}
|
||||
|
||||
fn encoded_block_receipts(&self, hash: &H256) -> Option<Bytes> {
|
||||
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts> {
|
||||
// starts with 'f' ?
|
||||
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
|
||||
let receipt = BlockReceipts::new(vec![Receipt::new(
|
||||
TransactionOutcome::StateRoot(H256::zero()),
|
||||
U256::zero(),
|
||||
vec![])]);
|
||||
let mut rlp = RlpStream::new();
|
||||
rlp.append(&receipt);
|
||||
return Some(rlp.out());
|
||||
return Some(receipt);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::sync::Arc;
|
||||
use itertools::Itertools;
|
||||
|
||||
use block::{OpenBlock, SealedBlock, ClosedBlock};
|
||||
use blockchain::TreeRoute;
|
||||
use blockchain::{BlockReceipts, TreeRoute};
|
||||
use client::Mode;
|
||||
use encoded;
|
||||
use vm::LastHashes;
|
||||
@@ -282,7 +282,7 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
|
||||
fn transaction_receipt(&self, id: TransactionId) -> Option<LocalizedReceipt>;
|
||||
|
||||
/// Get localized receipts for all transaction in given block.
|
||||
fn block_receipts(&self, id: BlockId) -> Option<Vec<LocalizedReceipt>>;
|
||||
fn localized_block_receipts(&self, id: BlockId) -> Option<Vec<LocalizedReceipt>>;
|
||||
|
||||
/// Get a tree route between `from` and `to`.
|
||||
/// See `BlockChain::tree_route`.
|
||||
@@ -294,12 +294,17 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
|
||||
/// Get latest state node
|
||||
fn state_data(&self, hash: &H256) -> Option<Bytes>;
|
||||
|
||||
/// Get raw block receipts data by block header hash.
|
||||
fn encoded_block_receipts(&self, hash: &H256) -> Option<Bytes>;
|
||||
/// Get block receipts data by block header hash.
|
||||
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts>;
|
||||
|
||||
/// 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);
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
//! A blockchain engine that supports a non-instant BFT proof-of-authority.
|
||||
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::fmt;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||
use std::{cmp, fmt};
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||
@@ -81,6 +81,8 @@ pub struct AuthorityRoundParams {
|
||||
pub empty_steps_transition: u64,
|
||||
/// Number of accepted empty steps.
|
||||
pub maximum_empty_steps: usize,
|
||||
/// Transition block to strict empty steps validation.
|
||||
pub strict_empty_steps_transition: u64,
|
||||
}
|
||||
|
||||
const U16_MAX: usize = ::std::u16::MAX as usize;
|
||||
@@ -110,6 +112,7 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
|
||||
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),
|
||||
strict_empty_steps_transition: p.strict_empty_steps_transition.map_or(0, Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,10 +126,10 @@ struct Step {
|
||||
}
|
||||
|
||||
impl Step {
|
||||
fn load(&self) -> usize { self.inner.load(AtomicOrdering::SeqCst) }
|
||||
fn load(&self) -> u64 { self.inner.load(AtomicOrdering::SeqCst) as u64 }
|
||||
fn duration_remaining(&self) -> Duration {
|
||||
let now = unix_now();
|
||||
let expected_seconds = (self.load() as u64)
|
||||
let expected_seconds = self.load()
|
||||
.checked_add(1)
|
||||
.and_then(|ctr| ctr.checked_mul(self.duration as u64))
|
||||
.map(Duration::from_secs);
|
||||
@@ -162,8 +165,8 @@ impl Step {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_future(&self, given: usize) -> Result<(), Option<OutOfBounds<u64>>> {
|
||||
const REJECTED_STEP_DRIFT: usize = 4;
|
||||
fn check_future(&self, given: u64) -> Result<(), Option<OutOfBounds<u64>>> {
|
||||
const REJECTED_STEP_DRIFT: u64 = 4;
|
||||
|
||||
// Verify if the step is correct.
|
||||
if given <= self.load() {
|
||||
@@ -182,8 +185,8 @@ impl Step {
|
||||
let d = self.duration as u64;
|
||||
Err(Some(OutOfBounds {
|
||||
min: None,
|
||||
max: Some(d * current as u64),
|
||||
found: d * given as u64,
|
||||
max: Some(d * current),
|
||||
found: d * given,
|
||||
}))
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -192,8 +195,8 @@ impl Step {
|
||||
}
|
||||
|
||||
// Chain scoring: total weight is sqrt(U256::max_value())*height - step
|
||||
fn calculate_score(parent_step: U256, current_step: U256, current_empty_steps: U256) -> U256 {
|
||||
U256::from(U128::max_value()) + parent_step - current_step + current_empty_steps
|
||||
fn calculate_score(parent_step: u64, current_step: u64, current_empty_steps: usize) -> U256 {
|
||||
U256::from(U128::max_value()) + U256::from(parent_step) - U256::from(current_step) + U256::from(current_empty_steps)
|
||||
}
|
||||
|
||||
struct EpochManager {
|
||||
@@ -284,13 +287,26 @@ impl EpochManager {
|
||||
/// A message broadcast by authorities when it's their turn to seal a block but there are no
|
||||
/// transactions. Other authorities accumulate these messages and later include them in the seal as
|
||||
/// proof.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct EmptyStep {
|
||||
signature: H520,
|
||||
step: usize,
|
||||
step: u64,
|
||||
parent_hash: H256,
|
||||
}
|
||||
|
||||
impl PartialOrd for EmptyStep {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
impl Ord for EmptyStep {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.step.cmp(&other.step)
|
||||
.then_with(|| self.parent_hash.cmp(&other.parent_hash))
|
||||
.then_with(|| self.signature.cmp(&other.signature))
|
||||
}
|
||||
}
|
||||
|
||||
impl EmptyStep {
|
||||
fn from_sealed(sealed_empty_step: SealedEmptyStep, parent_hash: &H256) -> EmptyStep {
|
||||
let signature = sealed_empty_step.signature;
|
||||
@@ -353,7 +369,7 @@ pub fn empty_step_full_rlp(signature: &H520, empty_step_rlp: &[u8]) -> Vec<u8> {
|
||||
s.out()
|
||||
}
|
||||
|
||||
pub fn empty_step_rlp(step: usize, parent_hash: &H256) -> Vec<u8> {
|
||||
pub fn empty_step_rlp(step: u64, parent_hash: &H256) -> Vec<u8> {
|
||||
let mut s = RlpStream::new_list(2);
|
||||
s.append(&step).append(parent_hash);
|
||||
s.out()
|
||||
@@ -365,7 +381,7 @@ pub fn empty_step_rlp(step: usize, parent_hash: &H256) -> Vec<u8> {
|
||||
/// empty message is included.
|
||||
struct SealedEmptyStep {
|
||||
signature: H520,
|
||||
step: usize,
|
||||
step: u64,
|
||||
}
|
||||
|
||||
impl Encodable for SealedEmptyStep {
|
||||
@@ -399,7 +415,7 @@ pub struct AuthorityRound {
|
||||
validators: Box<ValidatorSet>,
|
||||
validate_score_transition: u64,
|
||||
validate_step_transition: u64,
|
||||
empty_steps: Mutex<Vec<EmptyStep>>,
|
||||
empty_steps: Mutex<BTreeSet<EmptyStep>>,
|
||||
epoch_manager: Mutex<EpochManager>,
|
||||
immediate_transitions: bool,
|
||||
block_reward: U256,
|
||||
@@ -408,6 +424,7 @@ pub struct AuthorityRound {
|
||||
maximum_uncle_count_transition: u64,
|
||||
maximum_uncle_count: usize,
|
||||
empty_steps_transition: u64,
|
||||
strict_empty_steps_transition: u64,
|
||||
maximum_empty_steps: usize,
|
||||
machine: EthereumMachine,
|
||||
}
|
||||
@@ -494,7 +511,7 @@ fn header_expected_seal_fields(header: &Header, empty_steps_transition: u64) ->
|
||||
}
|
||||
}
|
||||
|
||||
fn header_step(header: &Header, empty_steps_transition: u64) -> Result<usize, ::rlp::DecoderError> {
|
||||
fn header_step(header: &Header, empty_steps_transition: u64) -> Result<u64, ::rlp::DecoderError> {
|
||||
let expected_seal_fields = header_expected_seal_fields(header, empty_steps_transition);
|
||||
Rlp::new(&header.seal().get(0).expect(
|
||||
&format!("was either checked with verify_block_basic or is genesis; has {} fields; qed (Make sure the spec file has a correct genesis seal)", expected_seal_fields))).as_val()
|
||||
@@ -533,17 +550,17 @@ fn header_empty_steps_signers(header: &Header, empty_steps_transition: u64) -> R
|
||||
}
|
||||
}
|
||||
|
||||
fn step_proposer(validators: &ValidatorSet, bh: &H256, step: usize) -> Address {
|
||||
let proposer = validators.get(bh, step);
|
||||
fn step_proposer(validators: &ValidatorSet, bh: &H256, step: u64) -> Address {
|
||||
let proposer = validators.get(bh, step as usize);
|
||||
trace!(target: "engine", "Fetched proposer for step {}: {}", step, proposer);
|
||||
proposer
|
||||
}
|
||||
|
||||
fn is_step_proposer(validators: &ValidatorSet, bh: &H256, step: usize, address: &Address) -> bool {
|
||||
fn is_step_proposer(validators: &ValidatorSet, bh: &H256, step: u64, address: &Address) -> bool {
|
||||
step_proposer(validators, bh, step) == *address
|
||||
}
|
||||
|
||||
fn verify_timestamp(step: &Step, header_step: usize) -> Result<(), BlockError> {
|
||||
fn verify_timestamp(step: &Step, header_step: u64) -> Result<(), BlockError> {
|
||||
match step.check_future(header_step) {
|
||||
Err(None) => {
|
||||
trace!(target: "engine", "verify_timestamp: block from the future");
|
||||
@@ -564,7 +581,7 @@ fn verify_external(header: &Header, validators: &ValidatorSet, empty_steps_trans
|
||||
let header_step = header_step(header, empty_steps_transition)?;
|
||||
|
||||
let proposer_signature = header_signature(header, empty_steps_transition)?;
|
||||
let correct_proposer = validators.get(header.parent_hash(), header_step);
|
||||
let correct_proposer = validators.get(header.parent_hash(), header_step as usize);
|
||||
let is_invalid_proposer = *header.author() != correct_proposer || {
|
||||
let empty_steps_rlp = if header.number() >= empty_steps_transition {
|
||||
Some(header_empty_steps_raw(header))
|
||||
@@ -634,13 +651,13 @@ impl AuthorityRound {
|
||||
panic!("authority_round: step duration can't be zero")
|
||||
}
|
||||
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))) as usize;
|
||||
let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / (our_params.step_duration as u64)));
|
||||
let engine = Arc::new(
|
||||
AuthorityRound {
|
||||
transition_service: IoService::<()>::start()?,
|
||||
step: Arc::new(PermissionedStep {
|
||||
inner: Step {
|
||||
inner: AtomicUsize::new(initial_step),
|
||||
inner: AtomicUsize::new(initial_step as usize),
|
||||
calibrate: our_params.start_step.is_none(),
|
||||
duration: our_params.step_duration,
|
||||
},
|
||||
@@ -651,7 +668,7 @@ impl AuthorityRound {
|
||||
validators: our_params.validators,
|
||||
validate_score_transition: our_params.validate_score_transition,
|
||||
validate_step_transition: our_params.validate_step_transition,
|
||||
empty_steps: Mutex::new(Vec::new()),
|
||||
empty_steps: Default::default(),
|
||||
epoch_manager: Mutex::new(EpochManager::blank()),
|
||||
immediate_transitions: our_params.immediate_transitions,
|
||||
block_reward: our_params.block_reward,
|
||||
@@ -661,6 +678,7 @@ impl AuthorityRound {
|
||||
maximum_uncle_count: our_params.maximum_uncle_count,
|
||||
empty_steps_transition: our_params.empty_steps_transition,
|
||||
maximum_empty_steps: our_params.maximum_empty_steps,
|
||||
strict_empty_steps_transition: our_params.strict_empty_steps_transition,
|
||||
machine: machine,
|
||||
});
|
||||
|
||||
@@ -699,22 +717,41 @@ impl AuthorityRound {
|
||||
})
|
||||
}
|
||||
|
||||
fn empty_steps(&self, from_step: U256, to_step: U256, parent_hash: H256) -> Vec<EmptyStep> {
|
||||
self.empty_steps.lock().iter().filter(|e| {
|
||||
U256::from(e.step) > from_step &&
|
||||
U256::from(e.step) < to_step &&
|
||||
e.parent_hash == parent_hash
|
||||
}).cloned().collect()
|
||||
fn empty_steps(&self, from_step: u64, to_step: u64, parent_hash: H256) -> Vec<EmptyStep> {
|
||||
let from = EmptyStep {
|
||||
step: from_step + 1,
|
||||
parent_hash,
|
||||
signature: Default::default(),
|
||||
};
|
||||
let to = EmptyStep {
|
||||
step: to_step,
|
||||
parent_hash: Default::default(),
|
||||
signature: Default::default(),
|
||||
};
|
||||
|
||||
if from >= to {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
self.empty_steps.lock()
|
||||
.range(from..to)
|
||||
.filter(|e| e.parent_hash == parent_hash)
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn clear_empty_steps(&self, step: U256) {
|
||||
fn clear_empty_steps(&self, step: u64) {
|
||||
// clear old `empty_steps` messages
|
||||
self.empty_steps.lock().retain(|e| U256::from(e.step) > step);
|
||||
let mut empty_steps = self.empty_steps.lock();
|
||||
*empty_steps = empty_steps.split_off(&EmptyStep {
|
||||
step: step + 1,
|
||||
parent_hash: Default::default(),
|
||||
signature: Default::default(),
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_empty_step_message(&self, empty_step: EmptyStep) {
|
||||
let mut empty_steps = self.empty_steps.lock();
|
||||
empty_steps.push(empty_step);
|
||||
self.empty_steps.lock().insert(empty_step);
|
||||
}
|
||||
|
||||
fn generate_empty_step(&self, parent_hash: &H256) {
|
||||
@@ -744,7 +781,7 @@ impl AuthorityRound {
|
||||
}
|
||||
}
|
||||
|
||||
fn report_skipped(&self, header: &Header, current_step: usize, parent_step: usize, validators: &ValidatorSet, set_number: u64) {
|
||||
fn report_skipped(&self, header: &Header, current_step: u64, parent_step: u64, validators: &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;
|
||||
@@ -798,6 +835,7 @@ impl AuthorityRound {
|
||||
}
|
||||
};
|
||||
|
||||
let epoch_transition_hash = epoch_manager.epoch_transition_hash;
|
||||
let ancestry_iter = ancestry.map(|header| {
|
||||
let mut signers = vec![*header.author()];
|
||||
signers.extend(parent_empty_steps_signers.drain(..));
|
||||
@@ -815,10 +853,11 @@ impl AuthorityRound {
|
||||
None
|
||||
}
|
||||
})
|
||||
.while_some();
|
||||
.while_some()
|
||||
.take_while(|&(h, _)| h != epoch_transition_hash);
|
||||
|
||||
if let Err(_) = epoch_manager.finality_checker.build_ancestry_subchain(ancestry_iter) {
|
||||
debug!(target: "engine", "inconsistent validator set within epoch");
|
||||
if let Err(e) = epoch_manager.finality_checker.build_ancestry_subchain(ancestry_iter) {
|
||||
debug!(target: "engine", "inconsistent validator set within epoch: {:?}", e);
|
||||
return Vec::new();
|
||||
}
|
||||
}
|
||||
@@ -935,12 +974,12 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
||||
let current_step = self.step.inner.load();
|
||||
|
||||
let current_empty_steps_len = if header.number() >= self.empty_steps_transition {
|
||||
self.empty_steps(parent_step.into(), current_step.into(), parent.hash()).len()
|
||||
self.empty_steps(parent_step, current_step, parent.hash()).len()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let score = calculate_score(parent_step.into(), current_step.into(), current_empty_steps_len.into());
|
||||
let score = calculate_score(parent_step, current_step, current_empty_steps_len);
|
||||
header.set_difficulty(score);
|
||||
}
|
||||
|
||||
@@ -984,8 +1023,8 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
||||
}
|
||||
|
||||
let header = block.header();
|
||||
let parent_step: U256 = header_step(parent, self.empty_steps_transition)
|
||||
.expect("Header has been verified; qed").into();
|
||||
let parent_step = header_step(parent, self.empty_steps_transition)
|
||||
.expect("Header has been verified; qed");
|
||||
|
||||
let step = self.step.inner.load();
|
||||
|
||||
@@ -1020,7 +1059,7 @@ impl Engine<EthereumMachine> 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.into() {
|
||||
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;
|
||||
}
|
||||
@@ -1032,7 +1071,10 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
||||
block.transactions().is_empty() &&
|
||||
empty_steps.len() < self.maximum_empty_steps {
|
||||
|
||||
self.generate_empty_step(header.parent_hash());
|
||||
if self.step.can_propose.compare_and_swap(true, false, AtomicOrdering::SeqCst) {
|
||||
self.generate_empty_step(header.parent_hash());
|
||||
}
|
||||
|
||||
return Seal::None;
|
||||
}
|
||||
|
||||
@@ -1056,7 +1098,7 @@ impl Engine<EthereumMachine> 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, u64::from(parent_step) as usize, &*validators, set_number);
|
||||
self.report_skipped(header, step, parent_step, &*validators, set_number);
|
||||
}
|
||||
|
||||
let mut fields = vec![
|
||||
@@ -1213,8 +1255,11 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
||||
// 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 {
|
||||
let validate_empty_steps = || -> Result<usize, Error> {
|
||||
let strict_empty_steps = header.number() >= self.strict_empty_steps_transition;
|
||||
let empty_steps = header_empty_steps(header)?;
|
||||
let empty_steps_len = empty_steps.len();
|
||||
let mut prev_empty_step = 0;
|
||||
|
||||
for empty_step in empty_steps {
|
||||
if empty_step.step <= parent_step || empty_step.step >= step {
|
||||
Err(EngineError::InsufficientProof(
|
||||
@@ -1230,7 +1275,20 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
||||
Err(EngineError::InsufficientProof(
|
||||
format!("invalid empty step proof: {:?}", empty_step)))?;
|
||||
}
|
||||
|
||||
if strict_empty_steps {
|
||||
if empty_step.step <= prev_empty_step {
|
||||
Err(EngineError::InsufficientProof(format!(
|
||||
"{} empty step: {:?}",
|
||||
if empty_step.step == prev_empty_step { "duplicate" } else { "unordered" },
|
||||
empty_step
|
||||
)))?;
|
||||
}
|
||||
|
||||
prev_empty_step = empty_step.step;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(empty_steps_len)
|
||||
};
|
||||
|
||||
@@ -1363,16 +1421,21 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
||||
let mut finality_proof: Vec<_> = itertools::repeat_call(move || {
|
||||
chain(hash).and_then(|header| {
|
||||
hash = *header.parent_hash();
|
||||
if header.number() == 0 { return None }
|
||||
else { return Some(header) }
|
||||
if header.number() == 0 { None }
|
||||
else { Some(header) }
|
||||
})
|
||||
})
|
||||
.while_some()
|
||||
.take_while(|h| h.hash() != *finalized_hash)
|
||||
.collect();
|
||||
|
||||
let finalized_header = chain(*finalized_hash)
|
||||
.expect("header is finalized; finalized headers must exist in the chain; qed");
|
||||
let finalized_header = if *finalized_hash == chain_head.hash() {
|
||||
// chain closure only stores ancestry, but the chain head is also unfinalized.
|
||||
chain_head.clone()
|
||||
} else {
|
||||
chain(*finalized_hash)
|
||||
.expect("header is finalized; finalized headers must exist in the chain; qed")
|
||||
};
|
||||
|
||||
let signal_number = finalized_header.number();
|
||||
info!(target: "engine", "Applying validator set change signalled at block {}", signal_number);
|
||||
@@ -1448,9 +1511,9 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
||||
super::total_difficulty_fork_choice(new, current)
|
||||
}
|
||||
|
||||
fn ancestry_actions(&self, block: &ExecutedBlock, ancestry: &mut Iterator<Item=ExtendedHeader>) -> Vec<AncestryAction> {
|
||||
fn ancestry_actions(&self, header: &Header, ancestry: &mut Iterator<Item=ExtendedHeader>) -> Vec<AncestryAction> {
|
||||
let finalized = self.build_finality(
|
||||
block.header(),
|
||||
header,
|
||||
&mut ancestry.take_while(|e| !e.is_finalized).map(|e| e.header),
|
||||
);
|
||||
|
||||
@@ -1481,10 +1544,40 @@ mod tests {
|
||||
use spec::Spec;
|
||||
use transaction::{Action, Transaction};
|
||||
use engines::{Seal, Engine, EngineError, EthEngine};
|
||||
use engines::validator_set::TestSet;
|
||||
use engines::validator_set::{TestSet, SimpleList};
|
||||
use error::{Error, ErrorKind};
|
||||
use super::{AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep, calculate_score};
|
||||
|
||||
fn aura<F>(f: F) -> Arc<AuthorityRound> where
|
||||
F: FnOnce(&mut AuthorityRoundParams),
|
||||
{
|
||||
let mut params = AuthorityRoundParams {
|
||||
step_duration: 1,
|
||||
start_step: Some(1),
|
||||
validators: Box::new(TestSet::default()),
|
||||
validate_score_transition: 0,
|
||||
validate_step_transition: 0,
|
||||
immediate_transitions: true,
|
||||
maximum_uncle_count_transition: 0,
|
||||
maximum_uncle_count: 0,
|
||||
empty_steps_transition: u64::max_value(),
|
||||
maximum_empty_steps: 0,
|
||||
block_reward: Default::default(),
|
||||
block_reward_contract_transition: 0,
|
||||
block_reward_contract: Default::default(),
|
||||
strict_empty_steps_transition: 0,
|
||||
};
|
||||
|
||||
// mutate aura params
|
||||
f(&mut params);
|
||||
|
||||
// create engine
|
||||
let mut c_params = ::spec::CommonParams::default();
|
||||
c_params.gas_limit_bound_divisor = 5.into();
|
||||
let machine = ::machine::EthereumMachine::regular(c_params, Default::default());
|
||||
AuthorityRound::new(params, machine).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_valid_metadata() {
|
||||
let engine = Spec::new_test_round().engine;
|
||||
@@ -1591,12 +1684,12 @@ mod tests {
|
||||
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_difficulty(calculate_score(U256::from(0), U256::from(2), U256::zero()));
|
||||
header.set_difficulty(calculate_score(0, 2, 0));
|
||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||
header.set_seal(vec![encode(&2usize), encode(&(&*signature as &[u8]))]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||
assert!(engine.verify_block_external(&header).is_err());
|
||||
header.set_difficulty(calculate_score(U256::from(0), U256::from(1), U256::zero()));
|
||||
header.set_difficulty(calculate_score(0, 1, 0));
|
||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||
header.set_seal(vec![encode(&1usize), encode(&(&*signature as &[u8]))]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||
@@ -1620,7 +1713,7 @@ mod tests {
|
||||
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_difficulty(calculate_score(U256::from(0), U256::from(1), U256::zero()));
|
||||
header.set_difficulty(calculate_score(0, 1, 0));
|
||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||
header.set_seal(vec![encode(&1usize), encode(&(&*signature as &[u8]))]);
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||
@@ -1648,44 +1741,25 @@ mod tests {
|
||||
// Two validators.
|
||||
// Spec starts with step 2.
|
||||
header.set_seal(vec![encode(&5usize), encode(&(&*signature as &[u8]))]);
|
||||
header.set_difficulty(calculate_score(U256::from(4), U256::from(5), U256::zero()));
|
||||
header.set_difficulty(calculate_score(4, 5, 0));
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||
header.set_seal(vec![encode(&3usize), encode(&(&*signature as &[u8]))]);
|
||||
header.set_difficulty(calculate_score(U256::from(4), U256::from(3), U256::zero()));
|
||||
header.set_difficulty(calculate_score(4, 3, 0));
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reports_skipped() {
|
||||
let last_benign = Arc::new(AtomicUsize::new(0));
|
||||
let params = AuthorityRoundParams {
|
||||
step_duration: 1,
|
||||
start_step: Some(1),
|
||||
validators: Box::new(TestSet::new(Default::default(), last_benign.clone())),
|
||||
validate_score_transition: 0,
|
||||
validate_step_transition: 0,
|
||||
immediate_transitions: true,
|
||||
maximum_uncle_count_transition: 0,
|
||||
maximum_uncle_count: 0,
|
||||
empty_steps_transition: u64::max_value(),
|
||||
maximum_empty_steps: 0,
|
||||
block_reward: Default::default(),
|
||||
block_reward_contract_transition: 0,
|
||||
block_reward_contract: Default::default(),
|
||||
};
|
||||
|
||||
let aura = {
|
||||
let mut c_params = ::spec::CommonParams::default();
|
||||
c_params.gas_limit_bound_divisor = 5.into();
|
||||
let machine = ::machine::EthereumMachine::regular(c_params, Default::default());
|
||||
AuthorityRound::new(params, machine).unwrap()
|
||||
};
|
||||
let aura = aura(|p| {
|
||||
p.validators = Box::new(TestSet::new(Default::default(), last_benign.clone()));
|
||||
});
|
||||
|
||||
let mut parent_header: Header = Header::default();
|
||||
parent_header.set_seal(vec![encode(&1usize)]);
|
||||
parent_header.set_gas_limit("222222".parse::<U256>().unwrap());
|
||||
let mut header: Header = Header::default();
|
||||
header.set_difficulty(calculate_score(U256::from(1), U256::from(3), U256::zero()));
|
||||
header.set_difficulty(calculate_score(1, 3, 0));
|
||||
header.set_gas_limit("222222".parse::<U256>().unwrap());
|
||||
header.set_seal(vec![encode(&3usize)]);
|
||||
|
||||
@@ -1708,29 +1782,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_uncles_transition() {
|
||||
let last_benign = Arc::new(AtomicUsize::new(0));
|
||||
let params = AuthorityRoundParams {
|
||||
step_duration: 1,
|
||||
start_step: Some(1),
|
||||
validators: Box::new(TestSet::new(Default::default(), last_benign.clone())),
|
||||
validate_score_transition: 0,
|
||||
validate_step_transition: 0,
|
||||
immediate_transitions: true,
|
||||
maximum_uncle_count_transition: 1,
|
||||
maximum_uncle_count: 0,
|
||||
empty_steps_transition: u64::max_value(),
|
||||
maximum_empty_steps: 0,
|
||||
block_reward: Default::default(),
|
||||
block_reward_contract_transition: 0,
|
||||
block_reward_contract: Default::default(),
|
||||
};
|
||||
|
||||
let aura = {
|
||||
let mut c_params = ::spec::CommonParams::default();
|
||||
c_params.gas_limit_bound_divisor = 5.into();
|
||||
let machine = ::machine::EthereumMachine::regular(c_params, Default::default());
|
||||
AuthorityRound::new(params, machine).unwrap()
|
||||
};
|
||||
let aura = aura(|params| {
|
||||
params.maximum_uncle_count_transition = 1;
|
||||
});
|
||||
|
||||
assert_eq!(aura.maximum_uncle_count(0), 2);
|
||||
assert_eq!(aura.maximum_uncle_count(1), 0);
|
||||
@@ -1764,27 +1818,9 @@ mod tests {
|
||||
#[test]
|
||||
#[should_panic(expected="authority_round: step duration can't be zero")]
|
||||
fn test_step_duration_zero() {
|
||||
let last_benign = Arc::new(AtomicUsize::new(0));
|
||||
let params = AuthorityRoundParams {
|
||||
step_duration: 0,
|
||||
start_step: Some(1),
|
||||
validators: Box::new(TestSet::new(Default::default(), last_benign.clone())),
|
||||
validate_score_transition: 0,
|
||||
validate_step_transition: 0,
|
||||
immediate_transitions: true,
|
||||
maximum_uncle_count_transition: 0,
|
||||
maximum_uncle_count: 0,
|
||||
empty_steps_transition: u64::max_value(),
|
||||
maximum_empty_steps: 0,
|
||||
block_reward: Default::default(),
|
||||
block_reward_contract_transition: 0,
|
||||
block_reward_contract: Default::default(),
|
||||
};
|
||||
|
||||
let mut c_params = ::spec::CommonParams::default();
|
||||
c_params.gas_limit_bound_divisor = 5.into();
|
||||
let machine = ::machine::EthereumMachine::regular(c_params, Default::default());
|
||||
AuthorityRound::new(params, machine).unwrap();
|
||||
aura(|params| {
|
||||
params.step_duration = 0;
|
||||
});
|
||||
}
|
||||
|
||||
fn setup_empty_steps() -> (Spec, Arc<AccountProvider>, Vec<Address>) {
|
||||
@@ -1799,19 +1835,36 @@ mod tests {
|
||||
(spec, tap, accounts)
|
||||
}
|
||||
|
||||
fn empty_step(engine: &EthEngine, step: usize, parent_hash: &H256) -> EmptyStep {
|
||||
fn empty_step(engine: &EthEngine, 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: usize, parent_hash: &H256) -> SealedEmptyStep {
|
||||
fn sealed_empty_step(engine: &EthEngine, 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]) {
|
||||
header.set_seal(vec![
|
||||
encode(&(step as usize)),
|
||||
encode(&(&**block_signature as &[u8])),
|
||||
::rlp::encode_list(&empty_steps),
|
||||
]);
|
||||
}
|
||||
|
||||
fn assert_insufficient_proof<T: ::std::fmt::Debug>(result: Result<T, Error>, contains: &str) {
|
||||
match result {
|
||||
Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _)) =>{
|
||||
assert!(s.contains(contains), "Expected {:?} to contain {:?}", s, contains);
|
||||
},
|
||||
e => assert!(false, "Unexpected result: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broadcast_empty_step_message() {
|
||||
let (spec, tap, accounts) = setup_empty_steps();
|
||||
@@ -1842,6 +1895,11 @@ mod tests {
|
||||
|
||||
// we've received the message
|
||||
assert!(notify.messages.read().contains(&empty_step_rlp));
|
||||
let len = notify.messages.read().len();
|
||||
|
||||
// make sure that we don't generate empty step for the second time
|
||||
assert_eq!(engine.generate_seal(b1.block(), &genesis_header), Seal::None);
|
||||
assert_eq!(len, notify.messages.read().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2008,46 +2066,31 @@ mod tests {
|
||||
|
||||
// empty step with invalid step
|
||||
let empty_steps = vec![SealedEmptyStep { signature: 0.into(), step: 2 }];
|
||||
header.set_seal(vec![
|
||||
encode(&2usize),
|
||||
encode(&(&*signature as &[u8])),
|
||||
::rlp::encode_list(&empty_steps),
|
||||
]);
|
||||
set_empty_steps_seal(&mut header, 2, &signature, &empty_steps);
|
||||
|
||||
assert!(match engine.verify_block_family(&header, &parent_header) {
|
||||
Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _))
|
||||
if s.contains("invalid step") => true,
|
||||
_ => false,
|
||||
});
|
||||
assert_insufficient_proof(
|
||||
engine.verify_block_family(&header, &parent_header),
|
||||
"invalid step"
|
||||
);
|
||||
|
||||
// empty step with invalid signature
|
||||
let empty_steps = vec![SealedEmptyStep { signature: 0.into(), step: 1 }];
|
||||
header.set_seal(vec![
|
||||
encode(&2usize),
|
||||
encode(&(&*signature as &[u8])),
|
||||
::rlp::encode_list(&empty_steps),
|
||||
]);
|
||||
set_empty_steps_seal(&mut header, 2, &signature, &empty_steps);
|
||||
|
||||
assert!(match engine.verify_block_family(&header, &parent_header) {
|
||||
Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _))
|
||||
if s.contains("invalid empty step proof") => true,
|
||||
_ => false,
|
||||
});
|
||||
assert_insufficient_proof(
|
||||
engine.verify_block_family(&header, &parent_header),
|
||||
"invalid empty step proof"
|
||||
);
|
||||
|
||||
// empty step with valid signature from incorrect proposer for step
|
||||
engine.set_signer(tap.clone(), addr1, "1".into());
|
||||
let empty_steps = vec![sealed_empty_step(engine, 1, &parent_header.hash())];
|
||||
header.set_seal(vec![
|
||||
encode(&2usize),
|
||||
encode(&(&*signature as &[u8])),
|
||||
::rlp::encode_list(&empty_steps),
|
||||
]);
|
||||
set_empty_steps_seal(&mut header, 2, &signature, &empty_steps);
|
||||
|
||||
assert!(match engine.verify_block_family(&header, &parent_header) {
|
||||
Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _))
|
||||
if s.contains("invalid empty step proof") => true,
|
||||
_ => false,
|
||||
});
|
||||
assert_insufficient_proof(
|
||||
engine.verify_block_family(&header, &parent_header),
|
||||
"invalid empty step proof"
|
||||
);
|
||||
|
||||
// valid empty steps
|
||||
engine.set_signer(tap.clone(), addr1, "1".into());
|
||||
@@ -2056,13 +2099,9 @@ mod tests {
|
||||
let empty_step3 = sealed_empty_step(engine, 3, &parent_header.hash());
|
||||
|
||||
let empty_steps = vec![empty_step2, empty_step3];
|
||||
header.set_difficulty(calculate_score(U256::from(0), U256::from(4), U256::from(2)));
|
||||
header.set_difficulty(calculate_score(0, 4, 2));
|
||||
let signature = tap.sign(addr1, Some("1".into()), header.bare_hash()).unwrap();
|
||||
header.set_seal(vec![
|
||||
encode(&4usize),
|
||||
encode(&(&*signature as &[u8])),
|
||||
::rlp::encode_list(&empty_steps),
|
||||
]);
|
||||
set_empty_steps_seal(&mut header, 4, &signature, &empty_steps);
|
||||
|
||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||
}
|
||||
@@ -2171,4 +2210,116 @@ mod tests {
|
||||
BTreeMap::default(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_steps() {
|
||||
let engine = aura(|p| {
|
||||
p.step_duration = 4;
|
||||
p.empty_steps_transition = 0;
|
||||
p.maximum_empty_steps = 0;
|
||||
});
|
||||
|
||||
let parent_hash: H256 = 1.into();
|
||||
let signature = H520::default();
|
||||
let step = |step: u64| EmptyStep {
|
||||
step,
|
||||
parent_hash,
|
||||
signature,
|
||||
};
|
||||
|
||||
engine.handle_empty_step_message(step(1));
|
||||
engine.handle_empty_step_message(step(3));
|
||||
engine.handle_empty_step_message(step(2));
|
||||
engine.handle_empty_step_message(step(1));
|
||||
|
||||
assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![step(1), step(2), step(3)]);
|
||||
assert_eq!(engine.empty_steps(2, 3, parent_hash), vec![]);
|
||||
assert_eq!(engine.empty_steps(2, 4, parent_hash), vec![step(3)]);
|
||||
|
||||
engine.clear_empty_steps(2);
|
||||
|
||||
assert_eq!(engine.empty_steps(0, 3, parent_hash), vec![]);
|
||||
assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![step(3)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_reject_duplicate_empty_steps() {
|
||||
// given
|
||||
let (_spec, tap, accounts) = setup_empty_steps();
|
||||
let engine = aura(|p| {
|
||||
p.validators = Box::new(SimpleList::new(accounts.clone()));
|
||||
p.step_duration = 4;
|
||||
p.empty_steps_transition = 0;
|
||||
p.maximum_empty_steps = 0;
|
||||
});
|
||||
|
||||
let mut parent = Header::default();
|
||||
parent.set_seal(vec![encode(&0usize)]);
|
||||
|
||||
let mut header = Header::default();
|
||||
header.set_number(parent.number() + 1);
|
||||
header.set_parent_hash(parent.hash());
|
||||
header.set_author(accounts[0]);
|
||||
|
||||
// when
|
||||
engine.set_signer(tap.clone(), accounts[1], "0".into());
|
||||
let empty_steps = vec![
|
||||
sealed_empty_step(&*engine, 1, &parent.hash()),
|
||||
sealed_empty_step(&*engine, 1, &parent.hash()),
|
||||
];
|
||||
let step = 2;
|
||||
let signature = tap.sign(accounts[0], Some("1".into()), header.bare_hash()).unwrap();
|
||||
set_empty_steps_seal(&mut header, step, &signature, &empty_steps);
|
||||
header.set_difficulty(calculate_score(0, step, empty_steps.len()));
|
||||
|
||||
// then
|
||||
assert_insufficient_proof(
|
||||
engine.verify_block_family(&header, &parent),
|
||||
"duplicate empty step"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_reject_empty_steps_out_of_order() {
|
||||
// given
|
||||
let (_spec, tap, accounts) = setup_empty_steps();
|
||||
let engine = aura(|p| {
|
||||
p.validators = Box::new(SimpleList::new(accounts.clone()));
|
||||
p.step_duration = 4;
|
||||
p.empty_steps_transition = 0;
|
||||
p.maximum_empty_steps = 0;
|
||||
});
|
||||
|
||||
let mut parent = Header::default();
|
||||
parent.set_seal(vec![encode(&0usize)]);
|
||||
|
||||
let mut header = Header::default();
|
||||
header.set_number(parent.number() + 1);
|
||||
header.set_parent_hash(parent.hash());
|
||||
header.set_author(accounts[0]);
|
||||
|
||||
// when
|
||||
engine.set_signer(tap.clone(), accounts[1], "0".into());
|
||||
let es1 = sealed_empty_step(&*engine, 1, &parent.hash());
|
||||
engine.set_signer(tap.clone(), accounts[0], "1".into());
|
||||
let es2 = sealed_empty_step(&*engine, 2, &parent.hash());
|
||||
|
||||
let mut empty_steps = vec![es2, es1];
|
||||
|
||||
let step = 3;
|
||||
let signature = tap.sign(accounts[1], Some("0".into()), header.bare_hash()).unwrap();
|
||||
set_empty_steps_seal(&mut header, step, &signature, &empty_steps);
|
||||
header.set_difficulty(calculate_score(0, step, empty_steps.len()));
|
||||
|
||||
// then make sure it's rejected because of the order
|
||||
assert_insufficient_proof(
|
||||
engine.verify_block_family(&header, &parent),
|
||||
"unordered empty step"
|
||||
);
|
||||
|
||||
// now try to fix the order
|
||||
empty_steps.reverse();
|
||||
set_empty_steps_seal(&mut header, step, &signature, &empty_steps);
|
||||
assert_eq!(engine.verify_block_family(&header, &parent).unwrap(), ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,7 +429,7 @@ pub trait Engine<M: Machine>: 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, _block: &M::LiveBlock, _ancestry: &mut Iterator<Item=M::ExtendedHeader>) -> Vec<AncestryAction> {
|
||||
fn ancestry_actions(&self, _header: &M::Header, _ancestry: &mut Iterator<Item=M::ExtendedHeader>) -> Vec<AncestryAction> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
|
||||
@@ -34,12 +34,18 @@ pub struct TestSet {
|
||||
last_benign: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
impl Default for TestSet {
|
||||
fn default() -> Self {
|
||||
TestSet::new(Default::default(), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl TestSet {
|
||||
pub fn new(last_malicious: Arc<AtomicUsize>, last_benign: Arc<AtomicUsize>) -> Self {
|
||||
TestSet {
|
||||
validator: SimpleList::new(vec![Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()]),
|
||||
last_malicious: last_malicious,
|
||||
last_benign: last_benign,
|
||||
last_malicious,
|
||||
last_benign,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -979,7 +979,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
||||
scope.builder().stack_size(::std::cmp::max(self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || {
|
||||
self.call_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer)
|
||||
}).expect("Sub-thread creation cannot fail; the host might run out of resources; qed")
|
||||
}).join()
|
||||
}).join().expect("Sub-thread never panics; qed")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1063,7 +1063,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
||||
scope.builder().stack_size(::std::cmp::max(self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || {
|
||||
self.create_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer)
|
||||
}).expect("Sub-thread creation cannot fail; the host might run out of resources; qed")
|
||||
}).join()
|
||||
}).join().expect("Sub-thread never panics; qed")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +139,9 @@ extern crate trace_time;
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate evm;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate env_logger;
|
||||
|
||||
pub extern crate ethstore;
|
||||
|
||||
#[macro_use]
|
||||
|
||||
@@ -371,11 +371,11 @@ impl EthereumMachine {
|
||||
}
|
||||
|
||||
/// Does verification of the transaction against the parent state.
|
||||
pub fn verify_transaction<C: BlockInfo + CallContract>(&self, t: &SignedTransaction, header: &Header, client: &C)
|
||||
pub fn verify_transaction<C: BlockInfo + CallContract>(&self, t: &SignedTransaction, parent: &Header, client: &C)
|
||||
-> Result<(), transaction::Error>
|
||||
{
|
||||
if let Some(ref filter) = self.tx_filter.as_ref() {
|
||||
if !filter.transaction_allowed(header.parent_hash(), header.number(), t, client) {
|
||||
if !filter.transaction_allowed(&parent.hash(), parent.number() + 1, t, client) {
|
||||
return Err(transaction::Error::NotAllowed.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,10 +576,9 @@ impl Miner {
|
||||
trace!(target: "miner", "requires_reseal: sealing enabled");
|
||||
|
||||
// Disable sealing if there were no requests for SEALING_TIMEOUT_IN_BLOCKS
|
||||
let had_requests = sealing.last_request.map(|last_request| {
|
||||
best_block > last_request
|
||||
&& best_block - last_request <= SEALING_TIMEOUT_IN_BLOCKS
|
||||
}).unwrap_or(false);
|
||||
let had_requests = sealing.last_request.map(|last_request|
|
||||
best_block.saturating_sub(last_request) <= SEALING_TIMEOUT_IN_BLOCKS
|
||||
).unwrap_or(false);
|
||||
|
||||
// keep sealing enabled if any of the conditions is met
|
||||
let sealing_enabled = self.forced_sealing()
|
||||
@@ -1394,6 +1393,33 @@ mod tests {
|
||||
assert_eq!(miner.prepare_pending_block(&client), BlockPreparationStatus::NotPrepared);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_return_stale_work_packages() {
|
||||
// given
|
||||
let client = TestBlockChainClient::default();
|
||||
let miner = miner();
|
||||
|
||||
// initial work package should create the pending block
|
||||
let res = miner.work_package(&client);
|
||||
assert_eq!(res.unwrap().1, 1);
|
||||
// This should be true, since there were some requests.
|
||||
assert_eq!(miner.requires_reseal(0), true);
|
||||
|
||||
// when new block is imported
|
||||
let client = generate_dummy_client(2);
|
||||
let imported = [0.into()];
|
||||
let empty = &[];
|
||||
miner.chain_new_blocks(&*client, &imported, empty, &imported, empty, false);
|
||||
|
||||
// then
|
||||
// This should be false, because it's too early.
|
||||
assert_eq!(miner.requires_reseal(2), false);
|
||||
// but still work package should be ready
|
||||
let res = miner.work_package(&*client);
|
||||
assert_eq!(res.unwrap().1, 3);
|
||||
assert_eq!(miner.prepare_pending_block(&*client), BlockPreparationStatus::NotPrepared);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_use_pending_block_if_best_block_is_higher() {
|
||||
// given
|
||||
|
||||
@@ -65,6 +65,8 @@ pub enum Error {
|
||||
BadEpochProof(u64),
|
||||
/// Wrong chunk format.
|
||||
WrongChunkFormat(String),
|
||||
/// Unlinked ancient block chain
|
||||
UnlinkedAncientBlockChain,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
@@ -91,6 +93,7 @@ impl fmt::Display for Error {
|
||||
Error::SnapshotsUnsupported => write!(f, "Snapshots unsupported by consensus engine."),
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ use rand::{Rng, OsRng};
|
||||
pub use self::error::Error;
|
||||
|
||||
pub use self::consensus::*;
|
||||
pub use self::service::{Service, DatabaseRestore};
|
||||
pub use self::service::{SnapshotClient, Service, DatabaseRestore};
|
||||
pub use self::traits::SnapshotService;
|
||||
pub use self::watcher::Watcher;
|
||||
pub use types::snapshot_manifest::ManifestData;
|
||||
@@ -192,11 +192,11 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
|
||||
state_guards.push(state_guard);
|
||||
}
|
||||
|
||||
let block_hashes = block_guard.join()?;
|
||||
let block_hashes = block_guard.join().expect("Sub-thread never panics; qed")?;
|
||||
let mut state_hashes = Vec::new();
|
||||
|
||||
for guard in state_guards {
|
||||
let part_state_hashes = guard.join()?;
|
||||
let part_state_hashes = guard.join().expect("Sub-thread never panics; qed")?;
|
||||
state_hashes.extend(part_state_hashes);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,13 @@ use std::fs::{self, File};
|
||||
use std::path::PathBuf;
|
||||
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::{Client, ChainInfo, ClientIoMessage};
|
||||
use client::{BlockInfo, BlockChainClient, Client, ChainInfo, ClientIoMessage};
|
||||
use engines::EthEngine;
|
||||
use error::{Error, ErrorKind as SnapshotErrorKind};
|
||||
use snapshot::{Error as SnapshotError};
|
||||
@@ -40,6 +41,7 @@ use ethereum_types::H256;
|
||||
use parking_lot::{Mutex, RwLock, RwLockReadGuard};
|
||||
use bytes::Bytes;
|
||||
use journaldb::Algorithm;
|
||||
use kvdb::DBTransaction;
|
||||
use snappy;
|
||||
|
||||
/// Helper for removing directories in case of error.
|
||||
@@ -203,6 +205,9 @@ impl Restoration {
|
||||
/// Type alias for client io channel.
|
||||
pub type Channel = IoChannel<ClientIoMessage>;
|
||||
|
||||
/// Trait alias for the Client Service used
|
||||
pub trait SnapshotClient: BlockChainClient + BlockInfo + DatabaseRestore {}
|
||||
|
||||
/// Snapshot service parameters.
|
||||
pub struct ServiceParams {
|
||||
/// The consensus engine this is built on.
|
||||
@@ -219,7 +224,7 @@ pub struct ServiceParams {
|
||||
/// Usually "<chain hash>/snapshot"
|
||||
pub snapshot_root: PathBuf,
|
||||
/// A handle for database restoration.
|
||||
pub db_restore: Arc<DatabaseRestore>,
|
||||
pub client: Arc<SnapshotClient>,
|
||||
}
|
||||
|
||||
/// `SnapshotService` implementation.
|
||||
@@ -236,7 +241,7 @@ pub struct Service {
|
||||
genesis_block: Bytes,
|
||||
state_chunks: AtomicUsize,
|
||||
block_chunks: AtomicUsize,
|
||||
db_restore: Arc<DatabaseRestore>,
|
||||
client: Arc<SnapshotClient>,
|
||||
progress: super::Progress,
|
||||
taking_snapshot: AtomicBool,
|
||||
restoring_snapshot: AtomicBool,
|
||||
@@ -257,7 +262,7 @@ impl Service {
|
||||
genesis_block: params.genesis_block,
|
||||
state_chunks: AtomicUsize::new(0),
|
||||
block_chunks: AtomicUsize::new(0),
|
||||
db_restore: params.db_restore,
|
||||
client: params.client,
|
||||
progress: Default::default(),
|
||||
taking_snapshot: AtomicBool::new(false),
|
||||
restoring_snapshot: AtomicBool::new(false),
|
||||
@@ -334,12 +339,110 @@ impl Service {
|
||||
|
||||
// replace one the client's database with our own.
|
||||
fn replace_client_db(&self) -> Result<(), Error> {
|
||||
let our_db = self.restoration_db();
|
||||
let migrated_blocks = self.migrate_blocks()?;
|
||||
trace!(target: "snapshot", "Migrated {} ancient blocks", migrated_blocks);
|
||||
|
||||
self.db_restore.restore_db(&*our_db.to_string_lossy())?;
|
||||
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<usize, Error> {
|
||||
// Count the number of migrated blocks
|
||||
let mut count = 0;
|
||||
let rest_db = self.restoration_db();
|
||||
|
||||
let cur_chain_info = self.client.chain_info();
|
||||
|
||||
let next_db = self.restoration_db_handler.open(&rest_db)?;
|
||||
let next_chain = BlockChain::new(Default::default(), &[], next_db.clone());
|
||||
let next_chain_info = next_chain.chain_info();
|
||||
|
||||
// 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]),
|
||||
// so the useful set of blocks is defined as:
|
||||
// [0 ... min(new.first_block, best_ancient_block or best_block)]
|
||||
let find_range = || -> Option<(H256, H256)> {
|
||||
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);
|
||||
|
||||
let highest_block_num = cmp::min(next_available_from.saturating_sub(1), cur_available_to);
|
||||
|
||||
if highest_block_num == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
trace!(target: "snapshot", "Trying to import ancient blocks until {}", highest_block_num);
|
||||
|
||||
// Here we start from the highest block number and go backward to 0,
|
||||
// thus starting at `highest_block_num` and targetting `0`.
|
||||
let target_hash = self.client.block_hash(BlockId::Number(0))?;
|
||||
let start_hash = self.client.block_hash(BlockId::Number(highest_block_num))?;
|
||||
|
||||
Some((start_hash, target_hash))
|
||||
};
|
||||
|
||||
let (start_hash, target_hash) = match find_range() {
|
||||
Some(x) => x,
|
||||
None => return Ok(0),
|
||||
};
|
||||
|
||||
let mut batch = DBTransaction::new();
|
||||
let mut parent_hash = start_hash;
|
||||
while parent_hash != target_hash {
|
||||
// Early return if restoration is aborted
|
||||
if !self.restoring_snapshot.load(Ordering::SeqCst) {
|
||||
return Ok(count);
|
||||
}
|
||||
|
||||
let block = self.client.block(BlockId::Hash(parent_hash)).ok_or(::snapshot::error::Error::UnlinkedAncientBlockChain)?;
|
||||
parent_hash = block.parent_hash();
|
||||
|
||||
let block_number = block.number();
|
||||
let block_receipts = self.client.block_receipts(&block.hash());
|
||||
let parent_total_difficulty = self.client.block_total_difficulty(BlockId::Hash(parent_hash));
|
||||
|
||||
match (block_receipts, parent_total_difficulty) {
|
||||
(Some(block_receipts), Some(parent_total_difficulty)) => {
|
||||
let block_receipts = block_receipts.receipts;
|
||||
|
||||
next_chain.insert_unordered_block(&mut batch, block, block_receipts, Some(parent_total_difficulty), false, true);
|
||||
count += 1;
|
||||
},
|
||||
_ => break,
|
||||
}
|
||||
|
||||
// Writting changes to DB and logging every now and then
|
||||
if block_number % 1_000 == 0 {
|
||||
next_db.key_value().write_buffered(batch);
|
||||
next_chain.commit();
|
||||
next_db.key_value().flush().expect("DB flush failed.");
|
||||
batch = DBTransaction::new();
|
||||
}
|
||||
|
||||
if block_number % 10_000 == 0 {
|
||||
trace!(target: "snapshot", "Block restoration at #{}", block_number);
|
||||
}
|
||||
}
|
||||
|
||||
// Final commit to the DB
|
||||
next_db.key_value().write_buffered(batch);
|
||||
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)
|
||||
}
|
||||
|
||||
/// Get a reference to the snapshot reader.
|
||||
pub fn reader(&self) -> RwLockReadGuard<Option<LooseReader>> {
|
||||
self.reader.read()
|
||||
@@ -480,12 +583,16 @@ impl Service {
|
||||
// Import previous chunks, continue if it fails
|
||||
self.import_prev_chunks(&mut res, manifest).ok();
|
||||
|
||||
*self.status.lock() = RestorationStatus::Ongoing {
|
||||
state_chunks: state_chunks as u32,
|
||||
block_chunks: block_chunks as u32,
|
||||
state_chunks_done: self.state_chunks.load(Ordering::SeqCst) as u32,
|
||||
block_chunks_done: self.block_chunks.load(Ordering::SeqCst) as u32,
|
||||
};
|
||||
// It could be that the restoration failed or completed in the meanwhile
|
||||
let mut restoration_status = self.status.lock();
|
||||
if let RestorationStatus::Initializing { .. } = *restoration_status {
|
||||
*restoration_status = RestorationStatus::Ongoing {
|
||||
state_chunks: state_chunks as u32,
|
||||
block_chunks: block_chunks as u32,
|
||||
state_chunks_done: self.state_chunks.load(Ordering::SeqCst) as u32,
|
||||
block_chunks_done: self.block_chunks.load(Ordering::SeqCst) as u32,
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -752,26 +859,19 @@ impl Drop for Service {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use client::ClientIoMessage;
|
||||
use io::{IoService};
|
||||
use spec::Spec;
|
||||
use journaldb::Algorithm;
|
||||
use error::Error;
|
||||
use snapshot::{ManifestData, RestorationStatus, SnapshotService};
|
||||
use super::*;
|
||||
use tempdir::TempDir;
|
||||
use test_helpers::restoration_db_handler;
|
||||
|
||||
struct NoopDBRestore;
|
||||
impl DatabaseRestore for NoopDBRestore {
|
||||
fn restore_db(&self, _new_db: &str) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
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::<ClientIoMessage>::start().unwrap();
|
||||
let spec = Spec::new_test();
|
||||
|
||||
@@ -785,7 +885,7 @@ mod tests {
|
||||
pruning: Algorithm::Archive,
|
||||
channel: service.channel(),
|
||||
snapshot_root: dir,
|
||||
db_restore: Arc::new(NoopDBRestore),
|
||||
client: client,
|
||||
};
|
||||
|
||||
let service = Service::new(snapshot_params).unwrap();
|
||||
|
||||
@@ -16,26 +16,23 @@
|
||||
|
||||
//! Tests for the snapshot service.
|
||||
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
|
||||
use tempdir::TempDir;
|
||||
use client::{Client, BlockInfo};
|
||||
use blockchain::BlockProvider;
|
||||
use client::{Client, ClientConfig, ImportBlock, BlockInfo};
|
||||
use ids::BlockId;
|
||||
use snapshot::io::{PackedReader, PackedWriter, SnapshotReader, SnapshotWriter};
|
||||
use snapshot::service::{Service, ServiceParams};
|
||||
use snapshot::{self, ManifestData, SnapshotService};
|
||||
use snapshot::{chunk_state, chunk_secondary, ManifestData, Progress, SnapshotService, RestorationStatus};
|
||||
use spec::Spec;
|
||||
use test_helpers::{generate_dummy_client_with_spec_and_data, restoration_db_handler};
|
||||
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 kvdb_rocksdb::DatabaseConfig;
|
||||
|
||||
struct NoopDBRestore;
|
||||
|
||||
impl snapshot::DatabaseRestore for NoopDBRestore {
|
||||
fn restore_db(&self, _new_db: &str) -> Result<(), ::error::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
use verification::queue::kind::blocks::Unverified;
|
||||
|
||||
#[test]
|
||||
fn restored_is_equivalent() {
|
||||
@@ -46,7 +43,6 @@ 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 tempdir = TempDir::new("").unwrap();
|
||||
@@ -73,7 +69,7 @@ fn restored_is_equivalent() {
|
||||
pruning: ::journaldb::Algorithm::Archive,
|
||||
channel: IoChannel::disconnected(),
|
||||
snapshot_root: path,
|
||||
db_restore: client2.clone(),
|
||||
client: client2.clone(),
|
||||
};
|
||||
|
||||
let service = Service::new(service_params).unwrap();
|
||||
@@ -94,7 +90,7 @@ fn restored_is_equivalent() {
|
||||
service.feed_block_chunk(hash, &chunk);
|
||||
}
|
||||
|
||||
assert_eq!(service.status(), ::snapshot::RestorationStatus::Inactive);
|
||||
assert_eq!(service.status(), RestorationStatus::Inactive);
|
||||
|
||||
for x in 0..NUM_BLOCKS {
|
||||
let block1 = client.block(BlockId::Number(x as u64)).unwrap();
|
||||
@@ -106,6 +102,9 @@ 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 spec = Spec::new_null();
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let service_params = ServiceParams {
|
||||
@@ -115,7 +114,7 @@ fn guards_delete_folders() {
|
||||
pruning: ::journaldb::Algorithm::Archive,
|
||||
channel: IoChannel::disconnected(),
|
||||
snapshot_root: tempdir.path().to_owned(),
|
||||
db_restore: Arc::new(NoopDBRestore),
|
||||
client: client,
|
||||
};
|
||||
|
||||
let service = Service::new(service_params).unwrap();
|
||||
@@ -146,3 +145,201 @@ fn guards_delete_folders() {
|
||||
assert!(!path.join("db").exists());
|
||||
assert!(path.join("temp").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keep_ancient_blocks() {
|
||||
::env_logger::init().ok();
|
||||
|
||||
// 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 };
|
||||
|
||||
// Temporary folders
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let snapshot_path = tempdir.path().join("SNAP");
|
||||
|
||||
// Generate blocks
|
||||
let gas_prices = vec![1.into(), 2.into(), 3.into(), 999.into()];
|
||||
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 bc = client.chain();
|
||||
|
||||
// Create the Snapshot
|
||||
let best_hash = bc.best_block_hash();
|
||||
let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap());
|
||||
let block_hashes = chunk_secondary(
|
||||
Box::new(SNAPSHOT_MODE),
|
||||
&bc,
|
||||
best_hash,
|
||||
&writer,
|
||||
&Progress::default()
|
||||
).unwrap();
|
||||
let state_db = client.state_db().journal_db().boxed_clone();
|
||||
let start_header = bc.block_header_data(&best_hash).unwrap();
|
||||
let state_root = start_header.state_root();
|
||||
let state_hashes = chunk_state(
|
||||
state_db.as_hashdb(),
|
||||
&state_root,
|
||||
&writer,
|
||||
&Progress::default(),
|
||||
None
|
||||
).unwrap();
|
||||
|
||||
let manifest = ::snapshot::ManifestData {
|
||||
version: 2,
|
||||
state_hashes: state_hashes,
|
||||
state_root: state_root,
|
||||
block_hashes: block_hashes,
|
||||
block_number: NUM_BLOCKS,
|
||||
block_hash: best_hash,
|
||||
};
|
||||
|
||||
writer.into_inner().finish(manifest.clone()).unwrap();
|
||||
|
||||
// Initialize the Client
|
||||
let db_config = DatabaseConfig::with_columns(::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)),
|
||||
IoChannel::disconnected(),
|
||||
).unwrap();
|
||||
|
||||
// Add some ancient blocks
|
||||
for block_number in 1..50 {
|
||||
let block_hash = bc.block_hash(block_number).unwrap();
|
||||
let block = bc.block(&block_hash).unwrap();
|
||||
client2.import_block(Unverified::from_rlp(block.into_inner()).unwrap()).unwrap();
|
||||
}
|
||||
|
||||
client2.import_verified_blocks();
|
||||
client2.flush_queue();
|
||||
|
||||
// Restore the Snapshot
|
||||
let reader = PackedReader::new(&snapshot_path).unwrap().unwrap();
|
||||
let service_params = ServiceParams {
|
||||
engine: spec.engine.clone(),
|
||||
genesis_block: spec.genesis_block(),
|
||||
restoration_db_handler: restoration_db_handler(db_config),
|
||||
pruning: ::journaldb::Algorithm::Archive,
|
||||
channel: IoChannel::disconnected(),
|
||||
snapshot_root: tempdir.path().to_owned(),
|
||||
client: client2.clone(),
|
||||
};
|
||||
let service = Service::new(service_params).unwrap();
|
||||
service.init_restore(manifest.clone(), false).unwrap();
|
||||
|
||||
for hash in &manifest.block_hashes {
|
||||
let chunk = reader.chunk(*hash).unwrap();
|
||||
service.feed_block_chunk(*hash, &chunk);
|
||||
}
|
||||
|
||||
for hash in &manifest.state_hashes {
|
||||
let chunk = reader.chunk(*hash).unwrap();
|
||||
service.feed_state_chunk(*hash, &chunk);
|
||||
}
|
||||
|
||||
match service.status() {
|
||||
RestorationStatus::Inactive => (),
|
||||
RestorationStatus::Failed => panic!("Snapshot Restoration has failed."),
|
||||
RestorationStatus::Ongoing { .. } => panic!("Snapshot Restoration should be done."),
|
||||
_ => panic!("Invalid Snapshot Service status."),
|
||||
}
|
||||
|
||||
// Check that the latest block number is the right one
|
||||
assert_eq!(client2.block(BlockId::Latest).unwrap().number(), NUM_BLOCKS as u64);
|
||||
|
||||
// Check that we have blocks in [NUM_BLOCKS - NUM_SNAPSHOT_BLOCKS + 1 ; NUM_BLOCKS]
|
||||
// but none before
|
||||
assert!(client2.block(BlockId::Number(NUM_BLOCKS - NUM_SNAPSHOT_BLOCKS + 1)).is_some());
|
||||
assert!(client2.block(BlockId::Number(100)).is_none());
|
||||
|
||||
// Check that the first 50 blocks have been migrated
|
||||
for block_number in 1..49 {
|
||||
assert!(client2.block(BlockId::Number(block_number)).is_some());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recover_aborted_recovery() {
|
||||
::env_logger::init().ok();
|
||||
|
||||
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 spec = Spec::new_null();
|
||||
let tempdir = TempDir::new("").unwrap();
|
||||
let db_config = DatabaseConfig::with_columns(::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)),
|
||||
IoChannel::disconnected(),
|
||||
).unwrap();
|
||||
let service_params = ServiceParams {
|
||||
engine: spec.engine.clone(),
|
||||
genesis_block: spec.genesis_block(),
|
||||
restoration_db_handler: restoration_db_handler(db_config),
|
||||
pruning: ::journaldb::Algorithm::Archive,
|
||||
channel: IoChannel::disconnected(),
|
||||
snapshot_root: tempdir.path().to_owned(),
|
||||
client: client2.clone(),
|
||||
};
|
||||
|
||||
let service = Service::new(service_params).unwrap();
|
||||
service.take_snapshot(&client, NUM_BLOCKS as u64).unwrap();
|
||||
|
||||
let manifest = service.manifest().unwrap();
|
||||
service.init_restore(manifest.clone(), true).unwrap();
|
||||
|
||||
// Restore only the state chunks
|
||||
for hash in &manifest.state_hashes {
|
||||
let chunk = service.chunk(*hash).unwrap();
|
||||
service.feed_state_chunk(*hash, &chunk);
|
||||
}
|
||||
|
||||
match service.status() {
|
||||
RestorationStatus::Ongoing { block_chunks_done, state_chunks_done, .. } => {
|
||||
assert_eq!(state_chunks_done, manifest.state_hashes.len() as u32);
|
||||
assert_eq!(block_chunks_done, 0);
|
||||
},
|
||||
e => panic!("Snapshot restoration must be ongoing ; {:?}", e),
|
||||
}
|
||||
|
||||
// Abort the restore...
|
||||
service.abort_restore();
|
||||
|
||||
// And try again!
|
||||
service.init_restore(manifest.clone(), true).unwrap();
|
||||
|
||||
match service.status() {
|
||||
RestorationStatus::Ongoing { block_chunks_done, state_chunks_done, .. } => {
|
||||
assert_eq!(state_chunks_done, manifest.state_hashes.len() as u32);
|
||||
assert_eq!(block_chunks_done, 0);
|
||||
},
|
||||
e => panic!("Snapshot restoration must be ongoing ; {:?}", e),
|
||||
}
|
||||
|
||||
// Remove the snapshot directory, and restart the restoration
|
||||
// It shouldn't have restored any previous blocks
|
||||
fs::remove_dir_all(tempdir.path()).unwrap();
|
||||
|
||||
// And try again!
|
||||
service.init_restore(manifest.clone(), true).unwrap();
|
||||
|
||||
match service.status() {
|
||||
RestorationStatus::Ongoing { block_chunks_done, state_chunks_done, .. } => {
|
||||
assert_eq!(block_chunks_done, 0);
|
||||
assert_eq!(state_chunks_done, 0);
|
||||
},
|
||||
_ => panic!("Snapshot restoration must be ongoing"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,13 @@
|
||||
//! Watcher for snapshot-related chain events.
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use client::{BlockInfo, Client, ChainNotify, ChainRoute, ClientIoMessage};
|
||||
use client::{BlockInfo, Client, ChainNotify, NewBlocks, ClientIoMessage};
|
||||
use ids::BlockId;
|
||||
|
||||
use io::IoChannel;
|
||||
use ethereum_types::H256;
|
||||
use bytes::Bytes;
|
||||
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use std::sync::Arc;
|
||||
|
||||
// helper trait for transforming hashes to numbers and checking if syncing.
|
||||
trait Oracle: Send + Sync {
|
||||
@@ -99,20 +98,12 @@ impl Watcher {
|
||||
}
|
||||
|
||||
impl ChainNotify for Watcher {
|
||||
fn new_blocks(
|
||||
&self,
|
||||
imported: Vec<H256>,
|
||||
_: Vec<H256>,
|
||||
_: ChainRoute,
|
||||
_: Vec<H256>,
|
||||
_: Vec<Bytes>,
|
||||
_duration: Duration)
|
||||
{
|
||||
if self.oracle.is_major_importing() { return }
|
||||
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", imported.len());
|
||||
trace!(target: "snapshot_watcher", "{} imported", new_blocks.imported.len());
|
||||
|
||||
let highest = imported.into_iter()
|
||||
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)
|
||||
@@ -130,7 +121,7 @@ impl ChainNotify for Watcher {
|
||||
mod tests {
|
||||
use super::{Broadcast, Oracle, Watcher};
|
||||
|
||||
use client::{ChainNotify, ChainRoute};
|
||||
use client::{ChainNotify, NewBlocks, ChainRoute};
|
||||
|
||||
use ethereum_types::{H256, U256};
|
||||
|
||||
@@ -170,14 +161,15 @@ mod tests {
|
||||
history: history,
|
||||
};
|
||||
|
||||
watcher.new_blocks(
|
||||
watcher.new_blocks(NewBlocks::new(
|
||||
hashes,
|
||||
vec![],
|
||||
ChainRoute::default(),
|
||||
vec![],
|
||||
vec![],
|
||||
DURATION_ZERO,
|
||||
);
|
||||
false
|
||||
));
|
||||
}
|
||||
|
||||
// helper
|
||||
|
||||
@@ -1136,7 +1136,7 @@ impl<B: Backend> State<B> {
|
||||
self.note_cache(a);
|
||||
|
||||
// at this point the entry is guaranteed to be in the cache.
|
||||
Ok(RefMut::map(self.cache.borrow_mut(), |c| {
|
||||
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 {
|
||||
@@ -1146,18 +1146,19 @@ impl<B: Backend> State<B> {
|
||||
|
||||
// set the dirty flag after changing account data.
|
||||
entry.state = AccountState::Dirty;
|
||||
match entry.account {
|
||||
Some(ref mut account) => {
|
||||
if require_code {
|
||||
let addr_hash = account.address_hash(a);
|
||||
let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), addr_hash);
|
||||
Self::update_account_cache(RequireCache::Code, account, &self.db, accountdb.as_hashdb());
|
||||
}
|
||||
account
|
||||
},
|
||||
_ => panic!("Required account must always exist; qed"),
|
||||
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_hashdb(), addr_hash);
|
||||
|
||||
if !Self::update_account_cache(RequireCache::Code, &mut account, &self.db, accountdb.as_hashdb()) {
|
||||
return Err(Box::new(TrieError::IncompleteDatabase(H256::from(a))))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
/// Replace account code and storage. Creates account if it does not exist.
|
||||
@@ -2591,12 +2592,12 @@ mod tests {
|
||||
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());
|
||||
pod_account::diff_pod(Some(&PodAccount {
|
||||
balance: U256::from(100),
|
||||
nonce: U256::zero(),
|
||||
code: Some(Default::default()),
|
||||
storage: Default::default()
|
||||
}), None).as_ref());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2622,18 +2623,18 @@ mod tests {
|
||||
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());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ use transaction::{Action, Transaction, SignedTransaction};
|
||||
use views::BlockView;
|
||||
use blooms_db;
|
||||
use kvdb::KeyValueDB;
|
||||
use kvdb_rocksdb;
|
||||
use kvdb_rocksdb::{self, Database, DatabaseConfig};
|
||||
use tempdir::TempDir;
|
||||
use verification::queue::kind::blocks::Unverified;
|
||||
use encoded;
|
||||
@@ -263,30 +263,30 @@ pub fn get_test_client_with_blocks(blocks: Vec<Bytes>) -> Arc<Client> {
|
||||
client
|
||||
}
|
||||
|
||||
struct TestBlockChainDB {
|
||||
_blooms_dir: TempDir,
|
||||
_trace_blooms_dir: TempDir,
|
||||
blooms: blooms_db::Database,
|
||||
trace_blooms: blooms_db::Database,
|
||||
key_value: Arc<KeyValueDB>,
|
||||
}
|
||||
|
||||
impl BlockChainDB for TestBlockChainDB {
|
||||
fn key_value(&self) -> &Arc<KeyValueDB> {
|
||||
&self.key_value
|
||||
}
|
||||
|
||||
fn blooms(&self) -> &blooms_db::Database {
|
||||
&self.blooms
|
||||
}
|
||||
|
||||
fn trace_blooms(&self) -> &blooms_db::Database {
|
||||
&self.trace_blooms
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new test instance of `BlockChainDB`
|
||||
pub fn new_db() -> Arc<BlockChainDB> {
|
||||
struct TestBlockChainDB {
|
||||
_blooms_dir: TempDir,
|
||||
_trace_blooms_dir: TempDir,
|
||||
blooms: blooms_db::Database,
|
||||
trace_blooms: blooms_db::Database,
|
||||
key_value: Arc<KeyValueDB>,
|
||||
}
|
||||
|
||||
impl BlockChainDB for TestBlockChainDB {
|
||||
fn key_value(&self) -> &Arc<KeyValueDB> {
|
||||
&self.key_value
|
||||
}
|
||||
|
||||
fn blooms(&self) -> &blooms_db::Database {
|
||||
&self.blooms
|
||||
}
|
||||
|
||||
fn trace_blooms(&self) -> &blooms_db::Database {
|
||||
&self.trace_blooms
|
||||
}
|
||||
}
|
||||
|
||||
let blooms_dir = TempDir::new("").unwrap();
|
||||
let trace_blooms_dir = TempDir::new("").unwrap();
|
||||
|
||||
@@ -301,6 +301,26 @@ pub fn new_db() -> Arc<BlockChainDB> {
|
||||
Arc::new(db)
|
||||
}
|
||||
|
||||
/// Creates a new temporary `BlockChainDB` on FS
|
||||
pub fn new_temp_db(tempdir: &Path) -> Arc<BlockChainDB> {
|
||||
let blooms_dir = TempDir::new("").unwrap();
|
||||
let trace_blooms_dir = TempDir::new("").unwrap();
|
||||
let key_value_dir = tempdir.join("key_value");
|
||||
|
||||
let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
|
||||
let key_value_db = Database::open(&db_config, key_value_dir.to_str().unwrap()).unwrap();
|
||||
|
||||
let db = TestBlockChainDB {
|
||||
blooms: blooms_db::Database::open(blooms_dir.path()).unwrap(),
|
||||
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(key_value_db)
|
||||
};
|
||||
|
||||
Arc::new(db)
|
||||
}
|
||||
|
||||
/// Creates new instance of KeyValueDBHandler
|
||||
pub fn restoration_db_handler(config: kvdb_rocksdb::DatabaseConfig) -> Box<BlockChainDBHandler> {
|
||||
struct RestorationDBHandler {
|
||||
|
||||
@@ -583,6 +583,13 @@ impl<K: Kind> VerificationQueue<K> {
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns true if there is nothing currently in the queue.
|
||||
/// TODO [ToDr] Optimize to avoid locking
|
||||
pub fn is_empty(&self) -> bool {
|
||||
let v = &self.verification;
|
||||
v.unverified.lock().is_empty() && v.verifying.lock().is_empty() && v.verified.lock().is_empty()
|
||||
}
|
||||
|
||||
/// Get queue status.
|
||||
pub fn queue_info(&self) -> QueueInfo {
|
||||
use std::mem::size_of;
|
||||
|
||||
@@ -147,7 +147,9 @@ pub fn verify_block_family<C: BlockInfo + CallContract>(header: &Header, parent:
|
||||
verify_uncles(params.block, params.block_provider, engine)?;
|
||||
|
||||
for tx in ¶ms.block.transactions {
|
||||
engine.machine().verify_transaction(tx, header, params.client)?;
|
||||
// transactions are verified against the parent header since the current
|
||||
// state wasn't available when the tx was created
|
||||
engine.machine().verify_transaction(tx, parent, params.client)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, mpsc, atomic};
|
||||
use std::collections::{HashMap, BTreeMap};
|
||||
use std::io;
|
||||
use std::ops::Range;
|
||||
@@ -29,14 +29,14 @@ use types::pruning_info::PruningInfo;
|
||||
use ethereum_types::{H256, H512, U256};
|
||||
use io::{TimerToken};
|
||||
use ethcore::ethstore::ethkey::Secret;
|
||||
use ethcore::client::{BlockChainClient, ChainNotify, ChainRoute, ChainMessageType};
|
||||
use ethcore::client::{BlockChainClient, ChainNotify, NewBlocks, ChainMessageType};
|
||||
use ethcore::snapshot::SnapshotService;
|
||||
use ethcore::header::BlockNumber;
|
||||
use sync_io::NetSyncIo;
|
||||
use chain::{ChainSync, SyncStatus as EthSyncStatus};
|
||||
use chain::{ChainSyncApi, SyncStatus as EthSyncStatus};
|
||||
use std::net::{SocketAddr, AddrParseError};
|
||||
use std::str::FromStr;
|
||||
use parking_lot::RwLock;
|
||||
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,
|
||||
PRIVATE_TRANSACTION_PACKET, SIGNED_PRIVATE_TRANSACTION_PACKET};
|
||||
@@ -228,6 +228,37 @@ impl AttachedProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// NOTE These tasks might not complete at all, so anything
|
||||
/// that happens here should work even if the task is cancelled.
|
||||
#[derive(Debug)]
|
||||
pub enum PriorityTask {
|
||||
/// Propagate given block
|
||||
PropagateBlock {
|
||||
/// When the task was initiated
|
||||
started: ::std::time::Instant,
|
||||
/// Raw block RLP to propagate
|
||||
block: Bytes,
|
||||
/// Block hash
|
||||
hash: H256,
|
||||
/// Blocks difficulty
|
||||
difficulty: U256,
|
||||
},
|
||||
/// Propagate a list of transactions
|
||||
PropagateTransactions(::std::time::Instant, Arc<atomic::AtomicBool>),
|
||||
}
|
||||
impl PriorityTask {
|
||||
/// Mark the task as being processed, right after it's retrieved from the queue.
|
||||
pub fn starting(&self) {
|
||||
match *self {
|
||||
PriorityTask::PropagateTransactions(_, ref is_ready) => is_ready.store(true, atomic::Ordering::SeqCst),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// EthSync initialization parameters.
|
||||
pub struct Params {
|
||||
/// Configuration.
|
||||
@@ -260,16 +291,16 @@ pub struct EthSync {
|
||||
subprotocol_name: [u8; 3],
|
||||
/// Light subprotocol name.
|
||||
light_subprotocol_name: [u8; 3],
|
||||
/// Priority tasks notification channel
|
||||
priority_tasks: Mutex<mpsc::Sender<PriorityTask>>,
|
||||
}
|
||||
|
||||
fn light_params(
|
||||
network_id: u64,
|
||||
max_peers: u32,
|
||||
median_peers: f64,
|
||||
pruning_info: PruningInfo,
|
||||
sample_store: Option<Box<SampleStore>>,
|
||||
) -> LightParams {
|
||||
const MAX_LIGHTSERV_LOAD: f64 = 0.5;
|
||||
|
||||
let mut light_params = LightParams {
|
||||
network_id: network_id,
|
||||
config: Default::default(),
|
||||
@@ -282,9 +313,7 @@ fn light_params(
|
||||
sample_store: sample_store,
|
||||
};
|
||||
|
||||
let max_peers = ::std::cmp::max(max_peers, 1);
|
||||
light_params.config.load_share = MAX_LIGHTSERV_LOAD / max_peers as f64;
|
||||
|
||||
light_params.config.median_peers = median_peers;
|
||||
light_params
|
||||
}
|
||||
|
||||
@@ -301,9 +330,10 @@ impl EthSync {
|
||||
.map(|mut p| { p.push("request_timings"); light_net::FileStore(p) })
|
||||
.map(|store| Box::new(store) as Box<_>);
|
||||
|
||||
let median_peers = (params.network_config.min_peers + params.network_config.max_peers) as f64 / 2.0;
|
||||
let light_params = light_params(
|
||||
params.config.network_id,
|
||||
params.network_config.max_peers,
|
||||
median_peers,
|
||||
pruning_info,
|
||||
sample_store,
|
||||
);
|
||||
@@ -315,13 +345,19 @@ impl EthSync {
|
||||
})
|
||||
};
|
||||
|
||||
let chain_sync = ChainSync::new(params.config, &*params.chain, params.private_tx_handler.clone());
|
||||
let (priority_tasks_tx, priority_tasks_rx) = mpsc::channel();
|
||||
let sync = ChainSyncApi::new(
|
||||
params.config,
|
||||
&*params.chain,
|
||||
params.private_tx_handler.clone(),
|
||||
priority_tasks_rx,
|
||||
);
|
||||
let service = NetworkService::new(params.network_config.clone().into_basic()?, connection_filter)?;
|
||||
|
||||
let sync = Arc::new(EthSync {
|
||||
network: service,
|
||||
eth_handler: Arc::new(SyncProtocolHandler {
|
||||
sync: RwLock::new(chain_sync),
|
||||
sync,
|
||||
chain: params.chain,
|
||||
snapshot_service: params.snapshot_service,
|
||||
overlay: RwLock::new(HashMap::new()),
|
||||
@@ -330,26 +366,32 @@ impl EthSync {
|
||||
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),
|
||||
});
|
||||
|
||||
Ok(sync)
|
||||
}
|
||||
|
||||
/// Priority tasks producer
|
||||
pub fn priority_tasks(&self) -> mpsc::Sender<PriorityTask> {
|
||||
self.priority_tasks.lock().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl SyncProvider for EthSync {
|
||||
/// Get sync status
|
||||
fn status(&self) -> EthSyncStatus {
|
||||
self.eth_handler.sync.read().status()
|
||||
self.eth_handler.sync.status()
|
||||
}
|
||||
|
||||
/// Get sync peers
|
||||
fn peers(&self) -> Vec<PeerInfo> {
|
||||
self.network.with_context_eval(self.subprotocol_name, |ctx| {
|
||||
let peer_ids = self.network.connected_peers();
|
||||
let eth_sync = self.eth_handler.sync.read();
|
||||
let light_proto = self.light_proto.as_ref();
|
||||
|
||||
peer_ids.into_iter().filter_map(|peer_id| {
|
||||
let peer_info = self.eth_handler.sync.peer_info(&peer_ids);
|
||||
peer_ids.into_iter().zip(peer_info).filter_map(|(peer_id, peer_info)| {
|
||||
let session_info = match ctx.session_info(peer_id) {
|
||||
None => return None,
|
||||
Some(info) => info,
|
||||
@@ -361,7 +403,7 @@ impl SyncProvider for EthSync {
|
||||
capabilities: session_info.peer_capabilities.into_iter().map(|c| c.to_string()).collect(),
|
||||
remote_address: session_info.remote_address,
|
||||
local_address: session_info.local_address,
|
||||
eth_info: eth_sync.peer_info(&peer_id),
|
||||
eth_info: peer_info,
|
||||
pip_info: light_proto.as_ref().and_then(|lp| lp.peer_status(peer_id)).map(Into::into),
|
||||
})
|
||||
}).collect()
|
||||
@@ -373,17 +415,17 @@ impl SyncProvider for EthSync {
|
||||
}
|
||||
|
||||
fn transactions_stats(&self) -> BTreeMap<H256, TransactionStats> {
|
||||
let sync = self.eth_handler.sync.read();
|
||||
sync.transactions_stats()
|
||||
.iter()
|
||||
.map(|(hash, stats)| (*hash, stats.into()))
|
||||
.collect()
|
||||
self.eth_handler.sync.transactions_stats()
|
||||
}
|
||||
}
|
||||
|
||||
const PEERS_TIMER: TimerToken = 0;
|
||||
const SYNC_TIMER: TimerToken = 1;
|
||||
const TX_TIMER: TimerToken = 2;
|
||||
const MAINTAIN_SYNC_TIMER: TimerToken = 1;
|
||||
const CONTINUE_SYNC_TIMER: TimerToken = 2;
|
||||
const TX_TIMER: TimerToken = 3;
|
||||
const PRIORITY_TIMER: TimerToken = 4;
|
||||
|
||||
pub(crate) const PRIORITY_TIMER_INTERVAL: Duration = Duration::from_millis(250);
|
||||
|
||||
struct SyncProtocolHandler {
|
||||
/// Shared blockchain client.
|
||||
@@ -391,7 +433,7 @@ struct SyncProtocolHandler {
|
||||
/// Shared snapshot service.
|
||||
snapshot_service: Arc<SnapshotService>,
|
||||
/// Sync strategy
|
||||
sync: RwLock<ChainSync>,
|
||||
sync: ChainSyncApi,
|
||||
/// Chain overlay used to cache data such as fork block.
|
||||
overlay: RwLock<HashMap<BlockNumber, Bytes>>,
|
||||
}
|
||||
@@ -400,13 +442,16 @@ impl NetworkProtocolHandler for SyncProtocolHandler {
|
||||
fn initialize(&self, io: &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(SYNC_TIMER, Duration::from_millis(1100)).expect("Error registering sync timer");
|
||||
io.register_timer(MAINTAIN_SYNC_TIMER, Duration::from_millis(1100)).expect("Error registering sync timer");
|
||||
io.register_timer(CONTINUE_SYNC_TIMER, Duration::from_millis(2500)).expect("Error registering sync timer");
|
||||
io.register_timer(TX_TIMER, Duration::from_millis(1300)).expect("Error registering transactions timer");
|
||||
|
||||
io.register_timer(PRIORITY_TIMER, PRIORITY_TIMER_INTERVAL).expect("Error registering peers timer");
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) {
|
||||
ChainSync::dispatch_packet(&self.sync, &mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer, packet_id, data);
|
||||
self.sync.dispatch_packet(&mut NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay), *peer, packet_id, data);
|
||||
}
|
||||
|
||||
fn connected(&self, io: &NetworkContext, peer: &PeerId) {
|
||||
@@ -431,24 +476,31 @@ impl NetworkProtocolHandler for SyncProtocolHandler {
|
||||
let mut io = NetSyncIo::new(io, &*self.chain, &*self.snapshot_service, &self.overlay);
|
||||
match timer {
|
||||
PEERS_TIMER => self.sync.write().maintain_peers(&mut io),
|
||||
SYNC_TIMER => self.sync.write().maintain_sync(&mut io),
|
||||
TX_TIMER => {
|
||||
self.sync.write().propagate_new_transactions(&mut io);
|
||||
},
|
||||
MAINTAIN_SYNC_TIMER => self.sync.write().maintain_sync(&mut io),
|
||||
CONTINUE_SYNC_TIMER => self.sync.write().continue_sync(&mut io),
|
||||
TX_TIMER => self.sync.write().propagate_new_transactions(&mut io),
|
||||
PRIORITY_TIMER => self.sync.process_priority_queue(&mut io),
|
||||
_ => warn!("Unknown timer {} triggered.", timer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChainNotify for EthSync {
|
||||
fn new_blocks(&self,
|
||||
imported: Vec<H256>,
|
||||
invalid: Vec<H256>,
|
||||
route: ChainRoute,
|
||||
sealed: Vec<H256>,
|
||||
proposed: Vec<Bytes>,
|
||||
_duration: Duration)
|
||||
fn block_pre_import(&self, bytes: &Bytes, hash: &H256, difficulty: &U256) {
|
||||
let task = PriorityTask::PropagateBlock {
|
||||
started: ::std::time::Instant::now(),
|
||||
block: bytes.clone(),
|
||||
hash: *hash,
|
||||
difficulty: *difficulty,
|
||||
};
|
||||
if let Err(e) = self.priority_tasks.lock().send(task) {
|
||||
warn!(target: "sync", "Unexpected error during priority block propagation: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn new_blocks(&self, new_blocks: NewBlocks)
|
||||
{
|
||||
if new_blocks.has_more_blocks_to_import { return }
|
||||
use light::net::Announcement;
|
||||
|
||||
self.network.with_context(self.subprotocol_name, |context| {
|
||||
@@ -456,12 +508,12 @@ impl ChainNotify for EthSync {
|
||||
&self.eth_handler.overlay);
|
||||
self.eth_handler.sync.write().chain_new_blocks(
|
||||
&mut sync_io,
|
||||
&imported,
|
||||
&invalid,
|
||||
route.enacted(),
|
||||
route.retracted(),
|
||||
&sealed,
|
||||
&proposed);
|
||||
&new_blocks.imported,
|
||||
&new_blocks.invalid,
|
||||
new_blocks.route.enacted(),
|
||||
new_blocks.route.retracted(),
|
||||
&new_blocks.sealed,
|
||||
&new_blocks.proposed);
|
||||
});
|
||||
|
||||
self.network.with_context(self.light_subprotocol_name, |context| {
|
||||
@@ -940,19 +992,3 @@ impl LightSyncProvider for LightSync {
|
||||
Default::default() // TODO
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn light_params_load_share_depends_on_max_peers() {
|
||||
let pruning_info = PruningInfo {
|
||||
earliest_chain: 0,
|
||||
earliest_state: 0,
|
||||
};
|
||||
let params1 = light_params(0, 10, pruning_info.clone(), None);
|
||||
let params2 = light_params(0, 20, pruning_info, None);
|
||||
assert!(params1.config.load_share > params2.config.load_share)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ use rlp::Rlp;
|
||||
use snapshot::ChunkType;
|
||||
use std::cmp;
|
||||
use std::mem;
|
||||
use std::collections::HashSet;
|
||||
use std::time::Instant;
|
||||
use sync_io::SyncIo;
|
||||
|
||||
@@ -58,7 +57,6 @@ use super::{
|
||||
SNAPSHOT_DATA_PACKET,
|
||||
SNAPSHOT_MANIFEST_PACKET,
|
||||
STATUS_PACKET,
|
||||
TRANSACTIONS_PACKET,
|
||||
};
|
||||
|
||||
/// The Chain Sync Handler: handles responses from peers
|
||||
@@ -67,14 +65,9 @@ pub struct SyncHandler;
|
||||
impl SyncHandler {
|
||||
/// Handle incoming packet from peer
|
||||
pub fn on_packet(sync: &mut ChainSync, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) {
|
||||
if packet_id != STATUS_PACKET && !sync.peers.contains_key(&peer) {
|
||||
debug!(target:"sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_info(peer));
|
||||
return;
|
||||
}
|
||||
let rlp = Rlp::new(data);
|
||||
let result = match packet_id {
|
||||
STATUS_PACKET => SyncHandler::on_peer_status(sync, io, peer, &rlp),
|
||||
TRANSACTIONS_PACKET => SyncHandler::on_peer_transactions(sync, io, peer, &rlp),
|
||||
BLOCK_HEADERS_PACKET => SyncHandler::on_peer_block_headers(sync, io, peer, &rlp),
|
||||
BLOCK_BODIES_PACKET => SyncHandler::on_peer_block_bodies(sync, io, peer, &rlp),
|
||||
RECEIPTS_PACKET => SyncHandler::on_peer_block_receipts(sync, io, peer, &rlp),
|
||||
@@ -104,15 +97,12 @@ impl SyncHandler {
|
||||
sync.sync_peer(io, peer, false);
|
||||
},
|
||||
}
|
||||
// give tasks to other peers
|
||||
sync.continue_sync(io);
|
||||
}
|
||||
|
||||
/// Called when peer sends us new consensus packet
|
||||
pub fn on_consensus_packet(io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> {
|
||||
pub fn on_consensus_packet(io: &mut 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());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called by peer when it is disconnecting
|
||||
@@ -578,8 +568,8 @@ impl SyncHandler {
|
||||
asking_blocks: Vec::new(),
|
||||
asking_hash: None,
|
||||
ask_time: Instant::now(),
|
||||
last_sent_transactions: HashSet::new(),
|
||||
last_sent_private_transactions: HashSet::new(),
|
||||
last_sent_transactions: Default::default(),
|
||||
last_sent_private_transactions: Default::default(),
|
||||
expired: false,
|
||||
confirmation: if sync.fork_block.is_none() { ForkConfirmation::Confirmed } else { ForkConfirmation::Unconfirmed },
|
||||
asking_snapshot_data: None,
|
||||
@@ -635,7 +625,7 @@ impl SyncHandler {
|
||||
}
|
||||
|
||||
/// Called when peer sends us new transactions
|
||||
fn on_peer_transactions(sync: &mut ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> {
|
||||
pub fn on_peer_transactions(sync: &ChainSync, io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> {
|
||||
// Accept transactions only when fully synced
|
||||
if !io.is_chain_queue_empty() || (sync.state != SyncState::Idle && sync.state != SyncState::NewBlocks) {
|
||||
trace!(target: "sync", "{} Ignoring transactions while syncing", peer_id);
|
||||
|
||||
@@ -92,17 +92,17 @@ mod propagator;
|
||||
mod requester;
|
||||
mod supplier;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::collections::{HashSet, HashMap};
|
||||
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 ethereum_types::{H256, U256};
|
||||
use fastmap::H256FastMap;
|
||||
use parking_lot::RwLock;
|
||||
use fastmap::{H256FastMap, H256FastSet};
|
||||
use parking_lot::{Mutex, RwLock, RwLockWriteGuard};
|
||||
use bytes::Bytes;
|
||||
use rlp::{Rlp, RlpStream, DecoderError};
|
||||
use rlp::{RlpStream, DecoderError};
|
||||
use network::{self, PeerId, PacketId};
|
||||
use ethcore::header::{BlockNumber};
|
||||
use ethcore::client::{BlockChainClient, BlockStatus, BlockId, BlockChainInfo, BlockQueueInfo};
|
||||
@@ -112,7 +112,7 @@ use super::{WarpSync, SyncConfig};
|
||||
use block_sync::{BlockDownloader, DownloadAction};
|
||||
use rand::Rng;
|
||||
use snapshot::{Snapshot};
|
||||
use api::{EthProtocolInfo as PeerInfoDigest, WARP_SYNC_PROTOCOL_ID};
|
||||
use api::{EthProtocolInfo as PeerInfoDigest, WARP_SYNC_PROTOCOL_ID, PriorityTask};
|
||||
use private_tx::PrivateTxHandler;
|
||||
use transactions_stats::{TransactionsStats, Stats as TransactionStats};
|
||||
use transaction::UnverifiedTransaction;
|
||||
@@ -120,7 +120,7 @@ use transaction::UnverifiedTransaction;
|
||||
use self::handler::SyncHandler;
|
||||
use self::propagator::SyncPropagator;
|
||||
use self::requester::SyncRequester;
|
||||
use self::supplier::SyncSupplier;
|
||||
pub(crate) use self::supplier::SyncSupplier;
|
||||
|
||||
known_heap_size!(0, PeerInfo);
|
||||
|
||||
@@ -187,6 +187,11 @@ const FORK_HEADER_TIMEOUT: Duration = Duration::from_secs(3);
|
||||
const SNAPSHOT_MANIFEST_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const SNAPSHOT_DATA_TIMEOUT: Duration = Duration::from_secs(120);
|
||||
|
||||
/// 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)]
|
||||
/// Sync state
|
||||
pub enum SyncState {
|
||||
@@ -323,9 +328,9 @@ pub struct PeerInfo {
|
||||
/// Request timestamp
|
||||
ask_time: Instant,
|
||||
/// Holds a set of transactions recently sent to this peer to avoid spamming.
|
||||
last_sent_transactions: HashSet<H256>,
|
||||
last_sent_transactions: H256FastSet,
|
||||
/// Holds a set of private transactions and their signatures recently sent to this peer to avoid spamming.
|
||||
last_sent_private_transactions: HashSet<H256>,
|
||||
last_sent_private_transactions: H256FastSet,
|
||||
/// Pending request is expired and result should be ignored
|
||||
expired: bool,
|
||||
/// Peer fork confirmation status
|
||||
@@ -375,6 +380,217 @@ pub mod random {
|
||||
pub type RlpResponseResult = Result<Option<(PacketId, RlpStream)>, PacketDecodeError>;
|
||||
pub type Peers = HashMap<PeerId, PeerInfo>;
|
||||
|
||||
/// Thread-safe wrapper for `ChainSync`.
|
||||
///
|
||||
/// NOTE always lock in order of fields declaration
|
||||
pub struct ChainSyncApi {
|
||||
/// Priority tasks queue
|
||||
priority_tasks: Mutex<mpsc::Receiver<PriorityTask>>,
|
||||
/// The rest of sync data
|
||||
sync: RwLock<ChainSync>,
|
||||
}
|
||||
|
||||
impl ChainSyncApi {
|
||||
/// Creates new `ChainSyncApi`
|
||||
pub fn new(
|
||||
config: SyncConfig,
|
||||
chain: &BlockChainClient,
|
||||
private_tx_handler: Arc<PrivateTxHandler>,
|
||||
priority_tasks: mpsc::Receiver<PriorityTask>,
|
||||
) -> Self {
|
||||
ChainSyncApi {
|
||||
sync: RwLock::new(ChainSync::new(config, chain, private_tx_handler)),
|
||||
priority_tasks: Mutex::new(priority_tasks),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives `write` access to underlying `ChainSync`
|
||||
pub fn write(&self) -> RwLockWriteGuard<ChainSync> {
|
||||
self.sync.write()
|
||||
}
|
||||
|
||||
/// Returns info about given list of peers
|
||||
pub fn peer_info(&self, ids: &[PeerId]) -> Vec<Option<PeerInfoDigest>> {
|
||||
let sync = self.sync.read();
|
||||
ids.iter().map(|id| sync.peer_info(id)).collect()
|
||||
}
|
||||
|
||||
/// Returns synchonization status
|
||||
pub fn status(&self) -> SyncStatus {
|
||||
self.sync.read().status()
|
||||
}
|
||||
|
||||
/// Returns transactions propagation statistics
|
||||
pub fn transactions_stats(&self) -> BTreeMap<H256, ::TransactionStats> {
|
||||
self.sync.read().transactions_stats()
|
||||
.iter()
|
||||
.map(|(hash, stats)| (*hash, stats.into()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Dispatch incoming requests and responses
|
||||
pub fn dispatch_packet(&self, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) {
|
||||
SyncSupplier::dispatch_packet(&self.sync, io, peer, packet_id, data)
|
||||
}
|
||||
|
||||
/// Process a priority propagation queue.
|
||||
/// This task is run from a timer and should be time constrained.
|
||||
/// Hence we set up a deadline for the execution and cancel the task if the deadline is exceeded.
|
||||
///
|
||||
/// 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) {
|
||||
fn check_deadline(deadline: Instant) -> Option<Duration> {
|
||||
let now = Instant::now();
|
||||
if now > deadline {
|
||||
None
|
||||
} else {
|
||||
Some(deadline - now)
|
||||
}
|
||||
}
|
||||
|
||||
// deadline to get the task from the queue
|
||||
let deadline = Instant::now() + ::api::PRIORITY_TIMER_INTERVAL;
|
||||
let mut work = || {
|
||||
let task = {
|
||||
let tasks = self.priority_tasks.try_lock_until(deadline)?;
|
||||
let left = check_deadline(deadline)?;
|
||||
tasks.recv_timeout(left).ok()?
|
||||
};
|
||||
task.starting();
|
||||
// wait for the sync lock until deadline,
|
||||
// note we might drop the task here if we won't manage to acquire the lock.
|
||||
let mut sync = self.sync.try_write_until(deadline)?;
|
||||
// since we already have everything let's use a different deadline
|
||||
// to do the rest of the job now, so that previous work is not wasted.
|
||||
let deadline = Instant::now() + PRIORITY_TASK_DEADLINE;
|
||||
let as_ms = move |prev| {
|
||||
let dur: Duration = Instant::now() - prev;
|
||||
dur.as_secs() * 1_000 + dur.subsec_millis() as u64
|
||||
};
|
||||
match task {
|
||||
// NOTE We can't simply use existing methods,
|
||||
// cause the block is not in the DB yet.
|
||||
PriorityTask::PropagateBlock { started, block, hash, difficulty } => {
|
||||
// try to send to peers that are on the same block as us
|
||||
// (they will most likely accept the new block).
|
||||
let chain_info = io.chain().chain_info();
|
||||
let total_difficulty = chain_info.total_difficulty + difficulty;
|
||||
let rlp = ChainSync::create_block_rlp(&block, total_difficulty);
|
||||
for peers in sync.get_peers(&chain_info, PeerState::SameBlock).chunks(10) {
|
||||
check_deadline(deadline)?;
|
||||
for peer in peers {
|
||||
SyncPropagator::send_packet(io, *peer, NEW_BLOCK_PACKET, rlp.clone());
|
||||
if let Some(ref mut peer) = sync.peers.get_mut(peer) {
|
||||
peer.latest_hash = hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!(target: "sync", "Finished block propagation, took {}ms", as_ms(started));
|
||||
},
|
||||
PriorityTask::PropagateTransactions(time, _) => {
|
||||
SyncPropagator::propagate_new_transactions(&mut sync, io, || {
|
||||
check_deadline(deadline).is_some()
|
||||
});
|
||||
debug!(target: "sync", "Finished transaction propagation, took {}ms", as_ms(time));
|
||||
},
|
||||
}
|
||||
|
||||
Some(())
|
||||
};
|
||||
|
||||
// Process as many items as we can until the deadline is reached.
|
||||
loop {
|
||||
if work().is_none() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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<Bytes> {
|
||||
match chain.tree_route(from, to) {
|
||||
Some(route) => {
|
||||
let uncles = chain.find_uncles(from).unwrap_or_else(Vec::new);
|
||||
match route.blocks.len() {
|
||||
0 => None,
|
||||
_ => {
|
||||
let mut blocks = route.blocks;
|
||||
blocks.extend(uncles);
|
||||
let mut rlp_stream = RlpStream::new_list(blocks.len());
|
||||
for block_hash in blocks {
|
||||
let mut hash_rlp = RlpStream::new_list(2);
|
||||
let number = chain.block_header(BlockId::Hash(block_hash.clone()))
|
||||
.expect("chain.tree_route and chain.find_uncles only return hahses of blocks that are in the blockchain. qed.").number();
|
||||
hash_rlp.append(&block_hash);
|
||||
hash_rlp.append(&number);
|
||||
rlp_stream.append_raw(hash_rlp.as_raw(), 1);
|
||||
}
|
||||
Some(rlp_stream.out())
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
/// creates rlp from block bytes and total difficulty
|
||||
fn create_block_rlp(bytes: &Bytes, total_difficulty: U256) -> Bytes {
|
||||
let mut rlp_stream = RlpStream::new_list(2);
|
||||
rlp_stream.append_raw(bytes, 1);
|
||||
rlp_stream.append(&total_difficulty);
|
||||
rlp_stream.out()
|
||||
}
|
||||
|
||||
/// creates latest block rlp for the given client
|
||||
fn create_latest_block_rlp(chain: &BlockChainClient) -> Bytes {
|
||||
Self::create_block_rlp(
|
||||
&chain.block(BlockId::Hash(chain.chain_info().best_block_hash))
|
||||
.expect("Best block always exists").into_inner(),
|
||||
chain.chain_info().total_difficulty
|
||||
)
|
||||
}
|
||||
|
||||
/// creates given hash block rlp for the given client
|
||||
fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes {
|
||||
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.")
|
||||
)
|
||||
}
|
||||
|
||||
fn select_random_peers(peers: &[PeerId]) -> Vec<PeerId> {
|
||||
// take sqrt(x) peers
|
||||
let mut peers = peers.to_vec();
|
||||
let mut count = (peers.len() as f64).powf(0.5).round() as usize;
|
||||
count = cmp::min(count, MAX_PEERS_PROPAGATION);
|
||||
count = cmp::max(count, MIN_PEERS_PROPAGATION);
|
||||
random::new().shuffle(&mut peers);
|
||||
peers.truncate(count);
|
||||
peers
|
||||
}
|
||||
|
||||
fn get_init_state(warp_sync: WarpSync, chain: &BlockChainClient) -> SyncState {
|
||||
let best_block = chain.chain_info().best_block_number;
|
||||
match warp_sync {
|
||||
WarpSync::Enabled => SyncState::WaitingPeers,
|
||||
WarpSync::OnlyAndAfter(block) if block > best_block => SyncState::WaitingPeers,
|
||||
_ => SyncState::Idle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A peer query method for getting a list of peers
|
||||
enum PeerState {
|
||||
/// Peer is on different hash than us
|
||||
Lagging,
|
||||
/// Peer is on the same block as us
|
||||
SameBlock
|
||||
}
|
||||
|
||||
/// Blockchain sync handler.
|
||||
/// See module documentation for more details.
|
||||
pub struct ChainSync {
|
||||
@@ -417,10 +633,14 @@ pub struct ChainSync {
|
||||
|
||||
impl ChainSync {
|
||||
/// Create a new instance of syncing strategy.
|
||||
pub fn new(config: SyncConfig, chain: &BlockChainClient, private_tx_handler: Arc<PrivateTxHandler>) -> ChainSync {
|
||||
pub fn new(
|
||||
config: SyncConfig,
|
||||
chain: &BlockChainClient,
|
||||
private_tx_handler: Arc<PrivateTxHandler>,
|
||||
) -> Self {
|
||||
let chain_info = chain.chain_info();
|
||||
let best_block = chain.chain_info().best_block_number;
|
||||
let state = ChainSync::get_init_state(config.warp_sync, chain);
|
||||
let state = Self::get_init_state(config.warp_sync, chain);
|
||||
|
||||
let mut sync = ChainSync {
|
||||
state,
|
||||
@@ -445,15 +665,6 @@ impl ChainSync {
|
||||
sync
|
||||
}
|
||||
|
||||
fn get_init_state(warp_sync: WarpSync, chain: &BlockChainClient) -> SyncState {
|
||||
let best_block = chain.chain_info().best_block_number;
|
||||
match warp_sync {
|
||||
WarpSync::Enabled => SyncState::WaitingPeers,
|
||||
WarpSync::OnlyAndAfter(block) if block > best_block => SyncState::WaitingPeers,
|
||||
_ => SyncState::Idle,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns synchonization status
|
||||
pub fn status(&self) -> SyncStatus {
|
||||
let last_imported_number = self.new_blocks.last_imported_block_number();
|
||||
@@ -521,7 +732,7 @@ impl ChainSync {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.state = state.unwrap_or_else(|| ChainSync::get_init_state(self.warp_sync, io.chain()));
|
||||
self.state = state.unwrap_or_else(|| Self::get_init_state(self.warp_sync, io.chain()));
|
||||
// Reactivate peers only if some progress has been made
|
||||
// since the last sync round of if starting fresh.
|
||||
self.active_peers = self.peers.keys().cloned().collect();
|
||||
@@ -655,37 +866,35 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
/// Resume downloading
|
||||
fn continue_sync(&mut self, io: &mut SyncIo) {
|
||||
// Collect active peers that can sync
|
||||
let confirmed_peers: Vec<(PeerId, u8)> = self.peers.iter().filter_map(|(peer_id, peer)|
|
||||
if peer.can_sync() {
|
||||
Some((*peer_id, peer.protocol_version))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
).collect();
|
||||
|
||||
trace!(
|
||||
target: "sync",
|
||||
"Syncing with peers: {} active, {} confirmed, {} total",
|
||||
self.active_peers.len(), confirmed_peers.len(), self.peers.len()
|
||||
);
|
||||
|
||||
pub fn continue_sync(&mut self, io: &mut SyncIo) {
|
||||
if self.state == SyncState::Waiting {
|
||||
trace!(target: "sync", "Waiting for the block queue");
|
||||
} else if self.state == SyncState::SnapshotWaiting {
|
||||
trace!(target: "sync", "Waiting for the snapshot restoration");
|
||||
} else {
|
||||
let mut peers: Vec<(PeerId, u8)> = confirmed_peers.iter().filter(|&&(peer_id, _)|
|
||||
self.active_peers.contains(&peer_id)
|
||||
).map(|v| *v).collect();
|
||||
// Collect active peers that can sync
|
||||
let mut peers: Vec<(PeerId, u8)> = self.peers.iter().filter_map(|(peer_id, peer)|
|
||||
if peer.can_sync() && peer.asking == PeerAsking::Nothing && self.active_peers.contains(&peer_id) {
|
||||
Some((*peer_id, peer.protocol_version))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
).collect();
|
||||
|
||||
random::new().shuffle(&mut peers); //TODO: sort by rating
|
||||
// prefer peers with higher protocol version
|
||||
peers.sort_by(|&(_, ref v1), &(_, ref v2)| v1.cmp(v2));
|
||||
if peers.len() > 0 {
|
||||
trace!(
|
||||
target: "sync",
|
||||
"Syncing with peers: {} active, {} available, {} total",
|
||||
self.active_peers.len(), peers.len(), self.peers.len()
|
||||
);
|
||||
|
||||
for (peer_id, _) in peers {
|
||||
self.sync_peer(io, peer_id, false);
|
||||
random::new().shuffle(&mut peers); // TODO (#646): sort by rating
|
||||
// prefer peers with higher protocol version
|
||||
peers.sort_by(|&(_, ref v1), &(_, ref v2)| v1.cmp(v2));
|
||||
|
||||
for (peer_id, _) in peers {
|
||||
self.sync_peer(io, peer_id, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,6 +1179,12 @@ impl ChainSync {
|
||||
self.state = SyncState::Blocks;
|
||||
self.continue_sync(io);
|
||||
},
|
||||
SyncState::SnapshotData => match io.snapshot_service().status() {
|
||||
RestorationStatus::Inactive | RestorationStatus::Failed => {
|
||||
self.state = SyncState::SnapshotWaiting;
|
||||
},
|
||||
RestorationStatus::Initializing { .. } | RestorationStatus::Ongoing { .. } => (),
|
||||
},
|
||||
SyncState::SnapshotWaiting => {
|
||||
match io.snapshot_service().status() {
|
||||
RestorationStatus::Inactive => {
|
||||
@@ -998,67 +1213,24 @@ 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<Bytes> {
|
||||
match chain.tree_route(from, to) {
|
||||
Some(route) => {
|
||||
let uncles = chain.find_uncles(from).unwrap_or_else(Vec::new);
|
||||
match route.blocks.len() {
|
||||
0 => None,
|
||||
_ => {
|
||||
let mut blocks = route.blocks;
|
||||
blocks.extend(uncles);
|
||||
let mut rlp_stream = RlpStream::new_list(blocks.len());
|
||||
for block_hash in blocks {
|
||||
let mut hash_rlp = RlpStream::new_list(2);
|
||||
let number = chain.block_header(BlockId::Hash(block_hash.clone()))
|
||||
.expect("chain.tree_route and chain.find_uncles only return hahses of blocks that are in the blockchain. qed.").number();
|
||||
hash_rlp.append(&block_hash);
|
||||
hash_rlp.append(&number);
|
||||
rlp_stream.append_raw(hash_rlp.as_raw(), 1);
|
||||
}
|
||||
Some(rlp_stream.out())
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None
|
||||
}
|
||||
/// returns peer ids that have different block than our chain
|
||||
fn get_lagging_peers(&self, chain_info: &BlockChainInfo) -> Vec<PeerId> {
|
||||
self.get_peers(chain_info, PeerState::Lagging)
|
||||
}
|
||||
|
||||
/// creates rlp from block bytes and total difficulty
|
||||
fn create_block_rlp(bytes: &Bytes, total_difficulty: U256) -> Bytes {
|
||||
let mut rlp_stream = RlpStream::new_list(2);
|
||||
rlp_stream.append_raw(bytes, 1);
|
||||
rlp_stream.append(&total_difficulty);
|
||||
rlp_stream.out()
|
||||
}
|
||||
|
||||
/// creates latest block rlp for the given client
|
||||
fn create_latest_block_rlp(chain: &BlockChainClient) -> Bytes {
|
||||
ChainSync::create_block_rlp(
|
||||
&chain.block(BlockId::Hash(chain.chain_info().best_block_hash))
|
||||
.expect("Best block always exists").into_inner(),
|
||||
chain.chain_info().total_difficulty
|
||||
)
|
||||
}
|
||||
|
||||
/// creates given hash block rlp for the given client
|
||||
fn create_new_block_rlp(chain: &BlockChainClient, hash: &H256) -> Bytes {
|
||||
ChainSync::create_block_rlp(
|
||||
&chain.block(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed").into_inner(),
|
||||
chain.block_total_difficulty(BlockId::Hash(hash.clone())).expect("Block has just been sealed; qed.")
|
||||
)
|
||||
}
|
||||
|
||||
/// returns peer ids that have different blocks than our chain
|
||||
fn get_lagging_peers(&mut self, chain_info: &BlockChainInfo) -> Vec<PeerId> {
|
||||
/// returns peer ids that have different or the same blocks than our chain
|
||||
fn get_peers(&self, chain_info: &BlockChainInfo, peers: PeerState) -> Vec<PeerId> {
|
||||
let latest_hash = chain_info.best_block_hash;
|
||||
self
|
||||
.peers
|
||||
.iter_mut()
|
||||
.iter()
|
||||
.filter_map(|(&id, ref mut peer_info)| {
|
||||
trace!(target: "sync", "Checking peer our best {} their best {}", latest_hash, peer_info.latest_hash);
|
||||
if peer_info.latest_hash != latest_hash {
|
||||
let matches = match peers {
|
||||
PeerState::Lagging => peer_info.latest_hash != latest_hash,
|
||||
PeerState::SameBlock => peer_info.latest_hash == latest_hash,
|
||||
};
|
||||
if matches {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
@@ -1067,17 +1239,6 @@ impl ChainSync {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn select_random_peers(peers: &[PeerId]) -> Vec<PeerId> {
|
||||
// take sqrt(x) peers
|
||||
let mut peers = peers.to_vec();
|
||||
let mut count = (peers.len() as f64).powf(0.5).round() as usize;
|
||||
count = cmp::min(count, MAX_PEERS_PROPAGATION);
|
||||
count = cmp::max(count, MIN_PEERS_PROPAGATION);
|
||||
random::new().shuffle(&mut peers);
|
||||
peers.truncate(count);
|
||||
peers
|
||||
}
|
||||
|
||||
fn get_consensus_peers(&self) -> Vec<PeerId> {
|
||||
self.peers.iter().filter_map(|(id, p)| if p.protocol_version >= PAR_PROTOCOL_VERSION_2.0 { Some(*id) } else { None }).collect()
|
||||
}
|
||||
@@ -1126,21 +1287,10 @@ impl ChainSync {
|
||||
}
|
||||
}
|
||||
|
||||
/// Dispatch incoming requests and responses
|
||||
pub fn dispatch_packet(sync: &RwLock<ChainSync>, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) {
|
||||
SyncSupplier::dispatch_packet(sync, io, peer, packet_id, data)
|
||||
}
|
||||
|
||||
pub fn on_packet(&mut self, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) {
|
||||
debug!(target: "sync", "{} -> Dispatching packet: {}", peer, packet_id);
|
||||
SyncHandler::on_packet(self, io, peer, packet_id, data);
|
||||
}
|
||||
|
||||
/// Called when peer sends us new consensus packet
|
||||
pub fn on_consensus_packet(io: &mut SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), PacketDecodeError> {
|
||||
SyncHandler::on_consensus_packet(io, peer_id, r)
|
||||
}
|
||||
|
||||
/// Called by peer when it is disconnecting
|
||||
pub fn on_peer_aborting(&mut self, io: &mut SyncIo, peer: PeerId) {
|
||||
SyncHandler::on_peer_aborting(self, io, peer);
|
||||
@@ -1152,8 +1302,16 @@ impl ChainSync {
|
||||
}
|
||||
|
||||
/// propagates new transactions to all peers
|
||||
pub fn propagate_new_transactions(&mut self, io: &mut SyncIo) -> usize {
|
||||
SyncPropagator::propagate_new_transactions(self, io)
|
||||
pub fn propagate_new_transactions(&mut self, io: &mut SyncIo) {
|
||||
let deadline = Instant::now() + Duration::from_millis(500);
|
||||
SyncPropagator::propagate_new_transactions(self, io, || {
|
||||
if deadline > Instant::now() {
|
||||
true
|
||||
} else {
|
||||
debug!(target: "sync", "Wasn't able to finish transaction propagation within a deadline.");
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Broadcast consensus message to peers.
|
||||
@@ -1169,7 +1327,7 @@ impl ChainSync {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
use std::collections::{VecDeque};
|
||||
use ethkey;
|
||||
use network::PeerId;
|
||||
use tests::helpers::{TestIo};
|
||||
@@ -1285,8 +1443,8 @@ pub mod tests {
|
||||
asking_blocks: Vec::new(),
|
||||
asking_hash: None,
|
||||
ask_time: Instant::now(),
|
||||
last_sent_transactions: HashSet::new(),
|
||||
last_sent_private_transactions: HashSet::new(),
|
||||
last_sent_transactions: Default::default(),
|
||||
last_sent_private_transactions: Default::default(),
|
||||
expired: false,
|
||||
confirmation: super::ForkConfirmation::Confirmed,
|
||||
snapshot_number: None,
|
||||
@@ -1301,7 +1459,7 @@ pub mod tests {
|
||||
fn finds_lagging_peers() {
|
||||
let mut client = TestBlockChainClient::new();
|
||||
client.add_blocks(100, EachBlockWith::Uncle);
|
||||
let mut sync = dummy_sync_with_peer(client.block_hash_delta_minus(10), &client);
|
||||
let sync = dummy_sync_with_peer(client.block_hash_delta_minus(10), &client);
|
||||
let chain_info = client.chain_info();
|
||||
|
||||
let lagging_peers = sync.get_lagging_peers(&chain_info);
|
||||
@@ -1441,3 +1599,4 @@ pub mod tests {
|
||||
assert_eq!(status.status.transaction_count, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ use bytes::Bytes;
|
||||
use ethereum_types::H256;
|
||||
use ethcore::client::BlockChainInfo;
|
||||
use ethcore::header::BlockNumber;
|
||||
use fastmap::H256FastSet;
|
||||
use network::{PeerId, PacketId};
|
||||
use rand::Rng;
|
||||
use rlp::{Encodable, RlpStream};
|
||||
@@ -44,13 +45,13 @@ fn accepts_service_transaction(client_id: &str) -> bool {
|
||||
// Parity versions starting from this will accept service-transactions
|
||||
const SERVICE_TRANSACTIONS_VERSION: (u32, u32) = (1u32, 6u32);
|
||||
// Parity client string prefix
|
||||
const LEGACY_CLIENT_ID_PREFIX: &'static str = "Parity/v";
|
||||
const PARITY_CLIENT_ID_PREFIX: &'static str = "Parity-Ethereum/v";
|
||||
|
||||
let splitted = if client_id.starts_with(LEGACY_CLIENT_ID_PREFIX) {
|
||||
client_id[LEGACY_CLIENT_ID_PREFIX.len()..].split('.')
|
||||
} else if client_id.starts_with(PARITY_CLIENT_ID_PREFIX) {
|
||||
client_id[PARITY_CLIENT_ID_PREFIX.len()..].split('.')
|
||||
const LEGACY_CLIENT_ID_PREFIX: &'static str = "Parity/";
|
||||
const PARITY_CLIENT_ID_PREFIX: &'static str = "Parity-Ethereum/";
|
||||
const VERSION_PREFIX: &'static str = "/v";
|
||||
|
||||
let idx = client_id.rfind(VERSION_PREFIX).map(|idx| idx + VERSION_PREFIX.len()).unwrap_or(client_id.len());
|
||||
let splitted = if client_id.starts_with(LEGACY_CLIENT_ID_PREFIX) || client_id.starts_with(PARITY_CLIENT_ID_PREFIX) {
|
||||
client_id[idx..].split('.')
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
@@ -69,49 +70,51 @@ impl SyncPropagator {
|
||||
/// propagates latest block to a set of peers
|
||||
pub fn propagate_blocks(sync: &mut ChainSync, chain_info: &BlockChainInfo, io: &mut SyncIo, blocks: &[H256], peers: &[PeerId]) -> usize {
|
||||
trace!(target: "sync", "Sending NewBlocks to {:?}", peers);
|
||||
let mut sent = 0;
|
||||
for peer_id in peers {
|
||||
if blocks.is_empty() {
|
||||
let rlp = ChainSync::create_latest_block_rlp(io.chain());
|
||||
SyncPropagator::send_packet(io, *peer_id, NEW_BLOCK_PACKET, rlp);
|
||||
} else {
|
||||
for h in blocks {
|
||||
let rlp = ChainSync::create_new_block_rlp(io.chain(), h);
|
||||
SyncPropagator::send_packet(io, *peer_id, NEW_BLOCK_PACKET, rlp);
|
||||
let sent = peers.len();
|
||||
let mut send_packet = |io: &mut SyncIo, rlp: Bytes| {
|
||||
for peer_id in peers {
|
||||
SyncPropagator::send_packet(io, *peer_id, NEW_BLOCK_PACKET, rlp.clone());
|
||||
if let Some(ref mut peer) = sync.peers.get_mut(peer_id) {
|
||||
peer.latest_hash = chain_info.best_block_hash.clone();
|
||||
}
|
||||
}
|
||||
if let Some(ref mut peer) = sync.peers.get_mut(peer_id) {
|
||||
peer.latest_hash = chain_info.best_block_hash.clone();
|
||||
};
|
||||
|
||||
if blocks.is_empty() {
|
||||
let rlp = ChainSync::create_latest_block_rlp(io.chain());
|
||||
send_packet(io, rlp);
|
||||
} else {
|
||||
for h in blocks {
|
||||
let rlp = ChainSync::create_new_block_rlp(io.chain(), h);
|
||||
send_packet(io, rlp);
|
||||
}
|
||||
sent += 1;
|
||||
}
|
||||
|
||||
sent
|
||||
}
|
||||
|
||||
/// propagates new known hashes to all peers
|
||||
pub fn propagate_new_hashes(sync: &mut ChainSync, chain_info: &BlockChainInfo, io: &mut SyncIo, peers: &[PeerId]) -> usize {
|
||||
trace!(target: "sync", "Sending NewHashes to {:?}", peers);
|
||||
let mut sent = 0;
|
||||
let last_parent = *io.chain().best_block_header().parent_hash();
|
||||
let best_block_hash = chain_info.best_block_hash;
|
||||
let rlp = match ChainSync::create_new_hashes_rlp(io.chain(), &last_parent, &best_block_hash) {
|
||||
Some(rlp) => rlp,
|
||||
None => return 0
|
||||
};
|
||||
|
||||
let sent = peers.len();
|
||||
for peer_id in peers {
|
||||
sent += match ChainSync::create_new_hashes_rlp(io.chain(), &last_parent, &chain_info.best_block_hash) {
|
||||
Some(rlp) => {
|
||||
{
|
||||
if let Some(ref mut peer) = sync.peers.get_mut(peer_id) {
|
||||
peer.latest_hash = chain_info.best_block_hash.clone();
|
||||
}
|
||||
}
|
||||
SyncPropagator::send_packet(io, *peer_id, NEW_BLOCK_HASHES_PACKET, rlp);
|
||||
1
|
||||
},
|
||||
None => 0
|
||||
if let Some(ref mut peer) = sync.peers.get_mut(peer_id) {
|
||||
peer.latest_hash = best_block_hash;
|
||||
}
|
||||
SyncPropagator::send_packet(io, *peer_id, NEW_BLOCK_HASHES_PACKET, rlp.clone());
|
||||
}
|
||||
sent
|
||||
}
|
||||
|
||||
/// propagates new transactions to all peers
|
||||
pub fn propagate_new_transactions(sync: &mut ChainSync, io: &mut SyncIo) -> usize {
|
||||
pub fn propagate_new_transactions<F: FnMut() -> bool>(sync: &mut ChainSync, io: &mut SyncIo, mut should_continue: F) -> usize {
|
||||
// Early out if nobody to send to.
|
||||
if sync.peers.is_empty() {
|
||||
return 0;
|
||||
@@ -122,6 +125,10 @@ impl SyncPropagator {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if !should_continue() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions.iter()
|
||||
.map(|tx| tx.signed())
|
||||
.partition(|tx| !tx.gas_price.is_zero());
|
||||
@@ -130,24 +137,34 @@ impl SyncPropagator {
|
||||
let mut affected_peers = HashSet::new();
|
||||
if !transactions.is_empty() {
|
||||
let peers = SyncPropagator::select_peers_for_transactions(sync, |_| true);
|
||||
affected_peers = SyncPropagator::propagate_transactions_to_peers(sync, io, peers, transactions);
|
||||
affected_peers = SyncPropagator::propagate_transactions_to_peers(
|
||||
sync, io, peers, transactions, &mut should_continue,
|
||||
);
|
||||
}
|
||||
|
||||
// most of times service_transactions will be empty
|
||||
// => there's no need to merge packets
|
||||
if !service_transactions.is_empty() {
|
||||
let service_transactions_peers = SyncPropagator::select_peers_for_transactions(sync, |peer_id| accepts_service_transaction(&io.peer_info(*peer_id)));
|
||||
let service_transactions_affected_peers = SyncPropagator::propagate_transactions_to_peers(sync, io, service_transactions_peers, service_transactions);
|
||||
let service_transactions_affected_peers = SyncPropagator::propagate_transactions_to_peers(
|
||||
sync, io, service_transactions_peers, service_transactions, &mut should_continue
|
||||
);
|
||||
affected_peers.extend(&service_transactions_affected_peers);
|
||||
}
|
||||
|
||||
affected_peers.len()
|
||||
}
|
||||
|
||||
fn propagate_transactions_to_peers(sync: &mut ChainSync, io: &mut SyncIo, peers: Vec<PeerId>, transactions: Vec<&SignedTransaction>) -> HashSet<PeerId> {
|
||||
fn propagate_transactions_to_peers<F: FnMut() -> bool>(
|
||||
sync: &mut ChainSync,
|
||||
io: &mut SyncIo,
|
||||
peers: Vec<PeerId>,
|
||||
transactions: Vec<&SignedTransaction>,
|
||||
mut should_continue: F,
|
||||
) -> HashSet<PeerId> {
|
||||
let all_transactions_hashes = transactions.iter()
|
||||
.map(|tx| tx.hash())
|
||||
.collect::<HashSet<H256>>();
|
||||
.collect::<H256FastSet>();
|
||||
let all_transactions_rlp = {
|
||||
let mut packet = RlpStream::new_list(transactions.len());
|
||||
for tx in &transactions { packet.append(&**tx); }
|
||||
@@ -157,102 +174,104 @@ impl SyncPropagator {
|
||||
// Clear old transactions from stats
|
||||
sync.transactions_stats.retain(&all_transactions_hashes);
|
||||
|
||||
// sqrt(x)/x scaled to max u32
|
||||
let block_number = io.chain().chain_info().best_block_number;
|
||||
|
||||
let lucky_peers = {
|
||||
peers.into_iter()
|
||||
.filter_map(|peer_id| {
|
||||
let stats = &mut sync.transactions_stats;
|
||||
let peer_info = sync.peers.get_mut(&peer_id)
|
||||
.expect("peer_id is form peers; peers is result of select_peers_for_transactions; select_peers_for_transactions selects peers from self.peers; qed");
|
||||
|
||||
// Send all transactions
|
||||
if peer_info.last_sent_transactions.is_empty() {
|
||||
// update stats
|
||||
for hash in &all_transactions_hashes {
|
||||
let id = io.peer_session_info(peer_id).and_then(|info| info.id);
|
||||
stats.propagated(hash, id, block_number);
|
||||
}
|
||||
peer_info.last_sent_transactions = all_transactions_hashes.clone();
|
||||
return Some((peer_id, all_transactions_hashes.len(), all_transactions_rlp.clone()));
|
||||
}
|
||||
|
||||
// Get hashes of all transactions to send to this peer
|
||||
let to_send = all_transactions_hashes.difference(&peer_info.last_sent_transactions)
|
||||
.cloned()
|
||||
.collect::<HashSet<_>>();
|
||||
if to_send.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Construct RLP
|
||||
let (packet, to_send) = {
|
||||
let mut to_send = to_send;
|
||||
let mut packet = RlpStream::new();
|
||||
packet.begin_unbounded_list();
|
||||
let mut pushed = 0;
|
||||
for tx in &transactions {
|
||||
let hash = tx.hash();
|
||||
if to_send.contains(&hash) {
|
||||
let mut transaction = RlpStream::new();
|
||||
tx.rlp_append(&mut transaction);
|
||||
let appended = packet.append_raw_checked(&transaction.drain(), 1, MAX_TRANSACTION_PACKET_SIZE);
|
||||
if !appended {
|
||||
// Maximal packet size reached just proceed with sending
|
||||
debug!(target: "sync", "Transaction packet size limit reached. Sending incomplete set of {}/{} transactions.", pushed, to_send.len());
|
||||
to_send = to_send.into_iter().take(pushed).collect();
|
||||
break;
|
||||
}
|
||||
pushed += 1;
|
||||
}
|
||||
}
|
||||
packet.complete_unbounded_list();
|
||||
(packet, to_send)
|
||||
};
|
||||
|
||||
// Update stats
|
||||
let id = io.peer_session_info(peer_id).and_then(|info| info.id);
|
||||
for hash in &to_send {
|
||||
// update stats
|
||||
stats.propagated(hash, id, block_number);
|
||||
}
|
||||
|
||||
peer_info.last_sent_transactions = all_transactions_hashes
|
||||
.intersection(&peer_info.last_sent_transactions)
|
||||
.chain(&to_send)
|
||||
.cloned()
|
||||
.collect();
|
||||
Some((peer_id, to_send.len(), packet.out()))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
let send_packet = |io: &mut SyncIo, peer_id: PeerId, sent: usize, rlp: Bytes| {
|
||||
let size = rlp.len();
|
||||
SyncPropagator::send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp);
|
||||
trace!(target: "sync", "{:02} <- Transactions ({} entries; {} bytes)", peer_id, sent, size);
|
||||
};
|
||||
|
||||
// Send RLPs
|
||||
let mut peers = HashSet::new();
|
||||
if lucky_peers.len() > 0 {
|
||||
let mut max_sent = 0;
|
||||
let lucky_peers_len = lucky_peers.len();
|
||||
for (peer_id, sent, rlp) in lucky_peers {
|
||||
peers.insert(peer_id);
|
||||
let size = rlp.len();
|
||||
SyncPropagator::send_packet(io, peer_id, TRANSACTIONS_PACKET, rlp);
|
||||
trace!(target: "sync", "{:02} <- Transactions ({} entries; {} bytes)", peer_id, sent, size);
|
||||
max_sent = cmp::max(max_sent, sent);
|
||||
let block_number = io.chain().chain_info().best_block_number;
|
||||
let mut sent_to_peers = HashSet::new();
|
||||
let mut max_sent = 0;
|
||||
|
||||
// for every peer construct and send transactions packet
|
||||
for peer_id in peers {
|
||||
if !should_continue() {
|
||||
debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, sent_to_peers.len());
|
||||
return sent_to_peers;
|
||||
}
|
||||
debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, lucky_peers_len);
|
||||
|
||||
let stats = &mut sync.transactions_stats;
|
||||
let peer_info = sync.peers.get_mut(&peer_id)
|
||||
.expect("peer_id is form peers; peers is result of select_peers_for_transactions; select_peers_for_transactions selects peers from self.peers; qed");
|
||||
|
||||
// Send all transactions, if the peer doesn't know about anything
|
||||
if peer_info.last_sent_transactions.is_empty() {
|
||||
// update stats
|
||||
for hash in &all_transactions_hashes {
|
||||
let id = io.peer_session_info(peer_id).and_then(|info| info.id);
|
||||
stats.propagated(hash, id, block_number);
|
||||
}
|
||||
peer_info.last_sent_transactions = all_transactions_hashes.clone();
|
||||
|
||||
send_packet(io, peer_id, all_transactions_hashes.len(), all_transactions_rlp.clone());
|
||||
sent_to_peers.insert(peer_id);
|
||||
max_sent = cmp::max(max_sent, all_transactions_hashes.len());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get hashes of all transactions to send to this peer
|
||||
let to_send = all_transactions_hashes.difference(&peer_info.last_sent_transactions)
|
||||
.cloned()
|
||||
.collect::<HashSet<_>>();
|
||||
if to_send.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Construct RLP
|
||||
let (packet, to_send) = {
|
||||
let mut to_send = to_send;
|
||||
let mut packet = RlpStream::new();
|
||||
packet.begin_unbounded_list();
|
||||
let mut pushed = 0;
|
||||
for tx in &transactions {
|
||||
let hash = tx.hash();
|
||||
if to_send.contains(&hash) {
|
||||
let mut transaction = RlpStream::new();
|
||||
tx.rlp_append(&mut transaction);
|
||||
let appended = packet.append_raw_checked(&transaction.drain(), 1, MAX_TRANSACTION_PACKET_SIZE);
|
||||
if !appended {
|
||||
// Maximal packet size reached just proceed with sending
|
||||
debug!(target: "sync", "Transaction packet size limit reached. Sending incomplete set of {}/{} transactions.", pushed, to_send.len());
|
||||
to_send = to_send.into_iter().take(pushed).collect();
|
||||
break;
|
||||
}
|
||||
pushed += 1;
|
||||
}
|
||||
}
|
||||
packet.complete_unbounded_list();
|
||||
(packet, to_send)
|
||||
};
|
||||
|
||||
// Update stats
|
||||
let id = io.peer_session_info(peer_id).and_then(|info| info.id);
|
||||
for hash in &to_send {
|
||||
// update stats
|
||||
stats.propagated(hash, id, block_number);
|
||||
}
|
||||
|
||||
peer_info.last_sent_transactions = all_transactions_hashes
|
||||
.intersection(&peer_info.last_sent_transactions)
|
||||
.chain(&to_send)
|
||||
.cloned()
|
||||
.collect();
|
||||
send_packet(io, peer_id, to_send.len(), packet.out());
|
||||
sent_to_peers.insert(peer_id);
|
||||
max_sent = cmp::max(max_sent, to_send.len());
|
||||
|
||||
}
|
||||
|
||||
peers
|
||||
debug!(target: "sync", "Sent up to {} transactions to {} peers.", max_sent, sent_to_peers.len());
|
||||
sent_to_peers
|
||||
}
|
||||
|
||||
pub fn propagate_latest_blocks(sync: &mut ChainSync, io: &mut SyncIo, sealed: &[H256]) {
|
||||
let chain_info = io.chain().chain_info();
|
||||
if (((chain_info.best_block_number as i64) - (sync.last_sent_block_number as i64)).abs() as BlockNumber) < MAX_PEER_LAG_PROPAGATION {
|
||||
let mut peers = sync.get_lagging_peers(&chain_info);
|
||||
let peers = sync.get_lagging_peers(&chain_info);
|
||||
if sealed.is_empty() {
|
||||
let hashes = SyncPropagator::propagate_new_hashes(sync, &chain_info, io, &peers);
|
||||
peers = ChainSync::select_random_peers(&peers);
|
||||
let peers = ChainSync::select_random_peers(&peers);
|
||||
let blocks = SyncPropagator::propagate_blocks(sync, &chain_info, io, sealed, &peers);
|
||||
if blocks != 0 || hashes != 0 {
|
||||
trace!(target: "sync", "Sent latest {} blocks and {} hashes to peers.", blocks, hashes);
|
||||
@@ -318,7 +337,7 @@ impl SyncPropagator {
|
||||
}
|
||||
|
||||
/// Generic packet sender
|
||||
fn send_packet(sync: &mut SyncIo, peer_id: PeerId, packet_id: PacketId, packet: Bytes) {
|
||||
pub fn send_packet(sync: &mut SyncIo, peer_id: PeerId, packet_id: PacketId, packet: Bytes) {
|
||||
if let Err(e) = sync.send(peer_id, packet_id, packet) {
|
||||
debug!(target:"sync", "Error sending packet: {:?}", e);
|
||||
sync.disconnect_peer(peer_id);
|
||||
@@ -419,8 +438,8 @@ mod tests {
|
||||
asking_blocks: Vec::new(),
|
||||
asking_hash: None,
|
||||
ask_time: Instant::now(),
|
||||
last_sent_transactions: HashSet::new(),
|
||||
last_sent_private_transactions: HashSet::new(),
|
||||
last_sent_transactions: Default::default(),
|
||||
last_sent_private_transactions: Default::default(),
|
||||
expired: false,
|
||||
confirmation: ForkConfirmation::Confirmed,
|
||||
snapshot_number: None,
|
||||
@@ -447,13 +466,13 @@ mod tests {
|
||||
let queue = RwLock::new(VecDeque::new());
|
||||
let ss = TestSnapshotService::new();
|
||||
let mut io = TestIo::new(&mut client, &ss, &queue, None);
|
||||
let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
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);
|
||||
let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true);
|
||||
// Even after new block transactions should not be propagated twice
|
||||
sync.chain_new_blocks(&mut io, &[], &[], &[], &[], &[], &[]);
|
||||
// Try to propagate same transactions for the third time
|
||||
let peer_count3 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
let peer_count3 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true);
|
||||
|
||||
// 1 message should be send
|
||||
assert_eq!(1, io.packets.len());
|
||||
@@ -474,7 +493,7 @@ mod tests {
|
||||
let queue = RwLock::new(VecDeque::new());
|
||||
let ss = TestSnapshotService::new();
|
||||
let mut io = TestIo::new(&mut client, &ss, &queue, None);
|
||||
let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
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.
|
||||
// (we only propagate on timeout)
|
||||
@@ -498,10 +517,10 @@ mod tests {
|
||||
let queue = RwLock::new(VecDeque::new());
|
||||
let ss = TestSnapshotService::new();
|
||||
let mut io = TestIo::new(&mut client, &ss, &queue, None);
|
||||
let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
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
|
||||
let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true);
|
||||
|
||||
assert_eq!(0, io.packets.len());
|
||||
assert_eq!(0, peer_count);
|
||||
@@ -519,7 +538,7 @@ mod tests {
|
||||
// should sent some
|
||||
{
|
||||
let mut io = TestIo::new(&mut client, &ss, &queue, None);
|
||||
let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
let peer_count = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true);
|
||||
assert_eq!(1, io.packets.len());
|
||||
assert_eq!(1, peer_count);
|
||||
}
|
||||
@@ -528,9 +547,9 @@ mod tests {
|
||||
let (peer_count2, peer_count3) = {
|
||||
let mut io = TestIo::new(&mut client, &ss, &queue, None);
|
||||
// Propagate new transactions
|
||||
let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
let peer_count2 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true);
|
||||
// And now the peer should have all transactions
|
||||
let peer_count3 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
let peer_count3 = SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true);
|
||||
(peer_count2, peer_count3)
|
||||
};
|
||||
|
||||
@@ -553,7 +572,7 @@ mod tests {
|
||||
let queue = RwLock::new(VecDeque::new());
|
||||
let ss = TestSnapshotService::new();
|
||||
let mut io = TestIo::new(&mut client, &ss, &queue, None);
|
||||
SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true);
|
||||
|
||||
let stats = sync.transactions_stats();
|
||||
assert_eq!(stats.len(), 1, "Should maintain stats for single transaction.")
|
||||
@@ -580,10 +599,10 @@ mod tests {
|
||||
io.peers_info.insert(3, "Parity/v1.5".to_owned());
|
||||
// and peer#4 is Parity, accepting service transactions
|
||||
insert_dummy_peer(&mut sync, 4, block_hash);
|
||||
io.peers_info.insert(4, "Parity-Ethereum/v2.7.3-ABCDEFGH".to_owned());
|
||||
io.peers_info.insert(4, "Parity-Ethereum/ABCDEFGH/v2.7.3".to_owned());
|
||||
|
||||
// and new service transaction is propagated to peers
|
||||
SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true);
|
||||
|
||||
// peer#2 && peer#4 are receiving service transaction
|
||||
assert!(io.packets.iter().any(|p| p.packet_id == 0x02 && p.recipient == 2)); // TRANSACTIONS_PACKET
|
||||
@@ -607,7 +626,7 @@ mod tests {
|
||||
io.peers_info.insert(1, "Parity-Ethereum/v2.6".to_owned());
|
||||
|
||||
// and service + non-service transactions are propagated to peers
|
||||
SyncPropagator::propagate_new_transactions(&mut sync, &mut io);
|
||||
SyncPropagator::propagate_new_transactions(&mut sync, &mut io, || true);
|
||||
|
||||
// two separate packets for peer are queued:
|
||||
// 1) with non-service-transaction
|
||||
|
||||
@@ -27,6 +27,7 @@ use sync_io::SyncIo;
|
||||
|
||||
use super::{
|
||||
ChainSync,
|
||||
SyncHandler,
|
||||
RlpResponseResult,
|
||||
PacketDecodeError,
|
||||
BLOCK_BODIES_PACKET,
|
||||
@@ -47,6 +48,8 @@ use super::{
|
||||
RECEIPTS_PACKET,
|
||||
SNAPSHOT_DATA_PACKET,
|
||||
SNAPSHOT_MANIFEST_PACKET,
|
||||
STATUS_PACKET,
|
||||
TRANSACTIONS_PACKET,
|
||||
};
|
||||
|
||||
/// The Chain Sync Supplier: answers requests from peers with available data
|
||||
@@ -56,6 +59,7 @@ impl SyncSupplier {
|
||||
/// Dispatch incoming requests and responses
|
||||
pub fn dispatch_packet(sync: &RwLock<ChainSync>, io: &mut SyncIo, peer: PeerId, packet_id: u8, data: &[u8]) {
|
||||
let rlp = Rlp::new(data);
|
||||
|
||||
let result = match packet_id {
|
||||
GET_BLOCK_BODIES_PACKET => SyncSupplier::return_rlp(io, &rlp, peer,
|
||||
SyncSupplier::return_block_bodies,
|
||||
@@ -80,9 +84,39 @@ impl SyncSupplier {
|
||||
GET_SNAPSHOT_DATA_PACKET => SyncSupplier::return_rlp(io, &rlp, peer,
|
||||
SyncSupplier::return_snapshot_data,
|
||||
|e| format!("Error sending snapshot data: {:?}", e)),
|
||||
CONSENSUS_DATA_PACKET => ChainSync::on_consensus_packet(io, peer, &rlp),
|
||||
_ => {
|
||||
|
||||
STATUS_PACKET => {
|
||||
sync.write().on_packet(io, peer, packet_id, data);
|
||||
Ok(())
|
||||
},
|
||||
// Packets that require the peer to be confirmed
|
||||
_ => {
|
||||
if !sync.read().peers.contains_key(&peer) {
|
||||
debug!(target:"sync", "Unexpected packet {} from unregistered peer: {}:{}", packet_id, peer, io.peer_info(peer));
|
||||
return;
|
||||
}
|
||||
debug!(target: "sync", "{} -> Dispatching packet: {}", peer, packet_id);
|
||||
|
||||
match packet_id {
|
||||
CONSENSUS_DATA_PACKET => {
|
||||
SyncHandler::on_consensus_packet(io, peer, &rlp)
|
||||
},
|
||||
TRANSACTIONS_PACKET => {
|
||||
let res = {
|
||||
let sync_ro = sync.read();
|
||||
SyncHandler::on_peer_transactions(&*sync_ro, io, peer, &rlp)
|
||||
};
|
||||
if res.is_err() {
|
||||
// peer sent invalid data, disconnect.
|
||||
io.disable_peer(peer);
|
||||
sync.write().deactivate_peer(io, peer);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
sync.write().on_packet(io, peer, packet_id, data);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
@@ -226,7 +260,8 @@ impl SyncSupplier {
|
||||
let mut added_receipts = 0usize;
|
||||
let mut data = Bytes::new();
|
||||
for i in 0..count {
|
||||
if let Some(mut receipts_bytes) = io.chain().encoded_block_receipts(&rlp.val_at::<H256>(i)?) {
|
||||
if let Some(receipts) = io.chain().block_receipts(&rlp.val_at::<H256>(i)?) {
|
||||
let mut receipts_bytes = ::rlp::encode(&receipts);
|
||||
data.append(&mut receipts_bytes);
|
||||
added_receipts += receipts_bytes.len();
|
||||
added_headers += 1;
|
||||
@@ -403,7 +438,7 @@ mod test {
|
||||
|
||||
io.sender = Some(2usize);
|
||||
|
||||
ChainSync::dispatch_packet(&RwLock::new(sync), &mut io, 0usize, GET_NODE_DATA_PACKET, &node_request);
|
||||
SyncSupplier::dispatch_packet(&RwLock::new(sync), &mut io, 0usize, GET_NODE_DATA_PACKET, &node_request);
|
||||
assert_eq!(1, io.packets.len());
|
||||
}
|
||||
|
||||
@@ -445,7 +480,7 @@ mod test {
|
||||
assert_eq!(603, rlp_result.unwrap().1.out().len());
|
||||
|
||||
io.sender = Some(2usize);
|
||||
ChainSync::dispatch_packet(&RwLock::new(sync), &mut io, 0usize, GET_RECEIPTS_PACKET, &receipts_request);
|
||||
SyncSupplier::dispatch_packet(&RwLock::new(sync), &mut io, 0usize, GET_RECEIPTS_PACKET, &receipts_request);
|
||||
assert_eq!(1, io.packets.len());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Instant, Duration};
|
||||
|
||||
@@ -213,6 +214,44 @@ enum SyncState {
|
||||
Rounds(SyncRound),
|
||||
}
|
||||
|
||||
/// A wrapper around the SyncState that makes sure to
|
||||
/// update the giving reference to `is_idle`
|
||||
#[derive(Debug)]
|
||||
struct SyncStateWrapper {
|
||||
state: SyncState,
|
||||
}
|
||||
|
||||
impl SyncStateWrapper {
|
||||
/// Create a new wrapper for SyncState::Idle
|
||||
pub fn idle() -> Self {
|
||||
SyncStateWrapper {
|
||||
state: SyncState::Idle,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the new state's value, making sure `is_idle` gets updated
|
||||
pub fn set(&mut self, state: SyncState, is_idle_handle: &mut bool) {
|
||||
*is_idle_handle = match state {
|
||||
SyncState::Idle => true,
|
||||
_ => false,
|
||||
};
|
||||
self.state = state;
|
||||
}
|
||||
|
||||
/// Returns the internal state's value
|
||||
pub fn into_inner(self) -> SyncState {
|
||||
self.state
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SyncStateWrapper {
|
||||
type Target = SyncState;
|
||||
|
||||
fn deref(&self) -> &SyncState {
|
||||
&self.state
|
||||
}
|
||||
}
|
||||
|
||||
struct ResponseCtx<'a> {
|
||||
peer: PeerId,
|
||||
req_id: ReqId,
|
||||
@@ -235,7 +274,9 @@ pub struct LightSync<L: AsLightClient> {
|
||||
pending_reqs: Mutex<HashMap<ReqId, PendingReq>>, // requests from this handler
|
||||
client: Arc<L>,
|
||||
rng: Mutex<OsRng>,
|
||||
state: Mutex<SyncState>,
|
||||
state: Mutex<SyncStateWrapper>,
|
||||
// We duplicate this state tracking to avoid deadlocks in `is_major_importing`.
|
||||
is_idle: Mutex<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -309,16 +350,17 @@ impl<L: AsLightClient + Send + Sync> Handler for LightSync<L> {
|
||||
|
||||
if new_best.is_none() {
|
||||
debug!(target: "sync", "No peers remain. Reverting to idle");
|
||||
*self.state.lock() = SyncState::Idle;
|
||||
self.set_state(&mut self.state.lock(), SyncState::Idle);
|
||||
} else {
|
||||
let mut state = self.state.lock();
|
||||
|
||||
*state = match mem::replace(&mut *state, SyncState::Idle) {
|
||||
let next_state = match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() {
|
||||
SyncState::Idle => SyncState::Idle,
|
||||
SyncState::AncestorSearch(search) =>
|
||||
SyncState::AncestorSearch(search.requests_abandoned(unfulfilled)),
|
||||
SyncState::Rounds(round) => SyncState::Rounds(round.requests_abandoned(unfulfilled)),
|
||||
};
|
||||
self.set_state(&mut state, next_state);
|
||||
}
|
||||
|
||||
self.maintain_sync(ctx.as_basic());
|
||||
@@ -390,12 +432,13 @@ impl<L: AsLightClient + Send + Sync> Handler for LightSync<L> {
|
||||
data: headers,
|
||||
};
|
||||
|
||||
*state = match mem::replace(&mut *state, SyncState::Idle) {
|
||||
let next_state = match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() {
|
||||
SyncState::Idle => SyncState::Idle,
|
||||
SyncState::AncestorSearch(search) =>
|
||||
SyncState::AncestorSearch(search.process_response(&ctx, &*self.client)),
|
||||
SyncState::Rounds(round) => SyncState::Rounds(round.process_response(&ctx)),
|
||||
};
|
||||
self.set_state(&mut state, next_state);
|
||||
}
|
||||
|
||||
self.maintain_sync(ctx.as_basic());
|
||||
@@ -408,12 +451,18 @@ impl<L: AsLightClient + Send + Sync> Handler for LightSync<L> {
|
||||
|
||||
// private helpers
|
||||
impl<L: AsLightClient> LightSync<L> {
|
||||
/// Sets the LightSync's state, and update
|
||||
/// `is_idle`
|
||||
fn set_state(&self, state: &mut SyncStateWrapper, next_state: SyncState) {
|
||||
state.set(next_state, &mut self.is_idle.lock());
|
||||
}
|
||||
|
||||
// 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 SyncState) {
|
||||
fn begin_search(&self, state: &mut SyncStateWrapper) {
|
||||
if let None = *self.best_seen.lock() {
|
||||
// no peers.
|
||||
*state = SyncState::Idle;
|
||||
self.set_state(state, SyncState::Idle);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -422,7 +471,8 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
|
||||
trace!(target: "sync", "Beginning search for common ancestor from {:?}",
|
||||
(chain_info.best_block_number, chain_info.best_block_hash));
|
||||
*state = SyncState::AncestorSearch(AncestorSearch::begin(chain_info.best_block_number));
|
||||
let next_state = SyncState::AncestorSearch(AncestorSearch::begin(chain_info.best_block_number));
|
||||
self.set_state(state, next_state);
|
||||
}
|
||||
|
||||
// handles request dispatch, block import, state machine transitions, and timeouts.
|
||||
@@ -435,7 +485,7 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
let chain_info = client.chain_info();
|
||||
|
||||
let mut state = self.state.lock();
|
||||
debug!(target: "sync", "Maintaining sync ({:?})", &*state);
|
||||
debug!(target: "sync", "Maintaining sync ({:?})", **state);
|
||||
|
||||
// drain any pending blocks into the queue.
|
||||
{
|
||||
@@ -445,11 +495,12 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
loop {
|
||||
if client.queue_info().is_full() { break }
|
||||
|
||||
*state = match mem::replace(&mut *state, SyncState::Idle) {
|
||||
let next_state = match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() {
|
||||
SyncState::Rounds(round)
|
||||
=> SyncState::Rounds(round.drain(&mut sink, Some(DRAIN_AMOUNT))),
|
||||
other => other,
|
||||
};
|
||||
self.set_state(&mut state, next_state);
|
||||
|
||||
if sink.is_empty() { break }
|
||||
trace!(target: "sync", "Drained {} headers to import", sink.len());
|
||||
@@ -483,15 +534,15 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
let network_score = other.as_ref().map(|target| target.head_td);
|
||||
trace!(target: "sync", "No target to sync to. Network score: {:?}, Local score: {:?}",
|
||||
network_score, best_td);
|
||||
*state = SyncState::Idle;
|
||||
self.set_state(&mut state, SyncState::Idle);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match mem::replace(&mut *state, SyncState::Idle) {
|
||||
match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() {
|
||||
SyncState::Rounds(SyncRound::Abort(reason, remaining)) => {
|
||||
if remaining.len() > 0 {
|
||||
*state = SyncState::Rounds(SyncRound::Abort(reason, remaining));
|
||||
self.set_state(&mut state, SyncState::Rounds(SyncRound::Abort(reason, remaining)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -505,7 +556,7 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
AbortReason::NoResponses => {}
|
||||
AbortReason::TargetReached => {
|
||||
debug!(target: "sync", "Sync target reached. Going idle");
|
||||
*state = SyncState::Idle;
|
||||
self.set_state(&mut state, SyncState::Idle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -514,15 +565,15 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
self.begin_search(&mut state);
|
||||
}
|
||||
SyncState::AncestorSearch(AncestorSearch::FoundCommon(num, hash)) => {
|
||||
*state = SyncState::Rounds(SyncRound::begin((num, hash), sync_target));
|
||||
self.set_state(&mut state, SyncState::Rounds(SyncRound::begin((num, hash), sync_target)));
|
||||
}
|
||||
SyncState::AncestorSearch(AncestorSearch::Genesis) => {
|
||||
// Same here.
|
||||
let g_hash = chain_info.genesis_hash;
|
||||
*state = SyncState::Rounds(SyncRound::begin((0, g_hash), sync_target));
|
||||
self.set_state(&mut state, SyncState::Rounds(SyncRound::begin((0, g_hash), sync_target)));
|
||||
}
|
||||
SyncState::Idle => self.begin_search(&mut state),
|
||||
other => *state = other, // restore displaced state.
|
||||
other => self.set_state(&mut state, other), // restore displaced state.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,12 +594,13 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
}
|
||||
drop(pending_reqs);
|
||||
|
||||
*state = match mem::replace(&mut *state, SyncState::Idle) {
|
||||
let next_state = match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() {
|
||||
SyncState::Idle => SyncState::Idle,
|
||||
SyncState::AncestorSearch(search) =>
|
||||
SyncState::AncestorSearch(search.requests_abandoned(&unfulfilled)),
|
||||
SyncState::Rounds(round) => SyncState::Rounds(round.requests_abandoned(&unfulfilled)),
|
||||
};
|
||||
self.set_state(&mut state, next_state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -605,34 +657,14 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
None
|
||||
};
|
||||
|
||||
*state = match mem::replace(&mut *state, SyncState::Idle) {
|
||||
let next_state = match mem::replace(&mut *state, SyncStateWrapper::idle()).into_inner() {
|
||||
SyncState::Rounds(round) =>
|
||||
SyncState::Rounds(round.dispatch_requests(dispatcher)),
|
||||
SyncState::AncestorSearch(search) =>
|
||||
SyncState::AncestorSearch(search.dispatch_request(dispatcher)),
|
||||
other => other,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn is_major_importing_do_wait(&self, wait: bool) -> bool {
|
||||
const EMPTY_QUEUE: usize = 3;
|
||||
|
||||
if self.client.as_light_client().queue_info().unverified_queue_size > EMPTY_QUEUE {
|
||||
return true;
|
||||
}
|
||||
let mg_state = if wait {
|
||||
self.state.lock()
|
||||
} else {
|
||||
if let Some(mg_state) = self.state.try_lock() {
|
||||
mg_state
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
match *mg_state {
|
||||
SyncState::Idle => false,
|
||||
_ => true,
|
||||
self.set_state(&mut state, next_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -651,7 +683,8 @@ impl<L: AsLightClient> LightSync<L> {
|
||||
pending_reqs: Mutex::new(HashMap::new()),
|
||||
client: client,
|
||||
rng: Mutex::new(OsRng::new()?),
|
||||
state: Mutex::new(SyncState::Idle),
|
||||
state: Mutex::new(SyncStateWrapper::idle()),
|
||||
is_idle: Mutex::new(true),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -666,9 +699,6 @@ pub trait SyncInfo {
|
||||
|
||||
/// Whether major sync is underway.
|
||||
fn is_major_importing(&self) -> bool;
|
||||
|
||||
/// Whether major sync is underway, skipping some synchronization.
|
||||
fn is_major_importing_no_sync(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<L: AsLightClient> SyncInfo for LightSync<L> {
|
||||
@@ -681,11 +711,13 @@ impl<L: AsLightClient> SyncInfo for LightSync<L> {
|
||||
}
|
||||
|
||||
fn is_major_importing(&self) -> bool {
|
||||
self.is_major_importing_do_wait(true)
|
||||
}
|
||||
const EMPTY_QUEUE: usize = 3;
|
||||
|
||||
fn is_major_importing_no_sync(&self) -> bool {
|
||||
self.is_major_importing_do_wait(false)
|
||||
let queue_info = self.client.as_light_client().queue_info();
|
||||
let is_verifying = queue_info.unverified_queue_size + queue_info.verified_queue_size > EMPTY_QUEUE;
|
||||
let is_syncing = !*self.is_idle.lock();
|
||||
|
||||
is_verifying || is_syncing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ pub trait SyncIo {
|
||||
fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8;
|
||||
/// Returns if the chain block queue empty
|
||||
fn is_chain_queue_empty(&self) -> bool {
|
||||
self.chain().queue_info().is_empty()
|
||||
self.chain().is_queue_empty()
|
||||
}
|
||||
/// Check if the session is expired
|
||||
fn is_expired(&self) -> bool;
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
|
||||
use std::collections::{VecDeque, HashSet, HashMap};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use ethereum_types::H256;
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
use bytes::Bytes;
|
||||
use network::{self, PeerId, ProtocolId, PacketId, SessionInfo};
|
||||
use tests::snapshot::*;
|
||||
use ethcore::client::{TestBlockChainClient, BlockChainClient, Client as EthcoreClient,
|
||||
ClientConfig, ChainNotify, ChainRoute, ChainMessageType, ClientIoMessage};
|
||||
ClientConfig, ChainNotify, NewBlocks, ChainMessageType, ClientIoMessage};
|
||||
use ethcore::header::BlockNumber;
|
||||
use ethcore::snapshot::SnapshotService;
|
||||
use ethcore::spec::Spec;
|
||||
@@ -33,7 +32,7 @@ use ethcore::test_helpers;
|
||||
use sync_io::SyncIo;
|
||||
use io::{IoChannel, IoContext, IoHandler};
|
||||
use api::WARP_SYNC_PROTOCOL_ID;
|
||||
use chain::{ChainSync, ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_3, PRIVATE_TRANSACTION_PACKET, SIGNED_PRIVATE_TRANSACTION_PACKET};
|
||||
use chain::{ChainSync, ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_3, PRIVATE_TRANSACTION_PACKET, SIGNED_PRIVATE_TRANSACTION_PACKET, SyncSupplier};
|
||||
use SyncConfig;
|
||||
use private_tx::SimplePrivateTxHandler;
|
||||
|
||||
@@ -271,7 +270,7 @@ impl<C: FlushingBlockChainClient> Peer for EthPeer<C> {
|
||||
|
||||
fn receive_message(&self, from: PeerId, msg: TestPacket) -> HashSet<PeerId> {
|
||||
let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, Some(from));
|
||||
ChainSync::dispatch_packet(&self.sync, &mut io, from, msg.packet_id, &msg.data);
|
||||
SyncSupplier::dispatch_packet(&self.sync, &mut io, from, msg.packet_id, &msg.data);
|
||||
self.chain.flush();
|
||||
io.to_disconnect.clone()
|
||||
}
|
||||
@@ -286,10 +285,12 @@ impl<C: FlushingBlockChainClient> Peer for EthPeer<C> {
|
||||
}
|
||||
|
||||
fn sync_step(&self) {
|
||||
let mut io = TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None);
|
||||
self.chain.flush();
|
||||
self.sync.write().maintain_peers(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None));
|
||||
self.sync.write().maintain_sync(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None));
|
||||
self.sync.write().propagate_new_transactions(&mut TestIo::new(&*self.chain, &self.snapshot_service, &self.queue, None));
|
||||
self.sync.write().maintain_peers(&mut io);
|
||||
self.sync.write().maintain_sync(&mut io);
|
||||
self.sync.write().continue_sync(&mut io);
|
||||
self.sync.write().propagate_new_transactions(&mut io);
|
||||
}
|
||||
|
||||
fn restart_sync(&self) {
|
||||
@@ -533,23 +534,18 @@ impl IoHandler<ClientIoMessage> for TestIoHandler {
|
||||
}
|
||||
|
||||
impl ChainNotify for EthPeer<EthcoreClient> {
|
||||
fn new_blocks(&self,
|
||||
imported: Vec<H256>,
|
||||
invalid: Vec<H256>,
|
||||
route: ChainRoute,
|
||||
sealed: Vec<H256>,
|
||||
proposed: Vec<Bytes>,
|
||||
_duration: Duration)
|
||||
fn new_blocks(&self, new_blocks: NewBlocks)
|
||||
{
|
||||
let (enacted, retracted) = route.into_enacted_retracted();
|
||||
if new_blocks.has_more_blocks_to_import { return }
|
||||
let (enacted, retracted) = new_blocks.route.into_enacted_retracted();
|
||||
|
||||
self.new_blocks_queue.write().push_back(NewBlockMessage {
|
||||
imported,
|
||||
invalid,
|
||||
imported: new_blocks.imported,
|
||||
invalid: new_blocks.invalid,
|
||||
enacted,
|
||||
retracted,
|
||||
sealed,
|
||||
proposed,
|
||||
sealed: new_blocks.sealed,
|
||||
proposed: new_blocks.proposed,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use api::TransactionStats;
|
||||
use std::hash::BuildHasher;
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use ethereum_types::{H256, H512};
|
||||
use fastmap::H256FastMap;
|
||||
@@ -74,7 +75,7 @@ impl TransactionsStats {
|
||||
}
|
||||
|
||||
/// Retains only transactions present in given `HashSet`.
|
||||
pub fn retain(&mut self, hashes: &HashSet<H256>) {
|
||||
pub fn retain<S: BuildHasher>(&mut self, hashes: &HashSet<H256, S>) {
|
||||
let to_remove = self.pending_transactions.keys()
|
||||
.filter(|hash| !hashes.contains(hash))
|
||||
.cloned()
|
||||
|
||||
@@ -9,7 +9,7 @@ ethereum-types = "0.4"
|
||||
log = "0.4"
|
||||
parity-wasm = "0.31"
|
||||
libc = "0.2"
|
||||
pwasm-utils = "0.2.2"
|
||||
pwasm-utils = "0.6.1"
|
||||
vm = { path = "../vm" }
|
||||
ethcore-logger = { path = "../../logger" }
|
||||
wasmi = "0.3.0"
|
||||
|
||||
@@ -55,6 +55,22 @@ impl Informant {
|
||||
Self::with_informant_in_depth(informant.subinfos.last_mut().expect("prepare/done_trace are not balanced"), depth - 1, f);
|
||||
}
|
||||
}
|
||||
|
||||
fn informant_trace(informant: &Informant, gas_used: U256) -> String {
|
||||
let 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()
|
||||
}
|
||||
}
|
||||
|
||||
impl vm::Informant for Informant {
|
||||
@@ -125,22 +141,11 @@ impl trace::VMTracer for Informant {
|
||||
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) {
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
|
||||
let mem_diff = informant.mem_written.clone().map(|(o, s)| (o, &(mem[o..o+s])));
|
||||
let store_diff = informant.store_written.clone();
|
||||
let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info());
|
||||
|
||||
let trace = 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,
|
||||
});
|
||||
informant.traces.push(trace.to_string());
|
||||
let trace = Self::informant_trace(informant, gas_used);
|
||||
informant.traces.push(trace);
|
||||
|
||||
informant.unmatched = false;
|
||||
informant.gas_used = gas_used;
|
||||
@@ -151,11 +156,11 @@ impl trace::VMTracer for Informant {
|
||||
informant.stack.extend_from_slice(stack_push);
|
||||
|
||||
// TODO [ToDr] Align memory?
|
||||
if let Some((pos, data)) = mem_diff {
|
||||
if informant.memory.len() < (pos + data.len()) {
|
||||
informant.memory.resize(pos + data.len(), 0);
|
||||
if let Some((pos, size)) = informant.mem_written.clone() {
|
||||
if informant.memory.len() < (pos + size) {
|
||||
informant.memory.resize(pos + size, 0);
|
||||
}
|
||||
informant.memory[pos..pos + data.len()].copy_from_slice(data);
|
||||
informant.memory[pos..(pos + size)].copy_from_slice(&mem[pos..(pos + size)]);
|
||||
}
|
||||
|
||||
if let Some((pos, val)) = store_diff {
|
||||
@@ -195,7 +200,12 @@ impl trace::VMTracer for Informant {
|
||||
// print last line with final state:
|
||||
self.gas_cost = 0.into();
|
||||
let gas_used = self.gas_used;
|
||||
self.trace_executed(gas_used, &[], &[]);
|
||||
let subdepth = self.subdepth;
|
||||
|
||||
Self::with_informant_in_depth(&mut self, subdepth, |informant: &mut Informant| {
|
||||
let trace = Self::informant_trace(informant, gas_used);
|
||||
informant.traces.push(trace);
|
||||
});
|
||||
} else if !self.subtraces.is_empty() {
|
||||
self.traces.extend(mem::replace(&mut self.subtraces, vec![]));
|
||||
}
|
||||
@@ -280,6 +290,17 @@ mod tests {
|
||||
{"pc":0,"op":248,"opName":"","gas":"0xffff","gasCost":"0x0","memory":"0x","stack":[],"storage":{},"depth":1}
|
||||
"#,
|
||||
);
|
||||
|
||||
run_test(
|
||||
Informant::default(),
|
||||
&compare_json,
|
||||
"5A51",
|
||||
0xfffff,
|
||||
r#"
|
||||
{"depth":1,"gas":"0xfffff","gasCost":"0x2","memory":"0x","op":90,"opName":"GAS","pc":0,"stack":[],"storage":{}}
|
||||
{"depth":1,"gas":"0xffffd","gasCost":"0x0","memory":"0x","op":81,"opName":"MLOAD","pc":1,"stack":["0xffffd"],"storage":{}}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -23,6 +23,7 @@ use spec::builtin::Builtin;
|
||||
|
||||
/// Spec account.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Account {
|
||||
/// Builtin contract.
|
||||
pub builtin: Option<Builtin>,
|
||||
|
||||
@@ -23,6 +23,7 @@ use super::ValidatorSet;
|
||||
|
||||
/// Authority params deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct AuthorityRoundParams {
|
||||
/// Block duration, in seconds.
|
||||
#[serde(rename="stepDuration")]
|
||||
@@ -67,10 +68,13 @@ pub struct AuthorityRoundParams {
|
||||
/// Maximum number of accepted empty steps.
|
||||
#[serde(rename="maximumEmptySteps")]
|
||||
pub maximum_empty_steps: Option<Uint>,
|
||||
/// Strict validation of empty steps transition block.
|
||||
pub strict_empty_steps_transition: Option<Uint>,
|
||||
}
|
||||
|
||||
/// Authority engine deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct AuthorityRound {
|
||||
/// Ethash params.
|
||||
pub params: AuthorityRoundParams,
|
||||
|
||||
@@ -21,6 +21,7 @@ use super::ValidatorSet;
|
||||
|
||||
/// Authority params deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct BasicAuthorityParams {
|
||||
/// Block duration.
|
||||
#[serde(rename="durationLimit")]
|
||||
@@ -31,6 +32,7 @@ pub struct BasicAuthorityParams {
|
||||
|
||||
/// Authority engine deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct BasicAuthority {
|
||||
/// Ethash params.
|
||||
pub params: BasicAuthorityParams,
|
||||
|
||||
@@ -20,6 +20,7 @@ use uint::Uint;
|
||||
|
||||
/// Linear pricing.
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Linear {
|
||||
/// Base price.
|
||||
pub base: usize,
|
||||
@@ -29,6 +30,7 @@ pub struct Linear {
|
||||
|
||||
/// Pricing for modular exponentiation.
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Modexp {
|
||||
/// Price divisor.
|
||||
pub divisor: usize,
|
||||
@@ -36,6 +38,7 @@ pub struct Modexp {
|
||||
|
||||
/// Pricing for alt_bn128_pairing.
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct AltBn128Pairing {
|
||||
/// Base price.
|
||||
pub base: usize,
|
||||
@@ -45,6 +48,7 @@ pub struct AltBn128Pairing {
|
||||
|
||||
/// Pricing variants.
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum Pricing {
|
||||
/// Linear pricing.
|
||||
#[serde(rename="linear")]
|
||||
@@ -59,6 +63,7 @@ pub enum Pricing {
|
||||
|
||||
/// Spec builtin.
|
||||
#[derive(Debug, PartialEq, Deserialize, Clone)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Builtin {
|
||||
/// Builtin name.
|
||||
pub name: String,
|
||||
|
||||
@@ -20,6 +20,7 @@ use super::{Ethash, BasicAuthority, AuthorityRound, Tendermint, NullEngine, Inst
|
||||
|
||||
/// Engine deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum Engine {
|
||||
/// Null engine.
|
||||
#[serde(rename="null")]
|
||||
@@ -28,6 +29,7 @@ pub enum Engine {
|
||||
#[serde(rename="instantSeal")]
|
||||
InstantSeal(Option<InstantSeal>),
|
||||
/// Ethash engine.
|
||||
#[serde(rename = "Ethash")]
|
||||
Ethash(Ethash),
|
||||
/// BasicAuthority engine.
|
||||
#[serde(rename="basicAuthority")]
|
||||
@@ -88,7 +90,6 @@ mod tests {
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"homesteadTransition" : "0x",
|
||||
"daoHardforkTransition": "0xffffffffffffffff",
|
||||
"daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000",
|
||||
|
||||
@@ -23,6 +23,7 @@ use hash::Address;
|
||||
|
||||
/// Deserializable doppelganger of block rewards for EthashParams
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(untagged)]
|
||||
pub enum BlockReward {
|
||||
Single(Uint),
|
||||
@@ -31,6 +32,7 @@ pub enum BlockReward {
|
||||
|
||||
/// Deserializable doppelganger of EthashParams.
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct EthashParams {
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="minimumDifficulty")]
|
||||
@@ -119,6 +121,7 @@ pub struct EthashParams {
|
||||
|
||||
/// Ethash engine deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Ethash {
|
||||
/// Ethash params.
|
||||
pub params: EthashParams,
|
||||
|
||||
@@ -23,6 +23,7 @@ use spec::Seal;
|
||||
|
||||
/// Spec genesis.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Genesis {
|
||||
/// Seal.
|
||||
pub seal: Seal,
|
||||
@@ -70,7 +71,6 @@ mod tests {
|
||||
#[test]
|
||||
fn genesis_deserialization() {
|
||||
let s = r#"{
|
||||
"nonce": "0x0000000000000042",
|
||||
"difficulty": "0x400000000",
|
||||
"seal": {
|
||||
"ethereum": {
|
||||
|
||||
@@ -21,6 +21,7 @@ use uint::Uint;
|
||||
|
||||
/// Spec hardcoded sync.
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct HardcodedSync {
|
||||
/// Hexadecimal of the RLP encoding of the header of the block to start synchronization from.
|
||||
pub header: String,
|
||||
@@ -28,7 +29,7 @@ pub struct HardcodedSync {
|
||||
#[serde(rename="totalDifficulty")]
|
||||
pub total_difficulty: Uint,
|
||||
/// Ordered trie roots of blocks before and including `header`.
|
||||
#[serde(rename="CHTs")]
|
||||
#[serde(rename = "CHTs")]
|
||||
pub chts: Vec<H256>,
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
/// Instant seal engine params deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct InstantSealParams {
|
||||
/// Whether to enable millisecond timestamp.
|
||||
#[serde(rename="millisecondTimestamp")]
|
||||
@@ -27,6 +28,7 @@ pub struct InstantSealParams {
|
||||
|
||||
/// Instant seal engine descriptor.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct InstantSeal {
|
||||
/// Instant seal parameters.
|
||||
pub params: InstantSealParams,
|
||||
|
||||
@@ -20,6 +20,7 @@ use uint::Uint;
|
||||
|
||||
/// Authority params deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct NullEngineParams {
|
||||
/// Block reward.
|
||||
#[serde(rename="blockReward")]
|
||||
@@ -28,6 +29,7 @@ pub struct NullEngineParams {
|
||||
|
||||
/// Null engine descriptor
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct NullEngine {
|
||||
/// Ethash params.
|
||||
pub params: NullEngineParams,
|
||||
|
||||
@@ -22,6 +22,7 @@ use bytes::Bytes;
|
||||
|
||||
/// Spec params.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Params {
|
||||
/// Account start nonce, defaults to 0.
|
||||
#[serde(rename="accountStartNonce")]
|
||||
|
||||
@@ -22,6 +22,7 @@ use bytes::Bytes;
|
||||
|
||||
/// Ethereum seal.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Ethereum {
|
||||
/// Seal nonce.
|
||||
pub nonce: H64,
|
||||
@@ -32,6 +33,7 @@ pub struct Ethereum {
|
||||
|
||||
/// AuthorityRound seal.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct AuthorityRoundSeal {
|
||||
/// Seal step.
|
||||
pub step: Uint,
|
||||
@@ -41,6 +43,7 @@ pub struct AuthorityRoundSeal {
|
||||
|
||||
/// Tendermint seal.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct TendermintSeal {
|
||||
/// Seal round.
|
||||
pub round: Uint,
|
||||
@@ -52,6 +55,7 @@ pub struct TendermintSeal {
|
||||
|
||||
/// Seal variants.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum Seal {
|
||||
/// Ethereum seal.
|
||||
#[serde(rename="ethereum")]
|
||||
|
||||
@@ -38,6 +38,7 @@ pub enum ForkSpec {
|
||||
|
||||
/// Spec deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Spec {
|
||||
/// Spec name.
|
||||
pub name: String,
|
||||
@@ -71,6 +72,71 @@ mod tests {
|
||||
use serde_json;
|
||||
use spec::spec::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"
|
||||
}
|
||||
},
|
||||
"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<Spec, _> = serde_json::from_str(s);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_deserialization() {
|
||||
let s = r#"{
|
||||
@@ -91,7 +157,6 @@ mod tests {
|
||||
},
|
||||
"params": {
|
||||
"accountStartNonce": "0x0100000",
|
||||
"homesteadTransition": "0x789b0",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
"minGasLimit": "0x1388",
|
||||
"networkID" : "0x2",
|
||||
|
||||
@@ -23,6 +23,7 @@ use spec::{Account, Builtin};
|
||||
|
||||
/// Blockchain test state deserializer.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct State(BTreeMap<Address, Account>);
|
||||
|
||||
impl State {
|
||||
|
||||
@@ -21,6 +21,7 @@ use super::ValidatorSet;
|
||||
|
||||
/// Tendermint params deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct TendermintParams {
|
||||
/// Valid validators.
|
||||
pub validators: ValidatorSet,
|
||||
@@ -43,6 +44,7 @@ pub struct TendermintParams {
|
||||
|
||||
/// Tendermint engine deserialization.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Tendermint {
|
||||
/// Ethash params.
|
||||
pub params: TendermintParams,
|
||||
|
||||
@@ -22,6 +22,7 @@ use hash::Address;
|
||||
|
||||
/// Different ways of specifying validators.
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum ValidatorSet {
|
||||
/// A simple list of authorities.
|
||||
#[serde(rename="list")]
|
||||
|
||||
@@ -50,6 +50,10 @@ impl Notifier {
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
@@ -26,12 +26,18 @@ pub mod client;
|
||||
use self::tx::{Tx, TxExt, PairExt};
|
||||
use self::client::TestClient;
|
||||
|
||||
// max mem for 3 transaction, this is relative
|
||||
// to the global use allocator, the value is currently
|
||||
// set to reflect malloc usage.
|
||||
// 50 was enough when using jmalloc.
|
||||
const TEST_QUEUE_MAX_MEM: usize = 80;
|
||||
|
||||
fn new_queue() -> TransactionQueue {
|
||||
TransactionQueue::new(
|
||||
txpool::Options {
|
||||
max_count: 3,
|
||||
max_per_sender: 3,
|
||||
max_mem_usage: 50
|
||||
max_mem_usage: TEST_QUEUE_MAX_MEM
|
||||
},
|
||||
verifier::Options {
|
||||
minimal_gas_price: 1.into(),
|
||||
@@ -49,7 +55,7 @@ fn should_return_correct_nonces_when_dropped_because_of_limit() {
|
||||
txpool::Options {
|
||||
max_count: 3,
|
||||
max_per_sender: 1,
|
||||
max_mem_usage: 50
|
||||
max_mem_usage: TEST_QUEUE_MAX_MEM
|
||||
},
|
||||
verifier::Options {
|
||||
minimal_gas_price: 1.into(),
|
||||
@@ -103,7 +109,7 @@ fn should_never_drop_local_transactions_from_different_senders() {
|
||||
txpool::Options {
|
||||
max_count: 3,
|
||||
max_per_sender: 1,
|
||||
max_mem_usage: 50
|
||||
max_mem_usage: TEST_QUEUE_MAX_MEM
|
||||
},
|
||||
verifier::Options {
|
||||
minimal_gas_price: 1.into(),
|
||||
@@ -477,7 +483,7 @@ fn should_prefer_current_transactions_when_hitting_the_limit() {
|
||||
txpool::Options {
|
||||
max_count: 1,
|
||||
max_per_sender: 2,
|
||||
max_mem_usage: 50
|
||||
max_mem_usage: TEST_QUEUE_MAX_MEM
|
||||
},
|
||||
verifier::Options {
|
||||
minimal_gas_price: 1.into(),
|
||||
@@ -707,7 +713,7 @@ fn should_accept_local_transactions_below_min_gas_price() {
|
||||
txpool::Options {
|
||||
max_count: 3,
|
||||
max_per_sender: 3,
|
||||
max_mem_usage: 50
|
||||
max_mem_usage: TEST_QUEUE_MAX_MEM
|
||||
},
|
||||
verifier::Options {
|
||||
minimal_gas_price: 10.into(),
|
||||
@@ -890,7 +896,7 @@ fn should_include_local_transaction_to_a_full_pool() {
|
||||
txpool::Options {
|
||||
max_count: 1,
|
||||
max_per_sender: 2,
|
||||
max_mem_usage: 50
|
||||
max_mem_usage: TEST_QUEUE_MAX_MEM
|
||||
},
|
||||
verifier::Options {
|
||||
minimal_gas_price: 1.into(),
|
||||
@@ -922,7 +928,7 @@ fn should_avoid_verifying_transaction_already_in_pool() {
|
||||
txpool::Options {
|
||||
max_count: 1,
|
||||
max_per_sender: 2,
|
||||
max_mem_usage: 50
|
||||
max_mem_usage: TEST_QUEUE_MAX_MEM
|
||||
},
|
||||
verifier::Options {
|
||||
minimal_gas_price: 1.into(),
|
||||
@@ -957,7 +963,7 @@ fn should_avoid_reverifying_recently_rejected_transactions() {
|
||||
txpool::Options {
|
||||
max_count: 1,
|
||||
max_per_sender: 2,
|
||||
max_mem_usage: 50
|
||||
max_mem_usage: TEST_QUEUE_MAX_MEM
|
||||
},
|
||||
verifier::Options {
|
||||
minimal_gas_price: 1.into(),
|
||||
@@ -999,7 +1005,7 @@ fn should_reject_early_in_case_gas_price_is_less_than_min_effective() {
|
||||
txpool::Options {
|
||||
max_count: 1,
|
||||
max_per_sender: 2,
|
||||
max_mem_usage: 50
|
||||
max_mem_usage: TEST_QUEUE_MAX_MEM
|
||||
},
|
||||
verifier::Options {
|
||||
minimal_gas_price: 1.into(),
|
||||
@@ -1039,7 +1045,7 @@ fn should_not_reject_early_in_case_gas_price_is_less_than_min_effective() {
|
||||
txpool::Options {
|
||||
max_count: 1,
|
||||
max_per_sender: 2,
|
||||
max_mem_usage: 50
|
||||
max_mem_usage: TEST_QUEUE_MAX_MEM
|
||||
},
|
||||
verifier::Options {
|
||||
minimal_gas_price: 1.into(),
|
||||
|
||||
@@ -467,6 +467,10 @@ usage! {
|
||||
"--no-jsonrpc",
|
||||
"Disable the HTTP JSON-RPC API server.",
|
||||
|
||||
FLAG flag_jsonrpc_experimental: (bool) = false, or |c: &Config| c.rpc.as_ref()?.experimental_rpcs.clone(),
|
||||
"--jsonrpc-experimental",
|
||||
"Enable experimental RPCs. Enable to have access to methods from unfinalised EIPs in all namespaces",
|
||||
|
||||
ARG arg_jsonrpc_port: (u16) = 8545u16, or |c: &Config| c.rpc.as_ref()?.port.clone(),
|
||||
"--jsonrpc-port=[PORT]",
|
||||
"Specify the port portion of the HTTP JSON-RPC API server.",
|
||||
@@ -1141,7 +1145,7 @@ struct Operating {
|
||||
no_persistent_txqueue: Option<bool>,
|
||||
no_hardcoded_sync: Option<bool>,
|
||||
|
||||
#[serde(rename="public_node")]
|
||||
#[serde(rename = "public_node")]
|
||||
_legacy_public_node: Option<bool>,
|
||||
}
|
||||
|
||||
@@ -1173,15 +1177,15 @@ struct PrivateTransactions {
|
||||
struct Ui {
|
||||
path: Option<String>,
|
||||
|
||||
#[serde(rename="force")]
|
||||
#[serde(rename = "force")]
|
||||
_legacy_force: Option<bool>,
|
||||
#[serde(rename="disable")]
|
||||
#[serde(rename = "disable")]
|
||||
_legacy_disable: Option<bool>,
|
||||
#[serde(rename="port")]
|
||||
#[serde(rename = "port")]
|
||||
_legacy_port: Option<u16>,
|
||||
#[serde(rename="interface")]
|
||||
#[serde(rename = "interface")]
|
||||
_legacy_interface: Option<String>,
|
||||
#[serde(rename="hosts")]
|
||||
#[serde(rename = "hosts")]
|
||||
_legacy_hosts: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
@@ -1219,6 +1223,7 @@ struct Rpc {
|
||||
server_threads: Option<usize>,
|
||||
processing_threads: Option<usize>,
|
||||
max_payload: Option<usize>,
|
||||
experimental_rpcs: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Deserialize)]
|
||||
@@ -1244,21 +1249,21 @@ struct Ipc {
|
||||
#[derive(Default, Debug, PartialEq, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct Dapps {
|
||||
#[serde(rename="disable")]
|
||||
#[serde(rename = "disable")]
|
||||
_legacy_disable: Option<bool>,
|
||||
#[serde(rename="port")]
|
||||
#[serde(rename = "port")]
|
||||
_legacy_port: Option<u16>,
|
||||
#[serde(rename="interface")]
|
||||
#[serde(rename = "interface")]
|
||||
_legacy_interface: Option<String>,
|
||||
#[serde(rename="hosts")]
|
||||
#[serde(rename = "hosts")]
|
||||
_legacy_hosts: Option<Vec<String>>,
|
||||
#[serde(rename="cors")]
|
||||
#[serde(rename = "cors")]
|
||||
_legacy_cors: Option<String>,
|
||||
#[serde(rename="path")]
|
||||
#[serde(rename = "path")]
|
||||
_legacy_path: Option<String>,
|
||||
#[serde(rename="user")]
|
||||
#[serde(rename = "user")]
|
||||
_legacy_user: Option<String>,
|
||||
#[serde(rename="pass")]
|
||||
#[serde(rename = "pass")]
|
||||
_legacy_pass: Option<String>,
|
||||
}
|
||||
|
||||
@@ -1676,6 +1681,7 @@ mod tests {
|
||||
// -- API and Console Options
|
||||
// RPC
|
||||
flag_no_jsonrpc: false,
|
||||
flag_jsonrpc_experimental: false,
|
||||
arg_jsonrpc_port: 8545u16,
|
||||
arg_jsonrpc_interface: "local".into(),
|
||||
arg_jsonrpc_cors: "null".into(),
|
||||
@@ -1958,6 +1964,7 @@ mod tests {
|
||||
server_threads: None,
|
||||
processing_threads: None,
|
||||
max_payload: None,
|
||||
experimental_rpcs: None,
|
||||
}),
|
||||
ipc: Some(Ipc {
|
||||
disable: None,
|
||||
|
||||
@@ -138,6 +138,7 @@ impl Configuration {
|
||||
let compaction = self.args.arg_db_compaction.parse()?;
|
||||
let warp_sync = !self.args.flag_no_warp;
|
||||
let geth_compatibility = self.args.flag_geth;
|
||||
let experimental_rpcs = self.args.flag_jsonrpc_experimental;
|
||||
let ipfs_conf = self.ipfs_config();
|
||||
let secretstore_conf = self.secretstore_config()?;
|
||||
let format = self.format()?;
|
||||
@@ -377,6 +378,7 @@ impl Configuration {
|
||||
warp_sync: warp_sync,
|
||||
warp_barrier: self.args.arg_warp_barrier,
|
||||
geth_compatibility: geth_compatibility,
|
||||
experimental_rpcs,
|
||||
net_settings: self.network_settings()?,
|
||||
ipfs_conf: ipfs_conf,
|
||||
secretstore_conf: secretstore_conf,
|
||||
@@ -1418,6 +1420,7 @@ mod tests {
|
||||
compaction: Default::default(),
|
||||
vm_type: Default::default(),
|
||||
geth_compatibility: false,
|
||||
experimental_rpcs: false,
|
||||
net_settings: Default::default(),
|
||||
ipfs_conf: Default::default(),
|
||||
secretstore_conf: Default::default(),
|
||||
|
||||
@@ -23,6 +23,8 @@ use rlp;
|
||||
use super::kvdb_rocksdb::DatabaseConfig;
|
||||
use super::open_database;
|
||||
|
||||
const LOG_BLOOMS_ELEMENTS_PER_INDEX: u64 = 16;
|
||||
|
||||
pub fn migrate_blooms<P: AsRef<Path>>(path: P, config: &DatabaseConfig) -> Result<(), Error> {
|
||||
// init
|
||||
let db = open_database(&path.as_ref().to_string_lossy(), config)?;
|
||||
@@ -41,11 +43,12 @@ pub fn migrate_blooms<P: AsRef<Path>>(path: P, config: &DatabaseConfig) -> Resul
|
||||
key[0] == 3u8 && key[1] == 0u8
|
||||
})
|
||||
.map(|(key, group)| {
|
||||
let number =
|
||||
let index =
|
||||
(key[2] as u64) << 24 |
|
||||
(key[3] as u64) << 16 |
|
||||
(key[4] as u64) << 8 |
|
||||
(key[5] as u64);
|
||||
let number = index * LOG_BLOOMS_ELEMENTS_PER_INDEX;
|
||||
|
||||
let blooms = rlp::decode_list::<Bloom>(&group);
|
||||
(number, blooms)
|
||||
@@ -66,11 +69,12 @@ pub fn migrate_blooms<P: AsRef<Path>>(path: P, config: &DatabaseConfig) -> Resul
|
||||
key[0] == 1u8 && key[1] == 0u8
|
||||
})
|
||||
.map(|(key, group)| {
|
||||
let number =
|
||||
let index =
|
||||
(key[2] as u64) |
|
||||
(key[3] as u64) << 8 |
|
||||
(key[4] as u64) << 16 |
|
||||
(key[5] as u64) << 24;
|
||||
let number = index * LOG_BLOOMS_ELEMENTS_PER_INDEX;
|
||||
|
||||
let blooms = rlp::decode_list::<Bloom>(&group);
|
||||
(number, blooms)
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::time::{Instant, Duration};
|
||||
use atty;
|
||||
use ethcore::client::{
|
||||
BlockId, BlockChainClient, ChainInfo, BlockInfo, BlockChainInfo,
|
||||
BlockQueueInfo, ChainNotify, ChainRoute, ClientReport, Client, ClientIoMessage
|
||||
BlockQueueInfo, ChainNotify, NewBlocks, ClientReport, Client, ClientIoMessage
|
||||
};
|
||||
use ethcore::header::BlockNumber;
|
||||
use ethcore::snapshot::{RestorationStatus, SnapshotService as SS};
|
||||
@@ -38,7 +38,6 @@ use number_prefix::{binary_prefix, Standalone, Prefixed};
|
||||
use parity_rpc::is_major_importing_or_waiting;
|
||||
use parity_rpc::informant::RpcStats;
|
||||
use ethereum_types::H256;
|
||||
use bytes::Bytes;
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
|
||||
/// Format byte counts to standard denominations.
|
||||
@@ -184,7 +183,7 @@ impl InformantData for LightNodeInformantData {
|
||||
fn executes_transactions(&self) -> bool { false }
|
||||
|
||||
fn is_major_importing(&self) -> bool {
|
||||
self.sync.is_major_importing_no_sync()
|
||||
self.sync.is_major_importing()
|
||||
}
|
||||
|
||||
fn report(&self) -> Report {
|
||||
@@ -256,16 +255,13 @@ impl<T: InformantData> Informant<T> {
|
||||
}
|
||||
|
||||
pub fn tick(&self) {
|
||||
let elapsed = self.last_tick.read().elapsed();
|
||||
if elapsed < Duration::from_secs(5) {
|
||||
return;
|
||||
}
|
||||
let now = Instant::now();
|
||||
let elapsed = now.duration_since(*self.last_tick.read());
|
||||
|
||||
let (client_report, full_report) = {
|
||||
let mut last_report = self.last_report.lock();
|
||||
let full_report = self.target.report();
|
||||
let diffed = full_report.client_report.clone() - &*last_report;
|
||||
*last_report = full_report.client_report.clone();
|
||||
(diffed, full_report)
|
||||
};
|
||||
|
||||
@@ -289,7 +285,8 @@ impl<T: InformantData> Informant<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
*self.last_tick.write() = Instant::now();
|
||||
*self.last_tick.write() = now;
|
||||
*self.last_report.lock() = full_report.client_report.clone();
|
||||
|
||||
let paint = |c: Style, t: String| match self.with_color && atty::is(atty::Stream::Stdout) {
|
||||
true => format!("{}", c.paint(t)),
|
||||
@@ -306,7 +303,7 @@ impl<T: InformantData> Informant<T> {
|
||||
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!("{:4}", (client_report.gas_processed / (elapsed.as_milliseconds() * 1000)).low_u64()))
|
||||
paint(Yellow.bold(), format!("{:6.1}", (client_report.gas_processed / 1000).low_u64() as f64 / elapsed.as_milliseconds() as f64))
|
||||
)
|
||||
} else {
|
||||
format!("{} hdr/s",
|
||||
@@ -367,29 +364,30 @@ impl<T: InformantData> Informant<T> {
|
||||
}
|
||||
|
||||
impl ChainNotify for Informant<FullNodeInformantData> {
|
||||
fn new_blocks(&self, imported: Vec<H256>, _invalid: Vec<H256>, _route: ChainRoute, _sealed: Vec<H256>, _proposed: Vec<Bytes>, duration: Duration) {
|
||||
fn new_blocks(&self, new_blocks: NewBlocks) {
|
||||
if new_blocks.has_more_blocks_to_import { return }
|
||||
let mut last_import = self.last_import.lock();
|
||||
let client = &self.target.client;
|
||||
|
||||
let importing = self.target.is_major_importing();
|
||||
let ripe = Instant::now() > *last_import + Duration::from_secs(1) && !importing;
|
||||
let txs_imported = imported.iter()
|
||||
.take(imported.len().saturating_sub(if ripe { 1 } else { 0 }))
|
||||
let txs_imported = new_blocks.imported.iter()
|
||||
.take(new_blocks.imported.len().saturating_sub(if ripe { 1 } else { 0 }))
|
||||
.filter_map(|h| client.block(BlockId::Hash(*h)))
|
||||
.map(|b| b.transactions_count())
|
||||
.sum();
|
||||
|
||||
if ripe {
|
||||
if let Some(block) = imported.last().and_then(|h| client.block(BlockId::Hash(*h))) {
|
||||
if let Some(block) = new_blocks.imported.last().and_then(|h| client.block(BlockId::Hash(*h))) {
|
||||
let header_view = block.header_view();
|
||||
let size = block.rlp().as_raw().len();
|
||||
let (skipped, skipped_txs) = (self.skipped.load(AtomicOrdering::Relaxed) + imported.len() - 1, self.skipped_txs.load(AtomicOrdering::Relaxed) + txs_imported);
|
||||
let (skipped, skipped_txs) = (self.skipped.load(AtomicOrdering::Relaxed) + new_blocks.imported.len() - 1, self.skipped_txs.load(AtomicOrdering::Relaxed) + txs_imported);
|
||||
info!(target: "import", "Imported {} {} ({} txs, {} Mgas, {} ms, {} KiB){}",
|
||||
Colour::White.bold().paint(format!("#{}", header_view.number())),
|
||||
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!("{}", duration.as_milliseconds())),
|
||||
Colour::Purple.bold().paint(format!("{}", new_blocks.duration.as_milliseconds())),
|
||||
Colour::Blue.bold().paint(format!("{:.2}", size as f32 / 1024f32)),
|
||||
if skipped > 0 {
|
||||
format!(" + another {} block(s) containing {} tx(s)",
|
||||
@@ -405,7 +403,7 @@ impl ChainNotify for Informant<FullNodeInformantData> {
|
||||
*last_import = Instant::now();
|
||||
}
|
||||
} else {
|
||||
self.skipped.fetch_add(imported.len(), AtomicOrdering::Relaxed);
|
||||
self.skipped.fetch_add(new_blocks.imported.len(), AtomicOrdering::Relaxed);
|
||||
self.skipped_txs.fetch_add(txs_imported, AtomicOrdering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, mpsc};
|
||||
|
||||
use ethcore::client::BlockChainClient;
|
||||
use sync::{self, AttachedProtocol, SyncConfig, NetworkConfiguration, Params, ConnectionFilter};
|
||||
@@ -25,12 +25,17 @@ pub use sync::{EthSync, SyncProvider, ManageNetwork, PrivateTxHandler};
|
||||
pub use ethcore::client::ChainNotify;
|
||||
use ethcore_logger::Config as LogConfig;
|
||||
|
||||
pub type SyncModules = (Arc<SyncProvider>, Arc<ManageNetwork>, Arc<ChainNotify>);
|
||||
pub type SyncModules = (
|
||||
Arc<SyncProvider>,
|
||||
Arc<ManageNetwork>,
|
||||
Arc<ChainNotify>,
|
||||
mpsc::Sender<sync::PriorityTask>,
|
||||
);
|
||||
|
||||
pub fn sync(
|
||||
sync_cfg: SyncConfig,
|
||||
net_cfg: NetworkConfiguration,
|
||||
client: Arc<BlockChainClient>,
|
||||
config: SyncConfig,
|
||||
network_config: NetworkConfiguration,
|
||||
chain: Arc<BlockChainClient>,
|
||||
snapshot_service: Arc<SnapshotService>,
|
||||
private_tx_handler: Arc<PrivateTxHandler>,
|
||||
provider: Arc<Provider>,
|
||||
@@ -39,15 +44,20 @@ pub fn sync(
|
||||
connection_filter: Option<Arc<ConnectionFilter>>,
|
||||
) -> Result<SyncModules, sync::Error> {
|
||||
let eth_sync = EthSync::new(Params {
|
||||
config: sync_cfg,
|
||||
chain: client,
|
||||
provider: provider,
|
||||
snapshot_service: snapshot_service,
|
||||
config,
|
||||
chain,
|
||||
provider,
|
||||
snapshot_service,
|
||||
private_tx_handler,
|
||||
network_config: net_cfg,
|
||||
attached_protos: attached_protos,
|
||||
network_config,
|
||||
attached_protos,
|
||||
},
|
||||
connection_filter)?;
|
||||
|
||||
Ok((eth_sync.clone() as Arc<SyncProvider>, eth_sync.clone() as Arc<ManageNetwork>, eth_sync.clone() as Arc<ChainNotify>))
|
||||
Ok((
|
||||
eth_sync.clone() as Arc<SyncProvider>,
|
||||
eth_sync.clone() as Arc<ManageNetwork>,
|
||||
eth_sync.clone() as Arc<ChainNotify>,
|
||||
eth_sync.priority_tasks()
|
||||
))
|
||||
}
|
||||
|
||||
@@ -228,6 +228,7 @@ pub struct FullDependencies {
|
||||
pub net_service: Arc<ManageNetwork>,
|
||||
pub updater: Arc<Updater>,
|
||||
pub geth_compatibility: bool,
|
||||
pub experimental_rpcs: bool,
|
||||
pub ws_address: Option<Host>,
|
||||
pub fetch: FetchClient,
|
||||
pub executor: Executor,
|
||||
@@ -317,7 +318,7 @@ impl FullDependencies {
|
||||
}
|
||||
},
|
||||
Api::Personal => {
|
||||
handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate());
|
||||
handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility, self.experimental_rpcs).to_delegate());
|
||||
},
|
||||
Api::Signer => {
|
||||
handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.executor.clone()).to_delegate());
|
||||
@@ -438,6 +439,7 @@ pub struct LightDependencies<T> {
|
||||
pub ws_address: Option<Host>,
|
||||
pub fetch: FetchClient,
|
||||
pub geth_compatibility: bool,
|
||||
pub experimental_rpcs: bool,
|
||||
pub executor: Executor,
|
||||
pub whisper_rpc: Option<::whisper::RpcFactory>,
|
||||
pub private_tx_service: Option<Arc<PrivateTransactionManager>>,
|
||||
@@ -531,7 +533,7 @@ impl<C: LightChainClient + 'static> LightDependencies<C> {
|
||||
handler.extend_with(EthPubSub::to_delegate(client));
|
||||
},
|
||||
Api::Personal => {
|
||||
handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate());
|
||||
handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility, self.experimental_rpcs).to_delegate());
|
||||
},
|
||||
Api::Signer => {
|
||||
handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.executor.clone()).to_delegate());
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::any::Any;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::{Arc, Weak, atomic};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::thread;
|
||||
|
||||
@@ -115,6 +115,7 @@ pub struct RunCmd {
|
||||
pub compaction: DatabaseCompactionProfile,
|
||||
pub vm_type: VMType,
|
||||
pub geth_compatibility: bool,
|
||||
pub experimental_rpcs: bool,
|
||||
pub net_settings: NetworkSettings,
|
||||
pub ipfs_conf: ipfs::Configuration,
|
||||
pub secretstore_conf: secretstore::Configuration,
|
||||
@@ -312,6 +313,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
|
||||
ws_address: cmd.ws_conf.address(),
|
||||
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.
|
||||
@@ -402,11 +404,6 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
||||
// create dirs used by parity
|
||||
cmd.dirs.create_dirs(cmd.acc_conf.unlocked_accounts.len() == 0, cmd.secretstore_conf.enabled)?;
|
||||
|
||||
// run in daemon mode
|
||||
if let Some(pid_file) = cmd.daemon {
|
||||
daemonize(pid_file)?;
|
||||
}
|
||||
|
||||
//print out running parity environment
|
||||
print_running_environment(&spec.data_dir, &cmd.dirs, &db_dirs);
|
||||
|
||||
@@ -466,6 +463,14 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
||||
|
||||
let passwords = passwords_from_files(&cmd.acc_conf.password_files)?;
|
||||
|
||||
// Run in daemon mode.
|
||||
// Note, that it should be called before we leave any file descriptor open,
|
||||
// since `daemonize` will close them.
|
||||
if let Some(pid_file) = cmd.daemon {
|
||||
info!("Running as a daemon process!");
|
||||
daemonize(pid_file)?;
|
||||
}
|
||||
|
||||
// prepare account provider
|
||||
let account_provider = Arc::new(prepare_account_provider(&cmd.spec, &cmd.dirs, &spec.data_dir, cmd.acc_conf, &passwords)?);
|
||||
|
||||
@@ -482,7 +487,6 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
||||
cmd.gas_pricer_conf.to_gas_pricer(fetch.clone(), runtime.executor()),
|
||||
&spec,
|
||||
Some(account_provider.clone()),
|
||||
|
||||
));
|
||||
miner.set_author(cmd.miner_extras.author, None).expect("Fails only if password is Some; password is None; qed");
|
||||
miner.set_gas_range_target(cmd.miner_extras.gas_range_target);
|
||||
@@ -639,7 +643,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
||||
};
|
||||
|
||||
// create sync object
|
||||
let (sync_provider, manage_network, chain_notify) = modules::sync(
|
||||
let (sync_provider, manage_network, chain_notify, priority_tasks) = modules::sync(
|
||||
sync_config,
|
||||
net_conf.clone().into(),
|
||||
client.clone(),
|
||||
@@ -653,6 +657,18 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
||||
|
||||
service.add_notify(chain_notify.clone());
|
||||
|
||||
// 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);
|
||||
}
|
||||
}));
|
||||
|
||||
// provider not added to a notification center is effectively disabled
|
||||
// TODO [debris] refactor it later on
|
||||
if cmd.private_tx_enabled {
|
||||
@@ -712,6 +728,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
||||
net_service: manage_network.clone(),
|
||||
updater: updater.clone(),
|
||||
geth_compatibility: cmd.geth_compatibility,
|
||||
experimental_rpcs: cmd.experimental_rpcs,
|
||||
ws_address: cmd.ws_conf.address(),
|
||||
fetch: fetch.clone(),
|
||||
executor: runtime.executor(),
|
||||
@@ -737,7 +754,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
||||
let secretstore_deps = secretstore::Dependencies {
|
||||
client: client.clone(),
|
||||
sync: sync_provider.clone(),
|
||||
miner: miner,
|
||||
miner: miner.clone(),
|
||||
account_provider: account_provider,
|
||||
accounts_passwords: &passwords,
|
||||
};
|
||||
|
||||
@@ -59,6 +59,7 @@ parity-updater = { path = "../updater" }
|
||||
parity-version = { path = "../util/version" }
|
||||
patricia-trie = "0.3.0"
|
||||
rlp = { version = "0.3.0", features = ["ethereum"] }
|
||||
eip712 = { path = "../util/EIP-712" }
|
||||
stats = { path = "../util/stats" }
|
||||
vm = { path = "../ethcore/vm" }
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ extern crate parity_runtime;
|
||||
extern crate parity_updater as updater;
|
||||
extern crate parity_version as version;
|
||||
extern crate patricia_trie as trie;
|
||||
extern crate eip712;
|
||||
extern crate rlp;
|
||||
extern crate stats;
|
||||
extern crate vm;
|
||||
|
||||
@@ -28,8 +28,7 @@ use tests::helpers::{GuardedAuthCodes, Server};
|
||||
|
||||
/// Setup a mock signer for tests
|
||||
pub fn serve() -> (Server<ws::Server>, usize, GuardedAuthCodes) {
|
||||
let port = 35000 + rand::random::<usize>() % 10000;
|
||||
let address = format!("127.0.0.1:{}", port).parse().unwrap();
|
||||
let address = "127.0.0.1:0".parse().unwrap();
|
||||
let io = MetaIoHandler::default();
|
||||
let authcodes = GuardedAuthCodes::new();
|
||||
let stats = Arc::new(informant::RpcStats::default());
|
||||
@@ -44,6 +43,7 @@ pub fn serve() -> (Server<ws::Server>, usize, GuardedAuthCodes) {
|
||||
extractors::WsExtractor::new(Some(&authcodes.path)),
|
||||
extractors::WsStats::new(stats),
|
||||
).unwrap());
|
||||
let port = res.addr().port() as usize;
|
||||
|
||||
(res, port, authcodes)
|
||||
}
|
||||
|
||||
@@ -218,9 +218,10 @@ impl<M: core::Middleware<Metadata>> WsDispatcher<M> {
|
||||
|
||||
impl<M: core::Middleware<Metadata>> core::Middleware<Metadata> for WsDispatcher<M> {
|
||||
type Future = Either<
|
||||
core::FutureRpcResult<M::Future>,
|
||||
core::FutureRpcResult<M::Future, M::CallFuture>,
|
||||
core::FutureResponse,
|
||||
>;
|
||||
type CallFuture = core::middleware::NoopCallFuture;
|
||||
|
||||
fn on_request<F, X>(&self, request: core::Request, meta: Metadata, process: F)
|
||||
-> Either<Self::Future, X>
|
||||
|
||||
@@ -49,7 +49,8 @@ use v1::types::{
|
||||
RichRawTransaction as RpcRichRawTransaction,
|
||||
ConfirmationPayload as RpcConfirmationPayload,
|
||||
ConfirmationResponse,
|
||||
SignRequest as RpcSignRequest,
|
||||
EthSignRequest as RpcEthSignRequest,
|
||||
EIP191SignRequest as RpcSignRequest,
|
||||
DecryptRequest as RpcDecryptRequest,
|
||||
};
|
||||
use rlp;
|
||||
@@ -693,6 +694,19 @@ pub fn execute<D: Dispatcher + 'static>(
|
||||
);
|
||||
Box::new(future::done(res))
|
||||
},
|
||||
ConfirmationPayload::SignMessage(address, data) => {
|
||||
if accounts.is_hardware_address(&address) {
|
||||
return Box::new(future::err(errors::account("Error signing message with hardware_wallet",
|
||||
"Message signing is unsupported")));
|
||||
}
|
||||
let res = signature(&accounts, address, data, pass)
|
||||
.map(|result| result
|
||||
.map(|rsv| H520(rsv.into_electrum()))
|
||||
.map(RpcH520::from)
|
||||
.map(ConfirmationResponse::Signature)
|
||||
);
|
||||
Box::new(future::done(res))
|
||||
},
|
||||
ConfirmationPayload::Decrypt(address, data) => {
|
||||
if accounts.is_hardware_address(&address) {
|
||||
return Box::new(future::err(errors::unsupported("Decrypting via hardware wallets is not supported.", None)));
|
||||
@@ -775,8 +789,11 @@ pub fn from_rpc<D>(payload: RpcConfirmationPayload, default_account: Address, di
|
||||
RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => {
|
||||
Box::new(future::ok(ConfirmationPayload::Decrypt(address.into(), msg.into())))
|
||||
},
|
||||
RpcConfirmationPayload::EthSignMessage(RpcSignRequest { address, data }) => {
|
||||
RpcConfirmationPayload::EthSignMessage(RpcEthSignRequest { address, data }) => {
|
||||
Box::new(future::ok(ConfirmationPayload::EthSignMessage(address.into(), data.into())))
|
||||
},
|
||||
RpcConfirmationPayload::EIP191SignMessage(RpcSignRequest { address, data }) => {
|
||||
Box::new(future::ok(ConfirmationPayload::SignMessage(address.into(), data.into())))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
61
rpc/src/v1/helpers/eip191.rs
Normal file
61
rpc/src/v1/helpers/eip191.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! EIP-191 compliant decoding + hashing
|
||||
use v1::types::{EIP191Version, Bytes, PresignedTransaction};
|
||||
use eip712::{hash_structured_data, EIP712};
|
||||
use serde_json::{Value, from_value};
|
||||
use v1::helpers::errors;
|
||||
use jsonrpc_core::Error;
|
||||
use v1::helpers::dispatch::eth_data_hash;
|
||||
use hash::keccak;
|
||||
use std::fmt::Display;
|
||||
use ethereum_types::H256;
|
||||
|
||||
/// deserializes and hashes the message depending on the version specifier
|
||||
pub fn hash_message(version: EIP191Version, message: Value) -> Result<H256, Error> {
|
||||
let data = match version {
|
||||
EIP191Version::StructuredData => {
|
||||
let typed_data = from_value::<EIP712>(message)
|
||||
.map_err(map_serde_err("StructuredData"))?;
|
||||
|
||||
hash_structured_data(typed_data)
|
||||
.map_err(|err| errors::invalid_call_data(err.kind()))?
|
||||
}
|
||||
|
||||
EIP191Version::PresignedTransaction => {
|
||||
let data = from_value::<PresignedTransaction>(message)
|
||||
.map_err(map_serde_err("WithValidator"))?;
|
||||
let prefix = b"\x19\x00";
|
||||
let data = [&prefix[..], &data.validator.0[..], &data.data.0[..]].concat();
|
||||
keccak(data)
|
||||
}
|
||||
|
||||
EIP191Version::PersonalMessage => {
|
||||
let bytes = from_value::<Bytes>(message)
|
||||
.map_err(map_serde_err("Bytes"))?;
|
||||
eth_data_hash(bytes.0)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
fn map_serde_err<T: Display>(struct_name: &'static str) -> impl Fn(T) -> Error {
|
||||
move |error: T| {
|
||||
errors::invalid_call_data(format!("Error deserializing '{}': {}", struct_name, error))
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ mod codes {
|
||||
pub const FETCH_ERROR: i64 = -32060;
|
||||
pub const NO_LIGHT_PEERS: i64 = -32065;
|
||||
pub const DEPRECATED: i64 = -32070;
|
||||
pub const EXPERIMENTAL_RPC: i64 = -32071;
|
||||
}
|
||||
|
||||
pub fn unimplemented(details: Option<String>) -> Error {
|
||||
@@ -286,6 +287,14 @@ pub fn signing(error: AccountError) -> Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalid_call_data<T: fmt::Display>(error: T) -> Error {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::ENCODING_ERROR),
|
||||
message: format!("{}", error),
|
||||
data: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn password(error: AccountError) -> Error {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::PASSWORD_INVALID),
|
||||
@@ -492,3 +501,15 @@ pub fn on_demand_others(err: &OnDemandError) -> Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a descriptive error in case experimental RPCs are not enabled.
|
||||
pub fn require_experimental(allow_experimental_rpcs: bool, eip: &str) -> Result<(), Error> {
|
||||
if allow_experimental_rpcs {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error {
|
||||
code: ErrorCode::ServerError(codes::EXPERIMENTAL_RPC),
|
||||
message: format!("This method is not part of the official RPC API yet (EIP-{}). Run with `--jsonrpc-experimental` to enable it.", eip),
|
||||
data: Some(Value::String(format!("See EIP: https://eips.ethereum.org/EIPS/eip-{}", eip))),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
|
||||
use light::on_demand::error::Error as OnDemandError;
|
||||
use ethcore::basic_account::BasicAccount;
|
||||
use ethcore::encoded;
|
||||
use ethcore::filter::Filter as EthcoreFilter;
|
||||
use ethcore::ids::BlockId;
|
||||
use ethcore::receipt::Receipt;
|
||||
use ethcore::executed::ExecutionError;
|
||||
|
||||
use jsonrpc_core::{Result, Error};
|
||||
use jsonrpc_core::futures::{future, Future};
|
||||
@@ -38,6 +38,7 @@ use light::on_demand::{
|
||||
request, OnDemand, HeaderRef, Request as OnDemandRequest,
|
||||
Response as OnDemandResponse, ExecutionResult,
|
||||
};
|
||||
use light::on_demand::error::Error as OnDemandError;
|
||||
use light::request::Field;
|
||||
|
||||
use sync::LightSync;
|
||||
@@ -45,6 +46,7 @@ use ethereum_types::{U256, Address};
|
||||
use hash::H256;
|
||||
use parking_lot::Mutex;
|
||||
use fastmap::H256FastMap;
|
||||
use std::collections::BTreeMap;
|
||||
use transaction::{Action, Transaction as EthTransaction, PendingTransaction, SignedTransaction, LocalizedTransaction};
|
||||
|
||||
use v1::helpers::{CallRequest as CallRequestHelper, errors, dispatch};
|
||||
@@ -202,8 +204,8 @@ impl LightFetch {
|
||||
/// Helper for getting proved execution.
|
||||
pub fn proved_read_only_execution(&self, req: CallRequest, num: Trailing<BlockNumber>) -> impl Future<Item = ExecutionResult, Error = Error> + Send {
|
||||
const DEFAULT_GAS_PRICE: u64 = 21_000;
|
||||
// starting gas when gas not provided.
|
||||
const START_GAS: u64 = 50_000;
|
||||
// (21000 G_transaction + 32000 G_create + some marginal to allow a few operations)
|
||||
const START_GAS: u64 = 60_000;
|
||||
|
||||
let (sync, on_demand, client) = (self.sync.clone(), self.on_demand.clone(), self.client.clone());
|
||||
let req: CallRequestHelper = req.into();
|
||||
@@ -309,9 +311,7 @@ impl LightFetch {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Get transaction logs
|
||||
pub fn logs(&self, filter: EthcoreFilter) -> impl Future<Item = Vec<Log>, Error = Error> + Send {
|
||||
use std::collections::BTreeMap;
|
||||
pub fn logs_no_tx_hash(&self, filter: EthcoreFilter) -> impl Future<Item = Vec<Log>, Error = Error> + Send {
|
||||
use jsonrpc_core::futures::stream::{self, Stream};
|
||||
|
||||
const MAX_BLOCK_RANGE: u64 = 1000;
|
||||
@@ -342,7 +342,7 @@ impl LightFetch {
|
||||
// insert them into a BTreeMap to maintain order by number and block index.
|
||||
stream::futures_unordered(receipts_futures)
|
||||
.fold(BTreeMap::new(), move |mut matches, (num, hash, receipts)| {
|
||||
let mut block_index = 0;
|
||||
let mut block_index: usize = 0;
|
||||
for (transaction_index, receipt) in receipts.into_iter().enumerate() {
|
||||
for (transaction_log_index, log) in receipt.logs.into_iter().enumerate() {
|
||||
if filter.matches(&log) {
|
||||
@@ -365,9 +365,9 @@ impl LightFetch {
|
||||
}
|
||||
}
|
||||
future::ok::<_,OnDemandError>(matches)
|
||||
}) // and then collect them into a vector.
|
||||
.map(|matches| matches.into_iter().map(|(_, v)| v).collect())
|
||||
})
|
||||
.map_err(errors::on_demand_error)
|
||||
.map(|matches| matches.into_iter().map(|(_, v)| v).collect())
|
||||
});
|
||||
|
||||
match maybe_future {
|
||||
@@ -377,6 +377,39 @@ impl LightFetch {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Get transaction logs
|
||||
pub fn logs(&self, filter: EthcoreFilter) -> impl Future<Item = Vec<Log>, Error = Error> + Send {
|
||||
use jsonrpc_core::futures::stream::{self, Stream};
|
||||
let fetcher_block = self.clone();
|
||||
self.logs_no_tx_hash(filter)
|
||||
// retrieve transaction hash.
|
||||
.and_then(move |mut result| {
|
||||
let mut blocks = BTreeMap::new();
|
||||
for log in result.iter() {
|
||||
let block_hash = log.block_hash.as_ref().expect("Previously initialized with value; qed");
|
||||
blocks.entry(block_hash.clone()).or_insert_with(|| {
|
||||
fetcher_block.block(BlockId::Hash(block_hash.clone().into()))
|
||||
});
|
||||
}
|
||||
// future get blocks (unordered it)
|
||||
stream::futures_unordered(blocks.into_iter().map(|(_, v)| v)).collect().map(move |blocks| {
|
||||
let transactions_per_block: BTreeMap<_, _> = blocks.iter()
|
||||
.map(|block| (block.hash(), block.transactions())).collect();
|
||||
for log in result.iter_mut() {
|
||||
let log_index: U256 = log.transaction_index.expect("Previously initialized with value; qed").into();
|
||||
let block_hash = log.block_hash.clone().expect("Previously initialized with value; qed").into();
|
||||
let tx_hash = transactions_per_block.get(&block_hash)
|
||||
// transaction index is from an enumerate call in log common so not need to check value
|
||||
.and_then(|txs| txs.get(log_index.as_usize()))
|
||||
.map(|tr| tr.hash().into());
|
||||
log.transaction_hash = tx_hash;
|
||||
}
|
||||
result
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Get a transaction by hash. also returns the index in the block.
|
||||
// Only returns transactions in the canonical chain.
|
||||
pub fn transaction_by_hash(&self, tx_hash: H256)
|
||||
@@ -615,28 +648,41 @@ struct ExecuteParams {
|
||||
sync: Arc<LightSync>,
|
||||
}
|
||||
|
||||
// has a peer execute the transaction with given params. If `gas_known` is false,
|
||||
// this will double the gas on each `OutOfGas` error.
|
||||
// Has a peer execute the transaction with given params. If `gas_known` is false, this will set the `gas value` to the
|
||||
// `required gas value` unless it exceeds the block gas limit
|
||||
fn execute_read_only_tx(gas_known: bool, params: ExecuteParams) -> impl Future<Item = ExecutionResult, Error = Error> + Send {
|
||||
if !gas_known {
|
||||
Box::new(future::loop_fn(params, |mut params| {
|
||||
execute_read_only_tx(true, params.clone()).and_then(move |res| {
|
||||
match res {
|
||||
Ok(executed) => {
|
||||
// TODO: how to distinguish between actual OOG and
|
||||
// exception?
|
||||
if executed.exception.is_some() {
|
||||
let old_gas = params.tx.gas;
|
||||
params.tx.gas = params.tx.gas * 2u32;
|
||||
if params.tx.gas > params.hdr.gas_limit() {
|
||||
params.tx.gas = old_gas;
|
||||
// `OutOfGas` exception, try double the gas
|
||||
if let Some(vm::Error::OutOfGas) = executed.exception {
|
||||
// block gas limit already tried, regard as an error and don't retry
|
||||
if params.tx.gas >= params.hdr.gas_limit() {
|
||||
trace!(target: "light_fetch", "OutOutGas exception received, gas increase: failed");
|
||||
} else {
|
||||
params.tx.gas = cmp::min(params.tx.gas * 2_u32, params.hdr.gas_limit());
|
||||
trace!(target: "light_fetch", "OutOutGas exception received, gas increased to {}",
|
||||
params.tx.gas);
|
||||
return Ok(future::Loop::Continue(params))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(future::Loop::Break(Ok(executed)))
|
||||
}
|
||||
Err(ExecutionError::NotEnoughBaseGas { required, got }) => {
|
||||
trace!(target: "light_fetch", "Not enough start gas provided required: {}, got: {}",
|
||||
required, got);
|
||||
if required <= params.hdr.gas_limit() {
|
||||
params.tx.gas = required;
|
||||
return Ok(future::Loop::Continue(params))
|
||||
} else {
|
||||
warn!(target: "light_fetch",
|
||||
"Required gas is bigger than block header's gas dropping the request");
|
||||
Ok(future::Loop::Break(Err(ExecutionError::NotEnoughBaseGas { required, got })))
|
||||
}
|
||||
}
|
||||
// Non-recoverable execution error
|
||||
failed => Ok(future::Loop::Break(failed)),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -25,6 +25,7 @@ pub mod light_fetch;
|
||||
pub mod nonce;
|
||||
pub mod oneshot;
|
||||
pub mod secretstore;
|
||||
pub mod eip191;
|
||||
|
||||
mod network_settings;
|
||||
mod poll_filter;
|
||||
|
||||
@@ -18,6 +18,7 @@ use ethereum_types::{U256, Address};
|
||||
use bytes::Bytes;
|
||||
|
||||
use v1::types::{Origin, TransactionCondition};
|
||||
use ethereum_types::H256;
|
||||
|
||||
/// Transaction request coming from RPC
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
||||
@@ -117,6 +118,8 @@ pub enum ConfirmationPayload {
|
||||
SignTransaction(FilledTransactionRequest),
|
||||
/// Sign a message with an Ethereum specific security prefix.
|
||||
EthSignMessage(Address, Bytes),
|
||||
/// Sign a message
|
||||
SignMessage(Address, H256),
|
||||
/// Decrypt request
|
||||
Decrypt(Address, Bytes),
|
||||
}
|
||||
@@ -127,6 +130,7 @@ impl ConfirmationPayload {
|
||||
ConfirmationPayload::SendTransaction(ref request) => request.from,
|
||||
ConfirmationPayload::SignTransaction(ref request) => request.from,
|
||||
ConfirmationPayload::EthSignMessage(ref address, _) => *address,
|
||||
ConfirmationPayload::SignMessage(ref address, _) => *address,
|
||||
ConfirmationPayload::Decrypt(ref address, _) => *address,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,33 @@ enum PendingTransactionId {
|
||||
Location(PendingOrBlock, usize)
|
||||
}
|
||||
|
||||
pub fn base_logs<C, M, T: StateInfo + 'static> (client: &C, miner: &M, filter: Filter) -> BoxFuture<Vec<Log>> where
|
||||
C: miner::BlockChainClient + BlockChainClient + StateClient<State=T> + Call<State=T>,
|
||||
M: MinerService<State=T> {
|
||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||
let filter: EthcoreFilter = match filter.try_into() {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Box::new(future::err(err)),
|
||||
};
|
||||
let mut logs = match client.logs(filter.clone()) {
|
||||
Ok(logs) => logs
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>(),
|
||||
Err(id) => return Box::new(future::err(errors::filter_block_not_found(id))),
|
||||
};
|
||||
|
||||
if include_pending {
|
||||
let best_block = client.chain_info().best_block_number;
|
||||
let pending = pending_logs(&*miner, best_block, &filter);
|
||||
logs.extend(pending);
|
||||
}
|
||||
|
||||
let logs = limit_logs(logs, filter.limit);
|
||||
|
||||
Box::new(future::ok(logs))
|
||||
}
|
||||
|
||||
impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> EthClient<C, SN, S, M, EM> where
|
||||
C: miner::BlockChainClient + BlockChainClient + StateClient<State=T> + Call<State=T> + EngineInfo,
|
||||
SN: SnapshotService,
|
||||
@@ -714,28 +741,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
|
||||
}
|
||||
|
||||
fn logs(&self, filter: Filter) -> BoxFuture<Vec<Log>> {
|
||||
let include_pending = filter.to_block == Some(BlockNumber::Pending);
|
||||
let filter: EthcoreFilter = match filter.try_into() {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Box::new(future::err(err)),
|
||||
};
|
||||
let mut logs = match self.client.logs(filter.clone()) {
|
||||
Ok(logs) => logs
|
||||
.into_iter()
|
||||
.map(From::from)
|
||||
.collect::<Vec<Log>>(),
|
||||
Err(id) => return Box::new(future::err(errors::filter_block_not_found(id))),
|
||||
};
|
||||
|
||||
if include_pending {
|
||||
let best_block = self.client.chain_info().best_block_number;
|
||||
let pending = pending_logs(&*self.miner, best_block, &filter);
|
||||
logs.extend(pending);
|
||||
}
|
||||
|
||||
let logs = limit_logs(logs, filter.limit);
|
||||
|
||||
Box::new(future::ok(logs))
|
||||
base_logs(&*self.client, &*self.miner, filter.into())
|
||||
}
|
||||
|
||||
fn work(&self, no_new_work_timeout: Trailing<u64>) -> Result<Work> {
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::collections::BTreeMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use jsonrpc_core::{BoxFuture, Result, Error};
|
||||
use jsonrpc_core::futures::{self, Future, IntoFuture};
|
||||
@@ -34,14 +33,13 @@ use v1::types::{pubsub, RichHeader, Log};
|
||||
|
||||
use ethcore::encoded;
|
||||
use ethcore::filter::Filter as EthFilter;
|
||||
use ethcore::client::{BlockChainClient, ChainNotify, ChainRoute, ChainRouteType, BlockId};
|
||||
use ethcore::client::{BlockChainClient, ChainNotify, NewBlocks, ChainRouteType, BlockId};
|
||||
use sync::LightSync;
|
||||
use light::cache::Cache;
|
||||
use light::on_demand::OnDemand;
|
||||
use light::client::{LightChainClient, LightChainNotify};
|
||||
use parity_runtime::Executor;
|
||||
use ethereum_types::H256;
|
||||
use bytes::Bytes;
|
||||
use parking_lot::{RwLock, Mutex};
|
||||
|
||||
type Client = Sink<pubsub::Result>;
|
||||
@@ -220,18 +218,10 @@ impl<C: LightClient> LightChainNotify for ChainNotificationHandler<C> {
|
||||
}
|
||||
|
||||
impl<C: BlockChainClient> ChainNotify for ChainNotificationHandler<C> {
|
||||
fn new_blocks(
|
||||
&self,
|
||||
_imported: Vec<H256>,
|
||||
_invalid: Vec<H256>,
|
||||
route: ChainRoute,
|
||||
_sealed: Vec<H256>,
|
||||
// Block bytes.
|
||||
_proposed: Vec<Bytes>,
|
||||
_duration: Duration,
|
||||
) {
|
||||
fn new_blocks(&self, new_blocks: NewBlocks) {
|
||||
if self.heads_subscribers.read().is_empty() && self.logs_subscribers.read().is_empty() { return }
|
||||
const EXTRA_INFO_PROOF: &'static str = "Object exists in in blockchain (fetched earlier), extra_info is always available if object exists; qed";
|
||||
let headers = route.route()
|
||||
let headers = new_blocks.route.route()
|
||||
.iter()
|
||||
.filter_map(|&(hash, ref typ)| {
|
||||
match typ {
|
||||
@@ -249,7 +239,7 @@ impl<C: BlockChainClient> ChainNotify for ChainNotificationHandler<C> {
|
||||
self.notify_heads(&headers);
|
||||
|
||||
// We notify logs enacting and retracting as the order in route.
|
||||
self.notify_logs(route.route(), |filter, ex| {
|
||||
self.notify_logs(new_blocks.route.route(), |filter, ex| {
|
||||
match ex {
|
||||
&ChainRouteType::Enacted =>
|
||||
Ok(self.client.logs(filter).unwrap_or_default().into_iter().map(Into::into).collect()),
|
||||
|
||||
@@ -28,7 +28,7 @@ use ethcore::account_provider::AccountProvider;
|
||||
use ethcore_logger::RotatingLogger;
|
||||
|
||||
use jsonrpc_core::{Result, BoxFuture};
|
||||
use jsonrpc_core::futures::Future;
|
||||
use jsonrpc_core::futures::{future, Future};
|
||||
use jsonrpc_macros::Trailing;
|
||||
use v1::helpers::{self, errors, ipfs, SigningQueue, SignerService, NetworkSettings};
|
||||
use v1::helpers::dispatch::LightDispatcher;
|
||||
@@ -42,6 +42,7 @@ use v1::types::{
|
||||
BlockNumber, LightBlockNumber, ConsensusCapability, VersionInfo,
|
||||
OperationsInfo, ChainStatus,
|
||||
AccountInfo, HwAccountInfo, Header, RichHeader, Receipt,
|
||||
Log, Filter,
|
||||
};
|
||||
use Host;
|
||||
|
||||
@@ -414,4 +415,13 @@ impl Parity for ParityClient {
|
||||
fn submit_work_detail(&self, _nonce: H64, _pow_hash: H256, _mix_hash: H256) -> Result<H256> {
|
||||
Err(errors::light_unimplemented(None))
|
||||
}
|
||||
|
||||
|
||||
fn logs_no_tx_hash(&self, filter: Filter) -> BoxFuture<Vec<Log>> {
|
||||
let filter = match filter.try_into() {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Box::new(future::err(err)),
|
||||
};
|
||||
Box::new(self.fetcher().logs_no_tx_hash(filter)) as BoxFuture<_>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ use v1::types::{
|
||||
Peers, Transaction, RpcSettings, Histogram,
|
||||
TransactionStats, LocalTransactionStatus,
|
||||
BlockNumber, ConsensusCapability, VersionInfo,
|
||||
OperationsInfo, ChainStatus,
|
||||
OperationsInfo, ChainStatus, Log, Filter,
|
||||
AccountInfo, HwAccountInfo, RichHeader, Receipt,
|
||||
block_number_to_id
|
||||
};
|
||||
@@ -434,7 +434,7 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
};
|
||||
let receipts = try_bf!(self.client.block_receipts(id).ok_or_else(errors::unknown_block));
|
||||
let receipts = try_bf!(self.client.localized_block_receipts(id).ok_or_else(errors::unknown_block));
|
||||
Box::new(future::ok(receipts.into_iter().map(Into::into).collect()))
|
||||
}
|
||||
|
||||
@@ -481,4 +481,10 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
||||
fn submit_work_detail(&self, nonce: H64, pow_hash: H256, mix_hash: H256) -> Result<H256> {
|
||||
helpers::submit_work_detail(&self.client, &self.miner, nonce, pow_hash, mix_hash)
|
||||
}
|
||||
|
||||
fn logs_no_tx_hash(&self, filter: Filter) -> BoxFuture<Vec<Log>> {
|
||||
use v1::impls::eth::base_logs;
|
||||
// only specific impl for lightclient
|
||||
base_logs(&*self.client, &*self.miner, filter.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ use ethkey::{public_to_address, recover, Signature};
|
||||
|
||||
use jsonrpc_core::{BoxFuture, Result};
|
||||
use jsonrpc_core::futures::{future, Future};
|
||||
use v1::helpers::errors;
|
||||
use v1::helpers::{errors, eip191};
|
||||
use v1::helpers::dispatch::{self, eth_data_hash, Dispatcher, SignWith};
|
||||
use v1::traits::Personal;
|
||||
use v1::types::{
|
||||
@@ -36,23 +36,33 @@ use v1::types::{
|
||||
ConfirmationResponse as RpcConfirmationResponse,
|
||||
TransactionRequest,
|
||||
RichRawTransaction as RpcRichRawTransaction,
|
||||
EIP191Version,
|
||||
};
|
||||
use v1::metadata::Metadata;
|
||||
use eip712::{EIP712, hash_structured_data};
|
||||
use jsonrpc_core::types::Value;
|
||||
|
||||
/// Account management (personal) rpc implementation.
|
||||
pub struct PersonalClient<D: Dispatcher> {
|
||||
accounts: Arc<AccountProvider>,
|
||||
dispatcher: D,
|
||||
allow_perm_unlock: bool,
|
||||
allow_experimental_rpcs: bool,
|
||||
}
|
||||
|
||||
impl<D: Dispatcher> PersonalClient<D> {
|
||||
/// Creates new PersonalClient
|
||||
pub fn new(accounts: &Arc<AccountProvider>, dispatcher: D, allow_perm_unlock: bool) -> Self {
|
||||
pub fn new(
|
||||
accounts: &Arc<AccountProvider>,
|
||||
dispatcher: D,
|
||||
allow_perm_unlock: bool,
|
||||
allow_experimental_rpcs: bool,
|
||||
) -> Self {
|
||||
PersonalClient {
|
||||
accounts: accounts.clone(),
|
||||
dispatcher,
|
||||
allow_perm_unlock,
|
||||
allow_experimental_rpcs,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,6 +160,53 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
|
||||
}))
|
||||
}
|
||||
|
||||
fn sign_191(&self, version: EIP191Version, data: Value, account: RpcH160, password: String) -> BoxFuture<RpcH520> {
|
||||
try_bf!(errors::require_experimental(self.allow_experimental_rpcs, "191"));
|
||||
|
||||
let data = try_bf!(eip191::hash_message(version, data));
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
let accounts = self.accounts.clone();
|
||||
|
||||
let payload = RpcConfirmationPayload::EIP191SignMessage((account.clone(), data.into()).into());
|
||||
|
||||
Box::new(dispatch::from_rpc(payload, account.into(), &dispatcher)
|
||||
.and_then(|payload| {
|
||||
dispatch::execute(dispatcher, accounts, payload, dispatch::SignWith::Password(password.into()))
|
||||
})
|
||||
.map(|v| v.into_value())
|
||||
.then(|res| match res {
|
||||
Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature),
|
||||
Err(e) => Err(e),
|
||||
e => Err(errors::internal("Unexpected result", e)),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn sign_typed_data(&self, typed_data: EIP712, account: RpcH160, password: String) -> BoxFuture<RpcH520> {
|
||||
try_bf!(errors::require_experimental(self.allow_experimental_rpcs, "712"));
|
||||
|
||||
let data = match hash_structured_data(typed_data) {
|
||||
Ok(d) => d,
|
||||
Err(err) => return Box::new(future::err(errors::invalid_call_data(err.kind()))),
|
||||
};
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
let accounts = self.accounts.clone();
|
||||
|
||||
let payload = RpcConfirmationPayload::EIP191SignMessage((account.clone(), data.into()).into());
|
||||
|
||||
Box::new(dispatch::from_rpc(payload, account.into(), &dispatcher)
|
||||
.and_then(|payload| {
|
||||
dispatch::execute(dispatcher, accounts, payload, dispatch::SignWith::Password(password.into()))
|
||||
})
|
||||
.map(|v| v.into_value())
|
||||
.then(|res| match res {
|
||||
Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature),
|
||||
Err(e) => Err(e),
|
||||
e => Err(errors::internal("Unexpected result", e)),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn ec_recover(&self, data: RpcBytes, signature: RpcH520) -> BoxFuture<RpcH160> {
|
||||
let signature: H520 = signature.into();
|
||||
let signature = Signature::from_electrum(&signature);
|
||||
|
||||
@@ -21,7 +21,7 @@ use std::time::Duration;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use jsonrpc_core::{self as core, Result, MetaIoHandler};
|
||||
use jsonrpc_core::futures::{Future, Stream, Sink};
|
||||
use jsonrpc_core::futures::{future, Future, Stream, Sink};
|
||||
use jsonrpc_macros::Trailing;
|
||||
use jsonrpc_macros::pubsub::Subscriber;
|
||||
use jsonrpc_pubsub::SubscriptionId;
|
||||
@@ -42,7 +42,7 @@ impl<S: core::Middleware<Metadata>> PubSubClient<S> {
|
||||
/// Creates new `PubSubClient`.
|
||||
pub fn new(rpc: MetaIoHandler<Metadata, S>, executor: Executor) -> Self {
|
||||
let poll_manager = Arc::new(RwLock::new(GenericPollManager::new(rpc)));
|
||||
let pm2 = poll_manager.clone();
|
||||
let pm2 = Arc::downgrade(&poll_manager);
|
||||
|
||||
let timer = tokio_timer::wheel()
|
||||
.tick_duration(Duration::from_millis(500))
|
||||
@@ -52,7 +52,13 @@ impl<S: core::Middleware<Metadata>> PubSubClient<S> {
|
||||
let interval = timer.interval(Duration::from_millis(1000));
|
||||
executor.spawn(interval
|
||||
.map_err(|e| warn!("Polling timer error: {:?}", e))
|
||||
.for_each(move |_| pm2.read().tick())
|
||||
.for_each(move |_| {
|
||||
if let Some(pm2) = pm2.upgrade() {
|
||||
pm2.read().tick()
|
||||
} else {
|
||||
Box::new(future::err(()))
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
PubSubClient {
|
||||
|
||||
@@ -215,6 +215,14 @@ impl<D: Dispatcher + 'static> Signer for SignerClient<D> {
|
||||
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())),
|
||||
Ok(false) => Err(errors::invalid_params("Sender address does not match the signature.", ())),
|
||||
Err(err) => Err(errors::invalid_params("Invalid signature received.", err)),
|
||||
}
|
||||
},
|
||||
ConfirmationPayload::Decrypt(_address, _data) => {
|
||||
// TODO [ToDr]: Decrypt can we verify if the answer is correct?
|
||||
Ok(ConfirmationResponse::Decrypt(bytes))
|
||||
|
||||
@@ -205,6 +205,7 @@ impl<T: ActivityNotifier> Middleware<T> {
|
||||
|
||||
impl<M: core::Metadata, T: ActivityNotifier> core::Middleware<M> for Middleware<T> {
|
||||
type Future = core::FutureResponse;
|
||||
type CallFuture = core::middleware::NoopCallFuture;
|
||||
|
||||
fn on_request<F, X>(&self, request: core::Request, meta: M, process: F) -> Either<Self::Future, X> where
|
||||
F: FnOnce(core::Request, M) -> X,
|
||||
|
||||
@@ -243,6 +243,7 @@ const TRANSACTION_COUNT_SPEC: &'static [u8] = br#"{
|
||||
"params": {
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"durationLimit": "0x0d",
|
||||
"homesteadTransition": "0xffffffffffffffff",
|
||||
"daoHardforkTransition": "0xffffffffffffffff",
|
||||
@@ -253,7 +254,6 @@ const TRANSACTION_COUNT_SPEC: &'static [u8] = br#"{
|
||||
},
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"accountStartNonce": "0x00",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
@@ -292,6 +292,7 @@ const POSITIVE_NONCE_SPEC: &'static [u8] = br#"{
|
||||
"minimumDifficulty": "0x020000",
|
||||
"difficultyBoundDivisor": "0x0800",
|
||||
"durationLimit": "0x0d",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"homesteadTransition": "0xffffffffffffffff",
|
||||
"daoHardforkTransition": "0xffffffffffffffff",
|
||||
"daoHardforkBeneficiary": "0x0000000000000000000000000000000000000000",
|
||||
@@ -301,7 +302,6 @@ const POSITIVE_NONCE_SPEC: &'static [u8] = br#"{
|
||||
},
|
||||
"params": {
|
||||
"gasLimitBoundDivisor": "0x0400",
|
||||
"blockReward": "0x4563918244F40000",
|
||||
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
|
||||
"accountStartNonce": "0x0100",
|
||||
"maximumExtraDataSize": "0x20",
|
||||
|
||||
@@ -24,7 +24,7 @@ use std::time::Duration;
|
||||
|
||||
use v1::{EthPubSub, EthPubSubClient, Metadata};
|
||||
|
||||
use ethcore::client::{TestBlockChainClient, EachBlockWith, ChainNotify, ChainRoute, ChainRouteType};
|
||||
use ethcore::client::{TestBlockChainClient, EachBlockWith, ChainNotify, NewBlocks, ChainRoute, ChainRouteType};
|
||||
use parity_runtime::Runtime;
|
||||
|
||||
const DURATION_ZERO: Duration = Duration::from_millis(0);
|
||||
@@ -57,13 +57,13 @@ fn should_subscribe_to_new_heads() {
|
||||
assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned()));
|
||||
|
||||
// Check notifications
|
||||
handler.new_blocks(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO);
|
||||
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"}}"#;
|
||||
assert_eq!(res, Some(response.into()));
|
||||
|
||||
// Notify about two blocks
|
||||
handler.new_blocks(vec![], vec![], ChainRoute::new(vec![(h2, ChainRouteType::Enacted), (h3, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO);
|
||||
handler.new_blocks(NewBlocks::new(vec![], vec![], ChainRoute::new(vec![(h2, ChainRouteType::Enacted), (h3, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO, true));
|
||||
|
||||
// Receive both
|
||||
let (res, receiver) = receiver.into_future().wait().unwrap();
|
||||
@@ -129,7 +129,7 @@ fn should_subscribe_to_logs() {
|
||||
assert_eq!(io.handle_request_sync(request, metadata.clone()), Some(response.to_owned()));
|
||||
|
||||
// Check notifications (enacted)
|
||||
handler.new_blocks(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO);
|
||||
handler.new_blocks(NewBlocks::new(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]), vec![], vec![], DURATION_ZERO, false));
|
||||
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)
|
||||
@@ -137,7 +137,7 @@ fn should_subscribe_to_logs() {
|
||||
assert_eq!(res, Some(response.into()));
|
||||
|
||||
// Check notifications (retracted)
|
||||
handler.new_blocks(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Retracted)]), vec![], vec![], DURATION_ZERO);
|
||||
handler.new_blocks(NewBlocks::new(vec![], vec![], ChainRoute::new(vec![(h1, ChainRouteType::Retracted)]), vec![], vec![], DURATION_ZERO, false));
|
||||
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)
|
||||
|
||||
@@ -25,12 +25,16 @@ use jsonrpc_core::IoHandler;
|
||||
use parking_lot::Mutex;
|
||||
use transaction::{Action, Transaction};
|
||||
use parity_runtime::Runtime;
|
||||
use hash::keccak;
|
||||
|
||||
use v1::{PersonalClient, Personal, Metadata};
|
||||
use v1::helpers::nonce;
|
||||
use v1::helpers::{nonce, eip191};
|
||||
use v1::helpers::dispatch::{eth_data_hash, FullDispatcher};
|
||||
use v1::tests::helpers::TestMinerService;
|
||||
use v1::types::H520;
|
||||
use v1::types::{EIP191Version, PresignedTransaction, H520};
|
||||
use rustc_hex::ToHex;
|
||||
use serde_json::to_value;
|
||||
use ethkey::Secret;
|
||||
|
||||
struct PersonalTester {
|
||||
_runtime: Runtime,
|
||||
@@ -53,6 +57,16 @@ fn miner_service() -> Arc<TestMinerService> {
|
||||
}
|
||||
|
||||
fn setup() -> PersonalTester {
|
||||
setup_with(Config {
|
||||
allow_experimental_rpcs: true
|
||||
})
|
||||
}
|
||||
|
||||
struct Config {
|
||||
pub allow_experimental_rpcs: bool,
|
||||
}
|
||||
|
||||
fn setup_with(c: Config) -> PersonalTester {
|
||||
let runtime = Runtime::with_thread_count(1);
|
||||
let accounts = accounts_provider();
|
||||
let client = blockchain_client();
|
||||
@@ -60,7 +74,7 @@ fn setup() -> PersonalTester {
|
||||
let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
|
||||
|
||||
let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50);
|
||||
let personal = PersonalClient::new(&accounts, dispatcher, false);
|
||||
let personal = PersonalClient::new(&accounts, dispatcher, false, c.allow_experimental_rpcs);
|
||||
|
||||
let mut io = IoHandler::default();
|
||||
io.extend_with(personal.to_delegate());
|
||||
@@ -328,3 +342,195 @@ fn should_unlock_account_permanently() {
|
||||
assert_eq!(tester.io.handle_request_sync(&request), Some(response.into()));
|
||||
assert!(tester.accounts.sign(address, None, Default::default()).is_ok(), "Should unlock account.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_eip191_with_validator() {
|
||||
let tester = setup();
|
||||
let address = tester.accounts.new_account(&"password123".into()).unwrap();
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "personal_sign191",
|
||||
"params": [
|
||||
"0x00",
|
||||
{
|
||||
"validator": ""#.to_owned() + &format!("0x{:x}", address) + r#"",
|
||||
"data": ""# + &format!("0x{:x}", keccak("hello world")) + r#""
|
||||
},
|
||||
""# + &format!("0x{:x}", address) + r#"",
|
||||
"password123"
|
||||
],
|
||||
"id": 1
|
||||
}"#;
|
||||
let with_validator = to_value(PresignedTransaction {
|
||||
validator: address.into(),
|
||||
data: keccak("hello world").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();
|
||||
let expected = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{}", result.to_hex()) + r#"","id":1}"#;
|
||||
let response = tester.io.handle_request_sync(&request).unwrap();
|
||||
assert_eq!(response, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_eip191_structured_data() {
|
||||
let tester = setup();
|
||||
let secret: Secret = keccak("cow").into();
|
||||
let address = tester.accounts.insert_account(secret, &"lol".into()).unwrap();
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "personal_sign191",
|
||||
"params": [
|
||||
"0x01",
|
||||
{
|
||||
"primaryType": "Mail",
|
||||
"domain": {
|
||||
"name": "Ether Mail",
|
||||
"version": "1",
|
||||
"chainId": "0x1",
|
||||
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||
},
|
||||
"message": {
|
||||
"from": {
|
||||
"name": "Cow",
|
||||
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
|
||||
},
|
||||
"to": {
|
||||
"name": "Bob",
|
||||
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
|
||||
},
|
||||
"contents": "Hello, Bob!"
|
||||
},
|
||||
"types": {
|
||||
"EIP712Domain": [
|
||||
{ "name": "name", "type": "string" },
|
||||
{ "name": "version", "type": "string" },
|
||||
{ "name": "chainId", "type": "uint256" },
|
||||
{ "name": "verifyingContract", "type": "address" }
|
||||
],
|
||||
"Person": [
|
||||
{ "name": "name", "type": "string" },
|
||||
{ "name": "wallet", "type": "address" }
|
||||
],
|
||||
"Mail": [
|
||||
{ "name": "from", "type": "Person" },
|
||||
{ "name": "to", "type": "Person" },
|
||||
{ "name": "contents", "type": "string" }
|
||||
]
|
||||
}
|
||||
},
|
||||
""#.to_owned() + &format!("0x{:x}", address) + r#"",
|
||||
"lol"
|
||||
],
|
||||
"id": 1
|
||||
}"#;
|
||||
let expected = r#"{"jsonrpc":"2.0","result":"0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c","id":1}"#;
|
||||
let response = tester.io.handle_request_sync(&request).unwrap();
|
||||
assert_eq!(response, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_structured_data() {
|
||||
let tester = setup();
|
||||
let secret: Secret = keccak("cow").into();
|
||||
let address = tester.accounts.insert_account(secret, &"lol".into()).unwrap();
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "personal_signTypedData",
|
||||
"params": [
|
||||
{
|
||||
"primaryType": "Mail",
|
||||
"domain": {
|
||||
"name": "Ether Mail",
|
||||
"version": "1",
|
||||
"chainId": "0x1",
|
||||
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||
},
|
||||
"message": {
|
||||
"from": {
|
||||
"name": "Cow",
|
||||
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
|
||||
},
|
||||
"to": {
|
||||
"name": "Bob",
|
||||
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
|
||||
},
|
||||
"contents": "Hello, Bob!"
|
||||
},
|
||||
"types": {
|
||||
"EIP712Domain": [
|
||||
{ "name": "name", "type": "string" },
|
||||
{ "name": "version", "type": "string" },
|
||||
{ "name": "chainId", "type": "uint256" },
|
||||
{ "name": "verifyingContract", "type": "address" }
|
||||
],
|
||||
"Person": [
|
||||
{ "name": "name", "type": "string" },
|
||||
{ "name": "wallet", "type": "address" }
|
||||
],
|
||||
"Mail": [
|
||||
{ "name": "from", "type": "Person" },
|
||||
{ "name": "to", "type": "Person" },
|
||||
{ "name": "contents", "type": "string" }
|
||||
]
|
||||
}
|
||||
},
|
||||
""#.to_owned() + &format!("0x{:x}", address) + r#"",
|
||||
"lol"
|
||||
],
|
||||
"id": 1
|
||||
}"#;
|
||||
let expected = r#"{"jsonrpc":"2.0","result":"0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c","id":1}"#;
|
||||
let response = tester.io.handle_request_sync(&request).unwrap();
|
||||
assert_eq!(response, expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_disable_experimental_apis() {
|
||||
// given
|
||||
let tester = setup_with(Config {
|
||||
allow_experimental_rpcs: false,
|
||||
});
|
||||
|
||||
// when
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "personal_sign191",
|
||||
"params": [
|
||||
"0x01",
|
||||
{},
|
||||
"0x1234567891234567891234567891234567891234",
|
||||
"lol"
|
||||
],
|
||||
"id": 1
|
||||
}"#;
|
||||
let r1 = tester.io.handle_request_sync(&request).unwrap();
|
||||
let request = r#"{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "personal_signTypedData",
|
||||
"params": [
|
||||
{
|
||||
"types": {},
|
||||
"message": {},
|
||||
"domain": {
|
||||
"name": "",
|
||||
"version": "1",
|
||||
"chainId": "0x1",
|
||||
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||
},
|
||||
"primaryType": ""
|
||||
},
|
||||
"0x1234567891234567891234567891234678912344",
|
||||
"lol"
|
||||
],
|
||||
"id": 1
|
||||
}"#;
|
||||
let r2 = tester.io.handle_request_sync(&request).unwrap();
|
||||
|
||||
// then
|
||||
let expected = r#"{"jsonrpc":"2.0","error":{"code":-32071,"message":"This method is not part of the official RPC API yet (EIP-191). Run with `--jsonrpc-experimental` to enable it.","data":"See EIP: https://eips.ethereum.org/EIPS/eip-191"},"id":1}"#;
|
||||
assert_eq!(r1, expected);
|
||||
|
||||
let expected = r#"{"jsonrpc":"2.0","error":{"code":-32071,"message":"This method is not part of the official RPC API yet (EIP-712). Run with `--jsonrpc-experimental` to enable it.","data":"See EIP: https://eips.ethereum.org/EIPS/eip-712"},"id":1}"#;
|
||||
assert_eq!(r2, expected);
|
||||
}
|
||||
|
||||
@@ -20,13 +20,12 @@ use std::collections::BTreeMap;
|
||||
|
||||
use jsonrpc_core::{BoxFuture, Result};
|
||||
use jsonrpc_macros::Trailing;
|
||||
|
||||
use v1::types::{
|
||||
H64, H160, H256, H512, U256, Bytes, CallRequest,
|
||||
Peers, Transaction, RpcSettings, Histogram,
|
||||
TransactionStats, LocalTransactionStatus,
|
||||
BlockNumber, ConsensusCapability, VersionInfo,
|
||||
OperationsInfo, ChainStatus,
|
||||
OperationsInfo, ChainStatus, Log, Filter,
|
||||
AccountInfo, HwAccountInfo, RichHeader, Receipt,
|
||||
};
|
||||
|
||||
@@ -227,5 +226,11 @@ build_rpc_trait! {
|
||||
/// 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<H256>;
|
||||
|
||||
/// 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<Vec<Log>>;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Personal rpc interface.
|
||||
use eip712::EIP712;
|
||||
use jsonrpc_core::types::Value;
|
||||
use jsonrpc_core::{BoxFuture, Result};
|
||||
|
||||
use v1::types::{Bytes, U128, H160, H256, H520, TransactionRequest, RichRawTransaction as RpcRichRawTransaction};
|
||||
use v1::types::{Bytes, U128, H160, H256, H520, TransactionRequest, RichRawTransaction as RpcRichRawTransaction, EIP191Version};
|
||||
|
||||
build_rpc_trait! {
|
||||
/// Personal rpc interface. Safe (read-only) functions.
|
||||
@@ -42,6 +43,15 @@ build_rpc_trait! {
|
||||
#[rpc(name = "personal_sign")]
|
||||
fn sign(&self, Bytes, H160, String) -> BoxFuture<H520>;
|
||||
|
||||
/// 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<H520>;
|
||||
|
||||
/// Signs an arbitrary message based on the version specified
|
||||
#[rpc(name = "personal_sign191")]
|
||||
fn sign_191(&self, EIP191Version, Value, H160, String) -> BoxFuture<H520>;
|
||||
|
||||
/// Returns the account associated with the private key that was used to calculate the signature in
|
||||
/// `personal_sign`.
|
||||
#[rpc(name = "personal_ecRecover")]
|
||||
|
||||
@@ -59,31 +59,62 @@ impl fmt::Display for ConfirmationPayload {
|
||||
ConfirmationPayload::SendTransaction(ref transaction) => write!(f, "{}", transaction),
|
||||
ConfirmationPayload::SignTransaction(ref transaction) => write!(f, "(Sign only) {}", transaction),
|
||||
ConfirmationPayload::EthSignMessage(ref sign) => write!(f, "{}", sign),
|
||||
ConfirmationPayload::EIP191SignMessage(ref sign) => write!(f, "{}", sign),
|
||||
ConfirmationPayload::Decrypt(ref decrypt) => write!(f, "{}", decrypt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign request
|
||||
/// Ethereum-prefixed Sign request
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct SignRequest {
|
||||
pub struct EthSignRequest {
|
||||
/// Address
|
||||
pub address: H160,
|
||||
/// Hash to sign
|
||||
pub data: Bytes,
|
||||
}
|
||||
|
||||
impl From<(H160, Bytes)> for SignRequest {
|
||||
fn from(tuple: (H160, Bytes)) -> Self {
|
||||
SignRequest {
|
||||
/// EIP191 Sign request
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct EIP191SignRequest {
|
||||
/// Address
|
||||
pub address: H160,
|
||||
/// Hash to sign
|
||||
pub data: H256,
|
||||
}
|
||||
|
||||
impl From<(H160, H256)> for EIP191SignRequest {
|
||||
fn from(tuple: (H160, H256)) -> Self {
|
||||
EIP191SignRequest {
|
||||
address: tuple.0,
|
||||
data: tuple.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SignRequest {
|
||||
impl fmt::Display for EIP191SignRequest {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"sign 0x{} with {}",
|
||||
self.data.0.pretty(),
|
||||
Colour::White.bold().paint(format!("0x{:?}", self.address)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(H160, Bytes)> for EthSignRequest {
|
||||
fn from(tuple: (H160, Bytes)) -> Self {
|
||||
EthSignRequest {
|
||||
address: tuple.0,
|
||||
data: tuple.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EthSignRequest {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
@@ -169,8 +200,10 @@ pub enum ConfirmationPayload {
|
||||
#[serde(rename="signTransaction")]
|
||||
SignTransaction(TransactionRequest),
|
||||
/// Signature
|
||||
#[serde(rename="sign")]
|
||||
EthSignMessage(SignRequest),
|
||||
#[serde(rename = "sign")]
|
||||
EthSignMessage(EthSignRequest),
|
||||
/// signature without prefix
|
||||
EIP191SignMessage(EIP191SignRequest),
|
||||
/// Decryption
|
||||
#[serde(rename="decrypt")]
|
||||
Decrypt(DecryptRequest),
|
||||
@@ -181,7 +214,11 @@ impl From<helpers::ConfirmationPayload> for ConfirmationPayload {
|
||||
match c {
|
||||
helpers::ConfirmationPayload::SendTransaction(t) => ConfirmationPayload::SendTransaction(t.into()),
|
||||
helpers::ConfirmationPayload::SignTransaction(t) => ConfirmationPayload::SignTransaction(t.into()),
|
||||
helpers::ConfirmationPayload::EthSignMessage(address, data) => ConfirmationPayload::EthSignMessage(SignRequest {
|
||||
helpers::ConfirmationPayload::EthSignMessage(address, data) => ConfirmationPayload::EthSignMessage(EthSignRequest {
|
||||
address: address.into(),
|
||||
data: data.into(),
|
||||
}),
|
||||
helpers::ConfirmationPayload::SignMessage(address, data) => ConfirmationPayload::EIP191SignMessage(EIP191SignRequest {
|
||||
address: address.into(),
|
||||
data: data.into(),
|
||||
}),
|
||||
|
||||
57
rpc/src/v1/types/eip191.rs
Normal file
57
rpc/src/v1/types/eip191.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! EIP-191 specific types
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde::de;
|
||||
use v1::types::{H160, Bytes};
|
||||
|
||||
/// EIP-191 version specifier
|
||||
#[derive(Debug)]
|
||||
pub enum EIP191Version {
|
||||
/// byte specifier for structured data (0x01)
|
||||
StructuredData,
|
||||
/// byte specifier for personal message (0x45)
|
||||
PersonalMessage,
|
||||
/// byte specifier for presignedtransaction (0x00)
|
||||
PresignedTransaction
|
||||
}
|
||||
|
||||
/// EIP-191 version 0x0 struct
|
||||
#[derive(Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PresignedTransaction {
|
||||
// address of intended validator
|
||||
pub validator: H160,
|
||||
// application specific data
|
||||
pub data: Bytes
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for EIP191Version {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let byte_version = match s.as_str() {
|
||||
"0x00" => EIP191Version::PresignedTransaction,
|
||||
"0x01" => EIP191Version::StructuredData,
|
||||
"0x45" => EIP191Version::PersonalMessage,
|
||||
other => return Err(de::Error::custom(format!("Invalid byte version '{}'", other))),
|
||||
};
|
||||
Ok(byte_version)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user