Merge branch 'master' into issues/4673

This commit is contained in:
Joseph Mark 2017-07-17 13:07:00 +07:00
commit d93a5d3da1
No known key found for this signature in database
GPG Key ID: 9BA8D1EE2E53C283
81 changed files with 4302 additions and 448 deletions

349
Cargo.lock generated
View File

@ -257,7 +257,7 @@ dependencies = [
name = "common-types" name = "common-types"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"rlp 0.2.0", "rlp 0.2.0",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -429,7 +429,7 @@ dependencies = [
[[package]] [[package]]
name = "ethash" name = "ethash"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -439,7 +439,7 @@ dependencies = [
[[package]] [[package]]
name = "ethcore" name = "ethcore"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -450,22 +450,22 @@ dependencies = [
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethash 1.7.0", "ethash 1.8.0",
"ethcore-bloom-journal 0.1.0", "ethcore-bloom-journal 0.1.0",
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-io 1.7.0", "ethcore-io 1.8.0",
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-ipc-codegen 1.7.0", "ethcore-ipc-codegen 1.8.0",
"ethcore-ipc-nano 1.7.0", "ethcore-ipc-nano 1.8.0",
"ethcore-logger 1.7.0", "ethcore-logger 1.8.0",
"ethcore-stratum 1.7.0", "ethcore-stratum 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"ethstore 0.1.0", "ethstore 0.1.0",
"evm 0.1.0", "evm 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"hardware-wallet 1.7.0", "hardware-wallet 1.8.0",
"hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)", "hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)",
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -475,11 +475,11 @@ dependencies = [
"native-contracts 0.1.0", "native-contracts 0.1.0",
"num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"price-info 1.7.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.0", "rlp 0.2.0",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"stats 0.1.0", "stats 0.1.0",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
@ -506,14 +506,14 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-devtools" name = "ethcore-devtools"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "ethcore-io" name = "ethcore-io"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -524,17 +524,17 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-ipc" name = "ethcore-ipc"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)", "nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "ethcore-ipc-codegen" name = "ethcore-ipc-codegen"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", "aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
@ -549,9 +549,9 @@ dependencies = [
name = "ethcore-ipc-hypervisor" name = "ethcore-ipc-hypervisor"
version = "1.2.0" version = "1.2.0"
dependencies = [ dependencies = [
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-ipc-codegen 1.7.0", "ethcore-ipc-codegen 1.8.0",
"ethcore-ipc-nano 1.7.0", "ethcore-ipc-nano 1.8.0",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)", "nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -560,9 +560,9 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-ipc-nano" name = "ethcore-ipc-nano"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)", "nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)",
@ -572,11 +572,11 @@ dependencies = [
name = "ethcore-ipc-tests" name = "ethcore-ipc-tests"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-ipc-codegen 1.7.0", "ethcore-ipc-codegen 1.8.0",
"ethcore-ipc-nano 1.7.0", "ethcore-ipc-nano 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)", "nanomsg 0.5.1 (git+https://github.com/paritytech/nanomsg.rs.git?branch=parity-1.7)",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -584,16 +584,16 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-light" name = "ethcore-light"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0", "ethcore 1.8.0",
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-io 1.7.0", "ethcore-io 1.8.0",
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-ipc-codegen 1.7.0", "ethcore-ipc-codegen 1.8.0",
"ethcore-network 1.7.0", "ethcore-network 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"evm 0.1.0", "evm 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
@ -609,7 +609,7 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-logger" name = "ethcore-logger"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", "arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
@ -624,15 +624,15 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-network" name = "ethcore-network"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-io 1.7.0", "ethcore-io 1.8.0",
"ethcore-logger 1.7.0", "ethcore-logger 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"ethcrypto 0.1.0", "ethcrypto 0.1.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"igd 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "igd 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -657,17 +657,17 @@ version = "1.0.0"
dependencies = [ dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0", "ethcore 1.8.0",
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-ipc-codegen 1.7.0", "ethcore-ipc-codegen 1.8.0",
"ethcore-ipc-nano 1.7.0", "ethcore-ipc-nano 1.8.0",
"ethcore-logger 1.7.0", "ethcore-logger 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"ethcrypto 0.1.0", "ethcrypto 0.1.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"native-contracts 0.1.0", "native-contracts 0.1.0",
@ -685,15 +685,15 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-stratum" name = "ethcore-stratum"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-ipc-codegen 1.7.0", "ethcore-ipc-codegen 1.8.0",
"ethcore-ipc-nano 1.7.0", "ethcore-ipc-nano 1.8.0",
"ethcore-logger 1.7.0", "ethcore-logger 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-macros 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)",
@ -706,7 +706,7 @@ dependencies = [
[[package]] [[package]]
name = "ethcore-util" name = "ethcore-util"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
@ -715,8 +715,8 @@ dependencies = [
"eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)", "eth-secp256k1 0.5.6 (git+https://github.com/paritytech/rust-secp256k1)",
"ethcore-bigint 0.1.3", "ethcore-bigint 0.1.3",
"ethcore-bloom-journal 0.1.0", "ethcore-bloom-journal 0.1.0",
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-logger 1.7.0", "ethcore-logger 1.8.0",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
@ -756,7 +756,7 @@ name = "ethjson"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
@ -825,19 +825,19 @@ dependencies = [
[[package]] [[package]]
name = "ethsync" name = "ethsync"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0", "ethcore 1.8.0",
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-io 1.7.0", "ethcore-io 1.8.0",
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-ipc-codegen 1.7.0", "ethcore-ipc-codegen 1.8.0",
"ethcore-ipc-nano 1.7.0", "ethcore-ipc-nano 1.8.0",
"ethcore-light 1.7.0", "ethcore-light 1.8.0",
"ethcore-network 1.7.0", "ethcore-network 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -856,9 +856,9 @@ dependencies = [
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"common-types 0.1.0", "common-types 0.1.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"evmjit 1.7.0", "evmjit 1.8.0",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -872,8 +872,8 @@ name = "evmbin"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0", "ethcore 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"evm 0.1.0", "evm 0.1.0",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
@ -882,7 +882,7 @@ dependencies = [
[[package]] [[package]]
name = "evmjit" name = "evmjit"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"tiny-keccak 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "tiny-keccak 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -900,7 +900,7 @@ name = "fetch"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -933,10 +933,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "futures-cpupool" name = "futures-cpupool"
version = "0.1.2" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -987,7 +986,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "hardware-wallet" name = "hardware-wallet"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"ethcore-bigint 0.1.3", "ethcore-bigint 0.1.3",
"ethkey 0.2.0", "ethkey 0.2.0",
@ -1014,6 +1013,11 @@ dependencies = [
"unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "hex"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "hidapi" name = "hidapi"
version = "0.3.1" version = "0.3.1"
@ -1114,11 +1118,11 @@ dependencies = [
[[package]] [[package]]
name = "ipc-common-types" name = "ipc-common-types"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-ipc-codegen 1.7.0", "ethcore-ipc-codegen 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1512,7 +1516,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"native-contract-generator 0.1.0", "native-contract-generator 0.1.0",
] ]
@ -1692,6 +1696,15 @@ name = "order-stat"
version = "0.1.3" version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ordered-float"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "owning_ref" name = "owning_ref"
version = "0.3.3" version = "0.3.3"
@ -1711,36 +1724,37 @@ dependencies = [
"daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0", "ethcore 1.8.0",
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-io 1.7.0", "ethcore-io 1.8.0",
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-ipc-hypervisor 1.2.0", "ethcore-ipc-hypervisor 1.2.0",
"ethcore-ipc-nano 1.7.0", "ethcore-ipc-nano 1.8.0",
"ethcore-ipc-tests 0.1.0", "ethcore-ipc-tests 0.1.0",
"ethcore-light 1.7.0", "ethcore-light 1.8.0",
"ethcore-logger 1.7.0", "ethcore-logger 1.8.0",
"ethcore-secretstore 1.0.0", "ethcore-secretstore 1.0.0",
"ethcore-stratum 1.7.0", "ethcore-stratum 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"ethsync 1.7.0", "ethsync 1.8.0",
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps 1.7.0", "parity-dapps 1.8.0",
"parity-hash-fetch 1.7.0", "parity-hash-fetch 1.8.0",
"parity-ipfs-api 1.7.0", "parity-ipfs-api 1.8.0",
"parity-local-store 0.1.0", "parity-local-store 0.1.0",
"parity-reactor 0.1.0", "parity-reactor 0.1.0",
"parity-rpc 1.7.0", "parity-rpc 1.8.0",
"parity-rpc-client 1.4.0", "parity-rpc-client 1.4.0",
"parity-updater 1.7.0", "parity-updater 1.8.0",
"parity-whisper 0.1.0",
"path 0.1.0", "path 0.1.0",
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1761,16 +1775,16 @@ dependencies = [
[[package]] [[package]]
name = "parity-dapps" name = "parity-dapps"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"fetch 0.1.0", "fetch 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.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-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1779,9 +1793,9 @@ dependencies = [
"mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ntp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "ntp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-hash-fetch 1.7.0", "parity-hash-fetch 1.8.0",
"parity-reactor 0.1.0", "parity-reactor 0.1.0",
"parity-ui 1.7.0", "parity-ui 1.8.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1809,10 +1823,10 @@ dependencies = [
[[package]] [[package]]
name = "parity-hash-fetch" name = "parity-hash-fetch"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"fetch 0.1.0", "fetch 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1826,11 +1840,11 @@ dependencies = [
[[package]] [[package]]
name = "parity-ipfs-api" name = "parity-ipfs-api"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"cid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "cid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0", "ethcore 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"multihash 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "multihash 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1841,9 +1855,9 @@ dependencies = [
name = "parity-local-store" name = "parity-local-store"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ethcore 1.7.0", "ethcore 1.8.0",
"ethcore-io 1.7.0", "ethcore-io 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.0", "rlp 0.2.0",
@ -1862,27 +1876,27 @@ dependencies = [
[[package]] [[package]]
name = "parity-rpc" name = "parity-rpc"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"cid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "cid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
"ethash 1.7.0", "ethash 1.8.0",
"ethcore 1.7.0", "ethcore 1.8.0",
"ethcore-devtools 1.7.0", "ethcore-devtools 1.8.0",
"ethcore-io 1.7.0", "ethcore-io 1.8.0",
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-light 1.7.0", "ethcore-light 1.8.0",
"ethcore-logger 1.7.0", "ethcore-logger 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"ethcrypto 0.1.0", "ethcrypto 0.1.0",
"ethjson 0.1.0", "ethjson 0.1.0",
"ethkey 0.2.0", "ethkey 0.2.0",
"ethstore 0.1.0", "ethstore 0.1.0",
"ethsync 1.7.0", "ethsync 1.8.0",
"evm 0.1.0", "evm 0.1.0",
"fetch 0.1.0", "fetch 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.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-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
@ -1894,7 +1908,7 @@ dependencies = [
"multihash 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "multihash 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-reactor 0.1.0", "parity-reactor 0.1.0",
"parity-updater 1.7.0", "parity-updater 1.8.0",
"pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.0", "rlp 0.2.0",
@ -1914,13 +1928,13 @@ dependencies = [
name = "parity-rpc-client" name = "parity-rpc-client"
version = "1.4.0" version = "1.4.0"
dependencies = [ dependencies = [
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)",
"jsonrpc-ws-server 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)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-rpc 1.7.0", "parity-rpc 1.8.0",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1947,16 +1961,16 @@ dependencies = [
[[package]] [[package]]
name = "parity-ui" name = "parity-ui"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"parity-ui-dev 1.7.0", "parity-ui-dev 1.8.0",
"parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git)", "parity-ui-precompiled 1.4.0 (git+https://github.com/paritytech/js-precompiled.git)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "parity-ui-dev" name = "parity-ui-dev"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -1964,25 +1978,25 @@ dependencies = [
[[package]] [[package]]
name = "parity-ui-precompiled" name = "parity-ui-precompiled"
version = "1.4.0" version = "1.4.0"
source = "git+https://github.com/paritytech/js-precompiled.git#b49a1d46cc6c545403d18579ef7ae9f9d14eea7e" source = "git+https://github.com/paritytech/js-precompiled.git#26873a9ae6fca50fbf523f201325a20792db3e23"
dependencies = [ dependencies = [
"parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "parity-updater" name = "parity-updater"
version = "1.7.0" version = "1.8.0"
dependencies = [ dependencies = [
"ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore 1.7.0", "ethcore 1.8.0",
"ethcore-ipc 1.7.0", "ethcore-ipc 1.8.0",
"ethcore-ipc-codegen 1.7.0", "ethcore-ipc-codegen 1.8.0",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"ethsync 1.7.0", "ethsync 1.8.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-common-types 1.7.0", "ipc-common-types 1.8.0",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-hash-fetch 1.7.0", "parity-hash-fetch 1.8.0",
"parity-reactor 0.1.0", "parity-reactor 0.1.0",
"path 0.1.0", "path 0.1.0",
"target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1998,6 +2012,36 @@ dependencies = [
"parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "parity-whisper"
version = "0.1.0"
dependencies = [
"bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bigint 0.1.3",
"ethcore-network 1.8.0",
"ethcrypto 0.1.0",
"ethkey 0.2.0",
"futures 0.1.11 (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)",
"log 0.3.7 (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.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rlp 0.2.0",
"serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "parity-wordlist" name = "parity-wordlist"
version = "1.0.1" version = "1.0.1"
@ -2086,6 +2130,17 @@ dependencies = [
"difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "price-info"
version = "1.7.0"
dependencies = [
"ethcore-util 1.7.0",
"fetch 0.1.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "primal" name = "primal"
version = "0.2.3" version = "0.2.3"
@ -2324,9 +2379,9 @@ name = "rpc-cli"
version = "1.4.0" version = "1.4.0"
dependencies = [ dependencies = [
"bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "bigint 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-util 1.7.0", "ethcore-util 1.8.0",
"futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-rpc 1.7.0", "parity-rpc 1.8.0",
"parity-rpc-client 1.4.0", "parity-rpc-client 1.4.0",
"rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -3119,7 +3174,7 @@ dependencies = [
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344" "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 foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d"
"checksum futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8e51e7f9c150ba7fd4cee9df8bf6ea3dea5b63b68955ddad19ccd35b71dcfb4d" "checksum futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8e51e7f9c150ba7fd4cee9df8bf6ea3dea5b63b68955ddad19ccd35b71dcfb4d"
"checksum futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bb982bb25cd8fa5da6a8eb3a460354c984ff1113da82bcb4f0b0862b5795db82" "checksum futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a283c84501e92cade5ea673a2a7ca44f71f209ccdd302a3e0896f50083d2c5ff"
"checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a" "checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a"
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
"checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685" "checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685"
@ -3128,6 +3183,7 @@ dependencies = [
"checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1" "checksum hamming 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65043da274378d68241eb9a8f8f8aa54e349136f7b8e12f63e3ef44043cc30e1"
"checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4" "checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"
"checksum heck 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f807d2f64cc044a6bcf250ff23e59be0deec7a16612c014f962a06fa7e020f9" "checksum heck 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f807d2f64cc044a6bcf250ff23e59be0deec7a16612c014f962a06fa7e020f9"
"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa"
"checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "<none>" "checksum hidapi 0.3.1 (git+https://github.com/paritytech/hidapi-rs)" = "<none>"
"checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae" "checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae"
"checksum hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)" = "<none>" "checksum hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)" = "<none>"
@ -3197,6 +3253,7 @@ dependencies = [
"checksum openssl 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b34cd77cf91301fff3123fbd46b065c3b728b17a392835de34c397315dce5586" "checksum openssl 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b34cd77cf91301fff3123fbd46b065c3b728b17a392835de34c397315dce5586"
"checksum openssl-sys 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e035022a50faa380bd7ccdbd184d946ce539ebdb0a358780de92a995882af97a" "checksum openssl-sys 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e035022a50faa380bd7ccdbd184d946ce539ebdb0a358780de92a995882af97a"
"checksum order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb" "checksum order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb"
"checksum ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "58d25b6c0e47b20d05226d288ff434940296e7e2f8b877975da32f862152241f"
"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
"checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c" "checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c"
"checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>" "checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>"

View File

@ -51,6 +51,7 @@ parity-reactor = { path = "util/reactor" }
parity-rpc = { path = "rpc" } parity-rpc = { path = "rpc" }
parity-rpc-client = { path = "rpc_client" } parity-rpc-client = { path = "rpc_client" }
parity-updater = { path = "updater" } parity-updater = { path = "updater" }
parity-whisper = { path = "whisper" }
path = { path = "util/path" } path = { path = "util/path" }
parity-dapps = { path = "dapps", optional = true } parity-dapps = { path = "dapps", optional = true }
@ -106,4 +107,4 @@ lto = false
panic = "abort" panic = "abort"
[workspace] [workspace]
members = ["ethstore/cli", "ethkey/cli", "evmbin"] members = ["ethstore/cli", "ethkey/cli", "evmbin", "whisper"]

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Parity Dapps crate" description = "Parity Dapps crate"
name = "parity-dapps" name = "parity-dapps"
version = "1.7.0" version = "1.8.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]

View File

@ -68,6 +68,9 @@ pub fn add_security_headers(headers: &mut header::Headers, embeddable_on: Embedd
b"font-src 'self' data: https:;".to_vec(), b"font-src 'self' data: https:;".to_vec(),
// Allow inline scripts and scripts eval (webpack/jsconsole) // Allow inline scripts and scripts eval (webpack/jsconsole)
b"script-src 'self' 'unsafe-inline' 'unsafe-eval';".to_vec(), b"script-src 'self' 'unsafe-inline' 'unsafe-eval';".to_vec(),
// Same restrictions as script-src (fallback) with additional
// blob: that is required for camera access (worker)
b"worker-src 'self' 'unsafe-inline' 'unsafe-eval' blob: ;".to_vec(),
// Restrict everything else to the same origin. // Restrict everything else to the same origin.
b"default-src 'self';".to_vec(), b"default-src 'self';".to_vec(),
// Run in sandbox mode (although it's not fully safe since we allow same-origin and script) // Run in sandbox mode (although it's not fully safe since we allow same-origin and script)
@ -140,4 +143,3 @@ pub fn convert_uri_to_url(uri: &uri::RequestUri, host: Option<&header::Host>) ->
_ => None, _ => None,
} }
} }

View File

@ -3,7 +3,7 @@ description = "Ethcore Parity UI"
homepage = "http://parity.io" homepage = "http://parity.io"
license = "GPL-3.0" license = "GPL-3.0"
name = "parity-ui" name = "parity-ui"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[build-dependencies] [build-dependencies]

View File

@ -3,7 +3,7 @@ description = "Ethcore development/test/build tools"
homepage = "http://parity.io" homepage = "http://parity.io"
license = "GPL-3.0" license = "GPL-3.0"
name = "ethcore-devtools" name = "ethcore-devtools"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ethash" name = "ethash"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[lib] [lib]

View File

@ -3,7 +3,7 @@ description = "Ethcore library"
homepage = "http://parity.io" homepage = "http://parity.io"
license = "GPL-3.0" license = "GPL-3.0"
name = "ethcore" name = "ethcore"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs" build = "build.rs"
@ -44,11 +44,11 @@ lru-cache = "0.1.0"
native-contracts = { path = "native_contracts" } native-contracts = { path = "native_contracts" }
num = "0.1" num = "0.1"
num_cpus = "1.2" num_cpus = "1.2"
price-info = { path = "../price-info" }
rand = "0.3" rand = "0.3"
rlp = { path = "../util/rlp" } rlp = { path = "../util/rlp" }
rust-crypto = "0.2.34" rust-crypto = "0.2.34"
rustc-hex = "1.0" rustc-hex = "1.0"
rustc-serialize = "0.3"
semver = "0.6" semver = "0.6"
stats = { path = "../util/stats" } stats = { path = "../util/stats" }
time = "0.1" time = "0.1"

View File

@ -3,7 +3,7 @@ description = "Parity Light Client Implementation"
homepage = "http://parity.io" homepage = "http://parity.io"
license = "GPL-3.0" license = "GPL-3.0"
name = "ethcore-light" name = "ethcore-light"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs" build = "build.rs"

View File

@ -21,7 +21,7 @@
use ethcore::transaction::UnverifiedTransaction; use ethcore::transaction::UnverifiedTransaction;
use io::TimerToken; use io::TimerToken;
use network::{NetworkProtocolHandler, NetworkContext, PeerId}; use network::{HostInfo, NetworkProtocolHandler, NetworkContext, PeerId};
use rlp::{RlpStream, UntrustedRlp}; use rlp::{RlpStream, UntrustedRlp};
use util::hash::H256; use util::hash::H256;
use util::{DBValue, Mutex, RwLock, U256}; use util::{DBValue, Mutex, RwLock, U256};
@ -1074,7 +1074,7 @@ fn punish(peer: PeerId, io: &IoContext, e: Error) {
} }
impl NetworkProtocolHandler for LightProtocol { impl NetworkProtocolHandler for LightProtocol {
fn initialize(&self, io: &NetworkContext) { fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) {
io.register_timer(TIMEOUT, TIMEOUT_INTERVAL_MS) io.register_timer(TIMEOUT, TIMEOUT_INTERVAL_MS)
.expect("Error registering sync timer."); .expect("Error registering sync timer.");
io.register_timer(TICK_TIMEOUT, TICK_TIMEOUT_INTERVAL_MS) io.register_timer(TICK_TIMEOUT, TICK_TIMEOUT_INTERVAL_MS)

View File

@ -352,7 +352,7 @@ fn read_point(reader: &mut io::Chain<&[u8], io::Repeat>) -> Result<::bn::G1, Err
let px = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?; let px = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?;
reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed"); reader.read_exact(&mut buf[..]).expect("reading from zero-extended memory cannot fail; qed");
let py = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point x coordinate"))?; let py = Fq::from_slice(&buf[0..32]).map_err(|_| Error::from("Invalid point y coordinate"))?;
Ok( Ok(
if px == Fq::zero() && py == Fq::zero() { if px == Fq::zero() && py == Fq::zero() {

View File

@ -0,0 +1,49 @@
// 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/>.
// Epoch verifiers.
use error::Error;
use header::Header;
/// Verifier for all blocks within an epoch with self-contained state.
///
/// See docs on `Engine` relating to proving functions for more details.
pub trait EpochVerifier: Send + Sync {
/// Get the epoch number.
fn epoch_number(&self) -> u64;
/// Lightly verify the next block header.
/// This may not be a header belonging to a different epoch.
fn verify_light(&self, header: &Header) -> Result<(), Error>;
/// Perform potentially heavier checks on the next block header.
fn verify_heavy(&self, header: &Header) -> Result<(), Error> {
self.verify_light(header)
}
/// Check if the header is the end of an epoch.
fn is_epoch_end(&self, header: &Header, Ancestry) -> EpochChange;
}
/// Special "no-op" verifier for stateless, epoch-less engines.
pub struct NoOp;
impl EpochVerifier for NoOp {
fn epoch_number(&self) -> u64 { 0 }
fn verify_light(&self, _header: &Header) -> Result<(), Error> { Ok(()) }
}

View File

@ -98,10 +98,10 @@ extern crate lru_cache;
extern crate native_contracts; extern crate native_contracts;
extern crate num_cpus; extern crate num_cpus;
extern crate num; extern crate num;
extern crate price_info;
extern crate rand; extern crate rand;
extern crate rlp; extern crate rlp;
extern crate rustc_hex; extern crate rustc_hex;
extern crate rustc_serialize;
extern crate semver; extern crate semver;
extern crate stats; extern crate stats;
extern crate time; extern crate time;

View File

@ -33,9 +33,10 @@ use miner::{MinerService, MinerStatus, TransactionQueue, RemovalReason, Transact
AccountDetails, TransactionOrigin}; AccountDetails, TransactionOrigin};
use miner::banning_queue::{BanningTransactionQueue, Threshold}; use miner::banning_queue::{BanningTransactionQueue, Threshold};
use miner::work_notify::{WorkPoster, NotifyWork}; use miner::work_notify::{WorkPoster, NotifyWork};
use miner::price_info::PriceInfo;
use miner::local_transactions::{Status as LocalTransactionStatus}; use miner::local_transactions::{Status as LocalTransactionStatus};
use miner::service_transaction_checker::ServiceTransactionChecker; use miner::service_transaction_checker::ServiceTransactionChecker;
use price_info::{Client as PriceInfoClient, PriceInfo};
use price_info::fetch::Client as FetchClient;
use header::BlockNumber; use header::BlockNumber;
/// Different possible definitions for pending transaction set. /// Different possible definitions for pending transaction set.
@ -98,6 +99,8 @@ pub struct MinerOptions {
pub tx_gas_limit: U256, pub tx_gas_limit: U256,
/// Maximum size of the transaction queue. /// Maximum size of the transaction queue.
pub tx_queue_size: usize, pub tx_queue_size: usize,
/// Maximum memory usage of transactions in the queue (current / future).
pub tx_queue_memory_limit: Option<usize>,
/// Strategy to use for prioritizing transactions in the queue. /// Strategy to use for prioritizing transactions in the queue.
pub tx_queue_strategy: PrioritizationStrategy, pub tx_queue_strategy: PrioritizationStrategy,
/// Whether we should fallback to providing all the queue's transactions or just pending. /// Whether we should fallback to providing all the queue's transactions or just pending.
@ -123,8 +126,9 @@ impl Default for MinerOptions {
reseal_on_own_tx: true, reseal_on_own_tx: true,
reseal_on_uncle: false, reseal_on_uncle: false,
tx_gas_limit: !U256::zero(), tx_gas_limit: !U256::zero(),
tx_queue_size: 1024, tx_queue_size: 8192,
tx_queue_gas_limit: GasLimit::Auto, tx_queue_memory_limit: Some(2 * 1024 * 1024),
tx_queue_gas_limit: GasLimit::None,
tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, tx_queue_strategy: PrioritizationStrategy::GasPriceOnly,
pending_set: PendingSet::AlwaysQueue, pending_set: PendingSet::AlwaysQueue,
reseal_min_period: Duration::from_secs(2), reseal_min_period: Duration::from_secs(2),
@ -151,6 +155,7 @@ pub struct GasPriceCalibratorOptions {
pub struct GasPriceCalibrator { pub struct GasPriceCalibrator {
options: GasPriceCalibratorOptions, options: GasPriceCalibratorOptions,
next_calibration: Instant, next_calibration: Instant,
price_info: PriceInfoClient,
} }
impl GasPriceCalibrator { impl GasPriceCalibrator {
@ -160,7 +165,7 @@ impl GasPriceCalibrator {
let usd_per_tx = self.options.usd_per_tx; let usd_per_tx = self.options.usd_per_tx;
trace!(target: "miner", "Getting price info"); trace!(target: "miner", "Getting price info");
PriceInfo::get(move |price: PriceInfo| { self.price_info.get(move |price: PriceInfo| {
trace!(target: "miner", "Price info arrived: {:?}", price); trace!(target: "miner", "Price info arrived: {:?}", price);
let usd_per_eth = price.ethusd; let usd_per_eth = price.ethusd;
let wei_per_usd: f32 = 1.0e18 / usd_per_eth; let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
@ -186,10 +191,11 @@ pub enum GasPricer {
impl GasPricer { impl GasPricer {
/// Create a new Calibrated `GasPricer`. /// Create a new Calibrated `GasPricer`.
pub fn new_calibrated(options: GasPriceCalibratorOptions) -> GasPricer { pub fn new_calibrated(options: GasPriceCalibratorOptions, fetch: FetchClient) -> GasPricer {
GasPricer::Calibrated(GasPriceCalibrator { GasPricer::Calibrated(GasPriceCalibrator {
options: options, options: options,
next_calibration: Instant::now(), next_calibration: Instant::now(),
price_info: PriceInfoClient::new(fetch),
}) })
} }
@ -252,8 +258,15 @@ impl Miner {
GasLimit::Fixed(ref limit) => *limit, GasLimit::Fixed(ref limit) => *limit,
_ => !U256::zero(), _ => !U256::zero(),
}; };
let mem_limit = options.tx_queue_memory_limit.unwrap_or_else(usize::max_value);
let txq = TransactionQueue::with_limits(options.tx_queue_strategy, options.tx_queue_size, gas_limit, options.tx_gas_limit); let txq = TransactionQueue::with_limits(
options.tx_queue_strategy,
options.tx_queue_size,
mem_limit,
gas_limit,
options.tx_gas_limit
);
let txq = match options.tx_queue_banning { let txq = match options.tx_queue_banning {
Banning::Disabled => BanningTransactionQueue::new(txq, Threshold::NeverBan, Duration::from_secs(180)), Banning::Disabled => BanningTransactionQueue::new(txq, Threshold::NeverBan, Duration::from_secs(180)),
Banning::Enabled { ban_duration, min_offends, .. } => BanningTransactionQueue::new( Banning::Enabled { ban_duration, min_offends, .. } => BanningTransactionQueue::new(
@ -1328,6 +1341,7 @@ mod tests {
reseal_max_period: Duration::from_secs(120), reseal_max_period: Duration::from_secs(120),
tx_gas_limit: !U256::zero(), tx_gas_limit: !U256::zero(),
tx_queue_size: 1024, tx_queue_size: 1024,
tx_queue_memory_limit: None,
tx_queue_gas_limit: GasLimit::None, tx_queue_gas_limit: GasLimit::None,
tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice, tx_queue_strategy: PrioritizationStrategy::GasFactorAndGasPrice,
pending_set: PendingSet::AlwaysSealing, pending_set: PendingSet::AlwaysSealing,

View File

@ -45,7 +45,6 @@ mod banning_queue;
mod external; mod external;
mod local_transactions; mod local_transactions;
mod miner; mod miner;
mod price_info;
mod service_transaction_checker; mod service_transaction_checker;
mod transaction_queue; mod transaction_queue;
mod work_notify; mod work_notify;

View File

@ -1,125 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use rustc_serialize::json::Json;
use std::thread;
use std::io::Read;
use std::time::Duration;
use std::str::FromStr;
use std::sync::mpsc;
use hyper::client::{Handler, Request, Response, Client};
use hyper::{Url, Next, Encoder, Decoder};
use hyper::net::HttpStream;
#[derive(Debug)]
pub struct PriceInfo {
pub ethusd: f32,
}
pub struct SetPriceHandler<F> {
set_price: F,
channel: mpsc::Sender<()>,
}
impl<F> Drop for SetPriceHandler<F> {
fn drop(&mut self) {
let _ = self.channel.send(());
}
}
impl<F: Fn(PriceInfo) + Sync + Send + 'static> Handler<HttpStream> for SetPriceHandler<F> {
fn on_request(&mut self, _: &mut Request) -> Next { Next::read().timeout(Duration::from_secs(3)) }
fn on_request_writable(&mut self, _: &mut Encoder<HttpStream>) -> Next { Next::read().timeout(Duration::from_secs(3)) }
fn on_response(&mut self, _: Response) -> Next { Next::read().timeout(Duration::from_secs(3)) }
fn on_response_readable(&mut self, r: &mut Decoder<HttpStream>) -> Next {
let mut body = String::new();
let info = r.read_to_string(&mut body)
.map_err(|e| format!("Unable to read response: {:?}", e))
.and_then(|_| self.process_response(&body));
if let Err(e) = info {
warn!("Failed to auto-update latest ETH price: {:?}", e);
}
Next::end()
}
}
impl<F: Fn(PriceInfo) + Sync + Send + 'static> SetPriceHandler<F> {
fn process_response(&self, body: &str) -> Result<(), String> {
let json = Json::from_str(body).map_err(|e| format!("Invalid JSON returned: {:?}", e))?;
let obj = json.find_path(&["result", "ethusd"]).ok_or("USD price not found".to_owned())?;
let ethusd = match *obj {
Json::String(ref s) => FromStr::from_str(s).ok(),
_ => None,
}.ok_or("Unexpected price format.".to_owned())?;
(self.set_price)(PriceInfo {
ethusd: ethusd,
});
Ok(())
}
}
impl PriceInfo {
pub fn get<F: Fn(PriceInfo) + Sync + Send + 'static>(set_price: F) {
thread::spawn(move || {
let url = FromStr::from_str("http://api.etherscan.io/api?module=stats&action=ethprice")
.expect("string known to be a valid URL; qed");
if let Err(e) = Self::request(url, set_price) {
warn!("Failed to auto-update latest ETH price: {:?}", e);
}
});
}
fn request<F: Fn(PriceInfo) + Send + Sync + 'static>(url: Url, set_price: F) -> Result<(), String> {
let (tx, rx) = mpsc::channel();
let client = Client::new().map_err(|e| format!("Unable to start client: {:?}", e))?;
client.request(
url,
SetPriceHandler {
set_price: set_price,
channel: tx,
},
).map_err(|_| "Request failed.".to_owned())?;
// Wait for exit
let _ = rx.recv().map_err(|e| format!("Request interrupted: {:?}", e))?;
client.close();
Ok(())
}
}
#[test] #[ignore]
fn should_get_price_info() {
use std::sync::Arc;
use std::time::Duration;
use ethcore_logger::init_log;
use util::{Condvar, Mutex};
init_log();
let done = Arc::new((Mutex::new(PriceInfo { ethusd: 0f32 }), Condvar::new()));
let rdone = done.clone();
PriceInfo::get(move |price| { let mut p = rdone.0.lock(); *p = price; rdone.1.notify_one(); });
let mut p = done.0.lock();
let t = done.1.wait_for(&mut p, Duration::from_millis(10000));
assert!(!t.timed_out());
assert!(p.ethusd != 0f32);
}

View File

@ -105,7 +105,7 @@ use std::cmp::Ordering;
use std::cmp; use std::cmp;
use std::collections::{HashSet, HashMap, BTreeSet, BTreeMap}; use std::collections::{HashSet, HashMap, BTreeSet, BTreeMap};
use linked_hash_map::LinkedHashMap; use linked_hash_map::LinkedHashMap;
use util::{Address, H256, U256}; use util::{Address, H256, U256, HeapSizeOf};
use util::table::Table; use util::table::Table;
use transaction::*; use transaction::*;
use error::{Error, TransactionError}; use error::{Error, TransactionError};
@ -171,6 +171,8 @@ struct TransactionOrder {
/// Gas (limit) of the transaction. Usage depends on strategy. /// Gas (limit) of the transaction. Usage depends on strategy.
/// Low gas limit = High priority (processed earlier) /// Low gas limit = High priority (processed earlier)
gas: U256, gas: U256,
/// Heap usage of this transaction.
mem_usage: usize,
/// Transaction ordering strategy /// Transaction ordering strategy
strategy: PrioritizationStrategy, strategy: PrioritizationStrategy,
/// Hash to identify associated transaction /// Hash to identify associated transaction
@ -191,8 +193,9 @@ impl TransactionOrder {
TransactionOrder { TransactionOrder {
nonce_height: tx.nonce() - base_nonce, nonce_height: tx.nonce() - base_nonce,
gas_price: tx.transaction.gas_price, gas_price: tx.transaction.gas_price,
gas: tx.transaction.gas,
gas_factor: factor, gas_factor: factor,
gas: tx.transaction.gas,
mem_usage: tx.transaction.heap_size_of_children(),
strategy: strategy, strategy: strategy,
hash: tx.hash(), hash: tx.hash(),
insertion_id: tx.insertion_id, insertion_id: tx.insertion_id,
@ -370,7 +373,8 @@ struct TransactionSet {
by_address: Table<Address, U256, TransactionOrder>, by_address: Table<Address, U256, TransactionOrder>,
by_gas_price: GasPriceQueue, by_gas_price: GasPriceQueue,
limit: usize, limit: usize,
gas_limit: U256, total_gas_limit: U256,
memory_limit: usize,
} }
impl TransactionSet { impl TransactionSet {
@ -402,18 +406,24 @@ impl TransactionSet {
/// Returns addresses and lowest nonces of transactions removed because of limit. /// Returns addresses and lowest nonces of transactions removed because of limit.
fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>, local: &mut LocalTransactionsList) -> Option<HashMap<Address, U256>> { fn enforce_limit(&mut self, by_hash: &mut HashMap<H256, VerifiedTransaction>, local: &mut LocalTransactionsList) -> Option<HashMap<Address, U256>> {
let mut count = 0; let mut count = 0;
let mut mem_usage = 0;
let mut gas: U256 = 0.into(); let mut gas: U256 = 0.into();
let to_drop : Vec<(Address, U256)> = { let to_drop : Vec<(Address, U256)> = {
self.by_priority self.by_priority
.iter() .iter()
.filter(|order| { .filter(|order| {
count = count + 1; // update transaction count and mem usage
count += 1;
mem_usage += order.mem_usage;
// calculate current gas usage
let r = gas.overflowing_add(order.gas); let r = gas.overflowing_add(order.gas);
if r.1 { return false } if r.1 { return false }
gas = r.0; gas = r.0;
let is_own_or_retracted = order.origin.is_local() || order.origin == TransactionOrigin::RetractedBlock;
// Own and retracted transactions are allowed to go above all limits. // Own and retracted transactions are allowed to go above all limits.
order.origin != TransactionOrigin::Local && order.origin != TransactionOrigin::RetractedBlock && !is_own_or_retracted && (mem_usage > self.memory_limit || count > self.limit || gas > self.total_gas_limit)
(gas > self.gas_limit || count > self.limit)
}) })
.map(|order| by_hash.get(&order.hash) .map(|order| by_hash.get(&order.hash)
.expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`.")) .expect("All transactions in `self.by_priority` and `self.by_address` are kept in sync with `by_hash`."))
@ -502,6 +512,10 @@ const GAS_LIMIT_HYSTERESIS: usize = 200; // (100/GAS_LIMIT_HYSTERESIS) %
/// `new_gas_price > old_gas_price + old_gas_price >> SHIFT` /// `new_gas_price > old_gas_price + old_gas_price >> SHIFT`
const GAS_PRICE_BUMP_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25% const GAS_PRICE_BUMP_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25%
/// Future queue limits are lower from current queue limits:
/// `future_limit = current_limit >> SHIFT`
const FUTURE_QUEUE_LIMITS_SHIFT: usize = 3; // 2 = 25%, 3 = 12.5%, 4 = 6.25%
/// Describes the strategy used to prioritize transactions in the queue. /// Describes the strategy used to prioritize transactions in the queue.
#[cfg_attr(feature="dev", allow(enum_variant_names))] #[cfg_attr(feature="dev", allow(enum_variant_names))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -557,7 +571,7 @@ pub struct TransactionQueue {
/// The maximum amount of gas any individual transaction may use. /// The maximum amount of gas any individual transaction may use.
tx_gas_limit: U256, tx_gas_limit: U256,
/// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0) /// Current gas limit (block gas limit * factor). Transactions above the limit will not be accepted (default to !0)
gas_limit: U256, total_gas_limit: U256,
/// Maximal time transaction may occupy the queue. /// Maximal time transaction may occupy the queue.
/// When we reach `max_time_in_queue / 2^3` we re-validate /// When we reach `max_time_in_queue / 2^3` we re-validate
/// account balance. /// account balance.
@ -585,35 +599,43 @@ impl Default for TransactionQueue {
impl TransactionQueue { impl TransactionQueue {
/// Creates new instance of this Queue /// Creates new instance of this Queue
pub fn new(strategy: PrioritizationStrategy) -> Self { pub fn new(strategy: PrioritizationStrategy) -> Self {
Self::with_limits(strategy, 1024, !U256::zero(), !U256::zero()) Self::with_limits(strategy, 8192, usize::max_value(), !U256::zero(), !U256::zero())
} }
/// Create new instance of this Queue with specified limits /// Create new instance of this Queue with specified limits
pub fn with_limits(strategy: PrioritizationStrategy, limit: usize, gas_limit: U256, tx_gas_limit: U256) -> Self { pub fn with_limits(
strategy: PrioritizationStrategy,
limit: usize,
memory_limit: usize,
total_gas_limit: U256,
tx_gas_limit: U256,
) -> Self {
let current = TransactionSet { let current = TransactionSet {
by_priority: BTreeSet::new(), by_priority: BTreeSet::new(),
by_address: Table::new(), by_address: Table::new(),
by_gas_price: Default::default(), by_gas_price: Default::default(),
limit: limit, limit,
gas_limit: gas_limit, total_gas_limit,
memory_limit,
}; };
let future = TransactionSet { let future = TransactionSet {
by_priority: BTreeSet::new(), by_priority: BTreeSet::new(),
by_address: Table::new(), by_address: Table::new(),
by_gas_price: Default::default(), by_gas_price: Default::default(),
limit: limit, total_gas_limit: total_gas_limit >> FUTURE_QUEUE_LIMITS_SHIFT,
gas_limit: gas_limit, limit: limit >> FUTURE_QUEUE_LIMITS_SHIFT,
memory_limit: memory_limit >> FUTURE_QUEUE_LIMITS_SHIFT,
}; };
TransactionQueue { TransactionQueue {
strategy: strategy, strategy,
minimal_gas_price: U256::zero(), minimal_gas_price: U256::zero(),
tx_gas_limit: tx_gas_limit, total_gas_limit: !U256::zero(),
gas_limit: !U256::zero(), tx_gas_limit,
max_time_in_queue: DEFAULT_QUEUING_PERIOD, max_time_in_queue: DEFAULT_QUEUING_PERIOD,
current: current, current,
future: future, future,
by_hash: HashMap::new(), by_hash: HashMap::new(),
last_nonces: HashMap::new(), last_nonces: HashMap::new(),
local_transactions: LocalTransactionsList::default(), local_transactions: LocalTransactionsList::default(),
@ -624,7 +646,7 @@ impl TransactionQueue {
/// Set the new limit for `current` and `future` queue. /// Set the new limit for `current` and `future` queue.
pub fn set_limit(&mut self, limit: usize) { pub fn set_limit(&mut self, limit: usize) {
self.current.set_limit(limit); self.current.set_limit(limit);
self.future.set_limit(limit); self.future.set_limit(limit >> FUTURE_QUEUE_LIMITS_SHIFT);
// And ensure the limits // And ensure the limits
self.current.enforce_limit(&mut self.by_hash, &mut self.local_transactions); self.current.enforce_limit(&mut self.by_hash, &mut self.local_transactions);
self.future.enforce_limit(&mut self.by_hash, &mut self.local_transactions); self.future.enforce_limit(&mut self.by_hash, &mut self.local_transactions);
@ -657,16 +679,17 @@ impl TransactionQueue {
pub fn set_gas_limit(&mut self, gas_limit: U256) { pub fn set_gas_limit(&mut self, gas_limit: U256) {
let extra = gas_limit / U256::from(GAS_LIMIT_HYSTERESIS); let extra = gas_limit / U256::from(GAS_LIMIT_HYSTERESIS);
self.gas_limit = match gas_limit.overflowing_add(extra) { let total_gas_limit = match gas_limit.overflowing_add(extra) {
(_, true) => !U256::zero(), (_, true) => !U256::zero(),
(val, false) => val, (val, false) => val,
}; };
self.total_gas_limit = total_gas_limit;
} }
/// Sets new total gas limit. /// Sets new total gas limit.
pub fn set_total_gas_limit(&mut self, gas_limit: U256) { pub fn set_total_gas_limit(&mut self, total_gas_limit: U256) {
self.future.gas_limit = gas_limit; self.current.total_gas_limit = total_gas_limit;
self.current.gas_limit = gas_limit; self.future.total_gas_limit = total_gas_limit >> FUTURE_QUEUE_LIMITS_SHIFT;
self.future.enforce_limit(&mut self.by_hash, &mut self.local_transactions); self.future.enforce_limit(&mut self.by_hash, &mut self.local_transactions);
} }
@ -796,16 +819,17 @@ impl TransactionQueue {
})); }));
} }
if tx.gas > self.gas_limit || tx.gas > self.tx_gas_limit { let gas_limit = cmp::min(self.tx_gas_limit, self.total_gas_limit);
if tx.gas > gas_limit {
trace!(target: "txqueue", trace!(target: "txqueue",
"Dropping transaction above gas limit: {:?} ({} > min({}, {}))", "Dropping transaction above gas limit: {:?} ({} > min({}, {}))",
tx.hash(), tx.hash(),
tx.gas, tx.gas,
self.gas_limit, self.total_gas_limit,
self.tx_gas_limit self.tx_gas_limit
); );
return Err(Error::Transaction(TransactionError::GasLimitExceeded { return Err(Error::Transaction(TransactionError::GasLimitExceeded {
limit: self.gas_limit, limit: gas_limit,
got: tx.gas, got: tx.gas,
})); }));
} }
@ -1591,7 +1615,13 @@ pub mod test {
#[test] #[test]
fn should_return_correct_nonces_when_dropped_because_of_limit() { fn should_return_correct_nonces_when_dropped_because_of_limit() {
// given // given
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 2, !U256::zero(), !U256::zero()); let mut txq = TransactionQueue::with_limits(
PrioritizationStrategy::GasPriceOnly,
2,
usize::max_value(),
!U256::zero(),
!U256::zero(),
);
let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into()); let (tx1, tx2) = new_tx_pair(123.into(), 1.into(), 1.into(), 0.into());
let sender = tx1.sender(); let sender = tx1.sender();
let nonce = tx1.nonce; let nonce = tx1.nonce;
@ -1631,7 +1661,8 @@ pub mod test {
by_address: Table::new(), by_address: Table::new(),
by_gas_price: Default::default(), by_gas_price: Default::default(),
limit: 1, limit: 1,
gas_limit: !U256::zero(), total_gas_limit: !U256::zero(),
memory_limit: usize::max_value(),
}; };
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None, 0, 0); let tx1 = VerifiedTransaction::new(tx1, TransactionOrigin::External, None, 0, 0);
@ -1672,7 +1703,8 @@ pub mod test {
by_address: Table::new(), by_address: Table::new(),
by_gas_price: Default::default(), by_gas_price: Default::default(),
limit: 1, limit: 1,
gas_limit: !U256::zero(), total_gas_limit: !U256::zero(),
memory_limit: 0,
}; };
// Create two transactions with same nonce // Create two transactions with same nonce
// (same hash) // (same hash)
@ -1721,7 +1753,8 @@ pub mod test {
by_address: Table::new(), by_address: Table::new(),
by_gas_price: Default::default(), by_gas_price: Default::default(),
limit: 2, limit: 2,
gas_limit: !U256::zero(), total_gas_limit: !U256::zero(),
memory_limit: 0,
}; };
let tx = new_tx_default(); let tx = new_tx_default();
let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, None, 0, 0); let tx1 = VerifiedTransaction::new(tx.clone(), TransactionOrigin::External, None, 0, 0);
@ -1739,7 +1772,8 @@ pub mod test {
by_address: Table::new(), by_address: Table::new(),
by_gas_price: Default::default(), by_gas_price: Default::default(),
limit: 1, limit: 1,
gas_limit: !U256::zero(), total_gas_limit: !U256::zero(),
memory_limit: 0,
}; };
assert_eq!(set.gas_price_entry_limit(), 0.into()); assert_eq!(set.gas_price_entry_limit(), 0.into());
@ -1884,17 +1918,17 @@ pub mod test {
} }
#[test] #[test]
fn gas_limit_should_never_overflow() { fn tx_gas_limit_should_never_overflow() {
// given // given
let mut txq = TransactionQueue::default(); let mut txq = TransactionQueue::default();
txq.set_gas_limit(U256::zero()); txq.set_gas_limit(U256::zero());
assert_eq!(txq.gas_limit, U256::zero()); assert_eq!(txq.total_gas_limit, U256::zero());
// when // when
txq.set_gas_limit(!U256::zero()); txq.set_gas_limit(!U256::zero());
// then // then
assert_eq!(txq.gas_limit, !U256::zero()); assert_eq!(txq.total_gas_limit, !U256::zero());
} }
#[test] #[test]
@ -2352,7 +2386,13 @@ pub mod test {
#[test] #[test]
fn should_drop_old_transactions_when_hitting_the_limit() { fn should_drop_old_transactions_when_hitting_the_limit() {
// given // given
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero()); let mut txq = TransactionQueue::with_limits(
PrioritizationStrategy::GasPriceOnly,
1,
usize::max_value(),
!U256::zero(),
!U256::zero()
);
let (tx, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx, tx2) = new_tx_pair_default(1.into(), 0.into());
let sender = tx.sender(); let sender = tx.sender();
let nonce = tx.nonce; let nonce = tx.nonce;
@ -2373,7 +2413,13 @@ pub mod test {
#[test] #[test]
fn should_limit_future_transactions() { fn should_limit_future_transactions() {
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero()); let mut txq = TransactionQueue::with_limits(
PrioritizationStrategy::GasPriceOnly,
1 << FUTURE_QUEUE_LIMITS_SHIFT,
usize::max_value(),
!U256::zero(),
!U256::zero(),
);
txq.current.set_limit(10); txq.current.set_limit(10);
let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into()); let (tx1, tx2) = new_tx_pair_default(4.into(), 1.into());
let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into()); let (tx3, tx4) = new_tx_pair_default(4.into(), 2.into());
@ -2392,7 +2438,13 @@ pub mod test {
#[test] #[test]
fn should_limit_by_gas() { fn should_limit_by_gas() {
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero()); let mut txq = TransactionQueue::with_limits(
PrioritizationStrategy::GasPriceOnly,
100,
usize::max_value(),
default_gas_val() * U256::from(2),
!U256::zero()
);
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap(); txq.add(tx1.clone(), TransactionOrigin::External, 0, None, &default_tx_provider()).unwrap();
@ -2405,7 +2457,13 @@ pub mod test {
#[test] #[test]
fn should_keep_own_transactions_above_gas_limit() { fn should_keep_own_transactions_above_gas_limit() {
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 100, default_gas_val() * U256::from(2), !U256::zero()); let mut txq = TransactionQueue::with_limits(
PrioritizationStrategy::GasPriceOnly,
100,
usize::max_value(),
default_gas_val() * U256::from(2),
!U256::zero()
);
let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1)); let (tx1, tx2) = new_tx_pair_default(U256::from(1), U256::from(1));
let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2)); let (tx3, tx4) = new_tx_pair_default(U256::from(1), U256::from(2));
let (tx5, _) = new_tx_pair_default(U256::from(1), U256::from(2)); let (tx5, _) = new_tx_pair_default(U256::from(1), U256::from(2));
@ -2679,7 +2737,13 @@ pub mod test {
#[test] #[test]
fn should_keep_right_order_in_future() { fn should_keep_right_order_in_future() {
// given // given
let mut txq = TransactionQueue::with_limits(PrioritizationStrategy::GasPriceOnly, 1, !U256::zero(), !U256::zero()); let mut txq = TransactionQueue::with_limits(
PrioritizationStrategy::GasPriceOnly,
1 << FUTURE_QUEUE_LIMITS_SHIFT,
usize::max_value(),
!U256::zero(),
!U256::zero()
);
let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into()); let (tx1, tx2) = new_tx_pair_default(1.into(), 0.into());
let prev_nonce = default_account_details().nonce - U256::one(); let prev_nonce = default_account_details().nonce - U256::one();

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use keccak::Keccak256; use keccak::Keccak256;
use super::{KeyPair, Error, Generator, Secret}; use super::{KeyPair, Generator, Secret};
/// Simple brainwallet. /// Simple brainwallet.
pub struct Brain(String); pub struct Brain(String);
@ -27,7 +27,9 @@ impl Brain {
} }
impl Generator for Brain { impl Generator for Brain {
fn generate(self) -> Result<KeyPair, Error> { type Error = ::Void;
fn generate(self) -> Result<KeyPair, Self::Error> {
let seed = self.0; let seed = self.0;
let mut secret = seed.into_bytes().keccak256(); let mut secret = seed.into_bytes().keccak256();
@ -38,11 +40,10 @@ impl Generator for Brain {
match i > 16384 { match i > 16384 {
false => i += 1, false => i += 1,
true => { true => {
if let Ok(secret) = Secret::from_unsafe_slice(&secret) { if let Ok(pair) = Secret::from_unsafe_slice(&secret)
let result = KeyPair::from_secret(secret); .and_then(KeyPair::from_secret)
if result.as_ref().ok().map_or(false, |r| r.address()[0] == 0) { {
return result; if pair.address()[0] == 0 { return Ok(pair) }
}
} }
}, },
} }

View File

@ -15,8 +15,6 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
extern crate rand; extern crate rand;
#[macro_use]
extern crate lazy_static;
extern crate tiny_keccak; extern crate tiny_keccak;
extern crate secp256k1; extern crate secp256k1;
extern crate rustc_hex; extern crate rustc_hex;
@ -24,6 +22,9 @@ extern crate ethcore_bigint as bigint;
extern crate crypto as rcrypto; extern crate crypto as rcrypto;
extern crate byteorder; extern crate byteorder;
#[macro_use]
extern crate lazy_static;
mod brain; mod brain;
mod error; mod error;
mod keypair; mod keypair;
@ -34,21 +35,12 @@ mod signature;
mod secret; mod secret;
mod extended; mod extended;
lazy_static! {
pub static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new();
}
/// Generates new keypair.
pub trait Generator {
/// Should be called to generate new keypair.
fn generate(self) -> Result<KeyPair, Error>;
}
pub mod math; pub mod math;
pub use self::brain::Brain; pub use self::brain::Brain;
pub use self::error::Error; pub use self::error::Error;
pub use self::keypair::{KeyPair, public_to_address}; pub use self::keypair::{KeyPair, public_to_address};
pub use self::math::public_is_valid;
pub use self::prefix::Prefix; pub use self::prefix::Prefix;
pub use self::random::Random; pub use self::random::Random;
pub use self::signature::{sign, verify_public, verify_address, recover, Signature}; pub use self::signature::{sign, verify_public, verify_address, recover, Signature};
@ -57,6 +49,22 @@ pub use self::extended::{ExtendedPublic, ExtendedSecret, ExtendedKeyPair, Deriva
use bigint::hash::{H160, H256, H512}; use bigint::hash::{H160, H256, H512};
lazy_static! {
pub static ref SECP256K1: secp256k1::Secp256k1 = secp256k1::Secp256k1::new();
}
/// Uninstantiatable error type for infallible generators.
#[derive(Debug)]
pub enum Void {}
/// Generates new keypair.
pub trait Generator {
type Error;
/// Should be called to generate new keypair.
fn generate(self) -> Result<KeyPair, Self::Error>;
}
pub type Address = H160; pub type Address = H160;
pub type Message = H256; pub type Message = H256;
pub type Public = H512; pub type Public = H512;

View File

@ -20,6 +20,12 @@ use secp256k1::constants::{GENERATOR_X, GENERATOR_Y, CURVE_ORDER};
use bigint::prelude::U256; use bigint::prelude::U256;
use bigint::hash::H256; use bigint::hash::H256;
/// Whether the public key is valid.
pub fn public_is_valid(public: &Public) -> bool {
to_secp256k1_public(public).ok()
.map_or(false, |p| p.is_valid())
}
/// Inplace multiply public key by secret key (EC point * scalar) /// Inplace multiply public key by secret key (EC point * scalar)
pub fn public_mul_secret(public: &mut Public, secret: &Secret) -> Result<(), Error> { pub fn public_mul_secret(public: &mut Public, secret: &Secret) -> Result<(), Error> {
let key_secret = secret.to_secp256k1_secret()?; let key_secret = secret.to_secp256k1_secret()?;

View File

@ -32,6 +32,8 @@ impl Prefix {
} }
impl Generator for Prefix { impl Generator for Prefix {
type Error = Error;
fn generate(self) -> Result<KeyPair, Error> { fn generate(self) -> Result<KeyPair, Error> {
for _ in 0..self.iterations { for _ in 0..self.iterations {
let keypair = Random.generate()?; let keypair = Random.generate()?;

View File

@ -15,18 +15,30 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
use rand::os::OsRng; use rand::os::OsRng;
use super::{Generator, KeyPair, Error, SECP256K1}; use super::{Generator, KeyPair, SECP256K1};
/// Randomly generates new keypair. /// Randomly generates new keypair, instantiating the RNG each time.
pub struct Random; pub struct Random;
impl Generator for Random { impl Generator for Random {
fn generate(self) -> Result<KeyPair, Error> { type Error = ::std::io::Error;
let context = &SECP256K1;
fn generate(self) -> Result<KeyPair, Self::Error> {
let mut rng = OsRng::new()?; let mut rng = OsRng::new()?;
let (sec, publ) = context.generate_keypair(&mut rng)?; match rng.generate() {
Ok(pair) => Ok(pair),
Err(void) => match void {}, // LLVM unreachable
}
}
}
impl<'a> Generator for &'a mut OsRng {
type Error = ::Void;
fn generate(self) -> Result<KeyPair, Self::Error> {
let (sec, publ) = SECP256K1.generate_keypair(self)
.expect("context always created with full capabilities; qed");
Ok(KeyPair::from_keypair(sec, publ)) Ok(KeyPair::from_keypair(sec, publ))
} }
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "evmjit" name = "evmjit"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[lib] [lib]

View File

@ -3,7 +3,7 @@ description = "Fetching hash-addressed content."
homepage = "http://parity.io" homepage = "http://parity.io"
license = "GPL-3.0" license = "GPL-3.0"
name = "parity-hash-fetch" name = "parity-hash-fetch"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]

View File

@ -3,7 +3,7 @@ description = "Hardware wallet support."
homepage = "http://parity.io" homepage = "http://parity.io"
license = "GPL-3.0" license = "GPL-3.0"
name = "hardware-wallet" name = "hardware-wallet"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Types that implement IPC and are common to multiple modules." description = "Types that implement IPC and are common to multiple modules."
name = "ipc-common-types" name = "ipc-common-types"
version = "1.7.0" version = "1.8.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs" build = "build.rs"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ethcore-ipc-codegen" name = "ethcore-ipc-codegen"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
license = "GPL-3.0" license = "GPL-3.0"
description = "Macros to auto-generate implementations for ipc call" description = "Macros to auto-generate implementations for ipc call"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ethcore-ipc-nano" name = "ethcore-ipc-nano"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
license = "GPL-3.0" license = "GPL-3.0"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "ethcore-ipc" name = "ethcore-ipc"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
license = "GPL-3.0" license = "GPL-3.0"

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Parity IPFS-compatible API" description = "Parity IPFS-compatible API"
name = "parity-ipfs-api" name = "parity-ipfs-api"
version = "1.7.0" version = "1.8.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Parity built-in dapps." description = "Parity built-in dapps."
name = "parity-ui-dev" name = "parity-ui-dev"
version = "1.7.0" version = "1.8.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs" build = "build.rs"

View File

@ -1,6 +1,6 @@
{ {
"name": "parity.js", "name": "parity.js",
"version": "1.7.99", "version": "1.7.101",
"main": "release/index.js", "main": "release/index.js",
"jsnext:main": "src/index.js", "jsnext:main": "src/index.js",
"author": "Parity Team <admin@parity.io>", "author": "Parity Team <admin@parity.io>",
@ -209,7 +209,7 @@
"react-intl": "2.1.5", "react-intl": "2.1.5",
"react-markdown": "2.4.4", "react-markdown": "2.4.4",
"react-portal": "3.0.0", "react-portal": "3.0.0",
"react-qr-reader": "1.0.3", "react-qr-reader": "1.1.3",
"react-redux": "4.4.6", "react-redux": "4.4.6",
"react-router": "3.0.0", "react-router": "3.0.0",
"react-router-redux": "4.0.7", "react-router-redux": "4.0.7",

View File

@ -39,7 +39,7 @@ export const ERROR_CODES = {
COMPILATION_ERROR: -32050, COMPILATION_ERROR: -32050,
ENCRYPTION_ERROR: -32055, ENCRYPTION_ERROR: -32055,
FETCH_ERROR: -32060, FETCH_ERROR: -32060,
INVALID_PARAMS: -32602, INVALID_PARAMS: -32602
}; };
export default class TransportError extends ExtendableError { export default class TransportError extends ExtendableError {

View File

@ -69,14 +69,14 @@ export default class ConfirmDialog extends Component {
<Portal <Portal
buttons={ [ buttons={ [
<Button <Button
disabled={ disabledDeny } disabled={ disabledDeny || busy }
icon={ iconDeny || <CancelIcon /> } icon={ iconDeny || <CancelIcon /> }
key='deny' key='deny'
label={ labelDeny || DEFAULT_NO } label={ labelDeny || DEFAULT_NO }
onClick={ onDeny } onClick={ onDeny }
/>, />,
<Button <Button
disabled={ disabledConfirm } disabled={ disabledConfirm || busy }
icon={ iconConfirm || <CheckIcon /> } icon={ iconConfirm || <CheckIcon /> }
key='confirm' key='confirm'
label={ labelConfirm || DEFAULT_YES } label={ labelConfirm || DEFAULT_YES }

View File

@ -41,8 +41,13 @@ class Delete extends Component {
newError: PropTypes.func newError: PropTypes.func
}; };
state = {
isBusy: false
};
render () { render () {
const { account, confirmMessage, visible } = this.props; const { account, confirmMessage, visible } = this.props;
const { isBusy } = this.state;
if (!visible) { if (!visible) {
return null; return null;
@ -50,6 +55,7 @@ class Delete extends Component {
return ( return (
<ConfirmDialog <ConfirmDialog
busy={ isBusy }
className={ styles.delete } className={ styles.delete }
title={ title={
<FormattedMessage <FormattedMessage
@ -99,6 +105,8 @@ class Delete extends Component {
const { api, router } = this.context; const { api, router } = this.context;
const { account, route, newError } = this.props; const { account, route, newError } = this.props;
this.setState({ isBusy: true });
api.parity api.parity
.removeAddress(account.address) .removeAddress(account.address)
.then(() => { .then(() => {

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Parity ethkey WASM module." description = "Parity ethkey WASM module."
name = "parity-ethkey-wasm" name = "parity-ethkey-wasm"
version = "1.7.0" version = "1.8.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Ethcore client." description = "Ethcore client."
name = "ethcore-logger" name = "ethcore-logger"
version = "1.7.0" version = "1.8.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]

View File

@ -107,8 +107,8 @@ usd_per_eth = "auto"
price_update_period = "hourly" price_update_period = "hourly"
gas_floor_target = "4700000" gas_floor_target = "4700000"
gas_cap = "6283184" gas_cap = "6283184"
tx_queue_size = 1024 tx_queue_size = 8192
tx_queue_gas = "auto" tx_queue_gas = "off"
tx_queue_strategy = "gas_factor" tx_queue_strategy = "gas_factor"
tx_queue_ban_count = 1 tx_queue_ban_count = 1
tx_queue_ban_time = 180 #s tx_queue_ban_time = 180 #s
@ -145,3 +145,7 @@ jit = false
logging = "own_tx=trace" logging = "own_tx=trace"
log_file = "/var/log/parity.log" log_file = "/var/log/parity.log"
color = true color = true
[whisper]
enabled = false
pool_size = 20

View File

@ -56,8 +56,8 @@ reseal_on_txs = "all"
reseal_min_period = 4000 reseal_min_period = 4000
reseal_max_period = 60000 reseal_max_period = 60000
price_update_period = "hourly" price_update_period = "hourly"
tx_queue_size = 1024 tx_queue_size = 8192
tx_queue_gas = "auto" tx_queue_gas = "off"
[footprint] [footprint]
tracing = "on" tracing = "on"
@ -84,3 +84,7 @@ log_file = "/var/log/parity.log"
color = true color = true
ports_shift = 0 ports_shift = 0
unsafe_expose = false unsafe_expose = false
[whisper]
enabled = true
pool_size = 50

View File

@ -179,7 +179,7 @@ usage! {
or |c: &Config| otry!(c.rpc).interface.clone(), or |c: &Config| otry!(c.rpc).interface.clone(),
flag_jsonrpc_cors: Option<String> = None, flag_jsonrpc_cors: Option<String> = None,
or |c: &Config| otry!(c.rpc).cors.clone().map(Some), or |c: &Config| otry!(c.rpc).cors.clone().map(Some),
flag_jsonrpc_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore", flag_jsonrpc_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore,shh,shh_pubsub",
or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")), or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")),
flag_jsonrpc_hosts: String = "none", flag_jsonrpc_hosts: String = "none",
or |c: &Config| otry!(c.rpc).hosts.as_ref().map(|vec| vec.join(",")), or |c: &Config| otry!(c.rpc).hosts.as_ref().map(|vec| vec.join(",")),
@ -195,7 +195,7 @@ usage! {
or |c: &Config| otry!(c.websockets).port.clone(), or |c: &Config| otry!(c.websockets).port.clone(),
flag_ws_interface: String = "local", flag_ws_interface: String = "local",
or |c: &Config| otry!(c.websockets).interface.clone(), or |c: &Config| otry!(c.websockets).interface.clone(),
flag_ws_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore", flag_ws_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore,shh,shh_pubsub",
or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")), or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")),
flag_ws_origins: String = "chrome-extension://*", flag_ws_origins: String = "chrome-extension://*",
or |c: &Config| otry!(c.websockets).origins.as_ref().map(|vec| vec.join(",")), or |c: &Config| otry!(c.websockets).origins.as_ref().map(|vec| vec.join(",")),
@ -207,7 +207,7 @@ usage! {
or |c: &Config| otry!(c.ipc).disable.clone(), or |c: &Config| otry!(c.ipc).disable.clone(),
flag_ipc_path: String = if cfg!(windows) { r"\\.\pipe\jsonrpc.ipc" } else { "$BASE/jsonrpc.ipc" }, flag_ipc_path: String = if cfg!(windows) { r"\\.\pipe\jsonrpc.ipc" } else { "$BASE/jsonrpc.ipc" },
or |c: &Config| otry!(c.ipc).path.clone(), or |c: &Config| otry!(c.ipc).path.clone(),
flag_ipc_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,traces,rpc,secretstore", flag_ipc_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,traces,rpc,secretstore,shh,shh_pubsub",
or |c: &Config| otry!(c.ipc).apis.as_ref().map(|vec| vec.join(",")), or |c: &Config| otry!(c.ipc).apis.as_ref().map(|vec| vec.join(",")),
// DAPPS // DAPPS
@ -283,9 +283,11 @@ usage! {
or |c: &Config| otry!(c.mining).gas_cap.clone(), or |c: &Config| otry!(c.mining).gas_cap.clone(),
flag_extra_data: Option<String> = None, flag_extra_data: Option<String> = None,
or |c: &Config| otry!(c.mining).extra_data.clone().map(Some), or |c: &Config| otry!(c.mining).extra_data.clone().map(Some),
flag_tx_queue_size: usize = 1024usize, flag_tx_queue_size: usize = 8192usize,
or |c: &Config| otry!(c.mining).tx_queue_size.clone(), or |c: &Config| otry!(c.mining).tx_queue_size.clone(),
flag_tx_queue_gas: String = "auto", flag_tx_queue_mem_limit: u32 = 2u32,
or |c: &Config| otry!(c.mining).tx_queue_mem_limit.clone(),
flag_tx_queue_gas: String = "off",
or |c: &Config| otry!(c.mining).tx_queue_gas.clone(), or |c: &Config| otry!(c.mining).tx_queue_gas.clone(),
flag_tx_queue_strategy: String = "gas_price", flag_tx_queue_strategy: String = "gas_price",
or |c: &Config| otry!(c.mining).tx_queue_strategy.clone(), or |c: &Config| otry!(c.mining).tx_queue_strategy.clone(),
@ -368,6 +370,12 @@ usage! {
flag_no_color: bool = false, flag_no_color: bool = false,
or |c: &Config| otry!(c.misc).color.map(|c| !c).clone(), or |c: &Config| otry!(c.misc).color.map(|c| !c).clone(),
// -- Whisper options
flag_whisper: bool = false,
or |c: &Config| otry!(c.whisper).enabled,
flag_whisper_pool_size: usize = 10usize,
or |c: &Config| otry!(c.whisper).pool_size.clone(),
// -- Legacy Options supported in configs // -- Legacy Options supported in configs
flag_dapps_port: Option<u16> = None, flag_dapps_port: Option<u16> = None,
or |c: &Config| otry!(c.dapps).port.clone().map(Some), or |c: &Config| otry!(c.dapps).port.clone().map(Some),
@ -410,6 +418,7 @@ struct Config {
vm: Option<VM>, vm: Option<VM>,
misc: Option<Misc>, misc: Option<Misc>,
stratum: Option<Stratum>, stratum: Option<Stratum>,
whisper: Option<Whisper>,
} }
#[derive(Default, Debug, PartialEq, Deserialize)] #[derive(Default, Debug, PartialEq, Deserialize)]
@ -552,6 +561,7 @@ struct Mining {
gas_cap: Option<String>, gas_cap: Option<String>,
extra_data: Option<String>, extra_data: Option<String>,
tx_queue_size: Option<usize>, tx_queue_size: Option<usize>,
tx_queue_mem_limit: Option<u32>,
tx_queue_gas: Option<String>, tx_queue_gas: Option<String>,
tx_queue_strategy: Option<String>, tx_queue_strategy: Option<String>,
tx_queue_ban_count: Option<u16>, tx_queue_ban_count: Option<u16>,
@ -606,12 +616,18 @@ struct Misc {
unsafe_expose: Option<bool>, unsafe_expose: Option<bool>,
} }
#[derive(Default, Debug, PartialEq, Deserialize)]
struct Whisper {
enabled: Option<bool>,
pool_size: Option<usize>,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{ use super::{
Args, ArgsError, Args, ArgsError,
Config, Operating, Account, Ui, Network, Ws, Rpc, Ipc, Dapps, Ipfs, Mining, Footprint, Config, Operating, Account, Ui, Network, Ws, Rpc, Ipc, Dapps, Ipfs, Mining, Footprint,
Snapshots, VM, Misc, SecretStore, Snapshots, VM, Misc, Whisper, SecretStore,
}; };
use toml; use toml;
@ -818,8 +834,9 @@ mod tests {
flag_gas_floor_target: "4700000".into(), flag_gas_floor_target: "4700000".into(),
flag_gas_cap: "6283184".into(), flag_gas_cap: "6283184".into(),
flag_extra_data: Some("Parity".into()), flag_extra_data: Some("Parity".into()),
flag_tx_queue_size: 1024usize, flag_tx_queue_size: 8192usize,
flag_tx_queue_gas: "auto".into(), flag_tx_queue_mem_limit: 2u32,
flag_tx_queue_gas: "off".into(),
flag_tx_queue_strategy: "gas_factor".into(), flag_tx_queue_strategy: "gas_factor".into(),
flag_tx_queue_ban_count: 1u16, flag_tx_queue_ban_count: 1u16,
flag_tx_queue_ban_time: 180u16, flag_tx_queue_ban_time: 180u16,
@ -865,6 +882,10 @@ mod tests {
// -- Virtual Machine Options // -- Virtual Machine Options
flag_jitvm: false, flag_jitvm: false,
// -- Whisper options.
flag_whisper: false,
flag_whisper_pool_size: 20,
// -- Legacy Options // -- Legacy Options
flag_geth: false, flag_geth: false,
flag_testnet: false, flag_testnet: false,
@ -1045,8 +1066,9 @@ mod tests {
price_update_period: Some("hourly".into()), price_update_period: Some("hourly".into()),
gas_floor_target: None, gas_floor_target: None,
gas_cap: None, gas_cap: None,
tx_queue_size: Some(1024), tx_queue_size: Some(8192),
tx_queue_gas: Some("auto".into()), tx_queue_mem_limit: None,
tx_queue_gas: Some("off".into()),
tx_queue_strategy: None, tx_queue_strategy: None,
tx_queue_ban_count: None, tx_queue_ban_count: None,
tx_queue_ban_time: None, tx_queue_ban_time: None,
@ -1087,6 +1109,10 @@ mod tests {
ports_shift: Some(0), ports_shift: Some(0),
unsafe_expose: Some(false), unsafe_expose: Some(false),
}), }),
whisper: Some(Whisper {
enabled: Some(true),
pool_size: Some(50),
}),
stratum: None, stratum: None,
}); });
} }

View File

@ -316,6 +316,9 @@ Sealing/Mining Options:
block due to transaction volume (default: {flag_gas_cap}). block due to transaction volume (default: {flag_gas_cap}).
--extra-data STRING Specify a custom extra-data for authored blocks, no --extra-data STRING Specify a custom extra-data for authored blocks, no
more than 32 characters. (default: {flag_extra_data:?}) more than 32 characters. (default: {flag_extra_data:?})
--tx-queue-mem-limit MB Maximum amount of memory that can be used by the
transaction queue. Setting this parameter to 0
disables limiting (default: {flag_tx_queue_mem_limit}).
--tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting --tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting
to be included in next block) (default: {flag_tx_queue_size}). to be included in next block) (default: {flag_tx_queue_size}).
--tx-queue-gas LIMIT Maximum amount of total gas for external transactions in --tx-queue-gas LIMIT Maximum amount of total gas for external transactions in
@ -427,6 +430,11 @@ Snapshot Options:
Virtual Machine Options: Virtual Machine Options:
--jitvm Enable the JIT VM. (default: {flag_jitvm}) --jitvm Enable the JIT VM. (default: {flag_jitvm})
Whisper Options:
--whisper Enable the Whisper network. (default: {flag_whisper})
--whisper-pool-size MB Target size of the whisper message pool in megabytes.
(default: {flag_whisper_pool_size})
Legacy Options: Legacy Options:
--geth Run in Geth-compatibility mode. Sets the IPC path --geth Run in Geth-compatibility mode. Sets the IPC path
to be the same as Geth's. Overrides the --ipc-path to be the same as Geth's. Overrides the --ipc-path

View File

@ -331,6 +331,7 @@ impl Configuration {
}; };
let verifier_settings = self.verifier_settings(); let verifier_settings = self.verifier_settings();
let whisper_config = self.whisper_config();
let run_cmd = RunCmd { let run_cmd = RunCmd {
cache_config: cache_config, cache_config: cache_config,
@ -377,6 +378,7 @@ impl Configuration {
serve_light: !self.args.flag_no_serve_light, serve_light: !self.args.flag_no_serve_light,
light: self.args.flag_light, light: self.args.flag_light,
no_persistent_txqueue: self.args.flag_no_persistent_txqueue, no_persistent_txqueue: self.args.flag_no_persistent_txqueue,
whisper: whisper_config,
}; };
Cmd::Run(run_cmd) Cmd::Run(run_cmd)
}; };
@ -401,7 +403,6 @@ impl Configuration {
extra_data: self.extra_data()?, extra_data: self.extra_data()?,
gas_floor_target: to_u256(&self.args.flag_gas_floor_target)?, gas_floor_target: to_u256(&self.args.flag_gas_floor_target)?,
gas_ceil_target: to_u256(&self.args.flag_gas_cap)?, gas_ceil_target: to_u256(&self.args.flag_gas_cap)?,
transactions_limit: self.args.flag_tx_queue_size,
engine_signer: self.engine_signer()?, engine_signer: self.engine_signer()?,
}; };
@ -526,6 +527,9 @@ impl Configuration {
None => U256::max_value(), None => U256::max_value(),
}, },
tx_queue_size: self.args.flag_tx_queue_size, tx_queue_size: self.args.flag_tx_queue_size,
tx_queue_memory_limit: if self.args.flag_tx_queue_mem_limit > 0 {
Some(self.args.flag_tx_queue_mem_limit as usize * 1024 * 1024)
} else { None },
tx_queue_gas_limit: to_gas_limit(&self.args.flag_tx_queue_gas)?, tx_queue_gas_limit: to_gas_limit(&self.args.flag_tx_queue_gas)?,
tx_queue_strategy: to_queue_strategy(&self.args.flag_tx_queue_strategy)?, tx_queue_strategy: to_queue_strategy(&self.args.flag_tx_queue_strategy)?,
pending_set: to_pending_set(&self.args.flag_relay_set)?, pending_set: to_pending_set(&self.args.flag_relay_set)?,
@ -1062,6 +1066,13 @@ impl Configuration {
settings settings
} }
fn whisper_config(&self) -> ::whisper::Config {
::whisper::Config {
enabled: self.args.flag_whisper,
target_message_pool_size: self.args.flag_whisper_pool_size * 1024 * 1024,
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -1293,7 +1304,7 @@ mod tests {
public_node: false, public_node: false,
warp_sync: true, warp_sync: true,
acc_conf: Default::default(), acc_conf: Default::default(),
gas_pricer: Default::default(), gas_pricer_conf: Default::default(),
miner_extras: Default::default(), miner_extras: Default::default(),
update_policy: UpdatePolicy { enable_downloading: true, require_consensus: true, filter: UpdateFilter::Critical, track: ReleaseTrack::Unknown, path: default_hypervisor_path() }, update_policy: UpdatePolicy { enable_downloading: true, require_consensus: true, filter: UpdateFilter::Critical, track: ReleaseTrack::Unknown, path: default_hypervisor_path() },
mode: Default::default(), mode: Default::default(),
@ -1320,6 +1331,7 @@ mod tests {
serve_light: true, serve_light: true,
light: false, light: false,
no_persistent_txqueue: false, no_persistent_txqueue: false,
whisper: Default::default(),
}; };
expected.secretstore_conf.enabled = cfg!(feature = "secretstore"); expected.secretstore_conf.enabled = cfg!(feature = "secretstore");
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(expected)); assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(expected));
@ -1588,7 +1600,7 @@ mod tests {
match conf.into_command().unwrap().cmd { match conf.into_command().unwrap().cmd {
Cmd::Run(c) => { Cmd::Run(c) => {
assert_eq!(c.net_settings.chain, "dev"); assert_eq!(c.net_settings.chain, "dev");
assert_eq!(c.gas_pricer, GasPricerConfig::Fixed(0.into())); assert_eq!(c.gas_pricer_conf, GasPricerConfig::Fixed(0.into()));
assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(0)); assert_eq!(c.miner_options.reseal_min_period, Duration::from_millis(0));
}, },
_ => panic!("Should be Cmd::Run"), _ => panic!("Should be Cmd::Run"),

View File

@ -254,21 +254,24 @@ impl<T: InformantData> Informant<T> {
return; return;
} }
*self.last_tick.write() = Instant::now();
let (client_report, full_report) = {
let mut last_report = self.last_report.lock();
let full_report = self.target.report();
let diffed = full_report.client_report.clone() - &*last_report;
*last_report = full_report.client_report.clone();
(diffed, full_report)
};
let Report { let Report {
importing, importing,
chain_info, chain_info,
client_report,
queue_info, queue_info,
cache_sizes, cache_sizes,
sync_info, sync_info,
} = self.target.report(); ..
} = full_report;
let client_report = {
let mut last_report = self.last_report.lock();
let diffed = client_report.clone() - &*last_report;
*last_report = client_report.clone();
diffed
};
let rpc_stats = self.rpc_stats.as_ref(); let rpc_stats = self.rpc_stats.as_ref();
@ -284,8 +287,6 @@ impl<T: InformantData> Informant<T> {
return; return;
} }
*self.last_tick.write() = Instant::now();
let paint = |c: Style, t: String| match self.with_color && stdout_isatty() { let paint = |c: Style, t: String| match self.with_color && stdout_isatty() {
true => format!("{}", c.paint(t)), true => format!("{}", c.paint(t)),
false => t, false => t,

View File

@ -63,6 +63,7 @@ extern crate parity_local_store as local_store;
extern crate parity_reactor; extern crate parity_reactor;
extern crate parity_rpc; extern crate parity_rpc;
extern crate parity_updater as updater; extern crate parity_updater as updater;
extern crate parity_whisper;
extern crate path; extern crate path;
extern crate rpc_cli; extern crate rpc_cli;
@ -110,6 +111,7 @@ mod snapshot;
mod upgrade; mod upgrade;
mod url; mod url;
mod user_defaults; mod user_defaults;
mod whisper;
#[cfg(feature="ipc")] #[cfg(feature="ipc")]
mod boot; mod boot;
@ -364,4 +366,3 @@ fn main() {
process::exit(main_direct(can_restart)); process::exit(main_direct(can_restart));
} }
} }

View File

@ -19,7 +19,7 @@ use std::path::Path;
use ethcore::client::BlockChainClient; use ethcore::client::BlockChainClient;
use hypervisor::Hypervisor; use hypervisor::Hypervisor;
use ethsync::{SyncConfig, NetworkConfiguration, NetworkError, Params}; use ethsync::{AttachedProtocol, SyncConfig, NetworkConfiguration, NetworkError, Params};
use ethcore::snapshot::SnapshotService; use ethcore::snapshot::SnapshotService;
use light::Provider; use light::Provider;
@ -151,6 +151,7 @@ pub fn sync(
_snapshot_service: Arc<SnapshotService>, _snapshot_service: Arc<SnapshotService>,
_provider: Arc<Provider>, _provider: Arc<Provider>,
log_settings: &LogConfig, log_settings: &LogConfig,
_attached_protos: Vec<AttachedProtocol>,
) -> Result<SyncModules, NetworkError> { ) -> Result<SyncModules, NetworkError> {
let mut hypervisor = hypervisor_ref.take().expect("There should be hypervisor for ipc configuration"); let mut hypervisor = hypervisor_ref.take().expect("There should be hypervisor for ipc configuration");
let args = sync_arguments(&hypervisor.io_path, sync_cfg, net_cfg, log_settings); let args = sync_arguments(&hypervisor.io_path, sync_cfg, net_cfg, log_settings);
@ -181,6 +182,7 @@ pub fn sync(
snapshot_service: Arc<SnapshotService>, snapshot_service: Arc<SnapshotService>,
provider: Arc<Provider>, provider: Arc<Provider>,
_log_settings: &LogConfig, _log_settings: &LogConfig,
attached_protos: Vec<AttachedProtocol>,
) -> Result<SyncModules, NetworkError> { ) -> Result<SyncModules, NetworkError> {
let eth_sync = EthSync::new(Params { let eth_sync = EthSync::new(Params {
config: sync_cfg, config: sync_cfg,
@ -188,6 +190,7 @@ pub fn sync(
provider: provider, provider: provider,
snapshot_service: snapshot_service, snapshot_service: snapshot_service,
network_config: net_cfg, network_config: net_cfg,
attached_protos: attached_protos,
})?; })?;
Ok((eth_sync.clone() as Arc<SyncProvider>, eth_sync.clone() as Arc<ManageNetwork>, eth_sync.clone() as Arc<ChainNotify>)) Ok((eth_sync.clone() as Arc<SyncProvider>, eth_sync.clone() as Arc<ManageNetwork>, eth_sync.clone() as Arc<ChainNotify>))

View File

@ -22,6 +22,7 @@ use ethcore::spec::Spec;
use ethcore::ethereum; use ethcore::ethereum;
use ethcore::client::Mode; use ethcore::client::Mode;
use ethcore::miner::{GasPricer, GasPriceCalibratorOptions}; use ethcore::miner::{GasPricer, GasPriceCalibratorOptions};
use hash_fetch::fetch::Client as FetchClient;
use user_defaults::UserDefaults; use user_defaults::UserDefaults;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -226,15 +227,18 @@ impl Default for GasPricerConfig {
} }
} }
impl Into<GasPricer> for GasPricerConfig { impl GasPricerConfig {
fn into(self) -> GasPricer { pub fn to_gas_pricer(&self, fetch: FetchClient) -> GasPricer {
match self { match *self {
GasPricerConfig::Fixed(u) => GasPricer::Fixed(u), GasPricerConfig::Fixed(u) => GasPricer::Fixed(u),
GasPricerConfig::Calibrated { usd_per_tx, recalibration_period, .. } => { GasPricerConfig::Calibrated { usd_per_tx, recalibration_period, .. } => {
GasPricer::new_calibrated(GasPriceCalibratorOptions { GasPricer::new_calibrated(
GasPriceCalibratorOptions {
usd_per_tx: usd_per_tx, usd_per_tx: usd_per_tx,
recalibration_period: recalibration_period, recalibration_period: recalibration_period,
}) },
fetch
)
} }
} }
} }
@ -246,7 +250,6 @@ pub struct MinerExtras {
pub extra_data: Vec<u8>, pub extra_data: Vec<u8>,
pub gas_floor_target: U256, pub gas_floor_target: U256,
pub gas_ceil_target: U256, pub gas_ceil_target: U256,
pub transactions_limit: usize,
pub engine_signer: Address, pub engine_signer: Address,
} }
@ -257,7 +260,6 @@ impl Default for MinerExtras {
extra_data: version_data(), extra_data: version_data(),
gas_floor_target: U256::from(4_700_000), gas_floor_target: U256::from(4_700_000),
gas_ceil_target: U256::from(6_283_184), gas_ceil_target: U256::from(6_283_184),
transactions_limit: 1024,
engine_signer: Default::default(), engine_signer: Default::default(),
} }
} }

View File

@ -32,7 +32,6 @@ pub use parity_rpc::{IpcServer, HttpServer, RequestMiddleware};
pub use parity_rpc::ws::Server as WsServer; pub use parity_rpc::ws::Server as WsServer;
pub use parity_rpc::informant::CpuPool; pub use parity_rpc::informant::CpuPool;
pub const DAPPS_DOMAIN: &'static str = "web3.site"; pub const DAPPS_DOMAIN: &'static str = "web3.site";
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -168,7 +167,6 @@ impl Default for WsConfiguration {
} }
} }
impl WsConfiguration { impl WsConfiguration {
pub fn address(&self) -> Option<(String, u16)> { pub fn address(&self) -> Option<(String, u16)> {
match self.enabled { match self.enabled {

View File

@ -67,6 +67,12 @@ pub enum Api {
Rpc, Rpc,
/// SecretStore (Safe) /// SecretStore (Safe)
SecretStore, SecretStore,
/// Whisper (Safe)
// TODO: _if_ someone guesses someone else's key or filter IDs they can remove
// BUT these are all ephemeral so it seems fine.
Whisper,
/// Whisper Pub-Sub (Safe but same concerns as above).
WhisperPubSub,
} }
impl FromStr for Api { impl FromStr for Api {
@ -89,6 +95,8 @@ impl FromStr for Api {
"traces" => Ok(Traces), "traces" => Ok(Traces),
"rpc" => Ok(Rpc), "rpc" => Ok(Rpc),
"secretstore" => Ok(SecretStore), "secretstore" => Ok(SecretStore),
"shh" => Ok(Whisper),
"shh_pubsub" => Ok(WhisperPubSub),
api => Err(format!("Unknown api: {}", api)) api => Err(format!("Unknown api: {}", api))
} }
} }
@ -172,6 +180,8 @@ fn to_modules(apis: &HashSet<Api>) -> BTreeMap<String, String> {
Api::Traces => ("traces", "1.0"), Api::Traces => ("traces", "1.0"),
Api::Rpc => ("rpc", "1.0"), Api::Rpc => ("rpc", "1.0"),
Api::SecretStore => ("secretstore", "1.0"), Api::SecretStore => ("secretstore", "1.0"),
Api::Whisper => ("shh", "1.0"),
Api::WhisperPubSub => ("shh_pubsub", "1.0"),
}; };
modules.insert(name.into(), version.into()); modules.insert(name.into(), version.into());
} }
@ -213,6 +223,7 @@ pub struct FullDependencies {
pub ws_address: Option<(String, u16)>, pub ws_address: Option<(String, u16)>,
pub fetch: FetchClient, pub fetch: FetchClient,
pub remote: parity_reactor::Remote, pub remote: parity_reactor::Remote,
pub whisper_rpc: Option<::whisper::RpcFactory>,
} }
impl FullDependencies { impl FullDependencies {
@ -335,6 +346,18 @@ impl FullDependencies {
Api::SecretStore => { Api::SecretStore => {
handler.extend_with(SecretStoreClient::new(&self.secret_store).to_delegate()); handler.extend_with(SecretStoreClient::new(&self.secret_store).to_delegate());
}, },
Api::Whisper => {
if let Some(ref whisper_rpc) = self.whisper_rpc {
let whisper = whisper_rpc.make_handler();
handler.extend_with(::parity_whisper::rpc::Whisper::to_delegate(whisper));
}
}
Api::WhisperPubSub => {
if let Some(ref whisper_rpc) = self.whisper_rpc {
let whisper = whisper_rpc.make_handler();
handler.extend_with(::parity_whisper::rpc::WhisperPubSub::to_delegate(whisper));
}
}
} }
} }
} }
@ -383,6 +406,7 @@ pub struct LightDependencies {
pub fetch: FetchClient, pub fetch: FetchClient,
pub geth_compatibility: bool, pub geth_compatibility: bool,
pub remote: parity_reactor::Remote, pub remote: parity_reactor::Remote,
pub whisper_rpc: Option<::whisper::RpcFactory>,
} }
impl LightDependencies { impl LightDependencies {
@ -516,6 +540,18 @@ impl LightDependencies {
let secret_store = Some(self.secret_store.clone()); let secret_store = Some(self.secret_store.clone());
handler.extend_with(SecretStoreClient::new(&secret_store).to_delegate()); handler.extend_with(SecretStoreClient::new(&secret_store).to_delegate());
}, },
Api::Whisper => {
if let Some(ref whisper_rpc) = self.whisper_rpc {
let whisper = whisper_rpc.make_handler();
handler.extend_with(::parity_whisper::rpc::Whisper::to_delegate(whisper));
}
}
Api::WhisperPubSub => {
if let Some(ref whisper_rpc) = self.whisper_rpc {
let whisper = whisper_rpc.make_handler();
handler.extend_with(::parity_whisper::rpc::WhisperPubSub::to_delegate(whisper));
}
}
} }
} }
} }
@ -543,8 +579,17 @@ impl ApiSet {
pub fn list_apis(&self) -> HashSet<Api> { pub fn list_apis(&self) -> HashSet<Api> {
let mut public_list = [ let mut public_list = [
Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::Rpc, Api::SecretStore, Api::Web3,
Api::Net,
Api::Eth,
Api::EthPubSub,
Api::Parity,
Api::Rpc,
Api::SecretStore,
Api::Whisper,
Api::WhisperPubSub,
].into_iter().cloned().collect(); ].into_iter().cloned().collect();
match *self { match *self {
ApiSet::List(ref apis) => apis.clone(), ApiSet::List(ref apis) => apis.clone(),
ApiSet::PublicContext => public_list, ApiSet::PublicContext => public_list,
@ -605,6 +650,8 @@ mod test {
assert_eq!(Api::Traces, "traces".parse().unwrap()); assert_eq!(Api::Traces, "traces".parse().unwrap());
assert_eq!(Api::Rpc, "rpc".parse().unwrap()); assert_eq!(Api::Rpc, "rpc".parse().unwrap());
assert_eq!(Api::SecretStore, "secretstore".parse().unwrap()); assert_eq!(Api::SecretStore, "secretstore".parse().unwrap());
assert_eq!(Api::Whisper, "shh".parse().unwrap());
assert_eq!(Api::WhisperPubSub, "shh_pubsub".parse().unwrap());
assert!("rp".parse::<Api>().is_err()); assert!("rp".parse::<Api>().is_err());
} }
@ -622,7 +669,7 @@ mod test {
fn test_api_set_unsafe_context() { fn test_api_set_unsafe_context() {
let expected = vec![ let expected = vec![
// make sure this list contains only SAFE methods // make sure this list contains only SAFE methods
Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub,
].into_iter().collect(); ].into_iter().collect();
assert_eq!(ApiSet::UnsafeContext.list_apis(), expected); assert_eq!(ApiSet::UnsafeContext.list_apis(), expected);
} }
@ -631,7 +678,7 @@ mod test {
fn test_api_set_ipc_context() { fn test_api_set_ipc_context() {
let expected = vec![ let expected = vec![
// safe // safe
Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub,
// semi-safe // semi-safe
Api::ParityAccounts Api::ParityAccounts
].into_iter().collect(); ].into_iter().collect();
@ -642,7 +689,7 @@ mod test {
fn test_api_set_safe_context() { fn test_api_set_safe_context() {
let expected = vec![ let expected = vec![
// safe // safe
Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub,
// semi-safe // semi-safe
Api::ParityAccounts, Api::ParityAccounts,
// Unsafe // Unsafe
@ -654,7 +701,7 @@ mod test {
#[test] #[test]
fn test_all_apis() { fn test_all_apis() {
assert_eq!("all".parse::<ApiSet>().unwrap(), ApiSet::List(vec![ assert_eq!("all".parse::<ApiSet>().unwrap(), ApiSet::List(vec![
Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub,
Api::ParityAccounts, Api::ParityAccounts,
Api::ParitySet, Api::Signer, Api::ParitySet, Api::Signer,
Api::Personal Api::Personal
@ -664,7 +711,7 @@ mod test {
#[test] #[test]
fn test_all_without_personal_apis() { fn test_all_without_personal_apis() {
assert_eq!("personal,all,-personal".parse::<ApiSet>().unwrap(), ApiSet::List(vec![ assert_eq!("personal,all,-personal".parse::<ApiSet>().unwrap(), ApiSet::List(vec![
Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub,
Api::ParityAccounts, Api::ParityAccounts,
Api::ParitySet, Api::Signer, Api::ParitySet, Api::Signer,
].into_iter().collect())); ].into_iter().collect()));
@ -673,7 +720,7 @@ mod test {
#[test] #[test]
fn test_safe_parsing() { fn test_safe_parsing() {
assert_eq!("safe".parse::<ApiSet>().unwrap(), ApiSet::List(vec![ assert_eq!("safe".parse::<ApiSet>().unwrap(), ApiSet::List(vec![
Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub,
].into_iter().collect())); ].into_iter().collect()));
} }
} }

View File

@ -88,7 +88,7 @@ pub struct RunCmd {
pub warp_sync: bool, pub warp_sync: bool,
pub public_node: bool, pub public_node: bool,
pub acc_conf: AccountsConfig, pub acc_conf: AccountsConfig,
pub gas_pricer: GasPricerConfig, pub gas_pricer_conf: GasPricerConfig,
pub miner_extras: MinerExtras, pub miner_extras: MinerExtras,
pub update_policy: UpdatePolicy, pub update_policy: UpdatePolicy,
pub mode: Option<Mode>, pub mode: Option<Mode>,
@ -115,6 +115,7 @@ pub struct RunCmd {
pub serve_light: bool, pub serve_light: bool,
pub light: bool, pub light: bool,
pub no_persistent_txqueue: bool, pub no_persistent_txqueue: bool,
pub whisper: ::whisper::Config
} }
pub fn open_ui(ws_conf: &rpc::WsConfiguration, ui_conf: &rpc::UiConfiguration) -> Result<(), String> { pub fn open_ui(ws_conf: &rpc::WsConfiguration, ui_conf: &rpc::UiConfiguration) -> Result<(), String> {
@ -230,6 +231,17 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
// start on_demand service. // start on_demand service.
let on_demand = Arc::new(::light::on_demand::OnDemand::new(cache.clone())); let on_demand = Arc::new(::light::on_demand::OnDemand::new(cache.clone()));
let mut attached_protos = Vec::new();
let whisper_factory = if cmd.whisper.enabled {
let (whisper_net, whisper_factory) = ::whisper::setup(cmd.whisper.target_message_pool_size)
.map_err(|e| format!("Failed to initialize whisper: {}", e))?;
attached_protos.push(whisper_net);
whisper_factory
} else {
None
};
// set network path. // set network path.
net_conf.net_config_path = Some(db_dirs.network_path().to_string_lossy().into_owned()); net_conf.net_config_path = Some(db_dirs.network_path().to_string_lossy().into_owned());
let sync_params = LightSyncParams { let sync_params = LightSyncParams {
@ -238,6 +250,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
network_id: cmd.network_id.unwrap_or(spec.network_id()), network_id: cmd.network_id.unwrap_or(spec.network_id()),
subprotocol_name: ethsync::LIGHT_PROTOCOL, subprotocol_name: ethsync::LIGHT_PROTOCOL,
handlers: vec![on_demand.clone()], handlers: vec![on_demand.clone()],
attached_protos: attached_protos,
}; };
let light_sync = LightSync::new(sync_params).map_err(|e| format!("Error starting network: {}", e))?; let light_sync = LightSync::new(sync_params).map_err(|e| format!("Error starting network: {}", e))?;
let light_sync = Arc::new(light_sync); let light_sync = Arc::new(light_sync);
@ -318,6 +331,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) ->
fetch: fetch, fetch: fetch,
geth_compatibility: cmd.geth_compatibility, geth_compatibility: cmd.geth_compatibility,
remote: event_loop.remote(), remote: event_loop.remote(),
whisper_rpc: whisper_factory,
}); });
let dependencies = rpc::Dependencies { let dependencies = rpc::Dependencies {
@ -466,14 +480,16 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
// prepare account provider // prepare account provider
let account_provider = Arc::new(prepare_account_provider(&cmd.spec, &cmd.dirs, &spec.data_dir, cmd.acc_conf, &passwords)?); let account_provider = Arc::new(prepare_account_provider(&cmd.spec, &cmd.dirs, &spec.data_dir, cmd.acc_conf, &passwords)?);
// fetch service
let fetch = FetchClient::new().map_err(|e| format!("Error starting fetch client: {:?}", e))?;
// create miner // create miner
let initial_min_gas_price = cmd.gas_pricer.initial_min(); let initial_min_gas_price = cmd.gas_pricer_conf.initial_min();
let miner = Miner::new(cmd.miner_options, cmd.gas_pricer.into(), &spec, Some(account_provider.clone())); let miner = Miner::new(cmd.miner_options, cmd.gas_pricer_conf.to_gas_pricer(fetch.clone()), &spec, Some(account_provider.clone()));
miner.set_author(cmd.miner_extras.author); miner.set_author(cmd.miner_extras.author);
miner.set_gas_floor_target(cmd.miner_extras.gas_floor_target); miner.set_gas_floor_target(cmd.miner_extras.gas_floor_target);
miner.set_gas_ceil_target(cmd.miner_extras.gas_ceil_target); miner.set_gas_ceil_target(cmd.miner_extras.gas_ceil_target);
miner.set_extra_data(cmd.miner_extras.extra_data); miner.set_extra_data(cmd.miner_extras.extra_data);
miner.set_transactions_limit(cmd.miner_extras.transactions_limit);
miner.set_minimal_gas_price(initial_min_gas_price); miner.set_minimal_gas_price(initial_min_gas_price);
miner.recalibrate_minimal_gas_price(); miner.recalibrate_minimal_gas_price();
let engine_signer = cmd.miner_extras.engine_signer; let engine_signer = cmd.miner_extras.engine_signer;
@ -590,6 +606,18 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
.map_err(|e| format!("Stratum start error: {:?}", e))?; .map_err(|e| format!("Stratum start error: {:?}", e))?;
} }
let mut attached_protos = Vec::new();
let whisper_factory = if cmd.whisper.enabled {
let (whisper_net, whisper_factory) = ::whisper::setup(cmd.whisper.target_message_pool_size)
.map_err(|e| format!("Failed to initialize whisper: {}", e))?;
attached_protos.push(whisper_net);
whisper_factory
} else {
None
};
// create sync object // create sync object
let (sync_provider, manage_network, chain_notify) = modules::sync( let (sync_provider, manage_network, chain_notify) = modules::sync(
&mut hypervisor, &mut hypervisor,
@ -599,6 +627,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
snapshot_service.clone(), snapshot_service.clone(),
client.clone(), client.clone(),
&cmd.logger_config, &cmd.logger_config,
attached_protos,
).map_err(|e| format!("Sync error: {}", e))?; ).map_err(|e| format!("Sync error: {}", e))?;
service.add_notify(chain_notify.clone()); service.add_notify(chain_notify.clone());
@ -611,9 +640,6 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
// spin up event loop // spin up event loop
let event_loop = EventLoop::spawn(); let event_loop = EventLoop::spawn();
// fetch service
let fetch = FetchClient::new().map_err(|e| format!("Error starting fetch client: {:?}", e))?;
// the updater service // the updater service
let updater = Updater::new( let updater = Updater::new(
Arc::downgrade(&(service.client() as Arc<BlockChainClient>)), Arc::downgrade(&(service.client() as Arc<BlockChainClient>)),
@ -682,6 +708,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
ws_address: cmd.ws_conf.address(), ws_address: cmd.ws_conf.address(),
fetch: fetch.clone(), fetch: fetch.clone(),
remote: event_loop.remote(), remote: event_loop.remote(),
whisper_rpc: whisper_factory,
}); });
let dependencies = rpc::Dependencies { let dependencies = rpc::Dependencies {

View File

@ -57,6 +57,7 @@ pub fn main() {
snapshot_service: remote_snapshot.service().clone(), snapshot_service: remote_snapshot.service().clone(),
provider: remote_provider.service().clone(), provider: remote_provider.service().clone(),
network_config: service_config.net network_config: service_config.net
attached_protos: Vec::new(),
}).unwrap(); }).unwrap();
let _ = boot::main_thread(); let _ = boot::main_thread();

77
parity/whisper.rs Normal file
View File

@ -0,0 +1,77 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use std::io;
use ethsync::AttachedProtocol;
use parity_rpc::Metadata;
use parity_whisper::net::{self as whisper_net, PoolHandle, Network as WhisperNetwork};
use parity_whisper::rpc::{WhisperClient, FilterManager};
/// Whisper config.
#[derive(Debug, PartialEq, Eq)]
pub struct Config {
pub enabled: bool,
pub target_message_pool_size: usize,
}
impl Default for Config {
fn default() -> Self {
Config {
enabled: false,
target_message_pool_size: 10 * 1024 * 1024,
}
}
}
/// Factory for standard whisper RPC.
pub struct RpcFactory {
net: Arc<WhisperNetwork<Arc<FilterManager>>>,
manager: Arc<FilterManager>,
}
impl RpcFactory {
pub fn make_handler(&self) -> WhisperClient<PoolHandle, Metadata> {
WhisperClient::new(self.net.handle(), self.manager.clone())
}
}
/// Sets up whisper protocol and RPC handler.
///
/// Will target the given pool size.
#[cfg(not(feature = "ipc"))]
pub fn setup(target_pool_size: usize) -> io::Result<(AttachedProtocol, Option<RpcFactory>)> {
let manager = Arc::new(FilterManager::new()?);
let net = Arc::new(WhisperNetwork::new(target_pool_size, manager.clone()));
let proto = AttachedProtocol {
handler: net.clone() as Arc<_>,
packet_count: whisper_net::PACKET_COUNT,
versions: whisper_net::SUPPORTED_VERSIONS,
protocol_id: *b"shh",
};
let factory = RpcFactory { net: net, manager: manager };
Ok((proto, Some(factory)))
}
// TODO: make it possible to attach generic protocols in IPC.
#[cfg(feature = "ipc")]
pub fn setup(_pool: usize) -> (AttachedProtocol, Option<RpcFactory>) {
Ok((AttachedProtocol, None))
}

16
price-info/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
description = "Fetch current ETH price"
homepage = "http://parity.io"
license = "GPL-3.0"
name = "price-info"
version = "1.7.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
fetch = { path = "../util/fetch" }
futures = "0.1"
log = "0.3"
serde_json = "1.0"
[dev-dependencies]
ethcore-util = { path = "../util" }

31
price-info/src/lib.rs Normal file
View File

@ -0,0 +1,31 @@
// 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/>.
#![warn(missing_docs)]
//! A simple client to get the current ETH price using an external API.
extern crate futures;
extern crate serde_json;
#[macro_use]
extern crate log;
pub extern crate fetch;
mod price_info;
pub use price_info::*;

View File

@ -0,0 +1,222 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use std::cmp;
use std::fmt;
use std::io;
use std::io::Read;
use std::str::FromStr;
use fetch;
use fetch::{Client as FetchClient, Fetch};
use futures::Future;
use serde_json;
use serde_json::Value;
/// Current ETH price information.
#[derive(Debug)]
pub struct PriceInfo {
/// Current ETH price in USD.
pub ethusd: f32,
}
/// Price info error.
#[derive(Debug)]
pub enum Error {
/// The API returned an unexpected status code or content.
UnexpectedResponse(&'static str, String),
/// There was an error when trying to reach the API.
Fetch(fetch::Error),
/// IO error when reading API response.
Io(io::Error),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self { Error::Io(err) }
}
impl From<fetch::Error> for Error {
fn from(err: fetch::Error) -> Self { Error::Fetch(err) }
}
/// A client to get the current ETH price using an external API.
pub struct Client<F = FetchClient> {
api_endpoint: String,
fetch: F,
}
impl<F> fmt::Debug for Client<F> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("price_info::Client")
.field("api_endpoint", &self.api_endpoint)
.finish()
}
}
impl<F> cmp::PartialEq for Client<F> {
fn eq(&self, other: &Client<F>) -> bool {
self.api_endpoint == other.api_endpoint
}
}
impl<F: Fetch> Client<F> {
/// Creates a new instance of the `Client` given a `fetch::Client`.
pub fn new(fetch: F) -> Client<F> {
let api_endpoint = "http://api.etherscan.io/api?module=stats&action=ethprice".to_owned();
Client { api_endpoint, fetch }
}
/// 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)
.map_err(|err| Error::Fetch(err))
.and_then(move |mut response| {
let mut result = String::new();
response.read_to_string(&mut result)?;
if response.is_success() {
let value: Result<Value, _> = serde_json::from_str(&result);
if let Ok(v) = value {
let obj = v.pointer("/result/ethusd").and_then(|obj| {
match *obj {
Value::String(ref s) => FromStr::from_str(s).ok(),
_ => None,
}
});
if let Some(ethusd) = obj {
set_price(PriceInfo { ethusd });
return Ok(());
}
}
}
let status = response.status().canonical_reason().unwrap_or("unknown");
Err(Error::UnexpectedResponse(status, result))
})
.map_err(|err| {
warn!("Failed to auto-update latest ETH price: {:?}", err);
err
})
);
}
}
#[cfg(test)]
mod test {
extern crate ethcore_util as util;
use self::util::Mutex;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use fetch;
use fetch::Fetch;
use futures;
use futures::future::{Future, FutureResult};
use price_info::Client;
#[derive(Clone)]
struct FakeFetch(Option<String>, Arc<Mutex<u64>>);
impl Fetch for FakeFetch {
type Result = FutureResult<fetch::Response, fetch::Error>;
fn new() -> Result<Self, fetch::Error> where Self: Sized { Ok(FakeFetch(None, Default::default())) }
fn fetch_with_abort(&self, url: &str, _abort: fetch::Abort) -> Self::Result {
assert_eq!(url, "http://api.etherscan.io/api?module=stats&action=ethprice");
let mut val = self.1.lock();
*val = *val + 1;
if let Some(ref response) = self.0 {
let data = ::std::io::Cursor::new(response.clone());
futures::future::ok(fetch::Response::from_reader(data))
} else {
futures::future::ok(fetch::Response::not_found())
}
}
// this guarantees that the calls to price_info::Client::get will block for execution
fn forget<F, I, E>(&self, f: F) where
F: Future<Item=I, Error=E> + Send + 'static,
I: Send + 'static,
E: Send + 'static {
let _ = f.wait();
}
}
fn price_info_ok(response: &str) -> Client<FakeFetch> {
Client::new(FakeFetch(Some(response.to_owned()), Default::default()))
}
fn price_info_not_found() -> Client<FakeFetch> {
Client::new(FakeFetch::new().unwrap())
}
#[test]
fn should_get_price_info() {
// given
let response = r#"{
"status": "1",
"message": "OK",
"result": {
"ethbtc": "0.0891",
"ethbtc_timestamp": "1499894236",
"ethusd": "209.55",
"ethusd_timestamp": "1499894229"
}
}"#;
let price_info = price_info_ok(response);
// when
price_info.get(|price| {
// then
assert_eq!(price.ethusd, 209.55);
});
}
#[test]
fn should_not_call_set_price_if_response_is_malformed() {
// given
let response = "{}";
let price_info = price_info_ok(response);
let b = Arc::new(AtomicBool::new(false));
// when
let bb = b.clone();
price_info.get(move |_| {
bb.store(true, Ordering::Relaxed);
});
// then
assert_eq!(b.load(Ordering::Relaxed), false);
}
#[test]
fn should_not_call_set_price_if_response_is_invalid() {
// given
let price_info = price_info_not_found();
let b = Arc::new(AtomicBool::new(false));
// when
let bb = b.clone();
price_info.get(move |_| {
bb.store(true, Ordering::Relaxed);
});
// then
assert_eq!(b.load(Ordering::Relaxed), false);
}
}

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Parity JSON-RPC servers." description = "Parity JSON-RPC servers."
name = "parity-rpc" name = "parity-rpc"
version = "1.7.0" version = "1.8.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]

View File

@ -64,6 +64,7 @@ fn miner_service(spec: &Spec, accounts: Arc<AccountProvider>) -> Arc<Miner> {
tx_queue_strategy: PrioritizationStrategy::GasPriceOnly, tx_queue_strategy: PrioritizationStrategy::GasPriceOnly,
tx_queue_gas_limit: GasLimit::None, tx_queue_gas_limit: GasLimit::None,
tx_queue_banning: Banning::Disabled, tx_queue_banning: Banning::Disabled,
tx_queue_memory_limit: None,
pending_set: PendingSet::SealingOrElseQueue, pending_set: PendingSet::SealingOrElseQueue,
reseal_min_period: Duration::from_secs(0), reseal_min_period: Duration::from_secs(0),
reseal_max_period: Duration::from_secs(120), reseal_max_period: Duration::from_secs(120),

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Ethcore stratum lib" description = "Ethcore stratum lib"
name = "ethcore-stratum" name = "ethcore-stratum"
version = "1.7.0" version = "1.8.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs" build = "build.rs"

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Ethcore blockchain sync" description = "Ethcore blockchain sync"
name = "ethsync" name = "ethsync"
version = "1.7.0" version = "1.8.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs" build = "build.rs"

View File

@ -18,7 +18,7 @@ use std::sync::Arc;
use std::collections::{HashMap, BTreeMap}; use std::collections::{HashMap, BTreeMap};
use std::io; use std::io;
use util::Bytes; use util::Bytes;
use network::{NetworkProtocolHandler, NetworkService, NetworkContext, PeerId, ProtocolId, use network::{NetworkProtocolHandler, NetworkService, NetworkContext, HostInfo, PeerId, ProtocolId,
NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, NetworkError, NetworkConfiguration as BasicNetworkConfiguration, NonReservedPeerMode, NetworkError,
AllowIP as NetworkAllowIP}; AllowIP as NetworkAllowIP};
use util::{U256, H256, H512}; use util::{U256, H256, H512};
@ -163,6 +163,44 @@ impl From<light_net::Status> for PipProtocolInfo {
} }
} }
/// Configuration to attach alternate protocol handlers.
/// Only works when IPC is disabled.
#[cfg(not(feature = "ipc"))]
pub struct AttachedProtocol {
/// The protocol handler in question.
pub handler: Arc<NetworkProtocolHandler + Send + Sync>,
/// 3-character ID for the protocol.
pub protocol_id: ProtocolId,
/// Packet count.
pub packet_count: u8,
/// Supported versions.
pub versions: &'static [u8],
}
/// Attached protocol: disabled in IPC mode.
#[cfg(feature = "ipc")]
#[cfg_attr(feature = "ipc", derive(Binary))]
pub struct AttachedProtocol;
impl AttachedProtocol {
#[cfg(feature = "ipc")]
fn register(&self, network: &NetworkService) {
let res = network.register_protocol(
self.handler.clone(),
self.protocol_id,
self.packet_count,
self.versions
);
if let Err(e) = res {
warn!(target: "sync","Error attaching protocol {:?}", protocol_id);
}
}
#[cfg(not(feature = "ipc"))]
fn register(&self, _network: &NetworkService) {}
}
/// EthSync initialization parameters. /// EthSync initialization parameters.
#[cfg_attr(feature = "ipc", derive(Binary))] #[cfg_attr(feature = "ipc", derive(Binary))]
pub struct Params { pub struct Params {
@ -176,6 +214,8 @@ pub struct Params {
pub provider: Arc<::light::Provider>, pub provider: Arc<::light::Provider>,
/// Network layer configuration. /// Network layer configuration.
pub network_config: NetworkConfiguration, pub network_config: NetworkConfiguration,
/// Other protocols to attach.
pub attached_protos: Vec<AttachedProtocol>,
} }
/// Ethereum network protocol handler /// Ethereum network protocol handler
@ -186,6 +226,8 @@ pub struct EthSync {
eth_handler: Arc<SyncProtocolHandler>, eth_handler: Arc<SyncProtocolHandler>,
/// Light (pip) protocol handler /// Light (pip) protocol handler
light_proto: Option<Arc<LightProtocol>>, light_proto: Option<Arc<LightProtocol>>,
/// Other protocols to attach.
attached_protos: Vec<AttachedProtocol>,
/// The main subprotocol name /// The main subprotocol name
subprotocol_name: [u8; 3], subprotocol_name: [u8; 3],
/// Light subprotocol name. /// Light subprotocol name.
@ -243,6 +285,7 @@ impl EthSync {
light_proto: light_proto, light_proto: light_proto,
subprotocol_name: params.config.subprotocol_name, subprotocol_name: params.config.subprotocol_name,
light_subprotocol_name: params.config.light_subprotocol_name, light_subprotocol_name: params.config.light_subprotocol_name,
attached_protos: params.attached_protos,
}); });
Ok(sync) Ok(sync)
@ -307,7 +350,7 @@ struct SyncProtocolHandler {
} }
impl NetworkProtocolHandler for SyncProtocolHandler { impl NetworkProtocolHandler for SyncProtocolHandler {
fn initialize(&self, io: &NetworkContext) { fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) {
if io.subprotocol_name() != WARP_SYNC_PROTOCOL_ID { if io.subprotocol_name() != WARP_SYNC_PROTOCOL_ID {
io.register_timer(0, 1000).expect("Error registering sync timer"); io.register_timer(0, 1000).expect("Error registering sync timer");
} }
@ -400,6 +443,9 @@ impl ChainNotify for EthSync {
self.network.register_protocol(light_proto, self.light_subprotocol_name, ::light::net::PACKET_COUNT, ::light::net::PROTOCOL_VERSIONS) self.network.register_protocol(light_proto, self.light_subprotocol_name, ::light::net::PACKET_COUNT, ::light::net::PROTOCOL_VERSIONS)
.unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e)); .unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e));
} }
// register any attached protocols.
for proto in &self.attached_protos { proto.register(&self.network) }
} }
fn stop(&self) { fn stop(&self) {
@ -676,12 +722,15 @@ pub struct LightSyncParams<L> {
pub subprotocol_name: [u8; 3], pub subprotocol_name: [u8; 3],
/// Other handlers to attach. /// Other handlers to attach.
pub handlers: Vec<Arc<LightHandler>>, pub handlers: Vec<Arc<LightHandler>>,
/// Other subprotocols to run.
pub attached_protos: Vec<AttachedProtocol>,
} }
/// Service for light synchronization. /// Service for light synchronization.
pub struct LightSync { pub struct LightSync {
proto: Arc<LightProtocol>, proto: Arc<LightProtocol>,
sync: Arc<::light_sync::SyncInfo + Sync + Send>, sync: Arc<::light_sync::SyncInfo + Sync + Send>,
attached_protos: Vec<AttachedProtocol>,
network: NetworkService, network: NetworkService,
subprotocol_name: [u8; 3], subprotocol_name: [u8; 3],
network_id: u64, network_id: u64,
@ -724,6 +773,7 @@ impl LightSync {
Ok(LightSync { Ok(LightSync {
proto: light_proto, proto: light_proto,
sync: sync, sync: sync,
attached_protos: params.attached_protos,
network: service, network: service,
subprotocol_name: params.subprotocol_name, subprotocol_name: params.subprotocol_name,
network_id: params.network_id, network_id: params.network_id,
@ -775,6 +825,8 @@ impl ManageNetwork for LightSync {
self.network.register_protocol(light_proto, self.subprotocol_name, ::light::net::PACKET_COUNT, ::light::net::PROTOCOL_VERSIONS) self.network.register_protocol(light_proto, self.subprotocol_name, ::light::net::PACKET_COUNT, ::light::net::PROTOCOL_VERSIONS)
.unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e)); .unwrap_or_else(|e| warn!("Error registering light client protocol: {:?}", e));
for proto in &self.attached_protos { proto.register(&self.network) }
} }
fn stop_network(&self) { fn stop_network(&self) {

View File

@ -24,4 +24,3 @@ esac
. ./scripts/targets.sh . ./scripts/targets.sh
cargo test -j 8 $OPTIONS --features "$FEATURES" $TARGETS $1 \ cargo test -j 8 $OPTIONS --features "$FEATURES" $TARGETS $1 \

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Parity Updater Service." description = "Parity Updater Service."
name = "parity-updater" name = "parity-updater"
version = "1.7.0" version = "1.8.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs" build = "build.rs"

View File

@ -3,7 +3,7 @@ description = "Ethcore utility library"
homepage = "http://parity.io" homepage = "http://parity.io"
license = "GPL-3.0" license = "GPL-3.0"
name = "ethcore-util" name = "ethcore-util"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs" build = "build.rs"

View File

@ -13,7 +13,7 @@ use std::cmp::{min, Ordering};
use std::ops::{Deref, DerefMut, BitXor, BitAnd, BitOr, IndexMut, Index}; use std::ops::{Deref, DerefMut, BitXor, BitAnd, BitOr, IndexMut, Index};
use std::hash::{Hash, Hasher, BuildHasherDefault}; use std::hash::{Hash, Hasher, BuildHasherDefault};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use rand::Rng; use rand::{Rand, Rng};
use rand::os::OsRng; use rand::os::OsRng;
use rustc_hex::{FromHex, FromHexError}; use rustc_hex::{FromHex, FromHexError};
use bigint::U256; use bigint::U256;
@ -91,7 +91,7 @@ macro_rules! impl_hash {
/// Assign self have a cryptographically random value. /// Assign self have a cryptographically random value.
pub fn randomize(&mut self) { pub fn randomize(&mut self) {
let mut rng = OsRng::new().unwrap(); let mut rng = OsRng::new().unwrap();
rng.fill_bytes(&mut self.0); *self= $from::rand(&mut rng);
} }
/// Get the size of this object in bytes. /// Get the size of this object in bytes.
@ -360,6 +360,14 @@ macro_rules! impl_hash {
$from::from_slice(s) $from::from_slice(s)
} }
} }
impl Rand for $from {
fn rand<R: Rng>(r: &mut R) -> Self {
let mut hash = $from::new();
r.fill_bytes(&mut hash.0);
hash
}
}
} }
} }

View File

@ -61,6 +61,14 @@ pub trait Fetch: Clone + Send + Sync + 'static {
f.boxed() f.boxed()
} }
/// Spawn the future in context of this `Fetch` thread pool as "fire and forget", i.e. dropping this future without
/// canceling the underlying future.
/// Implementation is optional.
fn forget<F, I, E>(&self, _: F) where
F: Future<Item=I, Error=E> + Send + 'static,
I: Send + 'static,
E: Send + 'static {}
/// Fetch URL and get a future for the result. /// Fetch URL and get a future for the result.
/// Supports aborting the request in the middle of execution. /// Supports aborting the request in the middle of execution.
fn fetch_with_abort(&self, url: &str, abort: Abort) -> Self::Result; fn fetch_with_abort(&self, url: &str, abort: Abort) -> Self::Result;
@ -149,6 +157,14 @@ impl Fetch for Client {
self.pool.spawn(f).boxed() self.pool.spawn(f).boxed()
} }
fn forget<F, I, E>(&self, f: F) where
F: Future<Item=I, Error=E> + Send + 'static,
I: Send + 'static,
E: Send + 'static,
{
self.pool.spawn(f).forget()
}
fn fetch_with_abort(&self, url: &str, abort: Abort) -> Self::Result { fn fetch_with_abort(&self, url: &str, abort: Abort) -> Self::Result {
debug!(target: "fetch", "Fetching from: {:?}", url); debug!(target: "fetch", "Fetching from: {:?}", url);

View File

@ -3,7 +3,7 @@ description = "Ethcore IO library"
homepage = "http://parity.io" homepage = "http://parity.io"
license = "GPL-3.0" license = "GPL-3.0"
name = "ethcore-io" name = "ethcore-io"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]

View File

@ -3,7 +3,7 @@ description = "Ethcore network library"
homepage = "http://parity.io" homepage = "http://parity.io"
license = "GPL-3.0" license = "GPL-3.0"
name = "ethcore-network" name = "ethcore-network"
version = "1.7.0" version = "1.8.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]

View File

@ -1099,7 +1099,10 @@ impl IoHandler<NetworkIoMessage> for Host {
} => { } => {
let h = handler.clone(); let h = handler.clone();
let reserved = self.reserved_nodes.read(); let reserved = self.reserved_nodes.read();
h.initialize(&NetworkContext::new(io, *protocol, None, self.sessions.clone(), &reserved)); h.initialize(
&NetworkContext::new(io, *protocol, None, self.sessions.clone(), &reserved),
&*self.info.read(),
);
self.handlers.write().insert(*protocol, h); self.handlers.write().insert(*protocol, h);
let mut info = self.info.write(); let mut info = self.info.write();
for v in versions { for v in versions {

View File

@ -26,7 +26,7 @@
//! struct MyHandler; //! struct MyHandler;
//! //!
//! impl NetworkProtocolHandler for MyHandler { //! impl NetworkProtocolHandler for MyHandler {
//! fn initialize(&self, io: &NetworkContext) { //! fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) {
//! io.register_timer(0, 1000); //! io.register_timer(0, 1000);
//! } //! }
//! //!
@ -98,13 +98,13 @@ mod ip_utils;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use host::{PeerId, PacketId, ProtocolId, NetworkContext, NetworkIoMessage, NetworkConfiguration}; pub use host::{HostInfo, PeerId, PacketId, ProtocolId, NetworkContext, NetworkIoMessage, NetworkConfiguration};
pub use service::NetworkService; pub use service::NetworkService;
pub use error::NetworkError; pub use error::NetworkError;
pub use stats::NetworkStats; pub use stats::NetworkStats;
pub use session::SessionInfo; pub use session::SessionInfo;
use io::TimerToken; pub use io::TimerToken;
pub use node_table::{is_valid_node_url, NodeId}; pub use node_table::{is_valid_node_url, NodeId};
const PROTOCOL_VERSION: u32 = 4; const PROTOCOL_VERSION: u32 = 4;
@ -114,7 +114,7 @@ const PROTOCOL_VERSION: u32 = 4;
/// `Message` is the type for message data. /// `Message` is the type for message data.
pub trait NetworkProtocolHandler: Sync + Send { pub trait NetworkProtocolHandler: Sync + Send {
/// Initialize the handler /// Initialize the handler
fn initialize(&self, _io: &NetworkContext) {} fn initialize(&self, _io: &NetworkContext, _host_info: &HostInfo) {}
/// Called when new network packet received. /// Called when new network packet received.
fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]); fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]);
/// Called when new peer is connected. Only called when peer supports the same protocol. /// Called when new peer is connected. Only called when peer supports the same protocol.

View File

@ -59,7 +59,7 @@ impl TestProtocol {
} }
impl NetworkProtocolHandler for TestProtocol { impl NetworkProtocolHandler for TestProtocol {
fn initialize(&self, io: &NetworkContext) { fn initialize(&self, io: &NetworkContext, _host_info: &HostInfo) {
io.register_timer(0, 10).unwrap(); io.register_timer(0, 10).unwrap();
} }

32
whisper/Cargo.toml Normal file
View File

@ -0,0 +1,32 @@
[package]
name = "parity-whisper"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Whisper Protocol implementation for Parity"
[dependencies]
bitflags = "0.9"
byteorder = "1.0.0"
ethcore-bigint = { path = "../util/bigint" }
ethcore-network = { path = "../util/network" }
ethcrypto = { path = "../ethcrypto" }
ethkey = { path = "../ethkey" }
futures = "0.1"
hex = "0.2"
log = "0.3"
ordered-float = "0.5"
parking_lot = "0.4"
rand = "0.3"
ring = "0.9.5"
rlp = { path = "../util/rlp" }
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
slab = "0.3"
smallvec = "0.4"
time = "0.1"
tiny-keccak = "1.2.1"
jsonrpc-core = { 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" }

3
whisper/README.md Normal file
View File

@ -0,0 +1,3 @@
# Whisper
Implementation of Whisper based on the Whisper-v2 PoC.

59
whisper/src/lib.rs Normal file
View File

@ -0,0 +1,59 @@
// 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/>.
//! Whisper P2P messaging system as a DevP2P subprotocol, with RPC and Rust
//! interface.
extern crate byteorder;
extern crate ethcore_bigint as bigint;
extern crate ethcore_network as network;
extern crate ethcrypto;
extern crate ethkey;
extern crate futures;
extern crate hex;
extern crate ordered_float;
extern crate parking_lot;
extern crate rand;
extern crate rlp;
extern crate ring;
extern crate serde;
extern crate serde_json;
extern crate slab;
extern crate smallvec;
extern crate time;
extern crate tiny_keccak;
extern crate jsonrpc_core;
extern crate jsonrpc_pubsub;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate log;
#[macro_use]
extern crate jsonrpc_macros;
#[macro_use]
extern crate serde_derive;
pub use self::message::Message;
pub use self::net::{Network, MessageHandler};
pub mod message;
pub mod net;
pub mod rpc;

479
whisper/src/message.rs Normal file
View File

@ -0,0 +1,479 @@
// 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/>.
//! Whisper message parsing, handlers, and construction.
use std::fmt;
use std::time::{self, SystemTime, Duration};
use bigint::hash::{H256, H512};
use rlp::{self, DecoderError, RlpStream, UntrustedRlp};
use smallvec::SmallVec;
use tiny_keccak::{keccak256, Keccak};
/// Work-factor proved. Takes 3 parameters: size of message, time to live,
/// and hash.
///
/// Panics if size or TTL is zero.
pub fn work_factor_proved(size: u64, ttl: u64, hash: H256) -> f64 {
assert!(size != 0 && ttl != 0);
let leading_zeros = {
let leading_zeros = hash.iter().take_while(|&&x| x == 0).count();
(leading_zeros * 8) + hash.get(leading_zeros + 1).map_or(0, |b| b.leading_zeros() as usize)
};
let spacetime = size as f64 * ttl as f64;
(1u64 << leading_zeros) as f64 / spacetime
}
/// A topic of a message.
#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Topic(pub [u8; 4]);
impl From<[u8; 4]> for Topic {
fn from(x: [u8; 4]) -> Self {
Topic(x)
}
}
impl Topic {
/// set up to three bits in the 64-byte bloom passed.
///
/// this takes 3 sets of 9 bits, treating each as an index in the range
/// 0..512 into the bloom and setting the corresponding bit in the bloom to 1.
pub fn bloom_into(&self, bloom: &mut H512) {
let mut set_bit = |idx: usize| {
let idx = idx & 511;
bloom[idx / 8] |= 1 << idx % 8;
};
let data = &self.0;
let mut combined = ((data[0] as usize) << 24) |
((data[1] as usize) << 16) |
((data[2] as usize) << 8) |
data[3] as usize;
// take off the last 5 bits as we only use 27.
combined >>= 5;
set_bit(combined);
set_bit(combined >> 9);
set_bit(combined >> 18);
}
/// Get bloom for single topic.
pub fn bloom(&self) -> H512 {
let mut bloom = Default::default();
self.bloom_into(&mut bloom);
bloom
}
}
impl rlp::Encodable for Topic {
fn rlp_append(&self, s: &mut RlpStream) {
s.encoder().encode_value(&self.0);
}
}
impl rlp::Decodable for Topic {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
use std::cmp;
rlp.decoder().decode_value(|bytes| match bytes.len().cmp(&4) {
cmp::Ordering::Less => Err(DecoderError::RlpIsTooShort),
cmp::Ordering::Greater => Err(DecoderError::RlpIsTooBig),
cmp::Ordering::Equal => {
let mut t = [0u8; 4];
t.copy_from_slice(bytes);
Ok(Topic(t))
}
})
}
}
/// Calculate union of blooms for given topics.
pub fn bloom_topics(topics: &[Topic]) -> H512 {
let mut bloom = H512::default();
for topic in topics {
topic.bloom_into(&mut bloom);
}
bloom
}
/// Message errors.
#[derive(Debug)]
pub enum Error {
Decoder(DecoderError),
LivesTooLong,
IssuedInFuture,
ZeroTTL,
}
impl From<DecoderError> for Error {
fn from(err: DecoderError) -> Self {
Error::Decoder(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Decoder(ref err) => write!(f, "Failed to decode message: {}", err),
Error::LivesTooLong => write!(f, "Message claims to be issued before the unix epoch."),
Error::IssuedInFuture => write!(f, "Message issued in future."),
Error::ZeroTTL => write!(f, "Message live for zero time."),
}
}
}
// Raw envelope struct.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Envelope {
/// Expiry timestamp
pub expiry: u64,
/// Time-to-live in seconds
pub ttl: u64,
/// series of 4-byte topics.
pub topics: SmallVec<[Topic; 4]>,
/// The message contained within.
pub data: Vec<u8>,
/// Arbitrary value used to target lower PoW hash.
pub nonce: u64,
}
impl Envelope {
fn proving_hash(&self) -> H256 {
use byteorder::{BigEndian, ByteOrder};
let mut buf = [0; 32];
let mut stream = RlpStream::new_list(4);
stream.append(&self.expiry)
.append(&self.ttl)
.append_list(&self.topics)
.append(&self.data);
let mut digest = Keccak::new_keccak256();
digest.update(&*stream.drain());
digest.update(&{
let mut nonce_bytes = [0u8; 8];
BigEndian::write_u64(&mut nonce_bytes, self.nonce);
nonce_bytes
});
digest.finalize(&mut buf);
H256(buf)
}
}
impl rlp::Encodable for Envelope {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(5)
.append(&self.expiry)
.append(&self.ttl)
.append_list(&self.topics)
.append(&self.data)
.append(&self.nonce);
}
}
impl rlp::Decodable for Envelope {
fn decode(rlp: &UntrustedRlp) -> Result<Self, DecoderError> {
if rlp.item_count()? != 5 { return Err(DecoderError::RlpIncorrectListLen) }
Ok(Envelope {
expiry: rlp.val_at(0)?,
ttl: rlp.val_at(1)?,
topics: rlp.at(2)?.iter().map(|x| x.as_val()).collect::<Result<_, _>>()?,
data: rlp.val_at(3)?,
nonce: rlp.val_at(4)?,
})
}
}
/// Message creation parameters.
/// Pass this to `Message::create` to make a message.
pub struct CreateParams {
/// time-to-live in seconds.
pub ttl: u64,
/// payload data.
pub payload: Vec<u8>,
/// Topics.
pub topics: Vec<Topic>,
/// How many milliseconds to spend proving work.
pub work: u64,
}
/// A whisper message. This is a checked message carrying around metadata.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Message {
envelope: Envelope,
bloom: H512,
hash: H256,
encoded_size: usize,
}
impl Message {
/// Create a message from creation parameters.
/// Panics if TTL is 0.
pub fn create(params: CreateParams) -> Self {
use byteorder::{BigEndian, ByteOrder};
use rand::{Rng, SeedableRng, XorShiftRng};
let mut rng = {
let mut thread_rng = ::rand::thread_rng();
XorShiftRng::from_seed(thread_rng.gen::<[u32; 4]>())
};
assert!(params.ttl > 0);
let expiry = {
let after_mining = SystemTime::now() + Duration::from_millis(params.work);
let since_epoch = after_mining.duration_since(time::UNIX_EPOCH)
.expect("time after now is after unix epoch; qed");
// round up the sub-second to next whole second.
since_epoch.as_secs() + if since_epoch.subsec_nanos() == 0 { 0 } else { 1 }
};
let start_digest = {
let mut stream = RlpStream::new_list(4);
stream.append(&expiry)
.append(&params.ttl)
.append_list(&params.topics)
.append(&params.payload);
let mut digest = Keccak::new_keccak256();
digest.update(&*stream.drain());
digest
};
let mut buf = [0; 32];
let mut try_nonce = move |nonce: &[u8; 8]| {
let mut digest = start_digest.clone();
digest.update(&nonce[..]);
digest.finalize(&mut buf[..]);
buf.clone()
};
let mut nonce: [u8; 8] = rng.gen();
let mut best_found = try_nonce(&nonce);
let start = ::time::precise_time_ns();
while ::time::precise_time_ns() <= start + params.work * 1_000_000 {
let temp_nonce = rng.gen();
let hash = try_nonce(&temp_nonce);
if hash < best_found {
nonce = temp_nonce;
best_found = hash;
}
}
let envelope = Envelope {
expiry: expiry,
ttl: params.ttl,
topics: params.topics.into_iter().collect(),
data: params.payload,
nonce: BigEndian::read_u64(&nonce[..]),
};
debug_assert_eq!(H256(best_found.clone()), envelope.proving_hash());
let encoded = ::rlp::encode(&envelope);
Message::from_components(
envelope,
encoded.len(),
H256(keccak256(&encoded)),
SystemTime::now(),
).expect("Message generated here known to be valid; qed")
}
/// Decode message from RLP and check for validity against system time.
pub fn decode(rlp: UntrustedRlp, now: SystemTime) -> Result<Self, Error> {
let envelope: Envelope = rlp.as_val()?;
let encoded_size = rlp.as_raw().len();
let hash = H256(keccak256(rlp.as_raw()));
Message::from_components(envelope, encoded_size, hash, now)
}
// create message from envelope, hash, and encoded size.
// does checks for validity.
fn from_components(envelope: Envelope, size: usize, hash: H256, now: SystemTime)
-> Result<Self, Error>
{
const LEEWAY_SECONDS: u64 = 2;
if envelope.expiry <= envelope.ttl { return Err(Error::LivesTooLong) }
if envelope.ttl == 0 { return Err(Error::ZeroTTL) }
let issue_time_adjusted = Duration::from_secs(
(envelope.expiry - envelope.ttl).saturating_sub(LEEWAY_SECONDS)
);
if time::UNIX_EPOCH + issue_time_adjusted > now {
return Err(Error::IssuedInFuture);
}
// other validity checks?
let bloom = bloom_topics(&envelope.topics);
Ok(Message {
envelope: envelope,
bloom: bloom,
hash: hash,
encoded_size: size,
})
}
/// Get a reference to the envelope.
pub fn envelope(&self) -> &Envelope {
&self.envelope
}
/// Get the encoded size of the envelope.
pub fn encoded_size(&self) -> usize {
self.encoded_size
}
/// Get a uniquely identifying hash for the message.
pub fn hash(&self) -> &H256 {
&self.hash
}
/// Get the bloom filter of the topics
pub fn bloom(&self) -> &H512 {
&self.bloom
}
/// Get the work proved by the hash.
pub fn work_proved(&self) -> f64 {
let proving_hash = self.envelope.proving_hash();
work_factor_proved(self.encoded_size as _, self.envelope.ttl, proving_hash)
}
/// Get the expiry time.
pub fn expiry(&self) -> SystemTime {
time::UNIX_EPOCH + Duration::from_secs(self.envelope.expiry)
}
/// Get the topics.
pub fn topics(&self) -> &[Topic] {
&self.envelope.topics
}
/// Get the message data.
pub fn data(&self) -> &[u8] {
&self.envelope.data
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{self, Duration, SystemTime};
use rlp::UntrustedRlp;
fn unix_time(x: u64) -> SystemTime {
time::UNIX_EPOCH + Duration::from_secs(x)
}
#[test]
fn create_message() {
let _ = Message::create(CreateParams {
ttl: 100,
payload: vec![1, 2, 3, 4],
topics: Vec::new(),
work: 50,
});
}
#[test]
fn round_trip() {
let envelope = Envelope {
expiry: 100_000,
ttl: 30,
data: vec![9; 256],
topics: Default::default(),
nonce: 1010101,
};
let encoded = ::rlp::encode(&envelope);
let decoded = ::rlp::decode(&encoded);
assert_eq!(envelope, decoded)
}
#[test]
fn passes_checks() {
let envelope = Envelope {
expiry: 100_000,
ttl: 30,
data: vec![9; 256],
topics: Default::default(),
nonce: 1010101,
};
let encoded = ::rlp::encode(&envelope);
for i in 0..30 {
let now = unix_time(100_000 - i);
Message::decode(UntrustedRlp::new(&*encoded), now).unwrap();
}
}
#[test]
#[should_panic]
fn future_message() {
let envelope = Envelope {
expiry: 100_000,
ttl: 30,
data: vec![9; 256],
topics: Default::default(),
nonce: 1010101,
};
let encoded = ::rlp::encode(&envelope);
let now = unix_time(100_000 - 1_000);
Message::decode(UntrustedRlp::new(&*encoded), now).unwrap();
}
#[test]
#[should_panic]
fn pre_epoch() {
let envelope = Envelope {
expiry: 100_000,
ttl: 200_000,
data: vec![9; 256],
topics: Default::default(),
nonce: 1010101,
};
let encoded = ::rlp::encode(&envelope);
let now = unix_time(95_000);
Message::decode(UntrustedRlp::new(&*encoded), now).unwrap();
}
}

638
whisper/src/net.rs Normal file
View File

@ -0,0 +1,638 @@
// 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/>.
//! Whisper messaging system as a DevP2P subprotocol.
use std::collections::{HashMap, HashSet};
use std::cmp::Ordering;
use std::fmt;
use std::time::{Duration, SystemTime};
use std::sync::Arc;
use bigint::hash::{H256, H512};
use network::{HostInfo, NetworkContext, NetworkError, NodeId, PeerId, TimerToken};
use ordered_float::OrderedFloat;
use parking_lot::{Mutex, RwLock};
use rlp::{DecoderError, RlpStream, UntrustedRlp};
use message::{Message, Error as MessageError};
const RALLY_TOKEN: TimerToken = 1;
const RALLY_TIMEOUT_MS: u64 = 750; // supposed to be at least once per second.
const PROTOCOL_VERSION: usize = 2;
/// Supported protocol versions.
pub const SUPPORTED_VERSIONS: &'static [u8] = &[PROTOCOL_VERSION as u8];
// maximum tolerated delay between messages packets.
const MAX_TOLERATED_DELAY_MS: u64 = 2000;
/// Number of packets.
pub const PACKET_COUNT: u8 = 3;
mod packet {
pub const STATUS: u8 = 0;
pub const MESSAGES: u8 = 1;
pub const TOPIC_FILTER: u8 = 2;
}
/// Handles messages within a single packet.
pub trait MessageHandler: Send + Sync {
/// Evaluate the message and handle it.
///
/// The same message will not be passed twice.
/// Heavy handling should be done asynchronously.
/// If there is a significant overhead in this thread, then an attacker
/// can determine which kinds of messages we are listening for.
fn handle_messages(&self, message: &[Message]);
}
// errors in importing a whisper message.
#[derive(Debug)]
enum Error {
Decoder(DecoderError),
Network(NetworkError),
Message(MessageError),
UnknownPacket(u8),
UnknownPeer(PeerId),
ProtocolVersionMismatch(usize),
SameNodeKey,
UnexpectedMessage,
}
impl From<DecoderError> for Error {
fn from(err: DecoderError) -> Self {
Error::Decoder(err)
}
}
impl From<NetworkError> for Error {
fn from(err: NetworkError) -> Self {
Error::Network(err)
}
}
impl From<MessageError> for Error {
fn from(err: MessageError) -> Self {
Error::Message(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Decoder(ref err) => write!(f, "Failed to decode packet: {}", err),
Error::Network(ref err) => write!(f, "Network error: {}", err),
Error::Message(ref err) => write!(f, "Error decoding message: {}", err),
Error::UnknownPacket(ref id) => write!(f, "Unknown packet kind: {}", id),
Error::UnknownPeer(ref id) => write!(f, "Message received from unknown peer: {}", id),
Error::ProtocolVersionMismatch(ref proto) =>
write!(f, "Unknown protocol version: {}", proto),
Error::UnexpectedMessage => write!(f, "Unexpected message."),
Error::SameNodeKey => write!(f, "Peer and us have same node key."),
}
}
}
// sorts by work proved, descending.
#[derive(PartialEq, Eq)]
struct SortedEntry {
slab_id: usize,
work_proved: OrderedFloat<f64>,
expiry: SystemTime,
}
impl Ord for SortedEntry {
fn cmp(&self, other: &SortedEntry) -> Ordering {
self.work_proved.cmp(&other.work_proved)
}
}
impl PartialOrd for SortedEntry {
fn partial_cmp(&self, other: &SortedEntry) -> Option<Ordering> {
Some(self.cmp(other))
}
}
// stores messages by two metrics: expiry and PoW rating
// when full, will accept messages above the minimum stored.
struct Messages {
slab: ::slab::Slab<Message>,
sorted: Vec<SortedEntry>,
known: HashSet<H256>,
removed_hashes: Vec<H256>,
cumulative_size: usize,
ideal_size: usize,
}
impl Messages {
fn new(ideal_size: usize) -> Self {
Messages {
slab: ::slab::Slab::with_capacity(0),
sorted: Vec::new(),
known: HashSet::new(),
removed_hashes: Vec::new(),
cumulative_size: 0,
ideal_size: ideal_size,
}
}
// reserve space for additional elements.
fn reserve(&mut self, additional: usize) {
self.slab.reserve_exact(additional);
self.sorted.reserve(additional);
self.known.reserve(additional);
}
// whether a message is not known and within the bounds of PoW.
fn may_accept(&self, message: &Message) -> bool {
!self.known.contains(message.hash()) && {
self.sorted.last().map_or(true, |entry| {
let work_proved = OrderedFloat(message.work_proved());
OrderedFloat(self.slab[entry.slab_id].work_proved()) < work_proved
})
}
}
// insert a message into the store. for best performance,
// call `reserve` before inserting a bunch.
//
fn insert(&mut self, message: Message) -> bool {
if !self.known.insert(message.hash().clone()) { return false }
let work_proved = OrderedFloat(message.work_proved());
// pop off entries by low PoW until we have enough space for the higher
// PoW message being inserted.
let size_upon_insertion = self.cumulative_size + message.encoded_size();
if size_upon_insertion >= self.ideal_size {
let diff = size_upon_insertion - self.ideal_size;
let mut found_diff = 0;
for entry in self.sorted.iter().rev() {
if found_diff >= diff { break }
// if we encounter a message with at least the PoW we're looking
// at, don't push that message out.
if entry.work_proved >= work_proved { return false }
found_diff += self.slab[entry.slab_id].encoded_size();
}
// message larger than ideal size.
if found_diff < diff { return false }
while found_diff > 0 {
let entry = self.sorted.pop()
.expect("found_diff built by traversing entries; therefore that many entries exist; qed");
let message = self.slab.remove(entry.slab_id)
.expect("sorted entry slab IDs always filled; qed");
found_diff -= message.encoded_size();
self.cumulative_size -= message.encoded_size();
self.known.remove(message.hash());
self.removed_hashes.push(message.hash().clone());
}
}
let expiry = message.expiry();
self.cumulative_size += message.encoded_size();
if !self.slab.has_available() { self.slab.reserve_exact(1) }
let id = self.slab.insert(message).expect("just ensured enough space in slab; qed");
let sorted_entry = SortedEntry {
slab_id: id,
work_proved: work_proved,
expiry: expiry,
};
match self.sorted.binary_search(&sorted_entry) {
Ok(idx) | Err(idx) => self.sorted.insert(idx, sorted_entry),
}
true
}
// prune expired messages, and then prune low proof-of-work messages
// until below ideal size.
fn prune(&mut self, now: SystemTime) -> Vec<H256> {
{
let slab = &mut self.slab;
let known = &mut self.known;
let cumulative_size = &mut self.cumulative_size;
let ideal_size = &self.ideal_size;
let removed = &mut self.removed_hashes;
// first pass, we look just at expired entries.
let all_expired = self.sorted.iter()
.filter(|entry| entry.expiry <= now)
.map(|x| (true, x));
// second pass, we look at entries which aren't expired but in order
// by PoW
let low_proof = self.sorted.iter().rev()
.filter(|entry| entry.expiry > now)
.map(|x| (false, x));
for (is_expired, entry) in all_expired.chain(low_proof) {
// break once we've removed all expired entries
// or have taken enough low-work entries.
if !is_expired && *cumulative_size <= *ideal_size {
break
}
let message = slab.remove(entry.slab_id)
.expect("references to ID kept upon creation; only destroyed upon removal; qed");
known.remove(message.hash());
removed.push(message.hash().clone());
*cumulative_size -= message.encoded_size();
}
}
// clear all the sorted entries we removed from slab.
let slab = &self.slab;
self.sorted.retain(|entry| slab.contains(entry.slab_id));
::std::mem::replace(&mut self.removed_hashes, Vec::new())
}
fn iter(&self) -> ::slab::Iter<Message, usize> {
self.slab.iter()
}
fn is_full(&self) -> bool {
self.cumulative_size >= self.ideal_size
}
fn status(&self) -> PoolStatus {
PoolStatus {
required_pow: if self.is_full() {
self.sorted.last().map(|entry| entry.work_proved.0)
} else {
None
},
message_count: self.sorted.len(),
cumulative_size: self.cumulative_size,
target_size: self.ideal_size,
}
}
}
enum State {
Unconfirmed(SystemTime), // awaiting status packet.
TheirTurn(SystemTime), // it has been their turn to send since stored time.
OurTurn,
}
struct Peer {
node_key: NodeId,
state: State,
known_messages: HashSet<H256>,
topic_filter: Option<H512>,
}
impl Peer {
// note that a message has been evicted from the queue.
fn note_evicted(&mut self, messages: &[H256]) {
for message_hash in messages {
self.known_messages.remove(message_hash);
}
}
// whether this peer will accept the message.
fn will_accept(&self, message: &Message) -> bool {
let known = self.known_messages.contains(message.hash());
let matches_bloom = self.topic_filter.as_ref()
.map_or(true, |topic| topic & message.bloom() == message.bloom().clone());
!known && matches_bloom
}
// note a message as known. returns true if it was already
// known, false otherwise.
fn note_known(&mut self, message: &Message) -> bool {
self.known_messages.insert(message.hash().clone())
}
fn set_topic_filter(&mut self, topic: H512) {
self.topic_filter = Some(topic);
}
fn can_send_messages(&self) -> bool {
match self.state {
State::Unconfirmed(_) | State::OurTurn => false,
State::TheirTurn(_) => true,
}
}
}
/// Pool status.
pub struct PoolStatus {
/// Required PoW to be accepted into the pool
pub required_pow: Option<f64>,
/// Number of messages in the pool.
pub message_count: usize,
/// Cumulative size of the messages in the pool
pub cumulative_size: usize,
/// Target size of the pool.
pub target_size: usize,
}
/// Handle to the pool, for posting messages or getting info.
#[derive(Clone)]
pub struct PoolHandle {
messages: Arc<RwLock<Messages>>,
}
impl PoolHandle {
/// Post a message to the whisper network to be relayed.
pub fn post_message(&self, message: Message) -> bool {
self.messages.write().insert(message)
}
/// Get number of messages and amount of memory used by them.
pub fn pool_status(&self) -> PoolStatus {
self.messages.read().status()
}
}
/// The whisper network protocol handler.
pub struct Network<T> {
messages: Arc<RwLock<Messages>>,
handler: T,
peers: RwLock<HashMap<PeerId, Mutex<Peer>>>,
node_key: RwLock<NodeId>,
}
// public API.
impl<T> Network<T> {
/// Create a new network handler.
pub fn new(messages_size_bytes: usize, handler: T) -> Self {
Network {
messages: Arc::new(RwLock::new(Messages::new(messages_size_bytes))),
handler: handler,
peers: RwLock::new(HashMap::new()),
node_key: RwLock::new(Default::default()),
}
}
/// Acquire a sender to asynchronously feed messages to the whisper
/// network.
pub fn handle(&self) -> PoolHandle {
PoolHandle { messages: self.messages.clone() }
}
}
impl<T: MessageHandler> Network<T> {
fn rally(&self, io: &NetworkContext) {
// cannot be greater than 16MB (protocol limitation)
const MAX_MESSAGES_PACKET_SIZE: usize = 8 * 1024 * 1024;
// prune messages.
let now = SystemTime::now();
let pruned_hashes = self.messages.write().prune(now);
let messages = self.messages.read();
let peers = self.peers.read();
// send each peer a packet with new messages it may find relevant.
for (peer_id, peer) in peers.iter() {
let mut peer_data = peer.lock();
peer_data.note_evicted(&pruned_hashes);
let punish_timeout = |last_activity: &SystemTime| {
if *last_activity + Duration::from_millis(MAX_TOLERATED_DELAY_MS) <= now {
debug!(target: "whisper", "Disconnecting peer {} due to excessive timeout.", peer_id);
io.disconnect_peer(*peer_id);
}
};
// check timeouts and skip peers who we can't send a rally to.
match peer_data.state {
State::Unconfirmed(ref time) | State::TheirTurn(ref time) => {
punish_timeout(time);
continue;
}
State::OurTurn => {}
}
// construct packet, skipping messages the peer won't accept.
let mut stream = RlpStream::new();
stream.begin_unbounded_list();
for message in messages.iter() {
if !peer_data.will_accept(message) { continue }
if stream.estimate_size(message.encoded_size()) > MAX_MESSAGES_PACKET_SIZE {
break;
}
peer_data.note_known(message);
stream.append(message.envelope());
}
stream.complete_unbounded_list();
peer_data.state = State::TheirTurn(SystemTime::now());
if let Err(e) = io.send(*peer_id, packet::MESSAGES, stream.out()) {
debug!(target: "whisper", "Failed to send messages packet to peer {}: {}", peer_id, e);
io.disconnect_peer(*peer_id);
}
}
}
// handle status packet from peer.
fn on_status(&self, peer: &PeerId, status: UntrustedRlp)
-> Result<(), Error>
{
let proto: usize = status.as_val()?;
if proto != PROTOCOL_VERSION { return Err(Error::ProtocolVersionMismatch(proto)) }
let peers = self.peers.read();
match peers.get(peer) {
Some(peer) => {
let mut peer = peer.lock();
let our_node_key = self.node_key.read().clone();
// handle this basically impossible edge case gracefully.
if peer.node_key == our_node_key {
return Err(Error::SameNodeKey);
}
// peer with lower node key begins the rally.
if peer.node_key > our_node_key {
peer.state = State::OurTurn;
} else {
peer.state = State::TheirTurn(SystemTime::now());
}
Ok(())
}
None => {
debug!(target: "whisper", "Received message from unknown peer.");
Err(Error::UnknownPeer(*peer))
}
}
}
fn on_messages(&self, peer: &PeerId, message_packet: UntrustedRlp)
-> Result<(), Error>
{
let mut messages_vec = {
let peers = self.peers.read();
let peer = match peers.get(peer) {
Some(peer) => peer,
None => {
debug!(target: "whisper", "Received message from unknown peer.");
return Err(Error::UnknownPeer(*peer));
}
};
let mut peer = peer.lock();
if !peer.can_send_messages() {
return Err(Error::UnexpectedMessage);
}
peer.state = State::OurTurn;
let now = SystemTime::now();
let mut messages_vec = message_packet.iter().map(|rlp| Message::decode(rlp, now))
.collect::<Result<Vec<_>, _>>()?;
if messages_vec.is_empty() { return Ok(()) }
// disallow duplicates in packet.
messages_vec.retain(|message| peer.note_known(&message));
messages_vec
};
// import for relaying.
let mut messages = self.messages.write();
messages_vec.retain(|message| messages.may_accept(&message));
messages.reserve(messages_vec.len());
self.handler.handle_messages(&messages_vec);
for message in messages_vec {
messages.insert(message);
}
Ok(())
}
fn on_topic_filter(&self, peer: &PeerId, filter: UntrustedRlp)
-> Result<(), Error>
{
let peers = self.peers.read();
match peers.get(peer) {
Some(peer) => {
let mut peer = peer.lock();
if let State::Unconfirmed(_) = peer.state {
return Err(Error::UnexpectedMessage);
}
peer.set_topic_filter(filter.as_val()?)
}
None => {
debug!(target: "whisper", "Received message from unknown peer.");
return Err(Error::UnknownPeer(*peer));
}
}
Ok(())
}
fn on_connect(&self, io: &NetworkContext, peer: &PeerId) {
trace!(target: "whisper", "Connecting peer {}", peer);
let node_key = match io.session_info(*peer).and_then(|info| info.id) {
Some(node_key) => node_key,
None => {
debug!(target: "whisper", "Disconnecting peer {}, who has no node key.", peer);
io.disable_peer(*peer);
return;
}
};
self.peers.write().insert(*peer, Mutex::new(Peer {
node_key: node_key,
state: State::Unconfirmed(SystemTime::now()),
known_messages: HashSet::new(),
topic_filter: None,
}));
if let Err(e) = io.send(*peer, packet::STATUS, ::rlp::encode(&PROTOCOL_VERSION).to_vec()) {
debug!(target: "whisper", "Error sending status: {}", e);
io.disconnect_peer(*peer);
}
}
fn on_disconnect(&self, peer: &PeerId) {
trace!(target: "whisper", "Disconnecting peer {}", peer);
let _ = self.peers.write().remove(peer);
}
}
impl<T: MessageHandler> ::network::NetworkProtocolHandler for Network<T> {
fn initialize(&self, io: &NetworkContext, host_info: &HostInfo) {
// set up broadcast timer (< 1s)
io.register_timer(RALLY_TOKEN, RALLY_TIMEOUT_MS)
.expect("Failed to initialize message rally timer");
*self.node_key.write() = host_info.id().clone();
}
fn read(&self, io: &NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]) {
let rlp = UntrustedRlp::new(data);
let res = match packet_id {
packet::STATUS => self.on_status(peer, rlp),
packet::MESSAGES => self.on_messages(peer, rlp),
packet::TOPIC_FILTER => self.on_topic_filter(peer, rlp),
other => Err(Error::UnknownPacket(other)),
};
if let Err(e) = res {
trace!(target: "whisper", "Disabling peer due to misbehavior: {}", e);
io.disable_peer(*peer);
}
}
fn connected(&self, io: &NetworkContext, peer: &PeerId) {
// peer with higher ID should begin rallying.
self.on_connect(io, peer)
}
fn disconnected(&self, _io: &NetworkContext, peer: &PeerId) {
self.on_disconnect(peer)
}
fn timeout(&self, io: &NetworkContext, timer: TimerToken) {
// rally with each peer and handle timeouts.
match timer {
RALLY_TOKEN => self.rally(io),
other => debug!(target: "whisper", "Timout triggered on unknown token {}", other),
}
}
}

316
whisper/src/rpc/crypto.rs Normal file
View File

@ -0,0 +1,316 @@
// 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/>.
//! Encryption schemes supported by RPC layer.
use bigint::hash::H256;
use ethkey::{self, Public, Secret};
use ring::aead::{self, AES_256_GCM, SealingKey, OpeningKey};
/// Length of AES key
pub const AES_KEY_LEN: usize = 32;
/// Length of AES nonce (IV)
pub const AES_NONCE_LEN: usize = 12;
// nonce used for encryption when broadcasting
const BROADCAST_IV: [u8; AES_NONCE_LEN] = [0xff; AES_NONCE_LEN];
// how to encode aes key/nonce.
enum AesEncode {
AppendedNonce, // receiver known, random nonce appended.
OnTopics(Vec<H256>), // receiver knows topics but not key. nonce global.
}
enum EncryptionInner {
AES([u8; AES_KEY_LEN], [u8; AES_NONCE_LEN], AesEncode),
ECIES(Public),
}
/// Encryption good for single usage.
pub struct EncryptionInstance(EncryptionInner);
impl EncryptionInstance {
/// ECIES encryption using public key. Fails if invalid public key.
pub fn ecies(public: Public) -> Result<Self, &'static str> {
if !ethkey::public_is_valid(&public) {
return Err("Invalid public key");
}
Ok(EncryptionInstance(EncryptionInner::ECIES(public)))
}
/// 256-bit AES GCM encryption with given nonce.
/// It is extremely insecure to reuse nonces.
///
/// If generating nonces with a secure RNG, limit uses such that
/// the chance of collision is negligible.
pub fn aes(key: [u8; AES_KEY_LEN], nonce: [u8; AES_NONCE_LEN]) -> Self {
EncryptionInstance(EncryptionInner::AES(key, nonce, AesEncode::AppendedNonce))
}
/// Broadcast encryption for the message based on the given topics.
///
/// Key reuse here is extremely dangerous. It should be randomly generated
/// with a secure RNG.
pub fn broadcast(key: [u8; AES_KEY_LEN], topics: Vec<H256>) -> Self {
EncryptionInstance(EncryptionInner::AES(key, BROADCAST_IV, AesEncode::OnTopics(topics)))
}
/// Encrypt the supplied plaintext
pub fn encrypt(self, plain: &[u8]) -> Vec<u8> {
match self.0 {
EncryptionInner::AES(key, nonce, encode) => {
let sealing_key = SealingKey::new(&AES_256_GCM, &key)
.expect("key is of correct len; qed");
let encrypt_plain = move |buf: &mut Vec<u8>| {
let out_suffix_capacity = AES_256_GCM.tag_len();
let prepend_len = buf.len();
buf.extend(plain);
buf.resize(prepend_len + plain.len() + out_suffix_capacity, 0);
let out_size = aead::seal_in_place(
&sealing_key,
&nonce,
&[], // no authenticated data.
&mut buf[prepend_len..],
out_suffix_capacity,
).expect("key, nonce, buf are valid and out suffix large enough; qed");
// truncate to the output size and return.
buf.truncate(prepend_len + out_size);
};
match encode {
AesEncode::AppendedNonce => {
let mut buf = Vec::new();
encrypt_plain(&mut buf);
buf.extend(&nonce[..]);
buf
}
AesEncode::OnTopics(topics) => {
let mut buf = Vec::new();
let key = H256(key);
for topic in topics {
buf.extend(&*(topic ^ key));
}
encrypt_plain(&mut buf);
buf
}
}
}
EncryptionInner::ECIES(valid_public) => {
::ethcrypto::ecies::encrypt(&valid_public, &[], plain)
.expect("validity of public key an invariant of the type; qed")
}
}
}
}
enum AesExtract {
AppendedNonce([u8; AES_KEY_LEN]), // extract appended nonce.
OnTopics(usize, usize, H256), // number of topics, index we know, topic we know.
}
enum DecryptionInner {
AES(AesExtract),
ECIES(Secret),
}
/// Decryption instance good for single usage.
pub struct DecryptionInstance(DecryptionInner);
impl DecryptionInstance {
/// ECIES decryption using secret key. Fails if invalid secret.
pub fn ecies(secret: Secret) -> Result<Self, &'static str> {
secret.check_validity().map_err(|_| "Invalid secret key")?;
Ok(DecryptionInstance(DecryptionInner::ECIES(secret)))
}
/// 256-bit AES GCM decryption with appended nonce.
pub fn aes(key: [u8; AES_KEY_LEN]) -> Self {
DecryptionInstance(DecryptionInner::AES(AesExtract::AppendedNonce(key)))
}
/// Decode broadcast based on number of topics and known topic.
/// Known topic index may not be larger than num topics - 1.
pub fn broadcast(num_topics: usize, topic_idx: usize, known_topic: H256) -> Result<Self, &'static str> {
if topic_idx >= num_topics { return Err("topic index out of bounds") }
Ok(DecryptionInstance(DecryptionInner::AES(AesExtract::OnTopics(num_topics, topic_idx, known_topic))))
}
/// Decrypt ciphertext. Fails if it's an invalid message.
pub fn decrypt(self, ciphertext: &[u8]) -> Option<Vec<u8>> {
match self.0 {
DecryptionInner::AES(extract) => {
let decrypt = |
key: [u8; AES_KEY_LEN],
nonce: [u8; AES_NONCE_LEN],
ciphertext: &[u8]
| {
if ciphertext.len() < AES_256_GCM.tag_len() { return None }
let opening_key = OpeningKey::new(&AES_256_GCM, &key)
.expect("key length is valid for mode; qed");
let mut buf = ciphertext.to_vec();
// decrypted plaintext always ends up at the
// front of the buffer.
let maybe_decrypted = aead::open_in_place(
&opening_key,
&nonce,
&[], // no authenticated data
0, // no header.
&mut buf,
).ok().map(|plain_slice| plain_slice.len());
maybe_decrypted.map(move |len| { buf.truncate(len); buf })
};
match extract {
AesExtract::AppendedNonce(key) => {
if ciphertext.len() < AES_NONCE_LEN { return None }
// nonce is the suffix of ciphertext.
let mut nonce = [0; AES_NONCE_LEN];
let nonce_offset = ciphertext.len() - AES_NONCE_LEN;
nonce.copy_from_slice(&ciphertext[nonce_offset..]);
decrypt(key, nonce, &ciphertext[..nonce_offset])
}
AesExtract::OnTopics(num_topics, known_index, known_topic) => {
if ciphertext.len() < num_topics * 32 { return None }
let mut salted_topic = H256::new();
salted_topic.copy_from_slice(&ciphertext[(known_index * 32)..][..32]);
let key = (salted_topic ^ known_topic).0;
let offset = num_topics * 32;
decrypt(key, BROADCAST_IV, &ciphertext[offset..])
}
}
}
DecryptionInner::ECIES(secret) => {
// secret is checked for validity, so only fails on invalid message.
::ethcrypto::ecies::decrypt(&secret, &[], ciphertext).ok()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn aes_key_len_should_be_equal_to_constant() {
assert_eq!(::ring::aead::AES_256_GCM.key_len(), AES_KEY_LEN);
}
#[test]
fn aes_nonce_len_should_be_equal_to_constant() {
assert_eq!(::ring::aead::AES_256_GCM.nonce_len(), AES_NONCE_LEN);
}
#[test]
fn encrypt_asymmetric() {
use ethkey::{Generator, Random};
let key_pair = Random.generate().unwrap();
let test_message = move |message: &[u8]| {
let instance = EncryptionInstance::ecies(key_pair.public().clone()).unwrap();
let ciphertext = instance.encrypt(&message);
if !message.is_empty() {
assert!(&ciphertext[..message.len()] != message)
}
let instance = DecryptionInstance::ecies(key_pair.secret().clone()).unwrap();
let decrypted = instance.decrypt(&ciphertext).unwrap();
assert_eq!(message, &decrypted[..])
};
test_message(&[1, 2, 3, 4, 5]);
test_message(&[]);
test_message(&[255; 512]);
}
#[test]
fn encrypt_symmetric() {
use rand::{Rng, OsRng};
let mut rng = OsRng::new().unwrap();
let mut test_message = move |message: &[u8]| {
let key = rng.gen();
let instance = EncryptionInstance::aes(key, rng.gen());
let ciphertext = instance.encrypt(message);
if !message.is_empty() {
assert!(&ciphertext[..message.len()] != message)
}
let instance = DecryptionInstance::aes(key);
let decrypted = instance.decrypt(&ciphertext).unwrap();
assert_eq!(message, &decrypted[..])
};
test_message(&[1, 2, 3, 4, 5]);
test_message(&[]);
test_message(&[255; 512]);
}
#[test]
fn encrypt_broadcast() {
use rand::{Rng, OsRng};
let mut rng = OsRng::new().unwrap();
let mut test_message = move |message: &[u8]| {
let all_topics = (0..5).map(|_| rng.gen()).collect::<Vec<_>>();
let known_idx = 2;
let known_topic = all_topics[2];
let key = rng.gen();
let instance = EncryptionInstance::broadcast(key, all_topics);
let ciphertext = instance.encrypt(message);
if !message.is_empty() {
assert!(&ciphertext[..message.len()] != message)
}
let instance = DecryptionInstance::broadcast(5, known_idx, known_topic).unwrap();
let decrypted = instance.decrypt(&ciphertext).unwrap();
assert_eq!(message, &decrypted[..])
};
test_message(&[1, 2, 3, 4, 5]);
test_message(&[]);
test_message(&[255; 512]);
}
}

416
whisper/src/rpc/filter.rs Normal file
View File

@ -0,0 +1,416 @@
// 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/>.
//! Abstraction over filters which works with polling and subscription.
use std::collections::HashMap;
use std::sync::{mpsc, Arc};
use std::thread;
use bigint::hash::{H256, H512};
use ethkey::Public;
use jsonrpc_macros::pubsub::{Subscriber, Sink};
use parking_lot::{Mutex, RwLock};
use rand::{Rng, OsRng};
use message::{Message, Topic};
use super::key_store::KeyStore;
use super::types::{self, FilterItem, HexEncode};
/// Kinds of filters,
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum Kind {
/// Polled filter only returns data upon request
Poll,
/// Subscription filter pushes data to subscriber immediately.
Subscription,
}
pub type ItemBuffer = Arc<Mutex<Vec<FilterItem>>>;
enum FilterEntry {
Poll(Arc<Filter>, ItemBuffer),
Subscription(Arc<Filter>, Sink<FilterItem>),
}
/// Filter manager. Handles filters as well as a thread for doing decryption
/// and payload decoding.
pub struct Manager {
key_store: Arc<RwLock<KeyStore>>,
filters: RwLock<HashMap<H256, FilterEntry>>,
tx: Mutex<mpsc::Sender<Box<Fn() + Send>>>,
join: Option<thread::JoinHandle<()>>,
}
impl Manager {
/// Create a new filter manager that will dispatch decryption tasks onto
/// the given thread pool.
pub fn new() -> ::std::io::Result<Self> {
let (tx, rx) = mpsc::channel::<Box<Fn() + Send>>();
let join_handle = thread::Builder::new()
.name("Whisper Decryption Worker".to_string())
.spawn(move || for item in rx { (item)() })?;
Ok(Manager {
key_store: Arc::new(RwLock::new(KeyStore::new()?)),
filters: RwLock::new(HashMap::new()),
tx: Mutex::new(tx),
join: Some(join_handle),
})
}
/// Get a handle to the key store.
pub fn key_store(&self) -> Arc<RwLock<KeyStore>> {
self.key_store.clone()
}
/// Get filter kind if it's known.
pub fn kind(&self, id: &H256) -> Option<Kind> {
self.filters.read().get(id).map(|filter| match *filter {
FilterEntry::Poll(_, _) => Kind::Poll,
FilterEntry::Subscription(_, _) => Kind::Subscription,
})
}
/// Remove filter by ID.
pub fn remove(&self, id: &H256) {
self.filters.write().remove(id);
}
/// Add a new polled filter.
pub fn insert_polled(&self, filter: Filter) -> Result<H256, &'static str> {
let buffer = Arc::new(Mutex::new(Vec::new()));
let entry = FilterEntry::Poll(Arc::new(filter), buffer);
let id = OsRng::new()
.map_err(|_| "unable to acquire secure randomness")?
.gen();
self.filters.write().insert(id, entry);
Ok(id)
}
/// Insert new subscription filter. Generates a secure ID and sends it to
/// the
pub fn insert_subscription(&self, filter: Filter, sub: Subscriber<FilterItem>)
-> Result<(), &'static str>
{
let id: H256 = OsRng::new()
.map_err(|_| "unable to acquire secure randomness")?
.gen();
sub.assign_id(::jsonrpc_pubsub::SubscriptionId::String(id.hex()))
.map(move |sink| {
let entry = FilterEntry::Subscription(Arc::new(filter), sink);
self.filters.write().insert(id, entry);
})
.map_err(|_| "subscriber disconnected")
}
/// Poll changes on filter identified by ID.
pub fn poll_changes(&self, id: &H256) -> Option<Vec<FilterItem>> {
self.filters.read().get(id).and_then(|filter| match *filter {
FilterEntry::Subscription(_, _) => None,
FilterEntry::Poll(_, ref changes)
=> Some(::std::mem::replace(&mut *changes.lock(), Vec::new())),
})
}
}
// machinery for attaching the manager to the network instance.
impl ::net::MessageHandler for Arc<Manager> {
fn handle_messages(&self, messages: &[Message]) {
let filters = self.filters.read();
let filters_iter = filters
.values()
.flat_map(|filter| messages.iter().map(move |msg| (filter, msg))) ;
for (filter, message) in filters_iter {
// if the message matches any of the possible bloom filters,
// send to thread pool to attempt decryption and avoid
// blocking the network thread for long.
let failed_send = match *filter {
FilterEntry::Poll(ref filter, _) | FilterEntry::Subscription(ref filter, _)
if !filter.basic_matches(message) => None,
FilterEntry::Poll(ref filter, ref buffer) => {
let (message, key_store) = (message.clone(), self.key_store.clone());
let (filter, buffer) = (filter.clone(), buffer.clone());
self.tx.lock().send(Box::new(move || {
filter.handle_message(
&message,
&*key_store,
|matched| buffer.lock().push(matched),
)
})).err().map(|x| x.0)
}
FilterEntry::Subscription(ref filter, ref sink) => {
let (message, key_store) = (message.clone(), self.key_store.clone());
let (filter, sink) = (filter.clone(), sink.clone());
self.tx.lock().send(Box::new(move || {
filter.handle_message(
&message,
&*key_store,
|matched| { let _ = sink.notify(Ok(matched)); },
)
})).err().map(|x| x.0)
}
};
// if we failed to send work, no option but to do it locally.
if let Some(local_work) = failed_send {
(local_work)()
}
}
}
}
impl Drop for Manager {
fn drop(&mut self) {
if let Some(guard) = self.join.take() {
let _ = guard.join();
}
}
}
/// Filter incoming messages by critera.
pub struct Filter {
topics: Vec<(Vec<u8>, H512, Topic)>,
from: Option<Public>,
decrypt_with: Option<H256>,
}
impl Filter {
/// Create a new filter from filter request.
///
/// Fails if the topics vector is empty.
pub fn new(params: types::FilterRequest) -> Result<Self, &'static str> {
if params.topics.is_empty() {
return Err("no topics for filter");
}
let topics: Vec<_> = params.topics.into_iter()
.map(|x| x.into_inner())
.map(|topic| {
let abridged = super::abridge_topic(&topic);
(topic, abridged.bloom(), abridged)
})
.collect();
Ok(Filter {
topics: topics,
from: params.from.map(|x| x.into_inner()),
decrypt_with: params.decrypt_with.map(|x| x.into_inner()),
})
}
// does basic matching:
// whether the given message matches at least one of the topics of the
// filter.
// TODO: minimum PoW heuristic.
fn basic_matches(&self, message: &Message) -> bool {
self.topics.iter().any(|&(_, ref bloom, _)| {
&(bloom & message.bloom()) == bloom
})
}
// handle a message that matches the bloom.
fn handle_message<F: Fn(FilterItem)>(
&self,
message: &Message,
store: &RwLock<KeyStore>,
on_match: F,
) {
use rpc::crypto::DecryptionInstance;
use tiny_keccak::keccak256;
let matched_indices: Vec<_> = self.topics.iter()
.enumerate()
.filter_map(|(i, &(_, ref bloom, ref abridged))| {
let contains_topic = &(bloom & message.bloom()) == bloom
&& message.topics().contains(abridged);
if contains_topic { Some(i) } else { None }
})
.collect();
if matched_indices.is_empty() { return }
let decrypt = match self.decrypt_with {
Some(ref id) => match store.read().decryption_instance(id) {
Some(d) => d,
None => {
warn!(target: "whisper", "Filter attempted to decrypt with destroyed identity {}",
id);
return
}
},
None => {
let known_idx = matched_indices[0];
let known_topic = H256(keccak256(&self.topics[0].0));
DecryptionInstance::broadcast(message.topics().len(), known_idx, known_topic)
.expect("known idx is within the range 0..message.topics.len(); qed")
}
};
let decrypted = match decrypt.decrypt(message.data()) {
Some(d) => d,
None => {
trace!(target: "whisper", "Failed to decrypt message with {} matching topics",
matched_indices.len());
return
}
};
match ::rpc::payload::decode(&decrypted) {
Ok(decoded) => {
if decoded.from != self.from { return }
let matched_topics = matched_indices
.into_iter()
.map(|i| self.topics[i].0.clone())
.map(HexEncode)
.collect();
on_match(FilterItem {
from: decoded.from.map(HexEncode),
recipient: self.decrypt_with.map(HexEncode),
ttl: message.envelope().ttl,
topics: matched_topics,
timestamp: message.envelope().expiry - message.envelope().ttl,
payload: HexEncode(decoded.message.to_vec()),
padding: decoded.padding.map(|pad| HexEncode(pad.to_vec())),
})
}
Err(reason) =>
trace!(target: "whisper", "Bad payload in decrypted message with {} topics: {}",
matched_indices.len(), reason),
}
}
}
#[cfg(test)]
mod tests {
use message::{CreateParams, Message};
use rpc::types::{FilterRequest, HexEncode};
use rpc::abridge_topic;
use super::*;
#[test]
fn rejects_empty_topics() {
let req = FilterRequest {
decrypt_with: Default::default(),
from: None,
topics: Vec::new(),
};
assert!(Filter::new(req).is_err());
}
#[test]
fn basic_match() {
let topics = vec![vec![1, 2, 3], vec![4, 5, 6]];
let req = FilterRequest {
decrypt_with: Default::default(),
from: None,
topics: topics.iter().cloned().map(HexEncode).collect(),
};
let filter = Filter::new(req).unwrap();
let message = Message::create(CreateParams {
ttl: 100,
payload: vec![1, 3, 5, 7, 9],
topics: topics.iter().map(|x| abridge_topic(&x)).collect(),
work: 0,
});
assert!(filter.basic_matches(&message));
let message = Message::create(CreateParams {
ttl: 100,
payload: vec![1, 3, 5, 7, 9],
topics: topics.iter().take(1).map(|x| abridge_topic(&x)).collect(),
work: 0,
});
assert!(filter.basic_matches(&message));
let message = Message::create(CreateParams {
ttl: 100,
payload: vec![1, 3, 5, 7, 9],
topics: Vec::new(),
work: 0,
});
assert!(!filter.basic_matches(&message));
}
#[test]
fn decrypt_and_decode() {
use rpc::payload::{self, EncodeParams};
use rpc::key_store::{Key, KeyStore};
let mut store = KeyStore::new().unwrap();
let signing_pair = Key::new_asymmetric(store.rng());
let encrypting_key = Key::new_symmetric(store.rng());
let decrypt_id = store.insert(encrypting_key);
let encryption_instance = store.encryption_instance(&decrypt_id).unwrap();
let store = ::parking_lot::RwLock::new(store);
let payload = payload::encode(EncodeParams {
message: &[1, 2, 3],
padding: Some(&[4, 5, 4, 5]),
sign_with: Some(signing_pair.secret().unwrap())
}).unwrap();
let encrypted = encryption_instance.encrypt(&payload);
let message = Message::create(CreateParams {
ttl: 100,
payload: encrypted,
topics: vec![abridge_topic(&[9; 32])],
work: 0,
});
let message2 = Message::create(CreateParams {
ttl: 100,
payload: vec![3, 5, 7, 9],
topics: vec![abridge_topic(&[9; 32])],
work: 0,
});
let filter = Filter::new(FilterRequest {
decrypt_with: Some(HexEncode(decrypt_id)),
from: Some(HexEncode(signing_pair.public().unwrap().clone())),
topics: vec![HexEncode(vec![9; 32])],
}).unwrap();
assert!(filter.basic_matches(&message));
let items = ::std::cell::Cell::new(0);
let on_match = |_| { items.set(items.get() + 1); };
filter.handle_message(&message, &store, &on_match);
filter.handle_message(&message2, &store, &on_match);
assert_eq!(items.get(), 1);
}
}

View File

@ -0,0 +1,197 @@
// 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/>.
//! Identity and keystore for Whisper sessions.
//!
//! Can handle symmetric and asymmetric keys.
//! Symmetric encryption is done via AES-256 in GCM mode.
use std::collections::HashMap;
use bigint::hash::H256;
use ethkey::{KeyPair, Public, Secret};
use rand::{Rng, OsRng};
use ring::error::Unspecified;
use rpc::crypto::{AES_KEY_LEN, EncryptionInstance, DecryptionInstance};
/// A symmetric or asymmetric key used for encryption, decryption, and signing
/// of payloads.
pub enum Key {
/// ECIES key pair for Secp2561k curve. Suitable for encryption, decryption,
/// and signing.
Asymmetric(KeyPair),
/// AES-256 GCM mode. Suitable for encryption, decryption, but not signing.
Symmetric([u8; AES_KEY_LEN]),
}
impl Key {
/// Generate a random asymmetric key with the given cryptographic RNG.
pub fn new_asymmetric(rng: &mut OsRng) -> Self {
match ::ethkey::Generator::generate(rng) {
Ok(pair) => Key::Asymmetric(pair),
Err(void) => match void {},
}
}
/// Generate a random symmetric key with the given cryptographic RNG.
pub fn new_symmetric(rng: &mut OsRng) -> Self {
Key::Symmetric(rng.gen())
}
/// From secret asymmetric key. Fails if secret is invalid.
pub fn from_secret(secret: Secret) -> Result<Self, Unspecified> {
KeyPair::from_secret(secret)
.map(Key::Asymmetric)
.map_err(|_| Unspecified)
}
/// From raw symmetric key.
pub fn from_raw_symmetric(key: [u8; AES_KEY_LEN]) -> Self {
Key::Symmetric(key)
}
/// Get a handle to the public key if this is an asymmetric key.
pub fn public(&self) -> Option<&Public> {
match *self {
Key::Asymmetric(ref pair) => Some(pair.public()),
Key::Symmetric(_) => None,
}
}
/// Get a handle to the secret key if this is an asymmetric key.
pub fn secret(&self) -> Option<&Secret> {
match *self {
Key::Asymmetric(ref pair) => Some(pair.secret()),
Key::Symmetric(_) => None,
}
}
/// Get a handle to the symmetric key.
pub fn symmetric(&self) -> Option<&[u8; AES_KEY_LEN]> {
match *self {
Key::Asymmetric(_) => None,
Key::Symmetric(ref key) => Some(key),
}
}
}
/// Key store.
pub struct KeyStore {
rng: OsRng,
identities: HashMap<H256, Key>,
}
impl KeyStore {
/// Create the key store. Returns any error in accessing the system's secure
/// RNG.
pub fn new() -> Result<Self, ::std::io::Error> {
Ok(KeyStore {
rng: OsRng::new()?,
identities: HashMap::new(),
})
}
/// Import a key, generating a random identity for it.
pub fn insert(&mut self, key: Key) -> H256 {
let id = self.rng().gen();
self.identities.insert(id, key);
id
}
/// Get a key by ID.
pub fn get<'a>(&'a self, id: &H256) -> Option<&'a Key> {
self.identities.get(id)
}
/// Get asymmetric ID's public key.
pub fn public<'a>(&'a self, id: &H256) -> Option<&'a Public> {
self.get(id).and_then(Key::public)
}
/// Get asymmetric ID's secret key.
pub fn secret<'a>(&'a self, id: &H256) -> Option<&'a Secret> {
self.get(id).and_then(Key::secret)
}
/// Get symmetric ID's key.
pub fn symmetric<'a>(&'a self, id: &H256) -> Option<&'a [u8; AES_KEY_LEN]> {
self.get(id).and_then(Key::symmetric)
}
/// Get encryption instance for identity.
pub fn encryption_instance(&self, id: &H256) -> Result<EncryptionInstance, &'static str> {
self.get(id).ok_or("no such identity").and_then(|key| match *key {
Key::Asymmetric(ref pair) => EncryptionInstance::ecies(pair.public().clone())
.map_err(|_| "could not create encryption instance for id"),
Key::Symmetric(ref key) =>
OsRng::new()
.map(|mut rng| EncryptionInstance::aes(key.clone(), rng.gen()))
.map_err(|_| "unable to get secure randomness")
})
}
/// Get decryption instance for identity.
/// If the identity is known, always succeeds.
pub fn decryption_instance(&self, id: &H256) -> Option<DecryptionInstance> {
self.get(id).map(|key| match *key {
Key::Asymmetric(ref pair) => DecryptionInstance::ecies(pair.secret().clone())
.expect("all keys stored are valid; qed"),
Key::Symmetric(ref key) => DecryptionInstance::aes(key.clone()),
})
}
/// Whether the store contains a key by this ID.
pub fn contains(&self, id: &H256) -> bool {
self.identities.contains_key(id)
}
/// Remove a key by ID.
pub fn remove(&mut self, id: &H256) -> bool {
self.identities.remove(id).is_some()
}
/// Get RNG.
pub fn rng(&mut self) -> &mut OsRng {
&mut self.rng
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_invalid_secret() {
let bad_secret = ::ethkey::Secret::from_slice(&[0xff; 32]);
assert!(Key::from_secret(bad_secret).is_err());
}
#[test]
fn generated_key_should_exist() {
let mut store = KeyStore::new().unwrap();
let key = Key::new_asymmetric(store.rng());
assert!(key.public().is_some());
assert!(key.secret().is_some());
let id = store.insert(key);
assert!(store.contains(&id));
assert!(store.get(&id).is_some());
}
}

402
whisper/src/rpc/mod.rs Normal file
View File

@ -0,0 +1,402 @@
// 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/>.
//! JSONRPC interface for Whisper.
//!
//! Manages standard message format decoding, ephemeral identities, signing,
//! encryption, and decryption.
//!
//! Provides an interface for using whisper to transmit data securely.
use std::sync::Arc;
use jsonrpc_core::{Error, ErrorCode, Metadata};
use jsonrpc_pubsub::{Session, PubSubMetadata, SubscriptionId};
use jsonrpc_macros::pubsub;
use bigint::hash::H256;
use futures::{future, BoxFuture};
use parking_lot::RwLock;
use self::filter::Filter;
use self::key_store::{Key, KeyStore};
use self::types::HexEncode;
use message::{CreateParams, Message, Topic};
mod crypto;
mod filter;
mod key_store;
mod payload;
mod types;
pub use self::filter::Manager as FilterManager;
// create whisper RPC error.
fn whisper_error<T: Into<String>>(message: T) -> Error {
const ERROR_CODE: i64 = -32085;
Error {
code: ErrorCode::ServerError(ERROR_CODE),
message: message.into(),
data: None,
}
}
fn topic_hash(topic: &[u8]) -> H256 {
H256(::tiny_keccak::keccak256(topic))
}
// abridge topic using first four bytes of hash.
fn abridge_topic(topic: &[u8]) -> Topic {
let mut abridged = [0; 4];
let hash = topic_hash(topic).0;
abridged.copy_from_slice(&hash[..4]);
abridged.into()
}
build_rpc_trait! {
/// Whisper RPC interface.
pub trait Whisper {
/// Info about the node.
#[rpc(name = "shh_info")]
fn info(&self) -> Result<types::NodeInfo, Error>;
/// Generate a new asymmetric key pair and return an identity.
#[rpc(name = "shh_newKeyPair")]
fn new_key_pair(&self) -> Result<types::Identity, Error>;
/// Import the given SECP2561k private key and return an identity.
#[rpc(name = "shh_addPrivateKey")]
fn add_private_key(&self, types::Private) -> Result<types::Identity, Error>;
/// Generate a new symmetric key and return an identity.
#[rpc(name = "shh_newSymKey")]
fn new_sym_key(&self) -> Result<types::Identity, Error>;
/// Import the given symmetric key and return an identity.
#[rpc(name = "shh_addSymKey")]
fn add_sym_key(&self, types::Symmetric) -> Result<types::Identity, Error>;
/// Get public key. Succeeds if identity is stored and asymmetric.
#[rpc(name = "shh_getPublicKey")]
fn get_public(&self, types::Identity) -> Result<types::Public, Error>;
/// Get private key. Succeeds if identity is stored and asymmetric.
#[rpc(name = "shh_getPrivateKey")]
fn get_private(&self, types::Identity) -> Result<types::Private, Error>;
#[rpc(name = "shh_getSymKey")]
fn get_symmetric(&self, types::Identity) -> Result<types::Symmetric, Error>;
/// Delete key pair denoted by given identity.
///
/// Return true if successfully removed, false if unknown,
/// and error otherwise.
#[rpc(name = "shh_deleteKey")]
fn remove_key(&self, types::Identity) -> Result<bool, Error>;
/// Post a message to the network with given parameters.
#[rpc(name = "shh_post")]
fn post(&self, types::PostRequest) -> Result<bool, Error>;
/// Create a new polled filter.
#[rpc(name = "shh_newMessageFilter")]
fn new_filter(&self, types::FilterRequest) -> Result<types::Identity, Error>;
/// Poll changes on a polled filter.
#[rpc(name = "shh_getFilterMessages")]
fn poll_changes(&self, types::Identity) -> Result<Vec<types::FilterItem>, Error>;
/// Delete polled filter. Return bool indicating success.
#[rpc(name = "shh_deleteMessageFilter")]
fn delete_filter(&self, types::Identity) -> Result<bool, Error>;
}
}
build_rpc_trait! {
/// Whisper RPC pubsub.
pub trait WhisperPubSub {
type Metadata;
#[pubsub(name = "hello")] {
/// Subscribe to messages matching the filter.
#[rpc(name = "ssh_subscribe")]
fn subscribe(&self, Self::Metadata, pubsub::Subscriber<types::FilterItem>, types::FilterRequest);
/// Unsubscribe from filter matching given ID. Return
/// true on success, error otherwise.
#[rpc(name = "shh_unsubscribe")]
fn unsubscribe(&self, SubscriptionId) -> BoxFuture<bool, Error>;
}
}
}
/// Something which can send messages to the network.
pub trait PoolHandle: Send + Sync {
/// Give message to the whisper network for relay.
/// Returns false if PoW too low.
fn relay(&self, message: Message) -> bool;
/// Number of messages and memory used by resident messages.
fn pool_status(&self) -> ::net::PoolStatus;
}
impl PoolHandle for ::net::PoolHandle {
fn relay(&self, message: Message) -> bool {
self.post_message(message)
}
fn pool_status(&self) -> ::net::PoolStatus {
::net::PoolHandle::pool_status(self)
}
}
/// Default, simple metadata implementation.
#[derive(Clone, Default)]
pub struct Meta {
session: Option<Arc<Session>>,
}
impl Metadata for Meta {}
impl PubSubMetadata for Meta {
fn session(&self) -> Option<Arc<Session>> {
self.session.clone()
}
}
/// Implementation of whisper RPC.
pub struct WhisperClient<P, M = Meta> {
store: Arc<RwLock<KeyStore>>,
pool: P,
filter_manager: Arc<filter::Manager>,
_meta: ::std::marker::PhantomData<M>,
}
impl<P> WhisperClient<P> {
/// Create a new whisper client with basic metadata.
pub fn with_simple_meta(pool: P, filter_manager: Arc<filter::Manager>) -> Self {
WhisperClient::new(pool, filter_manager)
}
}
impl<P, M> WhisperClient<P, M> {
/// Create a new whisper client.
pub fn new(pool: P, filter_manager: Arc<filter::Manager>) -> Self {
WhisperClient {
store: filter_manager.key_store(),
pool: pool,
filter_manager: filter_manager,
_meta: ::std::marker::PhantomData,
}
}
fn delete_filter_kind(&self, id: H256, kind: filter::Kind) -> bool {
match self.filter_manager.kind(&id) {
Some(k) if k == kind => {
self.filter_manager.remove(&id);
true
}
None | Some(_) => false,
}
}
}
impl<P: PoolHandle + 'static, M: Send + Sync + 'static> Whisper for WhisperClient<P, M> {
fn info(&self) -> Result<types::NodeInfo, Error> {
let status = self.pool.pool_status();
Ok(types::NodeInfo {
required_pow: status.required_pow,
messages: status.message_count,
memory: status.cumulative_size,
target_memory: status.target_size,
})
}
fn new_key_pair(&self) -> Result<types::Identity, Error> {
let mut store = self.store.write();
let key_pair = Key::new_asymmetric(store.rng());
Ok(HexEncode(store.insert(key_pair)))
}
fn add_private_key(&self, private: types::Private) -> Result<types::Identity, Error> {
let key_pair = Key::from_secret(private.into_inner().into())
.map_err(|_| whisper_error("Invalid private key"))?;
Ok(HexEncode(self.store.write().insert(key_pair)))
}
fn new_sym_key(&self) -> Result<types::Identity, Error> {
let mut store = self.store.write();
let key = Key::new_symmetric(store.rng());
Ok(HexEncode(store.insert(key)))
}
fn add_sym_key(&self, raw_key: types::Symmetric) -> Result<types::Identity, Error> {
let raw_key = raw_key.into_inner().0;
let key = Key::from_raw_symmetric(raw_key);
Ok(HexEncode(self.store.write().insert(key)))
}
fn get_public(&self, id: types::Identity) -> Result<types::Public, Error> {
self.store.read().public(&id.into_inner())
.cloned()
.map(HexEncode)
.ok_or_else(|| whisper_error("Unknown identity"))
}
fn get_private(&self, id: types::Identity) -> Result<types::Private, Error> {
self.store.read().secret(&id.into_inner())
.map(|x| (&**x).clone())
.map(HexEncode)
.ok_or_else(|| whisper_error("Unknown identity"))
}
fn get_symmetric(&self, id: types::Identity) -> Result<types::Symmetric, Error> {
self.store.read().symmetric(&id.into_inner())
.cloned()
.map(H256)
.map(HexEncode)
.ok_or_else(|| whisper_error("Unknown identity"))
}
fn remove_key(&self, id: types::Identity) -> Result<bool, Error> {
Ok(self.store.write().remove(&id.into_inner()))
}
fn post(&self, req: types::PostRequest) -> Result<bool, Error> {
use self::crypto::EncryptionInstance;
let encryption = match req.to {
Some(types::Receiver::Public(public)) => EncryptionInstance::ecies(public.into_inner())
.map_err(whisper_error)?,
Some(types::Receiver::Identity(id)) => self.store.read().encryption_instance(&id.into_inner())
.map_err(whisper_error)?,
None => {
use rand::{Rng, OsRng};
// broadcast mode: use fixed nonce and fresh key each time.
let mut rng = OsRng::new()
.map_err(|_| whisper_error("unable to acquire secure randomness"))?;
let key = rng.gen();
if req.topics.is_empty() {
return Err(whisper_error("must supply at least one topic for broadcast message"));
}
EncryptionInstance::broadcast(
key,
req.topics.iter().map(|x| topic_hash(&x)).collect()
)
}
};
let sign_with = match req.from {
Some(from) => {
Some(
self.store.read().secret(&from.into_inner())
.cloned()
.ok_or_else(|| whisper_error("Unknown identity `from`"))?
)
}
None => None,
};
let encrypted = {
let payload = payload::encode(payload::EncodeParams {
message: &req.payload.into_inner(),
padding: req.padding.map(|p| p.into_inner()).as_ref().map(|x| &x[..]),
sign_with: sign_with.as_ref(),
}).map_err(whisper_error)?;
encryption.encrypt(&payload)
};
// mining the packet is the heaviest item of work by far.
// there may be a benefit to dispatching this onto the CPU pool
// and returning a future. but then things get _less_ efficient
// if the server infrastructure has more threads than the CPU pool.
let message = Message::create(CreateParams {
ttl: req.ttl,
payload: encrypted,
topics: req.topics.into_iter().map(|x| abridge_topic(&x.into_inner())).collect(),
work: req.priority,
});
if !self.pool.relay(message) {
Err(whisper_error("PoW too low to compete with other messages"))
} else {
Ok(true)
}
}
fn new_filter(&self, req: types::FilterRequest) -> Result<types::Identity, Error> {
let filter = Filter::new(req).map_err(whisper_error)?;
self.filter_manager.insert_polled(filter)
.map(HexEncode)
.map_err(whisper_error)
}
fn poll_changes(&self, id: types::Identity) -> Result<Vec<types::FilterItem>, Error> {
match self.filter_manager.poll_changes(&id.into_inner()) {
None => Err(whisper_error("no such message filter")),
Some(items) => Ok(items),
}
}
fn delete_filter(&self, id: types::Identity) -> Result<bool, Error> {
Ok(self.delete_filter_kind(id.into_inner(), filter::Kind::Poll))
}
}
impl<P: PoolHandle + 'static, M: Send + Sync + PubSubMetadata> WhisperPubSub for WhisperClient<P, M> {
type Metadata = M;
fn subscribe(
&self,
_meta: Self::Metadata,
subscriber: pubsub::Subscriber<types::FilterItem>,
req: types::FilterRequest,
) {
match Filter::new(req) {
Ok(filter) => {
if let Err(e) = self.filter_manager.insert_subscription(filter, subscriber) {
debug!(target: "whisper", "Failed to add subscription: {}", e);
}
}
Err(reason) => { let _ = subscriber.reject(whisper_error(reason)); }
}
}
fn unsubscribe(&self, id: SubscriptionId) -> BoxFuture<bool, Error> {
use std::str::FromStr;
let res = match id {
SubscriptionId::String(s) => H256::from_str(&s)
.map_err(|_| "unrecognized ID")
.map(|id| self.delete_filter_kind(id, filter::Kind::Subscription)),
SubscriptionId::Number(_) => Err("unrecognized ID"),
};
Box::new(future::done(res.map_err(whisper_error)))
}
}

357
whisper/src/rpc/payload.rs Normal file
View File

@ -0,0 +1,357 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Common payload format definition, construction, and decoding.
//!
//! Format:
//! flags: 1 byte
//!
//! payload size: 0..4 bytes, BE, determined by flags.
//! optional padding: byte array up to 2^24 bytes in length. encoded in payload size.
//! optional signature: 65 bytes (r, s, v)
//!
//! payload: byte array of length of arbitrary size.
//!
//! flag bits used:
//! 0, 1 => how many bytes indicate padding length (up to 3)
//! 2 => whether signature is present
//!
//! padding is used to mask information about size of message.
//!
//! AES-256-GCM will append 12 bytes of metadata to the front of the message.
use bigint::hash::H256;
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use ethkey::{Public, Secret};
use tiny_keccak::keccak256;
const SIGNATURE_LEN: usize = 65;
const STANDARD_PAYLOAD_VERSION: u8 = 1;
bitflags! {
struct Flags: u8 {
const FLAG_PAD_LEN_HIGH = 0b10000000;
const FLAG_PAD_LEN_LOW = 0b01000000;
const FLAG_SIGNED = 0b00100000;
}
}
// number of bytes of padding length (in the range 0..4)
fn padding_length_bytes(flags: Flags) -> usize {
match (flags & FLAG_PAD_LEN_HIGH, flags & FLAG_PAD_LEN_LOW) {
(FLAG_PAD_LEN_HIGH, FLAG_PAD_LEN_LOW) => 3,
(FLAG_PAD_LEN_HIGH, _) => 2,
(_, FLAG_PAD_LEN_LOW) => 1,
(_, _) => 0,
}
}
// how many bytes are necessary to encode the given length. Range 0..4.
// `None` if too large.
fn num_padding_length_bytes(padding_len: usize) -> Option<usize> {
let bits = 64 - (padding_len as u64).leading_zeros();
match bits {
0 => Some(0),
0 ... 8 => Some(1),
0 ... 16 => Some(2),
0 ... 24 => Some(3),
_ => None,
}
}
/// Parameters for encoding a standard payload.
pub struct EncodeParams<'a> {
/// Message to encode.
pub message: &'a [u8],
/// Padding bytes. Maximum padding allowed is 65536 bytes.
pub padding: Option<&'a [u8]>,
/// Private key to sign with.
pub sign_with: Option<&'a Secret>,
}
impl<'a> Default for EncodeParams<'a> {
fn default() -> Self {
EncodeParams {
message: &[],
padding: None,
sign_with: None,
}
}
}
/// Parameters for decoding a standard payload.
pub struct Decoded<'a> {
/// Decoded message.
pub message: &'a [u8],
/// optional padding.
pub padding: Option<&'a [u8]>,
/// Recovered signature.
pub from: Option<Public>,
}
/// Encode using provided parameters.
pub fn encode(params: EncodeParams) -> Result<Vec<u8>, &'static str> {
const VEC_WRITE_INFALLIBLE: &'static str = "writing to a Vec<u8> can never fail; qed";
let padding_len = params.padding.map_or(0, |x| x.len());
let padding_len_bytes = num_padding_length_bytes(padding_len)
.ok_or_else(|| "padding size too long")?;
let signature = params.sign_with.map(|secret| {
let hash = H256(keccak256(params.message));
::ethkey::sign(secret, &hash)
});
let signature = match signature {
Some(Ok(sig)) => Some(sig),
Some(Err(_)) => return Err("invalid signing key provided"),
None => None,
};
let (flags, plaintext_size) = {
let mut flags = Flags::empty();
// 1 byte each for flags and version.
let mut plaintext_size = 2
+ padding_len_bytes
+ padding_len
+ params.message.len();
flags.bits = (padding_len_bytes << 6) as u8;
debug_assert_eq!(padding_length_bytes(flags), padding_len_bytes);
if let Some(ref sig) = signature {
plaintext_size += sig.len();
flags |= FLAG_SIGNED;
}
(flags, plaintext_size)
};
let mut plaintext = Vec::with_capacity(plaintext_size);
plaintext.push(STANDARD_PAYLOAD_VERSION);
plaintext.push(flags.bits);
if let Some(padding) = params.padding {
plaintext.write_uint::<BigEndian>(padding_len as u64, padding_len_bytes)
.expect(VEC_WRITE_INFALLIBLE);
plaintext.extend(padding)
}
if let Some(signature) = signature {
plaintext.extend(signature.r());
plaintext.extend(signature.s());
plaintext.push(signature.v());
}
plaintext.extend(params.message);
Ok(plaintext)
}
/// Decode using provided parameters
pub fn decode(payload: &[u8]) -> Result<Decoded, &'static str> {
let mut offset = 0;
let (padding, signature) = {
// use a closure for reading slices since std::io::Read would require
// us to copy.
let mut next_slice = |len| {
let end = offset + len;
if payload.len() >= end {
let slice = &payload[offset .. end];
offset = end;
Ok(slice)
} else {
return Err("unexpected end of payload")
}
};
if next_slice(1)?[0] != STANDARD_PAYLOAD_VERSION {
return Err("unknown payload version.");
}
let flags = Flags::from_bits_truncate(next_slice(1)?[0]);
let padding_len_bytes = padding_length_bytes(flags);
let padding = if padding_len_bytes != 0 {
let padding_len = BigEndian::read_uint(
next_slice(padding_len_bytes)?,
padding_len_bytes,
);
Some(next_slice(padding_len as usize)?)
} else {
None
};
let signature = if flags & FLAG_SIGNED == FLAG_SIGNED {
let slice = next_slice(SIGNATURE_LEN)?;
let mut arr = [0; SIGNATURE_LEN];
arr.copy_from_slice(slice);
let signature = ::ethkey::Signature::from(arr);
let not_rsv = signature.r() != &slice[..32]
|| signature.s() != &slice[32..64]
|| signature.v() != slice[64];
if not_rsv {
return Err("signature not in RSV format");
} else {
Some(signature)
}
} else {
None
};
(padding, signature)
};
// remaining data is the message.
let message = &payload[offset..];
let from = match signature {
None => None,
Some(sig) => {
let hash = H256(keccak256(message));
Some(::ethkey::recover(&sig, &hash).map_err(|_| "invalid signature")?)
}
};
Ok(Decoded {
message: message,
padding: padding,
from: from,
})
}
#[cfg(test)]
mod tests {
use ethkey::{Generator, Random};
use super::*;
#[test]
fn padding_len_bytes_sanity() {
const U24_MAX: usize = (1 << 24) - 1;
assert_eq!(padding_length_bytes(FLAG_PAD_LEN_HIGH | FLAG_PAD_LEN_LOW), 3);
assert_eq!(padding_length_bytes(FLAG_PAD_LEN_HIGH), 2);
assert_eq!(padding_length_bytes(FLAG_PAD_LEN_LOW), 1);
assert_eq!(padding_length_bytes(Flags::empty()), 0);
assert!(num_padding_length_bytes(u32::max_value() as _).is_none());
assert!(num_padding_length_bytes(U24_MAX + 1).is_none());
assert_eq!(num_padding_length_bytes(U24_MAX), Some(3));
assert_eq!(num_padding_length_bytes(u16::max_value() as usize + 1), Some(3));
assert_eq!(num_padding_length_bytes(u16::max_value() as usize), Some(2));
assert_eq!(num_padding_length_bytes(u8::max_value() as usize + 1), Some(2));
assert_eq!(num_padding_length_bytes(u8::max_value() as usize), Some(1));
assert_eq!(num_padding_length_bytes(1), Some(1));
assert_eq!(num_padding_length_bytes(0), Some(0));
}
#[test]
fn encode_decode_roundtrip() {
let message = [1, 2, 3, 4, 5];
let encoded = encode(EncodeParams {
message: &message,
padding: None,
sign_with: None,
}).unwrap();
let decoded = decode(&encoded).unwrap();
assert_eq!(message, decoded.message);
}
#[test]
fn encode_empty() {
let encoded = encode(EncodeParams {
message: &[],
padding: None,
sign_with: None,
}).unwrap();
let decoded = decode(&encoded).unwrap();
assert!(decoded.message.is_empty());
}
#[test]
fn encode_with_signature() {
let key_pair = Random.generate().unwrap();
let message = [1, 3, 5, 7, 9];
let encoded = encode(EncodeParams {
message: &message,
padding: None,
sign_with: Some(key_pair.secret()),
}).unwrap();
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded.message, message);
assert_eq!(decoded.from, Some(key_pair.public().clone()));
assert!(decoded.padding.is_none());
}
#[test]
fn encode_with_padding() {
let message = [1, 3, 5, 7, 9];
let padding = [0xff; 1024 - 5];
let encoded = encode(EncodeParams {
message: &message,
padding: Some(&padding),
sign_with: None,
}).unwrap();
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded.message, message);
assert_eq!(decoded.padding, Some(&padding[..]));
assert!(decoded.from.is_none());
}
#[test]
fn encode_with_padding_and_signature() {
let key_pair = Random.generate().unwrap();
let message = [1, 3, 5, 7, 9];
let padding = [0xff; 1024 - 5];
let encoded = encode(EncodeParams {
message: &message,
padding: Some(&padding),
sign_with: Some(key_pair.secret()),
}).unwrap();
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded.message, message);
assert_eq!(decoded.padding, Some(&padding[..]));
assert_eq!(decoded.from, Some(key_pair.public().clone()));
}
}

298
whisper/src/rpc/types.rs Normal file
View File

@ -0,0 +1,298 @@
// 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/>.
//! Types for Whisper RPC.
use std::fmt;
use std::ops::Deref;
use bigint::hash::*;
use hex::{ToHex, FromHex};
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::{Error, Visitor};
/// Helper trait for generic hex bytes encoding.
pub trait HexEncodable: Sized + ::std::ops::Deref<Target=[u8]> {
fn from_bytes(bytes: Vec<u8>) -> Option<Self>;
}
impl HexEncodable for Vec<u8> {
fn from_bytes(bytes: Vec<u8>) -> Option<Self> { Some(bytes) }
}
macro_rules! impl_hex_for_hash {
($($t: ident)*) => {
$(
impl HexEncodable for $t {
fn from_bytes(bytes: Vec<u8>) -> Option<Self> {
if bytes.len() != $t::len() {
None
} else {
Some($t::from_slice(&bytes))
}
}
}
)*
}
}
impl_hex_for_hash!(
H32 H64 H128 H256 H264 H512 H1024
);
/// Wrapper structure around hex-encoded data.
#[derive(Debug, PartialEq, Eq, Default, Hash, Clone)]
pub struct HexEncode<T>(pub T);
impl<T> From<T> for HexEncode<T> {
fn from(x: T) -> Self {
HexEncode(x)
}
}
impl<T> HexEncode<T> {
/// Create a new wrapper from the inner value.
pub fn new(x: T) -> Self { HexEncode(x) }
/// Consume the wrapper, yielding the inner value.
pub fn into_inner(self) -> T { self.0 }
}
impl<T> Deref for HexEncode<T> {
type Target = T;
fn deref(&self) -> &T { &self.0 }
}
/// Hex-encoded arbitrary-byte vector.
pub type Bytes = HexEncode<Vec<u8>>;
/// 32-byte local identity
pub type Identity = HexEncode<H256>;
/// Public key for ECIES, SECP256k1
pub type Public = HexEncode<::ethkey::Public>;
/// Unvalidated private key for ECIES, SECP256k1
pub type Private = HexEncode<H256>;
/// Abridged topic is four bytes.
// only used in tests for now.
#[cfg(test)]
pub type AbridgedTopic = HexEncode<H32>;
/// 32-byte AES key.
pub type Symmetric = HexEncode<H256>;
impl<T: HexEncodable> Serialize for HexEncode<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let data = &self.0[..];
let serialized = "0x".to_owned() + &data.to_hex();
serializer.serialize_str(serialized.as_ref())
}
}
impl<'a, T: 'a + HexEncodable> Deserialize<'a> for HexEncode<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'a>
{
deserializer.deserialize_any(HexEncodeVisitor::<T>(::std::marker::PhantomData))
}
}
// helper type for decoding anything from hex.
struct HexEncodeVisitor<T>(::std::marker::PhantomData<T>);
impl<'a, T: HexEncodable> Visitor<'a> for HexEncodeVisitor<T> {
type Value = HexEncode<T>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a 0x-prefixed, hex-encoded vector of bytes")
}
fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
let decoded = if value.len() >= 2 && &value[0..2] == "0x" && value.len() & 1 == 0 {
Ok(Vec::from_hex(&value[2..]).map_err(|_| Error::custom("invalid hex"))?)
} else {
Err(Error::custom("invalid format"))
};
decoded
.and_then(|x| T::from_bytes(x).ok_or(Error::custom("invalid format")))
.map(HexEncode)
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> where E: Error {
self.visit_str(value.as_ref())
}
}
/// Receiver of a message. Either a public key, identity (presumably symmetric),
/// or broadcast over the topics.
#[derive(Deserialize)]
pub enum Receiver {
#[serde(rename="public")]
Public(Public),
#[serde(rename="identity")]
Identity(Identity),
}
/// A request to post a message to the whisper network.
#[derive(Deserialize)]
pub struct PostRequest {
/// Receiver of the message. Either a public key or
/// an identity. If the identity is symmetric, it will
/// encrypt to that identity.
///
/// If the receiver is missing, this will be a broadcast message.
pub to: Option<Receiver>,
/// Sender of the message.
///
/// If present, the payload will be signed by this
/// identity. The call will fail if the whisper node doesn't store the
/// signing key for this identity.
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<Identity>,
/// Full topics to identify a message by.
/// At least one topic must be specified if the receiver is
/// not specified.
pub topics: Vec<Bytes>,
/// Payload of the message
pub payload: Bytes,
/// Optional padding of the message. No larger than 2^24 - 1.
pub padding: Option<Bytes>,
/// Priority of the message: how many milliseconds to spend doing PoW
pub priority: u64,
/// Time-To-Live of the message in seconds.
pub ttl: u64,
}
/// Request for filter or subscription creation.
#[derive(Deserialize)]
pub struct FilterRequest {
/// ID of key used for decryption.
///
/// If this identity is removed, then no further messages will be returned.
///
/// If optional, this will listen for broadcast messages.
#[serde(rename = "decryptWith")]
pub decrypt_with: Option<Identity>,
/// Accept only messages signed by given public key.
pub from: Option<Public>,
/// Possible topics. Cannot be empty if the identity is `None`
pub topics: Vec<Bytes>,
}
/// A message captured by a filter or subscription.
#[derive(Serialize, Clone)]
pub struct FilterItem {
/// Public key that signed this message.
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<Public>,
/// Identity of recipient. If the filter wasn't registered with a
/// recipient, this will be `None`.
#[serde(skip_serializing_if = "Option::is_none")]
pub recipient: Option<Identity>,
/// Time to live in seconds.
pub ttl: u64,
/// Abridged topics that matched the filter.
pub topics: Vec<Bytes>,
/// Unix timestamp of the message generation.
pub timestamp: u64,
/// Decrypted/Interpreted payload.
pub payload: Bytes,
/// Optional padding data.
#[serde(skip_serializing_if = "Option::is_none")]
pub padding: Option<Bytes>,
}
/// Whisper node info.
#[derive(Serialize)]
pub struct NodeInfo {
/// min PoW to be accepted into the local pool.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "minPow")]
pub required_pow: Option<f64>,
/// Number of messages in the pool.
pub messages: usize,
/// Memory used by messages in the pool.
pub memory: usize,
/// Target memory of the pool.
#[serde(rename = "targetMemory")]
pub target_memory: usize,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
use hex::FromHex;
#[test]
fn test_bytes_serialize() {
let bytes = Bytes::new(Vec::from_hex("0123456789abcdef").unwrap());
let serialized = serde_json::to_string(&bytes).unwrap();
assert_eq!(serialized, r#""0x0123456789abcdef""#);
}
#[test]
fn test_bytes_deserialize() {
let bytes2: Result<Bytes, serde_json::Error> = serde_json::from_str(r#""0x123""#);
let bytes3: Result<Bytes, serde_json::Error> = serde_json::from_str(r#""0xgg""#);
let bytes4: Bytes = serde_json::from_str(r#""0x""#).unwrap();
let bytes5: Bytes = serde_json::from_str(r#""0x12""#).unwrap();
let bytes6: Bytes = serde_json::from_str(r#""0x0123""#).unwrap();
assert!(bytes2.is_err());
assert!(bytes3.is_err());
assert_eq!(bytes4, Bytes::new(vec![]));
assert_eq!(bytes5, Bytes::new(vec![0x12]));
assert_eq!(bytes6, Bytes::new(vec![0x1, 0x23]));
}
#[test]
fn deserialize_topic() {
let topic = AbridgedTopic::new([1, 2, 3, 15].into());
let topic1: Result<AbridgedTopic, _> = serde_json::from_str(r#""0x010203""#);
let topic2: Result<AbridgedTopic, _> = serde_json::from_str(r#""0102030F""#);
let topic3: AbridgedTopic = serde_json::from_str(r#""0x0102030F""#).unwrap();
assert!(topic1.is_err());
assert!(topic2.is_err());
assert_eq!(topic3, topic);
}
}