From 63f1ca924388b44da300c10f10acb0192252bb56 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Tue, 21 Mar 2017 20:48:59 +0000 Subject: [PATCH 01/41] [ci skip] js-precompiled 20170321-204617 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e61658e16..a0ad6bede 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1700,7 +1700,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#47da49294ad958933e85a9c4f0f2bb4df5dc47de" +source = "git+https://github.com/ethcore/js-precompiled.git#24a28bd6f55c3540f18e14ec00fbb8f207be25d4" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 1e770ac34..eac319740 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.22", + "version": "1.7.23", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From d530cc86f36a8fac940d4fd1c86bfbc375f0deea Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 22 Mar 2017 06:23:40 +0100 Subject: [PATCH 02/41] splitting part of util into smaller crates (#4956) * split path module from util * moved RotatingLogger from util to logger crate * fix tests * fix tests * use only one version of ansi_term crate --- Cargo.lock | 40 +++++++++++++++-------- Cargo.toml | 3 +- ethcore/Cargo.toml | 1 + ethcore/src/json_tests/chain.rs | 2 +- ethcore/src/json_tests/state.rs | 2 +- ethcore/src/lib.rs | 1 + ethcore/src/miner/price_info.rs | 2 +- ethcore/src/miner/transaction_queue.rs | 1 + ethcore/src/state/mod.rs | 2 +- ethcore/src/state_db.rs | 2 +- logger/Cargo.toml | 4 ++- logger/src/lib.rs | 12 +++++-- util/src/log.rs => logger/src/rotating.rs | 1 - parity/configuration.rs | 4 +-- parity/helpers.rs | 5 +-- parity/main.rs | 1 + parity/rpc_apis.rs | 2 +- parity/run.rs | 4 +-- parity/signer.rs | 2 +- rpc/Cargo.toml | 1 + rpc/src/lib.rs | 1 + rpc/src/v1/impls/light/parity.rs | 2 +- rpc/src/v1/impls/parity.rs | 3 +- rpc/src/v1/tests/mocked/parity.rs | 2 +- rpc/src/v1/types/confirmations.rs | 2 +- rpc/src/v1/types/transaction_request.rs | 2 +- stratum/Cargo.toml | 1 + stratum/src/lib.rs | 29 ++-------------- updater/Cargo.toml | 1 + updater/src/lib.rs | 1 + updater/src/updater.rs | 2 +- util/Cargo.toml | 5 ++- util/network/Cargo.toml | 4 ++- util/network/src/host.rs | 2 +- util/network/src/lib.rs | 2 ++ util/network/src/tests.rs | 2 +- util/path/Cargo.toml | 6 ++++ util/{src/path.rs => path/src/lib.rs} | 0 util/src/journaldb/earlymergedb.rs | 2 +- util/src/journaldb/overlayrecentdb.rs | 2 +- util/src/lib.rs | 8 ++--- util/src/trie/triedbmut.rs | 2 +- 42 files changed, 93 insertions(+), 80 deletions(-) rename util/src/log.rs => logger/src/rotating.rs (98%) create mode 100644 util/path/Cargo.toml rename util/{src/path.rs => path/src/lib.rs} (100%) diff --git a/Cargo.lock b/Cargo.lock index a0ad6bede..d039c1646 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,7 +2,7 @@ name = "parity" version = "1.7.0" dependencies = [ - "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)", @@ -39,6 +39,7 @@ dependencies = [ "parity-reactor 0.1.0", "parity-rpc-client 1.4.0", "parity-updater 1.7.0", + "path 0.1.0", "regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", "rpassword 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -72,7 +73,7 @@ dependencies = [ [[package]] name = "ansi_term" -version = "0.7.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -93,10 +94,10 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.3.16" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nodrop 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -347,7 +348,7 @@ name = "eth-secp256k1" version = "0.5.6" source = "git+https://github.com/ethcore/rust-secp256k1#98ad9b9ecae44a563efdd64273bcebc6b4ed81c6" dependencies = [ - "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -394,6 +395,7 @@ dependencies = [ "ethcore-ipc 1.7.0", "ethcore-ipc-codegen 1.7.0", "ethcore-ipc-nano 1.7.0", + "ethcore-logger 1.7.0", "ethcore-stratum 1.7.0", "ethcore-util 1.7.0", "ethjson 0.1.0", @@ -570,11 +572,13 @@ dependencies = [ name = "ethcore-logger" version = "1.7.0" dependencies = [ + "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ethcore-util 1.7.0", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.68 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -583,10 +587,11 @@ dependencies = [ name = "ethcore-network" version = "1.7.0" dependencies = [ - "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-devtools 1.7.0", "ethcore-io 1.7.0", + "ethcore-logger 1.7.0", "ethcore-util 1.7.0", "ethcrypto 0.1.0", "ethkey 0.2.0", @@ -595,6 +600,7 @@ dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.1 (git+https://github.com/ethcore/mio)", "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "path 0.1.0", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -615,6 +621,7 @@ dependencies = [ "ethcore-io 1.7.0", "ethcore-ipc 1.7.0", "ethcore-light 1.7.0", + "ethcore-logger 1.7.0", "ethcore-util 1.7.0", "ethcrypto 0.1.0", "ethjson 0.1.0", @@ -687,6 +694,7 @@ dependencies = [ "ethcore-ipc 1.7.0", "ethcore-ipc-codegen 1.7.0", "ethcore-ipc-nano 1.7.0", + "ethcore-logger 1.7.0", "ethcore-util 1.7.0", "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", @@ -703,8 +711,7 @@ dependencies = [ name = "ethcore-util" version = "1.7.0" dependencies = [ - "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "elastic-array 0.6.0 (git+https://github.com/ethcore/elastic-array)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -712,9 +719,9 @@ dependencies = [ "ethcore-bigint 0.1.2", "ethcore-bloom-journal 0.1.0", "ethcore-devtools 1.7.0", + "ethcore-logger 1.7.0", "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1457,7 +1464,7 @@ dependencies = [ [[package]] name = "nodrop" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "odds 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1719,6 +1726,7 @@ dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-hash-fetch 1.7.0", "parity-reactor 0.1.0", + "path 0.1.0", "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1743,6 +1751,10 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "path" +version = "0.1.0" + [[package]] name = "phf" version = "0.7.14" @@ -2601,10 +2613,10 @@ dependencies = [ [metadata] "checksum advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a" "checksum aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67077478f0a03952bed2e6786338d400d40c25e9836e08ad50af96607317fd03" -"checksum ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1f46cd5b1d660c938e3f92dfe7a73d832b3281479363dd0cd9c1c2fbf60f7962" +"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" "checksum app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7d1c0d48a81bbb13043847f957971f4d87c81542d80ece5e84ba3cba4058fd4" -"checksum arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "16e3bdb2f54b3ace0285975d59a97cf8ed3855294b2b6bc651fcf22a9c352975" +"checksum arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d89f1b0e242270b5b797778af0c8d182a1a2ccac5d8d6fadf414223cc0fab096" "checksum aster 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfdf7355d9db158df68f976ed030ab0f6578af811f5a7bb6dcf221ec24e0e0" "checksum base-x 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f59103b47307f76e03bef1633aec7fa9e29bfb5aa6daf5a334f94233c71f6c1" "checksum base32 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9605ba46d61df0410d8ac686b0007add8172eba90e8e909c347856fe794d8c" @@ -2699,7 +2711,7 @@ dependencies = [ "checksum nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f05c2fc965fc1cd6b73fa57fa7b89f288178737f2f3ce9e63e4a6a141189000e" "checksum nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7bb1da2be7da3cbffda73fc681d509ffd9e665af478d2bee1907cee0bc64b2" "checksum nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d95c5fa8b641c10ad0b8887454ebaafa3c92b5cd5350f8fc693adafd178e7b" -"checksum nodrop 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4d9a22dbcebdeef7bf275cbf444d6521d4e7a2fee187b72d80dba0817120dd8f" +"checksum nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "52cd74cd09beba596430cc6e3091b74007169a56246e1262f0ba451ea95117b2" "checksum nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6caab12c5f97aa316cb249725aa32115118e1522b445e26c257dd77cad5ffd4e" "checksum num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "c04bd954dbf96f76bab6e5bd6cef6f1ce1262d15268ce4f926d2b5b778fa7af2" "checksum num-bigint 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "41655c8d667be847a0b72fe0888857a7b3f052f691cf40852be5fcf87b274a65" diff --git a/Cargo.toml b/Cargo.toml index 099be68e5..f62f8b42f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ num_cpus = "1.2" number_prefix = "0.2" rpassword = "0.2.1" semver = "0.5" -ansi_term = "0.7" +ansi_term = "0.9" regex = "0.1" isatty = "0.1" toml = "0.2" @@ -50,6 +50,7 @@ parity-updater = { path = "updater" } parity-reactor = { path = "util/reactor" } parity-local-store = { path = "local-store" } ethcore-dapps = { path = "dapps", optional = true } +path = { path = "util/path" } clippy = { version = "0.0.103", optional = true} ethcore-secretstore = { path = "secret_store", optional = true } diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index d1c9d624b..17f79840e 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -43,6 +43,7 @@ rlp = { path = "../util/rlp" } ethcore-stratum = { path = "../stratum" } ethcore-bloom-journal = { path = "../util/bloom" } hardware-wallet = { path = "../hw" } +ethcore-logger = { path = "../logger" } stats = { path = "../util/stats" } num = "0.1" diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index 221fa1aba..987280bc1 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -25,7 +25,7 @@ use miner::Miner; use io::IoChannel; pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { - init_log(); + ::ethcore_logger::init_log(); let tests = ethjson::blockchain::Test::load(json_data).unwrap(); let mut failed = Vec::new(); diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index 5a6b1ccd0..6388daf94 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -22,7 +22,7 @@ use ethereum; use ethjson; pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { - init_log(); + ::ethcore_logger::init_log(); let tests = ethjson::state::Test::load(json_data).unwrap(); let mut failed = Vec::new(); let engine = match era { diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 75c8a80e1..df0738997 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -106,6 +106,7 @@ extern crate ethcore_stratum; extern crate ethabi; extern crate hardware_wallet; extern crate stats; +extern crate ethcore_logger; extern crate num; #[macro_use] diff --git a/ethcore/src/miner/price_info.rs b/ethcore/src/miner/price_info.rs index 4cc6fb36b..29994afb4 100644 --- a/ethcore/src/miner/price_info.rs +++ b/ethcore/src/miner/price_info.rs @@ -110,7 +110,7 @@ impl PriceInfo { fn should_get_price_info() { use std::sync::Arc; use std::time::Duration; - use util::log::init_log; + use ethcore_logger::init_log; use util::{Condvar, Mutex}; init_log(); diff --git a/ethcore/src/miner/transaction_queue.rs b/ethcore/src/miner/transaction_queue.rs index 9a3e6af1a..81c944e4d 100644 --- a/ethcore/src/miner/transaction_queue.rs +++ b/ethcore/src/miner/transaction_queue.rs @@ -2454,6 +2454,7 @@ pub mod test { #[test] fn should_replace_same_transaction_when_has_higher_fee() { + use ethcore_logger::init_log; init_log(); // given let mut txq = TransactionQueue::default(); diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index c34f43041..6e2a956ab 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -946,7 +946,7 @@ mod tests { use env_info::EnvInfo; use spec::*; use transaction::*; - use util::log::init_log; + use ethcore_logger::init_log; use trace::{FlatTrace, TraceError, trace}; use types::executed::CallType; diff --git a/ethcore/src/state_db.rs b/ethcore/src/state_db.rs index bfd1068ca..2831d2f9b 100644 --- a/ethcore/src/state_db.rs +++ b/ethcore/src/state_db.rs @@ -457,7 +457,7 @@ mod tests { use util::{U256, H256, Address, DBTransaction}; use tests::helpers::*; use state::{Account, Backend}; - use util::log::init_log; + use ethcore_logger::init_log; #[test] fn state_db_smoke() { diff --git a/logger/Cargo.toml b/logger/Cargo.toml index e0004cd48..b505355c8 100644 --- a/logger/Cargo.toml +++ b/logger/Cargo.toml @@ -8,11 +8,13 @@ authors = ["Parity Technologies "] [dependencies] log = "0.3" env_logger = "0.3" -ethcore-util = { path = "../util" } isatty = "0.1" lazy_static = "0.2" regex = "0.1" time = "0.1" +parking_lot = "0.3" +arrayvec = "0.3" +ansi_term = "0.9" [profile.release] debug = true diff --git a/logger/src/lib.rs b/logger/src/lib.rs index f9188d928..b492f4f78 100644 --- a/logger/src/lib.rs +++ b/logger/src/lib.rs @@ -16,7 +16,7 @@ //! Logger for parity executables -extern crate ethcore_util as util; +extern crate arrayvec; extern crate log as rlog; extern crate isatty; extern crate regex; @@ -24,6 +24,10 @@ extern crate env_logger; extern crate time; #[macro_use] extern crate lazy_static; +extern crate parking_lot; +extern crate ansi_term; + +mod rotating; use std::{env, thread, fs}; use std::sync::{Weak, Arc}; @@ -31,8 +35,10 @@ use std::io::Write; use isatty::{stderr_isatty, stdout_isatty}; use env_logger::LogBuilder; use regex::Regex; -use util::{Mutex, RotatingLogger} ; -use util::log::Colour; +use ansi_term::Colour; +use parking_lot::Mutex; + +pub use rotating::{RotatingLogger, init_log}; #[derive(Debug, PartialEq, Clone)] pub struct Config { diff --git a/util/src/log.rs b/logger/src/rotating.rs similarity index 98% rename from util/src/log.rs rename to logger/src/rotating.rs index 4e537603d..2d98ebcd0 100644 --- a/util/src/log.rs +++ b/logger/src/rotating.rs @@ -20,7 +20,6 @@ use std::env; use rlog::LogLevelFilter; use env_logger::LogBuilder; use arrayvec::ArrayVec; -pub use ansi_term::{Colour, Style}; use parking_lot::{RwLock, RwLockReadGuard}; diff --git a/parity/configuration.rs b/parity/configuration.rs index 9b491143d..a9a9159e1 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -22,7 +22,7 @@ use std::cmp::max; use cli::{Args, ArgsError}; use util::{Hashable, H256, U256, Uint, Bytes, version_data, Address}; use util::journaldb::Algorithm; -use util::log::Colour; +use util::Colour; use ethsync::{NetworkConfiguration, is_valid_node_url, AllowIP}; use ethcore::ethstore::ethkey::Secret; use ethcore::client::{VMType}; @@ -824,7 +824,7 @@ impl Configuration { } fn directories(&self) -> Directories { - use util::path; + use path; let local_path = default_local_path(); let base_path = self.args.flag_base_path.as_ref().map_or_else(|| default_data_path(), |s| s.clone()); diff --git a/parity/helpers.rs b/parity/helpers.rs index d691aabc6..e98c31ab7 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -18,7 +18,7 @@ use std::{io, env}; use std::io::{Write, BufReader, BufRead}; use std::time::Duration; use std::fs::File; -use util::{clean_0x, U256, Uint, Address, path, CompactionProfile}; +use util::{clean_0x, U256, Uint, Address, CompactionProfile}; use util::journaldb::Algorithm; use ethcore::client::{Mode, BlockId, VMType, DatabaseCompactionProfile, ClientConfig, VerifierType}; use ethcore::miner::{PendingSet, GasLimit, PrioritizationStrategy}; @@ -27,6 +27,7 @@ use dir::DatabaseDirectories; use upgrade::{upgrade, upgrade_data_paths}; use migration::migrate; use ethsync::is_valid_node_url; +use path; pub fn to_duration(s: &str) -> Result { to_seconds(s).map(Duration::from_secs) @@ -465,7 +466,7 @@ but the first password is trimmed #[test] #[cfg(not(windows))] fn test_geth_ipc_path() { - use util::path; + use path; assert_eq!(geth_ipc_path(true), path::ethereum::with_testnet("geth.ipc").to_str().unwrap().to_owned()); assert_eq!(geth_ipc_path(false), path::ethereum::with_default("geth.ipc").to_str().unwrap().to_owned()); } diff --git a/parity/main.rs b/parity/main.rs index 7d22be1c3..3e499d483 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -61,6 +61,7 @@ extern crate parity_reactor; extern crate parity_updater as updater; extern crate parity_local_store as local_store; extern crate rpc_cli; +extern crate path; #[macro_use] extern crate log as rlog; diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 469245c19..cc45e3f8b 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -33,7 +33,7 @@ use ethsync::{ManageNetwork, SyncProvider}; use hash_fetch::fetch::Client as FetchClient; use jsonrpc_core::{MetaIoHandler}; use updater::Updater; -use util::RotatingLogger; +use ethcore_logger::RotatingLogger; #[derive(Debug, PartialEq, Clone, Eq, Hash)] pub enum Api { diff --git a/parity/run.rs b/parity/run.rs index b3313c16e..f781a8774 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -20,9 +20,9 @@ use ctrlc::CtrlC; use fdlimit::raise_fd_limit; use ethcore_rpc::{NetworkSettings, informant, is_major_importing}; use ethsync::NetworkConfiguration; -use util::{Colour, version, RotatingLogger, Mutex, Condvar}; +use util::{Colour, version, Mutex, Condvar}; use io::{MayPanic, ForwardPanic, PanicHandler}; -use ethcore_logger::{Config as LogConfig}; +use ethcore_logger::{Config as LogConfig, RotatingLogger}; use ethcore::miner::{StratumOptions, Stratum}; use ethcore::client::{Client, Mode, DatabaseCompactionProfile, VMType, BlockChainClient}; use ethcore::service::ClientService; diff --git a/parity/signer.rs b/parity/signer.rs index 346276496..d69670204 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -29,7 +29,7 @@ use helpers::replace_home; use io::{ForwardPanic, PanicHandler}; use jsonrpc_core::reactor::{RpcHandler, Remote}; use rpc_apis; -use util::path::restrict_permissions_owner; +use path::restrict_permissions_owner; use util::H256; const CODES_FILENAME: &'static str = "authcodes"; diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 0b3cc4b82..d18393902 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -34,6 +34,7 @@ ethsync = { path = "../sync" } ethjson = { path = "../json" } ethcore-devtools = { path = "../devtools" } ethcore-light = { path = "../ethcore/light" } +ethcore-logger = { path = "../logger" } parity-updater = { path = "../updater" } rlp = { path = "../util/rlp" } fetch = { path = "../util/fetch" } diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 201f41c22..a00b138ac 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -34,6 +34,7 @@ extern crate ethstore; extern crate ethsync; extern crate ethash; extern crate ethcore_light as light; +extern crate ethcore_logger; extern crate transient_hashmap; extern crate jsonrpc_ipc_server as ipc; extern crate ethcore_ipc; diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 49421fd04..545935417 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -19,7 +19,7 @@ use std::sync::Arc; use std::collections::{BTreeMap, HashSet}; use futures::{future, Future, BoxFuture}; -use util::RotatingLogger; +use ethcore_logger::RotatingLogger; use util::misc::version_data; use crypto::ecies; diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index aee4dc3fd..2a7f85aa3 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -20,7 +20,8 @@ use std::str::FromStr; use std::collections::{BTreeMap, HashSet}; use futures::{future, Future, BoxFuture}; -use util::{RotatingLogger, Address}; +use ethcore_logger::RotatingLogger; +use util::Address; use util::misc::version_data; use crypto::ecies; diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 59cde8594..8acedc2d4 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::sync::Arc; -use util::log::RotatingLogger; +use ethcore_logger::RotatingLogger; use util::Address; use ethsync::ManageNetwork; use ethcore::account_provider::AccountProvider; diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index dd44e5750..f749df449 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -18,7 +18,7 @@ use std::fmt; use serde::{Serialize, Serializer}; -use util::log::Colour; +use util::Colour; use util::bytes::ToPretty; use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes, TransactionCondition, Origin}; diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index 5839a0de8..242c02360 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -18,7 +18,7 @@ use v1::types::{Bytes, H160, U256, TransactionCondition}; use v1::helpers; -use util::log::Colour; +use util::Colour; use std::fmt; diff --git a/stratum/Cargo.toml b/stratum/Cargo.toml index 1b309985a..df984bef1 100644 --- a/stratum/Cargo.toml +++ b/stratum/Cargo.toml @@ -24,6 +24,7 @@ semver = "0.5" ethcore-ipc-nano = { path = "../ipc/nano" } futures = "0.1" tokio-core = "0.1" +ethcore-logger = { path = "../logger" } [profile.release] debug = true diff --git a/stratum/src/lib.rs b/stratum/src/lib.rs index 59964773c..5a7d50613 100644 --- a/stratum/src/lib.rs +++ b/stratum/src/lib.rs @@ -24,6 +24,7 @@ extern crate ethcore_util as util; extern crate ethcore_ipc as ipc; extern crate semver; extern crate futures; +extern crate ethcore_logger; #[cfg(test)] extern crate tokio_core; extern crate ethcore_devtools as devtools; @@ -323,6 +324,8 @@ mod tests { use tokio_core::io; use futures::{Future, future}; + use ethcore_logger::init_log; + pub struct VoidManager; impl JobDispatcher for VoidManager { @@ -331,32 +334,6 @@ mod tests { } } - lazy_static! { - static ref LOG_DUMMY: bool = { - use log::LogLevelFilter; - use env_logger::LogBuilder; - use std::env; - - let mut builder = LogBuilder::new(); - builder.filter(None, LogLevelFilter::Info); - - if let Ok(log) = env::var("RUST_LOG") { - builder.parse(&log); - } - - if let Ok(_) = builder.init() { - println!("logger initialized"); - } - true - }; - } - - /// Intialize log with default settings - #[cfg(test)] - fn init_log() { - let _ = *LOG_DUMMY; - } - fn dummy_request(addr: &SocketAddr, data: &str) -> Vec { let mut core = Core::new().expect("Tokio Core should be created with no errors"); let mut buffer = vec![0u8; 2048]; diff --git a/updater/Cargo.toml b/updater/Cargo.toml index c0738cc59..53088d030 100644 --- a/updater/Cargo.toml +++ b/updater/Cargo.toml @@ -20,6 +20,7 @@ parity-hash-fetch = { path = "../hash-fetch" } ipc-common-types = { path = "../ipc-common-types" } ethcore-ipc = { path = "../ipc/rpc" } parity-reactor = { path = "../util/reactor" } +path = { path = "../util/path" } [profile.release] debug = true diff --git a/updater/src/lib.rs b/updater/src/lib.rs index 7d7146d7e..730af956d 100644 --- a/updater/src/lib.rs +++ b/updater/src/lib.rs @@ -26,6 +26,7 @@ extern crate ethsync; extern crate ethcore_ipc as ipc; extern crate target_info; extern crate parity_reactor; +extern crate path; mod updater; mod operations; diff --git a/updater/src/updater.rs b/updater/src/updater.rs index c2bc98273..5a4e2e1c9 100644 --- a/updater/src/updater.rs +++ b/updater/src/updater.rs @@ -21,7 +21,7 @@ use std::path::{PathBuf}; use target_info::Target; use util::misc; use ipc_common_types::{VersionInfo, ReleaseTrack}; -use util::path::restrict_permissions_owner; +use path::restrict_permissions_owner; use util::{Address, H160, H256, Mutex, Bytes}; use ethsync::{SyncProvider}; use ethcore::client::{BlockId, BlockChainClient, ChainNotify}; diff --git a/util/Cargo.toml b/util/Cargo.toml index d32d2519a..df34ccbfb 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -11,11 +11,9 @@ build = "build.rs" log = "0.3" env_logger = "0.3" rustc-serialize = "0.3" -arrayvec = "0.3" rand = "0.3.12" time = "0.1.34" rocksdb = { git = "https://github.com/ethcore/rust-rocksdb" } -lazy_static = "0.2" eth-secp256k1 = { git = "https://github.com/ethcore/rust-secp256k1" } rust-crypto = "0.2.34" elastic-array = { git = "https://github.com/ethcore/elastic-array" } @@ -32,11 +30,12 @@ ethcore-bigint = { path = "bigint" } parking_lot = "0.3" using_queue = { path = "using_queue" } table = { path = "table" } -ansi_term = "0.7" +ansi_term = "0.9" tiny-keccak= "1.0" ethcore-bloom-journal = { path = "bloom" } regex = "0.1" lru-cache = "0.1.0" +ethcore-logger = { path = "../logger" } [features] default = [] diff --git a/util/network/Cargo.toml b/util/network/Cargo.toml index 7fced467a..2f1ac81d3 100644 --- a/util/network/Cargo.toml +++ b/util/network/Cargo.toml @@ -19,7 +19,7 @@ clippy = { version = "0.0.103", optional = true} igd = "0.5.0" libc = "0.2.7" parking_lot = "0.3" -ansi_term = "0.7" +ansi_term = "0.9" rustc-serialize = "0.3" ethcore-io = { path = "../io" } ethcore-util = { path = ".." } @@ -27,6 +27,8 @@ ethcore-devtools = { path = "../../devtools" } ethkey = { path = "../../ethkey" } ethcrypto = { path = "../../ethcrypto" } rlp = { path = "../rlp" } +path = { path = "../path" } +ethcore-logger = { path ="../../logger" } [features] default = [] diff --git a/util/network/src/host.rs b/util/network/src/host.rs index b64301369..bb4b4fb1e 100644 --- a/util/network/src/host.rs +++ b/util/network/src/host.rs @@ -40,7 +40,7 @@ use node_table::*; use stats::NetworkStats; use discovery::{Discovery, TableUpdates, NodeEntry}; use ip_utils::{map_external_address, select_public_address}; -use util::path::restrict_permissions_owner; +use path::restrict_permissions_owner; use parking_lot::{Mutex, RwLock}; type Slab = ::slab::Slab; diff --git a/util/network/src/lib.rs b/util/network/src/lib.rs index 862200fa3..c4cc524f4 100644 --- a/util/network/src/lib.rs +++ b/util/network/src/lib.rs @@ -71,6 +71,8 @@ extern crate ethkey; extern crate ethcrypto as crypto; extern crate rlp; extern crate bytes; +extern crate path; +extern crate ethcore_logger; #[macro_use] extern crate log; diff --git a/util/network/src/tests.rs b/util/network/src/tests.rs index fba8f6eb4..692dd94a0 100644 --- a/util/network/src/tests.rs +++ b/util/network/src/tests.rs @@ -98,7 +98,7 @@ fn net_service() { #[test] fn net_connect() { - ::util::log::init_log(); + ::ethcore_logger::init_log(); let key1 = Random.generate().unwrap(); let mut config1 = NetworkConfiguration::new_local(); config1.use_secret = Some(key1.secret().clone()); diff --git a/util/path/Cargo.toml b/util/path/Cargo.toml new file mode 100644 index 000000000..7df3917b1 --- /dev/null +++ b/util/path/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "path" +version = "0.1.0" +authors = ["debris "] + +[dependencies] diff --git a/util/src/path.rs b/util/path/src/lib.rs similarity index 100% rename from util/src/path.rs rename to util/path/src/lib.rs diff --git a/util/src/journaldb/earlymergedb.rs b/util/src/journaldb/earlymergedb.rs index f484a623a..bd05d5fc1 100644 --- a/util/src/journaldb/earlymergedb.rs +++ b/util/src/journaldb/earlymergedb.rs @@ -553,7 +553,7 @@ mod tests { use hashdb::{HashDB, DBValue}; use super::*; use super::super::traits::JournalDB; - use log::init_log; + use ethcore_logger::init_log; use kvdb::{DatabaseConfig}; #[test] diff --git a/util/src/journaldb/overlayrecentdb.rs b/util/src/journaldb/overlayrecentdb.rs index 90f7acfcf..e8095e71e 100644 --- a/util/src/journaldb/overlayrecentdb.rs +++ b/util/src/journaldb/overlayrecentdb.rs @@ -456,7 +456,7 @@ mod tests { use common::*; use super::*; use hashdb::{HashDB, DBValue}; - use log::init_log; + use ethcore_logger::init_log; use journaldb::JournalDB; use kvdb::Database; diff --git a/util/src/lib.rs b/util/src/lib.rs index 4e46dbc24..93e03c8b1 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -93,7 +93,6 @@ extern crate rocksdb; extern crate env_logger; extern crate crypto as rcrypto; extern crate secp256k1; -extern crate arrayvec; extern crate elastic_array; extern crate time; extern crate ethcore_devtools as devtools; @@ -108,9 +107,8 @@ extern crate regex; extern crate lru_cache; extern crate heapsize; extern crate itertools; +extern crate ethcore_logger; -#[macro_use] -extern crate lazy_static; #[macro_use] extern crate log as rlog; @@ -137,8 +135,6 @@ pub mod trie; pub mod nibbleslice; pub mod nibblevec; pub mod semantic_version; -pub mod log; -pub mod path; pub mod snappy; pub mod cache; mod timer; @@ -153,9 +149,9 @@ pub use triehash::*; pub use trie::{Trie, TrieMut, TrieDB, TrieDBMut, TrieFactory, TrieError, SecTrieDB, SecTrieDBMut}; pub use nibbleslice::*; pub use semantic_version::*; -pub use log::*; pub use kvdb::*; pub use timer::*; +pub use ansi_term::{Colour, Style}; /// 160-bit integer representing account address pub type Address = H160; diff --git a/util/src/trie/triedbmut.rs b/util/src/trie/triedbmut.rs index 10d7f3aa6..c5f2a739b 100644 --- a/util/src/trie/triedbmut.rs +++ b/util/src/trie/triedbmut.rs @@ -958,7 +958,7 @@ mod tests { #[test] fn playpen() { - ::log::init_log(); + ::ethcore_logger::init_log(); let mut seed = H256::new(); for test_i in 0..10 { From 7e87e9e8ad14bbb9c93d7ce5589cff439e13d8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 22 Mar 2017 07:02:14 +0100 Subject: [PATCH 03/41] Updating JSON-RPC crates (#4934) * New version of jsonrpc. * Better invalid encoding messages * Fixing deprecated methods of tokio_core * Using dedicated branch for jsonrpc * Bump --- Cargo.lock | 397 +++++++++++++-------- Cargo.toml | 6 +- dapps/Cargo.toml | 29 +- dapps/src/api/api.rs | 49 ++- dapps/src/lib.rs | 92 ++--- dapps/src/router/host_validation.rs | 15 +- dapps/src/router/mod.rs | 30 +- dapps/src/rpc.rs | 52 ++- dapps/src/tests/api.rs | 46 +-- dapps/src/tests/helpers/fetch.rs | 2 +- dapps/src/tests/helpers/mod.rs | 38 +- dapps/src/tests/redirection.rs | 22 +- dapps/src/tests/rpc.rs | 5 +- ethcore/Cargo.toml | 5 +- ethcore/light/src/on_demand/mod.rs | 21 +- ethcore/src/spec/spec.rs | 2 +- ipfs/Cargo.toml | 3 +- ipfs/src/error.rs | 8 +- ipfs/src/lib.rs | 83 ++--- json/src/hash.rs | 8 +- json/src/uint.rs | 10 +- parity/dapps.rs | 27 +- parity/ipfs.rs | 69 ++-- parity/rpc.rs | 43 ++- parity/run.rs | 3 - parity/signer.rs | 17 +- rpc/Cargo.toml | 21 +- rpc/src/lib.rs | 113 +++--- rpc/src/v1/helpers/mod.rs | 1 + rpc/src/v1/helpers/oneshot.rs | 67 ++++ rpc/src/v1/impls/signing.rs | 45 ++- rpc/src/v1/tests/helpers/fetch.rs | 2 +- rpc/src/v1/tests/mocked/eth.rs | 6 +- rpc/src/v1/tests/mocked/parity.rs | 8 +- rpc/src/v1/tests/mocked/parity_accounts.rs | 2 +- rpc/src/v1/tests/mocked/traces.rs | 6 +- rpc/src/v1/types/hash.rs | 9 +- rpc/src/v1/types/uint.rs | 16 +- rpc_client/Cargo.toml | 8 +- rpc_client/src/client.rs | 20 +- signer/Cargo.toml | 5 +- signer/src/lib.rs | 11 +- signer/src/tests/mod.rs | 9 +- signer/src/ws_server/mod.rs | 26 +- signer/src/ws_server/session.rs | 21 +- stratum/Cargo.toml | 7 +- stratum/src/lib.rs | 28 +- util/reactor/src/lib.rs | 11 +- 48 files changed, 847 insertions(+), 677 deletions(-) create mode 100644 rpc/src/v1/helpers/oneshot.rs diff --git a/Cargo.lock b/Cargo.lock index d039c1646..819226c00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,7 +5,7 @@ dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)", + "ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)", "daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -27,9 +27,9 @@ dependencies = [ "ethsync 1.7.0", "evmbin 0.1.0", "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", + "hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)", "isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -194,6 +194,15 @@ dependencies = [ "stable-heap 0.1.0 (git+https://github.com/carllerche/stable-heap?rev=3c5cd1ca47)", ] +[[package]] +name = "bytes" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cfg-if" version = "0.1.0" @@ -283,7 +292,7 @@ dependencies = [ [[package]] name = "ctrlc" version = "1.1.1" -source = "git+https://github.com/ethcore/rust-ctrlc.git#f4927770f89eca80ec250911eea3adcbf579ac48" +source = "git+https://github.com/paritytech/rust-ctrlc.git#b523017108bb2d571a7a69bd97bc406e63bc7a9d" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", @@ -403,7 +412,7 @@ dependencies = [ "ethstore 0.1.0", "evmjit 1.7.0", "hardware-wallet 1.7.0", - "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", + "hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -449,10 +458,10 @@ dependencies = [ "ethcore-rpc 1.7.0", "ethcore-util 1.7.0", "fetch 0.1.0", - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", - "jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -558,7 +567,7 @@ dependencies = [ "ethcore-ipc-codegen 1.7.0", "ethcore-network 1.7.0", "ethcore-util 1.7.0", - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -629,11 +638,11 @@ dependencies = [ "ethstore 0.1.0", "ethsync 1.7.0", "fetch 0.1.0", - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", - "jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", - "jsonrpc-ipc-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", - "jsonrpc-macros 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parity-reactor 0.1.0", @@ -660,7 +669,7 @@ dependencies = [ "ethcore-util 1.7.0", "ethcrypto 0.1.0", "ethkey 0.2.0", - "hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -676,13 +685,14 @@ dependencies = [ "ethcore-io 1.7.0", "ethcore-rpc 1.7.0", "ethcore-util 1.7.0", - "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-ui 1.7.0", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)", + "ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=mio-upstream-stable)", ] [[package]] @@ -696,15 +706,14 @@ dependencies = [ "ethcore-ipc-nano 1.7.0", "ethcore-logger 1.7.0", "ethcore-util 1.7.0", - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", - "jsonrpc-macros 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", - "jsonrpc-tcp-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-tcp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)", "semver 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -858,7 +867,7 @@ dependencies = [ name = "fetch" version = "0.1.0" dependencies = [ - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -877,11 +886,8 @@ dependencies = [ [[package]] name = "futures" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "futures-cpupool" @@ -889,7 +895,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -985,7 +991,7 @@ dependencies = [ [[package]] name = "hyper" version = "0.10.0-a.0" -source = "git+https://github.com/ethcore/hyper#453c683b52208fefc32d29e4ac7c863439b2321f" +source = "git+https://github.com/paritytech/hyper#453c683b52208fefc32d29e4ac7c863439b2321f" dependencies = [ "cookie 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1003,7 +1009,7 @@ dependencies = [ [[package]] name = "hyper" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1014,7 +1020,7 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1026,7 +1032,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1057,6 +1063,15 @@ name = "integer-encoding" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "iovec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "ipc-common-types" version = "1.7.0" @@ -1092,67 +1107,82 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-core" -version = "6.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6#86d7a89c85f324b5f6671315d9b71010ca995300" +version = "7.0.0" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed" dependencies = [ - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-http-server" -version = "6.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6#86d7a89c85f324b5f6671315d9b71010ca995300" +version = "7.0.0" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed" dependencies = [ - "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", - "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", + "hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-ipc-server" -version = "6.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6#86d7a89c85f324b5f6671315d9b71010ca995300" +version = "7.0.0" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed" dependencies = [ - "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", - "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-tokio-ipc 0.1.0 (git+https://github.com/nikvolf/parity-tokio-ipc)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-macros" -version = "6.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6#86d7a89c85f324b5f6671315d9b71010ca995300" +version = "7.0.0" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed" dependencies = [ - "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "jsonrpc-tcp-server" -version = "6.0.0" -source = "git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6#86d7a89c85f324b5f6671315d9b71010ca995300" +name = "jsonrpc-pubsub" +version = "7.0.0" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed" dependencies = [ - "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", - "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jsonrpc-server-utils" +version = "7.0.0" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed" +dependencies = [ + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jsonrpc-tcp-server" +version = "7.0.0" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7#707cf73a7b72f2eecbf3665c53b4159ec867cbed" +dependencies = [ + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1180,6 +1210,11 @@ name = "lazycell" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lazycell" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "libc" version = "0.2.21" @@ -1268,38 +1303,6 @@ dependencies = [ "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "mio" -version = "0.5.1" -source = "git+https://github.com/ethcore/mio?branch=v0.5.x#3842d3b250ffd7bd9b16f9586b875ddcbac2b0dd" -dependencies = [ - "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mio" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "mio" version = "0.6.0-dev" @@ -1308,7 +1311,7 @@ dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)", @@ -1318,13 +1321,13 @@ dependencies = [ [[package]] name = "mio" version = "0.6.1" -source = "git+https://github.com/ethcore/mio#ef182bae193a9c7457cd2cf661fcaffb226e3eef" +source = "git+https://github.com/ethcore/mio#15a577039bed3c72f2952459f8ad687a56f63e29" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1333,23 +1336,56 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mio-named-pipes" +version = "0.1.4" +source = "git+https://github.com/alexcrichton/mio-named-pipes#903dc2f7eac6700c62bfdda258a599db13a9228f" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-uds" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "miow" -version = "0.1.3" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1427,15 +1463,6 @@ dependencies = [ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "nix" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "nix" version = "0.6.0" @@ -1624,7 +1651,7 @@ dependencies = [ "ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore-util 1.7.0", "fetch 0.1.0", - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1640,8 +1667,7 @@ dependencies = [ "cid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "ethcore 1.7.0", "ethcore-util 1.7.0", - "hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)", - "jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", + "jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "multihash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", @@ -1666,8 +1692,8 @@ dependencies = [ name = "parity-reactor" version = "0.1.0" dependencies = [ - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1677,15 +1703,31 @@ dependencies = [ "ethcore-rpc 1.7.0", "ethcore-signer 1.7.0", "ethcore-util 1.7.0", - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)", + "ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=mio-upstream-stable)", +] + +[[package]] +name = "parity-tokio-ipc" +version = "0.1.0" +source = "git+https://github.com/nikvolf/parity-tokio-ipc#3d4234de6bdc78688ef803935111003080fd5375" +dependencies = [ + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-line 0.1.0 (git+https://github.com/tokio-rs/tokio-line)", + "tokio-named-pipes 0.1.0 (git+https://github.com/alexcrichton/tokio-named-pipes)", + "tokio-uds 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1916,7 +1958,7 @@ name = "reqwest" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2005,7 +2047,7 @@ dependencies = [ "ethcore-bigint 0.1.2", "ethcore-rpc 1.7.0", "ethcore-util 1.7.0", - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "parity-rpc-client 1.4.0", "rpassword 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2205,11 +2247,6 @@ name = "siphasher" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "slab" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "slab" version = "0.2.0" @@ -2395,29 +2432,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "tokio-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio-io" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-line" +version = "0.1.0" +source = "git+https://github.com/tokio-rs/tokio-line#482614ae0c82daf584727ae65a80d854fe861f81" +dependencies = [ + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-named-pipes" +version = "0.1.0" +source = "git+https://github.com/alexcrichton/tokio-named-pipes#3a22f8fc9a441b548aec25bd5df3b1e0ab99fabe" +dependencies = [ + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio-proto" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2426,7 +2494,19 @@ name = "tokio-service" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-uds" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2450,6 +2530,11 @@ name = "traitobject" version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "transient-hashmap" version = "0.4.0" @@ -2557,7 +2642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ws" version = "0.5.3" -source = "git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable#f5c0b35d660244d1b7500693c8cc28277ce1d418" +source = "git+https://github.com/paritytech/ws-rs.git?branch=mio-upstream-stable#f5c0b35d660244d1b7500693c8cc28277ce1d418" dependencies = [ "bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)", "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2632,6 +2717,7 @@ dependencies = [ "checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" "checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27" "checksum bytes 0.4.0-dev (git+https://github.com/carllerche/bytes)" = "" +"checksum bytes 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46112a0060ae15e3a3f9a445428a53e082b91215b744fa27a1948842f4a64b96" "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" "checksum cid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e53e6cdfa5ca294863e8c8a32a7cdb4dc0a442c8971d47a0e75b6c27ea268a6a" "checksum clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "5b4fabf979ddf6419a313c1c0ada4a5b95cfd2049c56e8418d622d27b4b6ff32" @@ -2642,7 +2728,7 @@ dependencies = [ "checksum core-foundation-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05eed248dc504a5391c63794fe4fb64f46f071280afaa1b73308f3c0ce4574c5" "checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97" "checksum crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec" -"checksum ctrlc 1.1.1 (git+https://github.com/ethcore/rust-ctrlc.git)" = "" +"checksum ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)" = "" "checksum daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "271ec51b7e0bee92f0d04601422c73eb76ececf197026711c97ad25038a010cf" "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf" "checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76" @@ -2654,7 +2740,7 @@ dependencies = [ "checksum ethabi 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d8f6cc4c1acd005f48e1d17b06a461adac8fb6eeeb331fbf19a0e656fba91cd" "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" "checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb" -"checksum futures 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c1913eb7083840b1bbcbf9631b7fda55eaf35fe7ead13cca034e8946f9e2bc41" +"checksum futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8e51e7f9c150ba7fd4cee9df8bf6ea3dea5b63b68955ddad19ccd35b71dcfb4d" "checksum futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bb982bb25cd8fa5da6a8eb3a460354c984ff1113da82bcb4f0b0862b5795db82" "checksum gcc 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)" = "c07c758b972368e703a562686adb39125707cc1ef3399da8c019fc6c2498a75d" "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" @@ -2664,25 +2750,29 @@ dependencies = [ "checksum hidapi 0.3.1 (git+https://github.com/ethcore/hidapi-rs)" = "" "checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58" "checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae" -"checksum hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)" = "" -"checksum hyper 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "220407e5a263f110ec30a071787c9535918fdfc97def5680c90013c3f30c38c1" +"checksum hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)" = "" +"checksum hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "43a15e3273b2133aaac0150478ab443fb89f15c3de41d8d93d8f3bb14bf560f6" "checksum hyper 0.9.18 (registry+https://github.com/rust-lang/crates.io-index)" = "1b9bf64f730d6ee4b0528a5f0a316363da9d8104318731509d4ccc86248f82b3" "checksum hyper-native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "afe68f772f0497a7205e751626bb8e1718568b58534b6108c73a74ef80483409" "checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11" "checksum igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c8c12b1795b8b168f577c45fa10379b3814dcb11b7ab702406001f0d63f40484" "checksum integer-encoding 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a053c9c7dcb7db1f2aa012c37dc176c62e4cdf14898dee0eecc606de835b8acb" +"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be" "checksum isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7408a548dc0e406b7912d9f84c261cc533c1866e047644a811c133c56041ac0c" "checksum itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d95557e7ba6b71377b0f2c3b3ae96c53f1b75a926a6901a500f557a370af730a" "checksum itoa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91fd9dc2c587067de817fec4ad355e3818c3d893a78cab32a0a474c7a15bb8d5" -"checksum jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)" = "" -"checksum jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)" = "" -"checksum jsonrpc-ipc-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)" = "" -"checksum jsonrpc-macros 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)" = "" -"checksum jsonrpc-tcp-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git?branch=parity-1.6)" = "" +"checksum jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" +"checksum jsonrpc-http-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" +"checksum jsonrpc-ipc-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" +"checksum jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" +"checksum jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" +"checksum jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" +"checksum jsonrpc-tcp-server 7.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-1.7)" = "" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b" +"checksum lazycell 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec38a5c22f1ef3e30d2642aa875620d60edeef36cef43c4739d86215ce816331" "checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135" "checksum libusb 0.3.0 (git+https://github.com/ethcore/libusb-rs)" = "" "checksum libusb-sys 0.2.3 (git+https://github.com/ethcore/libusb-sys)" = "" @@ -2695,12 +2785,13 @@ dependencies = [ "checksum mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a74cc2587bf97c49f3f5bab62860d6abf3902ca73b66b51d9b049fbdcd727bd2" "checksum mime_guess 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e50bf542f81754ef69e5cea856946a3819f7c09ea97b4903c8bc8a89f74e7b6" "checksum miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d1f4d337a01c32e1f2122510fed46393d53ca35a7f429cb0450abaedfa3ed54" -"checksum mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)" = "" -"checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e" "checksum mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)" = "" "checksum mio 0.6.1 (git+https://github.com/ethcore/mio)" = "" -"checksum mio 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "410a1a0ff76f5a226f1e4e3ff1756128e65cd30166e39c3892283e2ac09d5b67" -"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a" +"checksum mio 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5b493dc9fd96bd2077f2117f178172b0765db4dfda3ea4d8000401e6d65d3e80" +"checksum mio-named-pipes 0.1.4 (git+https://github.com/alexcrichton/mio-named-pipes)" = "" +"checksum mio-uds 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "78437f00d9615c366932cbfe79790b5c2945706ba67cf78378ffacc0069ed9de" +"checksum miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3e690c5df6b2f60acd45d56378981e827ff8295562fc8d34f573deb267a59cd1" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8" "checksum multibase 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b9c35dac080fd6e16a99924c8dfdef0af89d797dd851adab25feaffacf7850d6" "checksum multihash 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c9f70f2402fa07c16c40be8fd0a748a39257c5dc3ff5c857cbbde2f39135c505" @@ -2708,7 +2799,6 @@ dependencies = [ "checksum nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git?branch=parity-1.7)" = "" "checksum native-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4e52995154bb6f0b41e4379a279482c9387c1632e3798ba4e511ef8c54ee09" "checksum net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6a816012ca11cb47009693c1e0c6130e26d39e4d97ee2a13c50e868ec83e3204" -"checksum nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f05c2fc965fc1cd6b73fa57fa7b89f288178737f2f3ce9e63e4a6a141189000e" "checksum nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7bb1da2be7da3cbffda73fc681d509ffd9e665af478d2bee1907cee0bc64b2" "checksum nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d95c5fa8b641c10ad0b8887454ebaafa3c92b5cd5350f8fc693adafd178e7b" "checksum nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "52cd74cd09beba596430cc6e3091b74007169a56246e1262f0ba451ea95117b2" @@ -2729,6 +2819,7 @@ dependencies = [ "checksum order-stat 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "efa535d5117d3661134dbf1719b6f0ffe06f2375843b13935db186cd094105eb" "checksum owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8d91377085359426407a287ab16884a0111ba473aa6844ff01d4ec20ce3d75e7" "checksum parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1d06f6ee0fda786df3784a96ee3f0629f529b91cbfb7d142f6410e6bcd1ce2c" +"checksum parity-tokio-ipc 0.1.0 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "" "checksum parity-ui-precompiled 1.4.0 (git+https://github.com/ethcore/js-precompiled.git)" = "" "checksum parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e1435e7a2a00dfebededd6c6bdbd54008001e94b4a2aadd6aef0dc4c56317621" "checksum parking_lot_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb1b97670a2ffadce7c397fb80a3d687c4f3060140b885621ef1653d0e5d5068" @@ -2782,7 +2873,6 @@ dependencies = [ "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" "checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d" "checksum siphasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c44e42fa187b5a8782489cf7740cc27c3125806be2bf33563cf5e02e9533fcd" -"checksum slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d807fd58c4181bbabed77cb3b891ba9748241a552bcc5be698faaebefc54f46e" "checksum slab 0.2.0 (git+https://github.com/carllerche/slab?rev=5476efcafb)" = "" "checksum slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6dbdd334bd28d328dad1c41b0ea662517883d8880d8533895ef96c8003dec9c4" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" @@ -2807,12 +2897,17 @@ dependencies = [ "checksum thread_local 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0694f51610ef7cfac7a1b81de7f1602ee5356e76541bcd62c40e71933338cab1" "checksum time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7ec6d62a20df54e07ab3b78b9a3932972f4b7981de295563686849eb3989af" "checksum tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f7aef43048292ca0bae4ab32180e85f6202cf2816c2a210c396a84b99dab9270" -"checksum tokio-core 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "52416b3e937abac22a543a7f1c66bd37feb60137ff1ab42390fa02df85347e58" +"checksum tokio-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3d1be481b55126f02ef88ff86748086473cb537a949fc4a8f4be403a530ae54b" +"checksum tokio-io 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6a278fde45f1be68e44995227d426aaa4841e0980bb0a21b981092f28c3c8473" +"checksum tokio-line 0.1.0 (git+https://github.com/tokio-rs/tokio-line)" = "" +"checksum tokio-named-pipes 0.1.0 (git+https://github.com/alexcrichton/tokio-named-pipes)" = "" "checksum tokio-proto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c0d6031f94d78d7b4d509d4a7c5e1cdf524a17e7b08d1c188a83cf720e69808" "checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" +"checksum tokio-uds 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc7b5fc8e19e220b29566d1750949224a518478eab9cebc8df60583242ca30a" "checksum toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "fcd27a04ca509aff336ba5eb2abc58d456f52c4ff64d9724d88acb85ead560b6" "checksum toml 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a442dfc13508e603c3f763274361db7f79d7469a0e95c411cde53662ab30fc72" "checksum traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "07eaeb7689bb7fca7ce15628319635758eda769fed481ecfe6686ddef2600616" +"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "715254c8f0811be1a79ad3ea5e6fa3c8eddec2b03d7f5ba78cf093e56d79c24f" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" "checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764" @@ -2828,7 +2923,7 @@ dependencies = [ "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum ws 0.5.3 (git+https://github.com/ethcore/ws-rs.git?branch=mio-upstream-stable)" = "" +"checksum ws 0.5.3 (git+https://github.com/paritytech/ws-rs.git?branch=mio-upstream-stable)" = "" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xdg 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77b831a5ba77110f438f0ac5583aafeb087f70432998ba6b7dcb1d32185db453" "checksum xml-rs 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65e74b96bd3179209dc70a980da6df843dff09e46eee103a0376c0949257e3ef" diff --git a/Cargo.toml b/Cargo.toml index f62f8b42f..f0978f420 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,9 @@ serde = "0.9" serde_json = "0.9" app_dirs = "1.1.1" fdlimit = "0.1" -hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } -ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" } -jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } +hyper = { default-features = false, git = "https://github.com/paritytech/hyper" } +ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } +jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } ethsync = { path = "sync" } ethcore = { path = "ethcore" } ethcore-util = { path = "util" } diff --git a/dapps/Cargo.toml b/dapps/Cargo.toml index 8a00f503f..27a786cb3 100644 --- a/dapps/Cargo.toml +++ b/dapps/Cargo.toml @@ -8,33 +8,36 @@ authors = ["Parity Technologies "] [lib] [dependencies] -rand = "0.3" -log = "0.3" +base32 = "0.3" env_logger = "0.3" futures = "0.1" -jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } -jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } -hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } -unicase = "1.3" -url = "1.0" -rustc-serialize = "0.3" -serde = "0.9" -serde_json = "0.9" -serde_derive = "0.9" linked-hash-map = "0.3" +log = "0.3" parity-dapps-glue = "1.7" -base32 = "0.3" mime = "0.2" mime_guess = "1.6.1" +rand = "0.3" +rustc-serialize = "0.3" +serde = "0.9" +serde_derive = "0.9" +serde_json = "0.9" time = "0.1.35" +unicase = "1.3" +url = "1.0" zip = { version = "0.1", default-features = false } + +jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } +jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } +# TODO [ToDr] Temporary solution, server should be merged with RPC. +jsonrpc-server-utils = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } + ethcore-devtools = { path = "../devtools" } ethcore-rpc = { path = "../rpc" } ethcore-util = { path = "../util" } fetch = { path = "../util/fetch" } -parity-ui = { path = "./ui" } parity-hash-fetch = { path = "../hash-fetch" } parity-reactor = { path = "../util/reactor" } +parity-ui = { path = "./ui" } clippy = { version = "0.0.103", optional = true} diff --git a/dapps/src/api/api.rs b/dapps/src/api/api.rs index 9106e0d70..e07bd4535 100644 --- a/dapps/src/api/api.rs +++ b/dapps/src/api/api.rs @@ -19,7 +19,6 @@ use unicase::UniCase; use hyper::{server, net, Decoder, Encoder, Next, Control}; use hyper::header; use hyper::method::Method; -use hyper::header::AccessControlAllowOrigin; use api::types::{App, ApiError}; use api::response; @@ -27,23 +26,20 @@ use apps::fetcher::Fetcher; use handlers::extract_url; use endpoint::{Endpoint, Endpoints, Handler, EndpointPath}; -use jsonrpc_http_server::cors; +use jsonrpc_http_server; +use jsonrpc_server_utils::cors; #[derive(Clone)] pub struct RestApi { - cors_domains: Option>, + cors_domains: Option>, endpoints: Arc, fetcher: Arc, } impl RestApi { - pub fn new(cors_domains: Vec, endpoints: Arc, fetcher: Arc) -> Box { + pub fn new(cors_domains: Vec, endpoints: Arc, fetcher: Arc) -> Box { Box::new(RestApi { - cors_domains: Some(cors_domains.into_iter().map(|domain| match domain.as_ref() { - "all" | "*" | "any" => AccessControlAllowOrigin::Any, - "null" => AccessControlAllowOrigin::Null, - other => AccessControlAllowOrigin::Value(other.into()), - }).collect()), + cors_domains: Some(cors_domains), endpoints: endpoints, fetcher: fetcher, }) @@ -64,7 +60,7 @@ impl Endpoint for RestApi { struct RestApiRouter { api: RestApi, - origin: Option, + cors_header: Option, path: Option, control: Option, handler: Box, @@ -74,7 +70,7 @@ impl RestApiRouter { fn new(api: RestApi, path: EndpointPath, control: Control) -> Self { RestApiRouter { path: Some(path), - origin: None, + cors_header: None, control: Some(control), api: api, handler: response::as_json_error(&ApiError { @@ -95,21 +91,22 @@ impl RestApiRouter { } /// Returns basic headers for a response (it may be overwritten by the handler) - fn response_headers(&self) -> header::Headers { + fn response_headers(cors_header: Option) -> header::Headers { let mut headers = header::Headers::new(); - headers.set(header::AccessControlAllowCredentials); - headers.set(header::AccessControlAllowMethods(vec![ - Method::Options, - Method::Post, - Method::Get, - ])); - headers.set(header::AccessControlAllowHeaders(vec![ - UniCase("origin".to_owned()), - UniCase("content-type".to_owned()), - UniCase("accept".to_owned()), - ])); - if let Some(cors_header) = cors::get_cors_header(&self.api.cors_domains, &self.origin) { + if let Some(cors_header) = cors_header { + headers.set(header::AccessControlAllowCredentials); + headers.set(header::AccessControlAllowMethods(vec![ + Method::Options, + Method::Post, + Method::Get, + ])); + headers.set(header::AccessControlAllowHeaders(vec![ + UniCase("origin".to_owned()), + UniCase("content-type".to_owned()), + UniCase("accept".to_owned()), + ])); + headers.set(cors_header); } @@ -120,7 +117,7 @@ impl RestApiRouter { impl server::Handler for RestApiRouter { fn on_request(&mut self, request: server::Request) -> Next { - self.origin = cors::read_origin(&request); + self.cors_header = jsonrpc_http_server::cors_header(&request, &self.api.cors_domains).into(); if let Method::Options = *request.method() { self.handler = response::empty(); @@ -164,7 +161,7 @@ impl server::Handler for RestApiRouter { } fn on_response(&mut self, res: &mut server::Response) -> Next { - *res.headers_mut() = self.response_headers(); + *res.headers_mut() = Self::response_headers(self.cors_header.take()); self.handler.on_response(res) } diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 30c62a031..252e1c3bb 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -20,25 +20,27 @@ #![cfg_attr(feature="nightly", plugin(clippy))] extern crate base32; -extern crate hyper; -extern crate time; -extern crate url as url_lib; -extern crate unicase; +extern crate futures; +extern crate linked_hash_map; +extern crate mime_guess; +extern crate rand; +extern crate rustc_serialize; extern crate serde; extern crate serde_json; +extern crate time; +extern crate unicase; +extern crate url as url_lib; extern crate zip; -extern crate rand; + extern crate jsonrpc_core; extern crate jsonrpc_http_server; -extern crate mime_guess; -extern crate rustc_serialize; +extern crate jsonrpc_server_utils; + extern crate ethcore_rpc; extern crate ethcore_util as util; -extern crate parity_hash_fetch as hash_fetch; -extern crate linked_hash_map; extern crate fetch; extern crate parity_dapps_glue as parity_dapps; -extern crate futures; +extern crate parity_hash_fetch as hash_fetch; extern crate parity_reactor; #[macro_use] @@ -68,17 +70,20 @@ mod web; mod tests; use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::net::SocketAddr; use std::collections::HashMap; -use ethcore_rpc::{Metadata}; +use jsonrpc_core::{Middleware, MetaIoHandler}; +use jsonrpc_http_server::tokio_core::reactor::Remote as TokioRemote; +pub use jsonrpc_http_server::{DomainsValidation, Host, AccessControlAllowOrigin}; +pub use jsonrpc_http_server::hyper; + +use ethcore_rpc::Metadata; use fetch::{Fetch, Client as FetchClient}; use hash_fetch::urlhint::ContractClient; -use jsonrpc_core::Middleware; -use jsonrpc_core::reactor::RpcHandler; -use router::auth::{Authorization, NoAuth, HttpBasicAuth}; use parity_reactor::Remote; +use router::auth::{Authorization, NoAuth, HttpBasicAuth}; use self::apps::{HOME_PAGE, DAPPS_DOMAIN}; @@ -110,8 +115,8 @@ pub struct ServerBuilder { sync_status: Arc, web_proxy_tokens: Arc, signer_address: Option<(String, u16)>, - allowed_hosts: Option>, - extra_cors: Option>, + allowed_hosts: Option>, + extra_cors: Option>, remote: Remote, fetch: Option, } @@ -172,15 +177,15 @@ impl ServerBuilder { /// Change allowed hosts. /// `None` - All hosts are allowed /// `Some(whitelist)` - Allow only whitelisted hosts (+ listen address) - pub fn allowed_hosts(mut self, allowed_hosts: Option>) -> Self { - self.allowed_hosts = allowed_hosts; + pub fn allowed_hosts(mut self, allowed_hosts: DomainsValidation) -> Self { + self.allowed_hosts = allowed_hosts.into(); self } /// Extra cors headers. /// `None` - no additional CORS URLs - pub fn extra_cors_headers(mut self, cors: Option>) -> Self { - self.extra_cors = cors; + pub fn extra_cors_headers(mut self, cors: DomainsValidation) -> Self { + self.extra_cors = cors.into(); self } @@ -192,7 +197,7 @@ impl ServerBuilder { /// Asynchronously start server with no authentication, /// returns result with `Server` handle on success or an error. - pub fn start_unsecured_http>(self, addr: &SocketAddr, handler: RpcHandler) -> Result { + pub fn start_unsecured_http>(self, addr: &SocketAddr, handler: MetaIoHandler, tokio_remote: TokioRemote) -> Result { let fetch = self.fetch_client()?; Server::start_http( addr, @@ -207,13 +212,14 @@ impl ServerBuilder { self.sync_status, self.web_proxy_tokens, self.remote, + tokio_remote, fetch, ) } /// Asynchronously start server with `HTTP Basic Authentication`, /// return result with `Server` handle on success or an error. - pub fn start_basic_auth_http>(self, addr: &SocketAddr, username: &str, password: &str, handler: RpcHandler) -> Result { + pub fn start_basic_auth_http>(self, addr: &SocketAddr, username: &str, password: &str, handler: MetaIoHandler, tokio_remote: TokioRemote) -> Result { let fetch = self.fetch_client()?; Server::start_http( addr, @@ -228,6 +234,7 @@ impl ServerBuilder { self.sync_status, self.web_proxy_tokens, self.remote, + tokio_remote, fetch, ) } @@ -243,12 +250,11 @@ impl ServerBuilder { /// Webapps HTTP server. pub struct Server { server: Option, - panic_handler: Arc () + Send>>>>, } impl Server { /// Returns a list of allowed hosts or `None` if all hosts are allowed. - fn allowed_hosts(hosts: Option>, bind_address: String) -> Option> { + fn allowed_hosts(hosts: Option>, bind_address: String) -> Option> { let mut allowed = Vec::new(); match hosts { @@ -263,16 +269,19 @@ impl Server { } /// Returns a list of CORS domains for API endpoint. - fn cors_domains(signer_address: Option<(String, u16)>, extra_cors: Option>) -> Vec { + fn cors_domains( + signer_address: Option<(String, u16)>, + extra_cors: Option>, + ) -> Vec { let basic_cors = match signer_address { - Some(signer_address) => vec![ + Some(signer_address) => [ format!("http://{}{}", HOME_PAGE, DAPPS_DOMAIN), format!("http://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1), format!("http://{}", address(&signer_address)), format!("https://{}{}", HOME_PAGE, DAPPS_DOMAIN), format!("https://{}{}:{}", HOME_PAGE, DAPPS_DOMAIN, signer_address.1), format!("https://{}", address(&signer_address)), - ], + ].into_iter().map(|val| AccessControlAllowOrigin::Value(val.into())).collect(), None => vec![], }; @@ -284,10 +293,10 @@ impl Server { fn start_http>( addr: &SocketAddr, - hosts: Option>, - extra_cors: Option>, + hosts: Option>, + extra_cors: Option>, authorization: A, - handler: RpcHandler, + handler: MetaIoHandler, dapps_path: PathBuf, extra_dapps: Vec, signer_address: Option<(String, u16)>, @@ -295,9 +304,9 @@ impl Server { sync_status: Arc, web_proxy_tokens: Arc, remote: Remote, + tokio_remote: TokioRemote, fetch: F, ) -> Result { - let panic_handler = Arc::new(Mutex::new(None)); let authorization = Arc::new(authorization); let content_fetcher = Arc::new(apps::fetcher::ContentFetcher::new( hash_fetch::urlhint::URLHintContract::new(registrar), @@ -318,7 +327,7 @@ impl Server { let special = Arc::new({ let mut special = HashMap::new(); - special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, cors_domains.clone(), panic_handler.clone())); + special.insert(router::SpecialEndpoint::Rpc, rpc::rpc(handler, tokio_remote, cors_domains.clone())); special.insert(router::SpecialEndpoint::Utils, apps::utils()); special.insert( router::SpecialEndpoint::Api, @@ -346,17 +355,11 @@ impl Server { Server { server: Some(l), - panic_handler: panic_handler, } }) .map_err(ServerError::from) } - /// Set callback for panics. - pub fn set_panic_handler(&self, handler: F) where F : Fn() -> () + Send + 'static { - *self.panic_handler.lock().unwrap() = Some(Box::new(handler)); - } - #[cfg(test)] /// Returns address that this server is bound to. pub fn addr(&self) -> &SocketAddr { @@ -408,6 +411,7 @@ fn address(address: &(String, u16)) -> String { #[cfg(test)] mod util_tests { use super::Server; + use jsonrpc_http_server::AccessControlAllowOrigin; #[test] fn should_return_allowed_hosts() { @@ -432,18 +436,18 @@ mod util_tests { // when let none = Server::cors_domains(None, None); let some = Server::cors_domains(Some(("127.0.0.1".into(), 18180)), None); - let extra = Server::cors_domains(None, Some(vec!["all".to_owned()])); + let extra = Server::cors_domains(None, Some(vec!["all".into()])); // then - assert_eq!(none, Vec::::new()); + assert_eq!(none, Vec::::new()); assert_eq!(some, vec![ - "http://parity.web3.site".to_owned(), + "http://parity.web3.site".into(), "http://parity.web3.site:18180".into(), "http://127.0.0.1:18180".into(), "https://parity.web3.site".into(), "https://parity.web3.site:18180".into(), - "https://127.0.0.1:18180".into() + "https://127.0.0.1:18180".into(), ]); - assert_eq!(extra, vec!["all".to_owned()]); + assert_eq!(extra, vec![AccessControlAllowOrigin::Any]); } } diff --git a/dapps/src/router/host_validation.rs b/dapps/src/router/host_validation.rs index 9f40c177e..e5fcedd94 100644 --- a/dapps/src/router/host_validation.rs +++ b/dapps/src/router/host_validation.rs @@ -19,18 +19,13 @@ use apps::DAPPS_DOMAIN; use hyper::{server, header, StatusCode}; use hyper::net::HttpStream; -use jsonrpc_http_server::{is_host_header_valid}; use handlers::ContentHandler; +use jsonrpc_http_server; +use jsonrpc_server_utils::hosts; -pub fn is_valid(request: &server::Request, allowed_hosts: &[String], endpoints: Vec) -> bool { - let mut endpoints = endpoints.iter() - .map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN)) - .collect::>(); - endpoints.extend_from_slice(allowed_hosts); - - let header_valid = is_host_header_valid(request, &endpoints); - - match (header_valid, request.headers().get::()) { +pub fn is_valid(req: &server::Request, allowed_hosts: &Option>) -> bool { + let header_valid = jsonrpc_http_server::is_host_allowed(req, allowed_hosts); + match (header_valid, req.headers().get::()) { (true, _) => true, (_, Some(host)) => host.hostname.ends_with(DAPPS_DOMAIN), _ => false, diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index f34151552..0b4e632a6 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -24,14 +24,16 @@ use address; use std::cmp; use std::sync::Arc; use std::collections::HashMap; + use url::{Url, Host}; use hyper::{self, server, header, Next, Encoder, Decoder, Control, StatusCode}; use hyper::net::HttpStream; +use jsonrpc_server_utils::hosts; + use apps::{self, DAPPS_DOMAIN}; use apps::fetcher::Fetcher; use endpoint::{Endpoint, Endpoints, EndpointPath}; use handlers::{self, Redirection, ContentHandler}; -use self::auth::{Authorization, Authorized}; /// Special endpoints are accessible on every domain (every dapp) #[derive(Debug, PartialEq, Hash, Eq)] @@ -42,18 +44,18 @@ pub enum SpecialEndpoint { None, } -pub struct Router { +pub struct Router { control: Option, signer_address: Option<(String, u16)>, endpoints: Arc, fetch: Arc, special: Arc>>, authorization: Arc, - allowed_hosts: Option>, + allowed_hosts: Option>, handler: Box + Send>, } -impl server::Handler for Router { +impl server::Handler for Router { fn on_request(&mut self, req: server::Request) -> Next { // Choose proper handler depending on path / domain @@ -66,20 +68,18 @@ impl server::Handler for Router { trace!(target: "dapps", "Routing request to {:?}. Details: {:?}", url, req); // Validate Host header - if let Some(ref hosts) = self.allowed_hosts { - trace!(target: "dapps", "Validating host headers against: {:?}", hosts); - let is_valid = is_utils || host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect()); - if !is_valid { - debug!(target: "dapps", "Rejecting invalid host header."); - self.handler = host_validation::host_invalid_response(); - return self.handler.on_request(req); - } + trace!(target: "dapps", "Validating host headers against: {:?}", self.allowed_hosts); + let is_valid = is_utils || host_validation::is_valid(&req, &self.allowed_hosts); + if !is_valid { + debug!(target: "dapps", "Rejecting invalid host header."); + self.handler = host_validation::host_invalid_response(); + return self.handler.on_request(req); } trace!(target: "dapps", "Checking authorization."); // Check authorization let auth = self.authorization.is_authorized(&req); - if let Authorized::No(handler) = auth { + if let auth::Authorized::No(handler) = auth { debug!(target: "dapps", "Authorization denied."); self.handler = handler; return self.handler.on_request(req); @@ -181,7 +181,7 @@ impl server::Handler for Router { } } -impl Router { +impl Router { pub fn new( control: Control, signer_address: Option<(String, u16)>, @@ -189,7 +189,7 @@ impl Router { endpoints: Arc, special: Arc>>, authorization: Arc, - allowed_hosts: Option>, + allowed_hosts: Option>, ) -> Self { let handler = special.get(&SpecialEndpoint::Utils) diff --git a/dapps/src/rpc.rs b/dapps/src/rpc.rs index cc6f4d81a..6ddb31db0 100644 --- a/dapps/src/rpc.rs +++ b/dapps/src/rpc.rs @@ -14,55 +14,69 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use hyper; use ethcore_rpc::{Metadata, Origin}; -use jsonrpc_core::Middleware; -use jsonrpc_core::reactor::RpcHandler; -use jsonrpc_http_server::{Rpc, ServerHandler, PanicHandler, AccessControlAllowOrigin, HttpMetaExtractor}; +use jsonrpc_core::{Middleware, MetaIoHandler}; +use jsonrpc_http_server::{self as http, AccessControlAllowOrigin, HttpMetaExtractor}; +use jsonrpc_http_server::tokio_core::reactor::Remote; use endpoint::{Endpoint, EndpointPath, Handler}; pub fn rpc>( - handler: RpcHandler, - cors_domains: Vec, - panic_handler: Arc () + Send>>>>, + handler: MetaIoHandler, + remote: Remote, + cors_domains: Vec, ) -> Box { Box::new(RpcEndpoint { - handler: handler, + handler: Arc::new(handler), + remote: remote, meta_extractor: Arc::new(MetadataExtractor), - panic_handler: panic_handler, - cors_domain: Some(cors_domains.into_iter().map(AccessControlAllowOrigin::Value).collect()), + cors_domain: Some(cors_domains), // NOTE [ToDr] We don't need to do any hosts validation here. It's already done in router. allowed_hosts: None, }) } struct RpcEndpoint> { - handler: RpcHandler, + handler: Arc>, + remote: Remote, meta_extractor: Arc>, - panic_handler: Arc () + Send>>>>, cors_domain: Option>, - allowed_hosts: Option>, + allowed_hosts: Option>, } + impl> Endpoint for RpcEndpoint { fn to_async_handler(&self, _path: EndpointPath, control: hyper::Control) -> Box { - let panic_handler = PanicHandler { handler: self.panic_handler.clone() }; - Box::new(ServerHandler::new( - Rpc::new(self.handler.clone(), self.meta_extractor.clone()), + Box::new(http::ServerHandler::new( + http::Rpc { + handler: self.handler.clone(), + remote: self.remote.clone(), + extractor: self.meta_extractor.clone(), + }, self.cors_domain.clone(), self.allowed_hosts.clone(), - panic_handler, + Arc::new(NoopMiddleware), control, )) } } +#[derive(Default)] +struct NoopMiddleware; +impl http::RequestMiddleware for NoopMiddleware { + fn on_request(&self, request: &http::hyper::server::Request) -> http::RequestMiddlewareAction { + http::RequestMiddlewareAction::Proceed { + should_continue_on_invalid_cors: request.headers().get::().is_none(), + } + } +} + struct MetadataExtractor; impl HttpMetaExtractor for MetadataExtractor { - fn read_metadata(&self, request: &hyper::server::Request) -> Metadata { - let dapp_id = request.headers().get::() + fn read_metadata(&self, request: &http::hyper::server::Request) -> Metadata { + let dapp_id = request.headers().get::() .map(|origin| format!("{}://{}", origin.scheme, origin.host)) .or_else(|| { // fallback to custom header, but only if origin is null diff --git a/dapps/src/tests/api.rs b/dapps/src/tests/api.rs index 1b9f64b7f..73467e854 100644 --- a/dapps/src/tests/api.rs +++ b/dapps/src/tests/api.rs @@ -33,8 +33,8 @@ fn should_return_error() { ); // then - assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); - assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json"); + response.assert_status("HTTP/1.1 404 Not Found"); + response.assert_header("Content-Type", "application/json"); assert_eq!(response.body, format!("58\n{}\n0\n\n", r#"{"code":"404","title":"Not Found","detail":"Resource you requested has not been found."}"#)); assert_security_headers(&response.headers); } @@ -56,8 +56,8 @@ fn should_serve_apps() { ); // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json"); + response.assert_status("HTTP/1.1 200 OK"); + response.assert_header("Content-Type", "application/json"); assert!(response.body.contains("Parity UI"), response.body); assert_security_headers(&response.headers); } @@ -79,8 +79,8 @@ fn should_handle_ping() { ); // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert_eq!(response.headers.get(3).unwrap(), "Content-Type: application/json"); + response.assert_status("HTTP/1.1 200 OK"); + response.assert_header("Content-Type", "application/json"); assert_eq!(response.body, "0\n\n".to_owned()); assert_security_headers(&response.headers); } @@ -102,7 +102,7 @@ fn should_try_to_resolve_dapp() { ); // then - assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); + response.assert_status("HTTP/1.1 404 Not Found"); assert_eq!(registrar.calls.lock().len(), 2); assert_security_headers(&response.headers); } @@ -125,12 +125,8 @@ fn should_return_signer_port_cors_headers() { ); // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert!( - response.headers_raw.contains("Access-Control-Allow-Origin: http://127.0.0.1:18180"), - "CORS header for signer missing: {:?}", - response.headers - ); + response.assert_status("HTTP/1.1 200 OK"); + response.assert_header("Access-Control-Allow-Origin", "http://127.0.0.1:18180"); } #[test] @@ -151,12 +147,8 @@ fn should_return_signer_port_cors_headers_for_home_parity() { ); // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert!( - response.headers_raw.contains("Access-Control-Allow-Origin: http://parity.web3.site"), - "CORS header for parity.web3.site missing: {:?}", - response.headers - ); + response.assert_status("HTTP/1.1 200 OK"); + response.assert_header("Access-Control-Allow-Origin", "http://parity.web3.site"); } @@ -178,12 +170,8 @@ fn should_return_signer_port_cors_headers_for_home_parity_with_https() { ); // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert!( - response.headers_raw.contains("Access-Control-Allow-Origin: https://parity.web3.site"), - "CORS header for parity.web3.site missing: {:?}", - response.headers - ); + response.assert_status("HTTP/1.1 200 OK"); + response.assert_header("Access-Control-Allow-Origin", "https://parity.web3.site"); } #[test] @@ -204,12 +192,8 @@ fn should_return_signer_port_cors_headers_for_home_parity_with_port() { ); // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert!( - response.headers_raw.contains("Access-Control-Allow-Origin: http://parity.web3.site:18180"), - "CORS header for parity.web3.site missing: {:?}", - response.headers - ); + response.assert_status("HTTP/1.1 200 OK"); + response.assert_header("Access-Control-Allow-Origin", "http://parity.web3.site:18180"); } #[test] diff --git a/dapps/src/tests/helpers/fetch.rs b/dapps/src/tests/helpers/fetch.rs index fcfd4db9c..e6e875c51 100644 --- a/dapps/src/tests/helpers/fetch.rs +++ b/dapps/src/tests/helpers/fetch.rs @@ -114,7 +114,7 @@ impl Fetch for FakeFetch { let data = response.lock().take().unwrap_or(b"Some content"); let cursor = io::Cursor::new(data); - tx.complete(fetch::Response::from_reader(cursor)); + tx.send(fetch::Response::from_reader(cursor)).unwrap(); }); rx.map_err(|_| fetch::Error::Aborted).boxed() diff --git a/dapps/src/tests/helpers/mod.rs b/dapps/src/tests/helpers/mod.rs index d1a1e9900..d1466c77c 100644 --- a/dapps/src/tests/helpers/mod.rs +++ b/dapps/src/tests/helpers/mod.rs @@ -21,13 +21,12 @@ use std::sync::Arc; use env_logger::LogBuilder; use ethcore_rpc::Metadata; use jsonrpc_core::MetaIoHandler; -use jsonrpc_core::reactor::RpcEventLoop; use ServerBuilder; use Server; use fetch::Fetch; use devtools::http_client; -use parity_reactor::Remote; +use parity_reactor::{EventLoop, Remote}; mod registrar; mod fetch; @@ -48,7 +47,7 @@ fn init_logger() { pub struct ServerLoop { pub server: Server, - pub event_loop: RpcEventLoop, + pub event_loop: EventLoop, } impl Deref for ServerLoop { @@ -70,13 +69,12 @@ pub fn init_server(process: F, io: MetaIoHandler, remote: Remote // TODO [ToDr] When https://github.com/ethcore/jsonrpc/issues/26 is resolved // this additional EventLoop wouldn't be needed, we should be able to re-use remote. - let event_loop = RpcEventLoop::spawn(); - let handler = event_loop.handler(Arc::new(io)); + let event_loop = EventLoop::spawn(); let server = process(ServerBuilder::new( &dapps_path, registrar.clone(), remote, )) .signer_address(Some(("127.0.0.1".into(), SIGNER_PORT))) - .start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), handler).unwrap(); + .start_unsecured_http(&"127.0.0.1:0".parse().unwrap(), io, event_loop.raw_remote()).unwrap(); ( ServerLoop { server: server, event_loop: event_loop }, registrar, @@ -89,12 +87,12 @@ pub fn serve_with_auth(user: &str, pass: &str) -> ServerLoop { let mut dapps_path = env::temp_dir(); dapps_path.push("non-existent-dir-to-prevent-fs-files-from-loading"); - let event_loop = RpcEventLoop::spawn(); - let handler = event_loop.handler(Arc::new(MetaIoHandler::default())); - let server = ServerBuilder::new(&dapps_path, registrar, Remote::new(event_loop.remote())) + let event_loop = EventLoop::spawn(); + let io = MetaIoHandler::default(); + let server = ServerBuilder::new(&dapps_path, registrar, event_loop.remote()) .signer_address(Some(("127.0.0.1".into(), SIGNER_PORT))) - .allowed_hosts(None) - .start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), user, pass, handler).unwrap(); + .allowed_hosts(None.into()) + .start_basic_auth_http(&"127.0.0.1:0".parse().unwrap(), user, pass, io, event_loop.raw_remote()).unwrap(); ServerLoop { server: server, event_loop: event_loop, @@ -102,26 +100,28 @@ pub fn serve_with_auth(user: &str, pass: &str) -> ServerLoop { } pub fn serve_with_rpc(io: MetaIoHandler) -> ServerLoop { - init_server(|builder| builder.allowed_hosts(None), io, Remote::new_sync()).0 + init_server(|builder| builder.allowed_hosts(None.into()), io, Remote::new_sync()).0 } pub fn serve_hosts(hosts: Option>) -> ServerLoop { - init_server(|builder| builder.allowed_hosts(hosts), Default::default(), Remote::new_sync()).0 + let hosts = hosts.map(|hosts| hosts.into_iter().map(Into::into).collect()); + init_server(|builder| builder.allowed_hosts(hosts.into()), Default::default(), Remote::new_sync()).0 } pub fn serve_extra_cors(extra_cors: Option>) -> ServerLoop { - init_server(|builder| builder.allowed_hosts(None).extra_cors_headers(extra_cors), Default::default(), Remote::new_sync()).0 + let extra_cors = extra_cors.map(|cors| cors.into_iter().map(Into::into).collect()); + init_server(|builder| builder.allowed_hosts(None.into()).extra_cors_headers(extra_cors.into()), Default::default(), Remote::new_sync()).0 } pub fn serve_with_registrar() -> (ServerLoop, Arc) { - init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync()) + init_server(|builder| builder.allowed_hosts(None.into()), Default::default(), Remote::new_sync()) } pub fn serve_with_registrar_and_sync() -> (ServerLoop, Arc) { init_server(|builder| { builder .sync_status(Arc::new(|| true)) - .allowed_hosts(None) + .allowed_hosts(None.into()) }, Default::default(), Remote::new_sync()) } @@ -133,7 +133,7 @@ pub fn serve_with_registrar_and_fetch_and_threads(multi_threaded: bool) -> (Serv let fetch = FakeFetch::default(); let f = fetch.clone(); let (server, reg) = init_server(move |builder| { - builder.allowed_hosts(None).fetch(f.clone()) + builder.allowed_hosts(None.into()).fetch(f.clone()) }, Default::default(), if multi_threaded { Remote::new_thread_per_future() } else { Remote::new_sync() }); (server, fetch, reg) @@ -144,7 +144,7 @@ pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) { let f = fetch.clone(); let (server, _) = init_server(move |builder| { builder - .allowed_hosts(None) + .allowed_hosts(None.into()) .fetch(f.clone()) .web_proxy_tokens(Arc::new(move |token| &token == web_token)) }, Default::default(), Remote::new_sync()); @@ -153,7 +153,7 @@ pub fn serve_with_fetch(web_token: &'static str) -> (ServerLoop, FakeFetch) { } pub fn serve() -> ServerLoop { - init_server(|builder| builder.allowed_hosts(None), Default::default(), Remote::new_sync()).0 + init_server(|builder| builder.allowed_hosts(None.into()), Default::default(), Remote::new_sync()).0 } pub fn request(server: ServerLoop, request: &str) -> http_client::Response { diff --git a/dapps/src/tests/redirection.rs b/dapps/src/tests/redirection.rs index 8b529a851..4e3fff4dc 100644 --- a/dapps/src/tests/redirection.rs +++ b/dapps/src/tests/redirection.rs @@ -32,7 +32,7 @@ fn should_redirect_to_home() { ); // then - assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned()); + response.assert_status("HTTP/1.1 302 Found"); assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180"); } @@ -52,7 +52,7 @@ fn should_redirect_to_home_when_trailing_slash_is_missing() { ); // then - assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned()); + response.assert_status("HTTP/1.1 302 Found"); assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180"); } @@ -72,7 +72,7 @@ fn should_redirect_to_home_for_users_with_cached_redirection() { ); // then - assert_eq!(response.status, "HTTP/1.1 302 Found".to_owned()); + response.assert_status("HTTP/1.1 302 Found"); assert_eq!(response.headers.get(0).unwrap(), "Location: http://127.0.0.1:18180"); } @@ -92,7 +92,7 @@ fn should_display_404_on_invalid_dapp() { ); // then - assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); + response.assert_status("HTTP/1.1 404 Not Found"); assert_security_headers_for_embed(&response.headers); } @@ -112,7 +112,7 @@ fn should_display_404_on_invalid_dapp_with_domain() { ); // then - assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); + response.assert_status("HTTP/1.1 404 Not Found"); assert_security_headers_for_embed(&response.headers); } @@ -134,8 +134,8 @@ fn should_serve_rpc() { ); // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert_eq!(response.body, format!("58\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":null},"id":null}"#)); + response.assert_status("HTTP/1.1 200 OK"); + assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#)); } #[test] @@ -156,8 +156,8 @@ fn should_serve_rpc_at_slash_rpc() { ); // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); - assert_eq!(response.body, format!("58\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error","data":null},"id":null}"#)); + response.assert_status("HTTP/1.1 200 OK"); + assert_eq!(response.body, format!("4C\n{}\n\n0\n\n", r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"#)); } @@ -178,7 +178,7 @@ fn should_serve_proxy_pac() { ); // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); + response.assert_status("HTTP/1.1 200 OK"); assert_eq!(response.body, "DD\n\nfunction FindProxyForURL(url, host) {\n\tif (shExpMatch(host, \"parity.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:18180\";\n\t}\n\n\tif (shExpMatch(host, \"*.web3.site\"))\n\t{\n\t\treturn \"PROXY 127.0.0.1:8080\";\n\t}\n\n\treturn \"DIRECT\";\n}\n\n0\n\n".to_owned()); assert_security_headers(&response.headers); } @@ -200,7 +200,7 @@ fn should_serve_utils() { ); // then - assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned()); + response.assert_status("HTTP/1.1 200 OK"); assert_eq!(response.body.contains("function(){"), true); assert_security_headers(&response.headers); } diff --git a/dapps/src/tests/rpc.rs b/dapps/src/tests/rpc.rs index 0dbba384c..2cc4ccb24 100644 --- a/dapps/src/tests/rpc.rs +++ b/dapps/src/tests/rpc.rs @@ -55,8 +55,8 @@ fn should_extract_metadata() { // given let mut io = MetaIoHandler::default(); io.add_method_with_meta("rpc_test", |_params, meta: Metadata| { - assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into())); - assert_eq!(meta.dapp_id(), "https://parity.io/".into()); + assert_eq!(meta.origin, Origin::Dapps("".into())); + assert_eq!(meta.dapp_id(), "".into()); future::ok(Value::String("Hello World!".into())).boxed() }); let server = serve_with_rpc(io); @@ -68,7 +68,6 @@ fn should_extract_metadata() { POST /rpc/ HTTP/1.1\r\n\ Host: 127.0.0.1:8080\r\n\ Connection: close\r\n\ - Origin: https://parity.io/\r\n\ X-Parity-Origin: https://this.should.be.ignored\r\n\ Content-Type: application/json\r\n\ Content-Length: {}\r\n\ diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 17f79840e..d910f9a56 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -45,12 +45,9 @@ ethcore-bloom-journal = { path = "../util/bloom" } hardware-wallet = { path = "../hw" } ethcore-logger = { path = "../logger" } stats = { path = "../util/stats" } +hyper = { git = "https://github.com/paritytech/hyper", default-features = false } num = "0.1" -[dependencies.hyper] -git = "https://github.com/ethcore/hyper" -default-features = false - [features] jit = ["evmjit"] evm-debug = ["slow-blocks"] diff --git a/ethcore/light/src/on_demand/mod.rs b/ethcore/light/src/on_demand/mod.rs index 2ef01a0b1..488535568 100644 --- a/ethcore/light/src/on_demand/mod.rs +++ b/ethcore/light/src/on_demand/mod.rs @@ -18,6 +18,9 @@ //! The request service is implemented using Futures. Higher level request handlers //! will take the raw data received here and extract meaningful results from it. +// TODO [ToDr] Suppressing deprecation warnings. Rob will fix the API anyway. +#![allow(deprecated)] + use std::collections::HashMap; use std::sync::Arc; @@ -74,6 +77,8 @@ pub struct OnDemand { orphaned_requests: RwLock>, } +const RECEIVER_IN_SCOPE: &'static str = "Receiver is still in scope, so it's not dropped; qed"; + impl OnDemand { /// Create a new `OnDemand` service with the given cache. pub fn new(cache: Arc>) -> Self { @@ -95,7 +100,7 @@ impl OnDemand { }; match cached { - Some(hdr) => sender.complete(hdr), + Some(hdr) => sender.send(hdr).expect(RECEIVER_IN_SCOPE), None => self.dispatch_header_by_number(ctx, req, ChtProofSender::Header(sender)), } receiver @@ -111,7 +116,7 @@ impl OnDemand { }; match cached { - Some(score) => sender.complete(score), + Some(score) => sender.send(score).expect(RECEIVER_IN_SCOPE), None => self.dispatch_header_by_number(ctx, req, ChtProofSender::ChainScore(sender)), } @@ -132,7 +137,7 @@ impl OnDemand { }; match cached { - (Some(hdr), Some(score)) => sender.complete((hdr, score)), + (Some(hdr), Some(score)) => sender.send((hdr, score)).expect(RECEIVER_IN_SCOPE), _ => self.dispatch_header_by_number(ctx, req, ChtProofSender::Both(sender)), } @@ -183,7 +188,7 @@ impl OnDemand { pub fn header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash) -> Receiver { let (sender, receiver) = oneshot::channel(); match self.cache.lock().block_header(&req.0) { - Some(hdr) => sender.complete(hdr), + Some(hdr) => sender.send(hdr).expect(RECEIVER_IN_SCOPE), None => self.dispatch_header_by_hash(ctx, req, sender), } receiver @@ -241,7 +246,7 @@ impl OnDemand { stream.begin_list(0); stream.begin_list(0); - sender.complete(encoded::Block::new(stream.out())) + sender.send(encoded::Block::new(stream.out())).expect(RECEIVER_IN_SCOPE) } else { match self.cache.lock().block_body(&req.hash) { Some(body) => { @@ -293,10 +298,10 @@ impl OnDemand { // fast path for empty receipts. if req.0.receipts_root() == SHA3_NULL_RLP { - sender.complete(Vec::new()) + sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE) } else { match self.cache.lock().block_receipts(&req.0.hash()) { - Some(receipts) => sender.complete(receipts), + Some(receipts) => sender.send(receipts).expect(RECEIVER_IN_SCOPE), None => self.dispatch_block_receipts(ctx, req, sender), } } @@ -381,7 +386,7 @@ impl OnDemand { // fast path for no code. if req.code_hash == ::util::sha3::SHA3_EMPTY { - sender.complete(Vec::new()) + sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE) } else { self.dispatch_code(ctx, req, sender); } diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 1bc693a4f..76eefa3a6 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -326,7 +326,7 @@ impl Spec { pub fn load(reader: R) -> Result where R: Read { match ethjson::spec::Spec::load(reader) { Ok(spec) => Ok(spec.into()), - _ => Err("Spec json is invalid".into()), + Err(e) => Err(format!("Spec json is invalid: {}", e)), } } diff --git a/ipfs/Cargo.toml b/ipfs/Cargo.toml index 6ce5518c3..c6241a7aa 100644 --- a/ipfs/Cargo.toml +++ b/ipfs/Cargo.toml @@ -8,9 +8,8 @@ authors = ["Parity Technologies "] [dependencies] ethcore = { path = "../ethcore" } ethcore-util = { path = "../util" } -jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } +jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } rlp = { path = "../util/rlp" } mime = "0.2" -hyper = { default-features = false, git = "https://github.com/ethcore/hyper" } cid = "0.2.1" multihash = "0.5" diff --git a/ipfs/src/error.rs b/ipfs/src/error.rs index 1cbd54f1c..fadd75b9b 100644 --- a/ipfs/src/error.rs +++ b/ipfs/src/error.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use {multihash, cid, hyper}; +use {multihash, cid, http}; use route::Out; pub type Result = ::std::result::Result; @@ -25,7 +25,7 @@ pub enum ServerError { /// Wrapped `std::io::Error` IoError(::std::io::Error), /// Other `hyper` error - Other(hyper::error::Error), + Other(http::hyper::error::Error), /// Invalid --ipfs-api-interface InvalidInterface } @@ -80,8 +80,8 @@ impl From<::std::io::Error> for ServerError { } } -impl From for ServerError { - fn from(err: hyper::error::Error) -> ServerError { +impl From for ServerError { + fn from(err: http::hyper::error::Error) -> ServerError { ServerError::Other(err) } } diff --git a/ipfs/src/lib.rs b/ipfs/src/lib.rs index 80e910f9e..df03b6cd7 100644 --- a/ipfs/src/lib.rs +++ b/ipfs/src/lib.rs @@ -16,14 +16,13 @@ #[macro_use] extern crate mime; -extern crate hyper; extern crate multihash; extern crate cid; extern crate rlp; extern crate ethcore; extern crate ethcore_util as util; -extern crate jsonrpc_http_server; +extern crate jsonrpc_http_server as http; pub mod error; mod route; @@ -33,13 +32,13 @@ use std::sync::Arc; use std::net::{SocketAddr, IpAddr}; use error::ServerError; use route::Out; -use jsonrpc_http_server::cors; -use hyper::server::{Listening, Handler, Request, Response}; -use hyper::net::HttpStream; -use hyper::header::{Vary, ContentLength, ContentType, AccessControlAllowOrigin}; -use hyper::{Next, Encoder, Decoder, Method, RequestUri, StatusCode}; +use http::hyper::server::{Listening, Handler, Request, Response}; +use http::hyper::net::HttpStream; +use http::hyper::header::{self, Vary, ContentLength, ContentType}; +use http::hyper::{Next, Encoder, Decoder, Method, RequestUri, StatusCode}; use ethcore::client::BlockChainClient; +pub use http::{AccessControlAllowOrigin, Host, DomainsValidation}; /// Request/response handler pub struct IpfsHandler { @@ -47,12 +46,12 @@ pub struct IpfsHandler { out: Out, /// How many bytes from the response have been written out_progress: usize, - /// Origin request header - origin: Option, + /// CORS response header + cors_header: Option, /// Allowed CORS domains cors_domains: Option>, /// Hostnames allowed in the `Host` request header - allowed_hosts: Option>, + allowed_hosts: Option>, /// Reference to the Blockchain Client client: Arc, } @@ -62,50 +61,16 @@ impl IpfsHandler { &*self.client } - pub fn new(cors: Option>, hosts: Option>, client: Arc) -> Self { - fn origin_to_header(origin: String) -> AccessControlAllowOrigin { - match origin.as_str() { - "*" => AccessControlAllowOrigin::Any, - "null" | "" => AccessControlAllowOrigin::Null, - _ => AccessControlAllowOrigin::Value(origin), - } - } - + pub fn new(cors: DomainsValidation, hosts: DomainsValidation, client: Arc) -> Self { IpfsHandler { out: Out::Bad("Invalid Request"), out_progress: 0, - origin: None, - cors_domains: cors.map(|vec| vec.into_iter().map(origin_to_header).collect()), - allowed_hosts: hosts, + cors_header: None, + cors_domains: cors.into(), + allowed_hosts: hosts.into(), client: client, } } - - fn is_host_allowed(&self, req: &Request) -> bool { - match self.allowed_hosts { - Some(ref hosts) => jsonrpc_http_server::is_host_header_valid(&req, hosts), - None => true, - } - } - - fn is_origin_allowed(&self) -> bool { - // Check origin header first, no header passed is good news - let origin = match self.origin { - Some(ref origin) => origin, - None => return true, - }; - - let cors_domains = match self.cors_domains { - Some(ref domains) => domains, - None => return false, - }; - - cors_domains.iter().any(|domain| match *domain { - AccessControlAllowOrigin::Value(ref allowed) => origin == allowed, - AccessControlAllowOrigin::Any => true, - AccessControlAllowOrigin::Null => origin == "", - }) - } } /// Implement Hyper's HTTP handler @@ -115,19 +80,20 @@ impl Handler for IpfsHandler { return Next::write(); } - self.origin = cors::read_origin(&req); - if !self.is_host_allowed(&req) { + if !http::is_host_allowed(&req, &self.allowed_hosts) { self.out = Out::Bad("Disallowed Host header"); return Next::write(); } - if !self.is_origin_allowed() { + let cors_header = http::cors_header(&req, &self.cors_domains); + if cors_header == http::CorsHeader::Invalid { self.out = Out::Bad("Disallowed Origin header"); return Next::write(); } + self.cors_header = cors_header.into(); let (path, query) = match *req.uri() { RequestUri::AbsolutePath { ref path, ref query } => (path, query.as_ref().map(AsRef::as_ref)), @@ -176,7 +142,7 @@ impl Handler for IpfsHandler { } } - if let Some(cors_header) = cors::get_cors_header(&self.cors_domains, &self.origin) { + if let Some(cors_header) = self.cors_header.take() { res.headers_mut().set(cors_header); res.headers_mut().set(Vary::Items(vec!["Origin".into()])); } @@ -219,11 +185,11 @@ fn write_chunk(transport: &mut W, progress: &mut usize, data: &[u8]) - } /// Add current interface (default: "127.0.0.1:5001") to list of allowed hosts -fn include_current_interface(mut hosts: Vec, interface: String, port: u16) -> Vec { +fn include_current_interface(mut hosts: Vec, interface: String, port: u16) -> Vec { hosts.push(match port { 80 => interface, _ => format!("{}:{}", interface, port), - }); + }.into()); hosts } @@ -231,17 +197,18 @@ fn include_current_interface(mut hosts: Vec, interface: String, port: u1 pub fn start_server( port: u16, interface: String, - cors: Option>, - hosts: Option>, + cors: DomainsValidation, + hosts: DomainsValidation, client: Arc ) -> Result { let ip: IpAddr = interface.parse().map_err(|_| ServerError::InvalidInterface)?; let addr = SocketAddr::new(ip, port); - let hosts = hosts.map(move |hosts| include_current_interface(hosts, interface, port)); + let hosts: Option> = hosts.into(); + let hosts: DomainsValidation<_> = hosts.map(move |hosts| include_current_interface(hosts, interface, port)).into(); Ok( - hyper::Server::http(&addr)? + http::hyper::Server::http(&addr)? .handle(move |_| IpfsHandler::new(cors.clone(), hosts.clone(), client.clone())) .map(|(listening, srv)| { diff --git a/json/src/hash.rs b/json/src/hash.rs index ae6ba1a81..78fa77bd9 100644 --- a/json/src/hash.rs +++ b/json/src/hash.rs @@ -59,11 +59,11 @@ macro_rules! impl_hash { let value = match value.len() { 0 => $inner::from(0), 2 if value == "0x" => $inner::from(0), - _ if value.starts_with("0x") => $inner::from_str(&value[2..]).map_err(|_| { - Error::custom(format!("Invalid hex value {}.", value).as_str()) + _ if value.starts_with("0x") => $inner::from_str(&value[2..]).map_err(|e| { + Error::custom(format!("Invalid hex value {}: {}", value, e).as_str()) })?, - _ => $inner::from_str(value).map_err(|_| { - Error::custom(format!("Invalid hex value {}.", value).as_str()) + _ => $inner::from_str(value).map_err(|e| { + Error::custom(format!("Invalid hex value {}: {}", value, e).as_str()) })?, }; diff --git a/json/src/uint.rs b/json/src/uint.rs index 281820d78..6b206b380 100644 --- a/json/src/uint.rs +++ b/json/src/uint.rs @@ -63,7 +63,7 @@ impl Visitor for UintVisitor { type Value = Uint; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a hex encoded uint") + write!(formatter, "a hex encoded or decimal uint") } fn visit_u64(self, value: u64) -> Result where E: Error { @@ -74,11 +74,11 @@ impl Visitor for UintVisitor { let value = match value.len() { 0 => U256::from(0), 2 if value.starts_with("0x") => U256::from(0), - _ if value.starts_with("0x") => U256::from_str(&value[2..]).map_err(|_| { - Error::custom(format!("Invalid hex value {}.", value).as_str()) + _ if value.starts_with("0x") => U256::from_str(&value[2..]).map_err(|e| { + Error::custom(format!("Invalid hex value {}: {}", value, e).as_str()) })?, - _ => U256::from_dec_str(value).map_err(|_| { - Error::custom(format!("Invalid decimal value {}.", value).as_str()) + _ => U256::from_dec_str(value).map_err(|e| { + Error::custom(format!("Invalid decimal value {}: {:?}", value, e).as_str()) })? }; diff --git a/parity/dapps.rs b/parity/dapps.rs index b9094c16d..bbd5f4960 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -23,9 +23,8 @@ use ethcore_rpc::informant::RpcStats; use ethsync::SyncProvider; use hash_fetch::fetch::Client as FetchClient; use helpers::replace_home; -use io::PanicHandler; -use jsonrpc_core::reactor::Remote; use rpc_apis::{self, SignerService}; +use parity_reactor; #[derive(Debug, PartialEq, Clone)] pub struct Configuration { @@ -60,11 +59,10 @@ impl Default for Configuration { } pub struct Dependencies { - pub panic_handler: Arc, pub apis: Arc, pub client: Arc, pub sync: Arc, - pub remote: Remote, + pub remote: parity_reactor::TokioRemote, pub fetch: FetchClient, pub signer: Arc, pub stats: Arc, @@ -137,9 +135,9 @@ mod server { use ansi_term::Colour; use ethcore::transaction::{Transaction, Action}; use ethcore::client::{Client, BlockChainClient, BlockId}; + use ethcore_dapps::{AccessControlAllowOrigin, Host}; use ethcore_rpc::is_major_importing; use hash_fetch::urlhint::ContractClient; - use jsonrpc_core::reactor::RpcHandler; use parity_reactor; use rpc_apis; @@ -162,6 +160,8 @@ mod server { Arc::new(Registrar { client: deps.client.clone() }), parity_reactor::Remote::new(deps.remote.clone()), ); + let allowed_hosts: Option> = allowed_hosts.map(|hosts| hosts.into_iter().map(Host::from).collect()); + let cors: Option> = cors.map(|cors| cors.into_iter().map(AccessControlAllowOrigin::from).collect()); let sync = deps.sync.clone(); let client = deps.client.clone(); @@ -172,8 +172,8 @@ mod server { .web_proxy_tokens(Arc::new(move |token| signer.is_valid_web_proxy_access_token(&token))) .extra_dapps(&extra_dapps) .signer_address(deps.signer.address()) - .allowed_hosts(allowed_hosts) - .extra_cors_headers(cors); + .allowed_hosts(allowed_hosts.into()) + .extra_cors_headers(cors.into()); let api_set = if all_apis { warn!("{}", Colour::Red.bold().paint("*** INSECURE *** Running Dapps with all APIs exposed.")); @@ -183,13 +183,12 @@ mod server { rpc_apis::ApiSet::UnsafeContext }; let apis = rpc_apis::setup_rpc(deps.stats, deps.apis.clone(), api_set); - let handler = RpcHandler::new(Arc::new(apis), deps.remote); let start_result = match auth { None => { - server.start_unsecured_http(url, handler) + server.start_unsecured_http(url, apis, deps.remote) }, Some((username, password)) => { - server.start_basic_auth_http(url, &username, &password, handler) + server.start_basic_auth_http(url, &username, &password, apis, deps.remote) }, }; @@ -199,13 +198,7 @@ mod server { _ => Err(format!("WebApps io error: {}", err)), }, Err(e) => Err(format!("WebApps error: {:?}", e)), - Ok(server) => { - let ph = deps.panic_handler; - server.set_panic_handler(move || { - ph.notify_all("Panic in WebApp thread.".to_owned()); - }); - Ok(server) - }, + Ok(server) => Ok(server), } } diff --git a/parity/ipfs.rs b/parity/ipfs.rs index e33dcf68b..760868f91 100644 --- a/parity/ipfs.rs +++ b/parity/ipfs.rs @@ -1,40 +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 . + use std::sync::Arc; -use parity_ipfs_api; +use parity_ipfs_api::{self, AccessControlAllowOrigin, Host}; use parity_ipfs_api::error::ServerError; use ethcore::client::BlockChainClient; use hyper::server::Listening; #[derive(Debug, PartialEq, Clone)] pub struct Configuration { - pub enabled: bool, - pub port: u16, - pub interface: String, - pub cors: Option>, - pub hosts: Option>, + pub enabled: bool, + pub port: u16, + pub interface: String, + pub cors: Option>, + pub hosts: Option>, } impl Default for Configuration { - fn default() -> Self { - Configuration { - enabled: false, - port: 5001, - interface: "127.0.0.1".into(), - cors: None, - hosts: Some(Vec::new()), - } - } + fn default() -> Self { + Configuration { + enabled: false, + port: 5001, + interface: "127.0.0.1".into(), + cors: None, + hosts: Some(Vec::new()), + } + } } pub fn start_server(conf: Configuration, client: Arc) -> Result, ServerError> { - if !conf.enabled { - return Ok(None); - } + if !conf.enabled { + return Ok(None); + } - parity_ipfs_api::start_server( - conf.port, - conf.interface, - conf.cors, - conf.hosts, - client - ).map(Some) + let cors = conf.cors.map(|cors| cors.into_iter().map(AccessControlAllowOrigin::from).collect()); + let hosts = conf.hosts.map(|hosts| hosts.into_iter().map(Host::from).collect()); + + parity_ipfs_api::start_server( + conf.port, + conf.interface, + cors.into(), + hosts.into(), + client + ).map(Some) } diff --git a/parity/rpc.rs b/parity/rpc.rs index 49bd94699..a435f24db 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -18,19 +18,18 @@ use std::fmt; use std::sync::Arc; use std::net::SocketAddr; use std::io; -use io::PanicHandler; use dir::default_data_path; -use ethcore_rpc::{self as rpc, RpcServerError, IpcServerError, Metadata, Origin}; +use ethcore_rpc::{self as rpc, HttpServerError, Metadata, Origin, AccessControlAllowOrigin, Host}; use ethcore_rpc::informant::{RpcStats, Middleware}; use helpers::parity_ipc_path; use hyper; use jsonrpc_core::MetaIoHandler; -use jsonrpc_core::reactor::{RpcHandler, Remote}; use rpc_apis; use rpc_apis::ApiSet; +use parity_reactor::TokioRemote; -pub use ethcore_rpc::{IpcServer, Server as HttpServer}; +pub use ethcore_rpc::{IpcServer, HttpServer}; #[derive(Debug, PartialEq)] pub struct HttpConfiguration { @@ -84,9 +83,8 @@ impl fmt::Display for IpcConfiguration { } pub struct Dependencies { - pub panic_handler: Arc, pub apis: Arc, - pub remote: Remote, + pub remote: TokioRemote, pub stats: Arc, } @@ -102,6 +100,15 @@ impl rpc::HttpMetaExtractor for RpcExtractor { } } +impl rpc::IpcMetaExtractor for RpcExtractor { + fn extract(&self, _req: &rpc::IpcRequestContext) -> Metadata { + let mut metadata = Metadata::default(); + // TODO [ToDr] Extract proper session id when it's available in context. + metadata.origin = Origin::Ipc(1.into()); + metadata + } +} + pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Result, String> { if !conf.enabled { return Ok(None); @@ -123,12 +130,13 @@ pub fn setup_http_rpc_server( allowed_hosts: Option>, apis: ApiSet ) -> Result { - let apis = setup_apis(apis, dependencies); - let handler = RpcHandler::new(Arc::new(apis), dependencies.remote.clone()); - let ph = dependencies.panic_handler.clone(); - let start_result = rpc::start_http(url, cors_domains, allowed_hosts, ph, handler, RpcExtractor); + let handler = setup_apis(apis, dependencies); + let remote = dependencies.remote.clone(); + let cors_domains: Option> = cors_domains.map(|domains| domains.into_iter().map(AccessControlAllowOrigin::from).collect()); + let allowed_hosts: Option> = allowed_hosts.map(|hosts| hosts.into_iter().map(Host::from).collect()); + let start_result = rpc::start_http(url, cors_domains.into(), allowed_hosts.into(), handler, remote, RpcExtractor); match start_result { - Err(RpcServerError::IoError(err)) => match err.kind() { + Err(HttpServerError::IoError(err)) => match err.kind() { io::ErrorKind::AddrInUse => Err(format!("RPC address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --jsonrpc-port and --jsonrpc-interface options.", url)), _ => Err(format!("RPC io error: {}", err)), }, @@ -137,17 +145,16 @@ pub fn setup_http_rpc_server( } } -pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Result>, String> { +pub fn new_ipc(conf: IpcConfiguration, deps: &Dependencies) -> Result, String> { if !conf.enabled { return Ok(None); } Ok(Some(setup_ipc_rpc_server(deps, &conf.socket_addr, conf.apis)?)) } -pub fn setup_ipc_rpc_server(dependencies: &Dependencies, addr: &str, apis: ApiSet) -> Result, String> { - let apis = setup_apis(apis, dependencies); - let handler = RpcHandler::new(Arc::new(apis), dependencies.remote.clone()); - match rpc::start_ipc(addr, handler) { - Err(IpcServerError::Io(io_error)) => Err(format!("RPC io error: {}", io_error)), - Err(any_error) => Err(format!("Rpc error: {:?}", any_error)), +pub fn setup_ipc_rpc_server(dependencies: &Dependencies, addr: &str, apis: ApiSet) -> Result { + let handler = setup_apis(apis, dependencies); + let remote = dependencies.remote.clone(); + match rpc::start_ipc(addr, handler, remote, RpcExtractor) { + Err(io_error) => Err(format!("RPC io error: {}", io_error)), Ok(server) => Ok(server) } } diff --git a/parity/run.rs b/parity/run.rs index f781a8774..86702edfc 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -435,7 +435,6 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R }); let dependencies = rpc::Dependencies { - panic_handler: panic_handler.clone(), apis: deps_for_rpc_apis.clone(), remote: event_loop.raw_remote(), stats: rpc_stats.clone(), @@ -447,7 +446,6 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R // the dapps server let dapps_deps = dapps::Dependencies { - panic_handler: panic_handler.clone(), apis: deps_for_rpc_apis.clone(), client: client.clone(), sync: sync_provider.clone(), @@ -460,7 +458,6 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R // the signer server let signer_deps = signer::Dependencies { - panic_handler: panic_handler.clone(), apis: deps_for_rpc_apis.clone(), remote: event_loop.raw_remote(), rpc_stats: rpc_stats.clone(), diff --git a/parity/signer.rs b/parity/signer.rs index d69670204..664a0e6e4 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -26,8 +26,7 @@ use ethcore_rpc::informant::RpcStats; use ethcore_rpc; use ethcore_signer as signer; use helpers::replace_home; -use io::{ForwardPanic, PanicHandler}; -use jsonrpc_core::reactor::{RpcHandler, Remote}; +use parity_reactor::TokioRemote; use rpc_apis; use path::restrict_permissions_owner; use util::H256; @@ -57,9 +56,8 @@ impl Default for Configuration { } pub struct Dependencies { - pub panic_handler: Arc, pub apis: Arc, - pub remote: Remote, + pub remote: TokioRemote, pub rpc_stats: Arc, } @@ -143,9 +141,9 @@ fn do_start(conf: Configuration, deps: Dependencies) -> Result Result Err(format!("Trusted Signer io error: {}", err)), }, Err(e) => Err(format!("Trusted Signer Error: {:?}", e)), - Ok(server) => { - deps.panic_handler.forward_from(&server); - Ok(server) - }, + Ok(server) => Ok(server), } } diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index d18393902..08f813f68 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -10,18 +10,20 @@ authors = ["Parity Technologies "] [dependencies] futures = "0.1" log = "0.3" +order-stat = "0.1" +rustc-serialize = "0.3" semver = "0.5" serde = "0.9" -serde_json = "0.9" serde_derive = "0.9" -rustc-serialize = "0.3" +serde_json = "0.9" time = "0.1" transient-hashmap = "0.4" -order-stat = "0.1" -jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } -jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } -jsonrpc-ipc-server = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } -jsonrpc-macros = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } + +jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } +jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } +jsonrpc-ipc-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } +jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } + ethcore-io = { path = "../util/io" } ethcore-ipc = { path = "../ipc/rpc" } ethcore-util = { path = "../util" } @@ -36,11 +38,12 @@ ethcore-devtools = { path = "../devtools" } ethcore-light = { path = "../ethcore/light" } ethcore-logger = { path = "../logger" } parity-updater = { path = "../updater" } +parity-reactor = { path = "../util/reactor" } rlp = { path = "../util/rlp" } fetch = { path = "../util/fetch" } -parity-reactor = { path = "../util/reactor" } -clippy = { version = "0.0.103", optional = true} stats = { path = "../util/stats" } +clippy = { version = "0.0.103", optional = true} + [features] dev = ["clippy", "ethcore/dev", "ethcore-util/dev", "ethsync/dev"] diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index a00b138ac..abc51f2ed 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -19,32 +19,33 @@ #![cfg_attr(feature="nightly", feature(plugin))] #![cfg_attr(feature="nightly", plugin(clippy))] -extern crate semver; -extern crate rustc_serialize; -extern crate serde; -extern crate serde_json; -extern crate jsonrpc_core; -extern crate jsonrpc_http_server; - -extern crate ethcore_io as io; -extern crate ethcore; -extern crate ethkey; -extern crate ethcrypto as crypto; -extern crate ethstore; -extern crate ethsync; -extern crate ethash; -extern crate ethcore_light as light; -extern crate ethcore_logger; -extern crate transient_hashmap; -extern crate jsonrpc_ipc_server as ipc; -extern crate ethcore_ipc; -extern crate time; -extern crate rlp; -extern crate fetch; extern crate futures; extern crate order_stat; -extern crate parity_updater as updater; +extern crate rustc_serialize; +extern crate semver; +extern crate serde; +extern crate serde_json; +extern crate time; +extern crate transient_hashmap; + +extern crate jsonrpc_core; +pub extern crate jsonrpc_http_server as http; +pub extern crate jsonrpc_ipc_server as ipc; + +extern crate ethash; +extern crate ethcore; +extern crate ethcore_io as io; +extern crate ethcore_ipc; +extern crate ethcore_light as light; +extern crate ethcrypto as crypto; +extern crate ethkey; +extern crate ethstore; +extern crate ethsync; +extern crate ethcore_logger; +extern crate fetch; extern crate parity_reactor; +extern crate parity_updater as updater; +extern crate rlp; extern crate stats; #[macro_use] @@ -61,57 +62,53 @@ extern crate ethjson; #[cfg(test)] extern crate ethcore_devtools as devtools; -use std::sync::Arc; -use std::net::SocketAddr; -use io::PanicHandler; -use jsonrpc_core::reactor::RpcHandler; - -pub use ipc::{Server as IpcServer, Error as IpcServerError}; -pub use jsonrpc_http_server::{ServerBuilder, Server, RpcServerError, HttpMetaExtractor}; pub mod v1; + +pub use ipc::{Server as IpcServer, MetaExtractor as IpcMetaExtractor, RequestContext as IpcRequestContext}; +pub use http::{HttpMetaExtractor, Server as HttpServer, Error as HttpServerError, AccessControlAllowOrigin, Host}; + pub use v1::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, Metadata, Origin, informant, dispatch}; pub use v1::block_import::is_major_importing; +use std::net::SocketAddr; +use http::tokio_core; + /// Start http server asynchronously and returns result with `Server` handle on success or an error. -pub fn start_http( +pub fn start_http( addr: &SocketAddr, - cors_domains: Option>, - allowed_hosts: Option>, - panic_handler: Arc, - handler: RpcHandler, + cors_domains: http::DomainsValidation, + allowed_hosts: http::DomainsValidation, + handler: H, + remote: tokio_core::reactor::Remote, extractor: T, -) -> Result where +) -> Result where M: jsonrpc_core::Metadata, S: jsonrpc_core::Middleware, + H: Into>, T: HttpMetaExtractor, { - - let cors_domains = cors_domains.map(|domains| { - domains.into_iter() - .map(|v| match v.as_str() { - "*" => jsonrpc_http_server::AccessControlAllowOrigin::Any, - "null" => jsonrpc_http_server::AccessControlAllowOrigin::Null, - v => jsonrpc_http_server::AccessControlAllowOrigin::Value(v.into()), - }) - .collect() - }); - - ServerBuilder::with_rpc_handler(handler) - .meta_extractor(Arc::new(extractor)) + http::ServerBuilder::new(handler) + .event_loop_remote(remote) + .meta_extractor(extractor) .cors(cors_domains.into()) .allowed_hosts(allowed_hosts.into()) - .panic_handler(move || { - panic_handler.notify_all("Panic in RPC thread.".to_owned()); - }) .start_http(addr) } /// Start ipc server asynchronously and returns result with `Server` handle on success or an error. -pub fn start_ipc>( +pub fn start_ipc( addr: &str, - handler: RpcHandler, -) -> Result, ipc::Error> { - let server = ipc::Server::with_rpc_handler(addr, handler)?; - server.run_async()?; - Ok(server) + handler: H, + remote: tokio_core::reactor::Remote, + extractor: T, +) -> ::std::io::Result where + M: jsonrpc_core::Metadata, + S: jsonrpc_core::Middleware, + H: Into>, + T: IpcMetaExtractor, +{ + ipc::ServerBuilder::new(handler) + .event_loop_remote(remote) + .session_metadata_extractor(extractor) + .start(addr) } diff --git a/rpc/src/v1/helpers/mod.rs b/rpc/src/v1/helpers/mod.rs index 76d34abdf..bc99b65ff 100644 --- a/rpc/src/v1/helpers/mod.rs +++ b/rpc/src/v1/helpers/mod.rs @@ -21,6 +21,7 @@ pub mod block_import; pub mod dispatch; pub mod fake_sign; pub mod informant; +pub mod oneshot; mod network_settings; mod poll_manager; diff --git a/rpc/src/v1/helpers/oneshot.rs b/rpc/src/v1/helpers/oneshot.rs new file mode 100644 index 000000000..c128ccf55 --- /dev/null +++ b/rpc/src/v1/helpers/oneshot.rs @@ -0,0 +1,67 @@ +// 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 . + +use jsonrpc_core::Error; +use futures::{self, Future}; +use futures::sync::oneshot; +use v1::helpers::errors; + +pub type Res = Result; + +pub struct Sender { + sender: oneshot::Sender>, +} + +impl Sender { + pub fn send(self, data: Res) { + let res = self.sender.send(data); + if let Err(_) = res { + debug!(target: "rpc", "Responding to a no longer active request."); + } + } +} + +pub struct Receiver { + receiver: oneshot::Receiver>, +} + +impl Future for Receiver { + type Item = T; + type Error = Error; + + fn poll(&mut self) -> futures::Poll { + let res = self.receiver.poll(); + match res { + Ok(futures::Async::NotReady) => Ok(futures::Async::NotReady), + Ok(futures::Async::Ready(Ok(res))) => Ok(futures::Async::Ready(res)), + Ok(futures::Async::Ready(Err(err))) => Err(err), + Err(e) => { + debug!(target: "rpc", "Responding to a canceled request: {:?}", e); + Err(errors::internal("Request was canceled by client.", e)) + }, + } + } +} + +pub fn oneshot() -> (Sender, Receiver) { + let (tx, rx) = futures::oneshot(); + + (Sender { + sender: tx, + }, Receiver { + receiver: rx, + }) +} diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs index dcda6fa9e..598b98763 100644 --- a/rpc/src/v1/impls/signing.rs +++ b/rpc/src/v1/impls/signing.rs @@ -22,10 +22,10 @@ use util::{U256, Mutex}; use ethcore::account_provider::AccountProvider; -use futures::{self, future, BoxFuture, Future}; +use futures::{future, BoxFuture, Future}; use jsonrpc_core::Error; use v1::helpers::{ - errors, + errors, oneshot, DefaultAccount, SIGNING_QUEUE_LIMIT, SigningQueue, ConfirmationPromise, ConfirmationResult, SignerService }; @@ -188,21 +188,20 @@ impl ParitySigning for SigningQueueClient { meta.origin, ); - let (ready, p) = futures::oneshot(); + let (ready, p) = oneshot::oneshot(); // when dispatch is complete res.then(move |res| { // register callback via the oneshot sender. handle_dispatch(res, move |response| { match response { - Ok(RpcConfirmationResponse::Decrypt(data)) => ready.complete(Ok(data)), - Err(e) => ready.complete(Err(e)), - e => ready.complete(Err(errors::internal("Unexpected result.", e))), + Ok(RpcConfirmationResponse::Decrypt(data)) => ready.send(Ok(data)), + Err(e) => ready.send(Err(e)), + e => ready.send(Err(errors::internal("Unexpected result.", e))), } }); - // and wait for that to resolve. - p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))) + p }).boxed() } } @@ -217,18 +216,18 @@ impl EthSigning for SigningQueueClient { meta.origin, ); - let (ready, p) = futures::oneshot(); + let (ready, p) = oneshot::oneshot(); res.then(move |res| { handle_dispatch(res, move |response| { match response { - Ok(RpcConfirmationResponse::Signature(sig)) => ready.complete(Ok(sig)), - Err(e) => ready.complete(Err(e)), - e => ready.complete(Err(errors::internal("Unexpected result.", e))), + Ok(RpcConfirmationResponse::Signature(sig)) => ready.send(Ok(sig)), + Err(e) => ready.send(Err(e)), + e => ready.send(Err(errors::internal("Unexpected result.", e))), } }); - p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))) + p }).boxed() } @@ -239,18 +238,18 @@ impl EthSigning for SigningQueueClient { meta.origin, ); - let (ready, p) = futures::oneshot(); + let (ready, p) = oneshot::oneshot(); res.then(move |res| { handle_dispatch(res, move |response| { match response { - Ok(RpcConfirmationResponse::SendTransaction(hash)) => ready.complete(Ok(hash)), - Err(e) => ready.complete(Err(e)), - e => ready.complete(Err(errors::internal("Unexpected result.", e))), + Ok(RpcConfirmationResponse::SendTransaction(hash)) => ready.send(Ok(hash)), + Err(e) => ready.send(Err(e)), + e => ready.send(Err(errors::internal("Unexpected result.", e))), } }); - p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))) + p }).boxed() } @@ -261,18 +260,18 @@ impl EthSigning for SigningQueueClient { meta.origin, ); - let (ready, p) = futures::oneshot(); + let (ready, p) = oneshot::oneshot(); res.then(move |res| { handle_dispatch(res, move |response| { match response { - Ok(RpcConfirmationResponse::SignTransaction(tx)) => ready.complete(Ok(tx)), - Err(e) => ready.complete(Err(e)), - e => ready.complete(Err(errors::internal("Unexpected result.", e))), + Ok(RpcConfirmationResponse::SignTransaction(tx)) => ready.send(Ok(tx)), + Err(e) => ready.send(Err(e)), + e => ready.send(Err(errors::internal("Unexpected result.", e))), } }); - p.then(|result| futures::done(result.expect("Ready is never dropped nor canceled."))) + p }).boxed() } } diff --git a/rpc/src/v1/tests/helpers/fetch.rs b/rpc/src/v1/tests/helpers/fetch.rs index 58ac96bcb..236dae91b 100644 --- a/rpc/src/v1/tests/helpers/fetch.rs +++ b/rpc/src/v1/tests/helpers/fetch.rs @@ -35,7 +35,7 @@ impl Fetch for TestFetch { let (tx, rx) = futures::oneshot(); thread::spawn(move || { let cursor = io::Cursor::new(b"Some content"); - tx.complete(fetch::Response::from_reader(cursor)); + tx.send(fetch::Response::from_reader(cursor)).unwrap(); }); rx.map_err(|_| fetch::Error::Aborted).boxed() diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 146444c21..ee27c27ba 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -901,7 +901,7 @@ fn rpc_eth_send_transaction_with_bad_to() { "id": 1 }"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid length.","data":null},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: expected a hex-encoded hash with 0x prefix."},"id":1}"#; assert_eq!(tester.io.handle_request_sync(&request), Some(response.into())); } @@ -1084,7 +1084,7 @@ fn rpc_get_work_returns_no_work_if_cant_mine() { eth_tester.client.set_queue_size(10); let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing.","data":null},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing."},"id":1}"#; assert_eq!(eth_tester.io.handle_request_sync(request), Some(response.to_owned())); } @@ -1142,6 +1142,6 @@ fn rpc_get_work_should_timeout() { // Request with timeout of 10 seconds. This should fail. let request = r#"{"jsonrpc": "2.0", "method": "eth_getWork", "params": ["10"], "id": 1}"#; - let err_response = r#"{"jsonrpc":"2.0","error":{"code":-32003,"message":"Work has not changed.","data":null},"id":1}"#; + let err_response = r#"{"jsonrpc":"2.0","error":{"code":-32003,"message":"Work has not changed."},"id":1}"#; assert_eq!(eth_tester.io.handle_request_sync(request), Some(err_response.to_owned())); } diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 8acedc2d4..3af627037 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -357,7 +357,7 @@ fn rpc_parity_unsigned_transactions_count_when_signer_disabled() { let io = deps.default_client(); let request = r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available.","data":null},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available."},"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } @@ -393,7 +393,7 @@ fn rpc_parity_signer_port() { // when let request = r#"{"jsonrpc": "2.0", "method": "parity_signerPort", "params": [], "id": 1}"#; let response1 = r#"{"jsonrpc":"2.0","result":18180,"id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available.","data":null},"id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available."},"id":1}"#; // then assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned())); @@ -411,7 +411,7 @@ fn rpc_parity_dapps_port() { // when let request = r#"{"jsonrpc": "2.0", "method": "parity_dappsPort", "params": [], "id": 1}"#; let response1 = r#"{"jsonrpc":"2.0","result":18080,"id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available.","data":null},"id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available."},"id":1}"#; // then assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned())); @@ -429,7 +429,7 @@ fn rpc_parity_dapps_interface() { // when let request = r#"{"jsonrpc": "2.0", "method": "parity_dappsInterface", "params": [], "id": 1}"#; let response1 = r#"{"jsonrpc":"2.0","result":"127.0.0.1","id":1}"#; - let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available.","data":null},"id":1}"#; + let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Dapps Server is disabled. This API is not available."},"id":1}"#; // then assert_eq!(io1.handle_request_sync(request), Some(response1.to_owned())); diff --git a/rpc/src/v1/tests/mocked/parity_accounts.rs b/rpc/src/v1/tests/mocked/parity_accounts.rs index 304ffd45e..ae4f74b49 100644 --- a/rpc/src/v1/tests/mocked/parity_accounts.rs +++ b/rpc/src/v1/tests/mocked/parity_accounts.rs @@ -230,7 +230,7 @@ fn should_be_able_to_kill_account() { let address = accounts[0]; let request = format!(r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0xf00baba2f00baba2f00baba2f00baba2f00baba2"], "id": 1}}"#); - let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid length 1, expected a tuple of size 2","data":null},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: invalid length 1, expected a tuple of size 2."},"id":1}"#; let res = tester.io.handle_request_sync(&request); assert_eq!(res, Some(response.into())); diff --git a/rpc/src/v1/tests/mocked/traces.rs b/rpc/src/v1/tests/mocked/traces.rs index 83a266d2f..369cf726a 100644 --- a/rpc/src/v1/tests/mocked/traces.rs +++ b/rpc/src/v1/tests/mocked/traces.rs @@ -174,7 +174,7 @@ fn rpc_trace_call_state_pruned() { *tester.client.execution_result.write() = Some(Err(CallError::StatePruned)); let request = r#"{"jsonrpc":"2.0","method":"trace_call","params":[{}, ["stateDiff", "vmTrace", "trace"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive.","data":null},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } @@ -195,7 +195,7 @@ fn rpc_trace_raw_transaction_state_pruned() { *tester.client.execution_result.write() = Some(Err(CallError::StatePruned)); let request = r#"{"jsonrpc":"2.0","method":"trace_rawTransaction","params":["0xf869018609184e72a0008276c094d46e8dd67c5d32be8058bb8eb970870f07244567849184e72a801ba0617f39c1a107b63302449c476d96a6cb17a5842fc98ff0c5bcf4d5c4d8166b95a009fdb6097c6196b9bbafc3a59f02f38d91baeef23d0c60a8e4f23c7714cea3a9", ["stateDiff", "vmTrace", "trace"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive.","data":null},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } @@ -216,7 +216,7 @@ fn rpc_trace_replay_transaction_state_pruned() { *tester.client.execution_result.write() = Some(Err(CallError::StatePruned)); let request = r#"{"jsonrpc":"2.0","method":"trace_replayTransaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["trace", "stateDiff", "vmTrace"]],"id":1}"#; - let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive.","data":null},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#; assert_eq!(tester.io.handle_request_sync(request), Some(response.to_owned())); } diff --git a/rpc/src/v1/types/hash.rs b/rpc/src/v1/types/hash.rs index c96a3433b..2f230510d 100644 --- a/rpc/src/v1/types/hash.rs +++ b/rpc/src/v1/types/hash.rs @@ -124,13 +124,16 @@ macro_rules! impl_hash { type Value = $name; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed, padded, hex-encoded hash of type {}", stringify!($name)) + write!(formatter, "a 0x-prefixed, padded, hex-encoded hash with length {}", $size * 2) } fn visit_str(self, value: &str) -> Result where E: serde::de::Error { + if value.len() < 2 || &value[0..2] != "0x" { + return Err(E::custom("expected a hex-encoded hash with 0x prefix")); + } if value.len() != 2 + $size * 2 { - return Err(E::custom("Invalid length.")); + return Err(E::invalid_length(value.len() - 2, &self)); } match value[2..].from_hex() { @@ -139,7 +142,7 @@ macro_rules! impl_hash { result.copy_from_slice(v); Ok($name(result)) }, - _ => Err(E::custom("Invalid hex value.")) + Err(e) => Err(E::custom(format!("invalid hex value: {:?}", e))), } } diff --git a/rpc/src/v1/types/uint.rs b/rpc/src/v1/types/uint.rs index ba3b83fa7..e61f0ab41 100644 --- a/rpc/src/v1/types/uint.rs +++ b/rpc/src/v1/types/uint.rs @@ -74,20 +74,20 @@ macro_rules! impl_uint { type Value = $name; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed, hex-encoded number of type {}", stringify!($name)) + write!(formatter, "a 0x-prefixed, hex-encoded number of length {}", $size*16) } fn visit_str(self, value: &str) -> Result where E: serde::de::Error { + if value.len() < 2 || &value[0..2] != "0x" { + return Err(E::custom("expected a hex-encoded numbers with 0x prefix")) + } + // 0x + len - if value.len() > 2 + $size * 16 || value.len() < 2 { - return Err(E::custom("Invalid length.")); + if value.len() > 2 + $size * 16 { + return Err(E::invalid_length(value.len() - 2, &self)); } - if &value[0..2] != "0x" { - return Err(E::custom("Use hex encoded numbers with 0x prefix.")) - } - - $other::from_str(&value[2..]).map($name).map_err(|_| E::custom("Invalid hex value.")) + $other::from_str(&value[2..]).map($name).map_err(|e| E::custom(&format!("invalid hex value: {:?}", e))) } fn visit_string(self, value: String) -> Result where E: serde::de::Error { diff --git a/rpc_client/Cargo.toml b/rpc_client/Cargo.toml index 77f9c3edf..eb4a0ecff 100644 --- a/rpc_client/Cargo.toml +++ b/rpc_client/Cargo.toml @@ -1,7 +1,7 @@ [package] -authors = ["Ethcore "] +authors = ["Ethcore "] description = "Parity Rpc Client" -homepage = "http://ethcore.io" +homepage = "http://parity.io" license = "GPL-3.0" name = "parity-rpc-client" version = "1.4.0" @@ -14,8 +14,8 @@ serde = "0.9" serde_json = "0.9" tempdir = "0.3.5" url = "1.2.0" -jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } -ws = { git = "https://github.com/ethcore/ws-rs.git", branch = "mio-upstream-stable" } +jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } +ws = { git = "https://github.com/paritytech/ws-rs.git", branch = "mio-upstream-stable" } ethcore-rpc = { path = "../rpc" } ethcore-signer = { path = "../signer" } ethcore-util = { path = "../util" } diff --git a/rpc_client/src/client.rs b/rpc_client/src/client.rs index 5a4568d9e..3ef7ad7ce 100644 --- a/rpc_client/src/client.rs +++ b/rpc_client/src/client.rs @@ -83,18 +83,24 @@ impl Handler for RpcHandler { } fn on_error(&mut self, err: WsError) { match self.complete.take() { - Some(c) => c.complete(Err(RpcError::WsError(err))), - None => println!("unexpected error: {}", err), + Some(c) => match c.send(Err(RpcError::WsError(err))) { + Ok(_) => {}, + Err(_) => warn!(target: "rpc-client", "Unable to notify about error."), + }, + None => warn!(target: "rpc-client", "unexpected error: {}", err), } } fn on_open(&mut self, _: Handshake) -> WsResult<()> { match (self.complete.take(), self.out.take()) { (Some(c), Some(out)) => { - c.complete(Ok(Rpc { + let res = c.send(Ok(Rpc { out: out, counter: AtomicUsize::new(0), pending: self.pending.clone(), })); + if let Err(_) = res { + warn!(target: "rpc-client", "Unable to open a connection.") + } Ok(()) }, _ => { @@ -137,9 +143,9 @@ impl Handler for RpcHandler { } match self.pending.remove(response_id) { - Some(c) => c.complete(ret.map_err(|err| { - RpcError::JsonRpc(err) - })), + Some(c) => if let Err(_) = c.send(ret.map_err(|err| RpcError::JsonRpc(err))) { + warn!(target: "rpc-client", "Unable to send response.") + }, None => warn!( target: "rpc-client", "warning: unexpected id: {}", @@ -225,7 +231,7 @@ impl Rpc { // both fail and succeed. let c = once.take() .expect("connection closure called only once"); - c.complete(Err(RpcError::WsError(err))); + let _ = c.send(Err(RpcError::WsError(err))); }, // c will complete on the `on_open` event in the Handler _ => () diff --git a/signer/Cargo.toml b/signer/Cargo.toml index 4b80cfac5..2ed4df5be 100644 --- a/signer/Cargo.toml +++ b/signer/Cargo.toml @@ -12,11 +12,12 @@ rustc_version = "0.1" [dependencies] rand = "0.3.14" -jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } +jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } +jsonrpc-server-utils = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } log = "0.3" env_logger = "0.3" +ws = { git = "https://github.com/paritytech/ws-rs.git", branch = "mio-upstream-stable" } parity-dapps-glue = { version = "1.7", optional = true } -ws = { git = "https://github.com/ethcore/ws-rs.git", branch = "mio-upstream-stable" } ethcore-util = { path = "../util" } ethcore-io = { path = "../util/io" } ethcore-rpc = { path = "../rpc" } diff --git a/signer/src/lib.rs b/signer/src/lib.rs index 5cc103ba8..d211e2eac 100644 --- a/signer/src/lib.rs +++ b/signer/src/lib.rs @@ -30,21 +30,23 @@ //! //! ``` //! extern crate jsonrpc_core; +//! extern crate jsonrpc_server_utils; //! extern crate ethcore_signer; //! extern crate ethcore_rpc; //! //! use std::sync::Arc; //! use jsonrpc_core::IoHandler; -//! use jsonrpc_core::reactor::RpcEventLoop; +//! use jsonrpc_server_utils::reactor::RpcEventLoop; //! use ethcore_signer::ServerBuilder; //! use ethcore_rpc::ConfirmationsQueue; //! //! fn main() { //! let queue = Arc::new(ConfirmationsQueue::default()); -//! let io = Arc::new(IoHandler::new().into()); -//! let event_loop = RpcEventLoop::spawn(); +//! let io = IoHandler::default(); +//! let event_loop = RpcEventLoop::spawn().unwrap(); +//! let remote = event_loop.remote(); //! let _server = ServerBuilder::new(queue, "/tmp/authcodes".into()) -//! .start("127.0.0.1:8084".parse().unwrap(), event_loop.handler(io)); +//! .start("127.0.0.1:8084".parse().unwrap(), io, remote); //! } //! ``` @@ -57,6 +59,7 @@ extern crate ethcore_util as util; extern crate ethcore_rpc as rpc; extern crate ethcore_io as io; extern crate jsonrpc_core; +extern crate jsonrpc_server_utils; extern crate ws; extern crate ethcore_devtools as devtools; diff --git a/signer/src/tests/mod.rs b/signer/src/tests/mod.rs index 7de3a167a..bc90a6cd3 100644 --- a/signer/src/tests/mod.rs +++ b/signer/src/tests/mod.rs @@ -22,7 +22,7 @@ use devtools::RandomTempPath; use rpc::ConfirmationsQueue; use jsonrpc_core::IoHandler; -use jsonrpc_core::reactor::RpcEventLoop; +use jsonrpc_server_utils::reactor::RpcEventLoop; use rand; use ServerBuilder; @@ -70,9 +70,10 @@ pub fn serve() -> (ServerLoop, usize, GuardedAuthCodes) { let queue = Arc::new(ConfirmationsQueue::default()); let builder = ServerBuilder::new(queue, path.to_path_buf()); let port = 35000 + rand::random::() % 10000; - let event_loop = RpcEventLoop::spawn(); - let handler = event_loop.handler(Arc::new(IoHandler::default().into())); - let server = builder.start(format!("127.0.0.1:{}", port).parse().unwrap(), handler).unwrap(); + let event_loop = RpcEventLoop::spawn().unwrap(); + let io = IoHandler::default(); + let remote = event_loop.remote(); + let server = builder.start(format!("127.0.0.1:{}", port).parse().unwrap(), io, remote).unwrap(); let res = ServerLoop { server: server, event_loop: event_loop, diff --git a/signer/src/ws_server/mod.rs b/signer/src/ws_server/mod.rs index b799b0f66..314351938 100644 --- a/signer/src/ws_server/mod.rs +++ b/signer/src/ws_server/mod.rs @@ -26,8 +26,8 @@ use std::thread; use std; use io::{PanicHandler, OnPanicListener, MayPanic}; -use jsonrpc_core::{Metadata, Middleware}; -use jsonrpc_core::reactor::RpcHandler; +use jsonrpc_core::{Metadata, Middleware, MetaIoHandler}; +use jsonrpc_server_utils::tokio_core::reactor::Remote; use rpc::{ConfirmationsQueue}; use rpc::informant::RpcStats; @@ -92,21 +92,28 @@ impl ServerBuilder { /// Starts a new `WebSocket` server in separate thread. /// Returns a `Server` handle which closes the server when droped. - pub fn start>(self, addr: SocketAddr, handler: RpcHandler) -> Result { - self.start_with_extractor(addr, handler, NoopExtractor) + pub fn start, H: Into>>( + self, + addr: SocketAddr, + handler: H, + remote: Remote, + ) -> Result { + self.start_with_extractor(addr, handler, remote, NoopExtractor) } /// Starts a new `WebSocket` server in separate thread. /// Returns a `Server` handle which closes the server when droped. - pub fn start_with_extractor, T: session::MetaExtractor>( + pub fn start_with_extractor, H: Into>, T: session::MetaExtractor>( self, addr: SocketAddr, - handler: RpcHandler, + handler: H, + remote: Remote, meta_extractor: T, ) -> Result { Server::start( addr, - handler, + handler.into(), + remote, self.queue, self.authcodes_path, self.skip_origin_validation, @@ -136,7 +143,8 @@ impl Server { /// Returns a `Server` handle which closes the server when droped. fn start, T: session::MetaExtractor>( addr: SocketAddr, - handler: RpcHandler, + handler: MetaIoHandler, + remote: Remote, queue: Arc, authcodes_path: PathBuf, skip_origin_validation: bool, @@ -156,7 +164,7 @@ impl Server { let origin = format!("{}", addr); let port = addr.port(); let ws = ws::Builder::new().with_settings(config).build( - session::Factory::new(handler, origin, port, authcodes_path, skip_origin_validation, stats, meta_extractor) + session::Factory::new(handler, remote, origin, port, authcodes_path, skip_origin_validation, stats, meta_extractor) )?; let panic_handler = PanicHandler::new_in_arc(); diff --git a/signer/src/ws_server/session.rs b/signer/src/ws_server/session.rs index 5194855ab..91984ff05 100644 --- a/signer/src/ws_server/session.rs +++ b/signer/src/ws_server/session.rs @@ -21,8 +21,9 @@ use std::sync::Arc; use std::str::FromStr; use authcode_store::AuthCodes; -use jsonrpc_core::{Metadata, Middleware}; -use jsonrpc_core::reactor::RpcHandler; +use jsonrpc_core::{Metadata, Middleware, MetaIoHandler}; +use jsonrpc_core::futures::Future; +use jsonrpc_server_utils::tokio_core::reactor::Remote; use rpc::informant::RpcStats; use util::{H256, version}; use ws; @@ -145,7 +146,8 @@ pub struct Session, T> { self_origin: String, self_port: u16, authcodes_path: PathBuf, - handler: RpcHandler, + handler: Arc>, + remote: Remote, file_handler: Arc, stats: Option>, meta_extractor: T, @@ -237,7 +239,7 @@ impl, T: MetaExtractor> ws::Handler for Session // TODO [ToDr] Move to on_connect let metadata = self.meta_extractor.extract_metadata(&self.session_id); - self.handler.handle_request(req, metadata, move |response| { + let future = self.handler.handle_request(req, metadata).map(move |response| { if let Some(result) = response { let res = out.send(result); if let Err(e) = res { @@ -245,12 +247,14 @@ impl, T: MetaExtractor> ws::Handler for Session } } }); + self.remote.spawn(move |_| future); Ok(()) } } pub struct Factory, T> { - handler: RpcHandler, + handler: Arc>, + remote: Remote, skip_origin_validation: bool, self_origin: String, self_port: u16, @@ -262,7 +266,8 @@ pub struct Factory, T> { impl, T> Factory { pub fn new( - handler: RpcHandler, + handler: MetaIoHandler, + remote: Remote, self_origin: String, self_port: u16, authcodes_path: PathBuf, @@ -271,7 +276,8 @@ impl, T> Factory { meta_extractor: T, ) -> Self { Factory { - handler: handler, + handler: Arc::new(handler), + remote: remote, skip_origin_validation: skip_origin_validation, self_origin: self_origin, self_port: self_port, @@ -293,6 +299,7 @@ impl, T: MetaExtractor> ws::Factory for Factory session_id: 0.into(), out: sender, handler: self.handler.clone(), + remote: self.remote.clone(), skip_origin_validation: self.skip_origin_validation, self_origin: self.self_origin.clone(), self_port: self.self_port, diff --git a/stratum/Cargo.toml b/stratum/Cargo.toml index df984bef1..83d101b76 100644 --- a/stratum/Cargo.toml +++ b/stratum/Cargo.toml @@ -11,10 +11,9 @@ ethcore-ipc-codegen = { path = "../ipc/codegen" } [dependencies] log = "0.3" -jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } -jsonrpc-macros = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } -jsonrpc-tcp-server = { git = "https://github.com/ethcore/jsonrpc.git", branch = "parity-1.6" } -mio = { git = "https://github.com/ethcore/mio", branch = "v0.5.x" } +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-tcp-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } ethcore-util = { path = "../util" } ethcore-devtools = { path = "../devtools" } lazy_static = "0.2" diff --git a/stratum/src/lib.rs b/stratum/src/lib.rs index 5a7d50613..0042ab1e9 100644 --- a/stratum/src/lib.rs +++ b/stratum/src/lib.rs @@ -45,8 +45,8 @@ pub use traits::{ }; use jsonrpc_tcp_server::{ - Server as JsonRpcServer, RequestContext, MetaExtractor, Dispatcher, - PushMessageError + Server as JsonRpcServer, ServerBuilder as JsonRpcServerBuilder, + RequestContext, MetaExtractor, Dispatcher, PushMessageError, }; use jsonrpc_core::{MetaIoHandler, Params, to_value, Value, Metadata, Compatibility}; use jsonrpc_macros::IoDelegate; @@ -58,6 +58,8 @@ use util::{H256, Hashable, RwLock, RwLockReadGuard}; type RpcResult = BoxFuture; +const NOTIFY_COUNTER_INITIAL: u32 = 16; + struct StratumRpc { stratum: RwLock>>, } @@ -113,7 +115,7 @@ impl MetaExtractor for PeerMetaExtractor { } pub struct Stratum { - rpc_server: JsonRpcServer, + rpc_server: Option, /// Subscribed clients subscribers: RwLock>, /// List of workers supposed to receive job update @@ -130,7 +132,11 @@ pub struct Stratum { tcp_dispatcher: Dispatcher, } -const NOTIFY_COUNTER_INITIAL: u32 = 16; +impl Drop for Stratum { + fn drop(&mut self) { + self.rpc_server.take().map(|server| server.close()); + } +} impl Stratum { pub fn start( @@ -149,12 +155,14 @@ impl Stratum { let mut handler = MetaIoHandler::::with_compatibility(Compatibility::Both); handler.extend_with(delegate); - let server = JsonRpcServer::new(addr.clone(), Arc::new(handler)) - .extractor(Arc::new(PeerMetaExtractor) as Arc>); + let server = JsonRpcServerBuilder::new(handler) + .session_meta_extractor(PeerMetaExtractor); + let tcp_dispatcher = server.dispatcher(); + let server = server.start(addr)?; let stratum = Arc::new(Stratum { - tcp_dispatcher: server.dispatcher(), - rpc_server: server, + tcp_dispatcher: tcp_dispatcher, + rpc_server: Some(server), subscribers: RwLock::new(Vec::new()), job_que: RwLock::new(HashSet::new()), dispatcher: dispatcher, @@ -163,10 +171,6 @@ impl Stratum { notify_counter: RwLock::new(NOTIFY_COUNTER_INITIAL), }); *rpc.stratum.write() = Some(stratum.clone()); - - let running_stratum = stratum.clone(); - ::std::thread::spawn(move || running_stratum.rpc_server.run()); - Ok(stratum) } diff --git a/util/reactor/src/lib.rs b/util/reactor/src/lib.rs index 73ce9e404..e5f04d652 100644 --- a/util/reactor/src/lib.rs +++ b/util/reactor/src/lib.rs @@ -24,7 +24,7 @@ use std::thread; use std::sync::mpsc; use std::time::Duration; use futures::{Future, IntoFuture}; -use self::tokio_core::reactor::{Remote as TokioRemote, Timeout}; +pub use tokio_core::reactor::{Remote as TokioRemote, Timeout}; /// Event Loop for futures. /// Wrapper around `tokio::reactor::Core`. @@ -47,7 +47,7 @@ impl EventLoop { let remote = rx.recv().expect("tx is transfered to a newly spawned thread."); EventLoop { - remote: Remote{ + remote: Remote { inner: Mode::Tokio(remote), }, handle: EventLoopHandle { @@ -190,7 +190,7 @@ impl From for EventLoopHandle { impl Drop for EventLoopHandle { fn drop(&mut self) { - self.close.take().map(|v| v.complete(())); + self.close.take().map(|v| v.send(())); } } @@ -203,7 +203,8 @@ impl EventLoopHandle { /// Finishes this event loop. pub fn close(mut self) { - self.close.take() - .expect("Close is taken only in `close` and `drop`. `close` is consuming; qed").complete(()) + let _ = self.close.take() + .expect("Close is taken only in `close` and `drop`. `close` is consuming; qed") + .send(()); } } From e1f2ccd138c38bcaf0a5d41e78bfc299590b7dcc Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 22 Mar 2017 10:36:41 +0100 Subject: [PATCH 04/41] Extend api.util (#4979) * cleanupValue * abiUnencode & abiSignature * Export new functions --- js/src/api/util/encode.js | 31 +++++++++++++++++++++++++++++++ js/src/api/util/encode.spec.js | 31 +++++++++++++++++++++++++++++-- js/src/api/util/format.js | 32 ++++++++++++++++++++++++++++++++ js/src/api/util/format.spec.js | 24 +++++++++++++++++++++++- js/src/api/util/index.js | 7 +++++-- 5 files changed, 120 insertions(+), 5 deletions(-) diff --git a/js/src/api/util/encode.js b/js/src/api/util/encode.js index 5b5fb5eac..f18fbbe2b 100644 --- a/js/src/api/util/encode.js +++ b/js/src/api/util/encode.js @@ -17,6 +17,10 @@ import Abi from '~/abi'; import Func from '~/abi/spec/function'; +import { abiDecode } from './decode'; +import { cleanupValue } from './format'; +import { sha3 } from './sha3'; + export function encodeMethodCallAbi (methodAbi = {}, values = []) { const func = new Func(methodAbi); const tokens = Abi.encodeTokens(func.inputParamTypes(), values); @@ -36,3 +40,30 @@ export function abiEncode (methodName, inputTypes, data) { return result; } + +export function abiUnencode (abi, data) { + const callsig = data.substr(2, 8); + const op = abi.find((field) => { + return field.type === 'function' && + abiSignature(field.name, field.inputs.map((input) => input.type)).substr(2, 8) === callsig; + }); + + if (!op) { + console.warn(`Unknown function ID: ${callsig}`); + return null; + } + + let argsByIndex = abiDecode(op.inputs.map((field) => field.type), '0x' + data.substr(10)) + .map((value, index) => cleanupValue(value, op.inputs[index].type)); + const argsByName = op.inputs.reduce((result, field, index) => { + result[field.name] = argsByIndex[index]; + + return result; + }, {}); + + return [op.name, argsByName, argsByIndex]; +} + +export function abiSignature (name, inputs) { + return sha3(`${name}(${inputs.join()})`); +} diff --git a/js/src/api/util/encode.spec.js b/js/src/api/util/encode.spec.js index 6fead2ed5..7847f06ec 100644 --- a/js/src/api/util/encode.spec.js +++ b/js/src/api/util/encode.spec.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { abiEncode, encodeMethodCallAbi } from './encode'; +import { abiEncode, abiUnencode, abiSignature, encodeMethodCallAbi } from './encode'; const ABI = { type: 'function', @@ -51,7 +51,11 @@ describe('api/util/encode', () => { }); it('encodes variable values', () => { - expect(abiEncode('hintUrl', ['bytes32', 'string'], ['0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', 'http://foo.bar/'])).to.equal(`0x${VARIABLE}`); + expect( + abiEncode( + 'hintUrl', ['bytes32', 'string'], ['0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', 'http://foo.bar/'] + ) + ).to.equal(`0x${VARIABLE}`); }); it('encodes only the data with null name', () => { @@ -60,4 +64,27 @@ describe('api/util/encode', () => { ).to.equal(`0x${RESULT.substr(8)}`); }); }); + + describe('abiUnencode', () => { + it('decodes data correctly from abi', () => { + expect( + abiUnencode([{ + name: 'test', + type: 'function', + inputs: [ + { type: 'uint', name: 'arga' } + ] + }], '0x1acb6f7700000000000000000000000000000038') + ).to.deep.equal(['test', { arga: 56 }, [56]]); + }); + }); + + // Same example as in abi/util/signature.spec.js + describe('abiSignature', () => { + it('encodes baz(uint32,bool) correctly', () => { + expect( + abiSignature('baz', ['uint32', 'bool']) + ).to.equal('0xcdcd77c0992ec5bbfc459984220f8c45084cc24d9b6efed1fae540db8de801d2'); + }); + }); }); diff --git a/js/src/api/util/format.js b/js/src/api/util/format.js index c1fac8fff..c7594b692 100644 --- a/js/src/api/util/format.js +++ b/js/src/api/util/format.js @@ -20,6 +20,38 @@ export function bytesToHex (bytes) { return '0x' + bytes.map((b) => ('0' + b.toString(16)).slice(-2)).join(''); } +export function cleanupValue (value, type) { + // TODO: make work with arbitrary depth arrays + if (value instanceof Array && type.match(/bytes[0-9]+/)) { + // figure out if it's an ASCII string hiding in there: + let ascii = ''; + + for (let index = 0, ended = false; index < value.length && ascii !== null; ++index) { + const val = value[index]; + + if (val === 0) { + ended = true; + } else { + ascii += String.fromCharCode(val); + } + + if ((ended && val !== 0) || (!ended && (val < 32 || val >= 128))) { + ascii = null; + } + } + + value = ascii === null + ? bytesToHex(value) + : ascii; + } + + if (type.substr(0, 4) === 'uint' && +type.substr(4) <= 48) { + value = +value; + } + + return value; +} + export function hexToBytes (hex) { const raw = toHex(hex).slice(2); const bytes = []; diff --git a/js/src/api/util/format.spec.js b/js/src/api/util/format.spec.js index 8a89c0862..c37205569 100644 --- a/js/src/api/util/format.spec.js +++ b/js/src/api/util/format.spec.js @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import { bytesToHex, hexToBytes, hexToAscii, bytesToAscii, asciiToHex } from './format'; +import { bytesToHex, cleanupValue, hexToBytes, hexToAscii, bytesToAscii, asciiToHex } from './format'; describe('api/util/format', () => { describe('bytesToHex', () => { @@ -27,6 +27,28 @@ describe('api/util/format', () => { }); }); + describe('cleanupValue', () => { + it('returns unknown values as the original', () => { + expect(cleanupValue('original', 'unknown')).to.equal('original'); + }); + + it('returns ascii arrays as ascii', () => { + expect(cleanupValue([97, 115, 99, 105, 105, 0], 'bytes32')).to.equal('ascii'); + }); + + it('returns non-ascii arrays as hex strings', () => { + expect(cleanupValue([97, 200, 0, 0], 'bytes4')).to.equal('0x61c80000'); + }); + + it('returns uint (>48) as the original', () => { + expect(cleanupValue('original', 'uint49')).to.equal('original'); + }); + + it('returns uint (<=48) as the number value', () => { + expect(cleanupValue('12345', 'uint48')).to.equal(12345); + }); + }); + describe('hexToBytes', () => { it('correctly converts an empty string', () => { expect(hexToBytes('')).to.deep.equal([]); diff --git a/js/src/api/util/index.js b/js/src/api/util/index.js index 254b97c74..cc8fc9b93 100644 --- a/js/src/api/util/index.js +++ b/js/src/api/util/index.js @@ -16,8 +16,8 @@ import { isAddress as isAddressValid, toChecksumAddress } from '../../abi/util/address'; import { abiDecode, decodeCallData, decodeMethodInput, methodToAbi } from './decode'; -import { abiEncode, encodeMethodCallAbi } from './encode'; -import { bytesToHex, hexToAscii, asciiToHex } from './format'; +import { abiEncode, abiUnencode, abiSignature, encodeMethodCallAbi } from './encode'; +import { bytesToHex, hexToAscii, asciiToHex, cleanupValue } from './format'; import { fromWei, toWei } from './wei'; import { sha3 } from './sha3'; import { isArray, isFunction, isHex, isInstanceOf, isString } from './types'; @@ -26,6 +26,9 @@ import { createIdentityImg } from './identity'; export default { abiDecode, abiEncode, + abiUnencode, + abiSignature, + cleanupValue, isAddressValid, isArray, isFunction, From b725829bfd799be8a86c66652c340771838621d5 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 22 Mar 2017 09:57:28 +0000 Subject: [PATCH 05/41] [ci skip] js-precompiled 20170322-095443 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 819226c00..66694c7fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1749,7 +1749,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#24a28bd6f55c3540f18e14ec00fbb8f207be25d4" +source = "git+https://github.com/ethcore/js-precompiled.git#69e44266d3b2e434dfcd99d5cba1ac0cbc01a0eb" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index eac319740..23b2ac804 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.23", + "version": "1.7.24", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From a028e445fe8818f6e0b921e49a87676a8159bbde Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 22 Mar 2017 11:56:52 +0100 Subject: [PATCH 06/41] Fixes to the Registry dapp (#4984) * Don't show fee warning when there is none * Hide Warning in Registry onclick * Use the default account in the Registry * Fix Etherscan links in Regsitry --- js/src/api/subscriptions/personal.js | 2 +- js/src/dapps/registry/Accounts/accounts.css | 20 +---- js/src/dapps/registry/Accounts/accounts.js | 83 ++++++------------- js/src/dapps/registry/Accounts/actions.js | 23 +++++ .../registry/Application/application.css | 5 +- .../dapps/registry/Application/application.js | 43 +++++++++- js/src/dapps/registry/Names/actions.js | 14 ++-- js/src/dapps/registry/Records/actions.js | 8 +- js/src/dapps/registry/Reverse/actions.js | 16 ++-- .../registry/addresses/accounts-reducer.js | 4 +- js/src/dapps/registry/util/etherscan-url.js | 2 +- 11 files changed, 118 insertions(+), 102 deletions(-) diff --git a/js/src/api/subscriptions/personal.js b/js/src/api/subscriptions/personal.js index 15b037b42..fa7ae823c 100644 --- a/js/src/api/subscriptions/personal.js +++ b/js/src/api/subscriptions/personal.js @@ -42,7 +42,7 @@ export default class Personal { // FIXME: Because of the different API instances, the "wait for valid changes" approach // doesn't work. Since the defaultAccount is critical to operation, we poll in exactly - // same way we do in ../eth (ala same as eth_blockNumber) and update. This should be moved + // same way we do in ../eth (ala eth_blockNumber) and update. This should be moved // to pub-sub as it becomes available _defaultAccount = (timerDisabled = false) => { const nextTimeout = (timeout = 1000) => { diff --git a/js/src/dapps/registry/Accounts/accounts.css b/js/src/dapps/registry/Accounts/accounts.css index d69c25e2b..d886138f0 100644 --- a/js/src/dapps/registry/Accounts/accounts.css +++ b/js/src/dapps/registry/Accounts/accounts.css @@ -15,24 +15,10 @@ /* along with Parity. If not, see . */ -.button { - /* TODO remove !important once material design lite is used */ - padding: 0 !important; -} - .icon { /* TODO remove !important once material design lite is used */ - margin: 0 !important; - width: 30px !important; height: 30px !important; -} - -.menuIcon { - display: inline-block; - vertical-align: middle; -} -.menuText { - display: inline-block; - line-height: 24px; - vertical-align: top; + margin: 0 !important; + padding: 0 !important; + width: 30px !important; } diff --git a/js/src/dapps/registry/Accounts/accounts.js b/js/src/dapps/registry/Accounts/accounts.js index 0651f6857..b17ad63b1 100644 --- a/js/src/dapps/registry/Accounts/accounts.js +++ b/js/src/dapps/registry/Accounts/accounts.js @@ -17,83 +17,50 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import IconMenu from 'material-ui/IconMenu'; -import IconButton from 'material-ui/IconButton/IconButton'; import AccountIcon from 'material-ui/svg-icons/action/account-circle'; -import MenuItem from 'material-ui/MenuItem'; +import { init } from './actions'; import IdentityIcon from '../IdentityIcon'; -import Address from '../ui/address'; -import { select } from './actions'; import styles from './accounts.css'; class Accounts extends Component { static propTypes = { - all: PropTypes.object.isRequired, - selected: PropTypes.object, + selected: PropTypes.oneOfType([ + PropTypes.oneOf([ null ]), + PropTypes.string + ]), + onInit: PropTypes.func.isRequired + }; - select: PropTypes.func.isRequired + componentWillMount () { + this.props.onInit(); } render () { - const { all, selected } = this.props; + const { selected } = this.props; - const origin = { horizontal: 'right', vertical: 'top' }; - - const accountsButton = ( - - { selected - ? ( - - ) : ( - - ) - } - ); + if (!selected) { + return ( + + ); + } return ( - - { Object.values(all).map(this.renderAccount) } - + ); } - - renderAccount = (account) => { - const { selected } = this.props; - const isSelected = selected && selected.address === account.address; - - return ( - -
- - ); - }; - - onAccountSelect = (e, address) => { - this.props.select(address); - }; } const mapStateToProps = (state) => state.accounts; -const mapDispatchToProps = (dispatch) => bindActionCreators({ select }, dispatch); +const mapDispatchToProps = (dispatch) => bindActionCreators({ + onInit: init +}, dispatch); export default connect(mapStateToProps, mapDispatchToProps)(Accounts); diff --git a/js/src/dapps/registry/Accounts/actions.js b/js/src/dapps/registry/Accounts/actions.js index bacd85f2e..7f38de579 100644 --- a/js/src/dapps/registry/Accounts/actions.js +++ b/js/src/dapps/registry/Accounts/actions.js @@ -14,4 +14,27 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { api } from '../parity'; + export const select = (address) => ({ type: 'accounts select', address }); + +export const init = () => (dispatch) => { + api.subscribe('parity_defaultAccount', (error, accountAddress) => { + if (error) { + return console.error(error); + } + + if (accountAddress) { + dispatch(select(accountAddress)); + } + }); + + return api.parity + .defaultAccount() + .then((accountAddress) => { + dispatch(select(accountAddress)); + }) + .catch((error) => { + console.error(error); + }); +}; diff --git a/js/src/dapps/registry/Application/application.css b/js/src/dapps/registry/Application/application.css index e4fc21a26..b9fac12ae 100644 --- a/js/src/dapps/registry/Application/application.css +++ b/js/src/dapps/registry/Application/application.css @@ -16,9 +16,11 @@ */ .header { + align-items: center; display: flex; justify-content: space-between; - margin: 0; padding: .3em 1em; + margin: 0; + padding: 0.3em 1em; color: #fff; background-color: #333; } @@ -54,6 +56,7 @@ background: #f80; bottom: 0; color: #fff; + cursor: pointer; left: 0; opacity: 1; padding: 1.5em; diff --git a/js/src/dapps/registry/Application/application.js b/js/src/dapps/registry/Application/application.js index 33b3662b1..7857b1ad1 100644 --- a/js/src/dapps/registry/Application/application.js +++ b/js/src/dapps/registry/Application/application.js @@ -49,6 +49,10 @@ export default class Application extends Component { fee: nullableProptype(PropTypes.object.isRequired) }; + state = { + showWarning: true + }; + render () { const { contract, fee } = this.props; let warning = null; @@ -65,9 +69,7 @@ export default class Application extends Component { { this.renderActions() } -
- WARNING: The name registry is experimental. Please ensure that you understand the risks, benefits & consequences of registering a name before doing so. A non-refundable fee of { api.util.fromWei(fee).toFormat(3) }ETH is required for all registrations. -
+ { this.renderWarning() } ) : ( @@ -98,4 +100,39 @@ export default class Application extends Component { ); } + + renderWarning () { + const { showWarning } = this.state; + const { fee } = this.props; + + if (!showWarning) { + return null; + } + + return ( +
+ + WARNING: The name registry is experimental. Please ensure that you understand the risks, + benefits & consequences of registering a name before doing so. + + { + fee && api.util.fromWei(fee).gt(0) + ? ( + +  A non-refundable fee of { api.util.fromWei(fee).toFormat(3) } ETH +  is required for all registrations. + + ) + : null + } +
+ ); + } + + handleHideWarning = () => { + this.setState({ showWarning: false }); + } } diff --git a/js/src/dapps/registry/Names/actions.js b/js/src/dapps/registry/Names/actions.js index 8bc981d0d..9e5ff4fc6 100644 --- a/js/src/dapps/registry/Names/actions.js +++ b/js/src/dapps/registry/Names/actions.js @@ -33,11 +33,11 @@ export const reserveFail = (name, error) => ({ type: 'names reserve fail', name, export const reserve = (name) => (dispatch, getState) => { const state = getState(); - const account = state.accounts.selected; + const accountAddress = state.accounts.selected; const contract = state.contract; const fee = state.fee; - if (!contract || !account) { + if (!contract || !accountAddress) { return; } @@ -58,7 +58,7 @@ export const reserve = (name) => (dispatch, getState) => { const { reserve } = contract.instance; const options = { - from: account.address, + from: accountAddress, value: fee }; const values = [ @@ -88,10 +88,10 @@ export const dropFail = (name, error) => ({ type: 'names drop fail', name, error export const drop = (name) => (dispatch, getState) => { const state = getState(); - const account = state.accounts.selected; + const accountAddress = state.accounts.selected; const contract = state.contract; - if (!contract || !account) { + if (!contract || !accountAddress) { return; } @@ -105,14 +105,14 @@ export const drop = (name) => (dispatch, getState) => { return getOwner(contract, name) .then((owner) => { - if (owner.toLowerCase() !== account.address.toLowerCase()) { + if (owner.toLowerCase() !== accountAddress.toLowerCase()) { throw new Error(`you are not the owner of "${name}"`); } const { drop } = contract.instance; const options = { - from: account.address + from: accountAddress }; const values = [ diff --git a/js/src/dapps/registry/Records/actions.js b/js/src/dapps/registry/Records/actions.js index 9f0d1beff..4b7ef51d1 100644 --- a/js/src/dapps/registry/Records/actions.js +++ b/js/src/dapps/registry/Records/actions.js @@ -30,10 +30,10 @@ export const fail = (error) => ({ type: 'records update fail', error }); export const update = (name, key, value) => (dispatch, getState) => { const state = getState(); - const account = state.accounts.selected; + const accountAddress = state.accounts.selected; const contract = state.contract; - if (!contract || !account) { + if (!contract || !accountAddress) { return; } @@ -42,7 +42,7 @@ export const update = (name, key, value) => (dispatch, getState) => { return getOwner(contract, name) .then((owner) => { - if (owner.toLowerCase() !== account.address.toLowerCase()) { + if (owner.toLowerCase() !== accountAddress.toLowerCase()) { throw new Error(`you are not the owner of "${name}"`); } @@ -51,7 +51,7 @@ export const update = (name, key, value) => (dispatch, getState) => { : contract.instance.setData || contract.instance.set; const options = { - from: account.address + from: accountAddress }; const values = [ diff --git a/js/src/dapps/registry/Reverse/actions.js b/js/src/dapps/registry/Reverse/actions.js index 6effa9f10..f78ff3434 100644 --- a/js/src/dapps/registry/Reverse/actions.js +++ b/js/src/dapps/registry/Reverse/actions.js @@ -30,10 +30,10 @@ export const fail = (action, error) => ({ type: `reverse ${action} fail`, error export const propose = (name, address) => (dispatch, getState) => { const state = getState(); - const account = state.accounts.selected; + const accountAddress = state.accounts.selected; const contract = state.contract; - if (!contract || !account) { + if (!contract || !accountAddress) { return; } @@ -42,14 +42,14 @@ export const propose = (name, address) => (dispatch, getState) => { return getOwner(contract, name) .then((owner) => { - if (owner.toLowerCase() !== account.address.toLowerCase()) { + if (owner.toLowerCase() !== accountAddress.toLowerCase()) { throw new Error(`you are not the owner of "${name}"`); } const { proposeReverse } = contract.instance; const options = { - from: account.address + from: accountAddress }; const values = [ @@ -74,10 +74,10 @@ export const propose = (name, address) => (dispatch, getState) => { export const confirm = (name) => (dispatch, getState) => { const state = getState(); - const account = state.accounts.selected; + const accountAddress = state.accounts.selected; const contract = state.contract; - if (!contract || !account) { + if (!contract || !accountAddress) { return; } @@ -86,14 +86,14 @@ export const confirm = (name) => (dispatch, getState) => { return getOwner(contract, name) .then((owner) => { - if (owner.toLowerCase() !== account.address.toLowerCase()) { + if (owner.toLowerCase() !== accountAddress.toLowerCase()) { throw new Error(`you are not the owner of "${name}"`); } const { confirmReverse } = contract.instance; const options = { - from: account.address + from: accountAddress }; const values = [ diff --git a/js/src/dapps/registry/addresses/accounts-reducer.js b/js/src/dapps/registry/addresses/accounts-reducer.js index 5be0421ec..2ff11ae27 100644 --- a/js/src/dapps/registry/addresses/accounts-reducer.js +++ b/js/src/dapps/registry/addresses/accounts-reducer.js @@ -31,8 +31,8 @@ export default (state = initialState, action) => { return { ...state, all: accounts }; } - if (action.type === 'accounts select' && state.all[action.address]) { - return { ...state, selected: state.all[action.address] }; + if (action.type === 'accounts select') { + return { ...state, selected: action.address }; } return state; diff --git a/js/src/dapps/registry/util/etherscan-url.js b/js/src/dapps/registry/util/etherscan-url.js index bb4e2fe98..68e765c17 100644 --- a/js/src/dapps/registry/util/etherscan-url.js +++ b/js/src/dapps/registry/util/etherscan-url.js @@ -22,7 +22,7 @@ const etherscanUrl = (hash, isTestnet, netVersion) => { hash = hash.toLowerCase().replace(leading0x, ''); const type = hash.length === 40 ? 'address' : 'tx'; - return `https://${externalUrl(isTestnet, netVersion)}/${type}/0x${hash}`; + return `${externalUrl(isTestnet, netVersion)}/${type}/0x${hash}`; }; export default etherscanUrl; From 67c225f971ada7c9c9b28c67d3c1fa11c7048f6f Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 22 Mar 2017 12:12:21 +0100 Subject: [PATCH 07/41] Double click to select account creation type (#4986) * Extend links to whole container * Add doubleClick to Account Creator --- .../CreationType/creationType.js | 8 ++++++ js/src/ui/Container/container.js | 9 +++++-- js/src/ui/SelectionList/selectionList.js | 26 ++++++++++++++----- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/js/src/modals/CreateAccount/CreationType/creationType.js b/js/src/modals/CreateAccount/CreationType/creationType.js index 392db7445..a7d23a667 100644 --- a/js/src/modals/CreateAccount/CreationType/creationType.js +++ b/js/src/modals/CreateAccount/CreationType/creationType.js @@ -145,6 +145,7 @@ export default class CreationType extends Component { items={ TYPES } noStretch onSelectClick={ this.onChange } + onSelectDoubleClick={ this.onSelect } renderItem={ this.renderItem } /> ); @@ -180,4 +181,11 @@ export default class CreationType extends Component { store.setCreateType(item.key); } + + onSelect = (item) => { + const { store } = this.props; + + store.setCreateType(item.key); + store.nextStage(); + } } diff --git a/js/src/ui/Container/container.js b/js/src/ui/Container/container.js index ef26a3ad8..81e48f7f3 100644 --- a/js/src/ui/Container/container.js +++ b/js/src/ui/Container/container.js @@ -82,11 +82,16 @@ export default class Container extends Component { to={ link } > { card } + { this.renderHover() } ) - : card + : ( +
+ { card } + { this.renderHover() } +
+ ) } - { this.renderHover() } ); } diff --git a/js/src/ui/SelectionList/selectionList.js b/js/src/ui/SelectionList/selectionList.js index b8ee1fee6..a7375581d 100644 --- a/js/src/ui/SelectionList/selectionList.js +++ b/js/src/ui/SelectionList/selectionList.js @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +import { noop } from 'lodash'; import React, { Component, PropTypes } from 'react'; import { StarIcon } from '~/ui/Icons'; @@ -29,8 +30,13 @@ export default class SelectionList extends Component { noStretch: PropTypes.bool, onDefaultClick: PropTypes.func, onSelectClick: PropTypes.func.isRequired, + onSelectDoubleClick: PropTypes.func, renderItem: PropTypes.func.isRequired - } + }; + + static defaultProps = { + onSelectDoubleClick: noop + }; render () { const { items, noStretch } = this.props; @@ -45,23 +51,28 @@ export default class SelectionList extends Component { } renderItem = (item, index) => { - const { isChecked, onDefaultClick, onSelectClick, renderItem } = this.props; + const { isChecked, onDefaultClick, onSelectClick, onSelectDoubleClick, renderItem } = this.props; const isSelected = isChecked ? isChecked(item) : item.checked; - const makeDefault = () => { - onDefaultClick(item); + const handleClick = () => { + onSelectClick(item); return false; }; - const selectItem = () => { - onSelectClick(item); + const handleDoubleClick = () => { + onSelectDoubleClick(item); return false; }; let defaultIcon = null; if (onDefaultClick) { + const makeDefault = () => { + onDefaultClick(item); + return false; + }; + defaultIcon = (
{ @@ -85,7 +96,8 @@ export default class SelectionList extends Component {
{ renderItem(item, index) }
From 07ee7a341b86251d9bab09339ad348b257f092e9 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 22 Mar 2017 11:18:36 +0000 Subject: [PATCH 08/41] [ci skip] js-precompiled 20170322-111524 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66694c7fd..91918a115 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1749,7 +1749,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#69e44266d3b2e434dfcd99d5cba1ac0cbc01a0eb" +source = "git+https://github.com/ethcore/js-precompiled.git#2a4710a12e4bdebfd4240891ad81c469b9cd6642" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 23b2ac804..1e1488a0d 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.24", + "version": "1.7.25", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From b6f9cf4ba78ec98870f9efe366f6e521d59f5f21 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 22 Mar 2017 11:32:23 +0000 Subject: [PATCH 09/41] [ci skip] js-precompiled 20170322-112932 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91918a115..458e3cae5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1749,7 +1749,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#2a4710a12e4bdebfd4240891ad81c469b9cd6642" +source = "git+https://github.com/ethcore/js-precompiled.git#9651995aa0fa718b9b9b58f1c7281900643bdf8f" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 1e1488a0d..54cc076fe 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.25", + "version": "1.7.26", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 044d070667c924652ec6c5997eadd0d30e538afb Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Wed, 22 Mar 2017 14:41:46 +0100 Subject: [PATCH 10/41] rlp deserialization refactor, 30% faster (#4901) * fixed naming of rlp modules * RlpStream cleanup * appending short rlp lists (0...55 bytes) is 25% faster * RlpStream does not use bytes module, nor trait Stream * removed unused code from rlp module * compiling ethcore-util with new rlp serialization * compiling parity with new rlp serialization * fixed compiling ethcore-light with new rlp serialization * fixed compiling ethsync with new rlp serialization * moved rlp benches and rlp tests * rlp deserialization refactor, 30% faster * removed redundant comment, print * fixed compiling parity with new rlp deserialization * removed redundant double-space * fixed failing test * updated rlp docs, removed unused traits * fixed rlp benchmarks * replace usage of WriteBytesExt with ByteOrder * removed unused, commented out code * fixed merge conflict --- ethcore/light/src/cht.rs | 2 +- ethcore/light/src/net/mod.rs | 4 +- ethcore/light/src/net/request_credits.rs | 6 +- ethcore/light/src/net/status.rs | 10 +- ethcore/light/src/on_demand/request.rs | 2 +- ethcore/src/block.rs | 15 +- ethcore/src/blockchain/extras.rs | 22 ++- ethcore/src/blooms/bloom.rs | 4 +- ethcore/src/blooms/bloom_group.rs | 4 +- ethcore/src/client/client.rs | 4 +- ethcore/src/engines/authority_round.rs | 2 +- ethcore/src/engines/basic_authority.rs | 2 +- ethcore/src/engines/tendermint/message.rs | 9 +- ethcore/src/engines/tendermint/mod.rs | 2 +- ethcore/src/ethereum/ethash.rs | 2 +- ethcore/src/header.rs | 6 +- ethcore/src/json_tests/transaction.rs | 2 +- ethcore/src/migrations/blocks/v8.rs | 2 +- ethcore/src/migrations/state/v7.rs | 5 +- ethcore/src/migrations/v9.rs | 2 +- ethcore/src/snapshot/account.rs | 4 +- ethcore/src/snapshot/block.rs | 8 +- ethcore/src/snapshot/io.rs | 16 +- ethcore/src/snapshot/mod.rs | 10 +- ethcore/src/spec/spec.rs | 2 +- ethcore/src/state/account.rs | 2 +- ethcore/src/tests/client.rs | 1 - ethcore/src/trace/bloom.rs | 8 +- ethcore/src/types/basic_account.rs | 3 +- ethcore/src/types/encoded.rs | 2 +- ethcore/src/types/executed.rs | 4 +- ethcore/src/types/log_entry.rs | 9 +- ethcore/src/types/receipt.rs | 19 +- ethcore/src/types/snapshot_manifest.rs | 4 +- ethcore/src/types/trace_types/error.rs | 6 +- ethcore/src/types/trace_types/flat.rs | 13 +- ethcore/src/types/trace_types/trace.rs | 122 ++++++------- ethcore/src/types/transaction.rs | 10 +- ethcore/src/verification/verification.rs | 5 +- ethcore/src/views/block.rs | 6 +- ethcore/src/views/body.rs | 6 +- ethcore/src/views/header.rs | 2 +- ethcore/src/views/transaction.rs | 2 +- local-store/src/lib.rs | 2 +- parity/informant.rs | 1 - rpc/src/v1/impls/eth.rs | 2 +- rpc/src/v1/impls/light/eth.rs | 2 +- rpc/src/v1/impls/signer.rs | 2 +- rpc/src/v1/impls/traces.rs | 2 +- rpc/src/v1/tests/mocked/eth.rs | 2 +- sync/src/block_sync.rs | 6 +- sync/src/chain.rs | 24 +-- sync/src/light_sync/response.rs | 2 +- util/network/src/discovery.rs | 2 +- util/network/src/session.rs | 9 +- util/{ => rlp}/benches/rlp.rs | 20 +-- util/rlp/src/bytes.rs | 148 ---------------- util/rlp/src/compression.rs | 8 +- util/rlp/src/error.rs | 9 - util/rlp/src/impls.rs | 159 ++++++++++++++++- util/rlp/src/lib.rs | 19 +- util/rlp/src/rlpin.rs | 201 ++++++++++++++++----- util/rlp/src/traits.rs | 204 +--------------------- util/rlp/src/untrusted_rlp.rs | 185 +++++--------------- util/rlp/{src => tests}/tests.rs | 56 +++--- util/src/journaldb/earlymergedb.rs | 6 +- util/src/journaldb/overlayrecentdb.rs | 2 +- util/src/journaldb/refcounteddb.rs | 2 +- util/src/kvdb.rs | 2 +- util/src/trie/lookup.rs | 2 +- util/src/trie/triedbmut.rs | 2 +- 71 files changed, 618 insertions(+), 834 deletions(-) rename util/{ => rlp}/benches/rlp.rs (85%) delete mode 100644 util/rlp/src/bytes.rs rename util/rlp/{src => tests}/tests.rs (90%) diff --git a/ethcore/light/src/cht.rs b/ethcore/light/src/cht.rs index 94b9946c0..7c749b44f 100644 --- a/ethcore/light/src/cht.rs +++ b/ethcore/light/src/cht.rs @@ -23,7 +23,7 @@ use ethcore::ids::BlockId; use util::{Bytes, H256, U256, HashDB, MemoryDB}; use util::trie::{self, TrieMut, TrieDBMut, Trie, TrieDB, Recorder}; -use rlp::{RlpStream, UntrustedRlp, View}; +use rlp::{RlpStream, UntrustedRlp}; // encode a key. macro_rules! key { diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 4749dc281..3830dde4b 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -24,7 +24,7 @@ use ethcore::receipt::Receipt; use io::TimerToken; use network::{NetworkProtocolHandler, NetworkContext, PeerId}; -use rlp::{RlpStream, UntrustedRlp, View}; +use rlp::{RlpStream, UntrustedRlp}; use util::hash::H256; use util::{Bytes, DBValue, Mutex, RwLock, U256}; use time::{Duration, SteadyTime}; @@ -953,7 +953,7 @@ impl LightProtocol { let id_guard = self.pre_verify_response(peer, request::Kind::Receipts, &raw)?; let raw_receipts: Vec> = raw.at(2)? .iter() - .map(|x| x.as_val()) + .map(|x| x.as_list()) .collect::>()?; let req_id = id_guard.defuse(); diff --git a/ethcore/light/src/net/request_credits.rs b/ethcore/light/src/net/request_credits.rs index 7f1960fc5..f35c6662f 100644 --- a/ethcore/light/src/net/request_credits.rs +++ b/ethcore/light/src/net/request_credits.rs @@ -126,10 +126,8 @@ impl Encodable for CostTable { } } -impl RlpDecodable for CostTable { - fn decode(decoder: &D) -> Result where D: Decoder { - let rlp = decoder.as_rlp(); - +impl Decodable for CostTable { + fn decode(rlp: &UntrustedRlp) -> Result { let mut headers = None; let mut bodies = None; let mut receipts = None; diff --git a/ethcore/light/src/net/status.rs b/ethcore/light/src/net/status.rs index f5464c036..732826430 100644 --- a/ethcore/light/src/net/status.rs +++ b/ethcore/light/src/net/status.rs @@ -16,7 +16,7 @@ //! Peer status and capabilities. -use rlp::{DecoderError, RlpDecodable, Encodable, RlpStream, UntrustedRlp, View}; +use rlp::{DecoderError, Encodable, Decodable, RlpStream, UntrustedRlp}; use util::{H256, U256}; use super::request_credits::FlowParams; @@ -91,7 +91,7 @@ struct Parser<'a> { impl<'a> Parser<'a> { // expect a specific next key, and decode the value. // error on unexpected key or invalid value. - fn expect(&mut self, key: Key) -> Result { + fn expect(&mut self, key: Key) -> Result { self.expect_raw(key).and_then(|item| item.as_val()) } @@ -110,7 +110,7 @@ impl<'a> Parser<'a> { // get the next key and value RLP. fn get_next(&mut self) -> Result)>, DecoderError> { - while self.pos < self.rlp.item_count() { + while self.pos < self.rlp.item_count()? { let pair = self.rlp.at(self.pos)?; let k: String = pair.val_at(0)?; @@ -374,7 +374,7 @@ mod tests { use super::*; use super::super::request_credits::FlowParams; use util::{U256, H256}; - use rlp::{RlpStream, UntrustedRlp, View}; + use rlp::{RlpStream, UntrustedRlp}; #[test] fn full_handshake() { @@ -474,7 +474,7 @@ mod tests { let handshake = write_handshake(&status, &capabilities, Some(&flow_params)); let interleaved = { let handshake = UntrustedRlp::new(&handshake); - let mut stream = RlpStream::new_list(handshake.item_count() * 3); + let mut stream = RlpStream::new_list(handshake.item_count().unwrap_or(0) * 3); for item in handshake.iter() { stream.append_raw(item.as_raw(), 1); diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index 0b16d091c..1db796982 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -26,7 +26,7 @@ use ethcore::receipt::Receipt; use ethcore::state::{self, ProvedExecution}; use ethcore::transaction::SignedTransaction; -use rlp::{RlpStream, UntrustedRlp, View}; +use rlp::{RlpStream, UntrustedRlp}; use util::{Address, Bytes, DBValue, HashDB, H256, U256}; use util::memorydb::MemoryDB; use util::sha3::Hashable; diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index c0ae90bb4..38278af05 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -20,7 +20,7 @@ use std::cmp; use std::sync::Arc; use std::collections::HashSet; -use rlp::{UntrustedRlp, RlpStream, Encodable, Decodable, Decoder, DecoderError, View}; +use rlp::{UntrustedRlp, RlpStream, Encodable, Decodable, DecoderError}; use util::{Bytes, Address, Uint, Hashable, U256, H256, ordered_trie_root, SHA3_NULL_RLP}; use util::error::{Mismatch, OutOfBounds}; @@ -67,18 +67,17 @@ impl Block { impl Decodable for Block { - fn decode(decoder: &D) -> Result where D: Decoder { - if decoder.as_raw().len() != decoder.as_rlp().payload_info()?.total() { + fn decode(rlp: &UntrustedRlp) -> Result { + if rlp.as_raw().len() != rlp.payload_info()?.total() { return Err(DecoderError::RlpIsTooBig); } - let d = decoder.as_rlp(); - if d.item_count() != 3 { + if rlp.item_count()? != 3 { return Err(DecoderError::RlpIncorrectListLen); } Ok(Block { - header: d.val_at(0)?, - transactions: d.val_at(1)?, - uncles: d.val_at(2)?, + header: rlp.val_at(0)?, + transactions: rlp.list_at(1)?, + uncles: rlp.list_at(2)?, }) } } diff --git a/ethcore/src/blockchain/extras.rs b/ethcore/src/blockchain/extras.rs index 3122a78d7..0c8616a45 100644 --- a/ethcore/src/blockchain/extras.rs +++ b/ethcore/src/blockchain/extras.rs @@ -154,13 +154,12 @@ impl HeapSizeOf for BlockDetails { } impl Decodable for BlockDetails { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let details = BlockDetails { - number: d.val_at(0)?, - total_difficulty: d.val_at(1)?, - parent: d.val_at(2)?, - children: d.val_at(3)?, + number: rlp.val_at(0)?, + total_difficulty: rlp.val_at(1)?, + parent: rlp.val_at(2)?, + children: rlp.list_at(3)?, }; Ok(details) } @@ -190,11 +189,10 @@ impl HeapSizeOf for TransactionAddress { } impl Decodable for TransactionAddress { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let tx_address = TransactionAddress { - block_hash: d.val_at(0)?, - index: d.val_at(1)?, + block_hash: rlp.val_at(0)?, + index: rlp.val_at(1)?, }; Ok(tx_address) @@ -224,9 +222,9 @@ impl BlockReceipts { } impl Decodable for BlockReceipts { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(rlp: &UntrustedRlp) -> Result { Ok(BlockReceipts { - receipts: Decodable::decode(decoder)? + receipts: rlp.as_list()?, }) } } diff --git a/ethcore/src/blooms/bloom.rs b/ethcore/src/blooms/bloom.rs index a3584f739..cac9ff448 100644 --- a/ethcore/src/blooms/bloom.rs +++ b/ethcore/src/blooms/bloom.rs @@ -44,8 +44,8 @@ impl Into for Bloom { } impl Decodable for Bloom { - fn decode(decoder: &D) -> Result where D: Decoder { - Decodable::decode(decoder).map(Bloom) + fn decode(rlp: &UntrustedRlp) -> Result { + LogBloom::decode(rlp).map(Bloom) } } diff --git a/ethcore/src/blooms/bloom_group.rs b/ethcore/src/blooms/bloom_group.rs index 6d8c40f75..087f20b6f 100644 --- a/ethcore/src/blooms/bloom_group.rs +++ b/ethcore/src/blooms/bloom_group.rs @@ -52,8 +52,8 @@ impl Into for BloomGroup { } impl Decodable for BloomGroup { - fn decode(decoder: &D) -> Result where D: Decoder { - let blooms = Decodable::decode(decoder)?; + fn decode(rlp: &UntrustedRlp) -> Result { + let blooms = rlp.as_list()?; let group = BloomGroup { blooms: blooms }; diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 06889a538..d284954e7 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -66,7 +66,7 @@ use evm::{Factory as EvmFactory, Schedule}; use miner::{Miner, MinerService, TransactionImportResult}; use snapshot::{self, io as snapshot_io}; use factory::Factories; -use rlp::{View, UntrustedRlp}; +use rlp::UntrustedRlp; use state_db::StateDB; use rand::OsRng; use client::registry::Registry; @@ -539,7 +539,7 @@ impl Client { )?; // Commit results - let receipts = ::rlp::decode(&receipts_bytes); + let receipts = ::rlp::decode_list(&receipts_bytes); let mut batch = DBTransaction::new(); chain.insert_unordered_block(&mut batch, &block_bytes, receipts, None, false, true); // Final commit to the DB diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 03b5d785f..2a18c748d 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -21,7 +21,7 @@ use std::sync::Weak; use std::time::{UNIX_EPOCH, Duration}; use util::*; use ethkey::{verify_address, Signature}; -use rlp::{UntrustedRlp, View, encode}; +use rlp::{UntrustedRlp, encode}; use account_provider::AccountProvider; use block::*; use spec::CommonParams; diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 34b89b2d6..d8a1df947 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -138,7 +138,7 @@ impl Engine for BasicAuthority { } fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { - use rlp::{UntrustedRlp, View}; + use rlp::UntrustedRlp; // Check if the signature belongs to a validator, can depend on parent state. let sig = UntrustedRlp::new(&header.seal()[0]).as_val::()?; let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?); diff --git a/ethcore/src/engines/tendermint/message.rs b/ethcore/src/engines/tendermint/message.rs index ead58ba98..0649ea050 100644 --- a/ethcore/src/engines/tendermint/message.rs +++ b/ethcore/src/engines/tendermint/message.rs @@ -20,7 +20,7 @@ use util::*; use super::{Height, View, BlockHash, Step}; use error::Error; use header::Header; -use rlp::{Rlp, UntrustedRlp, RlpStream, Encodable, Decodable, Decoder, DecoderError, View as RlpView}; +use rlp::{Rlp, UntrustedRlp, RlpStream, Encodable, Decodable, DecoderError}; use ethkey::{recover, public_to_address}; use super::super::vote_collector::Message; @@ -150,8 +150,8 @@ impl Step { } impl Decodable for Step { - fn decode(decoder: &D) -> Result where D: Decoder { - match decoder.as_rlp().as_val()? { + fn decode(rlp: &UntrustedRlp) -> Result { + match rlp.as_val()? { 0u8 => Ok(Step::Propose), 1 => Ok(Step::Prevote), 2 => Ok(Step::Precommit), @@ -168,8 +168,7 @@ impl Encodable for Step { /// (signature, (height, view, step, block_hash)) impl Decodable for ConsensusMessage { - fn decode(decoder: &D) -> Result where D: Decoder { - let rlp = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let m = rlp.at(1)?; let block_message: H256 = m.val_at(3)?; Ok(ConsensusMessage { diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 2d2714e97..46e67a2a8 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -33,7 +33,7 @@ use error::{Error, BlockError}; use header::Header; use builtin::Builtin; use env_info::EnvInfo; -use rlp::{UntrustedRlp, View as RlpView}; +use rlp::UntrustedRlp; use ethkey::{recover, public_to_address, Signature}; use account_provider::AccountProvider; use block::*; diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index fbb9c8a5c..b1907971c 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -27,7 +27,7 @@ use transaction::UnverifiedTransaction; use engines::Engine; use evm::Schedule; use ethjson; -use rlp::{self, UntrustedRlp, View}; +use rlp::{self, UntrustedRlp}; /// Parity tries to round block.gas_limit to multiple of this constant pub const PARITY_GAS_LIMIT_DETERMINANT: U256 = U256([37, 0, 0, 0]); diff --git a/ethcore/src/header.rs b/ethcore/src/header.rs index 60b9dcb46..4517c5764 100644 --- a/ethcore/src/header.rs +++ b/ethcore/src/header.rs @@ -261,9 +261,7 @@ impl Header { } impl Decodable for Header { - fn decode(decoder: &D) -> Result where D: Decoder { - let r = decoder.as_rlp(); - + fn decode(r: &UntrustedRlp) -> Result { let mut blockheader = Header { parent_hash: r.val_at(0)?, uncles_hash: r.val_at(1)?, @@ -283,7 +281,7 @@ impl Decodable for Header { bare_hash: RefCell::new(None), }; - for i in 13..r.item_count() { + for i in 13..r.item_count()? { blockheader.seal.push(r.at(i)?.as_raw().to_vec()) } diff --git a/ethcore/src/json_tests/transaction.rs b/ethcore/src/json_tests/transaction.rs index 5c8ac710a..f400180ee 100644 --- a/ethcore/src/json_tests/transaction.rs +++ b/ethcore/src/json_tests/transaction.rs @@ -17,7 +17,7 @@ use super::test_common::*; use evm; use ethjson; -use rlp::{UntrustedRlp, View}; +use rlp::UntrustedRlp; use transaction::{Action, UnverifiedTransaction}; use ethstore::ethkey::public_to_address; diff --git a/ethcore/src/migrations/blocks/v8.rs b/ethcore/src/migrations/blocks/v8.rs index 2c1d955e0..2514db702 100644 --- a/ethcore/src/migrations/blocks/v8.rs +++ b/ethcore/src/migrations/blocks/v8.rs @@ -17,7 +17,7 @@ //! This migration compresses the state db. use util::migration::{SimpleMigration, Progress}; -use rlp::{Compressible, UntrustedRlp, View, RlpType}; +use rlp::{Compressible, UntrustedRlp, RlpType}; /// Compressing migration. #[derive(Default)] diff --git a/ethcore/src/migrations/state/v7.rs b/ethcore/src/migrations/state/v7.rs index 9e2d3c110..e9da80c31 100644 --- a/ethcore/src/migrations/state/v7.rs +++ b/ethcore/src/migrations/state/v7.rs @@ -26,8 +26,7 @@ use util::migration::{Batch, Config, Error, Migration, SimpleMigration, Progress use util::sha3::Hashable; use std::sync::Arc; -use rlp::{decode, Rlp, RlpStream, View}; - +use rlp::{decode, Rlp, RlpStream}; // attempt to migrate a key, value pair. None if migration not possible. fn attempt_migrate(mut key_h: H256, val: &[u8]) -> Option { @@ -184,7 +183,7 @@ impl OverlayRecentV7 { } // migrate all deleted keys. - let mut deleted_keys: Vec = rlp.val_at(2); + let mut deleted_keys: Vec = rlp.list_at(2); for old_key in &mut deleted_keys { if let Some(new) = self.migrated_keys.get(&*old_key) { *old_key = new.clone(); diff --git a/ethcore/src/migrations/v9.rs b/ethcore/src/migrations/v9.rs index 7e469fb7b..9a6eb5088 100644 --- a/ethcore/src/migrations/v9.rs +++ b/ethcore/src/migrations/v9.rs @@ -17,7 +17,7 @@ //! This migration consolidates all databases into single one using Column Families. -use rlp::{Rlp, RlpStream, View}; +use rlp::{Rlp, RlpStream}; use util::kvdb::Database; use util::migration::{Batch, Config, Error, Migration, Progress}; use std::sync::Arc; diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index 62ee28f89..c086b5c44 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -22,7 +22,7 @@ use snapshot::Error; use util::{U256, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP}; use util::trie::{TrieDB, Trie}; -use rlp::{RlpStream, UntrustedRlp, View}; +use rlp::{RlpStream, UntrustedRlp}; use std::collections::HashSet; @@ -180,7 +180,7 @@ mod tests { use util::sha3::{SHA3_EMPTY, SHA3_NULL_RLP}; use util::{Address, H256, HashDB, DBValue}; - use rlp::{UntrustedRlp, View}; + use rlp::UntrustedRlp; use std::collections::HashSet; diff --git a/ethcore/src/snapshot/block.rs b/ethcore/src/snapshot/block.rs index 0eee05575..b5144b23d 100644 --- a/ethcore/src/snapshot/block.rs +++ b/ethcore/src/snapshot/block.rs @@ -20,7 +20,7 @@ use block::Block; use header::Header; use views::BlockView; -use rlp::{DecoderError, RlpStream, UntrustedRlp, View}; +use rlp::{DecoderError, RlpStream, UntrustedRlp}; use util::{Bytes, Hashable, H256}; use util::triehash::ordered_trie_root; @@ -101,8 +101,8 @@ impl AbridgedBlock { header.set_timestamp(rlp.val_at(6)?); header.set_extra_data(rlp.val_at(7)?); - let transactions = rlp.val_at(8)?; - let uncles: Vec
= rlp.val_at(9)?; + let transactions = rlp.list_at(8)?; + let uncles: Vec
= rlp.list_at(9)?; header.set_transactions_root(ordered_trie_root( rlp.at(8)?.iter().map(|r| r.as_raw().to_owned()) @@ -114,7 +114,7 @@ impl AbridgedBlock { header.set_uncles_hash(uncles_rlp.as_raw().sha3()); let mut seal_fields = Vec::new(); - for i in (HEADER_FIELDS + BLOCK_FIELDS)..rlp.item_count() { + for i in (HEADER_FIELDS + BLOCK_FIELDS)..rlp.item_count()? { let seal_rlp = rlp.at(i)?; seal_fields.push(seal_rlp.as_raw().to_owned()); } diff --git a/ethcore/src/snapshot/io.rs b/ethcore/src/snapshot/io.rs index 45f3ec4df..e8f11efd1 100644 --- a/ethcore/src/snapshot/io.rs +++ b/ethcore/src/snapshot/io.rs @@ -27,7 +27,7 @@ use std::path::{Path, PathBuf}; use util::Bytes; use util::hash::H256; -use rlp::{self, Encodable, RlpStream, UntrustedRlp, View}; +use rlp::{self, Encodable, RlpStream, UntrustedRlp}; use super::ManifestData; @@ -57,12 +57,10 @@ impl Encodable for ChunkInfo { } impl rlp::Decodable for ChunkInfo { - fn decode(decoder: &D) -> Result { - let d = decoder.as_rlp(); - - let hash = d.val_at(0)?; - let len = d.val_at(1)?; - let off = d.val_at(2)?; + fn decode(rlp: &UntrustedRlp) -> Result { + let hash = rlp.val_at(0)?; + let len = rlp.val_at(1)?; + let off = rlp.val_at(2)?; Ok(ChunkInfo(hash, len, off)) } } @@ -257,8 +255,8 @@ impl PackedReader { let rlp = UntrustedRlp::new(&manifest_buf); - let state: Vec = rlp.val_at(0)?; - let blocks: Vec = rlp.val_at(1)?; + let state: Vec = rlp.list_at(0)?; + let blocks: Vec = rlp.list_at(1)?; let manifest = ManifestData { state_hashes: state.iter().map(|c| c.0).collect(), diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 473c91e4d..d496139ff 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -37,7 +37,7 @@ use util::journaldb::{self, Algorithm, JournalDB}; use util::kvdb::Database; use util::trie::{TrieDB, TrieDBMut, Trie, TrieMut}; use util::sha3::SHA3_NULL_RLP; -use rlp::{RlpStream, UntrustedRlp, View}; +use rlp::{RlpStream, UntrustedRlp}; use bloom_journal::Bloom; use self::block::AbridgedBlock; @@ -408,10 +408,10 @@ impl StateRebuilder { pub fn feed(&mut self, chunk: &[u8], flag: &AtomicBool) -> Result<(), ::error::Error> { let rlp = UntrustedRlp::new(chunk); let empty_rlp = StateAccount::new_basic(U256::zero(), U256::zero()).rlp(); - let mut pairs = Vec::with_capacity(rlp.item_count()); + let mut pairs = Vec::with_capacity(rlp.item_count()?); // initialize the pairs vector with empty values so we have slots to write into. - pairs.resize(rlp.item_count(), (H256::new(), Vec::new())); + pairs.resize(rlp.item_count()?, (H256::new(), Vec::new())); let status = rebuild_accounts( self.db.as_hashdb_mut(), @@ -601,7 +601,7 @@ impl BlockRebuilder { use util::triehash::ordered_trie_root; let rlp = UntrustedRlp::new(chunk); - let item_count = rlp.item_count(); + let item_count = rlp.item_count()?; let num_blocks = (item_count - 3) as u64; trace!(target: "snapshot", "restoring block chunk with {} blocks.", item_count - 3); @@ -621,7 +621,7 @@ impl BlockRebuilder { let pair = rlp.at(idx)?; let abridged_rlp = pair.at(0)?.as_raw().to_owned(); let abridged_block = AbridgedBlock::from_raw(abridged_rlp); - let receipts: Vec<::receipt::Receipt> = pair.val_at(1)?; + let receipts: Vec<::receipt::Receipt> = pair.list_at(1)?; let receipts_root = ordered_trie_root( pair.at(1)?.iter().map(|r| r.as_raw().to_owned()) ); diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 76eefa3a6..21c07c9a3 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -34,7 +34,7 @@ use super::genesis::Genesis; use super::seal::Generic as GenericSeal; use ethereum; use ethjson; -use rlp::{Rlp, RlpStream, View}; +use rlp::{Rlp, RlpStream}; /// Parameters common to all engines. #[derive(Debug, PartialEq, Clone, Default)] diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs index ebdf36d89..9e762979b 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -461,7 +461,7 @@ impl fmt::Debug for Account { #[cfg(test)] mod tests { - use rlp::{UntrustedRlp, RlpType, View, Compressible}; + use rlp::{UntrustedRlp, RlpType, Compressible}; use util::*; use super::*; use account_db::*; diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 3734c5520..e61edd478 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -25,7 +25,6 @@ use types::filter::Filter; use util::*; use devtools::*; use miner::Miner; -use rlp::View; use spec::Spec; use views::BlockView; use ethkey::{KeyPair, Secret}; diff --git a/ethcore/src/trace/bloom.rs b/ethcore/src/trace/bloom.rs index e0a32d5ae..561a83719 100644 --- a/ethcore/src/trace/bloom.rs +++ b/ethcore/src/trace/bloom.rs @@ -60,8 +60,8 @@ impl Into for BlockTracesBloomGroup { } impl Decodable for BlockTracesBloom { - fn decode(decoder: &D) -> Result where D: Decoder { - Decodable::decode(decoder).map(BlockTracesBloom) + fn decode(rlp: &UntrustedRlp) -> Result { + LogBloom::decode(rlp).map(BlockTracesBloom) } } @@ -72,8 +72,8 @@ impl Encodable for BlockTracesBloom { } impl Decodable for BlockTracesBloomGroup { - fn decode(decoder: &D) -> Result where D: Decoder { - let blooms = Decodable::decode(decoder)?; + fn decode(rlp: &UntrustedRlp) -> Result { + let blooms = rlp.as_list()?; let group = BlockTracesBloomGroup { blooms: blooms }; diff --git a/ethcore/src/types/basic_account.rs b/ethcore/src/types/basic_account.rs index de091827f..c071040cf 100644 --- a/ethcore/src/types/basic_account.rs +++ b/ethcore/src/types/basic_account.rs @@ -43,8 +43,7 @@ impl Encodable for BasicAccount { } impl Decodable for BasicAccount { - fn decode(decoder: &D) -> Result where D: Decoder { - let rlp = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { Ok(BasicAccount { nonce: rlp.val_at(0)?, balance: rlp.val_at(1)?, diff --git a/ethcore/src/types/encoded.rs b/ethcore/src/types/encoded.rs index 49f83f0b7..0a4164044 100644 --- a/ethcore/src/types/encoded.rs +++ b/ethcore/src/types/encoded.rs @@ -29,7 +29,7 @@ use transaction::UnverifiedTransaction; use views; use util::{Address, Hashable, H256, H2048, U256, HeapSizeOf}; -use rlp::{Rlp, View}; +use rlp::Rlp; /// Owning header view. #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/ethcore/src/types/executed.rs b/ethcore/src/types/executed.rs index 4301044ce..f18bd1bee 100644 --- a/ethcore/src/types/executed.rs +++ b/ethcore/src/types/executed.rs @@ -51,8 +51,8 @@ impl Encodable for CallType { } impl Decodable for CallType { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.as_rlp().as_val().and_then(|v| Ok(match v { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.as_val().and_then(|v| Ok(match v { 0u32 => CallType::None, 1 => CallType::Call, 2 => CallType::CallCode, diff --git a/ethcore/src/types/log_entry.rs b/ethcore/src/types/log_entry.rs index 3eac1dad5..f3260c114 100644 --- a/ethcore/src/types/log_entry.rs +++ b/ethcore/src/types/log_entry.rs @@ -47,12 +47,11 @@ impl Encodable for LogEntry { } impl Decodable for LogEntry { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let entry = LogEntry { - address: d.val_at(0)?, - topics: d.val_at(1)?, - data: d.val_at(2)?, + address: rlp.val_at(0)?, + topics: rlp.list_at(1)?, + data: rlp.val_at(2)?, }; Ok(entry) } diff --git a/ethcore/src/types/receipt.rs b/ethcore/src/types/receipt.rs index 5db74b0b6..fc082d40b 100644 --- a/ethcore/src/types/receipt.rs +++ b/ethcore/src/types/receipt.rs @@ -65,21 +65,20 @@ impl Encodable for Receipt { } impl Decodable for Receipt { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); - if d.item_count() == 3 { + fn decode(rlp: &UntrustedRlp) -> Result { + if rlp.item_count()? == 3 { Ok(Receipt { state_root: None, - gas_used: d.val_at(0)?, - log_bloom: d.val_at(1)?, - logs: d.val_at(2)?, + gas_used: rlp.val_at(0)?, + log_bloom: rlp.val_at(1)?, + logs: rlp.list_at(2)?, }) } else { Ok(Receipt { - state_root: Some(d.val_at(0)?), - gas_used: d.val_at(1)?, - log_bloom: d.val_at(2)?, - logs: d.val_at(3)?, + state_root: Some(rlp.val_at(0)?), + gas_used: rlp.val_at(1)?, + log_bloom: rlp.val_at(2)?, + logs: rlp.list_at(3)?, }) } } diff --git a/ethcore/src/types/snapshot_manifest.rs b/ethcore/src/types/snapshot_manifest.rs index 910a038bd..84ca05822 100644 --- a/ethcore/src/types/snapshot_manifest.rs +++ b/ethcore/src/types/snapshot_manifest.rs @@ -53,8 +53,8 @@ impl ManifestData { pub fn from_rlp(raw: &[u8]) -> Result { let decoder = UntrustedRlp::new(raw); - let state_hashes: Vec = decoder.val_at(0)?; - let block_hashes: Vec = decoder.val_at(1)?; + let state_hashes: Vec = decoder.list_at(0)?; + let block_hashes: Vec = decoder.list_at(1)?; let state_root: H256 = decoder.val_at(2)?; let block_number: u64 = decoder.val_at(3)?; let block_hash: H256 = decoder.val_at(4)?; diff --git a/ethcore/src/types/trace_types/error.rs b/ethcore/src/types/trace_types/error.rs index 33ccf2bb7..58657d7aa 100644 --- a/ethcore/src/types/trace_types/error.rs +++ b/ethcore/src/types/trace_types/error.rs @@ -17,7 +17,7 @@ //! Trace errors. use std::fmt; -use rlp::{Encodable, RlpStream, Decodable, Decoder, DecoderError, View}; +use rlp::{Encodable, RlpStream, Decodable, DecoderError, UntrustedRlp}; use evm::Error as EvmError; /// Trace evm errors. @@ -91,9 +91,9 @@ impl Encodable for Error { } impl Decodable for Error { - fn decode(decoder: &D) -> Result where D: Decoder { + fn decode(rlp: &UntrustedRlp) -> Result { use self::Error::*; - let value: u8 = decoder.as_rlp().as_val()?; + let value: u8 = rlp.as_val()?; match value { 0 => Ok(OutOfGas), 1 => Ok(BadJumpDestination), diff --git a/ethcore/src/types/trace_types/flat.rs b/ethcore/src/types/trace_types/flat.rs index 832e7d055..90b770beb 100644 --- a/ethcore/src/types/trace_types/flat.rs +++ b/ethcore/src/types/trace_types/flat.rs @@ -64,9 +64,8 @@ impl Encodable for FlatTrace { } impl Decodable for FlatTrace { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); - let v: Vec = d.val_at(3)?; + fn decode(d: &UntrustedRlp) -> Result { + let v: Vec = d.list_at(3)?; let res = FlatTrace { action: d.val_at(0)?, result: d.val_at(1)?, @@ -108,8 +107,8 @@ impl Encodable for FlatTransactionTraces { } impl Decodable for FlatTransactionTraces { - fn decode(decoder: &D) -> Result where D: Decoder { - Ok(FlatTransactionTraces(Decodable::decode(decoder)?)) + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(FlatTransactionTraces(rlp.as_list()?)) } } @@ -149,8 +148,8 @@ impl Encodable for FlatBlockTraces { } impl Decodable for FlatBlockTraces { - fn decode(decoder: &D) -> Result where D: Decoder { - Ok(FlatBlockTraces(Decodable::decode(decoder)?)) + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(FlatBlockTraces(rlp.as_list()?)) } } diff --git a/ethcore/src/types/trace_types/trace.rs b/ethcore/src/types/trace_types/trace.rs index 9c3377dac..ed5dfb9f1 100644 --- a/ethcore/src/types/trace_types/trace.rs +++ b/ethcore/src/types/trace_types/trace.rs @@ -45,11 +45,10 @@ impl Encodable for CallResult { } impl Decodable for CallResult { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let res = CallResult { - gas_used: d.val_at(0)?, - output: d.val_at(1)?, + gas_used: rlp.val_at(0)?, + output: rlp.val_at(1)?, }; Ok(res) @@ -78,12 +77,11 @@ impl Encodable for CreateResult { } impl Decodable for CreateResult { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let res = CreateResult { - gas_used: d.val_at(0)?, - code: d.val_at(1)?, - address: d.val_at(2)?, + gas_used: rlp.val_at(0)?, + code: rlp.val_at(1)?, + address: rlp.val_at(2)?, }; Ok(res) @@ -141,15 +139,14 @@ impl Encodable for Call { } impl Decodable for Call { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let res = Call { - from: d.val_at(0)?, - to: d.val_at(1)?, - value: d.val_at(2)?, - gas: d.val_at(3)?, - input: d.val_at(4)?, - call_type: d.val_at(5)?, + from: rlp.val_at(0)?, + to: rlp.val_at(1)?, + value: rlp.val_at(2)?, + gas: rlp.val_at(3)?, + input: rlp.val_at(4)?, + call_type: rlp.val_at(5)?, }; Ok(res) @@ -201,13 +198,12 @@ impl Encodable for Create { } impl Decodable for Create { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let res = Create { - from: d.val_at(0)?, - value: d.val_at(1)?, - gas: d.val_at(2)?, - init: d.val_at(3)?, + from: rlp.val_at(0)?, + value: rlp.val_at(1)?, + gas: rlp.val_at(2)?, + init: rlp.val_at(3)?, }; Ok(res) @@ -252,12 +248,11 @@ impl Encodable for Suicide { } impl Decodable for Suicide { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let res = Suicide { - address: d.val_at(0)?, - refund_address: d.val_at(1)?, - balance: d.val_at(2)?, + address: rlp.val_at(0)?, + refund_address: rlp.val_at(1)?, + balance: rlp.val_at(2)?, }; Ok(res) @@ -298,13 +293,12 @@ impl Encodable for Action { } impl Decodable for Action { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); - let action_type: u8 = d.val_at(0)?; + fn decode(rlp: &UntrustedRlp) -> Result { + let action_type: u8 = rlp.val_at(0)?; match action_type { - 0 => d.val_at(1).map(Action::Call), - 1 => d.val_at(1).map(Action::Create), - 2 => d.val_at(1).map(Action::Suicide), + 0 => rlp.val_at(1).map(Action::Call), + 1 => rlp.val_at(1).map(Action::Create), + 2 => rlp.val_at(1).map(Action::Suicide), _ => Err(DecoderError::Custom("Invalid action type.")), } } @@ -369,14 +363,13 @@ impl Encodable for Res { } impl Decodable for Res { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); - let action_type: u8 = d.val_at(0)?; + fn decode(rlp: &UntrustedRlp) -> Result { + let action_type: u8 = rlp.val_at(0)?; match action_type { - 0 => d.val_at(1).map(Res::Call), - 1 => d.val_at(1).map(Res::Create), - 2 => d.val_at(1).map(Res::FailedCall), - 3 => d.val_at(1).map(Res::FailedCreate), + 0 => rlp.val_at(1).map(Res::Call), + 1 => rlp.val_at(1).map(Res::Create), + 2 => rlp.val_at(1).map(Res::FailedCall), + 3 => rlp.val_at(1).map(Res::FailedCreate), 4 => Ok(Res::None), _ => Err(DecoderError::Custom("Invalid result type.")), } @@ -420,11 +413,10 @@ impl Encodable for MemoryDiff { } impl Decodable for MemoryDiff { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { Ok(MemoryDiff { - offset: d.val_at(0)?, - data: d.val_at(1)?, + offset: rlp.val_at(0)?, + data: rlp.val_at(1)?, }) } } @@ -448,11 +440,10 @@ impl Encodable for StorageDiff { } impl Decodable for StorageDiff { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { Ok(StorageDiff { - location: d.val_at(0)?, - value: d.val_at(1)?, + location: rlp.val_at(0)?, + value: rlp.val_at(1)?, }) } } @@ -482,13 +473,12 @@ impl Encodable for VMExecutedOperation { } impl Decodable for VMExecutedOperation { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { Ok(VMExecutedOperation { - gas_used: d.val_at(0)?, - stack_push: d.val_at(1)?, - mem_diff: d.val_at(2)?, - store_diff: d.val_at(3)?, + gas_used: rlp.val_at(0)?, + stack_push: rlp.list_at(1)?, + mem_diff: rlp.val_at(2)?, + store_diff: rlp.val_at(3)?, }) } } @@ -518,13 +508,12 @@ impl Encodable for VMOperation { } impl Decodable for VMOperation { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let res = VMOperation { - pc: d.val_at(0)?, - instruction: d.val_at(1)?, - gas_cost: d.val_at(2)?, - executed: d.val_at(3)?, + pc: rlp.val_at(0)?, + instruction: rlp.val_at(1)?, + gas_cost: rlp.val_at(2)?, + executed: rlp.val_at(3)?, }; Ok(res) @@ -557,13 +546,12 @@ impl Encodable for VMTrace { } impl Decodable for VMTrace { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { let res = VMTrace { - parent_step: d.val_at(0)?, - code: d.val_at(1)?, - operations: d.val_at(2)?, - subs: d.val_at(3)?, + parent_step: rlp.val_at(0)?, + code: rlp.val_at(1)?, + operations: rlp.list_at(2)?, + subs: rlp.list_at(3)?, }; Ok(res) diff --git a/ethcore/src/types/transaction.rs b/ethcore/src/types/transaction.rs index 506bf001e..79e27d97d 100644 --- a/ethcore/src/types/transaction.rs +++ b/ethcore/src/types/transaction.rs @@ -42,8 +42,7 @@ impl Default for Action { } impl Decodable for Action { - fn decode(decoder: &D) -> Result where D: Decoder { - let rlp = decoder.as_rlp(); + fn decode(rlp: &UntrustedRlp) -> Result { if rlp.is_empty() { Ok(Action::Create) } else { @@ -243,12 +242,11 @@ impl Deref for UnverifiedTransaction { } impl Decodable for UnverifiedTransaction { - fn decode(decoder: &D) -> Result where D: Decoder { - let d = decoder.as_rlp(); - if d.item_count() != 9 { + fn decode(d: &UntrustedRlp) -> Result { + if d.item_count()? != 9 { return Err(DecoderError::RlpIncorrectListLen); } - let hash = decoder.as_raw().sha3(); + let hash = d.as_raw().sha3(); Ok(UnverifiedTransaction { unsigned: Transaction { nonce: d.val_at(0)?, diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 7fbeb29bb..ab21ef6c2 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -26,7 +26,7 @@ use engines::Engine; use error::{BlockError, Error}; use blockchain::*; use header::{BlockNumber, Header}; -use rlp::{UntrustedRlp, View}; +use rlp::UntrustedRlp; use transaction::SignedTransaction; use views::BlockView; use time::get_time; @@ -101,7 +101,7 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: & verify_parent(&header, &parent)?; engine.verify_block_family(&header, &parent, Some(bytes))?; - let num_uncles = UntrustedRlp::new(bytes).at(2)?.item_count(); + let num_uncles = UntrustedRlp::new(bytes).at(2)?.item_count()?; if num_uncles != 0 { if num_uncles > engine.maximum_uncle_count() { return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: None, max: Some(engine.maximum_uncle_count()), found: num_uncles }))); @@ -264,7 +264,6 @@ mod tests { use transaction::*; use tests::helpers::*; use types::log_entry::{LogEntry, LocalizedLogEntry}; - use rlp::View; use time::get_time; use encoded; diff --git a/ethcore/src/views/block.rs b/ethcore/src/views/block.rs index a03151d40..463eb7240 100644 --- a/ethcore/src/views/block.rs +++ b/ethcore/src/views/block.rs @@ -20,7 +20,7 @@ use util::*; use header::*; use transaction::*; use super::{TransactionView, HeaderView}; -use rlp::{Rlp, View}; +use rlp::Rlp; /// View onto block rlp. pub struct BlockView<'a> { @@ -69,7 +69,7 @@ impl<'a> BlockView<'a> { /// Return List of transactions in given block. pub fn transactions(&self) -> Vec { - self.rlp.val_at(1) + self.rlp.list_at(1) } /// Return List of transactions with additional localization info. @@ -125,7 +125,7 @@ impl<'a> BlockView<'a> { /// Return list of uncles of given block. pub fn uncles(&self) -> Vec
{ - self.rlp.val_at(2) + self.rlp.list_at(2) } /// Return number of uncles in given block, without deserializing them. diff --git a/ethcore/src/views/body.rs b/ethcore/src/views/body.rs index f3ae0da37..c32796628 100644 --- a/ethcore/src/views/body.rs +++ b/ethcore/src/views/body.rs @@ -20,7 +20,7 @@ use util::*; use header::*; use transaction::*; use super::{TransactionView, HeaderView}; -use rlp::{Rlp, View}; +use rlp::Rlp; /// View onto block rlp. pub struct BodyView<'a> { @@ -49,7 +49,7 @@ impl<'a> BodyView<'a> { /// Return List of transactions in given block. pub fn transactions(&self) -> Vec { - self.rlp.val_at(0) + self.rlp.list_at(0) } /// Return List of transactions with additional localization info. @@ -99,7 +99,7 @@ impl<'a> BodyView<'a> { /// Return list of uncles of given block. pub fn uncles(&self) -> Vec
{ - self.rlp.val_at(1) + self.rlp.list_at(1) } /// Return number of uncles in given block, without deserializing them. diff --git a/ethcore/src/views/header.rs b/ethcore/src/views/header.rs index 9fa07ccc8..618728525 100644 --- a/ethcore/src/views/header.rs +++ b/ethcore/src/views/header.rs @@ -17,7 +17,7 @@ //! View onto block header rlp use util::{U256, Bytes, Hashable, H256, Address, H2048}; -use rlp::{Rlp, View}; +use rlp::Rlp; use header::BlockNumber; /// View onto block header rlp. diff --git a/ethcore/src/views/transaction.rs b/ethcore/src/views/transaction.rs index 72f4e808d..0b5b2e1ff 100644 --- a/ethcore/src/views/transaction.rs +++ b/ethcore/src/views/transaction.rs @@ -16,7 +16,7 @@ //! View onto transaction rlp use util::{U256, Bytes, Hashable, H256}; -use rlp::{Rlp, View}; +use rlp::Rlp; /// View onto transaction rlp. pub struct TransactionView<'a> { diff --git a/local-store/src/lib.rs b/local-store/src/lib.rs index f9e5fe385..d21ca031d 100644 --- a/local-store/src/lib.rs +++ b/local-store/src/lib.rs @@ -25,7 +25,7 @@ use ethcore::transaction::{ }; use ethcore::service::ClientIoMessage; use io::IoHandler; -use rlp::{UntrustedRlp, View}; +use rlp::UntrustedRlp; use util::kvdb::KeyValueDB; extern crate ethcore; diff --git a/parity/informant.rs b/parity/informant.rs index 2fa95db9a..4145b0282 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -32,7 +32,6 @@ use ethcore::snapshot::{RestorationStatus, SnapshotService as SS}; use number_prefix::{binary_prefix, Standalone, Prefixed}; use ethcore_rpc::{is_major_importing}; use ethcore_rpc::informant::RpcStats; -use rlp::View; pub struct Informant { report: RwLock>, diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 52bc2de71..811d5aa90 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -21,7 +21,7 @@ use std::time::{Instant, Duration}; use std::sync::{Arc, Weak}; use futures::{self, future, BoxFuture, Future}; -use rlp::{self, UntrustedRlp, View}; +use rlp::{self, UntrustedRlp}; use time::get_time; use util::{H160, H256, Address, U256, H64}; use util::sha3::Hashable; diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index 1c3d8fffd..c6f0d709d 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -36,7 +36,7 @@ use ethcore::executed::{Executed, ExecutionError}; use ethcore::ids::BlockId; use ethcore::transaction::{Action, SignedTransaction, Transaction as EthTransaction}; use ethsync::LightSync; -use rlp::{UntrustedRlp, View}; +use rlp::UntrustedRlp; use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY_LIST_RLP}; use util::{RwLock, Mutex, Uint, U256}; diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index ffd9f4108..f418a6760 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -18,7 +18,7 @@ use std::sync::{Arc, Weak}; -use rlp::{UntrustedRlp, View}; +use rlp::UntrustedRlp; use ethcore::account_provider::AccountProvider; use ethcore::transaction::{SignedTransaction, PendingTransaction}; use futures::{future, BoxFuture, Future, IntoFuture}; diff --git a/rpc/src/v1/impls/traces.rs b/rpc/src/v1/impls/traces.rs index b4be40a56..466dbb88c 100644 --- a/rpc/src/v1/impls/traces.rs +++ b/rpc/src/v1/impls/traces.rs @@ -18,7 +18,7 @@ use std::sync::{Weak, Arc}; -use rlp::{UntrustedRlp, View}; +use rlp::UntrustedRlp; use ethcore::client::{MiningBlockChainClient, CallAnalytics, TransactionId, TraceId}; use ethcore::miner::MinerService; use ethcore::transaction::SignedTransaction; diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index ee27c27ba..dfd64d38d 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -940,7 +940,7 @@ fn rpc_eth_send_raw_transaction_error() { ], "id": 1 }"#; - let res = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid RLP.","data":"RlpIncorrectListLen"},"id":1}"#.into(); + let res = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid RLP.","data":"RlpExpectedToBeList"},"id":1}"#.into(); assert_eq!(tester.io.handle_request_sync(&req), Some(res)); } diff --git a/sync/src/block_sync.rs b/sync/src/block_sync.rs index 50df6a0c0..e7192d525 100644 --- a/sync/src/block_sync.rs +++ b/sync/src/block_sync.rs @@ -214,7 +214,7 @@ impl BlockDownloader { /// Add new block headers. pub fn import_headers(&mut self, io: &mut SyncIo, r: &UntrustedRlp, expected_hash: Option) -> Result { - let item_count = r.item_count(); + let item_count = r.item_count().unwrap_or(0); if self.state == State::Idle { trace!(target: "sync", "Ignored unexpected block headers"); return Ok(DownloadAction::None) @@ -314,7 +314,7 @@ impl BlockDownloader { /// Called by peer once it has new block bodies pub fn import_bodies(&mut self, _io: &mut SyncIo, r: &UntrustedRlp) -> Result<(), BlockDownloaderImportError> { - let item_count = r.item_count(); + let item_count = r.item_count().unwrap_or(0); if item_count == 0 { return Err(BlockDownloaderImportError::Useless); } @@ -340,7 +340,7 @@ impl BlockDownloader { /// Called by peer once it has new block bodies pub fn import_receipts(&mut self, _io: &mut SyncIo, r: &UntrustedRlp) -> Result<(), BlockDownloaderImportError> { - let item_count = r.item_count(); + let item_count = r.item_count().unwrap_or(0); if item_count == 0 { return Err(BlockDownloaderImportError::Useless); } diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 415b44a39..47ddf6ab1 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -659,7 +659,7 @@ impl ChainSync { let confirmed = match self.peers.get_mut(&peer_id) { Some(ref mut peer) if peer.asking == PeerAsking::ForkHeader => { peer.asking = PeerAsking::Nothing; - let item_count = r.item_count(); + let item_count = r.item_count()?; let (fork_number, fork_hash) = self.fork_block.expect("ForkHeader request is sent only fork block is Some; qed").clone(); if item_count == 0 || item_count != 1 { trace!(target: "sync", "{}: Chain is too short to confirm the block", peer_id); @@ -696,7 +696,7 @@ impl ChainSync { self.continue_sync(io); return Ok(()); } - let item_count = r.item_count(); + let item_count = r.item_count()?; trace!(target: "sync", "{} -> BlockHeaders ({} entries), state = {:?}, set = {:?}", peer_id, item_count, self.state, block_set); if (self.state == SyncState::Idle || self.state == SyncState::WaitingPeers) && self.old_blocks.is_none() { trace!(target: "sync", "Ignored unexpected block headers"); @@ -764,7 +764,7 @@ impl ChainSync { self.continue_sync(io); return Ok(()); } - let item_count = r.item_count(); + let item_count = r.item_count()?; trace!(target: "sync", "{} -> BlockBodies ({} entries), set = {:?}", peer_id, item_count, block_set); if item_count == 0 { self.deactivate_peer(io, peer_id); @@ -818,7 +818,7 @@ impl ChainSync { self.continue_sync(io); return Ok(()); } - let item_count = r.item_count(); + let item_count = r.item_count()?; trace!(target: "sync", "{} -> BlockReceipts ({} entries)", peer_id, item_count); if item_count == 0 { self.deactivate_peer(io, peer_id); @@ -954,7 +954,7 @@ impl ChainSync { self.continue_sync(io); return Ok(()); } - trace!(target: "sync", "{} -> NewHashes ({} entries)", peer_id, r.item_count()); + trace!(target: "sync", "{} -> NewHashes ({} entries)", peer_id, r.item_count()?); let mut max_height: BlockNumber = 0; let mut new_hashes = Vec::new(); let last_imported_number = self.new_blocks.last_imported_block_number(); @@ -1439,7 +1439,7 @@ impl ChainSync { trace!(target: "sync", "{} Ignoring transactions from unconfirmed/unknown peer", peer_id); } - let mut item_count = r.item_count(); + let mut item_count = r.item_count()?; trace!(target: "sync", "{:02} -> Transactions ({} entries)", peer_id, item_count); item_count = min(item_count, MAX_TX_TO_IMPORT); let mut transactions = Vec::with_capacity(item_count); @@ -1557,7 +1557,7 @@ impl ChainSync { /// Respond to GetBlockBodies request fn return_block_bodies(io: &SyncIo, r: &UntrustedRlp, peer_id: PeerId) -> RlpResponseResult { - let mut count = r.item_count(); + let mut count = r.item_count().unwrap_or(0); if count == 0 { debug!(target: "sync", "Empty GetBlockBodies request, ignoring."); return Ok(None); @@ -1579,7 +1579,7 @@ impl ChainSync { /// Respond to GetNodeData request fn return_node_data(io: &SyncIo, r: &UntrustedRlp, peer_id: PeerId) -> RlpResponseResult { - let mut count = r.item_count(); + let mut count = r.item_count().unwrap_or(0); trace!(target: "sync", "{} -> GetNodeData: {} entries", peer_id, count); if count == 0 { debug!(target: "sync", "Empty GetNodeData request, ignoring."); @@ -1603,7 +1603,7 @@ impl ChainSync { } fn return_receipts(io: &SyncIo, rlp: &UntrustedRlp, peer_id: PeerId) -> RlpResponseResult { - let mut count = rlp.item_count(); + let mut count = rlp.item_count().unwrap_or(0); trace!(target: "sync", "{} -> GetReceipts: {} entries", peer_id, count); if count == 0 { debug!(target: "sync", "Empty GetReceipts request, ignoring."); @@ -1628,7 +1628,7 @@ impl ChainSync { /// Respond to GetSnapshotManifest request fn return_snapshot_manifest(io: &SyncIo, r: &UntrustedRlp, peer_id: PeerId) -> RlpResponseResult { - let count = r.item_count(); + let count = r.item_count().unwrap_or(0); trace!(target: "sync", "{} -> GetSnapshotManifest", peer_id); if count != 0 { debug!(target: "sync", "Invalid GetSnapshotManifest request, ignoring."); @@ -2177,7 +2177,7 @@ mod tests { use util::sha3::Hashable; use util::hash::H256; use util::bytes::Bytes; - use rlp::{Rlp, RlpStream, UntrustedRlp, View}; + use rlp::{Rlp, RlpStream, UntrustedRlp}; use super::*; use ::SyncConfig; use super::{PeerInfo, PeerAsking}; @@ -2746,7 +2746,7 @@ mod tests { } let rlp = UntrustedRlp::new(&*p.data); - let item_count = rlp.item_count(); + let item_count = rlp.item_count().unwrap_or(0); if item_count != 1 { return None; } diff --git a/sync/src/light_sync/response.rs b/sync/src/light_sync/response.rs index cb95824ce..0629da956 100644 --- a/sync/src/light_sync/response.rs +++ b/sync/src/light_sync/response.rs @@ -20,7 +20,7 @@ use std::fmt; use ethcore::header::Header; use light::request::{HashOrNumber, Headers as HeadersRequest}; -use rlp::{DecoderError, UntrustedRlp, View}; +use rlp::{DecoderError, UntrustedRlp}; use util::{Bytes, H256}; /// Errors found when decoding headers and verifying with basic constraints. diff --git a/util/network/src/discovery.rs b/util/network/src/discovery.rs index ae4c7e565..ad995188d 100644 --- a/util/network/src/discovery.rs +++ b/util/network/src/discovery.rs @@ -481,7 +481,7 @@ impl Discovery { fn on_neighbours(&mut self, rlp: &UntrustedRlp, _node: &NodeId, from: &SocketAddr) -> Result, NetworkError> { // TODO: validate packet let mut added = HashMap::new(); - trace!(target: "discovery", "Got {} Neighbours from {:?}", rlp.at(0)?.item_count(), &from); + trace!(target: "discovery", "Got {} Neighbours from {:?}", rlp.at(0)?.item_count()?, &from); for r in rlp.at(0)?.iter() { let endpoint = NodeEndpoint::from_rlp(&r)?; if !endpoint.is_valid() { diff --git a/util/network/src/session.rs b/util/network/src/session.rs index bc416b8d8..59eac6e5d 100644 --- a/util/network/src/session.rs +++ b/util/network/src/session.rs @@ -116,9 +116,8 @@ pub struct PeerCapabilityInfo { } impl Decodable for PeerCapabilityInfo { - fn decode(decoder: &D) -> Result where D: Decoder { - let c = decoder.as_rlp(); - let p: Vec = c.val_at(0)?; + fn decode(rlp: &UntrustedRlp) -> Result { + let p: Vec = rlp.val_at(0)?; if p.len() != 3 { return Err(DecoderError::Custom("Invalid subprotocol string length. Should be 3")); } @@ -126,7 +125,7 @@ impl Decodable for PeerCapabilityInfo { p2.clone_from_slice(&p); Ok(PeerCapabilityInfo { protocol: p2, - version: c.val_at(1)? + version: rlp.val_at(1)? }) } } @@ -473,7 +472,7 @@ impl Session { where Message: Send + Sync + Clone { let protocol = rlp.val_at::(0)?; let client_version = rlp.val_at::(1)?; - let peer_caps = rlp.val_at::>(2)?; + let peer_caps: Vec = rlp.list_at(2)?; let id = rlp.val_at::(4)?; // Intersect with host capabilities diff --git a/util/benches/rlp.rs b/util/rlp/benches/rlp.rs similarity index 85% rename from util/benches/rlp.rs rename to util/rlp/benches/rlp.rs index d5aac0d22..3f8166ec4 100644 --- a/util/benches/rlp.rs +++ b/util/rlp/benches/rlp.rs @@ -23,13 +23,12 @@ #![feature(test)] extern crate test; +extern crate ethcore_bigint as bigint; extern crate rlp; -extern crate ethcore_util as util; use test::Bencher; -use std::str::FromStr; -use rlp::*; -use util::U256; +use bigint::prelude::U256; +use rlp::{RlpStream, Rlp}; #[bench] fn bench_stream_u64_value(b: &mut Bencher) { @@ -56,9 +55,8 @@ fn bench_stream_u256_value(b: &mut Bencher) { b.iter(|| { // u256 let mut stream = RlpStream::new(); - stream.append(&U256::from_str("8090a0b0c0d0e0f009102030405060770000000000000001000000000\ - 00012f0") - .unwrap()); + let uint: U256 = "8090a0b0c0d0e0f00910203040506077000000000000000100000000000012f0".into(); + stream.append(&uint); let _ = stream.out(); }); } @@ -93,11 +91,11 @@ fn bench_decode_nested_empty_lists(b: &mut Bencher) { // [ [], [[]], [ [], [[]] ] ] let data = vec![0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]; let rlp = Rlp::new(&data); - let _v0: Vec = rlp.val_at(0); - let _v1: Vec> = rlp.val_at(1); + let _v0: Vec = rlp.at(0).as_list(); + let _v1: Vec = rlp.at(1).at(0).as_list(); let nested_rlp = rlp.at(2); - let _v2a: Vec = nested_rlp.val_at(0); - let _v2b: Vec> = nested_rlp.val_at(1); + let _v2a: Vec = nested_rlp.at(0).as_list(); + let _v2b: Vec = nested_rlp.at(1).at(0).as_list(); }); } diff --git a/util/rlp/src/bytes.rs b/util/rlp/src/bytes.rs deleted file mode 100644 index e5f266f92..000000000 --- a/util/rlp/src/bytes.rs +++ /dev/null @@ -1,148 +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 . - -//! Unified interfaces for RLP bytes operations on basic types -//! - -use std::{mem, fmt, cmp}; -use std::error::Error as StdError; -use bigint::prelude::{U128, U256, H64, H128, H160, H256, H512, H520, H2048}; - -/// Error returned when `FromBytes` conversation goes wrong -#[derive(Debug, PartialEq, Eq)] -pub enum FromBytesError { - /// Expected more RLP data - DataIsTooShort, - /// Extra bytes after the end of the last item - DataIsTooLong, - /// Integer-representation is non-canonically prefixed with zero byte(s). - ZeroPrefixedInt, - /// String representation is not utf-8 - InvalidUtf8, -} - -impl StdError for FromBytesError { - fn description(&self) -> &str { "from_bytes error" } -} - -impl fmt::Display for FromBytesError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self, f) - } -} - -/// Alias for the result of `FromBytes` trait -pub type FromBytesResult = Result; - -/// Converts to given type from its bytes representation -/// -/// TODO: check size of bytes before conversation and return appropriate error -pub trait FromBytes: Sized { - /// Create a value from bytes - fn from_bytes(bytes: &[u8]) -> FromBytesResult; -} - -impl FromBytes for String { - fn from_bytes(bytes: &[u8]) -> FromBytesResult { - ::std::str::from_utf8(bytes).map(|s| s.to_owned()).map_err(|_| FromBytesError::InvalidUtf8) - } -} - -macro_rules! impl_uint_from_bytes { - ($to: ident) => { - impl FromBytes for $to { - fn from_bytes(bytes: &[u8]) -> FromBytesResult<$to> { - match bytes.len() { - 0 => Ok(0), - l if l <= mem::size_of::<$to>() => { - if bytes[0] == 0 { - return Err(FromBytesError::ZeroPrefixedInt) - } - let mut res = 0 as $to; - for i in 0..l { - let shift = (l - 1 - i) * 8; - res = res + ((bytes[i] as $to) << shift); - } - Ok(res) - } - _ => Err(FromBytesError::DataIsTooLong) - } - } - } - } -} - -impl FromBytes for bool { - fn from_bytes(bytes: &[u8]) -> FromBytesResult { - match bytes.len() { - 0 => Ok(false), - 1 => Ok(bytes[0] != 0), - _ => Err(FromBytesError::DataIsTooLong), - } - } -} - -//impl_uint_from_bytes!(u8); -impl_uint_from_bytes!(u16); -impl_uint_from_bytes!(u32); -impl_uint_from_bytes!(u64); -impl_uint_from_bytes!(usize); - -macro_rules! impl_uint_from_bytes { - ($name: ident, $size: expr) => { - impl FromBytes for $name { - fn from_bytes(bytes: &[u8]) -> FromBytesResult<$name> { - if !bytes.is_empty() && bytes[0] == 0 { - Err(FromBytesError::ZeroPrefixedInt) - } else if bytes.len() <= $size { - Ok($name::from(bytes)) - } else { - Err(FromBytesError::DataIsTooLong) - } - } - } - } -} - -impl_uint_from_bytes!(U256, 32); -impl_uint_from_bytes!(U128, 16); - -macro_rules! impl_hash_from_bytes { - ($name: ident, $size: expr) => { - impl FromBytes for $name { - fn from_bytes(bytes: &[u8]) -> FromBytesResult<$name> { - match bytes.len().cmp(&$size) { - cmp::Ordering::Less => Err(FromBytesError::DataIsTooShort), - cmp::Ordering::Greater => Err(FromBytesError::DataIsTooLong), - cmp::Ordering::Equal => { - let mut t = [0u8; $size]; - t.copy_from_slice(bytes); - Ok($name(t)) - } - } - } - } - } -} - -impl_hash_from_bytes!(H64, 8); -impl_hash_from_bytes!(H128, 16); -impl_hash_from_bytes!(H160, 20); -impl_hash_from_bytes!(H256, 32); -impl_hash_from_bytes!(H512, 64); -impl_hash_from_bytes!(H520, 65); -impl_hash_from_bytes!(H2048, 256); - diff --git a/util/rlp/src/compression.rs b/util/rlp/src/compression.rs index b7cf72a4f..7a9c5a917 100644 --- a/util/rlp/src/compression.rs +++ b/util/rlp/src/compression.rs @@ -17,7 +17,7 @@ use std::collections::HashMap; use elastic_array::ElasticArray1024; use common::{BLOCKS_RLP_SWAPPER, SNAPSHOT_RLP_SWAPPER}; -use {UntrustedRlp, View, Compressible, encode, RlpStream}; +use {UntrustedRlp, Compressible, encode, RlpStream}; /// Stores RLPs used for compression pub struct InvalidRlpSwapper<'a> { @@ -69,7 +69,7 @@ fn to_elastic(slice: &[u8]) -> ElasticArray1024 { fn map_rlp(rlp: &UntrustedRlp, f: F) -> Option> where F: Fn(&UntrustedRlp) -> Option> { match rlp.iter() - .fold((false, RlpStream::new_list(rlp.item_count())), + .fold((false, RlpStream::new_list(rlp.item_count().unwrap_or(0))), |(is_some, mut acc), subrlp| { let new = f(&subrlp); if let Some(ref insert) = new { @@ -138,7 +138,7 @@ fn deep_decompress(rlp: &UntrustedRlp, swapper: &InvalidRlpSwapper) -> Option rlp.at(1).ok().map_or(simple_swap(), @@ -169,7 +169,7 @@ impl<'a> Compressible for UntrustedRlp<'a> { #[cfg(test)] mod tests { use compression::InvalidRlpSwapper; - use {UntrustedRlp, Compressible, View, RlpType}; + use {UntrustedRlp, Compressible, RlpType}; #[test] fn invalid_rlp_swapper() { diff --git a/util/rlp/src/error.rs b/util/rlp/src/error.rs index f3b43f850..4ad754dd0 100644 --- a/util/rlp/src/error.rs +++ b/util/rlp/src/error.rs @@ -16,13 +16,10 @@ use std::fmt; use std::error::Error as StdError; -use bytes::FromBytesError; #[derive(Debug, PartialEq, Eq)] /// Error concerning the RLP decoder. pub enum DecoderError { - /// Couldn't convert given bytes to an instance of required type. - FromBytesError(FromBytesError), /// Data has additional bytes at the end of the valid RLP fragment. RlpIsTooBig, /// Data has too few bytes for valid RLP. @@ -56,9 +53,3 @@ impl fmt::Display for DecoderError { fmt::Debug::fmt(&self, f) } } - -impl From for DecoderError { - fn from(err: FromBytesError) -> DecoderError { - DecoderError::FromBytesError(err) - } -} diff --git a/util/rlp/src/impls.rs b/util/rlp/src/impls.rs index affac1ddc..909f3bd63 100644 --- a/util/rlp/src/impls.rs +++ b/util/rlp/src/impls.rs @@ -1,7 +1,26 @@ +use std::{cmp, mem, str}; use byteorder::{ByteOrder, BigEndian}; use bigint::prelude::{Uint, U128, U256, H64, H128, H160, H256, H512, H520, H2048}; -use traits::Encodable; +use traits::{Encodable, Decodable}; use stream::RlpStream; +use {UntrustedRlp, DecoderError}; + +pub fn decode_usize(bytes: &[u8]) -> Result { + match bytes.len() { + l if l <= mem::size_of::() => { + if bytes[0] == 0 { + return Err(DecoderError::RlpInvalidIndirection); + } + let mut res = 0usize; + for i in 0..l { + let shift = (l - 1 - i) * 8; + res = res + ((bytes[i] as usize) << shift); + } + Ok(res) + } + _ => Err(DecoderError::RlpIsTooBig), + } +} impl Encodable for bool { fn rlp_append(&self, s: &mut RlpStream) { @@ -13,6 +32,18 @@ impl Encodable for bool { } } +impl Decodable for bool { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.decoder().decode_value(|bytes| { + match bytes.len() { + 0 => Ok(false), + 1 => Ok(bytes[0] != 0), + _ => Err(DecoderError::RlpIsTooBig), + } + }) + } +} + impl<'a> Encodable for &'a [u8] { fn rlp_append(&self, s: &mut RlpStream) { s.encoder().encode_value(self); @@ -25,6 +56,14 @@ impl Encodable for Vec { } } +impl Decodable for Vec { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.decoder().decode_value(|bytes| { + Ok(bytes.to_vec()) + }) + } +} + impl Encodable for Option where T: Encodable { fn rlp_append(&self, s: &mut RlpStream) { match *self { @@ -39,6 +78,17 @@ impl Encodable for Option where T: Encodable { } } +impl Decodable for Option where T: Decodable { + fn decode(rlp: &UntrustedRlp) -> Result { + let items = rlp.item_count()?; + match items { + 1 => rlp.val_at(0).map(Some), + 0 => Ok(None), + _ => Err(DecoderError::RlpIncorrectListLen), + } + } +} + impl Encodable for u8 { fn rlp_append(&self, s: &mut RlpStream) { if *self != 0 { @@ -49,6 +99,19 @@ impl Encodable for u8 { } } +impl Decodable for u8 { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.decoder().decode_value(|bytes| { + match bytes.len() { + 1 if bytes[0] != 0 => Ok(bytes[0]), + 0 => Ok(0), + 1 => Err(DecoderError::RlpInvalidIndirection), + _ => Err(DecoderError::RlpIsTooBig), + } + }) + } +} + macro_rules! impl_encodable_for_u { ($name: ident, $func: ident, $size: expr) => { impl Encodable for $name { @@ -62,16 +125,52 @@ macro_rules! impl_encodable_for_u { } } +macro_rules! impl_decodable_for_u { + ($name: ident) => { + impl Decodable for $name { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.decoder().decode_value(|bytes| { + match bytes.len() { + 0 | 1 => u8::decode(rlp).map(|v| v as $name), + l if l <= mem::size_of::<$name>() => { + if bytes[0] == 0 { + return Err(DecoderError::RlpInvalidIndirection); + } + let mut res = 0 as $name; + for i in 0..l { + let shift = (l - 1 - i) * 8; + res = res + ((bytes[i] as $name) << shift); + } + Ok(res) + } + _ => Err(DecoderError::RlpIsTooBig), + } + }) + } + } + } +} + impl_encodable_for_u!(u16, write_u16, 2); impl_encodable_for_u!(u32, write_u32, 4); impl_encodable_for_u!(u64, write_u64, 8); +impl_decodable_for_u!(u16); +impl_decodable_for_u!(u32); +impl_decodable_for_u!(u64); + impl Encodable for usize { fn rlp_append(&self, s: &mut RlpStream) { (*self as u64).rlp_append(s); } } +impl Decodable for usize { + fn decode(rlp: &UntrustedRlp) -> Result { + u64::decode(rlp).map(|value| value as usize) + } +} + macro_rules! impl_encodable_for_hash { ($name: ident) => { impl Encodable for $name { @@ -82,6 +181,24 @@ macro_rules! impl_encodable_for_hash { } } +macro_rules! impl_decodable_for_hash { + ($name: ident, $size: expr) => { + impl Decodable for $name { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.decoder().decode_value(|bytes| match bytes.len().cmp(&$size) { + cmp::Ordering::Less => Err(DecoderError::RlpIsTooShort), + cmp::Ordering::Greater => Err(DecoderError::RlpIsTooBig), + cmp::Ordering::Equal => { + let mut t = [0u8; $size]; + t.copy_from_slice(bytes); + Ok($name(t)) + } + }) + } + } + } +} + impl_encodable_for_hash!(H64); impl_encodable_for_hash!(H128); impl_encodable_for_hash!(H160); @@ -90,6 +207,14 @@ impl_encodable_for_hash!(H512); impl_encodable_for_hash!(H520); impl_encodable_for_hash!(H2048); +impl_decodable_for_hash!(H64, 8); +impl_decodable_for_hash!(H128, 16); +impl_decodable_for_hash!(H160, 20); +impl_decodable_for_hash!(H256, 32); +impl_decodable_for_hash!(H512, 64); +impl_decodable_for_hash!(H520, 65); +impl_decodable_for_hash!(H2048, 256); + macro_rules! impl_encodable_for_uint { ($name: ident, $size: expr) => { impl Encodable for $name { @@ -103,9 +228,30 @@ macro_rules! impl_encodable_for_uint { } } +macro_rules! impl_decodable_for_uint { + ($name: ident, $size: expr) => { + impl Decodable for $name { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.decoder().decode_value(|bytes| { + if !bytes.is_empty() && bytes[0] == 0 { + Err(DecoderError::RlpInvalidIndirection) + } else if bytes.len() <= $size { + Ok($name::from(bytes)) + } else { + Err(DecoderError::RlpIsTooBig) + } + }) + } + } + } +} + impl_encodable_for_uint!(U256, 32); impl_encodable_for_uint!(U128, 16); +impl_decodable_for_uint!(U256, 32); +impl_decodable_for_uint!(U128, 16); + impl<'a> Encodable for &'a str { fn rlp_append(&self, s: &mut RlpStream) { s.encoder().encode_value(self.as_bytes()); @@ -118,3 +264,14 @@ impl Encodable for String { } } +impl Decodable for String { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.decoder().decode_value(|bytes| { + match str::from_utf8(bytes) { + Ok(s) => Ok(s.to_owned()), + // consider better error type here + Err(_err) => Err(DecoderError::RlpExpectedToBeData), + } + }) + } +} diff --git a/util/rlp/src/lib.rs b/util/rlp/src/lib.rs index 406501b1e..4b01691e3 100644 --- a/util/rlp/src/lib.rs +++ b/util/rlp/src/lib.rs @@ -61,17 +61,13 @@ mod untrusted_rlp; mod stream; mod compression; mod common; -mod bytes; mod impls; -#[cfg(test)] -mod tests; - use std::borrow::Borrow; use elastic_array::ElasticArray1024; pub use error::DecoderError; -pub use traits::{Decoder, Decodable, View, Encodable, RlpDecodable, Compressible}; +pub use traits::{Decodable, Encodable, Compressible}; pub use untrusted_rlp::{UntrustedRlp, UntrustedRlpIterator, PayloadInfo, Prototype}; pub use rlpin::{Rlp, RlpIterator}; pub use stream::RlpStream; @@ -88,16 +84,21 @@ pub const EMPTY_LIST_RLP: [u8; 1] = [0xC0; 1]; /// extern crate rlp; /// /// fn main () { -/// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; -/// let animals: Vec = rlp::decode(&data); -/// assert_eq!(animals, vec!["cat".to_string(), "dog".to_string()]); +/// let data = vec![0x83, b'c', b'a', b't']; +/// let animal: String = rlp::decode(&data); +/// assert_eq!(animal, "cat".to_owned()); /// } /// ``` -pub fn decode(bytes: &[u8]) -> T where T: RlpDecodable { +pub fn decode(bytes: &[u8]) -> T where T: Decodable { let rlp = Rlp::new(bytes); rlp.as_val() } +pub fn decode_list(bytes: &[u8]) -> Vec where T: Decodable { + let rlp = Rlp::new(bytes); + rlp.as_list() +} + /// Shortcut function to encode structure into rlp. /// /// ```rust diff --git a/util/rlp/src/rlpin.rs b/util/rlp/src/rlpin.rs index 7ae715649..c7c054aa2 100644 --- a/util/rlp/src/rlpin.rs +++ b/util/rlp/src/rlpin.rs @@ -15,8 +15,7 @@ // along with Parity. If not, see . use std::fmt; -use rustc_serialize::hex::ToHex; -use {View, DecoderError, UntrustedRlp, PayloadInfo, Prototype, RlpDecodable}; +use {UntrustedRlp, PayloadInfo, Prototype, Decodable}; impl<'a> From> for Rlp<'a> { fn from(rlp: UntrustedRlp<'a>) -> Rlp<'a> { @@ -39,95 +38,215 @@ impl<'a> fmt::Display for Rlp<'a> { } } -impl<'a, 'view> View<'a, 'view> for Rlp<'a> where 'a: 'view { - type Prototype = Prototype; - type PayloadInfo = PayloadInfo; - type Data = &'a [u8]; - type Item = Rlp<'a>; - type Iter = RlpIterator<'a, 'view>; - +impl<'a, 'view> Rlp<'a> where 'a: 'view { /// Create a new instance of `Rlp` - fn new(bytes: &'a [u8]) -> Rlp<'a> { + pub fn new(bytes: &'a [u8]) -> Rlp<'a> { Rlp { rlp: UntrustedRlp::new(bytes) } } - fn as_raw(&'view self) -> &'a [u8] { + /// The raw data of the RLP as slice. + /// + /// ```rust + /// extern crate rlp; + /// use rlp::*; + /// + /// fn main () { + /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; + /// let rlp = Rlp::new(&data); + /// let dog = rlp.at(1).as_raw(); + /// assert_eq!(dog, &[0x83, b'd', b'o', b'g']); + /// } + /// ``` + pub fn as_raw(&'view self) -> &'a [u8] { self.rlp.as_raw() } - fn prototype(&self) -> Self::Prototype { + /// Get the prototype of the RLP. + pub fn prototype(&self) -> Prototype { self.rlp.prototype().unwrap() } - fn payload_info(&self) -> Self::PayloadInfo { + /// Get payload info. + pub fn payload_info(&self) -> PayloadInfo { self.rlp.payload_info().unwrap() } - fn data(&'view self) -> Self::Data { + /// Get underlieing data. + pub fn data(&'view self) -> &'a [u8] { self.rlp.data().unwrap() } - fn item_count(&self) -> usize { - self.rlp.item_count() + /// Returns number of RLP items. + /// + /// ```rust + /// extern crate rlp; + /// use rlp::*; + /// + /// fn main () { + /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; + /// let rlp = Rlp::new(&data); + /// assert_eq!(rlp.item_count(), 2); + /// let view = rlp.at(1); + /// assert_eq!(view.item_count(), 0); + /// } + /// ``` + pub fn item_count(&self) -> usize { + self.rlp.item_count().unwrap_or(0) } - fn size(&self) -> usize { + /// Returns the number of bytes in the data, or zero if it isn't data. + /// + /// ```rust + /// extern crate rlp; + /// use rlp::*; + /// + /// fn main () { + /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; + /// let rlp = Rlp::new(&data); + /// assert_eq!(rlp.size(), 0); + /// let view = rlp.at(1); + /// assert_eq!(view.size(), 3); + /// } + /// ``` + pub fn size(&self) -> usize { self.rlp.size() } - fn at(&'view self, index: usize) -> Self::Item { + /// Get view onto RLP-slice at index. + /// + /// Caches offset to given index, so access to successive + /// slices is faster. + /// + /// ```rust + /// extern crate rlp; + /// use rlp::*; + /// + /// fn main () { + /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; + /// let rlp = Rlp::new(&data); + /// let dog: String = rlp.at(1).as_val(); + /// assert_eq!(dog, "dog".to_string()); + /// } + /// ``` + pub fn at(&'view self, index: usize) -> Rlp<'a> { From::from(self.rlp.at(index).unwrap()) } - fn is_null(&self) -> bool { + /// No value + /// + /// ```rust + /// extern crate rlp; + /// use rlp::*; + /// + /// fn main () { + /// let data = vec![]; + /// let rlp = Rlp::new(&data); + /// assert!(rlp.is_null()); + /// } + /// ``` + pub fn is_null(&self) -> bool { self.rlp.is_null() } - fn is_empty(&self) -> bool { + /// Contains a zero-length string or zero-length list. + /// + /// ```rust + /// extern crate rlp; + /// use rlp::*; + /// + /// fn main () { + /// let data = vec![0xc0]; + /// let rlp = Rlp::new(&data); + /// assert!(rlp.is_empty()); + /// } + /// ``` + pub fn is_empty(&self) -> bool { self.rlp.is_empty() } - fn is_list(&self) -> bool { + /// List value + /// + /// ```rust + /// extern crate rlp; + /// use rlp::*; + /// + /// fn main () { + /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; + /// let rlp = Rlp::new(&data); + /// assert!(rlp.is_list()); + /// } + /// ``` + pub fn is_list(&self) -> bool { self.rlp.is_list() } - fn is_data(&self) -> bool { + /// String value + /// + /// ```rust + /// extern crate rlp; + /// use rlp::*; + /// + /// fn main () { + /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; + /// let rlp = Rlp::new(&data); + /// assert!(rlp.at(1).is_data()); + /// } + /// ``` + pub fn is_data(&self) -> bool { self.rlp.is_data() } - fn is_int(&self) -> bool { + /// Int value + /// + /// ```rust + /// extern crate rlp; + /// use rlp::*; + /// + /// fn main () { + /// let data = vec![0xc1, 0x10]; + /// let rlp = Rlp::new(&data); + /// assert_eq!(rlp.is_int(), false); + /// assert_eq!(rlp.at(0).is_int(), true); + /// } + /// ``` + pub fn is_int(&self) -> bool { self.rlp.is_int() } - fn iter(&'view self) -> Self::Iter { + /// Get iterator over rlp-slices + /// + /// ```rust + /// extern crate rlp; + /// use rlp::*; + /// + /// fn main () { + /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; + /// let rlp = Rlp::new(&data); + /// let strings: Vec = rlp.iter().map(| i | i.as_val()).collect(); + /// } + /// ``` + pub fn iter(&'view self) -> RlpIterator<'a, 'view> { self.into_iter() } - fn as_val(&self) -> Result where T: RlpDecodable { - self.rlp.as_val() + /// Decode data into an object + pub fn as_val(&self) -> T where T: Decodable { + self.rlp.as_val().expect("Unexpected rlp error") } - fn val_at(&self, index: usize) -> Result where T: RlpDecodable { - self.at(index).rlp.as_val() - } -} - -impl <'a, 'view> Rlp<'a> where 'a: 'view { - fn view_as_val(r: &'view R) -> T where R: View<'a, 'view>, T: RlpDecodable { - let res: Result = r.as_val(); - res.unwrap_or_else(|e| panic!("DecodeError: {}, {}", e, r.as_raw().to_hex())) + pub fn as_list(&self) -> Vec where T: Decodable { + self.iter().map(|rlp| rlp.as_val()).collect() } - /// Decode into an object - pub fn as_val(&self) -> T where T: RlpDecodable { - Self::view_as_val(self) + /// Decode data at given list index into an object + pub fn val_at(&self, index: usize) -> T where T: Decodable { + self.at(index).as_val() } - /// Decode list item at given index into an object - pub fn val_at(&self, index: usize) -> T where T: RlpDecodable { - Self::view_as_val(&self.at(index)) + pub fn list_at(&self, index: usize) -> Vec where T: Decodable { + self.at(index).as_list() } } diff --git a/util/rlp/src/traits.rs b/util/rlp/src/traits.rs index 3f79e9cab..33c5ae548 100644 --- a/util/rlp/src/traits.rs +++ b/util/rlp/src/traits.rs @@ -16,212 +16,12 @@ //! Common RLP traits use elastic_array::ElasticArray1024; -use stream::RlpStream; -use {DecoderError, UntrustedRlp}; - -/// Type is able to decode RLP. -pub trait Decoder: Sized { - /// Read a value from the RLP into a given type. - fn read_value(&self, f: &F) -> Result - where F: Fn(&[u8]) -> Result; - - /// Get underlying `UntrustedRLP` object. - fn as_rlp(&self) -> &UntrustedRlp; - /// Get underlying raw bytes slice. - fn as_raw(&self) -> &[u8]; -} +use {DecoderError, UntrustedRlp, RlpStream}; /// RLP decodable trait pub trait Decodable: Sized { /// Decode a value from RLP bytes - fn decode(decoder: &D) -> Result where D: Decoder; -} - -/// Internal helper trait. Implement `Decodable` for custom types. -pub trait RlpDecodable: Sized { - /// Decode a value from RLP bytes - fn decode(decoder: &D) -> Result where D: Decoder; -} - -/// A view into RLP encoded data -pub trait View<'a, 'view>: Sized { - /// RLP prototype type - type Prototype; - /// Payload info type - type PayloadInfo; - /// Data type - type Data; - /// Item type - type Item; - /// Iterator type - type Iter; - - /// Creates a new instance of `Rlp` reader - fn new(bytes: &'a [u8]) -> Self; - - /// The raw data of the RLP as slice. - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// let dog = rlp.at(1).as_raw(); - /// assert_eq!(dog, &[0x83, b'd', b'o', b'g']); - /// } - /// ``` - fn as_raw(&'view self) -> &'a [u8]; - - /// Get the prototype of the RLP. - fn prototype(&self) -> Self::Prototype; - - /// Get payload info. - fn payload_info(&self) -> Self::PayloadInfo; - - /// Get underlieing data. - fn data(&'view self) -> Self::Data; - - /// Returns number of RLP items. - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// assert_eq!(rlp.item_count(), 2); - /// let view = rlp.at(1); - /// assert_eq!(view.item_count(), 0); - /// } - /// ``` - fn item_count(&self) -> usize; - - /// Returns the number of bytes in the data, or zero if it isn't data. - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// assert_eq!(rlp.size(), 0); - /// let view = rlp.at(1); - /// assert_eq!(view.size(), 3); - /// } - /// ``` - fn size(&self) -> usize; - - /// Get view onto RLP-slice at index. - /// - /// Caches offset to given index, so access to successive - /// slices is faster. - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// let dog: String = rlp.at(1).as_val(); - /// assert_eq!(dog, "dog".to_string()); - /// } - fn at(&'view self, index: usize) -> Self::Item; - - /// No value - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![]; - /// let rlp = Rlp::new(&data); - /// assert!(rlp.is_null()); - /// } - /// ``` - fn is_null(&self) -> bool; - - /// Contains a zero-length string or zero-length list. - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc0]; - /// let rlp = Rlp::new(&data); - /// assert!(rlp.is_empty()); - /// } - /// ``` - fn is_empty(&self) -> bool; - - /// List value - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// assert!(rlp.is_list()); - /// } - /// ``` - fn is_list(&self) -> bool; - - /// String value - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// assert!(rlp.at(1).is_data()); - /// } - /// ``` - fn is_data(&self) -> bool; - - /// Int value - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc1, 0x10]; - /// let rlp = Rlp::new(&data); - /// assert_eq!(rlp.is_int(), false); - /// assert_eq!(rlp.at(0).is_int(), true); - /// } - /// ``` - fn is_int(&self) -> bool; - - /// Get iterator over rlp-slices - /// - /// ```rust - /// extern crate rlp; - /// use rlp::*; - /// - /// fn main () { - /// let data = vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g']; - /// let rlp = Rlp::new(&data); - /// let strings: Vec = rlp.iter().map(| i | i.as_val()).collect(); - /// } - /// ``` - fn iter(&'view self) -> Self::Iter; - - /// Decode data into an object - fn as_val(&self) -> Result where T: RlpDecodable; - - /// Decode data at given list index into an object - fn val_at(&self, index: usize) -> Result where T: RlpDecodable; + fn decode(rlp: &UntrustedRlp) -> Result; } /// Structure encodable to RLP diff --git a/util/rlp/src/untrusted_rlp.rs b/util/rlp/src/untrusted_rlp.rs index e111fde18..8f46bb690 100644 --- a/util/rlp/src/untrusted_rlp.rs +++ b/util/rlp/src/untrusted_rlp.rs @@ -17,9 +17,8 @@ use std::cell::Cell; use std::fmt; use rustc_serialize::hex::ToHex; - -use bytes::{FromBytes, FromBytesResult, FromBytesError}; -use ::{View, Decoder, Decodable, DecoderError, RlpDecodable}; +use impls::decode_usize; +use {Decodable, DecoderError}; /// rlp offset #[derive(Copy, Clone, Debug)] @@ -64,7 +63,7 @@ fn calculate_payload_info(header_bytes: &[u8], len_of_len: usize) -> Result (), } if header_bytes.len() < header_len { return Err(DecoderError::RlpIsTooShort); } - let value_len = usize::from_bytes(&header_bytes[1..header_len])?; + let value_len = decode_usize(&header_bytes[1..header_len])?; Ok(PayloadInfo::new(header_len, value_len)) } @@ -141,15 +140,8 @@ impl<'a> fmt::Display for UntrustedRlp<'a> { } } -impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view { - type Prototype = Result; - type PayloadInfo = Result; - type Data = Result<&'a [u8], DecoderError>; - type Item = Result, DecoderError>; - type Iter = UntrustedRlpIterator<'a, 'view>; - - //returns new instance of `UntrustedRlp` - fn new(bytes: &'a [u8]) -> UntrustedRlp<'a> { +impl<'a, 'view> UntrustedRlp<'a> where 'a: 'view { + pub fn new(bytes: &'a [u8]) -> UntrustedRlp<'a> { UntrustedRlp { bytes: bytes, offset_cache: Cell::new(OffsetCache::new(usize::max_value(), 0)), @@ -157,45 +149,45 @@ impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view { } } - fn as_raw(&'view self) -> &'a [u8] { + pub fn as_raw(&'view self) -> &'a [u8] { self.bytes } - fn prototype(&self) -> Self::Prototype { + pub fn prototype(&self) -> Result { // optimize? && return appropriate errors if self.is_data() { Ok(Prototype::Data(self.size())) } else if self.is_list() { - Ok(Prototype::List(self.item_count())) + self.item_count().map(Prototype::List) } else { Ok(Prototype::Null) } } - fn payload_info(&self) -> Self::PayloadInfo { + pub fn payload_info(&self) -> Result { BasicDecoder::payload_info(self.bytes) } - fn data(&'view self) -> Self::Data { + pub fn data(&'view self) -> Result<&'a [u8], DecoderError> { let pi = BasicDecoder::payload_info(self.bytes)?; Ok(&self.bytes[pi.header_len..(pi.header_len + pi.value_len)]) } - fn item_count(&self) -> usize { + pub fn item_count(&self) -> Result { match self.is_list() { true => match self.count_cache.get() { - Some(c) => c, + Some(c) => Ok(c), None => { let c = self.iter().count(); self.count_cache.set(Some(c)); - c + Ok(c) } }, - false => 0 + false => Err(DecoderError::RlpExpectedToBeList), } } - fn size(&self) -> usize { + pub fn size(&self) -> usize { match self.is_data() { // TODO: No panic on malformed data, but ideally would Err on no PayloadInfo. true => BasicDecoder::payload_info(self.bytes).map(|b| b.value_len).unwrap_or(0), @@ -203,7 +195,7 @@ impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view { } } - fn at(&'view self, index: usize) -> Self::Item { + pub fn at(&'view self, index: usize) -> Result, DecoderError> { if !self.is_list() { return Err(DecoderError::RlpExpectedToBeList); } @@ -213,7 +205,7 @@ impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view { let c = self.offset_cache.get(); let (mut bytes, to_skip) = match c.index <= index { true => (UntrustedRlp::consume(self.bytes, c.offset)?, index - c.index), - false => (self.consume_list_prefix()?, index), + false => (self.consume_list_payload()?, index), }; // skip up to x items @@ -227,23 +219,23 @@ impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view { Ok(UntrustedRlp::new(&bytes[0..found.header_len + found.value_len])) } - fn is_null(&self) -> bool { + pub fn is_null(&self) -> bool { self.bytes.len() == 0 } - fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool { !self.is_null() && (self.bytes[0] == 0xc0 || self.bytes[0] == 0x80) } - fn is_list(&self) -> bool { + pub fn is_list(&self) -> bool { !self.is_null() && self.bytes[0] >= 0xc0 } - fn is_data(&self) -> bool { + pub fn is_data(&self) -> bool { !self.is_null() && self.bytes[0] < 0xc0 } - fn is_int(&self) -> bool { + pub fn is_int(&self) -> bool { if self.is_null() { return false; } @@ -256,23 +248,32 @@ impl<'a, 'view> View<'a, 'view> for UntrustedRlp<'a> where 'a: 'view { } } - fn iter(&'view self) -> Self::Iter { + pub fn iter(&'view self) -> UntrustedRlpIterator<'a, 'view> { self.into_iter() } - fn as_val(&self) -> Result where T: RlpDecodable { - // optimize, so it doesn't use clone (although This clone is cheap) - T::decode(&BasicDecoder::new(self.clone())) + pub fn as_val(&self) -> Result where T: Decodable { + T::decode(self) } - fn val_at(&self, index: usize) -> Result where T: RlpDecodable { + pub fn as_list(&self) -> Result, DecoderError> where T: Decodable { + self.iter().map(|rlp| rlp.as_val()).collect() + } + + pub fn val_at(&self, index: usize) -> Result where T: Decodable { self.at(index)?.as_val() } -} -impl<'a> UntrustedRlp<'a> { + pub fn list_at(&self, index: usize) -> Result, DecoderError> where T: Decodable { + self.at(index)?.as_list() + } + + pub fn decoder(&self) -> BasicDecoder { + BasicDecoder::new(self.clone()) + } + /// consumes first found prefix - fn consume_list_prefix(&self) -> Result<&'a [u8], DecoderError> { + fn consume_list_payload(&self) -> Result<&'a [u8], DecoderError> { let item = BasicDecoder::payload_info(self.bytes)?; let bytes = UntrustedRlp::consume(self.bytes, item.header_len)?; Ok(bytes) @@ -327,7 +328,7 @@ impl<'a, 'view> Iterator for UntrustedRlpIterator<'a, 'view> { } } -struct BasicDecoder<'a> { +pub struct BasicDecoder<'a> { rlp: UntrustedRlp<'a> } @@ -346,10 +347,8 @@ impl<'a> BasicDecoder<'a> { _ => Err(DecoderError::RlpIsTooShort), } } -} -impl<'a> Decoder for BasicDecoder<'a> { - fn read_value(&self, f: &F) -> Result + pub fn decode_value(&self, f: F) -> Result where F: Fn(&[u8]) -> Result { let bytes = self.rlp.as_raw(); @@ -378,7 +377,7 @@ impl<'a> Decoder for BasicDecoder<'a> { if bytes.len() < begin_of_value { return Err(DecoderError::RlpInconsistentLengthAndData); } - let len = usize::from_bytes(&bytes[1..begin_of_value])?; + let len = decode_usize(&bytes[1..begin_of_value])?; let last_index_of_value = begin_of_value + len; if bytes.len() < last_index_of_value { @@ -390,108 +389,12 @@ impl<'a> Decoder for BasicDecoder<'a> { _ => Err(DecoderError::RlpExpectedToBeData) } } - - fn as_raw(&self) -> &[u8] { - self.rlp.as_raw() - } - - fn as_rlp(&self) -> &UntrustedRlp { - &self.rlp - } -} - -impl Decodable for T where T: FromBytes { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.read_value(&|bytes: &[u8]| Ok(T::from_bytes(bytes)?)) - } -} - -impl Decodable for Vec where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect() - } -} - -impl Decodable for Option where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.as_rlp().iter().map(|d| T::decode(&BasicDecoder::new(d))).collect::, DecoderError>>().map(|mut a| a.pop()) - } -} - -impl Decodable for Vec { - fn decode(decoder: &D) -> Result where D: Decoder { - decoder.read_value(&|bytes: &[u8]| Ok(bytes.to_vec())) - } -} - -macro_rules! impl_array_decodable { - ($index_type:ty, $len:expr ) => ( - impl Decodable for [T; $len] where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { - let decoders = decoder.as_rlp(); - - let mut result: [T; $len] = unsafe { ::std::mem::uninitialized() }; - if decoders.item_count() != $len { - return Err(DecoderError::RlpIncorrectListLen); - } - - for i in 0..decoders.item_count() { - result[i] = T::decode(&BasicDecoder::new(decoders.at(i)?))?; - } - - Ok(result) - } - } - ) -} - -macro_rules! impl_array_decodable_recursive { - ($index_type:ty, ) => (); - ($index_type:ty, $len:expr, $($more:expr,)*) => ( - impl_array_decodable!($index_type, $len); - impl_array_decodable_recursive!($index_type, $($more,)*); - ); -} - -impl_array_decodable_recursive!( - u8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 40, 48, 56, 64, 72, 96, 128, 160, 192, 224, -); - -impl RlpDecodable for T where T: Decodable { - fn decode(decoder: &D) -> Result where D: Decoder { - Decodable::decode(decoder) - } -} - -struct DecodableU8 (u8); - -impl FromBytes for DecodableU8 { - fn from_bytes(bytes: &[u8]) -> FromBytesResult { - match bytes.len() { - 0 => Ok(DecodableU8(0u8)), - 1 => { - if bytes[0] == 0 { - return Err(FromBytesError::ZeroPrefixedInt) - } - Ok(DecodableU8(bytes[0])) - } - _ => Err(FromBytesError::DataIsTooLong) - } - } -} - -impl RlpDecodable for u8 { - fn decode(decoder: &D) -> Result where D: Decoder { - let u: DecodableU8 = Decodable::decode(decoder)?; - Ok(u.0) - } } #[cfg(test)] mod tests { - use ::{UntrustedRlp, View}; + use UntrustedRlp; + #[test] fn test_rlp_display() { use rustc_serialize::hex::FromHex; diff --git a/util/rlp/src/tests.rs b/util/rlp/tests/tests.rs similarity index 90% rename from util/rlp/src/tests.rs rename to util/rlp/tests/tests.rs index bd3abfe63..1c996caac 100644 --- a/util/rlp/src/tests.rs +++ b/util/rlp/tests/tests.rs @@ -14,9 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +extern crate ethcore_bigint as bigint; +extern crate rlp; + use std::{fmt, cmp}; use bigint::prelude::U256; -use {Encodable, RlpDecodable, UntrustedRlp, RlpStream, View, DecoderError}; +use rlp::{Encodable, Decodable, UntrustedRlp, RlpStream, DecoderError}; #[test] fn rlp_at() { @@ -24,7 +27,7 @@ fn rlp_at() { { let rlp = UntrustedRlp::new(&data); assert!(rlp.is_list()); - let animals: Vec = rlp.as_val().unwrap(); + let animals: Vec = rlp.as_list().unwrap(); assert_eq!(animals, vec!["cat".to_owned(), "dog".to_owned()]); let cat = rlp.at(0).unwrap(); @@ -89,7 +92,7 @@ fn run_encode_tests(tests: Vec>) where T: Encodable { for t in &tests { - let res = super::encode(&t.0); + let res = rlp::encode(&t.0); assert_eq!(&res[..], &t.1[..]); } } @@ -100,7 +103,7 @@ fn run_encode_tests_list(tests: Vec>) where T: Encodable { for t in &tests { - let res = super::encode_list(&t.0); + let res = rlp::encode_list(&t.0); assert_eq!(&res[..], &t.1[..]); } } @@ -210,11 +213,20 @@ fn encode_vector_str() { run_encode_tests_list(tests); } -struct DTestPair(T, Vec) where T: RlpDecodable + fmt::Debug + cmp::Eq; +struct DTestPair(T, Vec) where T: Decodable + fmt::Debug + cmp::Eq; -fn run_decode_tests(tests: Vec>) where T: RlpDecodable + fmt::Debug + cmp::Eq { +struct VDTestPair(Vec, Vec) where T: Decodable + fmt::Debug + cmp::Eq; + +fn run_decode_tests(tests: Vec>) where T: Decodable + fmt::Debug + cmp::Eq { for t in &tests { - let res: T = super::decode(&t.1); + let res: T = rlp::decode(&t.1); + assert_eq!(res, t.0); + } +} + +fn run_decode_tests_list(tests: Vec>) where T: Decodable + fmt::Debug + cmp::Eq { + for t in &tests { + let res: Vec = rlp::decode_list(&t.1); assert_eq!(res, t.0); } } @@ -318,35 +330,19 @@ fn decode_untrusted_address() { #[test] fn decode_untrusted_vector_u64() { let tests = vec![ - DTestPair(vec![], vec![0xc0]), - DTestPair(vec![15u64], vec![0xc1, 0x0f]), - DTestPair(vec![1, 2, 3, 7, 0xff], vec![0xc6, 1, 2, 3, 7, 0x81, 0xff]), - DTestPair(vec![0xffffffff, 1, 2, 3, 7, 0xff], vec![0xcb, 0x84, 0xff, 0xff, 0xff, 0xff, 1, 2, 3, 7, 0x81, 0xff]), + VDTestPair(vec![], vec![0xc0]), + VDTestPair(vec![15u64], vec![0xc1, 0x0f]), + VDTestPair(vec![1, 2, 3, 7, 0xff], vec![0xc6, 1, 2, 3, 7, 0x81, 0xff]), + VDTestPair(vec![0xffffffff, 1, 2, 3, 7, 0xff], vec![0xcb, 0x84, 0xff, 0xff, 0xff, 0xff, 1, 2, 3, 7, 0x81, 0xff]), ]; - run_decode_tests(tests); + run_decode_tests_list(tests); } #[test] fn decode_untrusted_vector_str() { - let tests = vec![DTestPair(vec!["cat".to_owned(), "dog".to_owned()], + let tests = vec![VDTestPair(vec!["cat".to_owned(), "dog".to_owned()], vec![0xc8, 0x83, b'c', b'a', b't', 0x83, b'd', b'o', b'g'])]; - run_decode_tests(tests); -} - -#[test] -fn decode_untrusted_vector_of_vectors_str() { - let tests = vec![DTestPair(vec![vec!["cat".to_owned()]], - vec![0xc5, 0xc4, 0x83, b'c', b'a', b't'])]; - run_decode_tests(tests); -} - -#[test] -fn test_decoding_array() { - let v = vec![5u16, 2u16]; - let res = super::encode_list(&v); - let arr: [u16; 2] = super::decode(&res); - assert_eq!(arr[0], 5); - assert_eq!(arr[1], 2); + run_decode_tests_list(tests); } #[test] diff --git a/util/src/journaldb/earlymergedb.rs b/util/src/journaldb/earlymergedb.rs index bd05d5fc1..66e5a1cfd 100644 --- a/util/src/journaldb/earlymergedb.rs +++ b/util/src/journaldb/earlymergedb.rs @@ -290,7 +290,7 @@ impl EarlyMergeDB { &r.drain() }).expect("Low-level database error.") { let rlp = Rlp::new(&rlp_data); - let inserts: Vec = rlp.val_at(1); + let inserts: Vec = rlp.list_at(1); Self::replay_keys(&inserts, db, col, &mut refs); index += 1; }; @@ -466,11 +466,11 @@ impl JournalDB for EarlyMergeDB { &last })? { let rlp = Rlp::new(&rlp_data); - let inserts: Vec = rlp.val_at(1); + let inserts: Vec = rlp.list_at(1); if canon_id == &rlp.val_at::(0) { // Collect keys to be removed. Canon block - remove the (enacted) deletes. - let deletes: Vec = rlp.val_at(2); + let deletes: Vec = rlp.list_at(2); trace!(target: "jdb.ops", " Expunging: {:?}", deletes); Self::remove_keys(&deletes, &mut refs, batch, self.column, RemoveFrom::Archive, trace); diff --git a/util/src/journaldb/overlayrecentdb.rs b/util/src/journaldb/overlayrecentdb.rs index e8095e71e..7eeabf6df 100644 --- a/util/src/journaldb/overlayrecentdb.rs +++ b/util/src/journaldb/overlayrecentdb.rs @@ -155,7 +155,7 @@ impl OverlayRecentDB { let rlp = Rlp::new(&rlp_data); let id: H256 = rlp.val_at(0); let insertions = rlp.at(1); - let deletions: Vec = rlp.val_at(2); + let deletions: Vec = rlp.list_at(2); let mut inserted_keys = Vec::new(); for r in insertions.iter() { let k: H256 = r.val_at(0); diff --git a/util/src/journaldb/refcounteddb.rs b/util/src/journaldb/refcounteddb.rs index b18954eb3..13c2189c8 100644 --- a/util/src/journaldb/refcounteddb.rs +++ b/util/src/journaldb/refcounteddb.rs @@ -171,7 +171,7 @@ impl JournalDB for RefCountedDB { } { let rlp = Rlp::new(&rlp_data); let our_id: H256 = rlp.val_at(0); - let to_remove: Vec = rlp.val_at(if *canon_id == our_id {2} else {1}); + let to_remove: Vec = rlp.list_at(if *canon_id == our_id {2} else {1}); trace!(target: "rcdb", "delete journal for time #{}.{}=>{}, (canon was {}): deleting {:?}", end_era, index, our_id, canon_id, to_remove); for i in &to_remove { self.forward.remove(i); diff --git a/util/src/kvdb.rs b/util/src/kvdb.rs index 043b3d983..7de8dc910 100644 --- a/util/src/kvdb.rs +++ b/util/src/kvdb.rs @@ -23,7 +23,7 @@ use std::path::PathBuf; use common::*; use elastic_array::*; use hashdb::DBValue; -use rlp::{UntrustedRlp, RlpType, View, Compressible}; +use rlp::{UntrustedRlp, RlpType, Compressible}; use rocksdb::{DB, Writable, WriteBatch, WriteOptions, IteratorMode, DBIterator, Options, DBCompactionStyle, BlockBasedOptions, Direction, Cache, Column, ReadOptions}; #[cfg(target_os = "linux")] diff --git a/util/src/trie/lookup.rs b/util/src/trie/lookup.rs index 30e021527..d24a82e16 100644 --- a/util/src/trie/lookup.rs +++ b/util/src/trie/lookup.rs @@ -18,7 +18,7 @@ use hashdb::HashDB; use nibbleslice::NibbleSlice; -use rlp::{Rlp, View}; +use rlp::Rlp; use ::{H256}; use super::{TrieError, Query}; diff --git a/util/src/trie/triedbmut.rs b/util/src/trie/triedbmut.rs index c5f2a739b..2c2e556c5 100644 --- a/util/src/trie/triedbmut.rs +++ b/util/src/trie/triedbmut.rs @@ -24,7 +24,7 @@ use super::node::NodeKey; use ::{HashDB, H256}; use ::bytes::ToPretty; use ::nibbleslice::NibbleSlice; -use ::rlp::{Rlp, RlpStream, View}; +use ::rlp::{Rlp, RlpStream}; use ::sha3::SHA3_NULL_RLP; use hashdb::DBValue; From 1879dbca8aa1c71a2335abaf685954a92d13306f Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 22 Mar 2017 16:39:40 +0100 Subject: [PATCH 11/41] Consistent store naming in the Signer components (#4996) --- .../components/RequestPending/requestPending.js | 8 ++++---- .../Signer/components/SignRequest/signRequest.js | 10 +++++----- .../components/SignRequest/signRequest.spec.js | 2 +- .../TransactionPending/transactionPending.js | 14 +++++++------- .../views/Signer/containers/Embedded/embedded.js | 2 +- .../Signer/containers/RequestsPage/requestsPage.js | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/js/src/views/Signer/components/RequestPending/requestPending.js b/js/src/views/Signer/components/RequestPending/requestPending.js index 671df6cbb..3d8ea0c15 100644 --- a/js/src/views/Signer/components/RequestPending/requestPending.js +++ b/js/src/views/Signer/components/RequestPending/requestPending.js @@ -36,7 +36,7 @@ export default class RequestPending extends Component { PropTypes.shape({ sign: PropTypes.object.isRequired }), PropTypes.shape({ signTransaction: PropTypes.object.isRequired }) ]).isRequired, - signerstore: PropTypes.object.isRequired + signerStore: PropTypes.object.isRequired }; static defaultProps = { @@ -45,7 +45,7 @@ export default class RequestPending extends Component { }; render () { - const { className, date, focus, gasLimit, id, isSending, netVersion, onReject, payload, signerstore, origin } = this.props; + const { className, date, focus, gasLimit, id, isSending, netVersion, onReject, payload, signerStore, origin } = this.props; if (payload.sign) { const { sign } = payload; @@ -63,7 +63,7 @@ export default class RequestPending extends Component { onConfirm={ this.onConfirm } onReject={ onReject } origin={ origin } - signerstore={ signerstore } + signerStore={ signerStore } /> ); } @@ -83,7 +83,7 @@ export default class RequestPending extends Component { onConfirm={ this.onConfirm } onReject={ onReject } origin={ origin } - signerstore={ signerstore } + signerStore={ signerStore } transaction={ transaction } /> ); diff --git a/js/src/views/Signer/components/SignRequest/signRequest.js b/js/src/views/Signer/components/SignRequest/signRequest.js index 373262d41..8c9984b4b 100644 --- a/js/src/views/Signer/components/SignRequest/signRequest.js +++ b/js/src/views/Signer/components/SignRequest/signRequest.js @@ -47,7 +47,7 @@ export default class SignRequest extends Component { id: PropTypes.object.isRequired, isFinished: PropTypes.bool.isRequired, netVersion: PropTypes.string.isRequired, - signerstore: PropTypes.object.isRequired, + signerStore: PropTypes.object.isRequired, className: PropTypes.string, focus: PropTypes.bool, @@ -67,9 +67,9 @@ export default class SignRequest extends Component { }; componentWillMount () { - const { address, signerstore } = this.props; + const { address, signerStore } = this.props; - signerstore.fetchBalance(address); + signerStore.fetchBalance(address); } render () { @@ -106,8 +106,8 @@ export default class SignRequest extends Component { renderDetails () { const { api } = this.context; - const { address, data, netVersion, origin, signerstore } = this.props; - const { balances, externalLink } = signerstore; + const { address, data, netVersion, origin, signerStore } = this.props; + const { balances, externalLink } = signerStore; const balance = balances[address]; diff --git a/js/src/views/Signer/components/SignRequest/signRequest.spec.js b/js/src/views/Signer/components/SignRequest/signRequest.spec.js index cf7d7e9f6..c5c87d509 100644 --- a/js/src/views/Signer/components/SignRequest/signRequest.spec.js +++ b/js/src/views/Signer/components/SignRequest/signRequest.spec.js @@ -28,7 +28,7 @@ const store = { describe('views/Signer/components/SignRequest', () => { it('renders', () => { expect(shallow( - , + , )).to.be.ok; }); }); diff --git a/js/src/views/Signer/components/TransactionPending/transactionPending.js b/js/src/views/Signer/components/TransactionPending/transactionPending.js index 9b0b91ef6..90ea75b22 100644 --- a/js/src/views/Signer/components/TransactionPending/transactionPending.js +++ b/js/src/views/Signer/components/TransactionPending/transactionPending.js @@ -48,7 +48,7 @@ class TransactionPending extends Component { onConfirm: PropTypes.func.isRequired, onReject: PropTypes.func.isRequired, origin: PropTypes.any, - signerstore: PropTypes.object.isRequired, + signerStore: PropTypes.object.isRequired, transaction: PropTypes.shape({ condition: PropTypes.object, data: PropTypes.string, @@ -75,10 +75,10 @@ class TransactionPending extends Component { gasPrice: this.props.transaction.gasPrice.toFixed() }); - hwstore = HardwareStore.get(this.context.api); + hardwareStore = HardwareStore.get(this.context.api); componentWillMount () { - const { signerstore, transaction } = this.props; + const { signerStore, transaction } = this.props; const { from, gas, gasPrice, to, value } = transaction; const fee = tUtil.getFee(gas, gasPrice); // BigNumber object @@ -88,7 +88,7 @@ class TransactionPending extends Component { this.setState({ gasPriceEthmDisplay, totalValue, gasToDisplay }); this.gasStore.setEthValue(value); - signerstore.fetchBalances([from, to]); + signerStore.fetchBalances([from, to]); } render () { @@ -98,13 +98,13 @@ class TransactionPending extends Component { } renderTransaction () { - const { accounts, className, focus, id, isSending, netVersion, origin, signerstore, transaction } = this.props; + const { accounts, className, focus, id, isSending, netVersion, origin, signerStore, transaction } = this.props; const { totalValue } = this.state; - const { balances, externalLink } = signerstore; + const { balances, externalLink } = signerStore; const { from, value } = transaction; const fromBalance = balances[from]; const account = accounts[from] || {}; - const disabled = account.hardware && !this.hwstore.isConnected(from); + const disabled = account.hardware && !this.hardwareStore.isConnected(from); return (
diff --git a/js/src/views/Signer/containers/Embedded/embedded.js b/js/src/views/Signer/containers/Embedded/embedded.js index adac35621..b79b4f203 100644 --- a/js/src/views/Signer/containers/Embedded/embedded.js +++ b/js/src/views/Signer/containers/Embedded/embedded.js @@ -101,7 +101,7 @@ class Embedded extends Component { onReject={ actions.startRejectRequest } origin={ origin } payload={ payload } - signerstore={ this.store } + signerStore={ this.store } /> ); } diff --git a/js/src/views/Signer/containers/RequestsPage/requestsPage.js b/js/src/views/Signer/containers/RequestsPage/requestsPage.js index c0cddb8d0..8af2e5fd5 100644 --- a/js/src/views/Signer/containers/RequestsPage/requestsPage.js +++ b/js/src/views/Signer/containers/RequestsPage/requestsPage.js @@ -141,7 +141,7 @@ class RequestsPage extends Component { onReject={ actions.startRejectRequest } origin={ origin } payload={ payload } - signerstore={ this.store } + signerStore={ this.store } /> ); } From 5df3f5d136383e8647ffe846b99ea6059e0d7cb1 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 22 Mar 2017 16:39:49 +0100 Subject: [PATCH 12/41] Show busy indicator, focus first field (#4997) --- .../modals/PasswordManager/passwordManager.js | 261 ++++++++++-------- 1 file changed, 144 insertions(+), 117 deletions(-) diff --git a/js/src/modals/PasswordManager/passwordManager.js b/js/src/modals/PasswordManager/passwordManager.js index 91ac9ebf2..6436bc4cc 100644 --- a/js/src/modals/PasswordManager/passwordManager.js +++ b/js/src/modals/PasswordManager/passwordManager.js @@ -60,8 +60,11 @@ class PasswordManager extends Component { store = new Store(this.context.api, this.props.account); render () { + const { busy } = this.store; + return ( -
-
- - } - label={ - - } - onChange={ this.onEditTestPassword } - onSubmit={ this.testPassword } - submitOnBlur={ false } - type='password' - /> -
-
+ { this.renderTabTest() } -
-
- - } - label={ - - } - onChange={ this.onEditCurrentPassword } - type='password' - /> - - } - label={ - - } - onChange={ this.onEditNewPasswordHint } - value={ passwordHint } - /> -
-
- - } - label={ - - } - onChange={ this.onEditNewPassword } - onSubmit={ this.changePassword } - submitOnBlur={ false } - type='password' - /> -
-
- - } - hint={ - - } - label={ - - } - onChange={ this.onEditNewPasswordRepeat } - onSubmit={ this.changePassword } - submitOnBlur={ false } - type='password' - /> -
-
- - -
-
+ { this.renderTabChange() }
); } + renderTabTest () { + const { actionTab, busy } = this.store; + + if (actionTab !== TEST_ACTION) { + return null; + } + + return ( +
+
+ + } + label={ + + } + onChange={ this.onEditTestPassword } + onSubmit={ this.testPassword } + submitOnBlur={ false } + type='password' + /> +
+
+ ); + } + + renderTabChange () { + const { actionTab, busy, isRepeatValid, newPassword, passwordHint } = this.store; + + if (actionTab !== CHANGE_ACTION) { + return null; + } + + return ( +
+
+ + } + label={ + + } + onChange={ this.onEditCurrentPassword } + type='password' + /> + + } + label={ + + } + onChange={ this.onEditNewPasswordHint } + value={ passwordHint } + /> +
+
+ + } + label={ + + } + onChange={ this.onEditNewPassword } + onSubmit={ this.changePassword } + submitOnBlur={ false } + type='password' + /> +
+
+ + } + hint={ + + } + label={ + + } + onChange={ this.onEditNewPasswordRepeat } + onSubmit={ this.changePassword } + submitOnBlur={ false } + type='password' + /> +
+
+ + +
+
+ ); + } + renderDialogActions () { const { actionTab, busy, isRepeatValid } = this.store; From 6b4cb351499fa274893aa19d0e929c830148723a Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 22 Mar 2017 16:39:57 +0100 Subject: [PATCH 13/41] Fix FireFox overflows (#5000) * Max width for container * Set min-width --- js/src/ui/Container/container.css | 1 + js/src/ui/SectionList/sectionList.css | 2 ++ 2 files changed, 3 insertions(+) diff --git a/js/src/ui/Container/container.css b/js/src/ui/Container/container.css index d496931b9..3d66d2655 100644 --- a/js/src/ui/Container/container.css +++ b/js/src/ui/Container/container.css @@ -24,6 +24,7 @@ $transitionAll: all 0.75s cubic-bezier(0.23, 1, 0.32, 1); flex: 1; height: 100%; padding: 0em; + max-width: 100%; position: relative; /*transform: translateZ(0); transition: $transitionAll;*/ diff --git a/js/src/ui/SectionList/sectionList.css b/js/src/ui/SectionList/sectionList.css index 4613371ad..e81d8ce07 100644 --- a/js/src/ui/SectionList/sectionList.css +++ b/js/src/ui/SectionList/sectionList.css @@ -18,6 +18,7 @@ $transition: all 0.25s; $widthNormal: 33.33%; $widthExpanded: 42%; +$widthContracted: 29%; .section { position: relative; @@ -45,6 +46,7 @@ $widthExpanded: 42%; display: flex; flex: 0 1 $widthNormal; max-width: $widthNormal; + min-width: $widthContracted; opacity: 0.85; padding: 0.25em; From 5255b72f673a1afc9c3946b08f7e2936e47eac34 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 22 Mar 2017 16:10:12 +0000 Subject: [PATCH 14/41] [ci skip] js-precompiled 20170322-160703 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 458e3cae5..3014f3a54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1749,7 +1749,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#9651995aa0fa718b9b9b58f1c7281900643bdf8f" +source = "git+https://github.com/ethcore/js-precompiled.git#f44837a345d170642d44f953393aeea7a7cb9eab" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 54cc076fe..716028271 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.26", + "version": "1.7.27", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 41f66f33d54bfc3ecc4e34bd4defc60aa6c7c391 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 22 Mar 2017 16:22:41 +0000 Subject: [PATCH 15/41] [ci skip] js-precompiled 20170322-161945 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3014f3a54..73f2d6c73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1749,7 +1749,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#f44837a345d170642d44f953393aeea7a7cb9eab" +source = "git+https://github.com/ethcore/js-precompiled.git#476a016aa2458b77f49fbfd500c4d9b03456165f" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 716028271..dcdcc9097 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.27", + "version": "1.7.28", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From e5c2b2535128342d628be65ea18cd9db9cd3ad12 Mon Sep 17 00:00:00 2001 From: Craig O'Connor Date: Thu, 23 Mar 2017 05:14:32 -0400 Subject: [PATCH 16/41] auto lint (#5003) * auto lint * Usage consistency update --- js/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/package.json b/js/package.json index dcdcc9097..f92cc7d6b 100644 --- a/js/package.json +++ b/js/package.json @@ -48,8 +48,10 @@ "lint": "npm run lint:css && npm run lint:js", "lint:cached": "npm run lint:css && npm run lint:js:cached", "lint:css": "stylelint ./src/**/*.css", + "lint:fix": "npm run lint:js:fix", "lint:js": "eslint --ignore-path .gitignore ./src/", "lint:js:cached": "eslint --cache --ignore-path .gitignore ./src/", + "lint:js:fix": "eslint --fix --ignore-path .gitignore ./src/", "test": "NODE_ENV=test mocha --compilers ejs:ejsify 'src/**/*.spec.js'", "test:coverage": "NODE_ENV=test istanbul cover _mocha -- --compilers ejs:ejsify 'src/**/*.spec.js'", "test:e2e": "NODE_ENV=test mocha 'src/**/*.e2e.js'", From b931a225ba571e29a80b8a6d4b772ed338274fe2 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 23 Mar 2017 09:36:34 +0000 Subject: [PATCH 17/41] [ci skip] js-precompiled 20170323-093322 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 73f2d6c73..99ebbe7e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1749,7 +1749,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#476a016aa2458b77f49fbfd500c4d9b03456165f" +source = "git+https://github.com/ethcore/js-precompiled.git#a44b1cb29b80e4d3372ee47494499a61db7a8116" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index f92cc7d6b..6a1e63fb1 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.28", + "version": "1.7.29", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 64cec5ff7df6726d8cd5b83f1678975fe009f7b8 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 23 Mar 2017 13:17:05 +0100 Subject: [PATCH 18/41] Implement PIP messages, request builder, and handlers (#4945) * return errors on database corruption * fix tests, json tests * fix remainder of build * buffer flow -> request credits * proving state backend * generate transaction proofs from provider * network messages for transaction proof * transaction proof test * test for transaction proof message * fix call bug * request transaction proofs from on_demand * most of proved_execution rpc * proved execution future * initial request definitions * RLP encoding and decoding for requests * proofs of non-existance in ProvingBlockChainClient * new requests in provider. * encode and decode responses * complete initial request changes * handle request packet in LightProtocol * handle response packets * implement requesting from * re-do cost table * get tests compiling * fix cost table RLP encoding * roundtrip tests for request types * request builder tests * move request_builder -> request::builder * get network tests working * return only complete headers responses * request builder improvements * New version of jsonrpc. * split request filling into fill,complete * Better invalid encoding messages * Fixing deprecated methods of tokio_core * use PIP messages in on_demand, old API * migrate oneshot::complete to send in on_demand * get on_demand tests to compile * port ethsync to PIP messages * adjust to minor on_demand API changes in RPC * Using dedicated branch for jsonrpc * Bump --- ethcore/light/Cargo.toml | 2 +- ethcore/light/src/client/header_chain.rs | 1 - ethcore/light/src/client/mod.rs | 49 +- ethcore/light/src/lib.rs | 4 +- ethcore/light/src/net/context.rs | 35 +- ethcore/light/src/net/error.rs | 4 + ethcore/light/src/net/mod.rs | 900 ++--------- ethcore/light/src/net/request_credits.rs | 205 ++- ethcore/light/src/net/request_set.rs | 59 +- ethcore/light/src/net/tests/mod.rs | 309 ++-- ethcore/light/src/on_demand/mod.rs | 627 +++---- ethcore/light/src/on_demand/request.rs | 67 +- ethcore/light/src/provider.rs | 242 ++- ethcore/light/src/types/les_request.rs | 228 --- ethcore/light/src/types/mod.rs.in | 2 +- ethcore/light/src/types/request/builder.rs | 190 +++ ethcore/light/src/types/request/mod.rs | 1710 ++++++++++++++++++++ ethcore/src/client/client.rs | 18 +- ethcore/src/client/test_client.rs | 13 +- ethcore/src/client/traits.rs | 12 +- ethcore/src/state/account.rs | 13 +- ethcore/src/state/mod.rs | 45 +- rpc/src/v1/helpers/dispatch.rs | 5 +- rpc/src/v1/impls/light/eth.rs | 23 +- sync/src/light_sync/mod.rs | 65 +- sync/src/light_sync/response.rs | 34 +- sync/src/light_sync/sync_round.rs | 27 +- sync/src/light_sync/tests/test_net.rs | 6 +- 28 files changed, 2800 insertions(+), 2095 deletions(-) delete mode 100644 ethcore/light/src/types/les_request.rs create mode 100644 ethcore/light/src/types/request/builder.rs create mode 100644 ethcore/light/src/types/request/mod.rs diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index d8844dc3f..6f95d8a0e 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -1,5 +1,5 @@ [package] -description = "Parity LES primitives" +description = "Parity Light Client Implementation" homepage = "http://parity.io" license = "GPL-3.0" name = "ethcore-light" diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index 575938cd5..9dcd25888 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -24,7 +24,6 @@ //! - It stores only headers (and a pruned subset of them) //! - To allow for flexibility in the database layout once that's incorporated. // TODO: use DB instead of memory. DB Layout: just the contents of `candidates`/`headers` -// use std::collections::{BTreeMap, HashMap}; diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index 2872e0eec..c791caed1 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -31,7 +31,7 @@ use ethcore::service::ClientIoMessage; use ethcore::encoded; use io::IoChannel; -use util::{Bytes, DBValue, H256, Mutex, RwLock}; +use util::{H256, Mutex, RwLock}; use self::header_chain::{AncestryIter, HeaderChain}; @@ -315,50 +315,3 @@ impl LightChainClient for Client { Client::cht_root(self, i) } } - -// dummy implementation, should be removed when a `TestClient` is added. -impl ::provider::Provider for Client { - fn chain_info(&self) -> BlockChainInfo { - Client::chain_info(self) - } - - fn reorg_depth(&self, _a: &H256, _b: &H256) -> Option { - None - } - - fn earliest_state(&self) -> Option { - None - } - - fn block_header(&self, id: BlockId) -> Option { - Client::block_header(self, id) - } - - fn block_body(&self, _id: BlockId) -> Option { - None - } - - fn block_receipts(&self, _hash: &H256) -> Option { - None - } - - fn state_proof(&self, _req: ::request::StateProof) -> Vec { - Vec::new() - } - - fn contract_code(&self, _req: ::request::ContractCode) -> Bytes { - Vec::new() - } - - fn header_proof(&self, _req: ::request::HeaderProof) -> Option<(encoded::Header, Vec)> { - None - } - - fn transaction_proof(&self, _req: ::request::TransactionProof) -> Option> { - None - } - - fn ready_transactions(&self) -> Vec<::ethcore::transaction::PendingTransaction> { - Vec::new() - } -} diff --git a/ethcore/light/src/lib.rs b/ethcore/light/src/lib.rs index b6e06a02b..ada58d8de 100644 --- a/ethcore/light/src/lib.rs +++ b/ethcore/light/src/lib.rs @@ -26,7 +26,7 @@ //! use-cases like sending transactions from a personal account. //! //! The light client performs a header-only sync, doing verification and pruning -//! historical blocks. Upon pruning, batches of 2048 blocks have a number => hash +//! historical blocks. Upon pruning, batches of 2048 blocks have a number => (hash, TD) //! mapping sealed into "canonical hash tries" which can later be used to verify //! historical block queries from peers. @@ -57,7 +57,7 @@ mod types; pub use self::provider::Provider; pub use self::transaction_queue::TransactionQueue; -pub use types::les_request as request; +pub use types::request as request; #[macro_use] extern crate log; diff --git a/ethcore/light/src/net/context.rs b/ethcore/light/src/net/context.rs index bd0c8a6bb..9eafead57 100644 --- a/ethcore/light/src/net/context.rs +++ b/ethcore/light/src/net/context.rs @@ -12,7 +12,7 @@ // 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 . +// along with Parity. If not, see . //! I/O and event context generalizations. @@ -20,7 +20,7 @@ use network::{NetworkContext, PeerId, NodeId}; use super::{Announcement, LightProtocol, ReqId}; use super::error::Error; -use request::{self, Request}; +use request::Requests; /// An I/O context which allows sending and receiving packets as well as /// disconnecting peers. This is used as a generalization of the portions @@ -50,13 +50,13 @@ pub trait IoContext { impl<'a> IoContext for NetworkContext<'a> { fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { if let Err(e) = self.send(peer, packet_id, packet_body) { - debug!(target: "les", "Error sending packet to peer {}: {}", peer, e); + debug!(target: "pip", "Error sending packet to peer {}: {}", peer, e); } } fn respond(&self, packet_id: u8, packet_body: Vec) { if let Err(e) = self.respond(packet_id, packet_body) { - debug!(target: "les", "Error responding to peer message: {}", e); + debug!(target: "pip", "Error responding to peer message: {}", e); } } @@ -83,16 +83,17 @@ pub trait BasicContext { fn persistent_peer_id(&self, peer: PeerId) -> Option; /// Make a request from a peer. - fn request_from(&self, peer: PeerId, request: Request) -> Result; + /// + /// Fails on: nonexistent peer, network error, peer not server, + /// insufficient credits. Does not check capabilities before sending. + /// On success, returns a request id which can later be coordinated + /// with an event. + fn request_from(&self, peer: PeerId, request: Requests) -> Result; /// Make an announcement of new capabilities to the rest of the peers. // TODO: maybe just put this on a timer in LightProtocol? fn make_announcement(&self, announcement: Announcement); - /// Find the maximum number of requests of a specific type which can be made from - /// supplied peer. - fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize; - /// Disconnect a peer. fn disconnect_peer(&self, peer: PeerId); @@ -123,18 +124,14 @@ impl<'a> BasicContext for TickCtx<'a> { self.io.persistent_peer_id(id) } - fn request_from(&self, peer: PeerId, request: Request) -> Result { - self.proto.request_from(self.io, &peer, request) + fn request_from(&self, peer: PeerId, requests: Requests) -> Result { + self.proto.request_from(self.io, &peer, requests) } fn make_announcement(&self, announcement: Announcement) { self.proto.make_announcement(self.io, announcement); } - fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize { - self.proto.max_requests(peer, kind) - } - fn disconnect_peer(&self, peer: PeerId) { self.io.disconnect_peer(peer); } @@ -160,18 +157,14 @@ impl<'a> BasicContext for Ctx<'a> { self.io.persistent_peer_id(id) } - fn request_from(&self, peer: PeerId, request: Request) -> Result { - self.proto.request_from(self.io, &peer, request) + fn request_from(&self, peer: PeerId, requests: Requests) -> Result { + self.proto.request_from(self.io, &peer, requests) } fn make_announcement(&self, announcement: Announcement) { self.proto.make_announcement(self.io, announcement); } - fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize { - self.proto.max_requests(peer, kind) - } - fn disconnect_peer(&self, peer: PeerId) { self.io.disconnect_peer(peer); } diff --git a/ethcore/light/src/net/error.rs b/ethcore/light/src/net/error.rs index dda78e0b6..1c0374c7e 100644 --- a/ethcore/light/src/net/error.rs +++ b/ethcore/light/src/net/error.rs @@ -56,6 +56,8 @@ pub enum Error { UnknownPeer, /// Unsolicited response. UnsolicitedResponse, + /// Bad back-reference in request. + BadBackReference, /// Not a server. NotServer, /// Unsupported protocol version. @@ -78,6 +80,7 @@ impl Error { Error::WrongNetwork => Punishment::Disable, Error::UnknownPeer => Punishment::Disconnect, Error::UnsolicitedResponse => Punishment::Disable, + Error::BadBackReference => Punishment::Disable, Error::NotServer => Punishment::Disable, Error::UnsupportedProtocolVersion(_) => Punishment::Disable, Error::BadProtocolVersion => Punishment::Disable, @@ -109,6 +112,7 @@ impl fmt::Display for Error { Error::WrongNetwork => write!(f, "Wrong network"), Error::UnknownPeer => write!(f, "Unknown peer"), Error::UnsolicitedResponse => write!(f, "Peer provided unsolicited data"), + Error::BadBackReference => write!(f, "Bad back-reference in request."), Error::NotServer => write!(f, "Peer not a server."), Error::UnsupportedProtocolVersion(pv) => write!(f, "Unsupported protocol version: {}", pv), Error::BadProtocolVersion => write!(f, "Bad protocol version in handshake"), diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 3830dde4b..667e07cb4 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -14,19 +14,17 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -//! LES Protocol Version 1 implementation. +//! PIP Protocol Version 1 implementation. //! //! This uses a "Provider" to answer requests. -//! See https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) -use ethcore::transaction::{Action, UnverifiedTransaction}; -use ethcore::receipt::Receipt; +use ethcore::transaction::UnverifiedTransaction; use io::TimerToken; use network::{NetworkProtocolHandler, NetworkContext, PeerId}; use rlp::{RlpStream, UntrustedRlp}; use util::hash::H256; -use util::{Bytes, DBValue, Mutex, RwLock, U256}; +use util::{DBValue, Mutex, RwLock, U256}; use time::{Duration, SteadyTime}; use std::collections::HashMap; @@ -35,7 +33,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use provider::Provider; -use request::{self, HashOrNumber, Request}; +use request::{Request, Requests, Response}; use self::request_credits::{Credits, FlowParams}; use self::context::{Ctx, TickCtx}; @@ -72,8 +70,8 @@ pub const PROTOCOL_VERSIONS: &'static [u8] = &[1]; /// Max protocol version. pub const MAX_PROTOCOL_VERSION: u8 = 1; -/// Packet count for LES. -pub const PACKET_COUNT: u8 = 17; +/// Packet count for PIP. +pub const PACKET_COUNT: u8 = 5; // packet ID definitions. mod packet { @@ -83,49 +81,27 @@ mod packet { // announcement of new block hashes or capabilities. pub const ANNOUNCE: u8 = 0x01; - // request and response for block headers - pub const GET_BLOCK_HEADERS: u8 = 0x02; - pub const BLOCK_HEADERS: u8 = 0x03; - - // request and response for block bodies - pub const GET_BLOCK_BODIES: u8 = 0x04; - pub const BLOCK_BODIES: u8 = 0x05; - - // request and response for transaction receipts. - pub const GET_RECEIPTS: u8 = 0x06; - pub const RECEIPTS: u8 = 0x07; - - // request and response for merkle proofs. - pub const GET_PROOFS: u8 = 0x08; - pub const PROOFS: u8 = 0x09; - - // request and response for contract code. - pub const GET_CONTRACT_CODES: u8 = 0x0a; - pub const CONTRACT_CODES: u8 = 0x0b; + // request and response. + pub const REQUEST: u8 = 0x02; + pub const RESPONSE: u8 = 0x03; // relay transactions to peers. - pub const SEND_TRANSACTIONS: u8 = 0x0c; - - // request and response for header proofs in a CHT. - pub const GET_HEADER_PROOFS: u8 = 0x0d; - pub const HEADER_PROOFS: u8 = 0x0e; - - // request and response for transaction proof. - pub const GET_TRANSACTION_PROOF: u8 = 0x0f; - pub const TRANSACTION_PROOF: u8 = 0x10; + pub const SEND_TRANSACTIONS: u8 = 0x04; } // timeouts for different kinds of requests. all values are in milliseconds. -// TODO: variable timeouts based on request count. mod timeout { pub const HANDSHAKE: i64 = 2500; - pub const HEADERS: i64 = 5000; - pub const BODIES: i64 = 5000; - pub const RECEIPTS: i64 = 3500; - pub const PROOFS: i64 = 4000; - pub const CONTRACT_CODES: i64 = 5000; - pub const HEADER_PROOFS: i64 = 3500; - pub const TRANSACTION_PROOF: i64 = 5000; + pub const BASE: i64 = 1500; // base timeout for packet. + + // timeouts per request within packet. + pub const HEADERS: i64 = 250; // per header? + pub const BODY: i64 = 50; + pub const RECEIPT: i64 = 50; + pub const PROOF: i64 = 100; // state proof + pub const CONTRACT_CODE: i64 = 100; + pub const HEADER_PROOF: i64 = 100; + pub const TRANSACTION_PROOF: i64 = 1000; // per gas? } /// A request id. @@ -158,27 +134,7 @@ pub struct Peer { failed_requests: Vec, } -impl Peer { - // check the maximum cost of a request, returning an error if there's - // not enough credits left. - // returns the calculated maximum cost. - fn deduct_max(&mut self, flow_params: &FlowParams, kind: request::Kind, max: usize) -> Result { - flow_params.recharge(&mut self.local_credits); - - let max_cost = flow_params.compute_cost(kind, max); - self.local_credits.deduct_cost(max_cost)?; - Ok(max_cost) - } - - // refund credits for a request. returns new amount of credits. - fn refund(&mut self, flow_params: &FlowParams, amount: U256) -> U256 { - flow_params.refund(&mut self.local_credits, amount); - - self.local_credits.current() - } -} - -/// An LES event handler. +/// A light protocol event handler. /// /// Each handler function takes a context which describes the relevant peer /// and gives references to the IO layer and protocol structure so new messages @@ -197,20 +153,10 @@ pub trait Handler: Send + Sync { fn on_announcement(&self, _ctx: &EventContext, _announcement: &Announcement) { } /// Called when a peer requests relay of some transactions. fn on_transactions(&self, _ctx: &EventContext, _relay: &[UnverifiedTransaction]) { } - /// Called when a peer responds with block bodies. - fn on_block_bodies(&self, _ctx: &EventContext, _req_id: ReqId, _bodies: &[Bytes]) { } - /// Called when a peer responds with block headers. - fn on_block_headers(&self, _ctx: &EventContext, _req_id: ReqId, _headers: &[Bytes]) { } - /// Called when a peer responds with block receipts. - fn on_receipts(&self, _ctx: &EventContext, _req_id: ReqId, _receipts: &[Vec]) { } - /// Called when a peer responds with state proofs. Each proof should be a series of trie - /// nodes in ascending order by distance from the root. - fn on_state_proofs(&self, _ctx: &EventContext, _req_id: ReqId, _proofs: &[Vec]) { } - /// Called when a peer responds with contract code. - fn on_code(&self, _ctx: &EventContext, _req_id: ReqId, _codes: &[Bytes]) { } - /// Called when a peer responds with header proofs. Each proof should be a block header coupled - /// with a series of trie nodes is ascending order by distance from the root. - fn on_header_proofs(&self, _ctx: &EventContext, _req_id: ReqId, _proofs: &[(Bytes, Vec)]) { } + /// Called when a peer responds to requests. + /// Responses not guaranteed to contain valid data and are not yet checked against + /// the requests they correspond to. + fn on_responses(&self, _ctx: &EventContext, _req_id: ReqId, _responses: &[Response]) { } /// Called when a peer responds with a transaction proof. Each proof is a vector of state items. fn on_transaction_proof(&self, _ctx: &EventContext, _req_id: ReqId, _state_items: &[DBValue]) { } /// Called to "tick" the handler periodically. @@ -307,7 +253,7 @@ pub struct LightProtocol { impl LightProtocol { /// Create a new instance of the protocol manager. pub fn new(provider: Arc, params: Params) -> Self { - debug!(target: "les", "Initializing LES handler"); + debug!(target: "pip", "Initializing light protocol handler"); let genesis_hash = provider.chain_info().genesis_hash; LightProtocol { @@ -339,62 +285,43 @@ impl LightProtocol { ) } - /// Check the maximum amount of requests of a specific type - /// which a peer would be able to serve. Returns zero if the - /// peer is unknown or has no credit parameters. - fn max_requests(&self, peer: PeerId, kind: request::Kind) -> usize { - self.peers.read().get(&peer).and_then(|peer| { - let mut peer = peer.lock(); - match peer.remote_flow { - Some((ref mut c, ref flow)) => { - flow.recharge(c); - Some(flow.max_amount(&*c, kind)) - } - None => None, - } - }).unwrap_or(0) - } - /// Make a request to a peer. /// /// Fails on: nonexistent peer, network error, peer not server, /// insufficient credits. Does not check capabilities before sending. /// On success, returns a request id which can later be coordinated /// with an event. - pub fn request_from(&self, io: &IoContext, peer_id: &PeerId, request: Request) -> Result { + pub fn request_from(&self, io: &IoContext, peer_id: &PeerId, requests: Requests) -> Result { let peers = self.peers.read(); - let peer = peers.get(peer_id).ok_or_else(|| Error::UnknownPeer)?; - let mut peer = peer.lock(); - - match peer.remote_flow { - Some((ref mut c, ref flow)) => { - flow.recharge(c); - let max = flow.compute_cost(request.kind(), request.amount()); - c.deduct_cost(max)?; - } - None => return Err(Error::NotServer), - } - - let req_id = self.req_id.fetch_add(1, Ordering::SeqCst); - let packet_data = encode_request(&request, req_id); - - trace!(target: "les", "Dispatching request {} to peer {}", req_id, peer_id); - - let packet_id = match request.kind() { - request::Kind::Headers => packet::GET_BLOCK_HEADERS, - request::Kind::Bodies => packet::GET_BLOCK_BODIES, - request::Kind::Receipts => packet::GET_RECEIPTS, - request::Kind::StateProofs => packet::GET_PROOFS, - request::Kind::Codes => packet::GET_CONTRACT_CODES, - request::Kind::HeaderProofs => packet::GET_HEADER_PROOFS, - request::Kind::TransactionProof => packet::GET_TRANSACTION_PROOF, + let peer = match peers.get(peer_id) { + Some(peer) => peer, + None => return Err(Error::UnknownPeer), }; - io.send(*peer_id, packet_id, packet_data); + let mut peer = peer.lock(); + let peer = &mut *peer; + match peer.remote_flow { + None => Err(Error::NotServer), + Some((ref mut creds, ref params)) => { + // check that enough credits are available. + let mut temp_creds: Credits = creds.clone(); + for request in requests.requests() { + temp_creds.deduct_cost(params.compute_cost(request))?; + } + *creds = temp_creds; - peer.pending_requests.insert(ReqId(req_id), request, SteadyTime::now()); + let req_id = ReqId(self.req_id.fetch_add(1, Ordering::SeqCst)); + io.send(*peer_id, packet::REQUEST, { + let mut stream = RlpStream::new_list(2); + stream.append(&req_id.0).append_list(&requests.requests()); + stream.out() + }); - Ok(ReqId(req_id)) + // begin timeout. + peer.pending_requests.insert(req_id, requests, SteadyTime::now()); + Ok(req_id) + } + } } /// Make an announcement of new chain head and capabilities to all peers. @@ -427,7 +354,7 @@ impl LightProtocol { None => { // both values will always originate locally -- this means something // has gone really wrong - debug!(target: "les", "couldn't compute reorganization depth between {:?} and {:?}", + debug!(target: "pip", "couldn't compute reorganization depth between {:?} and {:?}", &announcement.head_hash, &peer_info.sent_head); 0 } @@ -470,85 +397,52 @@ impl LightProtocol { // - check whether peer exists // - check whether request was made // - check whether request kinds match - fn pre_verify_response(&self, peer: &PeerId, kind: request::Kind, raw: &UntrustedRlp) -> Result { + fn pre_verify_response(&self, peer: &PeerId, raw: &UntrustedRlp) -> Result { let req_id = ReqId(raw.val_at(0)?); let cur_credits: U256 = raw.val_at(1)?; - trace!(target: "les", "pre-verifying response from peer {}, kind={:?}", peer, kind); + trace!(target: "pip", "pre-verifying response from peer {}", peer); - let mut had_req = false; let peers = self.peers.read(); - let maybe_err = match peers.get(peer) { + let res = match peers.get(peer) { Some(peer_info) => { let mut peer_info = peer_info.lock(); let req_info = peer_info.pending_requests.remove(&req_id, SteadyTime::now()); let flow_info = peer_info.remote_flow.as_mut(); match (req_info, flow_info) { - (Some(request), Some(flow_info)) => { - had_req = true; - + (Some(_), Some(flow_info)) => { let &mut (ref mut c, ref mut flow) = flow_info; let actual_credits = ::std::cmp::min(cur_credits, *flow.limit()); c.update_to(actual_credits); - if request.kind() != kind { - Some(Error::UnsolicitedResponse) - } else { - None - } + Ok(()) } - (None, _) => Some(Error::UnsolicitedResponse), - (_, None) => Some(Error::NotServer), // really should be impossible. + (None, _) => Err(Error::UnsolicitedResponse), + (_, None) => Err(Error::NotServer), // really should be impossible. } } - None => Some(Error::UnknownPeer), // probably only occurs in a race of some kind. + None => Err(Error::UnknownPeer), // probably only occurs in a race of some kind. }; - if had_req { - let id_guard = IdGuard::new(peers, *peer, req_id); - match maybe_err { - Some(err) => Err(err), - None => Ok(id_guard) - } - } else { - Err(maybe_err.expect("every branch without a request leads to error; qed")) - } + res.map(|_| IdGuard::new(peers, *peer, req_id)) } - /// Handle an LES packet using the given io context. + /// Handle a packet using the given io context. /// Packet data is _untrusted_, which means that invalid data won't lead to /// issues. pub fn handle_packet(&self, io: &IoContext, peer: &PeerId, packet_id: u8, data: &[u8]) { let rlp = UntrustedRlp::new(data); - trace!(target: "les", "Incoming packet {} from peer {}", packet_id, peer); + trace!(target: "pip", "Incoming packet {} from peer {}", packet_id, peer); // handle the packet let res = match packet_id { packet::STATUS => self.status(peer, io, rlp), packet::ANNOUNCE => self.announcement(peer, io, rlp), - packet::GET_BLOCK_HEADERS => self.get_block_headers(peer, io, rlp), - packet::BLOCK_HEADERS => self.block_headers(peer, io, rlp), - - packet::GET_BLOCK_BODIES => self.get_block_bodies(peer, io, rlp), - packet::BLOCK_BODIES => self.block_bodies(peer, io, rlp), - - packet::GET_RECEIPTS => self.get_receipts(peer, io, rlp), - packet::RECEIPTS => self.receipts(peer, io, rlp), - - packet::GET_PROOFS => self.get_proofs(peer, io, rlp), - packet::PROOFS => self.proofs(peer, io, rlp), - - packet::GET_CONTRACT_CODES => self.get_contract_code(peer, io, rlp), - packet::CONTRACT_CODES => self.contract_code(peer, io, rlp), - - packet::GET_HEADER_PROOFS => self.get_header_proofs(peer, io, rlp), - packet::HEADER_PROOFS => self.header_proofs(peer, io, rlp), - - packet::GET_TRANSACTION_PROOF => self.get_transaction_proof(peer, io, rlp), - packet::TRANSACTION_PROOF => self.transaction_proof(peer, io, rlp), + packet::REQUEST => self.request(peer, io, rlp), + packet::RESPONSE => self.response(peer, io, rlp), packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), @@ -577,7 +471,7 @@ impl LightProtocol { .collect(); for slowpoke in slowpokes { - debug!(target: "les", "Peer {} handshake timed out", slowpoke); + debug!(target: "pip", "Peer {} handshake timed out", slowpoke); pending.remove(&slowpoke); io.disconnect_peer(slowpoke); } @@ -587,7 +481,7 @@ impl LightProtocol { { for (peer_id, peer) in self.peers.read().iter() { if peer.lock().pending_requests.check_timeout(now) { - debug!(target: "les", "Peer {} request timeout", peer_id); + debug!(target: "pip", "Peer {} request timeout", peer_id); io.disconnect_peer(*peer_id); } } @@ -631,7 +525,7 @@ impl LightProtocol { /// called when a peer disconnects. pub fn on_disconnect(&self, peer: PeerId, io: &IoContext) { - trace!(target: "les", "Peer {} disconnecting", peer); + trace!(target: "pip", "Peer {} disconnecting", peer); self.pending_peers.write().remove(&peer); let unfulfilled = match self.peers.write().remove(&peer) { @@ -686,7 +580,7 @@ impl LightProtocol { let (status, capabilities, flow_params) = status::parse_handshake(data)?; - trace!(target: "les", "Connected peer with chain head {:?}", (status.head_hash, status.head_num)); + trace!(target: "pip", "Connected peer with chain head {:?}", (status.head_hash, status.head_num)); if (status.network_id, status.genesis_hash) != (self.network_id, self.genesis_hash) { return Err(Error::WrongNetwork); @@ -723,7 +617,7 @@ impl LightProtocol { // Handle an announcement. fn announcement(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { if !self.peers.read().contains_key(peer) { - debug!(target: "les", "Ignoring announcement from unknown peer"); + debug!(target: "pip", "Ignoring announcement from unknown peer"); return Ok(()) } @@ -765,513 +659,80 @@ impl LightProtocol { Ok(()) } - // Handle a request for block headers. - fn get_block_headers(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_HEADERS: usize = 512; + // Receive requests from a peer. + fn request(&self, peer_id: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { + // the maximum amount of requests we'll fill in a single packet. + const MAX_REQUESTS: usize = 256; + + use ::request::RequestBuilder; + use ::request::CompleteRequest; let peers = self.peers.read(); - let peer = match peers.get(peer) { + let peer = match peers.get(peer_id) { Some(peer) => peer, None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - let data = data.at(1)?; - - let start_block = { - if data.at(0)?.size() == 32 { - HashOrNumber::Hash(data.val_at(0)?) - } else { - HashOrNumber::Number(data.val_at(0)?) - } - }; - - let req = request::Headers { - start: start_block, - max: ::std::cmp::min(MAX_HEADERS, data.val_at(1)?), - skip: data.val_at(2)?, - reverse: data.val_at(3)?, - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Headers, req.max)?; - - let response = self.provider.block_headers(req); - let actual_cost = self.flow_params.compute_cost(request::Kind::Headers, response.len()); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - io.respond(packet::BLOCK_HEADERS, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for header in response { - stream.append_raw(&header.into_inner(), 1); - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for block headers. - fn block_headers(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::Headers, &raw)?; - let raw_headers: Vec<_> = raw.at(2)?.iter().map(|x| x.as_raw().to_owned()).collect(); - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_block_headers(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_headers); - } - - Ok(()) - } - - // Handle a request for block bodies. - fn get_block_bodies(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_BODIES: usize = 256; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - - let req = request::Bodies { - block_hashes: data.at(1)?.iter() - .take(MAX_BODIES) - .map(|x| x.as_val()) - .collect::>()? - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Bodies, req.block_hashes.len())?; - - let response = self.provider.block_bodies(req); - let response_len = response.iter().filter(|x| x.is_some()).count(); - let actual_cost = self.flow_params.compute_cost(request::Kind::Bodies, response_len); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - - io.respond(packet::BLOCK_BODIES, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for body in response { - match body { - Some(body) => stream.append_raw(&body.into_inner(), 1), - None => stream.append_empty_data(), - }; - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for block bodies. - fn block_bodies(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::Bodies, &raw)?; - let raw_bodies: Vec = raw.at(2)?.iter().map(|x| x.as_raw().to_owned()).collect(); - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_block_bodies(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_bodies); - } - - Ok(()) - } - - // Handle a request for receipts. - fn get_receipts(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_RECEIPTS: usize = 256; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - - let req = request::Receipts { - block_hashes: data.at(1)?.iter() - .take(MAX_RECEIPTS) - .map(|x| x.as_val()) - .collect::>()? - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Receipts, req.block_hashes.len())?; - - let response = self.provider.receipts(req); - let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count(); - let actual_cost = self.flow_params.compute_cost(request::Kind::Receipts, response_len); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - - io.respond(packet::RECEIPTS, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for receipts in response { - stream.append_raw(&receipts, 1); - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for receipts. - fn receipts(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::Receipts, &raw)?; - let raw_receipts: Vec> = raw.at(2)? - .iter() - .map(|x| x.as_list()) - .collect::>()?; - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_receipts(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_receipts); - } - - Ok(()) - } - - // Handle a request for proofs. - fn get_proofs(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_PROOFS: usize = 128; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - - let req = { - let requests: Result, Error> = data.at(1)?.iter().take(MAX_PROOFS).map(|x| { - Ok(request::StateProof { - block: x.val_at(0)?, - key1: x.val_at(1)?, - key2: if x.at(2)?.is_empty() { None } else { Some(x.val_at(2)?) }, - from_level: x.val_at(3)?, - }) - }).collect(); - - request::StateProofs { - requests: requests?, - } - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::StateProofs, req.requests.len())?; - - let response = self.provider.proofs(req); - let response_len = response.iter().filter(|x| &x[..] != &::rlp::EMPTY_LIST_RLP).count(); - let actual_cost = self.flow_params.compute_cost(request::Kind::StateProofs, response_len); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - - io.respond(packet::PROOFS, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for proof in response { - stream.append_raw(&proof, 1); - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for proofs. - fn proofs(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::StateProofs, &raw)?; - - let raw_proofs: Vec> = raw.at(2)?.iter() - .map(|x| x.iter().map(|node| node.as_raw().to_owned()).collect()) - .collect(); - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_state_proofs(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_proofs); - } - - Ok(()) - } - - // Handle a request for contract code. - fn get_contract_code(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_CODES: usize = 256; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - - let req = { - let requests: Result, Error> = data.at(1)?.iter().take(MAX_CODES).map(|x| { - Ok(request::ContractCode { - block_hash: x.val_at(0)?, - account_key: x.val_at(1)?, - }) - }).collect(); - - request::ContractCodes { - code_requests: requests?, - } - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::Codes, req.code_requests.len())?; - - let response = self.provider.contract_codes(req); - let response_len = response.iter().filter(|x| !x.is_empty()).count(); - let actual_cost = self.flow_params.compute_cost(request::Kind::Codes, response_len); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - - io.respond(packet::CONTRACT_CODES, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for code in response { - stream.append(&code); - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for contract code. - fn contract_code(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::Codes, &raw)?; - - let raw_code: Vec = raw.at(2)?.iter() - .map(|x| x.as_val()) - .collect::>()?; - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_code(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_code); - } - - Ok(()) - } - - // Handle a request for header proofs - fn get_header_proofs(&self, peer: &PeerId, io: &IoContext, data: UntrustedRlp) -> Result<(), Error> { - const MAX_PROOFS: usize = 256; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); - return Ok(()) - } - }; - let mut peer = peer.lock(); - - let req_id: u64 = data.val_at(0)?; - - let req = { - let requests: Result, Error> = data.at(1)?.iter().take(MAX_PROOFS).map(|x| { - Ok(request::HeaderProof { - cht_number: x.val_at(0)?, - block_number: x.val_at(1)?, - from_level: x.val_at(2)?, - }) - }).collect(); - - request::HeaderProofs { - requests: requests?, - } - }; - - let max_cost = peer.deduct_max(&self.flow_params, request::Kind::HeaderProofs, req.requests.len())?; - - let response = self.provider.header_proofs(req); - let response_len = response.iter().filter(|x| &x[..] != ::rlp::EMPTY_LIST_RLP).count(); - let actual_cost = self.flow_params.compute_cost(request::Kind::HeaderProofs, response_len); - assert!(max_cost >= actual_cost, "Actual cost exceeded maximum computed cost."); - - let cur_credits = peer.refund(&self.flow_params, max_cost - actual_cost); - - io.respond(packet::HEADER_PROOFS, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for proof in response { - stream.append_raw(&proof, 1); - } - - stream.out() - }); - - Ok(()) - } - - // Receive a response for header proofs - fn header_proofs(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - fn decode_res(raw: UntrustedRlp) -> Result<(Bytes, Vec), ::rlp::DecoderError> { - Ok(( - raw.val_at(0)?, - raw.at(1)?.iter().map(|x| x.as_raw().to_owned()).collect(), - )) - } - - let id_guard = self.pre_verify_response(peer, request::Kind::HeaderProofs, &raw)?; - let raw_proofs: Vec<_> = raw.at(2)?.iter() - .map(decode_res) - .collect::>()?; - - let req_id = id_guard.defuse(); - for handler in &self.handlers { - handler.on_header_proofs(&Ctx { - peer: *peer, - io: io, - proto: self, - }, req_id, &raw_proofs); - } - - Ok(()) - } - - // Receive a request for proof-of-execution. - fn get_transaction_proof(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - // refuse to execute more than this amount of gas at once. - // this is appx. the point at which the proof of execution would no longer fit in - // a single Devp2p packet. - const MAX_GAS: usize = 50_000_000; - use util::Uint; - - let peers = self.peers.read(); - let peer = match peers.get(peer) { - Some(peer) => peer, - None => { - debug!(target: "les", "Ignoring request from unknown peer"); + debug!(target: "pip", "Ignoring request from unknown peer"); return Ok(()) } }; let mut peer = peer.lock(); let req_id: u64 = raw.val_at(0)?; + let mut request_builder = RequestBuilder::default(); - let req = { - let req_rlp = raw.at(1)?; - request::TransactionProof { - at: req_rlp.val_at(0)?, - from: req_rlp.val_at(1)?, - action: if req_rlp.at(2)?.is_empty() { - Action::Create - } else { - Action::Call(req_rlp.val_at(2)?) - }, - gas: ::std::cmp::min(req_rlp.val_at(3)?, MAX_GAS.into()), - gas_price: req_rlp.val_at(4)?, - value: req_rlp.val_at(5)?, - data: req_rlp.val_at(6)?, + trace!(target: "pip", "Received requests (id: {}) from peer {}", req_id, peer_id); + + // deserialize requests, check costs and request validity. + peer.local_credits.deduct_cost(self.flow_params.base_cost())?; + for request_rlp in raw.at(1)?.iter().take(MAX_REQUESTS) { + let request: Request = request_rlp.as_val()?; + peer.local_credits.deduct_cost(self.flow_params.compute_cost(&request))?; + request_builder.push(request).map_err(|_| Error::BadBackReference)?; + } + + let requests = request_builder.build(); + let num_requests = requests.requests().len(); + trace!(target: "pip", "Beginning to respond to requests (id: {}) from peer {}", req_id, peer_id); + + // respond to all requests until one fails. + let responses = requests.respond_to_all(|complete_req| { + match complete_req { + CompleteRequest::Headers(req) => self.provider.block_headers(req).map(Response::Headers), + CompleteRequest::HeaderProof(req) => self.provider.header_proof(req).map(Response::HeaderProof), + CompleteRequest::Body(req) => self.provider.block_body(req).map(Response::Body), + CompleteRequest::Receipts(req) => self.provider.block_receipts(req).map(Response::Receipts), + CompleteRequest::Account(req) => self.provider.account_proof(req).map(Response::Account), + CompleteRequest::Storage(req) => self.provider.storage_proof(req).map(Response::Storage), + CompleteRequest::Code(req) => self.provider.contract_code(req).map(Response::Code), + CompleteRequest::Execution(req) => self.provider.transaction_proof(req).map(Response::Execution), } - }; - - // always charge the peer for all the gas. - peer.deduct_max(&self.flow_params, request::Kind::TransactionProof, req.gas.low_u64() as usize)?; - - let response = match self.provider.transaction_proof(req) { - Some(res) => res, - None => vec![], - }; - - let cur_credits = peer.local_credits.current(); - - io.respond(packet::TRANSACTION_PROOF, { - let mut stream = RlpStream::new_list(3); - stream.append(&req_id).append(&cur_credits).begin_list(response.len()); - - for state_item in response { - stream.append(&&state_item[..]); - } - - stream.out() }); + trace!(target: "pip", "Responded to {}/{} requests in packet {}", responses.len(), num_requests, req_id); + + io.respond(packet::RESPONSE, { + let mut stream = RlpStream::new_list(3); + let cur_credits = peer.local_credits.current(); + stream.append(&req_id).append(&cur_credits).append_list(&responses); + stream.out() + }); Ok(()) } - // Receive a response for proof-of-execution. - fn transaction_proof(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { - let id_guard = self.pre_verify_response(peer, request::Kind::HeaderProofs, &raw)?; - let raw_proof: Vec = raw.at(2)?.iter() - .map(|rlp| { - let mut db_val = DBValue::new(); - db_val.append_slice(rlp.data()?); - Ok(db_val) - }) - .collect::, ::rlp::DecoderError>>()?; + // handle a packet with responses. + fn response(&self, peer: &PeerId, io: &IoContext, raw: UntrustedRlp) -> Result<(), Error> { + let (req_id, responses) = { + let id_guard = self.pre_verify_response(peer, &raw)?; + let responses: Vec = raw.list_at(2)?; + (id_guard.defuse(), responses) + }; - let req_id = id_guard.defuse(); for handler in &self.handlers { - handler.on_transaction_proof(&Ctx { - peer: *peer, + handler.on_responses(&Ctx { io: io, proto: self, - }, req_id, &raw_proof); + peer: *peer, + }, req_id, &responses); } Ok(()) @@ -1286,7 +747,7 @@ impl LightProtocol { .map(|x| x.as_val::()) .collect::>()?; - debug!(target: "les", "Received {} transactions to relay from peer {}", txs.len(), peer); + debug!(target: "pip", "Received {} transactions to relay from peer {}", txs.len(), peer); for handler in &self.handlers { handler.on_transactions(&Ctx { @@ -1305,11 +766,11 @@ fn punish(peer: PeerId, io: &IoContext, e: Error) { match e.punishment() { Punishment::None => {} Punishment::Disconnect => { - debug!(target: "les", "Disconnecting peer {}: {}", peer, e); + debug!(target: "pip", "Disconnecting peer {}: {}", peer, e); io.disconnect_peer(peer) } Punishment::Disable => { - debug!(target: "les", "Disabling peer {}: {}", peer, e); + debug!(target: "pip", "Disabling peer {}: {}", peer, e); io.disable_peer(peer) } } @@ -1339,112 +800,7 @@ impl NetworkProtocolHandler for LightProtocol { match timer { TIMEOUT => self.timeout_check(io), TICK_TIMEOUT => self.tick_handlers(io), - _ => warn!(target: "les", "received timeout on unknown token {}", timer), - } - } -} - -// Helper for encoding the request to RLP with the given ID. -fn encode_request(req: &Request, req_id: usize) -> Vec { - match *req { - Request::Headers(ref headers) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(4); - - match headers.start { - HashOrNumber::Hash(ref hash) => stream.append(hash), - HashOrNumber::Number(ref num) => stream.append(num), - }; - - stream - .append(&headers.max) - .append(&headers.skip) - .append(&headers.reverse); - - stream.out() - } - Request::Bodies(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(request.block_hashes.len()); - - for hash in &request.block_hashes { - stream.append(hash); - } - - stream.out() - } - Request::Receipts(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(request.block_hashes.len()); - - for hash in &request.block_hashes { - stream.append(hash); - } - - stream.out() - } - Request::StateProofs(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(request.requests.len()); - - for proof_req in &request.requests { - stream.begin_list(4) - .append(&proof_req.block) - .append(&proof_req.key1); - - match proof_req.key2 { - Some(ref key2) => stream.append(key2), - None => stream.append_empty_data(), - }; - - stream.append(&proof_req.from_level); - } - - stream.out() - } - Request::Codes(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(request.code_requests.len()); - - for code_req in &request.code_requests { - stream.begin_list(2) - .append(&code_req.block_hash) - .append(&code_req.account_key); - } - - stream.out() - } - Request::HeaderProofs(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(request.requests.len()); - - for proof_req in &request.requests { - stream.begin_list(3) - .append(&proof_req.cht_number) - .append(&proof_req.block_number) - .append(&proof_req.from_level); - } - - stream.out() - } - Request::TransactionProof(ref request) => { - let mut stream = RlpStream::new_list(2); - stream.append(&req_id).begin_list(7) - .append(&request.at) - .append(&request.from); - - match request.action { - Action::Create => stream.append_empty_data(), - Action::Call(ref to) => stream.append(to), - }; - - stream - .append(&request.gas) - .append(&request.gas_price) - .append(&request.value) - .append(&request.data); - - stream.out() + _ => warn!(target: "pip", "received timeout on unknown token {}", timer), } } } diff --git a/ethcore/light/src/net/request_credits.rs b/ethcore/light/src/net/request_credits.rs index f35c6662f..e505b293c 100644 --- a/ethcore/light/src/net/request_credits.rs +++ b/ethcore/light/src/net/request_credits.rs @@ -26,18 +26,13 @@ //! Current default costs are picked completely arbitrarily, not based //! on any empirical timings or mathematical models. -use request; -use super::packet; +use request::{self, Request}; use super::error::Error; use rlp::*; use util::U256; use time::{Duration, SteadyTime}; -/// A request cost specification. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Cost(pub U256, pub U256); - /// Credits value. /// /// Produced and recharged using `FlowParams`. @@ -81,90 +76,95 @@ impl Credits { /// A cost table, mapping requests to base and per-request costs. #[derive(Debug, Clone, PartialEq, Eq)] pub struct CostTable { - headers: Cost, // cost per header - bodies: Cost, - receipts: Cost, - state_proofs: Cost, - contract_codes: Cost, - header_proofs: Cost, - transaction_proof: Cost, // cost per gas. + base: U256, // cost per packet. + headers: U256, // cost per header + body: U256, + receipts: U256, + account: U256, + storage: U256, + code: U256, + header_proof: U256, + transaction_proof: U256, // cost per gas. } impl Default for CostTable { fn default() -> Self { // arbitrarily chosen constants. CostTable { - headers: Cost(100000.into(), 10000.into()), - bodies: Cost(150000.into(), 15000.into()), - receipts: Cost(50000.into(), 5000.into()), - state_proofs: Cost(250000.into(), 25000.into()), - contract_codes: Cost(200000.into(), 20000.into()), - header_proofs: Cost(150000.into(), 15000.into()), - transaction_proof: Cost(100000.into(), 2.into()), + base: 100000.into(), + headers: 10000.into(), + body: 15000.into(), + receipts: 5000.into(), + account: 25000.into(), + storage: 25000.into(), + code: 20000.into(), + header_proof: 15000.into(), + transaction_proof: 2.into(), } } } impl Encodable for CostTable { fn rlp_append(&self, s: &mut RlpStream) { - fn append_cost(s: &mut RlpStream, msg_id: u8, cost: &Cost) { - s.begin_list(3) - .append(&msg_id) - .append(&cost.0) - .append(&cost.1); + fn append_cost(s: &mut RlpStream, cost: &U256, kind: request::Kind) { + s.begin_list(2); + + // hack around https://github.com/ethcore/parity/issues/4356 + Encodable::rlp_append(&kind, s); + s.append(cost); } - s.begin_list(7); - - append_cost(s, packet::GET_BLOCK_HEADERS, &self.headers); - append_cost(s, packet::GET_BLOCK_BODIES, &self.bodies); - append_cost(s, packet::GET_RECEIPTS, &self.receipts); - append_cost(s, packet::GET_PROOFS, &self.state_proofs); - append_cost(s, packet::GET_CONTRACT_CODES, &self.contract_codes); - append_cost(s, packet::GET_HEADER_PROOFS, &self.header_proofs); - append_cost(s, packet::GET_TRANSACTION_PROOF, &self.transaction_proof); + s.begin_list(9).append(&self.base); + append_cost(s, &self.headers, request::Kind::Headers); + append_cost(s, &self.body, request::Kind::Body); + append_cost(s, &self.receipts, request::Kind::Receipts); + append_cost(s, &self.account, request::Kind::Account); + append_cost(s, &self.storage, request::Kind::Storage); + append_cost(s, &self.code, request::Kind::Code); + append_cost(s, &self.header_proof, request::Kind::HeaderProof); + append_cost(s, &self.transaction_proof, request::Kind::Execution); } } impl Decodable for CostTable { fn decode(rlp: &UntrustedRlp) -> Result { + let base = rlp.val_at(0)?; + let mut headers = None; - let mut bodies = None; + let mut body = None; let mut receipts = None; - let mut state_proofs = None; - let mut contract_codes = None; - let mut header_proofs = None; + let mut account = None; + let mut storage = None; + let mut code = None; + let mut header_proof = None; let mut transaction_proof = None; - for row in rlp.iter() { - let msg_id: u8 = row.val_at(0)?; - let cost = { - let base = row.val_at(1)?; - let per = row.val_at(2)?; - - Cost(base, per) - }; - - match msg_id { - packet::GET_BLOCK_HEADERS => headers = Some(cost), - packet::GET_BLOCK_BODIES => bodies = Some(cost), - packet::GET_RECEIPTS => receipts = Some(cost), - packet::GET_PROOFS => state_proofs = Some(cost), - packet::GET_CONTRACT_CODES => contract_codes = Some(cost), - packet::GET_HEADER_PROOFS => header_proofs = Some(cost), - packet::GET_TRANSACTION_PROOF => transaction_proof = Some(cost), - _ => return Err(DecoderError::Custom("Unrecognized message in cost table")), + for cost_list in rlp.iter().skip(1) { + let cost = cost_list.val_at(1)?; + match cost_list.val_at(0)? { + request::Kind::Headers => headers = Some(cost), + request::Kind::Body => body = Some(cost), + request::Kind::Receipts => receipts = Some(cost), + request::Kind::Account => account = Some(cost), + request::Kind::Storage => storage = Some(cost), + request::Kind::Code => code = Some(cost), + request::Kind::HeaderProof => header_proof = Some(cost), + request::Kind::Execution => transaction_proof = Some(cost), } } + let unwrap_cost = |cost: Option| cost.ok_or(DecoderError::Custom("Not all costs specified in cost table.")); + Ok(CostTable { - headers: headers.ok_or(DecoderError::Custom("No headers cost specified"))?, - bodies: bodies.ok_or(DecoderError::Custom("No bodies cost specified"))?, - receipts: receipts.ok_or(DecoderError::Custom("No receipts cost specified"))?, - state_proofs: state_proofs.ok_or(DecoderError::Custom("No proofs cost specified"))?, - contract_codes: contract_codes.ok_or(DecoderError::Custom("No contract codes specified"))?, - header_proofs: header_proofs.ok_or(DecoderError::Custom("No header proofs cost specified"))?, - transaction_proof: transaction_proof.ok_or(DecoderError::Custom("No transaction proof gas cost specified"))?, + base: base, + headers: unwrap_cost(headers)?, + body: unwrap_cost(body)?, + receipts: unwrap_cost(receipts)?, + account: unwrap_cost(account)?, + storage: unwrap_cost(storage)?, + code: unwrap_cost(code)?, + header_proof: unwrap_cost(header_proof)?, + transaction_proof: unwrap_cost(transaction_proof)?, }) } } @@ -190,17 +190,19 @@ impl FlowParams { /// Create effectively infinite flow params. pub fn free() -> Self { - let free_cost = Cost(0.into(), 0.into()); + let free_cost: U256 = 0.into(); FlowParams { limit: (!0u64).into(), recharge: 1.into(), costs: CostTable { + base: free_cost.clone(), headers: free_cost.clone(), - bodies: free_cost.clone(), + body: free_cost.clone(), receipts: free_cost.clone(), - state_proofs: free_cost.clone(), - contract_codes: free_cost.clone(), - header_proofs: free_cost.clone(), + account: free_cost.clone(), + storage: free_cost.clone(), + code: free_cost.clone(), + header_proof: free_cost.clone(), transaction_proof: free_cost, } } @@ -212,61 +214,34 @@ impl FlowParams { /// Get a reference to the cost table. pub fn cost_table(&self) -> &CostTable { &self.costs } + /// Get the base cost of a request. + pub fn base_cost(&self) -> U256 { self.costs.base } + /// Get a reference to the recharge rate. pub fn recharge_rate(&self) -> &U256 { &self.recharge } /// Compute the actual cost of a request, given the kind of request /// and number of requests made. - pub fn compute_cost(&self, kind: request::Kind, amount: usize) -> U256 { - let cost = match kind { - request::Kind::Headers => &self.costs.headers, - request::Kind::Bodies => &self.costs.bodies, - request::Kind::Receipts => &self.costs.receipts, - request::Kind::StateProofs => &self.costs.state_proofs, - request::Kind::Codes => &self.costs.contract_codes, - request::Kind::HeaderProofs => &self.costs.header_proofs, - request::Kind::TransactionProof => &self.costs.transaction_proof, - }; - - let amount: U256 = amount.into(); - cost.0 + (amount * cost.1) - } - - /// Compute the maximum number of costs of a specific kind which can be made - /// with the given amount of credits - /// Saturates at `usize::max()`. This is not a problem in practice because - /// this amount of requests is already prohibitively large. - pub fn max_amount(&self, credits: &Credits, kind: request::Kind) -> usize { - use util::Uint; - use std::usize; - - let cost = match kind { - request::Kind::Headers => &self.costs.headers, - request::Kind::Bodies => &self.costs.bodies, - request::Kind::Receipts => &self.costs.receipts, - request::Kind::StateProofs => &self.costs.state_proofs, - request::Kind::Codes => &self.costs.contract_codes, - request::Kind::HeaderProofs => &self.costs.header_proofs, - request::Kind::TransactionProof => &self.costs.transaction_proof, - }; - - let start = credits.current(); - - if start <= cost.0 { - return 0; - } else if cost.1 == U256::zero() { - return usize::MAX; - } - - let max = (start - cost.0) / cost.1; - if max >= usize::MAX.into() { - usize::MAX - } else { - max.as_u64() as usize + pub fn compute_cost(&self, request: &Request) -> U256 { + match *request { + Request::Headers(ref req) => self.costs.headers * req.max.into(), + Request::HeaderProof(_) => self.costs.header_proof, + Request::Body(_) => self.costs.body, + Request::Receipts(_) => self.costs.receipts, + Request::Account(_) => self.costs.account, + Request::Storage(_) => self.costs.storage, + Request::Code(_) => self.costs.code, + Request::Execution(ref req) => self.costs.transaction_proof * req.gas, } } - /// Create initial credits.. + /// Compute the cost of a set of requests. + /// This is the base cost plus the cost of each individual request. + pub fn compute_cost_multi(&self, requests: &[Request]) -> U256 { + requests.iter().fold(self.costs.base, |cost, req| cost + self.compute_cost(req)) + } + + /// Create initial credits. pub fn create_credits(&self) -> Credits { Credits { estimate: self.limit, diff --git a/ethcore/light/src/net/request_set.rs b/ethcore/light/src/net/request_set.rs index e6d4068da..a2391ef6f 100644 --- a/ethcore/light/src/net/request_set.rs +++ b/ethcore/light/src/net/request_set.rs @@ -24,7 +24,8 @@ use std::collections::{BTreeMap, HashMap}; use std::iter::FromIterator; -use request::{self, Request}; +use request::Request; +use request::Requests; use net::{timeout, ReqId}; use time::{Duration, SteadyTime}; @@ -35,7 +36,7 @@ pub struct RequestSet { counter: u64, base: Option, ids: HashMap, - reqs: BTreeMap, + reqs: BTreeMap, } impl Default for RequestSet { @@ -50,8 +51,8 @@ impl Default for RequestSet { } impl RequestSet { - /// Push a request onto the stack. - pub fn insert(&mut self, req_id: ReqId, req: Request, now: SteadyTime) { + /// Push requests onto the stack. + pub fn insert(&mut self, req_id: ReqId, req: Requests, now: SteadyTime) { let counter = self.counter; self.ids.insert(req_id, counter); self.reqs.insert(counter, req); @@ -63,8 +64,8 @@ impl RequestSet { self.counter += 1; } - /// Remove a request from the stack. - pub fn remove(&mut self, req_id: &ReqId, now: SteadyTime) -> Option { + /// Remove a set of requests from the stack. + pub fn remove(&mut self, req_id: &ReqId, now: SteadyTime) -> Option { let id = match self.ids.remove(&req_id) { Some(id) => id, None => return None, @@ -89,22 +90,10 @@ impl RequestSet { None => return false, }; - let kind = self.reqs.values() - .next() - .map(|r| r.kind()) - .expect("base time implies `reqs` non-empty; qed"); + let first_req = self.reqs.values().next() + .expect("base existing implies `reqs` non-empty; qed"); - let kind_timeout = match kind { - request::Kind::Headers => timeout::HEADERS, - request::Kind::Bodies => timeout::BODIES, - request::Kind::Receipts => timeout::RECEIPTS, - request::Kind::StateProofs => timeout::PROOFS, - request::Kind::Codes => timeout::CONTRACT_CODES, - request::Kind::HeaderProofs => timeout::HEADER_PROOFS, - request::Kind::TransactionProof => timeout::TRANSACTION_PROOF, - }; - - base + Duration::milliseconds(kind_timeout) <= now + base + compute_timeout(&first_req) <= now } /// Collect all pending request ids. @@ -121,25 +110,43 @@ impl RequestSet { pub fn is_empty(&self) -> bool { self.len() == 0 } } +// helper to calculate timeout for a specific set of requests. +// it's a base amount + some amount per request. +fn compute_timeout(reqs: &Requests) -> Duration { + Duration::milliseconds(reqs.requests().iter().fold(timeout::BASE, |tm, req| { + tm + match *req { + Request::Headers(_) => timeout::HEADERS, + Request::HeaderProof(_) => timeout::HEADER_PROOF, + Request::Receipts(_) => timeout::RECEIPT, + Request::Body(_) => timeout::BODY, + Request::Account(_) => timeout::PROOF, + Request::Storage(_) => timeout::PROOF, + Request::Code(_) => timeout::CONTRACT_CODE, + Request::Execution(_) => timeout::TRANSACTION_PROOF, + } + })) +} + #[cfg(test)] mod tests { - use net::{timeout, ReqId}; - use request::{Request, Receipts}; + use net::ReqId; + use request::RequestBuilder; use time::{SteadyTime, Duration}; - use super::RequestSet; + use super::{RequestSet, compute_timeout}; #[test] fn multi_timeout() { let test_begin = SteadyTime::now(); let mut req_set = RequestSet::default(); - let the_req = Request::Receipts(Receipts { block_hashes: Vec::new() }); + let the_req = RequestBuilder::default().build(); + let req_time = compute_timeout(&the_req); req_set.insert(ReqId(0), the_req.clone(), test_begin); req_set.insert(ReqId(1), the_req, test_begin + Duration::seconds(1)); assert_eq!(req_set.base, Some(test_begin)); - let test_end = test_begin + Duration::milliseconds(timeout::RECEIPTS); + let test_end = test_begin + req_time; assert!(req_set.check_timeout(test_end)); req_set.remove(&ReqId(0), test_begin + Duration::seconds(1)).unwrap(); diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 6a9de1467..e2081534c 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -27,15 +27,31 @@ use network::{PeerId, NodeId}; use net::request_credits::FlowParams; use net::context::IoContext; use net::status::{Capabilities, Status, write_handshake}; -use net::{encode_request, LightProtocol, Params, packet, Peer}; +use net::{LightProtocol, Params, packet, Peer}; use provider::Provider; -use request::{self, Request, Headers}; +use request; +use request::*; use rlp::*; -use util::{Address, Bytes, DBValue, H256, U256}; +use util::{Address, H256, U256}; use std::sync::Arc; +// helper for encoding a single request into a packet. +// panics on bad backreference. +fn encode_single(request: Request) -> Requests { + let mut builder = RequestBuilder::default(); + builder.push(request).unwrap(); + builder.build() +} + +// helper for making a packet out of `Requests`. +fn make_packet(req_id: usize, requests: &Requests) -> Vec { + let mut stream = RlpStream::new_list(2); + stream.append(&req_id).append_list(&requests.requests()); + stream.out() +} + // expected result from a call. #[derive(Debug, PartialEq, Eq)] enum Expect { @@ -99,35 +115,45 @@ impl Provider for TestProvider { self.0.client.block_header(id) } - fn block_body(&self, id: BlockId) -> Option { - self.0.client.block_body(id) + fn block_body(&self, req: request::CompleteBodyRequest) -> Option { + self.0.client.block_body(req) } - fn block_receipts(&self, hash: &H256) -> Option { - self.0.client.block_receipts(&hash) + fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option { + self.0.client.block_receipts(req) } - fn state_proof(&self, req: request::StateProof) -> Vec { - match req.key2 { - Some(_) => vec![::util::sha3::SHA3_NULL_RLP.to_vec()], - None => { - // sort of a leaf node - let mut stream = RlpStream::new_list(2); - stream.append(&req.key1).append_empty_data(); - vec![stream.out()] - } - } + fn account_proof(&self, req: request::CompleteAccountRequest) -> Option { + // sort of a leaf node + let mut stream = RlpStream::new_list(2); + stream.append(&req.address_hash).append_empty_data(); + Some(AccountResponse { + proof: vec![stream.out()], + balance: 10.into(), + nonce: 100.into(), + code_hash: Default::default(), + storage_root: Default::default(), + }) } - fn contract_code(&self, req: request::ContractCode) -> Bytes { - req.account_key.iter().chain(req.account_key.iter()).cloned().collect() + fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option { + Some(StorageResponse { + proof: vec![::rlp::encode(&req.key_hash).to_vec()], + value: req.key_hash | req.address_hash, + }) } - fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec)> { + fn contract_code(&self, req: request::CompleteCodeRequest) -> Option { + Some(CodeResponse { + code: req.block_hash.iter().chain(req.code_hash.iter()).cloned().collect(), + }) + } + + fn header_proof(&self, _req: request::CompleteHeaderProofRequest) -> Option { None } - fn transaction_proof(&self, _req: request::TransactionProof) -> Option> { + fn transaction_proof(&self, _req: request::CompleteExecutionRequest) -> Option { None } @@ -226,14 +252,15 @@ fn credit_overflow() { } // 1000 requests is far too many for the default flow params. - let request = encode_request(&Request::Headers(Headers { - start: 1.into(), + let requests = encode_single(Request::Headers(IncompleteHeadersRequest { + start: HashOrNumber::Number(1).into(), max: 1000, skip: 0, reverse: false, - }), 111); + })); + let request = make_packet(111, &requests); - proto.handle_packet(&Expect::Punish(1), &1, packet::GET_BLOCK_HEADERS, &request); + proto.handle_packet(&Expect::Punish(1), &1, packet::REQUEST, &request); } // test the basic request types -- these just make sure that requests are parsed @@ -259,33 +286,36 @@ fn get_block_headers() { proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); } - let request = Headers { - start: 1.into(), + let request = Request::Headers(IncompleteHeadersRequest { + start: HashOrNumber::Number(1).into(), max: 10, skip: 0, reverse: false, - }; + }); + let req_id = 111; - let request_body = encode_request(&Request::Headers(request.clone()), req_id); + let requests = encode_single(request.clone()); + let request_body = make_packet(req_id, &requests); + let response = { let headers: Vec<_> = (0..10).map(|i| provider.client.block_header(BlockId::Number(i + 1)).unwrap()).collect(); assert_eq!(headers.len(), 10); - let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::Headers, 10); + let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()); - let mut response_stream = RlpStream::new_list(3); + let response = vec![Response::Headers(HeadersResponse { + headers: headers, + })]; - response_stream.append(&req_id).append(&new_creds).begin_list(10); - for header in headers { - response_stream.append_raw(&header.into_inner(), 1); - } + let mut stream = RlpStream::new_list(3); + stream.append(&req_id).append(&new_creds).append_list(&response); - response_stream.out() + stream.out() }; - let expected = Expect::Respond(packet::BLOCK_HEADERS, response); - proto.handle_packet(&expected, &1, packet::GET_BLOCK_HEADERS, &request_body); + let expected = Expect::Respond(packet::RESPONSE, response); + proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); } #[test] @@ -308,33 +338,32 @@ fn get_block_bodies() { proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &my_status); } - let request = request::Bodies { - block_hashes: (0..10).map(|i| - provider.client.block_header(BlockId::Number(i)).unwrap().hash() - ).collect() - }; + let mut builder = RequestBuilder::default(); + let mut bodies = Vec::new(); + for i in 0..10 { + let hash = provider.client.block_header(BlockId::Number(i)).unwrap().hash(); + builder.push(Request::Body(IncompleteBodyRequest { + hash: hash.into(), + })).unwrap(); + bodies.push(Response::Body(provider.client.block_body(CompleteBodyRequest { + hash: hash, + }).unwrap())); + } let req_id = 111; + let requests = builder.build(); + let request_body = make_packet(req_id, &requests); - let request_body = encode_request(&Request::Bodies(request.clone()), req_id); let response = { - let bodies: Vec<_> = (0..10).map(|i| provider.client.block_body(BlockId::Number(i + 1)).unwrap()).collect(); - assert_eq!(bodies.len(), 10); - - let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::Bodies, 10); + let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()); let mut response_stream = RlpStream::new_list(3); - - response_stream.append(&req_id).append(&new_creds).begin_list(10); - for body in bodies { - response_stream.append_raw(&body.into_inner(), 1); - } - + response_stream.append(&req_id).append(&new_creds).append_list(&bodies); response_stream.out() }; - let expected = Expect::Respond(packet::BLOCK_BODIES, response); - proto.handle_packet(&expected, &1, packet::GET_BLOCK_BODIES, &request_body); + let expected = Expect::Respond(packet::RESPONSE, response); + proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); } #[test] @@ -359,36 +388,37 @@ fn get_block_receipts() { // find the first 10 block hashes starting with `f` because receipts are only provided // by the test client in that case. - let block_hashes: Vec<_> = (0..1000).map(|i| - provider.client.block_header(BlockId::Number(i)).unwrap().hash() - ).filter(|hash| format!("{}", hash).starts_with("f")).take(10).collect(); + let block_hashes: Vec = (0..1000) + .map(|i| provider.client.block_header(BlockId::Number(i)).unwrap().hash()) + .filter(|hash| format!("{}", hash).starts_with("f")) + .take(10) + .collect(); - let request = request::Receipts { - block_hashes: block_hashes.clone(), - }; + let mut builder = RequestBuilder::default(); + let mut receipts = Vec::new(); + for hash in block_hashes.iter().cloned() { + builder.push(Request::Receipts(IncompleteReceiptsRequest { hash: hash.into() })).unwrap(); + receipts.push(Response::Receipts(provider.client.block_receipts(CompleteReceiptsRequest { + hash: hash + }).unwrap())); + } let req_id = 111; + let requests = builder.build(); + let request_body = make_packet(req_id, &requests); - let request_body = encode_request(&Request::Receipts(request.clone()), req_id); let response = { - let receipts: Vec<_> = block_hashes.iter() - .map(|hash| provider.client.block_receipts(hash).unwrap()) - .collect(); + assert_eq!(receipts.len(), 10); - let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::Receipts, receipts.len()); + let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()); let mut response_stream = RlpStream::new_list(3); - - response_stream.append(&req_id).append(&new_creds).begin_list(receipts.len()); - for block_receipts in receipts { - response_stream.append_raw(&block_receipts, 1); - } - + response_stream.append(&req_id).append(&new_creds).append_list(&receipts); response_stream.out() }; - let expected = Expect::Respond(packet::RECEIPTS, response); - proto.handle_packet(&expected, &1, packet::GET_RECEIPTS, &request_body); + let expected = Expect::Respond(packet::RESPONSE, response); + proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); } #[test] @@ -397,8 +427,9 @@ fn get_state_proofs() { let capabilities = capabilities(); let (provider, proto) = setup(flow_params.clone(), capabilities.clone()); + let provider = TestProvider(provider); - let cur_status = status(provider.client.chain_info()); + let cur_status = status(provider.0.client.chain_info()); { let packet_body = write_handshake(&cur_status, &capabilities, Some(&flow_params)); @@ -407,40 +438,45 @@ fn get_state_proofs() { } let req_id = 112; - let key1 = U256::from(11223344).into(); - let key2 = U256::from(99988887).into(); + let key1: H256 = U256::from(11223344).into(); + let key2: H256 = U256::from(99988887).into(); - let request = Request::StateProofs (request::StateProofs { - requests: vec![ - request::StateProof { block: H256::default(), key1: key1, key2: None, from_level: 0 }, - request::StateProof { block: H256::default(), key1: key1, key2: Some(key2), from_level: 0}, - ] - }); + let mut builder = RequestBuilder::default(); + builder.push(Request::Account(IncompleteAccountRequest { + block_hash: H256::default().into(), + address_hash: key1.into(), + })).unwrap(); + builder.push(Request::Storage(IncompleteStorageRequest { + block_hash: H256::default().into(), + address_hash: key1.into(), + key_hash: key2.into(), + })).unwrap(); - let request_body = encode_request(&request, req_id); + let requests = builder.build(); + + let request_body = make_packet(req_id, &requests); let response = { - let proofs = vec![ - { let mut stream = RlpStream::new_list(2); stream.append(&key1).append_empty_data(); vec![stream.out()] }, - vec![::util::sha3::SHA3_NULL_RLP.to_vec()], + let responses = vec![ + Response::Account(provider.account_proof(CompleteAccountRequest { + block_hash: H256::default(), + address_hash: key1, + }).unwrap()), + Response::Storage(provider.storage_proof(CompleteStorageRequest { + block_hash: H256::default(), + address_hash: key1, + key_hash: key2, + }).unwrap()), ]; - let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::StateProofs, 2); + let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()); let mut response_stream = RlpStream::new_list(3); - - response_stream.append(&req_id).append(&new_creds).begin_list(2); - for proof in proofs { - response_stream.begin_list(proof.len()); - for node in proof { - response_stream.append_raw(&node, 1); - } - } - + response_stream.append(&req_id).append(&new_creds).append_list(&responses); response_stream.out() }; - let expected = Expect::Respond(packet::PROOFS, response); - proto.handle_packet(&expected, &1, packet::GET_PROOFS, &request_body); + let expected = Expect::Respond(packet::RESPONSE, response); + proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); } #[test] @@ -459,37 +495,31 @@ fn get_contract_code() { } let req_id = 112; - let key1 = U256::from(11223344).into(); - let key2 = U256::from(99988887).into(); + let key1: H256 = U256::from(11223344).into(); + let key2: H256 = U256::from(99988887).into(); - let request = Request::Codes (request::ContractCodes { - code_requests: vec![ - request::ContractCode { block_hash: H256::default(), account_key: key1 }, - request::ContractCode { block_hash: H256::default(), account_key: key2 }, - ], + let request = Request::Code(IncompleteCodeRequest { + block_hash: key1.into(), + code_hash: key2.into(), }); - let request_body = encode_request(&request, req_id); + let requests = encode_single(request.clone()); + let request_body = make_packet(req_id, &requests); let response = { - let codes: Vec> = vec![ - key1.iter().chain(key1.iter()).cloned().collect(), - key2.iter().chain(key2.iter()).cloned().collect(), - ]; + let response = vec![Response::Code(CodeResponse { + code: key1.iter().chain(key2.iter()).cloned().collect(), + })]; - let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::Codes, 2); + let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()); let mut response_stream = RlpStream::new_list(3); - response_stream.append(&req_id).append(&new_creds).begin_list(2); - for code in codes { - response_stream.append(&code); - } - + response_stream.append(&req_id).append(&new_creds).append_list(&response); response_stream.out() }; - let expected = Expect::Respond(packet::CONTRACT_CODES, response); - proto.handle_packet(&expected, &1, packet::GET_CONTRACT_CODES, &request_body); + let expected = Expect::Respond(packet::RESPONSE, response); + proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); } #[test] @@ -508,8 +538,8 @@ fn proof_of_execution() { } let req_id = 112; - let mut request = Request::TransactionProof (request::TransactionProof { - at: H256::default(), + let mut request = Request::Execution(request::IncompleteExecutionRequest { + block_hash: H256::default().into(), from: Address::default(), action: Action::Call(Address::default()), gas: 100.into(), @@ -519,9 +549,11 @@ fn proof_of_execution() { }); // first: a valid amount to request execution of. - let request_body = encode_request(&request, req_id); + let requests = encode_single(request.clone()); + let request_body = make_packet(req_id, &requests); + let response = { - let new_creds = *flow_params.limit() - flow_params.compute_cost(request::Kind::TransactionProof, 100); + let new_creds = *flow_params.limit() - flow_params.compute_cost_multi(requests.requests()); let mut response_stream = RlpStream::new_list(3); response_stream.append(&req_id).append(&new_creds).begin_list(0); @@ -529,17 +561,19 @@ fn proof_of_execution() { response_stream.out() }; - let expected = Expect::Respond(packet::TRANSACTION_PROOF, response); - proto.handle_packet(&expected, &1, packet::GET_TRANSACTION_PROOF, &request_body); + let expected = Expect::Respond(packet::RESPONSE, response); + proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); // next: way too much requested gas. - if let Request::TransactionProof(ref mut req) = request { + if let Request::Execution(ref mut req) = request { req.gas = 100_000_000.into(); } let req_id = 113; - let request_body = encode_request(&request, req_id); + let requests = encode_single(request.clone()); + let request_body = make_packet(req_id, &requests); + let expected = Expect::Punish(1); - proto.handle_packet(&expected, &1, packet::GET_TRANSACTION_PROOF, &request_body); + proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); } #[test] @@ -554,12 +588,13 @@ fn id_guard() { let req_id_1 = ReqId(5143); let req_id_2 = ReqId(1111); - let req = Request::Headers(request::Headers { - start: 5u64.into(), + + let req = encode_single(Request::Headers(IncompleteHeadersRequest { + start: HashOrNumber::Number(5u64).into(), max: 100, skip: 0, reverse: false, - }); + })); let peer_id = 9876; @@ -579,15 +614,15 @@ fn id_guard() { failed_requests: Vec::new(), })); - // first, supply wrong request type. + // first, malformed responses. { let mut stream = RlpStream::new_list(3); stream.append(&req_id_1.0); stream.append(&4_000_000usize); - stream.begin_list(0); + stream.begin_list(2).append(&125usize).append(&3usize); let packet = stream.out(); - assert!(proto.block_bodies(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_err()); + assert!(proto.response(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_err()); } // next, do an unexpected response. @@ -598,7 +633,7 @@ fn id_guard() { stream.begin_list(0); let packet = stream.out(); - assert!(proto.receipts(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_err()); + assert!(proto.response(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_err()); } // lastly, do a valid (but empty) response. @@ -609,7 +644,7 @@ fn id_guard() { stream.begin_list(0); let packet = stream.out(); - assert!(proto.block_headers(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_ok()); + assert!(proto.response(&peer_id, &Expect::Nothing, UntrustedRlp::new(&packet)).is_ok()); } let peers = proto.peers.read(); diff --git a/ethcore/light/src/on_demand/mod.rs b/ethcore/light/src/on_demand/mod.rs index 488535568..8d451c88e 100644 --- a/ethcore/light/src/on_demand/mod.rs +++ b/ethcore/light/src/on_demand/mod.rs @@ -34,12 +34,12 @@ use futures::{Async, Poll, Future}; use futures::sync::oneshot::{self, Sender, Receiver}; use network::PeerId; use rlp::RlpStream; -use util::{Bytes, DBValue, RwLock, Mutex, U256}; +use util::{Bytes, RwLock, Mutex, U256, H256}; use util::sha3::{SHA3_NULL_RLP, SHA3_EMPTY_LIST_RLP}; use net::{Handler, Status, Capabilities, Announcement, EventContext, BasicContext, ReqId}; use cache::Cache; -use types::les_request::{self as les_request, Request as LesRequest}; +use request::{self as basic_request, Request as NetworkRequest, Response as NetworkResponse}; pub mod request; @@ -49,24 +49,85 @@ struct Peer { capabilities: Capabilities, } +impl Peer { + // Whether a given peer can handle a specific request. + fn can_handle(&self, pending: &Pending) -> bool { + match *pending { + Pending::HeaderProof(ref req, _) => + self.capabilities.serve_headers && self.status.head_num > req.num(), + Pending::HeaderByHash(_, _) => self.capabilities.serve_headers, + Pending::Block(ref req, _) => + self.capabilities.serve_chain_since.as_ref().map_or(false, |x| *x >= req.header.number()), + Pending::BlockReceipts(ref req, _) => + self.capabilities.serve_chain_since.as_ref().map_or(false, |x| *x >= req.0.number()), + Pending::Account(ref req, _) => + self.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= req.header.number()), + Pending::Code(ref req, _) => + self.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= req.block_id.1), + Pending::TxProof(ref req, _) => + self.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= req.header.number()), + } + } +} + // Which portions of a CHT proof should be sent. enum ChtProofSender { - Both(Sender<(encoded::Header, U256)>), - Header(Sender), + Both(Sender<(H256, U256)>), + Hash(Sender), ChainScore(Sender), } // Attempted request info and sender to put received value. enum Pending { - HeaderByNumber(request::HeaderByNumber, ChtProofSender), + HeaderProof(request::HeaderProof, ChtProofSender), HeaderByHash(request::HeaderByHash, Sender), Block(request::Body, Sender), BlockReceipts(request::BlockReceipts, Sender>), - Account(request::Account, Sender), + Account(request::Account, Sender>), Code(request::Code, Sender), TxProof(request::TransactionProof, Sender>), } +impl Pending { + // Create a network request. + fn make_request(&self) -> NetworkRequest { + match *self { + Pending::HeaderByHash(ref req, _) => NetworkRequest::Headers(basic_request::IncompleteHeadersRequest { + start: basic_request::HashOrNumber::Hash(req.0).into(), + skip: 0, + max: 1, + reverse: false, + }), + Pending::HeaderProof(ref req, _) => NetworkRequest::HeaderProof(basic_request::IncompleteHeaderProofRequest { + num: req.num().into(), + }), + Pending::Block(ref req, _) => NetworkRequest::Body(basic_request::IncompleteBodyRequest { + hash: req.hash.into(), + }), + Pending::BlockReceipts(ref req, _) => NetworkRequest::Receipts(basic_request::IncompleteReceiptsRequest { + hash: req.0.hash().into(), + }), + Pending::Account(ref req, _) => NetworkRequest::Account(basic_request::IncompleteAccountRequest { + block_hash: req.header.hash().into(), + address_hash: ::util::Hashable::sha3(&req.address).into(), + }), + Pending::Code(ref req, _) => NetworkRequest::Code(basic_request::IncompleteCodeRequest { + block_hash: req.block_id.0.into(), + code_hash: req.code_hash.into(), + }), + Pending::TxProof(ref req, _) => NetworkRequest::Execution(basic_request::IncompleteExecutionRequest { + block_hash: req.header.hash().into(), + from: req.tx.sender(), + gas: req.tx.gas, + gas_price: req.tx.gas_price, + action: req.tx.action.clone(), + value: req.tx.value, + data: req.tx.data.clone(), + }), + } + } +} + /// On demand request service. See module docs for more details. /// Accumulates info about all peers' capabilities and dispatches /// requests to them accordingly. @@ -90,25 +151,25 @@ impl OnDemand { } } - /// Request a header by block number and CHT root hash. - /// Returns the header. - pub fn header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver { + /// Request a header's hash by block number and CHT root hash. + /// Returns the hash. + pub fn hash_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver { let (sender, receiver) = oneshot::channel(); let cached = { let mut cache = self.cache.lock(); - cache.block_hash(&req.num()).and_then(|hash| cache.block_header(&hash)) + cache.block_hash(&req.num()) }; match cached { - Some(hdr) => sender.send(hdr).expect(RECEIVER_IN_SCOPE), - None => self.dispatch_header_by_number(ctx, req, ChtProofSender::Header(sender)), + Some(hash) => sender.send(hash).expect(RECEIVER_IN_SCOPE), + None => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::Hash(sender))), } receiver } /// Request a canonical block's chain score. /// Returns the chain score. - pub fn chain_score_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver { + pub fn chain_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver { let (sender, receiver) = oneshot::channel(); let cached = { let mut cache = self.cache.lock(); @@ -117,71 +178,33 @@ impl OnDemand { match cached { Some(score) => sender.send(score).expect(RECEIVER_IN_SCOPE), - None => self.dispatch_header_by_number(ctx, req, ChtProofSender::ChainScore(sender)), + None => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::ChainScore(sender))), } receiver } - /// Request a canonical block's chain score. - /// Returns the header and chain score. - pub fn header_and_score_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber) -> Receiver<(encoded::Header, U256)> { + /// Request a canonical block's hash and chain score by number. + /// Returns the hash and chain score. + pub fn hash_and_score_by_number(&self, ctx: &BasicContext, req: request::HeaderProof) -> Receiver<(H256, U256)> { let (sender, receiver) = oneshot::channel(); let cached = { let mut cache = self.cache.lock(); let hash = cache.block_hash(&req.num()); ( - hash.clone().and_then(|hash| cache.block_header(&hash)), + hash.clone(), hash.and_then(|hash| cache.chain_score(&hash)), ) }; match cached { - (Some(hdr), Some(score)) => sender.send((hdr, score)).expect(RECEIVER_IN_SCOPE), - _ => self.dispatch_header_by_number(ctx, req, ChtProofSender::Both(sender)), + (Some(hash), Some(score)) => sender.send((hash, score)).expect(RECEIVER_IN_SCOPE), + _ => self.dispatch(ctx, Pending::HeaderProof(req, ChtProofSender::Both(sender))), } receiver } - // dispatch the request, completing the request if no peers available. - fn dispatch_header_by_number(&self, ctx: &BasicContext, req: request::HeaderByNumber, sender: ChtProofSender) { - let num = req.num(); - let cht_num = req.cht_num(); - - let les_req = LesRequest::HeaderProofs(les_request::HeaderProofs { - requests: vec![les_request::HeaderProof { - cht_number: cht_num, - block_number: num, - from_level: 0, - }], - }); - - let pending = Pending::HeaderByNumber(req, sender); - - // we're looking for a peer with serveHeaders who's far enough along in the - // chain. - for (id, peer) in self.peers.read().iter() { - if peer.capabilities.serve_headers && peer.status.head_num >= num { - match ctx.request_from(*id, les_req.clone()) { - Ok(req_id) => { - trace!(target: "on_demand", "Assigning request to peer {}", id); - self.pending_requests.write().insert( - req_id, - pending, - ); - return - }, - Err(e) => - trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e), - } - } - } - - trace!(target: "on_demand", "No suitable peer for request"); - self.orphaned_requests.write().push(pending) - } - /// Request a header by hash. This is less accurate than by-number because we don't know /// where in the chain this header lies, and therefore can't find a peer who is supposed to have /// it as easily. @@ -189,50 +212,11 @@ impl OnDemand { let (sender, receiver) = oneshot::channel(); match self.cache.lock().block_header(&req.0) { Some(hdr) => sender.send(hdr).expect(RECEIVER_IN_SCOPE), - None => self.dispatch_header_by_hash(ctx, req, sender), + None => self.dispatch(ctx, Pending::HeaderByHash(req, sender)), } receiver } - fn dispatch_header_by_hash(&self, ctx: &BasicContext, req: request::HeaderByHash, sender: Sender) { - let les_req = LesRequest::Headers(les_request::Headers { - start: req.0.into(), - max: 1, - skip: 0, - reverse: false, - }); - - // all we've got is a hash, so we'll just guess at peers who might have - // it randomly. - let mut potential_peers = self.peers.read().iter() - .filter(|&(_, peer)| peer.capabilities.serve_headers) - .map(|(id, _)| *id) - .collect::>(); - - let mut rng = ::rand::thread_rng(); - ::rand::Rng::shuffle(&mut rng, &mut potential_peers); - - let pending = Pending::HeaderByHash(req, sender); - - for id in potential_peers { - match ctx.request_from(id, les_req.clone()) { - Ok(req_id) => { - trace!(target: "on_demand", "Assigning request to peer {}", id); - self.pending_requests.write().insert( - req_id, - pending, - ); - return - } - Err(e) => - trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e), - } - } - - trace!(target: "on_demand", "No suitable peer for request"); - self.orphaned_requests.write().push(pending) - } - /// Request a block, given its header. Block bodies are requestable by hash only, /// and the header is required anyway to verify and complete the block body /// -- this just doesn't obscure the network query. @@ -246,7 +230,7 @@ impl OnDemand { stream.begin_list(0); stream.begin_list(0); - sender.send(encoded::Block::new(stream.out())).expect(RECEIVER_IN_SCOPE) + sender.send(encoded::Block::new(stream.out())).expect(RECEIVER_IN_SCOPE); } else { match self.cache.lock().block_body(&req.hash) { Some(body) => { @@ -254,43 +238,14 @@ impl OnDemand { stream.append_raw(&req.header.into_inner(), 1); stream.append_raw(&body.into_inner(), 2); - sender.complete(encoded::Block::new(stream.out())); + sender.send(encoded::Block::new(stream.out())).expect(RECEIVER_IN_SCOPE); } - None => self.dispatch_block(ctx, req, sender), + None => self.dispatch(ctx, Pending::Block(req, sender)), } } receiver } - fn dispatch_block(&self, ctx: &BasicContext, req: request::Body, sender: Sender) { - let num = req.header.number(); - let les_req = LesRequest::Bodies(les_request::Bodies { - block_hashes: vec![req.hash], - }); - let pending = Pending::Block(req, sender); - - // we're looking for a peer with serveChainSince(num) - for (id, peer) in self.peers.read().iter() { - if peer.capabilities.serve_chain_since.as_ref().map_or(false, |x| *x >= num) { - match ctx.request_from(*id, les_req.clone()) { - Ok(req_id) => { - trace!(target: "on_demand", "Assigning request to peer {}", id); - self.pending_requests.write().insert( - req_id, - pending, - ); - return - } - Err(e) => - trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e), - } - } - } - - trace!(target: "on_demand", "No suitable peer for request"); - self.orphaned_requests.write().push(pending) - } - /// Request the receipts for a block. The header serves two purposes: /// provide the block hash to fetch receipts for, and for verification of the receipts root. pub fn block_receipts(&self, ctx: &BasicContext, req: request::BlockReceipts) -> Receiver> { @@ -298,88 +253,25 @@ impl OnDemand { // fast path for empty receipts. if req.0.receipts_root() == SHA3_NULL_RLP { - sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE) + sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE); } else { match self.cache.lock().block_receipts(&req.0.hash()) { Some(receipts) => sender.send(receipts).expect(RECEIVER_IN_SCOPE), - None => self.dispatch_block_receipts(ctx, req, sender), + None => self.dispatch(ctx, Pending::BlockReceipts(req, sender)), } } receiver } - fn dispatch_block_receipts(&self, ctx: &BasicContext, req: request::BlockReceipts, sender: Sender>) { - let num = req.0.number(); - let les_req = LesRequest::Receipts(les_request::Receipts { - block_hashes: vec![req.0.hash()], - }); - let pending = Pending::BlockReceipts(req, sender); - - // we're looking for a peer with serveChainSince(num) - for (id, peer) in self.peers.read().iter() { - if peer.capabilities.serve_chain_since.as_ref().map_or(false, |x| *x >= num) { - match ctx.request_from(*id, les_req.clone()) { - Ok(req_id) => { - trace!(target: "on_demand", "Assigning request to peer {}", id); - self.pending_requests.write().insert( - req_id, - pending, - ); - return - } - Err(e) => - trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e), - } - } - } - - trace!(target: "on_demand", "No suitable peer for request"); - self.orphaned_requests.write().push(pending) - } - /// Request an account by address and block header -- which gives a hash to query and a state root /// to verify against. - pub fn account(&self, ctx: &BasicContext, req: request::Account) -> Receiver { + pub fn account(&self, ctx: &BasicContext, req: request::Account) -> Receiver> { let (sender, receiver) = oneshot::channel(); - self.dispatch_account(ctx, req, sender); + self.dispatch(ctx, Pending::Account(req, sender)); receiver } - fn dispatch_account(&self, ctx: &BasicContext, req: request::Account, sender: Sender) { - let num = req.header.number(); - let les_req = LesRequest::StateProofs(les_request::StateProofs { - requests: vec![les_request::StateProof { - block: req.header.hash(), - key1: ::util::Hashable::sha3(&req.address), - key2: None, - from_level: 0, - }], - }); - let pending = Pending::Account(req, sender); - - // we're looking for a peer with serveStateSince(num) - for (id, peer) in self.peers.read().iter() { - if peer.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= num) { - match ctx.request_from(*id, les_req.clone()) { - Ok(req_id) => { - trace!(target: "on_demand", "Assigning request to peer {}", id); - self.pending_requests.write().insert( - req_id, - pending, - ); - return - } - Err(e) => - trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e), - } - } - } - - trace!(target: "on_demand", "No suitable peer for request"); - self.orphaned_requests.write().push(pending) - } - /// Request code by address, known code hash, and block header. pub fn code(&self, ctx: &BasicContext, req: request::Code) -> Receiver { let (sender, receiver) = oneshot::channel(); @@ -388,88 +280,50 @@ impl OnDemand { if req.code_hash == ::util::sha3::SHA3_EMPTY { sender.send(Vec::new()).expect(RECEIVER_IN_SCOPE) } else { - self.dispatch_code(ctx, req, sender); + self.dispatch(ctx, Pending::Code(req, sender)); } receiver } - fn dispatch_code(&self, ctx: &BasicContext, req: request::Code, sender: Sender) { - let num = req.block_id.1; - let les_req = LesRequest::Codes(les_request::ContractCodes { - code_requests: vec![les_request::ContractCode { - block_hash: req.block_id.0, - account_key: ::util::Hashable::sha3(&req.address), - }] - }); - let pending = Pending::Code(req, sender); - - // we're looking for a peer with serveStateSince(num) - for (id, peer) in self.peers.read().iter() { - if peer.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= num) { - match ctx.request_from(*id, les_req.clone()) { - Ok(req_id) => { - trace!(target: "on_demand", "Assigning request to peer {}", id); - self.pending_requests.write().insert( - req_id, - pending - ); - return - } - Err(e) => - trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e), - } - } - } - - trace!(target: "on_demand", "No suitable peer for request"); - self.orphaned_requests.write().push(pending) - } - /// Request proof-of-execution for a transaction. pub fn transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof) -> Receiver> { let (sender, receiver) = oneshot::channel(); - self.dispatch_transaction_proof(ctx, req, sender); + self.dispatch(ctx, Pending::TxProof(req, sender)); receiver } - fn dispatch_transaction_proof(&self, ctx: &BasicContext, req: request::TransactionProof, sender: Sender>) { - let num = req.header.number(); - let les_req = LesRequest::TransactionProof(les_request::TransactionProof { - at: req.header.hash(), - from: req.tx.sender(), - gas: req.tx.gas, - gas_price: req.tx.gas_price, - action: req.tx.action.clone(), - value: req.tx.value, - data: req.tx.data.clone(), - }); - let pending = Pending::TxProof(req, sender); + // dispatch the request, with a "suitability" function to filter acceptable peers. + fn dispatch(&self, ctx: &BasicContext, pending: Pending) { + let mut builder = basic_request::RequestBuilder::default(); + builder.push(pending.make_request()) + .expect("make_request always returns fully complete request; qed"); + + let complete = builder.build(); - // we're looking for a peer with serveStateSince(num) for (id, peer) in self.peers.read().iter() { - if peer.capabilities.serve_state_since.as_ref().map_or(false, |x| *x >= num) { - match ctx.request_from(*id, les_req.clone()) { - Ok(req_id) => { - trace!(target: "on_demand", "Assigning request to peer {}", id); - self.pending_requests.write().insert( - req_id, - pending - ); - return - } - Err(e) => - trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e), + if !peer.can_handle(&pending) { continue } + match ctx.request_from(*id, complete.clone()) { + Ok(req_id) => { + trace!(target: "on_demand", "Assigning request to peer {}", id); + self.pending_requests.write().insert( + req_id, + pending, + ); + return } + Err(e) => + trace!(target: "on_demand", "Failed to make request of peer {}: {:?}", id, e), } } trace!(target: "on_demand", "No suitable peer for request"); - self.orphaned_requests.write().push(pending) + self.orphaned_requests.write().push(pending); } + // dispatch orphaned requests, and discard those for which the corresponding // receiver has been dropped. fn dispatch_orphaned(&self, ctx: &BasicContext) { @@ -499,30 +353,22 @@ impl OnDemand { let to_dispatch = ::std::mem::replace(&mut *self.orphaned_requests.write(), Vec::new()); - for orphaned in to_dispatch { - match orphaned { - Pending::HeaderByNumber(req, mut sender) => { - let hangup = match sender { + for mut orphaned in to_dispatch { + let hung_up = match orphaned { + Pending::HeaderProof(_, ref mut sender) => match *sender { ChtProofSender::Both(ref mut s) => check_hangup(s), - ChtProofSender::Header(ref mut s) => check_hangup(s), + ChtProofSender::Hash(ref mut s) => check_hangup(s), ChtProofSender::ChainScore(ref mut s) => check_hangup(s), - }; + }, + Pending::HeaderByHash(_, ref mut sender) => check_hangup(sender), + Pending::Block(_, ref mut sender) => check_hangup(sender), + Pending::BlockReceipts(_, ref mut sender) => check_hangup(sender), + Pending::Account(_, ref mut sender) => check_hangup(sender), + Pending::Code(_, ref mut sender) => check_hangup(sender), + Pending::TxProof(_, ref mut sender) => check_hangup(sender), + }; - if !hangup { self.dispatch_header_by_number(ctx, req, sender) } - } - Pending::HeaderByHash(req, mut sender) => - if !check_hangup(&mut sender) { self.dispatch_header_by_hash(ctx, req, sender) }, - Pending::Block(req, mut sender) => - if !check_hangup(&mut sender) { self.dispatch_block(ctx, req, sender) }, - Pending::BlockReceipts(req, mut sender) => - if !check_hangup(&mut sender) { self.dispatch_block_receipts(ctx, req, sender) }, - Pending::Account(req, mut sender) => - if !check_hangup(&mut sender) { self.dispatch_account(ctx, req, sender) }, - Pending::Code(req, mut sender) => - if !check_hangup(&mut sender) { self.dispatch_code(ctx, req, sender) }, - Pending::TxProof(req, mut sender) => - if !check_hangup(&mut sender) { self.dispatch_transaction_proof(ctx, req, sender) } - } + if !hung_up { self.dispatch(ctx, orphaned) } } } } @@ -560,218 +406,126 @@ impl Handler for OnDemand { self.dispatch_orphaned(ctx.as_basic()); } - fn on_header_proofs(&self, ctx: &EventContext, req_id: ReqId, proofs: &[(Bytes, Vec)]) { + fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[basic_request::Response]) { let peer = ctx.peer(); let req = match self.pending_requests.write().remove(&req_id) { Some(req) => req, None => return, }; + let response = match responses.get(0) { + Some(response) => response, + None => { + trace!(target: "on_demand", "Ignoring empty response for request {}", req_id); + self.dispatch(ctx.as_basic(), req); + return; + } + }; + + // handle the response appropriately for the request. + // all branches which do not return early lead to disabling of the peer + // due to misbehavior. match req { - Pending::HeaderByNumber(req, sender) => { - if let Some(&(ref header, ref proof)) = proofs.get(0) { - match req.check_response(header, proof) { - Ok((header, score)) => { + Pending::HeaderProof(req, sender) => { + if let NetworkResponse::HeaderProof(ref response) = *response { + match req.check_response(&response.proof) { + Ok((hash, score)) => { let mut cache = self.cache.lock(); - let hash = header.hash(); - cache.insert_block_header(hash, header.clone()); - cache.insert_block_hash(header.number(), hash); + cache.insert_block_hash(req.num(), hash); cache.insert_chain_score(hash, score); match sender { - ChtProofSender::Both(sender) => sender.complete((header, score)), - ChtProofSender::Header(sender) => sender.complete(header), - ChtProofSender::ChainScore(sender) => sender.complete(score), + ChtProofSender::Both(sender) => { let _ = sender.send((hash, score)); } + ChtProofSender::Hash(sender) => { let _ = sender.send(hash); } + ChtProofSender::ChainScore(sender) => { let _ = sender.send(score); } } - return } - Err(e) => { - warn!("Error handling response for header request: {:?}", e); - ctx.disable_peer(peer); - } + Err(e) => warn!("Error handling response for header request: {:?}", e), } } - - self.dispatch_header_by_number(ctx.as_basic(), req, sender); } - _ => panic!("Only header by number request fetches header proofs; qed"), - } - } - - fn on_block_headers(&self, ctx: &EventContext, req_id: ReqId, headers: &[Bytes]) { - let peer = ctx.peer(); - let req = match self.pending_requests.write().remove(&req_id) { - Some(req) => req, - None => return, - }; - - match req { Pending::HeaderByHash(req, sender) => { - if let Some(ref header) = headers.get(0) { - match req.check_response(header) { - Ok(header) => { - self.cache.lock().insert_block_header(req.0, header.clone()); - sender.complete(header); - return - } - Err(e) => { - warn!("Error handling response for header request: {:?}", e); - ctx.disable_peer(peer); + if let NetworkResponse::Headers(ref response) = *response { + if let Some(header) = response.headers.get(0) { + match req.check_response(header) { + Ok(header) => { + self.cache.lock().insert_block_header(req.0, header.clone()); + let _ = sender.send(header); + return + } + Err(e) => warn!("Error handling response for header request: {:?}", e), } } } - - self.dispatch_header_by_hash(ctx.as_basic(), req, sender); } - _ => panic!("Only header by hash request fetches headers; qed"), - } - } - - fn on_block_bodies(&self, ctx: &EventContext, req_id: ReqId, bodies: &[Bytes]) { - let peer = ctx.peer(); - let req = match self.pending_requests.write().remove(&req_id) { - Some(req) => req, - None => return, - }; - - match req { Pending::Block(req, sender) => { - if let Some(ref body) = bodies.get(0) { - match req.check_response(body) { + if let NetworkResponse::Body(ref response) = *response { + match req.check_response(&response.body) { Ok(block) => { - let body = encoded::Body::new(body.to_vec()); - self.cache.lock().insert_block_body(req.hash, body); - sender.complete(block); + self.cache.lock().insert_block_body(req.hash, response.body.clone()); + let _ = sender.send(block); return } - Err(e) => { - warn!("Error handling response for block request: {:?}", e); - ctx.disable_peer(peer); - } + Err(e) => warn!("Error handling response for block request: {:?}", e), } } - - self.dispatch_block(ctx.as_basic(), req, sender); } - _ => panic!("Only block request fetches bodies; qed"), - } - } - - fn on_receipts(&self, ctx: &EventContext, req_id: ReqId, receipts: &[Vec]) { - let peer = ctx.peer(); - let req = match self.pending_requests.write().remove(&req_id) { - Some(req) => req, - None => return, - }; - - match req { Pending::BlockReceipts(req, sender) => { - if let Some(ref receipts) = receipts.get(0) { - match req.check_response(receipts) { + if let NetworkResponse::Receipts(ref response) = *response { + match req.check_response(&response.receipts) { Ok(receipts) => { let hash = req.0.hash(); self.cache.lock().insert_block_receipts(hash, receipts.clone()); - sender.complete(receipts); + let _ = sender.send(receipts); return } - Err(e) => { - warn!("Error handling response for receipts request: {:?}", e); - ctx.disable_peer(peer); - } + Err(e) => warn!("Error handling response for receipts request: {:?}", e), } } - - self.dispatch_block_receipts(ctx.as_basic(), req, sender); } - _ => panic!("Only receipts request fetches receipts; qed"), - } - } - - fn on_state_proofs(&self, ctx: &EventContext, req_id: ReqId, proofs: &[Vec]) { - let peer = ctx.peer(); - let req = match self.pending_requests.write().remove(&req_id) { - Some(req) => req, - None => return, - }; - - match req { Pending::Account(req, sender) => { - if let Some(ref proof) = proofs.get(0) { - match req.check_response(proof) { - Ok(proof) => { - sender.complete(proof); + if let NetworkResponse::Account(ref response) = *response { + match req.check_response(&response.proof) { + Ok(maybe_account) => { + // TODO: validate against request outputs. + // needs engine + env info as part of request. + let _ = sender.send(maybe_account); return } - Err(e) => { - warn!("Error handling response for state request: {:?}", e); - ctx.disable_peer(peer); - } + Err(e) => warn!("Error handling response for state request: {:?}", e), } } - - self.dispatch_account(ctx.as_basic(), req, sender); } - _ => panic!("Only account request fetches state proof; qed"), - } - } - - fn on_code(&self, ctx: &EventContext, req_id: ReqId, codes: &[Bytes]) { - let peer = ctx.peer(); - let req = match self.pending_requests.write().remove(&req_id) { - Some(req) => req, - None => return, - }; - - match req { Pending::Code(req, sender) => { - if let Some(code) = codes.get(0) { - match req.check_response(code.as_slice()) { + if let NetworkResponse::Code(ref response) = *response { + match req.check_response(response.code.as_slice()) { Ok(()) => { - sender.complete(code.clone()); + let _ = sender.send(response.code.clone()); return } - Err(e) => { - warn!("Error handling response for code request: {:?}", e); - ctx.disable_peer(peer); - } + Err(e) => warn!("Error handling response for code request: {:?}", e), } - - self.dispatch_code(ctx.as_basic(), req, sender); } } - _ => panic!("Only code request fetches code; qed"), - } - } - - fn on_transaction_proof(&self, ctx: &EventContext, req_id: ReqId, items: &[DBValue]) { - let peer = ctx.peer(); - let req = match self.pending_requests.write().remove(&req_id) { - Some(req) => req, - None => return, - }; - - match req { Pending::TxProof(req, sender) => { - match req.check_response(items) { - ProvedExecution::Complete(executed) => { - sender.complete(Ok(executed)); - return - } - ProvedExecution::Failed(err) => { - sender.complete(Err(err)); - return - } - ProvedExecution::BadProof => { - warn!("Error handling response for transaction proof request"); - ctx.disable_peer(peer); + if let NetworkResponse::Execution(ref response) = *response { + match req.check_response(&response.items) { + ProvedExecution::Complete(executed) => { + let _ = sender.send(Ok(executed)); + return + } + ProvedExecution::Failed(err) => { + let _ = sender.send(Err(err)); + return + } + ProvedExecution::BadProof => warn!("Error handling response for transaction proof request"), } } - - self.dispatch_transaction_proof(ctx.as_basic(), req, sender); } - _ => panic!("Only transaction proof request dispatches transaction proof requests; qed"), } + + ctx.disable_peer(peer); } fn tick(&self, ctx: &BasicContext) { @@ -787,7 +541,7 @@ mod tests { use cache::Cache; use net::{Announcement, BasicContext, ReqId, Error as LesError}; - use request::{Request as LesRequest, Kind as LesRequestKind}; + use request::Requests; use network::{PeerId, NodeId}; use time::Duration; @@ -797,11 +551,10 @@ mod tests { impl BasicContext for FakeContext { fn persistent_peer_id(&self, _: PeerId) -> Option { None } - fn request_from(&self, _: PeerId, _: LesRequest) -> Result { + fn request_from(&self, _: PeerId, _: Requests) -> Result { unimplemented!() } fn make_announcement(&self, _: Announcement) { } - fn max_requests(&self, _: PeerId, _: LesRequestKind) -> usize { 0 } fn disconnect_peer(&self, _: PeerId) { } fn disable_peer(&self, _: PeerId) { } } diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index 1db796982..cda1d6feb 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -61,9 +61,9 @@ impl From> for Error { } } -/// Request for a header by number. +/// Request for header proof by number #[derive(Debug, Clone, PartialEq, Eq)] -pub struct HeaderByNumber { +pub struct HeaderProof { /// The header's number. num: u64, /// The cht number for the given block number. @@ -72,11 +72,11 @@ pub struct HeaderByNumber { cht_root: H256, } -impl HeaderByNumber { +impl HeaderProof { /// Construct a new header-by-number request. Fails if the given number is 0. /// Provide the expected CHT root to compare against. pub fn new(num: u64, cht_root: H256) -> Option { - ::cht::block_to_cht_number(num).map(|cht_num| HeaderByNumber { + ::cht::block_to_cht_number(num).map(|cht_num| HeaderProof { num: num, cht_num: cht_num, cht_root: cht_root, @@ -92,18 +92,11 @@ impl HeaderByNumber { /// Access the expected CHT root. pub fn cht_root(&self) -> H256 { self.cht_root } - /// Check a response with a header and cht proof. - pub fn check_response(&self, header: &[u8], proof: &[Bytes]) -> Result<(encoded::Header, U256), Error> { - let (expected_hash, td) = match ::cht::check_proof(proof, self.num, self.cht_root) { - Some((expected_hash, td)) => (expected_hash, td), - None => return Err(Error::BadProof), - }; - - // and compare the hash to the found header. - let found_hash = header.sha3(); - match expected_hash == found_hash { - true => Ok((encoded::Header::new(header.to_vec()), td)), - false => Err(Error::WrongHash(expected_hash, found_hash)), + /// Check a response with a CHT proof, get a hash and total difficulty back. + pub fn check_response(&self, proof: &[Bytes]) -> Result<(H256, U256), Error> { + match ::cht::check_proof(proof, self.num, self.cht_root) { + Some((expected_hash, td)) => Ok((expected_hash, td)), + None => Err(Error::BadProof), } } } @@ -114,10 +107,10 @@ pub struct HeaderByHash(pub H256); impl HeaderByHash { /// Check a response for the header. - pub fn check_response(&self, header: &[u8]) -> Result { + pub fn check_response(&self, header: &encoded::Header) -> Result { let hash = header.sha3(); match hash == self.0 { - true => Ok(encoded::Header::new(header.to_vec())), + true => Ok(header.clone()), false => Err(Error::WrongHash(self.0, hash)), } } @@ -143,16 +136,14 @@ impl Body { } /// Check a response for this block body. - pub fn check_response(&self, body: &[u8]) -> Result { - let body_view = UntrustedRlp::new(&body); - + pub fn check_response(&self, body: &encoded::Body) -> Result { // check the integrity of the the body against the header - let tx_root = ::util::triehash::ordered_trie_root(body_view.at(0)?.iter().map(|r| r.as_raw().to_vec())); + let tx_root = ::util::triehash::ordered_trie_root(body.rlp().at(0).iter().map(|r| r.as_raw().to_vec())); if tx_root != self.header.transactions_root() { return Err(Error::WrongTrieRoot(self.header.transactions_root(), tx_root)); } - let uncles_hash = body_view.at(1)?.as_raw().sha3(); + let uncles_hash = body.rlp().at(1).as_raw().sha3(); if uncles_hash != self.header.uncles_hash() { return Err(Error::WrongHash(self.header.uncles_hash(), uncles_hash)); } @@ -160,7 +151,7 @@ impl Body { // concatenate the header and the body. let mut stream = RlpStream::new_list(3); stream.append_raw(self.header.rlp().as_raw(), 1); - stream.append_raw(body, 2); + stream.append_raw(&body.rlp().as_raw(), 2); Ok(encoded::Block::new(stream.out())) } @@ -194,7 +185,7 @@ pub struct Account { impl Account { /// Check a response with an account against the stored header. - pub fn check_response(&self, proof: &[Bytes]) -> Result { + pub fn check_response(&self, proof: &[Bytes]) -> Result, Error> { let state_root = self.header.state_root(); let mut db = MemoryDB::new(); @@ -203,14 +194,14 @@ impl Account { match TrieDB::new(&db, &state_root).and_then(|t| t.get(&self.address.sha3()))? { Some(val) => { let rlp = UntrustedRlp::new(&val); - Ok(BasicAccount { + Ok(Some(BasicAccount { nonce: rlp.val_at(0)?, balance: rlp.val_at(1)?, storage_root: rlp.val_at(2)?, code_hash: rlp.val_at(3)?, - }) + })) }, - None => Err(Error::BadProof) + None => Ok(None), } } } @@ -219,8 +210,6 @@ impl Account { pub struct Code { /// Block hash, number pair. pub block_id: (H256, u64), - /// Address requested. - pub address: Address, /// Account's code hash. pub code_hash: H256, } @@ -278,11 +267,11 @@ mod tests { #[test] fn no_invalid_header_by_number() { - assert!(HeaderByNumber::new(0, Default::default()).is_none()) + assert!(HeaderProof::new(0, Default::default()).is_none()) } #[test] - fn check_header_by_number() { + fn check_header_proof() { use ::cht; let test_client = TestBlockChainClient::new(); @@ -303,11 +292,9 @@ mod tests { }; let proof = cht.prove(10_000, 0).unwrap().unwrap(); - let req = HeaderByNumber::new(10_000, cht.root()).unwrap(); + let req = HeaderProof::new(10_000, cht.root()).unwrap(); - let raw_header = test_client.block_header(::ethcore::ids::BlockId::Number(10_000)).unwrap(); - - assert!(req.check_response(&raw_header.into_inner(), &proof[..]).is_ok()); + assert!(req.check_response(&proof[..]).is_ok()); } #[test] @@ -316,9 +303,9 @@ mod tests { header.set_number(10_000); header.set_extra_data(b"test_header".to_vec()); let hash = header.hash(); - let raw_header = ::rlp::encode(&header); + let raw_header = encoded::Header::new(::rlp::encode(&header).to_vec()); - assert!(HeaderByHash(hash).check_response(&*raw_header).is_ok()) + assert!(HeaderByHash(hash).check_response(&raw_header).is_ok()) } #[test] @@ -334,7 +321,8 @@ mod tests { hash: header.hash(), }; - assert!(req.check_response(&*body_stream.drain()).is_ok()) + let response = encoded::Body::new(body_stream.drain().to_vec()); + assert!(req.check_response(&response).is_ok()) } #[test] @@ -412,7 +400,6 @@ mod tests { let code = vec![1u8; 256]; let req = Code { block_id: (Default::default(), 2), - address: Default::default(), code_hash: ::util::Hashable::sha3(&code), }; diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index d9f3937da..e74ab1c70 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -24,22 +24,15 @@ use ethcore::client::{BlockChainClient, ProvingBlockChainClient}; use ethcore::transaction::PendingTransaction; use ethcore::ids::BlockId; use ethcore::encoded; -use util::{Bytes, DBValue, RwLock, H256}; +use util::{RwLock, H256}; use cht::{self, BlockInfo}; use client::{LightChainClient, AsLightClient}; use transaction_queue::TransactionQueue; - use request; -/// Defines the operations that a provider for `LES` must fulfill. -/// -/// These are defined at [1], but may be subject to change. -/// Requests which can't be fulfilled should return either an empty RLP list -/// or empty vector where appropriate. -/// -/// [1]: https://github.com/ethcore/parity/wiki/Light-Ethereum-Subprotocol-(LES) +/// Defines the operations that a provider for the light subprotocol must fulfill. #[cfg_attr(feature = "ipc", ipc(client_ident="LightProviderClient"))] pub trait Provider: Send + Sync { /// Provide current blockchain info. @@ -59,18 +52,18 @@ pub trait Provider: Send + Sync { /// /// The returned vector may have any length in the range [0, `max`], but the /// results within must adhere to the `skip` and `reverse` parameters. - fn block_headers(&self, req: request::Headers) -> Vec { + fn block_headers(&self, req: request::CompleteHeadersRequest) -> Option { use request::HashOrNumber; - if req.max == 0 { return Vec::new() } + if req.max == 0 { return None } let best_num = self.chain_info().best_block_number; let start_num = match req.start { HashOrNumber::Number(start_num) => start_num, HashOrNumber::Hash(hash) => match self.block_header(BlockId::Hash(hash)) { None => { - trace!(target: "les_provider", "Unknown block hash {} requested", hash); - return Vec::new(); + trace!(target: "pip_provider", "Unknown block hash {} requested", hash); + return None; } Some(header) => { let num = header.number(); @@ -79,7 +72,9 @@ pub trait Provider: Send + Sync { if req.max == 1 || canon_hash != Some(hash) { // Non-canonical header or single header requested. - return vec![header]; + return Some(::request::HeadersResponse { + headers: vec![header], + }) } num @@ -87,116 +82,50 @@ pub trait Provider: Send + Sync { } }; - (0u64..req.max as u64) + let headers: Vec<_> = (0u64..req.max as u64) .map(|x: u64| x.saturating_mul(req.skip + 1)) .take_while(|x| if req.reverse { x < &start_num } else { best_num.saturating_sub(start_num) >= *x }) .map(|x| if req.reverse { start_num - x } else { start_num + x }) .map(|x| self.block_header(BlockId::Number(x))) .take_while(|x| x.is_some()) .flat_map(|x| x) - .collect() + .collect(); + + if headers.is_empty() { + None + } else { + Some(::request::HeadersResponse { headers: headers }) + } } /// Get a block header by id. fn block_header(&self, id: BlockId) -> Option; - /// Provide as many as possible of the requested blocks (minus the headers) encoded - /// in RLP format. - fn block_bodies(&self, req: request::Bodies) -> Vec> { - req.block_hashes.into_iter() - .map(|hash| self.block_body(BlockId::Hash(hash))) - .collect() - } + /// Fulfill a block body request. + fn block_body(&self, req: request::CompleteBodyRequest) -> Option; - /// Get a block body by id. - fn block_body(&self, id: BlockId) -> Option; + /// Fulfill a request for block receipts. + fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option; - /// Provide the receipts as many as possible of the requested blocks. - /// Returns a vector of RLP-encoded lists of receipts. - fn receipts(&self, req: request::Receipts) -> Vec { - req.block_hashes.into_iter() - .map(|hash| self.block_receipts(&hash)) - .map(|receipts| receipts.unwrap_or_else(|| ::rlp::EMPTY_LIST_RLP.to_vec())) - .collect() - } + /// Get an account proof. + fn account_proof(&self, req: request::CompleteAccountRequest) -> Option; - /// Get a block's receipts as an RLP-encoded list by block hash. - fn block_receipts(&self, hash: &H256) -> Option; + /// Get a storage proof. + fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option; - /// Provide a set of merkle proofs, as requested. Each request is a - /// block hash and request parameters. - /// - /// Returns a vector of RLP-encoded lists satisfying the requests. - fn proofs(&self, req: request::StateProofs) -> Vec { - use rlp::RlpStream; - - let mut results = Vec::with_capacity(req.requests.len()); - - for request in req.requests { - let proof = self.state_proof(request); - - let mut stream = RlpStream::new_list(proof.len()); - for node in proof { - stream.append_raw(&node, 1); - } - - results.push(stream.out()); - } - - results - } - - /// Get a state proof from a request. Each proof should be a vector - /// of rlp-encoded trie nodes, in ascending order by distance from the root. - fn state_proof(&self, req: request::StateProof) -> Vec; - - /// Provide contract code for the specified (block_hash, account_hash) pairs. - /// Each item in the resulting vector is either the raw bytecode or empty. - fn contract_codes(&self, req: request::ContractCodes) -> Vec { - req.code_requests.into_iter() - .map(|req| self.contract_code(req)) - .collect() - } - - /// Get contract code by request. Either the raw bytecode or empty. - fn contract_code(&self, req: request::ContractCode) -> Bytes; - - /// Provide header proofs from the Canonical Hash Tries as well as the headers - /// they correspond to -- each element in the returned vector is a 2-tuple. - /// The first element is a block header and the second a merkle proof of - /// the header in a requested CHT. - fn header_proofs(&self, req: request::HeaderProofs) -> Vec { - use rlp::{self, RlpStream}; - - req.requests.into_iter() - .map(|req| self.header_proof(req)) - .map(|maybe_proof| match maybe_proof { - None => rlp::EMPTY_LIST_RLP.to_vec(), - Some((header, proof)) => { - let mut stream = RlpStream::new_list(2); - stream.append_raw(&header.into_inner(), 1).begin_list(proof.len()); - - for node in proof { - stream.append_raw(&node, 1); - } - - stream.out() - } - }) - .collect() - } + /// Provide contract code for the specified (block_hash, code_hash) pair. + fn contract_code(&self, req: request::CompleteCodeRequest) -> Option; /// Provide a header proof from a given Canonical Hash Trie as well as the - /// corresponding header. The first element is the block header and the - /// second is a merkle proof of the CHT. - fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec)>; + /// corresponding header. + fn header_proof(&self, req: request::CompleteHeaderProofRequest) -> Option; /// Provide pending transactions. fn ready_transactions(&self) -> Vec; /// Provide a proof-of-execution for the given transaction proof request. /// Returns a vector of all state items necessary to execute the transaction. - fn transaction_proof(&self, req: request::TransactionProof) -> Option>; + fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option; } // Implementation of a light client data provider for a client. @@ -217,32 +146,52 @@ impl Provider for T { BlockChainClient::block_header(self, id) } - fn block_body(&self, id: BlockId) -> Option { - BlockChainClient::block_body(self, id) + fn block_body(&self, req: request::CompleteBodyRequest) -> Option { + BlockChainClient::block_body(self, BlockId::Hash(req.hash)) + .map(|body| ::request::BodyResponse { body: body }) } - fn block_receipts(&self, hash: &H256) -> Option { - BlockChainClient::block_receipts(self, hash) + fn block_receipts(&self, req: request::CompleteReceiptsRequest) -> Option { + BlockChainClient::block_receipts(self, &req.hash) + .map(|x| ::request::ReceiptsResponse { receipts: ::rlp::decode_list(&x) }) } - fn state_proof(&self, req: request::StateProof) -> Vec { - match req.key2 { - Some(key2) => self.prove_storage(req.key1, key2, req.from_level, BlockId::Hash(req.block)), - None => self.prove_account(req.key1, req.from_level, BlockId::Hash(req.block)), - } + fn account_proof(&self, req: request::CompleteAccountRequest) -> Option { + self.prove_account(req.address_hash, BlockId::Hash(req.block_hash)).map(|(proof, acc)| { + ::request::AccountResponse { + proof: proof, + nonce: acc.nonce, + balance: acc.balance, + code_hash: acc.code_hash, + storage_root: acc.storage_root, + } + }) } - fn contract_code(&self, req: request::ContractCode) -> Bytes { - self.code_by_hash(req.account_key, BlockId::Hash(req.block_hash)) + fn storage_proof(&self, req: request::CompleteStorageRequest) -> Option { + self.prove_storage(req.address_hash, req.key_hash, BlockId::Hash(req.block_hash)).map(|(proof, item) | { + ::request::StorageResponse { + proof: proof, + value: item, + } + }) } - fn header_proof(&self, req: request::HeaderProof) -> Option<(encoded::Header, Vec)> { - if Some(req.cht_number) != cht::block_to_cht_number(req.block_number) { - debug!(target: "les_provider", "Requested CHT number mismatch with block number."); - return None; - } + fn contract_code(&self, req: request::CompleteCodeRequest) -> Option { + self.state_data(&req.code_hash) + .map(|code| ::request::CodeResponse { code: code }) + } - let mut needed_hdr = None; + fn header_proof(&self, req: request::CompleteHeaderProofRequest) -> Option { + let cht_number = match cht::block_to_cht_number(req.num) { + Some(cht_num) => cht_num, + None => { + debug!(target: "pip_provider", "Requested CHT proof with invalid block number"); + return None; + } + }; + + let mut needed = None; // build the CHT, caching the requested header as we pass through it. let cht = { @@ -258,8 +207,8 @@ impl Provider for T { total_difficulty: td, }; - if hdr.number() == req.block_number { - needed_hdr = Some(hdr); + if hdr.number() == req.num { + needed = Some((hdr, td)); } Some(info) @@ -268,29 +217,33 @@ impl Provider for T { } }; - match cht::build(req.cht_number, block_info) { + match cht::build(cht_number, block_info) { Some(cht) => cht, None => return None, // incomplete CHT. } }; - let needed_hdr = needed_hdr.expect("`needed_hdr` always set in loop, number checked before; qed"); + let (needed_hdr, needed_td) = needed.expect("`needed` always set in loop, number checked before; qed"); // prove our result. - match cht.prove(req.block_number, req.from_level) { - Ok(Some(proof)) => Some((needed_hdr, proof)), + match cht.prove(req.num, 0) { + Ok(Some(proof)) => Some(::request::HeaderProofResponse { + proof: proof, + hash: needed_hdr.hash(), + td: needed_td, + }), Ok(None) => None, Err(e) => { - debug!(target: "les_provider", "Error looking up number in freshly-created CHT: {}", e); + debug!(target: "pip_provider", "Error looking up number in freshly-created CHT: {}", e); None } } } - fn transaction_proof(&self, req: request::TransactionProof) -> Option> { + fn transaction_proof(&self, req: request::CompleteExecutionRequest) -> Option { use ethcore::transaction::Transaction; - let id = BlockId::Hash(req.at); + let id = BlockId::Hash(req.block_hash); let nonce = match self.nonce(&req.from, id.clone()) { Some(nonce) => nonce, None => return None, @@ -305,6 +258,7 @@ impl Provider for T { }.fake_sign(req.from); self.prove_transaction(transaction, id) + .map(|proof| ::request::ExecutionResponse { items: proof }) } fn ready_transactions(&self) -> Vec { @@ -347,27 +301,31 @@ impl Provider for LightProvider { self.client.as_light_client().block_header(id) } - fn block_body(&self, _id: BlockId) -> Option { + fn block_body(&self, _req: request::CompleteBodyRequest) -> Option { None } - fn block_receipts(&self, _hash: &H256) -> Option { + fn block_receipts(&self, _req: request::CompleteReceiptsRequest) -> Option { None } - fn state_proof(&self, _req: request::StateProof) -> Vec { - Vec::new() - } - - fn contract_code(&self, _req: request::ContractCode) -> Bytes { - Vec::new() - } - - fn header_proof(&self, _req: request::HeaderProof) -> Option<(encoded::Header, Vec)> { + fn account_proof(&self, _req: request::CompleteAccountRequest) -> Option { None } - fn transaction_proof(&self, _req: request::TransactionProof) -> Option> { + fn storage_proof(&self, _req: request::CompleteStorageRequest) -> Option { + None + } + + fn contract_code(&self, _req: request::CompleteCodeRequest) -> Option { + None + } + + fn header_proof(&self, _req: request::CompleteHeaderProofRequest) -> Option { + None + } + + fn transaction_proof(&self, _req: request::CompleteExecutionRequest) -> Option { None } @@ -395,10 +353,8 @@ mod tests { let client = TestBlockChainClient::new(); client.add_blocks(2000, EachBlockWith::Nothing); - let req = ::request::HeaderProof { - cht_number: 0, - block_number: 1500, - from_level: 0, + let req = ::request::CompleteHeaderProofRequest { + num: 1500, }; assert!(client.header_proof(req.clone()).is_none()); diff --git a/ethcore/light/src/types/les_request.rs b/ethcore/light/src/types/les_request.rs deleted file mode 100644 index dbff19eb5..000000000 --- a/ethcore/light/src/types/les_request.rs +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -//! LES request types. - -use ethcore::transaction::Action; -use util::{Address, H256, U256, Uint}; - -/// Either a hash or a number. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub enum HashOrNumber { - /// Block hash variant. - Hash(H256), - /// Block number variant. - Number(u64), -} - -impl From for HashOrNumber { - fn from(hash: H256) -> Self { - HashOrNumber::Hash(hash) - } -} - -impl From for HashOrNumber { - fn from(num: u64) -> Self { - HashOrNumber::Number(num) - } -} - -/// A request for block headers. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub struct Headers { - /// Starting block number or hash. - pub start: HashOrNumber, - /// The maximum amount of headers which can be returned. - pub max: usize, - /// The amount of headers to skip between each response entry. - pub skip: u64, - /// Whether the headers should proceed in falling number from the initial block. - pub reverse: bool, -} - -/// A request for specific block bodies. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub struct Bodies { - /// Hashes which bodies are being requested for. - pub block_hashes: Vec -} - -/// A request for transaction receipts. -/// -/// This request is answered with a list of transaction receipts for each block -/// requested. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub struct Receipts { - /// Block hashes to return receipts for. - pub block_hashes: Vec, -} - -/// A request for a state proof -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub struct StateProof { - /// Block hash to query state from. - pub block: H256, - /// Key of the state trie -- corresponds to account hash. - pub key1: H256, - /// Key in that account's storage trie; if empty, then the account RLP should be - /// returned. - pub key2: Option, - /// if greater than zero, trie nodes beyond this level may be omitted. - pub from_level: u32, // could even safely be u8; trie w/ 32-byte key can be at most 64-levels deep. -} - -/// A request for state proofs. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub struct StateProofs { - /// All the proof requests. - pub requests: Vec, -} - -/// A request for contract code. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub struct ContractCode { - /// Block hash - pub block_hash: H256, - /// Account key (== sha3(address)) - pub account_key: H256, -} - -/// A request for contract code. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub struct ContractCodes { - /// Block hash and account key (== sha3(address)) pairs to fetch code for. - pub code_requests: Vec, -} - -/// A request for a header proof from the Canonical Hash Trie. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub struct HeaderProof { - /// Number of the CHT. - pub cht_number: u64, - /// Block number requested. May not be 0: genesis isn't included in any CHT. - pub block_number: u64, - /// If greater than zero, trie nodes beyond this level may be omitted. - pub from_level: u32, -} - -/// A request for header proofs from the CHT. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub struct HeaderProofs { - /// All the proof requests. - pub requests: Vec, -} - -/// A request for proof of (simulated) transaction execution. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub struct TransactionProof { - /// Block hash to request for. - pub at: H256, - /// Address to treat as the caller. - pub from: Address, - /// Action to take: either a call or a create. - pub action: Action, - /// Amount of gas to request proof-of-execution for. - pub gas: U256, - /// Price for each gas. - pub gas_price: U256, - /// Value to simulate sending. - pub value: U256, - /// Transaction data. - pub data: Vec, -} - -/// Kinds of requests. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub enum Kind { - /// Requesting headers. - Headers, - /// Requesting block bodies. - Bodies, - /// Requesting transaction receipts. - Receipts, - /// Requesting proofs of state trie nodes. - StateProofs, - /// Requesting contract code by hash. - Codes, - /// Requesting header proofs (from the CHT). - HeaderProofs, - /// Requesting proof of transaction execution. - TransactionProof, -} - -/// Encompasses all possible types of requests in a single structure. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "ipc", binary)] -pub enum Request { - /// Requesting headers. - Headers(Headers), - /// Requesting block bodies. - Bodies(Bodies), - /// Requesting transaction receipts. - Receipts(Receipts), - /// Requesting state proofs. - StateProofs(StateProofs), - /// Requesting contract codes. - Codes(ContractCodes), - /// Requesting header proofs. - HeaderProofs(HeaderProofs), - /// Requesting proof of transaction execution. - TransactionProof(TransactionProof), -} - -impl Request { - /// Get the kind of request this is. - pub fn kind(&self) -> Kind { - match *self { - Request::Headers(_) => Kind::Headers, - Request::Bodies(_) => Kind::Bodies, - Request::Receipts(_) => Kind::Receipts, - Request::StateProofs(_) => Kind::StateProofs, - Request::Codes(_) => Kind::Codes, - Request::HeaderProofs(_) => Kind::HeaderProofs, - Request::TransactionProof(_) => Kind::TransactionProof, - } - } - - /// Get the amount of requests being made. - /// In the case of `TransactionProof`, this is the amount of gas being requested. - pub fn amount(&self) -> usize { - match *self { - Request::Headers(ref req) => req.max, - Request::Bodies(ref req) => req.block_hashes.len(), - Request::Receipts(ref req) => req.block_hashes.len(), - Request::StateProofs(ref req) => req.requests.len(), - Request::Codes(ref req) => req.code_requests.len(), - Request::HeaderProofs(ref req) => req.requests.len(), - Request::TransactionProof(ref req) => match req.gas > usize::max_value().into() { - true => usize::max_value(), - false => req.gas.low_u64() as usize, - } - } - } -} diff --git a/ethcore/light/src/types/mod.rs.in b/ethcore/light/src/types/mod.rs.in index 0adfbf0e4..eba551b53 100644 --- a/ethcore/light/src/types/mod.rs.in +++ b/ethcore/light/src/types/mod.rs.in @@ -14,4 +14,4 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -pub mod les_request; \ No newline at end of file +pub mod request; diff --git a/ethcore/light/src/types/request/builder.rs b/ethcore/light/src/types/request/builder.rs new file mode 100644 index 000000000..77f1389c2 --- /dev/null +++ b/ethcore/light/src/types/request/builder.rs @@ -0,0 +1,190 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Request chain builder utility. +//! Push requests with `push`. Back-references and data required to verify responses must be +//! supplied as well. + +use std::collections::HashMap; +use request::{ + IncompleteRequest, CompleteRequest, Request, + OutputKind, Output, NoSuchOutput, Response, ResponseError, +}; + +/// Build chained requests. Push them onto the series with `push`, +/// and produce a `Requests` object with `build`. Outputs are checked for consistency. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct RequestBuilder { + output_kinds: HashMap<(usize, usize), OutputKind>, + requests: Vec, +} + +impl RequestBuilder { + /// Attempt to push a request onto the request chain. Fails if the request + /// references a non-existent output of a prior request. + pub fn push(&mut self, request: Request) -> Result<(), NoSuchOutput> { + request.check_outputs(|req, idx, kind| { + match self.output_kinds.get(&(req, idx)) { + Some(k) if k == &kind => Ok(()), + _ => Err(NoSuchOutput), + } + })?; + let req_idx = self.requests.len(); + request.note_outputs(|idx, kind| { self.output_kinds.insert((req_idx, idx), kind); }); + self.requests.push(request); + Ok(()) + } + + /// Get a reference to the output kinds map. + pub fn output_kinds(&self) -> &HashMap<(usize, usize), OutputKind> { + &self.output_kinds + } + + /// Convert this into a "requests" object. + pub fn build(self) -> Requests { + Requests { + outputs: HashMap::new(), + requests: self.requests, + answered: 0, + } + } +} + +/// Requests pending responses. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Requests { + outputs: HashMap<(usize, usize), Output>, + requests: Vec, + answered: usize, +} + +impl Requests { + /// For each request, produce responses for each. + /// The responses vector produced goes up to the point where the responder + /// first returns `None`, an invalid response, or until all requests have been responded to. + pub fn respond_to_all(mut self, responder: F) -> Vec + where F: Fn(CompleteRequest) -> Option + { + let mut responses = Vec::new(); + + while let Some(response) = self.next_complete().and_then(&responder) { + match self.supply_response(&response) { + Ok(()) => responses.push(response), + Err(e) => { + debug!(target: "pip", "produced bad response to request: {:?}", e); + return responses; + } + } + } + + responses + } + + /// Get access to the underlying slice of requests. + // TODO: unimplemented -> Vec, // do we _have to_ allocate? + pub fn requests(&self) -> &[Request] { &self.requests } + + /// Get the number of answered requests. + pub fn num_answered(&self) -> usize { self.answered } + + /// Get the next request as a filled request. Returns `None` when all requests answered. + pub fn next_complete(&self) -> Option { + if self.answered == self.requests.len() { + None + } else { + Some(self.requests[self.answered].clone() + .complete() + .expect("All outputs checked as invariant of `Requests` object; qed")) + } + } + + /// Supply a response for the next request. + /// Fails on: wrong request kind, all requests answered already. + pub fn supply_response(&mut self, response: &Response) -> Result<(), ResponseError> { + let idx = self.answered; + + // check validity. + if idx == self.requests.len() { return Err(ResponseError::Unexpected) } + if self.requests[idx].kind() != response.kind() { return Err(ResponseError::WrongKind) } + + let outputs = &mut self.outputs; + response.fill_outputs(|out_idx, output| { + // we don't need to check output kinds here because all back-references + // are validated in the builder. + // TODO: optimization for only storing outputs we "care about"? + outputs.insert((idx, out_idx), output); + }); + + self.answered += 1; + + // fill as much of the next request as we can. + if let Some(ref mut req) = self.requests.get_mut(self.answered) { + req.fill(|req_idx, out_idx| outputs.get(&(req_idx, out_idx)).cloned().ok_or(NoSuchOutput)) + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use request::*; + use super::RequestBuilder; + use util::H256; + + #[test] + fn all_scalar() { + let mut builder = RequestBuilder::default(); + builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { + num: 100.into(), + })).unwrap(); + builder.push(Request::Receipts(IncompleteReceiptsRequest { + hash: H256::default().into(), + })).unwrap(); + } + + #[test] + #[should_panic] + fn missing_backref() { + let mut builder = RequestBuilder::default(); + builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { + num: Field::BackReference(100, 3), + })).unwrap(); + } + + #[test] + #[should_panic] + fn wrong_kind() { + let mut builder = RequestBuilder::default(); + assert!(builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { + num: 100.into(), + })).is_ok()); + builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { + num: Field::BackReference(0, 0), + })).unwrap(); + } + + #[test] + fn good_backreference() { + let mut builder = RequestBuilder::default(); + builder.push(Request::HeaderProof(IncompleteHeaderProofRequest { + num: 100.into(), // header proof puts hash at output 0. + })).unwrap(); + builder.push(Request::Receipts(IncompleteReceiptsRequest { + hash: Field::BackReference(0, 0), + })).unwrap(); + } +} diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs new file mode 100644 index 000000000..83d7963ac --- /dev/null +++ b/ethcore/light/src/types/request/mod.rs @@ -0,0 +1,1710 @@ +// 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 . + +//! Light protocol request types. + +use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; +use util::H256; + +mod builder; + +// re-exports of request types. +pub use self::header::{ + Complete as CompleteHeadersRequest, + Incomplete as IncompleteHeadersRequest, + Response as HeadersResponse +}; +pub use self::header_proof::{ + Complete as CompleteHeaderProofRequest, + Incomplete as IncompleteHeaderProofRequest, + Response as HeaderProofResponse +}; +pub use self::block_body::{ + Complete as CompleteBodyRequest, + Incomplete as IncompleteBodyRequest, + Response as BodyResponse +}; +pub use self::block_receipts::{ + Complete as CompleteReceiptsRequest, + Incomplete as IncompleteReceiptsRequest, + Response as ReceiptsResponse +}; +pub use self::account::{ + Complete as CompleteAccountRequest, + Incomplete as IncompleteAccountRequest, + Response as AccountResponse, +}; +pub use self::storage::{ + Complete as CompleteStorageRequest, + Incomplete as IncompleteStorageRequest, + Response as StorageResponse +}; +pub use self::contract_code::{ + Complete as CompleteCodeRequest, + Incomplete as IncompleteCodeRequest, + Response as CodeResponse, +}; +pub use self::execution::{ + Complete as CompleteExecutionRequest, + Incomplete as IncompleteExecutionRequest, + Response as ExecutionResponse, +}; + +pub use self::builder::{RequestBuilder, Requests}; + +/// Error indicating a reference to a non-existent or wrongly-typed output. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NoSuchOutput; + +/// Error on processing a response. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ResponseError { + /// Wrong kind of response. + WrongKind, + /// No responses expected. + Unexpected, +} + +/// An input to a request. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Field { + /// A pre-specified input. + Scalar(T), + /// An input which can be resolved later on. + /// (Request index, output index) + BackReference(usize, usize), +} + +impl Field { + // attempt conversion into scalar value. + fn into_scalar(self) -> Result { + match self { + Field::Scalar(val) => Ok(val), + _ => Err(NoSuchOutput), + } + } +} + +impl From for Field { + fn from(val: T) -> Self { + Field::Scalar(val) + } +} + +impl Decodable for Field { + fn decode(rlp: &UntrustedRlp) -> Result { + match rlp.val_at::(0)? { + 0 => Ok(Field::Scalar(rlp.val_at::(1)?)), + 1 => Ok({ + let inner_rlp = rlp.at(1)?; + Field::BackReference(inner_rlp.val_at(0)?, inner_rlp.val_at(1)?) + }), + _ => Err(DecoderError::Custom("Unknown discriminant for PIP field.")), + } + } +} + +impl Encodable for Field { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + match *self { + Field::Scalar(ref data) => { + s.append(&0u8).append(data); + } + Field::BackReference(ref req, ref idx) => { + s.append(&1u8).begin_list(2).append(req).append(idx); + } + } + } +} + +/// Request outputs which can be reused as inputs. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Output { + /// A 32-byte hash output. + Hash(H256), + /// An unsigned-integer output. + Number(u64), +} + +impl Output { + /// Get the output kind. + pub fn kind(&self) -> OutputKind { + match *self { + Output::Hash(_) => OutputKind::Hash, + Output::Number(_) => OutputKind::Number, + } + } +} + +/// Response output kinds which can be used as back-references. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum OutputKind { + /// A 32-byte hash output. + Hash, + /// An unsigned-integer output. + Number, +} + +/// Either a hash or a number. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "ipc", binary)] +pub enum HashOrNumber { + /// Block hash variant. + Hash(H256), + /// Block number variant. + Number(u64), +} + +impl From for HashOrNumber { + fn from(hash: H256) -> Self { + HashOrNumber::Hash(hash) + } +} + +impl From for HashOrNumber { + fn from(num: u64) -> Self { + HashOrNumber::Number(num) + } +} + +impl Decodable for HashOrNumber { + fn decode(rlp: &UntrustedRlp) -> Result { + rlp.as_val::().map(HashOrNumber::Hash) + .or_else(|_| rlp.as_val().map(HashOrNumber::Number)) + } +} + +impl Encodable for HashOrNumber { + fn rlp_append(&self, s: &mut RlpStream) { + match *self { + HashOrNumber::Hash(ref hash) => s.append(hash), + HashOrNumber::Number(ref num) => s.append(num), + }; + } +} + +/// All request types, as they're sent over the network. +/// They may be incomplete, with back-references to outputs +/// of prior requests. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Request { + /// A request for block headers. + Headers(IncompleteHeadersRequest), + /// A request for a header proof (from a CHT) + HeaderProof(IncompleteHeaderProofRequest), + // TransactionIndex, + /// A request for a block's receipts. + Receipts(IncompleteReceiptsRequest), + /// A request for a block body. + Body(IncompleteBodyRequest), + /// A request for a merkle proof of an account. + Account(IncompleteAccountRequest), + /// A request for a merkle proof of contract storage. + Storage(IncompleteStorageRequest), + /// A request for contract code. + Code(IncompleteCodeRequest), + /// A request for proof of execution, + Execution(IncompleteExecutionRequest), +} + +/// All request types, in an answerable state. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CompleteRequest { + /// A request for block headers. + Headers(CompleteHeadersRequest), + /// A request for a header proof (from a CHT) + HeaderProof(CompleteHeaderProofRequest), + // TransactionIndex, + /// A request for a block's receipts. + Receipts(CompleteReceiptsRequest), + /// A request for a block body. + Body(CompleteBodyRequest), + /// A request for a merkle proof of an account. + Account(CompleteAccountRequest), + /// A request for a merkle proof of contract storage. + Storage(CompleteStorageRequest), + /// A request for contract code. + Code(CompleteCodeRequest), + /// A request for proof of execution, + Execution(CompleteExecutionRequest), +} + +impl Request { + fn kind(&self) -> Kind { + match *self { + Request::Headers(_) => Kind::Headers, + Request::HeaderProof(_) => Kind::HeaderProof, + Request::Receipts(_) => Kind::Receipts, + Request::Body(_) => Kind::Body, + Request::Account(_) => Kind::Account, + Request::Storage(_) => Kind::Storage, + Request::Code(_) => Kind::Code, + Request::Execution(_) => Kind::Execution, + } + } +} + +impl Decodable for Request { + fn decode(rlp: &UntrustedRlp) -> Result { + match rlp.val_at::(0)? { + Kind::Headers => Ok(Request::Headers(rlp.val_at(1)?)), + Kind::HeaderProof => Ok(Request::HeaderProof(rlp.val_at(1)?)), + Kind::Receipts => Ok(Request::Receipts(rlp.val_at(1)?)), + Kind::Body => Ok(Request::Body(rlp.val_at(1)?)), + Kind::Account => Ok(Request::Account(rlp.val_at(1)?)), + Kind::Storage => Ok(Request::Storage(rlp.val_at(1)?)), + Kind::Code => Ok(Request::Code(rlp.val_at(1)?)), + Kind::Execution => Ok(Request::Execution(rlp.val_at(1)?)), + } + } +} + +impl Encodable for Request { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + + // hack around https://github.com/ethcore/parity/issues/4356 + Encodable::rlp_append(&self.kind(), s); + + match *self { + Request::Headers(ref req) => s.append(req), + Request::HeaderProof(ref req) => s.append(req), + Request::Receipts(ref req) => s.append(req), + Request::Body(ref req) => s.append(req), + Request::Account(ref req) => s.append(req), + Request::Storage(ref req) => s.append(req), + Request::Code(ref req) => s.append(req), + Request::Execution(ref req) => s.append(req), + }; + } +} + +impl IncompleteRequest for Request { + type Complete = CompleteRequest; + + fn check_outputs(&self, f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> + { + match *self { + Request::Headers(ref req) => req.check_outputs(f), + Request::HeaderProof(ref req) => req.check_outputs(f), + Request::Receipts(ref req) => req.check_outputs(f), + Request::Body(ref req) => req.check_outputs(f), + Request::Account(ref req) => req.check_outputs(f), + Request::Storage(ref req) => req.check_outputs(f), + Request::Code(ref req) => req.check_outputs(f), + Request::Execution(ref req) => req.check_outputs(f), + } + } + + fn note_outputs(&self, f: F) where F: FnMut(usize, OutputKind) { + match *self { + Request::Headers(ref req) => req.note_outputs(f), + Request::HeaderProof(ref req) => req.note_outputs(f), + Request::Receipts(ref req) => req.note_outputs(f), + Request::Body(ref req) => req.note_outputs(f), + Request::Account(ref req) => req.note_outputs(f), + Request::Storage(ref req) => req.note_outputs(f), + Request::Code(ref req) => req.note_outputs(f), + Request::Execution(ref req) => req.note_outputs(f), + } + } + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + match *self { + Request::Headers(ref mut req) => req.fill(oracle), + Request::HeaderProof(ref mut req) => req.fill(oracle), + Request::Receipts(ref mut req) => req.fill(oracle), + Request::Body(ref mut req) => req.fill(oracle), + Request::Account(ref mut req) => req.fill(oracle), + Request::Storage(ref mut req) => req.fill(oracle), + Request::Code(ref mut req) => req.fill(oracle), + Request::Execution(ref mut req) => req.fill(oracle), + } + } + + fn complete(self) -> Result { + match self { + Request::Headers(req) => req.complete().map(CompleteRequest::Headers), + Request::HeaderProof(req) => req.complete().map(CompleteRequest::HeaderProof), + Request::Receipts(req) => req.complete().map(CompleteRequest::Receipts), + Request::Body(req) => req.complete().map(CompleteRequest::Body), + Request::Account(req) => req.complete().map(CompleteRequest::Account), + Request::Storage(req) => req.complete().map(CompleteRequest::Storage), + Request::Code(req) => req.complete().map(CompleteRequest::Code), + Request::Execution(req) => req.complete().map(CompleteRequest::Execution), + } + } +} + +/// Kinds of requests. +/// Doubles as the "ID" field of the request. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Kind { + /// A request for headers. + Headers = 0, + /// A request for a header proof. + HeaderProof = 1, + // TransactionIndex = 2, + /// A request for block receipts. + Receipts = 3, + /// A request for a block body. + Body = 4, + /// A request for an account + merkle proof. + Account = 5, + /// A request for contract storage + merkle proof + Storage = 6, + /// A request for contract. + Code = 7, + /// A request for transaction execution + state proof. + Execution = 8, +} + +impl Decodable for Kind { + fn decode(rlp: &UntrustedRlp) -> Result { + match rlp.as_val::()? { + 0 => Ok(Kind::Headers), + 1 => Ok(Kind::HeaderProof), + // 2 => Ok(Kind::TransactionIndex), + 3 => Ok(Kind::Receipts), + 4 => Ok(Kind::Body), + 5 => Ok(Kind::Account), + 6 => Ok(Kind::Storage), + 7 => Ok(Kind::Code), + 8 => Ok(Kind::Execution), + _ => Err(DecoderError::Custom("Unknown PIP request ID.")), + } + } +} + +impl Encodable for Kind { + fn rlp_append(&self, s: &mut RlpStream) { + s.append(&(*self as u8)); + } +} + +/// All response types. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Response { + /// A response for block headers. + Headers(HeadersResponse), + /// A response for a header proof (from a CHT) + HeaderProof(HeaderProofResponse), + // TransactionIndex, + /// A response for a block's receipts. + Receipts(ReceiptsResponse), + /// A response for a block body. + Body(BodyResponse), + /// A response for a merkle proof of an account. + Account(AccountResponse), + /// A response for a merkle proof of contract storage. + Storage(StorageResponse), + /// A response for contract code. + Code(CodeResponse), + /// A response for proof of execution, + Execution(ExecutionResponse), +} + +impl Response { + /// Fill reusable outputs by writing them into the function. + pub fn fill_outputs(&self, f: F) where F: FnMut(usize, Output) { + match *self { + Response::Headers(ref res) => res.fill_outputs(f), + Response::HeaderProof(ref res) => res.fill_outputs(f), + Response::Receipts(ref res) => res.fill_outputs(f), + Response::Body(ref res) => res.fill_outputs(f), + Response::Account(ref res) => res.fill_outputs(f), + Response::Storage(ref res) => res.fill_outputs(f), + Response::Code(ref res) => res.fill_outputs(f), + Response::Execution(ref res) => res.fill_outputs(f), + } + } + + fn kind(&self) -> Kind { + match *self { + Response::Headers(_) => Kind::Headers, + Response::HeaderProof(_) => Kind::HeaderProof, + Response::Receipts(_) => Kind::Receipts, + Response::Body(_) => Kind::Body, + Response::Account(_) => Kind::Account, + Response::Storage(_) => Kind::Storage, + Response::Code(_) => Kind::Code, + Response::Execution(_) => Kind::Execution, + } + } +} + +impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + match rlp.val_at::(0)? { + Kind::Headers => Ok(Response::Headers(rlp.val_at(1)?)), + Kind::HeaderProof => Ok(Response::HeaderProof(rlp.val_at(1)?)), + Kind::Receipts => Ok(Response::Receipts(rlp.val_at(1)?)), + Kind::Body => Ok(Response::Body(rlp.val_at(1)?)), + Kind::Account => Ok(Response::Account(rlp.val_at(1)?)), + Kind::Storage => Ok(Response::Storage(rlp.val_at(1)?)), + Kind::Code => Ok(Response::Code(rlp.val_at(1)?)), + Kind::Execution => Ok(Response::Execution(rlp.val_at(1)?)), + } + } +} + +impl Encodable for Response { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2); + + // hack around https://github.com/ethcore/parity/issues/4356 + Encodable::rlp_append(&self.kind(), s); + + match *self { + Response::Headers(ref res) => s.append(res), + Response::HeaderProof(ref res) => s.append(res), + Response::Receipts(ref res) => s.append(res), + Response::Body(ref res) => s.append(res), + Response::Account(ref res) => s.append(res), + Response::Storage(ref res) => s.append(res), + Response::Code(ref res) => s.append(res), + Response::Execution(ref res) => s.append(res), + }; + } +} + +/// A potentially incomplete request. +pub trait IncompleteRequest: Sized { + /// The complete variant of this request. + type Complete; + + /// Check prior outputs against the needed inputs. + /// + /// This is called to ensure consistency of this request with + /// others in the same packet. + fn check_outputs(&self, f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput>; + + /// Note that this request will produce the following outputs. + fn note_outputs(&self, f: F) where F: FnMut(usize, OutputKind); + + /// Fill fields of the request. + /// + /// This function is provided an "output oracle" which allows fetching of + /// prior request outputs. + /// Only outputs previously checked with `check_outputs` may be available. + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result; + + /// Attempt to convert this request into its complete variant. + /// Will succeed if all fields have been filled, will fail otherwise. + fn complete(self) -> Result; +} + +/// Header request. +pub mod header { + use super::{Field, HashOrNumber, NoSuchOutput, OutputKind, Output}; + use ethcore::encoded; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + + /// Potentially incomplete headers request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Incomplete { + /// Start block. + pub start: Field, + /// Skip between. + pub skip: u64, + /// Maximum to return. + pub max: u64, + /// Whether to reverse from start. + pub reverse: bool, + } + + impl Decodable for Incomplete { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Incomplete { + start: rlp.val_at(0)?, + skip: rlp.val_at(1)?, + max: rlp.val_at(2)?, + reverse: rlp.val_at(3)? + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(4) + .append(&self.start) + .append(&self.skip) + .append(&self.max) + .append(&self.reverse); + } + } + + impl super::IncompleteRequest for Incomplete { + type Complete = Complete; + + fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> + { + match self.start { + Field::Scalar(_) => Ok(()), + Field::BackReference(req, idx) => + f(req, idx, OutputKind::Hash).or_else(|_| f(req, idx, OutputKind::Number)) + } + } + + fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) { } + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + if let Field::BackReference(req, idx) = self.start { + self.start = match oracle(req, idx) { + Ok(Output::Hash(hash)) => Field::Scalar(hash.into()), + Ok(Output::Number(num)) => Field::Scalar(num.into()), + Err(_) => Field::BackReference(req, idx), + } + } + } + + fn complete(self) -> Result { + Ok(Complete { + start: self.start.into_scalar()?, + skip: self.skip, + max: self.max, + reverse: self.reverse, + }) + } + } + + /// A complete header request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Complete { + /// Start block. + pub start: HashOrNumber, + /// Skip between. + pub skip: u64, + /// Maximum to return. + pub max: u64, + /// Whether to reverse from start. + pub reverse: bool, + } + + /// The output of a request for headers. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Response { + /// The headers requested. + pub headers: Vec, + } + + impl Response { + /// Fill reusable outputs by writing them into the function. + pub fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) { } + } + + impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + use ethcore::header::Header as FullHeader; + + let mut headers = Vec::new(); + + for item in rlp.iter() { + // check that it's a valid encoding. + // TODO: just return full headers here? + let _: FullHeader = item.as_val()?; + headers.push(encoded::Header::new(item.as_raw().to_owned())); + } + + Ok(Response { + headers: headers, + }) + } + } + + impl Encodable for Response { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(self.headers.len()); + for header in &self.headers { + s.append_raw(header.rlp().as_raw(), 1); + } + } + } +} + +/// Request and response for header proofs. +pub mod header_proof { + use super::{Field, NoSuchOutput, OutputKind, Output}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use util::{Bytes, U256, H256}; + + /// Potentially incomplete header proof request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Incomplete { + /// Block number. + pub num: Field, + } + + impl Decodable for Incomplete { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Incomplete { + num: rlp.val_at(0)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(1).append(&self.num); + } + } + + impl super::IncompleteRequest for Incomplete { + type Complete = Complete; + + fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> + { + match self.num { + Field::Scalar(_) => Ok(()), + Field::BackReference(req, idx) => f(req, idx, OutputKind::Number), + } + } + + fn note_outputs(&self, mut note: F) where F: FnMut(usize, OutputKind) { + note(0, OutputKind::Hash); + } + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + if let Field::BackReference(req, idx) = self.num { + self.num = match oracle(req, idx) { + Ok(Output::Number(num)) => Field::Scalar(num.into()), + _ => Field::BackReference(req, idx), + } + } + } + + fn complete(self) -> Result { + Ok(Complete { + num: self.num.into_scalar()?, + }) + } + } + + /// A complete header proof request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Complete { + /// The number to get a header proof for. + pub num: u64, + } + + /// The output of a request for a header proof. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Response { + /// Inclusion proof of the header and total difficulty in the CHT. + pub proof: Vec, + /// The proved header's hash. + pub hash: H256, + /// The proved header's total difficulty. + pub td: U256, + } + + impl Response { + /// Fill reusable outputs by providing them to the function. + pub fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output) { + f(0, Output::Hash(self.hash)); + } + } + + impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + + Ok(Response { + proof: rlp.list_at(0)?, + hash: rlp.val_at(1)?, + td: rlp.val_at(2)?, + }) + } + } + + impl Encodable for Response { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3).begin_list(self.proof.len()); + for item in &self.proof { + s.append_list(&item); + } + + s.append(&self.hash).append(&self.td); + } + } +} + +/// Request and response for block receipts +pub mod block_receipts { + use super::{Field, NoSuchOutput, OutputKind, Output}; + use ethcore::receipt::Receipt; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use util::H256; + + /// Potentially incomplete block receipts request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Incomplete { + /// Block hash to get receipts for. + pub hash: Field, + } + + impl Decodable for Incomplete { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Incomplete { + hash: rlp.val_at(0)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(1).append(&self.hash); + } + } + + impl super::IncompleteRequest for Incomplete { + type Complete = Complete; + + fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> + { + match self.hash { + Field::Scalar(_) => Ok(()), + Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash), + } + } + + fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) {} + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + if let Field::BackReference(req, idx) = self.hash { + self.hash = match oracle(req, idx) { + Ok(Output::Number(hash)) => Field::Scalar(hash.into()), + _ => Field::BackReference(req, idx), + } + } + } + + fn complete(self) -> Result { + Ok(Complete { + hash: self.hash.into_scalar()?, + }) + } + } + + /// A complete block receipts request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Complete { + /// The number to get block receipts for. + pub hash: H256, + } + + /// The output of a request for block receipts. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Response { + /// The block receipts. + pub receipts: Vec + } + + impl Response { + /// Fill reusable outputs by providing them to the function. + pub fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} + } + + impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + + Ok(Response { + receipts: rlp.as_list()?, + }) + } + } + + impl Encodable for Response { + fn rlp_append(&self, s: &mut RlpStream) { + s.append_list(&self.receipts); + } + } +} + +/// Request and response for a block body +pub mod block_body { + use super::{Field, NoSuchOutput, OutputKind, Output}; + use ethcore::encoded; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use util::H256; + + /// Potentially incomplete block body request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Incomplete { + /// Block hash to get receipts for. + pub hash: Field, + } + + impl Decodable for Incomplete { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Incomplete { + hash: rlp.val_at(0)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(1).append(&self.hash); + } + } + + impl super::IncompleteRequest for Incomplete { + type Complete = Complete; + + fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> + { + match self.hash { + Field::Scalar(_) => Ok(()), + Field::BackReference(req, idx) => f(req, idx, OutputKind::Hash), + } + } + + fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) {} + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + if let Field::BackReference(req, idx) = self.hash { + self.hash = match oracle(req, idx) { + Ok(Output::Hash(hash)) => Field::Scalar(hash.into()), + _ => Field::BackReference(req, idx), + } + } + } + + fn complete(self) -> Result { + Ok(Complete { + hash: self.hash.into_scalar()?, + }) + } + } + + /// A complete block body request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Complete { + /// The hash to get a block body for. + pub hash: H256, + } + + /// The output of a request for block body. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Response { + /// The block body. + pub body: encoded::Body, + } + + impl Response { + /// Fill reusable outputs by providing them to the function. + pub fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} + } + + impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + use ethcore::header::Header as FullHeader; + use ethcore::transaction::UnverifiedTransaction; + + // check body validity. + let _: Vec = rlp.list_at(0)?; + let _: Vec = rlp.list_at(1)?; + + Ok(Response { + body: encoded::Body::new(rlp.as_raw().to_owned()), + }) + } + } + + impl Encodable for Response { + fn rlp_append(&self, s: &mut RlpStream) { + s.append_raw(&self.body.rlp().as_raw(), 1); + } + } +} + +/// A request for an account proof. +pub mod account { + use super::{Field, NoSuchOutput, OutputKind, Output}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use util::{Bytes, U256, H256}; + + /// Potentially incomplete request for an account proof. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Incomplete { + /// Block hash to request state proof for. + pub block_hash: Field, + /// Hash of the account's address. + pub address_hash: Field, + } + + impl Decodable for Incomplete { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Incomplete { + block_hash: rlp.val_at(0)?, + address_hash: rlp.val_at(1)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2) + .append(&self.block_hash) + .append(&self.address_hash); + } + } + + impl super::IncompleteRequest for Incomplete { + type Complete = Complete; + + fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> + { + if let Field::BackReference(req, idx) = self.block_hash { + f(req, idx, OutputKind::Hash)? + } + + if let Field::BackReference(req, idx) = self.address_hash { + f(req, idx, OutputKind::Hash)? + } + + Ok(()) + } + + fn note_outputs(&self, mut f: F) where F: FnMut(usize, OutputKind) { + f(0, OutputKind::Hash); + f(1, OutputKind::Hash); + } + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + if let Field::BackReference(req, idx) = self.block_hash { + self.block_hash = match oracle(req, idx) { + Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash.into()), + _ => Field::BackReference(req, idx), + } + } + + if let Field::BackReference(req, idx) = self.address_hash { + self.address_hash = match oracle(req, idx) { + Ok(Output::Hash(address_hash)) => Field::Scalar(address_hash.into()), + _ => Field::BackReference(req, idx), + } + } + } + + fn complete(self) -> Result { + Ok(Complete { + block_hash: self.block_hash.into_scalar()?, + address_hash: self.address_hash.into_scalar()?, + }) + } + } + + /// A complete request for an account. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Complete { + /// Block hash to request state proof for. + pub block_hash: H256, + /// Hash of the account's address. + pub address_hash: H256, + } + + /// The output of a request for an account state proof. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Response { + /// Inclusion/exclusion proof + pub proof: Vec, + /// Account nonce. + pub nonce: U256, + /// Account balance. + pub balance: U256, + /// Account's code hash. + pub code_hash: H256, + /// Account's storage trie root. + pub storage_root: H256, + } + + impl Response { + /// Fill reusable outputs by providing them to the function. + pub fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output) { + f(0, Output::Hash(self.code_hash)); + f(1, Output::Hash(self.storage_root)); + } + } + + impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Response { + proof: rlp.list_at(0)?, + nonce: rlp.val_at(1)?, + balance: rlp.val_at(2)?, + code_hash: rlp.val_at(3)?, + storage_root: rlp.val_at(4)? + }) + } + } + + impl Encodable for Response { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(5).begin_list(self.proof.len()); + for item in &self.proof { + s.append_list(&item); + } + + s.append(&self.nonce) + .append(&self.balance) + .append(&self.code_hash) + .append(&self.storage_root); + } + } +} + +/// A request for a storage proof. +pub mod storage { + use super::{Field, NoSuchOutput, OutputKind, Output}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use util::{Bytes, H256}; + + /// Potentially incomplete request for an storage proof. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Incomplete { + /// Block hash to request state proof for. + pub block_hash: Field, + /// Hash of the account's address. + pub address_hash: Field, + /// Hash of the storage key. + pub key_hash: Field, + } + + impl Decodable for Incomplete { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Incomplete { + block_hash: rlp.val_at(0)?, + address_hash: rlp.val_at(1)?, + key_hash: rlp.val_at(2)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(3) + .append(&self.block_hash) + .append(&self.address_hash) + .append(&self.key_hash); + } + } + + impl super::IncompleteRequest for Incomplete { + type Complete = Complete; + + fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> + { + if let Field::BackReference(req, idx) = self.block_hash { + f(req, idx, OutputKind::Hash)? + } + + if let Field::BackReference(req, idx) = self.address_hash { + f(req, idx, OutputKind::Hash)? + } + + if let Field::BackReference(req, idx) = self.key_hash { + f(req, idx, OutputKind::Hash)? + } + + Ok(()) + } + + fn note_outputs(&self, mut f: F) where F: FnMut(usize, OutputKind) { + f(0, OutputKind::Hash); + } + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + if let Field::BackReference(req, idx) = self.block_hash { + self.block_hash = match oracle(req, idx) { + Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash.into()), + _ => Field::BackReference(req, idx), + } + } + + if let Field::BackReference(req, idx) = self.address_hash { + self.address_hash = match oracle(req, idx) { + Ok(Output::Hash(address_hash)) => Field::Scalar(address_hash.into()), + _ => Field::BackReference(req, idx), + } + } + + if let Field::BackReference(req, idx) = self.key_hash { + self.key_hash = match oracle(req, idx) { + Ok(Output::Hash(key_hash)) => Field::Scalar(key_hash.into()), + _ => Field::BackReference(req, idx), + } + } + } + + fn complete(self) -> Result { + Ok(Complete { + block_hash: self.block_hash.into_scalar()?, + address_hash: self.address_hash.into_scalar()?, + key_hash: self.key_hash.into_scalar()?, + }) + } + } + + /// A complete request for a storage proof. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Complete { + /// Block hash to request state proof for. + pub block_hash: H256, + /// Hash of the account's address. + pub address_hash: H256, + /// Storage key hash. + pub key_hash: H256, + } + + /// The output of a request for an account state proof. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Response { + /// Inclusion/exclusion proof + pub proof: Vec, + /// Storage value. + pub value: H256, + } + + impl Response { + /// Fill reusable outputs by providing them to the function. + pub fn fill_outputs(&self, mut f: F) where F: FnMut(usize, Output) { + f(0, Output::Hash(self.value)); + } + } + + impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Response { + proof: rlp.list_at(0)?, + value: rlp.val_at(1)?, + }) + } + } + + impl Encodable for Response { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2).begin_list(self.proof.len()); + for item in &self.proof { + s.append_list(&item); + } + s.append(&self.value); + } + } +} + +/// A request for contract code. +pub mod contract_code { + use super::{Field, NoSuchOutput, OutputKind, Output}; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use util::{Bytes, H256}; + + /// Potentially incomplete contract code request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Incomplete { + /// The block hash to request the state for. + pub block_hash: Field, + /// The code hash. + pub code_hash: Field, + } + + impl Decodable for Incomplete { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Incomplete { + block_hash: rlp.val_at(0)?, + code_hash: rlp.val_at(1)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(2) + .append(&self.block_hash) + .append(&self.code_hash); + } + } + + impl super::IncompleteRequest for Incomplete { + type Complete = Complete; + + fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> + { + if let Field::BackReference(req, idx) = self.block_hash { + f(req, idx, OutputKind::Hash)?; + } + if let Field::BackReference(req, idx) = self.code_hash { + f(req, idx, OutputKind::Hash)?; + } + + Ok(()) + } + + fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) {} + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + if let Field::BackReference(req, idx) = self.block_hash { + self.block_hash = match oracle(req, idx) { + Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash.into()), + _ => Field::BackReference(req, idx), + } + } + + if let Field::BackReference(req, idx) = self.code_hash { + self.code_hash = match oracle(req, idx) { + Ok(Output::Hash(code_hash)) => Field::Scalar(code_hash.into()), + _ => Field::BackReference(req, idx), + } + } + } + + fn complete(self) -> Result { + Ok(Complete { + block_hash: self.block_hash.into_scalar()?, + code_hash: self.code_hash.into_scalar()?, + }) + } + } + + /// A complete request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Complete { + /// The block hash to request the state for. + pub block_hash: H256, + /// The code hash. + pub code_hash: H256, + } + + /// The output of a request for + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Response { + /// The requested code. + pub code: Bytes, + } + + impl Response { + /// Fill reusable outputs by providing them to the function. + pub fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} + } + + impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + + Ok(Response { + code: rlp.as_val()?, + }) + } + } + + impl Encodable for Response { + fn rlp_append(&self, s: &mut RlpStream) { + s.append(&self.code); + } + } +} + +/// A request for proof of execution. +pub mod execution { + use super::{Field, NoSuchOutput, OutputKind, Output}; + use ethcore::transaction::Action; + use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; + use util::{Bytes, Address, U256, H256, DBValue}; + + /// Potentially incomplete execution proof request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Incomplete { + /// The block hash to request the state for. + pub block_hash: Field, + /// The address the transaction should be from. + pub from: Address, + /// The action of the transaction. + pub action: Action, + /// The amount of gas to prove. + pub gas: U256, + /// The gas price. + pub gas_price: U256, + /// The value to transfer. + pub value: U256, + /// Call data. + pub data: Bytes, + } + + impl Decodable for Incomplete { + fn decode(rlp: &UntrustedRlp) -> Result { + Ok(Incomplete { + block_hash: rlp.val_at(0)?, + from: rlp.val_at(1)?, + action: rlp.val_at(2)?, + gas: rlp.val_at(3)?, + gas_price: rlp.val_at(4)?, + value: rlp.val_at(5)?, + data: rlp.val_at(6)?, + }) + } + } + + impl Encodable for Incomplete { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(7) + .append(&self.block_hash) + .append(&self.from); + + match self.action { + Action::Create => s.append_empty_data(), + Action::Call(ref addr) => s.append(addr), + }; + + s.append(&self.gas) + .append(&self.gas_price) + .append(&self.value) + .append(&self.data); + } + } + + impl super::IncompleteRequest for Incomplete { + type Complete = Complete; + + fn check_outputs(&self, mut f: F) -> Result<(), NoSuchOutput> + where F: FnMut(usize, usize, OutputKind) -> Result<(), NoSuchOutput> + { + if let Field::BackReference(req, idx) = self.block_hash { + f(req, idx, OutputKind::Hash)?; + } + + Ok(()) + } + + fn note_outputs(&self, _: F) where F: FnMut(usize, OutputKind) {} + + fn fill(&mut self, oracle: F) where F: Fn(usize, usize) -> Result { + if let Field::BackReference(req, idx) = self.block_hash { + self.block_hash = match oracle(req, idx) { + Ok(Output::Hash(block_hash)) => Field::Scalar(block_hash.into()), + _ => Field::BackReference(req, idx), + } + } + } + fn complete(self) -> Result { + Ok(Complete { + block_hash: self.block_hash.into_scalar()?, + from: self.from, + action: self.action, + gas: self.gas, + gas_price: self.gas_price, + value: self.value, + data: self.data, + }) + } + } + + /// A complete request. + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Complete { + /// The block hash to request the state for. + pub block_hash: H256, + /// The address the transaction should be from. + pub from: Address, + /// The action of the transaction. + pub action: Action, + /// The amount of gas to prove. + pub gas: U256, + /// The gas price. + pub gas_price: U256, + /// The value to transfer. + pub value: U256, + /// Call data. + pub data: Bytes, + } + + /// The output of a request for proof of execution + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct Response { + /// All state items (trie nodes, code) necessary to re-prove the transaction. + pub items: Vec, + } + + impl Response { + /// Fill reusable outputs by providing them to the function. + pub fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} + } + + impl Decodable for Response { + fn decode(rlp: &UntrustedRlp) -> Result { + let mut items = Vec::new(); + for raw_item in rlp.iter() { + let mut item = DBValue::new(); + item.append_slice(raw_item.data()?); + items.push(item); + } + + Ok(Response { + items: items, + }) + } + } + + impl Encodable for Response { + fn rlp_append(&self, s: &mut RlpStream) { + s.begin_list(self.items.len()); + + for item in &self.items { + s.append(&&**item); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ethcore::header::Header; + + fn check_roundtrip(val: T) + where T: ::rlp::Encodable + ::rlp::Decodable + PartialEq + ::std::fmt::Debug + { + let bytes = ::rlp::encode(&val); + let new_val: T = ::rlp::decode(&bytes); + assert_eq!(val, new_val); + } + + #[test] + fn hash_or_number_roundtrip() { + let hash = HashOrNumber::Hash(H256::default()); + let number = HashOrNumber::Number(5); + + check_roundtrip(hash); + check_roundtrip(number); + } + + #[test] + fn field_roundtrip() { + let field_scalar = Field::Scalar(5usize); + let field_back: Field = Field::BackReference(1, 2); + + check_roundtrip(field_scalar); + check_roundtrip(field_back); + } + + #[test] + fn headers_roundtrip() { + let req = IncompleteHeadersRequest { + start: Field::Scalar(5u64.into()), + skip: 0, + max: 100, + reverse: false, + }; + + let full_req = Request::Headers(req.clone()); + let res = HeadersResponse { + headers: vec![ + ::ethcore::encoded::Header::new(::rlp::encode(&Header::default()).to_vec()) + ] + }; + let full_res = Response::Headers(res.clone()); + + check_roundtrip(req); + check_roundtrip(full_req); + check_roundtrip(res); + check_roundtrip(full_res); + } + + #[test] + fn header_proof_roundtrip() { + let req = IncompleteHeaderProofRequest { + num: Field::BackReference(1, 234), + }; + + let full_req = Request::HeaderProof(req.clone()); + let res = HeaderProofResponse { + proof: Vec::new(), + hash: Default::default(), + td: 100.into(), + }; + let full_res = Response::HeaderProof(res.clone()); + + check_roundtrip(req); + check_roundtrip(full_req); + check_roundtrip(res); + check_roundtrip(full_res); + } + + #[test] + fn receipts_roundtrip() { + let req = IncompleteReceiptsRequest { + hash: Field::Scalar(Default::default()), + }; + + let full_req = Request::Receipts(req.clone()); + let res = ReceiptsResponse { + receipts: vec![Default::default(), Default::default()], + }; + let full_res = Response::Receipts(res.clone()); + + check_roundtrip(req); + check_roundtrip(full_req); + check_roundtrip(res); + check_roundtrip(full_res); + } + + #[test] + fn body_roundtrip() { + let req = IncompleteBodyRequest { + hash: Field::Scalar(Default::default()), + }; + + let full_req = Request::Body(req.clone()); + let res = BodyResponse { + body: { + let mut stream = RlpStream::new_list(2); + stream.begin_list(0).begin_list(0); + ::ethcore::encoded::Body::new(stream.out()) + }, + }; + let full_res = Response::Body(res.clone()); + + check_roundtrip(req); + check_roundtrip(full_req); + check_roundtrip(res); + check_roundtrip(full_res); + } + + #[test] + fn account_roundtrip() { + let req = IncompleteAccountRequest { + block_hash: Field::Scalar(Default::default()), + address_hash: Field::BackReference(1, 2), + }; + + let full_req = Request::Account(req.clone()); + let res = AccountResponse { + proof: Vec::new(), + nonce: 100.into(), + balance: 123456.into(), + code_hash: Default::default(), + storage_root: Default::default(), + }; + let full_res = Response::Account(res.clone()); + + check_roundtrip(req); + check_roundtrip(full_req); + check_roundtrip(res); + check_roundtrip(full_res); + } + + #[test] + fn storage_roundtrip() { + let req = IncompleteStorageRequest { + block_hash: Field::Scalar(Default::default()), + address_hash: Field::BackReference(1, 2), + key_hash: Field::BackReference(3, 2), + }; + + let full_req = Request::Storage(req.clone()); + let res = StorageResponse { + proof: Vec::new(), + value: H256::default(), + }; + let full_res = Response::Storage(res.clone()); + + check_roundtrip(req); + check_roundtrip(full_req); + check_roundtrip(res); + check_roundtrip(full_res); + } + + #[test] + fn code_roundtrip() { + let req = IncompleteCodeRequest { + block_hash: Field::Scalar(Default::default()), + code_hash: Field::BackReference(3, 2), + }; + + let full_req = Request::Code(req.clone()); + let res = CodeResponse { + code: vec![1, 2, 3, 4, 5, 6, 7, 6, 5, 4], + }; + let full_res = Response::Code(res.clone()); + + check_roundtrip(req); + check_roundtrip(full_req); + check_roundtrip(res); + check_roundtrip(full_res); + } + + #[test] + fn execution_roundtrip() { + use util::DBValue; + + let req = IncompleteExecutionRequest { + block_hash: Field::Scalar(Default::default()), + from: Default::default(), + action: ::ethcore::transaction::Action::Create, + gas: 100_000.into(), + gas_price: 0.into(), + value: 100_000_001.into(), + data: vec![1, 2, 3, 2, 1], + }; + + let full_req = Request::Execution(req.clone()); + let res = ExecutionResponse { + items: vec![DBValue::new(), { + let mut value = DBValue::new(); + value.append_slice(&[1, 1, 1, 2, 3]); + value + }], + }; + let full_res = Response::Execution(res.clone()); + + check_roundtrip(req); + check_roundtrip(full_req); + check_roundtrip(res); + check_roundtrip(full_res); + } + + #[test] + fn vec_test() { + use rlp::*; + + let reqs: Vec<_> = (0..10).map(|_| IncompleteExecutionRequest { + block_hash: Field::Scalar(Default::default()), + from: Default::default(), + action: ::ethcore::transaction::Action::Create, + gas: 100_000.into(), + gas_price: 0.into(), + value: 100_000_001.into(), + data: vec![1, 2, 3, 2, 1], + }).map(Request::Execution).collect(); + + let mut stream = RlpStream::new_list(2); + stream.append(&100usize).append_list(&reqs); + let out = stream.out(); + + let rlp = UntrustedRlp::new(&out); + assert_eq!(rlp.val_at::(0).unwrap(), 100usize); + assert_eq!(rlp.list_at::(1).unwrap(), reqs); + } +} diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index d284954e7..978f7a99c 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1607,23 +1607,14 @@ impl MayPanic for Client { } impl ::client::ProvingBlockChainClient for Client { - fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockId) -> Vec { + fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec, H256)> { self.state_at(id) - .and_then(move |state| state.prove_storage(key1, key2, from_level).ok()) - .unwrap_or_else(Vec::new) + .and_then(move |state| state.prove_storage(key1, key2).ok()) } - fn prove_account(&self, key1: H256, from_level: u32, id: BlockId) -> Vec { + fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec, ::types::basic_account::BasicAccount)> { self.state_at(id) - .and_then(move |state| state.prove_account(key1, from_level).ok()) - .unwrap_or_else(Vec::new) - } - - fn code_by_hash(&self, account_key: H256, id: BlockId) -> Bytes { - self.state_at(id) - .and_then(move |state| state.code_by_address_hash(account_key).ok()) - .and_then(|x| x) - .unwrap_or_else(Vec::new) + .and_then(move |state| state.prove_account(key1).ok()) } fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option> { @@ -1643,7 +1634,6 @@ impl ::client::ProvingBlockChainClient for Client { _ => return Some(state.drop().1.extract_proof()), } } - } impl Drop for Client { diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index d96b305de..16f38203f 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -38,6 +38,7 @@ use error::{ImportResult, Error as EthcoreError}; use evm::{Factory as EvmFactory, VMType, Schedule}; use miner::{Miner, MinerService, TransactionImportResult}; use spec::Spec; +use types::basic_account::BasicAccount; use types::mode::Mode; use types::pruning_info::PruningInfo; @@ -758,16 +759,12 @@ impl BlockChainClient for TestBlockChainClient { } impl ProvingBlockChainClient for TestBlockChainClient { - fn prove_storage(&self, _: H256, _: H256, _: u32, _: BlockId) -> Vec { - Vec::new() + fn prove_storage(&self, _: H256, _: H256, _: BlockId) -> Option<(Vec, H256)> { + None } - fn prove_account(&self, _: H256, _: u32, _: BlockId) -> Vec { - Vec::new() - } - - fn code_by_hash(&self, _: H256, _: BlockId) -> Bytes { - Vec::new() + fn prove_account(&self, _: H256, _: BlockId) -> Option<(Vec, BasicAccount)> { + None } fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option> { diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index b4c284f11..a612d8a77 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -34,6 +34,7 @@ use env_info::LastHashes; use block_import_error::BlockImportError; use ipc::IpcConfig; use types::ids::*; +use types::basic_account::BasicAccount; use types::trace_filter::Filter as TraceFilter; use types::call_analytics::CallAnalytics; use types::blockchain_info::BlockChainInfo; @@ -315,19 +316,12 @@ pub trait ProvingBlockChainClient: BlockChainClient { /// /// Both provided keys assume a secure trie. /// Returns a vector of raw trie nodes (in order from the root) proving the storage query. - /// Nodes after `from_level` may be omitted. - /// An empty vector indicates unservable query. - fn prove_storage(&self, key1: H256, key2: H256, from_level: u32, id: BlockId) -> Vec; + fn prove_storage(&self, key1: H256, key2: H256, id: BlockId) -> Option<(Vec, H256)>; /// Prove account existence at a specific block id. /// The key is the keccak hash of the account's address. /// Returns a vector of raw trie nodes (in order from the root) proving the query. - /// Nodes after `from_level` may be omitted. - /// An empty vector indicates unservable query. - fn prove_account(&self, key1: H256, from_level: u32, id: BlockId) -> Vec; - - /// Get code by address hash. - fn code_by_hash(&self, account_key: H256, id: BlockId) -> Bytes; + fn prove_account(&self, key1: H256, id: BlockId) -> Option<(Vec, BasicAccount)>; /// Prove execution of a transaction at the given block. fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option>; diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs index 9e762979b..d8aad430a 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -438,18 +438,19 @@ impl Account { /// trie. /// `storage_key` is the hash of the desired storage key, meaning /// this will only work correctly under a secure trie. - /// Returns a merkle proof of the storage trie node with all nodes before `from_level` - /// omitted. - pub fn prove_storage(&self, db: &HashDB, storage_key: H256, from_level: u32) -> Result, Box> { + pub fn prove_storage(&self, db: &HashDB, storage_key: H256) -> Result<(Vec, H256), Box> { use util::trie::{Trie, TrieDB}; use util::trie::recorder::Recorder; - let mut recorder = Recorder::with_depth(from_level); + let mut recorder = Recorder::new(); let trie = TrieDB::new(db, &self.storage_root)?; - let _ = trie.get_with(&storage_key, &mut recorder)?; + let item: U256 = { + let query = (&mut recorder, ::rlp::decode); + trie.get_with(&storage_key, query)?.unwrap_or_else(U256::zero) + }; - Ok(recorder.drain().into_iter().map(|r| r.data).collect()) + Ok((recorder.drain().into_iter().map(|r| r.data).collect(), item.into())) } } diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 6e2a956ab..26d33d152 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -31,6 +31,7 @@ use factory::Factories; use trace::FlatTrace; use pod_account::*; use pod_state::{self, PodState}; +use types::basic_account::BasicAccount; use types::executed::{Executed, ExecutionError}; use types::state_diff::StateDiff; use transaction::SignedTransaction; @@ -857,47 +858,43 @@ impl State { // State proof implementations; useful for light client protocols. impl State { /// Prove an account's existence or nonexistence in the state trie. - /// Returns a merkle proof of the account's trie node with all nodes before `from_level` - /// omitted or an encountered trie error. + /// Returns a merkle proof of the account's trie node omitted or an encountered trie error. + /// If the account doesn't exist in the trie, prove that and return defaults. /// Requires a secure trie to be used for accurate results. /// `account_key` == sha3(address) - pub fn prove_account(&self, account_key: H256, from_level: u32) -> trie::Result> { - let mut recorder = Recorder::with_depth(from_level); + pub fn prove_account(&self, account_key: H256) -> trie::Result<(Vec, BasicAccount)> { + let mut recorder = Recorder::new(); let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?; - trie.get_with(&account_key, &mut recorder)?; + let maybe_account: Option = { + let query = (&mut recorder, ::rlp::decode); + trie.get_with(&account_key, query)? + }; + let account = maybe_account.unwrap_or_else(|| BasicAccount { + balance: 0.into(), + nonce: self.account_start_nonce, + code_hash: SHA3_EMPTY, + storage_root: ::util::sha3::SHA3_NULL_RLP, + }); - Ok(recorder.drain().into_iter().map(|r| r.data).collect()) + Ok((recorder.drain().into_iter().map(|r| r.data).collect(), account)) } /// Prove an account's storage key's existence or nonexistence in the state. - /// Returns a merkle proof of the account's storage trie with all nodes before - /// `from_level` omitted. Requires a secure trie to be used for correctness. + /// Returns a merkle proof of the account's storage trie. + /// Requires a secure trie to be used for correctness. /// `account_key` == sha3(address) /// `storage_key` == sha3(key) - pub fn prove_storage(&self, account_key: H256, storage_key: H256, from_level: u32) -> trie::Result> { + pub fn prove_storage(&self, account_key: H256, storage_key: H256) -> trie::Result<(Vec, H256)> { // TODO: probably could look into cache somehow but it's keyed by // address, not sha3(address). let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?; let acc = match trie.get_with(&account_key, Account::from_rlp)? { Some(acc) => acc, - None => return Ok(Vec::new()), + None => return Ok((Vec::new(), H256::new())), }; let account_db = self.factories.accountdb.readonly(self.db.as_hashdb(), account_key); - acc.prove_storage(account_db.as_hashdb(), storage_key, from_level) - } - - /// Get code by address hash. - /// Only works when backed by a secure trie. - pub fn code_by_address_hash(&self, account_key: H256) -> trie::Result> { - let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?; - let mut acc = match trie.get_with(&account_key, Account::from_rlp)? { - Some(acc) => acc, - None => return Ok(None), - }; - - let account_db = self.factories.accountdb.readonly(self.db.as_hashdb(), account_key); - Ok(acc.cache_code(account_db.as_hashdb()).map(|c| (&*c).clone())) + acc.prove_storage(account_db.as_hashdb(), storage_key) } } diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index 8ff08b965..8a99a7239 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -268,7 +268,10 @@ impl LightDispatcher { })); match nonce_future { - Some(x) => x.map(|acc| acc.nonce).map_err(|_| errors::no_light_peers()).boxed(), + Some(x) => + x.map(|acc| acc.map_or_else(Default::default, |acc| acc.nonce)) + .map_err(|_| errors::no_light_peers()) + .boxed(), None => future::err(errors::network_disabled()).boxed() } } diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index c6f0d709d..251daf90d 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -105,15 +105,22 @@ impl EthClient { match cht_root { None => return future::ok(None).boxed(), Some(root) => { - let req = request::HeaderByNumber::new(n, root) + let req = request::HeaderProof::new(n, root) .expect("only fails for 0; client always stores genesis; client already queried; qed"); - self.sync.with_context(|ctx| - self.on_demand.header_by_number(ctx, req) - .map(Some) - .map_err(err_premature_cancel) - .boxed() - ) + let (sync, on_demand) = (self.sync.clone(), self.on_demand.clone()); + self.sync.with_context(|ctx| { + let fut = self.on_demand.hash_by_number(ctx, req) + .map(request::HeaderByHash) + .map_err(err_premature_cancel); + + fut.and_then(move |req| { + match sync.with_context(|ctx| on_demand.header_by_hash(ctx, req)) { + Some(fut) => fut.map_err(err_premature_cancel).boxed(), + None => future::err(errors::network_disabled()).boxed(), + } + }).map(Some).boxed() + }) } } } @@ -149,7 +156,7 @@ impl EthClient { sync.with_context(|ctx| on_demand.account(ctx, request::Account { header: header, address: address, - }).map(Some)) + })) .map(|x| x.map_err(err_premature_cancel).boxed()) .unwrap_or_else(|| future::err(errors::network_disabled()).boxed()) }).boxed() diff --git a/sync/src/light_sync/mod.rs b/sync/src/light_sync/mod.rs index fba89dd7b..4590103e7 100644 --- a/sync/src/light_sync/mod.rs +++ b/sync/src/light_sync/mod.rs @@ -16,7 +16,7 @@ //! Light client synchronization. //! -//! This will synchronize the header chain using LES messages. +//! This will synchronize the header chain using PIP messages. //! Dataflow is largely one-directional as headers are pushed into //! the light client queue for import. Where possible, they are batched //! in groups. @@ -36,14 +36,15 @@ use std::collections::HashMap; use std::mem; use std::sync::Arc; +use ethcore::encoded; use light::client::{AsLightClient, LightChainClient}; use light::net::{ Announcement, Handler, BasicContext, EventContext, - Capabilities, ReqId, Status, + Capabilities, ReqId, Status, Error as NetError, }; -use light::request; +use light::request::{self, CompleteHeadersRequest as HeadersRequest}; use network::PeerId; -use util::{Bytes, U256, H256, Mutex, RwLock}; +use util::{U256, H256, Mutex, RwLock}; use rand::{Rng, OsRng}; use self::sync_round::{AbortReason, SyncRound, ResponseContext}; @@ -91,7 +92,7 @@ impl Peer { #[derive(Debug)] enum AncestorSearch { Queued(u64), // queued to search for blocks starting from here. - Awaiting(ReqId, u64, request::Headers), // awaiting response for this request. + Awaiting(ReqId, u64, HeadersRequest), // awaiting response for this request. Prehistoric, // prehistoric block found. TODO: start to roll back CHTs. FoundCommon(u64, H256), // common block found. Genesis, // common ancestor is the genesis. @@ -113,7 +114,7 @@ impl AncestorSearch { match self { AncestorSearch::Awaiting(id, start, req) => { if &id == ctx.req_id() { - match response::decode_and_verify(ctx.data(), &req) { + match response::verify(ctx.data(), &req) { Ok(headers) => { for header in &headers { if client.is_known(&header.hash()) { @@ -150,17 +151,17 @@ impl AncestorSearch { } fn dispatch_request(self, mut dispatcher: F) -> AncestorSearch - where F: FnMut(request::Headers) -> Option + where F: FnMut(HeadersRequest) -> Option { - const BATCH_SIZE: usize = 64; + const BATCH_SIZE: u64 = 64; match self { AncestorSearch::Queued(start) => { - let batch_size = ::std::cmp::min(start as usize, BATCH_SIZE); + let batch_size = ::std::cmp::min(start, BATCH_SIZE); trace!(target: "sync", "Requesting {} reverse headers from {} to find common ancestor", batch_size, start); - let req = request::Headers { + let req = HeadersRequest { start: start.into(), max: batch_size, skip: 0, @@ -193,13 +194,13 @@ struct ResponseCtx<'a> { peer: PeerId, req_id: ReqId, ctx: &'a BasicContext, - data: &'a [Bytes], + data: &'a [encoded::Header], } impl<'a> ResponseContext for ResponseCtx<'a> { fn responder(&self) -> PeerId { self.peer } fn req_id(&self) -> &ReqId { &self.req_id } - fn data(&self) -> &[Bytes] { self.data } + fn data(&self) -> &[encoded::Header] { self.data } fn punish_responder(&self) { self.ctx.disable_peer(self.peer) } } @@ -313,11 +314,22 @@ impl Handler for LightSync { self.maintain_sync(ctx.as_basic()); } - fn on_block_headers(&self, ctx: &EventContext, req_id: ReqId, headers: &[Bytes]) { - if !self.peers.read().contains_key(&ctx.peer()) { + fn on_responses(&self, ctx: &EventContext, req_id: ReqId, responses: &[request::Response]) { + let peer = ctx.peer(); + if !self.peers.read().contains_key(&peer) { return } + let headers = match responses.get(0) { + Some(&request::Response::Headers(ref response)) => &response.headers[..], + Some(_) => { + trace!("Disabling peer {} for wrong response type.", peer); + ctx.disable_peer(peer); + &[] + } + None => &[], + }; + { let mut state = self.state.lock(); @@ -465,18 +477,27 @@ impl LightSync { // naive request dispatcher: just give to any peer which says it will // give us responses. - let dispatcher = move |req: request::Headers| { + let dispatcher = move |req: HeadersRequest| { rng.shuffle(&mut peer_ids); + let request = { + let mut builder = request::RequestBuilder::default(); + builder.push(request::Request::Headers(request::IncompleteHeadersRequest { + start: req.start.into(), + skip: req.skip, + max: req.max, + reverse: req.reverse, + })).expect("request provided fully complete with no unresolved back-references; qed"); + builder.build() + }; for peer in &peer_ids { - if ctx.max_requests(*peer, request::Kind::Headers) >= req.max { - match ctx.request_from(*peer, request::Request::Headers(req.clone())) { - Ok(id) => { - return Some(id) - } - Err(e) => - trace!(target: "sync", "Error requesting headers from viable peer: {}", e), + match ctx.request_from(*peer, request.clone()) { + Ok(id) => { + return Some(id) } + Err(NetError::NoCredits) => {} + Err(e) => + trace!(target: "sync", "Error requesting headers from viable peer: {}", e), } } diff --git a/sync/src/light_sync/response.rs b/sync/src/light_sync/response.rs index 0629da956..d85d2548d 100644 --- a/sync/src/light_sync/response.rs +++ b/sync/src/light_sync/response.rs @@ -18,10 +18,11 @@ use std::fmt; +use ethcore::encoded; use ethcore::header::Header; -use light::request::{HashOrNumber, Headers as HeadersRequest}; -use rlp::{DecoderError, UntrustedRlp}; -use util::{Bytes, H256}; +use light::request::{HashOrNumber, CompleteHeadersRequest as HeadersRequest}; +use rlp::DecoderError; +use util::H256; /// Errors found when decoding headers and verifying with basic constraints. #[derive(Debug, PartialEq)] @@ -71,13 +72,13 @@ pub trait Constraint { fn verify(&self, headers: &[Header], reverse: bool) -> Result<(), Self::Error>; } -/// Decode a response and do basic verification against a request. -pub fn decode_and_verify(headers: &[Bytes], request: &HeadersRequest) -> Result, BasicError> { - let headers: Vec<_> = try!(headers.iter().map(|x| UntrustedRlp::new(&x).as_val()).collect()); +/// Do basic verification of provided headers against a request. +pub fn verify(headers: &[encoded::Header], request: &HeadersRequest) -> Result, BasicError> { + let headers: Vec<_> = headers.iter().map(|h| h.decode()).collect(); let reverse = request.reverse; - try!(Max(request.max).verify(&headers, reverse)); + try!(Max(request.max as usize).verify(&headers, reverse)); match request.start { HashOrNumber::Number(ref num) => try!(StartsAtNumber(*num).verify(&headers, reverse)), HashOrNumber::Hash(ref hash) => try!(StartsAtHash(*hash).verify(&headers, reverse)), @@ -150,8 +151,9 @@ impl Constraint for Max { #[cfg(test)] mod tests { + use ethcore::encoded; use ethcore::header::Header; - use light::request::Headers as HeadersRequest; + use light::request::CompleteHeadersRequest as HeadersRequest; use super::*; @@ -175,10 +177,10 @@ mod tests { parent_hash = Some(header.hash()); - ::rlp::encode(&header).to_vec() + encoded::Header::new(::rlp::encode(&header).to_vec()) }).collect(); - assert!(decode_and_verify(&headers, &request).is_ok()); + assert!(verify(&headers, &request).is_ok()); } #[test] @@ -201,10 +203,10 @@ mod tests { parent_hash = Some(header.hash()); - ::rlp::encode(&header).to_vec() + encoded::Header::new(::rlp::encode(&header).to_vec()) }).collect(); - assert!(decode_and_verify(&headers, &request).is_ok()); + assert!(verify(&headers, &request).is_ok()); } #[test] @@ -227,10 +229,10 @@ mod tests { parent_hash = Some(header.hash()); - ::rlp::encode(&header).to_vec() + encoded::Header::new(::rlp::encode(&header).to_vec()) }).collect(); - assert_eq!(decode_and_verify(&headers, &request), Err(BasicError::TooManyHeaders(20, 25))); + assert_eq!(verify(&headers, &request), Err(BasicError::TooManyHeaders(20, 25))); } #[test] @@ -246,9 +248,9 @@ mod tests { let mut header = Header::default(); header.set_number(x); - ::rlp::encode(&header).to_vec() + encoded::Header::new(::rlp::encode(&header).to_vec()) }).collect(); - assert_eq!(decode_and_verify(&headers, &request), Err(BasicError::WrongSkip(5, Some(2)))); + assert_eq!(verify(&headers, &request), Err(BasicError::WrongSkip(5, Some(2)))); } } diff --git a/sync/src/light_sync/sync_round.rs b/sync/src/light_sync/sync_round.rs index 6fa635214..dfa17aad4 100644 --- a/sync/src/light_sync/sync_round.rs +++ b/sync/src/light_sync/sync_round.rs @@ -20,13 +20,14 @@ use std::cmp::Ordering; use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque}; use std::fmt; +use ethcore::encoded; use ethcore::header::Header; use light::net::ReqId; -use light::request::Headers as HeadersRequest; +use light::request::CompleteHeadersRequest as HeadersRequest; use network::PeerId; -use util::{Bytes, H256}; +use util::H256; use super::response; @@ -40,7 +41,7 @@ pub trait ResponseContext { /// Get the request ID this response corresponds to. fn req_id(&self) -> &ReqId; /// Get the (unverified) response data. - fn data(&self) -> &[Bytes]; + fn data(&self) -> &[encoded::Header]; /// Punish the responder. fn punish_responder(&self); } @@ -114,7 +115,7 @@ impl Fetcher { let needed_headers = HeadersRequest { start: high_rung.parent_hash().clone().into(), - max: diff as usize - 1, + max: diff - 1, skip: 0, reverse: true, }; @@ -190,7 +191,7 @@ impl Fetcher { return SyncRound::Fetch(self); } - match response::decode_and_verify(headers, &request.headers_request) { + match response::verify(headers, &request.headers_request) { Err(e) => { trace!(target: "sync", "Punishing peer {} for invalid response ({})", ctx.responder(), e); ctx.punish_responder(); @@ -286,21 +287,21 @@ impl Fetcher { } // Compute scaffold parameters from non-zero distance between start and target block: (skip, pivots). -fn scaffold_params(diff: u64) -> (u64, usize) { +fn scaffold_params(diff: u64) -> (u64, u64) { // default parameters. // amount of blocks between each scaffold pivot. const ROUND_SKIP: u64 = 255; // amount of scaffold pivots: these are the Xs in "X___X___X" - const ROUND_PIVOTS: usize = 256; + const ROUND_PIVOTS: u64 = 256; let rem = diff % (ROUND_SKIP + 1); if diff <= ROUND_SKIP { // just request headers from the start to the target. - (0, rem as usize) + (0, rem) } else { // the number of pivots necessary to exactly hit or overshoot the target. let pivots_to_target = (diff / (ROUND_SKIP + 1)) + if rem == 0 { 0 } else { 1 }; - let num_pivots = ::std::cmp::min(pivots_to_target, ROUND_PIVOTS as u64) as usize; + let num_pivots = ::std::cmp::min(pivots_to_target, ROUND_PIVOTS); (ROUND_SKIP, num_pivots) } } @@ -319,7 +320,7 @@ pub struct RoundStart { contributors: HashSet, attempt: usize, skip: u64, - pivots: usize, + pivots: u64, } impl RoundStart { @@ -372,7 +373,7 @@ impl RoundStart { } }; - match response::decode_and_verify(ctx.data(), &req) { + match response::verify(ctx.data(), &req) { Ok(headers) => { if self.sparse_headers.len() == 0 && headers.get(0).map_or(false, |x| x.parent_hash() != &self.start_block.1) { @@ -383,7 +384,7 @@ impl RoundStart { self.contributors.insert(ctx.responder()); self.sparse_headers.extend(headers); - if self.sparse_headers.len() == self.pivots { + if self.sparse_headers.len() as u64 == self.pivots { return if self.skip == 0 { SyncRound::abort(AbortReason::TargetReached, self.sparse_headers.into()) } else { @@ -429,7 +430,7 @@ impl RoundStart { let start = (self.start_block.0 + 1) + self.sparse_headers.len() as u64 * (self.skip + 1); - let max = self.pivots - self.sparse_headers.len(); + let max = self.pivots - self.sparse_headers.len() as u64; let headers_request = HeadersRequest { start: start.into(), diff --git a/sync/src/light_sync/tests/test_net.rs b/sync/src/light_sync/tests/test_net.rs index d0e472374..898f8766d 100644 --- a/sync/src/light_sync/tests/test_net.rs +++ b/sync/src/light_sync/tests/test_net.rs @@ -28,6 +28,7 @@ use io::IoChannel; use light::client::Client as LightClient; use light::net::{LightProtocol, IoContext, Capabilities, Params as LightParams}; use light::net::request_credits::FlowParams; +use light::provider::LightProvider; use network::{NodeId, PeerId}; use util::RwLock; @@ -71,7 +72,7 @@ enum PeerData { } // test peer type. -// Either a full peer or a LES peer. +// Either a full peer or a light peer. pub struct Peer { proto: LightProtocol, queue: RwLock>, @@ -115,7 +116,8 @@ impl Peer { }, }; - let mut proto = LightProtocol::new(chain.clone(), params); + let provider = LightProvider::new(chain.clone(), Arc::new(RwLock::new(Default::default()))); + let mut proto = LightProtocol::new(Arc::new(provider), params); proto.add_handler(sync.clone()); Peer { proto: proto, From 9fdd0e3a0a279b8b8574192c80374c2dcdb09619 Mon Sep 17 00:00:00 2001 From: keorn Date: Thu, 23 Mar 2017 12:19:28 +0000 Subject: [PATCH 19/41] Switching ValidatorSet (#4961) * add multi validator set * nicer comment * validate in constructor * reporting --- ethcore/res/validator_multi.json | 42 ++++++ ethcore/src/engines/authority_round.rs | 2 +- ethcore/src/engines/basic_authority.rs | 2 +- ethcore/src/engines/tendermint/mod.rs | 2 +- ethcore/src/engines/validator_set/mod.rs | 15 +- ethcore/src/engines/validator_set/multi.rs | 158 +++++++++++++++++++++ ethcore/src/spec/spec.rs | 4 + json/src/spec/validator_set.rs | 17 ++- 8 files changed, 231 insertions(+), 11 deletions(-) create mode 100644 ethcore/res/validator_multi.json create mode 100644 ethcore/src/engines/validator_set/multi.rs diff --git a/ethcore/res/validator_multi.json b/ethcore/res/validator_multi.json new file mode 100644 index 000000000..5d51b73da --- /dev/null +++ b/ethcore/res/validator_multi.json @@ -0,0 +1,42 @@ +{ + "name": "TestMutiValidator", + "engine": { + "basicAuthority": { + "params": { + "gasLimitBoundDivisor": "0x0400", + "durationLimit": "0x0d", + "validators": { + "multi": { + "0": { "list": ["0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"] }, + "2": { "list": ["0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e"] } + } + } + } + } + }, + "params": { + "accountStartNonce": "0x0", + "maximumExtraDataSize": "0x20", + "minGasLimit": "0x1388", + "networkID" : "0x69" + }, + "genesis": { + "seal": { + "generic": "0xc180" + }, + "difficulty": "0x20000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" + }, + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1": { "balance": "99999999999999999999999" }, + "0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e": { "balance": "99999999999999999999999" } + } +} diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 2a18c748d..4f823fa8d 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -82,7 +82,7 @@ pub struct AuthorityRound { proposed: AtomicBool, client: RwLock>>, signer: EngineSigner, - validators: Box, + validators: Box, /// Is this Engine just for testing (prevents step calibration). calibrate_step: bool, } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index d8a1df947..e5a53d4e9 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -58,7 +58,7 @@ pub struct BasicAuthority { gas_limit_bound_divisor: U256, builtins: BTreeMap, signer: EngineSigner, - validators: Box, + validators: Box, } impl BasicAuthority { diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 46e67a2a8..464e102de 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -98,7 +98,7 @@ pub struct Tendermint { /// Hash of the proposal parent block. proposal_parent: RwLock, /// Set used to determine the current validators. - validators: Box, + validators: Box, } impl Tendermint { diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index 3e86c357f..cbbedfb33 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -19,6 +19,7 @@ mod simple_list; mod safe_contract; mod contract; +mod multi; use std::sync::Weak; use util::{Address, H256}; @@ -27,23 +28,27 @@ use client::Client; use self::simple_list::SimpleList; use self::contract::ValidatorContract; use self::safe_contract::ValidatorSafeContract; +use self::multi::Multi; /// Creates a validator set from spec. -pub fn new_validator_set(spec: ValidatorSpec) -> Box { +pub fn new_validator_set(spec: ValidatorSpec) -> Box { match spec { ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())), ValidatorSpec::SafeContract(address) => Box::new(ValidatorSafeContract::new(address.into())), ValidatorSpec::Contract(address) => Box::new(ValidatorContract::new(address.into())), + ValidatorSpec::Multi(sequence) => Box::new( + Multi::new(sequence.into_iter().map(|(block, set)| (block.into(), new_validator_set(set))).collect()) + ), } } -pub trait ValidatorSet { +pub trait ValidatorSet: Send + Sync { /// Checks if a given address is a validator. - fn contains(&self, bh: &H256, address: &Address) -> bool; + fn contains(&self, parent_block_hash: &H256, address: &Address) -> bool; /// Draws an validator nonce modulo number of validators. - fn get(&self, bh: &H256, nonce: usize) -> Address; + fn get(&self, parent_block_hash: &H256, nonce: usize) -> Address; /// Returns the current number of validators. - fn count(&self, bh: &H256) -> usize; + fn count(&self, parent_block_hash: &H256) -> usize; /// Notifies about malicious behaviour. fn report_malicious(&self, _validator: &Address) {} /// Notifies about benign misbehaviour. diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs new file mode 100644 index 000000000..5027f23cd --- /dev/null +++ b/ethcore/src/engines/validator_set/multi.rs @@ -0,0 +1,158 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +/// Validator set changing at fork blocks. + +use std::collections::BTreeMap; +use std::sync::Weak; +use util::{H256, Address, RwLock}; +use ids::BlockId; +use header::BlockNumber; +use client::{Client, BlockChainClient}; +use super::ValidatorSet; + +type BlockNumberLookup = Box Result + Send + Sync + 'static>; + +pub struct Multi { + sets: BTreeMap>, + block_number: RwLock, +} + +impl Multi { + pub fn new(set_map: BTreeMap>) -> Self { + assert!(set_map.get(&0u64).is_some(), "ValidatorSet has to be specified from block 0."); + Multi { + sets: set_map, + block_number: RwLock::new(Box::new(move |_| Err("No client!".into()))), + } + } + + fn correct_set(&self, bh: &H256) -> Option<&Box> { + match self + .block_number + .read()(bh) + .map(|parent_block| self + .sets + .iter() + .rev() + .find(|&(block, _)| *block <= parent_block + 1) + .expect("constructor validation ensures that there is at least one validator set for block 0; + block 0 is less than any uint; + qed") + ) { + Ok((block, set)) => { + trace!(target: "engine", "Multi ValidatorSet retrieved for block {}.", block); + Some(set) + }, + Err(e) => { + debug!(target: "engine", "ValidatorSet could not be recovered: {}", e); + None + }, + } + } +} + +impl ValidatorSet for Multi { + fn contains(&self, bh: &H256, address: &Address) -> bool { + self.correct_set(bh).map_or(false, |set| set.contains(bh, address)) + } + + fn get(&self, bh: &H256, nonce: usize) -> Address { + self.correct_set(bh).map_or_else(Default::default, |set| set.get(bh, nonce)) + } + + fn count(&self, bh: &H256) -> usize { + self.correct_set(bh).map_or_else(usize::max_value, |set| set.count(bh)) + } + + fn report_malicious(&self, validator: &Address) { + for set in self.sets.values() { + set.report_malicious(validator); + } + } + + fn report_benign(&self, validator: &Address) { + for set in self.sets.values() { + set.report_benign(validator); + } + } + + fn register_contract(&self, client: Weak) { + for set in self.sets.values() { + set.register_contract(client.clone()); + } + *self.block_number.write() = Box::new(move |hash| client + .upgrade() + .ok_or("No client!".into()) + .and_then(|c| c.block_number(BlockId::Hash(*hash)).ok_or("Unknown block".into()))); + } +} + +#[cfg(test)] +mod tests { + use util::*; + use types::ids::BlockId; + use spec::Spec; + use account_provider::AccountProvider; + use client::{BlockChainClient, EngineClient}; + use ethkey::Secret; + use miner::MinerService; + use tests::helpers::{generate_dummy_client_with_spec_and_accounts, generate_dummy_client_with_spec_and_data}; + + #[test] + fn uses_current_set() { + ::env_logger::init().unwrap(); + let tap = Arc::new(AccountProvider::transient_provider()); + let s0 = Secret::from_slice(&"0".sha3()).unwrap(); + let v0 = tap.insert_account(s0.clone(), "").unwrap(); + let v1 = tap.insert_account(Secret::from_slice(&"1".sha3()).unwrap(), "").unwrap(); + let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_multi, Some(tap)); + client.engine().register_client(Arc::downgrade(&client)); + + // Make sure txs go through. + client.miner().set_gas_floor_target(1_000_000.into()); + + // Wrong signer for the first block. + client.miner().set_engine_signer(v1, "".into()).unwrap(); + client.transact_contract(Default::default(), Default::default()).unwrap(); + client.update_sealing(); + assert_eq!(client.chain_info().best_block_number, 0); + // Right signer for the first block. + client.miner().set_engine_signer(v0, "".into()).unwrap(); + client.update_sealing(); + assert_eq!(client.chain_info().best_block_number, 1); + // This time v0 is wrong. + client.transact_contract(Default::default(), Default::default()).unwrap(); + client.update_sealing(); + assert_eq!(client.chain_info().best_block_number, 1); + client.miner().set_engine_signer(v1, "".into()).unwrap(); + client.update_sealing(); + assert_eq!(client.chain_info().best_block_number, 2); + // v1 is still good. + client.transact_contract(Default::default(), Default::default()).unwrap(); + client.update_sealing(); + assert_eq!(client.chain_info().best_block_number, 3); + + // Check syncing. + let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_multi, 0, 0, &[]); + sync_client.engine().register_client(Arc::downgrade(&sync_client)); + for i in 1..4 { + sync_client.import_block(client.block(BlockId::Number(i)).unwrap().into_inner()).unwrap(); + } + sync_client.flush_queue(); + assert_eq!(sync_client.chain_info().best_block_number, 3); + } +} diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 21c07c9a3..455d0745f 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -360,6 +360,10 @@ impl Spec { /// Account is marked with `reportBenign` it can be checked as disliked with "0xd8f2e0bf". /// Validator can be removed with `reportMalicious`. pub fn new_validator_contract() -> Self { load_bundled!("validator_contract") } + + /// Create a new Spec with BasicAuthority which uses multiple validator sets changing with height. + /// Account with secrets "0".sha3() is the validator for block 1 and with "1".sha3() onwards. + pub fn new_validator_multi() -> Self { load_bundled!("validator_multi") } } #[cfg(test)] diff --git a/json/src/spec/validator_set.rs b/json/src/spec/validator_set.rs index 080a36c50..f433caa03 100644 --- a/json/src/spec/validator_set.rs +++ b/json/src/spec/validator_set.rs @@ -16,6 +16,8 @@ //! Validator set deserialization. +use std::collections::BTreeMap; +use uint::Uint; use hash::Address; /// Different ways of specifying validators. @@ -30,6 +32,9 @@ pub enum ValidatorSet { /// Address of a contract that indicates the list of authorities and enables reporting of theor misbehaviour using transactions. #[serde(rename="contract")] Contract(Address), + /// A map of starting blocks for each validator set. + #[serde(rename="multi")] + Multi(BTreeMap), } #[cfg(test)] @@ -40,11 +45,17 @@ mod tests { #[test] fn validator_set_deserialization() { let s = r#"[{ - "list" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] + "list": ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] }, { - "safeContract" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + "safeContract": "0xc6d9d2cd449a754c494264e1809c50e34d64562b" }, { - "contract" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + "contract": "0xc6d9d2cd449a754c494264e1809c50e34d64562b" + }, { + "multi": { + "0": { "list": ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"] }, + "10": { "list": ["0xd6d9d2cd449a754c494264e1809c50e34d64562b"] }, + "20": { "contract": "0xc6d9d2cd449a754c494264e1809c50e34d64562b" } + } }]"#; let _deserialized: Vec = serde_json::from_str(s).unwrap(); From bb1bbebfd63b5870478ff4bcd60d4a1ac1e00d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 23 Mar 2017 13:23:03 +0100 Subject: [PATCH 20/41] Export account RPC (#4967) * Export account RPC * Removing GethDirectory and ParityDirectory * Updating ethstore-cli help. --- ethcore/src/account_provider/mod.rs | 13 ++- ethstore/src/account/safe_account.rs | 14 +++ ethstore/src/bin/ethstore.rs | 18 ++-- ethstore/src/dir/geth.rs | 102 --------------------- ethstore/src/dir/memory.rs | 1 + ethstore/src/dir/mod.rs | 14 +-- ethstore/src/dir/parity.rs | 81 ---------------- ethstore/src/dir/paths.rs | 96 +++++++++++++++++++ ethstore/src/error.rs | 17 ++++ ethstore/src/ethstore.rs | 99 +++++++++++--------- ethstore/src/import.rs | 21 +---- ethstore/src/json/key_file.rs | 22 ++++- ethstore/src/json/mod.rs | 2 +- ethstore/src/lib.rs | 8 +- ethstore/src/presale.rs | 3 + ethstore/src/secret_store.rs | 28 +++++- rpc/src/v1/impls/parity_accounts.rs | 12 +++ rpc/src/v1/tests/mocked/parity_accounts.rs | 27 ++++++ rpc/src/v1/traits/parity_accounts.rs | 5 + 19 files changed, 318 insertions(+), 265 deletions(-) delete mode 100755 ethstore/src/dir/geth.rs delete mode 100755 ethstore/src/dir/parity.rs create mode 100644 ethstore/src/dir/paths.rs diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs index f9b7727db..0ecbf3b17 100755 --- a/ethcore/src/account_provider/mod.rs +++ b/ethcore/src/account_provider/mod.rs @@ -24,14 +24,16 @@ use std::fmt; use std::collections::{HashMap, HashSet}; use std::time::{Instant, Duration}; use util::{RwLock}; -use ethstore::{SimpleSecretStore, SecretStore, Error as SSError, EthStore, EthMultiStore, - random_string, SecretVaultRef, StoreAccountRef}; +use ethstore::{ + SimpleSecretStore, SecretStore, Error as SSError, EthStore, EthMultiStore, + random_string, SecretVaultRef, StoreAccountRef, +}; use ethstore::dir::MemoryDirectory; use ethstore::ethkey::{Address, Message, Public, Secret, Random, Generator}; use ethjson::misc::AccountMeta; use hardware_wallet::{Error as HardwareError, HardwareWalletManager, KeyPath}; pub use ethstore::ethkey::Signature; -pub use ethstore::{Derivation, IndexDerivation}; +pub use ethstore::{Derivation, IndexDerivation, KeyFile}; /// Type of unlock. #[derive(Clone)] @@ -500,6 +502,11 @@ impl AccountProvider { self.sstore.change_password(&self.sstore.account_ref(address)?, &password, &new_password) } + /// Exports an account for given address. + pub fn export_account(&self, address: &Address, password: String) -> Result { + self.sstore.export_account(&self.sstore.account_ref(address)?, &password) + } + /// Helper method used for unlocking accounts. fn unlock_account(&self, address: Address, password: String, unlock: Unlock) -> Result<(), Error> { // verify password by signing dump message diff --git a/ethstore/src/account/safe_account.rs b/ethstore/src/account/safe_account.rs index d628b56ac..e0512fe8d 100755 --- a/ethstore/src/account/safe_account.rs +++ b/ethstore/src/account/safe_account.rs @@ -19,14 +19,22 @@ use {json, Error, crypto}; use account::Version; use super::crypto::Crypto; +/// Account representation. #[derive(Debug, PartialEq, Clone)] pub struct SafeAccount { + /// Account ID pub id: [u8; 16], + /// Account version pub version: Version, + /// Account address pub address: Address, + /// Account private key derivation definition. pub crypto: Crypto, + /// Account filename pub filename: Option, + /// Account name pub name: String, + /// Account metadata pub meta: String, } @@ -44,6 +52,7 @@ impl Into for SafeAccount { } impl SafeAccount { + /// Create a new account pub fn create( keypair: &KeyPair, id: [u8; 16], @@ -114,21 +123,25 @@ impl SafeAccount { }) } + /// Sign a message. pub fn sign(&self, password: &str, message: &Message) -> Result { let secret = self.crypto.secret(password)?; sign(&secret, message).map_err(From::from) } + /// Decrypt a message. pub fn decrypt(&self, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error> { let secret = self.crypto.secret(password)?; crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from) } + /// Derive public key. pub fn public(&self, password: &str) -> Result { let secret = self.crypto.secret(password)?; Ok(KeyPair::from_secret(secret)?.public().clone()) } + /// Change account's password. pub fn change_password(&self, old_password: &str, new_password: &str, iterations: u32) -> Result { let secret = self.crypto.secret(old_password)?; let result = SafeAccount { @@ -143,6 +156,7 @@ impl SafeAccount { Ok(result) } + /// Check if password matches the account. pub fn check_password(&self, password: &str) -> bool { self.crypto.secret(password).is_ok() } diff --git a/ethstore/src/bin/ethstore.rs b/ethstore/src/bin/ethstore.rs index 20411a629..3e8df3a35 100644 --- a/ethstore/src/bin/ethstore.rs +++ b/ethstore/src/bin/ethstore.rs @@ -22,7 +22,7 @@ use std::{env, process, fs}; use std::io::Read; use docopt::Docopt; use ethstore::ethkey::Address; -use ethstore::dir::{KeyDirectory, ParityDirectory, RootDiskDirectory, GethDirectory, DirectoryType}; +use ethstore::dir::{paths, KeyDirectory, RootDiskDirectory}; use ethstore::{EthStore, SimpleSecretStore, SecretStore, import_accounts, Error, PresaleWallet, SecretVaultRef, StoreAccountRef}; @@ -49,14 +49,14 @@ Usage: Options: -h, --help Display this message and exit. --dir DIR Specify the secret store directory. It may be either - parity, parity-test, geth, geth-test + parity, parity-(chain), geth, geth-test or a path [default: parity]. --vault VAULT Specify vault to use in this operation. --vault-pwd VAULTPWD Specify vault password to use in this operation. Please note that this option is required when vault option is set. Otherwise it is ignored. --src DIR Specify import source. It may be either - parity, parity-test, get, geth-test + parity, parity-(chain), get, geth-test or a path [default: geth]. Commands: @@ -116,10 +116,13 @@ fn main() { fn key_dir(location: &str) -> Result, Error> { let dir: Box = match location { - "parity" => Box::new(ParityDirectory::create(DirectoryType::Main)?), - "parity-test" => Box::new(ParityDirectory::create(DirectoryType::Testnet)?), - "geth" => Box::new(GethDirectory::create(DirectoryType::Main)?), - "geth-test" => Box::new(GethDirectory::create(DirectoryType::Testnet)?), + "geth" => Box::new(RootDiskDirectory::create(paths::geth(false))?), + "geth-test" => Box::new(RootDiskDirectory::create(paths::geth(true))?), + path if path.starts_with("parity") => { + let chain = path.split('-').nth(1).unwrap_or("ethereum"); + let path = paths::parity(chain); + Box::new(RootDiskDirectory::create(path)?) + }, path => Box::new(RootDiskDirectory::create(path)?), }; @@ -254,4 +257,3 @@ fn execute(command: I) -> Result where I: IntoIterator. - -use std::env; -use std::path::PathBuf; -use {SafeAccount, Error}; -use super::{KeyDirectory, RootDiskDirectory, DirectoryType}; - -#[cfg(target_os = "macos")] -fn geth_dir_path() -> PathBuf { - let mut home = env::home_dir().expect("Failed to get home dir"); - home.push("Library"); - home.push("Ethereum"); - home -} - -#[cfg(windows)] -/// Default path for ethereum installation on Windows -pub fn geth_dir_path() -> PathBuf { - let mut home = env::home_dir().expect("Failed to get home dir"); - home.push("AppData"); - home.push("Roaming"); - home.push("Ethereum"); - home -} - -#[cfg(not(any(target_os = "macos", windows)))] -/// Default path for ethereum installation on posix system which is not Mac OS -pub fn geth_dir_path() -> PathBuf { - let mut home = env::home_dir().expect("Failed to get home dir"); - home.push(".ethereum"); - home -} - -fn geth_keystore(t: DirectoryType) -> PathBuf { - let mut dir = geth_dir_path(); - match t { - DirectoryType::Testnet => { - dir.push("testnet"); - dir.push("keystore"); - }, - DirectoryType::Main => { - dir.push("keystore"); - } - } - dir -} - -pub struct GethDirectory { - dir: RootDiskDirectory, -} - -impl GethDirectory { - pub fn create(t: DirectoryType) -> Result { - let result = GethDirectory { - dir: RootDiskDirectory::create(geth_keystore(t))?, - }; - - Ok(result) - } - - pub fn open(t: DirectoryType) -> Self { - GethDirectory { - dir: RootDiskDirectory::at(geth_keystore(t)), - } - } -} - -impl KeyDirectory for GethDirectory { - fn load(&self) -> Result, Error> { - self.dir.load() - } - - fn insert(&self, account: SafeAccount) -> Result { - self.dir.insert(account) - } - - fn update(&self, account: SafeAccount) -> Result { - self.dir.update(account) - } - - fn remove(&self, account: &SafeAccount) -> Result<(), Error> { - self.dir.remove(account) - } - - fn unique_repr(&self) -> Result { - self.dir.unique_repr() - } -} diff --git a/ethstore/src/dir/memory.rs b/ethstore/src/dir/memory.rs index 955afc5b0..b8c2ad9ff 100644 --- a/ethstore/src/dir/memory.rs +++ b/ethstore/src/dir/memory.rs @@ -22,6 +22,7 @@ use ethkey::Address; use {SafeAccount, Error}; use super::KeyDirectory; +/// Accounts in-memory storage. #[derive(Default)] pub struct MemoryDirectory { accounts: RwLock>>, diff --git a/ethstore/src/dir/mod.rs b/ethstore/src/dir/mod.rs index 83e978707..fb22c06ee 100755 --- a/ethstore/src/dir/mod.rs +++ b/ethstore/src/dir/mod.rs @@ -14,19 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Accounts Directory + use std::path::{PathBuf}; use {SafeAccount, Error}; mod disk; -mod geth; mod memory; -mod parity; mod vault; - -pub enum DirectoryType { - Testnet, - Main, -} +pub mod paths; /// `VaultKeyDirectory::set_key` error #[derive(Debug)] @@ -54,7 +50,7 @@ pub trait KeyDirectory: Send + Sync { fn load(&self) -> Result, Error>; /// Insert new key to directory fn insert(&self, account: SafeAccount) -> Result; - //// Update key in directory + /// Update key in the directory fn update(&self, account: SafeAccount) -> Result; /// Remove key from directory fn remove(&self, account: &SafeAccount) -> Result<(), Error>; @@ -95,9 +91,7 @@ pub trait VaultKeyDirectory: KeyDirectory { } pub use self::disk::RootDiskDirectory; -pub use self::geth::GethDirectory; pub use self::memory::MemoryDirectory; -pub use self::parity::ParityDirectory; pub use self::vault::VaultDiskDirectory; impl VaultKey { diff --git a/ethstore/src/dir/parity.rs b/ethstore/src/dir/parity.rs deleted file mode 100755 index df03260d3..000000000 --- a/ethstore/src/dir/parity.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use std::env; -use std::path::PathBuf; -use {SafeAccount, Error}; -use super::{KeyDirectory, RootDiskDirectory, DirectoryType}; - -fn parity_dir_path() -> PathBuf { - let mut home = env::home_dir().expect("Failed to get home dir"); - home.push(".parity"); - home -} - -fn parity_keystore(t: DirectoryType) -> PathBuf { - let mut dir = parity_dir_path(); - match t { - DirectoryType::Testnet => { - dir.push("testnet_keys"); - }, - DirectoryType::Main => { - dir.push("keys"); - } - } - dir -} - -pub struct ParityDirectory { - dir: RootDiskDirectory, -} - -impl ParityDirectory { - pub fn create(t: DirectoryType) -> Result { - let result = ParityDirectory { - dir: RootDiskDirectory::create(parity_keystore(t))?, - }; - - Ok(result) - } - - pub fn open(t: DirectoryType) -> Self { - ParityDirectory { - dir: RootDiskDirectory::at(parity_keystore(t)), - } - } -} - -impl KeyDirectory for ParityDirectory { - fn load(&self) -> Result, Error> { - self.dir.load() - } - - fn insert(&self, account: SafeAccount) -> Result { - self.dir.insert(account) - } - - fn update(&self, account: SafeAccount) -> Result { - self.dir.update(account) - } - - fn remove(&self, account: &SafeAccount) -> Result<(), Error> { - self.dir.remove(account) - } - - fn unique_repr(&self) -> Result { - self.dir.unique_repr() - } -} diff --git a/ethstore/src/dir/paths.rs b/ethstore/src/dir/paths.rs new file mode 100644 index 000000000..db3178cff --- /dev/null +++ b/ethstore/src/dir/paths.rs @@ -0,0 +1,96 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! Common tools paths. + +use std::env; +use std::path::PathBuf; + +fn home() -> PathBuf { + env::home_dir().expect("Failed to get home dir") +} + +/// Geth path +pub fn geth(testnet: bool) -> PathBuf { + let mut base = geth_base(); + if testnet { + base.push("testnet"); + } + base.push("keystore"); + base +} + +/// Parity path for specific chain +pub fn parity(chain: &str) -> PathBuf { + let mut base = parity_base(); + base.push(chain); + base +} + +#[cfg(target_os = "macos")] +fn parity_base() -> PathBuf { + let mut home = home(); + home.push("Library"); + home.push("Application Support"); + home.push("io.parity.ethereum"); + home.push("keys"); + home +} + +#[cfg(windows)] +fn parity_base() -> PathBuf { + let mut home = home(); + home.push("AppData"); + home.push("Roaming"); + home.push("Parity"); + home.push("Ethereum"); + home.push("keys"); + home +} + +#[cfg(not(any(target_os = "macos", windows)))] +fn parity_base() -> PathBuf { + let mut home = home(); + home.push(".local"); + home.push("share"); + home.push("io.parity.ethereum"); + home.push("keys"); + home +} + +#[cfg(target_os = "macos")] +fn geth_base() -> PathBuf { + let mut home = home(); + home.push("Library"); + home.push("Ethereum"); + home +} + +#[cfg(windows)] +fn geth_base() -> PathBuf { + let mut home = home(); + home.push("AppData"); + home.push("Roaming"); + home.push("Ethereum"); + home +} + +#[cfg(not(any(target_os = "macos", windows)))] +fn geth_base() -> PathBuf { + let mut home = home(); + home.push(".ethereum"); + home +} diff --git a/ethstore/src/error.rs b/ethstore/src/error.rs index 8a2eb5e8b..f7e0b0bfa 100755 --- a/ethstore/src/error.rs +++ b/ethstore/src/error.rs @@ -20,23 +20,40 @@ use ethkey::Error as EthKeyError; use crypto::Error as EthCryptoError; use ethkey::DerivationError; +/// Account-related errors. #[derive(Debug)] pub enum Error { + /// IO error Io(IoError), + /// Invalid Password InvalidPassword, + /// Account's secret is invalid. InvalidSecret, + /// Invalid Vault Crypto meta. InvalidCryptoMeta, + /// Invalid Account. InvalidAccount, + /// Invalid Message. InvalidMessage, + /// Invalid Key File InvalidKeyFile(String), + /// Vaults are not supported. VaultsAreNotSupported, + /// Unsupported vault UnsupportedVault, + /// Invalid vault name InvalidVaultName, + /// Vault not found VaultNotFound, + /// Account creation failed. CreationFailed, + /// `EthKey` error EthKey(EthKeyError), + /// `EthCrypto` error EthCrypto(EthCryptoError), + /// Derivation error Derivation(DerivationError), + /// Custom error Custom(String), } diff --git a/ethstore/src/ethstore.rs b/ethstore/src/ethstore.rs index cacb6054f..5fb76791e 100755 --- a/ethstore/src/ethstore.rs +++ b/ethstore/src/ethstore.rs @@ -25,18 +25,21 @@ use ethkey::{self, Signature, Address, Message, Secret, Public, KeyPair, Extende use dir::{KeyDirectory, VaultKeyDirectory, VaultKey, SetKeyError}; use account::SafeAccount; use presale::PresaleWallet; -use json::{self, Uuid}; +use json::{self, Uuid, OpaqueKeyFile}; use {import, Error, SimpleSecretStore, SecretStore, SecretVaultRef, StoreAccountRef, Derivation}; +/// Accounts store. pub struct EthStore { store: EthMultiStore, } impl EthStore { + /// Open a new accounts store with given key directory backend. pub fn open(directory: Box) -> Result { Self::open_with_iterations(directory, KEY_ITERATIONS as u32) } + /// Open a new account store with given key directory backend and custom number of iterations. pub fn open_with_iterations(directory: Box, iterations: u32) -> Result { Ok(EthStore { store: EthMultiStore::open_with_iterations(directory, iterations)?, @@ -44,7 +47,7 @@ impl EthStore { } fn get(&self, account: &StoreAccountRef) -> Result { - let mut accounts = self.store.get(account)?.into_iter(); + let mut accounts = self.store.get_accounts(account)?.into_iter(); accounts.next().ok_or(Error::InvalidAccount) } } @@ -76,6 +79,10 @@ impl SimpleSecretStore for EthStore { self.store.change_password(account, old_password, new_password) } + fn export_account(&self, account: &StoreAccountRef, password: &str) -> Result { + self.store.export_account(account, password) + } + fn remove_account(&self, account: &StoreAccountRef, password: &str) -> Result<(), Error> { self.store.remove_account(account, password) } @@ -234,11 +241,12 @@ pub struct EthMultiStore { } impl EthMultiStore { - + /// Open new multi-accounts store with given key directory backend. pub fn open(directory: Box) -> Result { Self::open_with_iterations(directory, KEY_ITERATIONS as u32) } + /// Open new multi-accounts store with given key directory backend and custom number of iterations for new keys. pub fn open_with_iterations(directory: Box, iterations: u32) -> Result { let store = EthMultiStore { dir: directory, @@ -259,7 +267,7 @@ impl EthMultiStore { } self.reload_accounts()?; *last_dir_hash = dir_hash; - Ok(()) + Ok(()) } fn reload_accounts(&self) -> Result<(), Error> { @@ -287,7 +295,7 @@ impl EthMultiStore { Ok(()) } - fn get(&self, account: &StoreAccountRef) -> Result, Error> { + fn get_accounts(&self, account: &StoreAccountRef) -> Result, Error> { { let cache = self.cache.read(); if let Some(accounts) = cache.get(account) { @@ -307,6 +315,15 @@ impl EthMultiStore { } } + fn get_matching(&self, account: &StoreAccountRef, password: &str) -> Result, Error> { + let accounts = self.get_accounts(account)?; + + Ok(accounts.into_iter() + .filter(|acc| acc.check_password(password)) + .collect() + ) + } + fn import(&self, vault: SecretVaultRef, account: SafeAccount) -> Result { // save to file let account = match vault { @@ -398,12 +415,8 @@ impl SimpleSecretStore for EthMultiStore { fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &str, derivation: Derivation) -> Result { - let accounts = self.get(account_ref)?; + let accounts = self.get_matching(account_ref, password)?; for account in accounts { - // Skip if password is invalid - if !account.check_password(password) { - continue; - } let extended = self.generate(account.crypto.secret(password)?, derivation)?; return self.insert_account(vault, extended.secret().as_raw().clone(), password); } @@ -413,14 +426,9 @@ impl SimpleSecretStore for EthMultiStore { fn generate_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation) -> Result { - let accounts = self.get(&account_ref)?; + let accounts = self.get_matching(&account_ref, password)?; for account in accounts { - // Skip if password is invalid - if !account.check_password(password) { - continue; - } let extended = self.generate(account.crypto.secret(password)?, derivation)?; - return Ok(ethkey::public_to_address(extended.public().public())); } Err(Error::InvalidPassword) @@ -429,18 +437,13 @@ impl SimpleSecretStore for EthMultiStore { fn sign_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation, message: &Message) -> Result { - let accounts = self.get(&account_ref)?; + let accounts = self.get_matching(&account_ref, password)?; for account in accounts { - // Skip if password is invalid - if !account.check_password(password) { - continue; - } let extended = self.generate(account.crypto.secret(password)?, derivation)?; let secret = extended.secret().as_raw(); return Ok(ethkey::sign(&secret, message)?) } Err(Error::InvalidPassword) - } fn account_ref(&self, address: &Address) -> Result { @@ -457,47 +460,47 @@ impl SimpleSecretStore for EthMultiStore { } fn remove_account(&self, account_ref: &StoreAccountRef, password: &str) -> Result<(), Error> { - let accounts = self.get(account_ref)?; + let accounts = self.get_matching(account_ref, password)?; for account in accounts { - // Skip if password is invalid - if !account.check_password(password) { - continue; - } - return self.remove_safe_account(account_ref, &account); } + Err(Error::InvalidPassword) } fn change_password(&self, account_ref: &StoreAccountRef, old_password: &str, new_password: &str) -> Result<(), Error> { - let accounts = self.get(account_ref)?; + let accounts = self.get_matching(account_ref, old_password)?; + + if accounts.is_empty() { + return Err(Error::InvalidPassword); + } for account in accounts { // Change password let new_account = account.change_password(old_password, new_password, self.iterations)?; self.update(account_ref, account, new_account)?; } + Ok(()) } - fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result { - let accounts = self.get(account)?; - for account in accounts { - if account.check_password(password) { - return account.sign(password, message); - } - } + fn export_account(&self, account_ref: &StoreAccountRef, password: &str) -> Result { + self.get_matching(account_ref, password)?.into_iter().nth(0).map(Into::into).ok_or(Error::InvalidPassword) + } + fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result { + let accounts = self.get_matching(account, password)?; + for account in accounts { + return account.sign(password, message); + } Err(Error::InvalidPassword) } fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error> { - let accounts = self.get(account)?; + let accounts = self.get_matching(account, password)?; for account in accounts { - if account.check_password(password) { - return account.decrypt(password, shared_mac, message); - } + return account.decrypt(password, shared_mac, message); } Err(Error::InvalidPassword) } @@ -586,7 +589,7 @@ impl SimpleSecretStore for EthMultiStore { return Ok(account_ref); } - let account = self.get(&account_ref)?.into_iter().nth(0).ok_or(Error::InvalidAccount)?; + let account = self.get_accounts(&account_ref)?.into_iter().nth(0).ok_or(Error::InvalidAccount)?; let new_account_ref = self.import(vault, account.clone())?; self.remove_safe_account(&account_ref, &account)?; self.reload_accounts()?; @@ -1032,4 +1035,18 @@ mod tests { // then assert_eq!(store.get_vault_meta(name).unwrap(), "OldMeta".to_owned()); } + + #[test] + fn should_export_account() { + // given + let store = store(); + let keypair = keypair(); + let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), "test").unwrap(); + + // when + let exported = store.export_account(&address, "test"); + + // then + assert!(exported.is_ok(), "Should export single account: {:?}", exported); + } } diff --git a/ethstore/src/import.rs b/ethstore/src/import.rs index 0090631bd..b7497c9ff 100644 --- a/ethstore/src/import.rs +++ b/ethstore/src/import.rs @@ -16,9 +16,10 @@ use std::collections::HashSet; use ethkey::Address; -use dir::{GethDirectory, KeyDirectory, DirectoryType}; +use dir::{paths, KeyDirectory, RootDiskDirectory}; use Error; +/// Import all accounts from one directory to the other. pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result, Error> { let accounts = src.load()?; let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::>(); @@ -34,27 +35,15 @@ pub fn import_accounts(src: &KeyDirectory, dst: &KeyDirectory) -> Result Vec
{ - let t = if testnet { - DirectoryType::Testnet - } else { - DirectoryType::Main - }; - - GethDirectory::open(t) + RootDiskDirectory::at(paths::geth(testnet)) .load() .map(|d| d.into_iter().map(|a| a.address).collect()) .unwrap_or_else(|_| Vec::new()) } -/// Import specific `desired` accounts from the Geth keystore into `dst`. +/// Import specific `desired` accounts from the Geth keystore into `dst`. pub fn import_geth_accounts(dst: &KeyDirectory, desired: HashSet
, testnet: bool) -> Result, Error> { - let t = if testnet { - DirectoryType::Testnet - } else { - DirectoryType::Main - }; - - let src = GethDirectory::open(t); + let src = RootDiskDirectory::at(paths::geth(testnet)); let accounts = src.load()?; let existing_accounts = dst.load()?.into_iter().map(|a| a.address).collect::>(); diff --git a/ethstore/src/json/key_file.rs b/ethstore/src/json/key_file.rs index 21711df8f..a1c20acf2 100644 --- a/ethstore/src/json/key_file.rs +++ b/ethstore/src/json/key_file.rs @@ -16,11 +16,31 @@ use std::fmt; use std::io::{Read, Write}; -use serde::{Deserialize, Deserializer}; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; use serde::de::{Error, Visitor, MapVisitor}; use serde_json; use super::{Uuid, Version, Crypto, H160}; +/// Public opaque type representing serializable `KeyFile`. +#[derive(Debug, PartialEq)] +pub struct OpaqueKeyFile { + key_file: KeyFile +} + +impl Serialize for OpaqueKeyFile { + fn serialize(&self, serializer: S) -> Result where + S: Serializer, + { + self.key_file.serialize(serializer) + } +} + +impl From for OpaqueKeyFile where T: Into { + fn from(val: T) -> Self { + OpaqueKeyFile { key_file: val.into() } + } +} + #[derive(Debug, PartialEq, Serialize)] pub struct KeyFile { pub id: Uuid, diff --git a/ethstore/src/json/mod.rs b/ethstore/src/json/mod.rs index 98033effd..865b75dea 100644 --- a/ethstore/src/json/mod.rs +++ b/ethstore/src/json/mod.rs @@ -36,7 +36,7 @@ pub use self::error::Error; pub use self::hash::{H128, H160, H256}; pub use self::id::Uuid; pub use self::kdf::{Kdf, KdfSer, Prf, Pbkdf2, Scrypt, KdfSerParams}; -pub use self::key_file::KeyFile; +pub use self::key_file::{KeyFile, OpaqueKeyFile}; pub use self::presale::{PresaleWallet, Encseed}; pub use self::vault_file::VaultFile; pub use self::vault_key_file::{VaultKeyFile, VaultKeyMeta, insert_vault_name_to_json_meta, remove_vault_name_from_json_meta}; diff --git a/ethstore/src/lib.rs b/ethstore/src/lib.rs index f092c3fe6..8203feeec 100755 --- a/ethstore/src/lib.rs +++ b/ethstore/src/lib.rs @@ -14,6 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Ethereum key-management. + + +#![warn(missing_docs)] + extern crate libc; extern crate itertools; extern crate smallvec; @@ -52,10 +57,11 @@ mod presale; mod random; mod secret_store; -pub use self::account::{SafeAccount}; +pub use self::account::SafeAccount; pub use self::error::Error; pub use self::ethstore::{EthStore, EthMultiStore}; pub use self::import::{import_accounts, read_geth_accounts}; +pub use self::json::OpaqueKeyFile as KeyFile; pub use self::presale::PresaleWallet; pub use self::secret_store::{ SecretVaultRef, StoreAccountRef, SimpleSecretStore, SecretStore, diff --git a/ethstore/src/presale.rs b/ethstore/src/presale.rs index 45d127664..dbbdcdc8d 100644 --- a/ethstore/src/presale.rs +++ b/ethstore/src/presale.rs @@ -8,6 +8,7 @@ use ethkey::{Address, Secret, KeyPair}; use crypto::Keccak256; use {crypto, Error}; +/// Pre-sale wallet. pub struct PresaleWallet { iv: [u8; 16], ciphertext: Vec, @@ -31,6 +32,7 @@ impl From for PresaleWallet { } impl PresaleWallet { + /// Open a pre-sale wallet. pub fn open

(path: P) -> Result where P: AsRef { let file = fs::File::open(path)?; let presale = json::PresaleWallet::load(file) @@ -38,6 +40,7 @@ impl PresaleWallet { Ok(PresaleWallet::from(presale)) } + /// Decrypt the wallet. pub fn decrypt(&self, password: &str) -> Result { let mut h_mac = Hmac::new(Sha256::new(), password.as_bytes()); let mut derived_key = vec![0u8; 16]; diff --git a/ethstore/src/secret_store.rs b/ethstore/src/secret_store.rs index 1eff95335..fd7eea50d 100755 --- a/ethstore/src/secret_store.rs +++ b/ethstore/src/secret_store.rs @@ -18,7 +18,7 @@ use std::hash::{Hash, Hasher}; use std::path::PathBuf; use ethkey::{Address, Message, Signature, Secret, Public}; use Error; -use json::Uuid; +use json::{Uuid, OpaqueKeyFile}; use util::H256; /// Key directory reference @@ -39,16 +39,28 @@ pub struct StoreAccountRef { pub address: Address, } +/// Simple Secret Store API pub trait SimpleSecretStore: Send + Sync { + /// Inserts new accounts to the store (or vault) with given password. fn insert_account(&self, vault: SecretVaultRef, secret: Secret, password: &str) -> Result; + /// Inserts new derived account to the store (or vault) with given password. fn insert_derived(&self, vault: SecretVaultRef, account_ref: &StoreAccountRef, password: &str, derivation: Derivation) -> Result; + /// Changes accounts password. fn change_password(&self, account: &StoreAccountRef, old_password: &str, new_password: &str) -> Result<(), Error>; + /// Exports key details for account. + fn export_account(&self, account: &StoreAccountRef, password: &str) -> Result; + /// Entirely removes account from the store and underlying storage. fn remove_account(&self, account: &StoreAccountRef, password: &str) -> Result<(), Error>; + /// Generates new derived account. fn generate_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation) -> Result; + /// Sign a message with given account. fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result; + /// Sign a message with derived account. fn sign_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation, message: &Message) -> Result; + /// Decrypt a messages with given account. fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error>; + /// Returns all accounts in this secret store. fn accounts(&self) -> Result, Error>; /// Get reference to some account with given address. /// This method could be removed if we will guarantee that there is max(1) account for given address. @@ -74,23 +86,37 @@ pub trait SimpleSecretStore: Send + Sync { fn set_vault_meta(&self, name: &str, meta: &str) -> Result<(), Error>; } +/// Secret Store API pub trait SecretStore: SimpleSecretStore { + /// Imports presale wallet fn import_presale(&self, vault: SecretVaultRef, json: &[u8], password: &str) -> Result; + /// Imports existing JSON wallet fn import_wallet(&self, vault: SecretVaultRef, json: &[u8], password: &str) -> Result; + /// Copies account between stores and vaults. fn copy_account(&self, new_store: &SimpleSecretStore, new_vault: SecretVaultRef, account: &StoreAccountRef, password: &str, new_password: &str) -> Result<(), Error>; + /// Checks if password matches given account. fn test_password(&self, account: &StoreAccountRef, password: &str) -> Result; + /// Returns a public key for given account. fn public(&self, account: &StoreAccountRef, password: &str) -> Result; + /// Returns uuid of an account. fn uuid(&self, account: &StoreAccountRef) -> Result; + /// Returns account's name. fn name(&self, account: &StoreAccountRef) -> Result; + /// Returns account's metadata. fn meta(&self, account: &StoreAccountRef) -> Result; + /// Modifies account metadata. fn set_name(&self, account: &StoreAccountRef, name: String) -> Result<(), Error>; + /// Modifies account name. fn set_meta(&self, account: &StoreAccountRef, meta: String) -> Result<(), Error>; + /// Returns local path of the store. fn local_path(&self) -> PathBuf; + /// Lists all found geth accounts. fn list_geth_accounts(&self, testnet: bool) -> Vec

; + /// Imports geth accounts to the store/vault. fn import_geth_accounts(&self, vault: SecretVaultRef, desired: Vec
, testnet: bool) -> Result, Error>; } diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index 60b615897..828dbf8f4 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -20,6 +20,7 @@ use std::collections::BTreeMap; use util::{Address}; use ethkey::{Brain, Generator, Secret}; +use ethstore::KeyFile; use ethcore::account_provider::AccountProvider; use jsonrpc_core::Error; @@ -315,6 +316,17 @@ impl ParityAccounts for ParityAccountsClient { .map(Into::into) .map_err(|e| errors::account("Could not derive account.", e)) } + + fn export_account(&self, addr: RpcH160, password: String) -> Result { + let addr = addr.into(); + take_weak!(self.accounts) + .export_account( + &addr, + password, + ) + .map(Into::into) + .map_err(|e| errors::account("Could not export account.", e)) + } } fn into_vec(a: Vec) -> Vec where diff --git a/rpc/src/v1/tests/mocked/parity_accounts.rs b/rpc/src/v1/tests/mocked/parity_accounts.rs index ae4f74b49..ef356cd42 100644 --- a/rpc/src/v1/tests/mocked/parity_accounts.rs +++ b/rpc/src/v1/tests/mocked/parity_accounts.rs @@ -472,3 +472,30 @@ fn derive_key_index() { let res = tester.io.handle_request_sync(&request); assert_eq!(res, Some(response.into())); } + + +#[test] +fn should_export_account() { + // given + let tester = setup(); + let wallet = r#"{"id":"6a186c80-7797-cff2-bc2e-7c1d6a6cc76e","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"a1c6ff99070f8032ca1c4e8add006373"},"ciphertext":"df27e3db64aa18d984b6439443f73660643c2d119a6f0fa2fa9a6456fc802d75","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"ddc325335cda5567a1719313e73b4842511f3e4a837c9658eeb78e51ebe8c815"},"mac":"3dc888ae79cbb226ff9c455669f6cf2d79be72120f2298f6cb0d444fddc0aa3d"},"address":"0042e5d2a662eeaca8a7e828c174f98f35d8925b","name":"parity-export-test","meta":"{\"passwordHint\":\"parity-export-test\",\"timestamp\":1490017814987}"}"#; + tester.accounts.import_wallet(wallet.as_bytes(), "parity-export-test").unwrap(); + let accounts = tester.accounts.accounts().unwrap(); + assert_eq!(accounts.len(), 1); + + // invalid password + let request = r#"{"jsonrpc":"2.0","method":"parity_exportAccount","params":["0x0042e5d2a662eeaca8a7e828c174f98f35d8925b","123"],"id":1}"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32023,"message":"Could not export account.","data":"InvalidPassword"},"id":1}"#; + let res = tester.io.handle_request_sync(&request); + assert_eq!(res, Some(response.into())); + + // correct password + let request = r#"{"jsonrpc":"2.0","method":"parity_exportAccount","params":["0x0042e5d2a662eeaca8a7e828c174f98f35d8925b","parity-export-test"],"id":1}"#; + + let response = r#"{"jsonrpc":"2.0","result":{"address":"0042e5d2a662eeaca8a7e828c174f98f35d8925b","crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"a1c6ff99070f8032ca1c4e8add006373"},"ciphertext":"df27e3db64aa18d984b6439443f73660643c2d119a6f0fa2fa9a6456fc802d75","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"ddc325335cda5567a1719313e73b4842511f3e4a837c9658eeb78e51ebe8c815"},"mac":"3dc888ae79cbb226ff9c455669f6cf2d79be72120f2298f6cb0d444fddc0aa3d"},"id":"6a186c80-7797-cff2-bc2e-7c1d6a6cc76e","meta":"{\"passwordHint\":\"parity-export-test\",\"timestamp\":1490017814987}","name":"parity-export-test","version":3},"id":1}"#; + let result = tester.io.handle_request_sync(&request); + + println!("Result: {:?}", result); + println!("Response: {:?}", response); + assert_eq!(result, Some(response.into())); +} diff --git a/rpc/src/v1/traits/parity_accounts.rs b/rpc/src/v1/traits/parity_accounts.rs index a3a9a8d9f..46372560c 100644 --- a/rpc/src/v1/traits/parity_accounts.rs +++ b/rpc/src/v1/traits/parity_accounts.rs @@ -18,6 +18,7 @@ use std::collections::BTreeMap; use jsonrpc_core::Error; +use ethstore::KeyFile; use v1::types::{H160, H256, DappId, DeriveHash, DeriveHierarchical}; build_rpc_trait! { @@ -175,5 +176,9 @@ build_rpc_trait! { /// Resulting address can be either saved as a new account (with the same password). #[rpc(name = "parity_deriveAddressIndex")] fn derive_key_index(&self, H160, String, DeriveHierarchical, bool) -> Result; + + /// Exports an account with given address if provided password matches. + #[rpc(name = "parity_exportAccount")] + fn export_account(&self, H160, String) -> Result; } } From f5ea47a7b2533c7c534ff6b0c921e6940325f427 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Thu, 23 Mar 2017 13:25:31 +0100 Subject: [PATCH 21/41] Various installer and tray apps fixes (#4970) * Mac tray app fixes * Windows restarting fixed --- Cargo.lock | 1 + Cargo.toml | 1 + mac/Parity/AppDelegate.swift | 6 +---- parity/main.rs | 43 +++++++++++++++++++++++++++--------- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99ebbe7e3..974206727 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,7 @@ dependencies = [ "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f0978f420..8420c5459 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ serde = "0.9" serde_json = "0.9" app_dirs = "1.1.1" fdlimit = "0.1" +ws2_32-sys = "0.2" hyper = { default-features = false, git = "https://github.com/paritytech/hyper" } ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" } diff --git a/mac/Parity/AppDelegate.swift b/mac/Parity/AppDelegate.swift index 3bf3bdd31..c017e79bd 100644 --- a/mac/Parity/AppDelegate.swift +++ b/mac/Parity/AppDelegate.swift @@ -26,7 +26,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength) var parityPid: Int32? = nil var commandLine: [String] = [] - let defaultConfig = "[network]\nwarp = true" let defaultDefaults = "{\"fat_db\":false,\"mode\":\"passive\",\"mode.alarm\":3600,\"mode.timeout\":300,\"pruning\":\"fast\",\"tracing\":false}" func menuAppPath() -> String { @@ -51,7 +50,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { func killParity() { if let pid = self.parityPid { - kill(pid, SIGINT) + kill(pid, SIGKILL) } } @@ -81,9 +80,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { } let configFile = basePath?.appendingPathComponent("config.toml") - if !FileManager.default.fileExists(atPath: configFile!.path) { - try defaultConfig.write(to: configFile!, atomically: false, encoding: String.Encoding.utf8) - } } catch {} } diff --git a/parity/main.rs b/parity/main.rs index 3e499d483..2044b3ee0 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -75,6 +75,9 @@ extern crate ethcore_secretstore; #[cfg(feature = "dapps")] extern crate ethcore_dapps; +#[cfg(windows)] extern crate ws2_32; +#[cfg(windows)] extern crate winapi; + macro_rules! dependency { ($dep_ty:ident, $url:expr) => { { @@ -123,7 +126,7 @@ mod stratum; use std::{process, env}; use std::collections::HashMap; use std::io::{self as stdio, BufReader, Read, Write}; -use std::fs::{remove_file, metadata, File}; +use std::fs::{remove_file, metadata, File, create_dir_all}; use std::path::PathBuf; use util::sha3::sha3; use cli::Args; @@ -210,10 +213,11 @@ fn latest_exe_path() -> Option { } fn set_spec_name_override(spec_name: String) { - if let Err(e) = File::create(updates_path("spec_name_overide")) - .and_then(|mut f| f.write_all(spec_name.as_bytes())) + if let Err(e) = create_dir_all(default_hypervisor_path()) + .and_then(|_| File::create(updates_path("spec_name_overide")) + .and_then(|mut f| f.write_all(spec_name.as_bytes()))) { - warn!("Couldn't override chain spec: {}", e); + warn!("Couldn't override chain spec: {} at {:?}", e, updates_path("spec_name_overide")); } } @@ -227,12 +231,24 @@ fn take_spec_name_override() -> Option { #[cfg(windows)] fn global_cleanup() { - extern "system" { pub fn WSACleanup() -> i32; } // We need to cleanup all sockets before spawning another Parity process. This makes shure everything is cleaned up. // The loop is required because of internal refernce counter for winsock dll. We don't know how many crates we use do // initialize it. There's at least 2 now. for _ in 0.. 10 { - unsafe { WSACleanup(); } + unsafe { ::ws2_32::WSACleanup(); } + } +} + +#[cfg(not(windows))] +fn global_init() {} + +#[cfg(windows)] +fn global_init() { + // When restarting in the same process this reinits windows sockets. + unsafe { + const WS_VERSION: u16 = 0x202; + let mut wsdata: ::winapi::winsock2::WSADATA = ::std::mem::zeroed(); + ::ws2_32::WSAStartup(WS_VERSION, &mut wsdata); } } @@ -241,15 +257,17 @@ fn global_cleanup() {} // Starts ~/.parity-updates/parity and returns the code it exits with. fn run_parity() -> Option { - global_cleanup(); + global_init(); use ::std::ffi::OsString; let prefix = vec![OsString::from("--can-restart"), OsString::from("--force-direct")]; - latest_exe_path().and_then(|exe| process::Command::new(exe) + let res = latest_exe_path().and_then(|exe| process::Command::new(exe) .args(&(env::args_os().skip(1).chain(prefix.into_iter()).collect::>())) .status() .map(|es| es.code().unwrap_or(128)) .ok() - ) + ); + global_cleanup(); + res } const PLEASE_RESTART_EXIT_CODE: i32 = 69; @@ -257,10 +275,11 @@ const PLEASE_RESTART_EXIT_CODE: i32 = 69; // Run our version of parity. // Returns the exit error code. fn main_direct(can_restart: bool) -> i32 { + global_init(); let mut alt_mains = HashMap::new(); sync_main(&mut alt_mains); stratum_main(&mut alt_mains); - if let Some(f) = std::env::args().nth(1).and_then(|arg| alt_mains.get(&arg.to_string())) { + let res = if let Some(f) = std::env::args().nth(1).and_then(|arg| alt_mains.get(&arg.to_string())) { f(); 0 } else { @@ -280,7 +299,9 @@ fn main_direct(can_restart: bool) -> i32 { 1 }, } - } + }; + global_cleanup(); + res } fn println_trace_main(s: String) { From 1490ba179cab6d98f3702d7353fd8e6a0a311cea Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Thu, 23 Mar 2017 15:39:13 +0100 Subject: [PATCH 22/41] Dispatch an open event on drag of Parity Bar (#4987) * Dispatch an open event on drag of Parity Bar * Bette id for ParityBar position // Replace on dapp unload * Fix PairtyBar Positionning --- js/src/views/ParityBar/parityBar.js | 172 ++++++++++++++++++---------- 1 file changed, 111 insertions(+), 61 deletions(-) diff --git a/js/src/views/ParityBar/parityBar.js b/js/src/views/ParityBar/parityBar.js index 3d34f430f..9d5027acf 100644 --- a/js/src/views/ParityBar/parityBar.js +++ b/js/src/views/ParityBar/parityBar.js @@ -78,16 +78,33 @@ class ParityBar extends Component { // Hook to the dapp loaded event to position the // Parity Bar accordingly - DappsStore.get(api).on('loaded', (app) => { - this.app = app; + const dappsStore = DappsStore.get(api); + + dappsStore + .on('loaded', (app) => { + this.app = app; + this.loadPosition(); + }); + + if (this.props.dapp) { this.loadPosition(); - }); + } } componentWillReceiveProps (nextProps) { const count = this.props.pending.length; const newCount = nextProps.pending.length; + // Replace to default position when leaving a dapp + if (this.props.dapp && !nextProps.dapp) { + this.loadPosition(true); + } + + // Load position when dapp loads + if (!this.props.dapp && nextProps.dapp) { + this.loadPosition(); + } + if (count === newCount) { return; } @@ -101,7 +118,10 @@ class ParityBar extends Component { setOpened (opened, displayType = DISPLAY_SIGNER) { this.setState({ displayType, opened }); + this.dispatchOpenEvent(opened); + } + dispatchOpenEvent (opened) { if (!this.bar) { return; } @@ -241,10 +261,6 @@ class ParityBar extends Component { } renderDrag () { - if (this.props.externalLink) { - return; - } - const dragButtonClasses = [ styles.dragButton ]; if (this.state.moving) { @@ -457,51 +473,56 @@ class ParityBar extends Component { return position; } - onMouseDown = (event) => { - const containerElt = ReactDOM.findDOMNode(this.refs.container); - const dragButtonElt = ReactDOM.findDOMNode(this.refs.dragButton); + onMouseDown = () => { + // Dispatch an open event in case in an iframe (get full w and h) + this.dispatchOpenEvent(true); - if (!containerElt || !dragButtonElt) { - console.warn(containerElt ? 'drag button' : 'container', 'not found...'); - return; - } + window.setTimeout(() => { + const containerElt = ReactDOM.findDOMNode(this.refs.container); + const dragButtonElt = ReactDOM.findDOMNode(this.refs.dragButton); - const bodyRect = document.body.getBoundingClientRect(); - const containerRect = containerElt.getBoundingClientRect(); - const buttonRect = dragButtonElt.getBoundingClientRect(); + if (!containerElt || !dragButtonElt) { + console.warn(containerElt ? 'drag button' : 'container', 'not found...'); + return; + } - const buttonOffset = { - top: (buttonRect.top + buttonRect.height / 2) - containerRect.top, - left: (buttonRect.left + buttonRect.width / 2) - containerRect.left - }; + const bodyRect = document.body.getBoundingClientRect(); + const containerRect = containerElt.getBoundingClientRect(); + const buttonRect = dragButtonElt.getBoundingClientRect(); - buttonOffset.bottom = containerRect.height - buttonOffset.top; - buttonOffset.right = containerRect.width - buttonOffset.left; + const buttonOffset = { + top: (buttonRect.top + buttonRect.height / 2) - containerRect.top, + left: (buttonRect.left + buttonRect.width / 2) - containerRect.left + }; - const button = { - offset: buttonOffset, - height: buttonRect.height, - width: buttonRect.width - }; + buttonOffset.bottom = containerRect.height - buttonOffset.top; + buttonOffset.right = containerRect.width - buttonOffset.left; - const container = { - height: containerRect.height, - width: containerRect.width - }; + const button = { + offset: buttonOffset, + height: buttonRect.height, + width: buttonRect.width + }; - const page = { - height: bodyRect.height, - width: bodyRect.width - }; + const container = { + height: containerRect.height, + width: containerRect.width + }; - this.moving = true; - this.measures = { - button, - container, - page - }; + const page = { + height: bodyRect.height, + width: bodyRect.width + }; - this.setState({ moving: true }); + this.moving = true; + this.measures = { + button, + container, + page + }; + + this.setMovingState(true); + }, 50); } onMouseEnter = (event) => { @@ -570,7 +591,7 @@ class ParityBar extends Component { } this.moving = false; - this.setState({ moving: false, position }); + this.setMovingState(false, { position }); this.savePosition(position); } @@ -587,39 +608,61 @@ class ParityBar extends Component { } get config () { - let config; + const config = store.get(LS_STORE_KEY); - try { - config = JSON.parse(store.get(LS_STORE_KEY)); - } catch (error) { - config = {}; + if (typeof config === 'string') { + try { + return JSON.parse(config); + } catch (e) { + return {}; + } } - return config; + return config || {}; } - loadPosition (props = this.props) { - const { app, config } = this; + /** + * Return the config key for the current view. + * If inside a dapp, should be the dapp id. + * Otherwise, try to get the current hostname. + */ + getConfigKey () { + const { app } = this; - if (!app) { + if (app && app.id) { + return app.id; + } + + return window.location.hostname; + } + + loadPosition (loadDefault = false) { + if (loadDefault) { return this.setState({ position: DEFAULT_POSITION }); } - if (config[app.id]) { - return this.setState({ position: config[app.id] }); + const { app, config } = this; + const configKey = this.getConfigKey(); + + if (config[configKey]) { + return this.setState({ position: config[configKey] }); } - const position = this.stringToPosition(app.position); + if (app && app.position) { + const position = this.stringToPosition(app.position); - this.setState({ position }); + return this.setState({ position }); + } + + return this.setState({ position: DEFAULT_POSITION }); } savePosition (position) { - const { app, config } = this; + const { config } = this; + const configKey = this.getConfigKey(); - config[app.id] = position; - - store.set(LS_STORE_KEY, JSON.stringify(config)); + config[configKey] = position; + store.set(LS_STORE_KEY, config); } stringToPosition (value) { @@ -647,6 +690,13 @@ class ParityBar extends Component { return DEFAULT_POSITION; } } + + setMovingState (moving, extras = {}) { + this.setState({ moving, ...extras }); + + // Trigger an open event if it's moving + this.dispatchOpenEvent(moving); + } } function mapStateToProps (state) { From df76f010dadcdb5dd6a7493ff48e7555c70dbb26 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 23 Mar 2017 15:47:42 +0100 Subject: [PATCH 23/41] Create webpack analysis files (size) (#5009) --- js/package.json | 5 +++ js/webpack/app.js | 3 +- js/webpack/shared.js | 93 +++++++++++++++++++++++--------------------- 3 files changed, 56 insertions(+), 45 deletions(-) diff --git a/js/package.json b/js/package.json index 6a1e63fb1..2c0dae7cc 100644 --- a/js/package.json +++ b/js/package.json @@ -26,6 +26,10 @@ "Promise" ], "scripts": { + "analize": "npm run analize:lib && npm run analize:dll && npm run analize:app", + "analize:app": "WPANALIZE=1 webpack --config webpack/app --json > .build/analize.app.json && cat .build/analize.app.json | webpack-bundle-size-analyzer", + "analize:lib": "WPANALIZE=1 webpack --config webpack/libraries --json > .build/analize.lib.json && cat .build/analize.lib.json | webpack-bundle-size-analyzer", + "analize:dll": "WPANALIZE=1 webpack --config webpack/vendor --json > .build/analize.dll.json && cat .build/analize.app.json | webpack-bundle-size-analyzer", "build": "npm run build:lib && npm run build:dll && npm run build:app && npm run build:embed", "build:app": "webpack --config webpack/app", "build:lib": "webpack --config webpack/libraries", @@ -141,6 +145,7 @@ "to-source": "2.0.3", "url-loader": "0.5.7", "webpack": "2.2.1", + "webpack-bundle-size-analyzer": "2.5.0", "webpack-dev-middleware": "1.10.1", "webpack-error-notification": "0.1.6", "webpack-hot-middleware": "2.17.1", diff --git a/js/webpack/app.js b/js/webpack/app.js index 14f2876db..6b6b4c8cd 100644 --- a/js/webpack/app.js +++ b/js/webpack/app.js @@ -36,6 +36,7 @@ const EMBED = process.env.EMBED; const isProd = ENV === 'production'; const isEmbed = EMBED === '1' || EMBED === 'true'; +const isAnalize = process.env.WPANALIZE === '1'; const entry = isEmbed ? { @@ -215,7 +216,7 @@ module.exports = { ); } - if (!isProd) { + if (!isAnalize && !isProd) { const DEST_I18N = path.join(__dirname, '..', DEST, 'i18n'); plugins.push( diff --git a/js/webpack/shared.js b/js/webpack/shared.js index 3f5fcd66f..2f151944e 100644 --- a/js/webpack/shared.js +++ b/js/webpack/shared.js @@ -29,6 +29,7 @@ const ProgressBarPlugin = require('progress-bar-webpack-plugin'); const EMBED = process.env.EMBED; const ENV = process.env.NODE_ENV || 'development'; const isProd = ENV === 'production'; +const isAnalize = process.env.WPANALIZE === '1'; function getBabelrc () { const babelrc = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../.babelrc'))); @@ -80,55 +81,59 @@ function getPlugins (_isProd = isProd) { }) ]; - const plugins = [ - new ProgressBarPlugin({ - format: '[:msg] [:bar] ' + ':percent' + ' (:elapsed seconds)' - }), + const plugins = (isAnalize + ? [] + : [ + new ProgressBarPlugin({ + format: '[:msg] [:bar] ' + ':percent' + ' (:elapsed seconds)' + }) + ]).concat([ + new HappyPack({ + id: 'css', + threads: 4, + loaders: [ + 'style-loader', + 'css-loader?modules&sourceMap&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]', + 'postcss-loader' + ], + verbose: !isAnalize + }), - new HappyPack({ - id: 'css', - threads: 4, - loaders: [ - 'style-loader', - 'css-loader?modules&sourceMap&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]', - 'postcss-loader' - ] - }), + new HappyPack({ + id: 'babel-js', + threads: 4, + loaders: [ isProd ? 'babel-loader' : 'babel-loader?cacheDirectory=true' ], + verbose: !isAnalize + }), - new HappyPack({ - id: 'babel-js', - threads: 4, - loaders: [ isProd ? 'babel-loader' : 'babel-loader?cacheDirectory=true' ] - }), + new webpack.DefinePlugin({ + 'process.env': { + EMBED: JSON.stringify(EMBED), + NODE_ENV: JSON.stringify(ENV), + RPC_ADDRESS: JSON.stringify(process.env.RPC_ADDRESS), + PARITY_URL: JSON.stringify(process.env.PARITY_URL), + DAPPS_URL: JSON.stringify(process.env.DAPPS_URL), + LOGGING: JSON.stringify(!isProd) + } + }), - new webpack.DefinePlugin({ - 'process.env': { - EMBED: JSON.stringify(EMBED), - NODE_ENV: JSON.stringify(ENV), - RPC_ADDRESS: JSON.stringify(process.env.RPC_ADDRESS), - PARITY_URL: JSON.stringify(process.env.PARITY_URL), - DAPPS_URL: JSON.stringify(process.env.DAPPS_URL), - LOGGING: JSON.stringify(!isProd) - } - }), + new webpack.LoaderOptionsPlugin({ + minimize: isProd, + debug: !isProd, + options: { + context: path.join(__dirname, '../src'), + postcss: postcss, + babel: getBabelrc() + } + }), - new webpack.LoaderOptionsPlugin({ - minimize: isProd, - debug: !isProd, - options: { - context: path.join(__dirname, '../src'), - postcss: postcss, - babel: getBabelrc() - } - }), + new webpack.optimize.OccurrenceOrderPlugin(!_isProd), - new webpack.optimize.OccurrenceOrderPlugin(!_isProd), - - new CircularDependencyPlugin({ - exclude: /node_modules/, - failOnError: true - }) - ]; + new CircularDependencyPlugin({ + exclude: /node_modules/, + failOnError: true + }) + ]); if (_isProd) { plugins.push(new webpack.optimize.UglifyJsPlugin({ From 35a43a1e82492cc28e7a90b915b58537d158c770 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 23 Mar 2017 15:03:35 +0000 Subject: [PATCH 24/41] [ci skip] js-precompiled 20170323-150033 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 974206727..7d3c577ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1750,7 +1750,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#a44b1cb29b80e4d3372ee47494499a61db7a8116" +source = "git+https://github.com/ethcore/js-precompiled.git#744561d103ca832187930ca7532f1d093a8335af" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 2c0dae7cc..466779efe 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.29", + "version": "1.7.30", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 9efab789aa875ad3b6930403135ea9bdf1c3468b Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 23 Mar 2017 15:10:28 +0000 Subject: [PATCH 25/41] [ci skip] js-precompiled 20170323-150743 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d3c577ad..badb522c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1750,7 +1750,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#744561d103ca832187930ca7532f1d093a8335af" +source = "git+https://github.com/ethcore/js-precompiled.git#8d7826d49c785726b17f14f150cab37e204fc84f" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 466779efe..b65ef6661 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.30", + "version": "1.7.31", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 3b54b49b0b899c56c25c0691117b529664145a24 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Fri, 24 Mar 2017 18:55:57 +0100 Subject: [PATCH 26/41] Resilient warp sync (#5018) --- sync/src/chain.rs | 33 +++++++++++++++++++-------------- sync/src/snapshot.rs | 5 +++++ util/network/src/session.rs | 4 ++-- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/sync/src/chain.rs b/sync/src/chain.rs index 47ddf6ab1..df5f65a86 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -164,8 +164,8 @@ const HEADERS_TIMEOUT_SEC: u64 = 15; const BODIES_TIMEOUT_SEC: u64 = 10; const RECEIPTS_TIMEOUT_SEC: u64 = 10; const FORK_HEADER_TIMEOUT_SEC: u64 = 3; -const SNAPSHOT_MANIFEST_TIMEOUT_SEC: u64 = 3; -const SNAPSHOT_DATA_TIMEOUT_SEC: u64 = 60; +const SNAPSHOT_MANIFEST_TIMEOUT_SEC: u64 = 5; +const SNAPSHOT_DATA_TIMEOUT_SEC: u64 = 120; #[derive(Copy, Clone, Eq, PartialEq, Debug)] /// Sync state @@ -463,12 +463,7 @@ impl ChainSync { /// Reset sync. Clear all downloaded data but keep the queue fn reset(&mut self, io: &mut SyncIo) { self.new_blocks.reset(); - self.snapshot.clear(); let chain_info = io.chain().chain_info(); - if self.state == SyncState::SnapshotData { - debug!(target:"sync", "Aborting snapshot restore"); - io.snapshot_service().abort_restore(); - } for (_, ref mut p) in &mut self.peers { if p.block_set != Some(BlockSet::OldBlocks) { p.reset_asking(); @@ -487,6 +482,11 @@ impl ChainSync { /// Restart sync pub fn reset_and_continue(&mut self, io: &mut SyncIo) { trace!(target: "sync", "Restarting"); + if self.state == SyncState::SnapshotData { + debug!(target:"sync", "Aborting snapshot restore"); + io.snapshot_service().abort_restore(); + } + self.snapshot.clear(); self.reset(io); self.continue_sync(io); } @@ -499,7 +499,7 @@ impl ChainSync { } fn maybe_start_snapshot_sync(&mut self, io: &mut SyncIo) { - if self.state != SyncState::WaitingPeers { + if self.state != SyncState::WaitingPeers && self.state != SyncState::Blocks && self.state != SyncState::Waiting { return; } // Make sure the snapshot block is not too far away from best block and network best block and @@ -531,7 +531,7 @@ impl ChainSync { (best_hash, max_peers, snapshot_peers) }; - let timeout = self.sync_start_time.map_or(false, |t| ((time::precise_time_ns() - t) / 1_000_000_000) > WAIT_PEERS_TIMEOUT_SEC); + let timeout = (self.state == SyncState::WaitingPeers) && self.sync_start_time.map_or(false, |t| ((time::precise_time_ns() - t) / 1_000_000_000) > WAIT_PEERS_TIMEOUT_SEC); if let (Some(hash), Some(peers)) = (best_hash, best_hash.map_or(None, |h| snapshot_peers.get(&h))) { if max_peers >= SNAPSHOT_MIN_PEERS { @@ -549,13 +549,18 @@ impl ChainSync { } fn start_snapshot_sync(&mut self, io: &mut SyncIo, peers: &[PeerId]) { - self.snapshot.clear(); - for p in peers { - if self.peers.get(p).map_or(false, |p| p.asking == PeerAsking::Nothing) { - self.request_snapshot_manifest(io, *p); + if !self.snapshot.have_manifest() { + for p in peers { + if self.peers.get(p).map_or(false, |p| p.asking == PeerAsking::Nothing) { + self.request_snapshot_manifest(io, *p); + } } + self.state = SyncState::SnapshotManifest; + trace!(target: "sync", "New snapshot sync with {:?}", peers); + } else { + self.state = SyncState::SnapshotData; + trace!(target: "sync", "Resumed snapshot sync with {:?}", peers); } - self.state = SyncState::SnapshotManifest; } /// Restart sync disregarding the block queue status. May end up re-downloading up to QUEUE_SIZE blocks diff --git a/sync/src/snapshot.rs b/sync/src/snapshot.rs index c19860101..94794a4d4 100644 --- a/sync/src/snapshot.rs +++ b/sync/src/snapshot.rs @@ -54,6 +54,11 @@ impl Snapshot { self.snapshot_hash = None; } + /// Check if currently downloading a snapshot. + pub fn have_manifest(&self) -> bool { + self.snapshot_hash.is_some() + } + /// Reset collection for a manifest RLP pub fn reset_to(&mut self, manifest: &ManifestData, hash: &H256) { self.clear(); diff --git a/util/network/src/session.rs b/util/network/src/session.rs index 59eac6e5d..8affb4cf7 100644 --- a/util/network/src/session.rs +++ b/util/network/src/session.rs @@ -35,8 +35,8 @@ use stats::NetworkStats; use time; // Timeout must be less than (interval - 1). -const PING_TIMEOUT_SEC: u64 = 15; -const PING_INTERVAL_SEC: u64 = 30; +const PING_TIMEOUT_SEC: u64 = 60; +const PING_INTERVAL_SEC: u64 = 120; #[derive(Debug, Clone)] enum ProtocolState { From 7e784808406ade575188d1ecec450d055ddda47e Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 24 Mar 2017 19:03:59 +0100 Subject: [PATCH 27/41] Avoid clogging up tmp when updater dir has bad permissions. (#5024) --- updater/src/updater.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updater/src/updater.rs b/updater/src/updater.rs index 5a4e2e1c9..442b2268d 100644 --- a/updater/src/updater.rs +++ b/updater/src/updater.rs @@ -274,7 +274,7 @@ impl Updater { let running_latest = latest.track.version.hash == self.version_info().hash; let already_have_latest = s.installed.as_ref().or(s.ready.as_ref()).map_or(false, |t| *t == latest.track); - if self.update_policy.enable_downloading && !running_later && !running_latest && !already_have_latest { + if !s.disabled && self.update_policy.enable_downloading && !running_later && !running_latest && !already_have_latest { if let Some(b) = latest.track.binary { if s.fetching.is_none() { if self.updates_path(&Self::update_file_name(&latest.track.version)).exists() { From 109012cae894dde87dac96a3423a7cfafbaabd3b Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Sat, 25 Mar 2017 10:00:50 +0100 Subject: [PATCH 28/41] force earliest era set in snapshot restore (#5021) --- ethcore/src/snapshot/mod.rs | 18 +++++++++++------- ethcore/src/snapshot/service.rs | 2 +- ethcore/src/snapshot/tests/state.rs | 26 ++++++++++++++++---------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index d496139ff..89901502b 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -464,14 +464,18 @@ impl StateRebuilder { Ok(()) } - /// Check for accounts missing code. Once all chunks have been fed, there should - /// be none. - pub fn check_missing(self) -> Result<(), Error> { + /// Finalize the restoration. Check for accounts missing code and make a dummy + /// journal entry. + /// Once all chunks have been fed, there should be nothing missing. + pub fn finalize(mut self, era: u64, id: H256) -> Result<(), ::error::Error> { let missing = self.missing_code.keys().cloned().collect::>(); - match missing.is_empty() { - true => Ok(()), - false => Err(Error::MissingCode(missing)), - } + if !missing.is_empty() { return Err(Error::MissingCode(missing).into()) } + + let mut batch = self.db.backing().transaction(); + self.db.journal_under(&mut batch, era, &id)?; + self.db.backing().write_buffered(batch); + + Ok(()) } /// Get the state root of the rebuilder. diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index a354f3fd3..ee39203ff 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -166,7 +166,7 @@ impl Restoration { } // check for missing code. - self.state.check_missing()?; + self.state.finalize(self.manifest.block_number, self.manifest.block_hash)?; // connect out-of-order chunks and verify chain integrity. self.blocks.finalize(self.canonical_hashes)?; diff --git a/ethcore/src/snapshot/tests/state.rs b/ethcore/src/snapshot/tests/state.rs index 854cd07d4..4b0a33c71 100644 --- a/ethcore/src/snapshot/tests/state.rs +++ b/ethcore/src/snapshot/tests/state.rs @@ -61,7 +61,7 @@ fn snap_and_restore() { state_hashes: state_hashes, block_hashes: Vec::new(), state_root: state_root, - block_number: 0, + block_number: 1000, block_hash: H256::default(), }).unwrap(); @@ -69,7 +69,7 @@ fn snap_and_restore() { db_path.push("db"); let db = { let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap()); - let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive); + let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent); let reader = PackedReader::new(&snap_file).unwrap().unwrap(); let flag = AtomicBool::new(true); @@ -82,12 +82,13 @@ fn snap_and_restore() { } assert_eq!(rebuilder.state_root(), state_root); - rebuilder.check_missing().unwrap(); + rebuilder.finalize(1000, H256::default()).unwrap(); new_db }; - let new_db = journaldb::new(db, Algorithm::Archive, ::db::COL_STATE); + let new_db = journaldb::new(db, Algorithm::OverlayRecent, ::db::COL_STATE); + assert_eq!(new_db.earliest_era(), Some(1000)); compare_dbs(&old_db, new_db.as_hashdb()); } @@ -134,13 +135,18 @@ fn get_code_from_prev_chunk() { let db_cfg = DatabaseConfig::with_columns(::db::NUM_COLUMNS); let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap()); - let mut rebuilder = StateRebuilder::new(new_db, Algorithm::Archive); - let flag = AtomicBool::new(true); + { + let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent); + let flag = AtomicBool::new(true); - rebuilder.feed(&chunk1, &flag).unwrap(); - rebuilder.feed(&chunk2, &flag).unwrap(); + rebuilder.feed(&chunk1, &flag).unwrap(); + rebuilder.feed(&chunk2, &flag).unwrap(); - rebuilder.check_missing().unwrap(); + rebuilder.finalize(1000, H256::random()).unwrap(); + } + + let state_db = journaldb::new(new_db, Algorithm::OverlayRecent, ::db::COL_STATE); + assert_eq!(state_db.earliest_era(), Some(1000)); } #[test] @@ -175,7 +181,7 @@ fn checks_flag() { db_path.push("db"); { let new_db = Arc::new(Database::open(&db_cfg, &db_path.to_string_lossy()).unwrap()); - let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::Archive); + let mut rebuilder = StateRebuilder::new(new_db.clone(), Algorithm::OverlayRecent); let reader = PackedReader::new(&snap_file).unwrap().unwrap(); let flag = AtomicBool::new(false); From 4ef89b5ccb85abb33277976b866d2a3ea3e8ced3 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 24 Mar 2017 14:02:04 +0100 Subject: [PATCH 29/41] Fine grained snapshot chunking --- Cargo.lock | 1 + ethcore/Cargo.toml | 1 + ethcore/src/lib.rs | 1 + ethcore/src/snapshot/account.rs | 138 +++++++++++++++---------- ethcore/src/snapshot/error.rs | 3 + ethcore/src/snapshot/io.rs | 32 ++++-- ethcore/src/snapshot/mod.rs | 27 +++-- ethcore/src/snapshot/service.rs | 1 + ethcore/src/snapshot/tests/blocks.rs | 2 + ethcore/src/snapshot/tests/mod.rs | 3 +- ethcore/src/snapshot/tests/service.rs | 1 + ethcore/src/snapshot/tests/state.rs | 6 +- ethcore/src/types/snapshot_manifest.rs | 21 ++-- sync/src/chain.rs | 10 +- util/src/journaldb/overlayrecentdb.rs | 7 +- 15 files changed, 172 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index badb522c4..bf4249000 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -414,6 +414,7 @@ dependencies = [ "evmjit 1.7.0", "hardware-wallet 1.7.0", "hyper 0.10.0-a.0 (git+https://github.com/paritytech/hyper)", + "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index d910f9a56..040b0ede0 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -27,6 +27,7 @@ byteorder = "1.0" transient-hashmap = "0.4" linked-hash-map = "0.3.0" lru-cache = "0.1.0" +itertools = "0.5" ethabi = "1.0.0" evmjit = { path = "../evmjit", optional = true } clippy = { version = "0.0.103", optional = true} diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index df0738997..f5a52ff3e 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -108,6 +108,7 @@ extern crate hardware_wallet; extern crate stats; extern crate ethcore_logger; extern crate num; +extern crate itertools; #[macro_use] extern crate log; diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index c086b5c44..9395eafac 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -23,6 +23,7 @@ use snapshot::Error; use util::{U256, H256, Bytes, HashDB, SHA3_EMPTY, SHA3_NULL_RLP}; use util::trie::{TrieDB, Trie}; use rlp::{RlpStream, UntrustedRlp}; +use itertools::Itertools; use std::collections::HashSet; @@ -62,53 +63,50 @@ impl CodeState { // walk the account's storage trie, returning an RLP item containing the // account properties and the storage. -pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet) -> Result { +pub fn to_fat_rlps(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet, preferred_size: usize) -> Result, Error> { + const AVERAGE_BYTES_PER_STORAGE_ENTRY: usize = 47; if acc == &ACC_EMPTY { - return Ok(::rlp::NULL_RLP.to_vec()); + return Ok(vec![::rlp::NULL_RLP.to_vec()]); } let db = TrieDB::new(acct_db, &acc.storage_root)?; - let mut pairs = Vec::new(); + let pair_chunks: Vec> = db.iter()?.chunks(preferred_size / AVERAGE_BYTES_PER_STORAGE_ENTRY).into_iter().map(|chunk| chunk.collect()).collect(); + pair_chunks.into_iter().pad_using(1, |_| Vec::new(), ).map(|pairs| { + let mut stream = RlpStream::new_list(pairs.len()); - for item in db.iter()? { - let (k, v) = item?; - pairs.push((k, v)); - } + for r in pairs { + let (k, v) = r?; + stream.begin_list(2).append(&k).append(&&*v); + } - let mut stream = RlpStream::new_list(pairs.len()); + let pairs_rlp = stream.out(); - for (k, v) in pairs { - stream.begin_list(2).append(&k).append(&&*v); - } + let mut account_stream = RlpStream::new_list(5); + account_stream.append(&acc.nonce) + .append(&acc.balance); - let pairs_rlp = stream.out(); - - let mut account_stream = RlpStream::new_list(5); - account_stream.append(&acc.nonce) - .append(&acc.balance); - - // [has_code, code_hash]. - if acc.code_hash == SHA3_EMPTY { - account_stream.append(&CodeState::Empty.raw()).append_empty_data(); - } else if used_code.contains(&acc.code_hash) { - account_stream.append(&CodeState::Hash.raw()).append(&acc.code_hash); - } else { - match acct_db.get(&acc.code_hash) { - Some(c) => { - used_code.insert(acc.code_hash.clone()); - account_stream.append(&CodeState::Inline.raw()).append(&&*c); - } - None => { - warn!("code lookup failed during snapshot"); - account_stream.append(&false).append_empty_data(); + // [has_code, code_hash]. + if acc.code_hash == SHA3_EMPTY { + account_stream.append(&CodeState::Empty.raw()).append_empty_data(); + } else if used_code.contains(&acc.code_hash) { + account_stream.append(&CodeState::Hash.raw()).append(&acc.code_hash); + } else { + match acct_db.get(&acc.code_hash) { + Some(c) => { + used_code.insert(acc.code_hash.clone()); + account_stream.append(&CodeState::Inline.raw()).append(&&*c); + } + None => { + warn!("code lookup failed during snapshot"); + account_stream.append(&false).append_empty_data(); + } } } - } - account_stream.append_raw(&pairs_rlp, 1); - - Ok(account_stream.out()) + account_stream.append_raw(&pairs_rlp, 1); + Ok(account_stream.out()) + }).collect() } // decode a fat rlp, and rebuild the storage trie as we go. @@ -117,6 +115,7 @@ pub fn to_fat_rlp(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashS pub fn from_fat_rlp( acct_db: &mut AccountDBMut, rlp: UntrustedRlp, + mut storage_root: H256, ) -> Result<(BasicAccount, Option), Error> { use util::{TrieDBMut, TrieMut}; @@ -148,10 +147,12 @@ pub fn from_fat_rlp( } }; - let mut storage_root = H256::zero(); - { - let mut storage_trie = TrieDBMut::new(acct_db, &mut storage_root); + let mut storage_trie = if storage_root.is_zero() { + TrieDBMut::new(acct_db, &mut storage_root) + } else { + TrieDBMut::from_existing(acct_db, &mut storage_root)? + }; let pairs = rlp.at(4)?; for pair_rlp in pairs.iter() { let k: Bytes = pair_rlp.val_at(0)?; @@ -184,7 +185,7 @@ mod tests { use std::collections::HashSet; - use super::{ACC_EMPTY, to_fat_rlp, from_fat_rlp}; + use super::{ACC_EMPTY, to_fat_rlps, from_fat_rlp}; #[test] fn encoding_basic() { @@ -201,9 +202,9 @@ mod tests { let thin_rlp = ::rlp::encode(&account); assert_eq!(::rlp::decode::(&thin_rlp), account); - let fat_rlp = to_fat_rlp(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap(); - let fat_rlp = UntrustedRlp::new(&fat_rlp); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account); + let fat_rlps = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value()).unwrap(); + let fat_rlp = UntrustedRlp::new(&fat_rlps[0]); + assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account); } #[test] @@ -226,9 +227,40 @@ mod tests { let thin_rlp = ::rlp::encode(&account); assert_eq!(::rlp::decode::(&thin_rlp), account); - let fat_rlp = to_fat_rlp(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default()).unwrap(); - let fat_rlp = UntrustedRlp::new(&fat_rlp); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp).unwrap().0, account); + let fat_rlp = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), usize::max_value()).unwrap(); + let fat_rlp = UntrustedRlp::new(&fat_rlp[0]); + assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, H256::zero()).unwrap().0, account); + } + + #[test] + fn encoding_storage_split() { + let mut db = get_temp_state_db(); + let addr = Address::random(); + + let account = { + let acct_db = AccountDBMut::new(db.as_hashdb_mut(), &addr); + let mut root = SHA3_NULL_RLP; + fill_storage(acct_db, &mut root, &mut H256::zero()); + BasicAccount { + nonce: 25.into(), + balance: 987654321.into(), + storage_root: root, + code_hash: SHA3_EMPTY, + } + }; + + let thin_rlp = ::rlp::encode(&account); + assert_eq!(::rlp::decode::(&thin_rlp), account); + + let fat_rlps = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), 1000).unwrap(); + let mut root = SHA3_NULL_RLP; + let mut restored_account = None; + for rlp in fat_rlps { + let fat_rlp = UntrustedRlp::new(&rlp); + restored_account = Some(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr), fat_rlp, root).unwrap().0); + root = restored_account.as_ref().unwrap().storage_root.clone(); + } + assert_eq!(restored_account, Some(account)); } #[test] @@ -264,18 +296,18 @@ mod tests { let mut used_code = HashSet::new(); - let fat_rlp1 = to_fat_rlp(&account1, &AccountDB::new(db.as_hashdb(), &addr1), &mut used_code).unwrap(); - let fat_rlp2 = to_fat_rlp(&account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code).unwrap(); + let fat_rlp1 = to_fat_rlps(&account1, &AccountDB::new(db.as_hashdb(), &addr1), &mut used_code, usize::max_value()).unwrap(); + let fat_rlp2 = to_fat_rlps(&account2, &AccountDB::new(db.as_hashdb(), &addr2), &mut used_code, usize::max_value()).unwrap(); assert_eq!(used_code.len(), 1); - let fat_rlp1 = UntrustedRlp::new(&fat_rlp1); - let fat_rlp2 = UntrustedRlp::new(&fat_rlp2); + let fat_rlp1 = UntrustedRlp::new(&fat_rlp1[0]); + let fat_rlp2 = UntrustedRlp::new(&fat_rlp2[0]); - let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2).unwrap(); + let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr2), fat_rlp2, H256::zero()).unwrap(); assert!(maybe_code.is_none()); assert_eq!(acc, account2); - let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1).unwrap(); + let (acc, maybe_code) = from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &addr1), fat_rlp1, H256::zero()).unwrap(); assert_eq!(maybe_code, Some(b"this is definitely code".to_vec())); assert_eq!(acc, account1); } @@ -285,7 +317,7 @@ mod tests { let mut db = get_temp_state_db(); let mut used_code = HashSet::new(); - assert_eq!(to_fat_rlp(&ACC_EMPTY, &AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code).unwrap(), ::rlp::NULL_RLP.to_vec()); - assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP)).unwrap(), (ACC_EMPTY, None)); + assert_eq!(to_fat_rlps(&ACC_EMPTY, &AccountDB::new(db.as_hashdb(), &Address::default()), &mut used_code, usize::max_value()).unwrap(), vec![::rlp::NULL_RLP.to_vec()]); + assert_eq!(from_fat_rlp(&mut AccountDBMut::new(db.as_hashdb_mut(), &Address::default()), UntrustedRlp::new(&::rlp::NULL_RLP), H256::zero()).unwrap(), (ACC_EMPTY, None)); } } diff --git a/ethcore/src/snapshot/error.rs b/ethcore/src/snapshot/error.rs index 11491cd73..da8a9816b 100644 --- a/ethcore/src/snapshot/error.rs +++ b/ethcore/src/snapshot/error.rs @@ -53,6 +53,8 @@ pub enum Error { Decoder(DecoderError), /// Io error. Io(::std::io::Error), + /// Snapshot version is not supported. + VersionNotSupported(u64), } impl fmt::Display for Error { @@ -73,6 +75,7 @@ impl fmt::Display for Error { Error::Io(ref err) => err.fmt(f), Error::Decoder(ref err) => err.fmt(f), Error::Trie(ref err) => err.fmt(f), + Error::VersionNotSupported(ref ver) => write!(f, "Snapshot version {} is not supprted.", ver), } } } diff --git a/ethcore/src/snapshot/io.rs b/ethcore/src/snapshot/io.rs index e8f11efd1..8cb778117 100644 --- a/ethcore/src/snapshot/io.rs +++ b/ethcore/src/snapshot/io.rs @@ -31,6 +31,8 @@ use rlp::{self, Encodable, RlpStream, UntrustedRlp}; use super::ManifestData; +const SNAPSHOT_VERSION: u64 = 2; + /// Something which can write snapshots. /// Writing the same chunk multiple times will lead to implementation-defined /// behavior, and is not advised. @@ -118,8 +120,9 @@ impl SnapshotWriter for PackedWriter { fn finish(mut self, manifest: ManifestData) -> io::Result<()> { // we ignore the hashes fields of the manifest under the assumption that // they are consistent with ours. - let mut stream = RlpStream::new_list(5); + let mut stream = RlpStream::new_list(6); stream + .append(&SNAPSHOT_VERSION) .append_list(&self.state_hashes) .append_list(&self.block_hashes) .append(&manifest.state_root) @@ -221,7 +224,7 @@ impl PackedReader { /// Create a new `PackedReader` for the file at the given path. /// This will fail if any io errors are encountered or the file /// is not a valid packed snapshot. - pub fn new(path: &Path) -> Result, ::error::Error> { + pub fn new(path: &Path) -> Result, ::snapshot::error::Error> { let mut file = File::open(path)?; let file_len = file.metadata()?.len(); if file_len < 8 { @@ -255,15 +258,26 @@ impl PackedReader { let rlp = UntrustedRlp::new(&manifest_buf); - let state: Vec = rlp.list_at(0)?; - let blocks: Vec = rlp.list_at(1)?; + let (start, version) = if rlp.item_count()? == 5 { + (0, 1) + } else { + (1, rlp.val_at(0)?) + }; + + if version > SNAPSHOT_VERSION { + return Err(::snapshot::error::Error::VersionNotSupported(version)); + } + + let state: Vec = rlp.list_at(0 + start)?; + let blocks: Vec = rlp.list_at(1 + start)?; let manifest = ManifestData { + version: version, state_hashes: state.iter().map(|c| c.0).collect(), block_hashes: blocks.iter().map(|c| c.0).collect(), - state_root: rlp.val_at(2)?, - block_number: rlp.val_at(3)?, - block_hash: rlp.val_at(4)?, + state_root: rlp.val_at(2 + start)?, + block_number: rlp.val_at(3 + start)?, + block_hash: rlp.val_at(4 + start)?, }; Ok(Some(PackedReader { @@ -346,7 +360,7 @@ mod tests { use util::sha3::Hashable; use snapshot::ManifestData; - use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader}; + use super::{SnapshotWriter, SnapshotReader, PackedWriter, PackedReader, LooseWriter, LooseReader, SNAPSHOT_VERSION}; const STATE_CHUNKS: &'static [&'static [u8]] = &[b"dog", b"cat", b"hello world", b"hi", b"notarealchunk"]; const BLOCK_CHUNKS: &'static [&'static [u8]] = &[b"hello!", b"goodbye!", b"abcdefg", b"hijklmnop", b"qrstuvwxy", b"and", b"z"]; @@ -372,6 +386,7 @@ mod tests { } let manifest = ManifestData { + version: SNAPSHOT_VERSION, state_hashes: state_hashes, block_hashes: block_hashes, state_root: b"notarealroot".sha3(), @@ -410,6 +425,7 @@ mod tests { } let manifest = ManifestData { + version: SNAPSHOT_VERSION, state_hashes: state_hashes, block_hashes: block_hashes, state_root: b"notarealroot".sha3(), diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 89901502b..57d044a47 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -56,6 +56,7 @@ pub use self::traits::SnapshotService; pub use self::watcher::Watcher; pub use types::snapshot_manifest::ManifestData; pub use types::restoration_status::RestorationStatus; +pub use types::basic_account::BasicAccount; pub mod io; pub mod service; @@ -147,6 +148,7 @@ pub fn take_snapshot( info!("produced {} state chunks and {} block chunks.", state_hashes.len(), block_hashes.len()); let manifest_data = ManifestData { + version: 2, state_hashes: state_hashes, block_hashes: block_hashes, state_root: *state_root, @@ -300,14 +302,14 @@ impl<'a> StateChunker<'a> { // // If the buffer is greater than the desired chunk size, // this will write out the data to disk. - fn push(&mut self, account_hash: Bytes, data: Bytes) -> Result<(), Error> { + fn push(&mut self, account_hash: Bytes, data: Bytes, force_chunk: bool) -> Result<(), Error> { let pair = { let mut stream = RlpStream::new_list(2); stream.append(&account_hash).append_raw(&data, 1); stream.out() }; - if self.cur_size + pair.len() >= PREFERRED_CHUNK_SIZE { + if force_chunk || self.cur_size + pair.len() >= PREFERRED_CHUNK_SIZE { self.write_chunk()?; } @@ -372,8 +374,10 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex 0)?; + } } if chunker.cur_size != 0 { @@ -390,6 +394,7 @@ pub struct StateRebuilder { known_code: HashMap, // code hashes mapped to first account with this code. missing_code: HashMap>, // maps code hashes to lists of accounts missing that code. bloom: Bloom, + known_storage_roots: HashMap, // maps account hashes to last known storage root. Only filled for last account per chunk. } impl StateRebuilder { @@ -401,6 +406,7 @@ impl StateRebuilder { known_code: HashMap::new(), missing_code: HashMap::new(), bloom: StateDB::load_bloom(&*db), + known_storage_roots: HashMap::new(), } } @@ -418,6 +424,7 @@ impl StateRebuilder { rlp, &mut pairs, &self.known_code, + &mut self.known_storage_roots, flag )?; @@ -496,10 +503,11 @@ fn rebuild_accounts( account_fat_rlps: UntrustedRlp, out_chunk: &mut [(H256, Bytes)], known_code: &HashMap, + known_storage_roots: &mut HashMap, abort_flag: &AtomicBool, ) -> Result { let mut status = RebuiltStatus::default(); - for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk) { + for (account_rlp, out) in account_fat_rlps.into_iter().zip(out_chunk.iter_mut()) { if !abort_flag.load(Ordering::SeqCst) { return Err(Error::RestorationAborted.into()) } let hash: H256 = account_rlp.val_at(0)?; @@ -510,7 +518,8 @@ fn rebuild_accounts( // fill out the storage trie and code while decoding. let (acc, maybe_code) = { let mut acct_db = AccountDBMut::from_hash(db, hash); - account::from_fat_rlp(&mut acct_db, fat_rlp)? + let storage_root = known_storage_roots.get(&hash).cloned().unwrap_or(H256::zero()); + account::from_fat_rlp(&mut acct_db, fat_rlp, storage_root)? }; let code_hash = acc.code_hash.clone(); @@ -542,6 +551,12 @@ fn rebuild_accounts( *out = (hash, thin_rlp); } + if let Some(&(ref hash, ref rlp)) = out_chunk.iter().last() { + known_storage_roots.insert(*hash, ::rlp::decode::(rlp).storage_root); + } + if let Some(&(ref hash, ref rlp)) = out_chunk.iter().next() { + known_storage_roots.insert(*hash, ::rlp::decode::(rlp).storage_root); + } Ok(status) } diff --git a/ethcore/src/snapshot/service.rs b/ethcore/src/snapshot/service.rs index ee39203ff..06e659bc1 100644 --- a/ethcore/src/snapshot/service.rs +++ b/ethcore/src/snapshot/service.rs @@ -656,6 +656,7 @@ mod tests { assert_eq!(service.status(), RestorationStatus::Inactive); let manifest = ManifestData { + version: 2, state_hashes: vec![], block_hashes: vec![], state_root: Default::default(), diff --git a/ethcore/src/snapshot/tests/blocks.rs b/ethcore/src/snapshot/tests/blocks.rs index f2ee40a7b..ff63afdfc 100644 --- a/ethcore/src/snapshot/tests/blocks.rs +++ b/ethcore/src/snapshot/tests/blocks.rs @@ -63,6 +63,7 @@ fn chunk_and_restore(amount: u64) { let writer = Mutex::new(PackedWriter::new(&snapshot_path).unwrap()); let block_hashes = chunk_blocks(&bc, best_hash, &writer, &Progress::default()).unwrap(); let manifest = ::snapshot::ManifestData { + version: 2, state_hashes: Vec::new(), block_hashes: block_hashes, state_root: ::util::sha3::SHA3_NULL_RLP, @@ -125,6 +126,7 @@ fn checks_flag() { let chain = BlockChain::new(Default::default(), &genesis, db.clone()); let manifest = ::snapshot::ManifestData { + version: 2, state_hashes: Vec::new(), block_hashes: Vec::new(), state_root: ::util::sha3::SHA3_NULL_RLP, diff --git a/ethcore/src/snapshot/tests/mod.rs b/ethcore/src/snapshot/tests/mod.rs index e63cd6c7c..6530bb42a 100644 --- a/ethcore/src/snapshot/tests/mod.rs +++ b/ethcore/src/snapshot/tests/mod.rs @@ -27,6 +27,7 @@ use super::ManifestData; #[test] fn manifest_rlp() { let manifest = ManifestData { + version: 2, block_hashes: Vec::new(), state_hashes: Vec::new(), block_number: 1234567, @@ -35,4 +36,4 @@ fn manifest_rlp() { }; let raw = manifest.clone().into_rlp(); assert_eq!(ManifestData::from_rlp(&raw).unwrap(), manifest); -} \ No newline at end of file +} diff --git a/ethcore/src/snapshot/tests/service.rs b/ethcore/src/snapshot/tests/service.rs index 555ee665b..64a8407aa 100644 --- a/ethcore/src/snapshot/tests/service.rs +++ b/ethcore/src/snapshot/tests/service.rs @@ -122,6 +122,7 @@ fn guards_delete_folders() { path.push("restoration"); let manifest = ManifestData { + version: 2, state_hashes: vec![], block_hashes: vec![], block_number: 0, diff --git a/ethcore/src/snapshot/tests/state.rs b/ethcore/src/snapshot/tests/state.rs index 4b0a33c71..cbe7e172f 100644 --- a/ethcore/src/snapshot/tests/state.rs +++ b/ethcore/src/snapshot/tests/state.rs @@ -58,6 +58,7 @@ fn snap_and_restore() { let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap(); writer.into_inner().finish(::snapshot::ManifestData { + version: 2, state_hashes: state_hashes, block_hashes: Vec::new(), state_root: state_root, @@ -121,10 +122,10 @@ fn get_code_from_prev_chunk() { let mut db = MemoryDB::new(); AccountDBMut::from_hash(&mut db, hash).insert(&code[..]); - let fat_rlp = account::to_fat_rlp(&acc, &AccountDB::from_hash(&db, hash), &mut used_code).unwrap(); + let fat_rlp = account::to_fat_rlps(&acc, &AccountDB::from_hash(&db, hash), &mut used_code, usize::max_value()).unwrap(); let mut stream = RlpStream::new_list(1); - stream.begin_list(2).append(&hash).append_raw(&fat_rlp, 1); + stream.begin_list(2).append(&hash).append_raw(&fat_rlp[0], 1); stream.out() }; @@ -170,6 +171,7 @@ fn checks_flag() { let state_hashes = chunk_state(&old_db, &state_root, &writer, &Progress::default()).unwrap(); writer.into_inner().finish(::snapshot::ManifestData { + version: 2, state_hashes: state_hashes, block_hashes: Vec::new(), state_root: state_root, diff --git a/ethcore/src/types/snapshot_manifest.rs b/ethcore/src/types/snapshot_manifest.rs index 84ca05822..58b25b6a7 100644 --- a/ethcore/src/types/snapshot_manifest.rs +++ b/ethcore/src/types/snapshot_manifest.rs @@ -24,6 +24,8 @@ use util::Bytes; #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "ipc", binary)] pub struct ManifestData { + /// Snapshot format version. + pub version: u64, /// List of state chunk hashes. pub state_hashes: Vec, /// List of block chunk hashes. @@ -39,7 +41,8 @@ pub struct ManifestData { impl ManifestData { /// Encode the manifest data to rlp. pub fn into_rlp(self) -> Bytes { - let mut stream = RlpStream::new_list(5); + let mut stream = RlpStream::new_list(6); + stream.append(&self.version); stream.append_list(&self.state_hashes); stream.append_list(&self.block_hashes); stream.append(&self.state_root); @@ -52,14 +55,20 @@ impl ManifestData { /// Try to restore manifest data from raw bytes, interpreted as RLP. pub fn from_rlp(raw: &[u8]) -> Result { let decoder = UntrustedRlp::new(raw); + let (start, version) = if decoder.item_count()? == 5 { + (0, 1) + } else { + (1, decoder.val_at(0)?) + }; - let state_hashes: Vec = decoder.list_at(0)?; - let block_hashes: Vec = decoder.list_at(1)?; - let state_root: H256 = decoder.val_at(2)?; - let block_number: u64 = decoder.val_at(3)?; - let block_hash: H256 = decoder.val_at(4)?; + let state_hashes: Vec = decoder.list_at(start + 0)?; + let block_hashes: Vec = decoder.list_at(start + 1)?; + let state_root: H256 = decoder.val_at(start + 2)?; + let block_number: u64 = decoder.val_at(start + 3)?; + let block_hash: H256 = decoder.val_at(start + 4)?; Ok(ManifestData { + version: version, state_hashes: state_hashes, block_hashes: block_hashes, state_root: state_root, diff --git a/sync/src/chain.rs b/sync/src/chain.rs index df5f65a86..ca5172fc2 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -158,6 +158,8 @@ pub const SNAPSHOT_SYNC_PACKET_COUNT: u8 = 0x16; const MAX_SNAPSHOT_CHUNKS_DOWNLOAD_AHEAD: usize = 3; +const MIN_SUPPORTED_SNAPSHOT_MANIFEST_VERSION: u64 = 1; + const WAIT_PEERS_TIMEOUT_SEC: u64 = 5; const STATUS_TIMEOUT_SEC: u64 = 5; const HEADERS_TIMEOUT_SEC: u64 = 15; @@ -1028,12 +1030,18 @@ impl ChainSync { let manifest = match ManifestData::from_rlp(manifest_rlp.as_raw()) { Err(e) => { trace!(target: "sync", "{}: Ignored bad manifest: {:?}", peer_id, e); - io.disconnect_peer(peer_id); + io.disable_peer(peer_id); self.continue_sync(io); return Ok(()); } Ok(manifest) => manifest, }; + if manifest.version < MIN_SUPPORTED_SNAPSHOT_MANIFEST_VERSION { + trace!(target: "sync", "{}: Snapshot manifest version too low: {}", peer_id, manifest.version); + io.disable_peer(peer_id); + self.continue_sync(io); + return Ok(()); + } self.snapshot.reset_to(&manifest, &manifest_rlp.as_raw().sha3()); io.snapshot_service().begin_restore(manifest); self.state = SyncState::SnapshotData; diff --git a/util/src/journaldb/overlayrecentdb.rs b/util/src/journaldb/overlayrecentdb.rs index 7eeabf6df..af92d3b40 100644 --- a/util/src/journaldb/overlayrecentdb.rs +++ b/util/src/journaldb/overlayrecentdb.rs @@ -380,10 +380,7 @@ impl JournalDB for OverlayRecentDB { match rc { 0 => {} - 1 => { - if cfg!(debug_assertions) && self.backing.get(self.column, &key)?.is_some() { - return Err(BaseDataError::AlreadyExists(key).into()); - } + _ if rc > 0 => { batch.put(self.column, &key, &value) } -1 => { @@ -392,7 +389,7 @@ impl JournalDB for OverlayRecentDB { } batch.delete(self.column, &key) } - _ => panic!("Attempted to inject invalid state."), + _ => panic!("Attempted to inject invalid state ({})", rc), } } From 56ba9a30ac46e0830ed9a1258176f61c2ec295a9 Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 24 Mar 2017 14:29:03 +0100 Subject: [PATCH 30/41] Fixed sync tests --- sync/src/snapshot.rs | 1 + sync/src/tests/snapshot.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/sync/src/snapshot.rs b/sync/src/snapshot.rs index 94794a4d4..727991caa 100644 --- a/sync/src/snapshot.rs +++ b/sync/src/snapshot.rs @@ -144,6 +144,7 @@ mod test { let state_chunks: Vec = (0..20).map(|_| H256::random().to_vec()).collect(); let block_chunks: Vec = (0..20).map(|_| H256::random().to_vec()).collect(); let manifest = ManifestData { + version: 2, state_hashes: state_chunks.iter().map(|data| data.sha3()).collect(), block_hashes: block_chunks.iter().map(|data| data.sha3()).collect(), state_root: H256::new(), diff --git a/sync/src/tests/snapshot.rs b/sync/src/tests/snapshot.rs index 0affb0b1a..16114e216 100644 --- a/sync/src/tests/snapshot.rs +++ b/sync/src/tests/snapshot.rs @@ -49,6 +49,7 @@ impl TestSnapshotService { let state_chunks: Vec = (0..num_state_chunks).map(|_| H256::random().to_vec()).collect(); let block_chunks: Vec = (0..num_block_chunks).map(|_| H256::random().to_vec()).collect(); let manifest = ManifestData { + version: 2, state_hashes: state_chunks.iter().map(|data| data.sha3()).collect(), block_hashes: block_chunks.iter().map(|data| data.sha3()).collect(), state_root: H256::new(), From b52c7bba4bd325251636a9a24a1460b643852d6d Mon Sep 17 00:00:00 2001 From: arkpar Date: Fri, 24 Mar 2017 15:43:54 +0100 Subject: [PATCH 31/41] Fewer allocations --- ethcore/src/snapshot/account.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index 9395eafac..4f80d61ea 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -71,8 +71,9 @@ pub fn to_fat_rlps(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut Hash let db = TrieDB::new(acct_db, &acc.storage_root)?; - let pair_chunks: Vec> = db.iter()?.chunks(preferred_size / AVERAGE_BYTES_PER_STORAGE_ENTRY).into_iter().map(|chunk| chunk.collect()).collect(); - pair_chunks.into_iter().pad_using(1, |_| Vec::new(), ).map(|pairs| { + let chunks = db.iter()?.chunks(preferred_size / AVERAGE_BYTES_PER_STORAGE_ENTRY); + let pair_chunks = chunks.into_iter().map(|chunk| chunk.collect()); + pair_chunks.pad_using(1, |_| Vec::new(), ).map(|pairs| { let mut stream = RlpStream::new_list(pairs.len()); for r in pairs { From b0a9c1e0fa87eb25a729d47af4ae5d1ae3b838b5 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sat, 25 Mar 2017 15:25:20 +0100 Subject: [PATCH 32/41] Limit by entry count --- ethcore/src/snapshot/account.rs | 7 +++---- ethcore/src/snapshot/mod.rs | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index 4f80d61ea..b06c220e7 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -63,15 +63,14 @@ impl CodeState { // walk the account's storage trie, returning an RLP item containing the // account properties and the storage. -pub fn to_fat_rlps(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet, preferred_size: usize) -> Result, Error> { - const AVERAGE_BYTES_PER_STORAGE_ENTRY: usize = 47; +pub fn to_fat_rlps(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet, max_storage_items: usize) -> Result, Error> { if acc == &ACC_EMPTY { return Ok(vec![::rlp::NULL_RLP.to_vec()]); } let db = TrieDB::new(acct_db, &acc.storage_root)?; - let chunks = db.iter()?.chunks(preferred_size / AVERAGE_BYTES_PER_STORAGE_ENTRY); + let chunks = db.iter()?.chunks(max_storage_items); let pair_chunks = chunks.into_iter().map(|chunk| chunk.collect()); pair_chunks.pad_using(1, |_| Vec::new(), ).map(|pairs| { let mut stream = RlpStream::new_list(pairs.len()); @@ -253,7 +252,7 @@ mod tests { let thin_rlp = ::rlp::encode(&account); assert_eq!(::rlp::decode::(&thin_rlp), account); - let fat_rlps = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), 1000).unwrap(); + let fat_rlps = to_fat_rlps(&account, &AccountDB::new(db.as_hashdb(), &addr), &mut Default::default(), 100).unwrap(); let mut root = SHA3_NULL_RLP; let mut restored_account = None; for rlp in fat_rlps { diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 57d044a47..1241152d0 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -83,6 +83,9 @@ mod traits { // Try to have chunks be around 4MB (before compression) const PREFERRED_CHUNK_SIZE: usize = 4 * 1024 * 1024; +// Try to have chunks be around 4MB (before compression) +const MAX_STORAGE_ENTRIES_PER_ACCOUNT_RECORD: usize = 80_000; + // How many blocks to include in a snapshot, starting from the head of the chain. const SNAPSHOT_BLOCKS: u64 = 30000; @@ -374,7 +377,7 @@ pub fn chunk_state<'a>(db: &HashDB, root: &H256, writer: &Mutex 0)?; } From 387a5fb03b83dda786fc81e38020afd8871b4ff3 Mon Sep 17 00:00:00 2001 From: arkpar Date: Sun, 26 Mar 2017 13:26:20 +0200 Subject: [PATCH 33/41] Doc --- ethcore/src/snapshot/account.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index b06c220e7..dacd9ba52 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -61,8 +61,9 @@ impl CodeState { } } -// walk the account's storage trie, returning an RLP item containing the -// account properties and the storage. +// walk the account's storage trie, returning a vector of RLP items containing the +// account properties and the storage. Each item contains at most `max_storage_items` +// storage records split according to snapshot format definition. pub fn to_fat_rlps(acc: &BasicAccount, acct_db: &AccountDB, used_code: &mut HashSet, max_storage_items: usize) -> Result, Error> { if acc == &ACC_EMPTY { return Ok(vec![::rlp::NULL_RLP.to_vec()]); From 23d3b79d9aa2a411b6414bbe35de80569eb93fe2 Mon Sep 17 00:00:00 2001 From: Marek Kotewicz Date: Mon, 27 Mar 2017 16:42:40 +0700 Subject: [PATCH 34/41] eip100b (#5027) * eip100b * fix eip100b build and tests * Conventional comparison order --- ethcore/src/ethereum/ethash.rs | 28 ++++++++++++++++++++++------ ethcore/src/tests/helpers.rs | 2 ++ json/src/spec/ethash.rs | 8 ++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index b1907971c..5bc31c5d9 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -43,6 +43,8 @@ pub struct EthashParams { pub difficulty_bound_divisor: U256, /// Difficulty increment divisor. pub difficulty_increment_divisor: u64, + /// Metropolis difficulty increment divisor. + pub metropolis_difficulty_increment_divisor: u64, /// Block duration. pub duration_limit: u64, /// Block reward. @@ -63,6 +65,8 @@ pub struct EthashParams { pub difficulty_hardfork_bound_divisor: U256, /// Block on which there is no additional difficulty from the exponential bomb. pub bomb_defuse_transition: u64, + /// Number of first block where EIP-100 rules begin. + pub eip100b_transition: u64, /// Number of first block where EIP-150 rules begin. pub eip150_transition: u64, /// Number of first block where EIP-155 rules begin. @@ -96,6 +100,7 @@ impl From for EthashParams { minimum_difficulty: p.minimum_difficulty.into(), difficulty_bound_divisor: p.difficulty_bound_divisor.into(), difficulty_increment_divisor: p.difficulty_increment_divisor.map_or(10, Into::into), + metropolis_difficulty_increment_divisor: p.metropolis_difficulty_increment_divisor.map_or(9, Into::into), duration_limit: p.duration_limit.into(), block_reward: p.block_reward.into(), registrar: p.registrar.map_or_else(Address::new, Into::into), @@ -106,6 +111,7 @@ impl From for EthashParams { difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(u64::max_value(), Into::into), difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), Into::into), bomb_defuse_transition: p.bomb_defuse_transition.map_or(u64::max_value(), Into::into), + eip100b_transition: p.eip100b_transition.map_or(u64::max_value(), Into::into), eip150_transition: p.eip150_transition.map_or(0, Into::into), eip155_transition: p.eip155_transition.map_or(0, Into::into), eip160_transition: p.eip160_transition.map_or(0, Into::into), @@ -406,6 +412,8 @@ impl Ethash { panic!("Can't calculate genesis block difficulty"); } + let parent_has_uncles = parent.uncles_hash() != &sha3::SHA3_EMPTY_LIST_RLP; + let min_difficulty = self.ethash_params.minimum_difficulty; let difficulty_hardfork = header.number() >= self.ethash_params.difficulty_hardfork_transition; let difficulty_bound_divisor = match difficulty_hardfork { @@ -417,19 +425,27 @@ impl Ethash { let mut target = if header.number() < frontier_limit { if header.timestamp() >= parent.timestamp() + duration_limit { - parent.difficulty().clone() - (parent.difficulty().clone() / difficulty_bound_divisor) + *parent.difficulty() - (*parent.difficulty() / difficulty_bound_divisor) } else { - parent.difficulty().clone() + (parent.difficulty().clone() / difficulty_bound_divisor) + *parent.difficulty() + (*parent.difficulty() / difficulty_bound_divisor) } } else { trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty(), header.timestamp(), parent.timestamp()); //block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99) - let diff_inc = (header.timestamp() - parent.timestamp()) / self.ethash_params.difficulty_increment_divisor; - if diff_inc <= 1 { - parent.difficulty().clone() + parent.difficulty().clone() / From::from(difficulty_bound_divisor) * From::from(1 - diff_inc) + let (increment_divisor, threshold) = if header.number() < self.ethash_params.eip100b_transition { + (self.ethash_params.difficulty_increment_divisor, 1) + } else if parent_has_uncles { + (self.ethash_params.metropolis_difficulty_increment_divisor, 2) } else { - parent.difficulty().clone() - parent.difficulty().clone() / From::from(difficulty_bound_divisor) * From::from(min(diff_inc - 1, 99)) + (self.ethash_params.metropolis_difficulty_increment_divisor, 1) + }; + + let diff_inc = (header.timestamp() - parent.timestamp()) / increment_divisor; + if diff_inc <= threshold { + *parent.difficulty() + *parent.difficulty() / difficulty_bound_divisor * (threshold - diff_inc).into() + } else { + *parent.difficulty() - *parent.difficulty() / difficulty_bound_divisor * min(diff_inc - threshold, 99).into() } }; target = max(min_difficulty, target); diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index dcb173b63..2f55fd581 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -438,6 +438,7 @@ pub fn get_default_ethash_params() -> EthashParams{ minimum_difficulty: U256::from(131072), difficulty_bound_divisor: U256::from(2048), difficulty_increment_divisor: 10, + metropolis_difficulty_increment_divisor: 9, duration_limit: 13, block_reward: U256::from(0), registrar: "0000000000000000000000000000000000000001".into(), @@ -448,6 +449,7 @@ pub fn get_default_ethash_params() -> EthashParams{ difficulty_hardfork_transition: u64::max_value(), difficulty_hardfork_bound_divisor: U256::from(0), bomb_defuse_transition: u64::max_value(), + eip100b_transition: u64::max_value(), eip150_transition: u64::max_value(), eip155_transition: u64::max_value(), eip160_transition: u64::max_value(), diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index 95a70f8a0..16d05a18f 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -35,6 +35,9 @@ pub struct EthashParams { #[serde(rename="difficultyIncrementDivisor")] pub difficulty_increment_divisor: Option, /// See main EthashParams docs. + #[serde(rename="metropolisDifficultyIncrementDivisor")] + pub metropolis_difficulty_increment_divisor: Option, + /// See main EthashParams docs. #[serde(rename="durationLimit")] pub duration_limit: Uint, /// See main EthashParams docs. @@ -67,6 +70,10 @@ pub struct EthashParams { #[serde(rename="bombDefuseTransition")] pub bomb_defuse_transition: Option, + /// See main EthashParams docs. + #[serde(rename="eip100bTransition")] + pub eip100b_transition: Option, + /// See main EthashParams docs. #[serde(rename="eip150Transition")] pub eip150_transition: Option, @@ -164,6 +171,7 @@ mod tests { "difficultyHardforkTransition": "0x59d9", "difficultyHardforkBoundDivisor": "0x0200", "bombDefuseTransition": "0x42", + "eip100bTransition": "0x42", "eip150Transition": "0x42", "eip155Transition": "0x42", "eip160Transition": "0x42", From e8c64b802dbb0980ea815e4894f5913fb1620977 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 27 Mar 2017 11:42:59 +0200 Subject: [PATCH 35/41] Auto-extract new i18n strings (update) (#5288) * Update passwordHint chain * Update auto-extracted strings --- js/src/i18n/_default/account.js | 6 +- js/src/i18n/_default/accounts.js | 7 +- js/src/i18n/_default/addAddress.js | 1 + js/src/i18n/_default/address.js | 28 +++++ js/src/i18n/_default/addresses.js | 25 +++++ js/src/i18n/_default/application.js | 3 + js/src/i18n/_default/contract.js | 24 +++- js/src/i18n/_default/contracts.js | 28 +++++ js/src/i18n/_default/createAccount.js | 30 ++--- js/src/i18n/_default/createWallet.js | 1 + js/src/i18n/_default/dapps.js | 1 - js/src/i18n/_default/deployContract.js | 9 ++ js/src/i18n/_default/faucet.js | 28 +++++ js/src/i18n/_default/firstRun.js | 17 +++ js/src/i18n/_default/index.js | 10 ++ js/src/i18n/_default/passwordChange.js | 1 + js/src/i18n/_default/saveContract.js | 27 +++++ js/src/i18n/_default/settings.js | 26 +++-- js/src/i18n/_default/signer.js | 103 ++++++++++++++++++ js/src/i18n/_default/status.js | 66 +++++++++++ js/src/i18n/_default/transfer.js | 34 ++++++ js/src/i18n/_default/ui.js | 97 +++++++++++++++-- js/src/i18n/_default/upgradeParity.js | 9 +- js/src/i18n/_default/vaults.js | 37 ++++++- js/src/i18n/_default/verification.js | 85 +++++++++++++++ js/src/i18n/_default/wallet.js | 45 ++++++++ js/src/i18n/_default/walletSettings.js | 23 +++- js/src/i18n/_default/writeContract.js | 62 +++++++++++ .../modals/PasswordManager/passwordManager.js | 2 +- 29 files changed, 789 insertions(+), 46 deletions(-) create mode 100644 js/src/i18n/_default/address.js create mode 100644 js/src/i18n/_default/addresses.js create mode 100644 js/src/i18n/_default/contracts.js create mode 100644 js/src/i18n/_default/faucet.js create mode 100644 js/src/i18n/_default/saveContract.js create mode 100644 js/src/i18n/_default/signer.js create mode 100644 js/src/i18n/_default/status.js create mode 100644 js/src/i18n/_default/verification.js create mode 100644 js/src/i18n/_default/wallet.js create mode 100644 js/src/i18n/_default/writeContract.js diff --git a/js/src/i18n/_default/account.js b/js/src/i18n/_default/account.js index ce5d277dd..7c9d25520 100644 --- a/js/src/i18n/_default/account.js +++ b/js/src/i18n/_default/account.js @@ -16,13 +16,17 @@ export default { button: { - delete: `delete account`, + delete: `delete`, edit: `edit`, + faucet: `Kovan ETH`, password: `password`, shapeshift: `shapeshift`, transfer: `transfer`, verify: `verify` }, + hardware: { + confirmDelete: `Are you sure you want to remove the following hardware address from your account list?` + }, header: { outgoingTransactions: `{count} outgoing transactions`, uuid: `uuid: {uuid}` diff --git a/js/src/i18n/_default/accounts.js b/js/src/i18n/_default/accounts.js index d22c5a504..2db3d5fd9 100644 --- a/js/src/i18n/_default/accounts.js +++ b/js/src/i18n/_default/accounts.js @@ -16,8 +16,8 @@ export default { button: { - newAccount: `new account`, - newWallet: `new wallet`, + newAccount: `account`, + newWallet: `wallet`, vaults: `vaults` }, summary: { @@ -27,5 +27,8 @@ export default { tooltip: { actions: `actions relating to the current view are available on the toolbar for quick access, be it for performing actions or creating a new item`, overview: `your accounts are visible for easy access, allowing you to edit the meta information, make transfers, view transactions and fund the account` + }, + tooltips: { + owner: `{name} (owner)` } }; diff --git a/js/src/i18n/_default/addAddress.js b/js/src/i18n/_default/addAddress.js index 38fd7e361..59ae71518 100644 --- a/js/src/i18n/_default/addAddress.js +++ b/js/src/i18n/_default/addAddress.js @@ -19,6 +19,7 @@ export default { add: `Save Address`, close: `Cancel` }, + header: `To add a new entry to your addressbook, you need the network address of the account and can supply an optional description. Once added it will reflect in your address book.`, input: { address: { hint: `the network address for the entry`, diff --git a/js/src/i18n/_default/address.js b/js/src/i18n/_default/address.js new file mode 100644 index 000000000..ec06d4237 --- /dev/null +++ b/js/src/i18n/_default/address.js @@ -0,0 +1,28 @@ +// 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 . + +export default { + buttons: { + edit: `edit`, + forget: `forget`, + save: `save` + }, + delete: { + confirmInfo: `Are you sure you want to remove the following address from your addressbook?`, + title: `confirm removal` + }, + title: `Address Information` +}; diff --git a/js/src/i18n/_default/addresses.js b/js/src/i18n/_default/addresses.js new file mode 100644 index 000000000..00bb28272 --- /dev/null +++ b/js/src/i18n/_default/addresses.js @@ -0,0 +1,25 @@ +// 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 . + +export default { + buttons: { + add: `address` + }, + errors: { + invalidFile: `The provided file is invalid...` + }, + title: `Saved Addresses` +}; diff --git a/js/src/i18n/_default/application.js b/js/src/i18n/_default/application.js index 1edafff6d..4469d83b2 100644 --- a/js/src/i18n/_default/application.js +++ b/js/src/i18n/_default/application.js @@ -15,6 +15,9 @@ // along with Parity. If not, see . export default { + frame: { + error: `ERROR: This application cannot and should not be loaded in an embedded iFrame` + }, status: { consensus: { capable: `Capable`, diff --git a/js/src/i18n/_default/contract.js b/js/src/i18n/_default/contract.js index e6a2a110b..bf7f00298 100644 --- a/js/src/i18n/_default/contract.js +++ b/js/src/i18n/_default/contract.js @@ -15,5 +15,27 @@ // along with Parity. If not, see . export default { - minedBlock: `Mined at block #{blockNumber}` + buttons: { + close: `Close`, + details: `details`, + edit: `edit`, + execute: `execute`, + forget: `forget` + }, + details: { + title: `contract details` + }, + events: { + eventPending: `pending`, + noEvents: `No events has been sent from this contract.`, + title: `events` + }, + minedBlock: `Mined at block #{blockNumber}`, + queries: { + buttons: { + query: `Query` + }, + title: `queries` + }, + title: `Contract Information` }; diff --git a/js/src/i18n/_default/contracts.js b/js/src/i18n/_default/contracts.js new file mode 100644 index 000000000..4ff840759 --- /dev/null +++ b/js/src/i18n/_default/contracts.js @@ -0,0 +1,28 @@ +// 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 . + +export default { + buttons: { + deploy: `deploy`, + develop: `develop`, + watch: `watch` + }, + sortOrder: { + date: `date`, + minedBlock: `mined block` + }, + title: `Contracts` +}; diff --git a/js/src/i18n/_default/createAccount.js b/js/src/i18n/_default/createAccount.js index 485a877f8..8930fc7b4 100644 --- a/js/src/i18n/_default/createAccount.js +++ b/js/src/i18n/_default/createAccount.js @@ -20,46 +20,49 @@ export default { hint: `the network address for the account`, label: `address` }, - name: { - hint: `a descriptive name for the account`, - label: `account name` - }, phrase: { hint: `the account recovery phrase`, label: `owner recovery phrase (keep private and secure, it allows full and unlimited access to the account)` } }, accountDetailsGeth: { - imported: `You have imported {number} addresses from the Geth keystore:` + imported: `You have completed the import of {number} addresses from the Geth keystore. These will now be available in your accounts list as a normal account, along with their associated balances on the network.` }, button: { back: `Back`, cancel: `Cancel`, - close: `Close`, create: `Create`, + done: `Done`, import: `Import`, next: `Next`, print: `Print Phrase` }, creationType: { fromGeth: { - label: `Import accounts from Geth keystore` + description: `Import accounts from the Geth keystore with the original password`, + label: `Geth keystore` }, fromJSON: { - label: `Import account from a backup JSON file` + description: `Import an industry-standard JSON keyfile with the original password`, + label: `JSON file` }, fromNew: { - label: `Create new account manually` + description: `Selecting your identity icon and specifying the password`, + label: `New Account` }, fromPhrase: { - label: `Recover account from recovery phrase` + description: `Recover using a previously stored recovery phrase and new password`, + label: `Recovery phrase` }, fromPresale: { - label: `Import account from an Ethereum pre-sale wallet` + description: `Import an Ethereum presale wallet file with the original password`, + label: `Presale wallet` }, fromRaw: { - label: `Import raw private key` - } + description: `Enter a previously created raw private key with a new password`, + label: `Private key` + }, + info: `Please select the type of account you want to create. Either create an account via name & password, or import it from a variety of existing sources. From here the wizard will guide you through the process of completing your account creation.` }, newAccount: { hint: { @@ -80,6 +83,7 @@ export default { } }, newGeth: { + available: `There are currently {count} importable keys available from the Geth keystore which are not already available on your Parity instance. Select the accounts you wish to import and move to the next step to complete the import.`, noKeys: `There are currently no importable keys available from the Geth keystore, which are not already available on your Parity instance` }, newImport: { diff --git a/js/src/i18n/_default/createWallet.js b/js/src/i18n/_default/createWallet.js index 9c7d8df3e..eeb9e9a98 100644 --- a/js/src/i18n/_default/createWallet.js +++ b/js/src/i18n/_default/createWallet.js @@ -80,6 +80,7 @@ export default { }, states: { completed: `The contract deployment has been completed`, + confirmationNeeded: `The contract deployment needs confirmations from other owners of the Wallet`, preparing: `Preparing transaction for network transmission`, validatingCode: `Validating the deployed contract code`, waitingConfirm: `Waiting for confirmation of the transaction in the Parity Secure Signer`, diff --git a/js/src/i18n/_default/dapps.js b/js/src/i18n/_default/dapps.js index d13caa46d..9ed3415d1 100644 --- a/js/src/i18n/_default/dapps.js +++ b/js/src/i18n/_default/dapps.js @@ -40,7 +40,6 @@ export default { }, label: `Decentralized Applications`, permissions: { - description: `{activeIcon} account is available to application, {defaultIcon} account is the default account`, label: `visible dapp accounts` } }; diff --git a/js/src/i18n/_default/deployContract.js b/js/src/i18n/_default/deployContract.js index fe7c9a69e..0b5a05503 100644 --- a/js/src/i18n/_default/deployContract.js +++ b/js/src/i18n/_default/deployContract.js @@ -37,6 +37,13 @@ export default { hint: `the owner account for this contract`, label: `from account (contract owner)` }, + advanced: { + label: `advanced sending options` + }, + amount: { + hint: `the amount to transfer to the contract`, + label: `amount to transfer (in {tag})` + }, code: { hint: `the compiled code of the contract to deploy`, label: `code` @@ -65,6 +72,7 @@ export default { }, state: { completed: `The contract deployment has been completed`, + confirmationNeeded: `The operation needs confirmations from the other owners of the contract`, preparing: `Preparing transaction for network transmission`, validatingCode: `Validating the deployed contract code`, waitReceipt: `Waiting for the contract deployment transaction receipt`, @@ -74,6 +82,7 @@ export default { completed: `completed`, deployment: `deployment`, details: `contract details`, + extras: `extra information`, failed: `deployment failed`, parameters: `contract parameters`, rejected: `rejected` diff --git a/js/src/i18n/_default/faucet.js b/js/src/i18n/_default/faucet.js new file mode 100644 index 000000000..11aacef9c --- /dev/null +++ b/js/src/i18n/_default/faucet.js @@ -0,0 +1,28 @@ +// 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 . + +export default { + buttons: { + close: `close`, + done: `close`, + request: `request` + }, + summary: { + done: `Your Kovan ETH has been requested from the faucet which responded with -`, + info: `To request a deposit of Kovan ETH to this address, you need to ensure that the address is sms-verified on the mainnet. Once executed the faucet will deposit Kovan ETH into the current account.` + }, + title: `Kovan ETH Faucet` +}; diff --git a/js/src/i18n/_default/firstRun.js b/js/src/i18n/_default/firstRun.js index 8439d0479..5a3e04a17 100644 --- a/js/src/i18n/_default/firstRun.js +++ b/js/src/i18n/_default/firstRun.js @@ -22,11 +22,28 @@ export default { print: `Print Phrase`, skip: `Skip` }, + completed: { + congrats: `Congratulations! Your node setup has been completed successfully and you are ready to use the application.`, + next: `Next you will receive a walk-through of the available functions and the general application interface to get you up and running in record time.` + }, title: { completed: `completed`, newAccount: `new account`, recovery: `recovery`, terms: `terms`, welcome: `welcome` + }, + tnc: { + accept: `I accept these terms and conditions` + }, + welcome: { + description: `As part of a new installation, the next few steps will guide you through the process of setting up you Parity instance and your associated accounts. Our aim is to make it as simple as possible and to get you up and running in record-time, so please bear with us. Once completed you will have -`, + greeting: `Welcome to Parity, the fastest and simplest way to run your node.`, + next: `Click Next to continue your journey.`, + step: { + account: `Created your first Parity account`, + privacy: `Understood our privacy policy & terms of operation`, + recovery: `Have the ability to recover your account` + } } }; diff --git a/js/src/i18n/_default/index.js b/js/src/i18n/_default/index.js index 7e8bac8ef..687e558dc 100644 --- a/js/src/i18n/_default/index.js +++ b/js/src/i18n/_default/index.js @@ -18,10 +18,13 @@ export account from './account'; export accounts from './accounts'; export addAddress from './addAddress'; export addContract from './addContract'; +export address from './address'; export addressSelect from './addressSelect'; +export addresses from './addresses'; export application from './application'; export connection from './connection'; export contract from './contract'; +export contracts from './contracts'; export createAccount from './createAccount'; export createWallet from './createWallet'; export dapp from './dapp'; @@ -32,18 +35,25 @@ export editMeta from './editMeta'; export errors from './errors'; export executeContract from './executeContract'; export extension from './extension'; +export faucet from './faucet'; export firstRun from './firstRun'; export home from './home'; export loadContract from './loadContract'; export parityBar from './parityBar'; export passwordChange from './passwordChange'; +export saveContract from './saveContract'; export settings from './settings'; export shapeshift from './shapeshift'; +export signer from './signer'; +export status from './status'; export tabBar from './tabBar'; export transfer from './transfer'; export txEditor from './txEditor'; export ui from './ui'; export upgradeParity from './upgradeParity'; export vaults from './vaults'; +export verification from './verification'; +export wallet from './wallet'; export walletSettings from './walletSettings'; export web from './web'; +export writeContract from './writeContract'; diff --git a/js/src/i18n/_default/passwordChange.js b/js/src/i18n/_default/passwordChange.js index fc0579250..714a6e25b 100644 --- a/js/src/i18n/_default/passwordChange.js +++ b/js/src/i18n/_default/passwordChange.js @@ -30,6 +30,7 @@ export default { label: `new password` }, passwordHint: { + display: `Hint {hint}`, hint: `hint for the new password`, label: `(optional) new password hint` }, diff --git a/js/src/i18n/_default/saveContract.js b/js/src/i18n/_default/saveContract.js new file mode 100644 index 000000000..62ce1e33f --- /dev/null +++ b/js/src/i18n/_default/saveContract.js @@ -0,0 +1,27 @@ +// 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 . + +export default { + buttons: { + cancel: `Cancel`, + save: `Save` + }, + name: { + hint: `choose a name for this contract`, + label: `contract name` + }, + title: `save contract` +}; diff --git a/js/src/i18n/_default/settings.js b/js/src/i18n/_default/settings.js index 5a656ca6a..3e475e087 100644 --- a/js/src/i18n/_default/settings.js +++ b/js/src/i18n/_default/settings.js @@ -22,20 +22,32 @@ export default { label: `background` }, parity: { + chains: { + chain_classic: `Parity syncs to the Ethereum Classic network`, + chain_dev: `Parity uses a local development chain`, + chain_expanse: `Parity syncs to the Expanse network`, + chain_foundation: `Parity syncs to the Ethereum network launched by the Ethereum Foundation`, + chain_kovan: `Parity syncs to the Kovan test network`, + chain_olympic: `Parity syncs to the Olympic test network`, + chain_ropsten: `Parity syncs to the Ropsten test network`, + cmorden_kovan: `Parity syncs to Morden (Classic) test network`, + hint: `the chain for the Parity node to sync to`, + label: `chain/network to sync` + }, languages: { hint: `the language this interface is displayed with`, label: `UI language` }, loglevels: `Choose the different logs level.`, modes: { - hint: `the syning mode for the Parity node`, + hint: `the syncing mode for the Parity node`, label: `mode of operation`, mode_active: `Parity continuously syncs the chain`, mode_dark: `Parity syncs only when the RPC is active`, mode_offline: `Parity doesn't sync`, mode_passive: `Parity syncs initially, then sleeps and wakes regularly to resync` }, - overview_0: `Control the Parity node settings and mode of operation via this interface.`, + overview_0: `Control the Parity node settings and nature of syncing via this interface.`, label: `parity` }, proxy: { @@ -49,25 +61,25 @@ export default { }, views: { accounts: { - description: `A list of all the accounts associated to and imported into this Parity instance. Send transactions, receive incoming values, manage your balances and fund your accounts.`, + description: `A list of all the accounts associated with and imported into this Parity instance. Send transactions, receive incoming values, manage your balances and fund your accounts.`, label: `Accounts` }, addresses: { - description: `A list of all contacts and address book entries that is managed by this Parity instance. Watch accounts and have the details available at the click of a button when transacting.`, + description: `A list of all contacts and address book entries managed by this Parity instance. Watch accounts and have the details available at the click of a button when transacting.`, label: `Addressbook` }, apps: { - description: `Distributed applications that interact with the underlying network. Add applications, manage you application portfolio and interact with applications from around the network.`, + description: `Distributed applications that interact with the underlying network. Add applications, manage you application portfolio and interact with application from around the network.`, label: `Applications` }, contracts: { description: `Watch and interact with specific contracts that have been deployed on the network. This is a more technically-focused environment, specifically for advanced users that understand the inner working of certain contracts.`, label: `Contracts` }, - overview_0: `Manage the available application views, using only the parts of the application applicable to you.`, + overview_0: `Manage the available application views using only the parts of the application applicable to you.`, overview_1: `Are you an end-user? The defaults are setup for both beginner and advanced users alike.`, overview_2: `Are you a developer? Add some features to manage contracts and interact with application deployments.`, - overview_3: `Are you a miner or do you run a large-scale node? Add the features to give you all the information needed to watch the node operation.`, + overview_3: `Are you a miner or run a large-scale node? Add the features to give you all the information needed to watch the node operation.`, settings: { description: `This view. Allows you to customize the application in term of options, operation and look and feel.`, label: `Settings` diff --git a/js/src/i18n/_default/signer.js b/js/src/i18n/_default/signer.js new file mode 100644 index 000000000..3f8615c52 --- /dev/null +++ b/js/src/i18n/_default/signer.js @@ -0,0 +1,103 @@ +// 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 . + +export default { + embedded: { + noPending: `There are currently no pending requests awaiting your confirmation` + }, + mainDetails: { + editTx: `Edit conditions/gas/gasPrice`, + tooltips: { + total1: `The value of the transaction including the mining fee is {total} {type}.`, + total2: `(This includes a mining fee of {fee} {token})`, + value1: `The value of the transaction.` + } + }, + requestOrigin: { + dapp: `by a dapp at {url}`, + ipc: `via IPC session`, + rpc: `via RPC {rpc}`, + signerCurrent: `via current tab`, + signerUI: `via UI session`, + unknownInterface: `via unknown interface`, + unknownRpc: `unidentified`, + unknownUrl: `unknown URL` + }, + requestsPage: { + noPending: `There are no requests requiring your confirmation.`, + pendingTitle: `Pending Requests`, + queueTitle: `Local Transactions` + }, + sending: { + hardware: { + confirm: `Please confirm the transaction on your attached hardware device`, + connect: `Please attach your hardware device before confirming the transaction` + } + }, + signRequest: { + request: `A request to sign data using your account:`, + state: { + confirmed: `Confirmed`, + rejected: `Rejected` + }, + unknownBinary: `(Unknown binary data)`, + warning: `WARNING: This consequences of doing this may be grave. Confirm the request only if you are sure.` + }, + title: `Trusted Signer`, + txPending: { + buttons: { + viewToggle: `view transaction` + } + }, + txPendingConfirm: { + buttons: { + confirmBusy: `Confirming...`, + confirmRequest: `Confirm Request` + }, + errors: { + invalidWallet: `Given wallet file is invalid.` + }, + password: { + decrypt: { + hint: `decrypt the key`, + label: `Key Password` + }, + unlock: { + hint: `unlock the account`, + label: `Account Password` + } + }, + passwordHint: `(hint) {passwordHint}`, + selectKey: { + hint: `The keyfile to use for this account`, + label: `Select Local Key` + }, + tooltips: { + password: `Please provide a password for this account` + } + }, + txPendingForm: { + changedMind: `I've changed my mind`, + reject: `reject request` + }, + txPendingReject: { + buttons: { + reject: `Reject Request` + }, + info: `Are you sure you want to reject request?`, + undone: `This cannot be undone` + } +}; diff --git a/js/src/i18n/_default/status.js b/js/src/i18n/_default/status.js new file mode 100644 index 000000000..536c0ff90 --- /dev/null +++ b/js/src/i18n/_default/status.js @@ -0,0 +1,66 @@ +// 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 . + +export default { + debug: { + reverse: `Reverse Order`, + stopped: `Refresh and display of logs from Parity is currently stopped via the UI, start it to see the latest updates.`, + title: `Node Logs` + }, + miningSettings: { + input: { + author: { + hint: `the mining author`, + label: `author` + }, + extradata: { + hint: `extra data for mined blocks`, + label: `extradata` + }, + gasFloor: { + hint: `the gas floor target for mining`, + label: `gas floor target` + }, + gasPrice: { + hint: `the minimum gas price for mining`, + label: `minimal gas price` + } + }, + title: `mining settings` + }, + status: { + hashrate: `{hashrate} H/s`, + input: { + chain: `chain`, + enode: `enode`, + no: `no`, + peers: `peers`, + port: `network port`, + rpcEnabled: `rpc enabled`, + rpcInterface: `rpc interface`, + rpcPort: `rpc port`, + yes: `yes` + }, + title: { + bestBlock: `best block`, + hashRate: `hash rate`, + network: `network settings`, + node: `Node`, + peers: `peers` + } + }, + title: `Status` +}; diff --git a/js/src/i18n/_default/transfer.js b/js/src/i18n/_default/transfer.js index 742e816f1..3207f5b06 100644 --- a/js/src/i18n/_default/transfer.js +++ b/js/src/i18n/_default/transfer.js @@ -21,6 +21,40 @@ export default { label: `transaction data` } }, + buttons: { + back: `Back`, + cancel: `Cancel`, + close: `Close`, + next: `Next`, + send: `Send` + }, + details: { + advanced: { + label: `advanced sending options` + }, + amount: { + hint: `the amount to transfer to the recipient`, + label: `amount to transfer (in {tag})` + }, + fullBalance: { + label: `full account balance` + }, + recipient: { + hint: `the recipient address`, + label: `recipient address` + }, + sender: { + hint: `the sender address`, + label: `sender address` + }, + total: { + label: `total transaction amount` + } + }, + wallet: { + confirmation: `This transaction needs confirmation from other owners.`, + operationHash: `operation hash` + }, warning: { wallet_spent_limit: `This transaction value is above the remaining daily limit. It will need to be confirmed by other owners.` } diff --git a/js/src/i18n/_default/ui.js b/js/src/i18n/_default/ui.js index 242f14b9b..d84e7bd13 100644 --- a/js/src/i18n/_default/ui.js +++ b/js/src/i18n/_default/ui.js @@ -15,8 +15,40 @@ // along with Parity. If not, see . export default { + actionbar: { + export: { + button: { + export: `export` + } + }, + import: { + button: { + cancel: `Cancel`, + confirm: `Confirm`, + import: `import` + }, + confirm: `Confirm that this is what was intended to import.`, + error: `An error occured: {errorText}`, + step: { + error: `error`, + select: `select a file`, + validate: `validate` + }, + title: `Import from a file` + }, + search: { + hint: `Enter search input...` + }, + sort: { + sortBy: `Sort by {label}`, + typeDefault: `Default`, + typeEth: `Sort by ETH`, + typeName: `Sort by name`, + typeTags: `Sort by tags` + } + }, balance: { - none: `There are no balances associated with this account` + none: `No balances associated with this account` }, blockStatus: { bestBlock: `{blockNumber} best block`, @@ -28,10 +60,61 @@ export default { no: `no`, yes: `yes` }, + copyToClipboard: { + copied: `copied {data} to clipboard` + }, + errors: { + close: `close` + }, + fileSelect: { + defaultLabel: `Drop a file here, or click to select a file to upload` + }, + gasPriceSelector: { + customTooltip: { + transactions: `{number} {number, plural, one {transaction} other {transactions}} with gas price set from {minPrice} to {maxPrice}` + } + }, identityName: { null: `NULL`, unnamed: `UNNAMED` }, + methodDecoding: { + condition: { + block: `, {historic, select, true {Submitted} false {Submission}} at block {blockNumber}`, + time: `, {historic, select, true {Submitted} false {Submission}} at {timestamp}` + }, + deploy: { + address: `Deployed a contract at address`, + params: `with the following parameters:`, + willDeploy: `Will deploy a contract`, + withValue: `, sending {value}` + }, + gasUsed: `({gas} gas used)`, + gasValues: `{gas} gas ({gasPrice}M/{tag})`, + input: { + data: `data`, + input: `input`, + withInput: `with the {inputDesc} {inputValue}` + }, + receive: { + contract: `the contract`, + info: `{historic, select, true {Received} false {Will receive}} {valueEth} from {aContract}{address}` + }, + signature: { + info: `{historic, select, true {Executed} false {Will execute}} the {method} function on the contract {address} trsansferring {ethValue}{inputLength, plural, zero {,} other {passing the following {inputLength, plural, one {parameter} other {parameters}}}}` + }, + token: { + transfer: `{historic, select, true {Transferred} false {Will transfer}} {value} to {address}` + }, + transfer: { + contract: `the contract`, + info: `{historic, select, true {Transferred} false {Will transfer}} {valueEth} to {aContract}{address}` + }, + txValues: `{historic, select, true {Provided} false {Provides}} {gasProvided}{gasUsed} for a total transaction value of {totalEthValue}`, + unknown: { + info: `{historic, select, true {Executed} false {Will execute}} the {method} on the contract {address} transferring {ethValue}.` + } + }, passwordStrength: { label: `password strength` }, @@ -48,6 +131,10 @@ export default { posted: `The transaction has been posted to the network with a hash of {hashLink}`, waiting: `waiting for confirmations` }, + vaultSelect: { + hint: `the vault this account is attached to`, + label: `associated vault` + }, verification: { gatherData: { accountHasRequested: { @@ -60,10 +147,6 @@ export default { pending: `Checking if your account is verified…`, true: `Your account is already verified.` }, - email: { - hint: `the code will be sent to this address`, - label: `e-mail address` - }, fee: `The additional fee is {amount} ETH.`, isAbleToRequest: { pending: `Validating your input…` @@ -74,10 +157,6 @@ export default { true: `The verification server is running.` }, nofee: `There is no additional fee.`, - phoneNumber: { - hint: `the SMS will be sent to this number`, - label: `phone number in international format` - }, termsOfService: `I agree to the terms and conditions below.` } } diff --git a/js/src/i18n/_default/upgradeParity.js b/js/src/i18n/_default/upgradeParity.js index cca634b88..b3d871452 100644 --- a/js/src/i18n/_default/upgradeParity.js +++ b/js/src/i18n/_default/upgradeParity.js @@ -15,13 +15,13 @@ // along with Parity. If not, see . export default { - busy: `Your upgrade to Parity {newversion} is currently in progress`, + busy: `Your upgrade to Parity {newversion} is currently in progress. Please wait until the process completes.`, button: { close: `close`, done: `done`, upgrade: `upgrade now` }, - completed: `Your upgrade to Parity {newversion} has been successfully completed.`, + completed: `Your upgrade to Parity {newversion} has been successfully completed. Click "done" to automatically reload the application.`, consensus: { capable: `Your current Parity version is capable of handling the network requirements.`, capableUntil: `Your current Parity version is capable of handling the network requirements until block {blockNumber}`, @@ -30,7 +30,10 @@ export default { }, failed: `Your upgrade to Parity {newversion} has failed with an error.`, info: { - upgrade: `A new version of Parity, version {newversion} is available as an upgrade from your current version {currentversion}` + currentVersion: `You are currently running {currentversion}`, + next: `Proceed with "upgrade now" to start your Parity upgrade.`, + upgrade: `An upgrade to version {newversion} is available`, + welcome: `Welcome to the Parity upgrade wizard, allowing you a completely seamless upgrade experience to the next version of Parity.` }, step: { completed: `upgrade completed`, diff --git a/js/src/i18n/_default/vaults.js b/js/src/i18n/_default/vaults.js index 3024beed3..04ff4d558 100644 --- a/js/src/i18n/_default/vaults.js +++ b/js/src/i18n/_default/vaults.js @@ -26,8 +26,9 @@ export default { button: { accounts: `accounts`, add: `create vault`, - close: `close vault`, - open: `open vault` + close: `close`, + edit: `edit`, + open: `open` }, confirmClose: { info: `You are about to close a vault. Any accounts associated with the vault won't be visible after this operation concludes. To view the associated accounts, open the vault again.`, @@ -70,6 +71,38 @@ export default { }, title: `Create a new vault` }, + editMeta: { + allowPassword: `Change vault password`, + button: { + close: `close`, + save: `save` + }, + currentPassword: { + hint: `your current vault password`, + label: `current password` + }, + description: { + hint: `the description for this vault`, + label: `vault description` + }, + password: { + hint: `a strong, unique password`, + label: `new password` + }, + password2: { + hint: `verify your new password`, + label: `new password (repeat)` + }, + passwordHint: { + hint: `your password hint for this vault`, + label: `password hint` + }, + title: `Edit Vault Metadata` + }, empty: `There are currently no vaults to display.`, + selector: { + noneAvailable: `There are currently no vaults opened and available for selection. Create and open some first before attempting to select a vault for an account move.`, + title: `Select Account Vault` + }, title: `Vault Management` }; diff --git a/js/src/i18n/_default/verification.js b/js/src/i18n/_default/verification.js new file mode 100644 index 000000000..2d98ed82a --- /dev/null +++ b/js/src/i18n/_default/verification.js @@ -0,0 +1,85 @@ +// 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 . + +export default { + button: { + cancel: `Cancel`, + done: `Done`, + next: `Next` + }, + code: { + error: `invalid code`, + hint: `Enter the code you received.`, + label: `verification code`, + sent: `The verification code has been sent to {receiver}.` + }, + confirmation: { + authorise: `The verification code will be sent to the contract. Please authorize this using the Parity Signer.`, + windowOpen: `Please keep this window open.` + }, + done: { + message: `Congratulations, your account is verified!` + }, + email: { + enterCode: `Enter the code you received via e-mail.` + }, + gatherData: { + email: { + hint: `the code will be sent to this address`, + label: `e-mail address` + }, + phoneNumber: { + hint: `the SMS will be sent to this number`, + label: `phone number in international format` + } + }, + gatherDate: { + email: { + error: `invalid e-mail` + }, + phoneNumber: { + error: `invalid number` + } + }, + loading: `Loading verification data.`, + request: { + authorise: `A verification request will be sent to the contract. Please authorize this using the Parity Signer.`, + requesting: `Requesting a code from the Parity server and waiting for the puzzle to be put into the contract.`, + windowOpen: `Please keep this window open.` + }, + sms: { + enterCode: `Enter the code you received via SMS.` + }, + steps: { + code: `Enter Code`, + completed: `Completed`, + confirm: `Confirm`, + data: `Enter Data`, + method: `Method`, + request: `Request` + }, + title: `verify your account`, + types: { + email: { + description: `The hash of the e-mail address you prove control over will be stored on the blockchain.`, + label: `E-mail Verification` + }, + sms: { + description: `It will be stored on the blockchain that you control a phone number (not which).`, + label: `SMS Verification` + } + } +}; diff --git a/js/src/i18n/_default/wallet.js b/js/src/i18n/_default/wallet.js new file mode 100644 index 000000000..92063af8b --- /dev/null +++ b/js/src/i18n/_default/wallet.js @@ -0,0 +1,45 @@ +// 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 . + +export default { + buttons: { + edit: `edit`, + forget: `forget`, + settings: `settings`, + transfer: `transfer` + }, + confirmations: { + buttons: { + confirmAs: `Confirm As...`, + revokeAs: `Revoke As...` + }, + none: `No transactions needs confirmation right now.`, + tooltip: { + confirmed: `Confirmed by {number}/{required} owners` + } + }, + details: { + requiredOwners: `This wallet requires at least {owners} to validate any action (transactions, modifications).`, + requiredOwnersNumber: `{number} {numberValue, plural, one {owner} other {owners}}`, + spent: `{spent} has been spent today, out of {limit} set as the daily limit, which has been reset on {date}`, + title: `Details` + }, + title: `Wallet Management`, + transactions: { + none: `No transactions has been sent.`, + title: `Transactions` + } +}; diff --git a/js/src/i18n/_default/walletSettings.js b/js/src/i18n/_default/walletSettings.js index 57dc1a169..ddeae3798 100644 --- a/js/src/i18n/_default/walletSettings.js +++ b/js/src/i18n/_default/walletSettings.js @@ -15,6 +15,16 @@ // along with Parity. If not, see . export default { + addOwner: { + title: `Add Owner` + }, + buttons: { + cancel: `Cancel`, + close: `Close`, + next: `Next`, + send: `Send`, + sending: `Sending...` + }, changes: { modificationString: `For your modifications to be taken into account, other owners have to send the same modifications. They can paste @@ -48,11 +58,12 @@ export default { label: `from account (wallet owner)` } }, - rejected: { - busyStep: { - state: `The wallet settings will not be modified. You can safely close this window.`, - title: `The modifications have been rejected` - }, - title: `rejected` + ownersChange: { + details: `from {from} to {to}`, + title: `Change Required Owners` + }, + rejected: `The transaction #{txid} has been rejected`, + removeOwner: { + title: `Remove Owner` } }; diff --git a/js/src/i18n/_default/writeContract.js b/js/src/i18n/_default/writeContract.js new file mode 100644 index 000000000..fc1100b77 --- /dev/null +++ b/js/src/i18n/_default/writeContract.js @@ -0,0 +1,62 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +export default { + buttons: { + autoCompile: `Auto-Compile`, + compile: `Compile`, + deploy: `Deploy`, + import: `Import Solidity`, + load: `Load`, + new: `New`, + optimise: `Optimise`, + save: `Save` + }, + compiling: { + action: `Please compile the source code.`, + busy: `Compiling...` + }, + details: { + saved: `(saved {timestamp})` + }, + error: { + noContract: `No contract has been found.`, + params: `An error occurred with the following description` + }, + input: { + abi: `ABI Interface`, + code: `Bytecode`, + metadata: `Metadata`, + swarm: `Swarm Metadata Hash` + }, + title: { + contract: `Select a contract`, + loading: `Loading...`, + main: `Write a Contract`, + messages: `Compiler messages`, + new: `New Solidity Contract`, + parameters: `Parameters`, + saved: `saved @ {timestamp}`, + selectSolidity: `Select a Solidity version`, + solidity: `Loading Solidity {version}` + }, + type: { + humanErc20: `Implementation of the Human Token Contract`, + implementErc20: `Implementation of ERC20 Token Contract`, + multisig: `Implementation of a multisig Wallet`, + standardErc20: `Standard ERC20 Token Contract` + } +}; diff --git a/js/src/modals/PasswordManager/passwordManager.js b/js/src/modals/PasswordManager/passwordManager.js index 6436bc4cc..d40ac7cde 100644 --- a/js/src/modals/PasswordManager/passwordManager.js +++ b/js/src/modals/PasswordManager/passwordManager.js @@ -122,7 +122,7 @@ class PasswordManager extends Component { Date: Mon, 27 Mar 2017 11:43:09 +0200 Subject: [PATCH 36/41] JS package bumps (#5287) * Update to React 15.4.2 * Update deprecated UglifyJS version --- js/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/package.json b/js/package.json index b65ef6661..7462bb4c4 100644 --- a/js/package.json +++ b/js/package.json @@ -184,11 +184,11 @@ "push.js": "0.0.11", "qrcode-npm": "0.0.3", "qs": "6.3.0", - "react": "15.4.1", + "react": "15.4.2", "react-ace": "4.1.0", - "react-addons-css-transition-group": "15.4.1", + "react-addons-css-transition-group": "15.4.2", "react-copy-to-clipboard": "4.2.3", - "react-dom": "15.4.1", + "react-dom": "15.4.2", "react-dropzone": "3.7.3", "react-element-to-jsx-string": "6.0.0", "react-event-listener": "0.4.1", @@ -211,7 +211,7 @@ "sw-toolbox": "^3.6.0", "u2f-api": "0.0.9", "u2f-api-polyfill": "0.4.3", - "uglify-js": "2.8.2", + "uglify-js": "2.8.16", "useragent.js": "0.5.6", "utf8": "2.1.2", "valid-url": "1.0.9", From 9e02271a683c8fe1d6001cfa974988bf41e3f9e2 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Mon, 27 Mar 2017 10:08:02 +0000 Subject: [PATCH 37/41] [ci skip] js-precompiled 20170327-100522 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index badb522c4..c9d6a7b49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1750,7 +1750,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#8d7826d49c785726b17f14f150cab37e204fc84f" +source = "git+https://github.com/ethcore/js-precompiled.git#9760f897356d6c2a2ec7c43e60c7c1d0f87545dd" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 7462bb4c4..e47130639 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.31", + "version": "1.7.32", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 7707f7557af4672c0bcbd0fe960d82b167cfa6b3 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Mon, 27 Mar 2017 10:23:02 +0000 Subject: [PATCH 38/41] [ci skip] js-precompiled 20170327-102018 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9d6a7b49..8edf235c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1750,7 +1750,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#9760f897356d6c2a2ec7c43e60c7c1d0f87545dd" +source = "git+https://github.com/ethcore/js-precompiled.git#92cf06c1e8c3b79014a9d6adcb1f5c17f0a88030" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index e47130639..c0a6df879 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.32", + "version": "1.7.33", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 986762a0bca014e4539915b264b26cf6db2709c2 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 27 Mar 2017 16:19:30 +0200 Subject: [PATCH 39/41] Scaffolding for zh translations, including first-round by @btceth (#5289) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial import of #5023 * Language naming * Update 简体中文 --- js/src/i18n/constants.js | 2 +- js/src/i18n/languages.js | 3 +- js/src/i18n/store.js | 7 +- js/src/i18n/zh/createAccount.js | 156 ++++++++++++++++++++++++++++++++ js/src/i18n/zh/index.js | 23 +++++ js/src/i18n/zh/settings.js | 67 ++++++++++++++ 6 files changed, 254 insertions(+), 4 deletions(-) create mode 100644 js/src/i18n/zh/createAccount.js create mode 100644 js/src/i18n/zh/index.js create mode 100644 js/src/i18n/zh/settings.js diff --git a/js/src/i18n/constants.js b/js/src/i18n/constants.js index c7d47a0fd..6b4d7dafd 100644 --- a/js/src/i18n/constants.js +++ b/js/src/i18n/constants.js @@ -17,7 +17,7 @@ const DEFAULT_LOCALE = 'en'; const DEFAULT_LOCALES = process.env.NODE_ENV === 'production' ? ['en'] - : ['en', 'de', 'nl']; + : ['en', 'de', 'nl', 'zh']; const LS_STORE_KEY = '_parity::locale'; export { diff --git a/js/src/i18n/languages.js b/js/src/i18n/languages.js index 76bd136ab..b178fe4c2 100644 --- a/js/src/i18n/languages.js +++ b/js/src/i18n/languages.js @@ -17,5 +17,6 @@ export default { de: 'Deutsch', en: 'English', - nl: 'Nederlands' + nl: 'Nederlands', + zh: '简体中文' }; diff --git a/js/src/i18n/store.js b/js/src/i18n/store.js index 43e580dd8..e9666c8c0 100644 --- a/js/src/i18n/store.js +++ b/js/src/i18n/store.js @@ -20,6 +20,7 @@ import { addLocaleData } from 'react-intl'; import de from 'react-intl/locale-data/de'; import en from 'react-intl/locale-data/en'; import nl from 'react-intl/locale-data/nl'; +import zh from 'react-intl/locale-data/zh'; import store from 'store'; import { DEFAULT_LOCALE, DEFAULT_LOCALES, LS_STORE_KEY } from './constants'; @@ -27,6 +28,7 @@ import languages from './languages'; import deMessages from './de'; import enMessages from './en'; import nlMessages from './nl'; +import zhMessages from './zh'; let instance = null; @@ -34,10 +36,11 @@ const LANGUAGES = flatten({ languages }); const MESSAGES = { de: Object.assign(flatten(deMessages), LANGUAGES), en: Object.assign(flatten(enMessages), LANGUAGES), - nl: Object.assign(flatten(nlMessages), LANGUAGES) + nl: Object.assign(flatten(nlMessages), LANGUAGES), + zh: Object.assign(flatten(zhMessages), LANGUAGES) }; -addLocaleData([...de, ...en, ...nl]); +addLocaleData([...de, ...en, ...nl, ...zh]); export default class Store { @observable locale = DEFAULT_LOCALE; diff --git a/js/src/i18n/zh/createAccount.js b/js/src/i18n/zh/createAccount.js new file mode 100644 index 000000000..bfe3ed2bc --- /dev/null +++ b/js/src/i18n/zh/createAccount.js @@ -0,0 +1,156 @@ +// 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 . + +export default { + accountDetails: { + address: { + hint: `账户地址`, + label: `地址` + }, + name: { + hint: `描述账户的名字`, + label: `账户名` + }, + phrase: { + hint: `账户恢复词`, + label: `账户恢复词(安全保存,别人拥有它就可以完全控制你的账户)` + } + }, + accountDetailsGeth: { + imported: `你已经从Geth keystore导入了{number}个地址` + }, + button: { + back: `返回`, + cancel: `取消`, + close: `关闭`, + create: `创建`, + import: `导入`, + next: `下一步`, + print: `打印恢复词` + }, + creationType: { + fromGeth: { + label: `从Geth keystore导入账户` + }, + fromJSON: { + label: `从JSON文件导入账户` + }, + fromNew: { + label: `手动创建新账户` + }, + fromPhrase: { + label: `通过恢复词恢复账户` + }, + fromPresale: { + label: `从以太坊预售钱包导入账户` + }, + fromRaw: { + label: `导入私钥` + } + }, + newAccount: { + hint: { + hint: `(可选)帮助你记住密码的提示`, + label: `密码提示` + }, + name: { + hint: `描述账户的名字`, + label: `账户名` + }, + password: { + hint: `足够强的密码`, + label: `密码` + }, + password2: { + hint: `确认你的密码`, + label: `再次输入密码` + } + }, + newGeth: { + noKeys: `现在Geth keystore中没有可导入的私钥` + }, + newImport: { + file: { + hint: `要导入的钱包文件`, + label: `钱包文件` + }, + hint: { + hint: `(可选)帮助你记住密码的提示`, + label: `密码提示` + }, + name: { + hint: `描述账户的名字`, + label: `账户名` + }, + password: { + hint: `输入密码,解锁钱包`, + label: `密码` + } + }, + rawKey: { + hint: { + hint: `(可选)帮助你记住密码的提示`, + label: `密码提示` + }, + name: { + hint: `描述账户的名字`, + label: `账户名` + }, + password: { + hint: `足够强的密码`, + label: `密码` + }, + password2: { + hint: `确认密码`, + label: `再次输入密码` + }, + private: { + hint: `原始的十六进制编码私钥`, + label: `私钥` + } + }, + recoveryPhrase: { + hint: { + hint: `(可选)帮助你记住密码的提示`, + label: `密码提示` + }, + name: { + hint: `描述账户的名字`, + label: `账户名` + }, + password: { + hint: `足够强的密码`, + label: `密码` + }, + password2: { + hint: `确认密码`, + label: `再次输入密码` + }, + phrase: { + hint: `账户恢复词`, + label: `账户恢复词` + }, + windowsKey: { + label: `在Windows系统上由Parity 1.4.5以前的版本创建的私钥` + } + }, + title: { + accountInfo: `账户信息`, + createAccount: `创建账户`, + createType: `创建类型`, + importWallet: `导入钱包` + } +}; diff --git a/js/src/i18n/zh/index.js b/js/src/i18n/zh/index.js new file mode 100644 index 000000000..107c8491a --- /dev/null +++ b/js/src/i18n/zh/index.js @@ -0,0 +1,23 @@ +// 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 . + +import createAccount from './createAccount'; +import settings from './settings'; + +export default { + createAccount, + settings +}; diff --git a/js/src/i18n/zh/settings.js b/js/src/i18n/zh/settings.js new file mode 100644 index 000000000..48a656b93 --- /dev/null +++ b/js/src/i18n/zh/settings.js @@ -0,0 +1,67 @@ +// 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 . + +export default { + label: 'settings', + + background: { + label: 'background' + }, + + parity: { + label: 'parity' + }, + + proxy: { + label: 'proxy' + }, + + views: { + label: 'views', + + accounts: { + label: 'Accounts' + }, + + addresses: { + label: 'Addressbook' + }, + + apps: { + label: 'Applications' + }, + + contracts: { + label: 'Contracts' + }, + + home: { + label: 'Home' + }, + + status: { + label: 'Status' + }, + + signer: { + label: 'Signer' + }, + + settings: { + label: 'Settings' + } + } +}; From a4c5375a639b1f5ce32d62549d207d0cfe47c32d Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 27 Mar 2017 16:27:25 +0200 Subject: [PATCH 40/41] Add lint:i18n to find missing & extra keys (#5290) * Add npm run lint:i18n (missing keys check) * Check extraneous keys --- js/package.json | 1 + js/scripts/lint-i18n.js | 53 +++++++++++++++++++++++++++++++++++++++++ js/src/i18n/store.js | 5 ++++ 3 files changed, 59 insertions(+) create mode 100644 js/scripts/lint-i18n.js diff --git a/js/package.json b/js/package.json index c0a6df879..799948443 100644 --- a/js/package.json +++ b/js/package.json @@ -53,6 +53,7 @@ "lint:cached": "npm run lint:css && npm run lint:js:cached", "lint:css": "stylelint ./src/**/*.css", "lint:fix": "npm run lint:js:fix", + "lint:i18n": "babel-node ./scripts/lint-i18n.js", "lint:js": "eslint --ignore-path .gitignore ./src/", "lint:js:cached": "eslint --cache --ignore-path .gitignore ./src/", "lint:js:fix": "eslint --fix --ignore-path .gitignore ./src/", diff --git a/js/scripts/lint-i18n.js b/js/scripts/lint-i18n.js new file mode 100644 index 000000000..d392353d0 --- /dev/null +++ b/js/scripts/lint-i18n.js @@ -0,0 +1,53 @@ +// 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 . + +import flatten from 'flat'; + +import * as defaults from '../src/i18n/_default'; +import { LANGUAGES, MESSAGES } from '../src/i18n/store'; + +const SKIP_LANG = ['en']; +const defaultKeys = Object.keys(flatten(Object.assign({}, defaults, LANGUAGES))); + +Object + .keys(MESSAGES) + .filter((lang) => !SKIP_LANG.includes(lang)) + .forEach((lang) => { + const messageKeys = Object.keys(MESSAGES[lang]); + let extra = 0; + let found = 0; + let missing = 0; + + console.log(`*** Checking translations for ${lang}`); + + defaultKeys.forEach((key) => { + if (messageKeys.includes(key)) { + found++; + } else { + missing++; + console.log(` Missing ${key}`); + } + }); + + messageKeys.forEach((key) => { + if (!defaultKeys.includes(key)) { + extra++; + console.log(` Extra ${key}`); + } + }); + + console.log(`Found ${found} keys, missing ${missing} keys, ${extra} extraneous keys\n`); + }); diff --git a/js/src/i18n/store.js b/js/src/i18n/store.js index e9666c8c0..fdceb6beb 100644 --- a/js/src/i18n/store.js +++ b/js/src/i18n/store.js @@ -73,3 +73,8 @@ export default class Store { return instance; } } + +export { + LANGUAGES, + MESSAGES +}; From 90746bd8c9dd629b2e338b1694fef720f5e265ca Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Mon, 27 Mar 2017 14:43:26 +0000 Subject: [PATCH 41/41] [ci skip] js-precompiled 20170327-144027 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8edf235c5..23b2ac440 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1750,7 +1750,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#92cf06c1e8c3b79014a9d6adcb1f5c17f0a88030" +source = "git+https://github.com/ethcore/js-precompiled.git#6f18d6f6ddfff55e93f67e11f445eb98bc860219" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 799948443..550f22075 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.7.33", + "version": "1.7.34", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ",