Merge remote-tracking branch 'origin/master' into eip-213

This commit is contained in:
NikVolf 2017-03-27 18:18:22 +03:00
commit efe0f8449c
265 changed files with 6912 additions and 4505 deletions

441
Cargo.lock generated
View File

@ -2,10 +2,10 @@
name = "parity"
version = "1.7.0"
dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)",
"ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)",
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -27,9 +27,9 @@ dependencies = [
"ethsync 1.7.0",
"evmbin 0.1.0",
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -39,6 +39,7 @@ dependencies = [
"parity-reactor 0.1.0",
"parity-rpc-client 1.4.0",
"parity-updater 1.7.0",
"path 0.1.0",
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.1.0",
"rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -51,6 +52,7 @@ dependencies = [
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -72,7 +74,7 @@ dependencies = [
[[package]]
name = "ansi_term"
version = "0.7.2"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -93,10 +95,10 @@ dependencies = [
[[package]]
name = "arrayvec"
version = "0.3.16"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nodrop 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -203,6 +205,15 @@ dependencies = [
"stable-heap 0.1.0 (git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47)",
]
[[package]]
name = "bytes"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cfg-if"
version = "0.1.0"
@ -292,7 +303,7 @@ dependencies = [
[[package]]
name = "ctrlc"
version = "1.1.1"
source = "git+https://github.com/ethcore/rust-ctrlc.git#f4927770f89eca80ec250911eea3adcbf579ac48"
source = "git+https://github.com/paritytech/rust-ctrlc.git#b523017108bb2d571a7a69bd97bc406e63bc7a9d"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
@ -357,7 +368,7 @@ name = "eth-secp256k1"
version = "0.5.6"
source = "git+https://github.com/ethcore/rust-secp256k1#98ad9b9ecae44a563efdd64273bcebc6b4ed81c6"
dependencies = [
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
"gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@ -405,6 +416,7 @@ dependencies = [
"ethcore-ipc 1.7.0",
"ethcore-ipc-codegen 1.7.0",
"ethcore-ipc-nano 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-stratum 1.7.0",
"ethcore-util 1.7.0",
"ethjson 0.1.0",
@ -412,7 +424,8 @@ dependencies = [
"ethstore 0.1.0",
"evmjit 1.7.0",
"hardware-wallet 1.7.0",
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -458,10 +471,10 @@ dependencies = [
"ethcore-rpc 1.7.0",
"ethcore-util 1.7.0",
"fetch 0.1.0",
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -567,7 +580,7 @@ dependencies = [
"ethcore-ipc-codegen 1.7.0",
"ethcore-network 1.7.0",
"ethcore-util 1.7.0",
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@ -581,11 +594,13 @@ dependencies = [
name = "ethcore-logger"
version = "1.7.0"
dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.0",
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -594,10 +609,11 @@ dependencies = [
name = "ethcore-network"
version = "1.7.0"
dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.7.0",
"ethcore-io 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-util 1.7.0",
"ethcrypto 0.1.0",
"ethkey 0.2.0",
@ -606,6 +622,7 @@ dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.1 (git+https://github.com/ethcore/mio)",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"path 0.1.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.1.0",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
@ -626,6 +643,7 @@ dependencies = [
"ethcore-io 1.7.0",
"ethcore-ipc 1.7.0",
"ethcore-light 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-util 1.7.0",
"ethcrypto 0.1.0",
"ethjson 0.1.0",
@ -633,11 +651,11 @@ dependencies = [
"ethstore 0.1.0",
"ethsync 1.7.0",
"fetch 0.1.0",
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"jsonrpc-ipc-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"jsonrpc-macros 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-reactor 0.1.0",
@ -664,7 +682,7 @@ dependencies = [
"ethcore-util 1.7.0",
"ethcrypto 0.1.0",
"ethkey 0.2.0",
"hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -680,13 +698,14 @@ dependencies = [
"ethcore-io 1.7.0",
"ethcore-rpc 1.7.0",
"ethcore-util 1.7.0",
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-ui 1.7.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
"ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=mio-upstream-stable)",
]
[[package]]
@ -698,24 +717,23 @@ dependencies = [
"ethcore-ipc 1.7.0",
"ethcore-ipc-codegen 1.7.0",
"ethcore-ipc-nano 1.7.0",
"ethcore-logger 1.7.0",
"ethcore-util 1.7.0",
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"jsonrpc-macros 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"jsonrpc-tcp-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-tcp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)",
"semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ethcore-util"
version = "1.7.0"
dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -723,9 +741,9 @@ dependencies = [
"ethcore-bigint 0.1.2",
"ethcore-bloom-journal 0.1.0",
"ethcore-devtools 1.7.0",
"ethcore-logger 1.7.0",
"heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -862,7 +880,7 @@ dependencies = [
name = "fetch"
version = "0.1.0"
dependencies = [
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -881,11 +899,8 @@ dependencies = [
[[package]]
name = "futures"
version = "0.1.10"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "futures-cpupool"
@ -893,7 +908,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -989,7 +1004,7 @@ dependencies = [
[[package]]
name = "hyper"
version = "0.10.0-a.0"
source = "git+https://github.com/ethcore/hyper#453c683b52208fefc32d29e4ac7c863439b2321f"
source = "git+https://github.com/paritytech/hyper#453c683b52208fefc32d29e4ac7c863439b2321f"
dependencies = [
"cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1007,7 +1022,7 @@ dependencies = [
[[package]]
name = "hyper"
version = "0.10.4"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1018,7 +1033,7 @@ dependencies = [
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1030,7 +1045,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
"native-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1061,6 +1076,15 @@ name = "integer-encoding"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "iovec"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ipc-common-types"
version = "1.7.0"
@ -1096,67 +1120,82 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "jsonrpc-core"
version = "6.0.0"
source = "git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6#86d7a89c85f324b5f6671315d9b71010ca995300"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed"
dependencies = [
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-http-server"
version = "6.0.0"
source = "git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6#86d7a89c85f324b5f6671315d9b71010ca995300"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed"
dependencies = [
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-ipc-server"
version = "6.0.0"
source = "git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6#86d7a89c85f324b5f6671315d9b71010ca995300"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed"
dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-tokio-ipc 0.1.0 (git+https://github.com/nikvolf/parity-tokio-ipc)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-macros"
version = "6.0.0"
source = "git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6#86d7a89c85f324b5f6671315d9b71010ca995300"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed"
dependencies = [
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-tcp-server"
version = "6.0.0"
source = "git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6#86d7a89c85f324b5f6671315d9b71010ca995300"
name = "jsonrpc-pubsub"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed"
dependencies = [
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-server-utils"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed"
dependencies = [
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-io 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "jsonrpc-tcp-server"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed"
dependencies = [
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1184,6 +1223,11 @@ name = "lazycell"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazycell"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.21"
@ -1272,38 +1316,6 @@ dependencies = [
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio"
version = "0.5.1"
source = "git+https://github.com/ethcore/mio?branch=v0.5.x#3842d3b250ffd7bd9b16f9586b875ddcbac2b0dd"
dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio"
version = "0.6.0-dev"
@ -1312,7 +1324,7 @@ dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)",
@ -1322,13 +1334,13 @@ dependencies = [
[[package]]
name = "mio"
version = "0.6.1"
source = "git+https://github.com/ethcore/mio#ef182bae193a9c7457cd2cf661fcaffb226e3eef"
source = "git+https://github.com/ethcore/mio#15a577039bed3c72f2952459f8ad687a56f63e29"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1337,23 +1349,56 @@ dependencies = [
[[package]]
name = "mio"
version = "0.6.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio-named-pipes"
version = "0.1.4"
source = "git+https://github.com/alexcrichton/mio-named-pipes#903dc2f7eac6700c62bfdda258a599db13a9228f"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazycell 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mio-uds"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miow"
version = "0.1.3"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miow"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1431,15 +1476,6 @@ dependencies = [
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.6.0"
@ -1468,7 +1504,7 @@ dependencies = [
[[package]]
name = "nodrop"
version = "0.1.6"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1628,7 +1664,7 @@ dependencies = [
"ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.0",
"fetch 0.1.0",
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1644,8 +1680,7 @@ dependencies = [
"cid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0",
"ethcore-util 1.7.0",
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
"jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"multihash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.1.0",
@ -1670,8 +1705,8 @@ dependencies = [
name = "parity-reactor"
version = "0.1.0"
dependencies = [
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1681,15 +1716,31 @@ dependencies = [
"ethcore-rpc 1.7.0",
"ethcore-signer 1.7.0",
"ethcore-util 1.7.0",
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)",
"ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=mio-upstream-stable)",
]
[[package]]
name = "parity-tokio-ipc"
version = "0.1.0"
source = "git+https://github.com/nikvolf/parity-tokio-ipc#3d4234de6bdc78688ef803935111003080fd5375"
dependencies = [
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)",
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-line 0.1.0 (git+https://github.com/tokio-rs/tokio-line)",
"tokio-named-pipes 0.1.0 (git+https://github.com/alexcrichton/tokio-named-pipes)",
"tokio-uds 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1711,7 +1762,7 @@ dependencies = [
[[package]]
name = "parity-ui-precompiled"
version = "1.4.0"
source = "git+https://github.com/ethcore/js-precompiled.git#47da49294ad958933e85a9c4f0f2bb4df5dc47de"
source = "git+https://github.com/ethcore/js-precompiled.git#6f18d6f6ddfff55e93f67e11f445eb98bc860219"
dependencies = [
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1730,6 +1781,7 @@ dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-hash-fetch 1.7.0",
"parity-reactor 0.1.0",
"path 0.1.0",
"target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -1754,6 +1806,10 @@ dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "path"
version = "0.1.0"
[[package]]
name = "phf"
version = "0.7.14"
@ -1915,7 +1971,7 @@ name = "reqwest"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2004,7 +2060,7 @@ dependencies = [
"ethcore-bigint 0.1.2",
"ethcore-rpc 1.7.0",
"ethcore-util 1.7.0",
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-rpc-client 1.4.0",
"rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2204,11 +2260,6 @@ name = "siphasher"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "slab"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "slab"
version = "0.2.0"
@ -2394,29 +2445,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "tokio-core"
version = "0.1.2"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-io"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bytes 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-line"
version = "0.1.0"
source = "git+https://github.com/tokio-rs/tokio-line#482614ae0c82daf584727ae65a80d854fe861f81"
dependencies = [
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-named-pipes"
version = "0.1.0"
source = "git+https://github.com/alexcrichton/tokio-named-pipes#3a22f8fc9a441b548aec25bd5df3b1e0ab99fabe"
dependencies = [
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)",
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-proto"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -2425,7 +2507,19 @@ name = "tokio-service"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tokio-uds"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"mio 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"mio-uds 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -2449,6 +2543,11 @@ name = "traitobject"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "traitobject"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "transient-hashmap"
version = "0.4.0"
@ -2556,7 +2655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ws"
version = "0.5.3"
source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#f5c0b35d660244d1b7500693c8cc28277ce1d418"
source = "git+https://github.com/paritytech/ws-rs.git?branch=mio-upstream-stable#f5c0b35d660244d1b7500693c8cc28277ce1d418"
dependencies = [
"bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)",
"httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2612,10 +2711,10 @@ dependencies = [
[metadata]
"checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a"
"checksum aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67077478f0a03952bed2e6786338d400d40c25e9836e08ad50af96607317fd03"
"checksum ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1f46cd5b1d660c938e3f92dfe7a73d832b3281479363dd0cd9c1c2fbf60f7962"
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5"
"checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4"
"checksum arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "16e3bdb2f54b3ace0285975d59a97cf8ed3855294b2b6bc651fcf22a9c352975"
"checksum arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d89f1b0e242270b5b797778af0c8d182a1a2ccac5d8d6fadf414223cc0fab096"
"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
"checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1"
"checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c"
@ -2632,6 +2731,7 @@ dependencies = [
"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"
"checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
"checksum bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)" = "<none>"
"checksum bytes 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46112a0060ae15e3a3f9a445428a53e082b91215b744fa27a1948842f4a64b96"
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
"checksum cid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e53e6cdfa5ca294863e8c8a32a7cdb4dc0a442c8971d47a0e75b6c27ea268a6a"
"checksum clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "5b4fabf979ddf6419a313c1c0ada4a5b95cfd2049c56e8418d622d27b4b6ff32"
@ -2642,7 +2742,7 @@ dependencies = [
"checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5"
"checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97"
"checksum crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec"
"checksum ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)" = "<none>"
"checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "<none>"
"checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf"
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
"checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76"
@ -2654,7 +2754,7 @@ dependencies = [
"checksum ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d8f6cc4c1acd005f48e1d17b06a461adac8fb6eeeb331fbf19a0e656fba91cd"
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
"checksum futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c1913eb7083840b1bbcbf9631b7fda55eaf35fe7ead13cca034e8946f9e2bc41"
"checksum futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8e51e7f9c150ba7fd4cee9df8bf6ea3dea5b63b68955ddad19ccd35b71dcfb4d"
"checksum futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bb982bb25cd8fa5da6a8eb3a460354c984ff1113da82bcb4f0b0862b5795db82"
"checksum gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)" = "c07c758b972368e703a562686adb39125707cc1ef3399da8c019fc6c2498a75d"
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
@ -2664,25 +2764,29 @@ dependencies = [
"checksum hidapi 0.3.1 (git+https://github.com/ethcore/hidapi-rs)" = "<none>"
"checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58"
"checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae"
"checksum hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)" = "<none>"
"checksum hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "220407e5a263f110ec30a071787c9535918fdfc97def5680c90013c3f30c38c1"
"checksum hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)" = "<none>"
"checksum hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "43a15e3273b2133aaac0150478ab443fb89f15c3de41d8d93d8f3bb14bf560f6"
"checksum hyper 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9bf64f730d6ee4b0528a5f0a316363da9d8104318731509d4ccc86248f82b3"
"checksum hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "afe68f772f0497a7205e751626bb8e1718568b58534b6108c73a74ef80483409"
"checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
"checksum igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8c12b1795b8b168f577c45fa10379b3814dcb11b7ab702406001f0d63f40484"
"checksum integer-encoding 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a053c9c7dcb7db1f2aa012c37dc176c62e4cdf14898dee0eecc606de835b8acb"
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
"checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c"
"checksum itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d95557e7ba6b71377b0f2c3b3ae96c53f1b75a926a6901a500f557a370af730a"
"checksum itoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91fd9dc2c587067de817fec4ad355e3818c3d893a78cab32a0a474c7a15bb8d5"
"checksum jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)" = "<none>"
"checksum jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)" = "<none>"
"checksum jsonrpc-ipc-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)" = "<none>"
"checksum jsonrpc-macros 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)" = "<none>"
"checksum jsonrpc-tcp-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)" = "<none>"
"checksum jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum jsonrpc-tcp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
"checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
"checksum lazycell 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec38a5c22f1ef3e30d2642aa875620d60edeef36cef43c4739d86215ce816331"
"checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135"
"checksum libusb 0.3.0 (git+https://github.com/ethcore/libusb-rs)" = "<none>"
"checksum libusb-sys 0.2.3 (git+https://github.com/ethcore/libusb-sys)" = "<none>"
@ -2695,12 +2799,13 @@ dependencies = [
"checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2"
"checksum mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e50bf542f81754ef69e5cea856946a3819f7c09ea97b4903c8bc8a89f74e7b6"
"checksum miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d1f4d337a01c32e1f2122510fed46393d53ca35a7f429cb0450abaedfa3ed54"
"checksum mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)" = "<none>"
"checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e"
"checksum mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)" = "<none>"
"checksum mio 0.6.1 (git+https://github.com/ethcore/mio)" = "<none>"
"checksum mio 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "410a1a0ff76f5a226f1e4e3ff1756128e65cd30166e39c3892283e2ac09d5b67"
"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a"
"checksum mio 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5b493dc9fd96bd2077f2117f178172b0765db4dfda3ea4d8000401e6d65d3e80"
"checksum mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)" = "<none>"
"checksum mio-uds 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "78437f00d9615c366932cbfe79790b5c2945706ba67cf78378ffacc0069ed9de"
"checksum miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3e690c5df6b2f60acd45d56378981e827ff8295562fc8d34f573deb267a59cd1"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
"checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8"
"checksum multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6"
"checksum multihash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c9f70f2402fa07c16c40be8fd0a748a39257c5dc3ff5c857cbbde2f39135c505"
@ -2708,10 +2813,9 @@ dependencies = [
"checksum nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7)" = "<none>"
"checksum native-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4e52995154bb6f0b41e4379a279482c9387c1632e3798ba4e511ef8c54ee09"
"checksum net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6a816012ca11cb47009693c1e0c6130e26d39e4d97ee2a13c50e868ec83e3204"
"checksum nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f05c2fc965fc1cd6b73fa57fa7b89f288178737f2f3ce9e63e4a6a141189000e"
"checksum nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7bb1da2be7da3cbffda73fc681d509ffd9e665af478d2bee1907cee0bc64b2"
"checksum nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d95c5fa8b641c10ad0b8887454ebaafa3c92b5cd5350f8fc693adafd178e7b"
"checksum nodrop 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4d9a22dbcebdeef7bf275cbf444d6521d4e7a2fee187b72d80dba0817120dd8f"
"checksum nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "52cd74cd09beba596430cc6e3091b74007169a56246e1262f0ba451ea95117b2"
"checksum nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6caab12c5f97aa316cb249725aa32115118e1522b445e26c257dd77cad5ffd4e"
"checksum num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "c04bd954dbf96f76bab6e5bd6cef6f1ce1262d15268ce4f926d2b5b778fa7af2"
"checksum num-bigint 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "41655c8d667be847a0b72fe0888857a7b3f052f691cf40852be5fcf87b274a65"
@ -2729,6 +2833,7 @@ dependencies = [
"checksum order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb"
"checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7"
"checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c"
"checksum parity-tokio-ipc 0.1.0 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>"
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)" = "<none>"
"checksum parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e1435e7a2a00dfebededd6c6bdbd54008001e94b4a2aadd6aef0dc4c56317621"
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
@ -2782,7 +2887,6 @@ dependencies = [
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
"checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d"
"checksum siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c44e42fa187b5a8782489cf7740cc27c3125806be2bf33563cf5e02e9533fcd"
"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e"
"checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = "<none>"
"checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
@ -2807,12 +2911,17 @@ dependencies = [
"checksum thread_local 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0694f51610ef7cfac7a1b81de7f1602ee5356e76541bcd62c40e71933338cab1"
"checksum time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7ec6d62a20df54e07ab3b78b9a3932972f4b7981de295563686849eb3989af"
"checksum tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f7aef43048292ca0bae4ab32180e85f6202cf2816c2a210c396a84b99dab9270"
"checksum tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "52416b3e937abac22a543a7f1c66bd37feb60137ff1ab42390fa02df85347e58"
"checksum tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3d1be481b55126f02ef88ff86748086473cb537a949fc4a8f4be403a530ae54b"
"checksum tokio-io 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6a278fde45f1be68e44995227d426aaa4841e0980bb0a21b981092f28c3c8473"
"checksum tokio-line 0.1.0 (git+https://github.com/tokio-rs/tokio-line)" = "<none>"
"checksum tokio-named-pipes 0.1.0 (git+https://github.com/alexcrichton/tokio-named-pipes)" = "<none>"
"checksum tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c0d6031f94d78d7b4d509d4a7c5e1cdf524a17e7b08d1c188a83cf720e69808"
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
"checksum tokio-uds 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc7b5fc8e19e220b29566d1750949224a518478eab9cebc8df60583242ca30a"
"checksum toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "fcd27a04ca509aff336ba5eb2abc58d456f52c4ff64d9724d88acb85ead560b6"
"checksum toml 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a442dfc13508e603c3f763274361db7f79d7469a0e95c411cde53662ab30fc72"
"checksum traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "07eaeb7689bb7fca7ce15628319635758eda769fed481ecfe6686ddef2600616"
"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
"checksum transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "715254c8f0811be1a79ad3ea5e6fa3c8eddec2b03d7f5ba78cf093e56d79c24f"
"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
"checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764"
@ -2828,7 +2937,7 @@ dependencies = [
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
"checksum ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=mio-upstream-stable)" = "<none>"
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
"checksum xdg 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77b831a5ba77110f438f0ac5583aafeb087f70432998ba6b7dcb1d32185db453"
"checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef"

View File

@ -16,7 +16,7 @@ num_cpus = "1.2"
number_prefix = "0.2"
rpassword = "0.2.1"
semver = "0.5"
ansi_term = "0.7"
ansi_term = "0.9"
regex = "0.1"
isatty = "0.1"
toml = "0.2"
@ -24,9 +24,10 @@ serde = "0.9"
serde_json = "0.9"
app_dirs = "1.1.1"
fdlimit = "0.1"
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" }
ws2_32-sys = "0.2"
hyper = { default-features = false, git = "https://github.com/paritytech/hyper" }
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
ethsync = { path = "sync" }
ethcore = { path = "ethcore" }
ethcore-util = { path = "util" }
@ -50,6 +51,7 @@ parity-updater = { path = "updater" }
parity-reactor = { path = "util/reactor" }
parity-local-store = { path = "local-store" }
ethcore-dapps = { path = "dapps", optional = true }
path = { path = "util/path" }
clippy = { version = "0.0.103", optional = true}
ethcore-secretstore = { path = "secret_store", optional = true }

View File

@ -8,33 +8,36 @@ authors = ["Parity Technologies <admin@parity.io>"]
[lib]
[dependencies]
rand = "0.3"
log = "0.3"
base32 = "0.3"
env_logger = "0.3"
futures = "0.1"
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" }
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" }
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
unicase = "1.3"
url = "1.0"
rustc-serialize = "0.3"
serde = "0.9"
serde_json = "0.9"
serde_derive = "0.9"
linked-hash-map = "0.3"
log = "0.3"
parity-dapps-glue = "1.7"
base32 = "0.3"
mime = "0.2"
mime_guess = "1.6.1"
rand = "0.3"
rustc-serialize = "0.3"
serde = "0.9"
serde_derive = "0.9"
serde_json = "0.9"
time = "0.1.35"
unicase = "1.3"
url = "1.0"
zip = { version = "0.1", default-features = false }
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
# TODO [ToDr] Temporary solution, server should be merged with RPC.
jsonrpc-server-utils = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
ethcore-devtools = { path = "../devtools" }
ethcore-rpc = { path = "../rpc" }
ethcore-util = { path = "../util" }
fetch = { path = "../util/fetch" }
parity-ui = { path = "./ui" }
parity-hash-fetch = { path = "../hash-fetch" }
parity-reactor = { path = "../util/reactor" }
parity-ui = { path = "./ui" }
clippy = { version = "0.0.103", optional = true}

View File

@ -19,7 +19,6 @@ use unicase::UniCase;
use hyper::{server, net, Decoder, Encoder, Next, Control};
use hyper::header;
use hyper::method::Method;
use hyper::header::AccessControlAllowOrigin;
use api::types::{App, ApiError};
use api::response;
@ -27,23 +26,20 @@ use apps::fetcher::Fetcher;
use handlers::extract_url;
use endpoint::{Endpoint, Endpoints, Handler, EndpointPath};
use jsonrpc_http_server::cors;
use jsonrpc_http_server;
use jsonrpc_server_utils::cors;
#[derive(Clone)]
pub struct RestApi {
cors_domains: Option<Vec<AccessControlAllowOrigin>>,
cors_domains: Option<Vec<cors::AccessControlAllowOrigin>>,
endpoints: Arc<Endpoints>,
fetcher: Arc<Fetcher>,
}
impl RestApi {
pub fn new(cors_domains: Vec<String>, endpoints: Arc<Endpoints>, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
pub fn new(cors_domains: Vec<cors::AccessControlAllowOrigin>, endpoints: Arc<Endpoints>, fetcher: Arc<Fetcher>) -> Box<Endpoint> {
Box::new(RestApi {
cors_domains: Some(cors_domains.into_iter().map(|domain| match domain.as_ref() {
"all" | "*" | "any" => AccessControlAllowOrigin::Any,
"null" => AccessControlAllowOrigin::Null,
other => AccessControlAllowOrigin::Value(other.into()),
}).collect()),
cors_domains: Some(cors_domains),
endpoints: endpoints,
fetcher: fetcher,
})
@ -64,7 +60,7 @@ impl Endpoint for RestApi {
struct RestApiRouter {
api: RestApi,
origin: Option<String>,
cors_header: Option<header::AccessControlAllowOrigin>,
path: Option<EndpointPath>,
control: Option<Control>,
handler: Box<Handler>,
@ -74,7 +70,7 @@ impl RestApiRouter {
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
RestApiRouter {
path: Some(path),
origin: None,
cors_header: None,
control: Some(control),
api: api,
handler: response::as_json_error(&ApiError {
@ -95,8 +91,10 @@ impl RestApiRouter {
}
/// Returns basic headers for a response (it may be overwritten by the handler)
fn response_headers(&self) -> header::Headers {
fn response_headers(cors_header: Option<header::AccessControlAllowOrigin>) -> header::Headers {
let mut headers = header::Headers::new();
if let Some(cors_header) = cors_header {
headers.set(header::AccessControlAllowCredentials);
headers.set(header::AccessControlAllowMethods(vec![
Method::Options,
@ -109,7 +107,6 @@ impl RestApiRouter {
UniCase("accept".to_owned()),
]));
if let Some(cors_header) = cors::get_cors_header(&self.api.cors_domains, &self.origin) {
headers.set(cors_header);
}
@ -120,7 +117,7 @@ impl RestApiRouter {
impl server::Handler<net::HttpStream> for RestApiRouter {
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
self.origin = cors::read_origin(&request);
self.cors_header = jsonrpc_http_server::cors_header(&request, &self.api.cors_domains).into();
if let Method::Options = *request.method() {
self.handler = response::empty();
@ -164,7 +161,7 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
}
fn on_response(&mut self, res: &mut server::Response) -> Next {
*res.headers_mut() = self.response_headers();
*res.headers_mut() = Self::response_headers(self.cors_header.take());
self.handler.on_response(res)
}

View File

@ -20,25 +20,27 @@
#![cfg_attr(feature="nightly", plugin(clippy))]
extern crate base32;
extern crate hyper;
extern crate time;
extern crate url as url_lib;
extern crate unicase;
extern crate futures;
extern crate linked_hash_map;
extern crate mime_guess;
extern crate rand;
extern crate rustc_serialize;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate unicase;
extern crate url as url_lib;
extern crate zip;
extern crate rand;
extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate mime_guess;
extern crate rustc_serialize;
extern crate jsonrpc_server_utils;
extern crate ethcore_rpc;
extern crate ethcore_util as util;
extern crate parity_hash_fetch as hash_fetch;
extern crate linked_hash_map;
extern crate fetch;
extern crate parity_dapps_glue as parity_dapps;
extern crate futures;
extern crate parity_hash_fetch as hash_fetch;
extern crate parity_reactor;
#[macro_use]
@ -68,17 +70,20 @@ mod web;
mod tests;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::net::SocketAddr;
use std::collections::HashMap;
use ethcore_rpc::{Metadata};
use jsonrpc_core::{Middleware, MetaIoHandler};
use jsonrpc_http_server::tokio_core::reactor::Remote as TokioRemote;
pub use jsonrpc_http_server::{DomainsValidation, Host, AccessControlAllowOrigin};
pub use jsonrpc_http_server::hyper;
use ethcore_rpc::Metadata;
use fetch::{Fetch, Client as FetchClient};
use hash_fetch::urlhint::ContractClient;
use jsonrpc_core::Middleware;
use jsonrpc_core::reactor::RpcHandler;
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
use parity_reactor::Remote;
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
use self::apps::{HOME_PAGE, DAPPS_DOMAIN};
@ -110,8 +115,8 @@ pub struct ServerBuilder<T: Fetch = FetchClient> {
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
signer_address: Option<(String, u16)>,
allowed_hosts: Option<Vec<String>>,
extra_cors: Option<Vec<String>>,
allowed_hosts: Option<Vec<Host>>,
extra_cors: Option<Vec<AccessControlAllowOrigin>>,
remote: Remote,
fetch: Option<T>,
}
@ -172,15 +177,15 @@ impl<T: Fetch> ServerBuilder<T> {
/// Change allowed hosts.
/// `None` - All hosts are allowed
/// `Some(whitelist)` - Allow only whitelisted hosts (+ listen address)
pub fn allowed_hosts(mut self, allowed_hosts: Option<Vec<String>>) -> Self {
self.allowed_hosts = allowed_hosts;
pub fn allowed_hosts(mut self, allowed_hosts: DomainsValidation<Host>) -> Self {
self.allowed_hosts = allowed_hosts.into();
self
}
/// Extra cors headers.
/// `None` - no additional CORS URLs
pub fn extra_cors_headers(mut self, cors: Option<Vec<String>>) -> Self {
self.extra_cors = cors;
pub fn extra_cors_headers(mut self, cors: DomainsValidation<AccessControlAllowOrigin>) -> Self {
self.extra_cors = cors.into();
self
}
@ -192,7 +197,7 @@ impl<T: Fetch> ServerBuilder<T> {
/// Asynchronously start server with no authentication,
/// returns result with `Server` handle on success or an error.
pub fn start_unsecured_http<S: Middleware<Metadata>>(self, addr: &SocketAddr, handler: RpcHandler<Metadata, S>) -> Result<Server, ServerError> {
pub fn start_unsecured_http<S: Middleware<Metadata>>(self, addr: &SocketAddr, handler: MetaIoHandler<Metadata, S>, tokio_remote: TokioRemote) -> Result<Server, ServerError> {
let fetch = self.fetch_client()?;
Server::start_http(
addr,
@ -207,13 +212,14 @@ impl<T: Fetch> ServerBuilder<T> {
self.sync_status,
self.web_proxy_tokens,
self.remote,
tokio_remote,
fetch,
)
}
/// Asynchronously start server with `HTTP Basic Authentication`,
/// return result with `Server` handle on success or an error.
pub fn start_basic_auth_http<S: Middleware<Metadata>>(self, addr: &SocketAddr, username: &str, password: &str, handler: RpcHandler<Metadata, S>) -> Result<Server, ServerError> {
pub fn start_basic_auth_http<S: Middleware<Metadata>>(self, addr: &SocketAddr, username: &str, password: &str, handler: MetaIoHandler<Metadata, S>, tokio_remote: TokioRemote) -> Result<Server, ServerError> {
let fetch = self.fetch_client()?;
Server::start_http(
addr,
@ -228,6 +234,7 @@ impl<T: Fetch> ServerBuilder<T> {
self.sync_status,
self.web_proxy_tokens,
self.remote,
tokio_remote,
fetch,
)
}
@ -243,12 +250,11 @@ impl<T: Fetch> ServerBuilder<T> {
/// Webapps HTTP server.
pub struct Server {
server: Option<hyper::server::Listening>,
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
}
impl Server {
/// Returns a list of allowed hosts or `None` if all hosts are allowed.
fn allowed_hosts(hosts: Option<Vec<String>>, bind_address: String) -> Option<Vec<String>> {
fn allowed_hosts(hosts: Option<Vec<Host>>, bind_address: String) -> Option<Vec<Host>> {
let mut allowed = Vec::new();
match hosts {
@ -263,16 +269,19 @@ impl Server {
}
/// Returns a list of CORS domains for API endpoint.
fn cors_domains(signer_address: Option<(String, u16)>, extra_cors: Option<Vec<String>>) -> Vec<String> {
fn cors_domains(
signer_address: Option<(String, u16)>,
extra_cors: Option<Vec<AccessControlAllowOrigin>>,
) -> Vec<AccessControlAllowOrigin> {
let basic_cors = match signer_address {
Some(signer_address) => vec![
Some(signer_address) => [
format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN),
format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
format!("http://{}", address(&signer_address)),
format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN),
format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1),
format!("https://{}", address(&signer_address)),
],
].into_iter().map(|val| AccessControlAllowOrigin::Value(val.into())).collect(),
None => vec![],
};
@ -284,10 +293,10 @@ impl Server {
fn start_http<A: Authorization + 'static, F: Fetch, T: Middleware<Metadata>>(
addr: &SocketAddr,
hosts: Option<Vec<String>>,
extra_cors: Option<Vec<String>>,
hosts: Option<Vec<Host>>,
extra_cors: Option<Vec<AccessControlAllowOrigin>>,
authorization: A,
handler: RpcHandler<Metadata, T>,
handler: MetaIoHandler<Metadata, T>,
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
signer_address: Option<(String, u16)>,
@ -295,9 +304,9 @@ impl Server {
sync_status: Arc<SyncStatus>,
web_proxy_tokens: Arc<WebProxyTokens>,
remote: Remote,
tokio_remote: TokioRemote,
fetch: F,
) -> Result<Server, ServerError> {
let panic_handler = Arc::new(Mutex::new(None));
let authorization = Arc::new(authorization);
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
hash_fetch::urlhint::URLHintContract::new(registrar),
@ -318,7 +327,7 @@ impl Server {
let special = Arc::new({
let mut special = HashMap::new();
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, cors_domains.clone(), panic_handler.clone()));
special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, tokio_remote, cors_domains.clone()));
special.insert(router::SpecialEndpoint::Utils, apps::utils());
special.insert(
router::SpecialEndpoint::Api,
@ -346,17 +355,11 @@ impl Server {
Server {
server: Some(l),
panic_handler: panic_handler,
}
})
.map_err(ServerError::from)
}
/// Set callback for panics.
pub fn set_panic_handler<F>(&self, handler: F) where F : Fn() -> () + Send + 'static {
*self.panic_handler.lock().unwrap() = Some(Box::new(handler));
}
#[cfg(test)]
/// Returns address that this server is bound to.
pub fn addr(&self) -> &SocketAddr {
@ -408,6 +411,7 @@ fn address(address: &(String, u16)) -> String {
#[cfg(test)]
mod util_tests {
use super::Server;
use jsonrpc_http_server::AccessControlAllowOrigin;
#[test]
fn should_return_allowed_hosts() {
@ -432,18 +436,18 @@ mod util_tests {
// when
let none = Server::cors_domains(None, None);
let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)), None);
let extra = Server::cors_domains(None, Some(vec!["all".to_owned()]));
let extra = Server::cors_domains(None, Some(vec!["all".into()]));
// then
assert_eq!(none, Vec::<String>::new());
assert_eq!(none, Vec::<AccessControlAllowOrigin>::new());
assert_eq!(some, vec![
"http://parity.web3.site".to_owned(),
"http://parity.web3.site".into(),
"http://parity.web3.site:18180".into(),
"http://127.0.0.1:18180".into(),
"https://parity.web3.site".into(),
"https://parity.web3.site:18180".into(),
"https://127.0.0.1:18180".into()
"https://127.0.0.1:18180".into(),
]);
assert_eq!(extra, vec!["all".to_owned()]);
assert_eq!(extra, vec![AccessControlAllowOrigin::Any]);
}
}

View File

@ -19,18 +19,13 @@ use apps::DAPPS_DOMAIN;
use hyper::{server, header, StatusCode};
use hyper::net::HttpStream;
use jsonrpc_http_server::{is_host_header_valid};
use handlers::ContentHandler;
use jsonrpc_http_server;
use jsonrpc_server_utils::hosts;
pub fn is_valid(request: &server::Request<HttpStream>, allowed_hosts: &[String], endpoints: Vec<String>) -> bool {
let mut endpoints = endpoints.iter()
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
.collect::<Vec<String>>();
endpoints.extend_from_slice(allowed_hosts);
let header_valid = is_host_header_valid(request, &endpoints);
match (header_valid, request.headers().get::<header::Host>()) {
pub fn is_valid(req: &server::Request<HttpStream>, allowed_hosts: &Option<Vec<hosts::Host>>) -> bool {
let header_valid = jsonrpc_http_server::is_host_allowed(req, allowed_hosts);
match (header_valid, req.headers().get::<header::Host>()) {
(true, _) => true,
(_, Some(host)) => host.hostname.ends_with(DAPPS_DOMAIN),
_ => false,

View File

@ -24,14 +24,16 @@ use address;
use std::cmp;
use std::sync::Arc;
use std::collections::HashMap;
use url::{Url, Host};
use hyper::{self, server, header, Next, Encoder, Decoder, Control, StatusCode};
use hyper::net::HttpStream;
use jsonrpc_server_utils::hosts;
use apps::{self, DAPPS_DOMAIN};
use apps::fetcher::Fetcher;
use endpoint::{Endpoint, Endpoints, EndpointPath};
use handlers::{self, Redirection, ContentHandler};
use self::auth::{Authorization, Authorized};
/// Special endpoints are accessible on every domain (every dapp)
#[derive(Debug, PartialEq, Hash, Eq)]
@ -42,18 +44,18 @@ pub enum SpecialEndpoint {
None,
}
pub struct Router<A: Authorization + 'static> {
pub struct Router<A: auth::Authorization + 'static> {
control: Option<Control>,
signer_address: Option<(String, u16)>,
endpoints: Arc<Endpoints>,
fetch: Arc<Fetcher>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
authorization: Arc<A>,
allowed_hosts: Option<Vec<String>>,
allowed_hosts: Option<Vec<hosts::Host>>,
handler: Box<server::Handler<HttpStream> + Send>,
}
impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
impl<A: auth::Authorization + 'static> server::Handler<HttpStream> for Router<A> {
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
// Choose proper handler depending on path / domain
@ -66,20 +68,18 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
// Validate Host header
if let Some(ref hosts) = self.allowed_hosts {
trace!(target: "dapps", "Validating host headers against: {:?}", hosts);
let is_valid = is_utils || host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect());
trace!(target: "dapps", "Validating host headers against: {:?}", self.allowed_hosts);
let is_valid = is_utils || host_validation::is_valid(&req, &self.allowed_hosts);
if !is_valid {
debug!(target: "dapps", "Rejecting invalid host header.");
self.handler = host_validation::host_invalid_response();
return self.handler.on_request(req);
}
}
trace!(target: "dapps", "Checking authorization.");
// Check authorization
let auth = self.authorization.is_authorized(&req);
if let Authorized::No(handler) = auth {
if let auth::Authorized::No(handler) = auth {
debug!(target: "dapps", "Authorization denied.");
self.handler = handler;
return self.handler.on_request(req);
@ -181,7 +181,7 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
}
}
impl<A: Authorization> Router<A> {
impl<A: auth::Authorization> Router<A> {
pub fn new(
control: Control,
signer_address: Option<(String, u16)>,
@ -189,7 +189,7 @@ impl<A: Authorization> Router<A> {
endpoints: Arc<Endpoints>,
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
authorization: Arc<A>,
allowed_hosts: Option<Vec<String>>,
allowed_hosts: Option<Vec<hosts::Host>>,
) -> Self {
let handler = special.get(&SpecialEndpoint::Utils)

View File

@ -14,55 +14,69 @@
// 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, Mutex};
use std::sync::Arc;
use hyper;
use ethcore_rpc::{Metadata, Origin};
use jsonrpc_core::Middleware;
use jsonrpc_core::reactor::RpcHandler;
use jsonrpc_http_server::{Rpc, ServerHandler, PanicHandler, AccessControlAllowOrigin, HttpMetaExtractor};
use jsonrpc_core::{Middleware, MetaIoHandler};
use jsonrpc_http_server::{self as http, AccessControlAllowOrigin, HttpMetaExtractor};
use jsonrpc_http_server::tokio_core::reactor::Remote;
use endpoint::{Endpoint, EndpointPath, Handler};
pub fn rpc<T: Middleware<Metadata>>(
handler: RpcHandler<Metadata, T>,
cors_domains: Vec<String>,
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
handler: MetaIoHandler<Metadata, T>,
remote: Remote,
cors_domains: Vec<AccessControlAllowOrigin>,
) -> Box<Endpoint> {
Box::new(RpcEndpoint {
handler: handler,
handler: Arc::new(handler),
remote: remote,
meta_extractor: Arc::new(MetadataExtractor),
panic_handler: panic_handler,
cors_domain: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()),
cors_domain: Some(cors_domains),
// NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router.
allowed_hosts: None,
})
}
struct RpcEndpoint<T: Middleware<Metadata>> {
handler: RpcHandler<Metadata, T>,
handler: Arc<MetaIoHandler<Metadata, T>>,
remote: Remote,
meta_extractor: Arc<HttpMetaExtractor<Metadata>>,
panic_handler: Arc<Mutex<Option<Box<Fn() -> () + Send>>>>,
cors_domain: Option<Vec<AccessControlAllowOrigin>>,
allowed_hosts: Option<Vec<String>>,
allowed_hosts: Option<Vec<http::Host>>,
}
impl<T: Middleware<Metadata>> Endpoint for RpcEndpoint<T> {
fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box<Handler> {
let panic_handler = PanicHandler { handler: self.panic_handler.clone() };
Box::new(ServerHandler::new(
Rpc::new(self.handler.clone(), self.meta_extractor.clone()),
Box::new(http::ServerHandler::new(
http::Rpc {
handler: self.handler.clone(),
remote: self.remote.clone(),
extractor: self.meta_extractor.clone(),
},
self.cors_domain.clone(),
self.allowed_hosts.clone(),
panic_handler,
Arc::new(NoopMiddleware),
control,
))
}
}
#[derive(Default)]
struct NoopMiddleware;
impl http::RequestMiddleware for NoopMiddleware {
fn on_request(&self, request: &http::hyper::server::Request<http::hyper::net::HttpStream>) -> http::RequestMiddlewareAction {
http::RequestMiddlewareAction::Proceed {
should_continue_on_invalid_cors: request.headers().get::<http::hyper::header::Origin>().is_none(),
}
}
}
struct MetadataExtractor;
impl HttpMetaExtractor<Metadata> for MetadataExtractor {
fn read_metadata(&self, request: &hyper::server::Request<hyper::net::HttpStream>) -> Metadata {
let dapp_id = request.headers().get::<hyper::header::Origin>()
fn read_metadata(&self, request: &http::hyper::server::Request<http::hyper::net::HttpStream>) -> Metadata {
let dapp_id = request.headers().get::<http::hyper::header::Origin>()
.map(|origin| format!("{}://{}", origin.scheme, origin.host))
.or_else(|| {
// fallback to custom header, but only if origin is null

View File

@ -33,8 +33,8 @@ fn should_return_error() {
);
// then
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
response.assert_status("HTTP/1.1 404 Not Found");
response.assert_header("Content-Type", "application/json");
assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#));
assert_security_headers(&response.headers);
}
@ -56,8 +56,8 @@ fn should_serve_apps() {
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Content-Type", "application/json");
assert!(response.body.contains("Parity UI"), response.body);
assert_security_headers(&response.headers);
}
@ -79,8 +79,8 @@ fn should_handle_ping() {
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json");
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Content-Type", "application/json");
assert_eq!(response.body, "0\n\n".to_owned());
assert_security_headers(&response.headers);
}
@ -102,7 +102,7 @@ fn should_try_to_resolve_dapp() {
);
// then
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
response.assert_status("HTTP/1.1 404 Not Found");
assert_eq!(registrar.calls.lock().len(), 2);
assert_security_headers(&response.headers);
}
@ -125,12 +125,8 @@ fn should_return_signer_port_cors_headers() {
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert!(
response.headers_raw.contains("Access-Control-Allow-Origin: http://127.0.0.1:18180"),
"CORS header for signer missing: {:?}",
response.headers
);
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Access-Control-Allow-Origin", "http://127.0.0.1:18180");
}
#[test]
@ -151,12 +147,8 @@ fn should_return_signer_port_cors_headers_for_home_parity() {
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert!(
response.headers_raw.contains("Access-Control-Allow-Origin: http://parity.web3.site"),
"CORS header for parity.web3.site missing: {:?}",
response.headers
);
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Access-Control-Allow-Origin", "http://parity.web3.site");
}
@ -178,12 +170,8 @@ fn should_return_signer_port_cors_headers_for_home_parity_with_https() {
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert!(
response.headers_raw.contains("Access-Control-Allow-Origin: https://parity.web3.site"),
"CORS header for parity.web3.site missing: {:?}",
response.headers
);
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Access-Control-Allow-Origin", "https://parity.web3.site");
}
#[test]
@ -204,12 +192,8 @@ fn should_return_signer_port_cors_headers_for_home_parity_with_port() {
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert!(
response.headers_raw.contains("Access-Control-Allow-Origin: http://parity.web3.site:18180"),
"CORS header for parity.web3.site missing: {:?}",
response.headers
);
response.assert_status("HTTP/1.1 200 OK");
response.assert_header("Access-Control-Allow-Origin", "http://parity.web3.site:18180");
}
#[test]

View File

@ -114,7 +114,7 @@ impl Fetch for FakeFetch {
let data = response.lock().take().unwrap_or(b"Some content");
let cursor = io::Cursor::new(data);
tx.complete(fetch::Response::from_reader(cursor));
tx.send(fetch::Response::from_reader(cursor)).unwrap();
});
rx.map_err(|_| fetch::Error::Aborted).boxed()

View File

@ -21,13 +21,12 @@ use std::sync::Arc;
use env_logger::LogBuilder;
use ethcore_rpc::Metadata;
use jsonrpc_core::MetaIoHandler;
use jsonrpc_core::reactor::RpcEventLoop;
use ServerBuilder;
use Server;
use fetch::Fetch;
use devtools::http_client;
use parity_reactor::Remote;
use parity_reactor::{EventLoop, Remote};
mod registrar;
mod fetch;
@ -48,7 +47,7 @@ fn init_logger() {
pub struct ServerLoop {
pub server: Server,
pub event_loop: RpcEventLoop,
pub event_loop: EventLoop,
}
impl Deref for ServerLoop {
@ -70,13 +69,12 @@ pub fn init_server<F, B>(process: F, io: MetaIoHandler<Metadata>, remote: Remote
// TODO [ToDr] When https://github.com/ethcore/jsonrpc/issues/26 is resolved
// this additional EventLoop wouldn't be needed, we should be able to re-use remote.
let event_loop = RpcEventLoop::spawn();
let handler = event_loop.handler(Arc::new(io));
let event_loop = EventLoop::spawn();
let server = process(ServerBuilder::new(
&dapps_path, registrar.clone(), remote,
))
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), handler).unwrap();
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io, event_loop.raw_remote()).unwrap();
(
ServerLoop { server: server, event_loop: event_loop },
registrar,
@ -89,12 +87,12 @@ pub fn serve_with_auth(user: &str, pass: &str) -> ServerLoop {
let mut dapps_path = env::temp_dir();
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
let event_loop = RpcEventLoop::spawn();
let handler = event_loop.handler(Arc::new(MetaIoHandler::default()));
let server = ServerBuilder::new(&dapps_path, registrar, Remote::new(event_loop.remote()))
let event_loop = EventLoop::spawn();
let io = MetaIoHandler::default();
let server = ServerBuilder::new(&dapps_path, registrar, event_loop.remote())
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
.allowed_hosts(None)
.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), user, pass, handler).unwrap();
.allowed_hosts(None.into())
.start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), user, pass, io, event_loop.raw_remote()).unwrap();
ServerLoop {
server: server,
event_loop: event_loop,
@ -102,26 +100,28 @@ pub fn serve_with_auth(user: &str, pass: &str) -> ServerLoop {
}
pub fn serve_with_rpc(io: MetaIoHandler<Metadata>) -> ServerLoop {
init_server(|builder| builder.allowed_hosts(None), io, Remote::new_sync()).0
init_server(|builder| builder.allowed_hosts(None.into()), io, Remote::new_sync()).0
}
pub fn serve_hosts(hosts: Option<Vec<String>>) -> ServerLoop {
init_server(|builder| builder.allowed_hosts(hosts), Default::default(), Remote::new_sync()).0
let hosts = hosts.map(|hosts| hosts.into_iter().map(Into::into).collect());
init_server(|builder| builder.allowed_hosts(hosts.into()), Default::default(), Remote::new_sync()).0
}
pub fn serve_extra_cors(extra_cors: Option<Vec<String>>) -> ServerLoop {
init_server(|builder| builder.allowed_hosts(None).extra_cors_headers(extra_cors), Default::default(), Remote::new_sync()).0
let extra_cors = extra_cors.map(|cors| cors.into_iter().map(Into::into).collect());
init_server(|builder| builder.allowed_hosts(None.into()).extra_cors_headers(extra_cors.into()), Default::default(), Remote::new_sync()).0
}
pub fn serve_with_registrar() -> (ServerLoop, Arc<FakeRegistrar>) {
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync())
init_server(|builder| builder.allowed_hosts(None.into()), Default::default(), Remote::new_sync())
}
pub fn serve_with_registrar_and_sync() -> (ServerLoop, Arc<FakeRegistrar>) {
init_server(|builder| {
builder
.sync_status(Arc::new(|| true))
.allowed_hosts(None)
.allowed_hosts(None.into())
}, Default::default(), Remote::new_sync())
}
@ -133,7 +133,7 @@ pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Serv
let fetch = FakeFetch::default();
let f = fetch.clone();
let (server, reg) = init_server(move |builder| {
builder.allowed_hosts(None).fetch(f.clone())
builder.allowed_hosts(None.into()).fetch(f.clone())
}, Default::default(), if multi_threaded { Remote::new_thread_per_future() } else { Remote::new_sync() });
(server, fetch, reg)
@ -144,7 +144,7 @@ pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) {
let f = fetch.clone();
let (server, _) = init_server(move |builder| {
builder
.allowed_hosts(None)
.allowed_hosts(None.into())
.fetch(f.clone())
.web_proxy_tokens(Arc::new(move |token| &token == web_token))
}, Default::default(), Remote::new_sync());
@ -153,7 +153,7 @@ pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) {
}
pub fn serve() -> ServerLoop {
init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync()).0
init_server(|builder| builder.allowed_hosts(None.into()), Default::default(), Remote::new_sync()).0
}
pub fn request(server: ServerLoop, request: &str) -> http_client::Response {

View File

@ -32,7 +32,7 @@ fn should_redirect_to_home() {
);
// then
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
response.assert_status("HTTP/1.1 302 Found");
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
}
@ -52,7 +52,7 @@ fn should_redirect_to_home_when_trailing_slash_is_missing() {
);
// then
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
response.assert_status("HTTP/1.1 302 Found");
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
}
@ -72,7 +72,7 @@ fn should_redirect_to_home_for_users_with_cached_redirection() {
);
// then
assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned());
response.assert_status("HTTP/1.1 302 Found");
assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180");
}
@ -92,7 +92,7 @@ fn should_display_404_on_invalid_dapp() {
);
// then
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
response.assert_status("HTTP/1.1 404 Not Found");
assert_security_headers_for_embed(&response.headers);
}
@ -112,7 +112,7 @@ fn should_display_404_on_invalid_dapp_with_domain() {
);
// then
assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned());
response.assert_status("HTTP/1.1 404 Not Found");
assert_security_headers_for_embed(&response.headers);
}
@ -134,8 +134,8 @@ fn should_serve_rpc() {
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert_eq!(response.body, format!("58\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":null},"id":null}"#));
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#));
}
#[test]
@ -156,8 +156,8 @@ fn should_serve_rpc_at_slash_rpc() {
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
assert_eq!(response.body, format!("58\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":null},"id":null}"#));
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#));
}
@ -178,7 +178,7 @@ fn should_serve_proxy_pac() {
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body, "DD\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"parity.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned());
assert_security_headers(&response.headers);
}
@ -200,7 +200,7 @@ fn should_serve_utils() {
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
response.assert_status("HTTP/1.1 200 OK");
assert_eq!(response.body.contains("function(){"), true);
assert_security_headers(&response.headers);
}

View File

@ -55,8 +55,8 @@ fn should_extract_metadata() {
// given
let mut io = MetaIoHandler::default();
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into()));
assert_eq!(meta.dapp_id(), "https://parity.io/".into());
assert_eq!(meta.origin, Origin::Dapps("".into()));
assert_eq!(meta.dapp_id(), "".into());
future::ok(Value::String("Hello World!".into())).boxed()
});
let server = serve_with_rpc(io);
@ -68,7 +68,6 @@ fn should_extract_metadata() {
POST /rpc/ HTTP/1.1\r\n\
Host: 127.0.0.1:8080\r\n\
Connection: close\r\n\
Origin: https://parity.io/\r\n\
X-Parity-Origin: https://this.should.be.ignored\r\n\
Content-Type: application/json\r\n\
Content-Length: {}\r\n\

View File

@ -27,6 +27,7 @@ byteorder = "1.0"
transient-hashmap = "0.4"
linked-hash-map = "0.3.0"
lru-cache = "0.1.0"
itertools = "0.5"
ethabi = "1.0.0"
evmjit = { path = "../evmjit", optional = true }
clippy = { version = "0.0.103", optional = true}
@ -43,14 +44,12 @@ rlp = { path = "../util/rlp" }
ethcore-stratum = { path = "../stratum" }
ethcore-bloom-journal = { path = "../util/bloom" }
hardware-wallet = { path = "../hw" }
ethcore-logger = { path = "../logger" }
stats = { path = "../util/stats" }
hyper = { git = "https://github.com/paritytech/hyper", default-features = false }
num = "0.1"
bn = { git = "https://github.com/paritytech/bn" }
[dependencies.hyper]
git = "https://github.com/ethcore/hyper"
default-features = false
[features]
jit = ["evmjit"]
evm-debug = ["slow-blocks"]

View File

@ -1,5 +1,5 @@
[package]
description = "Parity LES primitives"
description = "Parity Light Client Implementation"
homepage = "http://parity.io"
license = "GPL-3.0"
name = "ethcore-light"

View File

@ -23,7 +23,7 @@
use ethcore::ids::BlockId;
use util::{Bytes, H256, U256, HashDB, MemoryDB};
use util::trie::{self, TrieMut, TrieDBMut, Trie, TrieDB, Recorder};
use rlp::{RlpStream, UntrustedRlp, View};
use rlp::{RlpStream, UntrustedRlp};
// encode a key.
macro_rules! key {

View File

@ -24,7 +24,6 @@
//! - It stores only headers (and a pruned subset of them)
//! - To allow for flexibility in the database layout once that's incorporated.
// TODO: use DB instead of memory. DB Layout: just the contents of `candidates`/`headers`
//
use std::collections::{BTreeMap, HashMap};

View File

@ -31,7 +31,7 @@ use ethcore::service::ClientIoMessage;
use ethcore::encoded;
use io::IoChannel;
use util::{Bytes, DBValue, H256, Mutex, RwLock};
use util::{H256, Mutex, RwLock};
use self::header_chain::{AncestryIter, HeaderChain};
@ -315,50 +315,3 @@ impl LightChainClient for Client {
Client::cht_root(self, i)
}
}
// dummy implementation, should be removed when a `TestClient` is added.
impl ::provider::Provider for Client {
fn chain_info(&self) -> BlockChainInfo {
Client::chain_info(self)
}
fn reorg_depth(&self, _a: &H256, _b: &H256) -> Option<u64> {
None
}
fn earliest_state(&self) -> Option<u64> {
None
}
fn block_header(&self, id: BlockId) -> Option<encoded::Header> {
Client::block_header(self, id)
}
fn block_body(&self, _id: BlockId) -> Option<encoded::Body> {
None
}
fn block_receipts(&self, _hash: &H256) -> Option<Bytes> {
None
}
fn state_proof(&self, _req: ::request::StateProof) -> Vec<Bytes> {
Vec::new()
}
fn contract_code(&self, _req: ::request::ContractCode) -> Bytes {
Vec::new()
}
fn header_proof(&self, _req: ::request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
None
}
fn transaction_proof(&self, _req: ::request::TransactionProof) -> Option<Vec<DBValue>> {
None
}
fn ready_transactions(&self) -> Vec<::ethcore::transaction::PendingTransaction> {
Vec::new()
}
}

View File

@ -26,7 +26,7 @@
//! use-cases like sending transactions from a personal account.
//!
//! The light client performs a header-only sync, doing verification and pruning
//! historical blocks. Upon pruning, batches of 2048 blocks have a number => hash
//! historical blocks. Upon pruning, batches of 2048 blocks have a number => (hash, TD)
//! mapping sealed into "canonical hash tries" which can later be used to verify
//! historical block queries from peers.
@ -57,7 +57,7 @@ mod types;
pub use self::provider::Provider;
pub use self::transaction_queue::TransactionQueue;
pub use types::les_request as request;
pub use types::request as request;
#[macro_use]
extern crate log;

View File

@ -20,7 +20,7 @@ use network::{NetworkContext, PeerId, NodeId};
use super::{Announcement, LightProtocol, ReqId};
use super::error::Error;
use request::{self, Request};
use request::Requests;
/// An I/O context which allows sending and receiving packets as well as
/// disconnecting peers. This is used as a generalization of the portions
@ -50,13 +50,13 @@ pub trait IoContext {
impl<'a> IoContext for NetworkContext<'a> {
fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec<u8>) {
if let Err(e) = self.send(peer, packet_id, packet_body) {
debug!(target: "les", "Error sending packet to peer {}: {}", peer, e);
debug!(target: "pip", "Error sending packet to peer {}: {}", peer, e);
}
}
fn respond(&self, packet_id: u8, packet_body: Vec<u8>) {
if let Err(e) = self.respond(packet_id, packet_body) {
debug!(target: "les", "Error responding to peer message: {}", e);
debug!(target: "pip", "Error responding to peer message: {}", e);
}
}
@ -83,16 +83,17 @@ pub trait BasicContext {
fn persistent_peer_id(&self, peer: PeerId) -> Option<NodeId>;
/// Make a request from a peer.
fn request_from(&self, peer: PeerId, request: Request) -> Result<ReqId, Error>;
///
/// Fails on: nonexistent peer, network error, peer not server,
/// insufficient credits. Does not check capabilities before sending.
/// On success, returns a request id which can later be coordinated
/// with an event.
fn request_from(&self, peer: PeerId, request: Requests) -> Result<ReqId, Error>;
/// Make an announcement of new capabilities to the rest of the peers.
// TODO: maybe just put this on a timer in LightProtocol?
fn make_announcement(&self, announcement: Announcement);
/// Find the maximum number of requests of a specific type which can be made from
/// supplied peer.
fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize;
/// Disconnect a peer.
fn disconnect_peer(&self, peer: PeerId);
@ -123,18 +124,14 @@ impl<'a> BasicContext for TickCtx<'a> {
self.io.persistent_peer_id(id)
}
fn request_from(&self, peer: PeerId, request: Request) -> Result<ReqId, Error> {
self.proto.request_from(self.io, &peer, request)
fn request_from(&self, peer: PeerId, requests: Requests) -> Result<ReqId, Error> {
self.proto.request_from(self.io, &peer, requests)
}
fn make_announcement(&self, announcement: Announcement) {
self.proto.make_announcement(self.io, announcement);
}
fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize {
self.proto.max_requests(peer, kind)
}
fn disconnect_peer(&self, peer: PeerId) {
self.io.disconnect_peer(peer);
}
@ -160,18 +157,14 @@ impl<'a> BasicContext for Ctx<'a> {
self.io.persistent_peer_id(id)
}
fn request_from(&self, peer: PeerId, request: Request) -> Result<ReqId, Error> {
self.proto.request_from(self.io, &peer, request)
fn request_from(&self, peer: PeerId, requests: Requests) -> Result<ReqId, Error> {
self.proto.request_from(self.io, &peer, requests)
}
fn make_announcement(&self, announcement: Announcement) {
self.proto.make_announcement(self.io, announcement);
}
fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize {
self.proto.max_requests(peer, kind)
}
fn disconnect_peer(&self, peer: PeerId) {
self.io.disconnect_peer(peer);
}

View File

@ -56,6 +56,8 @@ pub enum Error {
UnknownPeer,
/// Unsolicited response.
UnsolicitedResponse,
/// Bad back-reference in request.
BadBackReference,
/// Not a server.
NotServer,
/// Unsupported protocol version.
@ -78,6 +80,7 @@ impl Error {
Error::WrongNetwork => Punishment::Disable,
Error::UnknownPeer => Punishment::Disconnect,
Error::UnsolicitedResponse => Punishment::Disable,
Error::BadBackReference => Punishment::Disable,
Error::NotServer => Punishment::Disable,
Error::UnsupportedProtocolVersion(_) => Punishment::Disable,
Error::BadProtocolVersion => Punishment::Disable,
@ -109,6 +112,7 @@ impl fmt::Display for Error {
Error::WrongNetwork => write!(f, "Wrong network"),
Error::UnknownPeer => write!(f, "Unknown peer"),
Error::UnsolicitedResponse => write!(f, "Peer provided unsolicited data"),
Error::BadBackReference => write!(f, "Bad back-reference in request."),
Error::NotServer => write!(f, "Peer not a server."),
Error::UnsupportedProtocolVersion(pv) => write!(f, "Unsupported protocol version: {}", pv),
Error::BadProtocolVersion => write!(f, "Bad protocol version in handshake"),

File diff suppressed because it is too large Load Diff

View File

@ -26,18 +26,13 @@
//! Current default costs are picked completely arbitrarily, not based
//! on any empirical timings or mathematical models.
use request;
use super::packet;
use request::{self, Request};
use super::error::Error;
use rlp::*;
use util::U256;
use time::{Duration, SteadyTime};
/// A request cost specification.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Cost(pub U256, pub U256);
/// Credits value.
///
/// Produced and recharged using `FlowParams`.
@ -81,92 +76,95 @@ impl Credits {
/// A cost table, mapping requests to base and per-request costs.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CostTable {
headers: Cost, // cost per header
bodies: Cost,
receipts: Cost,
state_proofs: Cost,
contract_codes: Cost,
header_proofs: Cost,
transaction_proof: Cost, // cost per gas.
base: U256, // cost per packet.
headers: U256, // cost per header
body: U256,
receipts: U256,
account: U256,
storage: U256,
code: U256,
header_proof: U256,
transaction_proof: U256, // cost per gas.
}
impl Default for CostTable {
fn default() -> Self {
// arbitrarily chosen constants.
CostTable {
headers: Cost(100000.into(), 10000.into()),
bodies: Cost(150000.into(), 15000.into()),
receipts: Cost(50000.into(), 5000.into()),
state_proofs: Cost(250000.into(), 25000.into()),
contract_codes: Cost(200000.into(), 20000.into()),
header_proofs: Cost(150000.into(), 15000.into()),
transaction_proof: Cost(100000.into(), 2.into()),
base: 100000.into(),
headers: 10000.into(),
body: 15000.into(),
receipts: 5000.into(),
account: 25000.into(),
storage: 25000.into(),
code: 20000.into(),
header_proof: 15000.into(),
transaction_proof: 2.into(),
}
}
}
impl Encodable for CostTable {
fn rlp_append(&self, s: &mut RlpStream) {
fn append_cost(s: &mut RlpStream, msg_id: u8, cost: &Cost) {
s.begin_list(3)
.append(&msg_id)
.append(&cost.0)
.append(&cost.1);
fn append_cost(s: &mut RlpStream, cost: &U256, kind: request::Kind) {
s.begin_list(2);
// hack around https://github.com/ethcore/parity/issues/4356
Encodable::rlp_append(&kind, s);
s.append(cost);
}
s.begin_list(7);
append_cost(s, packet::GET_BLOCK_HEADERS, &self.headers);
append_cost(s, packet::GET_BLOCK_BODIES, &self.bodies);
append_cost(s, packet::GET_RECEIPTS, &self.receipts);
append_cost(s, packet::GET_PROOFS, &self.state_proofs);
append_cost(s, packet::GET_CONTRACT_CODES, &self.contract_codes);
append_cost(s, packet::GET_HEADER_PROOFS, &self.header_proofs);
append_cost(s, packet::GET_TRANSACTION_PROOF, &self.transaction_proof);
s.begin_list(9).append(&self.base);
append_cost(s, &self.headers, request::Kind::Headers);
append_cost(s, &self.body, request::Kind::Body);
append_cost(s, &self.receipts, request::Kind::Receipts);
append_cost(s, &self.account, request::Kind::Account);
append_cost(s, &self.storage, request::Kind::Storage);
append_cost(s, &self.code, request::Kind::Code);
append_cost(s, &self.header_proof, request::Kind::HeaderProof);
append_cost(s, &self.transaction_proof, request::Kind::Execution);
}
}
impl RlpDecodable for CostTable {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let rlp = decoder.as_rlp();
impl Decodable for CostTable {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let base = rlp.val_at(0)?;
let mut headers = None;
let mut bodies = None;
let mut body = None;
let mut receipts = None;
let mut state_proofs = None;
let mut contract_codes = None;
let mut header_proofs = None;
let mut account = None;
let mut storage = None;
let mut code = None;
let mut header_proof = None;
let mut transaction_proof = None;
for row in rlp.iter() {
let msg_id: u8 = row.val_at(0)?;
let cost = {
let base = row.val_at(1)?;
let per = row.val_at(2)?;
Cost(base, per)
};
match msg_id {
packet::GET_BLOCK_HEADERS => headers = Some(cost),
packet::GET_BLOCK_BODIES => bodies = Some(cost),
packet::GET_RECEIPTS => receipts = Some(cost),
packet::GET_PROOFS => state_proofs = Some(cost),
packet::GET_CONTRACT_CODES => contract_codes = Some(cost),
packet::GET_HEADER_PROOFS => header_proofs = Some(cost),
packet::GET_TRANSACTION_PROOF => transaction_proof = Some(cost),
_ => return Err(DecoderError::Custom("Unrecognized message in cost table")),
for cost_list in rlp.iter().skip(1) {
let cost = cost_list.val_at(1)?;
match cost_list.val_at(0)? {
request::Kind::Headers => headers = Some(cost),
request::Kind::Body => body = Some(cost),
request::Kind::Receipts => receipts = Some(cost),
request::Kind::Account => account = Some(cost),
request::Kind::Storage => storage = Some(cost),
request::Kind::Code => code = Some(cost),
request::Kind::HeaderProof => header_proof = Some(cost),
request::Kind::Execution => transaction_proof = Some(cost),
}
}
let unwrap_cost = |cost: Option<U256>| cost.ok_or(DecoderError::Custom("Not all costs specified in cost table."));
Ok(CostTable {
headers: headers.ok_or(DecoderError::Custom("No headers cost specified"))?,
bodies: bodies.ok_or(DecoderError::Custom("No bodies cost specified"))?,
receipts: receipts.ok_or(DecoderError::Custom("No receipts cost specified"))?,
state_proofs: state_proofs.ok_or(DecoderError::Custom("No proofs cost specified"))?,
contract_codes: contract_codes.ok_or(DecoderError::Custom("No contract codes specified"))?,
header_proofs: header_proofs.ok_or(DecoderError::Custom("No header proofs cost specified"))?,
transaction_proof: transaction_proof.ok_or(DecoderError::Custom("No transaction proof gas cost specified"))?,
base: base,
headers: unwrap_cost(headers)?,
body: unwrap_cost(body)?,
receipts: unwrap_cost(receipts)?,
account: unwrap_cost(account)?,
storage: unwrap_cost(storage)?,
code: unwrap_cost(code)?,
header_proof: unwrap_cost(header_proof)?,
transaction_proof: unwrap_cost(transaction_proof)?,
})
}
}
@ -192,17 +190,19 @@ impl FlowParams {
/// Create effectively infinite flow params.
pub fn free() -> Self {
let free_cost = Cost(0.into(), 0.into());
let free_cost: U256 = 0.into();
FlowParams {
limit: (!0u64).into(),
recharge: 1.into(),
costs: CostTable {
base: free_cost.clone(),
headers: free_cost.clone(),
bodies: free_cost.clone(),
body: free_cost.clone(),
receipts: free_cost.clone(),
state_proofs: free_cost.clone(),
contract_codes: free_cost.clone(),
header_proofs: free_cost.clone(),
account: free_cost.clone(),
storage: free_cost.clone(),
code: free_cost.clone(),
header_proof: free_cost.clone(),
transaction_proof: free_cost,
}
}
@ -214,61 +214,34 @@ impl FlowParams {
/// Get a reference to the cost table.
pub fn cost_table(&self) -> &CostTable { &self.costs }
/// Get the base cost of a request.
pub fn base_cost(&self) -> U256 { self.costs.base }
/// Get a reference to the recharge rate.
pub fn recharge_rate(&self) -> &U256 { &self.recharge }
/// Compute the actual cost of a request, given the kind of request
/// and number of requests made.
pub fn compute_cost(&self, kind: request::Kind, amount: usize) -> U256 {
let cost = match kind {
request::Kind::Headers => &self.costs.headers,
request::Kind::Bodies => &self.costs.bodies,
request::Kind::Receipts => &self.costs.receipts,
request::Kind::StateProofs => &self.costs.state_proofs,
request::Kind::Codes => &self.costs.contract_codes,
request::Kind::HeaderProofs => &self.costs.header_proofs,
request::Kind::TransactionProof => &self.costs.transaction_proof,
};
let amount: U256 = amount.into();
cost.0 + (amount * cost.1)
}
/// Compute the maximum number of costs of a specific kind which can be made
/// with the given amount of credits
/// Saturates at `usize::max()`. This is not a problem in practice because
/// this amount of requests is already prohibitively large.
pub fn max_amount(&self, credits: &Credits, kind: request::Kind) -> usize {
use util::Uint;
use std::usize;
let cost = match kind {
request::Kind::Headers => &self.costs.headers,
request::Kind::Bodies => &self.costs.bodies,
request::Kind::Receipts => &self.costs.receipts,
request::Kind::StateProofs => &self.costs.state_proofs,
request::Kind::Codes => &self.costs.contract_codes,
request::Kind::HeaderProofs => &self.costs.header_proofs,
request::Kind::TransactionProof => &self.costs.transaction_proof,
};
let start = credits.current();
if start <= cost.0 {
return 0;
} else if cost.1 == U256::zero() {
return usize::MAX;
}
let max = (start - cost.0) / cost.1;
if max >= usize::MAX.into() {
usize::MAX
} else {
max.as_u64() as usize
pub fn compute_cost(&self, request: &Request) -> U256 {
match *request {
Request::Headers(ref req) => self.costs.headers * req.max.into(),
Request::HeaderProof(_) => self.costs.header_proof,
Request::Body(_) => self.costs.body,
Request::Receipts(_) => self.costs.receipts,
Request::Account(_) => self.costs.account,
Request::Storage(_) => self.costs.storage,
Request::Code(_) => self.costs.code,
Request::Execution(ref req) => self.costs.transaction_proof * req.gas,
}
}
/// Create initial credits..
/// Compute the cost of a set of requests.
/// This is the base cost plus the cost of each individual request.
pub fn compute_cost_multi(&self, requests: &[Request]) -> U256 {
requests.iter().fold(self.costs.base, |cost, req| cost + self.compute_cost(req))
}
/// Create initial credits.
pub fn create_credits(&self) -> Credits {
Credits {
estimate: self.limit,

View File

@ -24,7 +24,8 @@
use std::collections::{BTreeMap, HashMap};
use std::iter::FromIterator;
use request::{self, Request};
use request::Request;
use request::Requests;
use net::{timeout, ReqId};
use time::{Duration, SteadyTime};
@ -35,7 +36,7 @@ pub struct RequestSet {
counter: u64,
base: Option<SteadyTime>,
ids: HashMap<ReqId, u64>,
reqs: BTreeMap<u64, Request>,
reqs: BTreeMap<u64, Requests>,
}
impl Default for RequestSet {
@ -50,8 +51,8 @@ impl Default for RequestSet {
}
impl RequestSet {
/// Push a request onto the stack.
pub fn insert(&mut self, req_id: ReqId, req: Request, now: SteadyTime) {
/// Push requests onto the stack.
pub fn insert(&mut self, req_id: ReqId, req: Requests, now: SteadyTime) {
let counter = self.counter;
self.ids.insert(req_id, counter);
self.reqs.insert(counter, req);
@ -63,8 +64,8 @@ impl RequestSet {
self.counter += 1;
}
/// Remove a request from the stack.
pub fn remove(&mut self, req_id: &ReqId, now: SteadyTime) -> Option<Request> {
/// Remove a set of requests from the stack.
pub fn remove(&mut self, req_id: &ReqId, now: SteadyTime) -> Option<Requests> {
let id = match self.ids.remove(&req_id) {
Some(id) => id,
None => return None,
@ -89,22 +90,10 @@ impl RequestSet {
None => return false,
};
let kind = self.reqs.values()
.next()
.map(|r| r.kind())
.expect("base time implies `reqs` non-empty; qed");
let first_req = self.reqs.values().next()
.expect("base existing implies `reqs` non-empty; qed");
let kind_timeout = match kind {
request::Kind::Headers => timeout::HEADERS,
request::Kind::Bodies => timeout::BODIES,
request::Kind::Receipts => timeout::RECEIPTS,
request::Kind::StateProofs => timeout::PROOFS,
request::Kind::Codes => timeout::CONTRACT_CODES,
request::Kind::HeaderProofs => timeout::HEADER_PROOFS,
request::Kind::TransactionProof => timeout::TRANSACTION_PROOF,
};
base + Duration::milliseconds(kind_timeout) <= now
base + compute_timeout(&first_req) <= now
}
/// Collect all pending request ids.
@ -121,25 +110,43 @@ impl RequestSet {
pub fn is_empty(&self) -> bool { self.len() == 0 }
}
// helper to calculate timeout for a specific set of requests.
// it's a base amount + some amount per request.
fn compute_timeout(reqs: &Requests) -> Duration {
Duration::milliseconds(reqs.requests().iter().fold(timeout::BASE, |tm, req| {
tm + match *req {
Request::Headers(_) => timeout::HEADERS,
Request::HeaderProof(_) => timeout::HEADER_PROOF,
Request::Receipts(_) => timeout::RECEIPT,
Request::Body(_) => timeout::BODY,
Request::Account(_) => timeout::PROOF,
Request::Storage(_) => timeout::PROOF,
Request::Code(_) => timeout::CONTRACT_CODE,
Request::Execution(_) => timeout::TRANSACTION_PROOF,
}
}))
}
#[cfg(test)]
mod tests {
use net::{timeout, ReqId};
use request::{Request, Receipts};
use net::ReqId;
use request::RequestBuilder;
use time::{SteadyTime, Duration};
use super::RequestSet;
use super::{RequestSet, compute_timeout};
#[test]
fn multi_timeout() {
let test_begin = SteadyTime::now();
let mut req_set = RequestSet::default();
let the_req = Request::Receipts(Receipts { block_hashes: Vec::new() });
let the_req = RequestBuilder::default().build();
let req_time = compute_timeout(&the_req);
req_set.insert(ReqId(0), the_req.clone(), test_begin);
req_set.insert(ReqId(1), the_req, test_begin + Duration::seconds(1));
assert_eq!(req_set.base, Some(test_begin));
let test_end = test_begin + Duration::milliseconds(timeout::RECEIPTS);
let test_end = test_begin + req_time;
assert!(req_set.check_timeout(test_end));
req_set.remove(&ReqId(0), test_begin + Duration::seconds(1)).unwrap();

View File

@ -16,7 +16,7 @@
//! Peer status and capabilities.
use rlp::{DecoderError, RlpDecodable, Encodable, RlpStream, UntrustedRlp, View};
use rlp::{DecoderError, Encodable, Decodable, RlpStream, UntrustedRlp};
use util::{H256, U256};
use super::request_credits::FlowParams;
@ -91,7 +91,7 @@ struct Parser<'a> {
impl<'a> Parser<'a> {
// expect a specific next key, and decode the value.
// error on unexpected key or invalid value.
fn expect<T: RlpDecodable>(&mut self, key: Key) -> Result<T, DecoderError> {
fn expect<T: Decodable>(&mut self, key: Key) -> Result<T, DecoderError> {
self.expect_raw(key).and_then(|item| item.as_val())
}
@ -110,7 +110,7 @@ impl<'a> Parser<'a> {
// get the next key and value RLP.
fn get_next(&mut self) -> Result<Option<(Key, UntrustedRlp<'a>)>, DecoderError> {
while self.pos < self.rlp.item_count() {
while self.pos < self.rlp.item_count()? {
let pair = self.rlp.at(self.pos)?;
let k: String = pair.val_at(0)?;
@ -374,7 +374,7 @@ mod tests {
use super::*;
use super::super::request_credits::FlowParams;
use util::{U256, H256};
use rlp::{RlpStream, UntrustedRlp, View};
use rlp::{RlpStream, UntrustedRlp};
#[test]
fn full_handshake() {
@ -474,7 +474,7 @@ mod tests {
let handshake = write_handshake(&status, &capabilities, Some(&flow_params));
let interleaved = {
let handshake = UntrustedRlp::new(&handshake);
let mut stream = RlpStream::new_list(handshake.item_count() * 3);
let mut stream = RlpStream::new_list(handshake.item_count().unwrap_or(0) * 3);
for item in handshake.iter() {
stream.append_raw(item.as_raw(), 1);

View File

@ -27,15 +27,31 @@ use network::{PeerId, NodeId};
use net::request_credits::FlowParams;
use net::context::IoContext;
use net::status::{Capabilities, Status, write_handshake};
use net::{encode_request, LightProtocol, Params, packet, Peer};
use net::{LightProtocol, Params, packet, Peer};
use provider::Provider;
use request::{self, Request, Headers};
use request;
use request::*;
use rlp::*;
use util::{Address, Bytes, DBValue, H256, U256};
use util::{Address, H256, U256};
use std::sync::Arc;
// helper for encoding a single request into a packet.
// panics on bad backreference.
fn encode_single(request: Request) -> Requests {
let mut builder = RequestBuilder::default();
builder.push(request).unwrap();
builder.build()
}
// helper for making a packet out of `Requests`.
fn make_packet(req_id: usize, requests: &Requests) -> Vec<u8> {
let mut stream = RlpStream::new_list(2);
stream.append(&req_id).append_list(&requests.requests());
stream.out()
}
// expected result from a call.
#[derive(Debug, PartialEq, Eq)]
enum Expect {
@ -99,35 +115,45 @@ impl Provider for TestProvider {
self.0.client.block_header(id)
}
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
self.0.client.block_body(id)
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
self.0.client.block_body(req)
}
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
self.0.client.block_receipts(&hash)
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
self.0.client.block_receipts(req)
}
fn state_proof(&self, req: request::StateProof) -> Vec<Bytes> {
match req.key2 {
Some(_) => vec![::util::sha3::SHA3_NULL_RLP.to_vec()],
None => {
fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse> {
// sort of a leaf node
let mut stream = RlpStream::new_list(2);
stream.append(&req.key1).append_empty_data();
vec![stream.out()]
}
}
stream.append(&req.address_hash).append_empty_data();
Some(AccountResponse {
proof: vec![stream.out()],
balance: 10.into(),
nonce: 100.into(),
code_hash: Default::default(),
storage_root: Default::default(),
})
}
fn contract_code(&self, req: request::ContractCode) -> Bytes {
req.account_key.iter().chain(req.account_key.iter()).cloned().collect()
fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse> {
Some(StorageResponse {
proof: vec![::rlp::encode(&req.key_hash).to_vec()],
value: req.key_hash | req.address_hash,
})
}
fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
fn contract_code(&self, req: request::CompleteCodeRequest) -> Option<request::CodeResponse> {
Some(CodeResponse {
code: req.block_hash.iter().chain(req.code_hash.iter()).cloned().collect(),
})
}
fn header_proof(&self, _req: request::CompleteHeaderProofRequest) -> Option<request::HeaderProofResponse> {
None
}
fn transaction_proof(&self, _req: request::TransactionProof) -> Option<Vec<DBValue>> {
fn transaction_proof(&self, _req: request::CompleteExecutionRequest) -> Option<request::ExecutionResponse> {
None
}
@ -226,14 +252,15 @@ fn credit_overflow() {
}
// 1000 requests is far too many for the default flow params.
let request = encode_request(&Request::Headers(Headers {
start: 1.into(),
let requests = encode_single(Request::Headers(IncompleteHeadersRequest {
start: HashOrNumber::Number(1).into(),
max: 1000,
skip: 0,
reverse: false,
}), 111);
}));
let request = make_packet(111, &requests);
proto.handle_packet(&Expect::Punish(1), &1, packet::GET_BLOCK_HEADERS, &request);
proto.handle_packet(&Expect::Punish(1), &1, packet::REQUEST, &request);
}
// test the basic request types -- these just make sure that requests are parsed
@ -259,33 +286,36 @@ fn get_block_headers() {
proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status);
}
let request = Headers {
start: 1.into(),
let request = Request::Headers(IncompleteHeadersRequest {
start: HashOrNumber::Number(1).into(),
max: 10,
skip: 0,
reverse: false,
};
});
let req_id = 111;
let request_body = encode_request(&Request::Headers(request.clone()), req_id);
let requests = encode_single(request.clone());
let request_body = make_packet(req_id, &requests);
let response = {
let headers: Vec<_> = (0..10).map(|i| provider.client.block_header(BlockId::Number(i + 1)).unwrap()).collect();
assert_eq!(headers.len(), 10);
let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::Headers, 10);
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
let mut response_stream = RlpStream::new_list(3);
let response = vec![Response::Headers(HeadersResponse {
headers: headers,
})];
response_stream.append(&req_id).append(&new_creds).begin_list(10);
for header in headers {
response_stream.append_raw(&header.into_inner(), 1);
}
let mut stream = RlpStream::new_list(3);
stream.append(&req_id).append(&new_creds).append_list(&response);
response_stream.out()
stream.out()
};
let expected = Expect::Respond(packet::BLOCK_HEADERS, response);
proto.handle_packet(&expected, &1, packet::GET_BLOCK_HEADERS, &request_body);
let expected = Expect::Respond(packet::RESPONSE, response);
proto.handle_packet(&expected, &1, packet::REQUEST, &request_body);
}
#[test]
@ -308,33 +338,32 @@ fn get_block_bodies() {
proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status);
}
let request = request::Bodies {
block_hashes: (0..10).map(|i|
provider.client.block_header(BlockId::Number(i)).unwrap().hash()
).collect()
};
let mut builder = RequestBuilder::default();
let mut bodies = Vec::new();
for i in 0..10 {
let hash = provider.client.block_header(BlockId::Number(i)).unwrap().hash();
builder.push(Request::Body(IncompleteBodyRequest {
hash: hash.into(),
})).unwrap();
bodies.push(Response::Body(provider.client.block_body(CompleteBodyRequest {
hash: hash,
}).unwrap()));
}
let req_id = 111;
let requests = builder.build();
let request_body = make_packet(req_id, &requests);
let request_body = encode_request(&Request::Bodies(request.clone()), req_id);
let response = {
let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockId::Number(i + 1)).unwrap()).collect();
assert_eq!(bodies.len(), 10);
let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::Bodies, 10);
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
let mut response_stream = RlpStream::new_list(3);
response_stream.append(&req_id).append(&new_creds).begin_list(10);
for body in bodies {
response_stream.append_raw(&body.into_inner(), 1);
}
response_stream.append(&req_id).append(&new_creds).append_list(&bodies);
response_stream.out()
};
let expected = Expect::Respond(packet::BLOCK_BODIES, response);
proto.handle_packet(&expected, &1, packet::GET_BLOCK_BODIES, &request_body);
let expected = Expect::Respond(packet::RESPONSE, response);
proto.handle_packet(&expected, &1, packet::REQUEST, &request_body);
}
#[test]
@ -359,36 +388,37 @@ fn get_block_receipts() {
// find the first 10 block hashes starting with `f` because receipts are only provided
// by the test client in that case.
let block_hashes: Vec<_> = (0..1000).map(|i|
provider.client.block_header(BlockId::Number(i)).unwrap().hash()
).filter(|hash| format!("{}", hash).starts_with("f")).take(10).collect();
let request = request::Receipts {
block_hashes: block_hashes.clone(),
};
let req_id = 111;
let request_body = encode_request(&Request::Receipts(request.clone()), req_id);
let response = {
let receipts: Vec<_> = block_hashes.iter()
.map(|hash| provider.client.block_receipts(hash).unwrap())
let block_hashes: Vec<H256> = (0..1000)
.map(|i| provider.client.block_header(BlockId::Number(i)).unwrap().hash())
.filter(|hash| format!("{}", hash).starts_with("f"))
.take(10)
.collect();
let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::Receipts, receipts.len());
let mut response_stream = RlpStream::new_list(3);
response_stream.append(&req_id).append(&new_creds).begin_list(receipts.len());
for block_receipts in receipts {
response_stream.append_raw(&block_receipts, 1);
let mut builder = RequestBuilder::default();
let mut receipts = Vec::new();
for hash in block_hashes.iter().cloned() {
builder.push(Request::Receipts(IncompleteReceiptsRequest { hash: hash.into() })).unwrap();
receipts.push(Response::Receipts(provider.client.block_receipts(CompleteReceiptsRequest {
hash: hash
}).unwrap()));
}
let req_id = 111;
let requests = builder.build();
let request_body = make_packet(req_id, &requests);
let response = {
assert_eq!(receipts.len(), 10);
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
let mut response_stream = RlpStream::new_list(3);
response_stream.append(&req_id).append(&new_creds).append_list(&receipts);
response_stream.out()
};
let expected = Expect::Respond(packet::RECEIPTS, response);
proto.handle_packet(&expected, &1, packet::GET_RECEIPTS, &request_body);
let expected = Expect::Respond(packet::RESPONSE, response);
proto.handle_packet(&expected, &1, packet::REQUEST, &request_body);
}
#[test]
@ -397,8 +427,9 @@ fn get_state_proofs() {
let capabilities = capabilities();
let (provider, proto) = setup(flow_params.clone(), capabilities.clone());
let provider = TestProvider(provider);
let cur_status = status(provider.client.chain_info());
let cur_status = status(provider.0.client.chain_info());
{
let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params));
@ -407,40 +438,45 @@ fn get_state_proofs() {
}
let req_id = 112;
let key1 = U256::from(11223344).into();
let key2 = U256::from(99988887).into();
let key1: H256 = U256::from(11223344).into();
let key2: H256 = U256::from(99988887).into();
let request = Request::StateProofs (request::StateProofs {
requests: vec![
request::StateProof { block: H256::default(), key1: key1, key2: None, from_level: 0 },
request::StateProof { block: H256::default(), key1: key1, key2: Some(key2), from_level: 0},
]
});
let mut builder = RequestBuilder::default();
builder.push(Request::Account(IncompleteAccountRequest {
block_hash: H256::default().into(),
address_hash: key1.into(),
})).unwrap();
builder.push(Request::Storage(IncompleteStorageRequest {
block_hash: H256::default().into(),
address_hash: key1.into(),
key_hash: key2.into(),
})).unwrap();
let request_body = encode_request(&request, req_id);
let requests = builder.build();
let request_body = make_packet(req_id, &requests);
let response = {
let proofs = vec![
{ let mut stream = RlpStream::new_list(2); stream.append(&key1).append_empty_data(); vec![stream.out()] },
vec![::util::sha3::SHA3_NULL_RLP.to_vec()],
let responses = vec![
Response::Account(provider.account_proof(CompleteAccountRequest {
block_hash: H256::default(),
address_hash: key1,
}).unwrap()),
Response::Storage(provider.storage_proof(CompleteStorageRequest {
block_hash: H256::default(),
address_hash: key1,
key_hash: key2,
}).unwrap()),
];
let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::StateProofs, 2);
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
let mut response_stream = RlpStream::new_list(3);
response_stream.append(&req_id).append(&new_creds).begin_list(2);
for proof in proofs {
response_stream.begin_list(proof.len());
for node in proof {
response_stream.append_raw(&node, 1);
}
}
response_stream.append(&req_id).append(&new_creds).append_list(&responses);
response_stream.out()
};
let expected = Expect::Respond(packet::PROOFS, response);
proto.handle_packet(&expected, &1, packet::GET_PROOFS, &request_body);
let expected = Expect::Respond(packet::RESPONSE, response);
proto.handle_packet(&expected, &1, packet::REQUEST, &request_body);
}
#[test]
@ -459,37 +495,31 @@ fn get_contract_code() {
}
let req_id = 112;
let key1 = U256::from(11223344).into();
let key2 = U256::from(99988887).into();
let key1: H256 = U256::from(11223344).into();
let key2: H256 = U256::from(99988887).into();
let request = Request::Codes (request::ContractCodes {
code_requests: vec![
request::ContractCode { block_hash: H256::default(), account_key: key1 },
request::ContractCode { block_hash: H256::default(), account_key: key2 },
],
let request = Request::Code(IncompleteCodeRequest {
block_hash: key1.into(),
code_hash: key2.into(),
});
let request_body = encode_request(&request, req_id);
let requests = encode_single(request.clone());
let request_body = make_packet(req_id, &requests);
let response = {
let codes: Vec<Vec<_>> = vec![
key1.iter().chain(key1.iter()).cloned().collect(),
key2.iter().chain(key2.iter()).cloned().collect(),
];
let response = vec![Response::Code(CodeResponse {
code: key1.iter().chain(key2.iter()).cloned().collect(),
})];
let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::Codes, 2);
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
let mut response_stream = RlpStream::new_list(3);
response_stream.append(&req_id).append(&new_creds).begin_list(2);
for code in codes {
response_stream.append(&code);
}
response_stream.append(&req_id).append(&new_creds).append_list(&response);
response_stream.out()
};
let expected = Expect::Respond(packet::CONTRACT_CODES, response);
proto.handle_packet(&expected, &1, packet::GET_CONTRACT_CODES, &request_body);
let expected = Expect::Respond(packet::RESPONSE, response);
proto.handle_packet(&expected, &1, packet::REQUEST, &request_body);
}
#[test]
@ -508,8 +538,8 @@ fn proof_of_execution() {
}
let req_id = 112;
let mut request = Request::TransactionProof (request::TransactionProof {
at: H256::default(),
let mut request = Request::Execution(request::IncompleteExecutionRequest {
block_hash: H256::default().into(),
from: Address::default(),
action: Action::Call(Address::default()),
gas: 100.into(),
@ -519,9 +549,11 @@ fn proof_of_execution() {
});
// first: a valid amount to request execution of.
let request_body = encode_request(&request, req_id);
let requests = encode_single(request.clone());
let request_body = make_packet(req_id, &requests);
let response = {
let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::TransactionProof, 100);
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
let mut response_stream = RlpStream::new_list(3);
response_stream.append(&req_id).append(&new_creds).begin_list(0);
@ -529,17 +561,19 @@ fn proof_of_execution() {
response_stream.out()
};
let expected = Expect::Respond(packet::TRANSACTION_PROOF, response);
proto.handle_packet(&expected, &1, packet::GET_TRANSACTION_PROOF, &request_body);
let expected = Expect::Respond(packet::RESPONSE, response);
proto.handle_packet(&expected, &1, packet::REQUEST, &request_body);
// next: way too much requested gas.
if let Request::TransactionProof(ref mut req) = request {
if let Request::Execution(ref mut req) = request {
req.gas = 100_000_000.into();
}
let req_id = 113;
let request_body = encode_request(&request, req_id);
let requests = encode_single(request.clone());
let request_body = make_packet(req_id, &requests);
let expected = Expect::Punish(1);
proto.handle_packet(&expected, &1, packet::GET_TRANSACTION_PROOF, &request_body);
proto.handle_packet(&expected, &1, packet::REQUEST, &request_body);
}
#[test]
@ -554,12 +588,13 @@ fn id_guard() {
let req_id_1 = ReqId(5143);
let req_id_2 = ReqId(1111);
let req = Request::Headers(request::Headers {
start: 5u64.into(),
let req = encode_single(Request::Headers(IncompleteHeadersRequest {
start: HashOrNumber::Number(5u64).into(),
max: 100,
skip: 0,
reverse: false,
});
}));
let peer_id = 9876;
@ -579,15 +614,15 @@ fn id_guard() {
failed_requests: Vec::new(),
}));
// first, supply wrong request type.
// first, malformed responses.
{
let mut stream = RlpStream::new_list(3);
stream.append(&req_id_1.0);
stream.append(&4_000_000usize);
stream.begin_list(0);
stream.begin_list(2).append(&125usize).append(&3usize);
let packet = stream.out();
assert!(proto.block_bodies(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_err());
assert!(proto.response(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_err());
}
// next, do an unexpected response.
@ -598,7 +633,7 @@ fn id_guard() {
stream.begin_list(0);
let packet = stream.out();
assert!(proto.receipts(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_err());
assert!(proto.response(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_err());
}
// lastly, do a valid (but empty) response.
@ -609,7 +644,7 @@ fn id_guard() {
stream.begin_list(0);
let packet = stream.out();
assert!(proto.block_headers(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_ok());
assert!(proto.response(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_ok());
}
let peers = proto.peers.read();

View File

@ -18,6 +18,9 @@
//! The request service is implemented using Futures. Higher level request handlers
//! will take the raw data received here and extract meaningful results from it.
// TODO [ToDr] Suppressing deprecation warnings. Rob will fix the API anyway.
#![allow(deprecated)]
use std::collections::HashMap;
use std::sync::Arc;
@ -31,12 +34,12 @@ use futures::{Async, Poll, Future};
use futures::sync::oneshot::{self, Sender, Receiver};
use network::PeerId;
use rlp::RlpStream;
use util::{Bytes, DBValue, RwLock, Mutex, U256};
use util::{Bytes, RwLock, Mutex, U256, H256};
use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY_LIST_RLP};
use net::{Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId};
use cache::Cache;
use types::les_request::{self as les_request, Request as LesRequest};
use request::{self as basic_request, Request as NetworkRequest, Response as NetworkResponse};
pub mod request;
@ -46,24 +49,85 @@ struct Peer {
capabilities: Capabilities,
}
impl Peer {
// Whether a given peer can handle a specific request.
fn can_handle(&self, pending: &Pending) -> bool {
match *pending {
Pending::HeaderProof(ref req, _) =>
self.capabilities.serve_headers && self.status.head_num > req.num(),
Pending::HeaderByHash(_, _) => self.capabilities.serve_headers,
Pending::Block(ref req, _) =>
self.capabilities.serve_chain_since.as_ref().map_or(false, |x| *x >= req.header.number()),
Pending::BlockReceipts(ref req, _) =>
self.capabilities.serve_chain_since.as_ref().map_or(false, |x| *x >= req.0.number()),
Pending::Account(ref req, _) =>
self.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= req.header.number()),
Pending::Code(ref req, _) =>
self.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= req.block_id.1),
Pending::TxProof(ref req, _) =>
self.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= req.header.number()),
}
}
}
// Which portions of a CHT proof should be sent.
enum ChtProofSender {
Both(Sender<(encoded::Header, U256)>),
Header(Sender<encoded::Header>),
Both(Sender<(H256, U256)>),
Hash(Sender<H256>),
ChainScore(Sender<U256>),
}
// Attempted request info and sender to put received value.
enum Pending {
HeaderByNumber(request::HeaderByNumber, ChtProofSender),
HeaderProof(request::HeaderProof, ChtProofSender),
HeaderByHash(request::HeaderByHash, Sender<encoded::Header>),
Block(request::Body, Sender<encoded::Block>),
BlockReceipts(request::BlockReceipts, Sender<Vec<Receipt>>),
Account(request::Account, Sender<BasicAccount>),
Account(request::Account, Sender<Option<BasicAccount>>),
Code(request::Code, Sender<Bytes>),
TxProof(request::TransactionProof, Sender<Result<Executed, ExecutionError>>),
}
impl Pending {
// Create a network request.
fn make_request(&self) -> NetworkRequest {
match *self {
Pending::HeaderByHash(ref req, _) => NetworkRequest::Headers(basic_request::IncompleteHeadersRequest {
start: basic_request::HashOrNumber::Hash(req.0).into(),
skip: 0,
max: 1,
reverse: false,
}),
Pending::HeaderProof(ref req, _) => NetworkRequest::HeaderProof(basic_request::IncompleteHeaderProofRequest {
num: req.num().into(),
}),
Pending::Block(ref req, _) => NetworkRequest::Body(basic_request::IncompleteBodyRequest {
hash: req.hash.into(),
}),
Pending::BlockReceipts(ref req, _) => NetworkRequest::Receipts(basic_request::IncompleteReceiptsRequest {
hash: req.0.hash().into(),
}),
Pending::Account(ref req, _) => NetworkRequest::Account(basic_request::IncompleteAccountRequest {
block_hash: req.header.hash().into(),
address_hash: ::util::Hashable::sha3(&req.address).into(),
}),
Pending::Code(ref req, _) => NetworkRequest::Code(basic_request::IncompleteCodeRequest {
block_hash: req.block_id.0.into(),
code_hash: req.code_hash.into(),
}),
Pending::TxProof(ref req, _) => NetworkRequest::Execution(basic_request::IncompleteExecutionRequest {
block_hash: req.header.hash().into(),
from: req.tx.sender(),
gas: req.tx.gas,
gas_price: req.tx.gas_price,
action: req.tx.action.clone(),
value: req.tx.value,
data: req.tx.data.clone(),
}),
}
}
}
/// On demand request service. See module docs for more details.
/// Accumulates info about all peers' capabilities and dispatches
/// requests to them accordingly.
@ -74,6 +138,8 @@ pub struct OnDemand {
orphaned_requests: RwLock<Vec<Pending>>,
}
const RECEIVER_IN_SCOPE: &'static str = "Receiver is still in scope, so it's not dropped; qed";
impl OnDemand {
/// Create a new `OnDemand` service with the given cache.
pub fn new(cache: Arc<Mutex<Cache>>) -> Self {
@ -85,25 +151,25 @@ impl OnDemand {
}
}
/// Request a header by block number and CHT root hash.
/// Returns the header.
pub fn header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver<encoded::Header> {
/// Request a header's hash by block number and CHT root hash.
/// Returns the hash.
pub fn hash_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver<H256> {
let (sender, receiver) = oneshot::channel();
let cached = {
let mut cache = self.cache.lock();
cache.block_hash(&req.num()).and_then(|hash| cache.block_header(&hash))
cache.block_hash(&req.num())
};
match cached {
Some(hdr) => sender.complete(hdr),
None => self.dispatch_header_by_number(ctx, req, ChtProofSender::Header(sender)),
Some(hash) => sender.send(hash).expect(RECEIVER_IN_SCOPE),
None => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::Hash(sender))),
}
receiver
}
/// Request a canonical block's chain score.
/// Returns the chain score.
pub fn chain_score_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver<U256> {
pub fn chain_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver<U256> {
let (sender, receiver) = oneshot::channel();
let cached = {
let mut cache = self.cache.lock();
@ -111,123 +177,46 @@ impl OnDemand {
};
match cached {
Some(score) => sender.complete(score),
None => self.dispatch_header_by_number(ctx, req, ChtProofSender::ChainScore(sender)),
Some(score) => sender.send(score).expect(RECEIVER_IN_SCOPE),
None => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::ChainScore(sender))),
}
receiver
}
/// Request a canonical block's chain score.
/// Returns the header and chain score.
pub fn header_and_score_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver<(encoded::Header, U256)> {
/// Request a canonical block's hash and chain score by number.
/// Returns the hash and chain score.
pub fn hash_and_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver<(H256, U256)> {
let (sender, receiver) = oneshot::channel();
let cached = {
let mut cache = self.cache.lock();
let hash = cache.block_hash(&req.num());
(
hash.clone().and_then(|hash| cache.block_header(&hash)),
hash.clone(),
hash.and_then(|hash| cache.chain_score(&hash)),
)
};
match cached {
(Some(hdr), Some(score)) => sender.complete((hdr, score)),
_ => self.dispatch_header_by_number(ctx, req, ChtProofSender::Both(sender)),
(Some(hash), Some(score)) => sender.send((hash, score)).expect(RECEIVER_IN_SCOPE),
_ => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::Both(sender))),
}
receiver
}
// dispatch the request, completing the request if no peers available.
fn dispatch_header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber, sender: ChtProofSender) {
let num = req.num();
let cht_num = req.cht_num();
let les_req = LesRequest::HeaderProofs(les_request::HeaderProofs {
requests: vec![les_request::HeaderProof {
cht_number: cht_num,
block_number: num,
from_level: 0,
}],
});
let pending = Pending::HeaderByNumber(req, sender);
// we're looking for a peer with serveHeaders who's far enough along in the
// chain.
for (id, peer) in self.peers.read().iter() {
if peer.capabilities.serve_headers && peer.status.head_num >= num {
match ctx.request_from(*id, les_req.clone()) {
Ok(req_id) => {
trace!(target: "on_demand", "Assigning request to peer {}", id);
self.pending_requests.write().insert(
req_id,
pending,
);
return
},
Err(e) =>
trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e),
}
}
}
trace!(target: "on_demand", "No suitable peer for request");
self.orphaned_requests.write().push(pending)
}
/// Request a header by hash. This is less accurate than by-number because we don't know
/// where in the chain this header lies, and therefore can't find a peer who is supposed to have
/// it as easily.
pub fn header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash) -> Receiver<encoded::Header> {
let (sender, receiver) = oneshot::channel();
match self.cache.lock().block_header(&req.0) {
Some(hdr) => sender.complete(hdr),
None => self.dispatch_header_by_hash(ctx, req, sender),
Some(hdr) => sender.send(hdr).expect(RECEIVER_IN_SCOPE),
None => self.dispatch(ctx, Pending::HeaderByHash(req, sender)),
}
receiver
}
fn dispatch_header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash, sender: Sender<encoded::Header>) {
let les_req = LesRequest::Headers(les_request::Headers {
start: req.0.into(),
max: 1,
skip: 0,
reverse: false,
});
// all we've got is a hash, so we'll just guess at peers who might have
// it randomly.
let mut potential_peers = self.peers.read().iter()
.filter(|&(_, peer)| peer.capabilities.serve_headers)
.map(|(id, _)| *id)
.collect::<Vec<_>>();
let mut rng = ::rand::thread_rng();
::rand::Rng::shuffle(&mut rng, &mut potential_peers);
let pending = Pending::HeaderByHash(req, sender);
for id in potential_peers {
match ctx.request_from(id, les_req.clone()) {
Ok(req_id) => {
trace!(target: "on_demand", "Assigning request to peer {}", id);
self.pending_requests.write().insert(
req_id,
pending,
);
return
}
Err(e) =>
trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e),
}
}
trace!(target: "on_demand", "No suitable peer for request");
self.orphaned_requests.write().push(pending)
}
/// Request a block, given its header. Block bodies are requestable by hash only,
/// and the header is required anyway to verify and complete the block body
/// -- this just doesn't obscure the network query.
@ -241,7 +230,7 @@ impl OnDemand {
stream.begin_list(0);
stream.begin_list(0);
sender.complete(encoded::Block::new(stream.out()))
sender.send(encoded::Block::new(stream.out())).expect(RECEIVER_IN_SCOPE);
} else {
match self.cache.lock().block_body(&req.hash) {
Some(body) => {
@ -249,43 +238,14 @@ impl OnDemand {
stream.append_raw(&req.header.into_inner(), 1);
stream.append_raw(&body.into_inner(), 2);
sender.complete(encoded::Block::new(stream.out()));
sender.send(encoded::Block::new(stream.out())).expect(RECEIVER_IN_SCOPE);
}
None => self.dispatch_block(ctx, req, sender),
None => self.dispatch(ctx, Pending::Block(req, sender)),
}
}
receiver
}
fn dispatch_block(&self, ctx: &BasicContext, req: request::Body, sender: Sender<encoded::Block>) {
let num = req.header.number();
let les_req = LesRequest::Bodies(les_request::Bodies {
block_hashes: vec![req.hash],
});
let pending = Pending::Block(req, sender);
// we're looking for a peer with serveChainSince(num)
for (id, peer) in self.peers.read().iter() {
if peer.capabilities.serve_chain_since.as_ref().map_or(false, |x| *x >= num) {
match ctx.request_from(*id, les_req.clone()) {
Ok(req_id) => {
trace!(target: "on_demand", "Assigning request to peer {}", id);
self.pending_requests.write().insert(
req_id,
pending,
);
return
}
Err(e) =>
trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e),
}
}
}
trace!(target: "on_demand", "No suitable peer for request");
self.orphaned_requests.write().push(pending)
}
/// Request the receipts for a block. The header serves two purposes:
/// provide the block hash to fetch receipts for, and for verification of the receipts root.
pub fn block_receipts(&self, ctx: &BasicContext, req: request::BlockReceipts) -> Receiver<Vec<Receipt>> {
@ -293,165 +253,64 @@ impl OnDemand {
// fast path for empty receipts.
if req.0.receipts_root() == SHA3_NULL_RLP {
sender.complete(Vec::new())
sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE);
} else {
match self.cache.lock().block_receipts(&req.0.hash()) {
Some(receipts) => sender.complete(receipts),
None => self.dispatch_block_receipts(ctx, req, sender),
Some(receipts) => sender.send(receipts).expect(RECEIVER_IN_SCOPE),
None => self.dispatch(ctx, Pending::BlockReceipts(req, sender)),
}
}
receiver
}
fn dispatch_block_receipts(&self, ctx: &BasicContext, req: request::BlockReceipts, sender: Sender<Vec<Receipt>>) {
let num = req.0.number();
let les_req = LesRequest::Receipts(les_request::Receipts {
block_hashes: vec![req.0.hash()],
});
let pending = Pending::BlockReceipts(req, sender);
// we're looking for a peer with serveChainSince(num)
for (id, peer) in self.peers.read().iter() {
if peer.capabilities.serve_chain_since.as_ref().map_or(false, |x| *x >= num) {
match ctx.request_from(*id, les_req.clone()) {
Ok(req_id) => {
trace!(target: "on_demand", "Assigning request to peer {}", id);
self.pending_requests.write().insert(
req_id,
pending,
);
return
}
Err(e) =>
trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e),
}
}
}
trace!(target: "on_demand", "No suitable peer for request");
self.orphaned_requests.write().push(pending)
}
/// Request an account by address and block header -- which gives a hash to query and a state root
/// to verify against.
pub fn account(&self, ctx: &BasicContext, req: request::Account) -> Receiver<BasicAccount> {
pub fn account(&self, ctx: &BasicContext, req: request::Account) -> Receiver<Option<BasicAccount>> {
let (sender, receiver) = oneshot::channel();
self.dispatch_account(ctx, req, sender);
self.dispatch(ctx, Pending::Account(req, sender));
receiver
}
fn dispatch_account(&self, ctx: &BasicContext, req: request::Account, sender: Sender<BasicAccount>) {
let num = req.header.number();
let les_req = LesRequest::StateProofs(les_request::StateProofs {
requests: vec![les_request::StateProof {
block: req.header.hash(),
key1: ::util::Hashable::sha3(&req.address),
key2: None,
from_level: 0,
}],
});
let pending = Pending::Account(req, sender);
// we're looking for a peer with serveStateSince(num)
for (id, peer) in self.peers.read().iter() {
if peer.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= num) {
match ctx.request_from(*id, les_req.clone()) {
Ok(req_id) => {
trace!(target: "on_demand", "Assigning request to peer {}", id);
self.pending_requests.write().insert(
req_id,
pending,
);
return
}
Err(e) =>
trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e),
}
}
}
trace!(target: "on_demand", "No suitable peer for request");
self.orphaned_requests.write().push(pending)
}
/// Request code by address, known code hash, and block header.
pub fn code(&self, ctx: &BasicContext, req: request::Code) -> Receiver<Bytes> {
let (sender, receiver) = oneshot::channel();
// fast path for no code.
if req.code_hash == ::util::sha3::SHA3_EMPTY {
sender.complete(Vec::new())
sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE)
} else {
self.dispatch_code(ctx, req, sender);
self.dispatch(ctx, Pending::Code(req, sender));
}
receiver
}
fn dispatch_code(&self, ctx: &BasicContext, req: request::Code, sender: Sender<Bytes>) {
let num = req.block_id.1;
let les_req = LesRequest::Codes(les_request::ContractCodes {
code_requests: vec![les_request::ContractCode {
block_hash: req.block_id.0,
account_key: ::util::Hashable::sha3(&req.address),
}]
});
let pending = Pending::Code(req, sender);
// we're looking for a peer with serveStateSince(num)
for (id, peer) in self.peers.read().iter() {
if peer.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= num) {
match ctx.request_from(*id, les_req.clone()) {
Ok(req_id) => {
trace!(target: "on_demand", "Assigning request to peer {}", id);
self.pending_requests.write().insert(
req_id,
pending
);
return
}
Err(e) =>
trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e),
}
}
}
trace!(target: "on_demand", "No suitable peer for request");
self.orphaned_requests.write().push(pending)
}
/// Request proof-of-execution for a transaction.
pub fn transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof) -> Receiver<Result<Executed, ExecutionError>> {
let (sender, receiver) = oneshot::channel();
self.dispatch_transaction_proof(ctx, req, sender);
self.dispatch(ctx, Pending::TxProof(req, sender));
receiver
}
fn dispatch_transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof, sender: Sender<Result<Executed, ExecutionError>>) {
let num = req.header.number();
let les_req = LesRequest::TransactionProof(les_request::TransactionProof {
at: req.header.hash(),
from: req.tx.sender(),
gas: req.tx.gas,
gas_price: req.tx.gas_price,
action: req.tx.action.clone(),
value: req.tx.value,
data: req.tx.data.clone(),
});
let pending = Pending::TxProof(req, sender);
// dispatch the request, with a "suitability" function to filter acceptable peers.
fn dispatch(&self, ctx: &BasicContext, pending: Pending) {
let mut builder = basic_request::RequestBuilder::default();
builder.push(pending.make_request())
.expect("make_request always returns fully complete request; qed");
let complete = builder.build();
// we're looking for a peer with serveStateSince(num)
for (id, peer) in self.peers.read().iter() {
if peer.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= num) {
match ctx.request_from(*id, les_req.clone()) {
if !peer.can_handle(&pending) { continue }
match ctx.request_from(*id, complete.clone()) {
Ok(req_id) => {
trace!(target: "on_demand", "Assigning request to peer {}", id);
self.pending_requests.write().insert(
req_id,
pending
pending,
);
return
}
@ -459,12 +318,12 @@ impl OnDemand {
trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e),
}
}
}
trace!(target: "on_demand", "No suitable peer for request");
self.orphaned_requests.write().push(pending)
self.orphaned_requests.write().push(pending);
}
// dispatch orphaned requests, and discard those for which the corresponding
// receiver has been dropped.
fn dispatch_orphaned(&self, ctx: &BasicContext) {
@ -494,30 +353,22 @@ impl OnDemand {
let to_dispatch = ::std::mem::replace(&mut *self.orphaned_requests.write(), Vec::new());
for orphaned in to_dispatch {
match orphaned {
Pending::HeaderByNumber(req, mut sender) => {
let hangup = match sender {
for mut orphaned in to_dispatch {
let hung_up = match orphaned {
Pending::HeaderProof(_, ref mut sender) => match *sender {
ChtProofSender::Both(ref mut s) => check_hangup(s),
ChtProofSender::Header(ref mut s) => check_hangup(s),
ChtProofSender::Hash(ref mut s) => check_hangup(s),
ChtProofSender::ChainScore(ref mut s) => check_hangup(s),
},
Pending::HeaderByHash(_, ref mut sender) => check_hangup(sender),
Pending::Block(_, ref mut sender) => check_hangup(sender),
Pending::BlockReceipts(_, ref mut sender) => check_hangup(sender),
Pending::Account(_, ref mut sender) => check_hangup(sender),
Pending::Code(_, ref mut sender) => check_hangup(sender),
Pending::TxProof(_, ref mut sender) => check_hangup(sender),
};
if !hangup { self.dispatch_header_by_number(ctx, req, sender) }
}
Pending::HeaderByHash(req, mut sender) =>
if !check_hangup(&mut sender) { self.dispatch_header_by_hash(ctx, req, sender) },
Pending::Block(req, mut sender) =>
if !check_hangup(&mut sender) { self.dispatch_block(ctx, req, sender) },
Pending::BlockReceipts(req, mut sender) =>
if !check_hangup(&mut sender) { self.dispatch_block_receipts(ctx, req, sender) },
Pending::Account(req, mut sender) =>
if !check_hangup(&mut sender) { self.dispatch_account(ctx, req, sender) },
Pending::Code(req, mut sender) =>
if !check_hangup(&mut sender) { self.dispatch_code(ctx, req, sender) },
Pending::TxProof(req, mut sender) =>
if !check_hangup(&mut sender) { self.dispatch_transaction_proof(ctx, req, sender) }
}
if !hung_up { self.dispatch(ctx, orphaned) }
}
}
}
@ -555,218 +406,126 @@ impl Handler for OnDemand {
self.dispatch_orphaned(ctx.as_basic());
}
fn on_header_proofs(&self, ctx: &EventContext, req_id: ReqId, proofs: &[(Bytes, Vec<Bytes>)]) {
fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[basic_request::Response]) {
let peer = ctx.peer();
let req = match self.pending_requests.write().remove(&req_id) {
Some(req) => req,
None => return,
};
let response = match responses.get(0) {
Some(response) => response,
None => {
trace!(target: "on_demand", "Ignoring empty response for request {}", req_id);
self.dispatch(ctx.as_basic(), req);
return;
}
};
// handle the response appropriately for the request.
// all branches which do not return early lead to disabling of the peer
// due to misbehavior.
match req {
Pending::HeaderByNumber(req, sender) => {
if let Some(&(ref header, ref proof)) = proofs.get(0) {
match req.check_response(header, proof) {
Ok((header, score)) => {
Pending::HeaderProof(req, sender) => {
if let NetworkResponse::HeaderProof(ref response) = *response {
match req.check_response(&response.proof) {
Ok((hash, score)) => {
let mut cache = self.cache.lock();
let hash = header.hash();
cache.insert_block_header(hash, header.clone());
cache.insert_block_hash(header.number(), hash);
cache.insert_block_hash(req.num(), hash);
cache.insert_chain_score(hash, score);
match sender {
ChtProofSender::Both(sender) => sender.complete((header, score)),
ChtProofSender::Header(sender) => sender.complete(header),
ChtProofSender::ChainScore(sender) => sender.complete(score),
ChtProofSender::Both(sender) => { let _ = sender.send((hash, score)); }
ChtProofSender::Hash(sender) => { let _ = sender.send(hash); }
ChtProofSender::ChainScore(sender) => { let _ = sender.send(score); }
}
return
}
Err(e) => {
warn!("Error handling response for header request: {:?}", e);
ctx.disable_peer(peer);
Err(e) => warn!("Error handling response for header request: {:?}", e),
}
}
}
self.dispatch_header_by_number(ctx.as_basic(), req, sender);
}
_ => panic!("Only header by number request fetches header proofs; qed"),
}
}
fn on_block_headers(&self, ctx: &EventContext, req_id: ReqId, headers: &[Bytes]) {
let peer = ctx.peer();
let req = match self.pending_requests.write().remove(&req_id) {
Some(req) => req,
None => return,
};
match req {
Pending::HeaderByHash(req, sender) => {
if let Some(ref header) = headers.get(0) {
if let NetworkResponse::Headers(ref response) = *response {
if let Some(header) = response.headers.get(0) {
match req.check_response(header) {
Ok(header) => {
self.cache.lock().insert_block_header(req.0, header.clone());
sender.complete(header);
let _ = sender.send(header);
return
}
Err(e) => {
warn!("Error handling response for header request: {:?}", e);
ctx.disable_peer(peer);
Err(e) => warn!("Error handling response for header request: {:?}", e),
}
}
}
self.dispatch_header_by_hash(ctx.as_basic(), req, sender);
}
_ => panic!("Only header by hash request fetches headers; qed"),
}
}
fn on_block_bodies(&self, ctx: &EventContext, req_id: ReqId, bodies: &[Bytes]) {
let peer = ctx.peer();
let req = match self.pending_requests.write().remove(&req_id) {
Some(req) => req,
None => return,
};
match req {
Pending::Block(req, sender) => {
if let Some(ref body) = bodies.get(0) {
match req.check_response(body) {
if let NetworkResponse::Body(ref response) = *response {
match req.check_response(&response.body) {
Ok(block) => {
let body = encoded::Body::new(body.to_vec());
self.cache.lock().insert_block_body(req.hash, body);
sender.complete(block);
self.cache.lock().insert_block_body(req.hash, response.body.clone());
let _ = sender.send(block);
return
}
Err(e) => {
warn!("Error handling response for block request: {:?}", e);
ctx.disable_peer(peer);
Err(e) => warn!("Error handling response for block request: {:?}", e),
}
}
}
self.dispatch_block(ctx.as_basic(), req, sender);
}
_ => panic!("Only block request fetches bodies; qed"),
}
}
fn on_receipts(&self, ctx: &EventContext, req_id: ReqId, receipts: &[Vec<Receipt>]) {
let peer = ctx.peer();
let req = match self.pending_requests.write().remove(&req_id) {
Some(req) => req,
None => return,
};
match req {
Pending::BlockReceipts(req, sender) => {
if let Some(ref receipts) = receipts.get(0) {
match req.check_response(receipts) {
if let NetworkResponse::Receipts(ref response) = *response {
match req.check_response(&response.receipts) {
Ok(receipts) => {
let hash = req.0.hash();
self.cache.lock().insert_block_receipts(hash, receipts.clone());
sender.complete(receipts);
let _ = sender.send(receipts);
return
}
Err(e) => {
warn!("Error handling response for receipts request: {:?}", e);
ctx.disable_peer(peer);
Err(e) => warn!("Error handling response for receipts request: {:?}", e),
}
}
}
self.dispatch_block_receipts(ctx.as_basic(), req, sender);
}
_ => panic!("Only receipts request fetches receipts; qed"),
}
}
fn on_state_proofs(&self, ctx: &EventContext, req_id: ReqId, proofs: &[Vec<Bytes>]) {
let peer = ctx.peer();
let req = match self.pending_requests.write().remove(&req_id) {
Some(req) => req,
None => return,
};
match req {
Pending::Account(req, sender) => {
if let Some(ref proof) = proofs.get(0) {
match req.check_response(proof) {
Ok(proof) => {
sender.complete(proof);
if let NetworkResponse::Account(ref response) = *response {
match req.check_response(&response.proof) {
Ok(maybe_account) => {
// TODO: validate against request outputs.
// needs engine + env info as part of request.
let _ = sender.send(maybe_account);
return
}
Err(e) => {
warn!("Error handling response for state request: {:?}", e);
ctx.disable_peer(peer);
Err(e) => warn!("Error handling response for state request: {:?}", e),
}
}
}
self.dispatch_account(ctx.as_basic(), req, sender);
}
_ => panic!("Only account request fetches state proof; qed"),
}
}
fn on_code(&self, ctx: &EventContext, req_id: ReqId, codes: &[Bytes]) {
let peer = ctx.peer();
let req = match self.pending_requests.write().remove(&req_id) {
Some(req) => req,
None => return,
};
match req {
Pending::Code(req, sender) => {
if let Some(code) = codes.get(0) {
match req.check_response(code.as_slice()) {
if let NetworkResponse::Code(ref response) = *response {
match req.check_response(response.code.as_slice()) {
Ok(()) => {
sender.complete(code.clone());
let _ = sender.send(response.code.clone());
return
}
Err(e) => {
warn!("Error handling response for code request: {:?}", e);
ctx.disable_peer(peer);
Err(e) => warn!("Error handling response for code request: {:?}", e),
}
}
self.dispatch_code(ctx.as_basic(), req, sender);
}
}
_ => panic!("Only code request fetches code; qed"),
}
}
fn on_transaction_proof(&self, ctx: &EventContext, req_id: ReqId, items: &[DBValue]) {
let peer = ctx.peer();
let req = match self.pending_requests.write().remove(&req_id) {
Some(req) => req,
None => return,
};
match req {
Pending::TxProof(req, sender) => {
match req.check_response(items) {
if let NetworkResponse::Execution(ref response) = *response {
match req.check_response(&response.items) {
ProvedExecution::Complete(executed) => {
sender.complete(Ok(executed));
let _ = sender.send(Ok(executed));
return
}
ProvedExecution::Failed(err) => {
sender.complete(Err(err));
let _ = sender.send(Err(err));
return
}
ProvedExecution::BadProof => {
warn!("Error handling response for transaction proof request");
ctx.disable_peer(peer);
ProvedExecution::BadProof => warn!("Error handling response for transaction proof request"),
}
}
}
}
self.dispatch_transaction_proof(ctx.as_basic(), req, sender);
}
_ => panic!("Only transaction proof request dispatches transaction proof requests; qed"),
}
ctx.disable_peer(peer);
}
fn tick(&self, ctx: &BasicContext) {
@ -782,7 +541,7 @@ mod tests {
use cache::Cache;
use net::{Announcement, BasicContext, ReqId, Error as LesError};
use request::{Request as LesRequest, Kind as LesRequestKind};
use request::Requests;
use network::{PeerId, NodeId};
use time::Duration;
@ -792,11 +551,10 @@ mod tests {
impl BasicContext for FakeContext {
fn persistent_peer_id(&self, _: PeerId) -> Option<NodeId> { None }
fn request_from(&self, _: PeerId, _: LesRequest) -> Result<ReqId, LesError> {
fn request_from(&self, _: PeerId, _: Requests) -> Result<ReqId, LesError> {
unimplemented!()
}
fn make_announcement(&self, _: Announcement) { }
fn max_requests(&self, _: PeerId, _: LesRequestKind) -> usize { 0 }
fn disconnect_peer(&self, _: PeerId) { }
fn disable_peer(&self, _: PeerId) { }
}

View File

@ -26,7 +26,7 @@ use ethcore::receipt::Receipt;
use ethcore::state::{self, ProvedExecution};
use ethcore::transaction::SignedTransaction;
use rlp::{RlpStream, UntrustedRlp, View};
use rlp::{RlpStream, UntrustedRlp};
use util::{Address, Bytes, DBValue, HashDB, H256, U256};
use util::memorydb::MemoryDB;
use util::sha3::Hashable;
@ -61,9 +61,9 @@ impl From<Box<TrieError>> for Error {
}
}
/// Request for a header by number.
/// Request for header proof by number
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeaderByNumber {
pub struct HeaderProof {
/// The header's number.
num: u64,
/// The cht number for the given block number.
@ -72,11 +72,11 @@ pub struct HeaderByNumber {
cht_root: H256,
}
impl HeaderByNumber {
impl HeaderProof {
/// Construct a new header-by-number request. Fails if the given number is 0.
/// Provide the expected CHT root to compare against.
pub fn new(num: u64, cht_root: H256) -> Option<Self> {
::cht::block_to_cht_number(num).map(|cht_num| HeaderByNumber {
::cht::block_to_cht_number(num).map(|cht_num| HeaderProof {
num: num,
cht_num: cht_num,
cht_root: cht_root,
@ -92,18 +92,11 @@ impl HeaderByNumber {
/// Access the expected CHT root.
pub fn cht_root(&self) -> H256 { self.cht_root }
/// Check a response with a header and cht proof.
pub fn check_response(&self, header: &[u8], proof: &[Bytes]) -> Result<(encoded::Header, U256), Error> {
let (expected_hash, td) = match ::cht::check_proof(proof, self.num, self.cht_root) {
Some((expected_hash, td)) => (expected_hash, td),
None => return Err(Error::BadProof),
};
// and compare the hash to the found header.
let found_hash = header.sha3();
match expected_hash == found_hash {
true => Ok((encoded::Header::new(header.to_vec()), td)),
false => Err(Error::WrongHash(expected_hash, found_hash)),
/// Check a response with a CHT proof, get a hash and total difficulty back.
pub fn check_response(&self, proof: &[Bytes]) -> Result<(H256, U256), Error> {
match ::cht::check_proof(proof, self.num, self.cht_root) {
Some((expected_hash, td)) => Ok((expected_hash, td)),
None => Err(Error::BadProof),
}
}
}
@ -114,10 +107,10 @@ pub struct HeaderByHash(pub H256);
impl HeaderByHash {
/// Check a response for the header.
pub fn check_response(&self, header: &[u8]) -> Result<encoded::Header, Error> {
pub fn check_response(&self, header: &encoded::Header) -> Result<encoded::Header, Error> {
let hash = header.sha3();
match hash == self.0 {
true => Ok(encoded::Header::new(header.to_vec())),
true => Ok(header.clone()),
false => Err(Error::WrongHash(self.0, hash)),
}
}
@ -143,16 +136,14 @@ impl Body {
}
/// Check a response for this block body.
pub fn check_response(&self, body: &[u8]) -> Result<encoded::Block, Error> {
let body_view = UntrustedRlp::new(&body);
pub fn check_response(&self, body: &encoded::Body) -> Result<encoded::Block, Error> {
// check the integrity of the the body against the header
let tx_root = ::util::triehash::ordered_trie_root(body_view.at(0)?.iter().map(|r| r.as_raw().to_vec()));
let tx_root = ::util::triehash::ordered_trie_root(body.rlp().at(0).iter().map(|r| r.as_raw().to_vec()));
if tx_root != self.header.transactions_root() {
return Err(Error::WrongTrieRoot(self.header.transactions_root(), tx_root));
}
let uncles_hash = body_view.at(1)?.as_raw().sha3();
let uncles_hash = body.rlp().at(1).as_raw().sha3();
if uncles_hash != self.header.uncles_hash() {
return Err(Error::WrongHash(self.header.uncles_hash(), uncles_hash));
}
@ -160,7 +151,7 @@ impl Body {
// concatenate the header and the body.
let mut stream = RlpStream::new_list(3);
stream.append_raw(self.header.rlp().as_raw(), 1);
stream.append_raw(body, 2);
stream.append_raw(&body.rlp().as_raw(), 2);
Ok(encoded::Block::new(stream.out()))
}
@ -194,7 +185,7 @@ pub struct Account {
impl Account {
/// Check a response with an account against the stored header.
pub fn check_response(&self, proof: &[Bytes]) -> Result<BasicAccount, Error> {
pub fn check_response(&self, proof: &[Bytes]) -> Result<Option<BasicAccount>, Error> {
let state_root = self.header.state_root();
let mut db = MemoryDB::new();
@ -203,14 +194,14 @@ impl Account {
match TrieDB::new(&db, &state_root).and_then(|t| t.get(&self.address.sha3()))? {
Some(val) => {
let rlp = UntrustedRlp::new(&val);
Ok(BasicAccount {
Ok(Some(BasicAccount {
nonce: rlp.val_at(0)?,
balance: rlp.val_at(1)?,
storage_root: rlp.val_at(2)?,
code_hash: rlp.val_at(3)?,
})
}))
},
None => Err(Error::BadProof)
None => Ok(None),
}
}
}
@ -219,8 +210,6 @@ impl Account {
pub struct Code {
/// Block hash, number pair.
pub block_id: (H256, u64),
/// Address requested.
pub address: Address,
/// Account's code hash.
pub code_hash: H256,
}
@ -278,11 +267,11 @@ mod tests {
#[test]
fn no_invalid_header_by_number() {
assert!(HeaderByNumber::new(0, Default::default()).is_none())
assert!(HeaderProof::new(0, Default::default()).is_none())
}
#[test]
fn check_header_by_number() {
fn check_header_proof() {
use ::cht;
let test_client = TestBlockChainClient::new();
@ -303,11 +292,9 @@ mod tests {
};
let proof = cht.prove(10_000, 0).unwrap().unwrap();
let req = HeaderByNumber::new(10_000, cht.root()).unwrap();
let req = HeaderProof::new(10_000, cht.root()).unwrap();
let raw_header = test_client.block_header(::ethcore::ids::BlockId::Number(10_000)).unwrap();
assert!(req.check_response(&raw_header.into_inner(), &proof[..]).is_ok());
assert!(req.check_response(&proof[..]).is_ok());
}
#[test]
@ -316,9 +303,9 @@ mod tests {
header.set_number(10_000);
header.set_extra_data(b"test_header".to_vec());
let hash = header.hash();
let raw_header = ::rlp::encode(&header);
let raw_header = encoded::Header::new(::rlp::encode(&header).to_vec());
assert!(HeaderByHash(hash).check_response(&*raw_header).is_ok())
assert!(HeaderByHash(hash).check_response(&raw_header).is_ok())
}
#[test]
@ -334,7 +321,8 @@ mod tests {
hash: header.hash(),
};
assert!(req.check_response(&*body_stream.drain()).is_ok())
let response = encoded::Body::new(body_stream.drain().to_vec());
assert!(req.check_response(&response).is_ok())
}
#[test]
@ -412,7 +400,6 @@ mod tests {
let code = vec![1u8; 256];
let req = Code {
block_id: (Default::default(), 2),
address: Default::default(),
code_hash: ::util::Hashable::sha3(&code),
};

View File

@ -24,22 +24,15 @@ use ethcore::client::{BlockChainClient, ProvingBlockChainClient};
use ethcore::transaction::PendingTransaction;
use ethcore::ids::BlockId;
use ethcore::encoded;
use util::{Bytes, DBValue, RwLock, H256};
use util::{RwLock, H256};
use cht::{self, BlockInfo};
use client::{LightChainClient, AsLightClient};
use transaction_queue::TransactionQueue;
use request;
/// Defines the operations that a provider for `LES` must fulfill.
///
/// These are defined at [1], but may be subject to change.
/// Requests which can't be fulfilled should return either an empty RLP list
/// or empty vector where appropriate.
///
/// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES)
/// Defines the operations that a provider for the light subprotocol must fulfill.
#[cfg_attr(feature = "ipc", ipc(client_ident="LightProviderClient"))]
pub trait Provider: Send + Sync {
/// Provide current blockchain info.
@ -59,18 +52,18 @@ pub trait Provider: Send + Sync {
///
/// The returned vector may have any length in the range [0, `max`], but the
/// results within must adhere to the `skip` and `reverse` parameters.
fn block_headers(&self, req: request::Headers) -> Vec<encoded::Header> {
fn block_headers(&self, req: request::CompleteHeadersRequest) -> Option<request::HeadersResponse> {
use request::HashOrNumber;
if req.max == 0 { return Vec::new() }
if req.max == 0 { return None }
let best_num = self.chain_info().best_block_number;
let start_num = match req.start {
HashOrNumber::Number(start_num) => start_num,
HashOrNumber::Hash(hash) => match self.block_header(BlockId::Hash(hash)) {
None => {
trace!(target: "les_provider", "Unknown block hash {} requested", hash);
return Vec::new();
trace!(target: "pip_provider", "Unknown block hash {} requested", hash);
return None;
}
Some(header) => {
let num = header.number();
@ -79,7 +72,9 @@ pub trait Provider: Send + Sync {
if req.max == 1 || canon_hash != Some(hash) {
// Non-canonical header or single header requested.
return vec![header];
return Some(::request::HeadersResponse {
headers: vec![header],
})
}
num
@ -87,116 +82,50 @@ pub trait Provider: Send + Sync {
}
};
(0u64..req.max as u64)
let headers: Vec<_> = (0u64..req.max as u64)
.map(|x: u64| x.saturating_mul(req.skip + 1))
.take_while(|x| if req.reverse { x < &start_num } else { best_num.saturating_sub(start_num) >= *x })
.map(|x| if req.reverse { start_num - x } else { start_num + x })
.map(|x| self.block_header(BlockId::Number(x)))
.take_while(|x| x.is_some())
.flat_map(|x| x)
.collect()
.collect();
if headers.is_empty() {
None
} else {
Some(::request::HeadersResponse { headers: headers })
}
}
/// Get a block header by id.
fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
/// Provide as many as possible of the requested blocks (minus the headers) encoded
/// in RLP format.
fn block_bodies(&self, req: request::Bodies) -> Vec<Option<encoded::Body>> {
req.block_hashes.into_iter()
.map(|hash| self.block_body(BlockId::Hash(hash)))
.collect()
}
/// Fulfill a block body request.
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse>;
/// Get a block body by id.
fn block_body(&self, id: BlockId) -> Option<encoded::Body>;
/// Fulfill a request for block receipts.
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse>;
/// Provide the receipts as many as possible of the requested blocks.
/// Returns a vector of RLP-encoded lists of receipts.
fn receipts(&self, req: request::Receipts) -> Vec<Bytes> {
req.block_hashes.into_iter()
.map(|hash| self.block_receipts(&hash))
.map(|receipts| receipts.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec()))
.collect()
}
/// Get an account proof.
fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse>;
/// Get a block's receipts as an RLP-encoded list by block hash.
fn block_receipts(&self, hash: &H256) -> Option<Bytes>;
/// Get a storage proof.
fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse>;
/// Provide a set of merkle proofs, as requested. Each request is a
/// block hash and request parameters.
///
/// Returns a vector of RLP-encoded lists satisfying the requests.
fn proofs(&self, req: request::StateProofs) -> Vec<Bytes> {
use rlp::RlpStream;
let mut results = Vec::with_capacity(req.requests.len());
for request in req.requests {
let proof = self.state_proof(request);
let mut stream = RlpStream::new_list(proof.len());
for node in proof {
stream.append_raw(&node, 1);
}
results.push(stream.out());
}
results
}
/// Get a state proof from a request. Each proof should be a vector
/// of rlp-encoded trie nodes, in ascending order by distance from the root.
fn state_proof(&self, req: request::StateProof) -> Vec<Bytes>;
/// Provide contract code for the specified (block_hash, account_hash) pairs.
/// Each item in the resulting vector is either the raw bytecode or empty.
fn contract_codes(&self, req: request::ContractCodes) -> Vec<Bytes> {
req.code_requests.into_iter()
.map(|req| self.contract_code(req))
.collect()
}
/// Get contract code by request. Either the raw bytecode or empty.
fn contract_code(&self, req: request::ContractCode) -> Bytes;
/// Provide header proofs from the Canonical Hash Tries as well as the headers
/// they correspond to -- each element in the returned vector is a 2-tuple.
/// The first element is a block header and the second a merkle proof of
/// the header in a requested CHT.
fn header_proofs(&self, req: request::HeaderProofs) -> Vec<Bytes> {
use rlp::{self, RlpStream};
req.requests.into_iter()
.map(|req| self.header_proof(req))
.map(|maybe_proof| match maybe_proof {
None => rlp::EMPTY_LIST_RLP.to_vec(),
Some((header, proof)) => {
let mut stream = RlpStream::new_list(2);
stream.append_raw(&header.into_inner(), 1).begin_list(proof.len());
for node in proof {
stream.append_raw(&node, 1);
}
stream.out()
}
})
.collect()
}
/// Provide contract code for the specified (block_hash, code_hash) pair.
fn contract_code(&self, req: request::CompleteCodeRequest) -> Option<request::CodeResponse>;
/// Provide a header proof from a given Canonical Hash Trie as well as the
/// corresponding header. The first element is the block header and the
/// second is a merkle proof of the CHT.
fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)>;
/// corresponding header.
fn header_proof(&self, req: request::CompleteHeaderProofRequest) -> Option<request::HeaderProofResponse>;
/// Provide pending transactions.
fn ready_transactions(&self) -> Vec<PendingTransaction>;
/// Provide a proof-of-execution for the given transaction proof request.
/// Returns a vector of all state items necessary to execute the transaction.
fn transaction_proof(&self, req: request::TransactionProof) -> Option<Vec<DBValue>>;
fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option<request::ExecutionResponse>;
}
// Implementation of a light client data provider for a client.
@ -217,32 +146,52 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
BlockChainClient::block_header(self, id)
}
fn block_body(&self, id: BlockId) -> Option<encoded::Body> {
BlockChainClient::block_body(self, id)
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
BlockChainClient::block_body(self, BlockId::Hash(req.hash))
.map(|body| ::request::BodyResponse { body: body })
}
fn block_receipts(&self, hash: &H256) -> Option<Bytes> {
BlockChainClient::block_receipts(self, hash)
fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
BlockChainClient::block_receipts(self, &req.hash)
.map(|x| ::request::ReceiptsResponse { receipts: ::rlp::decode_list(&x) })
}
fn state_proof(&self, req: request::StateProof) -> Vec<Bytes> {
match req.key2 {
Some(key2) => self.prove_storage(req.key1, key2, req.from_level, BlockId::Hash(req.block)),
None => self.prove_account(req.key1, req.from_level, BlockId::Hash(req.block)),
fn account_proof(&self, req: request::CompleteAccountRequest) -> Option<request::AccountResponse> {
self.prove_account(req.address_hash, BlockId::Hash(req.block_hash)).map(|(proof, acc)| {
::request::AccountResponse {
proof: proof,
nonce: acc.nonce,
balance: acc.balance,
code_hash: acc.code_hash,
storage_root: acc.storage_root,
}
})
}
fn contract_code(&self, req: request::ContractCode) -> Bytes {
self.code_by_hash(req.account_key, BlockId::Hash(req.block_hash))
fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option<request::StorageResponse> {
self.prove_storage(req.address_hash, req.key_hash, BlockId::Hash(req.block_hash)).map(|(proof, item) | {
::request::StorageResponse {
proof: proof,
value: item,
}
})
}
fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
if Some(req.cht_number) != cht::block_to_cht_number(req.block_number) {
debug!(target: "les_provider", "Requested CHT number mismatch with block number.");
fn contract_code(&self, req: request::CompleteCodeRequest) -> Option<request::CodeResponse> {
self.state_data(&req.code_hash)
.map(|code| ::request::CodeResponse { code: code })
}
fn header_proof(&self, req: request::CompleteHeaderProofRequest) -> Option<request::HeaderProofResponse> {
let cht_number = match cht::block_to_cht_number(req.num) {
Some(cht_num) => cht_num,
None => {
debug!(target: "pip_provider", "Requested CHT proof with invalid block number");
return None;
}
};
let mut needed_hdr = None;
let mut needed = None;
// build the CHT, caching the requested header as we pass through it.
let cht = {
@ -258,8 +207,8 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
total_difficulty: td,
};
if hdr.number() == req.block_number {
needed_hdr = Some(hdr);
if hdr.number() == req.num {
needed = Some((hdr, td));
}
Some(info)
@ -268,29 +217,33 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
}
};
match cht::build(req.cht_number, block_info) {
match cht::build(cht_number, block_info) {
Some(cht) => cht,
None => return None, // incomplete CHT.
}
};
let needed_hdr = needed_hdr.expect("`needed_hdr` always set in loop, number checked before; qed");
let (needed_hdr, needed_td) = needed.expect("`needed` always set in loop, number checked before; qed");
// prove our result.
match cht.prove(req.block_number, req.from_level) {
Ok(Some(proof)) => Some((needed_hdr, proof)),
match cht.prove(req.num, 0) {
Ok(Some(proof)) => Some(::request::HeaderProofResponse {
proof: proof,
hash: needed_hdr.hash(),
td: needed_td,
}),
Ok(None) => None,
Err(e) => {
debug!(target: "les_provider", "Error looking up number in freshly-created CHT: {}", e);
debug!(target: "pip_provider", "Error looking up number in freshly-created CHT: {}", e);
None
}
}
}
fn transaction_proof(&self, req: request::TransactionProof) -> Option<Vec<DBValue>> {
fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option<request::ExecutionResponse> {
use ethcore::transaction::Transaction;
let id = BlockId::Hash(req.at);
let id = BlockId::Hash(req.block_hash);
let nonce = match self.nonce(&req.from, id.clone()) {
Some(nonce) => nonce,
None => return None,
@ -305,6 +258,7 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
}.fake_sign(req.from);
self.prove_transaction(transaction, id)
.map(|proof| ::request::ExecutionResponse { items: proof })
}
fn ready_transactions(&self) -> Vec<PendingTransaction> {
@ -347,27 +301,31 @@ impl<L: AsLightClient + Send + Sync> Provider for LightProvider<L> {
self.client.as_light_client().block_header(id)
}
fn block_body(&self, _id: BlockId) -> Option<encoded::Body> {
fn block_body(&self, _req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
None
}
fn block_receipts(&self, _hash: &H256) -> Option<Bytes> {
fn block_receipts(&self, _req: request::CompleteReceiptsRequest) -> Option<request::ReceiptsResponse> {
None
}
fn state_proof(&self, _req: request::StateProof) -> Vec<Bytes> {
Vec::new()
}
fn contract_code(&self, _req: request::ContractCode) -> Bytes {
Vec::new()
}
fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec<Bytes>)> {
fn account_proof(&self, _req: request::CompleteAccountRequest) -> Option<request::AccountResponse> {
None
}
fn transaction_proof(&self, _req: request::TransactionProof) -> Option<Vec<DBValue>> {
fn storage_proof(&self, _req: request::CompleteStorageRequest) -> Option<request::StorageResponse> {
None
}
fn contract_code(&self, _req: request::CompleteCodeRequest) -> Option<request::CodeResponse> {
None
}
fn header_proof(&self, _req: request::CompleteHeaderProofRequest) -> Option<request::HeaderProofResponse> {
None
}
fn transaction_proof(&self, _req: request::CompleteExecutionRequest) -> Option<request::ExecutionResponse> {
None
}
@ -395,10 +353,8 @@ mod tests {
let client = TestBlockChainClient::new();
client.add_blocks(2000, EachBlockWith::Nothing);
let req = ::request::HeaderProof {
cht_number: 0,
block_number: 1500,
from_level: 0,
let req = ::request::CompleteHeaderProofRequest {
num: 1500,
};
assert!(client.header_proof(req.clone()).is_none());

View File

@ -1,228 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! LES request types.
use ethcore::transaction::Action;
use util::{Address, H256, U256, Uint};
/// Either a hash or a number.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub enum HashOrNumber {
/// Block hash variant.
Hash(H256),
/// Block number variant.
Number(u64),
}
impl From<H256> for HashOrNumber {
fn from(hash: H256) -> Self {
HashOrNumber::Hash(hash)
}
}
impl From<u64> for HashOrNumber {
fn from(num: u64) -> Self {
HashOrNumber::Number(num)
}
}
/// A request for block headers.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct Headers {
/// Starting block number or hash.
pub start: HashOrNumber,
/// The maximum amount of headers which can be returned.
pub max: usize,
/// The amount of headers to skip between each response entry.
pub skip: u64,
/// Whether the headers should proceed in falling number from the initial block.
pub reverse: bool,
}
/// A request for specific block bodies.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct Bodies {
/// Hashes which bodies are being requested for.
pub block_hashes: Vec<H256>
}
/// A request for transaction receipts.
///
/// This request is answered with a list of transaction receipts for each block
/// requested.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct Receipts {
/// Block hashes to return receipts for.
pub block_hashes: Vec<H256>,
}
/// A request for a state proof
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct StateProof {
/// Block hash to query state from.
pub block: H256,
/// Key of the state trie -- corresponds to account hash.
pub key1: H256,
/// Key in that account's storage trie; if empty, then the account RLP should be
/// returned.
pub key2: Option<H256>,
/// if greater than zero, trie nodes beyond this level may be omitted.
pub from_level: u32, // could even safely be u8; trie w/ 32-byte key can be at most 64-levels deep.
}
/// A request for state proofs.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct StateProofs {
/// All the proof requests.
pub requests: Vec<StateProof>,
}
/// A request for contract code.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct ContractCode {
/// Block hash
pub block_hash: H256,
/// Account key (== sha3(address))
pub account_key: H256,
}
/// A request for contract code.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct ContractCodes {
/// Block hash and account key (== sha3(address)) pairs to fetch code for.
pub code_requests: Vec<ContractCode>,
}
/// A request for a header proof from the Canonical Hash Trie.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct HeaderProof {
/// Number of the CHT.
pub cht_number: u64,
/// Block number requested. May not be 0: genesis isn't included in any CHT.
pub block_number: u64,
/// If greater than zero, trie nodes beyond this level may be omitted.
pub from_level: u32,
}
/// A request for header proofs from the CHT.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct HeaderProofs {
/// All the proof requests.
pub requests: Vec<HeaderProof>,
}
/// A request for proof of (simulated) transaction execution.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct TransactionProof {
/// Block hash to request for.
pub at: H256,
/// Address to treat as the caller.
pub from: Address,
/// Action to take: either a call or a create.
pub action: Action,
/// Amount of gas to request proof-of-execution for.
pub gas: U256,
/// Price for each gas.
pub gas_price: U256,
/// Value to simulate sending.
pub value: U256,
/// Transaction data.
pub data: Vec<u8>,
}
/// Kinds of requests.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub enum Kind {
/// Requesting headers.
Headers,
/// Requesting block bodies.
Bodies,
/// Requesting transaction receipts.
Receipts,
/// Requesting proofs of state trie nodes.
StateProofs,
/// Requesting contract code by hash.
Codes,
/// Requesting header proofs (from the CHT).
HeaderProofs,
/// Requesting proof of transaction execution.
TransactionProof,
}
/// Encompasses all possible types of requests in a single structure.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub enum Request {
/// Requesting headers.
Headers(Headers),
/// Requesting block bodies.
Bodies(Bodies),
/// Requesting transaction receipts.
Receipts(Receipts),
/// Requesting state proofs.
StateProofs(StateProofs),
/// Requesting contract codes.
Codes(ContractCodes),
/// Requesting header proofs.
HeaderProofs(HeaderProofs),
/// Requesting proof of transaction execution.
TransactionProof(TransactionProof),
}
impl Request {
/// Get the kind of request this is.
pub fn kind(&self) -> Kind {
match *self {
Request::Headers(_) => Kind::Headers,
Request::Bodies(_) => Kind::Bodies,
Request::Receipts(_) => Kind::Receipts,
Request::StateProofs(_) => Kind::StateProofs,
Request::Codes(_) => Kind::Codes,
Request::HeaderProofs(_) => Kind::HeaderProofs,
Request::TransactionProof(_) => Kind::TransactionProof,
}
}
/// Get the amount of requests being made.
/// In the case of `TransactionProof`, this is the amount of gas being requested.
pub fn amount(&self) -> usize {
match *self {
Request::Headers(ref req) => req.max,
Request::Bodies(ref req) => req.block_hashes.len(),
Request::Receipts(ref req) => req.block_hashes.len(),
Request::StateProofs(ref req) => req.requests.len(),
Request::Codes(ref req) => req.code_requests.len(),
Request::HeaderProofs(ref req) => req.requests.len(),
Request::TransactionProof(ref req) => match req.gas > usize::max_value().into() {
true => usize::max_value(),
false => req.gas.low_u64() as usize,
}
}
}
}

View File

@ -14,4 +14,4 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
pub mod les_request;
pub mod request;

View File

@ -0,0 +1,190 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Request chain builder utility.
//! Push requests with `push`. Back-references and data required to verify responses must be
//! supplied as well.
use std::collections::HashMap;
use request::{
IncompleteRequest, CompleteRequest, Request,
OutputKind, Output, NoSuchOutput, Response, ResponseError,
};
/// Build chained requests. Push them onto the series with `push`,
/// and produce a `Requests` object with `build`. Outputs are checked for consistency.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct RequestBuilder {
output_kinds: HashMap<(usize, usize), OutputKind>,
requests: Vec<Request>,
}
impl RequestBuilder {
/// Attempt to push a request onto the request chain. Fails if the request
/// references a non-existent output of a prior request.
pub fn push(&mut self, request: Request) -> Result<(), NoSuchOutput> {
request.check_outputs(|req, idx, kind| {
match self.output_kinds.get(&(req, idx)) {
Some(k) if k == &kind => Ok(()),
_ => Err(NoSuchOutput),
}
})?;
let req_idx = self.requests.len();
request.note_outputs(|idx, kind| { self.output_kinds.insert((req_idx, idx), kind); });
self.requests.push(request);
Ok(())
}
/// Get a reference to the output kinds map.
pub fn output_kinds(&self) -> &HashMap<(usize, usize), OutputKind> {
&self.output_kinds
}
/// Convert this into a "requests" object.
pub fn build(self) -> Requests {
Requests {
outputs: HashMap::new(),
requests: self.requests,
answered: 0,
}
}
}
/// Requests pending responses.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Requests {
outputs: HashMap<(usize, usize), Output>,
requests: Vec<Request>,
answered: usize,
}
impl Requests {
/// For each request, produce responses for each.
/// The responses vector produced goes up to the point where the responder
/// first returns `None`, an invalid response, or until all requests have been responded to.
pub fn respond_to_all<F>(mut self, responder: F) -> Vec<Response>
where F: Fn(CompleteRequest) -> Option<Response>
{
let mut responses = Vec::new();
while let Some(response) = self.next_complete().and_then(&responder) {
match self.supply_response(&response) {
Ok(()) => responses.push(response),
Err(e) => {
debug!(target: "pip", "produced bad response to request: {:?}", e);
return responses;
}
}
}
responses
}
/// Get access to the underlying slice of requests.
// TODO: unimplemented -> Vec<Request>, // do we _have to_ allocate?
pub fn requests(&self) -> &[Request] { &self.requests }
/// Get the number of answered requests.
pub fn num_answered(&self) -> usize { self.answered }
/// Get the next request as a filled request. Returns `None` when all requests answered.
pub fn next_complete(&self) -> Option<CompleteRequest> {
if self.answered == self.requests.len() {
None
} else {
Some(self.requests[self.answered].clone()
.complete()
.expect("All outputs checked as invariant of `Requests` object; qed"))
}
}
/// Supply a response for the next request.
/// Fails on: wrong request kind, all requests answered already.
pub fn supply_response(&mut self, response: &Response) -> Result<(), ResponseError> {
let idx = self.answered;
// check validity.
if idx == self.requests.len() { return Err(ResponseError::Unexpected) }
if self.requests[idx].kind() != response.kind() { return Err(ResponseError::WrongKind) }
let outputs = &mut self.outputs;
response.fill_outputs(|out_idx, output| {
// we don't need to check output kinds here because all back-references
// are validated in the builder.
// TODO: optimization for only storing outputs we "care about"?
outputs.insert((idx, out_idx), output);
});
self.answered += 1;
// fill as much of the next request as we can.
if let Some(ref mut req) = self.requests.get_mut(self.answered) {
req.fill(|req_idx, out_idx| outputs.get(&(req_idx, out_idx)).cloned().ok_or(NoSuchOutput))
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use request::*;
use super::RequestBuilder;
use util::H256;
#[test]
fn all_scalar() {
let mut builder = RequestBuilder::default();
builder.push(Request::HeaderProof(IncompleteHeaderProofRequest {
num: 100.into(),
})).unwrap();
builder.push(Request::Receipts(IncompleteReceiptsRequest {
hash: H256::default().into(),
})).unwrap();
}
#[test]
#[should_panic]
fn missing_backref() {
let mut builder = RequestBuilder::default();
builder.push(Request::HeaderProof(IncompleteHeaderProofRequest {
num: Field::BackReference(100, 3),
})).unwrap();
}
#[test]
#[should_panic]
fn wrong_kind() {
let mut builder = RequestBuilder::default();
assert!(builder.push(Request::HeaderProof(IncompleteHeaderProofRequest {
num: 100.into(),
})).is_ok());
builder.push(Request::HeaderProof(IncompleteHeaderProofRequest {
num: Field::BackReference(0, 0),
})).unwrap();
}
#[test]
fn good_backreference() {
let mut builder = RequestBuilder::default();
builder.push(Request::HeaderProof(IncompleteHeaderProofRequest {
num: 100.into(), // header proof puts hash at output 0.
})).unwrap();
builder.push(Request::Receipts(IncompleteReceiptsRequest {
hash: Field::BackReference(0, 0),
})).unwrap();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
{
"name": "TestMutiValidator",
"engine": {
"basicAuthority": {
"params": {
"gasLimitBoundDivisor": "0x0400",
"durationLimit": "0x0d",
"validators": {
"multi": {
"0": { "list": ["0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"] },
"2": { "list": ["0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e"] }
}
}
}
}
},
"params": {
"accountStartNonce": "0x0",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x69"
},
"genesis": {
"seal": {
"generic": "0xc180"
},
"difficulty": "0x20000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"gasLimit": "0x2fefd8"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { "balance": "99999999999999999999999" },
"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { "balance": "99999999999999999999999" }
}
}

View File

@ -24,14 +24,16 @@ use std::fmt;
use std::collections::{HashMap, HashSet};
use std::time::{Instant, Duration};
use util::{RwLock};
use ethstore::{SimpleSecretStore, SecretStore, Error as SSError, EthStore, EthMultiStore,
random_string, SecretVaultRef, StoreAccountRef};
use ethstore::{
SimpleSecretStore, SecretStore, Error as SSError, EthStore, EthMultiStore,
random_string, SecretVaultRef, StoreAccountRef,
};
use ethstore::dir::MemoryDirectory;
use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator};
use ethjson::misc::AccountMeta;
use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath};
pub use ethstore::ethkey::Signature;
pub use ethstore::{Derivation, IndexDerivation};
pub use ethstore::{Derivation, IndexDerivation, KeyFile};
/// Type of unlock.
#[derive(Clone)]
@ -500,6 +502,11 @@ impl AccountProvider {
self.sstore.change_password(&self.sstore.account_ref(address)?, &password, &new_password)
}
/// Exports an account for given address.
pub fn export_account(&self, address: &Address, password: String) -> Result<KeyFile, Error> {
self.sstore.export_account(&self.sstore.account_ref(address)?, &password)
}
/// Helper method used for unlocking accounts.
fn unlock_account(&self, address: Address, password: String, unlock: Unlock) -> Result<(), Error> {
// verify password by signing dump message

View File

@ -20,7 +20,7 @@ use std::cmp;
use std::sync::Arc;
use std::collections::HashSet;
use rlp::{UntrustedRlp, RlpStream, Encodable, Decodable, Decoder, DecoderError, View};
use rlp::{UntrustedRlp, RlpStream, Encodable, Decodable, DecoderError};
use util::{Bytes, Address, Uint, Hashable, U256, H256, ordered_trie_root, SHA3_NULL_RLP};
use util::error::{Mismatch, OutOfBounds};
@ -67,18 +67,17 @@ impl Block {
impl Decodable for Block {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
if decoder.as_raw().len() != decoder.as_rlp().payload_info()?.total() {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
if rlp.as_raw().len() != rlp.payload_info()?.total() {
return Err(DecoderError::RlpIsTooBig);
}
let d = decoder.as_rlp();
if d.item_count() != 3 {
if rlp.item_count()? != 3 {
return Err(DecoderError::RlpIncorrectListLen);
}
Ok(Block {
header: d.val_at(0)?,
transactions: d.val_at(1)?,
uncles: d.val_at(2)?,
header: rlp.val_at(0)?,
transactions: rlp.list_at(1)?,
uncles: rlp.list_at(2)?,
})
}
}

View File

@ -154,13 +154,12 @@ impl HeapSizeOf for BlockDetails {
}
impl Decodable for BlockDetails {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let details = BlockDetails {
number: d.val_at(0)?,
total_difficulty: d.val_at(1)?,
parent: d.val_at(2)?,
children: d.val_at(3)?,
number: rlp.val_at(0)?,
total_difficulty: rlp.val_at(1)?,
parent: rlp.val_at(2)?,
children: rlp.list_at(3)?,
};
Ok(details)
}
@ -190,11 +189,10 @@ impl HeapSizeOf for TransactionAddress {
}
impl Decodable for TransactionAddress {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let tx_address = TransactionAddress {
block_hash: d.val_at(0)?,
index: d.val_at(1)?,
block_hash: rlp.val_at(0)?,
index: rlp.val_at(1)?,
};
Ok(tx_address)
@ -224,9 +222,9 @@ impl BlockReceipts {
}
impl Decodable for BlockReceipts {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(BlockReceipts {
receipts: Decodable::decode(decoder)?
receipts: rlp.as_list()?,
})
}
}

View File

@ -44,8 +44,8 @@ impl Into<bc::Bloom> for Bloom {
}
impl Decodable for Bloom {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
Decodable::decode(decoder).map(Bloom)
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
LogBloom::decode(rlp).map(Bloom)
}
}

View File

@ -52,8 +52,8 @@ impl Into<bc::BloomGroup> for BloomGroup {
}
impl Decodable for BloomGroup {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let blooms = Decodable::decode(decoder)?;
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let blooms = rlp.as_list()?;
let group = BloomGroup {
blooms: blooms
};

View File

@ -66,7 +66,7 @@ use evm::{Factory as EvmFactory, Schedule};
use miner::{Miner, MinerService, TransactionImportResult};
use snapshot::{self, io as snapshot_io};
use factory::Factories;
use rlp::{View, UntrustedRlp};
use rlp::UntrustedRlp;
use state_db::StateDB;
use rand::OsRng;
use client::registry::Registry;
@ -539,7 +539,7 @@ impl Client {
)?;
// Commit results
let receipts = ::rlp::decode(&receipts_bytes);
let receipts = ::rlp::decode_list(&receipts_bytes);
let mut batch = DBTransaction::new();
chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, false, true);
// Final commit to the DB
@ -1607,23 +1607,14 @@ impl MayPanic for Client {
}
impl ::client::ProvingBlockChainClient for Client {
fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockId) -> Vec<Bytes> {
fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec<Bytes>, H256)> {
self.state_at(id)
.and_then(move |state| state.prove_storage(key1, key2, from_level).ok())
.unwrap_or_else(Vec::new)
.and_then(move |state| state.prove_storage(key1, key2).ok())
}
fn prove_account(&self, key1: H256, from_level: u32, id: BlockId) -> Vec<Bytes> {
fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec<Bytes>, ::types::basic_account::BasicAccount)> {
self.state_at(id)
.and_then(move |state| state.prove_account(key1, from_level).ok())
.unwrap_or_else(Vec::new)
}
fn code_by_hash(&self, account_key: H256, id: BlockId) -> Bytes {
self.state_at(id)
.and_then(move |state| state.code_by_address_hash(account_key).ok())
.and_then(|x| x)
.unwrap_or_else(Vec::new)
.and_then(move |state| state.prove_account(key1).ok())
}
fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<Vec<DBValue>> {
@ -1643,7 +1634,6 @@ impl ::client::ProvingBlockChainClient for Client {
_ => return Some(state.drop().1.extract_proof()),
}
}
}
impl Drop for Client {

View File

@ -38,6 +38,7 @@ use error::{ImportResult, Error as EthcoreError};
use evm::{Factory as EvmFactory, VMType, Schedule};
use miner::{Miner, MinerService, TransactionImportResult};
use spec::Spec;
use types::basic_account::BasicAccount;
use types::mode::Mode;
use types::pruning_info::PruningInfo;
@ -758,16 +759,12 @@ impl BlockChainClient for TestBlockChainClient {
}
impl ProvingBlockChainClient for TestBlockChainClient {
fn prove_storage(&self, _: H256, _: H256, _: u32, _: BlockId) -> Vec<Bytes> {
Vec::new()
fn prove_storage(&self, _: H256, _: H256, _: BlockId) -> Option<(Vec<Bytes>, H256)> {
None
}
fn prove_account(&self, _: H256, _: u32, _: BlockId) -> Vec<Bytes> {
Vec::new()
}
fn code_by_hash(&self, _: H256, _: BlockId) -> Bytes {
Vec::new()
fn prove_account(&self, _: H256, _: BlockId) -> Option<(Vec<Bytes>, BasicAccount)> {
None
}
fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option<Vec<DBValue>> {

View File

@ -34,6 +34,7 @@ use env_info::LastHashes;
use block_import_error::BlockImportError;
use ipc::IpcConfig;
use types::ids::*;
use types::basic_account::BasicAccount;
use types::trace_filter::Filter as TraceFilter;
use types::call_analytics::CallAnalytics;
use types::blockchain_info::BlockChainInfo;
@ -315,19 +316,12 @@ pub trait ProvingBlockChainClient: BlockChainClient {
///
/// Both provided keys assume a secure trie.
/// Returns a vector of raw trie nodes (in order from the root) proving the storage query.
/// Nodes after `from_level` may be omitted.
/// An empty vector indicates unservable query.
fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockId) -> Vec<Bytes>;
fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec<Bytes>, H256)>;
/// Prove account existence at a specific block id.
/// The key is the keccak hash of the account's address.
/// Returns a vector of raw trie nodes (in order from the root) proving the query.
/// Nodes after `from_level` may be omitted.
/// An empty vector indicates unservable query.
fn prove_account(&self, key1: H256, from_level: u32, id: BlockId) -> Vec<Bytes>;
/// Get code by address hash.
fn code_by_hash(&self, account_key: H256, id: BlockId) -> Bytes;
fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec<Bytes>, BasicAccount)>;
/// Prove execution of a transaction at the given block.
fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<Vec<DBValue>>;

View File

@ -21,7 +21,7 @@ use std::sync::Weak;
use std::time::{UNIX_EPOCH, Duration};
use util::*;
use ethkey::{verify_address, Signature};
use rlp::{UntrustedRlp, View, encode};
use rlp::{UntrustedRlp, encode};
use account_provider::AccountProvider;
use block::*;
use spec::CommonParams;
@ -82,7 +82,7 @@ pub struct AuthorityRound {
proposed: AtomicBool,
client: RwLock<Option<Weak<EngineClient>>>,
signer: EngineSigner,
validators: Box<ValidatorSet + Send + Sync>,
validators: Box<ValidatorSet>,
/// Is this Engine just for testing (prevents step calibration).
calibrate_step: bool,
}

View File

@ -58,7 +58,7 @@ pub struct BasicAuthority {
gas_limit_bound_divisor: U256,
builtins: BTreeMap<Address, Builtin>,
signer: EngineSigner,
validators: Box<ValidatorSet + Send + Sync>,
validators: Box<ValidatorSet>,
}
impl BasicAuthority {
@ -138,7 +138,7 @@ impl Engine for BasicAuthority {
}
fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> {
use rlp::{UntrustedRlp, View};
use rlp::UntrustedRlp;
// Check if the signature belongs to a validator, can depend on parent state.
let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);

View File

@ -20,7 +20,7 @@ use util::*;
use super::{Height, View, BlockHash, Step};
use error::Error;
use header::Header;
use rlp::{Rlp, UntrustedRlp, RlpStream, Encodable, Decodable, Decoder, DecoderError, View as RlpView};
use rlp::{Rlp, UntrustedRlp, RlpStream, Encodable, Decodable, DecoderError};
use ethkey::{recover, public_to_address};
use super::super::vote_collector::Message;
@ -150,8 +150,8 @@ impl Step {
}
impl Decodable for Step {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
match decoder.as_rlp().as_val()? {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
match rlp.as_val()? {
0u8 => Ok(Step::Propose),
1 => Ok(Step::Prevote),
2 => Ok(Step::Precommit),
@ -168,8 +168,7 @@ impl Encodable for Step {
/// (signature, (height, view, step, block_hash))
impl Decodable for ConsensusMessage {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let rlp = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let m = rlp.at(1)?;
let block_message: H256 = m.val_at(3)?;
Ok(ConsensusMessage {

View File

@ -33,7 +33,7 @@ use error::{Error, BlockError};
use header::Header;
use builtin::Builtin;
use env_info::EnvInfo;
use rlp::{UntrustedRlp, View as RlpView};
use rlp::UntrustedRlp;
use ethkey::{recover, public_to_address, Signature};
use account_provider::AccountProvider;
use block::*;
@ -98,7 +98,7 @@ pub struct Tendermint {
/// Hash of the proposal parent block.
proposal_parent: RwLock<H256>,
/// Set used to determine the current validators.
validators: Box<ValidatorSet + Send + Sync>,
validators: Box<ValidatorSet>,
}
impl Tendermint {

View File

@ -19,6 +19,7 @@
mod simple_list;
mod safe_contract;
mod contract;
mod multi;
use std::sync::Weak;
use util::{Address, H256};
@ -27,23 +28,27 @@ use client::Client;
use self::simple_list::SimpleList;
use self::contract::ValidatorContract;
use self::safe_contract::ValidatorSafeContract;
use self::multi::Multi;
/// Creates a validator set from spec.
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet + Send + Sync> {
pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet> {
match spec {
ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())),
ValidatorSpec::SafeContract(address) => Box::new(ValidatorSafeContract::new(address.into())),
ValidatorSpec::Contract(address) => Box::new(ValidatorContract::new(address.into())),
ValidatorSpec::Multi(sequence) => Box::new(
Multi::new(sequence.into_iter().map(|(block, set)| (block.into(), new_validator_set(set))).collect())
),
}
}
pub trait ValidatorSet {
pub trait ValidatorSet: Send + Sync {
/// Checks if a given address is a validator.
fn contains(&self, bh: &H256, address: &Address) -> bool;
fn contains(&self, parent_block_hash: &H256, address: &Address) -> bool;
/// Draws an validator nonce modulo number of validators.
fn get(&self, bh: &H256, nonce: usize) -> Address;
fn get(&self, parent_block_hash: &H256, nonce: usize) -> Address;
/// Returns the current number of validators.
fn count(&self, bh: &H256) -> usize;
fn count(&self, parent_block_hash: &H256) -> usize;
/// Notifies about malicious behaviour.
fn report_malicious(&self, _validator: &Address) {}
/// Notifies about benign misbehaviour.

View File

@ -0,0 +1,158 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
/// Validator set changing at fork blocks.
use std::collections::BTreeMap;
use std::sync::Weak;
use util::{H256, Address, RwLock};
use ids::BlockId;
use header::BlockNumber;
use client::{Client, BlockChainClient};
use super::ValidatorSet;
type BlockNumberLookup = Box<Fn(&H256) -> Result<BlockNumber, String> + Send + Sync + 'static>;
pub struct Multi {
sets: BTreeMap<BlockNumber, Box<ValidatorSet>>,
block_number: RwLock<BlockNumberLookup>,
}
impl Multi {
pub fn new(set_map: BTreeMap<BlockNumber, Box<ValidatorSet>>) -> Self {
assert!(set_map.get(&0u64).is_some(), "ValidatorSet has to be specified from block 0.");
Multi {
sets: set_map,
block_number: RwLock::new(Box::new(move |_| Err("No client!".into()))),
}
}
fn correct_set(&self, bh: &H256) -> Option<&Box<ValidatorSet>> {
match self
.block_number
.read()(bh)
.map(|parent_block| self
.sets
.iter()
.rev()
.find(|&(block, _)| *block <= parent_block + 1)
.expect("constructor validation ensures that there is at least one validator set for block 0;
block 0 is less than any uint;
qed")
) {
Ok((block, set)) => {
trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block);
Some(set)
},
Err(e) => {
debug!(target: "engine", "ValidatorSet could not be recovered: {}", e);
None
},
}
}
}
impl ValidatorSet for Multi {
fn contains(&self, bh: &H256, address: &Address) -> bool {
self.correct_set(bh).map_or(false, |set| set.contains(bh, address))
}
fn get(&self, bh: &H256, nonce: usize) -> Address {
self.correct_set(bh).map_or_else(Default::default, |set| set.get(bh, nonce))
}
fn count(&self, bh: &H256) -> usize {
self.correct_set(bh).map_or_else(usize::max_value, |set| set.count(bh))
}
fn report_malicious(&self, validator: &Address) {
for set in self.sets.values() {
set.report_malicious(validator);
}
}
fn report_benign(&self, validator: &Address) {
for set in self.sets.values() {
set.report_benign(validator);
}
}
fn register_contract(&self, client: Weak<Client>) {
for set in self.sets.values() {
set.register_contract(client.clone());
}
*self.block_number.write() = Box::new(move |hash| client
.upgrade()
.ok_or("No client!".into())
.and_then(|c| c.block_number(BlockId::Hash(*hash)).ok_or("Unknown block".into())));
}
}
#[cfg(test)]
mod tests {
use util::*;
use types::ids::BlockId;
use spec::Spec;
use account_provider::AccountProvider;
use client::{BlockChainClient, EngineClient};
use ethkey::Secret;
use miner::MinerService;
use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data};
#[test]
fn uses_current_set() {
::env_logger::init().unwrap();
let tap = Arc::new(AccountProvider::transient_provider());
let s0 = Secret::from_slice(&"0".sha3()).unwrap();
let v0 = tap.insert_account(s0.clone(), "").unwrap();
let v1 = tap.insert_account(Secret::from_slice(&"1".sha3()).unwrap(), "").unwrap();
let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_multi, Some(tap));
client.engine().register_client(Arc::downgrade(&client));
// Make sure txs go through.
client.miner().set_gas_floor_target(1_000_000.into());
// Wrong signer for the first block.
client.miner().set_engine_signer(v1, "".into()).unwrap();
client.transact_contract(Default::default(), Default::default()).unwrap();
client.update_sealing();
assert_eq!(client.chain_info().best_block_number, 0);
// Right signer for the first block.
client.miner().set_engine_signer(v0, "".into()).unwrap();
client.update_sealing();
assert_eq!(client.chain_info().best_block_number, 1);
// This time v0 is wrong.
client.transact_contract(Default::default(), Default::default()).unwrap();
client.update_sealing();
assert_eq!(client.chain_info().best_block_number, 1);
client.miner().set_engine_signer(v1, "".into()).unwrap();
client.update_sealing();
assert_eq!(client.chain_info().best_block_number, 2);
// v1 is still good.
client.transact_contract(Default::default(), Default::default()).unwrap();
client.update_sealing();
assert_eq!(client.chain_info().best_block_number, 3);
// Check syncing.
let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_multi, 0, 0, &[]);
sync_client.engine().register_client(Arc::downgrade(&sync_client));
for i in 1..4 {
sync_client.import_block(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap();
}
sync_client.flush_queue();
assert_eq!(sync_client.chain_info().best_block_number, 3);
}
}

View File

@ -27,7 +27,7 @@ use transaction::UnverifiedTransaction;
use engines::Engine;
use evm::Schedule;
use ethjson;
use rlp::{self, UntrustedRlp, View};
use rlp::{self, UntrustedRlp};
/// Parity tries to round block.gas_limit to multiple of this constant
pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]);
@ -43,6 +43,8 @@ pub struct EthashParams {
pub difficulty_bound_divisor: U256,
/// Difficulty increment divisor.
pub difficulty_increment_divisor: u64,
/// Metropolis difficulty increment divisor.
pub metropolis_difficulty_increment_divisor: u64,
/// Block duration.
pub duration_limit: u64,
/// Block reward.
@ -63,6 +65,8 @@ pub struct EthashParams {
pub difficulty_hardfork_bound_divisor: U256,
/// Block on which there is no additional difficulty from the exponential bomb.
pub bomb_defuse_transition: u64,
/// Number of first block where EIP-100 rules begin.
pub eip100b_transition: u64,
/// Number of first block where EIP-150 rules begin.
pub eip150_transition: u64,
/// Number of first block where EIP-155 rules begin.
@ -96,6 +100,7 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
minimum_difficulty: p.minimum_difficulty.into(),
difficulty_bound_divisor: p.difficulty_bound_divisor.into(),
difficulty_increment_divisor: p.difficulty_increment_divisor.map_or(10, Into::into),
metropolis_difficulty_increment_divisor: p.metropolis_difficulty_increment_divisor.map_or(9, Into::into),
duration_limit: p.duration_limit.into(),
block_reward: p.block_reward.into(),
registrar: p.registrar.map_or_else(Address::new, Into::into),
@ -106,6 +111,7 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(u64::max_value(), Into::into),
difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), Into::into),
bomb_defuse_transition: p.bomb_defuse_transition.map_or(u64::max_value(), Into::into),
eip100b_transition: p.eip100b_transition.map_or(u64::max_value(), Into::into),
eip150_transition: p.eip150_transition.map_or(0, Into::into),
eip155_transition: p.eip155_transition.map_or(0, Into::into),
eip160_transition: p.eip160_transition.map_or(0, Into::into),
@ -406,6 +412,8 @@ impl Ethash {
panic!("Can't calculate genesis block difficulty");
}
let parent_has_uncles = parent.uncles_hash() != &sha3::SHA3_EMPTY_LIST_RLP;
let min_difficulty = self.ethash_params.minimum_difficulty;
let difficulty_hardfork = header.number() >= self.ethash_params.difficulty_hardfork_transition;
let difficulty_bound_divisor = match difficulty_hardfork {
@ -417,19 +425,27 @@ impl Ethash {
let mut target = if header.number() < frontier_limit {
if header.timestamp() >= parent.timestamp() + duration_limit {
parent.difficulty().clone() - (parent.difficulty().clone() / difficulty_bound_divisor)
*parent.difficulty() - (*parent.difficulty() / difficulty_bound_divisor)
} else {
parent.difficulty().clone() + (parent.difficulty().clone() / difficulty_bound_divisor)
*parent.difficulty() + (*parent.difficulty() / difficulty_bound_divisor)
}
}
else {
trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty(), header.timestamp(), parent.timestamp());
//block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99)
let diff_inc = (header.timestamp() - parent.timestamp()) / self.ethash_params.difficulty_increment_divisor;
if diff_inc <= 1 {
parent.difficulty().clone() + parent.difficulty().clone() / From::from(difficulty_bound_divisor) * From::from(1 - diff_inc)
let (increment_divisor, threshold) = if header.number() < self.ethash_params.eip100b_transition {
(self.ethash_params.difficulty_increment_divisor, 1)
} else if parent_has_uncles {
(self.ethash_params.metropolis_difficulty_increment_divisor, 2)
} else {
parent.difficulty().clone() - parent.difficulty().clone() / From::from(difficulty_bound_divisor) * From::from(min(diff_inc - 1, 99))
(self.ethash_params.metropolis_difficulty_increment_divisor, 1)
};
let diff_inc = (header.timestamp() - parent.timestamp()) / increment_divisor;
if diff_inc <= threshold {
*parent.difficulty() + *parent.difficulty() / difficulty_bound_divisor * (threshold - diff_inc).into()
} else {
*parent.difficulty() - *parent.difficulty() / difficulty_bound_divisor * min(diff_inc - threshold, 99).into()
}
};
target = max(min_difficulty, target);

View File

@ -261,9 +261,7 @@ impl Header {
}
impl Decodable for Header {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let r = decoder.as_rlp();
fn decode(r: &UntrustedRlp) -> Result<Self, DecoderError> {
let mut blockheader = Header {
parent_hash: r.val_at(0)?,
uncles_hash: r.val_at(1)?,
@ -283,7 +281,7 @@ impl Decodable for Header {
bare_hash: RefCell::new(None),
};
for i in 13..r.item_count() {
for i in 13..r.item_count()? {
blockheader.seal.push(r.at(i)?.as_raw().to_vec())
}

View File

@ -25,7 +25,7 @@ use miner::Miner;
use io::IoChannel;
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
init_log();
::ethcore_logger::init_log();
let tests = ethjson::blockchain::Test::load(json_data).unwrap();
let mut failed = Vec::new();

View File

@ -22,7 +22,7 @@ use ethereum;
use ethjson;
pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
init_log();
::ethcore_logger::init_log();
let tests = ethjson::state::Test::load(json_data).unwrap();
let mut failed = Vec::new();
let engine = match era {

View File

@ -17,7 +17,7 @@
use super::test_common::*;
use evm;
use ethjson;
use rlp::{UntrustedRlp, View};
use rlp::UntrustedRlp;
use transaction::{Action, UnverifiedTransaction};
use ethstore::ethkey::public_to_address;

View File

@ -106,8 +106,10 @@ extern crate ethcore_stratum;
extern crate ethabi;
extern crate hardware_wallet;
extern crate stats;
extern crate ethcore_logger;
extern crate num;
extern crate bn;
extern crate itertools;
#[macro_use]
extern crate log;

View File

@ -17,7 +17,7 @@
//! This migration compresses the state db.
use util::migration::{SimpleMigration, Progress};
use rlp::{Compressible, UntrustedRlp, View, RlpType};
use rlp::{Compressible, UntrustedRlp, RlpType};
/// Compressing migration.
#[derive(Default)]

View File

@ -26,8 +26,7 @@ use util::migration::{Batch, Config, Error, Migration, SimpleMigration, Progress
use util::sha3::Hashable;
use std::sync::Arc;
use rlp::{decode, Rlp, RlpStream, View};
use rlp::{decode, Rlp, RlpStream};
// attempt to migrate a key, value pair. None if migration not possible.
fn attempt_migrate(mut key_h: H256, val: &[u8]) -> Option<H256> {
@ -184,7 +183,7 @@ impl OverlayRecentV7 {
}
// migrate all deleted keys.
let mut deleted_keys: Vec<H256> = rlp.val_at(2);
let mut deleted_keys: Vec<H256> = rlp.list_at(2);
for old_key in &mut deleted_keys {
if let Some(new) = self.migrated_keys.get(&*old_key) {
*old_key = new.clone();

View File

@ -17,7 +17,7 @@
//! This migration consolidates all databases into single one using Column Families.
use rlp::{Rlp, RlpStream, View};
use rlp::{Rlp, RlpStream};
use util::kvdb::Database;
use util::migration::{Batch, Config, Error, Migration, Progress};
use std::sync::Arc;

View File

@ -110,7 +110,7 @@ impl PriceInfo {
fn should_get_price_info() {
use std::sync::Arc;
use std::time::Duration;
use util::log::init_log;
use ethcore_logger::init_log;
use util::{Condvar, Mutex};
init_log();

View File

@ -2454,6 +2454,7 @@ pub mod test {
#[test]
fn should_replace_same_transaction_when_has_higher_fee() {
use ethcore_logger::init_log;
init_log();
// given
let mut txq = TransactionQueue::default();

View File

@ -22,7 +22,8 @@ use snapshot::Error;
use util::{U256, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP};
use util::trie::{TrieDB, Trie};
use rlp::{RlpStream, UntrustedRlp, View};
use rlp::{RlpStream, UntrustedRlp};
use itertools::Itertools;
use std::collections::HashSet;
@ -60,25 +61,23 @@ impl CodeState {
}
}
// walk the account's storage trie, returning an RLP item containing the
// account properties and the storage.
pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet<H256>) -> Result<Bytes, Error> {
// walk the account's storage trie, returning a vector of RLP items containing the
// account properties and the storage. Each item contains at most `max_storage_items`
// storage records split according to snapshot format definition.
pub fn to_fat_rlps(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet<H256>, max_storage_items: usize) -> Result<Vec<Bytes>, Error> {
if acc == &ACC_EMPTY {
return Ok(::rlp::NULL_RLP.to_vec());
return Ok(vec![::rlp::NULL_RLP.to_vec()]);
}
let db = TrieDB::new(acct_db, &acc.storage_root)?;
let mut pairs = Vec::new();
for item in db.iter()? {
let (k, v) = item?;
pairs.push((k, v));
}
let chunks = db.iter()?.chunks(max_storage_items);
let pair_chunks = chunks.into_iter().map(|chunk| chunk.collect());
pair_chunks.pad_using(1, |_| Vec::new(), ).map(|pairs| {
let mut stream = RlpStream::new_list(pairs.len());
for (k, v) in pairs {
for r in pairs {
let (k, v) = r?;
stream.begin_list(2).append(&k).append(&&*v);
}
@ -107,8 +106,8 @@ pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashS
}
account_stream.append_raw(&pairs_rlp, 1);
Ok(account_stream.out())
}).collect()
}
// decode a fat rlp, and rebuild the storage trie as we go.
@ -117,6 +116,7 @@ pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashS
pub fn from_fat_rlp(
acct_db: &mut AccountDBMut,
rlp: UntrustedRlp,
mut storage_root: H256,
) -> Result<(BasicAccount, Option<Bytes>), Error> {
use util::{TrieDBMut, TrieMut};
@ -148,10 +148,12 @@ pub fn from_fat_rlp(
}
};
let mut storage_root = H256::zero();
{
let mut storage_trie = TrieDBMut::new(acct_db, &mut storage_root);
let mut storage_trie = if storage_root.is_zero() {
TrieDBMut::new(acct_db, &mut storage_root)
} else {
TrieDBMut::from_existing(acct_db, &mut storage_root)?
};
let pairs = rlp.at(4)?;
for pair_rlp in pairs.iter() {
let k: Bytes = pair_rlp.val_at(0)?;
@ -180,11 +182,11 @@ mod tests {
use util::sha3::{SHA3_EMPTY, SHA3_NULL_RLP};
use util::{Address, H256, HashDB, DBValue};
use rlp::{UntrustedRlp, View};
use rlp::UntrustedRlp;
use std::collections::HashSet;
use super::{ACC_EMPTY, to_fat_rlp, from_fat_rlp};
use super::{ACC_EMPTY, to_fat_rlps, from_fat_rlp};
#[test]
fn encoding_basic() {
@ -201,9 +203,9 @@ mod tests {
let thin_rlp = ::rlp::encode(&account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
let fat_rlp = to_fat_rlp(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
let fat_rlp = UntrustedRlp::new(&fat_rlp);
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
let fat_rlps = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value()).unwrap();
let fat_rlp = UntrustedRlp::new(&fat_rlps[0]);
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account);
}
#[test]
@ -226,9 +228,40 @@ mod tests {
let thin_rlp = ::rlp::encode(&account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
let fat_rlp = to_fat_rlp(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap();
let fat_rlp = UntrustedRlp::new(&fat_rlp);
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account);
let fat_rlp = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value()).unwrap();
let fat_rlp = UntrustedRlp::new(&fat_rlp[0]);
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account);
}
#[test]
fn encoding_storage_split() {
let mut db = get_temp_state_db();
let addr = Address::random();
let account = {
let acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr);
let mut root = SHA3_NULL_RLP;
fill_storage(acct_db, &mut root, &mut H256::zero());
BasicAccount {
nonce: 25.into(),
balance: 987654321.into(),
storage_root: root,
code_hash: SHA3_EMPTY,
}
};
let thin_rlp = ::rlp::encode(&account);
assert_eq!(::rlp::decode::<BasicAccount>(&thin_rlp), account);
let fat_rlps = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), 100).unwrap();
let mut root = SHA3_NULL_RLP;
let mut restored_account = None;
for rlp in fat_rlps {
let fat_rlp = UntrustedRlp::new(&rlp);
restored_account = Some(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, root).unwrap().0);
root = restored_account.as_ref().unwrap().storage_root.clone();
}
assert_eq!(restored_account, Some(account));
}
#[test]
@ -264,18 +297,18 @@ mod tests {
let mut used_code = HashSet::new();
let fat_rlp1 = to_fat_rlp(&account1, &AccountDB::new(db.as_hashdb(), &addr1), &mut used_code).unwrap();
let fat_rlp2 = to_fat_rlp(&account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code).unwrap();
let fat_rlp1 = to_fat_rlps(&account1, &AccountDB::new(db.as_hashdb(), &addr1), &mut used_code, usize::max_value()).unwrap();
let fat_rlp2 = to_fat_rlps(&account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code, usize::max_value()).unwrap();
assert_eq!(used_code.len(), 1);
let fat_rlp1 = UntrustedRlp::new(&fat_rlp1);
let fat_rlp2 = UntrustedRlp::new(&fat_rlp2);
let fat_rlp1 = UntrustedRlp::new(&fat_rlp1[0]);
let fat_rlp2 = UntrustedRlp::new(&fat_rlp2[0]);
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2).unwrap();
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2, H256::zero()).unwrap();
assert!(maybe_code.is_none());
assert_eq!(acc, account2);
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1).unwrap();
let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1, H256::zero()).unwrap();
assert_eq!(maybe_code, Some(b"this is definitely code".to_vec()));
assert_eq!(acc, account1);
}
@ -285,7 +318,7 @@ mod tests {
let mut db = get_temp_state_db();
let mut used_code = HashSet::new();
assert_eq!(to_fat_rlp(&ACC_EMPTY, &AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code).unwrap(), ::rlp::NULL_RLP.to_vec());
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP)).unwrap(), (ACC_EMPTY, None));
assert_eq!(to_fat_rlps(&ACC_EMPTY, &AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code, usize::max_value()).unwrap(), vec![::rlp::NULL_RLP.to_vec()]);
assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None));
}
}

View File

@ -20,7 +20,7 @@ use block::Block;
use header::Header;
use views::BlockView;
use rlp::{DecoderError, RlpStream, UntrustedRlp, View};
use rlp::{DecoderError, RlpStream, UntrustedRlp};
use util::{Bytes, Hashable, H256};
use util::triehash::ordered_trie_root;
@ -101,8 +101,8 @@ impl AbridgedBlock {
header.set_timestamp(rlp.val_at(6)?);
header.set_extra_data(rlp.val_at(7)?);
let transactions = rlp.val_at(8)?;
let uncles: Vec<Header> = rlp.val_at(9)?;
let transactions = rlp.list_at(8)?;
let uncles: Vec<Header> = rlp.list_at(9)?;
header.set_transactions_root(ordered_trie_root(
rlp.at(8)?.iter().map(|r| r.as_raw().to_owned())
@ -114,7 +114,7 @@ impl AbridgedBlock {
header.set_uncles_hash(uncles_rlp.as_raw().sha3());
let mut seal_fields = Vec::new();
for i in (HEADER_FIELDS + BLOCK_FIELDS)..rlp.item_count() {
for i in (HEADER_FIELDS + BLOCK_FIELDS)..rlp.item_count()? {
let seal_rlp = rlp.at(i)?;
seal_fields.push(seal_rlp.as_raw().to_owned());
}

View File

@ -53,6 +53,8 @@ pub enum Error {
Decoder(DecoderError),
/// Io error.
Io(::std::io::Error),
/// Snapshot version is not supported.
VersionNotSupported(u64),
}
impl fmt::Display for Error {
@ -73,6 +75,7 @@ impl fmt::Display for Error {
Error::Io(ref err) => err.fmt(f),
Error::Decoder(ref err) => err.fmt(f),
Error::Trie(ref err) => err.fmt(f),
Error::VersionNotSupported(ref ver) => write!(f, "Snapshot version {} is not supprted.", ver),
}
}
}

View File

@ -27,10 +27,12 @@ use std::path::{Path, PathBuf};
use util::Bytes;
use util::hash::H256;
use rlp::{self, Encodable, RlpStream, UntrustedRlp, View};
use rlp::{self, Encodable, RlpStream, UntrustedRlp};
use super::ManifestData;
const SNAPSHOT_VERSION: u64 = 2;
/// Something which can write snapshots.
/// Writing the same chunk multiple times will lead to implementation-defined
/// behavior, and is not advised.
@ -57,12 +59,10 @@ impl Encodable for ChunkInfo {
}
impl rlp::Decodable for ChunkInfo {
fn decode<D: rlp::Decoder>(decoder: &D) -> Result<Self, rlp::DecoderError> {
let d = decoder.as_rlp();
let hash = d.val_at(0)?;
let len = d.val_at(1)?;
let off = d.val_at(2)?;
fn decode(rlp: &UntrustedRlp) -> Result<Self, rlp::DecoderError> {
let hash = rlp.val_at(0)?;
let len = rlp.val_at(1)?;
let off = rlp.val_at(2)?;
Ok(ChunkInfo(hash, len, off))
}
}
@ -120,8 +120,9 @@ impl SnapshotWriter for PackedWriter {
fn finish(mut self, manifest: ManifestData) -> io::Result<()> {
// we ignore the hashes fields of the manifest under the assumption that
// they are consistent with ours.
let mut stream = RlpStream::new_list(5);
let mut stream = RlpStream::new_list(6);
stream
.append(&SNAPSHOT_VERSION)
.append_list(&self.state_hashes)
.append_list(&self.block_hashes)
.append(&manifest.state_root)
@ -223,7 +224,7 @@ impl PackedReader {
/// Create a new `PackedReader` for the file at the given path.
/// This will fail if any io errors are encountered or the file
/// is not a valid packed snapshot.
pub fn new(path: &Path) -> Result<Option<Self>, ::error::Error> {
pub fn new(path: &Path) -> Result<Option<Self>, ::snapshot::error::Error> {
let mut file = File::open(path)?;
let file_len = file.metadata()?.len();
if file_len < 8 {
@ -257,15 +258,26 @@ impl PackedReader {
let rlp = UntrustedRlp::new(&manifest_buf);
let state: Vec<ChunkInfo> = rlp.val_at(0)?;
let blocks: Vec<ChunkInfo> = rlp.val_at(1)?;
let (start, version) = if rlp.item_count()? == 5 {
(0, 1)
} else {
(1, rlp.val_at(0)?)
};
if version > SNAPSHOT_VERSION {
return Err(::snapshot::error::Error::VersionNotSupported(version));
}
let state: Vec<ChunkInfo> = rlp.list_at(0 + start)?;
let blocks: Vec<ChunkInfo> = rlp.list_at(1 + start)?;
let manifest = ManifestData {
version: version,
state_hashes: state.iter().map(|c| c.0).collect(),
block_hashes: blocks.iter().map(|c| c.0).collect(),
state_root: rlp.val_at(2)?,
block_number: rlp.val_at(3)?,
block_hash: rlp.val_at(4)?,
state_root: rlp.val_at(2 + start)?,
block_number: rlp.val_at(3 + start)?,
block_hash: rlp.val_at(4 + start)?,
};
Ok(Some(PackedReader {
@ -348,7 +360,7 @@ mod tests {
use util::sha3::Hashable;
use snapshot::ManifestData;
use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader};
use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader, SNAPSHOT_VERSION};
const STATE_CHUNKS: &'static [&'static [u8]] = &[b"dog", b"cat", b"hello world", b"hi", b"notarealchunk"];
const BLOCK_CHUNKS: &'static [&'static [u8]] = &[b"hello!", b"goodbye!", b"abcdefg", b"hijklmnop", b"qrstuvwxy", b"and", b"z"];
@ -374,6 +386,7 @@ mod tests {
}
let manifest = ManifestData {
version: SNAPSHOT_VERSION,
state_hashes: state_hashes,
block_hashes: block_hashes,
state_root: b"notarealroot".sha3(),
@ -412,6 +425,7 @@ mod tests {
}
let manifest = ManifestData {
version: SNAPSHOT_VERSION,
state_hashes: state_hashes,
block_hashes: block_hashes,
state_root: b"notarealroot".sha3(),

View File

@ -37,7 +37,7 @@ use util::journaldb::{self, Algorithm, JournalDB};
use util::kvdb::Database;
use util::trie::{TrieDB, TrieDBMut, Trie, TrieMut};
use util::sha3::SHA3_NULL_RLP;
use rlp::{RlpStream, UntrustedRlp, View};
use rlp::{RlpStream, UntrustedRlp};
use bloom_journal::Bloom;
use self::block::AbridgedBlock;
@ -56,6 +56,7 @@ pub use self::traits::SnapshotService;
pub use self::watcher::Watcher;
pub use types::snapshot_manifest::ManifestData;
pub use types::restoration_status::RestorationStatus;
pub use types::basic_account::BasicAccount;
pub mod io;
pub mod service;
@ -82,6 +83,9 @@ mod traits {
// Try to have chunks be around 4MB (before compression)
const PREFERRED_CHUNK_SIZE: usize = 4 * 1024 * 1024;
// Try to have chunks be around 4MB (before compression)
const MAX_STORAGE_ENTRIES_PER_ACCOUNT_RECORD: usize = 80_000;
// How many blocks to include in a snapshot, starting from the head of the chain.
const SNAPSHOT_BLOCKS: u64 = 30000;
@ -147,6 +151,7 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
info!("produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len());
let manifest_data = ManifestData {
version: 2,
state_hashes: state_hashes,
block_hashes: block_hashes,
state_root: *state_root,
@ -300,14 +305,14 @@ impl<'a> StateChunker<'a> {
//
// If the buffer is greater than the desired chunk size,
// this will write out the data to disk.
fn push(&mut self, account_hash: Bytes, data: Bytes) -> Result<(), Error> {
fn push(&mut self, account_hash: Bytes, data: Bytes, force_chunk: bool) -> Result<(), Error> {
let pair = {
let mut stream = RlpStream::new_list(2);
stream.append(&account_hash).append_raw(&data, 1);
stream.out()
};
if self.cur_size + pair.len() >= PREFERRED_CHUNK_SIZE {
if force_chunk || self.cur_size + pair.len() >= PREFERRED_CHUNK_SIZE {
self.write_chunk()?;
}
@ -372,8 +377,10 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex<SnapshotWriter +
let account_db = AccountDB::from_hash(db, account_key_hash);
let fat_rlp = account::to_fat_rlp(&account, &account_db, &mut used_code)?;
chunker.push(account_key, fat_rlp)?;
let fat_rlps = account::to_fat_rlps(&account, &account_db, &mut used_code, MAX_STORAGE_ENTRIES_PER_ACCOUNT_RECORD)?;
for (i, fat_rlp) in fat_rlps.into_iter().enumerate() {
chunker.push(account_key.clone(), fat_rlp, i > 0)?;
}
}
if chunker.cur_size != 0 {
@ -390,6 +397,7 @@ pub struct StateRebuilder {
known_code: HashMap<H256, H256>, // code hashes mapped to first account with this code.
missing_code: HashMap<H256, Vec<H256>>, // maps code hashes to lists of accounts missing that code.
bloom: Bloom,
known_storage_roots: HashMap<H256, H256>, // maps account hashes to last known storage root. Only filled for last account per chunk.
}
impl StateRebuilder {
@ -401,6 +409,7 @@ impl StateRebuilder {
known_code: HashMap::new(),
missing_code: HashMap::new(),
bloom: StateDB::load_bloom(&*db),
known_storage_roots: HashMap::new(),
}
}
@ -408,16 +417,17 @@ impl StateRebuilder {
pub fn feed(&mut self, chunk: &[u8], flag: &AtomicBool) -> Result<(), ::error::Error> {
let rlp = UntrustedRlp::new(chunk);
let empty_rlp = StateAccount::new_basic(U256::zero(), U256::zero()).rlp();
let mut pairs = Vec::with_capacity(rlp.item_count());
let mut pairs = Vec::with_capacity(rlp.item_count()?);
// initialize the pairs vector with empty values so we have slots to write into.
pairs.resize(rlp.item_count(), (H256::new(), Vec::new()));
pairs.resize(rlp.item_count()?, (H256::new(), Vec::new()));
let status = rebuild_accounts(
self.db.as_hashdb_mut(),
rlp,
&mut pairs,
&self.known_code,
&mut self.known_storage_roots,
flag
)?;
@ -464,14 +474,18 @@ impl StateRebuilder {
Ok(())
}
/// Check for accounts missing code. Once all chunks have been fed, there should
/// be none.
pub fn check_missing(self) -> Result<(), Error> {
/// Finalize the restoration. Check for accounts missing code and make a dummy
/// journal entry.
/// Once all chunks have been fed, there should be nothing missing.
pub fn finalize(mut self, era: u64, id: H256) -> Result<(), ::error::Error> {
let missing = self.missing_code.keys().cloned().collect::<Vec<_>>();
match missing.is_empty() {
true => Ok(()),
false => Err(Error::MissingCode(missing)),
}
if !missing.is_empty() { return Err(Error::MissingCode(missing).into()) }
let mut batch = self.db.backing().transaction();
self.db.journal_under(&mut batch, era, &id)?;
self.db.backing().write_buffered(batch);
Ok(())
}
/// Get the state root of the rebuilder.
@ -492,10 +506,11 @@ fn rebuild_accounts(
account_fat_rlps: UntrustedRlp,
out_chunk: &mut [(H256, Bytes)],
known_code: &HashMap<H256, H256>,
known_storage_roots: &mut HashMap<H256, H256>,
abort_flag: &AtomicBool,
) -> Result<RebuiltStatus, ::error::Error> {
let mut status = RebuiltStatus::default();
for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk) {
for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk.iter_mut()) {
if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) }
let hash: H256 = account_rlp.val_at(0)?;
@ -506,7 +521,8 @@ fn rebuild_accounts(
// fill out the storage trie and code while decoding.
let (acc, maybe_code) = {
let mut acct_db = AccountDBMut::from_hash(db, hash);
account::from_fat_rlp(&mut acct_db, fat_rlp)?
let storage_root = known_storage_roots.get(&hash).cloned().unwrap_or(H256::zero());
account::from_fat_rlp(&mut acct_db, fat_rlp, storage_root)?
};
let code_hash = acc.code_hash.clone();
@ -538,6 +554,12 @@ fn rebuild_accounts(
*out = (hash, thin_rlp);
}
if let Some(&(ref hash, ref rlp)) = out_chunk.iter().last() {
known_storage_roots.insert(*hash, ::rlp::decode::<BasicAccount>(rlp).storage_root);
}
if let Some(&(ref hash, ref rlp)) = out_chunk.iter().next() {
known_storage_roots.insert(*hash, ::rlp::decode::<BasicAccount>(rlp).storage_root);
}
Ok(status)
}
@ -601,7 +623,7 @@ impl BlockRebuilder {
use util::triehash::ordered_trie_root;
let rlp = UntrustedRlp::new(chunk);
let item_count = rlp.item_count();
let item_count = rlp.item_count()?;
let num_blocks = (item_count - 3) as u64;
trace!(target: "snapshot", "restoring block chunk with {} blocks.", item_count - 3);
@ -621,7 +643,7 @@ impl BlockRebuilder {
let pair = rlp.at(idx)?;
let abridged_rlp = pair.at(0)?.as_raw().to_owned();
let abridged_block = AbridgedBlock::from_raw(abridged_rlp);
let receipts: Vec<::receipt::Receipt> = pair.val_at(1)?;
let receipts: Vec<::receipt::Receipt> = pair.list_at(1)?;
let receipts_root = ordered_trie_root(
pair.at(1)?.iter().map(|r| r.as_raw().to_owned())
);

View File

@ -166,7 +166,7 @@ impl Restoration {
}
// check for missing code.
self.state.check_missing()?;
self.state.finalize(self.manifest.block_number, self.manifest.block_hash)?;
// connect out-of-order chunks and verify chain integrity.
self.blocks.finalize(self.canonical_hashes)?;
@ -656,6 +656,7 @@ mod tests {
assert_eq!(service.status(), RestorationStatus::Inactive);
let manifest = ManifestData {
version: 2,
state_hashes: vec![],
block_hashes: vec![],
state_root: Default::default(),

View File

@ -63,6 +63,7 @@ fn chunk_and_restore(amount: u64) {
let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap());
let block_hashes = chunk_blocks(&bc, best_hash, &writer, &Progress::default()).unwrap();
let manifest = ::snapshot::ManifestData {
version: 2,
state_hashes: Vec::new(),
block_hashes: block_hashes,
state_root: ::util::sha3::SHA3_NULL_RLP,
@ -125,6 +126,7 @@ fn checks_flag() {
let chain = BlockChain::new(Default::default(), &genesis, db.clone());
let manifest = ::snapshot::ManifestData {
version: 2,
state_hashes: Vec::new(),
block_hashes: Vec::new(),
state_root: ::util::sha3::SHA3_NULL_RLP,

View File

@ -27,6 +27,7 @@ use super::ManifestData;
#[test]
fn manifest_rlp() {
let manifest = ManifestData {
version: 2,
block_hashes: Vec::new(),
state_hashes: Vec::new(),
block_number: 1234567,

View File

@ -122,6 +122,7 @@ fn guards_delete_folders() {
path.push("restoration");
let manifest = ManifestData {
version: 2,
state_hashes: vec![],
block_hashes: vec![],
block_number: 0,

View File

@ -58,10 +58,11 @@ fn snap_and_restore() {
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap();
writer.into_inner().finish(::snapshot::ManifestData {
version: 2,
state_hashes: state_hashes,
block_hashes: Vec::new(),
state_root: state_root,
block_number: 0,
block_number: 1000,
block_hash: H256::default(),
}).unwrap();
@ -69,7 +70,7 @@ fn snap_and_restore() {
db_path.push("db");
let db = {
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive);
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent);
let reader = PackedReader::new(&snap_file).unwrap().unwrap();
let flag = AtomicBool::new(true);
@ -82,12 +83,13 @@ fn snap_and_restore() {
}
assert_eq!(rebuilder.state_root(), state_root);
rebuilder.check_missing().unwrap();
rebuilder.finalize(1000, H256::default()).unwrap();
new_db
};
let new_db = journaldb::new(db, Algorithm::Archive, ::db::COL_STATE);
let new_db = journaldb::new(db, Algorithm::OverlayRecent, ::db::COL_STATE);
assert_eq!(new_db.earliest_era(), Some(1000));
compare_dbs(&old_db, new_db.as_hashdb());
}
@ -120,10 +122,10 @@ fn get_code_from_prev_chunk() {
let mut db = MemoryDB::new();
AccountDBMut::from_hash(&mut db, hash).insert(&code[..]);
let fat_rlp = account::to_fat_rlp(&acc, &AccountDB::from_hash(&db, hash), &mut used_code).unwrap();
let fat_rlp = account::to_fat_rlps(&acc, &AccountDB::from_hash(&db, hash), &mut used_code, usize::max_value()).unwrap();
let mut stream = RlpStream::new_list(1);
stream.begin_list(2).append(&hash).append_raw(&fat_rlp, 1);
stream.begin_list(2).append(&hash).append_raw(&fat_rlp[0], 1);
stream.out()
};
@ -134,13 +136,18 @@ fn get_code_from_prev_chunk() {
let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
let mut rebuilder = StateRebuilder::new(new_db, Algorithm::Archive);
{
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent);
let flag = AtomicBool::new(true);
rebuilder.feed(&chunk1, &flag).unwrap();
rebuilder.feed(&chunk2, &flag).unwrap();
rebuilder.check_missing().unwrap();
rebuilder.finalize(1000, H256::random()).unwrap();
}
let state_db = journaldb::new(new_db, Algorithm::OverlayRecent, ::db::COL_STATE);
assert_eq!(state_db.earliest_era(), Some(1000));
}
#[test]
@ -164,6 +171,7 @@ fn checks_flag() {
let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap();
writer.into_inner().finish(::snapshot::ManifestData {
version: 2,
state_hashes: state_hashes,
block_hashes: Vec::new(),
state_root: state_root,
@ -175,7 +183,7 @@ fn checks_flag() {
db_path.push("db");
{
let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap());
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive);
let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent);
let reader = PackedReader::new(&snap_file).unwrap().unwrap();
let flag = AtomicBool::new(false);

View File

@ -34,7 +34,7 @@ use super::genesis::Genesis;
use super::seal::Generic as GenericSeal;
use ethereum;
use ethjson;
use rlp::{Rlp, RlpStream, View};
use rlp::{Rlp, RlpStream};
/// Parameters common to all engines.
#[derive(Debug, PartialEq, Clone, Default)]
@ -326,7 +326,7 @@ impl Spec {
pub fn load<R>(reader: R) -> Result<Self, String> where R: Read {
match ethjson::spec::Spec::load(reader) {
Ok(spec) => Ok(spec.into()),
_ => Err("Spec json is invalid".into()),
Err(e) => Err(format!("Spec json is invalid: {}", e)),
}
}
@ -360,6 +360,10 @@ impl Spec {
/// Account is marked with `reportBenign` it can be checked as disliked with "0xd8f2e0bf".
/// Validator can be removed with `reportMalicious`.
pub fn new_validator_contract() -> Self { load_bundled!("validator_contract") }
/// Create a new Spec with BasicAuthority which uses multiple validator sets changing with height.
/// Account with secrets "0".sha3() is the validator for block 1 and with "1".sha3() onwards.
pub fn new_validator_multi() -> Self { load_bundled!("validator_multi") }
}
#[cfg(test)]

View File

@ -438,18 +438,19 @@ impl Account {
/// trie.
/// `storage_key` is the hash of the desired storage key, meaning
/// this will only work correctly under a secure trie.
/// Returns a merkle proof of the storage trie node with all nodes before `from_level`
/// omitted.
pub fn prove_storage(&self, db: &HashDB, storage_key: H256, from_level: u32) -> Result<Vec<Bytes>, Box<TrieError>> {
pub fn prove_storage(&self, db: &HashDB, storage_key: H256) -> Result<(Vec<Bytes>, H256), Box<TrieError>> {
use util::trie::{Trie, TrieDB};
use util::trie::recorder::Recorder;
let mut recorder = Recorder::with_depth(from_level);
let mut recorder = Recorder::new();
let trie = TrieDB::new(db, &self.storage_root)?;
let _ = trie.get_with(&storage_key, &mut recorder)?;
let item: U256 = {
let query = (&mut recorder, ::rlp::decode);
trie.get_with(&storage_key, query)?.unwrap_or_else(U256::zero)
};
Ok(recorder.drain().into_iter().map(|r| r.data).collect())
Ok((recorder.drain().into_iter().map(|r| r.data).collect(), item.into()))
}
}
@ -461,7 +462,7 @@ impl fmt::Debug for Account {
#[cfg(test)]
mod tests {
use rlp::{UntrustedRlp, RlpType, View, Compressible};
use rlp::{UntrustedRlp, RlpType, Compressible};
use util::*;
use super::*;
use account_db::*;

View File

@ -31,6 +31,7 @@ use factory::Factories;
use trace::FlatTrace;
use pod_account::*;
use pod_state::{self, PodState};
use types::basic_account::BasicAccount;
use types::executed::{Executed, ExecutionError};
use types::state_diff::StateDiff;
use transaction::SignedTransaction;
@ -857,47 +858,43 @@ impl<B: Backend> State<B> {
// State proof implementations; useful for light client protocols.
impl<B: Backend> State<B> {
/// Prove an account's existence or nonexistence in the state trie.
/// Returns a merkle proof of the account's trie node with all nodes before `from_level`
/// omitted or an encountered trie error.
/// Returns a merkle proof of the account's trie node omitted or an encountered trie error.
/// If the account doesn't exist in the trie, prove that and return defaults.
/// Requires a secure trie to be used for accurate results.
/// `account_key` == sha3(address)
pub fn prove_account(&self, account_key: H256, from_level: u32) -> trie::Result<Vec<Bytes>> {
let mut recorder = Recorder::with_depth(from_level);
pub fn prove_account(&self, account_key: H256) -> trie::Result<(Vec<Bytes>, BasicAccount)> {
let mut recorder = Recorder::new();
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
trie.get_with(&account_key, &mut recorder)?;
let maybe_account: Option<BasicAccount> = {
let query = (&mut recorder, ::rlp::decode);
trie.get_with(&account_key, query)?
};
let account = maybe_account.unwrap_or_else(|| BasicAccount {
balance: 0.into(),
nonce: self.account_start_nonce,
code_hash: SHA3_EMPTY,
storage_root: ::util::sha3::SHA3_NULL_RLP,
});
Ok(recorder.drain().into_iter().map(|r| r.data).collect())
Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account))
}
/// Prove an account's storage key's existence or nonexistence in the state.
/// Returns a merkle proof of the account's storage trie with all nodes before
/// `from_level` omitted. Requires a secure trie to be used for correctness.
/// Returns a merkle proof of the account's storage trie.
/// Requires a secure trie to be used for correctness.
/// `account_key` == sha3(address)
/// `storage_key` == sha3(key)
pub fn prove_storage(&self, account_key: H256, storage_key: H256, from_level: u32) -> trie::Result<Vec<Bytes>> {
pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> trie::Result<(Vec<Bytes>, H256)> {
// TODO: probably could look into cache somehow but it's keyed by
// address, not sha3(address).
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
let acc = match trie.get_with(&account_key, Account::from_rlp)? {
Some(acc) => acc,
None => return Ok(Vec::new()),
None => return Ok((Vec::new(), H256::new())),
};
let account_db = self.factories.accountdb.readonly(self.db.as_hashdb(), account_key);
acc.prove_storage(account_db.as_hashdb(), storage_key, from_level)
}
/// Get code by address hash.
/// Only works when backed by a secure trie.
pub fn code_by_address_hash(&self, account_key: H256) -> trie::Result<Option<Bytes>> {
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
let mut acc = match trie.get_with(&account_key, Account::from_rlp)? {
Some(acc) => acc,
None => return Ok(None),
};
let account_db = self.factories.accountdb.readonly(self.db.as_hashdb(), account_key);
Ok(acc.cache_code(account_db.as_hashdb()).map(|c| (&*c).clone()))
acc.prove_storage(account_db.as_hashdb(), storage_key)
}
}
@ -946,7 +943,7 @@ mod tests {
use env_info::EnvInfo;
use spec::*;
use transaction::*;
use util::log::init_log;
use ethcore_logger::init_log;
use trace::{FlatTrace, TraceError, trace};
use types::executed::CallType;

View File

@ -457,7 +457,7 @@ mod tests {
use util::{U256, H256, Address, DBTransaction};
use tests::helpers::*;
use state::{Account, Backend};
use util::log::init_log;
use ethcore_logger::init_log;
#[test]
fn state_db_smoke() {

View File

@ -25,7 +25,6 @@ use types::filter::Filter;
use util::*;
use devtools::*;
use miner::Miner;
use rlp::View;
use spec::Spec;
use views::BlockView;
use ethkey::{KeyPair, Secret};

View File

@ -438,6 +438,7 @@ pub fn get_default_ethash_params() -> EthashParams{
minimum_difficulty: U256::from(131072),
difficulty_bound_divisor: U256::from(2048),
difficulty_increment_divisor: 10,
metropolis_difficulty_increment_divisor: 9,
duration_limit: 13,
block_reward: U256::from(0),
registrar: "0000000000000000000000000000000000000001".into(),
@ -448,6 +449,7 @@ pub fn get_default_ethash_params() -> EthashParams{
difficulty_hardfork_transition: u64::max_value(),
difficulty_hardfork_bound_divisor: U256::from(0),
bomb_defuse_transition: u64::max_value(),
eip100b_transition: u64::max_value(),
eip150_transition: u64::max_value(),
eip155_transition: u64::max_value(),
eip160_transition: u64::max_value(),

View File

@ -60,8 +60,8 @@ impl Into<BloomGroup> for BlockTracesBloomGroup {
}
impl Decodable for BlockTracesBloom {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
Decodable::decode(decoder).map(BlockTracesBloom)
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
LogBloom::decode(rlp).map(BlockTracesBloom)
}
}
@ -72,8 +72,8 @@ impl Encodable for BlockTracesBloom {
}
impl Decodable for BlockTracesBloomGroup {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let blooms = Decodable::decode(decoder)?;
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let blooms = rlp.as_list()?;
let group = BlockTracesBloomGroup {
blooms: blooms
};

View File

@ -43,8 +43,7 @@ impl Encodable for BasicAccount {
}
impl Decodable for BasicAccount {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let rlp = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(BasicAccount {
nonce: rlp.val_at(0)?,
balance: rlp.val_at(1)?,

View File

@ -29,7 +29,7 @@ use transaction::UnverifiedTransaction;
use views;
use util::{Address, Hashable, H256, H2048, U256, HeapSizeOf};
use rlp::{Rlp, View};
use rlp::Rlp;
/// Owning header view.
#[derive(Debug, Clone, PartialEq, Eq)]

View File

@ -51,8 +51,8 @@ impl Encodable for CallType {
}
impl Decodable for CallType {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
decoder.as_rlp().as_val().and_then(|v| Ok(match v {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
rlp.as_val().and_then(|v| Ok(match v {
0u32 => CallType::None,
1 => CallType::Call,
2 => CallType::CallCode,

View File

@ -47,12 +47,11 @@ impl Encodable for LogEntry {
}
impl Decodable for LogEntry {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let entry = LogEntry {
address: d.val_at(0)?,
topics: d.val_at(1)?,
data: d.val_at(2)?,
address: rlp.val_at(0)?,
topics: rlp.list_at(1)?,
data: rlp.val_at(2)?,
};
Ok(entry)
}

View File

@ -65,21 +65,20 @@ impl Encodable for Receipt {
}
impl Decodable for Receipt {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
if d.item_count() == 3 {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
if rlp.item_count()? == 3 {
Ok(Receipt {
state_root: None,
gas_used: d.val_at(0)?,
log_bloom: d.val_at(1)?,
logs: d.val_at(2)?,
gas_used: rlp.val_at(0)?,
log_bloom: rlp.val_at(1)?,
logs: rlp.list_at(2)?,
})
} else {
Ok(Receipt {
state_root: Some(d.val_at(0)?),
gas_used: d.val_at(1)?,
log_bloom: d.val_at(2)?,
logs: d.val_at(3)?,
state_root: Some(rlp.val_at(0)?),
gas_used: rlp.val_at(1)?,
log_bloom: rlp.val_at(2)?,
logs: rlp.list_at(3)?,
})
}
}

View File

@ -24,6 +24,8 @@ use util::Bytes;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct ManifestData {
/// Snapshot format version.
pub version: u64,
/// List of state chunk hashes.
pub state_hashes: Vec<H256>,
/// List of block chunk hashes.
@ -39,7 +41,8 @@ pub struct ManifestData {
impl ManifestData {
/// Encode the manifest data to rlp.
pub fn into_rlp(self) -> Bytes {
let mut stream = RlpStream::new_list(5);
let mut stream = RlpStream::new_list(6);
stream.append(&self.version);
stream.append_list(&self.state_hashes);
stream.append_list(&self.block_hashes);
stream.append(&self.state_root);
@ -52,14 +55,20 @@ impl ManifestData {
/// Try to restore manifest data from raw bytes, interpreted as RLP.
pub fn from_rlp(raw: &[u8]) -> Result<Self, DecoderError> {
let decoder = UntrustedRlp::new(raw);
let (start, version) = if decoder.item_count()? == 5 {
(0, 1)
} else {
(1, decoder.val_at(0)?)
};
let state_hashes: Vec<H256> = decoder.val_at(0)?;
let block_hashes: Vec<H256> = decoder.val_at(1)?;
let state_root: H256 = decoder.val_at(2)?;
let block_number: u64 = decoder.val_at(3)?;
let block_hash: H256 = decoder.val_at(4)?;
let state_hashes: Vec<H256> = decoder.list_at(start + 0)?;
let block_hashes: Vec<H256> = decoder.list_at(start + 1)?;
let state_root: H256 = decoder.val_at(start + 2)?;
let block_number: u64 = decoder.val_at(start + 3)?;
let block_hash: H256 = decoder.val_at(start + 4)?;
Ok(ManifestData {
version: version,
state_hashes: state_hashes,
block_hashes: block_hashes,
state_root: state_root,

View File

@ -17,7 +17,7 @@
//! Trace errors.
use std::fmt;
use rlp::{Encodable, RlpStream, Decodable, Decoder, DecoderError, View};
use rlp::{Encodable, RlpStream, Decodable, DecoderError, UntrustedRlp};
use evm::Error as EvmError;
/// Trace evm errors.
@ -96,9 +96,9 @@ impl Encodable for Error {
}
impl Decodable for Error {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
use self::Error::*;
let value: u8 = decoder.as_rlp().as_val()?;
let value: u8 = rlp.as_val()?;
match value {
0 => Ok(OutOfGas),
1 => Ok(BadJumpDestination),

View File

@ -64,9 +64,8 @@ impl Encodable for FlatTrace {
}
impl Decodable for FlatTrace {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let v: Vec<usize> = d.val_at(3)?;
fn decode(d: &UntrustedRlp) -> Result<Self, DecoderError> {
let v: Vec<usize> = d.list_at(3)?;
let res = FlatTrace {
action: d.val_at(0)?,
result: d.val_at(1)?,
@ -108,8 +107,8 @@ impl Encodable for FlatTransactionTraces {
}
impl Decodable for FlatTransactionTraces {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
Ok(FlatTransactionTraces(Decodable::decode(decoder)?))
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(FlatTransactionTraces(rlp.as_list()?))
}
}
@ -149,8 +148,8 @@ impl Encodable for FlatBlockTraces {
}
impl Decodable for FlatBlockTraces {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
Ok(FlatBlockTraces(Decodable::decode(decoder)?))
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(FlatBlockTraces(rlp.as_list()?))
}
}

View File

@ -45,11 +45,10 @@ impl Encodable for CallResult {
}
impl Decodable for CallResult {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = CallResult {
gas_used: d.val_at(0)?,
output: d.val_at(1)?,
gas_used: rlp.val_at(0)?,
output: rlp.val_at(1)?,
};
Ok(res)
@ -78,12 +77,11 @@ impl Encodable for CreateResult {
}
impl Decodable for CreateResult {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = CreateResult {
gas_used: d.val_at(0)?,
code: d.val_at(1)?,
address: d.val_at(2)?,
gas_used: rlp.val_at(0)?,
code: rlp.val_at(1)?,
address: rlp.val_at(2)?,
};
Ok(res)
@ -141,15 +139,14 @@ impl Encodable for Call {
}
impl Decodable for Call {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = Call {
from: d.val_at(0)?,
to: d.val_at(1)?,
value: d.val_at(2)?,
gas: d.val_at(3)?,
input: d.val_at(4)?,
call_type: d.val_at(5)?,
from: rlp.val_at(0)?,
to: rlp.val_at(1)?,
value: rlp.val_at(2)?,
gas: rlp.val_at(3)?,
input: rlp.val_at(4)?,
call_type: rlp.val_at(5)?,
};
Ok(res)
@ -201,13 +198,12 @@ impl Encodable for Create {
}
impl Decodable for Create {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = Create {
from: d.val_at(0)?,
value: d.val_at(1)?,
gas: d.val_at(2)?,
init: d.val_at(3)?,
from: rlp.val_at(0)?,
value: rlp.val_at(1)?,
gas: rlp.val_at(2)?,
init: rlp.val_at(3)?,
};
Ok(res)
@ -252,12 +248,11 @@ impl Encodable for Suicide {
}
impl Decodable for Suicide {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = Suicide {
address: d.val_at(0)?,
refund_address: d.val_at(1)?,
balance: d.val_at(2)?,
address: rlp.val_at(0)?,
refund_address: rlp.val_at(1)?,
balance: rlp.val_at(2)?,
};
Ok(res)
@ -298,13 +293,12 @@ impl Encodable for Action {
}
impl Decodable for Action {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let action_type: u8 = d.val_at(0)?;
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let action_type: u8 = rlp.val_at(0)?;
match action_type {
0 => d.val_at(1).map(Action::Call),
1 => d.val_at(1).map(Action::Create),
2 => d.val_at(1).map(Action::Suicide),
0 => rlp.val_at(1).map(Action::Call),
1 => rlp.val_at(1).map(Action::Create),
2 => rlp.val_at(1).map(Action::Suicide),
_ => Err(DecoderError::Custom("Invalid action type.")),
}
}
@ -369,14 +363,13 @@ impl Encodable for Res {
}
impl Decodable for Res {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
let action_type: u8 = d.val_at(0)?;
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let action_type: u8 = rlp.val_at(0)?;
match action_type {
0 => d.val_at(1).map(Res::Call),
1 => d.val_at(1).map(Res::Create),
2 => d.val_at(1).map(Res::FailedCall),
3 => d.val_at(1).map(Res::FailedCreate),
0 => rlp.val_at(1).map(Res::Call),
1 => rlp.val_at(1).map(Res::Create),
2 => rlp.val_at(1).map(Res::FailedCall),
3 => rlp.val_at(1).map(Res::FailedCreate),
4 => Ok(Res::None),
_ => Err(DecoderError::Custom("Invalid result type.")),
}
@ -420,11 +413,10 @@ impl Encodable for MemoryDiff {
}
impl Decodable for MemoryDiff {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(MemoryDiff {
offset: d.val_at(0)?,
data: d.val_at(1)?,
offset: rlp.val_at(0)?,
data: rlp.val_at(1)?,
})
}
}
@ -448,11 +440,10 @@ impl Encodable for StorageDiff {
}
impl Decodable for StorageDiff {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(StorageDiff {
location: d.val_at(0)?,
value: d.val_at(1)?,
location: rlp.val_at(0)?,
value: rlp.val_at(1)?,
})
}
}
@ -482,13 +473,12 @@ impl Encodable for VMExecutedOperation {
}
impl Decodable for VMExecutedOperation {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
Ok(VMExecutedOperation {
gas_used: d.val_at(0)?,
stack_push: d.val_at(1)?,
mem_diff: d.val_at(2)?,
store_diff: d.val_at(3)?,
gas_used: rlp.val_at(0)?,
stack_push: rlp.list_at(1)?,
mem_diff: rlp.val_at(2)?,
store_diff: rlp.val_at(3)?,
})
}
}
@ -518,13 +508,12 @@ impl Encodable for VMOperation {
}
impl Decodable for VMOperation {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = VMOperation {
pc: d.val_at(0)?,
instruction: d.val_at(1)?,
gas_cost: d.val_at(2)?,
executed: d.val_at(3)?,
pc: rlp.val_at(0)?,
instruction: rlp.val_at(1)?,
gas_cost: rlp.val_at(2)?,
executed: rlp.val_at(3)?,
};
Ok(res)
@ -557,13 +546,12 @@ impl Encodable for VMTrace {
}
impl Decodable for VMTrace {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
let res = VMTrace {
parent_step: d.val_at(0)?,
code: d.val_at(1)?,
operations: d.val_at(2)?,
subs: d.val_at(3)?,
parent_step: rlp.val_at(0)?,
code: rlp.val_at(1)?,
operations: rlp.list_at(2)?,
subs: rlp.list_at(3)?,
};
Ok(res)

View File

@ -42,8 +42,7 @@ impl Default for Action {
}
impl Decodable for Action {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let rlp = decoder.as_rlp();
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
if rlp.is_empty() {
Ok(Action::Create)
} else {
@ -243,12 +242,11 @@ impl Deref for UnverifiedTransaction {
}
impl Decodable for UnverifiedTransaction {
fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
let d = decoder.as_rlp();
if d.item_count() != 9 {
fn decode(d: &UntrustedRlp) -> Result<Self, DecoderError> {
if d.item_count()? != 9 {
return Err(DecoderError::RlpIncorrectListLen);
}
let hash = decoder.as_raw().sha3();
let hash = d.as_raw().sha3();
Ok(UnverifiedTransaction {
unsigned: Transaction {
nonce: d.val_at(0)?,

View File

@ -26,7 +26,7 @@ use engines::Engine;
use error::{BlockError, Error};
use blockchain::*;
use header::{BlockNumber, Header};
use rlp::{UntrustedRlp, View};
use rlp::UntrustedRlp;
use transaction::SignedTransaction;
use views::BlockView;
use time::get_time;
@ -101,7 +101,7 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &
verify_parent(&header, &parent)?;
engine.verify_block_family(&header, &parent, Some(bytes))?;
let num_uncles = UntrustedRlp::new(bytes).at(2)?.item_count();
let num_uncles = UntrustedRlp::new(bytes).at(2)?.item_count()?;
if num_uncles != 0 {
if num_uncles > engine.maximum_uncle_count() {
return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: None, max: Some(engine.maximum_uncle_count()), found: num_uncles })));
@ -264,7 +264,6 @@ mod tests {
use transaction::*;
use tests::helpers::*;
use types::log_entry::{LogEntry, LocalizedLogEntry};
use rlp::View;
use time::get_time;
use encoded;

View File

@ -20,7 +20,7 @@ use util::*;
use header::*;
use transaction::*;
use super::{TransactionView, HeaderView};
use rlp::{Rlp, View};
use rlp::Rlp;
/// View onto block rlp.
pub struct BlockView<'a> {
@ -69,7 +69,7 @@ impl<'a> BlockView<'a> {
/// Return List of transactions in given block.
pub fn transactions(&self) -> Vec<UnverifiedTransaction> {
self.rlp.val_at(1)
self.rlp.list_at(1)
}
/// Return List of transactions with additional localization info.
@ -125,7 +125,7 @@ impl<'a> BlockView<'a> {
/// Return list of uncles of given block.
pub fn uncles(&self) -> Vec<Header> {
self.rlp.val_at(2)
self.rlp.list_at(2)
}
/// Return number of uncles in given block, without deserializing them.

View File

@ -20,7 +20,7 @@ use util::*;
use header::*;
use transaction::*;
use super::{TransactionView, HeaderView};
use rlp::{Rlp, View};
use rlp::Rlp;
/// View onto block rlp.
pub struct BodyView<'a> {
@ -49,7 +49,7 @@ impl<'a> BodyView<'a> {
/// Return List of transactions in given block.
pub fn transactions(&self) -> Vec<UnverifiedTransaction> {
self.rlp.val_at(0)
self.rlp.list_at(0)
}
/// Return List of transactions with additional localization info.
@ -99,7 +99,7 @@ impl<'a> BodyView<'a> {
/// Return list of uncles of given block.
pub fn uncles(&self) -> Vec<Header> {
self.rlp.val_at(1)
self.rlp.list_at(1)
}
/// Return number of uncles in given block, without deserializing them.

View File

@ -17,7 +17,7 @@
//! View onto block header rlp
use util::{U256, Bytes, Hashable, H256, Address, H2048};
use rlp::{Rlp, View};
use rlp::Rlp;
use header::BlockNumber;
/// View onto block header rlp.

View File

@ -16,7 +16,7 @@
//! View onto transaction rlp
use util::{U256, Bytes, Hashable, H256};
use rlp::{Rlp, View};
use rlp::Rlp;
/// View onto transaction rlp.
pub struct TransactionView<'a> {

View File

@ -19,14 +19,22 @@ use {json, Error, crypto};
use account::Version;
use super::crypto::Crypto;
/// Account representation.
#[derive(Debug, PartialEq, Clone)]
pub struct SafeAccount {
/// Account ID
pub id: [u8; 16],
/// Account version
pub version: Version,
/// Account address
pub address: Address,
/// Account private key derivation definition.
pub crypto: Crypto,
/// Account filename
pub filename: Option<String>,
/// Account name
pub name: String,
/// Account metadata
pub meta: String,
}
@ -44,6 +52,7 @@ impl Into<json::KeyFile> for SafeAccount {
}
impl SafeAccount {
/// Create a new account
pub fn create(
keypair: &KeyPair,
id: [u8; 16],
@ -114,21 +123,25 @@ impl SafeAccount {
})
}
/// Sign a message.
pub fn sign(&self, password: &str, message: &Message) -> Result<Signature, Error> {
let secret = self.crypto.secret(password)?;
sign(&secret, message).map_err(From::from)
}
/// Decrypt a message.
pub fn decrypt(&self, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
let secret = self.crypto.secret(password)?;
crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from)
}
/// Derive public key.
pub fn public(&self, password: &str) -> Result<Public, Error> {
let secret = self.crypto.secret(password)?;
Ok(KeyPair::from_secret(secret)?.public().clone())
}
/// Change account's password.
pub fn change_password(&self, old_password: &str, new_password: &str, iterations: u32) -> Result<Self, Error> {
let secret = self.crypto.secret(old_password)?;
let result = SafeAccount {
@ -143,6 +156,7 @@ impl SafeAccount {
Ok(result)
}
/// Check if password matches the account.
pub fn check_password(&self, password: &str) -> bool {
self.crypto.secret(password).is_ok()
}

View File

@ -22,7 +22,7 @@ use std::{env, process, fs};
use std::io::Read;
use docopt::Docopt;
use ethstore::ethkey::Address;
use ethstore::dir::{KeyDirectory, ParityDirectory, RootDiskDirectory, GethDirectory, DirectoryType};
use ethstore::dir::{paths, KeyDirectory, RootDiskDirectory};
use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, Error, PresaleWallet,
SecretVaultRef, StoreAccountRef};
@ -49,14 +49,14 @@ Usage:
Options:
-h, --help Display this message and exit.
--dir DIR Specify the secret store directory. It may be either
parity, parity-test, geth, geth-test
parity, parity-(chain), geth, geth-test
or a path [default: parity].
--vault VAULT Specify vault to use in this operation.
--vault-pwd VAULTPWD Specify vault password to use in this operation. Please note
that this option is required when vault option is set.
Otherwise it is ignored.
--src DIR Specify import source. It may be either
parity, parity-test, get, geth-test
parity, parity-(chain), get, geth-test
or a path [default: geth].
Commands:
@ -116,10 +116,13 @@ fn main() {
fn key_dir(location: &str) -> Result<Box<KeyDirectory>, Error> {
let dir: Box<KeyDirectory> = match location {
"parity" => Box::new(ParityDirectory::create(DirectoryType::Main)?),
"parity-test" => Box::new(ParityDirectory::create(DirectoryType::Testnet)?),
"geth" => Box::new(GethDirectory::create(DirectoryType::Main)?),
"geth-test" => Box::new(GethDirectory::create(DirectoryType::Testnet)?),
"geth" => Box::new(RootDiskDirectory::create(paths::geth(false))?),
"geth-test" => Box::new(RootDiskDirectory::create(paths::geth(true))?),
path if path.starts_with("parity") => {
let chain = path.split('-').nth(1).unwrap_or("ethereum");
let path = paths::parity(chain);
Box::new(RootDiskDirectory::create(path)?)
},
path => Box::new(RootDiskDirectory::create(path)?),
};
@ -254,4 +257,3 @@ fn execute<S, I>(command: I) -> Result<String, Error> where I: IntoIterator<Item
Ok(format!("{}", USAGE))
}
}

View File

@ -1,102 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::env;
use std::path::PathBuf;
use {SafeAccount, Error};
use super::{KeyDirectory, RootDiskDirectory, DirectoryType};
#[cfg(target_os = "macos")]
fn geth_dir_path() -> PathBuf {
let mut home = env::home_dir().expect("Failed to get home dir");
home.push("Library");
home.push("Ethereum");
home
}
#[cfg(windows)]
/// Default path for ethereum installation on Windows
pub fn geth_dir_path() -> PathBuf {
let mut home = env::home_dir().expect("Failed to get home dir");
home.push("AppData");
home.push("Roaming");
home.push("Ethereum");
home
}
#[cfg(not(any(target_os = "macos", windows)))]
/// Default path for ethereum installation on posix system which is not Mac OS
pub fn geth_dir_path() -> PathBuf {
let mut home = env::home_dir().expect("Failed to get home dir");
home.push(".ethereum");
home
}
fn geth_keystore(t: DirectoryType) -> PathBuf {
let mut dir = geth_dir_path();
match t {
DirectoryType::Testnet => {
dir.push("testnet");
dir.push("keystore");
},
DirectoryType::Main => {
dir.push("keystore");
}
}
dir
}
pub struct GethDirectory {
dir: RootDiskDirectory,
}
impl GethDirectory {
pub fn create(t: DirectoryType) -> Result<Self, Error> {
let result = GethDirectory {
dir: RootDiskDirectory::create(geth_keystore(t))?,
};
Ok(result)
}
pub fn open(t: DirectoryType) -> Self {
GethDirectory {
dir: RootDiskDirectory::at(geth_keystore(t)),
}
}
}
impl KeyDirectory for GethDirectory {
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
self.dir.load()
}
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
self.dir.insert(account)
}
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
self.dir.update(account)
}
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
self.dir.remove(account)
}
fn unique_repr(&self) -> Result<u64, Error> {
self.dir.unique_repr()
}
}

View File

@ -22,6 +22,7 @@ use ethkey::Address;
use {SafeAccount, Error};
use super::KeyDirectory;
/// Accounts in-memory storage.
#[derive(Default)]
pub struct MemoryDirectory {
accounts: RwLock<HashMap<Address, Vec<SafeAccount>>>,

View File

@ -14,19 +14,15 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Accounts Directory
use std::path::{PathBuf};
use {SafeAccount, Error};
mod disk;
mod geth;
mod memory;
mod parity;
mod vault;
pub enum DirectoryType {
Testnet,
Main,
}
pub mod paths;
/// `VaultKeyDirectory::set_key` error
#[derive(Debug)]
@ -54,7 +50,7 @@ pub trait KeyDirectory: Send + Sync {
fn load(&self) -> Result<Vec<SafeAccount>, Error>;
/// Insert new key to directory
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error>;
//// Update key in directory
/// Update key in the directory
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error>;
/// Remove key from directory
fn remove(&self, account: &SafeAccount) -> Result<(), Error>;
@ -95,9 +91,7 @@ pub trait VaultKeyDirectory: KeyDirectory {
}
pub use self::disk::RootDiskDirectory;
pub use self::geth::GethDirectory;
pub use self::memory::MemoryDirectory;
pub use self::parity::ParityDirectory;
pub use self::vault::VaultDiskDirectory;
impl VaultKey {

View File

@ -1,81 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::env;
use std::path::PathBuf;
use {SafeAccount, Error};
use super::{KeyDirectory, RootDiskDirectory, DirectoryType};
fn parity_dir_path() -> PathBuf {
let mut home = env::home_dir().expect("Failed to get home dir");
home.push(".parity");
home
}
fn parity_keystore(t: DirectoryType) -> PathBuf {
let mut dir = parity_dir_path();
match t {
DirectoryType::Testnet => {
dir.push("testnet_keys");
},
DirectoryType::Main => {
dir.push("keys");
}
}
dir
}
pub struct ParityDirectory {
dir: RootDiskDirectory,
}
impl ParityDirectory {
pub fn create(t: DirectoryType) -> Result<Self, Error> {
let result = ParityDirectory {
dir: RootDiskDirectory::create(parity_keystore(t))?,
};
Ok(result)
}
pub fn open(t: DirectoryType) -> Self {
ParityDirectory {
dir: RootDiskDirectory::at(parity_keystore(t)),
}
}
}
impl KeyDirectory for ParityDirectory {
fn load(&self) -> Result<Vec<SafeAccount>, Error> {
self.dir.load()
}
fn insert(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
self.dir.insert(account)
}
fn update(&self, account: SafeAccount) -> Result<SafeAccount, Error> {
self.dir.update(account)
}
fn remove(&self, account: &SafeAccount) -> Result<(), Error> {
self.dir.remove(account)
}
fn unique_repr(&self) -> Result<u64, Error> {
self.dir.unique_repr()
}
}

96
ethstore/src/dir/paths.rs Normal file
View File

@ -0,0 +1,96 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Common tools paths.
use std::env;
use std::path::PathBuf;
fn home() -> PathBuf {
env::home_dir().expect("Failed to get home dir")
}
/// Geth path
pub fn geth(testnet: bool) -> PathBuf {
let mut base = geth_base();
if testnet {
base.push("testnet");
}
base.push("keystore");
base
}
/// Parity path for specific chain
pub fn parity(chain: &str) -> PathBuf {
let mut base = parity_base();
base.push(chain);
base
}
#[cfg(target_os = "macos")]
fn parity_base() -> PathBuf {
let mut home = home();
home.push("Library");
home.push("Application Support");
home.push("io.parity.ethereum");
home.push("keys");
home
}
#[cfg(windows)]
fn parity_base() -> PathBuf {
let mut home = home();
home.push("AppData");
home.push("Roaming");
home.push("Parity");
home.push("Ethereum");
home.push("keys");
home
}
#[cfg(not(any(target_os = "macos", windows)))]
fn parity_base() -> PathBuf {
let mut home = home();
home.push(".local");
home.push("share");
home.push("io.parity.ethereum");
home.push("keys");
home
}
#[cfg(target_os = "macos")]
fn geth_base() -> PathBuf {
let mut home = home();
home.push("Library");
home.push("Ethereum");
home
}
#[cfg(windows)]
fn geth_base() -> PathBuf {
let mut home = home();
home.push("AppData");
home.push("Roaming");
home.push("Ethereum");
home
}
#[cfg(not(any(target_os = "macos", windows)))]
fn geth_base() -> PathBuf {
let mut home = home();
home.push(".ethereum");
home
}

View File

@ -20,23 +20,40 @@ use ethkey::Error as EthKeyError;
use crypto::Error as EthCryptoError;
use ethkey::DerivationError;
/// Account-related errors.
#[derive(Debug)]
pub enum Error {
/// IO error
Io(IoError),
/// Invalid Password
InvalidPassword,
/// Account's secret is invalid.
InvalidSecret,
/// Invalid Vault Crypto meta.
InvalidCryptoMeta,
/// Invalid Account.
InvalidAccount,
/// Invalid Message.
InvalidMessage,
/// Invalid Key File
InvalidKeyFile(String),
/// Vaults are not supported.
VaultsAreNotSupported,
/// Unsupported vault
UnsupportedVault,
/// Invalid vault name
InvalidVaultName,
/// Vault not found
VaultNotFound,
/// Account creation failed.
CreationFailed,
/// `EthKey` error
EthKey(EthKeyError),
/// `EthCrypto` error
EthCrypto(EthCryptoError),
/// Derivation error
Derivation(DerivationError),
/// Custom error
Custom(String),
}

View File

@ -25,18 +25,21 @@ use ethkey::{self, Signature, Address, Message, Secret, Public, KeyPair, Extende
use dir::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError};
use account::SafeAccount;
use presale::PresaleWallet;
use json::{self, Uuid};
use json::{self, Uuid, OpaqueKeyFile};
use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation};
/// Accounts store.
pub struct EthStore {
store: EthMultiStore,
}
impl EthStore {
/// Open a new accounts store with given key directory backend.
pub fn open(directory: Box<KeyDirectory>) -> Result<Self, Error> {
Self::open_with_iterations(directory, KEY_ITERATIONS as u32)
}
/// Open a new account store with given key directory backend and custom number of iterations.
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
Ok(EthStore {
store: EthMultiStore::open_with_iterations(directory, iterations)?,
@ -44,7 +47,7 @@ impl EthStore {
}
fn get(&self, account: &StoreAccountRef) -> Result<SafeAccount, Error> {
let mut accounts = self.store.get(account)?.into_iter();
let mut accounts = self.store.get_accounts(account)?.into_iter();
accounts.next().ok_or(Error::InvalidAccount)
}
}
@ -76,6 +79,10 @@ impl SimpleSecretStore for EthStore {
self.store.change_password(account, old_password, new_password)
}
fn export_account(&self, account: &StoreAccountRef, password: &str) -> Result<OpaqueKeyFile, Error> {
self.store.export_account(account, password)
}
fn remove_account(&self, account: &StoreAccountRef, password: &str) -> Result<(), Error> {
self.store.remove_account(account, password)
}
@ -234,11 +241,12 @@ pub struct EthMultiStore {
}
impl EthMultiStore {
/// Open new multi-accounts store with given key directory backend.
pub fn open(directory: Box<KeyDirectory>) -> Result<Self, Error> {
Self::open_with_iterations(directory, KEY_ITERATIONS as u32)
}
/// Open new multi-accounts store with given key directory backend and custom number of iterations for new keys.
pub fn open_with_iterations(directory: Box<KeyDirectory>, iterations: u32) -> Result<Self, Error> {
let store = EthMultiStore {
dir: directory,
@ -287,7 +295,7 @@ impl EthMultiStore {
Ok(())
}
fn get(&self, account: &StoreAccountRef) -> Result<Vec<SafeAccount>, Error> {
fn get_accounts(&self, account: &StoreAccountRef) -> Result<Vec<SafeAccount>, Error> {
{
let cache = self.cache.read();
if let Some(accounts) = cache.get(account) {
@ -307,6 +315,15 @@ impl EthMultiStore {
}
}
fn get_matching(&self, account: &StoreAccountRef, password: &str) -> Result<Vec<SafeAccount>, Error> {
let accounts = self.get_accounts(account)?;
Ok(accounts.into_iter()
.filter(|acc| acc.check_password(password))
.collect()
)
}
fn import(&self, vault: SecretVaultRef, account: SafeAccount) -> Result<StoreAccountRef, Error> {
// save to file
let account = match vault {
@ -398,12 +415,8 @@ impl SimpleSecretStore for EthMultiStore {
fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &str, derivation: Derivation)
-> Result<StoreAccountRef, Error>
{
let accounts = self.get(account_ref)?;
let accounts = self.get_matching(account_ref, password)?;
for account in accounts {
// Skip if password is invalid
if !account.check_password(password) {
continue;
}
let extended = self.generate(account.crypto.secret(password)?, derivation)?;
return self.insert_account(vault, extended.secret().as_raw().clone(), password);
}
@ -413,14 +426,9 @@ impl SimpleSecretStore for EthMultiStore {
fn generate_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation)
-> Result<Address, Error>
{
let accounts = self.get(&account_ref)?;
let accounts = self.get_matching(&account_ref, password)?;
for account in accounts {
// Skip if password is invalid
if !account.check_password(password) {
continue;
}
let extended = self.generate(account.crypto.secret(password)?, derivation)?;
return Ok(ethkey::public_to_address(extended.public().public()));
}
Err(Error::InvalidPassword)
@ -429,18 +437,13 @@ impl SimpleSecretStore for EthMultiStore {
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation, message: &Message)
-> Result<Signature, Error>
{
let accounts = self.get(&account_ref)?;
let accounts = self.get_matching(&account_ref, password)?;
for account in accounts {
// Skip if password is invalid
if !account.check_password(password) {
continue;
}
let extended = self.generate(account.crypto.secret(password)?, derivation)?;
let secret = extended.secret().as_raw();
return Ok(ethkey::sign(&secret, message)?)
}
Err(Error::InvalidPassword)
}
fn account_ref(&self, address: &Address) -> Result<StoreAccountRef, Error> {
@ -457,48 +460,48 @@ impl SimpleSecretStore for EthMultiStore {
}
fn remove_account(&self, account_ref: &StoreAccountRef, password: &str) -> Result<(), Error> {
let accounts = self.get(account_ref)?;
let accounts = self.get_matching(account_ref, password)?;
for account in accounts {
// Skip if password is invalid
if !account.check_password(password) {
continue;
}
return self.remove_safe_account(account_ref, &account);
}
Err(Error::InvalidPassword)
}
fn change_password(&self, account_ref: &StoreAccountRef, old_password: &str, new_password: &str) -> Result<(), Error> {
let accounts = self.get(account_ref)?;
let accounts = self.get_matching(account_ref, old_password)?;
if accounts.is_empty() {
return Err(Error::InvalidPassword);
}
for account in accounts {
// Change password
let new_account = account.change_password(old_password, new_password, self.iterations)?;
self.update(account_ref, account, new_account)?;
}
Ok(())
}
fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result<Signature, Error> {
let accounts = self.get(account)?;
for account in accounts {
if account.check_password(password) {
return account.sign(password, message);
}
fn export_account(&self, account_ref: &StoreAccountRef, password: &str) -> Result<OpaqueKeyFile, Error> {
self.get_matching(account_ref, password)?.into_iter().nth(0).map(Into::into).ok_or(Error::InvalidPassword)
}
fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result<Signature, Error> {
let accounts = self.get_matching(account, password)?;
for account in accounts {
return account.sign(password, message);
}
Err(Error::InvalidPassword)
}
fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
let accounts = self.get(account)?;
let accounts = self.get_matching(account, password)?;
for account in accounts {
if account.check_password(password) {
return account.decrypt(password, shared_mac, message);
}
}
Err(Error::InvalidPassword)
}
@ -586,7 +589,7 @@ impl SimpleSecretStore for EthMultiStore {
return Ok(account_ref);
}
let account = self.get(&account_ref)?.into_iter().nth(0).ok_or(Error::InvalidAccount)?;
let account = self.get_accounts(&account_ref)?.into_iter().nth(0).ok_or(Error::InvalidAccount)?;
let new_account_ref = self.import(vault, account.clone())?;
self.remove_safe_account(&account_ref, &account)?;
self.reload_accounts()?;
@ -1032,4 +1035,18 @@ mod tests {
// then
assert_eq!(store.get_vault_meta(name).unwrap(), "OldMeta".to_owned());
}
#[test]
fn should_export_account() {
// given
let store = store();
let keypair = keypair();
let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "test").unwrap();
// when
let exported = store.export_account(&address, "test");
// then
assert!(exported.is_ok(), "Should export single account: {:?}", exported);
}
}

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