Merge branch 'master' into ui-2
This commit is contained in:
commit
439eb294bb
386
Cargo.lock
generated
386
Cargo.lock
generated
@ -39,11 +39,6 @@ name = "ansi_term"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "antidote"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "app_dirs"
|
||||
version = "1.1.1"
|
||||
@ -292,6 +287,14 @@ dependencies = [
|
||||
"vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
version = "0.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"clippy_lints 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
version = "0.0.103"
|
||||
@ -309,6 +312,20 @@ dependencies = [
|
||||
"clippy_lints 0.0.163 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clippy_lints"
|
||||
version = "0.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clippy_lints"
|
||||
version = "0.0.103"
|
||||
@ -553,7 +570,6 @@ name = "ethcore"
|
||||
version = "1.8.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bloomable 0.1.0",
|
||||
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bn 0.4.4 (git+https://github.com/paritytech/bn)",
|
||||
@ -561,8 +577,6 @@ dependencies = [
|
||||
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"common-types 0.1.0",
|
||||
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethash 1.8.0",
|
||||
"ethcore-bigint 0.1.3",
|
||||
"ethcore-bloom-journal 0.1.0",
|
||||
@ -579,7 +593,7 @@ dependencies = [
|
||||
"ethkey 0.2.0",
|
||||
"ethstore 0.1.0",
|
||||
"evm 0.1.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hardware-wallet 1.8.0",
|
||||
"hash 0.1.0",
|
||||
"hashdb 0.1.0",
|
||||
@ -587,7 +601,7 @@ dependencies = [
|
||||
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
|
||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memorydb 0.1.0",
|
||||
@ -605,7 +619,6 @@ dependencies = [
|
||||
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semantic_version 0.1.0",
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stats 0.1.0",
|
||||
"table 0.1.0",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -735,7 +748,7 @@ dependencies = [
|
||||
"ethcore-network 1.8.0",
|
||||
"ethcore-util 1.8.0",
|
||||
"evm 0.1.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hash 0.1.0",
|
||||
"heapsize 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -820,7 +833,7 @@ dependencies = [
|
||||
"ethcore-util 1.8.0",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-cpupool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hash 0.1.0",
|
||||
"hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -852,16 +865,14 @@ dependencies = [
|
||||
"ethcore-ipc-nano 1.8.0",
|
||||
"ethcore-logger 1.8.0",
|
||||
"ethcore-util 1.8.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hash 0.1.0",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-tcp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-tcp-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1077,12 +1088,11 @@ dependencies = [
|
||||
name = "fetch"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-cpupool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1116,7 +1126,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.1.14"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -1124,7 +1134,7 @@ name = "futures-cpupool"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -1269,13 +1279,39 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-native-tls"
|
||||
version = "0.2.4"
|
||||
name = "hyper"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-cpupool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1363,10 +1399,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-core"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
version = "8.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8#cf6f3481760f6ee8fbef7a987954ffc720ff4acf"
|
||||
dependencies = [
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1375,25 +1411,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-http-server"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
version = "8.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8#cf6f3481760f6ee8fbef7a987954ffc720ff4acf"
|
||||
dependencies = [
|
||||
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-server-utils 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-ipc-server"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
version = "8.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8#cf6f3481760f6ee8fbef7a987954ffc720ff4acf"
|
||||
dependencies = [
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-server-utils 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)",
|
||||
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1401,47 +1436,32 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-macros"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
version = "8.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8#cf6f3481760f6ee8fbef7a987954ffc720ff4acf"
|
||||
dependencies = [
|
||||
"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-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-minihttp-server"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
dependencies = [
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-minihttp 0.1.0 (git+https://github.com/tomusdrw/tokio-minihttp)",
|
||||
"tokio-proto 0.1.0 (git+https://github.com/tomusdrw/tokio-proto)",
|
||||
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-pubsub"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
version = "8.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8#cf6f3481760f6ee8fbef7a987954ffc720ff4acf"
|
||||
dependencies = [
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-server-utils"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
version = "8.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8#cf6f3481760f6ee8fbef7a987954ffc720ff4acf"
|
||||
dependencies = [
|
||||
"bytes 0.4.5 (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 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1449,25 +1469,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-tcp-server"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
version = "8.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8#cf6f3481760f6ee8fbef7a987954ffc720ff4acf"
|
||||
dependencies = [
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-server-utils 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-ws-server"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#b5490782884218c5ccf74cd61e54904cb3a3aeed"
|
||||
version = "8.0.0"
|
||||
source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8#cf6f3481760f6ee8fbef7a987954ffc720ff4acf"
|
||||
dependencies = [
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-server-utils 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1533,12 +1551,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.3.0"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -1634,17 +1652,6 @@ dependencies = [
|
||||
"unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.0-alpha.2"
|
||||
@ -1775,7 +1782,7 @@ dependencies = [
|
||||
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-bigint 0.1.3",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-contract-generator 0.1.0",
|
||||
]
|
||||
|
||||
@ -1828,7 +1835,7 @@ dependencies = [
|
||||
"ethcore-io 1.8.0",
|
||||
"ethcore-network 1.8.0",
|
||||
"ethcore-util 1.8.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-contracts 0.1.0",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1838,7 +1845,7 @@ dependencies = [
|
||||
name = "node-health"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-cpupool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ntp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2053,12 +2060,12 @@ dependencies = [
|
||||
"ethkey 0.2.0",
|
||||
"ethsync 1.8.0",
|
||||
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-cpupool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hash 0.1.0",
|
||||
"ipnetwork 0.12.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"isatty 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"node-filter 1.8.0",
|
||||
"node-health 0.1.0",
|
||||
@ -2105,15 +2112,15 @@ dependencies = [
|
||||
"ethcore-devtools 1.8.0",
|
||||
"ethcore-util 1.8.0",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-cpupool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hash 0.1.0",
|
||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 1.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"node-health 0.1.0",
|
||||
"parity-dapps-glue 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-hash-fetch 1.8.0",
|
||||
@ -2125,12 +2132,25 @@ dependencies = [
|
||||
"serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zip 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-dapps-glue"
|
||||
version = "1.8.0"
|
||||
dependencies = [
|
||||
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quasi_macros 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntex_syntax 0.58.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-dapps-glue"
|
||||
version = "1.8.0"
|
||||
@ -2154,11 +2174,11 @@ dependencies = [
|
||||
"ethcore-bytes 0.1.0",
|
||||
"ethcore-util 1.8.0",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hash 0.1.0",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 1.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-contracts 0.1.0",
|
||||
"parity-reactor 0.1.0",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2175,10 +2195,11 @@ dependencies = [
|
||||
"ethcore-bigint 0.1.3",
|
||||
"ethcore-bytes 0.1.0",
|
||||
"ethcore-util 1.8.0",
|
||||
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"multihash 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rlp 0.2.0",
|
||||
"unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2208,7 +2229,7 @@ dependencies = [
|
||||
name = "parity-reactor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -2236,18 +2257,16 @@ dependencies = [
|
||||
"ethstore 0.1.0",
|
||||
"ethsync 1.8.0",
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures-cpupool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hardware-wallet 1.8.0",
|
||||
"hash 0.1.0",
|
||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-minihttp-server 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-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-ipc-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-ws-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multihash 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"node-health 0.1.0",
|
||||
@ -2275,19 +2294,16 @@ dependencies = [
|
||||
name = "parity-rpc-client"
|
||||
version = "1.4.0"
|
||||
dependencies = [
|
||||
"ethcore-util 1.8.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hash 0.1.0",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-ws-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-rpc 1.8.0",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -2297,7 +2313,7 @@ version = "0.1.5"
|
||||
source = "git+https://github.com/nikvolf/parity-tokio-ipc#d6c5b3cfcc913a1b9cf0f0562a10b083ceb9fb7c"
|
||||
dependencies = [
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)",
|
||||
"miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2327,7 +2343,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "parity-ui-precompiled"
|
||||
version = "1.4.0"
|
||||
source = "git+https://github.com/paritytech/js-precompiled.git#6916f0be0012898f29d3e949a41be0daafee08af"
|
||||
source = "git+https://github.com/paritytech/js-precompiled.git#aaa1f3610aa39ce4b5ebb23debce39e5368edbbd"
|
||||
dependencies = [
|
||||
"parity-dapps-glue 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -2344,7 +2360,7 @@ dependencies = [
|
||||
"ethcore-ipc-codegen 1.8.0",
|
||||
"ethcore-util 1.8.0",
|
||||
"ethsync 1.8.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ipc-common-types 1.8.0",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-hash-fetch 1.8.0",
|
||||
@ -2374,11 +2390,10 @@ dependencies = [
|
||||
"ethcore-network 1.8.0",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
|
||||
"jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2518,7 +2533,7 @@ name = "price-info"
|
||||
version = "1.7.0"
|
||||
dependencies = [
|
||||
"fetch 0.1.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2662,7 +2677,7 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -2698,16 +2713,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.6.2"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper-native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libflate 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -2802,7 +2823,7 @@ version = "1.4.0"
|
||||
dependencies = [
|
||||
"bigint 4.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ethcore-util 1.8.0",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-rpc 1.8.0",
|
||||
"parity-rpc-client 1.4.0",
|
||||
"rpassword 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -3228,7 +3249,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -3243,61 +3264,28 @@ version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-minihttp"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/tomusdrw/tokio-minihttp#67a400060bd29e51beaf206c552845255b6f699f"
|
||||
dependencies = [
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-proto 0.1.0 (git+https://github.com/tomusdrw/tokio-proto)",
|
||||
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-named-pipes"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/nikvolf/tokio-named-pipes#0b9b728eaeb0a6673c287ac7692be398fd651752"
|
||||
dependencies = [
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)",
|
||||
"tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-proto"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/tomusdrw/tokio-proto#f6ee08cb594fa2fc1b4178eaaca0855d66e68fd3"
|
||||
dependencies = [
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-proto"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -3314,7 +3302,7 @@ name = "tokio-service"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3322,17 +3310,28 @@ name = "tokio-timer"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tls"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-uds"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -3622,7 +3621,6 @@ dependencies = [
|
||||
"checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a"
|
||||
"checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699"
|
||||
"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
|
||||
"checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5"
|
||||
"checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4"
|
||||
"checksum arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "699e63a93b79d717e8c3b5eb1b28b7780d0d6d9e59a72eb769291c83b0c8dc67"
|
||||
"checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0"
|
||||
@ -3653,8 +3651,10 @@ dependencies = [
|
||||
"checksum clap 2.26.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3451e409013178663435d6f15fdb212f14ee4424a3d74f979d081d0a66b6f1f2"
|
||||
"checksum clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "5b4fabf979ddf6419a313c1c0ada4a5b95cfd2049c56e8418d622d27b4b6ff32"
|
||||
"checksum clippy 0.0.163 (registry+https://github.com/rust-lang/crates.io-index)" = "5ad3f3dc94d81a6505eb28bf545b501fc9d7525ee9864df5a4b2b6d82629f038"
|
||||
"checksum clippy 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "d19bda68c3db98e3a780342f6101b44312fef20a5f13ce756d1202a35922b01b"
|
||||
"checksum clippy_lints 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "ce96ec05bfe018a0d5d43da115e54850ea2217981ff0f2e462780ab9d594651a"
|
||||
"checksum clippy_lints 0.0.163 (registry+https://github.com/rust-lang/crates.io-index)" = "c058b299bb1289c7e8c063bd49477715c91cb3c3344bcf2e25326860b0675654"
|
||||
"checksum clippy_lints 0.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4ed67c69b9bb35169be2538691d290a3aa0cbfd4b9f0bfb7c221fc1d399a96"
|
||||
"checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd"
|
||||
"checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299"
|
||||
"checksum cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d53b80dde876f47f03cda35303e368a79b91c70b0d65ecba5fd5280944a08591"
|
||||
@ -3682,7 +3682,7 @@ dependencies = [
|
||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
"checksum foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d"
|
||||
"checksum fs2 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ab76cfd2aaa59b7bf6688ad9ba15bbae64bff97f04ea02144cfd3443e5c2866"
|
||||
"checksum futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4b63a4792d4f8f686defe3b39b92127fea6344de5d38202b2ee5a11bbbf29d6a"
|
||||
"checksum futures 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "05a23db7bd162d4e8265968602930c476f688f0c180b44bdaf55e0cb2c687558"
|
||||
"checksum futures-cpupool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "77d49e7de8b91b20d6fda43eea906637eff18b96702eb6b2872df8bfab1ad2b5"
|
||||
"checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
|
||||
"checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9"
|
||||
@ -3696,7 +3696,8 @@ dependencies = [
|
||||
"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
|
||||
"checksum hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)" = "<none>"
|
||||
"checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
|
||||
"checksum hyper-native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72332e4a35d3059583623b50e98e491b78f8b96c5521fcb3f428167955aa56e8"
|
||||
"checksum hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "641abc3e3fcf0de41165595f801376e01106bca1fd876dda937730e477ca004c"
|
||||
"checksum hyper-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c81fa95203e2a6087242c38691a0210f23e9f3f8f944350bd676522132e2985"
|
||||
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
|
||||
"checksum igd 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "356a0dc23a4fa0f8ce4777258085d00a01ea4923b2efd93538fc44bf5e1bda76"
|
||||
"checksum integer-encoding 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a053c9c7dcb7db1f2aa012c37dc176c62e4cdf14898dee0eecc606de835b8acb"
|
||||
@ -3706,15 +3707,14 @@ dependencies = [
|
||||
"checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc"
|
||||
"checksum itertools 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ab4d6a273c31ef276c917019239588b23bc696f277af8db10742cba3c27ec2f0"
|
||||
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
|
||||
"checksum jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum jsonrpc-minihttp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum jsonrpc-tcp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "<none>"
|
||||
"checksum jsonrpc-core 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)" = "<none>"
|
||||
"checksum jsonrpc-http-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)" = "<none>"
|
||||
"checksum jsonrpc-ipc-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)" = "<none>"
|
||||
"checksum jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)" = "<none>"
|
||||
"checksum jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)" = "<none>"
|
||||
"checksum jsonrpc-server-utils 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)" = "<none>"
|
||||
"checksum jsonrpc-tcp-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)" = "<none>"
|
||||
"checksum jsonrpc-ws-server 8.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.8)" = "<none>"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||
"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
|
||||
@ -3723,8 +3723,8 @@ dependencies = [
|
||||
"checksum libflate 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "a2aa04ec0100812d31a5366130ff9e793291787bc31da845bede4a00ea329830"
|
||||
"checksum libusb 0.3.0 (git+https://github.com/paritytech/libusb-rs)" = "<none>"
|
||||
"checksum libusb-sys 0.2.3 (git+https://github.com/paritytech/libusb-sys)" = "<none>"
|
||||
"checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd"
|
||||
"checksum linked-hash-map 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7860ec297f7008ff7a1e3382d7f7e1dcd69efc94751a2284bafc3d013c2aa939"
|
||||
"checksum linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2aab0478615bb586559b0114d94dd8eca4fdbb73b443adcb0d00b61692b4bf"
|
||||
"checksum local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1ceb20f39ff7ae42f3ff9795f3986b1daad821caaa1e1732a0944103a5a1a66"
|
||||
"checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
|
||||
"checksum lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4d06ff7ff06f729ce5f4e227876cb88d10bc59cd4ae1e09fbb2bde15c850dc21"
|
||||
@ -3735,7 +3735,6 @@ dependencies = [
|
||||
"checksum memmap 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46f3c7359028b31999287dae4e5047ddfe90a23b7dca2282ce759b491080c99b"
|
||||
"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
|
||||
"checksum mime 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e3d709ffbb330e1566dc2f2a3c9b58a5ad4a381f740b810cd305dc3f089bc160"
|
||||
"checksum mime_guess 1.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bbee1a836f344ac39d4a59bfe7be2bd3150353ff71678afb740216f8270b333e"
|
||||
"checksum mime_guess 2.0.0-alpha.2 (registry+https://github.com/rust-lang/crates.io-index)" = "27a5e6679a0614e25adc14c6434ba84e41632b765a6d9cb2031a0cca682699ae"
|
||||
"checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4"
|
||||
"checksum mio 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "dbd91d3bfbceb13897065e97b2ef177a09a438cb33612b2d371bf568819a9313"
|
||||
@ -3805,7 +3804,7 @@ dependencies = [
|
||||
"checksum regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1731164734096285ec2a5ec7fea5248ae2f5485b3feeb0115af4fda2183b2d1b"
|
||||
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
"checksum regex-syntax 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad890a5eef7953f55427c50575c680c42841653abd2b028b68cd223d157f62db"
|
||||
"checksum reqwest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1d56dbe269dbe19d716b76ec8c3efce8ef84e974f5b7e5527463e8c0507d4e17"
|
||||
"checksum reqwest 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5866613d84e2a39c0479a960bf2d0eff1fbfc934f02cd42b5c08c1e1efc5b1fd"
|
||||
"checksum ring 0.9.7 (registry+https://github.com/rust-lang/crates.io-index)" = "24293de46bac74c9b9c05b40ff8496bbc8b9ae242a9b89f754e1154a43bc7c4c"
|
||||
"checksum rocksdb 0.4.5 (git+https://github.com/paritytech/rust-rocksdb)" = "<none>"
|
||||
"checksum rocksdb-sys 0.3.0 (git+https://github.com/paritytech/rust-rocksdb)" = "<none>"
|
||||
@ -3866,12 +3865,11 @@ dependencies = [
|
||||
"checksum tiny-keccak 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52d12ad79e4063e0cb0ca5efa202ed7244b6ce4d25f4d3abe410b2a66128292"
|
||||
"checksum tokio-core 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e85d419699ec4b71bfe35bbc25bb8771e52eff0471a7f75c853ad06e200b4f86"
|
||||
"checksum tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ab83e7adb5677e42e405fa4ceff75659d93c4d7d7dd22f52fcec59ee9f02af"
|
||||
"checksum tokio-minihttp 0.1.0 (git+https://github.com/tomusdrw/tokio-minihttp)" = "<none>"
|
||||
"checksum tokio-named-pipes 0.1.0 (git+https://github.com/nikvolf/tokio-named-pipes)" = "<none>"
|
||||
"checksum tokio-proto 0.1.0 (git+https://github.com/tomusdrw/tokio-proto)" = "<none>"
|
||||
"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
|
||||
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
|
||||
"checksum tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc"
|
||||
"checksum tokio-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d88e411cac1c87e405e4090be004493c5d8072a370661033b1a64ea205ec2e13"
|
||||
"checksum tokio-uds 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6116c71be48f8f1656551fd16458247fdd6c03201d7893ad81189055fcde03e8"
|
||||
"checksum toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "0590d72182e50e879c4da3b11c6488dae18fccb1ae0c7a3eda18e16795844796"
|
||||
"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e"
|
||||
|
@ -31,7 +31,7 @@ futures-cpupool = "0.1"
|
||||
fdlimit = "0.1"
|
||||
ws2_32-sys = "0.2"
|
||||
ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" }
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
ethsync = { path = "sync" }
|
||||
ethcore = { path = "ethcore" }
|
||||
ethcore-util = { path = "util" }
|
||||
@ -117,4 +117,4 @@ lto = false
|
||||
panic = "abort"
|
||||
|
||||
[workspace]
|
||||
members = ["ethstore/cli", "ethkey/cli", "evmbin", "whisper", "chainspec", "dapps/node-health"]
|
||||
members = ["ethstore/cli", "ethkey/cli", "evmbin", "whisper", "chainspec", "dapps/js-glue"]
|
||||
|
@ -10,25 +10,23 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
base32 = "0.3"
|
||||
futures = "0.1"
|
||||
linked-hash-map = "0.3"
|
||||
futures-cpupool = "0.1"
|
||||
linked-hash-map = "0.5"
|
||||
log = "0.3"
|
||||
parity-dapps-glue = "1.7"
|
||||
parity-dapps-glue = "1.8"
|
||||
parking_lot = "0.4"
|
||||
mime = "0.2"
|
||||
mime_guess = "1.6.1"
|
||||
mime_guess = "2.0.0-alpha.2"
|
||||
rand = "0.3"
|
||||
rustc-hex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
time = "0.1.35"
|
||||
unicase = "1.3"
|
||||
url = "1.0"
|
||||
unicase = "1.4"
|
||||
zip = { version = "0.1", default-features = false }
|
||||
itertools = "0.5"
|
||||
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
|
||||
ethcore-util = { path = "../util" }
|
||||
ethcore-bigint = { path = "../util/bigint" }
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
description = "Base Package for all Parity built-in dapps"
|
||||
name = "parity-dapps-glue"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
@ -12,7 +12,7 @@ syntex = { version = "0.58", optional = true }
|
||||
|
||||
[dependencies]
|
||||
glob = { version = "0.2.11" }
|
||||
mime_guess = { version = "1.6.1" }
|
||||
mime_guess = { version = "2.0.0-alpha.2" }
|
||||
aster = { version = "0.41", default-features = false }
|
||||
quasi = { version = "0.32", default-features = false }
|
||||
quasi_macros = { version = "0.32", optional = true }
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time;
|
||||
use futures::{Future, BoxFuture};
|
||||
use futures::Future;
|
||||
use futures::sync::oneshot;
|
||||
use types::{HealthInfo, HealthStatus, Health};
|
||||
use time::{TimeChecker, MAX_DRIFT};
|
||||
@ -44,7 +44,7 @@ impl NodeHealth {
|
||||
}
|
||||
|
||||
/// Query latest health report.
|
||||
pub fn health(&self) -> BoxFuture<Health, ()> {
|
||||
pub fn health(&self) -> Box<Future<Item = Health, Error = ()> + Send> {
|
||||
trace!(target: "dapps", "Checking node health.");
|
||||
// Check timediff
|
||||
let sync_status = self.sync_status.clone();
|
||||
@ -63,7 +63,7 @@ impl NodeHealth {
|
||||
},
|
||||
);
|
||||
|
||||
rx.map_err(|err| {
|
||||
Box::new(rx.map_err(|err| {
|
||||
warn!(target: "dapps", "Health request cancelled: {:?}", err);
|
||||
}).and_then(move |time| {
|
||||
// Check peers
|
||||
@ -117,6 +117,6 @@ impl NodeHealth {
|
||||
};
|
||||
|
||||
Ok(Health { peers, sync, time})
|
||||
}).boxed()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ use std::collections::VecDeque;
|
||||
use std::sync::atomic::{self, AtomicUsize};
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::{self, Future, BoxFuture};
|
||||
use futures::{self, Future};
|
||||
use futures::future::{self, IntoFuture};
|
||||
use futures_cpupool::{CpuPool, CpuFuture};
|
||||
use ntp;
|
||||
@ -195,6 +195,8 @@ const UPDATE_TIMEOUT_INCOMPLETE_SECS: u64 = 10;
|
||||
/// Maximal valid time drift.
|
||||
pub const MAX_DRIFT: i64 = 500;
|
||||
|
||||
type BoxFuture<A, B> = Box<Future<Item = A, Error = B> + Send>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A time checker.
|
||||
pub struct TimeChecker<N: Ntp = SimpleNtp> {
|
||||
@ -224,7 +226,7 @@ impl<N: Ntp> TimeChecker<N> where <N::Future as IntoFuture>::Future: Send + 'sta
|
||||
pub fn update(&self) -> BoxFuture<i64, Error> {
|
||||
trace!(target: "dapps", "Updating time from NTP.");
|
||||
let last_result = self.last_result.clone();
|
||||
self.ntp.drift().into_future().then(move |res| {
|
||||
Box::new(self.ntp.drift().into_future().then(move |res| {
|
||||
let res = res.map(|d| d.num_milliseconds());
|
||||
|
||||
if let Err(Error::NoServersAvailable) = res {
|
||||
@ -255,7 +257,7 @@ impl<N: Ntp> TimeChecker<N> where <N::Future as IntoFuture>::Future: Send + 'sta
|
||||
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.
|
||||
@ -264,7 +266,7 @@ impl<N: Ntp> TimeChecker<N> where <N::Future as IntoFuture>::Future: Send + 'sta
|
||||
{
|
||||
let res = self.last_result.read();
|
||||
if res.0 > time::Instant::now() {
|
||||
return futures::done(select_result(res.1.iter())).boxed();
|
||||
return Box::new(futures::done(select_result(res.1.iter())));
|
||||
}
|
||||
}
|
||||
// or update and return result
|
||||
|
@ -16,144 +16,82 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hyper::{server, net, Decoder, Encoder, Next, Control};
|
||||
use hyper::method::Method;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::{Method, StatusCode};
|
||||
|
||||
use api::{response, types};
|
||||
use api::response;
|
||||
use apps::fetcher::Fetcher;
|
||||
use handlers::{self, extract_url};
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use node_health::{NodeHealth, HealthStatus, Health};
|
||||
use parity_reactor::Remote;
|
||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
||||
use futures::{future, Future};
|
||||
use node_health::{NodeHealth, HealthStatus};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RestApi {
|
||||
fetcher: Arc<Fetcher>,
|
||||
health: NodeHealth,
|
||||
remote: Remote,
|
||||
}
|
||||
|
||||
impl Endpoint for RestApi {
|
||||
fn respond(&self, mut path: EndpointPath, req: Request) -> Response {
|
||||
if let Method::Options = *req.method() {
|
||||
return Box::new(future::ok(response::empty()));
|
||||
}
|
||||
|
||||
let endpoint = path.app_params.get(0).map(String::to_owned);
|
||||
let hash = path.app_params.get(1).map(String::to_owned);
|
||||
|
||||
// at this point path.app_id contains 'api', adjust it to the hash properly, otherwise
|
||||
// we will try and retrieve 'api' as the hash when doing the /api/content route
|
||||
if let Some(ref hash) = hash {
|
||||
path.app_id = hash.to_owned();
|
||||
}
|
||||
|
||||
trace!(target: "dapps", "Handling /api request: {:?}/{:?}", endpoint, hash);
|
||||
match endpoint.as_ref().map(String::as_str) {
|
||||
Some("ping") => Box::new(future::ok(response::ping(req))),
|
||||
Some("health") => self.health(),
|
||||
Some("content") => self.resolve_content(hash.as_ref().map(String::as_str), path, req),
|
||||
_ => Box::new(future::ok(response::not_found())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RestApi {
|
||||
pub fn new(
|
||||
fetcher: Arc<Fetcher>,
|
||||
health: NodeHealth,
|
||||
remote: Remote,
|
||||
) -> Box<Endpoint> {
|
||||
Box::new(RestApi {
|
||||
fetcher,
|
||||
health,
|
||||
remote,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for RestApi {
|
||||
fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box<Handler> {
|
||||
Box::new(RestApiRouter::new((*self).clone(), path, control))
|
||||
}
|
||||
}
|
||||
|
||||
struct RestApiRouter {
|
||||
api: RestApi,
|
||||
path: Option<EndpointPath>,
|
||||
control: Option<Control>,
|
||||
handler: Box<Handler>,
|
||||
}
|
||||
|
||||
impl RestApiRouter {
|
||||
fn new(api: RestApi, path: EndpointPath, control: Control) -> Self {
|
||||
RestApiRouter {
|
||||
path: Some(path),
|
||||
control: Some(control),
|
||||
api: api,
|
||||
handler: Box::new(response::as_json_error(StatusCode::NotFound, &types::ApiError {
|
||||
code: "404".into(),
|
||||
title: "Not Found".into(),
|
||||
detail: "Resource you requested has not been found.".into(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_content(&self, hash: Option<&str>, path: EndpointPath, control: Control) -> Option<Box<Handler>> {
|
||||
fn resolve_content(&self, hash: Option<&str>, path: EndpointPath, req: Request) -> Response {
|
||||
trace!(target: "dapps", "Resolving content: {:?} from path: {:?}", hash, path);
|
||||
match hash {
|
||||
Some(hash) if self.api.fetcher.contains(hash) => {
|
||||
Some(self.api.fetcher.to_async_handler(path, control))
|
||||
Some(hash) if self.fetcher.contains(hash) => {
|
||||
self.fetcher.respond(path, req)
|
||||
},
|
||||
_ => None
|
||||
_ => Box::new(future::ok(response::not_found())),
|
||||
}
|
||||
}
|
||||
|
||||
fn health(&self, control: Control) -> Box<Handler> {
|
||||
let map = move |health: Result<Result<Health, ()>, ()>| {
|
||||
let status = match health {
|
||||
Ok(Ok(ref health)) => {
|
||||
if [&health.peers.status, &health.sync.status].iter().any(|x| *x != &HealthStatus::Ok) {
|
||||
StatusCode::PreconditionFailed // HTTP 412
|
||||
} else {
|
||||
StatusCode::Ok // HTTP 200
|
||||
}
|
||||
},
|
||||
_ => StatusCode::ServiceUnavailable, // HTTP 503
|
||||
};
|
||||
fn health(&self) -> Response {
|
||||
Box::new(self.health.health()
|
||||
.then(|health| {
|
||||
let status = match health {
|
||||
Ok(ref health) => {
|
||||
if [&health.peers.status, &health.sync.status].iter().any(|x| *x != &HealthStatus::Ok) {
|
||||
StatusCode::PreconditionFailed // HTTP 412
|
||||
} else {
|
||||
StatusCode::Ok // HTTP 200
|
||||
}
|
||||
},
|
||||
_ => StatusCode::ServiceUnavailable, // HTTP 503
|
||||
};
|
||||
|
||||
response::as_json(status, &health)
|
||||
};
|
||||
let health = self.api.health.health();
|
||||
let remote = self.api.remote.clone();
|
||||
Box::new(handlers::AsyncHandler::new(health, map, remote, control))
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Handler<net::HttpStream> for RestApiRouter {
|
||||
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
||||
if let Method::Options = *request.method() {
|
||||
self.handler = response::empty();
|
||||
return Next::write();
|
||||
}
|
||||
|
||||
// TODO [ToDr] Consider using `path.app_params` instead
|
||||
let url = extract_url(&request);
|
||||
if url.is_none() {
|
||||
// Just return 404 if we can't parse URL
|
||||
return Next::write();
|
||||
}
|
||||
|
||||
let url = url.expect("Check for None early-exists above; qed");
|
||||
let mut path = self.path.take().expect("on_request called only once, and path is always defined in new; qed");
|
||||
let control = self.control.take().expect("on_request called only once, and control is always defined in new; qed");
|
||||
|
||||
let endpoint = url.path.get(1).map(|v| v.as_str());
|
||||
let hash = url.path.get(2).map(|v| v.as_str());
|
||||
// at this point path.app_id contains 'api', adjust it to the hash properly, otherwise
|
||||
// we will try and retrieve 'api' as the hash when doing the /api/content route
|
||||
if let Some(ref hash) = hash { path.app_id = hash.clone().to_owned() }
|
||||
|
||||
let handler = endpoint.and_then(|v| match v {
|
||||
"ping" => Some(response::ping()),
|
||||
"health" => Some(self.health(control)),
|
||||
"content" => self.resolve_content(hash, path, control),
|
||||
_ => None
|
||||
});
|
||||
|
||||
// Overwrite default
|
||||
if let Some(h) = handler {
|
||||
self.handler = h;
|
||||
}
|
||||
|
||||
self.handler.on_request(request)
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
|
||||
self.handler.on_request_readable(decoder)
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
self.handler.on_response(res)
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
|
||||
self.handler.on_response_writable(encoder)
|
||||
Ok(response::as_json(status, &health).into())
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -16,27 +16,28 @@
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::{self, mime, StatusCode};
|
||||
|
||||
use endpoint::Handler;
|
||||
use handlers::{ContentHandler, EchoHandler};
|
||||
|
||||
pub fn empty() -> Box<Handler> {
|
||||
Box::new(ContentHandler::ok("".into(), mime!(Text/Plain)))
|
||||
pub fn empty() -> hyper::Response {
|
||||
ContentHandler::ok("".into(), mime::TEXT_PLAIN).into()
|
||||
}
|
||||
|
||||
pub fn as_json<T: Serialize>(status: StatusCode, val: &T) -> ContentHandler {
|
||||
pub fn as_json<T: Serialize>(status: StatusCode, val: &T) -> hyper::Response {
|
||||
let json = serde_json::to_string(val)
|
||||
.expect("serialization to string is infallible; qed");
|
||||
ContentHandler::new(status, json, mime!(Application/Json))
|
||||
ContentHandler::new(status, json, mime::APPLICATION_JSON).into()
|
||||
}
|
||||
|
||||
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(req: hyper::Request) -> hyper::Response {
|
||||
EchoHandler::new(req).into()
|
||||
}
|
||||
|
||||
pub fn ping() -> Box<Handler> {
|
||||
Box::new(EchoHandler::default())
|
||||
pub fn not_found() -> hyper::Response {
|
||||
as_json(StatusCode::NotFound, &::api::types::ApiError {
|
||||
code: "404".into(),
|
||||
title: "Not Found".into(),
|
||||
detail: "Resource you requested has not been found.".into(),
|
||||
})
|
||||
}
|
||||
|
@ -19,12 +19,12 @@
|
||||
use std::fs;
|
||||
|
||||
use linked_hash_map::LinkedHashMap;
|
||||
use page::LocalPageEndpoint;
|
||||
use page::local;
|
||||
use handlers::FetchControl;
|
||||
|
||||
pub enum ContentStatus {
|
||||
Fetching(FetchControl),
|
||||
Ready(LocalPageEndpoint),
|
||||
Ready(local::Dapp),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -18,16 +18,17 @@ use zip;
|
||||
use std::{fs, fmt};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use fetch::{self, Mime};
|
||||
use hash::keccak_buffer;
|
||||
use bigint::hash::H256;
|
||||
use fetch::{self, Mime};
|
||||
use futures_cpupool::CpuPool;
|
||||
use hash::keccak_buffer;
|
||||
|
||||
use page::{LocalPageEndpoint, PageCache};
|
||||
use handlers::{ContentValidator, ValidatorResponse};
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest, serialize_manifest, Manifest};
|
||||
use handlers::{ContentValidator, ValidatorResponse};
|
||||
use page::{local, PageCache};
|
||||
use Embeddable;
|
||||
|
||||
type OnDone = Box<Fn(Option<LocalPageEndpoint>) + Send>;
|
||||
type OnDone = Box<Fn(Option<local::Dapp>) + Send>;
|
||||
|
||||
fn write_response_and_check_hash(
|
||||
id: &str,
|
||||
@ -75,15 +76,17 @@ pub struct Content {
|
||||
mime: Mime,
|
||||
content_path: PathBuf,
|
||||
on_done: OnDone,
|
||||
pool: CpuPool,
|
||||
}
|
||||
|
||||
impl Content {
|
||||
pub fn new(id: String, mime: Mime, content_path: PathBuf, on_done: OnDone) -> Self {
|
||||
pub fn new(id: String, mime: Mime, content_path: PathBuf, on_done: OnDone, pool: CpuPool) -> Self {
|
||||
Content {
|
||||
id: id,
|
||||
mime: mime,
|
||||
content_path: content_path,
|
||||
on_done: on_done,
|
||||
id,
|
||||
mime,
|
||||
content_path,
|
||||
on_done,
|
||||
pool,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,12 +94,15 @@ impl Content {
|
||||
impl ContentValidator for Content {
|
||||
type Error = ValidationError;
|
||||
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
||||
let validate = |content_path: PathBuf| {
|
||||
fn validate_and_install(self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
||||
let pool = self.pool;
|
||||
let id = self.id.clone();
|
||||
let mime = self.mime;
|
||||
let validate = move |content_path: PathBuf| {
|
||||
// Create dir
|
||||
let (_, content_path) = write_response_and_check_hash(self.id.as_str(), content_path.clone(), self.id.as_str(), response)?;
|
||||
let (_, content_path) = write_response_and_check_hash(&id, content_path, &id, response)?;
|
||||
|
||||
Ok(LocalPageEndpoint::single_file(content_path, self.mime.clone(), PageCache::Enabled))
|
||||
Ok(local::Dapp::single_file(pool, content_path, mime, PageCache::Enabled))
|
||||
};
|
||||
|
||||
// Prepare path for a file
|
||||
@ -118,15 +124,17 @@ pub struct Dapp {
|
||||
dapps_path: PathBuf,
|
||||
on_done: OnDone,
|
||||
embeddable_on: Embeddable,
|
||||
pool: CpuPool,
|
||||
}
|
||||
|
||||
impl Dapp {
|
||||
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Embeddable) -> Self {
|
||||
pub fn new(id: String, dapps_path: PathBuf, on_done: OnDone, embeddable_on: Embeddable, pool: CpuPool) -> Self {
|
||||
Dapp {
|
||||
id,
|
||||
dapps_path,
|
||||
on_done,
|
||||
embeddable_on,
|
||||
pool,
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,16 +166,19 @@ impl Dapp {
|
||||
impl ContentValidator for Dapp {
|
||||
type Error = ValidationError;
|
||||
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
||||
let validate = |dapp_path: PathBuf| {
|
||||
let (file, zip_path) = write_response_and_check_hash(self.id.as_str(), dapp_path.clone(), &format!("{}.zip", self.id), response)?;
|
||||
fn validate_and_install(self, response: fetch::Response) -> Result<ValidatorResponse, ValidationError> {
|
||||
let id = self.id.clone();
|
||||
let pool = self.pool;
|
||||
let embeddable_on = self.embeddable_on;
|
||||
let validate = move |dapp_path: PathBuf| {
|
||||
let (file, zip_path) = write_response_and_check_hash(&id, dapp_path.clone(), &format!("{}.zip", id), response)?;
|
||||
trace!(target: "dapps", "Opening dapp bundle at {:?}", zip_path);
|
||||
// Unpack archive
|
||||
let mut zip = zip::ZipArchive::new(file)?;
|
||||
// First find manifest file
|
||||
let (mut manifest, manifest_dir) = Self::find_manifest(&mut zip)?;
|
||||
// Overwrite id to match hash
|
||||
manifest.id = self.id.clone();
|
||||
manifest.id = id;
|
||||
|
||||
// Unpack zip
|
||||
for i in 0..zip.len() {
|
||||
@ -198,7 +209,7 @@ impl ContentValidator for Dapp {
|
||||
let mut manifest_file = fs::File::create(manifest_path)?;
|
||||
manifest_file.write_all(manifest_str.as_bytes())?;
|
||||
// Create endpoint
|
||||
let endpoint = LocalPageEndpoint::new(dapp_path, manifest.clone().into(), PageCache::Enabled, self.embeddable_on.clone());
|
||||
let endpoint = local::Dapp::new(pool, dapp_path, manifest.into(), PageCache::Enabled, embeddable_on);
|
||||
Ok(endpoint)
|
||||
};
|
||||
|
||||
|
@ -24,27 +24,25 @@ use std::{fs, env};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use rustc_hex::FromHex;
|
||||
use futures::{future, Future};
|
||||
use futures_cpupool::CpuPool;
|
||||
use fetch::{Client as FetchClient, Fetch};
|
||||
use hash_fetch::urlhint::{URLHintContract, URLHint, URLHintResult};
|
||||
use parity_reactor::Remote;
|
||||
|
||||
use hyper;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::StatusCode;
|
||||
|
||||
use {Embeddable, SyncStatus, random_filename};
|
||||
use parking_lot::Mutex;
|
||||
use page::LocalPageEndpoint;
|
||||
use page::local;
|
||||
use handlers::{ContentHandler, ContentFetcherHandler};
|
||||
use endpoint::{Endpoint, EndpointPath, Handler};
|
||||
use endpoint::{self, Endpoint, EndpointPath};
|
||||
use apps::cache::{ContentCache, ContentStatus};
|
||||
|
||||
/// Limit of cached dapps/content
|
||||
const MAX_CACHED_DAPPS: usize = 20;
|
||||
|
||||
pub trait Fetcher: Send + Sync + 'static {
|
||||
pub trait Fetcher: Endpoint + 'static {
|
||||
fn contains(&self, content_id: &str) -> bool;
|
||||
|
||||
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler>;
|
||||
}
|
||||
|
||||
pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHintContract> {
|
||||
@ -53,8 +51,8 @@ pub struct ContentFetcher<F: Fetch = FetchClient, R: URLHint + 'static = URLHint
|
||||
cache: Arc<Mutex<ContentCache>>,
|
||||
sync: Arc<SyncStatus>,
|
||||
embeddable_on: Embeddable,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
only_content: bool,
|
||||
}
|
||||
|
||||
@ -66,24 +64,23 @@ impl<R: URLHint + 'static, F: Fetch> Drop for ContentFetcher<F, R> {
|
||||
}
|
||||
|
||||
impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
|
||||
|
||||
pub fn new(
|
||||
resolver: R,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
remote: Remote,
|
||||
sync: Arc<SyncStatus>,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
) -> Self {
|
||||
let mut cache_path = env::temp_dir();
|
||||
cache_path.push(random_filename());
|
||||
|
||||
ContentFetcher {
|
||||
cache_path: cache_path,
|
||||
resolver: resolver,
|
||||
sync: sync_status,
|
||||
cache_path,
|
||||
resolver,
|
||||
sync,
|
||||
cache: Arc::new(Mutex::new(ContentCache::default())),
|
||||
embeddable_on: None,
|
||||
remote: remote,
|
||||
fetch: fetch,
|
||||
fetch,
|
||||
pool,
|
||||
only_content: true,
|
||||
}
|
||||
}
|
||||
@ -98,24 +95,34 @@ impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
|
||||
self
|
||||
}
|
||||
|
||||
fn still_syncing(embeddable: Embeddable) -> Box<Handler> {
|
||||
Box::new(ContentHandler::error(
|
||||
fn not_found(embeddable: Embeddable) -> endpoint::Response {
|
||||
Box::new(future::ok(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"Resource Not Found",
|
||||
"Requested resource was not found.",
|
||||
None,
|
||||
embeddable,
|
||||
).into()))
|
||||
}
|
||||
|
||||
fn still_syncing(embeddable: Embeddable) -> endpoint::Response {
|
||||
Box::new(future::ok(ContentHandler::error(
|
||||
StatusCode::ServiceUnavailable,
|
||||
"Sync In Progress",
|
||||
"Your node is still syncing. We cannot resolve any content before it's fully synced.",
|
||||
Some("<a href=\"javascript:window.location.reload()\">Refresh</a>"),
|
||||
embeddable,
|
||||
))
|
||||
).into()))
|
||||
}
|
||||
|
||||
fn dapps_disabled(address: Embeddable) -> Box<Handler> {
|
||||
Box::new(ContentHandler::error(
|
||||
fn dapps_disabled(address: Embeddable) -> endpoint::Response {
|
||||
Box::new(future::ok(ContentHandler::error(
|
||||
StatusCode::ServiceUnavailable,
|
||||
"Network Dapps Not Available",
|
||||
"This interface doesn't support network dapps for security reasons.",
|
||||
None,
|
||||
address,
|
||||
))
|
||||
).into()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -126,8 +133,6 @@ impl<R: URLHint + 'static, F: Fetch> ContentFetcher<F, R> {
|
||||
// resolve contract call synchronously.
|
||||
// TODO: port to futures-based hyper and make it all async.
|
||||
fn resolve(&self, content_id: Vec<u8>) -> Option<URLHintResult> {
|
||||
use futures::Future;
|
||||
|
||||
self.resolver.resolve(content_id)
|
||||
.wait()
|
||||
.unwrap_or_else(|e| { warn!("Error resolving content-id: {}", e); None })
|
||||
@ -151,8 +156,10 @@ impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler> {
|
||||
impl<R: URLHint + 'static, F: Fetch> Endpoint for ContentFetcher<F, R> {
|
||||
fn respond(&self, path: EndpointPath, req: endpoint::Request) -> endpoint::Response {
|
||||
let mut cache = self.cache.lock();
|
||||
let content_id = path.app_id.clone();
|
||||
|
||||
@ -161,12 +168,12 @@ impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
||||
match status {
|
||||
// Just serve the content
|
||||
Some(&mut ContentStatus::Ready(ref endpoint)) => {
|
||||
(None, endpoint.to_async_handler(path, control))
|
||||
(None, endpoint.to_response(&path))
|
||||
},
|
||||
// Content is already being fetched
|
||||
Some(&mut ContentStatus::Fetching(ref fetch_control)) if !fetch_control.is_deadline_reached() => {
|
||||
trace!(target: "dapps", "Content fetching in progress. Waiting...");
|
||||
(None, fetch_control.to_async_handler(path, control))
|
||||
(None, fetch_control.to_response(path))
|
||||
},
|
||||
// We need to start fetching the content
|
||||
_ => {
|
||||
@ -176,7 +183,7 @@ impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
||||
|
||||
let cache = self.cache.clone();
|
||||
let id = content_id.clone();
|
||||
let on_done = move |result: Option<LocalPageEndpoint>| {
|
||||
let on_done = move |result: Option<local::Dapp>| {
|
||||
let mut cache = cache.lock();
|
||||
match result {
|
||||
Some(endpoint) => cache.insert(id.clone(), ContentStatus::Ready(endpoint)),
|
||||
@ -195,39 +202,39 @@ impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
||||
},
|
||||
Some(URLHintResult::Dapp(dapp)) => {
|
||||
let handler = ContentFetcherHandler::new(
|
||||
dapp.url(),
|
||||
req.method(),
|
||||
&dapp.url(),
|
||||
path,
|
||||
control,
|
||||
installers::Dapp::new(
|
||||
content_id.clone(),
|
||||
self.cache_path.clone(),
|
||||
Box::new(on_done),
|
||||
self.embeddable_on.clone(),
|
||||
self.pool.clone(),
|
||||
),
|
||||
self.embeddable_on.clone(),
|
||||
self.remote.clone(),
|
||||
self.fetch.clone(),
|
||||
);
|
||||
|
||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as Box<Handler>)
|
||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response)
|
||||
},
|
||||
Some(URLHintResult::Content(content)) => {
|
||||
let handler = ContentFetcherHandler::new(
|
||||
content.url,
|
||||
req.method(),
|
||||
&content.url,
|
||||
path,
|
||||
control,
|
||||
installers::Content::new(
|
||||
content_id.clone(),
|
||||
content.mime,
|
||||
self.cache_path.clone(),
|
||||
Box::new(on_done),
|
||||
self.pool.clone(),
|
||||
),
|
||||
self.embeddable_on.clone(),
|
||||
self.remote.clone(),
|
||||
self.fetch.clone(),
|
||||
);
|
||||
|
||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as Box<Handler>)
|
||||
(Some(ContentStatus::Fetching(handler.fetch_control())), Box::new(handler) as endpoint::Response)
|
||||
},
|
||||
None if self.sync.is_major_importing() => {
|
||||
(None, Self::still_syncing(self.embeddable_on.clone()))
|
||||
@ -235,13 +242,7 @@ impl<R: URLHint + 'static, F: Fetch> Fetcher for ContentFetcher<F, R> {
|
||||
None => {
|
||||
// This may happen when sync status changes in between
|
||||
// `contains` and `to_handler`
|
||||
(None, Box::new(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"Resource Not Found",
|
||||
"Requested resource was not found.",
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)) as Box<Handler>)
|
||||
(None, Self::not_found(self.embeddable_on.clone()))
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -263,13 +264,12 @@ mod tests {
|
||||
use std::sync::Arc;
|
||||
use bytes::Bytes;
|
||||
use fetch::{Fetch, Client};
|
||||
use futures::{future, Future, BoxFuture};
|
||||
use hash_fetch::urlhint::{URLHint, URLHintResult};
|
||||
use parity_reactor::Remote;
|
||||
use futures::future;
|
||||
use hash_fetch::urlhint::{URLHint, URLHintResult, BoxFuture};
|
||||
|
||||
use apps::cache::ContentStatus;
|
||||
use endpoint::EndpointInfo;
|
||||
use page::LocalPageEndpoint;
|
||||
use page::local;
|
||||
use super::{ContentFetcher, Fetcher};
|
||||
use {SyncStatus};
|
||||
|
||||
@ -277,7 +277,7 @@ mod tests {
|
||||
struct FakeResolver;
|
||||
impl URLHint for FakeResolver {
|
||||
fn resolve(&self, _id: Bytes) -> BoxFuture<Option<URLHintResult>, String> {
|
||||
future::ok(None).boxed()
|
||||
Box::new(future::ok(None))
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,10 +291,16 @@ mod tests {
|
||||
#[test]
|
||||
fn should_true_if_contains_the_app() {
|
||||
// given
|
||||
let pool = ::futures_cpupool::CpuPool::new(1);
|
||||
let path = env::temp_dir();
|
||||
let fetcher = ContentFetcher::new(FakeResolver, Arc::new(FakeSync(false)), Remote::new_sync(), Client::new().unwrap())
|
||||
.allow_dapps(true);
|
||||
let handler = LocalPageEndpoint::new(path, EndpointInfo {
|
||||
let fetcher = ContentFetcher::new(
|
||||
FakeResolver,
|
||||
Arc::new(FakeSync(false)),
|
||||
Client::new().unwrap(),
|
||||
pool.clone(),
|
||||
).allow_dapps(true);
|
||||
|
||||
let handler = local::Dapp::new(pool, path, EndpointInfo {
|
||||
name: "fake".into(),
|
||||
description: "".into(),
|
||||
version: "".into(),
|
||||
|
@ -19,9 +19,11 @@ use std::io;
|
||||
use std::io::Read;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use page::{LocalPageEndpoint, PageCache};
|
||||
use endpoint::{Endpoint, EndpointInfo};
|
||||
use futures_cpupool::CpuPool;
|
||||
|
||||
use apps::manifest::{MANIFEST_FILENAME, deserialize_manifest};
|
||||
use endpoint::{Endpoint, EndpointInfo};
|
||||
use page::{local, PageCache};
|
||||
use Embeddable;
|
||||
|
||||
struct LocalDapp {
|
||||
@ -61,14 +63,14 @@ fn read_manifest(name: &str, mut path: PathBuf) -> EndpointInfo {
|
||||
/// Returns Dapp Id and Local Dapp Endpoint for given filesystem path.
|
||||
/// Parses the path to extract last component (for name).
|
||||
/// `None` is returned when path is invalid or non-existent.
|
||||
pub fn local_endpoint<P: AsRef<Path>>(path: P, embeddable: Embeddable) -> Option<(String, Box<LocalPageEndpoint>)> {
|
||||
pub fn local_endpoint<P: AsRef<Path>>(path: P, embeddable: Embeddable, pool: CpuPool) -> Option<(String, Box<local::Dapp>)> {
|
||||
let path = path.as_ref().to_owned();
|
||||
path.canonicalize().ok().and_then(|path| {
|
||||
let name = path.file_name().and_then(|name| name.to_str());
|
||||
name.map(|name| {
|
||||
let dapp = local_dapp(name.into(), path.clone());
|
||||
(dapp.id, Box::new(LocalPageEndpoint::new(
|
||||
dapp.path, dapp.info, PageCache::Disabled, embeddable.clone())
|
||||
(dapp.id, Box::new(local::Dapp::new(
|
||||
pool.clone(), dapp.path, dapp.info, PageCache::Disabled, embeddable.clone())
|
||||
))
|
||||
})
|
||||
})
|
||||
@ -86,13 +88,13 @@ fn local_dapp(name: String, path: PathBuf) -> LocalDapp {
|
||||
}
|
||||
|
||||
/// Returns endpoints for Local Dapps found for given filesystem path.
|
||||
/// Scans the directory and collects `LocalPageEndpoints`.
|
||||
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, embeddable: Embeddable) -> BTreeMap<String, Box<Endpoint>> {
|
||||
/// Scans the directory and collects `local::Dapp`.
|
||||
pub fn local_endpoints<P: AsRef<Path>>(dapps_path: P, embeddable: Embeddable, pool: CpuPool) -> BTreeMap<String, Box<Endpoint>> {
|
||||
let mut pages = BTreeMap::<String, Box<Endpoint>>::new();
|
||||
for dapp in local_dapps(dapps_path.as_ref()) {
|
||||
pages.insert(
|
||||
dapp.id,
|
||||
Box::new(LocalPageEndpoint::new(dapp.path, dapp.info, PageCache::Disabled, embeddable.clone()))
|
||||
Box::new(local::Dapp::new(pool.clone(), dapp.path, dapp.info, PageCache::Disabled, embeddable.clone()))
|
||||
);
|
||||
}
|
||||
pages
|
||||
|
@ -18,12 +18,12 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use endpoint::{Endpoints, Endpoint};
|
||||
use page::PageEndpoint;
|
||||
use futures_cpupool::CpuPool;
|
||||
use page;
|
||||
use proxypac::ProxyPac;
|
||||
use web::Web;
|
||||
use fetch::Fetch;
|
||||
use parity_dapps::WebApp;
|
||||
use parity_reactor::Remote;
|
||||
use parity_ui;
|
||||
use {WebProxyTokens, ParentFrameSettings};
|
||||
|
||||
@ -43,12 +43,12 @@ pub const UTILS_PATH: &'static str = "parity-utils";
|
||||
pub const WEB_PATH: &'static str = "web";
|
||||
pub const URL_REFERER: &'static str = "__referer=";
|
||||
|
||||
pub fn utils() -> Box<Endpoint> {
|
||||
Box::new(PageEndpoint::with_prefix(parity_ui::App::default(), UTILS_PATH.to_owned()))
|
||||
pub fn utils(pool: CpuPool) -> Box<Endpoint> {
|
||||
Box::new(page::builtin::Dapp::new(pool, parity_ui::App::default()))
|
||||
}
|
||||
|
||||
pub fn ui() -> Box<Endpoint> {
|
||||
Box::new(PageEndpoint::with_fallback_to_index(parity_ui::App::default()))
|
||||
pub fn ui(pool: CpuPool) -> Box<Endpoint> {
|
||||
Box::new(page::builtin::Dapp::with_fallback_to_index(pool, parity_ui::App::default()))
|
||||
}
|
||||
|
||||
pub fn ui_redirection(embeddable: Option<ParentFrameSettings>) -> Box<Endpoint> {
|
||||
@ -61,14 +61,14 @@ pub fn all_endpoints<F: Fetch>(
|
||||
dapps_domain: &str,
|
||||
embeddable: Option<ParentFrameSettings>,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
pool: CpuPool,
|
||||
) -> (Vec<String>, Endpoints) {
|
||||
// fetch fs dapps at first to avoid overwriting builtins
|
||||
let mut pages = fs::local_endpoints(dapps_path.clone(), embeddable.clone());
|
||||
let mut pages = fs::local_endpoints(dapps_path.clone(), embeddable.clone(), pool.clone());
|
||||
let local_endpoints: Vec<String> = pages.keys().cloned().collect();
|
||||
for path in extra_dapps {
|
||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), embeddable.clone()) {
|
||||
if let Some((id, endpoint)) = fs::local_endpoint(path.clone(), embeddable.clone(), pool.clone()) {
|
||||
pages.insert(id, endpoint);
|
||||
} else {
|
||||
warn!(target: "dapps", "Ignoring invalid dapp at {}", path.display());
|
||||
@ -76,17 +76,17 @@ pub fn all_endpoints<F: Fetch>(
|
||||
}
|
||||
|
||||
// NOTE [ToDr] Dapps will be currently embeded on 8180
|
||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(embeddable.clone()));
|
||||
insert::<parity_ui::App>(&mut pages, "ui", Embeddable::Yes(embeddable.clone()), pool.clone());
|
||||
pages.insert("proxy".into(), ProxyPac::boxed(embeddable.clone(), dapps_domain.to_owned()));
|
||||
pages.insert(WEB_PATH.into(), Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), remote.clone(), fetch.clone()));
|
||||
pages.insert(WEB_PATH.into(), Web::boxed(embeddable.clone(), web_proxy_tokens.clone(), fetch.clone()));
|
||||
|
||||
(local_endpoints, pages)
|
||||
}
|
||||
|
||||
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable) {
|
||||
fn insert<T : WebApp + Default + 'static>(pages: &mut Endpoints, id: &str, embed_at: Embeddable, pool: CpuPool) {
|
||||
pages.insert(id.to_owned(), Box::new(match embed_at {
|
||||
Embeddable::Yes(address) => PageEndpoint::new_safe_to_embed(T::default(), address),
|
||||
Embeddable::No => PageEndpoint::new(T::default()),
|
||||
Embeddable::Yes(address) => page::builtin::Dapp::new_safe_to_embed(pool, T::default(), address),
|
||||
Embeddable::No => page::builtin::Dapp::new(pool, T::default()),
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -16,9 +16,10 @@
|
||||
|
||||
//! UI redirections
|
||||
|
||||
use hyper::{Control, StatusCode};
|
||||
use hyper::StatusCode;
|
||||
use futures::future;
|
||||
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
||||
use {handlers, Embeddable};
|
||||
|
||||
/// Redirection to UI server.
|
||||
@ -37,19 +38,20 @@ impl Redirection {
|
||||
}
|
||||
|
||||
impl Endpoint for Redirection {
|
||||
fn to_async_handler(&self, _path: EndpointPath, _control: Control) -> Box<Handler> {
|
||||
if let Some(ref frame) = self.embeddable_on {
|
||||
fn respond(&self, _path: EndpointPath, req: Request) -> Response {
|
||||
Box::new(future::ok(if let Some(ref frame) = self.embeddable_on {
|
||||
trace!(target: "dapps", "Redirecting to signer interface.");
|
||||
handlers::Redirection::boxed(&format!("http://{}:{}", &frame.host, frame.port))
|
||||
let protocol = req.uri().scheme().unwrap_or("http");
|
||||
handlers::Redirection::new(format!("{}://{}:{}", protocol, &frame.host, frame.port)).into()
|
||||
} else {
|
||||
trace!(target: "dapps", "Signer disabled, returning 404.");
|
||||
Box::new(handlers::ContentHandler::error(
|
||||
handlers::ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"404 Not Found",
|
||||
"Your homepage is not available when Trusted Signer is disabled.",
|
||||
Some("You can still access dapps by writing a correct address, though. Re-enable Signer to get your homepage back."),
|
||||
None,
|
||||
))
|
||||
}
|
||||
).into()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -18,17 +18,25 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use hyper::{self, server, net};
|
||||
use jsonrpc_core::BoxFuture;
|
||||
use hyper;
|
||||
|
||||
#[derive(Debug, PartialEq, Default, Clone)]
|
||||
pub struct EndpointPath {
|
||||
pub app_id: String,
|
||||
pub app_params: Vec<String>,
|
||||
pub query: Option<String>,
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub using_dapps_domains: bool,
|
||||
}
|
||||
|
||||
impl EndpointPath {
|
||||
pub fn has_no_params(&self) -> bool {
|
||||
self.app_params.is_empty() || self.app_params.iter().all(|x| x.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct EndpointInfo {
|
||||
pub name: String,
|
||||
@ -39,16 +47,11 @@ pub struct EndpointInfo {
|
||||
}
|
||||
|
||||
pub type Endpoints = BTreeMap<String, Box<Endpoint>>;
|
||||
pub type Handler = server::Handler<net::HttpStream> + Send;
|
||||
pub type Response = BoxFuture<hyper::Response, hyper::Error>;
|
||||
pub type Request = hyper::Request;
|
||||
|
||||
pub trait Endpoint : Send + Sync {
|
||||
fn info(&self) -> Option<&EndpointInfo> { None }
|
||||
|
||||
fn to_handler(&self, _path: EndpointPath) -> Box<Handler> {
|
||||
panic!("This Endpoint is asynchronous and requires Control object.");
|
||||
}
|
||||
|
||||
fn to_async_handler(&self, path: EndpointPath, _control: hyper::Control) -> Box<Handler> {
|
||||
self.to_handler(path)
|
||||
}
|
||||
fn respond(&self, path: EndpointPath, req: Request) -> Response;
|
||||
}
|
||||
|
@ -1,112 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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()
|
||||
}
|
||||
}
|
||||
}
|
@ -16,32 +16,29 @@
|
||||
|
||||
//! Simple Content Handler
|
||||
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::mime::Mime;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::{self, mime, header};
|
||||
use hyper::StatusCode;
|
||||
|
||||
use util::version;
|
||||
|
||||
use handlers::add_security_headers;
|
||||
use Embeddable;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContentHandler {
|
||||
code: StatusCode,
|
||||
content: String,
|
||||
mimetype: Mime,
|
||||
write_pos: usize,
|
||||
mimetype: mime::Mime,
|
||||
safe_to_embed_on: Embeddable,
|
||||
}
|
||||
|
||||
impl ContentHandler {
|
||||
pub fn ok(content: String, mimetype: Mime) -> Self {
|
||||
pub fn ok(content: String, mimetype: mime::Mime) -> Self {
|
||||
Self::new(StatusCode::Ok, content, mimetype)
|
||||
}
|
||||
|
||||
pub fn html(code: StatusCode, content: String, embeddable_on: Embeddable) -> Self {
|
||||
Self::new_embeddable(code, content, mime!(Text/Html), embeddable_on)
|
||||
Self::new_embeddable(code, content, mime::TEXT_HTML, embeddable_on)
|
||||
}
|
||||
|
||||
pub fn error(
|
||||
@ -60,57 +57,32 @@ impl ContentHandler {
|
||||
), embeddable_on)
|
||||
}
|
||||
|
||||
pub fn new(code: StatusCode, content: String, mimetype: Mime) -> Self {
|
||||
pub fn new(code: StatusCode, content: String, mimetype: mime::Mime) -> Self {
|
||||
Self::new_embeddable(code, content, mimetype, None)
|
||||
}
|
||||
|
||||
pub fn new_embeddable(
|
||||
code: StatusCode,
|
||||
content: String,
|
||||
mimetype: Mime,
|
||||
mimetype: mime::Mime,
|
||||
safe_to_embed_on: Embeddable,
|
||||
) -> Self {
|
||||
ContentHandler {
|
||||
code,
|
||||
content,
|
||||
mimetype,
|
||||
write_pos: 0,
|
||||
safe_to_embed_on,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Handler<HttpStream> for ContentHandler {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(self.code);
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
let bytes = self.content.as_bytes();
|
||||
if self.write_pos == bytes.len() {
|
||||
return Next::end();
|
||||
}
|
||||
|
||||
match encoder.write(&bytes[self.write_pos..]) {
|
||||
Ok(bytes) => {
|
||||
self.write_pos += bytes;
|
||||
Next::write()
|
||||
},
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end()
|
||||
},
|
||||
}
|
||||
impl Into<hyper::Response> for ContentHandler {
|
||||
fn into(self) -> hyper::Response {
|
||||
let mut res = hyper::Response::new()
|
||||
.with_status(self.code)
|
||||
.with_header(header::ContentType(self.mimetype))
|
||||
.with_body(self.content);
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
@ -16,45 +16,31 @@
|
||||
|
||||
//! Echo Handler
|
||||
|
||||
use std::io::Read;
|
||||
use hyper::{server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use super::ContentHandler;
|
||||
use hyper::{self, header};
|
||||
|
||||
#[derive(Default)]
|
||||
use handlers::add_security_headers;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EchoHandler {
|
||||
content: String,
|
||||
handler: Option<ContentHandler>,
|
||||
request: hyper::Request,
|
||||
}
|
||||
|
||||
impl server::Handler<HttpStream> for EchoHandler {
|
||||
fn on_request(&mut self, _: server::Request<HttpStream>) -> Next {
|
||||
Next::read()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
match decoder.read_to_string(&mut self.content) {
|
||||
Ok(0) => {
|
||||
self.handler = Some(ContentHandler::ok(self.content.clone(), mime!(Application/Json)));
|
||||
Next::write()
|
||||
},
|
||||
Ok(_) => Next::read(),
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::read(),
|
||||
_ => Next::end(),
|
||||
}
|
||||
impl EchoHandler {
|
||||
pub fn new(request: hyper::Request) -> Self {
|
||||
EchoHandler {
|
||||
request,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
self.handler.as_mut()
|
||||
.expect("handler always set in on_request, which is before now; qed")
|
||||
.on_response(res)
|
||||
}
|
||||
impl Into<hyper::Response> for EchoHandler {
|
||||
fn into(self) -> hyper::Response {
|
||||
let content_type = self.request.headers().get().cloned();
|
||||
let mut res = hyper::Response::new()
|
||||
.with_header(content_type.unwrap_or(header::ContentType::json()))
|
||||
.with_body(self.request.body());
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
self.handler.as_mut()
|
||||
.expect("handler always set in on_request, which is before now; qed")
|
||||
.on_response_writable(encoder)
|
||||
add_security_headers(res.headers_mut(), None);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
@ -16,57 +16,39 @@
|
||||
|
||||
//! Hyper Server Handler that fetches a file during a request (proxy).
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::{fmt, mem};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Instant, Duration};
|
||||
use fetch::{self, Fetch};
|
||||
use futures::Future;
|
||||
use parity_reactor::Remote;
|
||||
use futures::sync::oneshot;
|
||||
use futures::{self, Future};
|
||||
use hyper::{self, Method, StatusCode};
|
||||
use jsonrpc_core::BoxFuture;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use hyper::{server, Decoder, Encoder, Next, Method, Control};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::uri::RequestUri;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use endpoint::EndpointPath;
|
||||
use endpoint::{self, EndpointPath};
|
||||
use handlers::{ContentHandler, StreamingHandler};
|
||||
use page::{LocalPageEndpoint, PageHandlerWaiting};
|
||||
use page::local;
|
||||
use {Embeddable};
|
||||
|
||||
const FETCH_TIMEOUT: u64 = 300;
|
||||
|
||||
pub enum ValidatorResponse {
|
||||
Local(LocalPageEndpoint),
|
||||
Local(local::Dapp),
|
||||
Streaming(StreamingHandler<fetch::Response>),
|
||||
}
|
||||
|
||||
pub trait ContentValidator: Send + 'static {
|
||||
pub trait ContentValidator: Sized + Send + 'static {
|
||||
type Error: fmt::Debug + fmt::Display;
|
||||
|
||||
fn validate_and_install(&self, fetch::Response) -> Result<ValidatorResponse, Self::Error>;
|
||||
fn validate_and_install(self, fetch::Response) -> Result<ValidatorResponse, Self::Error>;
|
||||
}
|
||||
|
||||
enum FetchState {
|
||||
Waiting,
|
||||
NotStarted(String),
|
||||
Error(ContentHandler),
|
||||
InProgress(mpsc::Receiver<FetchState>),
|
||||
Streaming(StreamingHandler<fetch::Response>),
|
||||
Done(LocalPageEndpoint, Box<PageHandlerWaiting>),
|
||||
}
|
||||
|
||||
enum WaitResult {
|
||||
Error(ContentHandler),
|
||||
Done(LocalPageEndpoint),
|
||||
NonAwaitable,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FetchControl {
|
||||
abort: Arc<AtomicBool>,
|
||||
listeners: Arc<Mutex<Vec<(Control, mpsc::Sender<WaitResult>)>>>,
|
||||
listeners: Arc<Mutex<Vec<oneshot::Sender<WaitResult>>>>,
|
||||
deadline: Instant,
|
||||
}
|
||||
|
||||
@ -81,14 +63,30 @@ impl Default for FetchControl {
|
||||
}
|
||||
|
||||
impl FetchControl {
|
||||
pub fn is_deadline_reached(&self) -> bool {
|
||||
self.deadline < Instant::now()
|
||||
}
|
||||
|
||||
pub fn abort(&self) {
|
||||
self.abort.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn to_response(&self, path: EndpointPath) -> endpoint::Response {
|
||||
let (tx, receiver) = oneshot::channel();
|
||||
self.listeners.lock().push(tx);
|
||||
|
||||
Box::new(WaitingHandler {
|
||||
path,
|
||||
state: WaitState::Waiting(receiver),
|
||||
})
|
||||
}
|
||||
|
||||
fn notify<F: Fn() -> WaitResult>(&self, status: F) {
|
||||
let mut listeners = self.listeners.lock();
|
||||
for (control, sender) in listeners.drain(..) {
|
||||
for sender in listeners.drain(..) {
|
||||
trace!(target: "dapps", "Resuming request waiting for content...");
|
||||
if let Err(e) = sender.send(status()) {
|
||||
trace!(target: "dapps", "Waiting listener notification failed: {:?}", e);
|
||||
} else {
|
||||
let _ = control.ready(Next::read());
|
||||
if let Err(_) = sender.send(status()) {
|
||||
trace!(target: "dapps", "Waiting listener notification failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,92 +96,79 @@ impl FetchControl {
|
||||
FetchState::Error(ref handler) => self.notify(|| WaitResult::Error(handler.clone())),
|
||||
FetchState::Done(ref endpoint, _) => self.notify(|| WaitResult::Done(endpoint.clone())),
|
||||
FetchState::Streaming(_) => self.notify(|| WaitResult::NonAwaitable),
|
||||
FetchState::NotStarted(_) | FetchState::InProgress(_) | FetchState::Waiting => {},
|
||||
FetchState::InProgress(_) => {},
|
||||
FetchState::Empty => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_deadline_reached(&self) -> bool {
|
||||
self.deadline < Instant::now()
|
||||
}
|
||||
|
||||
pub fn abort(&self) {
|
||||
self.abort.store(true, Ordering::SeqCst);
|
||||
}
|
||||
enum WaitState {
|
||||
Waiting(oneshot::Receiver<WaitResult>),
|
||||
Done(endpoint::Response),
|
||||
}
|
||||
|
||||
pub fn to_async_handler(&self, path: EndpointPath, control: Control) -> Box<server::Handler<HttpStream> + Send> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
self.listeners.lock().push((control, tx));
|
||||
|
||||
Box::new(WaitingHandler {
|
||||
receiver: rx,
|
||||
state: FetchState::Waiting,
|
||||
uri: RequestUri::default(),
|
||||
path: path,
|
||||
})
|
||||
}
|
||||
#[derive(Debug)]
|
||||
enum WaitResult {
|
||||
Error(ContentHandler),
|
||||
Done(local::Dapp),
|
||||
NonAwaitable,
|
||||
}
|
||||
|
||||
pub struct WaitingHandler {
|
||||
receiver: mpsc::Receiver<WaitResult>,
|
||||
state: FetchState,
|
||||
uri: RequestUri,
|
||||
path: EndpointPath,
|
||||
state: WaitState,
|
||||
}
|
||||
|
||||
impl server::Handler<HttpStream> for WaitingHandler {
|
||||
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
||||
self.uri = request.uri().clone();
|
||||
Next::wait()
|
||||
}
|
||||
impl Future for WaitingHandler {
|
||||
type Item = hyper::Response;
|
||||
type Error = hyper::Error;
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
let result = self.receiver.try_recv().ok();
|
||||
self.state = match result {
|
||||
Some(WaitResult::Error(handler)) => FetchState::Error(handler),
|
||||
Some(WaitResult::Done(endpoint)) => {
|
||||
let mut page_handler = endpoint.to_page_handler(self.path.clone());
|
||||
page_handler.set_uri(&self.uri);
|
||||
FetchState::Done(endpoint, page_handler)
|
||||
},
|
||||
_ => {
|
||||
warn!("A result for waiting request was not received.");
|
||||
FetchState::Waiting
|
||||
},
|
||||
};
|
||||
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
let new_state = match self.state {
|
||||
WaitState::Waiting(ref mut receiver) => {
|
||||
let result = try_ready!(receiver.poll().map_err(|_| hyper::Error::Timeout));
|
||||
|
||||
match self.state {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_request_readable(decoder),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_request_readable(decoder),
|
||||
FetchState::Error(ref mut handler) => handler.on_request_readable(decoder),
|
||||
_ => Next::write(),
|
||||
}
|
||||
}
|
||||
match result {
|
||||
WaitResult::Error(handler) => {
|
||||
return Ok(futures::Async::Ready(handler.into()));
|
||||
},
|
||||
WaitResult::NonAwaitable => {
|
||||
let errors = Errors { embeddable_on: None };
|
||||
return Ok(futures::Async::Ready(errors.streaming().into()));
|
||||
},
|
||||
WaitResult::Done(endpoint) => {
|
||||
WaitState::Done(endpoint.to_response(&self.path).into())
|
||||
},
|
||||
}
|
||||
},
|
||||
WaitState::Done(ref mut response) => {
|
||||
return response.poll()
|
||||
},
|
||||
};
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
match self.state {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response(res),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response(res),
|
||||
FetchState::Error(ref mut handler) => handler.on_response(res),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
match self.state {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Error(ref mut handler) => handler.on_response_writable(encoder),
|
||||
_ => Next::end(),
|
||||
self.state = new_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct Errors {
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl Errors {
|
||||
fn streaming(&self) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
"Streaming Error",
|
||||
"This content is being streamed in other place.",
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn download_error<E: fmt::Debug>(&self, e: E) -> ContentHandler {
|
||||
ContentHandler::error(
|
||||
StatusCode::BadGateway,
|
||||
@ -225,67 +210,102 @@ impl Errors {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContentFetcherHandler<H: ContentValidator, F: Fetch> {
|
||||
enum FetchState {
|
||||
Error(ContentHandler),
|
||||
InProgress(BoxFuture<FetchState, ()>),
|
||||
Streaming(hyper::Response),
|
||||
Done(local::Dapp, endpoint::Response),
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FetchState {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::FetchState::*;
|
||||
|
||||
write!(fmt, "FetchState(")?;
|
||||
match *self {
|
||||
Error(ref error) => write!(fmt, "error: {:?}", error),
|
||||
InProgress(_) => write!(fmt, "in progress"),
|
||||
Streaming(ref res) => write!(fmt, "streaming: {:?}", res),
|
||||
Done(ref endpoint, _) => write!(fmt, "done: {:?}", endpoint),
|
||||
Empty => write!(fmt, "?"),
|
||||
}?;
|
||||
write!(fmt, ")")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ContentFetcherHandler {
|
||||
fetch_control: FetchControl,
|
||||
control: Control,
|
||||
remote: Remote,
|
||||
status: FetchState,
|
||||
fetch: F,
|
||||
installer: Option<H>,
|
||||
path: EndpointPath,
|
||||
errors: Errors,
|
||||
}
|
||||
|
||||
impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
||||
pub fn new(
|
||||
url: String,
|
||||
path: EndpointPath,
|
||||
control: Control,
|
||||
installer: H,
|
||||
embeddable_on: Embeddable,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
) -> Self {
|
||||
ContentFetcherHandler {
|
||||
fetch_control: FetchControl::default(),
|
||||
control,
|
||||
remote,
|
||||
fetch,
|
||||
status: FetchState::NotStarted(url),
|
||||
installer: Some(installer),
|
||||
path,
|
||||
errors: Errors {
|
||||
embeddable_on,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl ContentFetcherHandler {
|
||||
pub fn fetch_control(&self) -> FetchControl {
|
||||
self.fetch_control.clone()
|
||||
}
|
||||
|
||||
fn fetch_content(&self, uri: RequestUri, url: &str, installer: H) -> mpsc::Receiver<FetchState> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let abort = self.fetch_control.abort.clone();
|
||||
pub fn new<H: ContentValidator, F: Fetch>(
|
||||
method: &hyper::Method,
|
||||
url: &str,
|
||||
path: EndpointPath,
|
||||
installer: H,
|
||||
embeddable_on: Embeddable,
|
||||
fetch: F,
|
||||
) -> Self {
|
||||
let fetch_control = FetchControl::default();
|
||||
let errors = Errors { embeddable_on };
|
||||
|
||||
let path = self.path.clone();
|
||||
let tx2 = tx.clone();
|
||||
let control = self.control.clone();
|
||||
let errors = self.errors.clone();
|
||||
// Validation of method
|
||||
let status = match *method {
|
||||
// Start fetching content
|
||||
Method::Get => {
|
||||
trace!(target: "dapps", "Fetching content from: {:?}", url);
|
||||
FetchState::InProgress(Self::fetch_content(
|
||||
fetch,
|
||||
url,
|
||||
fetch_control.abort.clone(),
|
||||
path,
|
||||
errors.clone(),
|
||||
installer,
|
||||
))
|
||||
},
|
||||
// or return error
|
||||
_ => FetchState::Error(errors.method_not_allowed()),
|
||||
};
|
||||
|
||||
let future = self.fetch.fetch_with_abort(url, abort.into()).then(move |result| {
|
||||
ContentFetcherHandler {
|
||||
fetch_control,
|
||||
status,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_content<H: ContentValidator, F: Fetch>(
|
||||
fetch: F,
|
||||
url: &str,
|
||||
abort: Arc<AtomicBool>,
|
||||
path: EndpointPath,
|
||||
errors: Errors,
|
||||
installer: H,
|
||||
) -> BoxFuture<FetchState, ()> {
|
||||
// Start fetching the content
|
||||
let fetch2 = fetch.clone();
|
||||
let future = fetch.fetch_with_abort(url, abort.into()).then(move |result| {
|
||||
trace!(target: "dapps", "Fetching content finished. Starting validation: {:?}", result);
|
||||
let new_state = match result {
|
||||
Ok(match result {
|
||||
Ok(response) => match installer.validate_and_install(response) {
|
||||
Ok(ValidatorResponse::Local(endpoint)) => {
|
||||
trace!(target: "dapps", "Validation OK. Returning response.");
|
||||
let mut handler = endpoint.to_page_handler(path);
|
||||
handler.set_uri(&uri);
|
||||
FetchState::Done(endpoint, handler)
|
||||
let response = endpoint.to_response(&path);
|
||||
FetchState::Done(endpoint, response)
|
||||
},
|
||||
Ok(ValidatorResponse::Streaming(handler)) => {
|
||||
Ok(ValidatorResponse::Streaming(stream)) => {
|
||||
trace!(target: "dapps", "Validation OK. Streaming response.");
|
||||
FetchState::Streaming(handler)
|
||||
let (reading, response) = stream.into_response();
|
||||
fetch2.process_and_forget(reading);
|
||||
FetchState::Streaming(response)
|
||||
},
|
||||
Err(e) => {
|
||||
trace!(target: "dapps", "Error while validating content: {:?}", e);
|
||||
@ -296,100 +316,55 @@ impl<H: ContentValidator, F: Fetch> ContentFetcherHandler<H, F> {
|
||||
warn!(target: "dapps", "Unable to fetch content: {:?}", e);
|
||||
FetchState::Error(errors.download_error(e))
|
||||
},
|
||||
};
|
||||
// Content may be resolved when the connection is already dropped.
|
||||
let _ = tx2.send(new_state);
|
||||
// Ignoring control errors
|
||||
let _ = control.ready(Next::read());
|
||||
Ok(()) as Result<(), ()>
|
||||
})
|
||||
});
|
||||
|
||||
// make sure to run within fetch thread pool.
|
||||
let future = self.fetch.process(future);
|
||||
// spawn to event loop
|
||||
let control = self.control.clone();
|
||||
let errors = self.errors.clone();
|
||||
self.remote.spawn_with_timeout(|| future, Duration::from_secs(FETCH_TIMEOUT), move || {
|
||||
// Notify about the timeout
|
||||
let _ = tx.send(FetchState::Error(errors.timeout_error()));
|
||||
// Ignoring control errors
|
||||
let _ = control.ready(Next::read());
|
||||
});
|
||||
|
||||
rx
|
||||
fetch.process(future)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: ContentValidator, F: Fetch> server::Handler<HttpStream> for ContentFetcherHandler<H, F> {
|
||||
fn on_request(&mut self, request: server::Request<HttpStream>) -> Next {
|
||||
let status = if let FetchState::NotStarted(ref url) = self.status {
|
||||
let uri = request.uri().clone();
|
||||
let installer = self.installer.take().expect("Installer always set initialy; installer used only in on_request; on_request invoked only once; qed");
|
||||
impl Future for ContentFetcherHandler {
|
||||
type Item = hyper::Response;
|
||||
type Error = hyper::Error;
|
||||
|
||||
Some(match *request.method() {
|
||||
// Start fetching content
|
||||
Method::Get => {
|
||||
trace!(target: "dapps", "Fetching content from: {:?}", url);
|
||||
let receiver = self.fetch_content(uri, url, installer);
|
||||
FetchState::InProgress(receiver)
|
||||
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
trace!(target: "dapps", "Polling status: {:?}", self.status);
|
||||
self.status = match mem::replace(&mut self.status, FetchState::Empty) {
|
||||
FetchState::Error(error) => {
|
||||
return Ok(futures::Async::Ready(error.into()));
|
||||
},
|
||||
// or return error
|
||||
_ => FetchState::Error(self.errors.method_not_allowed()),
|
||||
})
|
||||
} else { None };
|
||||
FetchState::Streaming(response) => {
|
||||
return Ok(futures::Async::Ready(response));
|
||||
},
|
||||
any => any,
|
||||
};
|
||||
|
||||
if let Some(status) = status {
|
||||
let status = match self.status {
|
||||
// Request may time out
|
||||
FetchState::InProgress(_) if self.fetch_control.is_deadline_reached() => {
|
||||
trace!(target: "dapps", "Fetching dapp failed because of timeout.");
|
||||
FetchState::Error(self.errors.timeout_error())
|
||||
},
|
||||
FetchState::InProgress(ref mut receiver) => {
|
||||
// Check if there is a response
|
||||
trace!(target: "dapps", "Polling streaming response.");
|
||||
try_ready!(receiver.poll().map_err(|err| {
|
||||
warn!(target: "dapps", "Error while fetching response: {:?}", err);
|
||||
hyper::Error::Timeout
|
||||
}))
|
||||
},
|
||||
FetchState::Done(_, ref mut response) => {
|
||||
return response.poll()
|
||||
},
|
||||
FetchState::Empty => panic!("Future polled twice."),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
trace!(target: "dapps", "New status: {:?}", status);
|
||||
self.fetch_control.set_status(&status);
|
||||
self.status = status;
|
||||
}
|
||||
|
||||
Next::read()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
let (status, next) = match self.status {
|
||||
// Request may time out
|
||||
FetchState::InProgress(_) if self.fetch_control.is_deadline_reached() => {
|
||||
trace!(target: "dapps", "Fetching dapp failed because of timeout.");
|
||||
(Some(FetchState::Error(self.errors.timeout_error())), Next::write())
|
||||
},
|
||||
FetchState::InProgress(ref receiver) => {
|
||||
// Check if there is an answer
|
||||
let rec = receiver.try_recv();
|
||||
match rec {
|
||||
// just return the new state
|
||||
Ok(state) => (Some(state), Next::write()),
|
||||
// wait some more
|
||||
_ => (None, Next::wait())
|
||||
}
|
||||
},
|
||||
FetchState::Error(ref mut handler) => (None, handler.on_request_readable(decoder)),
|
||||
_ => (None, Next::write()),
|
||||
};
|
||||
|
||||
if let Some(status) = status {
|
||||
self.fetch_control.set_status(&status);
|
||||
self.status = status;
|
||||
}
|
||||
|
||||
next
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
match self.status {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response(res),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response(res),
|
||||
FetchState::Error(ref mut handler) => handler.on_response(res),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
match self.status {
|
||||
FetchState::Done(_, ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Streaming(ref mut handler) => handler.on_response_writable(encoder),
|
||||
FetchState::Error(ref mut handler) => handler.on_response_writable(encoder),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,80 +16,79 @@
|
||||
|
||||
//! Hyper handlers implementations.
|
||||
|
||||
mod async;
|
||||
mod content;
|
||||
mod echo;
|
||||
mod fetch;
|
||||
mod reader;
|
||||
mod redirect;
|
||||
mod streaming;
|
||||
|
||||
pub use self::async::AsyncHandler;
|
||||
pub use self::content::ContentHandler;
|
||||
pub use self::echo::EchoHandler;
|
||||
pub use self::fetch::{ContentFetcherHandler, ContentValidator, FetchControl, ValidatorResponse};
|
||||
pub use self::reader::Reader;
|
||||
pub use self::redirect::Redirection;
|
||||
pub use self::streaming::StreamingHandler;
|
||||
|
||||
use std::iter;
|
||||
use itertools::Itertools;
|
||||
use url::Url;
|
||||
use hyper::{server, header, net, uri};
|
||||
use hyper::header;
|
||||
use {apps, address, Embeddable};
|
||||
|
||||
/// Adds security-related headers to the Response.
|
||||
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-Content-Type-Options", vec![b"nosniff".to_vec()]);
|
||||
headers.set_raw("X-XSS-Protection", "1; mode=block");
|
||||
headers.set_raw("X-Content-Type-Options", "nosniff");
|
||||
|
||||
// Embedding header:
|
||||
if let None = embeddable_on {
|
||||
headers.set_raw("X-Frame-Options", vec![b"SAMEORIGIN".to_vec()]);
|
||||
headers.set_raw("X-Frame-Options", "SAMEORIGIN");
|
||||
}
|
||||
|
||||
// Content Security Policy headers
|
||||
headers.set_raw("Content-Security-Policy", vec![
|
||||
headers.set_raw("Content-Security-Policy", String::new()
|
||||
// Allow connecting to WS servers and HTTP(S) servers.
|
||||
// We could be more restrictive and allow only RPC server URL.
|
||||
b"connect-src http: https: ws: wss:;".to_vec(),
|
||||
+ "connect-src http: https: ws: wss:;"
|
||||
// Allow framing any content from HTTP(S).
|
||||
// Again we could only allow embedding from RPC server URL.
|
||||
// (deprecated)
|
||||
b"frame-src 'self' http: https:;".to_vec(),
|
||||
+ "frame-src 'self' http: https:;"
|
||||
// Allow framing and web workers from HTTP(S).
|
||||
b"child-src 'self' http: https:;".to_vec(),
|
||||
+ "child-src 'self' http: https:;"
|
||||
// We allow data: blob: and HTTP(s) images.
|
||||
// We could get rid of wildcarding HTTP and only allow RPC server URL.
|
||||
// (http required for local dapps icons)
|
||||
b"img-src 'self' 'unsafe-inline' data: blob: http: https:;".to_vec(),
|
||||
+ "img-src 'self' 'unsafe-inline' data: blob: http: https:;"
|
||||
// Allow style from data: blob: and HTTPS.
|
||||
b"style-src 'self' 'unsafe-inline' data: blob: https:;".to_vec(),
|
||||
+ "style-src 'self' 'unsafe-inline' data: blob: https:;"
|
||||
// Allow fonts from data: and HTTPS.
|
||||
b"font-src 'self' data: https:;".to_vec(),
|
||||
+ "font-src 'self' data: https:;"
|
||||
// Allow inline scripts and scripts eval (webpack/jsconsole)
|
||||
{
|
||||
+ {
|
||||
let script_src = embeddable_on.as_ref()
|
||||
.map(|e| e.extra_script_src.iter()
|
||||
.map(|&(ref host, port)| address(host, port))
|
||||
.join(" ")
|
||||
).unwrap_or_default();
|
||||
format!(
|
||||
&format!(
|
||||
"script-src 'self' 'unsafe-inline' 'unsafe-eval' {};",
|
||||
script_src
|
||||
).into_bytes()
|
||||
},
|
||||
)
|
||||
}
|
||||
// Same restrictions as script-src with additional
|
||||
// blob: that is required for camera access (worker)
|
||||
b"worker-src 'self' 'unsafe-inline' 'unsafe-eval' https: blob:;".to_vec(),
|
||||
+ "worker-src 'self' 'unsafe-inline' 'unsafe-eval' https: blob:;"
|
||||
// Restrict everything else to the same origin.
|
||||
b"default-src 'self';".to_vec(),
|
||||
+ "default-src 'self';"
|
||||
// Run in sandbox mode (although it's not fully safe since we allow same-origin and script)
|
||||
b"sandbox allow-same-origin allow-forms allow-modals allow-popups allow-presentation allow-scripts;".to_vec(),
|
||||
+ "sandbox allow-same-origin allow-forms allow-modals allow-popups allow-presentation allow-scripts;"
|
||||
// Disallow subitting forms from any dapps
|
||||
b"form-action 'none';".to_vec(),
|
||||
+ "form-action 'none';"
|
||||
// Never allow mixed content
|
||||
b"block-all-mixed-content;".to_vec(),
|
||||
+ "block-all-mixed-content;"
|
||||
// Specify if the site can be embedded.
|
||||
match embeddable_on {
|
||||
+ &match embeddable_on {
|
||||
Some(ref embed) => {
|
||||
let std = address(&embed.host, embed.port);
|
||||
let proxy = format!("{}.{}", apps::HOME_PAGE, embed.dapps_domain);
|
||||
@ -112,43 +111,6 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd
|
||||
format!("frame-ancestors {};", ancestors)
|
||||
},
|
||||
None => format!("frame-ancestors 'self';"),
|
||||
}.into_bytes(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/// Extracts URL part from the Request.
|
||||
pub fn extract_url(req: &server::Request<net::HttpStream>) -> Option<Url> {
|
||||
convert_uri_to_url(req.uri(), req.headers().get::<header::Host>())
|
||||
}
|
||||
|
||||
/// Extracts URL given URI and Host header.
|
||||
pub fn convert_uri_to_url(uri: &uri::RequestUri, host: Option<&header::Host>) -> Option<Url> {
|
||||
match *uri {
|
||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||
match Url::from_generic_url(url.clone()) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
uri::RequestUri::AbsolutePath { ref path, ref query } => {
|
||||
let query = match *query {
|
||||
Some(ref query) => format!("?{}", query),
|
||||
None => "".into(),
|
||||
};
|
||||
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
||||
let url_string = match host {
|
||||
Some(ref host) => {
|
||||
format!("http://{}:{}{}{}", host.hostname, host.port.unwrap_or(80), path, query)
|
||||
},
|
||||
None => return None,
|
||||
};
|
||||
|
||||
match Url::parse(&url_string) {
|
||||
Ok(url) => Some(url),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
73
dapps/src/handlers/reader.rs
Normal file
73
dapps/src/handlers/reader.rs
Normal file
@ -0,0 +1,73 @@
|
||||
// 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/>.
|
||||
|
||||
//! A chunk-producing io::Read wrapper.
|
||||
|
||||
use std::io::{self, Read};
|
||||
|
||||
use futures::{self, sink, Sink, Future};
|
||||
use futures::sync::mpsc;
|
||||
use hyper;
|
||||
|
||||
type Sender = mpsc::Sender<Result<hyper::Chunk, hyper::Error>>;
|
||||
|
||||
const MAX_CHUNK_SIZE: usize = 32 * 1024;
|
||||
|
||||
/// A Reader is essentially a stream of `hyper::Chunks`.
|
||||
/// The chunks are read from given `io::Read` instance.
|
||||
///
|
||||
/// Unfortunately `hyper` doesn't allow you to pass `Stream`
|
||||
/// directly to the response, so you need to create
|
||||
/// a `Body::pair()` and send over chunks using `sink::Send`.
|
||||
/// Also `Chunks` need to take `Vec` by value, so we need
|
||||
/// to allocate it for each chunk being sent.
|
||||
pub struct Reader<R: io::Read> {
|
||||
buffer: [u8; MAX_CHUNK_SIZE],
|
||||
content: io::BufReader<R>,
|
||||
sending: sink::Send<Sender>,
|
||||
}
|
||||
|
||||
impl<R: io::Read> Reader<R> {
|
||||
pub fn pair(content: R, initial: Vec<u8>) -> (Self, hyper::Body) {
|
||||
let (tx, rx) = hyper::Body::pair();
|
||||
let reader = Reader {
|
||||
buffer: [0; MAX_CHUNK_SIZE],
|
||||
content: io::BufReader::new(content),
|
||||
sending: tx.send(Ok(initial.into())),
|
||||
};
|
||||
|
||||
(reader, rx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: io::Read> Future for Reader<R> {
|
||||
type Item = ();
|
||||
type Error = ();
|
||||
|
||||
fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
|
||||
loop {
|
||||
let next = try_ready!(self.sending.poll().map_err(|err| {
|
||||
warn!(target: "dapps", "Unable to send next chunk: {:?}", err);
|
||||
}));
|
||||
|
||||
self.sending = match self.content.read(&mut self.buffer) {
|
||||
Ok(0) => return Ok(futures::Async::Ready(())),
|
||||
Ok(read) => next.send(Ok(self.buffer[..read].to_vec().into())),
|
||||
Err(err) => next.send(Err(hyper::Error::Io(err))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -16,9 +16,7 @@
|
||||
|
||||
//! HTTP Redirection hyper handler
|
||||
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::{self, header, StatusCode};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Redirection {
|
||||
@ -26,36 +24,18 @@ pub struct Redirection {
|
||||
}
|
||||
|
||||
impl Redirection {
|
||||
pub fn new(url: &str) -> Self {
|
||||
pub fn new<T: Into<String>>(url: T) -> Self {
|
||||
Redirection {
|
||||
to_url: url.to_owned()
|
||||
to_url: url.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn boxed(url: &str) -> Box<Self> {
|
||||
Box::new(Self::new(url))
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Handler<HttpStream> for Redirection {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
impl Into<hyper::Response> for Redirection {
|
||||
fn into(self) -> hyper::Response {
|
||||
// Don't use `MovedPermanently` here to prevent browser from caching the redirections.
|
||||
res.set_status(StatusCode::Found);
|
||||
res.headers_mut().set(header::Location(self.to_url.to_owned()));
|
||||
Next::write()
|
||||
}
|
||||
fn on_response_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
Next::end()
|
||||
hyper::Response::new()
|
||||
.with_status(StatusCode::Found)
|
||||
.with_header(header::Location::new(self.to_url))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -16,87 +16,43 @@
|
||||
|
||||
//! Content Stream Response
|
||||
|
||||
use std::io::{self, Read};
|
||||
use std::io;
|
||||
use hyper::{self, header, mime, StatusCode};
|
||||
|
||||
use hyper::{header, server, Decoder, Encoder, Next};
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::mime::Mime;
|
||||
use hyper::status::StatusCode;
|
||||
|
||||
use handlers::add_security_headers;
|
||||
use handlers::{add_security_headers, Reader};
|
||||
use Embeddable;
|
||||
|
||||
const BUFFER_SIZE: usize = 1024;
|
||||
|
||||
pub struct StreamingHandler<R: io::Read> {
|
||||
buffer: [u8; BUFFER_SIZE],
|
||||
buffer_leftover: usize,
|
||||
pub struct StreamingHandler<R> {
|
||||
initial: Vec<u8>,
|
||||
content: R,
|
||||
status: StatusCode,
|
||||
content: io::BufReader<R>,
|
||||
mimetype: Mime,
|
||||
mimetype: mime::Mime,
|
||||
safe_to_embed_on: Embeddable,
|
||||
}
|
||||
|
||||
impl<R: io::Read> StreamingHandler<R> {
|
||||
pub fn new(content: R, status: StatusCode, mimetype: Mime, embeddable_on: Embeddable) -> Self {
|
||||
pub fn new(content: R, status: StatusCode, mimetype: mime::Mime, safe_to_embed_on: Embeddable) -> Self {
|
||||
StreamingHandler {
|
||||
buffer: [0; BUFFER_SIZE],
|
||||
buffer_leftover: 0,
|
||||
status: status,
|
||||
content: io::BufReader::new(content),
|
||||
mimetype: mimetype,
|
||||
safe_to_embed_on: embeddable_on,
|
||||
initial: Vec::new(),
|
||||
content,
|
||||
status,
|
||||
mimetype,
|
||||
safe_to_embed_on,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_initial_content(&mut self, content: &str) {
|
||||
assert_eq!(self.buffer_leftover, 0);
|
||||
let bytes = content.as_bytes();
|
||||
self.buffer_leftover = bytes.len();
|
||||
self.buffer[0..self.buffer_leftover].copy_from_slice(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: io::Read> server::Handler<HttpStream> for StreamingHandler<R> {
|
||||
fn on_request(&mut self, _request: server::Request<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
res.set_status(self.status);
|
||||
res.headers_mut().set(header::ContentType(self.mimetype.clone()));
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
fn handle_error(e: io::Error) -> Next {
|
||||
match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end(),
|
||||
}
|
||||
}
|
||||
|
||||
let write_pos = self.buffer_leftover;
|
||||
match self.content.read(&mut self.buffer[write_pos..]) {
|
||||
Err(e) => handle_error(e),
|
||||
Ok(read) => match encoder.write(&self.buffer[..write_pos + read]) {
|
||||
Err(e) => handle_error(e),
|
||||
Ok(0) => Next::end(),
|
||||
Ok(wrote) => {
|
||||
self.buffer_leftover = write_pos + read - wrote;
|
||||
if self.buffer_leftover > 0 {
|
||||
for i in self.buffer_leftover..write_pos + read {
|
||||
self.buffer.swap(i, i - self.buffer_leftover);
|
||||
}
|
||||
}
|
||||
Next::write()
|
||||
},
|
||||
},
|
||||
}
|
||||
self.initial = content.as_bytes().to_vec();
|
||||
}
|
||||
|
||||
pub fn into_response(self) -> (Reader<R>, hyper::Response) {
|
||||
let (reader, body) = Reader::pair(self.content, self.initial);
|
||||
let mut res = hyper::Response::new()
|
||||
.with_status(self.status)
|
||||
.with_header(header::ContentType(self.mimetype))
|
||||
.with_body(body);
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on);
|
||||
|
||||
(reader, res)
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
#![cfg_attr(feature="nightly", plugin(clippy))]
|
||||
|
||||
extern crate base32;
|
||||
extern crate futures;
|
||||
extern crate futures_cpupool;
|
||||
extern crate itertools;
|
||||
extern crate linked_hash_map;
|
||||
extern crate mime_guess;
|
||||
@ -29,9 +29,7 @@ extern crate rand;
|
||||
extern crate rustc_hex;
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
extern crate time;
|
||||
extern crate unicase;
|
||||
extern crate url as url_lib;
|
||||
extern crate zip;
|
||||
|
||||
extern crate jsonrpc_core;
|
||||
@ -44,14 +42,13 @@ extern crate fetch;
|
||||
extern crate node_health;
|
||||
extern crate parity_dapps_glue as parity_dapps;
|
||||
extern crate parity_hash_fetch as hash_fetch;
|
||||
extern crate parity_reactor;
|
||||
extern crate parity_ui;
|
||||
extern crate hash;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate futures;
|
||||
#[macro_use]
|
||||
extern crate mime;
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
@ -59,6 +56,8 @@ extern crate serde_derive;
|
||||
extern crate ethcore_devtools as devtools;
|
||||
#[cfg(test)]
|
||||
extern crate env_logger;
|
||||
#[cfg(test)]
|
||||
extern crate parity_reactor;
|
||||
|
||||
mod endpoint;
|
||||
mod apps;
|
||||
@ -67,7 +66,6 @@ mod router;
|
||||
mod handlers;
|
||||
mod api;
|
||||
mod proxypac;
|
||||
mod url;
|
||||
mod web;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@ -76,13 +74,12 @@ use std::collections::HashMap;
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use futures_cpupool::CpuPool;
|
||||
use jsonrpc_http_server::{self as http, hyper, Origin};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use fetch::Fetch;
|
||||
use node_health::NodeHealth;
|
||||
use parity_reactor::Remote;
|
||||
|
||||
pub use hash_fetch::urlhint::ContractClient;
|
||||
pub use node_health::SyncStatus;
|
||||
@ -105,6 +102,7 @@ pub struct Endpoints {
|
||||
endpoints: Arc<RwLock<endpoint::Endpoints>>,
|
||||
dapps_path: PathBuf,
|
||||
embeddable: Option<ParentFrameSettings>,
|
||||
pool: Option<CpuPool>,
|
||||
}
|
||||
|
||||
impl Endpoints {
|
||||
@ -117,7 +115,11 @@ impl Endpoints {
|
||||
|
||||
/// Check for any changes in the local dapps folder and update.
|
||||
pub fn refresh_local_dapps(&self) {
|
||||
let new_local = apps::fs::local_endpoints(&self.dapps_path, self.embeddable.clone());
|
||||
let pool = match self.pool.as_ref() {
|
||||
None => return,
|
||||
Some(pool) => pool,
|
||||
};
|
||||
let new_local = apps::fs::local_endpoints(&self.dapps_path, self.embeddable.clone(), pool.clone());
|
||||
let old_local = mem::replace(&mut *self.local_endpoints.write(), new_local.keys().cloned().collect());
|
||||
let (_, to_remove): (_, Vec<_>) = old_local
|
||||
.into_iter()
|
||||
@ -151,8 +153,8 @@ impl Middleware {
|
||||
|
||||
/// Creates new middleware for UI server.
|
||||
pub fn ui<F: Fetch>(
|
||||
pool: CpuPool,
|
||||
health: NodeHealth,
|
||||
remote: Remote,
|
||||
dapps_domain: &str,
|
||||
registrar: Arc<ContractClient>,
|
||||
sync_status: Arc<SyncStatus>,
|
||||
@ -161,16 +163,16 @@ impl Middleware {
|
||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||
sync_status.clone(),
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
pool.clone(),
|
||||
).embeddable_on(None).allow_dapps(false));
|
||||
let special = {
|
||||
let mut special = special_endpoints(
|
||||
pool.clone(),
|
||||
health,
|
||||
content_fetcher.clone(),
|
||||
remote.clone(),
|
||||
);
|
||||
special.insert(router::SpecialEndpoint::Home, Some(apps::ui()));
|
||||
special.insert(router::SpecialEndpoint::Home, Some(apps::ui(pool.clone())));
|
||||
special
|
||||
};
|
||||
let router = router::Router::new(
|
||||
@ -189,8 +191,8 @@ impl Middleware {
|
||||
|
||||
/// Creates new Dapps server middleware.
|
||||
pub fn dapps<F: Fetch>(
|
||||
pool: CpuPool,
|
||||
health: NodeHealth,
|
||||
remote: Remote,
|
||||
ui_address: Option<(String, u16)>,
|
||||
extra_embed_on: Vec<(String, u16)>,
|
||||
extra_script_src: Vec<(String, u16)>,
|
||||
@ -206,8 +208,8 @@ impl Middleware {
|
||||
let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new(
|
||||
hash_fetch::urlhint::URLHintContract::new(registrar),
|
||||
sync_status.clone(),
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
pool.clone(),
|
||||
).embeddable_on(embeddable.clone()).allow_dapps(true));
|
||||
let (local_endpoints, endpoints) = apps::all_endpoints(
|
||||
dapps_path.clone(),
|
||||
@ -215,21 +217,22 @@ impl Middleware {
|
||||
dapps_domain,
|
||||
embeddable.clone(),
|
||||
web_proxy_tokens,
|
||||
remote.clone(),
|
||||
fetch.clone(),
|
||||
pool.clone(),
|
||||
);
|
||||
let endpoints = Endpoints {
|
||||
endpoints: Arc::new(RwLock::new(endpoints)),
|
||||
dapps_path,
|
||||
local_endpoints: Arc::new(RwLock::new(local_endpoints)),
|
||||
embeddable: embeddable.clone(),
|
||||
pool: Some(pool.clone()),
|
||||
};
|
||||
|
||||
let special = {
|
||||
let mut special = special_endpoints(
|
||||
pool.clone(),
|
||||
health,
|
||||
content_fetcher.clone(),
|
||||
remote.clone(),
|
||||
);
|
||||
special.insert(
|
||||
router::SpecialEndpoint::Home,
|
||||
@ -254,23 +257,22 @@ impl Middleware {
|
||||
}
|
||||
|
||||
impl http::RequestMiddleware for Middleware {
|
||||
fn on_request(&self, req: &hyper::server::Request<hyper::net::HttpStream>, control: &hyper::Control) -> http::RequestMiddlewareAction {
|
||||
self.router.on_request(req, control)
|
||||
fn on_request(&self, req: hyper::Request) -> http::RequestMiddlewareAction {
|
||||
self.router.on_request(req)
|
||||
}
|
||||
}
|
||||
|
||||
fn special_endpoints(
|
||||
pool: CpuPool,
|
||||
health: NodeHealth,
|
||||
content_fetcher: Arc<apps::fetcher::Fetcher>,
|
||||
remote: Remote,
|
||||
) -> HashMap<router::SpecialEndpoint, Option<Box<endpoint::Endpoint>>> {
|
||||
let mut special = HashMap::new();
|
||||
special.insert(router::SpecialEndpoint::Rpc, None);
|
||||
special.insert(router::SpecialEndpoint::Utils, Some(apps::utils()));
|
||||
special.insert(router::SpecialEndpoint::Utils, Some(apps::utils(pool)));
|
||||
special.insert(router::SpecialEndpoint::Api, Some(api::RestApi::new(
|
||||
content_fetcher,
|
||||
health,
|
||||
remote,
|
||||
)));
|
||||
special
|
||||
}
|
||||
|
@ -14,71 +14,62 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::io;
|
||||
use futures::future;
|
||||
use futures_cpupool::CpuPool;
|
||||
use hyper::mime::{self, Mime};
|
||||
use itertools::Itertools;
|
||||
use parity_dapps::{WebApp, Info};
|
||||
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Request, Response};
|
||||
use page::{handler, PageCache};
|
||||
use std::sync::Arc;
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||
use parity_dapps::{WebApp, File, Info};
|
||||
use Embeddable;
|
||||
|
||||
pub struct PageEndpoint<T : WebApp + 'static> {
|
||||
pub struct Dapp<T: WebApp + 'static> {
|
||||
/// futures cpu pool
|
||||
pool: CpuPool,
|
||||
/// Content of the files
|
||||
pub app: Arc<T>,
|
||||
/// Prefix to strip from the path (when `None` deducted from `app_id`)
|
||||
pub prefix: Option<String>,
|
||||
app: T,
|
||||
/// Safe to be loaded in frame by other origin. (use wisely!)
|
||||
safe_to_embed_on: Embeddable,
|
||||
info: EndpointInfo,
|
||||
fallback_to_index_html: bool,
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||
/// Creates new `PageEndpoint` for builtin (compile time) Dapp.
|
||||
pub fn new(app: T) -> Self {
|
||||
impl<T: WebApp + 'static> Dapp<T> {
|
||||
/// Creates new `Dapp` for builtin (compile time) Dapp.
|
||||
pub fn new(pool: CpuPool, app: T) -> Self {
|
||||
let info = app.info();
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: None,
|
||||
Dapp {
|
||||
pool,
|
||||
app,
|
||||
safe_to_embed_on: None,
|
||||
info: EndpointInfo::from(info),
|
||||
fallback_to_index_html: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `PageEndpoint` for builtin (compile time) Dapp.
|
||||
/// Creates a new `Dapp` for builtin (compile time) Dapp.
|
||||
/// Instead of returning 404 this endpoint will always server index.html.
|
||||
pub fn with_fallback_to_index(app: T) -> Self {
|
||||
pub fn with_fallback_to_index(pool: CpuPool, app: T) -> Self {
|
||||
let info = app.info();
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: None,
|
||||
Dapp {
|
||||
pool,
|
||||
app,
|
||||
safe_to_embed_on: None,
|
||||
info: EndpointInfo::from(info),
|
||||
fallback_to_index_html: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new `PageEndpoint` and specify prefix that should be removed before looking for a file.
|
||||
/// It's used only for special endpoints (i.e. `/parity-utils/`)
|
||||
/// So `/parity-utils/inject.js` will be resolved to `/inject.js` is prefix is set.
|
||||
pub fn with_prefix(app: T, prefix: String) -> Self {
|
||||
let info = app.info();
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: Some(prefix),
|
||||
safe_to_embed_on: None,
|
||||
info: EndpointInfo::from(info),
|
||||
fallback_to_index_html: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new `PageEndpoint` which can be safely used in iframe
|
||||
/// Creates new `Dapp` which can be safely used in iframe
|
||||
/// even from different origin. It might be dangerous (clickjacking).
|
||||
/// Use wisely!
|
||||
pub fn new_safe_to_embed(app: T, address: Embeddable) -> Self {
|
||||
pub fn new_safe_to_embed(pool: CpuPool, app: T, address: Embeddable) -> Self {
|
||||
let info = app.info();
|
||||
PageEndpoint {
|
||||
app: Arc::new(app),
|
||||
prefix: None,
|
||||
Dapp {
|
||||
pool,
|
||||
app,
|
||||
safe_to_embed_on: address,
|
||||
info: EndpointInfo::from(info),
|
||||
fallback_to_index_html: false,
|
||||
@ -86,21 +77,51 @@ impl<T: WebApp + 'static> PageEndpoint<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WebApp> Endpoint for PageEndpoint<T> {
|
||||
|
||||
impl<T: WebApp> Endpoint for Dapp<T> {
|
||||
fn info(&self) -> Option<&EndpointInfo> {
|
||||
Some(&self.info)
|
||||
}
|
||||
|
||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||
Box::new(handler::PageHandler {
|
||||
app: BuiltinDapp::new(self.app.clone(), self.fallback_to_index_html),
|
||||
prefix: self.prefix.clone(),
|
||||
path: path,
|
||||
file: handler::ServedFile::new(self.safe_to_embed_on.clone()),
|
||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
||||
trace!(target: "dapps", "Builtin file path: {:?}", path);
|
||||
let file_path = if path.has_no_params() {
|
||||
"index.html".to_owned()
|
||||
} else {
|
||||
path.app_params.into_iter().filter(|x| !x.is_empty()).join("/")
|
||||
};
|
||||
trace!(target: "dapps", "Builtin file: {:?}", file_path);
|
||||
|
||||
let file = {
|
||||
let file = |path| self.app.file(path).map(|file| {
|
||||
let content_type = match file.content_type.parse() {
|
||||
Ok(mime) => mime,
|
||||
Err(_) => {
|
||||
warn!(target: "dapps", "invalid MIME type: {}", file.content_type);
|
||||
mime::TEXT_HTML
|
||||
},
|
||||
};
|
||||
BuiltinFile {
|
||||
content_type,
|
||||
content: io::Cursor::new(file.content),
|
||||
}
|
||||
});
|
||||
let res = file(&file_path);
|
||||
if self.fallback_to_index_html {
|
||||
res.or_else(|| file("index.html"))
|
||||
} else {
|
||||
res
|
||||
}
|
||||
};
|
||||
|
||||
let (reader, response) = handler::PageHandler {
|
||||
file,
|
||||
cache: PageCache::Disabled,
|
||||
safe_to_embed_on: self.safe_to_embed_on.clone(),
|
||||
})
|
||||
}.into_response();
|
||||
|
||||
self.pool.spawn(reader).forget();
|
||||
|
||||
Box::new(future::ok(response))
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,66 +137,20 @@ impl From<Info> for EndpointInfo {
|
||||
}
|
||||
}
|
||||
|
||||
struct BuiltinDapp<T: WebApp + 'static> {
|
||||
app: Arc<T>,
|
||||
fallback_to_index_html: bool,
|
||||
|
||||
struct BuiltinFile {
|
||||
content_type: Mime,
|
||||
content: io::Cursor<&'static [u8]>,
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> BuiltinDapp<T> {
|
||||
fn new(app: Arc<T>, fallback_to_index_html: bool) -> Self {
|
||||
BuiltinDapp {
|
||||
app: app,
|
||||
fallback_to_index_html: fallback_to_index_html,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> handler::Dapp for BuiltinDapp<T> {
|
||||
type DappFile = BuiltinDappFile<T>;
|
||||
|
||||
fn file(&self, path: &str) -> Option<Self::DappFile> {
|
||||
let file = |path| self.app.file(path).map(|_| {
|
||||
BuiltinDappFile {
|
||||
app: self.app.clone(),
|
||||
path: path.into(),
|
||||
write_pos: 0,
|
||||
}
|
||||
});
|
||||
let res = file(path);
|
||||
if self.fallback_to_index_html {
|
||||
res.or_else(|| file("index.html"))
|
||||
} else {
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BuiltinDappFile<T: WebApp + 'static> {
|
||||
app: Arc<T>,
|
||||
path: String,
|
||||
write_pos: usize,
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> BuiltinDappFile<T> {
|
||||
fn file(&self) -> &File {
|
||||
self.app.file(&self.path).expect("Check is done when structure is created.")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WebApp + 'static> handler::DappFile for BuiltinDappFile<T> {
|
||||
fn content_type(&self) -> &str {
|
||||
self.file().content_type
|
||||
}
|
||||
|
||||
fn is_drained(&self) -> bool {
|
||||
self.write_pos == self.file().content.len()
|
||||
}
|
||||
|
||||
fn next_chunk(&mut self) -> &[u8] {
|
||||
&self.file().content[self.write_pos..]
|
||||
}
|
||||
|
||||
fn bytes_written(&mut self, bytes: usize) {
|
||||
self.write_pos += bytes;
|
||||
impl handler::DappFile for BuiltinFile {
|
||||
type Reader = io::Cursor<&'static [u8]>;
|
||||
|
||||
fn content_type(&self) -> &Mime {
|
||||
&self.content_type
|
||||
}
|
||||
|
||||
fn into_reader(self) -> Self::Reader {
|
||||
self.content
|
||||
}
|
||||
}
|
||||
|
@ -14,61 +14,25 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use time::{self, Duration};
|
||||
use std::io;
|
||||
use std::time::{Duration, SystemTime};
|
||||
use hyper::{self, header, StatusCode};
|
||||
use hyper::mime::Mime;
|
||||
|
||||
use hyper::header;
|
||||
use hyper::server;
|
||||
use hyper::uri::RequestUri;
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::{Decoder, Encoder, Next};
|
||||
use endpoint::EndpointPath;
|
||||
use handlers::{ContentHandler, add_security_headers};
|
||||
use handlers::{Reader, ContentHandler, add_security_headers};
|
||||
use {Embeddable};
|
||||
|
||||
/// Represents a file that can be sent to client.
|
||||
/// Implementation should keep track of bytes already sent internally.
|
||||
pub trait DappFile: Send {
|
||||
pub trait DappFile {
|
||||
/// A reader type returned by this file.
|
||||
type Reader: io::Read;
|
||||
|
||||
/// Returns a content-type of this file.
|
||||
fn content_type(&self) -> &str;
|
||||
fn content_type(&self) -> &Mime;
|
||||
|
||||
/// Checks if all bytes from that file were written.
|
||||
fn is_drained(&self) -> bool;
|
||||
|
||||
/// Fetch next chunk to write to the client.
|
||||
fn next_chunk(&mut self) -> &[u8];
|
||||
|
||||
/// How many files have been written to the client.
|
||||
fn bytes_written(&mut self, bytes: usize);
|
||||
}
|
||||
|
||||
/// Dapp as a (dynamic) set of files.
|
||||
pub trait Dapp: Send + 'static {
|
||||
/// File type
|
||||
type DappFile: DappFile;
|
||||
|
||||
/// Returns file under given path.
|
||||
fn file(&self, path: &str) -> Option<Self::DappFile>;
|
||||
}
|
||||
|
||||
/// Currently served by `PageHandler` file
|
||||
pub enum ServedFile<T: Dapp> {
|
||||
/// File from dapp
|
||||
File(T::DappFile),
|
||||
/// Error (404)
|
||||
Error(ContentHandler),
|
||||
}
|
||||
|
||||
impl<T: Dapp> ServedFile<T> {
|
||||
pub fn new(embeddable_on: Embeddable) -> Self {
|
||||
ServedFile::Error(ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"404 Not Found",
|
||||
"Requested dapp resource was not found.",
|
||||
None,
|
||||
embeddable_on,
|
||||
))
|
||||
}
|
||||
/// Convert this file into io::Read instance.
|
||||
fn into_reader(self) -> Self::Reader where Self: Sized;
|
||||
}
|
||||
|
||||
/// Defines what cache headers should be appended to returned resources.
|
||||
@ -84,194 +48,55 @@ impl Default for PageCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// A generic type for `PageHandler` allowing to set the URL.
|
||||
/// Used by dapps fetching to set the URL after the content was downloaded.
|
||||
pub trait PageHandlerWaiting: server::Handler<HttpStream> + Send {
|
||||
fn set_uri(&mut self, uri: &RequestUri);
|
||||
}
|
||||
|
||||
/// A handler for a single webapp.
|
||||
/// Resolves correct paths and serves as a plumbing code between
|
||||
/// hyper server and dapp.
|
||||
pub struct PageHandler<T: Dapp> {
|
||||
/// A Dapp.
|
||||
pub app: T,
|
||||
pub struct PageHandler<T: DappFile> {
|
||||
/// File currently being served
|
||||
pub file: ServedFile<T>,
|
||||
/// Optional prefix to strip from path.
|
||||
pub prefix: Option<String>,
|
||||
/// Requested path.
|
||||
pub path: EndpointPath,
|
||||
pub file: Option<T>,
|
||||
/// Flag indicating if the file can be safely embeded (put in iframe).
|
||||
pub safe_to_embed_on: Embeddable,
|
||||
/// Cache settings for this page.
|
||||
pub cache: PageCache,
|
||||
}
|
||||
|
||||
impl<T: Dapp> PageHandlerWaiting for PageHandler<T> {
|
||||
fn set_uri(&mut self, uri: &RequestUri) {
|
||||
trace!(target: "dapps", "Setting URI: {:?}", uri);
|
||||
self.file = match *uri {
|
||||
RequestUri::AbsolutePath { ref path, .. } => {
|
||||
self.app.file(&self.extract_path(path))
|
||||
},
|
||||
RequestUri::AbsoluteUri(ref url) => {
|
||||
self.app.file(&self.extract_path(url.path()))
|
||||
},
|
||||
_ => None,
|
||||
}.map_or_else(|| ServedFile::new(self.safe_to_embed_on.clone()), |f| ServedFile::File(f));
|
||||
}
|
||||
}
|
||||
impl<T: DappFile> PageHandler<T> {
|
||||
pub fn into_response(self) -> (Option<Reader<T::Reader>>, hyper::Response) {
|
||||
let file = match self.file {
|
||||
None => return (None, ContentHandler::error(
|
||||
StatusCode::NotFound,
|
||||
"File not found",
|
||||
"Requested file has not been found.",
|
||||
None,
|
||||
self.safe_to_embed_on,
|
||||
).into()),
|
||||
Some(file) => file,
|
||||
};
|
||||
|
||||
impl<T: Dapp> PageHandler<T> {
|
||||
fn extract_path(&self, path: &str) -> String {
|
||||
let app_id = &self.path.app_id;
|
||||
let prefix = "/".to_owned() + self.prefix.as_ref().unwrap_or(app_id);
|
||||
let prefix_with_slash = prefix.clone() + "/";
|
||||
let query_pos = path.find('?').unwrap_or_else(|| path.len());
|
||||
let mut res = hyper::Response::new()
|
||||
.with_status(StatusCode::Ok);
|
||||
|
||||
// Index file support
|
||||
match path == "/" || path == &prefix || path == &prefix_with_slash {
|
||||
true => "index.html".to_owned(),
|
||||
false => if path.starts_with(&prefix_with_slash) {
|
||||
path[prefix_with_slash.len()..query_pos].to_owned()
|
||||
} else if path.starts_with("/") {
|
||||
path[1..query_pos].to_owned()
|
||||
} else {
|
||||
path[0..query_pos].to_owned()
|
||||
// headers
|
||||
{
|
||||
let mut headers = res.headers_mut();
|
||||
|
||||
if let PageCache::Enabled = self.cache {
|
||||
let validity_secs = 365u32 * 24 * 3600;
|
||||
let validity = Duration::from_secs(validity_secs as u64);
|
||||
headers.set(header::CacheControl(vec![
|
||||
header::CacheDirective::Public,
|
||||
header::CacheDirective::MaxAge(validity_secs),
|
||||
]));
|
||||
headers.set(header::Expires(header::HttpDate::from(SystemTime::now() + validity)));
|
||||
}
|
||||
|
||||
headers.set(header::ContentType(file.content_type().to_owned()));
|
||||
|
||||
add_security_headers(&mut headers, self.safe_to_embed_on);
|
||||
}
|
||||
|
||||
let (reader, body) = Reader::pair(file.into_reader(), Vec::new());
|
||||
res.set_body(body);
|
||||
(Some(reader), res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Dapp> server::Handler<HttpStream> for PageHandler<T> {
|
||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
||||
self.set_uri(req.uri());
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
match self.file {
|
||||
ServedFile::File(ref f) => {
|
||||
res.set_status(StatusCode::Ok);
|
||||
|
||||
if let PageCache::Enabled = self.cache {
|
||||
let mut headers = res.headers_mut();
|
||||
let validity = Duration::days(365);
|
||||
headers.set(header::CacheControl(vec![
|
||||
header::CacheDirective::Public,
|
||||
header::CacheDirective::MaxAge(validity.num_seconds() as u32),
|
||||
]));
|
||||
headers.set(header::Expires(header::HttpDate(time::now() + validity)));
|
||||
}
|
||||
|
||||
match f.content_type().parse() {
|
||||
Ok(mime) => res.headers_mut().set(header::ContentType(mime)),
|
||||
Err(()) => debug!(target: "dapps", "invalid MIME type: {}", f.content_type()),
|
||||
}
|
||||
|
||||
// Security headers:
|
||||
add_security_headers(&mut res.headers_mut(), self.safe_to_embed_on.take());
|
||||
Next::write()
|
||||
},
|
||||
ServedFile::Error(ref mut handler) => {
|
||||
handler.on_response(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
match self.file {
|
||||
ServedFile::Error(ref mut handler) => handler.on_response_writable(encoder),
|
||||
ServedFile::File(ref f) if f.is_drained() => Next::end(),
|
||||
ServedFile::File(ref mut f) => match encoder.write(f.next_chunk()) {
|
||||
Ok(bytes) => {
|
||||
f.bytes_written(bytes);
|
||||
Next::write()
|
||||
},
|
||||
Err(e) => match e.kind() {
|
||||
::std::io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => Next::end(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
pub struct TestWebAppFile;
|
||||
|
||||
impl DappFile for TestWebAppFile {
|
||||
fn content_type(&self) -> &str {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn is_drained(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn next_chunk(&mut self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn bytes_written(&mut self, _bytes: usize) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TestWebapp;
|
||||
|
||||
impl Dapp for TestWebapp {
|
||||
type DappFile = TestWebAppFile;
|
||||
|
||||
fn file(&self, _path: &str) -> Option<Self::DappFile> {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_path_with_appid() {
|
||||
|
||||
// given
|
||||
let path1 = "/";
|
||||
let path2= "/test.css";
|
||||
let path3 = "/app/myfile.txt";
|
||||
let path4 = "/app/myfile.txt?query=123";
|
||||
let page_handler = PageHandler {
|
||||
app: test::TestWebapp,
|
||||
prefix: None,
|
||||
path: EndpointPath {
|
||||
app_id: "app".to_owned(),
|
||||
app_params: vec![],
|
||||
host: "".to_owned(),
|
||||
port: 8080,
|
||||
using_dapps_domains: true,
|
||||
},
|
||||
file: ServedFile::new(None),
|
||||
cache: Default::default(),
|
||||
safe_to_embed_on: None,
|
||||
};
|
||||
|
||||
// when
|
||||
let res1 = page_handler.extract_path(path1);
|
||||
let res2 = page_handler.extract_path(path2);
|
||||
let res3 = page_handler.extract_path(path3);
|
||||
let res4 = page_handler.extract_path(path4);
|
||||
|
||||
// then
|
||||
assert_eq!(&res1, "index.html");
|
||||
assert_eq!(&res2, "test.css");
|
||||
assert_eq!(&res3, "myfile.txt");
|
||||
assert_eq!(&res4, "myfile.txt");
|
||||
}
|
||||
|
@ -15,16 +15,18 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use mime_guess;
|
||||
use std::io::{Seek, Read, SeekFrom};
|
||||
use std::fs;
|
||||
use std::{fs, fmt};
|
||||
use std::path::{Path, PathBuf};
|
||||
use page::handler::{self, PageCache, PageHandlerWaiting};
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Handler};
|
||||
use mime::Mime;
|
||||
use futures::{future};
|
||||
use futures_cpupool::CpuPool;
|
||||
use page::handler::{self, PageCache};
|
||||
use endpoint::{Endpoint, EndpointInfo, EndpointPath, Request, Response};
|
||||
use hyper::mime::Mime;
|
||||
use Embeddable;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LocalPageEndpoint {
|
||||
#[derive(Clone)]
|
||||
pub struct Dapp {
|
||||
pool: CpuPool,
|
||||
path: PathBuf,
|
||||
mime: Option<Mime>,
|
||||
info: Option<EndpointInfo>,
|
||||
@ -32,23 +34,37 @@ pub struct LocalPageEndpoint {
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl LocalPageEndpoint {
|
||||
pub fn new(path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Embeddable) -> Self {
|
||||
LocalPageEndpoint {
|
||||
path: path,
|
||||
impl fmt::Debug for Dapp {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("Dapp")
|
||||
.field("path", &self.path)
|
||||
.field("mime", &self.mime)
|
||||
.field("info", &self.info)
|
||||
.field("cache", &self.cache)
|
||||
.field("embeddable_on", &self.embeddable_on)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Dapp {
|
||||
pub fn new(pool: CpuPool, path: PathBuf, info: EndpointInfo, cache: PageCache, embeddable_on: Embeddable) -> Self {
|
||||
Dapp {
|
||||
pool,
|
||||
path,
|
||||
mime: None,
|
||||
info: Some(info),
|
||||
cache: cache,
|
||||
embeddable_on: embeddable_on,
|
||||
cache,
|
||||
embeddable_on,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_file(path: PathBuf, mime: Mime, cache: PageCache) -> Self {
|
||||
LocalPageEndpoint {
|
||||
path: path,
|
||||
pub fn single_file(pool: CpuPool, path: PathBuf, mime: Mime, cache: PageCache) -> Self {
|
||||
Dapp {
|
||||
pool,
|
||||
path,
|
||||
mime: Some(mime),
|
||||
info: None,
|
||||
cache: cache,
|
||||
cache,
|
||||
embeddable_on: None,
|
||||
}
|
||||
}
|
||||
@ -57,125 +73,75 @@ impl LocalPageEndpoint {
|
||||
self.path.clone()
|
||||
}
|
||||
|
||||
fn page_handler_with_mime(&self, path: EndpointPath, mime: &Mime) -> handler::PageHandler<LocalSingleFile> {
|
||||
handler::PageHandler {
|
||||
app: LocalSingleFile { path: self.path.clone(), mime: format!("{}", mime) },
|
||||
prefix: None,
|
||||
path: path,
|
||||
file: handler::ServedFile::new(None),
|
||||
safe_to_embed_on: self.embeddable_on.clone(),
|
||||
cache: self.cache,
|
||||
}
|
||||
}
|
||||
|
||||
fn page_handler(&self, path: EndpointPath) -> handler::PageHandler<LocalDapp> {
|
||||
handler::PageHandler {
|
||||
app: LocalDapp { path: self.path.clone() },
|
||||
prefix: None,
|
||||
path: path,
|
||||
file: handler::ServedFile::new(None),
|
||||
safe_to_embed_on: self.embeddable_on.clone(),
|
||||
cache: self.cache,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_page_handler(&self, path: EndpointPath) -> Box<PageHandlerWaiting> {
|
||||
fn get_file(&self, path: &EndpointPath) -> Option<LocalFile> {
|
||||
if let Some(ref mime) = self.mime {
|
||||
Box::new(self.page_handler_with_mime(path, mime))
|
||||
} else {
|
||||
Box::new(self.page_handler(path))
|
||||
return LocalFile::from_path(&self.path, mime.to_owned());
|
||||
}
|
||||
|
||||
let mut file_path = self.path.to_owned();
|
||||
|
||||
if path.has_no_params() {
|
||||
file_path.push("index.html");
|
||||
} else {
|
||||
for part in &path.app_params {
|
||||
file_path.push(part);
|
||||
}
|
||||
}
|
||||
|
||||
let mime = mime_guess::guess_mime_type(&file_path);
|
||||
LocalFile::from_path(&file_path, mime)
|
||||
}
|
||||
|
||||
|
||||
pub fn to_response(&self, path: &EndpointPath) -> Response {
|
||||
let (reader, response) = handler::PageHandler {
|
||||
file: self.get_file(path),
|
||||
cache: self.cache,
|
||||
safe_to_embed_on: self.embeddable_on.clone(),
|
||||
}.into_response();
|
||||
|
||||
self.pool.spawn(reader).forget();
|
||||
|
||||
Box::new(future::ok(response))
|
||||
}
|
||||
}
|
||||
|
||||
impl Endpoint for LocalPageEndpoint {
|
||||
impl Endpoint for Dapp {
|
||||
fn info(&self) -> Option<&EndpointInfo> {
|
||||
self.info.as_ref()
|
||||
}
|
||||
|
||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||
if let Some(ref mime) = self.mime {
|
||||
Box::new(self.page_handler_with_mime(path, mime))
|
||||
} else {
|
||||
Box::new(self.page_handler(path))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalSingleFile {
|
||||
path: PathBuf,
|
||||
mime: String,
|
||||
}
|
||||
|
||||
impl handler::Dapp for LocalSingleFile {
|
||||
type DappFile = LocalFile;
|
||||
|
||||
fn file(&self, _path: &str) -> Option<Self::DappFile> {
|
||||
LocalFile::from_path(&self.path, Some(&self.mime))
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalDapp {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl handler::Dapp for LocalDapp {
|
||||
type DappFile = LocalFile;
|
||||
|
||||
fn file(&self, file_path: &str) -> Option<Self::DappFile> {
|
||||
let mut path = self.path.clone();
|
||||
for part in file_path.split('/') {
|
||||
path.push(part);
|
||||
}
|
||||
LocalFile::from_path(&path, None)
|
||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
||||
self.to_response(&path)
|
||||
}
|
||||
}
|
||||
|
||||
struct LocalFile {
|
||||
content_type: String,
|
||||
buffer: [u8; 4096],
|
||||
content_type: Mime,
|
||||
file: fs::File,
|
||||
len: u64,
|
||||
pos: u64,
|
||||
}
|
||||
|
||||
impl LocalFile {
|
||||
fn from_path<P: AsRef<Path>>(path: P, mime: Option<&str>) -> Option<Self> {
|
||||
fn from_path<P: AsRef<Path>>(path: P, content_type: Mime) -> Option<Self> {
|
||||
trace!(target: "dapps", "Local file: {:?}", path.as_ref());
|
||||
// Check if file exists
|
||||
fs::File::open(&path).ok().map(|file| {
|
||||
let content_type = mime.map(|mime| mime.to_owned())
|
||||
.unwrap_or_else(|| mime_guess::guess_mime_type(path).to_string());
|
||||
let len = file.metadata().ok().map_or(0, |meta| meta.len());
|
||||
LocalFile {
|
||||
content_type: content_type,
|
||||
buffer: [0; 4096],
|
||||
file: file,
|
||||
pos: 0,
|
||||
len: len,
|
||||
content_type,
|
||||
file,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl handler::DappFile for LocalFile {
|
||||
fn content_type(&self) -> &str {
|
||||
type Reader = fs::File;
|
||||
|
||||
fn content_type(&self) -> &Mime {
|
||||
&self.content_type
|
||||
}
|
||||
|
||||
fn is_drained(&self) -> bool {
|
||||
self.pos == self.len
|
||||
}
|
||||
|
||||
fn next_chunk(&mut self) -> &[u8] {
|
||||
let _ = self.file.seek(SeekFrom::Start(self.pos));
|
||||
if let Ok(n) = self.file.read(&mut self.buffer) {
|
||||
&self.buffer[0..n]
|
||||
} else {
|
||||
&self.buffer[0..0]
|
||||
}
|
||||
}
|
||||
|
||||
fn bytes_written(&mut self, bytes: usize) {
|
||||
self.pos += bytes as u64;
|
||||
fn into_reader(self) -> Self::Reader {
|
||||
self.file
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,9 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
mod builtin;
|
||||
mod local;
|
||||
pub mod builtin;
|
||||
pub mod local;
|
||||
mod handler;
|
||||
|
||||
pub use self::local::LocalPageEndpoint;
|
||||
pub use self::builtin::PageEndpoint;
|
||||
pub use self::handler::{PageCache, PageHandlerWaiting};
|
||||
pub use self::handler::PageCache;
|
||||
|
||||
|
@ -16,9 +16,11 @@
|
||||
|
||||
//! Serving ProxyPac file
|
||||
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use handlers::ContentHandler;
|
||||
use apps::HOME_PAGE;
|
||||
use endpoint::{Endpoint, Request, Response, EndpointPath};
|
||||
use futures::future;
|
||||
use handlers::ContentHandler;
|
||||
use hyper::mime;
|
||||
use {address, Embeddable};
|
||||
|
||||
pub struct ProxyPac {
|
||||
@ -33,7 +35,7 @@ impl ProxyPac {
|
||||
}
|
||||
|
||||
impl Endpoint for ProxyPac {
|
||||
fn to_handler(&self, path: EndpointPath) -> Box<Handler> {
|
||||
fn respond(&self, path: EndpointPath, _req: Request) -> Response {
|
||||
let ui = self.embeddable
|
||||
.as_ref()
|
||||
.map(|ref parent| address(&parent.host, parent.port))
|
||||
@ -57,7 +59,9 @@ function FindProxyForURL(url, host) {{
|
||||
"#,
|
||||
HOME_PAGE, self.dapps_domain, path.host, path.port, ui);
|
||||
|
||||
Box::new(ContentHandler::ok(content, mime!(Application/Javascript)))
|
||||
Box::new(future::ok(
|
||||
ContentHandler::ok(content, mime::TEXT_JAVASCRIPT).into()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,18 +17,16 @@
|
||||
//! Router implementation
|
||||
//! Dispatch requests to proper application.
|
||||
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use url::{Url, Host};
|
||||
use hyper::{self, server, header, Control};
|
||||
use hyper::net::HttpStream;
|
||||
use futures::future;
|
||||
use hyper::{self, header, Uri};
|
||||
use jsonrpc_http_server as http;
|
||||
|
||||
use apps;
|
||||
use apps::fetcher::Fetcher;
|
||||
use endpoint::{Endpoint, EndpointPath, Handler};
|
||||
use endpoint::{self, Endpoint, EndpointPath};
|
||||
use Endpoints;
|
||||
use handlers;
|
||||
use Embeddable;
|
||||
@ -43,6 +41,13 @@ pub enum SpecialEndpoint {
|
||||
None,
|
||||
}
|
||||
|
||||
enum Response {
|
||||
Some(endpoint::Response),
|
||||
None(hyper::Request),
|
||||
}
|
||||
|
||||
/// An endpoint router.
|
||||
/// Dispatches the request to particular Endpoint by requested uri/path.
|
||||
pub struct Router {
|
||||
endpoints: Option<Endpoints>,
|
||||
fetch: Arc<Fetcher>,
|
||||
@ -52,11 +57,10 @@ pub struct Router {
|
||||
}
|
||||
|
||||
impl Router {
|
||||
fn resolve_request(&self, req: &server::Request<HttpStream>, control: Control, refresh_dapps: bool) -> (bool, Option<Box<Handler>>) {
|
||||
fn resolve_request(&self, req: hyper::Request, refresh_dapps: bool) -> (bool, Response) {
|
||||
// Choose proper handler depending on path / domain
|
||||
let url = handlers::extract_url(req);
|
||||
let endpoint = extract_endpoint(&url, &self.dapps_domain);
|
||||
let referer = extract_referer_endpoint(req, &self.dapps_domain);
|
||||
let endpoint = extract_endpoint(req.uri(), req.headers().get(), &self.dapps_domain);
|
||||
let referer = extract_referer_endpoint(&req, &self.dapps_domain);
|
||||
let is_utils = endpoint.1 == SpecialEndpoint::Utils;
|
||||
let is_get_request = *req.method() == hyper::Method::Get;
|
||||
let is_head_request = *req.method() == hyper::Method::Head;
|
||||
@ -64,47 +68,51 @@ impl Router {
|
||||
.as_ref()
|
||||
.map_or(false, |endpoints| endpoints.endpoints.read().contains_key(dapp));
|
||||
|
||||
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req);
|
||||
debug!(target: "dapps", "Handling endpoint request: {:?}", endpoint);
|
||||
trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", req.uri(), req);
|
||||
debug!(target: "dapps", "Handling endpoint request: {:?}, referer: {:?}", endpoint, referer);
|
||||
|
||||
(is_utils, match (endpoint.0, endpoint.1, referer) {
|
||||
// Handle invalid web requests that we can recover from
|
||||
(ref path, SpecialEndpoint::None, Some((ref referer, ref referer_url)))
|
||||
(ref path, SpecialEndpoint::None, Some(ref referer))
|
||||
if referer.app_id == apps::WEB_PATH
|
||||
&& has_dapp(apps::WEB_PATH)
|
||||
&& !is_web_endpoint(path)
|
||||
=>
|
||||
{
|
||||
trace!(target: "dapps", "Redirecting to correct web request: {:?}", referer_url);
|
||||
let len = cmp::min(referer_url.path.len(), 2); // /web/<encoded>/
|
||||
let base = referer_url.path[..len].join("/");
|
||||
let requested = url.map(|u| u.path.join("/")).unwrap_or_default();
|
||||
Some(handlers::Redirection::boxed(&format!("/{}/{}", base, requested)))
|
||||
let token = referer.app_params.get(0).map(String::as_str).unwrap_or("");
|
||||
let requested = req.uri().path();
|
||||
let query = req.uri().query().map_or_else(String::new, |query| format!("?{}", query));
|
||||
let redirect_url = format!("/{}/{}{}{}", apps::WEB_PATH, token, requested, query);
|
||||
trace!(target: "dapps", "Redirecting to correct web request: {:?}", redirect_url);
|
||||
Response::Some(Box::new(future::ok(
|
||||
handlers::Redirection::new(redirect_url).into()
|
||||
)))
|
||||
},
|
||||
// First check special endpoints
|
||||
(ref path, ref endpoint, _) if self.special.contains_key(endpoint) => {
|
||||
trace!(target: "dapps", "Resolving to special endpoint.");
|
||||
self.special.get(endpoint)
|
||||
.expect("special known to contain key; qed")
|
||||
.as_ref()
|
||||
.map(|special| special.to_async_handler(path.clone().unwrap_or_default(), control))
|
||||
let special = self.special.get(endpoint).expect("special known to contain key; qed");
|
||||
match *special {
|
||||
Some(ref special) => Response::Some(special.respond(path.clone().unwrap_or_default(), req)),
|
||||
None => Response::None(req),
|
||||
}
|
||||
},
|
||||
// Then delegate to dapp
|
||||
(Some(ref path), _, _) if has_dapp(&path.app_id) => {
|
||||
trace!(target: "dapps", "Resolving to local/builtin dapp.");
|
||||
Some(self.endpoints
|
||||
Response::Some(self.endpoints
|
||||
.as_ref()
|
||||
.expect("endpoints known to be set; qed")
|
||||
.endpoints
|
||||
.read()
|
||||
.get(&path.app_id)
|
||||
.expect("endpoints known to contain key; qed")
|
||||
.to_async_handler(path.clone(), control))
|
||||
.respond(path.clone(), req))
|
||||
},
|
||||
// Try to resolve and fetch the dapp
|
||||
(Some(ref path), _, _) if self.fetch.contains(&path.app_id) => {
|
||||
trace!(target: "dapps", "Resolving to fetchable content.");
|
||||
Some(self.fetch.to_async_handler(path.clone(), control))
|
||||
Response::Some(self.fetch.respond(path.clone(), req))
|
||||
},
|
||||
// 404 for non-existent content (only if serving endpoints and not homepage)
|
||||
(Some(ref path), _, _)
|
||||
@ -117,45 +125,50 @@ impl Router {
|
||||
if refresh_dapps {
|
||||
debug!(target: "dapps", "Refreshing dapps and re-trying.");
|
||||
self.endpoints.as_ref().map(|endpoints| endpoints.refresh_local_dapps());
|
||||
return self.resolve_request(req, control, false)
|
||||
return self.resolve_request(req, false);
|
||||
} else {
|
||||
Some(Box::new(handlers::ContentHandler::error(
|
||||
Response::Some(Box::new(future::ok(handlers::ContentHandler::error(
|
||||
hyper::StatusCode::NotFound,
|
||||
"404 Not Found",
|
||||
"Requested content was not found.",
|
||||
None,
|
||||
self.embeddable_on.clone(),
|
||||
)))
|
||||
).into())))
|
||||
}
|
||||
},
|
||||
// Any other GET|HEAD requests to home page.
|
||||
_ if (is_get_request || is_head_request) && self.special.contains_key(&SpecialEndpoint::Home) => {
|
||||
self.special.get(&SpecialEndpoint::Home)
|
||||
.expect("special known to contain key; qed")
|
||||
.as_ref()
|
||||
.map(|special| special.to_async_handler(Default::default(), control))
|
||||
let special = self.special.get(&SpecialEndpoint::Home).expect("special known to contain key; qed");
|
||||
match *special {
|
||||
Some(ref special) => {
|
||||
let mut endpoint = EndpointPath::default();
|
||||
endpoint.app_params = req.uri().path().split('/').map(str::to_owned).collect();
|
||||
Response::Some(special.respond(endpoint, req))
|
||||
},
|
||||
None => Response::None(req),
|
||||
}
|
||||
},
|
||||
// RPC by default
|
||||
_ => {
|
||||
trace!(target: "dapps", "Resolving to RPC call.");
|
||||
None
|
||||
Response::None(req)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl http::RequestMiddleware for Router {
|
||||
fn on_request(&self, req: &server::Request<HttpStream>, control: &Control) -> http::RequestMiddlewareAction {
|
||||
let control = control.clone();
|
||||
fn on_request(&self, req: hyper::Request) -> http::RequestMiddlewareAction {
|
||||
let is_origin_set = req.headers().get::<header::Origin>().is_some();
|
||||
let (is_utils, handler) = self.resolve_request(req, control, self.endpoints.is_some());
|
||||
match handler {
|
||||
Some(handler) => http::RequestMiddlewareAction::Respond {
|
||||
let (is_utils, response) = self.resolve_request(req, self.endpoints.is_some());
|
||||
match response {
|
||||
Response::Some(response) => http::RequestMiddlewareAction::Respond {
|
||||
should_validate_hosts: !is_utils,
|
||||
handler: handler,
|
||||
response,
|
||||
},
|
||||
None => http::RequestMiddlewareAction::Proceed {
|
||||
Response::None(request) => http::RequestMiddlewareAction::Proceed {
|
||||
should_continue_on_invalid_cors: !is_origin_set,
|
||||
request,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -186,41 +199,44 @@ fn is_web_endpoint(path: &Option<EndpointPath>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_referer_endpoint(req: &server::Request<HttpStream>, dapps_domain: &str) -> Option<(EndpointPath, Url)> {
|
||||
fn extract_referer_endpoint(req: &hyper::Request, dapps_domain: &str) -> Option<EndpointPath> {
|
||||
let referer = req.headers().get::<header::Referer>();
|
||||
|
||||
let url = referer.and_then(|referer| Url::parse(&referer.0).ok());
|
||||
let url = referer.and_then(|referer| referer.parse().ok());
|
||||
url.and_then(|url| {
|
||||
let option = Some(url);
|
||||
extract_url_referer_endpoint(&option, dapps_domain).or_else(|| {
|
||||
extract_endpoint(&option, dapps_domain).0.map(|endpoint| (endpoint, option.expect("Just wrapped; qed")))
|
||||
extract_url_referer_endpoint(&url, dapps_domain).or_else(|| {
|
||||
extract_endpoint(&url, None, dapps_domain).0
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_url_referer_endpoint(url: &Option<Url>, dapps_domain: &str) -> Option<(EndpointPath, Url)> {
|
||||
let query = url.as_ref().and_then(|url| url.query.as_ref());
|
||||
match (url, query) {
|
||||
(&Some(ref url), Some(ref query)) if query.starts_with(apps::URL_REFERER) => {
|
||||
let referer_url = format!("http://{}:{}/{}", url.host, url.port, &query[apps::URL_REFERER.len()..]);
|
||||
fn extract_url_referer_endpoint(url: &Uri, dapps_domain: &str) -> Option<EndpointPath> {
|
||||
let query = url.query();
|
||||
match query {
|
||||
Some(query) if query.starts_with(apps::URL_REFERER) => {
|
||||
let scheme = url.scheme().unwrap_or("http");
|
||||
let host = url.host().unwrap_or("unknown");
|
||||
let port = default_port(url, None);
|
||||
let referer_url = format!("{}://{}:{}/{}", scheme, host, port, &query[apps::URL_REFERER.len()..]);
|
||||
debug!(target: "dapps", "Recovering referer from query parameter: {}", referer_url);
|
||||
|
||||
let referer_url = Url::parse(&referer_url).ok();
|
||||
extract_endpoint(&referer_url, dapps_domain).0.map(|endpoint| {
|
||||
(endpoint, referer_url.expect("Endpoint returned only when url `is_some`").clone())
|
||||
})
|
||||
if let Some(referer_url) = referer_url.parse().ok() {
|
||||
extract_endpoint(&referer_url, None, dapps_domain).0
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_endpoint(url: &Option<Url>, dapps_domain: &str) -> (Option<EndpointPath>, SpecialEndpoint) {
|
||||
fn special_endpoint(url: &Url) -> SpecialEndpoint {
|
||||
if url.path.len() <= 1 {
|
||||
fn extract_endpoint(url: &Uri, extra_host: Option<&header::Host>, dapps_domain: &str) -> (Option<EndpointPath>, SpecialEndpoint) {
|
||||
fn special_endpoint(path: &[&str]) -> SpecialEndpoint {
|
||||
if path.len() <= 1 {
|
||||
return SpecialEndpoint::None;
|
||||
}
|
||||
|
||||
match url.path[0].as_ref() {
|
||||
match path[0].as_ref() {
|
||||
apps::RPC_PATH => SpecialEndpoint::Rpc,
|
||||
apps::API_PATH => SpecialEndpoint::Api,
|
||||
apps::UTILS_PATH => SpecialEndpoint::Utils,
|
||||
@ -229,114 +245,162 @@ fn extract_endpoint(url: &Option<Url>, dapps_domain: &str) -> (Option<EndpointPa
|
||||
}
|
||||
}
|
||||
|
||||
match *url {
|
||||
Some(ref url) => match url.host {
|
||||
Host::Domain(ref domain) if domain.ends_with(dapps_domain) => {
|
||||
let id = &domain[0..(domain.len() - dapps_domain.len())];
|
||||
let (id, params) = if let Some(split) = id.rfind('.') {
|
||||
let (params, id) = id.split_at(split);
|
||||
(id[1..].to_owned(), [params.to_owned()].into_iter().chain(&url.path).cloned().collect())
|
||||
} else {
|
||||
(id.to_owned(), url.path.clone())
|
||||
};
|
||||
let port = default_port(url, extra_host.as_ref().and_then(|h| h.port()));
|
||||
let host = url.host().or_else(|| extra_host.as_ref().map(|h| h.hostname()));
|
||||
let query = url.query().map(str::to_owned);
|
||||
let mut path_segments = url.path().split('/').skip(1).collect::<Vec<_>>();
|
||||
trace!(
|
||||
target: "dapps",
|
||||
"Extracting endpoint from: {:?} (dapps: {}). Got host {:?}:{} with path {:?}",
|
||||
url, dapps_domain, host, port, path_segments
|
||||
);
|
||||
match host {
|
||||
Some(host) if host.ends_with(dapps_domain) => {
|
||||
let id = &host[0..(host.len() - dapps_domain.len())];
|
||||
let special = special_endpoint(&path_segments);
|
||||
|
||||
(Some(EndpointPath {
|
||||
app_id: id,
|
||||
app_params: params,
|
||||
host: domain.clone(),
|
||||
port: url.port,
|
||||
using_dapps_domains: true,
|
||||
}), special_endpoint(url))
|
||||
},
|
||||
_ if url.path.len() > 1 => {
|
||||
let id = url.path[0].to_owned();
|
||||
(Some(EndpointPath {
|
||||
app_id: id,
|
||||
app_params: url.path[1..].to_vec(),
|
||||
host: format!("{}", url.host),
|
||||
port: url.port,
|
||||
using_dapps_domains: false,
|
||||
}), special_endpoint(url))
|
||||
},
|
||||
_ => (None, special_endpoint(url)),
|
||||
// remove special endpoint id from params
|
||||
if special != SpecialEndpoint::None {
|
||||
path_segments.remove(0);
|
||||
}
|
||||
|
||||
let (app_id, app_params) = if let Some(split) = id.rfind('.') {
|
||||
let (params, id) = id.split_at(split);
|
||||
path_segments.insert(0, params);
|
||||
(id[1..].to_owned(), path_segments)
|
||||
} else {
|
||||
(id.to_owned(), path_segments)
|
||||
};
|
||||
|
||||
(Some(EndpointPath {
|
||||
app_id,
|
||||
app_params: app_params.into_iter().map(Into::into).collect(),
|
||||
query,
|
||||
host: host.to_owned(),
|
||||
port,
|
||||
using_dapps_domains: true,
|
||||
}), special)
|
||||
},
|
||||
_ => (None, SpecialEndpoint::None)
|
||||
Some(host) if path_segments.len() > 1 => {
|
||||
let special = special_endpoint(&path_segments);
|
||||
let id = path_segments.remove(0);
|
||||
(Some(EndpointPath {
|
||||
app_id: id.to_owned(),
|
||||
app_params: path_segments.into_iter().map(Into::into).collect(),
|
||||
query,
|
||||
host: host.to_owned(),
|
||||
port,
|
||||
using_dapps_domains: false,
|
||||
}), special)
|
||||
},
|
||||
_ => (None, special_endpoint(&path_segments)),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_endpoint() {
|
||||
let dapps_domain = ".web3.site";
|
||||
assert_eq!(extract_endpoint(&None, dapps_domain), (None, SpecialEndpoint::None));
|
||||
|
||||
// With path prefix
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://localhost:8080/status/index.html").ok(), dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["index.html".to_owned()],
|
||||
host: "localhost".to_owned(),
|
||||
port: 8080,
|
||||
using_dapps_domains: false,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// With path prefix
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://localhost:8080/rpc/").ok(), dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "rpc".to_owned(),
|
||||
app_params: vec!["".to_owned()],
|
||||
host: "localhost".to_owned(),
|
||||
port: 8080,
|
||||
using_dapps_domains: false,
|
||||
}), SpecialEndpoint::Rpc)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://my.status.web3.site/parity-utils/inject.js").ok(), dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".to_owned(), "parity-utils".into(), "inject.js".into()],
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Utils)
|
||||
);
|
||||
|
||||
// By Subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://status.web3.site/test.html").ok(), dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["test.html".to_owned()],
|
||||
host: "status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// RPC by subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://my.status.web3.site/rpc/").ok(), dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".to_owned(), "rpc".into(), "".into()],
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Rpc)
|
||||
);
|
||||
|
||||
// API by subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&Url::parse("http://my.status.web3.site/api/").ok(), dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".to_owned(), "api".into(), "".into()],
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Api)
|
||||
);
|
||||
fn default_port(url: &Uri, extra_port: Option<u16>) -> u16 {
|
||||
let scheme = url.scheme().unwrap_or("http");
|
||||
url.port().or(extra_port).unwrap_or_else(|| match scheme {
|
||||
"http" => 80,
|
||||
"https" => 443,
|
||||
_ => 80,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{SpecialEndpoint, EndpointPath, extract_endpoint};
|
||||
|
||||
#[test]
|
||||
fn should_extract_endpoint() {
|
||||
let dapps_domain = ".web3.site";
|
||||
|
||||
// With path prefix
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://localhost:8080/status/index.html?q=1".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["index.html".to_owned()],
|
||||
query: Some("q=1".into()),
|
||||
host: "localhost".to_owned(),
|
||||
port: 8080,
|
||||
using_dapps_domains: false,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// With path prefix
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://localhost:8080/rpc/".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "rpc".to_owned(),
|
||||
app_params: vec!["".to_owned()],
|
||||
query: None,
|
||||
host: "localhost".to_owned(),
|
||||
port: 8080,
|
||||
using_dapps_domains: false,
|
||||
}), SpecialEndpoint::Rpc)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://my.status.web3.site/parity-utils/inject.js".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".into(), "inject.js".into()],
|
||||
query: None,
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Utils)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://my.status.web3.site/inject.js".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".into(), "inject.js".into()],
|
||||
query: None,
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// By Subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://status.web3.site/test.html".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["test.html".to_owned()],
|
||||
query: None,
|
||||
host: "status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::None)
|
||||
);
|
||||
|
||||
// RPC by subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://my.status.web3.site/rpc/".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".into(), "".into()],
|
||||
query: None,
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Rpc)
|
||||
);
|
||||
|
||||
// API by subdomain
|
||||
assert_eq!(
|
||||
extract_endpoint(&"http://my.status.web3.site/api/".parse().unwrap(), None, dapps_domain),
|
||||
(Some(EndpointPath {
|
||||
app_id: "status".to_owned(),
|
||||
app_params: vec!["my".into(), "".into()],
|
||||
query: None,
|
||||
host: "my.status.web3.site".to_owned(),
|
||||
port: 80,
|
||||
using_dapps_domains: true,
|
||||
}), SpecialEndpoint::Api)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ fn should_handle_ping() {
|
||||
"\
|
||||
POST /api/ping HTTP/1.1\r\n\
|
||||
Host: home.parity\r\n\
|
||||
Content-Type: application/json\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
|
@ -18,7 +18,7 @@ use devtools::http_client;
|
||||
use rustc_hex::FromHex;
|
||||
use tests::helpers::{
|
||||
serve_with_registrar, serve_with_registrar_and_sync, serve_with_fetch,
|
||||
serve_with_registrar_and_fetch, serve_with_registrar_and_fetch_and_threads,
|
||||
serve_with_registrar_and_fetch,
|
||||
request, assert_security_headers_for_embed,
|
||||
};
|
||||
|
||||
@ -171,6 +171,8 @@ fn should_return_fetched_dapp_content() {
|
||||
r#"18
|
||||
<h1>Hello Gavcoin!</h1>
|
||||
|
||||
0
|
||||
|
||||
"#
|
||||
);
|
||||
|
||||
@ -257,7 +259,7 @@ fn should_not_request_content_twice() {
|
||||
use std::thread;
|
||||
|
||||
// given
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch_and_threads(true);
|
||||
let (server, fetch, registrar) = serve_with_registrar_and_fetch();
|
||||
let gavcoin = GAVCOIN_ICON.from_hex().unwrap();
|
||||
registrar.set_result(
|
||||
"2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".parse().unwrap(),
|
||||
|
@ -94,7 +94,7 @@ impl FakeFetch {
|
||||
}
|
||||
|
||||
impl Fetch for FakeFetch {
|
||||
type Result = futures::BoxFuture<fetch::Response, fetch::Error>;
|
||||
type Result = Box<Future<Item = fetch::Response, Error = fetch::Error> + Send>;
|
||||
|
||||
fn new() -> Result<Self, fetch::Error> where Self: Sized {
|
||||
Ok(FakeFetch::default())
|
||||
@ -117,6 +117,17 @@ impl Fetch for FakeFetch {
|
||||
tx.send(fetch::Response::from_reader(cursor)).unwrap();
|
||||
});
|
||||
|
||||
rx.map_err(|_| fetch::Error::Aborted).boxed()
|
||||
Box::new(rx.map_err(|_| fetch::Error::Aborted))
|
||||
}
|
||||
|
||||
fn process_and_forget<F, I, E>(&self, f: F) where
|
||||
F: Future<Item=I, Error=E> + Send + 'static,
|
||||
I: Send + 'static,
|
||||
E: Send + 'static,
|
||||
{
|
||||
// Spawn the task in a separate thread.
|
||||
thread::spawn(|| {
|
||||
let _ = f.wait();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -22,12 +22,12 @@ use std::sync::Arc;
|
||||
use env_logger::LogBuilder;
|
||||
use jsonrpc_core::IoHandler;
|
||||
use jsonrpc_http_server::{self as http, Host, DomainsValidation};
|
||||
use parity_reactor::Remote;
|
||||
|
||||
use devtools::http_client;
|
||||
use hash_fetch::urlhint::ContractClient;
|
||||
use fetch::{Fetch, Client as FetchClient};
|
||||
use node_health::{NodeHealth, TimeChecker, CpuPool};
|
||||
use parity_reactor::Remote;
|
||||
|
||||
use {Middleware, SyncStatus, WebProxyTokens};
|
||||
|
||||
@ -55,7 +55,7 @@ fn init_logger() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_server<F, B>(process: F, io: IoHandler, remote: Remote) -> (Server, Arc<FakeRegistrar>) where
|
||||
pub fn init_server<F, B>(process: F, io: IoHandler) -> (Server, Arc<FakeRegistrar>) where
|
||||
F: FnOnce(ServerBuilder) -> ServerBuilder<B>,
|
||||
B: Fetch,
|
||||
{
|
||||
@ -64,11 +64,9 @@ pub fn init_server<F, B>(process: F, io: IoHandler, remote: Remote) -> (Server,
|
||||
let mut dapps_path = env::temp_dir();
|
||||
dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading");
|
||||
|
||||
let server = process(ServerBuilder::new(
|
||||
&dapps_path, registrar.clone(), remote,
|
||||
))
|
||||
.signer_address(Some(("127.0.0.1".into(), SIGNER_PORT)))
|
||||
.start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io).unwrap();
|
||||
let mut builder = ServerBuilder::new(&dapps_path, registrar.clone());
|
||||
builder.signer_address = Some(("127.0.0.1".into(), SIGNER_PORT));
|
||||
let server = process(builder).start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io).unwrap();
|
||||
(
|
||||
server,
|
||||
registrar,
|
||||
@ -76,34 +74,34 @@ pub fn init_server<F, B>(process: F, io: IoHandler, remote: Remote) -> (Server,
|
||||
}
|
||||
|
||||
pub fn serve_with_rpc(io: IoHandler) -> Server {
|
||||
init_server(|builder| builder, io, Remote::new_sync()).0
|
||||
init_server(|builder| builder, io).0
|
||||
}
|
||||
|
||||
pub fn serve_hosts(hosts: Option<Vec<String>>) -> Server {
|
||||
let hosts = hosts.map(|hosts| hosts.into_iter().map(Into::into).collect());
|
||||
init_server(|builder| builder.allowed_hosts(hosts.into()), Default::default(), Remote::new_sync()).0
|
||||
init_server(|mut builder| {
|
||||
builder.allowed_hosts = hosts.into();
|
||||
builder
|
||||
}, Default::default()).0
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar() -> (Server, Arc<FakeRegistrar>) {
|
||||
init_server(|builder| builder, Default::default(), Remote::new_sync())
|
||||
init_server(|builder| builder, Default::default())
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar_and_sync() -> (Server, Arc<FakeRegistrar>) {
|
||||
init_server(|builder| {
|
||||
builder.sync_status(Arc::new(FakeSync(true)))
|
||||
}, Default::default(), Remote::new_sync())
|
||||
init_server(|mut builder| {
|
||||
builder.sync_status = Arc::new(FakeSync(true));
|
||||
builder
|
||||
}, Default::default())
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar_and_fetch() -> (Server, FakeFetch, Arc<FakeRegistrar>) {
|
||||
serve_with_registrar_and_fetch_and_threads(false)
|
||||
}
|
||||
|
||||
pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Server, FakeFetch, Arc<FakeRegistrar>) {
|
||||
let fetch = FakeFetch::default();
|
||||
let f = fetch.clone();
|
||||
let (server, reg) = init_server(move |builder| {
|
||||
builder.fetch(f.clone())
|
||||
}, Default::default(), if multi_threaded { Remote::new_thread_per_future() } else { Remote::new_sync() });
|
||||
}, Default::default());
|
||||
|
||||
(server, fetch, reg)
|
||||
}
|
||||
@ -111,19 +109,25 @@ pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Serv
|
||||
pub fn serve_with_fetch(web_token: &'static str, domain: &'static str) -> (Server, FakeFetch) {
|
||||
let fetch = FakeFetch::default();
|
||||
let f = fetch.clone();
|
||||
let (server, _) = init_server(move |builder| {
|
||||
builder
|
||||
.fetch(f.clone())
|
||||
.web_proxy_tokens(Arc::new(move |token| {
|
||||
if &token == web_token { Some(domain.into()) } else { None }
|
||||
}))
|
||||
}, Default::default(), Remote::new_sync());
|
||||
let (server, _) = init_server(move |mut builder| {
|
||||
builder.web_proxy_tokens = Arc::new(move |token| {
|
||||
if &token == web_token { Some(domain.into()) } else { None }
|
||||
});
|
||||
builder.fetch(f.clone())
|
||||
}, Default::default());
|
||||
|
||||
(server, fetch)
|
||||
}
|
||||
|
||||
pub fn serve() -> Server {
|
||||
init_server(|builder| builder, Default::default(), Remote::new_sync()).0
|
||||
init_server(|builder| builder, Default::default()).0
|
||||
}
|
||||
|
||||
pub fn serve_ui() -> Server {
|
||||
init_server(|mut builder| {
|
||||
builder.serve_ui = true;
|
||||
builder
|
||||
}, Default::default()).0
|
||||
}
|
||||
|
||||
pub fn request(server: Server, request: &str) -> http_client::Response {
|
||||
@ -146,13 +150,13 @@ pub struct ServerBuilder<T: Fetch = FetchClient> {
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
signer_address: Option<(String, u16)>,
|
||||
allowed_hosts: DomainsValidation<Host>,
|
||||
remote: Remote,
|
||||
fetch: Option<T>,
|
||||
serve_ui: bool,
|
||||
}
|
||||
|
||||
impl ServerBuilder {
|
||||
/// Construct new dapps server
|
||||
pub fn new<P: AsRef<Path>>(dapps_path: P, registrar: Arc<ContractClient>, remote: Remote) -> Self {
|
||||
pub fn new<P: AsRef<Path>>(dapps_path: P, registrar: Arc<ContractClient>) -> Self {
|
||||
ServerBuilder {
|
||||
dapps_path: dapps_path.as_ref().to_owned(),
|
||||
registrar: registrar,
|
||||
@ -160,8 +164,8 @@ impl ServerBuilder {
|
||||
web_proxy_tokens: Arc::new(|_| None),
|
||||
signer_address: None,
|
||||
allowed_hosts: DomainsValidation::Disabled,
|
||||
remote: remote,
|
||||
fetch: None,
|
||||
serve_ui: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,37 +180,11 @@ impl<T: Fetch> ServerBuilder<T> {
|
||||
web_proxy_tokens: self.web_proxy_tokens,
|
||||
signer_address: self.signer_address,
|
||||
allowed_hosts: self.allowed_hosts,
|
||||
remote: self.remote,
|
||||
fetch: Some(fetch),
|
||||
serve_ui: self.serve_ui,
|
||||
}
|
||||
}
|
||||
|
||||
/// Change default sync status.
|
||||
pub fn sync_status(mut self, status: Arc<SyncStatus>) -> Self {
|
||||
self.sync_status = status;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change default web proxy tokens validator.
|
||||
pub fn web_proxy_tokens(mut self, tokens: Arc<WebProxyTokens>) -> Self {
|
||||
self.web_proxy_tokens = tokens;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change default signer port.
|
||||
pub fn signer_address(mut self, signer_address: Option<(String, u16)>) -> Self {
|
||||
self.signer_address = signer_address;
|
||||
self
|
||||
}
|
||||
|
||||
/// Change allowed hosts.
|
||||
/// `None` - All hosts are allowed
|
||||
/// `Some(whitelist)` - Allow only whitelisted hosts (+ listen address)
|
||||
pub fn allowed_hosts(mut self, allowed_hosts: DomainsValidation<Host>) -> Self {
|
||||
self.allowed_hosts = allowed_hosts;
|
||||
self
|
||||
}
|
||||
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// returns result with `Server` handle on success or an error.
|
||||
pub fn start_unsecured_http(self, addr: &SocketAddr, io: IoHandler) -> Result<Server, http::Error> {
|
||||
@ -221,8 +199,9 @@ impl<T: Fetch> ServerBuilder<T> {
|
||||
self.registrar,
|
||||
self.sync_status,
|
||||
self.web_proxy_tokens,
|
||||
self.remote,
|
||||
Remote::new_sync(),
|
||||
fetch,
|
||||
self.serve_ui,
|
||||
)
|
||||
}
|
||||
|
||||
@ -254,26 +233,39 @@ impl Server {
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
serve_ui: bool,
|
||||
) -> Result<Server, http::Error> {
|
||||
let health = NodeHealth::new(
|
||||
sync_status.clone(),
|
||||
TimeChecker::new::<String>(&[], CpuPool::new(1)),
|
||||
remote.clone(),
|
||||
);
|
||||
let middleware = Middleware::dapps(
|
||||
health,
|
||||
remote,
|
||||
signer_address,
|
||||
vec![],
|
||||
vec![],
|
||||
dapps_path,
|
||||
extra_dapps,
|
||||
DAPPS_DOMAIN.into(),
|
||||
registrar,
|
||||
sync_status,
|
||||
web_proxy_tokens,
|
||||
fetch,
|
||||
);
|
||||
let pool = ::futures_cpupool::CpuPool::new(1);
|
||||
let middleware = if serve_ui {
|
||||
Middleware::ui(
|
||||
pool,
|
||||
health,
|
||||
DAPPS_DOMAIN.into(),
|
||||
registrar,
|
||||
sync_status,
|
||||
fetch,
|
||||
)
|
||||
} else {
|
||||
Middleware::dapps(
|
||||
pool,
|
||||
health,
|
||||
signer_address,
|
||||
vec![],
|
||||
vec![],
|
||||
dapps_path,
|
||||
extra_dapps,
|
||||
DAPPS_DOMAIN.into(),
|
||||
registrar,
|
||||
sync_status,
|
||||
web_proxy_tokens,
|
||||
fetch,
|
||||
)
|
||||
};
|
||||
|
||||
let mut allowed_hosts: Option<Vec<Host>> = allowed_hosts.into();
|
||||
allowed_hosts.as_mut().map(|mut hosts| {
|
||||
@ -295,9 +287,7 @@ impl Server {
|
||||
pub fn addr(&self) -> &SocketAddr {
|
||||
self.server.as_ref()
|
||||
.expect("server is always Some at the start; it's consumed only when object is dropped; qed")
|
||||
.addrs()
|
||||
.first()
|
||||
.expect("You cannot start the server without binding to at least one address; qed")
|
||||
.address()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,13 +17,13 @@
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
use rustc_hex::FromHex;
|
||||
|
||||
use hash_fetch::urlhint::ContractClient;
|
||||
use bigint::hash::H256;
|
||||
use util::Address;
|
||||
use bytes::{Bytes, ToPretty};
|
||||
use hash_fetch::urlhint::{ContractClient, BoxFuture};
|
||||
use parking_lot::Mutex;
|
||||
use rustc_hex::FromHex;
|
||||
use util::Address;
|
||||
|
||||
const REGISTRAR: &'static str = "8e4e9b13d4b45cb0befc93c3061b1408f67316b2";
|
||||
const URLHINT: &'static str = "deadbeefcafe0000000000000000000000000000";
|
||||
@ -67,7 +67,7 @@ impl ContractClient for FakeRegistrar {
|
||||
Ok(REGISTRAR.parse().unwrap())
|
||||
}
|
||||
|
||||
fn call(&self, address: Address, data: Bytes) -> ::futures::BoxFuture<Bytes, String> {
|
||||
fn call(&self, address: Address, data: Bytes) -> BoxFuture<Bytes, String> {
|
||||
let call = (address.to_hex(), data.to_hex());
|
||||
self.calls.lock().push(call.clone());
|
||||
let res = self.responses.lock().get(&call).cloned().expect(&format!("No response for call: {:?}", call));
|
||||
|
62
dapps/src/tests/home.rs
Normal file
62
dapps/src/tests/home.rs
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use tests::helpers::{serve_ui, request, assert_security_headers};
|
||||
|
||||
#[test]
|
||||
fn should_serve_home_js() {
|
||||
// given
|
||||
let server = serve_ui();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET /inject.js HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_header("Content-Type", "application/javascript");
|
||||
assert_eq!(response.body.contains("function(){"), true, "Expected function in: {}", response.body);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_serve_home() {
|
||||
// given
|
||||
let server = serve_ui();
|
||||
|
||||
// when
|
||||
let response = request(server,
|
||||
"\
|
||||
GET / HTTP/1.1\r\n\
|
||||
Host: 127.0.0.1:8080\r\n\
|
||||
Connection: close\r\n\
|
||||
\r\n\
|
||||
{}
|
||||
"
|
||||
);
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
response.assert_header("Content-Type", "text/html");
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
@ -20,6 +20,7 @@ mod helpers;
|
||||
|
||||
mod api;
|
||||
mod fetch;
|
||||
mod home;
|
||||
mod redirection;
|
||||
mod rpc;
|
||||
mod validation;
|
||||
|
@ -201,6 +201,7 @@ fn should_serve_utils() {
|
||||
|
||||
// then
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
assert_eq!(response.body.contains("function(){"), true);
|
||||
response.assert_header("Content-Type", "application/javascript");
|
||||
assert_eq!(response.body.contains("function(){"), true, "Expected function in: {}", response.body);
|
||||
assert_security_headers(&response.headers);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ fn should_reject_invalid_host() {
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned());
|
||||
response.assert_status("HTTP/1.1 403 Forbidden");
|
||||
assert!(response.body.contains("Provided Host header is not whitelisted."), response.body);
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ fn should_allow_valid_host() {
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -74,7 +74,7 @@ fn should_serve_dapps_domains() {
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -95,5 +95,5 @@ fn should_allow_parity_utils_even_on_invalid_domain() {
|
||||
);
|
||||
|
||||
// then
|
||||
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
|
||||
response.assert_status("HTTP/1.1 200 OK");
|
||||
}
|
||||
|
150
dapps/src/url.rs
150
dapps/src/url.rs
@ -1,150 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! HTTP/HTTPS URL type. Based on URL type from Iron library.
|
||||
|
||||
use url_lib::{self};
|
||||
pub use url_lib::Host;
|
||||
|
||||
/// HTTP/HTTPS URL type for Iron.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Url {
|
||||
/// Raw url of url
|
||||
pub raw: url_lib::Url,
|
||||
|
||||
/// The host field of the URL, probably a domain.
|
||||
pub host: Host,
|
||||
|
||||
/// The connection port.
|
||||
pub port: u16,
|
||||
|
||||
/// The URL path, the resource to be accessed.
|
||||
///
|
||||
/// A *non-empty* vector encoding the parts of the URL path.
|
||||
/// Empty entries of `""` correspond to trailing slashes.
|
||||
pub path: Vec<String>,
|
||||
|
||||
/// The URL query.
|
||||
pub query: Option<String>,
|
||||
|
||||
/// The URL username field, from the userinfo section of the URL.
|
||||
///
|
||||
/// `None` if the `@` character was not part of the input OR
|
||||
/// if a blank username was provided.
|
||||
/// Otherwise, a non-empty string.
|
||||
pub username: Option<String>,
|
||||
|
||||
/// The URL password field, from the userinfo section of the URL.
|
||||
///
|
||||
/// `None` if the `@` character was not part of the input OR
|
||||
/// if a blank password was provided.
|
||||
/// Otherwise, a non-empty string.
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
impl Url {
|
||||
/// Create a URL from a string.
|
||||
///
|
||||
/// The input must be a valid URL with a special scheme for this to succeed.
|
||||
///
|
||||
/// HTTP and HTTPS are special schemes.
|
||||
///
|
||||
/// See: http://url.spec.whatwg.org/#special-scheme
|
||||
pub fn parse(input: &str) -> Result<Url, String> {
|
||||
// Parse the string using rust-url, then convert.
|
||||
match url_lib::Url::parse(input) {
|
||||
Ok(raw_url) => Url::from_generic_url(raw_url),
|
||||
Err(e) => Err(format!("{}", e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Url` from a `rust-url` `Url`.
|
||||
pub fn from_generic_url(raw_url: url_lib::Url) -> Result<Url, String> {
|
||||
// Map empty usernames to None.
|
||||
let username = match raw_url.username() {
|
||||
"" => None,
|
||||
username => Some(username.to_owned())
|
||||
};
|
||||
|
||||
// Map empty passwords to None.
|
||||
let password = match raw_url.password() {
|
||||
Some(password) if !password.is_empty() => Some(password.to_owned()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let port = raw_url.port_or_known_default().ok_or_else(|| format!("Unknown port for scheme: `{}`", raw_url.scheme()))?;
|
||||
let host = raw_url.host().ok_or_else(|| "Valid host, because only data:, mailto: protocols does not have host.".to_owned())?.to_owned();
|
||||
let path = raw_url.path_segments().ok_or_else(|| "Valid path segments. In HTTP we won't get cannot-be-a-base URLs".to_owned())?
|
||||
.map(|part| part.to_owned()).collect();
|
||||
let query = raw_url.query().map(|x| x.to_owned());
|
||||
|
||||
Ok(Url {
|
||||
port: port,
|
||||
host: host,
|
||||
path: path,
|
||||
query: query,
|
||||
raw: raw_url,
|
||||
username: username,
|
||||
password: password,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Url;
|
||||
|
||||
#[test]
|
||||
fn test_default_port() {
|
||||
assert_eq!(Url::parse("http://example.com/wow").unwrap().port, 80u16);
|
||||
assert_eq!(Url::parse("https://example.com/wow").unwrap().port, 443u16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_explicit_port() {
|
||||
assert_eq!(Url::parse("http://localhost:3097").unwrap().port, 3097u16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_username() {
|
||||
assert!(Url::parse("http://@example.com").unwrap().username.is_none());
|
||||
assert!(Url::parse("http://:password@example.com").unwrap().username.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_empty_username() {
|
||||
let user = Url::parse("http://john:pass@example.com").unwrap().username;
|
||||
assert_eq!(user.unwrap(), "john");
|
||||
|
||||
let user = Url::parse("http://john:@example.com").unwrap().username;
|
||||
assert_eq!(user.unwrap(), "john");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_password() {
|
||||
assert!(Url::parse("http://michael@example.com").unwrap().password.is_none());
|
||||
assert!(Url::parse("http://:@example.com").unwrap().password.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_empty_password() {
|
||||
let pass = Url::parse("http://michael:pass@example.com").unwrap().password;
|
||||
assert_eq!(pass.unwrap(), "pass");
|
||||
|
||||
let pass = Url::parse("http://:pass@example.com").unwrap().password;
|
||||
assert_eq!(pass.unwrap(), "pass");
|
||||
}
|
||||
}
|
211
dapps/src/web.rs
211
dapps/src/web.rs
@ -17,26 +17,23 @@
|
||||
//! Serving web-based content (proxying)
|
||||
|
||||
use std::sync::Arc;
|
||||
use fetch::{self, Fetch};
|
||||
use parity_reactor::Remote;
|
||||
|
||||
use base32;
|
||||
use hyper::{self, server, net, Next, Encoder, Decoder};
|
||||
use hyper::status::StatusCode;
|
||||
use fetch::{self, Fetch};
|
||||
use hyper::{mime, StatusCode};
|
||||
|
||||
use apps;
|
||||
use endpoint::{Endpoint, Handler, EndpointPath};
|
||||
use endpoint::{Endpoint, EndpointPath, Request, Response};
|
||||
use futures::future;
|
||||
use handlers::{
|
||||
ContentFetcherHandler, ContentHandler, ContentValidator, ValidatorResponse,
|
||||
StreamingHandler, extract_url,
|
||||
StreamingHandler,
|
||||
};
|
||||
use url::Url;
|
||||
use {Embeddable, WebProxyTokens};
|
||||
|
||||
pub struct Web<F> {
|
||||
embeddable_on: Embeddable,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
}
|
||||
|
||||
@ -44,92 +41,27 @@ impl<F: Fetch> Web<F> {
|
||||
pub fn boxed(
|
||||
embeddable_on: Embeddable,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
) -> Box<Endpoint> {
|
||||
Box::new(Web {
|
||||
embeddable_on,
|
||||
web_proxy_tokens,
|
||||
remote,
|
||||
fetch,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fetch> Endpoint for Web<F> {
|
||||
fn to_async_handler(&self, path: EndpointPath, control: hyper::Control) -> Box<Handler> {
|
||||
Box::new(WebHandler {
|
||||
control: control,
|
||||
state: State::Initial,
|
||||
path: path,
|
||||
remote: self.remote.clone(),
|
||||
fetch: self.fetch.clone(),
|
||||
web_proxy_tokens: self.web_proxy_tokens.clone(),
|
||||
embeddable_on: self.embeddable_on.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct WebInstaller {
|
||||
embeddable_on: Embeddable,
|
||||
referer: String,
|
||||
}
|
||||
|
||||
impl ContentValidator for WebInstaller {
|
||||
type Error = String;
|
||||
|
||||
fn validate_and_install(&self, response: fetch::Response) -> Result<ValidatorResponse, String> {
|
||||
let status = StatusCode::from_u16(response.status().to_u16());
|
||||
let is_html = response.is_html();
|
||||
let mime = response.content_type().unwrap_or(mime!(Text/Html));
|
||||
let mut handler = StreamingHandler::new(
|
||||
response,
|
||||
status,
|
||||
mime,
|
||||
self.embeddable_on.clone(),
|
||||
);
|
||||
if is_html {
|
||||
handler.set_initial_content(&format!(
|
||||
r#"<script src="/{}/inject.js"></script><script>history.replaceState({{}}, "", "/?{}{}/{}")</script>"#,
|
||||
apps::UTILS_PATH,
|
||||
apps::URL_REFERER,
|
||||
apps::WEB_PATH,
|
||||
&self.referer,
|
||||
));
|
||||
}
|
||||
Ok(ValidatorResponse::Streaming(handler))
|
||||
}
|
||||
}
|
||||
|
||||
enum State<F: Fetch> {
|
||||
Initial,
|
||||
Error(ContentHandler),
|
||||
Fetching(ContentFetcherHandler<WebInstaller, F>),
|
||||
}
|
||||
|
||||
struct WebHandler<F: Fetch> {
|
||||
control: hyper::Control,
|
||||
state: State<F>,
|
||||
path: EndpointPath,
|
||||
remote: Remote,
|
||||
fetch: F,
|
||||
web_proxy_tokens: Arc<WebProxyTokens>,
|
||||
embeddable_on: Embeddable,
|
||||
}
|
||||
|
||||
impl<F: Fetch> WebHandler<F> {
|
||||
fn extract_target_url(&self, url: Option<Url>) -> Result<String, State<F>> {
|
||||
let token_and_url = self.path.app_params.get(0)
|
||||
fn extract_target_url(&self, path: &EndpointPath) -> Result<String, ContentHandler> {
|
||||
let token_and_url = path.app_params.get(0)
|
||||
.map(|encoded| encoded.replace('.', ""))
|
||||
.and_then(|encoded| base32::decode(base32::Alphabet::Crockford, &encoded.to_uppercase()))
|
||||
.and_then(|data| String::from_utf8(data).ok())
|
||||
.ok_or_else(|| State::Error(ContentHandler::error(
|
||||
.ok_or_else(|| ContentHandler::error(
|
||||
StatusCode::BadRequest,
|
||||
"Invalid parameter",
|
||||
"Couldn't parse given parameter:",
|
||||
self.path.app_params.get(0).map(String::as_str),
|
||||
path.app_params.get(0).map(String::as_str),
|
||||
self.embeddable_on.clone()
|
||||
)))?;
|
||||
))?;
|
||||
|
||||
let mut token_it = token_and_url.split('+');
|
||||
let token = token_it.next();
|
||||
@ -139,9 +71,9 @@ impl<F: Fetch> WebHandler<F> {
|
||||
let domain = match token.and_then(|token| self.web_proxy_tokens.domain(token)) {
|
||||
Some(domain) => domain,
|
||||
_ => {
|
||||
return Err(State::Error(ContentHandler::error(
|
||||
return Err(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Access Token", "Invalid or old web proxy access token supplied.", Some("Try refreshing the page."), self.embeddable_on.clone()
|
||||
)));
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@ -149,95 +81,86 @@ impl<F: Fetch> WebHandler<F> {
|
||||
let mut target_url = match target_url {
|
||||
Some(url) if url.starts_with("http://") || url.starts_with("https://") => url.to_owned(),
|
||||
_ => {
|
||||
return Err(State::Error(ContentHandler::error(
|
||||
return Err(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Protocol", "Invalid protocol used.", None, self.embeddable_on.clone()
|
||||
)));
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if !target_url.starts_with(&*domain) {
|
||||
return Err(State::Error(ContentHandler::error(
|
||||
return Err(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid Domain", "Dapp attempted to access invalid domain.", Some(&target_url), self.embeddable_on.clone(),
|
||||
)));
|
||||
));
|
||||
}
|
||||
|
||||
if !target_url.ends_with("/") {
|
||||
target_url = format!("{}/", target_url);
|
||||
}
|
||||
|
||||
// TODO [ToDr] Should just use `path.app_params`
|
||||
let (path, query) = match (&url, self.path.using_dapps_domains) {
|
||||
(&Some(ref url), true) => (&url.path[..], &url.query),
|
||||
(&Some(ref url), false) => (&url.path[2..], &url.query),
|
||||
_ => {
|
||||
return Err(State::Error(ContentHandler::error(
|
||||
StatusCode::BadRequest, "Invalid URL", "Couldn't parse URL", None, self.embeddable_on.clone()
|
||||
)));
|
||||
}
|
||||
};
|
||||
// Skip the token
|
||||
let query = path.query.as_ref().map_or_else(String::new, |query| format!("?{}", query));
|
||||
let path = path.app_params[1..].join("/");
|
||||
|
||||
let query = match *query {
|
||||
Some(ref query) => format!("?{}", query),
|
||||
None => "".into(),
|
||||
};
|
||||
|
||||
Ok(format!("{}{}{}", target_url, path.join("/"), query))
|
||||
Ok(format!("{}{}{}", target_url, path, query))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fetch> server::Handler<net::HttpStream> for WebHandler<F> {
|
||||
fn on_request(&mut self, request: server::Request<net::HttpStream>) -> Next {
|
||||
let url = extract_url(&request);
|
||||
impl<F: Fetch> Endpoint for Web<F> {
|
||||
fn respond(&self, path: EndpointPath, req: Request) -> Response {
|
||||
// First extract the URL (reject invalid URLs)
|
||||
let target_url = match self.extract_target_url(url) {
|
||||
let target_url = match self.extract_target_url(&path) {
|
||||
Ok(url) => url,
|
||||
Err(error) => {
|
||||
self.state = error;
|
||||
return Next::write();
|
||||
Err(response) => {
|
||||
return Box::new(future::ok(response.into()));
|
||||
}
|
||||
};
|
||||
|
||||
let mut handler = ContentFetcherHandler::new(
|
||||
target_url,
|
||||
self.path.clone(),
|
||||
self.control.clone(),
|
||||
let token = path.app_params.get(0)
|
||||
.expect("`target_url` is valid; app_params is not empty;qed")
|
||||
.to_owned();
|
||||
|
||||
Box::new(ContentFetcherHandler::new(
|
||||
req.method(),
|
||||
&target_url,
|
||||
path,
|
||||
WebInstaller {
|
||||
embeddable_on: self.embeddable_on.clone(),
|
||||
referer: self.path.app_params.get(0)
|
||||
.expect("`target_url` is valid; app_params is not empty;qed")
|
||||
.to_owned(),
|
||||
token,
|
||||
},
|
||||
self.embeddable_on.clone(),
|
||||
self.remote.clone(),
|
||||
self.fetch.clone(),
|
||||
);
|
||||
let res = handler.on_request(request);
|
||||
self.state = State::Fetching(handler);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut Decoder<net::HttpStream>) -> Next {
|
||||
match self.state {
|
||||
State::Initial => Next::end(),
|
||||
State::Error(ref mut handler) => handler.on_request_readable(decoder),
|
||||
State::Fetching(ref mut handler) => handler.on_request_readable(decoder),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut server::Response) -> Next {
|
||||
match self.state {
|
||||
State::Initial => Next::end(),
|
||||
State::Error(ref mut handler) => handler.on_response(res),
|
||||
State::Fetching(ref mut handler) => handler.on_response(res),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<net::HttpStream>) -> Next {
|
||||
match self.state {
|
||||
State::Initial => Next::end(),
|
||||
State::Error(ref mut handler) => handler.on_response_writable(encoder),
|
||||
State::Fetching(ref mut handler) => handler.on_response_writable(encoder),
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
struct WebInstaller {
|
||||
embeddable_on: Embeddable,
|
||||
token: String,
|
||||
}
|
||||
|
||||
impl ContentValidator for WebInstaller {
|
||||
type Error = String;
|
||||
|
||||
fn validate_and_install(self, response: fetch::Response) -> Result<ValidatorResponse, String> {
|
||||
let status = response.status();
|
||||
let is_html = response.is_html();
|
||||
let mime = response.content_type().unwrap_or(mime::TEXT_HTML);
|
||||
let mut handler = StreamingHandler::new(
|
||||
response,
|
||||
status,
|
||||
mime,
|
||||
self.embeddable_on,
|
||||
);
|
||||
if is_html {
|
||||
handler.set_initial_content(&format!(
|
||||
r#"<script src="/{}/inject.js"></script><script>history.replaceState({{}}, "", "/?{}{}/{}")</script>"#,
|
||||
apps::UTILS_PATH,
|
||||
apps::URL_REFERER,
|
||||
apps::WEB_PATH,
|
||||
&self.token,
|
||||
));
|
||||
}
|
||||
Ok(ValidatorResponse::Streaming(handler))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,15 +12,12 @@ build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.9"
|
||||
bit-set = "0.4"
|
||||
bloomchain = "0.1"
|
||||
bn = { git = "https://github.com/paritytech/bn" }
|
||||
byteorder = "1.0"
|
||||
clippy = { version = "0.0.103", optional = true}
|
||||
common-types = { path = "types" }
|
||||
crossbeam = "0.2.9"
|
||||
env_logger = "0.4"
|
||||
ethabi = "2.0"
|
||||
ethash = { path = "../ethash" }
|
||||
ethcore-bloom-journal = { path = "../util/bloom" }
|
||||
ethcore-bytes = { path = "../util/bytes" }
|
||||
@ -45,9 +42,9 @@ heapsize = "0.4"
|
||||
hyper = { git = "https://github.com/paritytech/hyper", default-features = false }
|
||||
itertools = "0.5"
|
||||
lazy_static = "0.2"
|
||||
linked-hash-map = "0.3.0"
|
||||
linked-hash-map = "0.5"
|
||||
log = "0.3"
|
||||
lru-cache = "0.1.0"
|
||||
lru-cache = "0.1"
|
||||
native-contracts = { path = "native_contracts" }
|
||||
num = "0.1"
|
||||
num_cpus = "1.2"
|
||||
@ -60,7 +57,6 @@ rlp = { path = "../util/rlp" }
|
||||
rlp_derive = { path = "../util/rlp_derive" }
|
||||
rust-crypto = "0.2.34"
|
||||
rustc-hex = "1.0"
|
||||
semver = "0.6"
|
||||
stats = { path = "../util/stats" }
|
||||
time = "0.1"
|
||||
transient-hashmap = "0.4"
|
||||
|
@ -430,7 +430,11 @@ impl LightProtocol {
|
||||
|
||||
// compute and deduct cost.
|
||||
let pre_creds = creds.current();
|
||||
let cost = params.compute_cost_multi(requests.requests());
|
||||
let cost = match params.compute_cost_multi(requests.requests()) {
|
||||
Some(cost) => cost,
|
||||
None => return Err(Error::NotServer),
|
||||
};
|
||||
|
||||
creds.deduct_cost(cost)?;
|
||||
|
||||
trace!(target: "pip", "requesting from peer {}. Cost: {}; Available: {}",
|
||||
@ -924,7 +928,7 @@ impl LightProtocol {
|
||||
peer.local_credits.deduct_cost(peer.local_flow.base_cost())?;
|
||||
for request_rlp in raw.at(1)?.iter().take(MAX_REQUESTS) {
|
||||
let request: Request = request_rlp.as_val()?;
|
||||
let cost = peer.local_flow.compute_cost(&request);
|
||||
let cost = peer.local_flow.compute_cost(&request).ok_or(Error::NotServer)?;
|
||||
peer.local_credits.deduct_cost(cost)?;
|
||||
request_builder.push(request).map_err(|_| Error::BadBackReference)?;
|
||||
}
|
||||
@ -939,7 +943,7 @@ impl LightProtocol {
|
||||
match complete_req {
|
||||
CompleteRequest::Headers(req) => self.provider.block_headers(req).map(Response::Headers),
|
||||
CompleteRequest::HeaderProof(req) => self.provider.header_proof(req).map(Response::HeaderProof),
|
||||
CompleteRequest::TransactionIndex(_) => None, // don't answer these yet, but leave them in protocol.
|
||||
CompleteRequest::TransactionIndex(req) => self.provider.transaction_index(req).map(Response::TransactionIndex),
|
||||
CompleteRequest::Body(req) => self.provider.block_body(req).map(Response::Body),
|
||||
CompleteRequest::Receipts(req) => self.provider.block_receipts(req).map(Response::Receipts),
|
||||
CompleteRequest::Account(req) => self.provider.account_proof(req).map(Response::Account),
|
||||
|
@ -79,19 +79,42 @@ impl Credits {
|
||||
}
|
||||
|
||||
/// A cost table, mapping requests to base and per-request costs.
|
||||
/// Costs themselves may be missing.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CostTable {
|
||||
base: U256, // cost per packet.
|
||||
headers: U256, // cost per header
|
||||
transaction_index: U256,
|
||||
body: U256,
|
||||
receipts: U256,
|
||||
account: U256,
|
||||
storage: U256,
|
||||
code: U256,
|
||||
header_proof: U256,
|
||||
transaction_proof: U256, // cost per gas.
|
||||
epoch_signal: U256,
|
||||
headers: Option<U256>, // cost per header
|
||||
transaction_index: Option<U256>,
|
||||
body: Option<U256>,
|
||||
receipts: Option<U256>,
|
||||
account: Option<U256>,
|
||||
storage: Option<U256>,
|
||||
code: Option<U256>,
|
||||
header_proof: Option<U256>,
|
||||
transaction_proof: Option<U256>, // cost per gas.
|
||||
epoch_signal: Option<U256>,
|
||||
}
|
||||
|
||||
impl CostTable {
|
||||
fn costs_set(&self) -> usize {
|
||||
let mut num_set = 0;
|
||||
|
||||
{
|
||||
let mut incr_if_set = |cost: &Option<_>| if cost.is_some() { num_set += 1 };
|
||||
incr_if_set(&self.headers);
|
||||
incr_if_set(&self.transaction_index);
|
||||
incr_if_set(&self.body);
|
||||
incr_if_set(&self.receipts);
|
||||
incr_if_set(&self.account);
|
||||
incr_if_set(&self.storage);
|
||||
incr_if_set(&self.code);
|
||||
incr_if_set(&self.header_proof);
|
||||
incr_if_set(&self.transaction_proof);
|
||||
incr_if_set(&self.epoch_signal);
|
||||
}
|
||||
|
||||
num_set
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CostTable {
|
||||
@ -99,31 +122,32 @@ impl Default for CostTable {
|
||||
// arbitrarily chosen constants.
|
||||
CostTable {
|
||||
base: 100000.into(),
|
||||
headers: 10000.into(),
|
||||
transaction_index: 10000.into(),
|
||||
body: 15000.into(),
|
||||
receipts: 5000.into(),
|
||||
account: 25000.into(),
|
||||
storage: 25000.into(),
|
||||
code: 20000.into(),
|
||||
header_proof: 15000.into(),
|
||||
transaction_proof: 2.into(),
|
||||
epoch_signal: 10000.into(),
|
||||
headers: Some(10000.into()),
|
||||
transaction_index: Some(10000.into()),
|
||||
body: Some(15000.into()),
|
||||
receipts: Some(5000.into()),
|
||||
account: Some(25000.into()),
|
||||
storage: Some(25000.into()),
|
||||
code: Some(20000.into()),
|
||||
header_proof: Some(15000.into()),
|
||||
transaction_proof: Some(2.into()),
|
||||
epoch_signal: Some(10000.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for CostTable {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
fn append_cost(s: &mut RlpStream, cost: &U256, kind: request::Kind) {
|
||||
s.begin_list(2);
|
||||
|
||||
// hack around https://github.com/paritytech/parity/issues/4356
|
||||
Encodable::rlp_append(&kind, s);
|
||||
s.append(cost);
|
||||
fn append_cost(s: &mut RlpStream, cost: &Option<U256>, kind: request::Kind) {
|
||||
if let Some(ref cost) = *cost {
|
||||
s.begin_list(2);
|
||||
// hack around https://github.com/paritytech/parity/issues/4356
|
||||
Encodable::rlp_append(&kind, s);
|
||||
s.append(cost);
|
||||
}
|
||||
}
|
||||
|
||||
s.begin_list(11).append(&self.base);
|
||||
s.begin_list(1 + self.costs_set()).append(&self.base);
|
||||
append_cost(s, &self.headers, request::Kind::Headers);
|
||||
append_cost(s, &self.transaction_index, request::Kind::TransactionIndex);
|
||||
append_cost(s, &self.body, request::Kind::Body);
|
||||
@ -168,21 +192,25 @@ impl Decodable for CostTable {
|
||||
}
|
||||
}
|
||||
|
||||
let unwrap_cost = |cost: Option<U256>| cost.ok_or(DecoderError::Custom("Not all costs specified in cost table."));
|
||||
|
||||
Ok(CostTable {
|
||||
let table = CostTable {
|
||||
base: base,
|
||||
headers: unwrap_cost(headers)?,
|
||||
transaction_index: unwrap_cost(transaction_index)?,
|
||||
body: unwrap_cost(body)?,
|
||||
receipts: unwrap_cost(receipts)?,
|
||||
account: unwrap_cost(account)?,
|
||||
storage: unwrap_cost(storage)?,
|
||||
code: unwrap_cost(code)?,
|
||||
header_proof: unwrap_cost(header_proof)?,
|
||||
transaction_proof: unwrap_cost(transaction_proof)?,
|
||||
epoch_signal: unwrap_cost(epoch_signal)?,
|
||||
})
|
||||
headers: headers,
|
||||
transaction_index: transaction_index,
|
||||
body: body,
|
||||
receipts: receipts,
|
||||
account: account,
|
||||
storage: storage,
|
||||
code: code,
|
||||
header_proof: header_proof,
|
||||
transaction_proof: transaction_proof,
|
||||
epoch_signal: epoch_signal,
|
||||
};
|
||||
|
||||
if table.costs_set() == 0 {
|
||||
Err(DecoderError::Custom("no cost types set."))
|
||||
} else {
|
||||
Ok(table)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,7 +258,7 @@ impl FlowParams {
|
||||
let serve_per_second = serve_per_second.max(1.0 / 10_000.0);
|
||||
|
||||
// as a percentage of the recharge per second.
|
||||
U256::from((recharge as f64 / serve_per_second) as u64)
|
||||
Some(U256::from((recharge as f64 / serve_per_second) as u64))
|
||||
};
|
||||
|
||||
let costs = CostTable {
|
||||
@ -256,12 +284,12 @@ impl FlowParams {
|
||||
|
||||
/// Create effectively infinite flow params.
|
||||
pub fn free() -> Self {
|
||||
let free_cost: U256 = 0.into();
|
||||
let free_cost: Option<U256> = Some(0.into());
|
||||
FlowParams {
|
||||
limit: (!0u64).into(),
|
||||
recharge: 1.into(),
|
||||
costs: CostTable {
|
||||
base: free_cost.clone(),
|
||||
base: 0.into(),
|
||||
headers: free_cost.clone(),
|
||||
transaction_index: free_cost.clone(),
|
||||
body: free_cost.clone(),
|
||||
@ -290,9 +318,9 @@ impl FlowParams {
|
||||
|
||||
/// Compute the actual cost of a request, given the kind of request
|
||||
/// and number of requests made.
|
||||
pub fn compute_cost(&self, request: &Request) -> U256 {
|
||||
pub fn compute_cost(&self, request: &Request) -> Option<U256> {
|
||||
match *request {
|
||||
Request::Headers(ref req) => self.costs.headers * req.max.into(),
|
||||
Request::Headers(ref req) => self.costs.headers.map(|c| c * req.max.into()),
|
||||
Request::HeaderProof(_) => self.costs.header_proof,
|
||||
Request::TransactionIndex(_) => self.costs.transaction_index,
|
||||
Request::Body(_) => self.costs.body,
|
||||
@ -300,15 +328,23 @@ impl FlowParams {
|
||||
Request::Account(_) => self.costs.account,
|
||||
Request::Storage(_) => self.costs.storage,
|
||||
Request::Code(_) => self.costs.code,
|
||||
Request::Execution(ref req) => self.costs.transaction_proof * req.gas,
|
||||
Request::Execution(ref req) => self.costs.transaction_proof.map(|c| c * req.gas),
|
||||
Request::Signal(_) => self.costs.epoch_signal,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the cost of a set of requests.
|
||||
/// This is the base cost plus the cost of each individual request.
|
||||
pub fn compute_cost_multi(&self, requests: &[Request]) -> U256 {
|
||||
requests.iter().fold(self.costs.base, |cost, req| cost + self.compute_cost(req))
|
||||
pub fn compute_cost_multi(&self, requests: &[Request]) -> Option<U256> {
|
||||
let mut cost = self.costs.base;
|
||||
for request in requests {
|
||||
match self.compute_cost(request) {
|
||||
Some(c) => cost = cost + c,
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
|
||||
Some(cost)
|
||||
}
|
||||
|
||||
/// Create initial credits.
|
||||
@ -408,6 +444,6 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(flow_params2.costs, flow_params3.costs);
|
||||
assert_eq!(flow_params.costs.headers, flow_params2.costs.headers * 2.into());
|
||||
assert_eq!(flow_params.costs.headers.unwrap(), flow_params2.costs.headers.unwrap() * 2.into());
|
||||
}
|
||||
}
|
||||
|
@ -116,6 +116,16 @@ impl Provider for TestProvider {
|
||||
self.0.client.block_header(id)
|
||||
}
|
||||
|
||||
fn transaction_index(&self, req: request::CompleteTransactionIndexRequest)
|
||||
-> Option<request::TransactionIndexResponse>
|
||||
{
|
||||
Some(request::TransactionIndexResponse {
|
||||
num: 100,
|
||||
hash: req.hash,
|
||||
index: 55,
|
||||
})
|
||||
}
|
||||
|
||||
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
|
||||
self.0.client.block_body(req)
|
||||
}
|
||||
@ -308,7 +318,7 @@ fn get_block_headers() {
|
||||
let headers: Vec<_> = (0..10).map(|i| provider.client.block_header(BlockId::Number(i + 1)).unwrap()).collect();
|
||||
assert_eq!(headers.len(), 10);
|
||||
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
||||
|
||||
let response = vec![Response::Headers(HeadersResponse {
|
||||
headers: headers,
|
||||
@ -361,7 +371,7 @@ fn get_block_bodies() {
|
||||
let request_body = make_packet(req_id, &requests);
|
||||
|
||||
let response = {
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
||||
|
||||
let mut response_stream = RlpStream::new_list(3);
|
||||
response_stream.append(&req_id).append(&new_creds).append_list(&bodies);
|
||||
@ -416,7 +426,7 @@ fn get_block_receipts() {
|
||||
let response = {
|
||||
assert_eq!(receipts.len(), 10);
|
||||
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
||||
|
||||
let mut response_stream = RlpStream::new_list(3);
|
||||
response_stream.append(&req_id).append(&new_creds).append_list(&receipts);
|
||||
@ -475,7 +485,7 @@ fn get_state_proofs() {
|
||||
}).unwrap()),
|
||||
];
|
||||
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
||||
|
||||
let mut response_stream = RlpStream::new_list(3);
|
||||
response_stream.append(&req_id).append(&new_creds).append_list(&responses);
|
||||
@ -517,7 +527,7 @@ fn get_contract_code() {
|
||||
code: key1.iter().chain(key2.iter()).cloned().collect(),
|
||||
})];
|
||||
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests());
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
||||
|
||||
let mut response_stream = RlpStream::new_list(3);
|
||||
|
||||
@ -558,9 +568,8 @@ fn epoch_signal() {
|
||||
})];
|
||||
|
||||
let limit = *flow_params.limit();
|
||||
let cost = flow_params.compute_cost_multi(requests.requests());
|
||||
let cost = flow_params.compute_cost_multi(requests.requests()).unwrap();
|
||||
|
||||
println!("limit = {}, cost = {}", limit, cost);
|
||||
let new_creds = limit - cost;
|
||||
|
||||
let mut response_stream = RlpStream::new_list(3);
|
||||
@ -605,9 +614,8 @@ fn proof_of_execution() {
|
||||
|
||||
let response = {
|
||||
let limit = *flow_params.limit();
|
||||
let cost = flow_params.compute_cost_multi(requests.requests());
|
||||
let cost = flow_params.compute_cost_multi(requests.requests()).unwrap();
|
||||
|
||||
println!("limit = {}, cost = {}", limit, cost);
|
||||
let new_creds = limit - cost;
|
||||
|
||||
let mut response_stream = RlpStream::new_list(3);
|
||||
@ -713,3 +721,46 @@ fn id_guard() {
|
||||
assert_eq!(peer_info.failed_requests, &[req_id_1]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_transaction_index() {
|
||||
let capabilities = capabilities();
|
||||
|
||||
let (provider, proto) = setup(capabilities.clone());
|
||||
let flow_params = proto.flow_params.read().clone();
|
||||
|
||||
let cur_status = status(provider.client.chain_info());
|
||||
|
||||
{
|
||||
let packet_body = write_handshake(&cur_status, &capabilities, &proto);
|
||||
proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body.clone()));
|
||||
proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &packet_body);
|
||||
}
|
||||
|
||||
let req_id = 112;
|
||||
let key1: H256 = U256::from(11223344).into();
|
||||
|
||||
let request = Request::TransactionIndex(IncompleteTransactionIndexRequest {
|
||||
hash: key1.into(),
|
||||
});
|
||||
|
||||
let requests = encode_single(request.clone());
|
||||
let request_body = make_packet(req_id, &requests);
|
||||
let response = {
|
||||
let response = vec![Response::TransactionIndex(TransactionIndexResponse {
|
||||
num: 100,
|
||||
hash: key1,
|
||||
index: 55,
|
||||
})];
|
||||
|
||||
let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()).unwrap();
|
||||
|
||||
let mut response_stream = RlpStream::new_list(3);
|
||||
|
||||
response_stream.append(&req_id).append(&new_creds).append_list(&response);
|
||||
response_stream.out()
|
||||
};
|
||||
|
||||
let expected = Expect::Respond(packet::RESPONSE, response);
|
||||
proto.handle_packet(&expected, &1, packet::REQUEST, &request_body);
|
||||
}
|
||||
|
@ -195,6 +195,7 @@ fn guess_capabilities(requests: &[CheckedRequest]) -> Capabilities {
|
||||
caps.serve_headers = true,
|
||||
CheckedRequest::HeaderByHash(_, _) =>
|
||||
caps.serve_headers = true,
|
||||
CheckedRequest::TransactionIndex(_, _) => {} // hashes yield no info.
|
||||
CheckedRequest::Signal(_, _) =>
|
||||
caps.serve_headers = true,
|
||||
CheckedRequest::Body(ref req, _) => if let Ok(ref hdr) = req.0.as_ref() {
|
||||
|
@ -48,6 +48,8 @@ pub enum Request {
|
||||
HeaderProof(HeaderProof),
|
||||
/// A request for a header by hash.
|
||||
HeaderByHash(HeaderByHash),
|
||||
/// A request for the index of a transaction.
|
||||
TransactionIndex(TransactionIndex),
|
||||
/// A request for block receipts.
|
||||
Receipts(BlockReceipts),
|
||||
/// A request for a block body.
|
||||
@ -135,6 +137,7 @@ macro_rules! impl_single {
|
||||
// implement traits for each kind of request.
|
||||
impl_single!(HeaderProof, HeaderProof, (H256, U256));
|
||||
impl_single!(HeaderByHash, HeaderByHash, encoded::Header);
|
||||
impl_single!(TransactionIndex, TransactionIndex, net_request::TransactionIndexResponse);
|
||||
impl_single!(Receipts, BlockReceipts, Vec<Receipt>);
|
||||
impl_single!(Body, Body, encoded::Block);
|
||||
impl_single!(Account, Account, Option<BasicAccount>);
|
||||
@ -244,6 +247,7 @@ impl From<encoded::Header> for HeaderRef {
|
||||
pub enum CheckedRequest {
|
||||
HeaderProof(HeaderProof, net_request::IncompleteHeaderProofRequest),
|
||||
HeaderByHash(HeaderByHash, net_request::IncompleteHeadersRequest),
|
||||
TransactionIndex(TransactionIndex, net_request::IncompleteTransactionIndexRequest),
|
||||
Receipts(BlockReceipts, net_request::IncompleteReceiptsRequest),
|
||||
Body(Body, net_request::IncompleteBodyRequest),
|
||||
Account(Account, net_request::IncompleteAccountRequest),
|
||||
@ -270,6 +274,12 @@ impl From<Request> for CheckedRequest {
|
||||
};
|
||||
CheckedRequest::HeaderProof(req, net_req)
|
||||
}
|
||||
Request::TransactionIndex(req) => {
|
||||
let net_req = net_request::IncompleteTransactionIndexRequest {
|
||||
hash: req.0.clone(),
|
||||
};
|
||||
CheckedRequest::TransactionIndex(req, net_req)
|
||||
}
|
||||
Request::Body(req) => {
|
||||
let net_req = net_request::IncompleteBodyRequest {
|
||||
hash: req.0.field(),
|
||||
@ -326,6 +336,7 @@ impl CheckedRequest {
|
||||
match self {
|
||||
CheckedRequest::HeaderProof(_, req) => NetRequest::HeaderProof(req),
|
||||
CheckedRequest::HeaderByHash(_, req) => NetRequest::Headers(req),
|
||||
CheckedRequest::TransactionIndex(_, req) => NetRequest::TransactionIndex(req),
|
||||
CheckedRequest::Receipts(_, req) => NetRequest::Receipts(req),
|
||||
CheckedRequest::Body(_, req) => NetRequest::Body(req),
|
||||
CheckedRequest::Account(_, req) => NetRequest::Account(req),
|
||||
@ -454,6 +465,7 @@ macro_rules! match_me {
|
||||
match $me {
|
||||
CheckedRequest::HeaderProof($check, $req) => $e,
|
||||
CheckedRequest::HeaderByHash($check, $req) => $e,
|
||||
CheckedRequest::TransactionIndex($check, $req) => $e,
|
||||
CheckedRequest::Receipts($check, $req) => $e,
|
||||
CheckedRequest::Body($check, $req) => $e,
|
||||
CheckedRequest::Account($check, $req) => $e,
|
||||
@ -482,6 +494,7 @@ impl IncompleteRequest for CheckedRequest {
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
CheckedRequest::TransactionIndex(_, ref req) => req.check_outputs(f),
|
||||
CheckedRequest::Receipts(_, ref req) => req.check_outputs(f),
|
||||
CheckedRequest::Body(_, ref req) => req.check_outputs(f),
|
||||
CheckedRequest::Account(_, ref req) => req.check_outputs(f),
|
||||
@ -503,6 +516,7 @@ impl IncompleteRequest for CheckedRequest {
|
||||
match self {
|
||||
CheckedRequest::HeaderProof(_, req) => req.complete().map(CompleteRequest::HeaderProof),
|
||||
CheckedRequest::HeaderByHash(_, req) => req.complete().map(CompleteRequest::Headers),
|
||||
CheckedRequest::TransactionIndex(_, req) => req.complete().map(CompleteRequest::TransactionIndex),
|
||||
CheckedRequest::Receipts(_, req) => req.complete().map(CompleteRequest::Receipts),
|
||||
CheckedRequest::Body(_, req) => req.complete().map(CompleteRequest::Body),
|
||||
CheckedRequest::Account(_, req) => req.complete().map(CompleteRequest::Account),
|
||||
@ -545,6 +559,9 @@ impl net_request::CheckedRequest for CheckedRequest {
|
||||
CheckedRequest::HeaderByHash(ref prover, _) =>
|
||||
expect!((&NetResponse::Headers(ref res), &CompleteRequest::Headers(ref req)) =>
|
||||
prover.check_response(cache, &req.start, &res.headers).map(Response::HeaderByHash)),
|
||||
CheckedRequest::TransactionIndex(ref prover, _) =>
|
||||
expect!((&NetResponse::TransactionIndex(ref res), _) =>
|
||||
prover.check_response(cache, res).map(Response::TransactionIndex)),
|
||||
CheckedRequest::Receipts(ref prover, _) =>
|
||||
expect!((&NetResponse::Receipts(ref res), _) =>
|
||||
prover.check_response(cache, &res.receipts).map(Response::Receipts)),
|
||||
@ -575,6 +592,8 @@ pub enum Response {
|
||||
HeaderProof((H256, U256)),
|
||||
/// Response to a header-by-hash request.
|
||||
HeaderByHash(encoded::Header),
|
||||
/// Response to a transaction-index request.
|
||||
TransactionIndex(net_request::TransactionIndexResponse),
|
||||
/// Response to a receipts request.
|
||||
Receipts(Vec<Receipt>),
|
||||
/// Response to a block body request.
|
||||
@ -723,6 +742,33 @@ impl HeaderByHash {
|
||||
}
|
||||
}
|
||||
|
||||
/// Request for a transaction index.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TransactionIndex(pub Field<H256>);
|
||||
|
||||
impl TransactionIndex {
|
||||
/// Check a response for the transaction index.
|
||||
//
|
||||
// TODO: proper checking involves looking at canonicality of the
|
||||
// hash w.r.t. the current best block header.
|
||||
//
|
||||
// unlike all other forms of request, we don't know the header to check
|
||||
// until we make this request.
|
||||
//
|
||||
// This would require lookups in the database or perhaps CHT requests,
|
||||
// which aren't currently possible.
|
||||
//
|
||||
// Also, returning a result that is not locally canonical doesn't necessarily
|
||||
// indicate misbehavior, so the punishment scheme would need to be revised.
|
||||
pub fn check_response(
|
||||
&self,
|
||||
_cache: &Mutex<::cache::Cache>,
|
||||
res: &net_request::TransactionIndexResponse,
|
||||
) -> Result<net_request::TransactionIndexResponse, Error> {
|
||||
Ok(res.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Request for a block, with header for verification.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Body(pub HeaderRef);
|
||||
|
@ -14,7 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! A provider for the LES protocol. This is typically a full node, who can
|
||||
//! A provider for the PIP protocol. This is typically a full node, who can
|
||||
//! give as much data as necessary to its peers.
|
||||
|
||||
use std::sync::Arc;
|
||||
@ -102,6 +102,10 @@ pub trait Provider: Send + Sync {
|
||||
/// Get a block header by id.
|
||||
fn block_header(&self, id: BlockId) -> Option<encoded::Header>;
|
||||
|
||||
/// Get a transaction index by hash.
|
||||
fn transaction_index(&self, req: request::CompleteTransactionIndexRequest)
|
||||
-> Option<request::TransactionIndexResponse>;
|
||||
|
||||
/// Fulfill a block body request.
|
||||
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse>;
|
||||
|
||||
@ -150,6 +154,18 @@ impl<T: ProvingBlockChainClient + ?Sized> Provider for T {
|
||||
BlockChainClient::block_header(self, id)
|
||||
}
|
||||
|
||||
fn transaction_index(&self, req: request::CompleteTransactionIndexRequest)
|
||||
-> Option<request::TransactionIndexResponse>
|
||||
{
|
||||
use ethcore::ids::TransactionId;
|
||||
|
||||
self.transaction_receipt(TransactionId::Hash(req.hash)).map(|receipt| request::TransactionIndexResponse {
|
||||
num: receipt.block_number,
|
||||
hash: receipt.block_hash,
|
||||
index: receipt.transaction_index as u64,
|
||||
})
|
||||
}
|
||||
|
||||
fn block_body(&self, req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
|
||||
BlockChainClient::block_body(self, BlockId::Hash(req.hash))
|
||||
.map(|body| ::request::BodyResponse { body: body })
|
||||
@ -311,6 +327,12 @@ impl<L: AsLightClient + Send + Sync> Provider for LightProvider<L> {
|
||||
self.client.as_light_client().block_header(id)
|
||||
}
|
||||
|
||||
fn transaction_index(&self, _req: request::CompleteTransactionIndexRequest)
|
||||
-> Option<request::TransactionIndexResponse>
|
||||
{
|
||||
None
|
||||
}
|
||||
|
||||
fn block_body(&self, _req: request::CompleteBodyRequest) -> Option<request::BodyResponse> {
|
||||
None
|
||||
}
|
||||
|
@ -46,10 +46,12 @@ pub fn generate_module(struct_name: &str, abi: &str) -> Result<String, Error> {
|
||||
|
||||
Ok(format!(r##"
|
||||
use byteorder::{{BigEndian, ByteOrder}};
|
||||
use futures::{{future, Future, IntoFuture, BoxFuture}};
|
||||
use futures::{{future, Future, IntoFuture}};
|
||||
use ethabi::{{Contract, Interface, Token, Event}};
|
||||
use bigint;
|
||||
|
||||
type BoxFuture<A, B> = Box<Future<Item = A, Error = B> + Send>;
|
||||
|
||||
/// Generated Rust bindings to an Ethereum contract.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct {name} {{
|
||||
@ -118,15 +120,14 @@ pub fn {snake_name}<F, U>(&self, call: F, {params}) -> BoxFuture<{output_type},
|
||||
|
||||
let call_future = match function.encode_call({to_tokens}) {{
|
||||
Ok(call_data) => (call)(call_addr, call_data),
|
||||
Err(e) => return future::err(format!("Error encoding call: {{:?}}", e)).boxed(),
|
||||
Err(e) => return Box::new(future::err(format!("Error encoding call: {{:?}}", e))),
|
||||
}};
|
||||
|
||||
call_future
|
||||
Box::new(call_future
|
||||
.into_future()
|
||||
.and_then(move |out| function.decode_output(out).map_err(|e| format!("{{:?}}", e)))
|
||||
.map(Vec::into_iter)
|
||||
.and_then(|mut outputs| {decode_outputs})
|
||||
.boxed()
|
||||
.and_then(|mut outputs| {decode_outputs}))
|
||||
}}
|
||||
"##,
|
||||
abi_name = name,
|
||||
|
@ -14,7 +14,6 @@
|
||||
"ecip1010PauseTransition": 3000000,
|
||||
"ecip1010ContinueTransition": 5000000,
|
||||
"ecip1017EraRounds": 5000000,
|
||||
|
||||
"eip161abcTransition": "0x7fffffffffffffff",
|
||||
"eip161dTransition": "0x7fffffffffffffff"
|
||||
}
|
||||
@ -31,7 +30,6 @@
|
||||
"forkBlock": "0x1d4c00",
|
||||
"forkCanonHash": "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f",
|
||||
"eip155Transition": 3000000,
|
||||
|
||||
"eip98Transition": "0x7fffffffffffff",
|
||||
"eip86Transition": "0x7fffffffffffff"
|
||||
},
|
||||
|
167
ethcore/res/ethereum/mcip3_test.json
Normal file
167
ethcore/res/ethereum/mcip3_test.json
Normal file
@ -0,0 +1,167 @@
|
||||
{
|
||||
"name":"MCIP3 Test",
|
||||
"dataDir":"mcip3test",
|
||||
"engine":{
|
||||
"Ethash":{
|
||||
"params":{
|
||||
"minimumDifficulty":"0x020000",
|
||||
"difficultyBoundDivisor":"0x0800",
|
||||
"durationLimit":"0x0d",
|
||||
"homesteadTransition":"0x118c30",
|
||||
"eip100bTransition":"0x7fffffffffffff",
|
||||
"eip150Transition":"0x7fffffffffffff",
|
||||
"eip160Transition":"0x7fffffffffffff",
|
||||
"eip161abcTransition":"0x7fffffffffffff",
|
||||
"eip161dTransition":"0x7fffffffffffff",
|
||||
"eip649Transition":"0x7fffffffffffff",
|
||||
"blockReward":"0x1105a0185b50a80000",
|
||||
"mcip3Transition":"0x00",
|
||||
"mcip3MinerReward":"0xd8d726b7177a80000",
|
||||
"mcip3UbiReward":"0x2b5e3af16b1880000",
|
||||
"mcip3UbiContract":"0x00efdd5883ec628983e9063c7d969fe268bbf310",
|
||||
"mcip3DevReward":"0xc249fdd327780000",
|
||||
"mcip3DevContract":"0x00756cf8159095948496617f5fb17ed95059f536"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params":{
|
||||
"gasLimitBoundDivisor":"0x0400",
|
||||
"registrar":"0x5C271c4C9A67E7D73b7b3669d47504741354f21D",
|
||||
"accountStartNonce":"0x00",
|
||||
"maximumExtraDataSize":"0x20",
|
||||
"minGasLimit":"0x1388",
|
||||
"networkID":"0x76740b",
|
||||
"forkBlock":"0x5b6",
|
||||
"forkCanonHash":"0xa5e88ad9e34d113e264e307bc27e8471452c8fc13780324bb3abb96fd0558343",
|
||||
"eip86Transition":"0x7fffffffffffff",
|
||||
"eip98Transition":"0x7fffffffffffff",
|
||||
"eip140Transition":"0x7fffffffffffff",
|
||||
"eip155Transition":"0x7fffffffffffff",
|
||||
"eip211Transition":"0x7fffffffffffff",
|
||||
"eip214Transition":"0x7fffffffffffff",
|
||||
"eip658Transition":"0x7fffffffffffff",
|
||||
"maxCodeSize":"0x6000"
|
||||
},
|
||||
"genesis":{
|
||||
"seal":{
|
||||
"ethereum":{
|
||||
"nonce":"0x000000000000002a",
|
||||
"mixHash":"0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
||||
}
|
||||
},
|
||||
"difficulty":"0x3d0900",
|
||||
"author":"0x0000000000000000000000000000000000000000",
|
||||
"timestamp":"0x00",
|
||||
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData":"",
|
||||
"gasLimit":"0x7a1200"
|
||||
},
|
||||
"nodes":[
|
||||
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
|
||||
"enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303",
|
||||
"enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303",
|
||||
"enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303",
|
||||
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
|
||||
"enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303",
|
||||
"enode://d302f52c8789ad87ee528f1431a67f1aa646c9bec17babb4665dfb3d61de5b9119a70aa77b2147a5f28854092ba09769323c1c552a6ac6f6a34cbcf767e2d2fe@158.69.248.48:30303",
|
||||
"enode://c72564bce8331ae298fb8ece113a456e3927d7e5989c2be3e445678b3600579f722410ef9bbfe339335d676af77343cb21b5b1703b7bebc32be85fce937a2220@191.252.185.71:30303",
|
||||
"enode://e3ae4d25ee64791ff98bf17c37acf90933359f2505c00f65c84f6863231a32a94153cadb0a462e428f18f35ded6bd91cd91033d26576a28558c22678be9cfaee@5.63.158.137:35555"
|
||||
],
|
||||
"accounts":{
|
||||
"0000000000000000000000000000000000000001":{
|
||||
"balance":"1",
|
||||
"builtin":{
|
||||
"name":"ecrecover",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":3000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000002":{
|
||||
"balance":"1",
|
||||
"builtin":{
|
||||
"name":"sha256",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":60,
|
||||
"word":12
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000003":{
|
||||
"balance":"1",
|
||||
"builtin":{
|
||||
"name":"ripemd160",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":600,
|
||||
"word":120
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000004":{
|
||||
"balance":"1",
|
||||
"builtin":{
|
||||
"name":"identity",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":15,
|
||||
"word":3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000005":{
|
||||
"builtin":{
|
||||
"name":"modexp",
|
||||
"activate_at":"0x7fffffffffffff",
|
||||
"pricing":{
|
||||
"modexp":{
|
||||
"divisor":20
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000006":{
|
||||
"builtin":{
|
||||
"name":"alt_bn128_add",
|
||||
"activate_at":"0x7fffffffffffff",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":500,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000007":{
|
||||
"builtin":{
|
||||
"name":"alt_bn128_mul",
|
||||
"activate_at":"0x7fffffffffffff",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":40000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000008":{
|
||||
"builtin":{
|
||||
"name":"alt_bn128_pairing",
|
||||
"activate_at":"0x7fffffffffffff",
|
||||
"pricing":{
|
||||
"alt_bn128_pairing":{
|
||||
"base":100000,
|
||||
"pair":80000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
167
ethcore/res/ethereum/musicoin.json
Normal file
167
ethcore/res/ethereum/musicoin.json
Normal file
@ -0,0 +1,167 @@
|
||||
{
|
||||
"name":"Musicoin",
|
||||
"dataDir":"musicoin",
|
||||
"engine":{
|
||||
"Ethash":{
|
||||
"params":{
|
||||
"minimumDifficulty":"0x020000",
|
||||
"difficultyBoundDivisor":"0x0800",
|
||||
"durationLimit":"0x0d",
|
||||
"homesteadTransition":"0x118c30",
|
||||
"eip100bTransition":"0x7fffffffffffff",
|
||||
"eip150Transition":"0x7fffffffffffff",
|
||||
"eip160Transition":"0x7fffffffffffff",
|
||||
"eip161abcTransition":"0x7fffffffffffff",
|
||||
"eip161dTransition":"0x7fffffffffffff",
|
||||
"eip649Transition":"0x7fffffffffffff",
|
||||
"blockReward":"0x1105a0185b50a80000",
|
||||
"mcip3Transition":"0x124f81",
|
||||
"mcip3MinerReward":"0xd8d726b7177a80000",
|
||||
"mcip3UbiReward":"0x2b5e3af16b1880000",
|
||||
"mcip3UbiContract":"0x00efdd5883ec628983e9063c7d969fe268bbf310",
|
||||
"mcip3DevReward":"0xc249fdd327780000",
|
||||
"mcip3DevContract":"0x00756cf8159095948496617f5fb17ed95059f536"
|
||||
}
|
||||
}
|
||||
},
|
||||
"params":{
|
||||
"gasLimitBoundDivisor":"0x0400",
|
||||
"registrar":"0x5C271c4C9A67E7D73b7b3669d47504741354f21D",
|
||||
"accountStartNonce":"0x00",
|
||||
"maximumExtraDataSize":"0x20",
|
||||
"minGasLimit":"0x1388",
|
||||
"networkID":"0x76740f",
|
||||
"forkBlock":"0x5b6",
|
||||
"forkCanonHash":"0xa5e88ad9e34d113e264e307bc27e8471452c8fc13780324bb3abb96fd0558343",
|
||||
"eip86Transition":"0x7fffffffffffff",
|
||||
"eip98Transition":"0x7fffffffffffff",
|
||||
"eip140Transition":"0x7fffffffffffff",
|
||||
"eip155Transition":"0x7fffffffffffff",
|
||||
"eip211Transition":"0x7fffffffffffff",
|
||||
"eip214Transition":"0x7fffffffffffff",
|
||||
"eip658Transition":"0x7fffffffffffff",
|
||||
"maxCodeSize":"0x6000"
|
||||
},
|
||||
"genesis":{
|
||||
"seal":{
|
||||
"ethereum":{
|
||||
"nonce":"0x000000000000002a",
|
||||
"mixHash":"0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
||||
}
|
||||
},
|
||||
"difficulty":"0x3d0900",
|
||||
"author":"0x0000000000000000000000000000000000000000",
|
||||
"timestamp":"0x00",
|
||||
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData":"",
|
||||
"gasLimit":"0x7a1200"
|
||||
},
|
||||
"nodes":[
|
||||
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303",
|
||||
"enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303",
|
||||
"enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303",
|
||||
"enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303",
|
||||
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303",
|
||||
"enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303",
|
||||
"enode://d302f52c8789ad87ee528f1431a67f1aa646c9bec17babb4665dfb3d61de5b9119a70aa77b2147a5f28854092ba09769323c1c552a6ac6f6a34cbcf767e2d2fe@158.69.248.48:30303",
|
||||
"enode://c72564bce8331ae298fb8ece113a456e3927d7e5989c2be3e445678b3600579f722410ef9bbfe339335d676af77343cb21b5b1703b7bebc32be85fce937a2220@191.252.185.71:30303",
|
||||
"enode://e3ae4d25ee64791ff98bf17c37acf90933359f2505c00f65c84f6863231a32a94153cadb0a462e428f18f35ded6bd91cd91033d26576a28558c22678be9cfaee@5.63.158.137:35555"
|
||||
],
|
||||
"accounts":{
|
||||
"0000000000000000000000000000000000000001":{
|
||||
"balance":"1",
|
||||
"builtin":{
|
||||
"name":"ecrecover",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":3000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000002":{
|
||||
"balance":"1",
|
||||
"builtin":{
|
||||
"name":"sha256",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":60,
|
||||
"word":12
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000003":{
|
||||
"balance":"1",
|
||||
"builtin":{
|
||||
"name":"ripemd160",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":600,
|
||||
"word":120
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000004":{
|
||||
"balance":"1",
|
||||
"builtin":{
|
||||
"name":"identity",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":15,
|
||||
"word":3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000005":{
|
||||
"builtin":{
|
||||
"name":"modexp",
|
||||
"activate_at":"0x7fffffffffffff",
|
||||
"pricing":{
|
||||
"modexp":{
|
||||
"divisor":20
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000006":{
|
||||
"builtin":{
|
||||
"name":"alt_bn128_add",
|
||||
"activate_at":"0x7fffffffffffff",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":500,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000007":{
|
||||
"builtin":{
|
||||
"name":"alt_bn128_mul",
|
||||
"activate_at":"0x7fffffffffffff",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":40000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000008":{
|
||||
"builtin":{
|
||||
"name":"alt_bn128_pairing",
|
||||
"activate_at":"0x7fffffffffffff",
|
||||
"pricing":{
|
||||
"alt_bn128_pairing":{
|
||||
"base":100000,
|
||||
"pair":80000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit fcac936bf68cc271a6a6ac088efb458f3a08f38a
|
||||
Subproject commit c8129ce2f36c26ed634eda786960978a28e28d0e
|
@ -18,8 +18,10 @@
|
||||
|
||||
use bigint::hash::H256;
|
||||
|
||||
use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp};
|
||||
|
||||
/// A full epoch transition.
|
||||
#[derive(Debug, Clone, RlpEncodable, RlpDecodable)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Transition {
|
||||
/// Block hash at which the transition occurred.
|
||||
pub block_hash: H256,
|
||||
@ -29,14 +31,46 @@ pub struct Transition {
|
||||
pub proof: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Encodable for Transition {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.begin_list(3)
|
||||
.append(&self.block_hash)
|
||||
.append(&self.block_number)
|
||||
.append(&self.proof);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Transition {
|
||||
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
|
||||
Ok(Transition {
|
||||
block_hash: rlp.val_at(0)?,
|
||||
block_number: rlp.val_at(1)?,
|
||||
proof: rlp.val_at(2)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An epoch transition pending a finality proof.
|
||||
/// Not all transitions need one.
|
||||
#[derive(RlpEncodableWrapper, RlpDecodableWrapper)]
|
||||
pub struct PendingTransition {
|
||||
/// "transition/epoch" proof from the engine.
|
||||
pub proof: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Encodable for PendingTransition {
|
||||
fn rlp_append(&self, s: &mut RlpStream) {
|
||||
s.append(&self.proof);
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for PendingTransition {
|
||||
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
|
||||
Ok(PendingTransition {
|
||||
proof: rlp.as_val()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifier for all blocks within an epoch with self-contained state.
|
||||
pub trait EpochVerifier<M: ::parity_machine::Machine>: Send + Sync {
|
||||
/// Lightly verify the next block header.
|
||||
|
@ -191,9 +191,6 @@ pub trait Engine<M: Machine>: Sync + Send {
|
||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||
fn extra_info(&self, _header: &M::Header) -> BTreeMap<String, String> { BTreeMap::new() }
|
||||
|
||||
/// Additional information.
|
||||
fn additional_params(&self) -> HashMap<String, String> { HashMap::new() }
|
||||
|
||||
/// Maximum number of uncles a block is allowed to declare.
|
||||
fn maximum_uncle_count(&self) -> usize { 2 }
|
||||
/// The number of generations back that uncles can be.
|
||||
@ -396,6 +393,11 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {
|
||||
fn supports_wasm(&self) -> bool {
|
||||
self.machine().supports_wasm()
|
||||
}
|
||||
|
||||
/// Additional information.
|
||||
fn additional_params(&self) -> HashMap<String, String> {
|
||||
self.machine().additional_params()
|
||||
}
|
||||
}
|
||||
|
||||
// convenience wrappers for existing functions.
|
||||
|
@ -164,8 +164,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn uses_current_set() {
|
||||
let _ = ::env_logger::init();
|
||||
|
||||
let tap = Arc::new(AccountProvider::transient_provider());
|
||||
let s0: Secret = keccak("0").into();
|
||||
let v0 = tap.insert_account(s0.clone(), "").unwrap();
|
||||
|
@ -16,17 +16,18 @@
|
||||
|
||||
use std::path::Path;
|
||||
use std::cmp;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use hash::{KECCAK_EMPTY_LIST_RLP};
|
||||
use ethash::{quick_get_difficulty, slow_hash_block_number, EthashManager, OptimizeFor};
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::{H256, H64};
|
||||
use util::Address;
|
||||
use unexpected::{OutOfBounds, Mismatch};
|
||||
use block::*;
|
||||
use error::{BlockError, Error};
|
||||
use header::Header;
|
||||
use engines::{self, Engine, EthEngine};
|
||||
use engines::{self, Engine};
|
||||
use ethjson;
|
||||
use rlp::{self, UntrustedRlp};
|
||||
use machine::EthereumMachine;
|
||||
@ -69,6 +70,18 @@ pub struct EthashParams {
|
||||
pub ecip1010_continue_transition: u64,
|
||||
/// Total block number for one ECIP-1017 era.
|
||||
pub ecip1017_era_rounds: u64,
|
||||
/// Number of first block where MCIP-3 begins.
|
||||
pub mcip3_transition: u64,
|
||||
/// MCIP-3 Block reward coin-base for miners.
|
||||
pub mcip3_miner_reward: U256,
|
||||
/// MCIP-3 Block reward ubi-base for basic income.
|
||||
pub mcip3_ubi_reward: U256,
|
||||
/// MCIP-3 contract address for universal basic income.
|
||||
pub mcip3_ubi_contract: Address,
|
||||
/// MCIP-3 Block reward dev-base for dev funds.
|
||||
pub mcip3_dev_reward: U256,
|
||||
/// MCIP-3 contract address for the developer funds.
|
||||
pub mcip3_dev_contract: Address,
|
||||
/// Block reward in base units.
|
||||
pub block_reward: U256,
|
||||
/// EIP-649 transition block.
|
||||
@ -95,6 +108,12 @@ impl From<ethjson::spec::EthashParams> for EthashParams {
|
||||
ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(u64::max_value(), Into::into),
|
||||
ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(u64::max_value(), Into::into),
|
||||
ecip1017_era_rounds: p.ecip1017_era_rounds.map_or(u64::max_value(), Into::into),
|
||||
mcip3_transition: p.mcip3_transition.map_or(u64::max_value(), Into::into),
|
||||
mcip3_miner_reward: p.mcip3_miner_reward.map_or_else(Default::default, Into::into),
|
||||
mcip3_ubi_reward: p.mcip3_ubi_reward.map_or(U256::from(0), Into::into),
|
||||
mcip3_ubi_contract: p.mcip3_ubi_contract.map_or_else(Address::new, Into::into),
|
||||
mcip3_dev_reward: p.mcip3_dev_reward.map_or(U256::from(0), Into::into),
|
||||
mcip3_dev_contract: p.mcip3_dev_contract.map_or_else(Address::new, Into::into),
|
||||
block_reward: p.block_reward.map_or_else(Default::default, Into::into),
|
||||
eip649_transition: p.eip649_transition.map_or(u64::max_value(), Into::into),
|
||||
eip649_delay: p.eip649_delay.map_or(DEFAULT_EIP649_DELAY, Into::into),
|
||||
@ -150,8 +169,6 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
|
||||
// Two fields - nonce and mix.
|
||||
fn seal_fields(&self) -> usize { 2 }
|
||||
|
||||
fn additional_params(&self) -> HashMap<String, String> { hash_map!["registrar".to_owned() => self.params().registrar.hex()] }
|
||||
|
||||
/// Additional engine-specific information for the user/developer concerning `header`.
|
||||
fn extra_info(&self, header: &Header) -> BTreeMap<String, String> {
|
||||
if header.seal().len() == self.seal_fields() {
|
||||
@ -186,24 +203,38 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
|
||||
let author = *LiveBlock::header(&*block).author();
|
||||
let number = LiveBlock::header(&*block).number();
|
||||
|
||||
// Applies EIP-649 reward.
|
||||
let reward = if number >= self.ethash_params.eip649_transition {
|
||||
self.ethash_params.eip649_reward.unwrap_or(self.ethash_params.block_reward)
|
||||
} else {
|
||||
self.ethash_params.block_reward
|
||||
};
|
||||
|
||||
// Applies ECIP-1017 eras.
|
||||
let eras_rounds = self.ethash_params.ecip1017_era_rounds;
|
||||
let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, reward, number);
|
||||
|
||||
let n_uncles = LiveBlock::uncles(&*block).len();
|
||||
|
||||
// Bestow block reward
|
||||
let result_block_reward = reward + reward.shr(5) * U256::from(n_uncles);
|
||||
// Bestow block rewards.
|
||||
let mut result_block_reward = reward + reward.shr(5) * U256::from(n_uncles);
|
||||
let mut uncle_rewards = Vec::with_capacity(n_uncles);
|
||||
|
||||
self.machine.add_balance(block, &author, &result_block_reward)?;
|
||||
if number >= self.ethash_params.mcip3_transition {
|
||||
result_block_reward = self.ethash_params.mcip3_miner_reward;
|
||||
let ubi_contract = self.ethash_params.mcip3_ubi_contract;
|
||||
let ubi_reward = self.ethash_params.mcip3_ubi_reward;
|
||||
let dev_contract = self.ethash_params.mcip3_dev_contract;
|
||||
let dev_reward = self.ethash_params.mcip3_dev_reward;
|
||||
|
||||
// bestow uncle rewards.
|
||||
self.machine.add_balance(block, &author, &result_block_reward)?;
|
||||
self.machine.add_balance(block, &ubi_contract, &ubi_reward)?;
|
||||
self.machine.add_balance(block, &dev_contract, &dev_reward)?;
|
||||
} else {
|
||||
self.machine.add_balance(block, &author, &result_block_reward)?;
|
||||
}
|
||||
|
||||
// Bestow uncle rewards.
|
||||
for u in LiveBlock::uncles(&*block) {
|
||||
let uncle_author = u.author();
|
||||
let result_uncle_reward = if eras == 0 {
|
||||
@ -219,7 +250,7 @@ impl Engine<EthereumMachine> for Arc<Ethash> {
|
||||
self.machine.add_balance(block, a, reward)?;
|
||||
}
|
||||
|
||||
// note and trace.
|
||||
// Note and trace.
|
||||
self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards)
|
||||
}
|
||||
|
||||
@ -434,7 +465,7 @@ mod tests {
|
||||
use error::{BlockError, Error};
|
||||
use header::Header;
|
||||
use spec::Spec;
|
||||
use super::super::{new_morden, new_homestead_test_machine};
|
||||
use super::super::{new_morden, new_mcip3_test, new_homestead_test_machine};
|
||||
use super::{Ethash, EthashParams, ecip1017_eras_block_reward};
|
||||
use rlp;
|
||||
|
||||
@ -504,6 +535,23 @@ mod tests {
|
||||
assert_eq!(b.state().balance(&uncle_author).unwrap(), "3cb71f51fc558000".into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_valid_mcip3_era_block_rewards() {
|
||||
let spec = new_mcip3_test();
|
||||
let engine = &*spec.engine;
|
||||
let genesis_header = spec.genesis_header();
|
||||
let db = spec.ensure_db_good(get_temp_state_db(), &Default::default()).unwrap();
|
||||
let last_hashes = Arc::new(vec![genesis_header.hash()]);
|
||||
let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![], false).unwrap();
|
||||
let b = b.close();
|
||||
|
||||
let ubi_contract: Address = "00efdd5883ec628983e9063c7d969fe268bbf310".into();
|
||||
let dev_contract: Address = "00756cf8159095948496617f5fb17ed95059f536".into();
|
||||
assert_eq!(b.state().balance(&Address::zero()).unwrap(), U256::from_str("d8d726b7177a80000").unwrap());
|
||||
assert_eq!(b.state().balance(&ubi_contract).unwrap(), U256::from_str("2b5e3af16b1880000").unwrap());
|
||||
assert_eq!(b.state().balance(&dev_contract).unwrap(), U256::from_str("c249fdd327780000").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_valid_metadata() {
|
||||
let engine = test_spec().engine;
|
||||
|
@ -70,6 +70,11 @@ pub fn new_expanse<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
|
||||
load(params.into(), include_bytes!("../../res/ethereum/expanse.json"))
|
||||
}
|
||||
|
||||
/// Create a new Musicoin mainnet chain spec.
|
||||
pub fn new_musicoin<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
|
||||
load(params.into(), include_bytes!("../../res/ethereum/musicoin.json"))
|
||||
}
|
||||
|
||||
/// Create a new Kovan testnet chain spec.
|
||||
pub fn new_kovan<'a, T: Into<SpecParams<'a>>>(params: T) -> Spec {
|
||||
load(params.into(), include_bytes!("../../res/ethereum/kovan.json"))
|
||||
@ -111,6 +116,9 @@ pub fn new_byzantium_test() -> Spec { load(None, include_bytes!("../../res/ether
|
||||
/// Create a new Foundation Constantinople era spec.
|
||||
pub fn new_constantinople_test() -> Spec { load(None, include_bytes!("../../res/ethereum/constantinople_test.json")) }
|
||||
|
||||
/// Create a new Musicoin-MCIP3-era spec.
|
||||
pub fn new_mcip3_test() -> Spec { load(None, include_bytes!("../../res/ethereum/mcip3_test.json")) }
|
||||
|
||||
// For tests
|
||||
|
||||
/// Create a new Foundation Frontier-era chain spec as though it never changes to Homestead.
|
||||
@ -125,6 +133,9 @@ pub fn new_byzantium_test_machine() -> EthereumMachine { load_machine(include_by
|
||||
/// Create a new Foundation Constantinople era spec.
|
||||
pub fn new_constantinople_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/constantinople_test.json")) }
|
||||
|
||||
/// Create a new Musicoin-MCIP3-era spec.
|
||||
pub fn new_mcip3_test_machine() -> EthereumMachine { load_machine(include_bytes!("../../res/ethereum/mcip3_test.json")) }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bigint::prelude::U256;
|
||||
|
@ -71,15 +71,12 @@
|
||||
//! cargo build --release
|
||||
//! ```
|
||||
|
||||
extern crate bit_set;
|
||||
extern crate bloomchain;
|
||||
extern crate bn;
|
||||
extern crate byteorder;
|
||||
extern crate crossbeam;
|
||||
extern crate common_types as types;
|
||||
extern crate crypto;
|
||||
extern crate env_logger;
|
||||
extern crate ethabi;
|
||||
extern crate ethash;
|
||||
extern crate ethcore_bloom_journal as bloom_journal;
|
||||
extern crate ethcore_devtools as devtools;
|
||||
@ -119,7 +116,6 @@ extern crate unexpected;
|
||||
#[macro_use]
|
||||
extern crate rlp_derive;
|
||||
extern crate rustc_hex;
|
||||
extern crate semver;
|
||||
extern crate stats;
|
||||
extern crate time;
|
||||
extern crate transient_hashmap;
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
//! Ethereum-like state machine definition.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::cmp;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -378,6 +378,13 @@ impl EthereumMachine {
|
||||
pub fn supports_wasm(&self) -> bool {
|
||||
self.params().wasm
|
||||
}
|
||||
|
||||
/// Additional params.
|
||||
pub fn additional_params(&self) -> HashMap<String, String> {
|
||||
hash_map![
|
||||
"registrar".to_owned() => self.params.registrar.hex()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/// Auxiliary data fetcher for an Ethereum machine. In Ethereum-like machines
|
||||
|
@ -377,6 +377,12 @@ pub fn get_default_ethash_params() -> EthashParams {
|
||||
ecip1010_pause_transition: u64::max_value(),
|
||||
ecip1010_continue_transition: u64::max_value(),
|
||||
ecip1017_era_rounds: u64::max_value(),
|
||||
mcip3_transition: u64::max_value(),
|
||||
mcip3_miner_reward: 0.into(),
|
||||
mcip3_ubi_reward: 0.into(),
|
||||
mcip3_ubi_contract: "0000000000000000000000000000000000000001".into(),
|
||||
mcip3_dev_reward: 0.into(),
|
||||
mcip3_dev_contract: "0000000000000000000000000000000000000001".into(),
|
||||
eip649_transition: u64::max_value(),
|
||||
eip649_delay: 3_000_000,
|
||||
eip649_reward: None,
|
||||
|
@ -1,63 +0,0 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Wasm evm call arguments helper
|
||||
|
||||
use bigint::prelude::U256;
|
||||
use bigint::hash::H160;
|
||||
|
||||
/// Input part of the wasm call descriptor
|
||||
pub struct CallArgs {
|
||||
/// Receiver of the transaction
|
||||
pub address: [u8; 20],
|
||||
|
||||
/// Sender of the transaction
|
||||
pub sender: [u8; 20],
|
||||
|
||||
/// Original transaction initiator
|
||||
pub origin: [u8; 20],
|
||||
|
||||
/// Transfer value
|
||||
pub value: [u8; 32],
|
||||
|
||||
/// call/create params
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl CallArgs {
|
||||
/// New contract call payload with known parameters
|
||||
pub fn new(address: H160, sender: H160, origin: H160, value: U256, data: Vec<u8>) -> Self {
|
||||
let mut descriptor = CallArgs {
|
||||
address: [0u8; 20],
|
||||
sender: [0u8; 20],
|
||||
origin: [0u8; 20],
|
||||
value: [0u8; 32],
|
||||
data: data,
|
||||
};
|
||||
|
||||
descriptor.address.copy_from_slice(&*address);
|
||||
descriptor.sender.copy_from_slice(&*sender);
|
||||
descriptor.origin.copy_from_slice(&*origin);
|
||||
value.to_big_endian(&mut descriptor.value);
|
||||
|
||||
descriptor
|
||||
}
|
||||
|
||||
/// Total call payload length in linear memory
|
||||
pub fn len(&self) -> u32 {
|
||||
self.data.len() as u32 + 92
|
||||
}
|
||||
}
|
@ -32,6 +32,11 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
|
||||
&[I32; 2],
|
||||
Some(I32),
|
||||
),
|
||||
Static(
|
||||
"_balance",
|
||||
&[I32; 2],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_malloc",
|
||||
&[I32],
|
||||
@ -102,6 +107,26 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_sender",
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_origin",
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_address",
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_value",
|
||||
&[I32],
|
||||
None,
|
||||
),
|
||||
Static(
|
||||
"_timestamp",
|
||||
&[],
|
||||
@ -143,4 +168,4 @@ pub fn native_bindings<'a>(runtime: &'a mut Runtime) -> interpreter::UserDefined
|
||||
globals: ::std::collections::HashMap::new(),
|
||||
functions: ::std::borrow::Cow::from(SIGNATURES),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ extern crate wasm_utils;
|
||||
|
||||
mod runtime;
|
||||
mod ptr;
|
||||
mod call_args;
|
||||
mod result;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@ -107,7 +106,12 @@ impl vm::Vm for WasmInterpreter {
|
||||
env_memory,
|
||||
DEFAULT_STACK_SPACE,
|
||||
params.gas.low_u64(),
|
||||
RuntimeContext::new(params.address, params.sender),
|
||||
RuntimeContext {
|
||||
address: params.address,
|
||||
sender: params.sender,
|
||||
origin: params.origin,
|
||||
value: params.value.value(),
|
||||
},
|
||||
&self.program,
|
||||
);
|
||||
|
||||
@ -121,15 +125,8 @@ impl vm::Vm for WasmInterpreter {
|
||||
})?
|
||||
);
|
||||
|
||||
let d_ptr = runtime.write_descriptor(
|
||||
call_args::CallArgs::new(
|
||||
params.address,
|
||||
params.sender,
|
||||
params.origin,
|
||||
params.value.value(),
|
||||
params.data.unwrap_or(Vec::with_capacity(0)),
|
||||
)
|
||||
).map_err(|e| Error(e))?;
|
||||
let d_ptr = runtime.write_descriptor(¶ms.data.unwrap_or_default())
|
||||
.map_err(Error)?;
|
||||
|
||||
{
|
||||
let execution_params = runtime.execution_params()
|
||||
|
@ -28,7 +28,6 @@ use util::Address;
|
||||
|
||||
use vm::CallType;
|
||||
use super::ptr::{WasmPtr, Error as PtrError};
|
||||
use super::call_args::CallArgs;
|
||||
|
||||
/// User trap in native code
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@ -97,17 +96,10 @@ impl From<PtrError> for UserTrap {
|
||||
}
|
||||
|
||||
pub struct RuntimeContext {
|
||||
address: Address,
|
||||
sender: Address,
|
||||
}
|
||||
|
||||
impl RuntimeContext {
|
||||
pub fn new(address: Address, sender: Address) -> Self {
|
||||
RuntimeContext {
|
||||
address: address,
|
||||
sender: sender,
|
||||
}
|
||||
}
|
||||
pub address: Address,
|
||||
pub sender: Address,
|
||||
pub origin: Address,
|
||||
pub value: U256,
|
||||
}
|
||||
|
||||
/// Runtime enviroment data for wasm contract execution
|
||||
@ -171,6 +163,19 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
||||
Ok(Some(0.into()))
|
||||
}
|
||||
|
||||
/// Fetches balance for address
|
||||
pub fn balance(&mut self, context: InterpreterCallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||
{
|
||||
let mut context = context;
|
||||
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||
let address = self.pop_address(&mut context)?;
|
||||
let balance = self.ext.balance(&address).map_err(|_| UserTrap::BalanceQueryError)?;
|
||||
let value: H256 = balance.into();
|
||||
self.memory.set(return_ptr, &*value)?;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Pass suicide to state runtime
|
||||
pub fn suicide(&mut self, context: InterpreterCallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||
@ -442,10 +447,10 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
||||
}
|
||||
|
||||
/// Write call descriptor to wasm memory
|
||||
pub fn write_descriptor(&mut self, call_args: CallArgs) -> Result<WasmPtr, InterpreterError> {
|
||||
pub fn write_descriptor(&mut self, input: &[u8]) -> Result<WasmPtr, InterpreterError> {
|
||||
let d_ptr = self.alloc(16)?;
|
||||
|
||||
let args_len = call_args.len();
|
||||
let args_len = input.len() as u32;
|
||||
let args_ptr = self.alloc(args_len)?;
|
||||
|
||||
// write call descriptor
|
||||
@ -457,11 +462,7 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
||||
self.memory.set(d_ptr, &d_buf)?;
|
||||
|
||||
// write call args to memory
|
||||
self.memory.set(args_ptr, &call_args.address)?;
|
||||
self.memory.set(args_ptr+20, &call_args.sender)?;
|
||||
self.memory.set(args_ptr+40, &call_args.origin)?;
|
||||
self.memory.set(args_ptr+60, &call_args.value)?;
|
||||
self.memory.set(args_ptr+92, &call_args.data)?;
|
||||
self.memory.set(args_ptr, input)?;
|
||||
|
||||
Ok(d_ptr.into())
|
||||
}
|
||||
@ -559,6 +560,39 @@ impl<'a, 'b> Runtime<'a, 'b> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn sender(&mut self, context: InterpreterCallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||
{
|
||||
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||
self.memory.set(return_ptr, &*self.context.sender)?;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn address(&mut self, context: InterpreterCallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||
{
|
||||
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||
self.memory.set(return_ptr, &*self.context.address)?;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn origin(&mut self, context: InterpreterCallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||
{
|
||||
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||
self.memory.set(return_ptr, &*self.context.origin)?;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn value(&mut self, context: InterpreterCallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||
{
|
||||
let return_ptr = context.value_stack.pop_as::<i32>()? as u32;
|
||||
let value: H256 = self.context.value.clone().into();
|
||||
self.memory.set(return_ptr, &*value)?;
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn timestamp(&mut self, _context: InterpreterCallerContext)
|
||||
-> Result<Option<interpreter::RuntimeValue>, InterpreterError>
|
||||
{
|
||||
@ -643,6 +677,9 @@ impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
|
||||
"_storage_write" => {
|
||||
self.storage_write(context)
|
||||
},
|
||||
"_balance" => {
|
||||
self.balance(context)
|
||||
},
|
||||
"_suicide" => {
|
||||
self.suicide(context)
|
||||
},
|
||||
@ -691,6 +728,18 @@ impl<'a, 'b> interpreter::UserFunctionExecutor<UserTrap> for Runtime<'a, 'b> {
|
||||
"_gaslimit" => {
|
||||
self.ext_gas_limit(context)
|
||||
},
|
||||
"_sender" => {
|
||||
self.sender(context)
|
||||
},
|
||||
"_address" => {
|
||||
self.address(context)
|
||||
},
|
||||
"_origin" => {
|
||||
self.origin(context)
|
||||
},
|
||||
"_value" => {
|
||||
self.value(context)
|
||||
},
|
||||
_ => {
|
||||
trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name);
|
||||
Ok(self.unknown_trap(context)?)
|
||||
|
@ -88,7 +88,7 @@ fn logger() {
|
||||
};
|
||||
|
||||
println!("ext.store: {:?}", ext.store);
|
||||
assert_eq!(gas_left, U256::from(98417));
|
||||
assert_eq!(gas_left, U256::from(98_731));
|
||||
let address_val: H256 = address.into();
|
||||
assert_eq!(
|
||||
ext.store.get(&"0100000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"),
|
||||
@ -121,6 +121,8 @@ fn logger() {
|
||||
// if it has any result.
|
||||
#[test]
|
||||
fn identity() {
|
||||
::ethcore_logger::init_log();
|
||||
|
||||
let code = load_sample!("identity.wasm");
|
||||
let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap();
|
||||
|
||||
@ -139,7 +141,7 @@ fn identity() {
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(99_732));
|
||||
assert_eq!(gas_left, U256::from(99_812));
|
||||
|
||||
assert_eq!(
|
||||
Address::from_slice(&result),
|
||||
@ -173,7 +175,7 @@ fn dispersion() {
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(99_421));
|
||||
assert_eq!(gas_left, U256::from(99_474));
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
@ -202,7 +204,7 @@ fn suicide_not() {
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(99_664));
|
||||
assert_eq!(gas_left, U256::from(99_691));
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
@ -236,7 +238,7 @@ fn suicide() {
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(99_420));
|
||||
assert_eq!(gas_left, U256::from(99_490));
|
||||
assert!(ext.suicides.contains(&refund));
|
||||
}
|
||||
|
||||
@ -267,7 +269,7 @@ fn create() {
|
||||
assert!(ext.calls.contains(
|
||||
&FakeCall {
|
||||
call_type: FakeCallType::Create,
|
||||
gas: U256::from(98_908),
|
||||
gas: U256::from(99_144),
|
||||
sender_address: None,
|
||||
receive_address: None,
|
||||
value: Some(1_000_000_000.into()),
|
||||
@ -275,7 +277,7 @@ fn create() {
|
||||
code_address: None,
|
||||
}
|
||||
));
|
||||
assert_eq!(gas_left, U256::from(98_860));
|
||||
assert_eq!(gas_left, U256::from(99_113));
|
||||
}
|
||||
|
||||
|
||||
@ -309,7 +311,7 @@ fn call_code() {
|
||||
assert!(ext.calls.contains(
|
||||
&FakeCall {
|
||||
call_type: FakeCallType::Call,
|
||||
gas: U256::from(99_108),
|
||||
gas: U256::from(99_138),
|
||||
sender_address: Some(sender),
|
||||
receive_address: Some(receiver),
|
||||
value: None,
|
||||
@ -317,7 +319,7 @@ fn call_code() {
|
||||
code_address: Some("0d13710000000000000000000000000000000000".parse().unwrap()),
|
||||
}
|
||||
));
|
||||
assert_eq!(gas_left, U256::from(94_241));
|
||||
assert_eq!(gas_left, U256::from(94_269));
|
||||
|
||||
// siphash result
|
||||
let res = LittleEndian::read_u32(&result[..]);
|
||||
@ -354,7 +356,7 @@ fn call_static() {
|
||||
assert!(ext.calls.contains(
|
||||
&FakeCall {
|
||||
call_type: FakeCallType::Call,
|
||||
gas: U256::from(99_108),
|
||||
gas: U256::from(99_138),
|
||||
sender_address: Some(sender),
|
||||
receive_address: Some(receiver),
|
||||
value: None,
|
||||
@ -362,7 +364,7 @@ fn call_static() {
|
||||
code_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()),
|
||||
}
|
||||
));
|
||||
assert_eq!(gas_left, U256::from(94_241));
|
||||
assert_eq!(gas_left, U256::from(94_269));
|
||||
|
||||
// siphash result
|
||||
let res = LittleEndian::read_u32(&result[..]);
|
||||
@ -388,7 +390,7 @@ fn realloc() {
|
||||
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
|
||||
}
|
||||
};
|
||||
assert_eq!(gas_left, U256::from(99_562));
|
||||
assert_eq!(gas_left, U256::from(99_614));
|
||||
assert_eq!(result, vec![0u8; 2]);
|
||||
}
|
||||
|
||||
@ -414,7 +416,7 @@ fn storage_read() {
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(gas_left, U256::from(99_673));
|
||||
assert_eq!(gas_left, U256::from(99_695));
|
||||
assert_eq!(Address::from(&result[12..32]), address);
|
||||
}
|
||||
|
||||
@ -441,7 +443,7 @@ fn keccak() {
|
||||
};
|
||||
|
||||
assert_eq!(H256::from_slice(&result), H256::from("68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"));
|
||||
assert_eq!(gas_left, U256::from(84003));
|
||||
assert_eq!(gas_left, U256::from(84_026));
|
||||
}
|
||||
|
||||
|
||||
@ -495,7 +497,7 @@ fn math_add() {
|
||||
}
|
||||
).expect("Interpreter to execute without any errors");
|
||||
|
||||
assert_eq!(gas_left, U256::from(98177));
|
||||
assert_eq!(gas_left, U256::from(98_241));
|
||||
assert_eq!(
|
||||
U256::from_dec_str("1888888888888888888888888888887").unwrap(),
|
||||
(&result[..]).into()
|
||||
@ -517,7 +519,7 @@ fn math_mul() {
|
||||
}
|
||||
).expect("Interpreter to execute without any errors");
|
||||
|
||||
assert_eq!(gas_left, U256::from(97326));
|
||||
assert_eq!(gas_left, U256::from(97_390));
|
||||
assert_eq!(
|
||||
U256::from_dec_str("888888888888888888888888888887111111111111111111111111111112").unwrap(),
|
||||
(&result[..]).into()
|
||||
@ -539,7 +541,7 @@ fn math_sub() {
|
||||
}
|
||||
).expect("Interpreter to execute without any errors");
|
||||
|
||||
assert_eq!(gas_left, U256::from(98221));
|
||||
assert_eq!(gas_left, U256::from(98_285));
|
||||
assert_eq!(
|
||||
U256::from_dec_str("111111111111111111111111111111").unwrap(),
|
||||
(&result[..]).into()
|
||||
@ -578,7 +580,7 @@ fn math_div() {
|
||||
}
|
||||
).expect("Interpreter to execute without any errors");
|
||||
|
||||
assert_eq!(gas_left, U256::from(91_562));
|
||||
assert_eq!(gas_left, U256::from(91_574));
|
||||
assert_eq!(
|
||||
U256::from_dec_str("1125000").unwrap(),
|
||||
(&result[..]).into()
|
||||
@ -670,5 +672,5 @@ fn externs() {
|
||||
"Gas limit requested and returned does not match"
|
||||
);
|
||||
|
||||
assert_eq!(gas_left, U256::from(95_999));
|
||||
assert_eq!(gas_left, U256::from(96_284));
|
||||
}
|
||||
|
@ -7,14 +7,12 @@ version = "1.8.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
|
||||
[dependencies]
|
||||
ethabi = "2.0"
|
||||
futures = "0.1"
|
||||
log = "0.3"
|
||||
mime = "0.2"
|
||||
mime_guess = "1.6.1"
|
||||
mime = "0.3"
|
||||
mime_guess = "2.0.0-alpha.2"
|
||||
rand = "0.3"
|
||||
rustc-hex = "1.0"
|
||||
parking_lot = "0.4"
|
||||
fetch = { path = "../util/fetch" }
|
||||
ethcore-util = { path = "../util" }
|
||||
ethcore-bigint = { path = "../util/bigint" }
|
||||
@ -22,3 +20,7 @@ ethcore-bytes = { path = "../util/bytes" }
|
||||
parity-reactor = { path = "../util/reactor" }
|
||||
native-contracts = { path = "../ethcore/native_contracts" }
|
||||
hash = { path = "../util/hash" }
|
||||
|
||||
[dev-dependencies]
|
||||
ethabi = "2.0"
|
||||
parking_lot = "0.4"
|
||||
|
@ -20,24 +20,26 @@
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate mime;
|
||||
|
||||
extern crate ethabi;
|
||||
extern crate ethcore_util as util;
|
||||
extern crate ethcore_bigint as bigint;
|
||||
extern crate ethcore_bytes as bytes;
|
||||
extern crate futures;
|
||||
extern crate hash;
|
||||
extern crate mime;
|
||||
extern crate mime_guess;
|
||||
extern crate native_contracts;
|
||||
extern crate parity_reactor;
|
||||
extern crate parking_lot;
|
||||
extern crate rand;
|
||||
extern crate rustc_hex;
|
||||
extern crate hash;
|
||||
|
||||
pub extern crate fetch;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate parking_lot;
|
||||
#[cfg(test)]
|
||||
extern crate ethabi;
|
||||
|
||||
mod client;
|
||||
|
||||
pub mod urlhint;
|
||||
|
@ -18,15 +18,19 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
use rustc_hex::ToHex;
|
||||
use mime::Mime;
|
||||
use mime::{self, Mime};
|
||||
use mime_guess;
|
||||
use hash::keccak;
|
||||
|
||||
use futures::{future, BoxFuture, Future};
|
||||
use futures::{future, Future};
|
||||
use native_contracts::{Registry, Urlhint};
|
||||
use util::Address;
|
||||
use bytes::Bytes;
|
||||
|
||||
/// Boxed future that can be shared between threads.
|
||||
/// TODO [ToDr] Use concrete types!
|
||||
pub type BoxFuture<A, B> = Box<Future<Item = A, Error = B> + Send>;
|
||||
|
||||
const COMMIT_LEN: usize = 20;
|
||||
|
||||
/// RAW Contract interface.
|
||||
@ -127,7 +131,7 @@ fn decode_urlhint_output(output: (String, ::bigint::hash::H160, Address)) -> Opt
|
||||
|
||||
let commit = GithubApp::commit(&commit);
|
||||
if commit == Some(Default::default()) {
|
||||
let mime = guess_mime_type(&account_slash_repo).unwrap_or(mime!(Application/_));
|
||||
let mime = guess_mime_type(&account_slash_repo).unwrap_or(mime::APPLICATION_JSON);
|
||||
return Some(URLHintResult::Content(Content {
|
||||
url: account_slash_repo,
|
||||
mime: mime,
|
||||
@ -158,7 +162,8 @@ impl URLHint for URLHintContract {
|
||||
let do_call = |_, data| {
|
||||
let addr = match self.client.registrar() {
|
||||
Ok(addr) => addr,
|
||||
Err(e) => return future::err(e).boxed(),
|
||||
Err(e) => return Box::new(future::err(e))
|
||||
as BoxFuture<Vec<u8>, _>,
|
||||
};
|
||||
|
||||
self.client.call(addr, data)
|
||||
@ -166,7 +171,7 @@ impl URLHint for URLHintContract {
|
||||
|
||||
let urlhint = self.urlhint.clone();
|
||||
let client = self.client.clone();
|
||||
self.registrar.get_address(do_call, keccak("githubhint"), "A".into())
|
||||
Box::new(self.registrar.get_address(do_call, keccak("githubhint"), "A".into())
|
||||
.map(|addr| if addr == Address::default() { None } else { Some(addr) })
|
||||
.and_then(move |address| {
|
||||
let mut fixed_id = [0; 32];
|
||||
@ -180,7 +185,7 @@ impl URLHint for URLHintContract {
|
||||
Either::B(urlhint.entries(do_call, ::bigint::hash::H256(fixed_id)).map(decode_urlhint_output))
|
||||
}
|
||||
}
|
||||
}).boxed()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,7 +218,7 @@ pub mod tests {
|
||||
use std::str::FromStr;
|
||||
use rustc_hex::FromHex;
|
||||
|
||||
use futures::{BoxFuture, Future, IntoFuture};
|
||||
use futures::{Future, IntoFuture};
|
||||
|
||||
use super::*;
|
||||
use super::guess_mime_type;
|
||||
@ -251,7 +256,7 @@ pub mod tests {
|
||||
fn call(&self, address: Address, data: Bytes) -> BoxFuture<Bytes, String> {
|
||||
self.calls.lock().push((address.to_hex(), data.to_hex()));
|
||||
let res = self.responses.lock().remove(0);
|
||||
res.into_future().boxed()
|
||||
Box::new(res.into_future())
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,7 +331,7 @@ pub mod tests {
|
||||
// then
|
||||
assert_eq!(res, Some(URLHintResult::Content(Content {
|
||||
url: "https://parity.io/assets/images/ethcore-black-horizontal.png".into(),
|
||||
mime: mime!(Image/Png),
|
||||
mime: mime::IMAGE_PNG,
|
||||
owner: Address::from_str("deadcafebeefbeefcafedeaddeedfeedffffffff").unwrap(),
|
||||
})))
|
||||
}
|
||||
@ -358,9 +363,9 @@ pub mod tests {
|
||||
|
||||
|
||||
assert_eq!(guess_mime_type(url1), None);
|
||||
assert_eq!(guess_mime_type(url2), Some(mime!(Image/Png)));
|
||||
assert_eq!(guess_mime_type(url3), Some(mime!(Image/Png)));
|
||||
assert_eq!(guess_mime_type(url4), Some(mime!(Image/Jpeg)));
|
||||
assert_eq!(guess_mime_type(url5), Some(mime!(Image/Png)));
|
||||
assert_eq!(guess_mime_type(url2), Some(mime::IMAGE_PNG));
|
||||
assert_eq!(guess_mime_type(url3), Some(mime::IMAGE_PNG));
|
||||
assert_eq!(guess_mime_type(url4), Some(mime::IMAGE_JPEG));
|
||||
assert_eq!(guess_mime_type(url5), Some(mime::IMAGE_PNG));
|
||||
}
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ impl Manager {
|
||||
where F: Fn() -> Result<R, &'static str>
|
||||
{
|
||||
let mut err = Error::KeyNotFound;
|
||||
/// Try to open device a few times.
|
||||
// Try to open device a few times.
|
||||
for _ in 0..10 {
|
||||
match f() {
|
||||
Ok(handle) => return Ok(handle),
|
||||
|
@ -10,8 +10,9 @@ ethcore = { path = "../ethcore" }
|
||||
ethcore-util = { path = "../util" }
|
||||
ethcore-bigint = { path = "../util/bigint" }
|
||||
ethcore-bytes = { path = "../util/bytes" }
|
||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
rlp = { path = "../util/rlp" }
|
||||
mime = "0.2"
|
||||
cid = "0.2"
|
||||
multihash = "0.6"
|
||||
unicase = "2.0"
|
||||
|
264
ipfs/src/lib.rs
264
ipfs/src/lib.rs
@ -14,43 +14,40 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[macro_use]
|
||||
extern crate mime;
|
||||
extern crate multihash;
|
||||
extern crate cid;
|
||||
extern crate unicase;
|
||||
|
||||
extern crate rlp;
|
||||
extern crate ethcore;
|
||||
extern crate ethcore_util as util;
|
||||
extern crate ethcore_bigint as bigint;
|
||||
extern crate ethcore_bytes as bytes;
|
||||
extern crate jsonrpc_core as core;
|
||||
extern crate jsonrpc_http_server as http;
|
||||
|
||||
pub mod error;
|
||||
mod route;
|
||||
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::net::{SocketAddr, IpAddr};
|
||||
|
||||
use core::futures::future::{self, FutureResult};
|
||||
use core::futures::{self, Future};
|
||||
use ethcore::client::BlockChainClient;
|
||||
use http::hyper::header::{self, Vary, ContentType};
|
||||
use http::hyper::{Method, StatusCode};
|
||||
use http::hyper::{self, server};
|
||||
use unicase::Ascii;
|
||||
|
||||
use error::ServerError;
|
||||
use route::Out;
|
||||
use http::hyper::server::{Handler, Request, Response};
|
||||
use http::hyper::net::HttpStream;
|
||||
use http::hyper::header::{self, Vary, ContentLength, ContentType};
|
||||
use http::hyper::{Next, Encoder, Decoder, Method, RequestUri, StatusCode};
|
||||
use ethcore::client::BlockChainClient;
|
||||
|
||||
pub use http::hyper::server::Listening;
|
||||
pub use http::{AccessControlAllowOrigin, Host, DomainsValidation};
|
||||
|
||||
/// Request/response handler
|
||||
pub struct IpfsHandler {
|
||||
/// Response to send out
|
||||
out: Out,
|
||||
/// How many bytes from the response have been written
|
||||
out_progress: usize,
|
||||
/// CORS response header
|
||||
cors_header: Option<header::AccessControlAllowOrigin>,
|
||||
/// Allowed CORS domains
|
||||
cors_domains: Option<Vec<AccessControlAllowOrigin>>,
|
||||
/// Hostnames allowed in the `Host` request header
|
||||
@ -66,124 +63,68 @@ impl IpfsHandler {
|
||||
|
||||
pub fn new(cors: DomainsValidation<AccessControlAllowOrigin>, hosts: DomainsValidation<Host>, client: Arc<BlockChainClient>) -> Self {
|
||||
IpfsHandler {
|
||||
out: Out::Bad("Invalid Request"),
|
||||
out_progress: 0,
|
||||
cors_header: None,
|
||||
cors_domains: cors.into(),
|
||||
allowed_hosts: hosts.into(),
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement Hyper's HTTP handler
|
||||
impl Handler<HttpStream> for IpfsHandler {
|
||||
fn on_request(&mut self, req: Request<HttpStream>) -> Next {
|
||||
pub fn on_request(&self, req: hyper::Request) -> (Option<header::AccessControlAllowOrigin>, Out) {
|
||||
match *req.method() {
|
||||
Method::Get | Method::Post => {},
|
||||
_ => return Next::write()
|
||||
_ => return (None, Out::Bad("Invalid Request")),
|
||||
}
|
||||
|
||||
if !http::is_host_allowed(&req, &self.allowed_hosts) {
|
||||
self.out = Out::Bad("Disallowed Host header");
|
||||
|
||||
return Next::write();
|
||||
return (None, Out::Bad("Disallowed Host header"));
|
||||
}
|
||||
|
||||
let cors_header = http::cors_header(&req, &self.cors_domains);
|
||||
if cors_header == http::CorsHeader::Invalid {
|
||||
self.out = Out::Bad("Disallowed Origin header");
|
||||
|
||||
return Next::write();
|
||||
}
|
||||
self.cors_header = cors_header.into();
|
||||
|
||||
let (path, query) = match *req.uri() {
|
||||
RequestUri::AbsolutePath { ref path, ref query } => (path, query.as_ref().map(AsRef::as_ref)),
|
||||
_ => return Next::write(),
|
||||
};
|
||||
|
||||
self.out = self.route(path, query);
|
||||
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: &mut Response) -> Next {
|
||||
use Out::*;
|
||||
|
||||
match self.out {
|
||||
OctetStream(ref bytes) => {
|
||||
use mime::{Mime, TopLevel, SubLevel};
|
||||
|
||||
// `OctetStream` is not a valid variant, so need to construct
|
||||
// the type manually.
|
||||
let content_type = Mime(
|
||||
TopLevel::Application,
|
||||
SubLevel::Ext("octet-stream".into()),
|
||||
vec![]
|
||||
);
|
||||
|
||||
res.headers_mut().set(ContentLength(bytes.len() as u64));
|
||||
res.headers_mut().set(ContentType(content_type));
|
||||
|
||||
},
|
||||
NotFound(reason) => {
|
||||
res.set_status(StatusCode::NotFound);
|
||||
|
||||
res.headers_mut().set(ContentLength(reason.len() as u64));
|
||||
res.headers_mut().set(ContentType(mime!(Text/Plain)));
|
||||
},
|
||||
Bad(reason) => {
|
||||
res.set_status(StatusCode::BadRequest);
|
||||
|
||||
res.headers_mut().set(ContentLength(reason.len() as u64));
|
||||
res.headers_mut().set(ContentType(mime!(Text/Plain)));
|
||||
}
|
||||
return (None, Out::Bad("Disallowed Origin header"));
|
||||
}
|
||||
|
||||
if let Some(cors_header) = self.cors_header.take() {
|
||||
res.headers_mut().set(cors_header);
|
||||
res.headers_mut().set(Vary::Items(vec!["Origin".into()]));
|
||||
}
|
||||
|
||||
Next::write()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, transport: &mut Encoder<HttpStream>) -> Next {
|
||||
use Out::*;
|
||||
|
||||
// Get the data to write as a byte slice
|
||||
let data = match self.out {
|
||||
OctetStream(ref bytes) => &bytes,
|
||||
NotFound(reason) | Bad(reason) => reason.as_bytes(),
|
||||
};
|
||||
|
||||
write_chunk(transport, &mut self.out_progress, data)
|
||||
let path = req.uri().path();
|
||||
let query = req.uri().query();
|
||||
return (cors_header.into(), self.route(path, query));
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to write entire `data` from current `progress`
|
||||
fn write_chunk<W: Write>(transport: &mut W, progress: &mut usize, data: &[u8]) -> Next {
|
||||
// Skip any bytes that have already been written
|
||||
let chunk = &data[*progress..];
|
||||
impl server::Service for IpfsHandler {
|
||||
type Request = hyper::Request;
|
||||
type Response = hyper::Response;
|
||||
type Error = hyper::Error;
|
||||
type Future = FutureResult<hyper::Response, hyper::Error>;
|
||||
|
||||
// Write an get the amount of bytes written. End the connection in case of an error.
|
||||
let written = match transport.write(chunk) {
|
||||
Ok(written) => written,
|
||||
Err(_) => return Next::end(),
|
||||
};
|
||||
fn call(&self, request: Self::Request) -> Self::Future {
|
||||
let (cors_header, out) = self.on_request(request);
|
||||
|
||||
*progress += written;
|
||||
let mut res = match out {
|
||||
Out::OctetStream(bytes) => {
|
||||
hyper::Response::new()
|
||||
.with_status(StatusCode::Ok)
|
||||
.with_header(ContentType::octet_stream())
|
||||
.with_body(bytes)
|
||||
},
|
||||
Out::NotFound(reason) => {
|
||||
hyper::Response::new()
|
||||
.with_status(StatusCode::NotFound)
|
||||
.with_header(ContentType::plaintext())
|
||||
.with_body(reason)
|
||||
},
|
||||
Out::Bad(reason) => {
|
||||
hyper::Response::new()
|
||||
.with_status(StatusCode::BadRequest)
|
||||
.with_header(ContentType::plaintext())
|
||||
.with_body(reason)
|
||||
}
|
||||
};
|
||||
|
||||
// Close the connection if the entire remaining chunk has been written
|
||||
if written < chunk.len() {
|
||||
Next::write()
|
||||
} else {
|
||||
Next::end()
|
||||
if let Some(cors_header) = cors_header {
|
||||
res.headers_mut().set(cors_header);
|
||||
res.headers_mut().set(Vary::Items(vec![Ascii::new("Origin".into())]));
|
||||
}
|
||||
|
||||
future::ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,6 +138,19 @@ fn include_current_interface(mut hosts: Vec<Host>, interface: String, port: u16)
|
||||
hosts
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Listening {
|
||||
close: Option<futures::sync::oneshot::Sender<()>>,
|
||||
thread: Option<thread::JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl Drop for Listening {
|
||||
fn drop(&mut self) {
|
||||
self.close.take().unwrap().send(()).unwrap();
|
||||
let _ = self.thread.take().unwrap().join();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_server(
|
||||
port: u16,
|
||||
interface: String,
|
||||
@ -210,67 +164,31 @@ pub fn start_server(
|
||||
let hosts: Option<Vec<_>> = hosts.into();
|
||||
let hosts: DomainsValidation<_> = hosts.map(move |hosts| include_current_interface(hosts, interface, port)).into();
|
||||
|
||||
Ok(
|
||||
http::hyper::Server::http(&addr)?
|
||||
.handle(move |_| IpfsHandler::new(cors.clone(), hosts.clone(), client.clone()))
|
||||
.map(|(listening, srv)| {
|
||||
let (close, shutdown_signal) = futures::sync::oneshot::channel::<()>();
|
||||
let (tx, rx) = mpsc::sync_channel(1);
|
||||
let thread = thread::spawn(move || {
|
||||
let send = |res| tx.send(res).expect("rx end is never dropped; qed");
|
||||
let server = match server::Http::new().bind(&addr, move || {
|
||||
Ok(IpfsHandler::new(cors.clone(), hosts.clone(), client.clone()))
|
||||
}) {
|
||||
Ok(server) => {
|
||||
send(Ok(()));
|
||||
server
|
||||
},
|
||||
Err(err) => {
|
||||
send(Err(err));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
::std::thread::spawn(move || {
|
||||
srv.run();
|
||||
});
|
||||
let _ = server.run_until(shutdown_signal.map_err(|_| {}));
|
||||
});
|
||||
|
||||
listening
|
||||
})?
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn write_chunk_to_vec() {
|
||||
let mut transport = Vec::new();
|
||||
let mut progress = 0;
|
||||
|
||||
let _ = write_chunk(&mut transport, &mut progress, b"foobar");
|
||||
|
||||
assert_eq!(b"foobar".to_vec(), transport);
|
||||
assert_eq!(6, progress);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_chunk_to_vec_part() {
|
||||
let mut transport = Vec::new();
|
||||
let mut progress = 3;
|
||||
|
||||
let _ = write_chunk(&mut transport, &mut progress, b"foobar");
|
||||
|
||||
assert_eq!(b"bar".to_vec(), transport);
|
||||
assert_eq!(6, progress);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_chunk_to_array() {
|
||||
use std::io::Cursor;
|
||||
|
||||
let mut buf = [0u8; 3];
|
||||
let mut progress = 0;
|
||||
|
||||
{
|
||||
let mut transport: Cursor<&mut [u8]> = Cursor::new(&mut buf);
|
||||
let _ = write_chunk(&mut transport, &mut progress, b"foobar");
|
||||
}
|
||||
|
||||
assert_eq!(*b"foo", buf);
|
||||
assert_eq!(3, progress);
|
||||
|
||||
{
|
||||
let mut transport: Cursor<&mut [u8]> = Cursor::new(&mut buf);
|
||||
let _ = write_chunk(&mut transport, &mut progress, b"foobar");
|
||||
}
|
||||
|
||||
assert_eq!(*b"bar", buf);
|
||||
assert_eq!(6, progress);
|
||||
}
|
||||
// Wait for server to start successfuly.
|
||||
rx.recv().expect("tx end is never dropped; qed")?;
|
||||
|
||||
Ok(Listening {
|
||||
close: close.into(),
|
||||
thread: thread.into(),
|
||||
})
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ use-precompiled-js = ["parity-dapps-glue/use-precompiled-js"]
|
||||
with-syntex = ["parity-dapps-glue/with-syntex"]
|
||||
|
||||
[build-dependencies]
|
||||
parity-dapps-glue = "1.7"
|
||||
parity-dapps-glue = "1.8"
|
||||
|
||||
[dependencies]
|
||||
parity-dapps-glue = "1.7"
|
||||
parity-dapps-glue = "1.8"
|
||||
|
||||
|
@ -11,8 +11,8 @@ default = ["with-syntex"]
|
||||
with-syntex = ["parity-dapps-glue/with-syntex"]
|
||||
|
||||
[build-dependencies]
|
||||
parity-dapps-glue = "1.7"
|
||||
parity-dapps-glue = "1.8"
|
||||
|
||||
[dependencies]
|
||||
parity-dapps-glue = "1.7"
|
||||
parity-dapps-glue = "1.8"
|
||||
|
||||
|
@ -26,6 +26,7 @@ export default {
|
||||
chain_classic: `Parity syncs to the Ethereum Classic network`,
|
||||
chain_dev: `Parity uses a local development chain`,
|
||||
chain_expanse: `Parity syncs to the Expanse network`,
|
||||
chain_musicoin: `Parity syncs to the Musicoin network`,
|
||||
chain_foundation: `Parity syncs to the Ethereum network launched by the Ethereum Foundation`,
|
||||
chain_kovan: `Parity syncs to the Kovan test network`,
|
||||
chain_olympic: `Parity syncs to the Olympic test network`,
|
||||
|
@ -26,6 +26,7 @@ export default {
|
||||
chain_classic: `Parity synchroniseert met het Ethereum Classic netwerk`,
|
||||
chain_dev: `Parity gebruikt een lokale ontwikkelaars chain`,
|
||||
chain_expanse: `Parity synchroniseert met het Expanse netwerk`,
|
||||
chain_musicoin: `Parity synchroniseert met het Musicoin netwerk`,
|
||||
chain_foundation: `Parity synchroniseert met het Ethereum netwerk wat door de Ethereum Foundation is uitgebracht`,
|
||||
chain_kovan: `Parity synchroniseert met het Kovan test netwerk`,
|
||||
chain_olympic: `Parity synchroniseert met het Olympic test netwerk`,
|
||||
|
@ -30,6 +30,7 @@ export default {
|
||||
chain_classic: `將Parity同步至以太坊經典網路`, // Parity syncs to the Ethereum Classic network
|
||||
chain_dev: `將Parity使用一條本地開發用區塊鏈`, // Parity uses a local development chain
|
||||
chain_expanse: `將Parity同步至Expanse網路`, // Parity syncs to the Expanse network
|
||||
chain_musicoin: `將Parity同步至Musicoin網路`, // Parity syncs to the Musicoin network
|
||||
chain_foundation: `將Parity同步至以太坊基金會發起的以太坊網路`, // Parity syncs to the Ethereum network launched by the Ethereum Foundation
|
||||
chain_kovan: `將Parity同步至Kovan測試網路`, // Parity syncs to the Kovan test network
|
||||
chain_olympic: `將Parity同步至Olympic測試網路`, // Parity syncs to the Olympic test network
|
||||
|
@ -30,6 +30,7 @@ export default {
|
||||
chain_classic: `将Parity同步至以太坊经典网络`, // Parity syncs to the Ethereum Classic network
|
||||
chain_dev: `将Parity使用一条本地开发用区块链`, // Parity uses a local development chain
|
||||
chain_expanse: `将Parity同步至Expanse网络`, // Parity syncs to the Expanse network
|
||||
chain_musicoin: `将Parity同步至Musicoin网络`, // Parity syncs to the Musicoin network
|
||||
chain_foundation: `将Parity同步至以太坊基金会发起的以太坊网络`, // Parity syncs to the Ethereum network launched by the Ethereum Foundation
|
||||
chain_kovan: `将Parity同步至Kovan测试网络`, // Parity syncs to the Kovan test network
|
||||
chain_olympic: `将Parity同步至Olympic测试网络`, // Parity syncs to the Olympic test network
|
||||
|
2
js/package-lock.json
generated
2
js/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Parity",
|
||||
"version": "1.8.25",
|
||||
"version": "1.8.28",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Parity",
|
||||
"version": "1.8.25",
|
||||
"version": "1.8.28",
|
||||
"main": "src/index.parity.js",
|
||||
"jsnext:main": "src/index.parity.js",
|
||||
"author": "Parity Team <admin@parity.io>",
|
||||
|
@ -94,6 +94,26 @@ pub struct EthashParams {
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="ecip1017EraRounds")]
|
||||
pub ecip1017_era_rounds: Option<Uint>,
|
||||
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="mcip3Transition")]
|
||||
pub mcip3_transition: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="mcip3MinerReward")]
|
||||
pub mcip3_miner_reward: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="mcip3UbiReward")]
|
||||
pub mcip3_ubi_reward: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="mcip3UbiContract")]
|
||||
pub mcip3_ubi_contract: Option<Address>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="mcip3DevReward")]
|
||||
pub mcip3_dev_reward: Option<Uint>,
|
||||
/// See main EthashParams docs.
|
||||
#[serde(rename="mcip3DevContract")]
|
||||
pub mcip3_dev_contract: Option<Address>,
|
||||
|
||||
/// EIP-649 transition block.
|
||||
#[serde(rename="eip649Transition")]
|
||||
pub eip649_transition: Option<Uint>,
|
||||
@ -212,6 +232,12 @@ mod tests {
|
||||
ecip1010_pause_transition: None,
|
||||
ecip1010_continue_transition: None,
|
||||
ecip1017_era_rounds: None,
|
||||
mcip3_transition: None,
|
||||
mcip3_miner_reward: None,
|
||||
mcip3_ubi_reward: None,
|
||||
mcip3_ubi_contract: None,
|
||||
mcip3_dev_reward: None,
|
||||
mcip3_dev_contract: None,
|
||||
eip649_transition: None,
|
||||
eip649_delay: None,
|
||||
eip649_reward: None,
|
||||
@ -252,6 +278,12 @@ mod tests {
|
||||
ecip1010_pause_transition: None,
|
||||
ecip1010_continue_transition: None,
|
||||
ecip1017_era_rounds: None,
|
||||
mcip3_transition: None,
|
||||
mcip3_miner_reward: None,
|
||||
mcip3_ubi_reward: None,
|
||||
mcip3_ubi_contract: None,
|
||||
mcip3_dev_reward: None,
|
||||
mcip3_dev_contract: None,
|
||||
eip649_transition: None,
|
||||
eip649_delay: None,
|
||||
eip649_reward: None,
|
||||
|
@ -70,6 +70,7 @@ pub fn setup_log(config: &Config) -> Result<Arc<RotatingLogger>, String> {
|
||||
// Disable info logging by default for some modules:
|
||||
builder.filter(Some("ws"), LogLevelFilter::Warn);
|
||||
builder.filter(Some("reqwest"), LogLevelFilter::Warn);
|
||||
builder.filter(Some("hyper"), LogLevelFilter::Warn);
|
||||
builder.filter(Some("rustls"), LogLevelFilter::Warn);
|
||||
// Enable info for others.
|
||||
builder.filter(None, LogLevelFilter::Info);
|
||||
|
@ -116,7 +116,6 @@ section "install"
|
||||
# Firewall exception rules
|
||||
SimpleFC::AdvAddRule "Parity incoming peers (TCP:30303)" "" 6 1 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" 30303 "" "" ""
|
||||
SimpleFC::AdvAddRule "Parity outgoing peers (TCP:30303)" "" 6 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 30303 "" ""
|
||||
SimpleFC::AdvAddRule "Parity web queries (TCP:80)" "" 6 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 80 "" ""
|
||||
SimpleFC::AdvAddRule "Parity UDP discovery (UDP:30303)" "" 17 2 1 2147483647 1 "$INSTDIR\parity.exe" "" "" "Parity" "" 30303 "" ""
|
||||
|
||||
# Registry information for add/remove programs
|
||||
|
@ -301,7 +301,7 @@ usage! {
|
||||
|
||||
ARG arg_chain: (String) = "foundation", or |c: &Config| otry!(c.parity).chain.clone(),
|
||||
"--chain=[CHAIN]",
|
||||
"Specify the blockchain type. CHAIN may be either a JSON chain specification file or olympic, frontier, homestead, mainnet, morden, ropsten, classic, expanse, testnet, kovan or dev.",
|
||||
"Specify the blockchain type. CHAIN may be either a JSON chain specification file or olympic, frontier, homestead, mainnet, morden, ropsten, classic, expanse, musicoin, testnet, kovan or dev.",
|
||||
|
||||
ARG arg_keys_path: (String) = "$BASE/keys", or |c: &Config| otry!(c.parity).keys_path.clone(),
|
||||
"--keys-path=[PATH]",
|
||||
@ -480,7 +480,7 @@ usage! {
|
||||
|
||||
ARG arg_jsonrpc_server_threads: (Option<usize>) = None, or |c: &Config| otry!(c.rpc).server_threads,
|
||||
"--jsonrpc-server-threads=[NUM]",
|
||||
"Enables experimental faster implementation of JSON-RPC server. Requires Dapps server to be disabled using --no-dapps.",
|
||||
"Enables multiple threads handling incoming connections for HTTP JSON-RPC server.",
|
||||
|
||||
["API and console options – WebSockets"]
|
||||
FLAG flag_no_ws: (bool) = false, or |c: &Config| otry!(c.websockets).disable.clone(),
|
||||
|
@ -141,16 +141,11 @@ impl Configuration {
|
||||
}
|
||||
let warp_sync = !self.args.flag_no_warp && fat_db != Switch::On && tracing != Switch::On && pruning != Pruning::Specific(Algorithm::Archive);
|
||||
let geth_compatibility = self.args.flag_geth;
|
||||
let mut dapps_conf = self.dapps_config();
|
||||
let dapps_conf = self.dapps_config();
|
||||
let ipfs_conf = self.ipfs_config();
|
||||
let secretstore_conf = self.secretstore_config()?;
|
||||
let format = self.format()?;
|
||||
|
||||
if self.args.arg_jsonrpc_server_threads.is_some() && dapps_conf.enabled {
|
||||
dapps_conf.enabled = false;
|
||||
writeln!(&mut stderr(), "Warning: Disabling Dapps server because fast RPC server was enabled.").expect("Error writing to stderr.");
|
||||
}
|
||||
|
||||
let cmd = if self.args.flag_version {
|
||||
Cmd::Version
|
||||
} else if self.args.cmd_signer {
|
||||
@ -867,9 +862,8 @@ impl Configuration {
|
||||
hosts: self.rpc_hosts(),
|
||||
cors: self.rpc_cors(),
|
||||
server_threads: match self.args.arg_jsonrpc_server_threads {
|
||||
Some(threads) if threads > 0 => Some(threads),
|
||||
None => None,
|
||||
_ => return Err("--jsonrpc-server-threads number needs to be positive.".into()),
|
||||
Some(threads) if threads > 0 => threads,
|
||||
_ => 1,
|
||||
},
|
||||
processing_threads: self.args.arg_jsonrpc_threads,
|
||||
};
|
||||
|
@ -21,7 +21,8 @@ use dir::default_data_path;
|
||||
use ethcore::client::{Client, BlockChainClient, BlockId};
|
||||
use ethcore::transaction::{Transaction, Action};
|
||||
use ethsync::LightSync;
|
||||
use futures::{future, IntoFuture, Future, BoxFuture};
|
||||
use futures::{future, IntoFuture, Future};
|
||||
use jsonrpc_core::BoxFuture;
|
||||
use hash_fetch::fetch::Client as FetchClient;
|
||||
use hash_fetch::urlhint::ContractClient;
|
||||
use helpers::replace_home;
|
||||
@ -30,7 +31,6 @@ use light::on_demand::{self, OnDemand};
|
||||
use node_health::{SyncStatus, NodeHealth};
|
||||
use rpc;
|
||||
use rpc_apis::SignerService;
|
||||
use parity_reactor;
|
||||
use util::Address;
|
||||
use bytes::Bytes;
|
||||
|
||||
@ -81,9 +81,8 @@ impl ContractClient for FullRegistrar {
|
||||
}
|
||||
|
||||
fn call(&self, address: Address, data: Bytes) -> BoxFuture<Bytes, String> {
|
||||
self.client.call_contract(BlockId::Latest, address, data)
|
||||
.into_future()
|
||||
.boxed()
|
||||
Box::new(self.client.call_contract(BlockId::Latest, address, data)
|
||||
.into_future())
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +112,7 @@ impl<T: LightChainClient + 'static> ContractClient for LightRegistrar<T> {
|
||||
|
||||
let env_info = match env_info {
|
||||
Ok(x) => x,
|
||||
Err(e) => return future::err(e).boxed(),
|
||||
Err(e) => return Box::new(future::err(e)),
|
||||
};
|
||||
|
||||
let maybe_future = self.sync.with_context(move |ctx| {
|
||||
@ -140,8 +139,8 @@ impl<T: LightChainClient + 'static> ContractClient for LightRegistrar<T> {
|
||||
});
|
||||
|
||||
match maybe_future {
|
||||
Some(fut) => fut.boxed(),
|
||||
None => future::err("cannot query registry: network disabled".into()).boxed(),
|
||||
Some(fut) => Box::new(fut),
|
||||
None => Box::new(future::err("cannot query registry: network disabled".into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,7 +152,6 @@ pub struct Dependencies {
|
||||
pub node_health: NodeHealth,
|
||||
pub sync_status: Arc<SyncStatus>,
|
||||
pub contract_client: Arc<ContractClient>,
|
||||
pub remote: parity_reactor::TokioRemote,
|
||||
pub fetch: FetchClient,
|
||||
pub signer: Arc<SignerService>,
|
||||
pub ui_address: Option<(String, u16)>,
|
||||
@ -235,7 +233,6 @@ mod server {
|
||||
use rpc_apis;
|
||||
|
||||
use parity_dapps;
|
||||
use parity_reactor;
|
||||
|
||||
pub use parity_dapps::Middleware;
|
||||
|
||||
@ -248,12 +245,11 @@ mod server {
|
||||
extra_script_src: Vec<(String, u16)>,
|
||||
) -> Result<Middleware, String> {
|
||||
let signer = deps.signer;
|
||||
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));
|
||||
|
||||
Ok(parity_dapps::Middleware::dapps(
|
||||
deps.fetch.pool(),
|
||||
deps.node_health,
|
||||
parity_remote,
|
||||
deps.ui_address,
|
||||
extra_embed_on,
|
||||
extra_script_src,
|
||||
@ -271,10 +267,9 @@ mod server {
|
||||
deps: Dependencies,
|
||||
dapps_domain: &str,
|
||||
) -> Result<Middleware, String> {
|
||||
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
|
||||
Ok(parity_dapps::Middleware::ui(
|
||||
deps.fetch.pool(),
|
||||
deps.node_health,
|
||||
parity_remote,
|
||||
dapps_domain,
|
||||
deps.contract_client,
|
||||
deps.sync_status,
|
||||
|
@ -23,7 +23,8 @@ use ethcore::machine::EthereumMachine;
|
||||
use ethcore::receipt::Receipt;
|
||||
use ethsync::LightSync;
|
||||
|
||||
use futures::{future, Future, BoxFuture};
|
||||
use futures::{future, Future};
|
||||
use futures::future::Either;
|
||||
|
||||
use light::client::fetch::ChainDataFetcher;
|
||||
use light::on_demand::{request, OnDemand};
|
||||
@ -33,6 +34,8 @@ use bigint::hash::H256;
|
||||
|
||||
const ALL_VALID_BACKREFS: &str = "no back-references, therefore all back-references valid; qed";
|
||||
|
||||
type BoxFuture<T, E> = Box<Future<Item = T, Error = E>>;
|
||||
|
||||
/// Allows on-demand fetch of data useful for the light client.
|
||||
pub struct EpochFetch {
|
||||
/// A handle to the sync service.
|
||||
@ -45,7 +48,7 @@ impl EpochFetch {
|
||||
fn request<T>(&self, req: T) -> BoxFuture<T::Out, &'static str>
|
||||
where T: Send + request::RequestAdapter + 'static, T::Out: Send + 'static
|
||||
{
|
||||
match self.sync.read().upgrade() {
|
||||
Box::new(match self.sync.read().upgrade() {
|
||||
Some(sync) => {
|
||||
let on_demand = &self.on_demand;
|
||||
let maybe_future = sync.with_context(move |ctx| {
|
||||
@ -53,12 +56,12 @@ impl EpochFetch {
|
||||
});
|
||||
|
||||
match maybe_future {
|
||||
Some(x) => x.map_err(|_| "Request canceled").boxed(),
|
||||
None => future::err("Unable to access network.").boxed(),
|
||||
Some(x) => Either::A(x.map_err(|_| "Request canceled")),
|
||||
None => Either::B(future::err("Unable to access network.")),
|
||||
}
|
||||
}
|
||||
None => future::err("Unable to access network").boxed(),
|
||||
}
|
||||
None => Either::B(future::err("Unable to access network")),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,11 +94,11 @@ impl<T: LightChainClient + 'static> IoHandler<ClientIoMessage> for QueueCull<T>
|
||||
});
|
||||
|
||||
match maybe_fetching {
|
||||
Some(fut) => fut.boxed(),
|
||||
Some(fut) => future::Either::A(fut),
|
||||
None => {
|
||||
debug!(target: "cull", "Unable to acquire network context; qed");
|
||||
future::ok(()).boxed()
|
||||
}
|
||||
future::Either::B(future::ok(()))
|
||||
},
|
||||
}
|
||||
}, Duration::from_millis(PURGE_TIMEOUT_MS), || {})
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ pub enum SpecType {
|
||||
Olympic,
|
||||
Classic,
|
||||
Expanse,
|
||||
Musicoin,
|
||||
Dev,
|
||||
Custom(String),
|
||||
}
|
||||
@ -57,6 +58,7 @@ impl str::FromStr for SpecType {
|
||||
"kovan" | "testnet" => SpecType::Kovan,
|
||||
"olympic" => SpecType::Olympic,
|
||||
"expanse" => SpecType::Expanse,
|
||||
"musicoin" => SpecType::Musicoin,
|
||||
"dev" => SpecType::Dev,
|
||||
other => SpecType::Custom(other.into()),
|
||||
};
|
||||
@ -73,6 +75,7 @@ impl fmt::Display for SpecType {
|
||||
SpecType::Olympic => "olympic",
|
||||
SpecType::Classic => "classic",
|
||||
SpecType::Expanse => "expanse",
|
||||
SpecType::Musicoin => "musicoin",
|
||||
SpecType::Kovan => "kovan",
|
||||
SpecType::Dev => "dev",
|
||||
SpecType::Custom(ref custom) => custom,
|
||||
@ -90,6 +93,7 @@ impl SpecType {
|
||||
SpecType::Olympic => Ok(ethereum::new_olympic(params)),
|
||||
SpecType::Classic => Ok(ethereum::new_classic(params)),
|
||||
SpecType::Expanse => Ok(ethereum::new_expanse(params)),
|
||||
SpecType::Musicoin => Ok(ethereum::new_musicoin(params)),
|
||||
SpecType::Kovan => Ok(ethereum::new_kovan(params)),
|
||||
SpecType::Dev => Ok(Spec::new_instant()),
|
||||
SpecType::Custom(ref filename) => {
|
||||
@ -103,6 +107,7 @@ impl SpecType {
|
||||
match *self {
|
||||
SpecType::Classic => Some("classic".to_owned()),
|
||||
SpecType::Expanse => Some("expanse".to_owned()),
|
||||
SpecType::Musicoin => Some("musicoin".to_owned()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -353,6 +358,7 @@ mod tests {
|
||||
assert_eq!(format!("{}", SpecType::Olympic), "olympic");
|
||||
assert_eq!(format!("{}", SpecType::Classic), "classic");
|
||||
assert_eq!(format!("{}", SpecType::Expanse), "expanse");
|
||||
assert_eq!(format!("{}", SpecType::Musicoin), "musicoin");
|
||||
assert_eq!(format!("{}", SpecType::Kovan), "kovan");
|
||||
assert_eq!(format!("{}", SpecType::Dev), "dev");
|
||||
assert_eq!(format!("{}", SpecType::Custom("foo/bar".into())), "foo/bar");
|
||||
|
@ -42,7 +42,7 @@ pub struct HttpConfiguration {
|
||||
pub apis: ApiSet,
|
||||
pub cors: Option<Vec<String>>,
|
||||
pub hosts: Option<Vec<String>>,
|
||||
pub server_threads: Option<usize>,
|
||||
pub server_threads: usize,
|
||||
pub processing_threads: usize,
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ impl Default for HttpConfiguration {
|
||||
apis: ApiSet::UnsafeContext,
|
||||
cors: None,
|
||||
hosts: Some(Vec::new()),
|
||||
server_threads: None,
|
||||
server_threads: 1,
|
||||
processing_threads: 0,
|
||||
}
|
||||
}
|
||||
@ -100,7 +100,7 @@ impl From<UiConfiguration> for HttpConfiguration {
|
||||
apis: rpc_apis::ApiSet::UnsafeContext,
|
||||
cors: None,
|
||||
hosts: conf.hosts,
|
||||
server_threads: None,
|
||||
server_threads: 1,
|
||||
processing_threads: 0,
|
||||
}
|
||||
}
|
||||
@ -278,13 +278,8 @@ pub fn new_http<D: rpc_apis::Dependencies>(
|
||||
handler,
|
||||
remote,
|
||||
rpc::RpcExtractor,
|
||||
match (conf.server_threads, middleware) {
|
||||
(Some(threads), None) => rpc::HttpSettings::Threads(threads),
|
||||
(None, middleware) => rpc::HttpSettings::Dapps(middleware),
|
||||
(Some(_), Some(_)) => {
|
||||
return Err("Dapps and fast multi-threaded RPC server cannot be enabled at the same time.".into())
|
||||
},
|
||||
}
|
||||
middleware,
|
||||
conf.server_threads,
|
||||
);
|
||||
|
||||
match start_result {
|
||||
|
@ -327,7 +327,6 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
|
||||
sync_status,
|
||||
node_health,
|
||||
contract_client: contract_client,
|
||||
remote: event_loop.raw_remote(),
|
||||
fetch: fetch.clone(),
|
||||
signer: signer_service.clone(),
|
||||
ui_address: cmd.ui_conf.redirection_address(),
|
||||
@ -721,7 +720,6 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
||||
sync_status,
|
||||
node_health,
|
||||
contract_client: contract_client,
|
||||
remote: event_loop.raw_remote(),
|
||||
fetch: fetch.clone(),
|
||||
signer: signer_service.clone(),
|
||||
ui_address: cmd.ui_conf.redirection_address(),
|
||||
|
@ -92,7 +92,7 @@ impl<F: Fetch> Client<F> {
|
||||
|
||||
/// Gets the current ETH price and calls `set_price` with the result.
|
||||
pub fn get<G: Fn(PriceInfo) + Sync + Send + 'static>(&self, set_price: G) {
|
||||
self.fetch.forget(self.fetch.fetch(&self.api_endpoint)
|
||||
self.fetch.process_and_forget(self.fetch.fetch(&self.api_endpoint)
|
||||
.map_err(|err| Error::Fetch(err))
|
||||
.and_then(move |mut response| {
|
||||
if !response.is_success() {
|
||||
@ -156,10 +156,11 @@ mod test {
|
||||
}
|
||||
|
||||
// this guarantees that the calls to price_info::Client::get will block for execution
|
||||
fn forget<F, I, E>(&self, f: F) where
|
||||
fn process_and_forget<F, I, E>(&self, f: F) where
|
||||
F: Future<Item=I, Error=E> + Send + 'static,
|
||||
I: Send + 'static,
|
||||
E: Send + 'static {
|
||||
E: Send + 'static,
|
||||
{
|
||||
let _ = f.wait();
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
||||
[dependencies]
|
||||
ansi_term = "0.9"
|
||||
cid = "0.2"
|
||||
futures = "0.1"
|
||||
futures-cpupool = "0.1"
|
||||
log = "0.3"
|
||||
multihash ="0.6"
|
||||
@ -28,13 +27,12 @@ tokio-timer = "0.1"
|
||||
transient-hashmap = "0.4"
|
||||
itertools = "0.5"
|
||||
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-minihttp-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-ipc-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
|
||||
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
jsonrpc-ipc-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.8" }
|
||||
|
||||
ethcore-io = { path = "../util/io" }
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
|
@ -19,7 +19,6 @@
|
||||
use jsonrpc_core;
|
||||
use http;
|
||||
use hyper;
|
||||
use minihttp;
|
||||
|
||||
/// HTTP RPC server impl-independent metadata extractor
|
||||
pub trait HttpMetaExtractor: Send + Sync + 'static {
|
||||
@ -29,24 +28,22 @@ pub trait HttpMetaExtractor: Send + Sync + 'static {
|
||||
fn read_metadata(&self, origin: Option<String>, user_agent: Option<String>, dapps_origin: Option<String>) -> Self::Metadata;
|
||||
}
|
||||
|
||||
pub struct HyperMetaExtractor<T> {
|
||||
pub struct MetaExtractor<T> {
|
||||
extractor: T,
|
||||
}
|
||||
|
||||
impl<T> HyperMetaExtractor<T> {
|
||||
impl<T> MetaExtractor<T> {
|
||||
pub fn new(extractor: T) -> Self {
|
||||
HyperMetaExtractor {
|
||||
extractor: extractor,
|
||||
}
|
||||
MetaExtractor { extractor }
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, T> http::MetaExtractor<M> for HyperMetaExtractor<T> where
|
||||
impl<M, T> http::MetaExtractor<M> for MetaExtractor<T> where
|
||||
T: HttpMetaExtractor<Metadata = M>,
|
||||
M: jsonrpc_core::Metadata,
|
||||
{
|
||||
fn read_metadata(&self, req: &hyper::server::Request<hyper::net::HttpStream>) -> M {
|
||||
let as_string = |header: Option<&http::request_response::header::Raw>| header
|
||||
fn read_metadata(&self, req: &hyper::server::Request) -> M {
|
||||
let as_string = |header: Option<&hyper::header::Raw>| header
|
||||
.and_then(|raw| raw.one())
|
||||
.map(|raw| String::from_utf8_lossy(raw).into_owned());
|
||||
|
||||
@ -56,28 +53,3 @@ impl<M, T> http::MetaExtractor<M> for HyperMetaExtractor<T> where
|
||||
self.extractor.read_metadata(origin, user_agent, dapps_origin)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MiniMetaExtractor<T> {
|
||||
extractor: T,
|
||||
}
|
||||
|
||||
impl<T> MiniMetaExtractor<T> {
|
||||
pub fn new(extractor: T) -> Self {
|
||||
MiniMetaExtractor {
|
||||
extractor: extractor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, T> minihttp::MetaExtractor<M> for MiniMetaExtractor<T> where
|
||||
T: HttpMetaExtractor<Metadata = M>,
|
||||
M: jsonrpc_core::Metadata,
|
||||
{
|
||||
fn read_metadata(&self, req: &minihttp::Req) -> M {
|
||||
let origin = req.header("origin").map(|h| h.to_owned());
|
||||
let user_agent = req.header("user-agent").map(|h| h.to_owned());
|
||||
let dapps_origin = req.header("x-parity-origin").map(|h| h.to_owned());
|
||||
|
||||
self.extractor.read_metadata(origin, user_agent, dapps_origin)
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@
|
||||
extern crate ansi_term;
|
||||
extern crate cid;
|
||||
extern crate crypto as rust_crypto;
|
||||
extern crate futures;
|
||||
extern crate futures_cpupool;
|
||||
extern crate itertools;
|
||||
extern crate multihash;
|
||||
@ -41,7 +40,6 @@ extern crate transient_hashmap;
|
||||
extern crate jsonrpc_core;
|
||||
extern crate jsonrpc_http_server as http;
|
||||
extern crate jsonrpc_ipc_server as ipc;
|
||||
extern crate jsonrpc_minihttp_server as minihttp;
|
||||
extern crate jsonrpc_pubsub;
|
||||
|
||||
extern crate ethash;
|
||||
@ -109,22 +107,8 @@ use std::net::SocketAddr;
|
||||
use http::tokio_core;
|
||||
|
||||
/// RPC HTTP Server instance
|
||||
pub enum HttpServer {
|
||||
/// Fast MiniHTTP variant
|
||||
Mini(minihttp::Server),
|
||||
/// Hyper variant
|
||||
Hyper(http::Server),
|
||||
}
|
||||
pub type HttpServer = http::Server;
|
||||
|
||||
impl HttpServer {
|
||||
/// Returns current listening address.
|
||||
pub fn address(&self) -> &SocketAddr {
|
||||
match *self {
|
||||
HttpServer::Mini(ref s) => s.address(),
|
||||
HttpServer::Hyper(ref s) => &s.addrs()[0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RPC HTTP Server error
|
||||
#[derive(Debug)]
|
||||
@ -145,23 +129,6 @@ impl From<http::Error> for HttpServerError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<minihttp::Error> for HttpServerError {
|
||||
fn from(e: minihttp::Error) -> Self {
|
||||
use self::HttpServerError::*;
|
||||
match e {
|
||||
minihttp::Error::Io(io) => Io(io),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// HTTP server implementation-specific settings.
|
||||
pub enum HttpSettings<R: RequestMiddleware> {
|
||||
/// Enable fast minihttp server with given number of threads.
|
||||
Threads(usize),
|
||||
/// Enable standard server with optional dapps middleware.
|
||||
Dapps(Option<R>),
|
||||
}
|
||||
|
||||
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
||||
pub fn start_http<M, S, H, T, R>(
|
||||
addr: &SocketAddr,
|
||||
@ -170,7 +137,8 @@ pub fn start_http<M, S, H, T, R>(
|
||||
handler: H,
|
||||
remote: tokio_core::reactor::Remote,
|
||||
extractor: T,
|
||||
settings: HttpSettings<R>,
|
||||
middleware: Option<R>,
|
||||
threads: usize,
|
||||
) -> Result<HttpServer, HttpServerError> where
|
||||
M: jsonrpc_core::Metadata,
|
||||
S: jsonrpc_core::Middleware<M>,
|
||||
@ -178,30 +146,18 @@ pub fn start_http<M, S, H, T, R>(
|
||||
T: HttpMetaExtractor<Metadata=M>,
|
||||
R: RequestMiddleware,
|
||||
{
|
||||
Ok(match settings {
|
||||
HttpSettings::Dapps(middleware) => {
|
||||
let mut builder = http::ServerBuilder::new(handler)
|
||||
.event_loop_remote(remote)
|
||||
.meta_extractor(http_common::HyperMetaExtractor::new(extractor))
|
||||
.cors(cors_domains.into())
|
||||
.allowed_hosts(allowed_hosts.into());
|
||||
let mut builder = http::ServerBuilder::new(handler)
|
||||
.threads(threads)
|
||||
.event_loop_remote(remote)
|
||||
.meta_extractor(http_common::MetaExtractor::new(extractor))
|
||||
.cors(cors_domains.into())
|
||||
.allowed_hosts(allowed_hosts.into());
|
||||
|
||||
if let Some(dapps) = middleware {
|
||||
builder = builder.request_middleware(dapps)
|
||||
}
|
||||
builder.start_http(addr)
|
||||
.map(HttpServer::Hyper)?
|
||||
},
|
||||
HttpSettings::Threads(threads) => {
|
||||
minihttp::ServerBuilder::new(handler)
|
||||
.threads(threads)
|
||||
.meta_extractor(http_common::MiniMetaExtractor::new(extractor))
|
||||
.cors(cors_domains.into())
|
||||
.allowed_hosts(allowed_hosts.into())
|
||||
.start_http(addr)
|
||||
.map(HttpServer::Mini)?
|
||||
},
|
||||
})
|
||||
if let Some(dapps) = middleware {
|
||||
builder = builder.request_middleware(dapps)
|
||||
}
|
||||
|
||||
Ok(builder.start_http(addr)?)
|
||||
}
|
||||
|
||||
/// Start ipc server asynchronously and returns result with `Server` handle on success or an error.
|
||||
|
@ -18,7 +18,7 @@ use devtools::http_client;
|
||||
use jsonrpc_core::MetaIoHandler;
|
||||
use http::{self, hyper};
|
||||
|
||||
use {HttpSettings, HttpServer};
|
||||
use {HttpServer};
|
||||
use tests::helpers::Server;
|
||||
use v1::{extractors, Metadata};
|
||||
|
||||
@ -33,11 +33,13 @@ fn serve(handler: Option<MetaIoHandler<Metadata>>) -> Server<HttpServer> {
|
||||
handler,
|
||||
remote,
|
||||
extractors::RpcExtractor,
|
||||
HttpSettings::Dapps(Some(|_req: &hyper::server::Request<hyper::net::HttpStream>, _control: &hyper::Control| {
|
||||
Some(|request: hyper::Request| {
|
||||
http::RequestMiddlewareAction::Proceed {
|
||||
should_continue_on_invalid_cors: false
|
||||
should_continue_on_invalid_cors: false,
|
||||
request,
|
||||
}
|
||||
})),
|
||||
}),
|
||||
1,
|
||||
).unwrap())
|
||||
}
|
||||
|
||||
@ -49,14 +51,13 @@ fn request(server: Server<HttpServer>, request: &str) -> http_client::Response {
|
||||
#[cfg(test)]
|
||||
mod testsing {
|
||||
use jsonrpc_core::{MetaIoHandler, Value};
|
||||
use jsonrpc_core::futures::{Future, future};
|
||||
use v1::Metadata;
|
||||
use super::{request, Server};
|
||||
|
||||
fn serve() -> (Server<::HttpServer>, ::std::net::SocketAddr) {
|
||||
let mut io = MetaIoHandler::default();
|
||||
io.add_method_with_meta("hello", |_, meta: Metadata| {
|
||||
future::ok(Value::String(format!("{}", meta.origin))).boxed()
|
||||
Ok(Value::String(format!("{}", meta.origin)))
|
||||
});
|
||||
let server = super::serve(Some(io));
|
||||
let address = server.server.address().to_owned();
|
||||
|
@ -236,7 +236,7 @@ impl<M: core::Middleware<Metadata>> core::Middleware<Metadata> for WsDispatcher<
|
||||
if use_full {
|
||||
A(self.full_handler.handle_rpc_request(request, meta))
|
||||
} else {
|
||||
B(process(request, meta).boxed())
|
||||
B(Box::new(process(request, meta)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::{future, Future, BoxFuture};
|
||||
use light::cache::Cache as LightDataCache;
|
||||
use light::client::LightChainClient;
|
||||
use light::on_demand::{request, OnDemand};
|
||||
@ -43,7 +42,9 @@ use ethcore::transaction::{Action, SignedTransaction, PendingTransaction, Transa
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use crypto::DEFAULT_MAC;
|
||||
|
||||
use jsonrpc_core::Error;
|
||||
use jsonrpc_core::{BoxFuture, Error};
|
||||
use jsonrpc_core::futures::{future, Future};
|
||||
use jsonrpc_core::futures::future::Either;
|
||||
use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload};
|
||||
use v1::types::{
|
||||
H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes,
|
||||
@ -120,7 +121,7 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
|
||||
false => request.nonce,
|
||||
true => Some(Self::fill_nonce(request.nonce, &from, &miner, &client)),
|
||||
};
|
||||
future::ok(FilledTransactionRequest {
|
||||
Box::new(future::ok(FilledTransactionRequest {
|
||||
from: from,
|
||||
used_default_from: request.from.is_none(),
|
||||
to: request.to,
|
||||
@ -130,7 +131,7 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
|
||||
value: request.value.unwrap_or_else(|| 0.into()),
|
||||
data: request.data.unwrap_or_else(Vec::new),
|
||||
condition: request.condition,
|
||||
}).boxed()
|
||||
}))
|
||||
}
|
||||
|
||||
fn sign(&self, accounts: Arc<AccountProvider>, filled: FilledTransactionRequest, password: SignWith)
|
||||
@ -139,7 +140,7 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
|
||||
let (client, miner) = (self.client.clone(), self.miner.clone());
|
||||
let chain_id = client.signing_chain_id();
|
||||
let address = filled.from;
|
||||
future::done({
|
||||
Box::new(future::done({
|
||||
let t = Transaction {
|
||||
nonce: Self::fill_nonce(filled.nonce, &filled.from, &miner, &client),
|
||||
action: filled.to.map_or(Action::Create, Action::Call),
|
||||
@ -159,7 +160,7 @@ impl<C: MiningBlockChainClient, M: MinerService> Dispatcher for FullDispatcher<C
|
||||
.expect("Transaction was signed by AccountsProvider; it never produces invalid signatures; qed")
|
||||
}))
|
||||
}
|
||||
}).boxed()
|
||||
}))
|
||||
}
|
||||
|
||||
fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result<H256, Error> {
|
||||
@ -182,7 +183,7 @@ pub fn fetch_gas_price_corpus(
|
||||
const GAS_PRICE_SAMPLE_SIZE: usize = 100;
|
||||
|
||||
if let Some(cached) = { cache.lock().gas_price_corpus() } {
|
||||
return future::ok(cached).boxed()
|
||||
return Box::new(future::ok(cached))
|
||||
}
|
||||
|
||||
let cache = cache.clone();
|
||||
@ -217,8 +218,8 @@ pub fn fetch_gas_price_corpus(
|
||||
});
|
||||
|
||||
match eventual_corpus {
|
||||
Some(corp) => corp.map_err(|_| errors::no_light_peers()).boxed(),
|
||||
None => future::err(errors::network_disabled()).boxed(),
|
||||
Some(corp) => Box::new(corp.map_err(|_| errors::no_light_peers())),
|
||||
None => Box::new(future::err(errors::network_disabled())),
|
||||
}
|
||||
}
|
||||
|
||||
@ -284,7 +285,7 @@ impl LightDispatcher {
|
||||
// fast path where we don't go to network; nonce provided or can be gotten from queue.
|
||||
let maybe_nonce = self.transaction_queue.read().next_nonce(&addr);
|
||||
if let Some(nonce) = maybe_nonce {
|
||||
return future::ok(nonce).boxed()
|
||||
return Box::new(future::ok(nonce))
|
||||
}
|
||||
|
||||
let best_header = self.client.best_block_header();
|
||||
@ -295,11 +296,11 @@ impl LightDispatcher {
|
||||
}).expect("no back-references; therefore all back-references valid; qed"));
|
||||
|
||||
match nonce_future {
|
||||
Some(x) =>
|
||||
Some(x) => Box::new(
|
||||
x.map(move |acc| acc.map_or(account_start_nonce, |acc| acc.nonce))
|
||||
.map_err(|_| errors::no_light_peers())
|
||||
.boxed(),
|
||||
None => future::err(errors::network_disabled()).boxed()
|
||||
),
|
||||
None => Box::new(future::err(errors::network_disabled()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -332,29 +333,29 @@ impl Dispatcher for LightDispatcher {
|
||||
|
||||
// fast path for known gas price.
|
||||
let gas_price = match request_gas_price {
|
||||
Some(gas_price) => future::ok(with_gas_price(gas_price)).boxed(),
|
||||
None => fetch_gas_price_corpus(
|
||||
Some(gas_price) => Either::A(future::ok(with_gas_price(gas_price))),
|
||||
None => Either::B(fetch_gas_price_corpus(
|
||||
self.sync.clone(),
|
||||
self.client.clone(),
|
||||
self.on_demand.clone(),
|
||||
self.cache.clone()
|
||||
).and_then(|corp| match corp.median() {
|
||||
Some(median) => future::ok(*median),
|
||||
None => future::ok(DEFAULT_GAS_PRICE), // fall back to default on error.
|
||||
}).map(with_gas_price).boxed()
|
||||
Some(median) => Ok(*median),
|
||||
None => Ok(DEFAULT_GAS_PRICE), // fall back to default on error.
|
||||
}).map(with_gas_price))
|
||||
};
|
||||
|
||||
match (request_nonce, force_nonce) {
|
||||
(_, false) | (Some(_), true) => gas_price,
|
||||
(_, false) | (Some(_), true) => Box::new(gas_price),
|
||||
(None, true) => {
|
||||
let next_nonce = self.next_nonce(from);
|
||||
gas_price.and_then(move |mut filled| next_nonce
|
||||
Box::new(gas_price.and_then(move |mut filled| next_nonce
|
||||
.map_err(|_| errors::no_light_peers())
|
||||
.map(move |nonce| {
|
||||
filled.nonce = Some(nonce);
|
||||
filled
|
||||
})
|
||||
).boxed()
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -390,13 +391,12 @@ impl Dispatcher for LightDispatcher {
|
||||
|
||||
// fast path for pre-filled nonce.
|
||||
if let Some(nonce) = filled.nonce {
|
||||
return future::done(with_nonce(filled, nonce)).boxed()
|
||||
return Box::new(future::done(with_nonce(filled, nonce)))
|
||||
}
|
||||
|
||||
self.next_nonce(address)
|
||||
Box::new(self.next_nonce(address)
|
||||
.map_err(|_| errors::no_light_peers())
|
||||
.and_then(move |nonce| with_nonce(filled, nonce))
|
||||
.boxed()
|
||||
.and_then(move |nonce| with_nonce(filled, nonce)))
|
||||
}
|
||||
|
||||
fn dispatch_transaction(&self, signed_transaction: PendingTransaction) -> Result<H256, Error> {
|
||||
@ -497,7 +497,7 @@ pub fn execute<D: Dispatcher + 'static>(
|
||||
match payload {
|
||||
ConfirmationPayload::SendTransaction(request) => {
|
||||
let condition = request.condition.clone().map(Into::into);
|
||||
dispatcher.sign(accounts, request, pass)
|
||||
Box::new(dispatcher.sign(accounts, request, pass)
|
||||
.map(move |v| v.map(move |tx| PendingTransaction::new(tx, condition)))
|
||||
.map(WithToken::into_tuple)
|
||||
.map(|(tx, token)| (tx, token, dispatcher))
|
||||
@ -506,18 +506,18 @@ pub fn execute<D: Dispatcher + 'static>(
|
||||
.map(RpcH256::from)
|
||||
.map(ConfirmationResponse::SendTransaction)
|
||||
.map(move |h| WithToken::from((h, tok)))
|
||||
}).boxed()
|
||||
}))
|
||||
},
|
||||
ConfirmationPayload::SignTransaction(request) => {
|
||||
dispatcher.sign(accounts, request, pass)
|
||||
Box::new(dispatcher.sign(accounts, request, pass)
|
||||
.map(|result| result
|
||||
.map(RpcRichRawTransaction::from)
|
||||
.map(ConfirmationResponse::SignTransaction)
|
||||
).boxed()
|
||||
))
|
||||
},
|
||||
ConfirmationPayload::EthSignMessage(address, data) => {
|
||||
if accounts.is_hardware_address(address) {
|
||||
return future::err(errors::unsupported("Signing via hardware wallets is not supported.", None)).boxed();
|
||||
return Box::new(future::err(errors::unsupported("Signing via hardware wallets is not supported.", None)));
|
||||
}
|
||||
|
||||
let hash = eth_data_hash(data);
|
||||
@ -527,11 +527,11 @@ pub fn execute<D: Dispatcher + 'static>(
|
||||
.map(RpcH520::from)
|
||||
.map(ConfirmationResponse::Signature)
|
||||
);
|
||||
future::done(res).boxed()
|
||||
Box::new(future::done(res))
|
||||
},
|
||||
ConfirmationPayload::Decrypt(address, data) => {
|
||||
if accounts.is_hardware_address(address) {
|
||||
return future::err(errors::unsupported("Decrypting via hardware wallets is not supported.", None)).boxed();
|
||||
return Box::new(future::err(errors::unsupported("Decrypting via hardware wallets is not supported.", None)));
|
||||
}
|
||||
|
||||
let res = decrypt(&accounts, address, data, pass)
|
||||
@ -539,7 +539,7 @@ pub fn execute<D: Dispatcher + 'static>(
|
||||
.map(RpcBytes)
|
||||
.map(ConfirmationResponse::Decrypt)
|
||||
);
|
||||
future::done(res).boxed()
|
||||
Box::new(future::done(res))
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -602,20 +602,18 @@ pub fn from_rpc<D>(payload: RpcConfirmationPayload, default_account: Address, di
|
||||
{
|
||||
match payload {
|
||||
RpcConfirmationPayload::SendTransaction(request) => {
|
||||
dispatcher.fill_optional_fields(request.into(), default_account, false)
|
||||
.map(ConfirmationPayload::SendTransaction)
|
||||
.boxed()
|
||||
Box::new(dispatcher.fill_optional_fields(request.into(), default_account, false)
|
||||
.map(ConfirmationPayload::SendTransaction))
|
||||
},
|
||||
RpcConfirmationPayload::SignTransaction(request) => {
|
||||
dispatcher.fill_optional_fields(request.into(), default_account, false)
|
||||
.map(ConfirmationPayload::SignTransaction)
|
||||
.boxed()
|
||||
Box::new(dispatcher.fill_optional_fields(request.into(), default_account, false)
|
||||
.map(ConfirmationPayload::SignTransaction))
|
||||
},
|
||||
RpcConfirmationPayload::Decrypt(RpcDecryptRequest { address, msg }) => {
|
||||
future::ok(ConfirmationPayload::Decrypt(address.into(), msg.into())).boxed()
|
||||
Box::new(future::ok(ConfirmationPayload::Decrypt(address.into(), msg.into())))
|
||||
},
|
||||
RpcConfirmationPayload::EthSignMessage(RpcSignRequest { address, data }) => {
|
||||
future::ok(ConfirmationPayload::EthSignMessage(address.into(), data.into())).boxed()
|
||||
Box::new(future::ok(ConfirmationPayload::EthSignMessage(address.into(), data.into())))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use std::fmt;
|
||||
use rlp::DecoderError;
|
||||
use ethcore::error::{Error as EthcoreError, CallError, TransactionError};
|
||||
use ethcore::account_provider::{SignError as AccountError};
|
||||
use jsonrpc_core::{Error, ErrorCode, Value};
|
||||
use jsonrpc_core::{futures, Error, ErrorCode, Value};
|
||||
|
||||
mod codes {
|
||||
// NOTE [ToDr] Codes from [-32099, -32000]
|
||||
@ -379,6 +379,6 @@ pub fn deprecated<T: Into<Option<String>>>(message: T) -> Error {
|
||||
}
|
||||
|
||||
// on-demand sender cancelled.
|
||||
pub fn on_demand_cancel(_cancel: ::futures::sync::oneshot::Canceled) -> Error {
|
||||
pub fn on_demand_cancel(_cancel: futures::sync::oneshot::Canceled) -> Error {
|
||||
internal("on-demand sender cancelled", "")
|
||||
}
|
||||
|
@ -23,11 +23,12 @@ use ethcore::encoded;
|
||||
use ethcore::executed::{Executed, ExecutionError};
|
||||
use ethcore::ids::BlockId;
|
||||
use ethcore::filter::Filter as EthcoreFilter;
|
||||
use ethcore::transaction::{Action, Transaction as EthTransaction};
|
||||
use ethcore::transaction::{Action, Transaction as EthTransaction, SignedTransaction};
|
||||
use ethcore::receipt::Receipt;
|
||||
|
||||
use futures::{future, Future, BoxFuture};
|
||||
use futures::future::Either;
|
||||
use jsonrpc_core::Error;
|
||||
use jsonrpc_core::{BoxFuture, Error};
|
||||
use jsonrpc_core::futures::{future, Future};
|
||||
use jsonrpc_core::futures::future::Either;
|
||||
use jsonrpc_macros::Trailing;
|
||||
|
||||
use light::cache::Cache;
|
||||
@ -38,14 +39,18 @@ use light::request::Field;
|
||||
|
||||
use ethsync::LightSync;
|
||||
use bigint::prelude::U256;
|
||||
use hash::H256;
|
||||
use util::Address;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use v1::helpers::{CallRequest as CallRequestHelper, errors, dispatch};
|
||||
use v1::types::{BlockNumber, CallRequest, Log};
|
||||
use v1::types::{BlockNumber, CallRequest, Log, Transaction};
|
||||
|
||||
const NO_INVALID_BACK_REFS: &'static str = "Fails only on invalid back-references; back-references here known to be valid; qed";
|
||||
|
||||
/// Helper for fetching blockchain data either from the light client or the network
|
||||
/// as necessary.
|
||||
#[derive(Clone)]
|
||||
pub struct LightFetch {
|
||||
/// The light client.
|
||||
pub client: Arc<LightChainClient>,
|
||||
@ -57,6 +62,19 @@ pub struct LightFetch {
|
||||
pub cache: Arc<Mutex<Cache>>,
|
||||
}
|
||||
|
||||
/// Extract a transaction at given index.
|
||||
pub fn extract_transaction_at_index(block: encoded::Block, index: usize, eip86_transition: u64) -> Option<Transaction> {
|
||||
block.transactions().into_iter().nth(index)
|
||||
.and_then(|tx| SignedTransaction::new(tx).ok())
|
||||
.map(|tx| Transaction::from_signed(tx, block.number(), eip86_transition))
|
||||
.map(|mut tx| {
|
||||
tx.block_hash = Some(block.hash().into());
|
||||
tx.transaction_index = Some(index.into());
|
||||
tx
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Type alias for convenience.
|
||||
pub type ExecutionResult = Result<Executed, ExecutionError>;
|
||||
|
||||
@ -113,54 +131,52 @@ impl LightFetch {
|
||||
let mut reqs = Vec::new();
|
||||
let header_ref = match self.make_header_requests(id, &mut reqs) {
|
||||
Ok(r) => r,
|
||||
Err(e) => return future::err(e).boxed(),
|
||||
Err(e) => return Box::new(future::err(e)),
|
||||
};
|
||||
|
||||
let maybe_future = self.sync.with_context(move |ctx| {
|
||||
self.on_demand.request_raw(ctx, reqs)
|
||||
Box::new(self.on_demand.request_raw(ctx, reqs)
|
||||
.expect("all back-references known to be valid; qed")
|
||||
.map(|res| extract_header(&res, header_ref)
|
||||
.expect("these responses correspond to requests that header_ref belongs to. \
|
||||
therefore it will not fail; qed"))
|
||||
.map_err(errors::on_demand_cancel)
|
||||
.boxed()
|
||||
.map_err(errors::on_demand_cancel))
|
||||
});
|
||||
|
||||
match maybe_future {
|
||||
Some(recv) => recv,
|
||||
None => future::err(errors::network_disabled()).boxed()
|
||||
None => Box::new(future::err(errors::network_disabled()))
|
||||
}
|
||||
}
|
||||
|
||||
/// helper for getting account info at a given block.
|
||||
/// Helper for getting account info at a given block.
|
||||
/// `None` indicates the account doesn't exist at the given block.
|
||||
pub fn account(&self, address: Address, id: BlockId) -> BoxFuture<Option<BasicAccount>, Error> {
|
||||
let mut reqs = Vec::new();
|
||||
let header_ref = match self.make_header_requests(id, &mut reqs) {
|
||||
Ok(r) => r,
|
||||
Err(e) => return future::err(e).boxed(),
|
||||
Err(e) => return Box::new(future::err(e)),
|
||||
};
|
||||
|
||||
reqs.push(request::Account { header: header_ref, address: address }.into());
|
||||
|
||||
let maybe_future = self.sync.with_context(move |ctx| {
|
||||
self.on_demand.request_raw(ctx, reqs)
|
||||
Box::new(self.on_demand.request_raw(ctx, reqs)
|
||||
.expect("all back-references known to be valid; qed")
|
||||
.map(|mut res| match res.pop() {
|
||||
Some(OnDemandResponse::Account(acc)) => acc,
|
||||
_ => panic!("responses correspond directly with requests in amount and type; qed"),
|
||||
})
|
||||
.map_err(errors::on_demand_cancel)
|
||||
.boxed()
|
||||
.map_err(errors::on_demand_cancel))
|
||||
});
|
||||
|
||||
match maybe_future {
|
||||
Some(recv) => recv,
|
||||
None => future::err(errors::network_disabled()).boxed()
|
||||
None => Box::new(future::err(errors::network_disabled()))
|
||||
}
|
||||
}
|
||||
|
||||
/// helper for getting proved execution.
|
||||
/// Helper for getting proved execution.
|
||||
pub fn proved_execution(&self, req: CallRequest, num: Trailing<BlockNumber>) -> BoxFuture<ExecutionResult, Error> {
|
||||
const DEFAULT_GAS_PRICE: u64 = 21_000;
|
||||
// starting gas when gas not provided.
|
||||
@ -193,7 +209,7 @@ impl LightFetch {
|
||||
let header_fut = self.header(id);
|
||||
|
||||
// fetch missing transaction fields from the network.
|
||||
nonce_fut.join(gas_price_fut).and_then(move |(nonce, gas_price)| {
|
||||
Box::new(nonce_fut.join(gas_price_fut).and_then(move |(nonce, gas_price)| {
|
||||
let action = req.to.map_or(Action::Create, Action::Call);
|
||||
let value = req.value.unwrap_or_else(U256::zero);
|
||||
let data = req.data.unwrap_or_default();
|
||||
@ -222,10 +238,10 @@ impl LightFetch {
|
||||
// TODO: get last-hashes from network.
|
||||
let env_info = match client.env_info(id) {
|
||||
Some(env_info) => env_info,
|
||||
_ => return future::err(errors::unknown_block()).boxed(),
|
||||
_ => return Either::A(future::err(errors::unknown_block())),
|
||||
};
|
||||
|
||||
execute_tx(gas_known, ExecuteParams {
|
||||
Either::B(execute_tx(gas_known, ExecuteParams {
|
||||
from: from,
|
||||
tx: tx,
|
||||
hdr: hdr,
|
||||
@ -233,44 +249,66 @@ impl LightFetch {
|
||||
engine: client.engine().clone(),
|
||||
on_demand: on_demand,
|
||||
sync: sync,
|
||||
})
|
||||
}).boxed()
|
||||
}))
|
||||
}))
|
||||
}
|
||||
|
||||
/// get a block itself. fails on unknown block ID.
|
||||
/// Get a block itself. Fails on unknown block ID.
|
||||
pub fn block(&self, id: BlockId) -> BoxFuture<encoded::Block, Error> {
|
||||
let mut reqs = Vec::new();
|
||||
let header_ref = match self.make_header_requests(id, &mut reqs) {
|
||||
Ok(r) => r,
|
||||
Err(e) => return future::err(e).boxed(),
|
||||
Err(e) => return Box::new(future::err(e)),
|
||||
};
|
||||
|
||||
reqs.push(request::Body(header_ref).into());
|
||||
|
||||
let maybe_future = self.sync.with_context(move |ctx| {
|
||||
self.on_demand.request_raw(ctx, reqs)
|
||||
.expect("all back-references known to be valid; qed")
|
||||
Box::new(self.on_demand.request_raw(ctx, reqs)
|
||||
.expect(NO_INVALID_BACK_REFS)
|
||||
.map(|mut res| match res.pop() {
|
||||
Some(OnDemandResponse::Body(b)) => b,
|
||||
_ => panic!("responses correspond directly with requests in amount and type; qed"),
|
||||
})
|
||||
.map_err(errors::on_demand_cancel)
|
||||
.boxed()
|
||||
.map_err(errors::on_demand_cancel))
|
||||
});
|
||||
|
||||
match maybe_future {
|
||||
Some(recv) => recv,
|
||||
None => future::err(errors::network_disabled()).boxed()
|
||||
None => Box::new(future::err(errors::network_disabled()))
|
||||
}
|
||||
}
|
||||
|
||||
/// get transaction logs
|
||||
/// Get the block receipts. Fails on unknown block ID.
|
||||
pub fn receipts(&self, id: BlockId) -> BoxFuture<Vec<Receipt>, Error> {
|
||||
let mut reqs = Vec::new();
|
||||
let header_ref = match self.make_header_requests(id, &mut reqs) {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Box::new(future::err(e)),
|
||||
};
|
||||
|
||||
reqs.push(request::BlockReceipts(header_ref).into());
|
||||
|
||||
let maybe_future = self.sync.with_context(move |ctx| {
|
||||
Box::new(self.on_demand.request_raw(ctx, reqs)
|
||||
.expect(NO_INVALID_BACK_REFS)
|
||||
.map(|mut res| match res.pop() {
|
||||
Some(OnDemandResponse::Receipts(b)) => b,
|
||||
_ => panic!("responses correspond directly with requests in amount and type; qed"),
|
||||
})
|
||||
.map_err(errors::on_demand_cancel))
|
||||
});
|
||||
|
||||
match maybe_future {
|
||||
Some(recv) => recv,
|
||||
None => Box::new(future::err(errors::network_disabled()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get transaction logs
|
||||
pub fn logs(&self, filter: EthcoreFilter) -> BoxFuture<Vec<Log>, Error> {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use futures::stream::{self, Stream};
|
||||
|
||||
const NO_INVALID_BACK_REFS: &'static str = "Fails only on invalid back-references; back-references here known to be valid; qed";
|
||||
use jsonrpc_core::futures::stream::{self, Stream};
|
||||
|
||||
// early exit for "to" block before "from" block.
|
||||
let best_number = self.client.chain_info().best_block_number;
|
||||
@ -282,9 +320,9 @@ impl LightFetch {
|
||||
};
|
||||
|
||||
match (block_number(filter.to_block), block_number(filter.from_block)) {
|
||||
(Some(to), Some(from)) if to < from => return future::ok(Vec::new()).boxed(),
|
||||
(Some(to), Some(from)) if to < from => return Box::new(future::ok(Vec::new())),
|
||||
(Some(_), Some(_)) => {},
|
||||
_ => return future::err(errors::unknown_block()).boxed(),
|
||||
_ => return Box::new(future::err(errors::unknown_block())),
|
||||
}
|
||||
|
||||
let maybe_future = self.sync.with_context(move |ctx| {
|
||||
@ -318,10 +356,69 @@ impl LightFetch {
|
||||
});
|
||||
|
||||
match maybe_future {
|
||||
Some(fut) => fut.boxed(),
|
||||
None => future::err(errors::network_disabled()).boxed(),
|
||||
Some(fut) => Box::new(fut),
|
||||
None => Box::new(future::err(errors::network_disabled())),
|
||||
}
|
||||
}
|
||||
|
||||
// Get a transaction by hash. also returns the index in the block.
|
||||
// Only returns transactions in the canonical chain.
|
||||
pub fn transaction_by_hash(&self, tx_hash: H256, eip86_transition: u64)
|
||||
-> BoxFuture<Option<(Transaction, usize)>, Error>
|
||||
{
|
||||
let params = (self.sync.clone(), self.on_demand.clone());
|
||||
let fetcher: Self = self.clone();
|
||||
|
||||
Box::new(future::loop_fn(params, move |(sync, on_demand)| {
|
||||
let maybe_future = sync.with_context(|ctx| {
|
||||
let req = request::TransactionIndex(tx_hash.clone().into());
|
||||
on_demand.request(ctx, req)
|
||||
});
|
||||
|
||||
let eventual_index = match maybe_future {
|
||||
Some(e) => e.expect(NO_INVALID_BACK_REFS).map_err(errors::on_demand_cancel),
|
||||
None => return Either::A(future::err(errors::network_disabled())),
|
||||
};
|
||||
|
||||
let fetcher = fetcher.clone();
|
||||
let extract_transaction = eventual_index.and_then(move |index| {
|
||||
// check that the block is known by number.
|
||||
// that ensures that it is within the chain that we are aware of.
|
||||
fetcher.block(BlockId::Number(index.num)).then(move |blk| match blk {
|
||||
Ok(blk) => {
|
||||
// if the block is known by number, make sure the
|
||||
// index from earlier isn't garbage.
|
||||
|
||||
if blk.hash() != index.hash {
|
||||
// index is on a different chain from us.
|
||||
return Ok(future::Loop::Continue((sync, on_demand)))
|
||||
}
|
||||
|
||||
let index = index.index as usize;
|
||||
let transaction = extract_transaction_at_index(blk, index, eip86_transition);
|
||||
|
||||
if transaction.as_ref().map_or(true, |tx| tx.hash != tx_hash.into()) {
|
||||
// index is actively wrong: indicated block has
|
||||
// fewer transactions than necessary or the transaction
|
||||
// at that index had a different hash.
|
||||
// TODO: punish peer/move into OnDemand somehow?
|
||||
Ok(future::Loop::Continue((sync, on_demand)))
|
||||
} else {
|
||||
let transaction = transaction.map(move |tx| (tx, index));
|
||||
Ok(future::Loop::Break(transaction))
|
||||
}
|
||||
}
|
||||
Err(ref e) if e == &errors::unknown_block() => {
|
||||
// block by number not in the canonical chain.
|
||||
Ok(future::Loop::Break(None))
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
})
|
||||
});
|
||||
|
||||
Either::B(extract_transaction)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -339,7 +436,7 @@ struct ExecuteParams {
|
||||
// this will double the gas on each `OutOfGas` error.
|
||||
fn execute_tx(gas_known: bool, params: ExecuteParams) -> BoxFuture<ExecutionResult, Error> {
|
||||
if !gas_known {
|
||||
future::loop_fn(params, |mut params| {
|
||||
Box::new(future::loop_fn(params, |mut params| {
|
||||
execute_tx(true, params.clone()).and_then(move |res| {
|
||||
match res {
|
||||
Ok(executed) => {
|
||||
@ -360,7 +457,7 @@ fn execute_tx(gas_known: bool, params: ExecuteParams) -> BoxFuture<ExecutionResu
|
||||
failed => Ok(future::Loop::Break(failed)),
|
||||
}
|
||||
})
|
||||
}).boxed()
|
||||
}))
|
||||
} else {
|
||||
trace!(target: "light_fetch", "Placing execution request for {} gas in on_demand",
|
||||
params.tx.gas);
|
||||
@ -381,8 +478,8 @@ fn execute_tx(gas_known: bool, params: ExecuteParams) -> BoxFuture<ExecutionResu
|
||||
});
|
||||
|
||||
match proved_future {
|
||||
Some(fut) => fut.boxed(),
|
||||
None => future::err(errors::network_disabled()).boxed(),
|
||||
Some(fut) => Box::new(fut),
|
||||
None => Box::new(future::err(errors::network_disabled())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user