Merge branch 'master' into issues/4673
This commit is contained in:
commit
2d52c7b42f
122
Cargo.lock
generated
122
Cargo.lock
generated
@ -67,6 +67,29 @@ dependencies = [
|
|||||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace-sys"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base-x"
|
name = "base-x"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@ -159,6 +182,11 @@ dependencies = [
|
|||||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -225,6 +253,14 @@ dependencies = [
|
|||||||
"unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "conv"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -275,6 +311,11 @@ dependencies = [
|
|||||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "custom_derive"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "daemonize"
|
name = "daemonize"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@ -283,6 +324,15 @@ dependencies = [
|
|||||||
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dbghelp-sys"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deque"
|
name = "deque"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -335,6 +385,14 @@ dependencies = [
|
|||||||
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "error-chain"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eth-secp256k1"
|
name = "eth-secp256k1"
|
||||||
version = "0.5.6"
|
version = "0.5.6"
|
||||||
@ -406,7 +464,7 @@ dependencies = [
|
|||||||
"native-contracts 0.1.0",
|
"native-contracts 0.1.0",
|
||||||
"num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-wasm 0.12.1 (git+http://github.com/nikvolf/parity-wasm)",
|
"parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.2.0",
|
"rlp 0.2.0",
|
||||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1061,7 +1119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-core"
|
name = "jsonrpc-core"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#d12476f42ee672fa9d023f66fcfa5981d9aaba3a"
|
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures 0.1.11 (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.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1073,7 +1131,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-http-server"
|
name = "jsonrpc-http-server"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#d12476f42ee672fa9d023f66fcfa5981d9aaba3a"
|
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
|
"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-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||||
@ -1086,7 +1144,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-ipc-server"
|
name = "jsonrpc-ipc-server"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#d12476f42ee672fa9d023f66fcfa5981d9aaba3a"
|
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytes 0.4.4 (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-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||||
@ -1099,7 +1157,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-macros"
|
name = "jsonrpc-macros"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#d12476f42ee672fa9d023f66fcfa5981d9aaba3a"
|
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
"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)",
|
"jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||||
@ -1109,7 +1167,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-minihttp-server"
|
name = "jsonrpc-minihttp-server"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#d12476f42ee672fa9d023f66fcfa5981d9aaba3a"
|
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytes 0.4.4 (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-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||||
@ -1124,7 +1182,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-pubsub"
|
name = "jsonrpc-pubsub"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#d12476f42ee672fa9d023f66fcfa5981d9aaba3a"
|
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1134,8 +1192,9 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-server-utils"
|
name = "jsonrpc-server-utils"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#d12476f42ee672fa9d023f66fcfa5981d9aaba3a"
|
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"globset 0.1.4 (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-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1146,7 +1205,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-tcp-server"
|
name = "jsonrpc-tcp-server"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#d12476f42ee672fa9d023f66fcfa5981d9aaba3a"
|
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bytes 0.4.4 (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-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||||
@ -1160,11 +1219,13 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpc-ws-server"
|
name = "jsonrpc-ws-server"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#d12476f42ee672fa9d023f66fcfa5981d9aaba3a"
|
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#5e79be8a098cdda221713992f4a46b41a1d4d8f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
"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)",
|
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ws 0.7.1 (git+https://github.com/tomusdrw/ws-rs)",
|
"ws 0.7.1 (git+https://github.com/tomusdrw/ws-rs)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1464,6 +1525,19 @@ name = "nom"
|
|||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ntp"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"error-chain 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.1.32"
|
version = "0.1.32"
|
||||||
@ -1624,6 +1698,7 @@ dependencies = [
|
|||||||
"ethsync 1.7.0",
|
"ethsync 1.7.0",
|
||||||
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"futures 0.1.11 (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)",
|
||||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"isatty 0.1.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-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1666,12 +1741,14 @@ dependencies = [
|
|||||||
"ethcore-util 1.7.0",
|
"ethcore-util 1.7.0",
|
||||||
"fetch 0.1.0",
|
"fetch 0.1.0",
|
||||||
"futures 0.1.11 (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)",
|
||||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
"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-http-server 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)",
|
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mime 0.2.0 (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)",
|
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ntp 0.2.0 (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-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-hash-fetch 1.7.0",
|
"parity-hash-fetch 1.7.0",
|
||||||
"parity-reactor 0.1.0",
|
"parity-reactor 0.1.0",
|
||||||
@ -1775,6 +1852,7 @@ dependencies = [
|
|||||||
"ethsync 1.7.0",
|
"ethsync 1.7.0",
|
||||||
"fetch 0.1.0",
|
"fetch 0.1.0",
|
||||||
"futures 0.1.11 (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)",
|
||||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
"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-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-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||||
@ -1856,7 +1934,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-ui-precompiled"
|
name = "parity-ui-precompiled"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/paritytech/js-precompiled.git#fa572f52beb3a7b6f6473a5a5cf07518d899c4d9"
|
source = "git+https://github.com/paritytech/js-precompiled.git#b49a1d46cc6c545403d18579ef7ae9f9d14eea7e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -1883,7 +1961,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-wasm"
|
name = "parity-wasm"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
source = "git+http://github.com/nikvolf/parity-wasm#98311ec7333e0d3dc9e45c9df673cc79e41acb83"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -2235,6 +2313,11 @@ dependencies = [
|
|||||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hex"
|
name = "rustc-hex"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -2872,13 +2955,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-utils"
|
name = "wasm-utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/wasm-utils#357a5deed635938e79553227bfab976959ca3523"
|
source = "git+https://github.com/paritytech/wasm-utils#fee06b6d5826c2dc1fc1aa183b0c2c75e3e140c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-wasm 0.12.1 (git+http://github.com/nikvolf/parity-wasm)",
|
"parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2957,6 +3040,8 @@ dependencies = [
|
|||||||
"checksum arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d89f1b0e242270b5b797778af0c8d182a1a2ccac5d8d6fadf414223cc0fab096"
|
"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 aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
|
||||||
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
|
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
|
||||||
|
"checksum backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "346d7644f0b5f9bc73082d3b2236b69a05fd35cce0cfa3724e184e6a5c9e2a2f"
|
||||||
|
"checksum backtrace-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0d842ea781ce92be2bf78a9b38883948542749640b8378b3b2f03d1fd9f1ff"
|
||||||
"checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1"
|
"checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1"
|
||||||
"checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c"
|
"checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c"
|
||||||
"checksum bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0673c930652d3d4d6dcd5c45b5db4fa5f8f33994d7323618c43c083b223e8c"
|
"checksum bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0673c930652d3d4d6dcd5c45b5db4fa5f8f33994d7323618c43c083b223e8c"
|
||||||
@ -2971,6 +3056,7 @@ dependencies = [
|
|||||||
"checksum blastfig 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09640e0509d97d5cdff03a9f5daf087a8e04c735c3b113a75139634a19cfc7b2"
|
"checksum blastfig 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09640e0509d97d5cdff03a9f5daf087a8e04c735c3b113a75139634a19cfc7b2"
|
||||||
"checksum bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f421095d2a76fc24cd3fb3f912b90df06be7689912b1bdb423caefae59c258d"
|
"checksum bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f421095d2a76fc24cd3fb3f912b90df06be7689912b1bdb423caefae59c258d"
|
||||||
"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "<none>"
|
"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "<none>"
|
||||||
|
"checksum byteorder 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
|
||||||
"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"
|
"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"
|
||||||
"checksum bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8b24f16593f445422331a5eed46b72f7f171f910fead4f2ea8f17e727e9c5c14"
|
"checksum bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8b24f16593f445422331a5eed46b72f7f171f910fead4f2ea8f17e727e9c5c14"
|
||||||
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
||||||
@ -2978,13 +3064,16 @@ dependencies = [
|
|||||||
"checksum clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8f69e518f967224e628896b54e41ff6acfb4dcfefc5076325c36525dac900f"
|
"checksum clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8f69e518f967224e628896b54e41ff6acfb4dcfefc5076325c36525dac900f"
|
||||||
"checksum clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "5b4fabf979ddf6419a313c1c0ada4a5b95cfd2049c56e8418d622d27b4b6ff32"
|
"checksum clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "5b4fabf979ddf6419a313c1c0ada4a5b95cfd2049c56e8418d622d27b4b6ff32"
|
||||||
"checksum clippy_lints 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "ce96ec05bfe018a0d5d43da115e54850ea2217981ff0f2e462780ab9d594651a"
|
"checksum clippy_lints 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "ce96ec05bfe018a0d5d43da115e54850ea2217981ff0f2e462780ab9d594651a"
|
||||||
|
"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
|
||||||
"checksum cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d53b80dde876f47f03cda35303e368a79b91c70b0d65ecba5fd5280944a08591"
|
"checksum cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d53b80dde876f47f03cda35303e368a79b91c70b0d65ecba5fd5280944a08591"
|
||||||
"checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd"
|
"checksum core-foundation 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "20a6d0448d3a99d977ae4a2aa5a98d886a923e863e81ad9ff814645b6feb3bbd"
|
||||||
"checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5"
|
"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 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 crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec"
|
||||||
"checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "<none>"
|
"checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "<none>"
|
||||||
|
"checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
|
||||||
"checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf"
|
"checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf"
|
||||||
|
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
|
||||||
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
|
"checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
|
||||||
"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8"
|
"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8"
|
||||||
"checksum docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b5b93718f8b3e5544fcc914c43de828ca6c6ace23e0332c6080a2977b49787a"
|
"checksum docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b5b93718f8b3e5544fcc914c43de828ca6c6ace23e0332c6080a2977b49787a"
|
||||||
@ -2992,6 +3081,7 @@ dependencies = [
|
|||||||
"checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91"
|
"checksum either 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2b503c86dad62aaf414ecf2b8c527439abedb3f8d812537f0b12bfd6f32a91"
|
||||||
"checksum elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "258ff6a9a94f648d0379dbd79110e057edbb53eb85cc237e33eadf8e5a30df85"
|
"checksum elastic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "258ff6a9a94f648d0379dbd79110e057edbb53eb85cc237e33eadf8e5a30df85"
|
||||||
"checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83"
|
"checksum env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e3856f1697098606fc6cb97a93de88ca3f3bc35bb878c725920e6e82ecf05e83"
|
||||||
|
"checksum error-chain 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5c82c815138e278b8dcdeffc49f27ea6ffb528403e9dea4194f2e3dd40b143"
|
||||||
"checksum eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
|
"checksum eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)" = "<none>"
|
||||||
"checksum ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3d62319ee0f35abf20afe8859dd2668195912614346447bb2dee9fb8da7c62"
|
"checksum ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c3d62319ee0f35abf20afe8859dd2668195912614346447bb2dee9fb8da7c62"
|
||||||
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
|
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
|
||||||
@ -3062,6 +3152,7 @@ dependencies = [
|
|||||||
"checksum net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "bc01404e7568680f1259aa5729539f221cb1e6d047a0d9053cab4be8a73b5d67"
|
"checksum net2 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "bc01404e7568680f1259aa5729539f221cb1e6d047a0d9053cab4be8a73b5d67"
|
||||||
"checksum nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "52cd74cd09beba596430cc6e3091b74007169a56246e1262f0ba451ea95117b2"
|
"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 nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6caab12c5f97aa316cb249725aa32115118e1522b445e26c257dd77cad5ffd4e"
|
||||||
|
"checksum ntp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d23f30ae7da76e2c6c2f5de53f298aa9a3911d3955ab2c349eb944caedceb088"
|
||||||
"checksum num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "c04bd954dbf96f76bab6e5bd6cef6f1ce1262d15268ce4f926d2b5b778fa7af2"
|
"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"
|
"checksum num-bigint 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "41655c8d667be847a0b72fe0888857a7b3f052f691cf40852be5fcf87b274a65"
|
||||||
"checksum num-complex 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ccac67baf893ac97474f8d70eff7761dabb1f6c66e71f8f1c67a6859218db810"
|
"checksum num-complex 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ccac67baf893ac97474f8d70eff7761dabb1f6c66e71f8f1c67a6859218db810"
|
||||||
@ -3080,7 +3171,7 @@ dependencies = [
|
|||||||
"checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c"
|
"checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c"
|
||||||
"checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>"
|
"checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>"
|
||||||
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git)" = "<none>"
|
"checksum parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git)" = "<none>"
|
||||||
"checksum parity-wasm 0.12.1 (git+http://github.com/nikvolf/parity-wasm)" = "<none>"
|
"checksum parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "51104c8b8da5cd0ebe0ab765dfab37bc1927b4a01a3d870b0fe09d9ee65e35ea"
|
||||||
"checksum parity-wordlist 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52142d717754f7ff7ef0fc8da1bdce4f302dd576fb9bf8b727d6a5fdef33348d"
|
"checksum parity-wordlist 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52142d717754f7ff7ef0fc8da1bdce4f302dd576fb9bf8b727d6a5fdef33348d"
|
||||||
"checksum parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aebb68eebde2c99f89592d925288600fde220177e46b5c9a91ca218d245aeedf"
|
"checksum parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aebb68eebde2c99f89592d925288600fde220177e46b5c9a91ca218d245aeedf"
|
||||||
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
|
"checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068"
|
||||||
@ -3117,6 +3208,7 @@ dependencies = [
|
|||||||
"checksum rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5d3a99497c5c544e629cc8b359ae5ede321eba5fa8e5a8078f3ced727a976c3f"
|
"checksum rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5d3a99497c5c544e629cc8b359ae5ede321eba5fa8e5a8078f3ced727a976c3f"
|
||||||
"checksum rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab6e42be826e215f30ff830904f8f4a0933c6e2ae890e1af8b408f5bae60081e"
|
"checksum rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab6e42be826e215f30ff830904f8f4a0933c6e2ae890e1af8b408f5bae60081e"
|
||||||
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
|
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
|
||||||
|
"checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95"
|
||||||
"checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e"
|
"checksum rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e"
|
||||||
"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
|
"checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
|
||||||
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
||||||
|
@ -25,6 +25,7 @@ serde_json = "1.0"
|
|||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
app_dirs = "1.1.1"
|
app_dirs = "1.1.1"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
|
futures-cpupool = "0.1"
|
||||||
fdlimit = "0.1"
|
fdlimit = "0.1"
|
||||||
ws2_32-sys = "0.2"
|
ws2_32-sys = "0.2"
|
||||||
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
||||||
|
@ -81,14 +81,6 @@ Once you have rustup, install parity or download and build from source
|
|||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Quick build and install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo install --git https://github.com/paritytech/parity.git parity
|
|
||||||
```
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
## Install from the snap store
|
## Install from the snap store
|
||||||
|
|
||||||
In any of the [supported Linux distros](https://snapcraft.io/docs/core/install):
|
In any of the [supported Linux distros](https://snapcraft.io/docs/core/install):
|
||||||
|
@ -11,11 +11,13 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
|||||||
base32 = "0.3"
|
base32 = "0.3"
|
||||||
env_logger = "0.4"
|
env_logger = "0.4"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
|
futures-cpupool = "0.1"
|
||||||
linked-hash-map = "0.3"
|
linked-hash-map = "0.3"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
parity-dapps-glue = "1.7"
|
parity-dapps-glue = "1.7"
|
||||||
mime = "0.2"
|
mime = "0.2"
|
||||||
mime_guess = "1.6.1"
|
mime_guess = "1.6.1"
|
||||||
|
ntp = "0.2.0"
|
||||||
rand = "0.3"
|
rand = "0.3"
|
||||||
rustc-hex = "1.0"
|
rustc-hex = "1.0"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
|
@ -18,23 +18,36 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use hyper::{server, net, Decoder, Encoder, Next, Control};
|
use hyper::{server, net, Decoder, Encoder, Next, Control};
|
||||||
use hyper::method::Method;
|
use hyper::method::Method;
|
||||||
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use api::types::ApiError;
|
use api::{response, types};
|
||||||
use api::response;
|
use api::time::TimeChecker;
|
||||||
use apps::fetcher::Fetcher;
|
use apps::fetcher::Fetcher;
|
||||||
|
use handlers::{self, extract_url};
|
||||||
use handlers::extract_url;
|
|
||||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||||
|
use parity_reactor::Remote;
|
||||||
|
use {SyncStatus};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RestApi {
|
pub struct RestApi {
|
||||||
fetcher: Arc<Fetcher>,
|
fetcher: Arc<Fetcher>,
|
||||||
|
sync_status: Arc<SyncStatus>,
|
||||||
|
time: TimeChecker,
|
||||||
|
remote: Remote,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RestApi {
|
impl RestApi {
|
||||||
pub fn new(fetcher: Arc<Fetcher>) -> Box<Endpoint> {
|
pub fn new(
|
||||||
|
fetcher: Arc<Fetcher>,
|
||||||
|
sync_status: Arc<SyncStatus>,
|
||||||
|
time: TimeChecker,
|
||||||
|
remote: Remote,
|
||||||
|
) -> Box<Endpoint> {
|
||||||
Box::new(RestApi {
|
Box::new(RestApi {
|
||||||
fetcher: fetcher,
|
fetcher,
|
||||||
|
sync_status,
|
||||||
|
time,
|
||||||
|
remote,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,11 +71,11 @@ impl RestApiRouter {
|
|||||||
path: Some(path),
|
path: Some(path),
|
||||||
control: Some(control),
|
control: Some(control),
|
||||||
api: api,
|
api: api,
|
||||||
handler: response::as_json_error(&ApiError {
|
handler: Box::new(response::as_json_error(StatusCode::NotFound, &types::ApiError {
|
||||||
code: "404".into(),
|
code: "404".into(),
|
||||||
title: "Not Found".into(),
|
title: "Not Found".into(),
|
||||||
detail: "Resource you requested has not been found.".into(),
|
detail: "Resource you requested has not been found.".into(),
|
||||||
}),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,6 +88,78 @@ impl RestApiRouter {
|
|||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn health(&self, control: Control) -> Box<Handler> {
|
||||||
|
use self::types::{HealthInfo, HealthStatus, Health};
|
||||||
|
|
||||||
|
trace!(target: "dapps", "Checking node health.");
|
||||||
|
// Check timediff
|
||||||
|
let sync_status = self.api.sync_status.clone();
|
||||||
|
let map = move |time| {
|
||||||
|
// Check peers
|
||||||
|
let peers = {
|
||||||
|
let (connected, max) = sync_status.peers();
|
||||||
|
let (status, message) = match connected {
|
||||||
|
0 => {
|
||||||
|
(HealthStatus::Bad, "You are not connected to any peers. There is most likely some network issue. Fix connectivity.".into())
|
||||||
|
},
|
||||||
|
1 => (HealthStatus::NeedsAttention, "You are connected to only one peer. Your node might not be reliable. Check your network connection.".into()),
|
||||||
|
_ => (HealthStatus::Ok, "".into()),
|
||||||
|
};
|
||||||
|
HealthInfo { status, message, details: (connected, max) }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check sync
|
||||||
|
let sync = {
|
||||||
|
let is_syncing = sync_status.is_major_importing();
|
||||||
|
let (status, message) = if is_syncing {
|
||||||
|
(HealthStatus::NeedsAttention, "Your node is still syncing, the values you see might be outdated. Wait until it's fully synced.".into())
|
||||||
|
} else {
|
||||||
|
(HealthStatus::Ok, "".into())
|
||||||
|
};
|
||||||
|
HealthInfo { status, message, details: is_syncing }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check time
|
||||||
|
let time = {
|
||||||
|
const MAX_DRIFT: i64 = 500;
|
||||||
|
let (status, message, details) = match time {
|
||||||
|
Ok(Ok(diff)) if diff < MAX_DRIFT && diff > -MAX_DRIFT => {
|
||||||
|
(HealthStatus::Ok, "".into(), diff)
|
||||||
|
},
|
||||||
|
Ok(Ok(diff)) => {
|
||||||
|
(HealthStatus::Bad, format!(
|
||||||
|
"Your clock is not in sync. Detected difference is too big for the protocol to work: {}ms. Synchronize your clock.",
|
||||||
|
diff,
|
||||||
|
), diff)
|
||||||
|
},
|
||||||
|
Ok(Err(err)) => {
|
||||||
|
(HealthStatus::NeedsAttention, format!(
|
||||||
|
"Unable to reach time API: {}. Make sure that your clock is synchronized.",
|
||||||
|
err,
|
||||||
|
), 0)
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
(HealthStatus::NeedsAttention, "Time API request timed out. Make sure that the clock is synchronized.".into(), 0)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
HealthInfo { status, message, details, }
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = if [&peers.status, &sync.status, &time.status].iter().any(|x| *x != &HealthStatus::Ok) {
|
||||||
|
StatusCode::PreconditionFailed // HTTP 412
|
||||||
|
} else {
|
||||||
|
StatusCode::Ok // HTTP 200
|
||||||
|
};
|
||||||
|
|
||||||
|
response::as_json(status, &Health { peers, sync, time })
|
||||||
|
};
|
||||||
|
|
||||||
|
let time = self.api.time.time_drift();
|
||||||
|
let remote = self.api.remote.clone();
|
||||||
|
Box::new(handlers::AsyncHandler::new(time, map, remote, control))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl server::Handler<net::HttpStream> for RestApiRouter {
|
impl server::Handler<net::HttpStream> for RestApiRouter {
|
||||||
@ -103,6 +188,7 @@ impl server::Handler<net::HttpStream> for RestApiRouter {
|
|||||||
|
|
||||||
let handler = endpoint.and_then(|v| match v {
|
let handler = endpoint.and_then(|v| match v {
|
||||||
"ping" => Some(response::ping()),
|
"ping" => Some(response::ping()),
|
||||||
|
"health" => Some(self.health(control)),
|
||||||
"content" => self.resolve_content(hash, path, control),
|
"content" => self.resolve_content(hash, path, control),
|
||||||
_ => None
|
_ => None
|
||||||
});
|
});
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod response;
|
mod response;
|
||||||
|
mod time;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use self::api::RestApi;
|
pub use self::api::RestApi;
|
||||||
|
pub use self::time::TimeChecker;
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use endpoint::Handler;
|
use endpoint::Handler;
|
||||||
use handlers::{ContentHandler, EchoHandler};
|
use handlers::{ContentHandler, EchoHandler};
|
||||||
|
|
||||||
@ -23,10 +25,16 @@ pub fn empty() -> Box<Handler> {
|
|||||||
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
|
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_json_error<T: Serialize>(val: &T) -> Box<Handler> {
|
pub fn as_json<T: Serialize>(status: StatusCode, val: &T) -> ContentHandler {
|
||||||
let json = serde_json::to_string(val)
|
let json = serde_json::to_string(val)
|
||||||
.expect("serialization to string is infallible; qed");
|
.expect("serialization to string is infallible; qed");
|
||||||
Box::new(ContentHandler::not_found(json, mime!(Application/Json)))
|
ContentHandler::new(status, json, mime!(Application/Json))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_json_error<T: Serialize>(status: StatusCode, val: &T) -> ContentHandler {
|
||||||
|
let json = serde_json::to_string(val)
|
||||||
|
.expect("serialization to string is infallible; qed");
|
||||||
|
ContentHandler::new(status, json, mime!(Application/Json))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ping() -> Box<Handler> {
|
pub fn ping() -> Box<Handler> {
|
||||||
|
264
dapps/src/api/time.rs
Normal file
264
dapps/src/api/time.rs
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Periodically checks node's time drift using [SNTP](https://tools.ietf.org/html/rfc1769).
|
||||||
|
//!
|
||||||
|
//! An NTP packet is sent to the server with a local timestamp, the server then completes the packet, yielding the
|
||||||
|
//! following timestamps:
|
||||||
|
//!
|
||||||
|
//! Timestamp Name ID When Generated
|
||||||
|
//! ------------------------------------------------------------
|
||||||
|
//! Originate Timestamp T1 time request sent by client
|
||||||
|
//! Receive Timestamp T2 time request received at server
|
||||||
|
//! Transmit Timestamp T3 time reply sent by server
|
||||||
|
//! Destination Timestamp T4 time reply received at client
|
||||||
|
//!
|
||||||
|
//! The drift is defined as:
|
||||||
|
//!
|
||||||
|
//! drift = ((T2 - T1) + (T3 - T4)) / 2.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use std::{fmt, mem, time};
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use futures::{self, Future, BoxFuture};
|
||||||
|
use futures_cpupool::CpuPool;
|
||||||
|
use ntp;
|
||||||
|
use time::{Duration, Timespec};
|
||||||
|
use util::{Arc, RwLock};
|
||||||
|
|
||||||
|
/// Time checker error.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Error {
|
||||||
|
/// There was an error when trying to reach the NTP server.
|
||||||
|
Ntp(String),
|
||||||
|
/// IO error when reading NTP response.
|
||||||
|
Io(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use self::Error::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Ntp(ref err) => write!(fmt, "NTP error: {}", err),
|
||||||
|
Io(ref err) => write!(fmt, "Connection Error: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(err: io::Error) -> Self { Error::Io(format!("{}", err)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ntp::errors::Error> for Error {
|
||||||
|
fn from(err: ntp::errors::Error) -> Self { Error::Ntp(format!("{}", err)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NTP time drift checker.
|
||||||
|
pub trait Ntp {
|
||||||
|
/// Returns the current time drift.
|
||||||
|
fn drift(&self) -> BoxFuture<Duration, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NTP client using the SNTP algorithm for calculating drift.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SimpleNtp {
|
||||||
|
address: Arc<String>,
|
||||||
|
pool: CpuPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for SimpleNtp {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Ntp {{ address: {} }}", self.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleNtp {
|
||||||
|
fn new(address: &str, pool: CpuPool) -> SimpleNtp {
|
||||||
|
SimpleNtp {
|
||||||
|
address: Arc::new(address.to_owned()),
|
||||||
|
pool: pool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ntp for SimpleNtp {
|
||||||
|
fn drift(&self) -> BoxFuture<Duration, Error> {
|
||||||
|
let address = self.address.clone();
|
||||||
|
self.pool.spawn_fn(move || {
|
||||||
|
let packet = ntp::request(&*address)?;
|
||||||
|
let dest_time = ::time::now_utc().to_timespec();
|
||||||
|
let orig_time = Timespec::from(packet.orig_time);
|
||||||
|
let recv_time = Timespec::from(packet.recv_time);
|
||||||
|
let transmit_time = Timespec::from(packet.transmit_time);
|
||||||
|
|
||||||
|
let drift = ((recv_time - orig_time) + (transmit_time - dest_time)) / 2;
|
||||||
|
|
||||||
|
Ok(drift)
|
||||||
|
}).boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_RESULTS: usize = 4;
|
||||||
|
const UPDATE_TIMEOUT_OK_SECS: u64 = 30;
|
||||||
|
const UPDATE_TIMEOUT_ERR_SECS: u64 = 2;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
/// A time checker.
|
||||||
|
pub struct TimeChecker<N: Ntp = SimpleNtp> {
|
||||||
|
ntp: N,
|
||||||
|
last_result: Arc<RwLock<(time::Instant, VecDeque<Result<i64, Error>>)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimeChecker<SimpleNtp> {
|
||||||
|
/// Creates new time checker given the NTP server address.
|
||||||
|
pub fn new(ntp_address: String, pool: CpuPool) -> Self {
|
||||||
|
let last_result = Arc::new(RwLock::new(
|
||||||
|
// Assume everything is ok at the very beginning.
|
||||||
|
(time::Instant::now(), vec![Ok(0)].into())
|
||||||
|
));
|
||||||
|
|
||||||
|
let ntp = SimpleNtp::new(&ntp_address, pool);
|
||||||
|
|
||||||
|
TimeChecker {
|
||||||
|
ntp,
|
||||||
|
last_result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: Ntp> TimeChecker<N> {
|
||||||
|
/// Updates the time
|
||||||
|
pub fn update(&self) -> BoxFuture<i64, Error> {
|
||||||
|
let last_result = self.last_result.clone();
|
||||||
|
self.ntp.drift().then(move |res| {
|
||||||
|
let mut results = mem::replace(&mut last_result.write().1, VecDeque::new());
|
||||||
|
let valid_till = time::Instant::now() + time::Duration::from_secs(
|
||||||
|
if res.is_ok() && results.len() == MAX_RESULTS {
|
||||||
|
UPDATE_TIMEOUT_OK_SECS
|
||||||
|
} else {
|
||||||
|
UPDATE_TIMEOUT_ERR_SECS
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Push the result.
|
||||||
|
results.push_back(res.map(|d| d.num_milliseconds()));
|
||||||
|
while results.len() > MAX_RESULTS {
|
||||||
|
results.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select a response and update last result.
|
||||||
|
let res = select_result(results.iter());
|
||||||
|
*last_result.write() = (valid_till, results);
|
||||||
|
res
|
||||||
|
}).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a current time drift or error if last request to NTP server failed.
|
||||||
|
pub fn time_drift(&self) -> BoxFuture<i64, Error> {
|
||||||
|
// return cached result
|
||||||
|
{
|
||||||
|
let res = self.last_result.read();
|
||||||
|
if res.0 > time::Instant::now() {
|
||||||
|
return futures::done(select_result(res.1.iter())).boxed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// or update and return result
|
||||||
|
self.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_result<'a, T: Iterator<Item=&'a Result<i64, Error>>>(results: T) -> Result<i64, Error> {
|
||||||
|
let mut min = None;
|
||||||
|
for res in results {
|
||||||
|
min = Some(match (min.take(), res) {
|
||||||
|
(Some(Ok(min)), &Ok(ref new)) => Ok(::std::cmp::min(min, *new)),
|
||||||
|
(Some(Ok(old)), &Err(_)) => Ok(old),
|
||||||
|
(_, ref new) => (*new).clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
min.unwrap_or_else(|| Err(Error::Ntp("NTP server unavailable.".into())))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::time::Instant;
|
||||||
|
use time::Duration;
|
||||||
|
use futures::{self, BoxFuture, Future};
|
||||||
|
use super::{Ntp, TimeChecker, Error};
|
||||||
|
use util::RwLock;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct FakeNtp(RefCell<Vec<Duration>>, Cell<u64>);
|
||||||
|
impl FakeNtp {
|
||||||
|
fn new() -> FakeNtp {
|
||||||
|
FakeNtp(
|
||||||
|
RefCell::new(vec![Duration::milliseconds(150)]),
|
||||||
|
Cell::new(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ntp for FakeNtp {
|
||||||
|
fn drift(&self) -> BoxFuture<Duration, Error> {
|
||||||
|
self.1.set(self.1.get() + 1);
|
||||||
|
futures::future::ok(self.0.borrow_mut().pop().expect("Unexpected call to drift().")).boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn time_checker() -> TimeChecker<FakeNtp> {
|
||||||
|
let last_result = Arc::new(RwLock::new(
|
||||||
|
(Instant::now(), vec![Err(Error::Ntp("NTP server unavailable.".into()))].into())
|
||||||
|
));
|
||||||
|
|
||||||
|
TimeChecker {
|
||||||
|
ntp: FakeNtp::new(),
|
||||||
|
last_result: last_result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_fetch_time_on_start() {
|
||||||
|
// given
|
||||||
|
let time = time_checker();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let diff = time.time_drift().wait().unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(diff, 150);
|
||||||
|
assert_eq!(time.ntp.1.get(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_fetch_twice_if_timeout_has_not_passed() {
|
||||||
|
// given
|
||||||
|
let time = time_checker();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let diff1 = time.time_drift().wait().unwrap();
|
||||||
|
let diff2 = time.time_drift().wait().unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(diff1, 150);
|
||||||
|
assert_eq!(diff2, 150);
|
||||||
|
assert_eq!(time.ntp.1.get(), 1);
|
||||||
|
}
|
||||||
|
}
|
@ -14,11 +14,54 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/// A structure representing any error in REST API.
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct ApiError {
|
pub struct ApiError {
|
||||||
|
/// Error code.
|
||||||
pub code: String,
|
pub code: String,
|
||||||
|
/// Human-readable error summary.
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
/// More technical error details.
|
||||||
pub detail: String,
|
pub detail: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Health API endpoint status.
|
||||||
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
|
pub enum HealthStatus {
|
||||||
|
/// Everything's OK.
|
||||||
|
#[serde(rename = "ok")]
|
||||||
|
Ok,
|
||||||
|
/// Node health need attention
|
||||||
|
/// (the issue is not critical, but may need investigation)
|
||||||
|
#[serde(rename = "needsAttention")]
|
||||||
|
NeedsAttention,
|
||||||
|
/// There is something bad detected with the node.
|
||||||
|
#[serde(rename = "bad")]
|
||||||
|
Bad
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a single check in node health.
|
||||||
|
/// Cointains the status of that check and apropriate message and details.
|
||||||
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct HealthInfo<T> {
|
||||||
|
/// Check status.
|
||||||
|
pub status: HealthStatus,
|
||||||
|
/// Human-readable message.
|
||||||
|
pub message: String,
|
||||||
|
/// Technical details of the check.
|
||||||
|
pub details: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Node Health status.
|
||||||
|
#[derive(Debug, PartialEq, Serialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Health {
|
||||||
|
/// Status of peers.
|
||||||
|
pub peers: HealthInfo<(usize, usize)>,
|
||||||
|
/// Sync status.
|
||||||
|
pub sync: HealthInfo<bool>,
|
||||||
|
/// Time diff info.
|
||||||
|
pub time: HealthInfo<i64>,
|
||||||
|
}
|
||||||
|
@ -25,6 +25,7 @@ use util::sha3::sha3;
|
|||||||
use page::{LocalPageEndpoint, PageCache};
|
use page::{LocalPageEndpoint, PageCache};
|
||||||
use handlers::{ContentValidator, ValidatorResponse};
|
use handlers::{ContentValidator, ValidatorResponse};
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
|
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
|
||||||
|
|
||||||
@ -116,16 +117,16 @@ pub struct Dapp {
|
|||||||
id: String,
|
id: String,
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
on_done: OnDone,
|
on_done: OnDone,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dapp {
|
impl Dapp {
|
||||||
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Embeddable) -> Self {
|
||||||
Dapp {
|
Dapp {
|
||||||
id: id,
|
id,
|
||||||
dapps_path: dapps_path,
|
dapps_path,
|
||||||
on_done: on_done,
|
on_done,
|
||||||
embeddable_on: embeddable_on,
|
embeddable_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ use parity_reactor::Remote;
|
|||||||
use hyper;
|
use hyper;
|
||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use {SyncStatus, random_filename};
|
use {Embeddable, SyncStatus, random_filename};
|
||||||
use util::Mutex;
|
use util::Mutex;
|
||||||
use page::LocalPageEndpoint;
|
use page::LocalPageEndpoint;
|
||||||
use handlers::{ContentHandler, ContentFetcherHandler};
|
use handlers::{ContentHandler, ContentFetcherHandler};
|
||||||
@ -52,7 +52,7 @@ pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHint
|
|||||||
resolver: R,
|
resolver: R,
|
||||||
cache: Arc<Mutex<ContentCache>>,
|
cache: Arc<Mutex<ContentCache>>,
|
||||||
sync: Arc<SyncStatus>,
|
sync: Arc<SyncStatus>,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
only_content: bool,
|
only_content: bool,
|
||||||
@ -93,22 +93,22 @@ impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn embeddable_on(mut self, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn embeddable_on(mut self, embeddable_on: Embeddable) -> Self {
|
||||||
self.embeddable_on = embeddable_on;
|
self.embeddable_on = embeddable_on;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn still_syncing(address: Option<(String, u16)>) -> Box<Handler> {
|
fn still_syncing(embeddable: Embeddable) -> Box<Handler> {
|
||||||
Box::new(ContentHandler::error(
|
Box::new(ContentHandler::error(
|
||||||
StatusCode::ServiceUnavailable,
|
StatusCode::ServiceUnavailable,
|
||||||
"Sync In Progress",
|
"Sync In Progress",
|
||||||
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
||||||
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
|
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
|
||||||
address,
|
embeddable,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_disabled(address: Option<(String, u16)>) -> Box<Handler> {
|
fn dapps_disabled(address: Embeddable) -> Box<Handler> {
|
||||||
Box::new(ContentHandler::error(
|
Box::new(ContentHandler::error(
|
||||||
StatusCode::ServiceUnavailable,
|
StatusCode::ServiceUnavailable,
|
||||||
"Network Dapps Not Available",
|
"Network Dapps Not Available",
|
||||||
@ -271,6 +271,7 @@ mod tests {
|
|||||||
use endpoint::EndpointInfo;
|
use endpoint::EndpointInfo;
|
||||||
use page::LocalPageEndpoint;
|
use page::LocalPageEndpoint;
|
||||||
use super::{ContentFetcher, Fetcher};
|
use super::{ContentFetcher, Fetcher};
|
||||||
|
use {SyncStatus};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct FakeResolver;
|
struct FakeResolver;
|
||||||
@ -280,11 +281,17 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FakeSync(bool);
|
||||||
|
impl SyncStatus for FakeSync {
|
||||||
|
fn is_major_importing(&self) -> bool { self.0 }
|
||||||
|
fn peers(&self) -> (usize, usize) { (0, 5) }
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_true_if_contains_the_app() {
|
fn should_true_if_contains_the_app() {
|
||||||
// given
|
// given
|
||||||
let path = env::temp_dir();
|
let path = env::temp_dir();
|
||||||
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(|| false), Remote::new_sync(), Client::new().unwrap())
|
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(FakeSync(false)), Remote::new_sync(), Client::new().unwrap())
|
||||||
.allow_dapps(true);
|
.allow_dapps(true);
|
||||||
let handler = LocalPageEndpoint::new(path, EndpointInfo {
|
let handler = LocalPageEndpoint::new(path, EndpointInfo {
|
||||||
name: "fake".into(),
|
name: "fake".into(),
|
||||||
|
@ -22,6 +22,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use page::{LocalPageEndpoint, PageCache};
|
use page::{LocalPageEndpoint, PageCache};
|
||||||
use endpoint::{Endpoint, EndpointInfo};
|
use endpoint::{Endpoint, EndpointInfo};
|
||||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
struct LocalDapp {
|
struct LocalDapp {
|
||||||
id: String,
|
id: String,
|
||||||
@ -60,14 +61,14 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
|||||||
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
|
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
|
||||||
/// Parses the path to extract last component (for name).
|
/// Parses the path to extract last component (for name).
|
||||||
/// `None` is returned when path is invalid or non-existent.
|
/// `None` is returned when path is invalid or non-existent.
|
||||||
pub fn local_endpoint<P: AsRef<Path>>(path: P, signer_address: Option<(String, u16)>) -> Option<(String, Box<LocalPageEndpoint>)> {
|
pub fn local_endpoint<P: AsRef<Path>>(path: P, embeddable: Embeddable) -> Option<(String, Box<LocalPageEndpoint>)> {
|
||||||
let path = path.as_ref().to_owned();
|
let path = path.as_ref().to_owned();
|
||||||
path.canonicalize().ok().and_then(|path| {
|
path.canonicalize().ok().and_then(|path| {
|
||||||
let name = path.file_name().and_then(|name| name.to_str());
|
let name = path.file_name().and_then(|name| name.to_str());
|
||||||
name.map(|name| {
|
name.map(|name| {
|
||||||
let dapp = local_dapp(name.into(), path.clone());
|
let dapp = local_dapp(name.into(), path.clone());
|
||||||
(dapp.id, Box::new(LocalPageEndpoint::new(
|
(dapp.id, Box::new(LocalPageEndpoint::new(
|
||||||
dapp.path, dapp.info, PageCache::Disabled, signer_address.clone())
|
dapp.path, dapp.info, PageCache::Disabled, embeddable.clone())
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -86,12 +87,12 @@ fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
|
|||||||
|
|
||||||
/// Returns endpoints for Local Dapps found for given filesystem path.
|
/// Returns endpoints for Local Dapps found for given filesystem path.
|
||||||
/// Scans the directory and collects `LocalPageEndpoints`.
|
/// Scans the directory and collects `LocalPageEndpoints`.
|
||||||
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, signer_address: Option<(String, u16)>) -> BTreeMap<String, Box<Endpoint>> {
|
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, embeddable: Embeddable) -> BTreeMap<String, Box<Endpoint>> {
|
||||||
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
|
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
|
||||||
for dapp in local_dapps(dapps_path.as_ref()) {
|
for dapp in local_dapps(dapps_path.as_ref()) {
|
||||||
pages.insert(
|
pages.insert(
|
||||||
dapp.id,
|
dapp.id,
|
||||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, signer_address.clone()))
|
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, embeddable.clone()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pages
|
pages
|
||||||
|
@ -26,7 +26,7 @@ use fetch::Fetch;
|
|||||||
use parity_dapps::WebApp;
|
use parity_dapps::WebApp;
|
||||||
use parity_reactor::Remote;
|
use parity_reactor::Remote;
|
||||||
use parity_ui;
|
use parity_ui;
|
||||||
use {WebProxyTokens};
|
use {WebProxyTokens, ParentFrameSettings};
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod cache;
|
mod cache;
|
||||||
@ -52,23 +52,23 @@ pub fn ui() -> Box<Endpoint> {
|
|||||||
Box::new(PageEndpoint::with_fallback_to_index(parity_ui::App::default()))
|
Box::new(PageEndpoint::with_fallback_to_index(parity_ui::App::default()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui_redirection(ui_address: Option<(String, u16)>) -> Box<Endpoint> {
|
pub fn ui_redirection(embeddable: Option<ParentFrameSettings>) -> Box<Endpoint> {
|
||||||
Box::new(ui::Redirection::new(ui_address))
|
Box::new(ui::Redirection::new(embeddable))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_endpoints<F: Fetch>(
|
pub fn all_endpoints<F: Fetch>(
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
extra_dapps: Vec<PathBuf>,
|
extra_dapps: Vec<PathBuf>,
|
||||||
dapps_domain: String,
|
dapps_domain: &str,
|
||||||
ui_address: Option<(String, u16)>,
|
embeddable: Option<ParentFrameSettings>,
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
) -> Endpoints {
|
) -> Endpoints {
|
||||||
// fetch fs dapps at first to avoid overwriting builtins
|
// fetch fs dapps at first to avoid overwriting builtins
|
||||||
let mut pages = fs::local_endpoints(dapps_path, ui_address.clone());
|
let mut pages = fs::local_endpoints(dapps_path, embeddable.clone());
|
||||||
for path in extra_dapps {
|
for path in extra_dapps {
|
||||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), ui_address.clone()) {
|
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), embeddable.clone()) {
|
||||||
pages.insert(id, endpoint);
|
pages.insert(id, endpoint);
|
||||||
} else {
|
} else {
|
||||||
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
||||||
@ -76,9 +76,9 @@ pub fn all_endpoints<F: Fetch>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(ui_address.clone()));
|
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(embeddable.clone()));
|
||||||
pages.insert("proxy".into(), ProxyPac::boxed(ui_address.clone(), dapps_domain));
|
pages.insert("proxy".into(), ProxyPac::boxed(embeddable.clone(), dapps_domain.to_owned()));
|
||||||
pages.insert(WEB_PATH.into(), Web::boxed(ui_address.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
|
pages.insert(WEB_PATH.into(), Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
|
||||||
|
|
||||||
Arc::new(pages)
|
Arc::new(pages)
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ fn insert<T : WebApp + Default + 'static>(pages: &mut BTreeMap<String, Box<Endpo
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum Embeddable {
|
enum Embeddable {
|
||||||
Yes(Option<(String, u16)>),
|
Yes(Option<ParentFrameSettings>),
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
@ -19,28 +19,28 @@
|
|||||||
use hyper::{Control, StatusCode};
|
use hyper::{Control, StatusCode};
|
||||||
|
|
||||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||||
use {address, handlers};
|
use {handlers, Embeddable};
|
||||||
|
|
||||||
/// Redirection to UI server.
|
/// Redirection to UI server.
|
||||||
pub struct Redirection {
|
pub struct Redirection {
|
||||||
signer_address: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Redirection {
|
impl Redirection {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
signer_address: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Redirection {
|
Redirection {
|
||||||
signer_address: signer_address,
|
embeddable_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint for Redirection {
|
impl Endpoint for Redirection {
|
||||||
fn to_async_handler(&self, _path: EndpointPath, _control: Control) -> Box<Handler> {
|
fn to_async_handler(&self, _path: EndpointPath, _control: Control) -> Box<Handler> {
|
||||||
if let Some(ref signer_address) = self.signer_address {
|
if let Some(ref frame) = self.embeddable_on {
|
||||||
trace!(target: "dapps", "Redirecting to signer interface.");
|
trace!(target: "dapps", "Redirecting to signer interface.");
|
||||||
handlers::Redirection::boxed(&format!("http://{}", address(signer_address)))
|
handlers::Redirection::boxed(&format!("http://{}:{}", &frame.host, frame.port))
|
||||||
} else {
|
} else {
|
||||||
trace!(target: "dapps", "Signer disabled, returning 404.");
|
trace!(target: "dapps", "Signer disabled, returning 404.");
|
||||||
Box::new(handlers::ContentHandler::error(
|
Box::new(handlers::ContentHandler::error(
|
||||||
@ -48,7 +48,7 @@ impl Endpoint for Redirection {
|
|||||||
"404 Not Found",
|
"404 Not Found",
|
||||||
"Your homepage is not available when Trusted Signer is disabled.",
|
"Your homepage is not available when Trusted Signer is disabled.",
|
||||||
Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
|
Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
|
||||||
self.signer_address.clone(),
|
None,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
112
dapps/src/handlers/async.rs
Normal file
112
dapps/src/handlers/async.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
//! Async Content Handler
|
||||||
|
//! Temporary solution until we switch to future-based server.
|
||||||
|
//! Wraps a future and converts it to hyper::server::Handler;
|
||||||
|
|
||||||
|
use std::{mem, time};
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use futures::Future;
|
||||||
|
use hyper::{server, Decoder, Encoder, Next, Control};
|
||||||
|
use hyper::net::HttpStream;
|
||||||
|
|
||||||
|
use handlers::ContentHandler;
|
||||||
|
use parity_reactor::Remote;
|
||||||
|
|
||||||
|
const TIMEOUT_SECS: u64 = 15;
|
||||||
|
|
||||||
|
enum State<F, T, M> {
|
||||||
|
Initial(F, M, Remote, Control),
|
||||||
|
Waiting(mpsc::Receiver<Result<T, ()>>, M),
|
||||||
|
Done(ContentHandler),
|
||||||
|
Invalid,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AsyncHandler<F, T, M> {
|
||||||
|
state: State<F, T, M>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, T, M> AsyncHandler<F, T, M> {
|
||||||
|
pub fn new(future: F, map: M, remote: Remote, control: Control) -> Self {
|
||||||
|
AsyncHandler {
|
||||||
|
state: State::Initial(future, map, remote, control),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, T, E, M> server::Handler<HttpStream> for AsyncHandler<F, Result<T, E>, M> where
|
||||||
|
F: Future<Item=T, Error=E> + Send + 'static,
|
||||||
|
M: FnOnce(Result<Result<T, E>, ()>) -> ContentHandler,
|
||||||
|
T: Send + 'static,
|
||||||
|
E: Send + 'static,
|
||||||
|
{
|
||||||
|
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||||
|
if let State::Initial(future, map, remote, control) = mem::replace(&mut self.state, State::Invalid) {
|
||||||
|
let (tx, rx) = mpsc::sync_channel(1);
|
||||||
|
let control2 = control.clone();
|
||||||
|
let tx2 = tx.clone();
|
||||||
|
remote.spawn_with_timeout(move || future.then(move |result| {
|
||||||
|
// Send a result (ignore errors if the connection was dropped)
|
||||||
|
let _ = tx.send(Ok(result));
|
||||||
|
// Resume handler
|
||||||
|
let _ = control.ready(Next::read());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}), time::Duration::from_secs(TIMEOUT_SECS), move || {
|
||||||
|
// Notify about error
|
||||||
|
let _ = tx2.send(Err(()));
|
||||||
|
// Resume handler
|
||||||
|
let _ = control2.ready(Next::read());
|
||||||
|
});
|
||||||
|
|
||||||
|
self.state = State::Waiting(rx, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
Next::wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
|
if let State::Waiting(rx, map) = mem::replace(&mut self.state, State::Invalid) {
|
||||||
|
match rx.try_recv() {
|
||||||
|
Ok(result) => {
|
||||||
|
self.state = State::Done(map(result));
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
warn!("Resuming handler in incorrect state: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Next::write()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
|
if let State::Done(ref mut handler) = self.state {
|
||||||
|
handler.on_response(res)
|
||||||
|
} else {
|
||||||
|
Next::end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||||
|
if let State::Done(ref mut handler) = self.state {
|
||||||
|
handler.on_response_writable(encoder)
|
||||||
|
} else {
|
||||||
|
Next::end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ use hyper::status::StatusCode;
|
|||||||
use util::version;
|
use util::version;
|
||||||
|
|
||||||
use handlers::add_security_headers;
|
use handlers::add_security_headers;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ContentHandler {
|
pub struct ContentHandler {
|
||||||
@ -31,7 +32,7 @@ pub struct ContentHandler {
|
|||||||
content: String,
|
content: String,
|
||||||
mimetype: Mime,
|
mimetype: Mime,
|
||||||
write_pos: usize,
|
write_pos: usize,
|
||||||
safe_to_embed_on: Option<(String, u16)>,
|
safe_to_embed_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContentHandler {
|
impl ContentHandler {
|
||||||
@ -39,15 +40,17 @@ impl ContentHandler {
|
|||||||
Self::new(StatusCode::Ok, content, mimetype)
|
Self::new(StatusCode::Ok, content, mimetype)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not_found(content: String, mimetype: Mime) -> Self {
|
pub fn html(code: StatusCode, content: String, embeddable_on: Embeddable) -> Self {
|
||||||
Self::new(StatusCode::NotFound, content, mimetype)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn html(code: StatusCode, content: String, embeddable_on: Option<(String, u16)>) -> Self {
|
|
||||||
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_on)
|
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_on)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn error(
|
||||||
|
code: StatusCode,
|
||||||
|
title: &str,
|
||||||
|
message: &str,
|
||||||
|
details: Option<&str>,
|
||||||
|
embeddable_on: Embeddable,
|
||||||
|
) -> Self {
|
||||||
Self::html(code, format!(
|
Self::html(code, format!(
|
||||||
include_str!("../error_tpl.html"),
|
include_str!("../error_tpl.html"),
|
||||||
title=title,
|
title=title,
|
||||||
@ -61,13 +64,18 @@ impl ContentHandler {
|
|||||||
Self::new_embeddable(code, content, mimetype, None)
|
Self::new_embeddable(code, content, mimetype, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_embeddable(code: StatusCode, content: String, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn new_embeddable(
|
||||||
|
code: StatusCode,
|
||||||
|
content: String,
|
||||||
|
mimetype: Mime,
|
||||||
|
safe_to_embed_on: Embeddable,
|
||||||
|
) -> Self {
|
||||||
ContentHandler {
|
ContentHandler {
|
||||||
code: code,
|
code,
|
||||||
content: content,
|
content,
|
||||||
mimetype: mimetype,
|
mimetype,
|
||||||
write_pos: 0,
|
write_pos: 0,
|
||||||
safe_to_embed_on: embeddable_on,
|
safe_to_embed_on,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,7 +92,7 @@ impl server::Handler<HttpStream> for ContentHandler {
|
|||||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
res.set_status(self.code);
|
res.set_status(self.code);
|
||||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||||
Next::write()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ use hyper::status::StatusCode;
|
|||||||
use endpoint::EndpointPath;
|
use endpoint::EndpointPath;
|
||||||
use handlers::{ContentHandler, StreamingHandler};
|
use handlers::{ContentHandler, StreamingHandler};
|
||||||
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
||||||
|
use {Embeddable};
|
||||||
|
|
||||||
const FETCH_TIMEOUT: u64 = 300;
|
const FETCH_TIMEOUT: u64 = 300;
|
||||||
|
|
||||||
@ -179,7 +180,7 @@ impl server::Handler<HttpStream> for WaitingHandler {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Errors {
|
struct Errors {
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Errors {
|
impl Errors {
|
||||||
@ -241,20 +242,20 @@ impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
|||||||
path: EndpointPath,
|
path: EndpointPath,
|
||||||
control: Control,
|
control: Control,
|
||||||
installer: H,
|
installer: H,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ContentFetcherHandler {
|
ContentFetcherHandler {
|
||||||
fetch_control: FetchControl::default(),
|
fetch_control: FetchControl::default(),
|
||||||
control: control,
|
control,
|
||||||
remote: remote,
|
remote,
|
||||||
fetch: fetch,
|
fetch,
|
||||||
status: FetchState::NotStarted(url),
|
status: FetchState::NotStarted(url),
|
||||||
installer: Some(installer),
|
installer: Some(installer),
|
||||||
path: path,
|
path,
|
||||||
errors: Errors {
|
errors: Errors {
|
||||||
embeddable_on: embeddable_on,
|
embeddable_on,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,34 +16,34 @@
|
|||||||
|
|
||||||
//! Hyper handlers implementations.
|
//! Hyper handlers implementations.
|
||||||
|
|
||||||
|
mod async;
|
||||||
mod content;
|
mod content;
|
||||||
mod echo;
|
mod echo;
|
||||||
mod fetch;
|
mod fetch;
|
||||||
mod redirect;
|
mod redirect;
|
||||||
mod streaming;
|
mod streaming;
|
||||||
|
|
||||||
|
pub use self::async::AsyncHandler;
|
||||||
pub use self::content::ContentHandler;
|
pub use self::content::ContentHandler;
|
||||||
pub use self::echo::EchoHandler;
|
pub use self::echo::EchoHandler;
|
||||||
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};
|
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};
|
||||||
pub use self::redirect::Redirection;
|
pub use self::redirect::Redirection;
|
||||||
pub use self::streaming::StreamingHandler;
|
pub use self::streaming::StreamingHandler;
|
||||||
|
|
||||||
|
use std::iter;
|
||||||
|
use util::Itertools;
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use hyper::{server, header, net, uri};
|
use hyper::{server, header, net, uri};
|
||||||
use address;
|
use {apps, address, Embeddable};
|
||||||
|
|
||||||
/// Adds security-related headers to the Response.
|
/// Adds security-related headers to the Response.
|
||||||
pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option<(String, u16)>) {
|
pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embeddable) {
|
||||||
headers.set_raw("X-XSS-Protection", vec![b"1; mode=block".to_vec()]);
|
headers.set_raw("X-XSS-Protection", vec![b"1; mode=block".to_vec()]);
|
||||||
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
headers.set_raw("X-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
||||||
|
|
||||||
// Embedding header:
|
// Embedding header:
|
||||||
if let Some(ref embeddable_on) = embeddable_on {
|
if let None = embeddable_on {
|
||||||
headers.set_raw("X-Frame-Options", vec![
|
|
||||||
format!("ALLOW-FROM http://{}", address(embeddable_on)).into_bytes()
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
// TODO [ToDr] Should we be more strict here (DENY?)?
|
|
||||||
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option
|
|||||||
b"child-src 'self' http: https:;".to_vec(),
|
b"child-src 'self' http: https:;".to_vec(),
|
||||||
// We allow data: blob: and HTTP(s) images.
|
// We allow data: blob: and HTTP(s) images.
|
||||||
// We could get rid of wildcarding HTTP and only allow RPC server URL.
|
// We could get rid of wildcarding HTTP and only allow RPC server URL.
|
||||||
// (http require for local dapps icons)
|
// (http required for local dapps icons)
|
||||||
b"img-src 'self' 'unsafe-inline' data: blob: http: https:;".to_vec(),
|
b"img-src 'self' 'unsafe-inline' data: blob: http: https:;".to_vec(),
|
||||||
// Allow style from data: blob: and HTTPS.
|
// Allow style from data: blob: and HTTPS.
|
||||||
b"style-src 'self' 'unsafe-inline' data: blob: https:;".to_vec(),
|
b"style-src 'self' 'unsafe-inline' data: blob: https:;".to_vec(),
|
||||||
@ -78,10 +78,27 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Option
|
|||||||
b"block-all-mixed-content;".to_vec(),
|
b"block-all-mixed-content;".to_vec(),
|
||||||
// Specify if the site can be embedded.
|
// Specify if the site can be embedded.
|
||||||
match embeddable_on {
|
match embeddable_on {
|
||||||
Some((ref host, ref port)) if host == "127.0.0.1" => {
|
Some(ref embed) => {
|
||||||
format!("frame-ancestors {} {};", address(&(host.to_owned(), *port)), address(&("localhost".to_owned(), *port)))
|
let std = address(&embed.host, embed.port);
|
||||||
|
let proxy = format!("{}.{}", apps::HOME_PAGE, embed.dapps_domain);
|
||||||
|
let domain = format!("*.{}:{}", embed.dapps_domain, embed.port);
|
||||||
|
|
||||||
|
let mut ancestors = vec![std, domain, proxy]
|
||||||
|
.into_iter()
|
||||||
|
.chain(embed.extra_embed_on
|
||||||
|
.iter()
|
||||||
|
.map(|&(ref host, port)| format!("{}:{}", host, port))
|
||||||
|
);
|
||||||
|
|
||||||
|
let ancestors = if embed.host == "127.0.0.1" {
|
||||||
|
let localhost = address("localhost", embed.port);
|
||||||
|
ancestors.chain(iter::once(localhost)).join(" ")
|
||||||
|
} else {
|
||||||
|
ancestors.join(" ")
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("frame-ancestors {};", ancestors)
|
||||||
},
|
},
|
||||||
Some(ref embed) => format!("frame-ancestors {};", address(embed)),
|
|
||||||
None => format!("frame-ancestors 'self';"),
|
None => format!("frame-ancestors 'self';"),
|
||||||
}.into_bytes(),
|
}.into_bytes(),
|
||||||
]);
|
]);
|
||||||
|
@ -24,6 +24,7 @@ use hyper::mime::Mime;
|
|||||||
use hyper::status::StatusCode;
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
use handlers::add_security_headers;
|
use handlers::add_security_headers;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
const BUFFER_SIZE: usize = 1024;
|
const BUFFER_SIZE: usize = 1024;
|
||||||
|
|
||||||
@ -33,11 +34,11 @@ pub struct StreamingHandler<R: io::Read> {
|
|||||||
status: StatusCode,
|
status: StatusCode,
|
||||||
content: io::BufReader<R>,
|
content: io::BufReader<R>,
|
||||||
mimetype: Mime,
|
mimetype: Mime,
|
||||||
safe_to_embed_on: Option<(String, u16)>,
|
safe_to_embed_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: io::Read> StreamingHandler<R> {
|
impl<R: io::Read> StreamingHandler<R> {
|
||||||
pub fn new(content: R, status: StatusCode, mimetype: Mime, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn new(content: R, status: StatusCode, mimetype: Mime, embeddable_on: Embeddable) -> Self {
|
||||||
StreamingHandler {
|
StreamingHandler {
|
||||||
buffer: [0; BUFFER_SIZE],
|
buffer: [0; BUFFER_SIZE],
|
||||||
buffer_leftover: 0,
|
buffer_leftover: 0,
|
||||||
@ -68,7 +69,7 @@ impl<R: io::Read> server::Handler<HttpStream> for StreamingHandler<R> {
|
|||||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||||
res.set_status(self.status);
|
res.set_status(self.status);
|
||||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||||
Next::write()
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
106
dapps/src/lib.rs
106
dapps/src/lib.rs
@ -21,8 +21,10 @@
|
|||||||
|
|
||||||
extern crate base32;
|
extern crate base32;
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
|
extern crate futures_cpupool;
|
||||||
extern crate linked_hash_map;
|
extern crate linked_hash_map;
|
||||||
extern crate mime_guess;
|
extern crate mime_guess;
|
||||||
|
extern crate ntp;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate rustc_hex;
|
extern crate rustc_hex;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
@ -74,6 +76,7 @@ use std::collections::HashMap;
|
|||||||
use jsonrpc_http_server::{self as http, hyper, Origin};
|
use jsonrpc_http_server::{self as http, hyper, Origin};
|
||||||
|
|
||||||
use fetch::Fetch;
|
use fetch::Fetch;
|
||||||
|
use futures_cpupool::CpuPool;
|
||||||
use parity_reactor::Remote;
|
use parity_reactor::Remote;
|
||||||
|
|
||||||
pub use hash_fetch::urlhint::ContractClient;
|
pub use hash_fetch::urlhint::ContractClient;
|
||||||
@ -82,10 +85,9 @@ pub use hash_fetch::urlhint::ContractClient;
|
|||||||
pub trait SyncStatus: Send + Sync {
|
pub trait SyncStatus: Send + Sync {
|
||||||
/// Returns true if there is a major sync happening.
|
/// Returns true if there is a major sync happening.
|
||||||
fn is_major_importing(&self) -> bool;
|
fn is_major_importing(&self) -> bool;
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> SyncStatus for F where F: Fn() -> bool + Send + Sync {
|
/// Returns number of connected and ideal peers.
|
||||||
fn is_major_importing(&self) -> bool { self() }
|
fn peers(&self) -> (usize, usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates Web Proxy tokens
|
/// Validates Web Proxy tokens
|
||||||
@ -127,21 +129,29 @@ impl Middleware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new middleware for UI server.
|
/// Creates new middleware for UI server.
|
||||||
pub fn ui<F: Fetch + Clone>(
|
pub fn ui<F: Fetch>(
|
||||||
|
ntp_server: &str,
|
||||||
|
pool: CpuPool,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
|
dapps_domain: &str,
|
||||||
registrar: Arc<ContractClient>,
|
registrar: Arc<ContractClient>,
|
||||||
sync_status: Arc<SyncStatus>,
|
sync_status: Arc<SyncStatus>,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
dapps_domain: String,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||||
sync_status,
|
sync_status.clone(),
|
||||||
remote.clone(),
|
remote.clone(),
|
||||||
fetch.clone(),
|
fetch.clone(),
|
||||||
).embeddable_on(None).allow_dapps(false));
|
).embeddable_on(None).allow_dapps(false));
|
||||||
let special = {
|
let special = {
|
||||||
let mut special = special_endpoints(content_fetcher.clone());
|
let mut special = special_endpoints(
|
||||||
|
ntp_server,
|
||||||
|
pool,
|
||||||
|
content_fetcher.clone(),
|
||||||
|
remote.clone(),
|
||||||
|
sync_status.clone(),
|
||||||
|
);
|
||||||
special.insert(router::SpecialEndpoint::Home, Some(apps::ui()));
|
special.insert(router::SpecialEndpoint::Home, Some(apps::ui()));
|
||||||
special
|
special
|
||||||
};
|
};
|
||||||
@ -150,7 +160,7 @@ impl Middleware {
|
|||||||
None,
|
None,
|
||||||
special,
|
special,
|
||||||
None,
|
None,
|
||||||
dapps_domain,
|
dapps_domain.to_owned(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Middleware {
|
Middleware {
|
||||||
@ -160,36 +170,49 @@ impl Middleware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new Dapps server middleware.
|
/// Creates new Dapps server middleware.
|
||||||
pub fn dapps<F: Fetch + Clone>(
|
pub fn dapps<F: Fetch>(
|
||||||
|
ntp_server: &str,
|
||||||
|
pool: CpuPool,
|
||||||
remote: Remote,
|
remote: Remote,
|
||||||
ui_address: Option<(String, u16)>,
|
ui_address: Option<(String, u16)>,
|
||||||
|
extra_embed_on: Vec<(String, u16)>,
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
extra_dapps: Vec<PathBuf>,
|
extra_dapps: Vec<PathBuf>,
|
||||||
dapps_domain: String,
|
dapps_domain: &str,
|
||||||
registrar: Arc<ContractClient>,
|
registrar: Arc<ContractClient>,
|
||||||
sync_status: Arc<SyncStatus>,
|
sync_status: Arc<SyncStatus>,
|
||||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||||
fetch: F,
|
fetch: F,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let embeddable = as_embeddable(ui_address, extra_embed_on, dapps_domain);
|
||||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||||
sync_status,
|
sync_status.clone(),
|
||||||
remote.clone(),
|
remote.clone(),
|
||||||
fetch.clone(),
|
fetch.clone(),
|
||||||
).embeddable_on(ui_address.clone()).allow_dapps(true));
|
).embeddable_on(embeddable.clone()).allow_dapps(true));
|
||||||
let endpoints = apps::all_endpoints(
|
let endpoints = apps::all_endpoints(
|
||||||
dapps_path,
|
dapps_path,
|
||||||
extra_dapps,
|
extra_dapps,
|
||||||
dapps_domain.clone(),
|
dapps_domain,
|
||||||
ui_address.clone(),
|
embeddable.clone(),
|
||||||
web_proxy_tokens,
|
web_proxy_tokens,
|
||||||
remote.clone(),
|
remote.clone(),
|
||||||
fetch.clone(),
|
fetch.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let special = {
|
let special = {
|
||||||
let mut special = special_endpoints(content_fetcher.clone());
|
let mut special = special_endpoints(
|
||||||
special.insert(router::SpecialEndpoint::Home, Some(apps::ui_redirection(ui_address.clone())));
|
ntp_server,
|
||||||
|
pool,
|
||||||
|
content_fetcher.clone(),
|
||||||
|
remote.clone(),
|
||||||
|
sync_status,
|
||||||
|
);
|
||||||
|
special.insert(
|
||||||
|
router::SpecialEndpoint::Home,
|
||||||
|
Some(apps::ui_redirection(embeddable.clone())),
|
||||||
|
);
|
||||||
special
|
special
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -197,8 +220,8 @@ impl Middleware {
|
|||||||
content_fetcher,
|
content_fetcher,
|
||||||
Some(endpoints.clone()),
|
Some(endpoints.clone()),
|
||||||
special,
|
special,
|
||||||
ui_address,
|
embeddable,
|
||||||
dapps_domain,
|
dapps_domain.to_owned(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Middleware {
|
Middleware {
|
||||||
@ -214,16 +237,40 @@ impl http::RequestMiddleware for Middleware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn special_endpoints(content_fetcher: Arc<apps::fetcher::Fetcher>) -> HashMap<router::SpecialEndpoint, Option<Box<endpoint::Endpoint>>> {
|
fn special_endpoints(
|
||||||
|
ntp_server: &str,
|
||||||
|
pool: CpuPool,
|
||||||
|
content_fetcher: Arc<apps::fetcher::Fetcher>,
|
||||||
|
remote: Remote,
|
||||||
|
sync_status: Arc<SyncStatus>,
|
||||||
|
) -> HashMap<router::SpecialEndpoint, Option<Box<endpoint::Endpoint>>> {
|
||||||
let mut special = HashMap::new();
|
let mut special = HashMap::new();
|
||||||
special.insert(router::SpecialEndpoint::Rpc, None);
|
special.insert(router::SpecialEndpoint::Rpc, None);
|
||||||
special.insert(router::SpecialEndpoint::Utils, Some(apps::utils()));
|
special.insert(router::SpecialEndpoint::Utils, Some(apps::utils()));
|
||||||
special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(content_fetcher)));
|
special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(
|
||||||
|
content_fetcher,
|
||||||
|
sync_status,
|
||||||
|
api::TimeChecker::new(ntp_server.into(), pool),
|
||||||
|
remote,
|
||||||
|
)));
|
||||||
special
|
special
|
||||||
}
|
}
|
||||||
|
|
||||||
fn address(address: &(String, u16)) -> String {
|
fn address(host: &str, port: u16) -> String {
|
||||||
format!("{}:{}", address.0, address.1)
|
format!("{}:{}", host, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_embeddable(
|
||||||
|
ui_address: Option<(String, u16)>,
|
||||||
|
extra_embed_on: Vec<(String, u16)>,
|
||||||
|
dapps_domain: &str,
|
||||||
|
) -> Option<ParentFrameSettings> {
|
||||||
|
ui_address.map(|(host, port)| ParentFrameSettings {
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
extra_embed_on,
|
||||||
|
dapps_domain: dapps_domain.to_owned(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Random filename
|
/// Random filename
|
||||||
@ -232,3 +279,18 @@ fn random_filename() -> String {
|
|||||||
let mut rng = ::rand::OsRng::new().unwrap();
|
let mut rng = ::rand::OsRng::new().unwrap();
|
||||||
rng.gen_ascii_chars().take(12).collect()
|
rng.gen_ascii_chars().take(12).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Embeddable = Option<ParentFrameSettings>;
|
||||||
|
|
||||||
|
/// Parent frame host and port allowed to embed the content.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ParentFrameSettings {
|
||||||
|
/// Hostname
|
||||||
|
pub host: String,
|
||||||
|
/// Port
|
||||||
|
pub port: u16,
|
||||||
|
/// Additional pages the pages can be embedded on.
|
||||||
|
pub extra_embed_on: Vec<(String, u16)>,
|
||||||
|
/// Dapps Domain (web3.site)
|
||||||
|
pub dapps_domain: String,
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ use page::{handler, PageCache};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||||
use parity_dapps::{WebApp, File, Info};
|
use parity_dapps::{WebApp, File, Info};
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
pub struct PageEndpoint<T : WebApp + 'static> {
|
pub struct PageEndpoint<T : WebApp + 'static> {
|
||||||
/// Content of the files
|
/// Content of the files
|
||||||
@ -25,7 +26,7 @@ pub struct PageEndpoint<T : WebApp + 'static> {
|
|||||||
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
||||||
pub prefix: Option<String>,
|
pub prefix: Option<String>,
|
||||||
/// Safe to be loaded in frame by other origin. (use wisely!)
|
/// Safe to be loaded in frame by other origin. (use wisely!)
|
||||||
safe_to_embed_on: Option<(String, u16)>,
|
safe_to_embed_on: Embeddable,
|
||||||
info: EndpointInfo,
|
info: EndpointInfo,
|
||||||
fallback_to_index_html: bool,
|
fallback_to_index_html: bool,
|
||||||
}
|
}
|
||||||
@ -73,7 +74,7 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
|||||||
/// Creates new `PageEndpoint` which can be safely used in iframe
|
/// Creates new `PageEndpoint` which can be safely used in iframe
|
||||||
/// even from different origin. It might be dangerous (clickjacking).
|
/// even from different origin. It might be dangerous (clickjacking).
|
||||||
/// Use wisely!
|
/// Use wisely!
|
||||||
pub fn new_safe_to_embed(app: T, address: Option<(String, u16)>) -> Self {
|
pub fn new_safe_to_embed(app: T, address: Embeddable) -> Self {
|
||||||
let info = app.info();
|
let info = app.info();
|
||||||
PageEndpoint {
|
PageEndpoint {
|
||||||
app: Arc::new(app),
|
app: Arc::new(app),
|
||||||
|
@ -24,6 +24,7 @@ use hyper::status::StatusCode;
|
|||||||
use hyper::{Decoder, Encoder, Next};
|
use hyper::{Decoder, Encoder, Next};
|
||||||
use endpoint::EndpointPath;
|
use endpoint::EndpointPath;
|
||||||
use handlers::{ContentHandler, add_security_headers};
|
use handlers::{ContentHandler, add_security_headers};
|
||||||
|
use {Embeddable};
|
||||||
|
|
||||||
/// Represents a file that can be sent to client.
|
/// Represents a file that can be sent to client.
|
||||||
/// Implementation should keep track of bytes already sent internally.
|
/// Implementation should keep track of bytes already sent internally.
|
||||||
@ -59,7 +60,7 @@ pub enum ServedFile<T: Dapp> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Dapp> ServedFile<T> {
|
impl<T: Dapp> ServedFile<T> {
|
||||||
pub fn new(embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn new(embeddable_on: Embeddable) -> Self {
|
||||||
ServedFile::Error(ContentHandler::error(
|
ServedFile::Error(ContentHandler::error(
|
||||||
StatusCode::NotFound,
|
StatusCode::NotFound,
|
||||||
"404 Not Found",
|
"404 Not Found",
|
||||||
@ -102,7 +103,7 @@ pub struct PageHandler<T: Dapp> {
|
|||||||
/// Requested path.
|
/// Requested path.
|
||||||
pub path: EndpointPath,
|
pub path: EndpointPath,
|
||||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
/// Flag indicating if the file can be safely embeded (put in iframe).
|
||||||
pub safe_to_embed_on: Option<(String, u16)>,
|
pub safe_to_embed_on: Embeddable,
|
||||||
/// Cache settings for this page.
|
/// Cache settings for this page.
|
||||||
pub cache: PageCache,
|
pub cache: PageCache,
|
||||||
}
|
}
|
||||||
@ -174,7 +175,7 @@ impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Security headers:
|
// Security headers:
|
||||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.clone());
|
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||||
Next::write()
|
Next::write()
|
||||||
},
|
},
|
||||||
ServedFile::Error(ref mut handler) => {
|
ServedFile::Error(ref mut handler) => {
|
||||||
|
@ -21,6 +21,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use page::handler::{self, PageCache, PageHandlerWaiting};
|
use page::handler::{self, PageCache, PageHandlerWaiting};
|
||||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LocalPageEndpoint {
|
pub struct LocalPageEndpoint {
|
||||||
@ -28,11 +29,11 @@ pub struct LocalPageEndpoint {
|
|||||||
mime: Option<Mime>,
|
mime: Option<Mime>,
|
||||||
info: Option<EndpointInfo>,
|
info: Option<EndpointInfo>,
|
||||||
cache: PageCache,
|
cache: PageCache,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocalPageEndpoint {
|
impl LocalPageEndpoint {
|
||||||
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Option<(String, u16)>) -> Self {
|
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Embeddable) -> Self {
|
||||||
LocalPageEndpoint {
|
LocalPageEndpoint {
|
||||||
path: path,
|
path: path,
|
||||||
mime: None,
|
mime: None,
|
||||||
|
@ -19,27 +19,24 @@
|
|||||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||||
use handlers::ContentHandler;
|
use handlers::ContentHandler;
|
||||||
use apps::HOME_PAGE;
|
use apps::HOME_PAGE;
|
||||||
use address;
|
use {address, Embeddable};
|
||||||
|
|
||||||
pub struct ProxyPac {
|
pub struct ProxyPac {
|
||||||
signer_address: Option<(String, u16)>,
|
embeddable: Embeddable,
|
||||||
dapps_domain: String,
|
dapps_domain: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProxyPac {
|
impl ProxyPac {
|
||||||
pub fn boxed(signer_address: Option<(String, u16)>, dapps_domain: String) -> Box<Endpoint> {
|
pub fn boxed(embeddable: Embeddable, dapps_domain: String) -> Box<Endpoint> {
|
||||||
Box::new(ProxyPac {
|
Box::new(ProxyPac { embeddable, dapps_domain })
|
||||||
signer_address: signer_address,
|
|
||||||
dapps_domain: dapps_domain,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Endpoint for ProxyPac {
|
impl Endpoint for ProxyPac {
|
||||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||||
let signer = self.signer_address
|
let ui = self.embeddable
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(address)
|
.map(|ref parent| address(&parent.host, parent.port))
|
||||||
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
|
.unwrap_or_else(|| format!("{}:{}", path.host, path.port));
|
||||||
|
|
||||||
let content = format!(
|
let content = format!(
|
||||||
@ -58,7 +55,7 @@ function FindProxyForURL(url, host) {{
|
|||||||
return "DIRECT";
|
return "DIRECT";
|
||||||
}}
|
}}
|
||||||
"#,
|
"#,
|
||||||
HOME_PAGE, self.dapps_domain, path.host, path.port, signer);
|
HOME_PAGE, self.dapps_domain, path.host, path.port, ui);
|
||||||
|
|
||||||
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
|
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ use apps;
|
|||||||
use apps::fetcher::Fetcher;
|
use apps::fetcher::Fetcher;
|
||||||
use endpoint::{Endpoint, Endpoints, EndpointPath, Handler};
|
use endpoint::{Endpoint, Endpoints, EndpointPath, Handler};
|
||||||
use handlers;
|
use handlers;
|
||||||
|
use Embeddable;
|
||||||
|
|
||||||
/// Special endpoints are accessible on every domain (every dapp)
|
/// Special endpoints are accessible on every domain (every dapp)
|
||||||
#[derive(Debug, PartialEq, Hash, Eq)]
|
#[derive(Debug, PartialEq, Hash, Eq)]
|
||||||
@ -45,7 +46,7 @@ pub struct Router {
|
|||||||
endpoints: Option<Endpoints>,
|
endpoints: Option<Endpoints>,
|
||||||
fetch: Arc<Fetcher>,
|
fetch: Arc<Fetcher>,
|
||||||
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
dapps_domain: String,
|
dapps_domain: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +149,7 @@ impl Router {
|
|||||||
content_fetcher: Arc<Fetcher>,
|
content_fetcher: Arc<Fetcher>,
|
||||||
endpoints: Option<Endpoints>,
|
endpoints: Option<Endpoints>,
|
||||||
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
special: HashMap<SpecialEndpoint, Option<Box<Endpoint>>>,
|
||||||
embeddable_on: Option<(String, u16)>,
|
embeddable_on: Embeddable,
|
||||||
dapps_domain: String,
|
dapps_domain: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Router {
|
Router {
|
||||||
|
@ -26,6 +26,7 @@ use jsonrpc_http_server::{self as http, Host, DomainsValidation};
|
|||||||
use devtools::http_client;
|
use devtools::http_client;
|
||||||
use hash_fetch::urlhint::ContractClient;
|
use hash_fetch::urlhint::ContractClient;
|
||||||
use fetch::{Fetch, Client as FetchClient};
|
use fetch::{Fetch, Client as FetchClient};
|
||||||
|
use futures_cpupool::CpuPool;
|
||||||
use parity_reactor::Remote;
|
use parity_reactor::Remote;
|
||||||
|
|
||||||
use {Middleware, SyncStatus, WebProxyTokens};
|
use {Middleware, SyncStatus, WebProxyTokens};
|
||||||
@ -38,6 +39,12 @@ use self::fetch::FakeFetch;
|
|||||||
|
|
||||||
const SIGNER_PORT: u16 = 18180;
|
const SIGNER_PORT: u16 = 18180;
|
||||||
|
|
||||||
|
struct FakeSync(bool);
|
||||||
|
impl SyncStatus for FakeSync {
|
||||||
|
fn is_major_importing(&self) -> bool { self.0 }
|
||||||
|
fn peers(&self) -> (usize, usize) { (0, 5) }
|
||||||
|
}
|
||||||
|
|
||||||
fn init_logger() {
|
fn init_logger() {
|
||||||
// Initialize logger
|
// Initialize logger
|
||||||
if let Ok(log) = env::var("RUST_LOG") {
|
if let Ok(log) = env::var("RUST_LOG") {
|
||||||
@ -82,7 +89,7 @@ pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
|
|||||||
|
|
||||||
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
|
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
|
||||||
init_server(|builder| {
|
init_server(|builder| {
|
||||||
builder.sync_status(Arc::new(|| true))
|
builder.sync_status(Arc::new(FakeSync(true)))
|
||||||
}, Default::default(), Remote::new_sync())
|
}, Default::default(), Remote::new_sync())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +155,7 @@ impl ServerBuilder {
|
|||||||
ServerBuilder {
|
ServerBuilder {
|
||||||
dapps_path: dapps_path.as_ref().to_owned(),
|
dapps_path: dapps_path.as_ref().to_owned(),
|
||||||
registrar: registrar,
|
registrar: registrar,
|
||||||
sync_status: Arc::new(|| false),
|
sync_status: Arc::new(FakeSync(false)),
|
||||||
web_proxy_tokens: Arc::new(|_| None),
|
web_proxy_tokens: Arc::new(|_| None),
|
||||||
signer_address: None,
|
signer_address: None,
|
||||||
allowed_hosts: DomainsValidation::Disabled,
|
allowed_hosts: DomainsValidation::Disabled,
|
||||||
@ -248,8 +255,11 @@ impl Server {
|
|||||||
fetch: F,
|
fetch: F,
|
||||||
) -> Result<Server, http::Error> {
|
) -> Result<Server, http::Error> {
|
||||||
let middleware = Middleware::dapps(
|
let middleware = Middleware::dapps(
|
||||||
|
"pool.ntp.org:123",
|
||||||
|
CpuPool::new(4),
|
||||||
remote,
|
remote,
|
||||||
signer_address,
|
signer_address,
|
||||||
|
vec![],
|
||||||
dapps_path,
|
dapps_path,
|
||||||
extra_dapps,
|
extra_dapps,
|
||||||
DAPPS_DOMAIN.into(),
|
DAPPS_DOMAIN.into(),
|
||||||
@ -290,4 +300,3 @@ impl Drop for Server {
|
|||||||
self.server.take().unwrap().close()
|
self.server.take().unwrap().close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +31,7 @@ use handlers::{
|
|||||||
StreamingHandler, extract_url,
|
StreamingHandler, extract_url,
|
||||||
};
|
};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use WebProxyTokens;
|
use {Embeddable, WebProxyTokens};
|
||||||
|
|
||||||
pub type Embeddable = Option<(String, u16)>;
|
|
||||||
|
|
||||||
pub struct Web<F> {
|
pub struct Web<F> {
|
||||||
embeddable_on: Embeddable,
|
embeddable_on: Embeddable,
|
||||||
@ -43,12 +41,17 @@ pub struct Web<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Fetch> Web<F> {
|
impl<F: Fetch> Web<F> {
|
||||||
pub fn boxed(embeddable_on: Embeddable, web_proxy_tokens: Arc<WebProxyTokens>, remote: Remote, fetch: F) -> Box<Endpoint> {
|
pub fn boxed(
|
||||||
|
embeddable_on: Embeddable,
|
||||||
|
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||||
|
remote: Remote,
|
||||||
|
fetch: F,
|
||||||
|
) -> Box<Endpoint> {
|
||||||
Box::new(Web {
|
Box::new(Web {
|
||||||
embeddable_on: embeddable_on,
|
embeddable_on,
|
||||||
web_proxy_tokens: web_proxy_tokens,
|
web_proxy_tokens,
|
||||||
remote: remote,
|
remote,
|
||||||
fetch: fetch,
|
fetch,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,12 +102,7 @@ pub fn request(address: &SocketAddr, request: &str) -> Response {
|
|||||||
|
|
||||||
/// Check if all required security headers are present
|
/// Check if all required security headers are present
|
||||||
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
|
||||||
if let Some(port) = port {
|
if let None = port {
|
||||||
assert!(
|
|
||||||
headers.iter().find(|header| header.as_str() == &format!("X-Frame-Options: ALLOW-FROM http://127.0.0.1:{}", port)).is_some(),
|
|
||||||
"X-Frame-Options: ALLOW-FROM missing: {:?}", headers
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
assert!(
|
assert!(
|
||||||
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
headers.iter().find(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN").is_some(),
|
||||||
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
|
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
|
||||||
|
@ -52,7 +52,7 @@ semver = "0.6"
|
|||||||
stats = { path = "../util/stats" }
|
stats = { path = "../util/stats" }
|
||||||
time = "0.1"
|
time = "0.1"
|
||||||
transient-hashmap = "0.4"
|
transient-hashmap = "0.4"
|
||||||
parity-wasm = { git = "https://github.com/nikvolf/parity-wasm" }
|
parity-wasm = "0.12"
|
||||||
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
|
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -64,7 +64,9 @@ pub enum SignError {
|
|||||||
/// Low-level hardware device error.
|
/// Low-level hardware device error.
|
||||||
Hardware(HardwareError),
|
Hardware(HardwareError),
|
||||||
/// Low-level error from store
|
/// Low-level error from store
|
||||||
SStore(SSError)
|
SStore(SSError),
|
||||||
|
/// Inappropriate chain
|
||||||
|
InappropriateChain,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SignError {
|
impl fmt::Display for SignError {
|
||||||
@ -74,6 +76,7 @@ impl fmt::Display for SignError {
|
|||||||
SignError::NotFound => write!(f, "Account does not exist"),
|
SignError::NotFound => write!(f, "Account does not exist"),
|
||||||
SignError::Hardware(ref e) => write!(f, "{}", e),
|
SignError::Hardware(ref e) => write!(f, "{}", e),
|
||||||
SignError::SStore(ref e) => write!(f, "{}", e),
|
SignError::SStore(ref e) => write!(f, "{}", e),
|
||||||
|
SignError::InappropriateChain => write!(f, "Inappropriate chain"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -804,9 +804,15 @@ impl MinerService for Miner {
|
|||||||
// | Make sure to release the locks before calling that method. |
|
// | Make sure to release the locks before calling that method. |
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
self.engine.set_signer(ap.clone(), address, password);
|
self.engine.set_signer(ap.clone(), address, password);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
warn!(target: "miner", "No account provider");
|
||||||
|
Err(AccountError::NotFound)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
warn!(target: "miner", "Cannot set engine signer on a PoW chain.");
|
||||||
|
Err(AccountError::InappropriateChain)
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_extra_data(&self, extra_data: Bytes) {
|
fn set_extra_data(&self, extra_data: Bytes) {
|
||||||
@ -1085,6 +1091,10 @@ impl MinerService for Miner {
|
|||||||
self.transaction_queue.read().last_nonce(address)
|
self.transaction_queue.read().last_nonce(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_produce_work_package(&self) -> bool {
|
||||||
|
self.engine.seals_internally().is_none()
|
||||||
|
}
|
||||||
|
|
||||||
/// Update sealing if required.
|
/// Update sealing if required.
|
||||||
/// Prepare the block and work if the Engine does not seal internally.
|
/// Prepare the block and work if the Engine does not seal internally.
|
||||||
fn update_sealing(&self, chain: &MiningBlockChainClient) {
|
fn update_sealing(&self, chain: &MiningBlockChainClient) {
|
||||||
@ -1113,7 +1123,7 @@ impl MinerService for Miner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_sealing(&self) -> bool {
|
fn is_currently_sealing(&self) -> bool {
|
||||||
self.sealing_work.lock().queue.is_in_use()
|
self.sealing_work.lock().queue.is_in_use()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1272,7 +1282,7 @@ mod tests {
|
|||||||
use header::BlockNumber;
|
use header::BlockNumber;
|
||||||
use types::transaction::{SignedTransaction, Transaction, PendingTransaction, Action};
|
use types::transaction::{SignedTransaction, Transaction, PendingTransaction, Action};
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use tests::helpers::{generate_dummy_client};
|
use tests::helpers::{generate_dummy_client, generate_dummy_client_with_spec_and_accounts};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_prepare_block_to_seal() {
|
fn should_prepare_block_to_seal() {
|
||||||
@ -1440,4 +1450,22 @@ mod tests {
|
|||||||
assert!(miner.pending_block().is_none());
|
assert!(miner.pending_block().is_none());
|
||||||
assert_eq!(client.chain_info().best_block_number, 4 as BlockNumber);
|
assert_eq!(client.chain_info().best_block_number, 4 as BlockNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_fail_setting_engine_signer_on_pow() {
|
||||||
|
let spec = Spec::new_pow_test_spec;
|
||||||
|
let tap = Arc::new(AccountProvider::transient_provider());
|
||||||
|
let addr = tap.insert_account("1".sha3().into(), "").unwrap();
|
||||||
|
let client = generate_dummy_client_with_spec_and_accounts(spec, Some(tap.clone()));
|
||||||
|
assert!(match client.miner().set_engine_signer(addr, "".into()) { Err(AccountError::InappropriateChain) => true, _ => false })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_fail_setting_engine_signer_without_account_provider() {
|
||||||
|
let spec = Spec::new_instant;
|
||||||
|
let tap = Arc::new(AccountProvider::transient_provider());
|
||||||
|
let addr = tap.insert_account("1".sha3().into(), "").unwrap();
|
||||||
|
let client = generate_dummy_client_with_spec_and_accounts(spec, None);
|
||||||
|
assert!(match client.miner().set_engine_signer(addr, "".into()) { Err(AccountError::NotFound) => true, _ => false });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,6 +136,9 @@ pub trait MinerService : Send + Sync {
|
|||||||
/// Called when blocks are imported to chain, updates transactions queue.
|
/// Called when blocks are imported to chain, updates transactions queue.
|
||||||
fn chain_new_blocks(&self, chain: &MiningBlockChainClient, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]);
|
fn chain_new_blocks(&self, chain: &MiningBlockChainClient, imported: &[H256], invalid: &[H256], enacted: &[H256], retracted: &[H256]);
|
||||||
|
|
||||||
|
/// PoW chain - can produce work package
|
||||||
|
fn can_produce_work_package(&self) -> bool;
|
||||||
|
|
||||||
/// New chain head event. Restart mining operation.
|
/// New chain head event. Restart mining operation.
|
||||||
fn update_sealing(&self, chain: &MiningBlockChainClient);
|
fn update_sealing(&self, chain: &MiningBlockChainClient);
|
||||||
|
|
||||||
@ -176,7 +179,7 @@ pub trait MinerService : Send + Sync {
|
|||||||
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
fn last_nonce(&self, address: &Address) -> Option<U256>;
|
||||||
|
|
||||||
/// Is it currently sealing?
|
/// Is it currently sealing?
|
||||||
fn is_sealing(&self) -> bool;
|
fn is_currently_sealing(&self) -> bool;
|
||||||
|
|
||||||
/// Suggested gas price.
|
/// Suggested gas price.
|
||||||
fn sensible_gas_price(&self) -> U256;
|
fn sensible_gas_price(&self) -> U256;
|
||||||
|
@ -451,6 +451,9 @@ impl Spec {
|
|||||||
/// Create a new Spec with BasicAuthority which uses multiple validator sets changing with height.
|
/// 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.
|
/// 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") }
|
pub fn new_validator_multi() -> Self { load_bundled!("validator_multi") }
|
||||||
|
|
||||||
|
/// Create a new spec for a PoW chain
|
||||||
|
pub fn new_pow_test_spec() -> Self { load_bundled!("ethereum/olympic") }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -134,10 +134,10 @@ impl Args {
|
|||||||
Ok(match self.flag_spec {
|
Ok(match self.flag_spec {
|
||||||
Some(ref filename) => {
|
Some(ref filename) => {
|
||||||
let file = fs::File::open(filename).map_err(|e| format!("{}", e))?;
|
let file = fs::File::open(filename).map_err(|e| format!("{}", e))?;
|
||||||
spec::Spec::load(file)?
|
spec::Spec::load(::std::env::temp_dir(), file)?
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
ethcore::ethereum::new_foundation()
|
ethcore::ethereum::new_foundation(&::std::env::temp_dir())
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "parity.js",
|
"name": "parity.js",
|
||||||
"version": "1.7.98",
|
"version": "1.7.99",
|
||||||
"main": "release/index.js",
|
"main": "release/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
"author": "Parity Team <admin@parity.io>",
|
"author": "Parity Team <admin@parity.io>",
|
||||||
|
@ -21,6 +21,7 @@ export const ERROR_CODES = {
|
|||||||
NO_WORK: -32001,
|
NO_WORK: -32001,
|
||||||
NO_AUTHOR: -32002,
|
NO_AUTHOR: -32002,
|
||||||
NO_NEW_WORK: -32003,
|
NO_NEW_WORK: -32003,
|
||||||
|
NO_WORK_REQUIRED: -32004,
|
||||||
NOT_ENOUGH_DATA: -32006,
|
NOT_ENOUGH_DATA: -32006,
|
||||||
UNKNOWN_ERROR: -32009,
|
UNKNOWN_ERROR: -32009,
|
||||||
TRANSACTION_ERROR: -32010,
|
TRANSACTION_ERROR: -32010,
|
||||||
@ -38,7 +39,7 @@ export const ERROR_CODES = {
|
|||||||
COMPILATION_ERROR: -32050,
|
COMPILATION_ERROR: -32050,
|
||||||
ENCRYPTION_ERROR: -32055,
|
ENCRYPTION_ERROR: -32055,
|
||||||
FETCH_ERROR: -32060,
|
FETCH_ERROR: -32060,
|
||||||
INVALID_PARAMS: -32602
|
INVALID_PARAMS: -32602,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class TransportError extends ExtendableError {
|
export default class TransportError extends ExtendableError {
|
||||||
|
@ -94,6 +94,7 @@ class FrameSecureApi extends SecureApi {
|
|||||||
const transport = window.secureTransport || new FakeTransport();
|
const transport = window.secureTransport || new FakeTransport();
|
||||||
const uiUrl = transport.uiUrl || 'http://127.0.0.1:8180';
|
const uiUrl = transport.uiUrl || 'http://127.0.0.1:8180';
|
||||||
|
|
||||||
|
transport.uiUrlWithProtocol = uiUrl;
|
||||||
transport.uiUrl = uiUrl.replace('http://', '').replace('https://', '');
|
transport.uiUrl = uiUrl.replace('http://', '').replace('https://', '');
|
||||||
const api = new FrameSecureApi(transport);
|
const api = new FrameSecureApi(transport);
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@ import { statusBlockNumber, statusCollection } from './statusActions';
|
|||||||
const log = getLogger(LOG_KEYS.Signer);
|
const log = getLogger(LOG_KEYS.Signer);
|
||||||
let instance = null;
|
let instance = null;
|
||||||
|
|
||||||
|
const STATUS_OK = 'ok';
|
||||||
|
const STATUS_WARN = 'needsAttention';
|
||||||
|
const STATUS_BAD = 'bad';
|
||||||
|
|
||||||
export default class Status {
|
export default class Status {
|
||||||
_apiStatus = {};
|
_apiStatus = {};
|
||||||
_status = {};
|
_status = {};
|
||||||
@ -195,13 +199,19 @@ export default class Status {
|
|||||||
|
|
||||||
const statusPromises = [
|
const statusPromises = [
|
||||||
this._api.eth.syncing(),
|
this._api.eth.syncing(),
|
||||||
this._api.parity.netPeers()
|
this._api.parity.netPeers(),
|
||||||
|
this._fetchHealth()
|
||||||
];
|
];
|
||||||
|
|
||||||
return Promise
|
return Promise
|
||||||
.all(statusPromises)
|
.all(statusPromises)
|
||||||
.then(([ syncing, netPeers ]) => {
|
.then(([ syncing, netPeers, health ]) => {
|
||||||
const status = { netPeers, syncing };
|
const status = { netPeers, syncing, health };
|
||||||
|
|
||||||
|
health.overall = this._overallStatus(health);
|
||||||
|
health.peers = health.peers || {};
|
||||||
|
health.sync = health.sync || {};
|
||||||
|
health.time = health.time || {};
|
||||||
|
|
||||||
if (!isEqual(status, this._status)) {
|
if (!isEqual(status, this._status)) {
|
||||||
this._store.dispatch(statusCollection(status));
|
this._store.dispatch(statusCollection(status));
|
||||||
@ -216,6 +226,33 @@ export default class Status {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_overallStatus = (health) => {
|
||||||
|
const all = [health.peers, health.sync, health.time].filter(x => x);
|
||||||
|
const statuses = all.map(x => x.status);
|
||||||
|
const bad = statuses.find(x => x === STATUS_BAD);
|
||||||
|
const needsAttention = statuses.find(x => x === STATUS_WARN);
|
||||||
|
const message = all.map(x => x.message).filter(x => x);
|
||||||
|
|
||||||
|
if (all.length) {
|
||||||
|
return {
|
||||||
|
status: bad || needsAttention || STATUS_OK,
|
||||||
|
message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: STATUS_BAD,
|
||||||
|
message: ['Unable to fetch node health.']
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_fetchHealth = () => {
|
||||||
|
// Support Parity-Extension.
|
||||||
|
const uiUrl = this._api.transport.uiUrlWithProtocol || '';
|
||||||
|
|
||||||
|
return fetch(`${uiUrl}/api/health`).then(res => res.json());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data fetched here should not change
|
* The data fetched here should not change
|
||||||
* unless Parity is restarted. They are thus
|
* unless Parity is restarted. They are thus
|
||||||
|
@ -18,11 +18,28 @@ import BigNumber from 'bignumber.js';
|
|||||||
import { handleActions } from 'redux-actions';
|
import { handleActions } from 'redux-actions';
|
||||||
|
|
||||||
const DEFAULT_NETCHAIN = '(unknown)';
|
const DEFAULT_NETCHAIN = '(unknown)';
|
||||||
|
const DEFAULT_STATUS = 'needsAttention';
|
||||||
const initialState = {
|
const initialState = {
|
||||||
blockNumber: new BigNumber(0),
|
blockNumber: new BigNumber(0),
|
||||||
blockTimestamp: new Date(),
|
blockTimestamp: new Date(),
|
||||||
clientVersion: '',
|
clientVersion: '',
|
||||||
gasLimit: new BigNumber(0),
|
gasLimit: new BigNumber(0),
|
||||||
|
health: {
|
||||||
|
peers: {
|
||||||
|
status: DEFAULT_STATUS
|
||||||
|
},
|
||||||
|
sync: {
|
||||||
|
status: DEFAULT_STATUS
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
status: DEFAULT_STATUS
|
||||||
|
},
|
||||||
|
overall: {
|
||||||
|
isReady: false,
|
||||||
|
status: DEFAULT_STATUS,
|
||||||
|
message: []
|
||||||
|
}
|
||||||
|
},
|
||||||
netChain: DEFAULT_NETCHAIN,
|
netChain: DEFAULT_NETCHAIN,
|
||||||
netPeers: {
|
netPeers: {
|
||||||
active: new BigNumber(0),
|
active: new BigNumber(0),
|
||||||
|
@ -31,7 +31,8 @@ export default class Actionbar extends Component {
|
|||||||
title: nodeOrStringProptype(),
|
title: nodeOrStringProptype(),
|
||||||
buttons: PropTypes.array,
|
buttons: PropTypes.array,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string
|
className: PropTypes.string,
|
||||||
|
health: PropTypes.node
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
17
js/src/ui/StatusIndicator/index.js
Normal file
17
js/src/ui/StatusIndicator/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
export default from './statusIndicator';
|
89
js/src/ui/StatusIndicator/statusIndicator.css
Normal file
89
js/src/ui/StatusIndicator/statusIndicator.css
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
.status {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radial,.signal {
|
||||||
|
display: inline-block;
|
||||||
|
margin: .2em;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radial {
|
||||||
|
border-radius: 100%;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.5);
|
||||||
|
background-image: radial-gradient(ellipse at top, rgba(255, 255, 255, 0.38) 0%, rgba(255, 255, 255, 0) 100%);
|
||||||
|
|
||||||
|
&.ok {
|
||||||
|
background-color: #070;
|
||||||
|
}
|
||||||
|
&.bad {
|
||||||
|
background-color: #c00;
|
||||||
|
}
|
||||||
|
&.needsAttention {
|
||||||
|
background-color: #dc0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal {
|
||||||
|
width: 2em;
|
||||||
|
width: calc(.9em + 5px);
|
||||||
|
text-transform: initial;
|
||||||
|
vertical-align: bottom;
|
||||||
|
margin-top: -1em;
|
||||||
|
|
||||||
|
> .bar {
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid #444;
|
||||||
|
box-shadow: 0 0 1px rgba(0, 0, 0, 0.8);
|
||||||
|
width: .3em;
|
||||||
|
height: 1em;
|
||||||
|
opacity: 0.7;
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
vertical-align: bottom;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
opacity: 1.0;
|
||||||
|
background-image: linear-gradient(0, rgba(255, 255, 255, 0.38) 0%, rgba(255, 255, 255, 0) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bad {
|
||||||
|
height: .4em;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
&.needsAttention {
|
||||||
|
height: .6em;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
&.ok {
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bad > .bar.active {
|
||||||
|
background-color: #c00;
|
||||||
|
}
|
||||||
|
&.ok > .bar.active {
|
||||||
|
background-color: #080;
|
||||||
|
}
|
||||||
|
&.needsAttention > .bar.active {
|
||||||
|
background-color: #dc0;
|
||||||
|
}
|
||||||
|
}
|
70
js/src/ui/StatusIndicator/statusIndicator.js
Normal file
70
js/src/ui/StatusIndicator/statusIndicator.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import ReactTooltip from 'react-tooltip';
|
||||||
|
|
||||||
|
import styles from './statusIndicator.css';
|
||||||
|
|
||||||
|
const statuses = ['bad', 'needsAttention', 'ok'];
|
||||||
|
|
||||||
|
export default class StatusIndicator extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
type: PropTypes.oneOf(['radial', 'signal']),
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
status: PropTypes.oneOf(statuses).isRequired,
|
||||||
|
title: PropTypes.arrayOf(PropTypes.node),
|
||||||
|
tooltipPlacement: PropTypes.oneOf(['left', 'top', 'bottom', 'right'])
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
type: 'signal',
|
||||||
|
title: []
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { id, status, title, type, tooltipPlacement } = this.props;
|
||||||
|
const tooltip = title.find(x => !x.isEmpty) ? (
|
||||||
|
<ReactTooltip id={ `status-${id}` }>
|
||||||
|
{ title.map(x => (<div key={ x }>{ x }</div>)) }
|
||||||
|
</ReactTooltip>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={ styles.status }>
|
||||||
|
<span className={ `${styles[type]} ${styles[status]}` }
|
||||||
|
data-tip={ title.length }
|
||||||
|
data-for={ `status-${id}` }
|
||||||
|
data-place={ tooltipPlacement }
|
||||||
|
data-effect='solid'
|
||||||
|
>
|
||||||
|
{ type === 'signal' && statuses.map(this.renderBar) }
|
||||||
|
</span>
|
||||||
|
{tooltip}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBar = (signal) => {
|
||||||
|
const idx = statuses.indexOf(this.props.status);
|
||||||
|
const isActive = statuses.indexOf(signal) <= idx;
|
||||||
|
const activeClass = isActive ? styles.active : '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span key={ signal } className={ `${styles.bar} ${styles[signal]} ${activeClass}` } />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,7 @@ export SectionList from './SectionList';
|
|||||||
export SelectionList from './SelectionList';
|
export SelectionList from './SelectionList';
|
||||||
export ShortenedHash from './ShortenedHash';
|
export ShortenedHash from './ShortenedHash';
|
||||||
export SignerIcon from './SignerIcon';
|
export SignerIcon from './SignerIcon';
|
||||||
|
export StatusIndicator from './StatusIndicator';
|
||||||
export Tags from './Tags';
|
export Tags from './Tags';
|
||||||
export Title from './Title';
|
export Title from './Title';
|
||||||
export Tooltips, { Tooltip } from './Tooltips';
|
export Tooltips, { Tooltip } from './Tooltips';
|
||||||
|
@ -43,6 +43,7 @@ class Accounts extends Component {
|
|||||||
accountsInfo: PropTypes.object.isRequired,
|
accountsInfo: PropTypes.object.isRequired,
|
||||||
availability: PropTypes.string.isRequired,
|
availability: PropTypes.string.isRequired,
|
||||||
hasAccounts: PropTypes.bool.isRequired,
|
hasAccounts: PropTypes.bool.isRequired,
|
||||||
|
health: PropTypes.object.isRequired,
|
||||||
setVisibleAccounts: PropTypes.func.isRequired
|
setVisibleAccounts: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,12 +497,14 @@ class Accounts extends Component {
|
|||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const { accounts, accountsInfo, hasAccounts } = state.personal;
|
const { accounts, accountsInfo, hasAccounts } = state.personal;
|
||||||
const { availability = 'unknown' } = state.nodeStatus.nodeKind || {};
|
const { availability = 'unknown' } = state.nodeStatus.nodeKind || {};
|
||||||
|
const { health } = state.nodeStatus;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accounts,
|
accounts,
|
||||||
accountsInfo,
|
accountsInfo,
|
||||||
availability,
|
availability,
|
||||||
hasAccounts
|
hasAccounts,
|
||||||
|
health
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react';
|
|||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { BlockStatus } from '~/ui';
|
import { BlockStatus, StatusIndicator } from '~/ui';
|
||||||
|
|
||||||
import styles from './status.css';
|
import styles from './status.css';
|
||||||
|
|
||||||
@ -28,11 +28,12 @@ class Status extends Component {
|
|||||||
isTest: PropTypes.bool,
|
isTest: PropTypes.bool,
|
||||||
netChain: PropTypes.string,
|
netChain: PropTypes.string,
|
||||||
netPeers: PropTypes.object,
|
netPeers: PropTypes.object,
|
||||||
|
health: PropTypes.object,
|
||||||
upgradeStore: PropTypes.object.isRequired
|
upgradeStore: PropTypes.object.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { clientVersion, isTest, netChain, netPeers } = this.props;
|
const { clientVersion, isTest, netChain, netPeers, health } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.status }>
|
<div className={ styles.status }>
|
||||||
@ -44,13 +45,20 @@ class Status extends Component {
|
|||||||
{ this.renderUpgradeButton() }
|
{ this.renderUpgradeButton() }
|
||||||
</div>
|
</div>
|
||||||
<div className={ styles.netinfo }>
|
<div className={ styles.netinfo }>
|
||||||
<BlockStatus />
|
<div>
|
||||||
|
<StatusIndicator
|
||||||
|
type='signal'
|
||||||
|
id='application.status.health'
|
||||||
|
status={ health.overall.status }
|
||||||
|
title={ health.overall.message }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span title={ `${netPeers.connected.toFormat()}/${netPeers.max.toFormat()} peers` }>
|
||||||
|
<BlockStatus />
|
||||||
|
</span>
|
||||||
<div className={ `${styles.network} ${styles[isTest ? 'test' : 'live']}` }>
|
<div className={ `${styles.network} ${styles[isTest ? 'test' : 'live']}` }>
|
||||||
{ netChain }
|
{ netChain }
|
||||||
</div>
|
</div>
|
||||||
<div className={ styles.peers }>
|
|
||||||
{ netPeers.connected.toFormat() }/{ netPeers.max.toFormat() } peers
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -102,14 +110,7 @@ class Status extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return;
|
||||||
<div>
|
|
||||||
<FormattedMessage
|
|
||||||
id='application.status.consensus.unknown'
|
|
||||||
defaultMessage='Upgrade status is unknown.'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderUpgradeButton () {
|
renderUpgradeButton () {
|
||||||
@ -136,10 +137,11 @@ class Status extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const { clientVersion, netPeers, netChain, isTest } = state.nodeStatus;
|
const { clientVersion, health, netPeers, netChain, isTest } = state.nodeStatus;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
clientVersion,
|
clientVersion,
|
||||||
|
health,
|
||||||
netPeers,
|
netPeers,
|
||||||
netChain,
|
netChain,
|
||||||
isTest
|
isTest
|
||||||
|
@ -81,6 +81,16 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.indicatorTab {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
flex: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.indicator {
|
||||||
|
padding: 20px 12px 0;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
.first {
|
.first {
|
||||||
margin-left: -24px;
|
margin-left: -24px;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ import { Link } from 'react-router';
|
|||||||
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
|
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
import { Tooltip } from '~/ui';
|
import { Tooltip, StatusIndicator } from '~/ui';
|
||||||
|
|
||||||
import Tab from './Tab';
|
import Tab from './Tab';
|
||||||
import styles from './tabBar.css';
|
import styles from './tabBar.css';
|
||||||
@ -33,6 +33,7 @@ class TabBar extends Component {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
pending: PropTypes.array,
|
pending: PropTypes.array,
|
||||||
|
health: PropTypes.object.isRequired,
|
||||||
views: PropTypes.array.isRequired
|
views: PropTypes.array.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,12 +42,29 @@ class TabBar extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
const { health } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Toolbar className={ styles.toolbar }>
|
<Toolbar className={ styles.toolbar }>
|
||||||
<ToolbarGroup className={ styles.first }>
|
<ToolbarGroup className={ styles.first }>
|
||||||
<div />
|
<div />
|
||||||
</ToolbarGroup>
|
</ToolbarGroup>
|
||||||
<div className={ styles.tabs }>
|
<div className={ styles.tabs }>
|
||||||
|
<Link
|
||||||
|
activeClassName={ styles.tabactive }
|
||||||
|
className={ `${styles.tabLink} ${styles.indicatorTab}` }
|
||||||
|
key='status'
|
||||||
|
to='/status'
|
||||||
|
>
|
||||||
|
<div className={ styles.indicator }>
|
||||||
|
<StatusIndicator
|
||||||
|
type='signal'
|
||||||
|
id='topbar.health'
|
||||||
|
status={ health.overall.status }
|
||||||
|
title={ health.overall.message }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
{ this.renderTabItems() }
|
{ this.renderTabItems() }
|
||||||
<Tooltip
|
<Tooltip
|
||||||
className={ styles.tabbarTooltip }
|
className={ styles.tabbarTooltip }
|
||||||
@ -101,6 +119,7 @@ function mapStateToProps (initState) {
|
|||||||
return (state) => {
|
return (state) => {
|
||||||
const { availability = 'unknown' } = state.nodeStatus.nodeKind || {};
|
const { availability = 'unknown' } = state.nodeStatus.nodeKind || {};
|
||||||
const { views } = state.settings;
|
const { views } = state.settings;
|
||||||
|
const { health } = state.nodeStatus;
|
||||||
|
|
||||||
const viewIds = Object
|
const viewIds = Object
|
||||||
.keys(views)
|
.keys(views)
|
||||||
@ -114,7 +133,7 @@ function mapStateToProps (initState) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (isEqual(viewIds, filteredViewIds)) {
|
if (isEqual(viewIds, filteredViewIds)) {
|
||||||
return { views: filteredViews };
|
return { views: filteredViews, health };
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredViewIds = viewIds;
|
filteredViewIds = viewIds;
|
||||||
@ -123,7 +142,7 @@ function mapStateToProps (initState) {
|
|||||||
id
|
id
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return { views: filteredViews };
|
return { views: filteredViews, health };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,12 @@ function createStore () {
|
|||||||
nodeStatus: {
|
nodeStatus: {
|
||||||
nodeKind: {
|
nodeKind: {
|
||||||
'availability': 'personal'
|
'availability': 'personal'
|
||||||
|
},
|
||||||
|
health: {
|
||||||
|
overall: {
|
||||||
|
status: 'ok',
|
||||||
|
message: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -24,7 +24,7 @@ import { connect } from 'react-redux';
|
|||||||
import store from 'store';
|
import store from 'store';
|
||||||
|
|
||||||
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
|
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
|
||||||
import { AccountCard, Badge, Button, ContainerTitle, IdentityIcon, ParityBackground, SelectionList } from '~/ui';
|
import { AccountCard, Badge, Button, ContainerTitle, IdentityIcon, ParityBackground, SelectionList, StatusIndicator } from '~/ui';
|
||||||
import { CancelIcon, FingerprintIcon } from '~/ui/Icons';
|
import { CancelIcon, FingerprintIcon } from '~/ui/Icons';
|
||||||
import DappsStore from '~/views/Dapps/dappsStore';
|
import DappsStore from '~/views/Dapps/dappsStore';
|
||||||
import { Embedded as Signer } from '~/views/Signer';
|
import { Embedded as Signer } from '~/views/Signer';
|
||||||
@ -50,7 +50,8 @@ class ParityBar extends Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
dapp: PropTypes.bool,
|
dapp: PropTypes.bool,
|
||||||
externalLink: PropTypes.string,
|
externalLink: PropTypes.string,
|
||||||
pending: PropTypes.array
|
pending: PropTypes.array,
|
||||||
|
health: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -210,7 +211,7 @@ class ParityBar extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderBar () {
|
renderBar () {
|
||||||
const { dapp } = this.props;
|
const { dapp, health } = this.props;
|
||||||
|
|
||||||
if (!dapp) {
|
if (!dapp) {
|
||||||
return null;
|
return null;
|
||||||
@ -218,6 +219,13 @@ class ParityBar extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.cornercolor }>
|
<div className={ styles.cornercolor }>
|
||||||
|
<StatusIndicator
|
||||||
|
type='signal'
|
||||||
|
id='paritybar.health'
|
||||||
|
status={ health.overall.status }
|
||||||
|
title={ health.overall.message }
|
||||||
|
tooltipPlacement='right'
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
className={ styles.iconButton }
|
className={ styles.iconButton }
|
||||||
icon={
|
icon={
|
||||||
@ -699,9 +707,11 @@ class ParityBar extends Component {
|
|||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const { pending } = state.signer;
|
const { pending } = state.signer;
|
||||||
|
const { health } = state.nodeStatus;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pending
|
pending,
|
||||||
|
health
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,14 @@ function createRedux (state = {}) {
|
|||||||
},
|
},
|
||||||
signer: {
|
signer: {
|
||||||
pending: []
|
pending: []
|
||||||
|
},
|
||||||
|
nodeStatus: {
|
||||||
|
health: {
|
||||||
|
overall: {
|
||||||
|
status: 'ok',
|
||||||
|
message: []
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, state)
|
}, state)
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
|
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
|
||||||
import { AccountsIcon, AddressesIcon, AppsIcon, ContactsIcon, FingerprintIcon, SettingsIcon, StatusIcon } from '~/ui/Icons';
|
import { AccountsIcon, AddressesIcon, AppsIcon, ContactsIcon, FingerprintIcon, SettingsIcon } from '~/ui/Icons';
|
||||||
|
|
||||||
import styles from './views.css';
|
import styles from './views.css';
|
||||||
|
|
||||||
@ -65,14 +65,6 @@ const defaultViews = {
|
|||||||
value: 'contract'
|
value: 'contract'
|
||||||
},
|
},
|
||||||
|
|
||||||
status: {
|
|
||||||
active: false,
|
|
||||||
onlyPersonal: true,
|
|
||||||
icon: <StatusIcon />,
|
|
||||||
route: '/status',
|
|
||||||
value: 'status'
|
|
||||||
},
|
|
||||||
|
|
||||||
signer: {
|
signer: {
|
||||||
active: true,
|
active: true,
|
||||||
fixed: true,
|
fixed: true,
|
||||||
|
@ -113,17 +113,6 @@ class Views extends Component {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
|
||||||
this.renderView('status',
|
|
||||||
<FormattedMessage
|
|
||||||
id='settings.views.status.label'
|
|
||||||
/>,
|
|
||||||
<FormattedMessage
|
|
||||||
id='settings.views.status.description'
|
|
||||||
defaultMessage='See how the Parity node is performing in terms of connections to the network, logs from the actual running instance and details of mining (if enabled and configured).'
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
this.renderView('signer',
|
this.renderView('signer',
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
152
js/src/views/Status/Health/health.js
Normal file
152
js/src/views/Status/Health/health.js
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { Container, ContainerTitle, StatusIndicator } from '~/ui';
|
||||||
|
|
||||||
|
import grid from '../NodeStatus/nodeStatus.css';
|
||||||
|
|
||||||
|
const HealthItem = (props) => {
|
||||||
|
const status = props.item.status || 'needsAttention';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3>
|
||||||
|
<StatusIndicator
|
||||||
|
id={ props.id }
|
||||||
|
title={ [
|
||||||
|
(<div>{ props.item.message }</div>)
|
||||||
|
] }
|
||||||
|
status={ status }
|
||||||
|
/>
|
||||||
|
{ props.title }
|
||||||
|
<small> ({ props.details })</small>
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
{ status !== 'ok' ? props.item.message : '' }
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
HealthItem.propTypes = {
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
title: PropTypes.node.isRequired,
|
||||||
|
details: PropTypes.oneOfType([
|
||||||
|
PropTypes.string,
|
||||||
|
PropTypes.node
|
||||||
|
]).isRequired,
|
||||||
|
item: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
class Health extends Component {
|
||||||
|
static contextTypes = {
|
||||||
|
api: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
peers: PropTypes.object.isRequired,
|
||||||
|
sync: PropTypes.object.isRequired,
|
||||||
|
time: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { peers, sync, time } = this.props;
|
||||||
|
const [yes, no] = [(
|
||||||
|
<FormattedMessage
|
||||||
|
id='status.health.yes'
|
||||||
|
defaultMessage='yes'
|
||||||
|
/>
|
||||||
|
), (
|
||||||
|
<FormattedMessage
|
||||||
|
id='status.health.no'
|
||||||
|
defaultMessage='no'
|
||||||
|
/>
|
||||||
|
)];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<ContainerTitle
|
||||||
|
title={
|
||||||
|
<div>
|
||||||
|
<FormattedMessage
|
||||||
|
id='status.health.title'
|
||||||
|
defaultMessage='Node Health'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div className={ grid.container }>
|
||||||
|
<div className={ grid.row }>
|
||||||
|
<div className={ grid.col4 }>
|
||||||
|
<HealthItem
|
||||||
|
id='status.health.sync'
|
||||||
|
title={
|
||||||
|
<FormattedMessage
|
||||||
|
id='status.health.sync'
|
||||||
|
defaultMessage='Chain Synchronized'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
details={ !sync.details ? yes : no }
|
||||||
|
item={ sync }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={ grid.col4 }>
|
||||||
|
<HealthItem
|
||||||
|
id='status.health.peers'
|
||||||
|
title={
|
||||||
|
<FormattedMessage
|
||||||
|
id='status.health.peers'
|
||||||
|
defaultMessage='Connected Peers'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
details={ (peers.details || []).join('/') }
|
||||||
|
item={ peers }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={ grid.col4 }>
|
||||||
|
<HealthItem
|
||||||
|
id='status.health.time'
|
||||||
|
title={
|
||||||
|
<FormattedMessage
|
||||||
|
id='status.health.time'
|
||||||
|
defaultMessage='Time Synchronized'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
details={ `${time.details || 0} ms` }
|
||||||
|
item={ time }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return state.nodeStatus.health;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
null
|
||||||
|
)(Health);
|
17
js/src/views/Status/Health/index.js
Normal file
17
js/src/views/Status/Health/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// 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/>.
|
||||||
|
|
||||||
|
export default from './health';
|
@ -44,7 +44,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.col,
|
.col,
|
||||||
.col3, .col4_5, .col6, .col12 {
|
.col3, .col4, .col4_5, .col6, .col12 {
|
||||||
float: left;
|
float: left;
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -57,6 +57,13 @@
|
|||||||
width: calc(100% / 12 * 3);
|
width: calc(100% / 12 * 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.col4 {
|
||||||
|
width: 33.3%;
|
||||||
|
width: -webkit-calc(100% / 12 * 4);
|
||||||
|
width: -moz-calc(100% / 12 * 4);
|
||||||
|
width: calc(100% / 12 * 4);
|
||||||
|
}
|
||||||
|
|
||||||
.col4_5 {
|
.col4_5 {
|
||||||
width: 37.5%;
|
width: 37.5%;
|
||||||
width: -webkit-calc(100% / 12 * 4.5);
|
width: -webkit-calc(100% / 12 * 4.5);
|
||||||
|
@ -20,6 +20,7 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
import { Page } from '~/ui';
|
import { Page } from '~/ui';
|
||||||
|
|
||||||
import Debug from './Debug';
|
import Debug from './Debug';
|
||||||
|
import Health from './Health';
|
||||||
import Peers from './Peers';
|
import Peers from './Peers';
|
||||||
import NodeStatus from './NodeStatus';
|
import NodeStatus from './NodeStatus';
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ export default () => (
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className={ styles.body }>
|
<div className={ styles.body }>
|
||||||
|
<Health />
|
||||||
<NodeStatus />
|
<NodeStatus />
|
||||||
<Peers />
|
<Peers />
|
||||||
<Debug />
|
<Debug />
|
||||||
|
@ -58,3 +58,7 @@
|
|||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
@ -20,7 +20,7 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
|
|
||||||
import { Button } from '~/ui';
|
import { Button, StatusIndicator } from '~/ui';
|
||||||
|
|
||||||
import styles from './syncWarning.css';
|
import styles from './syncWarning.css';
|
||||||
|
|
||||||
@ -38,7 +38,8 @@ export const showSyncWarning = () => {
|
|||||||
|
|
||||||
class SyncWarning extends Component {
|
class SyncWarning extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
isSyncing: PropTypes.bool
|
isOk: PropTypes.bool.isRequired,
|
||||||
|
health: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -47,10 +48,10 @@ class SyncWarning extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { isSyncing } = this.props;
|
const { isOk, health } = this.props;
|
||||||
const { dontShowAgain, show } = this.state;
|
const { dontShowAgain, show } = this.state;
|
||||||
|
|
||||||
if (!isSyncing || isSyncing === null || !show) {
|
if (isOk || !show) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,18 +60,19 @@ class SyncWarning extends Component {
|
|||||||
<div className={ styles.overlay } />
|
<div className={ styles.overlay } />
|
||||||
<div className={ styles.modal }>
|
<div className={ styles.modal }>
|
||||||
<div className={ styles.body }>
|
<div className={ styles.body }>
|
||||||
<FormattedMessage
|
<div className={ styles.status }>
|
||||||
id='syncWarning.message.line1'
|
<StatusIndicator
|
||||||
defaultMessage={ `
|
type='signal'
|
||||||
Your Parity node is still syncing to the chain.
|
id='healthWarning.indicator'
|
||||||
` }
|
status={ health.overall.status }
|
||||||
/>
|
/>
|
||||||
<FormattedMessage
|
</div>
|
||||||
id='syncWarning.message.line2'
|
|
||||||
defaultMessage={ `
|
{
|
||||||
Some of the shown information might be out-of-date.
|
health.overall.message.map(message => (
|
||||||
` }
|
<p key={ message }>{ message }</p>
|
||||||
/>
|
))
|
||||||
|
}
|
||||||
|
|
||||||
<div className={ styles.button }>
|
<div className={ styles.button }>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@ -113,14 +115,13 @@ class SyncWarning extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const { syncing } = state.nodeStatus;
|
const { health } = state.nodeStatus;
|
||||||
// syncing could be an Object, false, or null
|
const isNotAvailableYet = health.overall.isReady;
|
||||||
const isSyncing = syncing
|
const isOk = isNotAvailableYet || health.overall.status === 'ok';
|
||||||
? true
|
|
||||||
: syncing;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isSyncing
|
isOk,
|
||||||
|
health
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,12 @@ function createRedux (syncing = null) {
|
|||||||
getState: () => {
|
getState: () => {
|
||||||
return {
|
return {
|
||||||
nodeStatus: {
|
nodeStatus: {
|
||||||
syncing
|
health: {
|
||||||
|
overall: {
|
||||||
|
status: syncing ? 'needsAttention' : 'ok',
|
||||||
|
message: []
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -67,10 +67,11 @@ pub fn setup_log(config: &Config) -> Result<Arc<RotatingLogger>, String> {
|
|||||||
|
|
||||||
let mut levels = String::new();
|
let mut levels = String::new();
|
||||||
let mut builder = LogBuilder::new();
|
let mut builder = LogBuilder::new();
|
||||||
// Disable ws info logging by default.
|
// Disable info logging by default for some modules:
|
||||||
builder.filter(Some("ws"), LogLevelFilter::Warn);
|
builder.filter(Some("ws"), LogLevelFilter::Warn);
|
||||||
// Disable rustls info logging by default.
|
builder.filter(Some("reqwest"), LogLevelFilter::Warn);
|
||||||
builder.filter(Some("rustls"), LogLevelFilter::Warn);
|
builder.filter(Some("rustls"), LogLevelFilter::Warn);
|
||||||
|
// Enable info for others.
|
||||||
builder.filter(None, LogLevelFilter::Info);
|
builder.filter(None, LogLevelFilter::Info);
|
||||||
|
|
||||||
if let Ok(lvl) = env::var("RUST_LOG") {
|
if let Ok(lvl) = env::var("RUST_LOG") {
|
||||||
|
@ -78,6 +78,7 @@ disable_periodic = true
|
|||||||
jit = false
|
jit = false
|
||||||
|
|
||||||
[misc]
|
[misc]
|
||||||
|
ntp_server = "pool.ntp.org:123"
|
||||||
logging = "own_tx=trace"
|
logging = "own_tx=trace"
|
||||||
log_file = "/var/log/parity.log"
|
log_file = "/var/log/parity.log"
|
||||||
color = true
|
color = true
|
||||||
|
@ -183,8 +183,10 @@ usage! {
|
|||||||
or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")),
|
or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")),
|
||||||
flag_jsonrpc_hosts: String = "none",
|
flag_jsonrpc_hosts: String = "none",
|
||||||
or |c: &Config| otry!(c.rpc).hosts.as_ref().map(|vec| vec.join(",")),
|
or |c: &Config| otry!(c.rpc).hosts.as_ref().map(|vec| vec.join(",")),
|
||||||
flag_jsonrpc_threads: Option<usize> = None,
|
flag_jsonrpc_server_threads: Option<usize> = None,
|
||||||
or |c: &Config| otry!(c.rpc).threads.map(Some),
|
or |c: &Config| otry!(c.rpc).server_threads.map(Some),
|
||||||
|
flag_jsonrpc_threads: usize = 0usize,
|
||||||
|
or |c: &Config| otry!(c.rpc).processing_threads,
|
||||||
|
|
||||||
// WS
|
// WS
|
||||||
flag_no_ws: bool = false,
|
flag_no_ws: bool = false,
|
||||||
@ -355,6 +357,8 @@ usage! {
|
|||||||
or |c: &Config| otry!(c.vm).jit.clone(),
|
or |c: &Config| otry!(c.vm).jit.clone(),
|
||||||
|
|
||||||
// -- Miscellaneous Options
|
// -- Miscellaneous Options
|
||||||
|
flag_ntp_server: String = "pool.ntp.org:123",
|
||||||
|
or |c: &Config| otry!(c.misc).ntp_server.clone(),
|
||||||
flag_logging: Option<String> = None,
|
flag_logging: Option<String> = None,
|
||||||
or |c: &Config| otry!(c.misc).logging.clone().map(Some),
|
or |c: &Config| otry!(c.misc).logging.clone().map(Some),
|
||||||
flag_log_file: Option<String> = None,
|
flag_log_file: Option<String> = None,
|
||||||
@ -471,7 +475,8 @@ struct Rpc {
|
|||||||
cors: Option<String>,
|
cors: Option<String>,
|
||||||
apis: Option<Vec<String>>,
|
apis: Option<Vec<String>>,
|
||||||
hosts: Option<Vec<String>>,
|
hosts: Option<Vec<String>>,
|
||||||
threads: Option<usize>,
|
server_threads: Option<usize>,
|
||||||
|
processing_threads: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Deserialize)]
|
#[derive(Default, Debug, PartialEq, Deserialize)]
|
||||||
@ -590,6 +595,7 @@ struct VM {
|
|||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Deserialize)]
|
#[derive(Default, Debug, PartialEq, Deserialize)]
|
||||||
struct Misc {
|
struct Misc {
|
||||||
|
ntp_server: Option<String>,
|
||||||
logging: Option<String>,
|
logging: Option<String>,
|
||||||
log_file: Option<String>,
|
log_file: Option<String>,
|
||||||
color: Option<bool>,
|
color: Option<bool>,
|
||||||
@ -752,7 +758,8 @@ mod tests {
|
|||||||
flag_jsonrpc_cors: Some("null".into()),
|
flag_jsonrpc_cors: Some("null".into()),
|
||||||
flag_jsonrpc_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(),
|
flag_jsonrpc_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(),
|
||||||
flag_jsonrpc_hosts: "none".into(),
|
flag_jsonrpc_hosts: "none".into(),
|
||||||
flag_jsonrpc_threads: None,
|
flag_jsonrpc_server_threads: None,
|
||||||
|
flag_jsonrpc_threads: 0,
|
||||||
|
|
||||||
// WS
|
// WS
|
||||||
flag_no_ws: false,
|
flag_no_ws: false,
|
||||||
@ -889,6 +896,7 @@ mod tests {
|
|||||||
flag_dapps_apis_all: None,
|
flag_dapps_apis_all: None,
|
||||||
|
|
||||||
// -- Miscellaneous Options
|
// -- Miscellaneous Options
|
||||||
|
flag_ntp_server: "pool.ntp.org:123".into(),
|
||||||
flag_version: false,
|
flag_version: false,
|
||||||
flag_logging: Some("own_tx=trace".into()),
|
flag_logging: Some("own_tx=trace".into()),
|
||||||
flag_log_file: Some("/var/log/parity.log".into()),
|
flag_log_file: Some("/var/log/parity.log".into()),
|
||||||
@ -980,7 +988,8 @@ mod tests {
|
|||||||
cors: None,
|
cors: None,
|
||||||
apis: None,
|
apis: None,
|
||||||
hosts: None,
|
hosts: None,
|
||||||
threads: None,
|
server_threads: None,
|
||||||
|
processing_threads: None,
|
||||||
}),
|
}),
|
||||||
ipc: Some(Ipc {
|
ipc: Some(Ipc {
|
||||||
disable: None,
|
disable: None,
|
||||||
@ -1064,6 +1073,7 @@ mod tests {
|
|||||||
jit: Some(false),
|
jit: Some(false),
|
||||||
}),
|
}),
|
||||||
misc: Some(Misc {
|
misc: Some(Misc {
|
||||||
|
ntp_server: Some("pool.ntp.org:123".into()),
|
||||||
logging: Some("own_tx=trace".into()),
|
logging: Some("own_tx=trace".into()),
|
||||||
log_file: Some("/var/log/parity.log".into()),
|
log_file: Some("/var/log/parity.log".into()),
|
||||||
color: Some(true),
|
color: Some(true),
|
||||||
|
@ -177,9 +177,12 @@ API and Console Options:
|
|||||||
is additional security against some attack
|
is additional security against some attack
|
||||||
vectors. Special options: "all", "none",
|
vectors. Special options: "all", "none",
|
||||||
(default: {flag_jsonrpc_hosts}).
|
(default: {flag_jsonrpc_hosts}).
|
||||||
--jsonrpc-threads THREADS Enables experimental faster implementation of JSON-RPC server.
|
--jsonrpc-server-threads NUM Enables experimental faster implementation of JSON-RPC server.
|
||||||
Requires Dapps server to be disabled
|
Requires Dapps server to be disabled
|
||||||
using --no-dapps. (default: {flag_jsonrpc_threads:?})
|
using --no-dapps. (default: {flag_jsonrpc_server_threads:?})
|
||||||
|
--jsonrpc-threads THREADS Turn on additional processing threads in all RPC servers.
|
||||||
|
Setting this to non-zero value allows parallel cpu-heavy queries
|
||||||
|
execution. (default: {flag_jsonrpc_threads})
|
||||||
|
|
||||||
--no-ws Disable the WebSockets server. (default: {flag_no_ws})
|
--no-ws Disable the WebSockets server. (default: {flag_no_ws})
|
||||||
--ws-port PORT Specify the port portion of the WebSockets server
|
--ws-port PORT Specify the port portion of the WebSockets server
|
||||||
@ -465,6 +468,8 @@ Internal Options:
|
|||||||
--can-restart Executable will auto-restart if exiting with 69.
|
--can-restart Executable will auto-restart if exiting with 69.
|
||||||
|
|
||||||
Miscellaneous Options:
|
Miscellaneous Options:
|
||||||
|
--ntp-server HOST NTP server to provide current time (host:port). Used to verify node health.
|
||||||
|
(default: {flag_ntp_server})
|
||||||
-l --logging LOGGING Specify the logging level. Must conform to the same
|
-l --logging LOGGING Specify the logging level. Must conform to the same
|
||||||
format as RUST_LOG. (default: {flag_logging:?})
|
format as RUST_LOG. (default: {flag_logging:?})
|
||||||
--log-file FILENAME Specify a filename into which logging should be
|
--log-file FILENAME Specify a filename into which logging should be
|
||||||
|
@ -137,7 +137,7 @@ impl Configuration {
|
|||||||
let secretstore_conf = self.secretstore_config()?;
|
let secretstore_conf = self.secretstore_config()?;
|
||||||
let format = self.format()?;
|
let format = self.format()?;
|
||||||
|
|
||||||
if self.args.flag_jsonrpc_threads.is_some() && dapps_conf.enabled {
|
if self.args.flag_jsonrpc_server_threads.is_some() && dapps_conf.enabled {
|
||||||
dapps_conf.enabled = false;
|
dapps_conf.enabled = false;
|
||||||
writeln!(&mut stderr(), "Warning: Disabling Dapps server because fast RPC server was enabled.").expect("Error writing to stderr.")
|
writeln!(&mut stderr(), "Warning: Disabling Dapps server because fast RPC server was enabled.").expect("Error writing to stderr.")
|
||||||
}
|
}
|
||||||
@ -556,6 +556,7 @@ impl Configuration {
|
|||||||
fn ui_config(&self) -> UiConfiguration {
|
fn ui_config(&self) -> UiConfiguration {
|
||||||
UiConfiguration {
|
UiConfiguration {
|
||||||
enabled: self.ui_enabled(),
|
enabled: self.ui_enabled(),
|
||||||
|
ntp_server: self.args.flag_ntp_server.clone(),
|
||||||
interface: self.ui_interface(),
|
interface: self.ui_interface(),
|
||||||
port: self.args.flag_ports_shift + self.args.flag_ui_port,
|
port: self.args.flag_ports_shift + self.args.flag_ui_port,
|
||||||
hosts: self.ui_hosts(),
|
hosts: self.ui_hosts(),
|
||||||
@ -565,12 +566,18 @@ impl Configuration {
|
|||||||
fn dapps_config(&self) -> DappsConfiguration {
|
fn dapps_config(&self) -> DappsConfiguration {
|
||||||
DappsConfiguration {
|
DappsConfiguration {
|
||||||
enabled: self.dapps_enabled(),
|
enabled: self.dapps_enabled(),
|
||||||
|
ntp_server: self.args.flag_ntp_server.clone(),
|
||||||
dapps_path: PathBuf::from(self.directories().dapps),
|
dapps_path: PathBuf::from(self.directories().dapps),
|
||||||
extra_dapps: if self.args.cmd_dapp {
|
extra_dapps: if self.args.cmd_dapp {
|
||||||
self.args.arg_path.iter().map(|path| PathBuf::from(path)).collect()
|
self.args.arg_path.iter().map(|path| PathBuf::from(path)).collect()
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
},
|
},
|
||||||
|
extra_embed_on: if self.args.flag_ui_no_validation {
|
||||||
|
vec![("localhost".to_owned(), 3000)]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,15 +783,11 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ws_hosts(&self) -> Option<Vec<String>> {
|
fn ws_hosts(&self) -> Option<Vec<String>> {
|
||||||
if self.args.flag_ui_no_validation {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.hosts(&self.args.flag_ws_hosts, &self.ws_interface())
|
self.hosts(&self.args.flag_ws_hosts, &self.ws_interface())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ws_origins(&self) -> Option<Vec<String>> {
|
fn ws_origins(&self) -> Option<Vec<String>> {
|
||||||
if self.args.flag_unsafe_expose {
|
if self.args.flag_unsafe_expose || self.args.flag_ui_no_validation {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -825,11 +828,12 @@ impl Configuration {
|
|||||||
},
|
},
|
||||||
hosts: self.rpc_hosts(),
|
hosts: self.rpc_hosts(),
|
||||||
cors: self.rpc_cors(),
|
cors: self.rpc_cors(),
|
||||||
threads: match self.args.flag_jsonrpc_threads {
|
server_threads: match self.args.flag_jsonrpc_server_threads {
|
||||||
Some(threads) if threads > 0 => Some(threads),
|
Some(threads) if threads > 0 => Some(threads),
|
||||||
None => None,
|
None => None,
|
||||||
_ => return Err("--jsonrpc-threads number needs to be positive.".into()),
|
_ => return Err("--jsonrpc-server-threads number needs to be positive.".into()),
|
||||||
}
|
},
|
||||||
|
processing_threads: self.args.flag_jsonrpc_threads,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(conf)
|
Ok(conf)
|
||||||
@ -1264,6 +1268,7 @@ mod tests {
|
|||||||
support_token_api: true
|
support_token_api: true
|
||||||
}, UiConfiguration {
|
}, UiConfiguration {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
ntp_server: "pool.ntp.org:123".into(),
|
||||||
interface: "127.0.0.1".into(),
|
interface: "127.0.0.1".into(),
|
||||||
port: 8180,
|
port: 8180,
|
||||||
hosts: Some(vec![]),
|
hosts: Some(vec![]),
|
||||||
@ -1504,6 +1509,7 @@ mod tests {
|
|||||||
assert_eq!(conf0.directories().signer, "signer".to_owned());
|
assert_eq!(conf0.directories().signer, "signer".to_owned());
|
||||||
assert_eq!(conf0.ui_config(), UiConfiguration {
|
assert_eq!(conf0.ui_config(), UiConfiguration {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
ntp_server: "pool.ntp.org:123".into(),
|
||||||
interface: "127.0.0.1".into(),
|
interface: "127.0.0.1".into(),
|
||||||
port: 8180,
|
port: 8180,
|
||||||
hosts: Some(vec![]),
|
hosts: Some(vec![]),
|
||||||
@ -1512,14 +1518,17 @@ mod tests {
|
|||||||
assert_eq!(conf1.directories().signer, "signer".to_owned());
|
assert_eq!(conf1.directories().signer, "signer".to_owned());
|
||||||
assert_eq!(conf1.ui_config(), UiConfiguration {
|
assert_eq!(conf1.ui_config(), UiConfiguration {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
ntp_server: "pool.ntp.org:123".into(),
|
||||||
interface: "127.0.0.1".into(),
|
interface: "127.0.0.1".into(),
|
||||||
port: 8180,
|
port: 8180,
|
||||||
hosts: Some(vec![]),
|
hosts: Some(vec![]),
|
||||||
});
|
});
|
||||||
assert_eq!(conf1.ws_config().unwrap().hosts, None);
|
assert_eq!(conf1.dapps_config().extra_embed_on, vec![("localhost".to_owned(), 3000)]);
|
||||||
|
assert_eq!(conf1.ws_config().unwrap().origins, None);
|
||||||
assert_eq!(conf2.directories().signer, "signer".to_owned());
|
assert_eq!(conf2.directories().signer, "signer".to_owned());
|
||||||
assert_eq!(conf2.ui_config(), UiConfiguration {
|
assert_eq!(conf2.ui_config(), UiConfiguration {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
ntp_server: "pool.ntp.org:123".into(),
|
||||||
interface: "127.0.0.1".into(),
|
interface: "127.0.0.1".into(),
|
||||||
port: 3123,
|
port: 3123,
|
||||||
hosts: Some(vec![]),
|
hosts: Some(vec![]),
|
||||||
@ -1528,6 +1537,7 @@ mod tests {
|
|||||||
assert_eq!(conf3.directories().signer, "signer".to_owned());
|
assert_eq!(conf3.directories().signer, "signer".to_owned());
|
||||||
assert_eq!(conf3.ui_config(), UiConfiguration {
|
assert_eq!(conf3.ui_config(), UiConfiguration {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
ntp_server: "pool.ntp.org:123".into(),
|
||||||
interface: "test".into(),
|
interface: "test".into(),
|
||||||
port: 8180,
|
port: 8180,
|
||||||
hosts: Some(vec![]),
|
hosts: Some(vec![]),
|
||||||
|
@ -22,6 +22,7 @@ use ethcore::client::{Client, BlockChainClient, BlockId};
|
|||||||
use ethcore::transaction::{Transaction, Action};
|
use ethcore::transaction::{Transaction, Action};
|
||||||
use ethsync::LightSync;
|
use ethsync::LightSync;
|
||||||
use futures::{future, IntoFuture, Future, BoxFuture};
|
use futures::{future, IntoFuture, Future, BoxFuture};
|
||||||
|
use futures_cpupool::CpuPool;
|
||||||
use hash_fetch::fetch::Client as FetchClient;
|
use hash_fetch::fetch::Client as FetchClient;
|
||||||
use hash_fetch::urlhint::ContractClient;
|
use hash_fetch::urlhint::ContractClient;
|
||||||
use helpers::replace_home;
|
use helpers::replace_home;
|
||||||
@ -35,8 +36,10 @@ use util::{Bytes, Address};
|
|||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
|
pub ntp_server: String,
|
||||||
pub dapps_path: PathBuf,
|
pub dapps_path: PathBuf,
|
||||||
pub extra_dapps: Vec<PathBuf>,
|
pub extra_dapps: Vec<PathBuf>,
|
||||||
|
pub extra_embed_on: Vec<(String, u16)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Configuration {
|
impl Default for Configuration {
|
||||||
@ -44,8 +47,10 @@ impl Default for Configuration {
|
|||||||
let data_dir = default_data_path();
|
let data_dir = default_data_path();
|
||||||
Configuration {
|
Configuration {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
ntp_server: "pool.ntp.org:123".into(),
|
||||||
dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
|
dapps_path: replace_home(&data_dir, "$BASE/dapps").into(),
|
||||||
extra_dapps: vec![],
|
extra_dapps: vec![],
|
||||||
|
extra_embed_on: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,6 +145,7 @@ pub struct Dependencies {
|
|||||||
pub sync_status: Arc<SyncStatus>,
|
pub sync_status: Arc<SyncStatus>,
|
||||||
pub contract_client: Arc<ContractClient>,
|
pub contract_client: Arc<ContractClient>,
|
||||||
pub remote: parity_reactor::TokioRemote,
|
pub remote: parity_reactor::TokioRemote,
|
||||||
|
pub pool: CpuPool,
|
||||||
pub fetch: FetchClient,
|
pub fetch: FetchClient,
|
||||||
pub signer: Arc<SignerService>,
|
pub signer: Arc<SignerService>,
|
||||||
pub ui_address: Option<(String, u16)>,
|
pub ui_address: Option<(String, u16)>,
|
||||||
@ -152,20 +158,23 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Mi
|
|||||||
|
|
||||||
server::dapps_middleware(
|
server::dapps_middleware(
|
||||||
deps,
|
deps,
|
||||||
|
&configuration.ntp_server,
|
||||||
configuration.dapps_path,
|
configuration.dapps_path,
|
||||||
configuration.extra_dapps,
|
configuration.extra_dapps,
|
||||||
rpc::DAPPS_DOMAIN.into(),
|
rpc::DAPPS_DOMAIN,
|
||||||
|
configuration.extra_embed_on,
|
||||||
).map(Some)
|
).map(Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_ui(enabled: bool, deps: Dependencies) -> Result<Option<Middleware>, String> {
|
pub fn new_ui(enabled: bool, ntp_server: &str, deps: Dependencies) -> Result<Option<Middleware>, String> {
|
||||||
if !enabled {
|
if !enabled {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
server::ui_middleware(
|
server::ui_middleware(
|
||||||
deps,
|
deps,
|
||||||
rpc::DAPPS_DOMAIN.into(),
|
ntp_server,
|
||||||
|
rpc::DAPPS_DOMAIN,
|
||||||
).map(Some)
|
).map(Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,16 +201,19 @@ mod server {
|
|||||||
|
|
||||||
pub fn dapps_middleware(
|
pub fn dapps_middleware(
|
||||||
_deps: Dependencies,
|
_deps: Dependencies,
|
||||||
|
_ntp_server: &str,
|
||||||
_dapps_path: PathBuf,
|
_dapps_path: PathBuf,
|
||||||
_extra_dapps: Vec<PathBuf>,
|
_extra_dapps: Vec<PathBuf>,
|
||||||
_dapps_domain: String,
|
_dapps_domain: &str,
|
||||||
|
_extra_embed_on: Vec<(String, u16)>,
|
||||||
) -> Result<Middleware, String> {
|
) -> Result<Middleware, String> {
|
||||||
Err("Your Parity version has been compiled without WebApps support.".into())
|
Err("Your Parity version has been compiled without WebApps support.".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui_middleware(
|
pub fn ui_middleware(
|
||||||
_deps: Dependencies,
|
_deps: Dependencies,
|
||||||
_dapps_domain: String,
|
_ntp_server: &str,
|
||||||
|
_dapps_domain: &str,
|
||||||
) -> Result<Middleware, String> {
|
) -> Result<Middleware, String> {
|
||||||
Err("Your Parity version has been compiled without UI support.".into())
|
Err("Your Parity version has been compiled without UI support.".into())
|
||||||
}
|
}
|
||||||
@ -226,17 +238,22 @@ mod server {
|
|||||||
|
|
||||||
pub fn dapps_middleware(
|
pub fn dapps_middleware(
|
||||||
deps: Dependencies,
|
deps: Dependencies,
|
||||||
|
ntp_server: &str,
|
||||||
dapps_path: PathBuf,
|
dapps_path: PathBuf,
|
||||||
extra_dapps: Vec<PathBuf>,
|
extra_dapps: Vec<PathBuf>,
|
||||||
dapps_domain: String,
|
dapps_domain: &str,
|
||||||
|
extra_embed_on: Vec<(String, u16)>,
|
||||||
) -> Result<Middleware, String> {
|
) -> Result<Middleware, String> {
|
||||||
let signer = deps.signer;
|
let signer = deps.signer;
|
||||||
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
|
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
|
||||||
let web_proxy_tokens = Arc::new(move |token| signer.web_proxy_access_token_domain(&token));
|
let web_proxy_tokens = Arc::new(move |token| signer.web_proxy_access_token_domain(&token));
|
||||||
|
|
||||||
Ok(parity_dapps::Middleware::dapps(
|
Ok(parity_dapps::Middleware::dapps(
|
||||||
|
ntp_server,
|
||||||
|
deps.pool,
|
||||||
parity_remote,
|
parity_remote,
|
||||||
deps.ui_address,
|
deps.ui_address,
|
||||||
|
extra_embed_on,
|
||||||
dapps_path,
|
dapps_path,
|
||||||
extra_dapps,
|
extra_dapps,
|
||||||
dapps_domain,
|
dapps_domain,
|
||||||
@ -249,15 +266,18 @@ mod server {
|
|||||||
|
|
||||||
pub fn ui_middleware(
|
pub fn ui_middleware(
|
||||||
deps: Dependencies,
|
deps: Dependencies,
|
||||||
dapps_domain: String,
|
ntp_server: &str,
|
||||||
|
dapps_domain: &str,
|
||||||
) -> Result<Middleware, String> {
|
) -> Result<Middleware, String> {
|
||||||
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
|
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
|
||||||
Ok(parity_dapps::Middleware::ui(
|
Ok(parity_dapps::Middleware::ui(
|
||||||
|
ntp_server,
|
||||||
|
deps.pool,
|
||||||
parity_remote,
|
parity_remote,
|
||||||
|
dapps_domain,
|
||||||
deps.contract_client,
|
deps.contract_client,
|
||||||
deps.sync_status,
|
deps.sync_status,
|
||||||
deps.fetch,
|
deps.fetch,
|
||||||
dapps_domain,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ extern crate docopt;
|
|||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
extern crate fdlimit;
|
extern crate fdlimit;
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
|
extern crate futures_cpupool;
|
||||||
extern crate isatty;
|
extern crate isatty;
|
||||||
extern crate jsonrpc_core;
|
extern crate jsonrpc_core;
|
||||||
extern crate num_cpus;
|
extern crate num_cpus;
|
||||||
|
@ -30,6 +30,7 @@ use rpc_apis::{self, ApiSet};
|
|||||||
|
|
||||||
pub use parity_rpc::{IpcServer, HttpServer, RequestMiddleware};
|
pub use parity_rpc::{IpcServer, HttpServer, RequestMiddleware};
|
||||||
pub use parity_rpc::ws::Server as WsServer;
|
pub use parity_rpc::ws::Server as WsServer;
|
||||||
|
pub use parity_rpc::informant::CpuPool;
|
||||||
|
|
||||||
|
|
||||||
pub const DAPPS_DOMAIN: &'static str = "web3.site";
|
pub const DAPPS_DOMAIN: &'static str = "web3.site";
|
||||||
@ -42,7 +43,8 @@ pub struct HttpConfiguration {
|
|||||||
pub apis: ApiSet,
|
pub apis: ApiSet,
|
||||||
pub cors: Option<Vec<String>>,
|
pub cors: Option<Vec<String>>,
|
||||||
pub hosts: Option<Vec<String>>,
|
pub hosts: Option<Vec<String>>,
|
||||||
pub threads: Option<usize>,
|
pub server_threads: Option<usize>,
|
||||||
|
pub processing_threads: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpConfiguration {
|
impl HttpConfiguration {
|
||||||
@ -63,7 +65,8 @@ impl Default for HttpConfiguration {
|
|||||||
apis: ApiSet::UnsafeContext,
|
apis: ApiSet::UnsafeContext,
|
||||||
cors: None,
|
cors: None,
|
||||||
hosts: Some(Vec::new()),
|
hosts: Some(Vec::new()),
|
||||||
threads: None,
|
server_threads: None,
|
||||||
|
processing_threads: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,6 +74,7 @@ impl Default for HttpConfiguration {
|
|||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct UiConfiguration {
|
pub struct UiConfiguration {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
|
pub ntp_server: String,
|
||||||
pub interface: String,
|
pub interface: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub hosts: Option<Vec<String>>,
|
pub hosts: Option<Vec<String>>,
|
||||||
@ -94,7 +98,8 @@ impl From<UiConfiguration> for HttpConfiguration {
|
|||||||
apis: rpc_apis::ApiSet::SafeContext,
|
apis: rpc_apis::ApiSet::SafeContext,
|
||||||
cors: None,
|
cors: None,
|
||||||
hosts: conf.hosts,
|
hosts: conf.hosts,
|
||||||
threads: None,
|
server_threads: None,
|
||||||
|
processing_threads: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,6 +108,7 @@ impl Default for UiConfiguration {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
UiConfiguration {
|
UiConfiguration {
|
||||||
enabled: true && cfg!(feature = "ui-enabled"),
|
enabled: true && cfg!(feature = "ui-enabled"),
|
||||||
|
ntp_server: "pool.ntp.org:123".into(),
|
||||||
port: 8180,
|
port: 8180,
|
||||||
interface: "127.0.0.1".into(),
|
interface: "127.0.0.1".into(),
|
||||||
hosts: Some(vec![]),
|
hosts: Some(vec![]),
|
||||||
@ -176,6 +182,7 @@ pub struct Dependencies<D: rpc_apis::Dependencies> {
|
|||||||
pub apis: Arc<D>,
|
pub apis: Arc<D>,
|
||||||
pub remote: TokioRemote,
|
pub remote: TokioRemote,
|
||||||
pub stats: Arc<RpcStats>,
|
pub stats: Arc<RpcStats>,
|
||||||
|
pub pool: Option<CpuPool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_ws<D: rpc_apis::Dependencies>(
|
pub fn new_ws<D: rpc_apis::Dependencies>(
|
||||||
@ -192,11 +199,12 @@ pub fn new_ws<D: rpc_apis::Dependencies>(
|
|||||||
let addr = url.parse().map_err(|_| format!("Invalid WebSockets listen host/port given: {}", url))?;
|
let addr = url.parse().map_err(|_| format!("Invalid WebSockets listen host/port given: {}", url))?;
|
||||||
|
|
||||||
|
|
||||||
let full_handler = setup_apis(rpc_apis::ApiSet::SafeContext, deps);
|
let pool = deps.pool.clone();
|
||||||
|
let full_handler = setup_apis(rpc_apis::ApiSet::SafeContext, deps, pool.clone());
|
||||||
let handler = {
|
let handler = {
|
||||||
let mut handler = MetaIoHandler::with_middleware((
|
let mut handler = MetaIoHandler::with_middleware((
|
||||||
rpc::WsDispatcher::new(full_handler),
|
rpc::WsDispatcher::new(full_handler),
|
||||||
Middleware::new(deps.stats.clone(), deps.apis.activity_notifier())
|
Middleware::new(deps.stats.clone(), deps.apis.activity_notifier(), pool)
|
||||||
));
|
));
|
||||||
let apis = conf.apis.list_apis();
|
let apis = conf.apis.list_apis();
|
||||||
deps.apis.extend_with_set(&mut handler, &apis);
|
deps.apis.extend_with_set(&mut handler, &apis);
|
||||||
@ -252,7 +260,8 @@ pub fn new_http<D: rpc_apis::Dependencies>(
|
|||||||
let http_address = (conf.interface, conf.port);
|
let http_address = (conf.interface, conf.port);
|
||||||
let url = format!("{}:{}", http_address.0, http_address.1);
|
let url = format!("{}:{}", http_address.0, http_address.1);
|
||||||
let addr = url.parse().map_err(|_| format!("Invalid {} listen host/port given: {}", id, url))?;
|
let addr = url.parse().map_err(|_| format!("Invalid {} listen host/port given: {}", id, url))?;
|
||||||
let handler = setup_apis(conf.apis, deps);
|
let pool = deps.pool.clone();
|
||||||
|
let handler = setup_apis(conf.apis, deps, pool);
|
||||||
let remote = deps.remote.clone();
|
let remote = deps.remote.clone();
|
||||||
|
|
||||||
let cors_domains = into_domains(conf.cors);
|
let cors_domains = into_domains(conf.cors);
|
||||||
@ -265,7 +274,7 @@ pub fn new_http<D: rpc_apis::Dependencies>(
|
|||||||
handler,
|
handler,
|
||||||
remote,
|
remote,
|
||||||
rpc::RpcExtractor,
|
rpc::RpcExtractor,
|
||||||
match (conf.threads, middleware) {
|
match (conf.server_threads, middleware) {
|
||||||
(Some(threads), None) => rpc::HttpSettings::Threads(threads),
|
(Some(threads), None) => rpc::HttpSettings::Threads(threads),
|
||||||
(None, middleware) => rpc::HttpSettings::Dapps(middleware),
|
(None, middleware) => rpc::HttpSettings::Dapps(middleware),
|
||||||
(Some(_), Some(_)) => {
|
(Some(_), Some(_)) => {
|
||||||
@ -291,7 +300,8 @@ pub fn new_ipc<D: rpc_apis::Dependencies>(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let handler = setup_apis(conf.apis, dependencies);
|
let pool = dependencies.pool.clone();
|
||||||
|
let handler = setup_apis(conf.apis, dependencies, pool);
|
||||||
let remote = dependencies.remote.clone();
|
let remote = dependencies.remote.clone();
|
||||||
match rpc::start_ipc(&conf.socket_addr, handler, remote, rpc::RpcExtractor) {
|
match rpc::start_ipc(&conf.socket_addr, handler, remote, rpc::RpcExtractor) {
|
||||||
Ok(server) => Ok(Some(server)),
|
Ok(server) => Ok(Some(server)),
|
||||||
@ -318,11 +328,11 @@ fn with_domain(items: Option<Vec<String>>, domain: &str, addresses: &[Option<(St
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_apis<D>(apis: ApiSet, deps: &Dependencies<D>) -> MetaIoHandler<Metadata, Middleware<D::Notifier>>
|
fn setup_apis<D>(apis: ApiSet, deps: &Dependencies<D>, pool: Option<CpuPool>) -> MetaIoHandler<Metadata, Middleware<D::Notifier>>
|
||||||
where D: rpc_apis::Dependencies
|
where D: rpc_apis::Dependencies
|
||||||
{
|
{
|
||||||
let mut handler = MetaIoHandler::with_middleware(
|
let mut handler = MetaIoHandler::with_middleware(
|
||||||
Middleware::new(deps.stats.clone(), deps.apis.activity_notifier())
|
Middleware::new(deps.stats.clone(), deps.apis.activity_notifier(), pool)
|
||||||
);
|
);
|
||||||
let apis = apis.list_apis();
|
let apis = apis.list_apis();
|
||||||
deps.apis.extend_with_set(&mut handler, &apis);
|
deps.apis.extend_with_set(&mut handler, &apis);
|
||||||
|
@ -27,8 +27,7 @@ use ethcore::miner::{StratumOptions, Stratum};
|
|||||||
use ethcore::service::ClientService;
|
use ethcore::service::ClientService;
|
||||||
use ethcore::snapshot;
|
use ethcore::snapshot;
|
||||||
use ethcore::verification::queue::VerifierSettings;
|
use ethcore::verification::queue::VerifierSettings;
|
||||||
use ethsync::NetworkConfiguration;
|
use ethsync::{self, SyncConfig};
|
||||||
use ethsync::SyncConfig;
|
|
||||||
use fdlimit::raise_fd_limit;
|
use fdlimit::raise_fd_limit;
|
||||||
use hash_fetch::fetch::{Fetch, Client as FetchClient};
|
use hash_fetch::fetch::{Fetch, Client as FetchClient};
|
||||||
use informant::{Informant, LightNodeInformantData, FullNodeInformantData};
|
use informant::{Informant, LightNodeInformantData, FullNodeInformantData};
|
||||||
@ -84,7 +83,7 @@ pub struct RunCmd {
|
|||||||
pub ws_conf: rpc::WsConfiguration,
|
pub ws_conf: rpc::WsConfiguration,
|
||||||
pub http_conf: rpc::HttpConfiguration,
|
pub http_conf: rpc::HttpConfiguration,
|
||||||
pub ipc_conf: rpc::IpcConfiguration,
|
pub ipc_conf: rpc::IpcConfiguration,
|
||||||
pub net_conf: NetworkConfiguration,
|
pub net_conf: ethsync::NetworkConfiguration,
|
||||||
pub network_id: Option<u64>,
|
pub network_id: Option<u64>,
|
||||||
pub warp_sync: bool,
|
pub warp_sync: bool,
|
||||||
pub public_node: bool,
|
pub public_node: bool,
|
||||||
@ -237,7 +236,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
|
|||||||
network_config: net_conf.into_basic().map_err(|e| format!("Failed to produce network config: {}", e))?,
|
network_config: net_conf.into_basic().map_err(|e| format!("Failed to produce network config: {}", e))?,
|
||||||
client: Arc::new(provider),
|
client: Arc::new(provider),
|
||||||
network_id: cmd.network_id.unwrap_or(spec.network_id()),
|
network_id: cmd.network_id.unwrap_or(spec.network_id()),
|
||||||
subprotocol_name: ::ethsync::LIGHT_PROTOCOL,
|
subprotocol_name: ethsync::LIGHT_PROTOCOL,
|
||||||
handlers: vec![on_demand.clone()],
|
handlers: vec![on_demand.clone()],
|
||||||
};
|
};
|
||||||
let light_sync = LightSync::new(sync_params).map_err(|e| format!("Error starting network: {}", e))?;
|
let light_sync = LightSync::new(sync_params).map_err(|e| format!("Error starting network: {}", e))?;
|
||||||
@ -277,9 +276,18 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
|
|||||||
on_demand: on_demand.clone(),
|
on_demand: on_demand.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let sync = light_sync.clone();
|
struct LightSyncStatus(Arc<LightSync>);
|
||||||
|
impl dapps::SyncStatus for LightSyncStatus {
|
||||||
|
fn is_major_importing(&self) -> bool { self.0.is_major_importing() }
|
||||||
|
fn peers(&self) -> (usize, usize) {
|
||||||
|
let peers = ethsync::LightSyncProvider::peer_numbers(&*self.0);
|
||||||
|
(peers.connected, peers.max)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dapps::Dependencies {
|
dapps::Dependencies {
|
||||||
sync_status: Arc::new(move || sync.is_major_importing()),
|
sync_status: Arc::new(LightSyncStatus(light_sync.clone())),
|
||||||
|
pool: fetch.pool(),
|
||||||
contract_client: contract_client,
|
contract_client: contract_client,
|
||||||
remote: event_loop.raw_remote(),
|
remote: event_loop.raw_remote(),
|
||||||
fetch: fetch.clone(),
|
fetch: fetch.clone(),
|
||||||
@ -289,7 +297,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
|
|||||||
};
|
};
|
||||||
|
|
||||||
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
|
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
|
||||||
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, dapps_deps)?;
|
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, &cmd.ui_conf.ntp_server, dapps_deps)?;
|
||||||
|
|
||||||
// start RPCs
|
// start RPCs
|
||||||
let dapps_service = dapps::service(&dapps_middleware);
|
let dapps_service = dapps::service(&dapps_middleware);
|
||||||
@ -316,6 +324,11 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
|
|||||||
apis: deps_for_rpc_apis.clone(),
|
apis: deps_for_rpc_apis.clone(),
|
||||||
remote: event_loop.raw_remote(),
|
remote: event_loop.raw_remote(),
|
||||||
stats: rpc_stats.clone(),
|
stats: rpc_stats.clone(),
|
||||||
|
pool: if cmd.http_conf.processing_threads > 0 {
|
||||||
|
Some(rpc::CpuPool::new(cmd.http_conf.processing_threads))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// start rpc servers
|
// start rpc servers
|
||||||
@ -581,7 +594,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
|||||||
let (sync_provider, manage_network, chain_notify) = modules::sync(
|
let (sync_provider, manage_network, chain_notify) = modules::sync(
|
||||||
&mut hypervisor,
|
&mut hypervisor,
|
||||||
sync_config,
|
sync_config,
|
||||||
net_conf.into(),
|
net_conf.clone().into(),
|
||||||
client.clone(),
|
client.clone(),
|
||||||
snapshot_service.clone(),
|
snapshot_service.clone(),
|
||||||
client.clone(),
|
client.clone(),
|
||||||
@ -625,8 +638,20 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
|||||||
let (sync, client) = (sync_provider.clone(), client.clone());
|
let (sync, client) = (sync_provider.clone(), client.clone());
|
||||||
let contract_client = Arc::new(::dapps::FullRegistrar { client: client.clone() });
|
let contract_client = Arc::new(::dapps::FullRegistrar { client: client.clone() });
|
||||||
|
|
||||||
|
struct SyncStatus(Arc<ethsync::SyncProvider>, Arc<Client>, ethsync::NetworkConfiguration);
|
||||||
|
impl dapps::SyncStatus for SyncStatus {
|
||||||
|
fn is_major_importing(&self) -> bool {
|
||||||
|
is_major_importing(Some(self.0.status().state), self.1.queue_info())
|
||||||
|
}
|
||||||
|
fn peers(&self) -> (usize, usize) {
|
||||||
|
let status = self.0.status();
|
||||||
|
(status.num_peers, status.current_max_peers(self.2.min_peers, self.2.max_peers) as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dapps::Dependencies {
|
dapps::Dependencies {
|
||||||
sync_status: Arc::new(move || is_major_importing(Some(sync.status().state), client.queue_info())),
|
sync_status: Arc::new(SyncStatus(sync, client, net_conf)),
|
||||||
|
pool: fetch.pool(),
|
||||||
contract_client: contract_client,
|
contract_client: contract_client,
|
||||||
remote: event_loop.raw_remote(),
|
remote: event_loop.raw_remote(),
|
||||||
fetch: fetch.clone(),
|
fetch: fetch.clone(),
|
||||||
@ -635,7 +660,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
|
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps.clone())?;
|
||||||
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, dapps_deps)?;
|
let ui_middleware = dapps::new_ui(cmd.ui_conf.enabled, &cmd.ui_conf.ntp_server, dapps_deps)?;
|
||||||
|
|
||||||
let dapps_service = dapps::service(&dapps_middleware);
|
let dapps_service = dapps::service(&dapps_middleware);
|
||||||
let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies {
|
let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies {
|
||||||
@ -663,6 +688,12 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
|||||||
apis: deps_for_rpc_apis.clone(),
|
apis: deps_for_rpc_apis.clone(),
|
||||||
remote: event_loop.raw_remote(),
|
remote: event_loop.raw_remote(),
|
||||||
stats: rpc_stats.clone(),
|
stats: rpc_stats.clone(),
|
||||||
|
pool: if cmd.http_conf.processing_threads > 0 {
|
||||||
|
Some(rpc::CpuPool::new(cmd.http_conf.processing_threads))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// start rpc servers
|
// start rpc servers
|
||||||
|
@ -10,6 +10,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
cid = "0.2"
|
cid = "0.2"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
|
futures-cpupool = "0.1"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
multihash ="0.6"
|
multihash ="0.6"
|
||||||
order-stat = "0.1"
|
order-stat = "0.1"
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
extern crate cid;
|
extern crate cid;
|
||||||
extern crate crypto as rust_crypto;
|
extern crate crypto as rust_crypto;
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
|
extern crate futures_cpupool;
|
||||||
extern crate multihash;
|
extern crate multihash;
|
||||||
extern crate order_stat;
|
extern crate order_stat;
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
@ -217,18 +217,26 @@ impl<M: core::Middleware<Metadata>> WsDispatcher<M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<M: core::Middleware<Metadata>> core::Middleware<Metadata> for WsDispatcher<M> {
|
impl<M: core::Middleware<Metadata>> core::Middleware<Metadata> for WsDispatcher<M> {
|
||||||
fn on_request<F>(&self, request: core::Request, meta: Metadata, process: F) -> core::FutureResponse where
|
type Future = core::futures::future::Either<
|
||||||
F: FnOnce(core::Request, Metadata) -> core::FutureResponse,
|
M::Future,
|
||||||
|
core::FutureResponse,
|
||||||
|
>;
|
||||||
|
|
||||||
|
fn on_request<F, X>(&self, request: core::Request, meta: Metadata, process: F) -> Self::Future where
|
||||||
|
F: FnOnce(core::Request, Metadata) -> X,
|
||||||
|
X: core::futures::Future<Item=Option<core::Response>, Error=()> + Send + 'static,
|
||||||
{
|
{
|
||||||
|
use self::core::futures::future::Either::{A, B};
|
||||||
|
|
||||||
let use_full = match &meta.origin {
|
let use_full = match &meta.origin {
|
||||||
&Origin::Signer { .. } => true,
|
&Origin::Signer { .. } => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if use_full {
|
if use_full {
|
||||||
self.full_handler.handle_rpc_request(request, meta)
|
A(self.full_handler.handle_rpc_request(request, meta))
|
||||||
} else {
|
} else {
|
||||||
process(request, meta)
|
B(process(request, meta).boxed())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ mod codes {
|
|||||||
pub const NO_WORK: i64 = -32001;
|
pub const NO_WORK: i64 = -32001;
|
||||||
pub const NO_AUTHOR: i64 = -32002;
|
pub const NO_AUTHOR: i64 = -32002;
|
||||||
pub const NO_NEW_WORK: i64 = -32003;
|
pub const NO_NEW_WORK: i64 = -32003;
|
||||||
|
pub const NO_WORK_REQUIRED: i64 = -32004;
|
||||||
pub const UNKNOWN_ERROR: i64 = -32009;
|
pub const UNKNOWN_ERROR: i64 = -32009;
|
||||||
pub const TRANSACTION_ERROR: i64 = -32010;
|
pub const TRANSACTION_ERROR: i64 = -32010;
|
||||||
pub const EXECUTION_ERROR: i64 = -32015;
|
pub const EXECUTION_ERROR: i64 = -32015;
|
||||||
@ -133,7 +134,7 @@ pub fn state_pruned() -> Error {
|
|||||||
Error {
|
Error {
|
||||||
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
|
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
|
||||||
message: "This request is not supported because your node is running with state pruning. Run with --pruning=archive.".into(),
|
message: "This request is not supported because your node is running with state pruning. Run with --pruning=archive.".into(),
|
||||||
data: None
|
data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +146,7 @@ pub fn exceptional() -> Error {
|
|||||||
Error {
|
Error {
|
||||||
code: ErrorCode::ServerError(codes::EXCEPTION_ERROR),
|
code: ErrorCode::ServerError(codes::EXCEPTION_ERROR),
|
||||||
message: "The execution failed due to an exception.".into(),
|
message: "The execution failed due to an exception.".into(),
|
||||||
data: None
|
data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +154,7 @@ pub fn no_work() -> Error {
|
|||||||
Error {
|
Error {
|
||||||
code: ErrorCode::ServerError(codes::NO_WORK),
|
code: ErrorCode::ServerError(codes::NO_WORK),
|
||||||
message: "Still syncing.".into(),
|
message: "Still syncing.".into(),
|
||||||
data: None
|
data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +162,7 @@ pub fn no_new_work() -> Error {
|
|||||||
Error {
|
Error {
|
||||||
code: ErrorCode::ServerError(codes::NO_NEW_WORK),
|
code: ErrorCode::ServerError(codes::NO_NEW_WORK),
|
||||||
message: "Work has not changed.".into(),
|
message: "Work has not changed.".into(),
|
||||||
data: None
|
data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +170,15 @@ pub fn no_author() -> Error {
|
|||||||
Error {
|
Error {
|
||||||
code: ErrorCode::ServerError(codes::NO_AUTHOR),
|
code: ErrorCode::ServerError(codes::NO_AUTHOR),
|
||||||
message: "Author not configured. Run Parity with --author to configure.".into(),
|
message: "Author not configured. Run Parity with --author to configure.".into(),
|
||||||
data: None
|
data: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn no_work_required() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(codes::NO_WORK_REQUIRED),
|
||||||
|
message: "External work is only required for Proof of Work engines.".into(),
|
||||||
|
data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_mining(&self) -> Result<bool, Error> {
|
fn is_mining(&self) -> Result<bool, Error> {
|
||||||
Ok(self.miner.is_sealing())
|
Ok(self.miner.is_currently_sealing())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hashrate(&self) -> Result<RpcU256, Error> {
|
fn hashrate(&self) -> Result<RpcU256, Error> {
|
||||||
@ -553,6 +553,11 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn work(&self, no_new_work_timeout: Trailing<u64>) -> Result<Work, Error> {
|
fn work(&self, no_new_work_timeout: Trailing<u64>) -> Result<Work, Error> {
|
||||||
|
if !self.miner.can_produce_work_package() {
|
||||||
|
warn!(target: "miner", "Cannot give work package - engine seals internally.");
|
||||||
|
return Err(errors::no_work_required())
|
||||||
|
}
|
||||||
|
|
||||||
let no_new_work_timeout = no_new_work_timeout.unwrap_or_default();
|
let no_new_work_timeout = no_new_work_timeout.unwrap_or_default();
|
||||||
|
|
||||||
// check if we're still syncing and return empty strings in that case
|
// check if we're still syncing and return empty strings in that case
|
||||||
@ -602,6 +607,11 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn submit_work(&self, nonce: RpcH64, pow_hash: RpcH256, mix_hash: RpcH256) -> Result<bool, Error> {
|
fn submit_work(&self, nonce: RpcH64, pow_hash: RpcH256, mix_hash: RpcH256) -> Result<bool, Error> {
|
||||||
|
if !self.miner.can_produce_work_package() {
|
||||||
|
warn!(target: "miner", "Cannot submit work - engine seals internally.");
|
||||||
|
return Err(errors::no_work_required())
|
||||||
|
}
|
||||||
|
|
||||||
let nonce: H64 = nonce.into();
|
let nonce: H64 = nonce.into();
|
||||||
let pow_hash: H256 = pow_hash.into();
|
let pow_hash: H256 = pow_hash.into();
|
||||||
let mix_hash: H256 = mix_hash.into();
|
let mix_hash: H256 = mix_hash.into();
|
||||||
|
@ -21,10 +21,13 @@ use std::sync::Arc;
|
|||||||
use std::sync::atomic::{self, AtomicUsize};
|
use std::sync::atomic::{self, AtomicUsize};
|
||||||
use std::time;
|
use std::time;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
|
use futures_cpupool as pool;
|
||||||
use jsonrpc_core as rpc;
|
use jsonrpc_core as rpc;
|
||||||
use order_stat;
|
use order_stat;
|
||||||
use util::RwLock;
|
use util::RwLock;
|
||||||
|
|
||||||
|
pub use self::pool::CpuPool;
|
||||||
|
|
||||||
const RATE_SECONDS: usize = 10;
|
const RATE_SECONDS: usize = 10;
|
||||||
const STATS_SAMPLES: usize = 60;
|
const STATS_SAMPLES: usize = 60;
|
||||||
|
|
||||||
@ -184,14 +187,16 @@ pub trait ActivityNotifier: Send + Sync + 'static {
|
|||||||
pub struct Middleware<T: ActivityNotifier = ClientNotifier> {
|
pub struct Middleware<T: ActivityNotifier = ClientNotifier> {
|
||||||
stats: Arc<RpcStats>,
|
stats: Arc<RpcStats>,
|
||||||
notifier: T,
|
notifier: T,
|
||||||
|
pool: Option<CpuPool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ActivityNotifier> Middleware<T> {
|
impl<T: ActivityNotifier> Middleware<T> {
|
||||||
/// Create new Middleware with stats counter and activity notifier.
|
/// Create new Middleware with stats counter and activity notifier.
|
||||||
pub fn new(stats: Arc<RpcStats>, notifier: T) -> Self {
|
pub fn new(stats: Arc<RpcStats>, notifier: T, pool: Option<CpuPool>) -> Self {
|
||||||
Middleware {
|
Middleware {
|
||||||
stats: stats,
|
stats,
|
||||||
notifier: notifier,
|
notifier,
|
||||||
|
pool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,19 +206,32 @@ impl<T: ActivityNotifier> Middleware<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<M: rpc::Metadata, T: ActivityNotifier> rpc::Middleware<M> for Middleware<T> {
|
impl<M: rpc::Metadata, T: ActivityNotifier> rpc::Middleware<M> for Middleware<T> {
|
||||||
fn on_request<F>(&self, request: rpc::Request, meta: M, process: F) -> rpc::FutureResponse where
|
type Future = rpc::futures::future::Either<
|
||||||
F: FnOnce(rpc::Request, M) -> rpc::FutureResponse,
|
pool::CpuFuture<Option<rpc::Response>, ()>,
|
||||||
|
rpc::FutureResponse,
|
||||||
|
>;
|
||||||
|
|
||||||
|
fn on_request<F, X>(&self, request: rpc::Request, meta: M, process: F) -> Self::Future where
|
||||||
|
F: FnOnce(rpc::Request, M) -> X,
|
||||||
|
X: rpc::futures::Future<Item=Option<rpc::Response>, Error=()> + Send + 'static,
|
||||||
{
|
{
|
||||||
|
use self::rpc::futures::future::Either::{A, B};
|
||||||
|
|
||||||
let start = time::Instant::now();
|
let start = time::Instant::now();
|
||||||
let response = process(request, meta);
|
|
||||||
|
|
||||||
self.notifier.active();
|
self.notifier.active();
|
||||||
|
self.stats.count_request();
|
||||||
|
|
||||||
let stats = self.stats.clone();
|
let stats = self.stats.clone();
|
||||||
stats.count_request();
|
let future = process(request, meta).map(move |res| {
|
||||||
response.map(move |res| {
|
|
||||||
stats.add_roundtrip(Self::as_micro(start.elapsed()));
|
stats.add_roundtrip(Self::as_micro(start.elapsed()));
|
||||||
res
|
res
|
||||||
}).boxed()
|
});
|
||||||
|
|
||||||
|
match self.pool {
|
||||||
|
Some(ref pool) => A(pool.spawn(future)),
|
||||||
|
None => B(future.boxed()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +207,11 @@ impl MinerService for TestMinerService {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// PoW chain - can produce work package
|
||||||
|
fn can_produce_work_package(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// New chain head event. Restart mining operation.
|
/// New chain head event. Restart mining operation.
|
||||||
fn update_sealing(&self, _chain: &MiningBlockChainClient) {
|
fn update_sealing(&self, _chain: &MiningBlockChainClient) {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
@ -265,7 +270,7 @@ impl MinerService for TestMinerService {
|
|||||||
self.last_nonces.read().get(address).cloned()
|
self.last_nonces.read().get(address).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_sealing(&self) -> bool {
|
fn is_currently_sealing(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +126,11 @@ impl Client {
|
|||||||
*self.client.write() = (time::Instant::now(), client.clone());
|
*self.client.write() = (time::Instant::now(), client.clone());
|
||||||
Ok(client)
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a handle to underlying CpuPool of this client.
|
||||||
|
pub fn pool(&self) -> CpuPool {
|
||||||
|
self.pool.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fetch for Client {
|
impl Fetch for Client {
|
||||||
@ -204,6 +209,15 @@ pub enum Error {
|
|||||||
Aborted,
|
Aborted,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Error::Aborted => write!(fmt, "The request has been aborted."),
|
||||||
|
Error::Fetch(ref err) => write!(fmt, "{}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<reqwest::Error> for Error {
|
impl From<reqwest::Error> for Error {
|
||||||
fn from(error: reqwest::Error) -> Self {
|
fn from(error: reqwest::Error) -> Self {
|
||||||
Error::Fetch(error)
|
Error::Fetch(error)
|
||||||
|
Loading…
Reference in New Issue
Block a user