diff --git a/Cargo.lock b/Cargo.lock index eaa563541..c43b51c1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1866,6 +1866,7 @@ version = "1.8.0" 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)", + "clap 2.24.2 (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/paritytech/rust-ctrlc.git)", "daemonize 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2148,7 +2149,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/paritytech/js-precompiled.git#f34bdb63272fd59edf2062dda44113831964d800" +source = "git+https://github.com/paritytech/js-precompiled.git#75e4afa0b77396aa8feefb49276672c3fe885a88" dependencies = [ "parity-dapps-glue 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index 18bad4cc9..5f3a6ad73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ log = "0.3" env_logger = "0.4" rustc-hex = "1.0" docopt = "0.8" +clap = "2" time = "0.1" num_cpus = "1.2" number_prefix = "0.2" diff --git a/ethcore/light/src/client/fetch.rs b/ethcore/light/src/client/fetch.rs deleted file mode 100644 index 93a2cde11..000000000 --- a/ethcore/light/src/client/fetch.rs +++ /dev/null @@ -1,74 +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 . - -//! Trait for fetching chain data. - -use std::sync::Arc; - -use ethcore::encoded; -use ethcore::engines::{Engine, StateDependentProof}; -use ethcore::header::Header; -use ethcore::receipt::Receipt; -use futures::future::IntoFuture; -use bigint::hash::H256; - -/// Provides full chain data. -pub trait ChainDataFetcher: Send + Sync + 'static { - /// Error type when data unavailable. - type Error: ::std::fmt::Debug; - - /// Future for fetching block body. - type Body: IntoFuture; - /// Future for fetching block receipts. - type Receipts: IntoFuture, Error=Self::Error>; - /// Future for fetching epoch transition - type Transition: IntoFuture, Error=Self::Error>; - - /// Fetch a block body. - fn block_body(&self, header: &Header) -> Self::Body; - - /// Fetch block receipts. - fn block_receipts(&self, header: &Header) -> Self::Receipts; - - /// Fetch epoch transition proof at given header. - fn epoch_transition(&self, hash: H256, engine: Arc, checker: Arc) -> Self::Transition; -} - -/// Fetcher implementation which cannot fetch anything. -pub struct Unavailable; - -/// Create a fetcher which has all data unavailable. -pub fn unavailable() -> Unavailable { Unavailable } - -impl ChainDataFetcher for Unavailable { - type Error = &'static str; - - type Body = Result; - type Receipts = Result, &'static str>; - type Transition = Result, &'static str>; - - fn block_body(&self, _header: &Header) -> Self::Body { - Err("fetching block bodies unavailable") - } - - fn block_receipts(&self, _header: &Header) -> Self::Receipts { - Err("fetching block receipts unavailable") - } - - fn epoch_transition(&self, _h: H256, _e: Arc, _check: Arc) -> Self::Transition { - Err("fetching epoch transition proofs unavailable") - } -} diff --git a/ethcore/light/src/client/header_chain.rs b/ethcore/light/src/client/header_chain.rs index 7320eddb4..3828e6954 100644 --- a/ethcore/light/src/client/header_chain.rs +++ b/ethcore/light/src/client/header_chain.rs @@ -18,12 +18,11 @@ //! //! Unlike a full node's `BlockChain` this doesn't store much in the database. //! It stores candidates for the last 2048-4096 blocks as well as CHT roots for -//! historical blocks all the way to the genesis. If the engine makes use -//! of epoch transitions, those are stored as well. +//! historical blocks all the way to the genesis. //! //! This is separate from the `BlockChain` for two reasons: //! - It stores only headers (and a pruned subset of them) -//! - To allow for flexibility in the database layout.. +//! - To allow for flexibility in the database layout once that's incorporated. use std::collections::BTreeMap; use std::sync::Arc; @@ -31,20 +30,15 @@ use std::sync::Arc; use cht; use ethcore::block_status::BlockStatus; -use ethcore::error::{BlockImportError, BlockError}; +use ethcore::error::BlockError; use ethcore::encoded; use ethcore::header::Header; use ethcore::ids::BlockId; -use ethcore::spec::Spec; -use ethcore::engines::epoch::{ - Transition as EpochTransition, - PendingTransition as PendingEpochTransition -}; use rlp::{Encodable, Decodable, DecoderError, RlpStream, Rlp, UntrustedRlp}; use heapsize::HeapSizeOf; use bigint::prelude::U256; -use bigint::hash::{H256, H256FastMap, H264}; +use bigint::hash::H256; use util::kvdb::{DBTransaction, KeyValueDB}; use cache::Cache; @@ -60,9 +54,6 @@ const HISTORY: u64 = 2048; /// The best block key. Maps to an RLP list: [best_era, last_era] const CURRENT_KEY: &'static [u8] = &*b"best_and_latest"; -/// Key storing the last canonical epoch transition. -const LAST_CANONICAL_TRANSITION: &'static [u8] = &*b"canonical_transition"; - /// Information about a block. #[derive(Debug, Clone)] pub struct BlockDescriptor { @@ -110,6 +101,7 @@ impl Encodable for Entry { impl Decodable for Entry { fn decode(rlp: &UntrustedRlp) -> Result { + let mut candidates = SmallVec::<[Candidate; 3]>::new(); for item in rlp.iter() { @@ -139,42 +131,6 @@ fn era_key(number: u64) -> String { format!("candidates_{}", number) } -fn pending_transition_key(block_hash: H256) -> H264 { - const LEADING: u8 = 1; - - let mut key = H264::default(); - - key[0] = LEADING; - key.0[1..].copy_from_slice(&block_hash.0[..]); - - key -} - -fn transition_key(block_hash: H256) -> H264 { - const LEADING: u8 = 2; - - let mut key = H264::default(); - - key[0] = LEADING; - key.0[1..].copy_from_slice(&block_hash.0[..]); - - key -} - -// encode last canonical transition entry: header and proof. -fn encode_canonical_transition(header: &Header, proof: &[u8]) -> Vec { - let mut stream = RlpStream::new_list(2); - stream.append(header).append(&proof); - stream.out() -} - -// decode last canonical transition entry. -fn decode_canonical_transition(t: &[u8]) -> Result<(Header, &[u8]), DecoderError> { - let rlp = UntrustedRlp::new(t); - - Ok((rlp.val_at(0)?, rlp.at(1)?.data()?)) -} - /// Pending changes from `insert` to be applied after the database write has finished. pub struct PendingChanges { best_block: Option, // new best block. @@ -185,7 +141,6 @@ pub struct HeaderChain { genesis_header: encoded::Header, // special-case the genesis. candidates: RwLock>, best_block: RwLock, - live_epoch_proofs: RwLock>, db: Arc, col: Option, cache: Arc>, @@ -193,16 +148,8 @@ pub struct HeaderChain { impl HeaderChain { /// Create a new header chain given this genesis block and database to read from. - pub fn new( - db: Arc, - col: Option, - spec: &Spec, - cache: Arc>, - ) -> Result { - let mut live_epoch_proofs = ::std::collections::HashMap::default(); - - let genesis = ::rlp::encode(&spec.genesis_header()).into_vec(); - let decoded_header = spec.genesis_header(); + pub fn new(db: Arc, col: Option, genesis: &[u8], cache: Arc>) -> Result { + use ethcore::views::HeaderView; let chain = if let Some(current) = db.get(col, CURRENT_KEY)? { let (best_number, highest_number) = { @@ -213,24 +160,12 @@ impl HeaderChain { let mut cur_number = highest_number; let mut candidates = BTreeMap::new(); - // load all era entries, referenced headers within them, - // and live epoch proofs. + // load all era entries and referenced headers within them. while let Some(entry) = db.get(col, era_key(cur_number).as_bytes())? { let entry: Entry = ::rlp::decode(&entry); trace!(target: "chain", "loaded header chain entry for era {} with {} candidates", cur_number, entry.candidates.len()); - for c in &entry.candidates { - let key = transition_key(c.hash); - - if let Some(proof) = db.get(col, &*key)? { - live_epoch_proofs.insert(c.hash, EpochTransition { - block_hash: c.hash, - block_number: cur_number, - proof: proof.into_vec(), - }); - } - } candidates.insert(cur_number, entry); cur_number -= 1; @@ -252,42 +187,29 @@ impl HeaderChain { }; HeaderChain { - genesis_header: encoded::Header::new(genesis), + genesis_header: encoded::Header::new(genesis.to_owned()), best_block: RwLock::new(best_block), candidates: RwLock::new(candidates), - live_epoch_proofs: RwLock::new(live_epoch_proofs), db: db, col: col, cache: cache, } } else { + let g_view = HeaderView::new(genesis); HeaderChain { - genesis_header: encoded::Header::new(genesis), + genesis_header: encoded::Header::new(genesis.to_owned()), best_block: RwLock::new(BlockDescriptor { - hash: decoded_header.hash(), + hash: g_view.hash(), number: 0, - total_difficulty: *decoded_header.difficulty(), + total_difficulty: g_view.difficulty(), }), candidates: RwLock::new(BTreeMap::new()), - live_epoch_proofs: RwLock::new(live_epoch_proofs), db: db, col: col, cache: cache, } }; - // instantiate genesis epoch data if it doesn't exist. - if let None = chain.db.get(col, LAST_CANONICAL_TRANSITION)? { - let genesis_data = spec.genesis_epoch_data()?; - - { - let mut batch = chain.db.transaction(); - let data = encode_canonical_transition(&decoded_header, &genesis_data); - batch.put_vec(col, LAST_CANONICAL_TRANSITION, data); - chain.db.write(batch)?; - } - } - Ok(chain) } @@ -296,24 +218,10 @@ impl HeaderChain { /// This blindly trusts that the data given to it is sensible. /// Returns a set of pending changes to be applied with `apply_pending` /// before the next call to insert and after the transaction has been written. - /// - /// If the block is an epoch transition, provide the transition along with - /// the header. - pub fn insert( - &self, - transaction: &mut DBTransaction, - header: Header, - transition_proof: Option>, - ) -> Result { + pub fn insert(&self, transaction: &mut DBTransaction, header: Header) -> Result { let hash = header.hash(); let number = header.number(); let parent_hash = *header.parent_hash(); - let transition = transition_proof.map(|proof| EpochTransition { - block_hash: hash, - block_number: number, - proof: proof, - }); - let mut pending = PendingChanges { best_block: None, }; @@ -329,8 +237,7 @@ impl HeaderChain { candidates.get(&(number - 1)) .and_then(|entry| entry.candidates.iter().find(|c| c.hash == parent_hash)) .map(|c| c.total_difficulty) - .ok_or_else(|| BlockError::UnknownParent(parent_hash)) - .map_err(BlockImportError::Block)? + .ok_or_else(|| BlockError::UnknownParent(parent_hash))? }; let total_difficulty = parent_td + *header.difficulty(); @@ -355,13 +262,8 @@ impl HeaderChain { transaction.put(self.col, era_key(number).as_bytes(), &::rlp::encode(&*cur_era)) } - if let Some(transition) = transition { - transaction.put(self.col, &*transition_key(hash), &transition.proof); - self.live_epoch_proofs.write().insert(hash, transition); - } - - let raw = header.encoded().into_inner(); - transaction.put_vec(self.col, &hash[..], raw); + let raw = ::rlp::encode(&header); + transaction.put(self.col, &hash[..], &*raw); let (best_num, is_new_best) = { let cur_best = self.best_block.read(); @@ -414,10 +316,8 @@ impl HeaderChain { let cht_num = cht::block_to_cht_number(earliest_era) .expect("fails only for number == 0; genesis never imported; qed"); - let mut last_canonical_transition = None; let cht_root = { let mut i = earliest_era; - let mut live_epoch_proofs = self.live_epoch_proofs.write(); // iterable function which removes the candidates as it goes // along. this will only be called until the CHT is complete. @@ -428,25 +328,7 @@ impl HeaderChain { i += 1; - // prune old blocks and epoch proofs. for ancient in &era_entry.candidates { - let maybe_transition = live_epoch_proofs.remove(&ancient.hash); - if let Some(epoch_transition) = maybe_transition { - transaction.delete(self.col, &*transition_key(ancient.hash)); - - if ancient.hash == era_entry.canonical_hash { - last_canonical_transition = match self.db.get(self.col, &ancient.hash) { - Err(e) => { - warn!(target: "chain", "Error reading from DB: {}\n - ", e); - None - } - Ok(None) => panic!("stored candidates always have corresponding headers; qed"), - Ok(Some(header)) => Some((epoch_transition, ::rlp::decode(&header))), - }; - } - } - transaction.delete(self.col, &ancient.hash); } @@ -460,12 +342,6 @@ impl HeaderChain { // write the CHT root to the database. debug!(target: "chain", "Produced CHT {} root: {:?}", cht_num, cht_root); transaction.put(self.col, cht_key(cht_num).as_bytes(), &::rlp::encode(&cht_root)); - - // update the last canonical transition proof - if let Some((epoch_transition, header)) = last_canonical_transition { - let x = encode_canonical_transition(&header, &epoch_transition.proof); - transaction.put_vec(self.col, LAST_CANONICAL_TRANSITION, x); - } } } @@ -491,7 +367,7 @@ impl HeaderChain { /// will be returned. pub fn block_hash(&self, id: BlockId) -> Option { match id { - BlockId::Earliest | BlockId::Number(0) => Some(self.genesis_hash()), + BlockId::Earliest => Some(self.genesis_hash()), BlockId::Hash(hash) => Some(hash), BlockId::Number(num) => { if self.best_block.read().number < num { return None } @@ -642,56 +518,6 @@ impl HeaderChain { false => BlockStatus::Unknown, } } - - /// Insert a pending transition. - pub fn insert_pending_transition(&self, batch: &mut DBTransaction, hash: H256, t: PendingEpochTransition) { - let key = pending_transition_key(hash); - batch.put(self.col, &*key, &*::rlp::encode(&t)); - } - - /// Get pending transition for a specific block hash. - pub fn pending_transition(&self, hash: H256) -> Option { - let key = pending_transition_key(hash); - match self.db.get(self.col, &*key) { - Ok(val) => val.map(|x| ::rlp::decode(&x)), - Err(e) => { - warn!(target: "chain", "Error reading from database: {}", e); - None - } - } - } - - /// Get the transition to the epoch the given parent hash is part of - /// or transitions to. - /// This will give the epoch that any children of this parent belong to. - /// - /// The header corresponding the the parent hash must be stored already. - pub fn epoch_transition_for(&self, parent_hash: H256) -> Option<(Header, Vec)> { - // slow path: loop back block by block - let live_proofs = self.live_epoch_proofs.read(); - - for hdr in self.ancestry_iter(BlockId::Hash(parent_hash)) { - if let Some(transition) = live_proofs.get(&hdr.hash()).cloned() { - return Some((hdr.decode(), transition.proof)) - } - } - - // any blocks left must be descendants of the last canonical transition block. - match self.db.get(self.col, LAST_CANONICAL_TRANSITION) { - Ok(x) => { - let x = x.expect("last canonical transition always instantiated; qed"); - - let (hdr, proof) = decode_canonical_transition(&x) - .expect("last canonical transition always encoded correctly; qed"); - - Some((hdr, proof.to_vec())) - } - Err(e) => { - warn!("Error reading from DB: {}", e); - None - } - } - } } impl HeapSizeOf for HeaderChain { @@ -744,7 +570,7 @@ mod tests { let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6)))); - let chain = HeaderChain::new(db.clone(), None, &spec, cache).unwrap(); + let chain = HeaderChain::new(db.clone(), None, &::rlp::encode(&genesis_header), cache).unwrap(); let mut parent_hash = genesis_header.hash(); let mut rolling_timestamp = genesis_header.timestamp(); @@ -757,7 +583,7 @@ mod tests { parent_hash = header.hash(); let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, header, None).unwrap(); + let pending = chain.insert(&mut tx, header).unwrap(); db.write(tx).unwrap(); chain.apply_pending(pending); @@ -777,7 +603,7 @@ mod tests { let db = make_db(); let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6)))); - let chain = HeaderChain::new(db.clone(), None, &spec, cache).unwrap(); + let chain = HeaderChain::new(db.clone(), None, &::rlp::encode(&genesis_header), cache).unwrap(); let mut parent_hash = genesis_header.hash(); let mut rolling_timestamp = genesis_header.timestamp(); @@ -790,7 +616,7 @@ mod tests { parent_hash = header.hash(); let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, header, None).unwrap(); + let pending = chain.insert(&mut tx, header).unwrap(); db.write(tx).unwrap(); chain.apply_pending(pending); @@ -809,7 +635,7 @@ mod tests { parent_hash = header.hash(); let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, header, None).unwrap(); + let pending = chain.insert(&mut tx, header).unwrap(); db.write(tx).unwrap(); chain.apply_pending(pending); @@ -833,7 +659,7 @@ mod tests { parent_hash = header.hash(); let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, header, None).unwrap(); + let pending = chain.insert(&mut tx, header).unwrap(); db.write(tx).unwrap(); chain.apply_pending(pending); @@ -856,10 +682,12 @@ mod tests { #[test] fn earliest_is_latest() { let spec = Spec::new_test(); + let genesis_header = spec.genesis_header(); let db = make_db(); let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6)))); - let chain = HeaderChain::new(db.clone(), None, &spec, cache).unwrap(); + let chain = HeaderChain::new(db.clone(), None, &::rlp::encode(&genesis_header), cache).unwrap(); + assert!(chain.block_header(BlockId::Earliest).is_some()); assert!(chain.block_header(BlockId::Latest).is_some()); @@ -874,7 +702,7 @@ mod tests { let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6)))); { - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone()).unwrap(); + let chain = HeaderChain::new(db.clone(), None, &::rlp::encode(&genesis_header), cache.clone()).unwrap(); let mut parent_hash = genesis_header.hash(); let mut rolling_timestamp = genesis_header.timestamp(); for i in 1..10000 { @@ -886,7 +714,7 @@ mod tests { parent_hash = header.hash(); let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, header, None).unwrap(); + let pending = chain.insert(&mut tx, header).unwrap(); db.write(tx).unwrap(); chain.apply_pending(pending); @@ -894,7 +722,7 @@ mod tests { } } - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone()).unwrap(); + let chain = HeaderChain::new(db.clone(), None, &::rlp::encode(&genesis_header), cache.clone()).unwrap(); assert!(chain.block_header(BlockId::Number(10)).is_none()); assert!(chain.block_header(BlockId::Number(9000)).is_some()); assert!(chain.cht_root(2).is_some()); @@ -910,7 +738,7 @@ mod tests { let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6)))); { - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone()).unwrap(); + let chain = HeaderChain::new(db.clone(), None, &::rlp::encode(&genesis_header), cache.clone()).unwrap(); let mut parent_hash = genesis_header.hash(); let mut rolling_timestamp = genesis_header.timestamp(); @@ -924,7 +752,7 @@ mod tests { parent_hash = header.hash(); let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, header, None).unwrap(); + let pending = chain.insert(&mut tx, header).unwrap(); db.write(tx).unwrap(); chain.apply_pending(pending); @@ -941,7 +769,7 @@ mod tests { parent_hash = header.hash(); let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, header, None).unwrap(); + let pending = chain.insert(&mut tx, header).unwrap(); db.write(tx).unwrap(); chain.apply_pending(pending); @@ -952,7 +780,7 @@ mod tests { } // after restoration, non-canonical eras should still be loaded. - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone()).unwrap(); + let chain = HeaderChain::new(db.clone(), None, &::rlp::encode(&genesis_header), cache.clone()).unwrap(); assert_eq!(chain.block_header(BlockId::Latest).unwrap().number(), 10); assert!(chain.candidates.read().get(&100).is_some()) } @@ -964,76 +792,10 @@ mod tests { let db = make_db(); let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6)))); - let chain = HeaderChain::new(db.clone(), None, &spec, cache.clone()).unwrap(); + let chain = HeaderChain::new(db.clone(), None, &::rlp::encode(&genesis_header), cache.clone()).unwrap(); assert!(chain.block_header(BlockId::Earliest).is_some()); assert!(chain.block_header(BlockId::Number(0)).is_some()); assert!(chain.block_header(BlockId::Hash(genesis_header.hash())).is_some()); } - - #[test] - fn epoch_transitions_available_after_cht() { - let spec = Spec::new_test(); - let genesis_header = spec.genesis_header(); - let db = make_db(); - let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6)))); - - let chain = HeaderChain::new(db.clone(), None, &spec, cache).unwrap(); - - let mut parent_hash = genesis_header.hash(); - let mut rolling_timestamp = genesis_header.timestamp(); - for i in 1..6 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * i.into()); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let epoch_proof = if i == 3 { - Some(vec![1, 2, 3, 4]) - } else { - None - }; - - let pending = chain.insert(&mut tx, header, epoch_proof).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - - // these 3 should end up falling back to the genesis epoch proof in DB - for i in 0..3 { - let hash = chain.block_hash(BlockId::Number(i)).unwrap(); - assert_eq!(chain.epoch_transition_for(hash).unwrap().1, Vec::::new()); - } - - // these are live. - for i in 3..6 { - let hash = chain.block_hash(BlockId::Number(i)).unwrap(); - assert_eq!(chain.epoch_transition_for(hash).unwrap().1, vec![1, 2, 3, 4]); - } - - for i in 6..10000 { - let mut header = Header::new(); - header.set_parent_hash(parent_hash); - header.set_number(i); - header.set_timestamp(rolling_timestamp); - header.set_difficulty(*genesis_header.difficulty() * i.into()); - parent_hash = header.hash(); - - let mut tx = db.transaction(); - let pending = chain.insert(&mut tx, header, None).unwrap(); - db.write(tx).unwrap(); - chain.apply_pending(pending); - - rolling_timestamp += 10; - } - - // no live blocks have associated epoch proofs -- make sure we aren't leaking memory. - assert!(chain.live_epoch_proofs.read().is_empty()); - assert_eq!(chain.epoch_transition_for(parent_hash).unwrap().1, vec![1, 2, 3, 4]); - } } diff --git a/ethcore/light/src/client/mod.rs b/ethcore/light/src/client/mod.rs index 2b77685bd..2067a23c2 100644 --- a/ethcore/light/src/client/mod.rs +++ b/ethcore/light/src/client/mod.rs @@ -19,11 +19,11 @@ use std::sync::{Weak, Arc}; use ethcore::block_status::BlockStatus; -use ethcore::client::{TransactionImportResult, ClientReport, EnvInfo}; -use ethcore::engines::{epoch, Engine, EpochChange, EpochTransition, Proof, Unsure}; -use ethcore::error::{TransactionError, BlockImportError, Error as EthcoreError}; +use ethcore::client::{ClientReport, EnvInfo}; +use ethcore::engines::Engine; +use ethcore::error::BlockImportError; use ethcore::ids::BlockId; -use ethcore::header::{BlockNumber, Header}; +use ethcore::header::Header; use ethcore::verification::queue::{self, HeaderQueue}; use ethcore::blockchain_info::BlockChainInfo; use ethcore::spec::Spec; @@ -33,12 +33,9 @@ use io::IoChannel; use parking_lot::{Mutex, RwLock}; use bigint::prelude::U256; use bigint::hash::H256; -use futures::{IntoFuture, Future}; -use util::Address; use util::kvdb::{KeyValueDB, CompactionProfile}; -use self::fetch::ChainDataFetcher; use self::header_chain::{AncestryIter, HeaderChain}; use cache::Cache; @@ -48,8 +45,6 @@ pub use self::service::Service; mod header_chain; mod service; -pub mod fetch; - /// Configuration for the light client. #[derive(Debug, Clone)] pub struct Config { @@ -85,9 +80,6 @@ impl Default for Config { /// Trait for interacting with the header chain abstractly. pub trait LightChainClient: Send + Sync { - /// Adds a new `LightChainNotify` listener. - fn add_listener(&self, listener: Weak); - /// Get chain info. fn chain_info(&self) -> BlockChainInfo; @@ -136,7 +128,7 @@ pub trait LightChainClient: Send + Sync { fn cht_root(&self, i: usize) -> Option; /// Get the EIP-86 transition block number. - fn eip86_transition(&self) -> BlockNumber; + fn eip86_transition(&self) -> u64; /// Get a report of import activity since the last call. fn report(&self) -> ClientReport; @@ -164,7 +156,7 @@ impl AsLightClient for T { } /// Light client implementation. -pub struct Client { +pub struct Client { queue: HeaderQueue, engine: Arc, chain: HeaderChain, @@ -172,30 +164,22 @@ pub struct Client { import_lock: Mutex<()>, db: Arc, listeners: RwLock>>, - fetcher: T, verify_full: bool, } -impl Client { +impl Client { /// Create a new `Client`. - pub fn new( - config: Config, - db: Arc, - chain_col: Option, - spec: &Spec, - fetcher: T, - io_channel: IoChannel, - cache: Arc> - ) -> Result { + pub fn new(config: Config, db: Arc, chain_col: Option, spec: &Spec, io_channel: IoChannel, cache: Arc>) -> Result { + let gh = ::rlp::encode(&spec.genesis_header()); + Ok(Client { queue: HeaderQueue::new(config.queue, spec.engine.clone(), io_channel, config.check_seal), engine: spec.engine.clone(), - chain: HeaderChain::new(db.clone(), chain_col, &spec, cache)?, + chain: HeaderChain::new(db.clone(), chain_col, &gh, cache)?, report: RwLock::new(ClientReport::default()), import_lock: Mutex::new(()), db: db, listeners: RwLock::new(vec![]), - fetcher: fetcher, verify_full: config.verify_full, }) } @@ -207,24 +191,10 @@ impl Client { /// Create a new `Client` backed purely in-memory. /// This will ignore all database options in the configuration. - pub fn in_memory( - config: Config, - spec: &Spec, - fetcher: T, - io_channel: IoChannel, - cache: Arc> - ) -> Self { + pub fn in_memory(config: Config, spec: &Spec, io_channel: IoChannel, cache: Arc>) -> Self { let db = ::util::kvdb::in_memory(0); - Client::new( - config, - Arc::new(db), - None, - spec, - fetcher, - io_channel, - cache - ).expect("New DB creation infallible; qed") + Client::new(config, Arc::new(db), None, spec, io_channel, cache).expect("New DB creation infallible; qed") } /// Import a header to the queue for additional verification. @@ -323,33 +293,19 @@ impl Client { continue } - let write_proof_result = match self.check_epoch_signal(&verified_header) { - Ok(Some(proof)) => self.write_pending_proof(&verified_header, proof), - Ok(None) => Ok(()), - Err(e) => - panic!("Unable to fetch epoch transition proof: {:?}", e), - }; - - if let Err(e) = write_proof_result { - warn!(target: "client", "Error writing pending transition proof to DB: {:?} \ - The node may not be able to synchronize further.", e); - } - - let epoch_proof = self.engine.is_epoch_end( - &verified_header, - &|h| self.chain.block_header(BlockId::Hash(h)).map(|hdr| hdr.decode()), - &|h| self.chain.pending_transition(h), - ); + // TODO: `epoch_end_signal`, `is_epoch_end`. + // proofs we get from the network would be _complete_, whereas we need + // _incomplete_ signals let mut tx = self.db.transaction(); - let pending = match self.chain.insert(&mut tx, verified_header, epoch_proof) { + let pending = match self.chain.insert(&mut tx, verified_header) { Ok(pending) => { good.push(hash); self.report.write().blocks_imported += 1; pending } Err(e) => { - debug!(target: "client", "Error importing header {:?}: {:?}", (num, hash), e); + debug!(target: "client", "Error importing header {:?}: {}", (num, hash), e); bad.push(hash); continue; } @@ -465,76 +421,9 @@ impl Client { true } - - fn check_epoch_signal(&self, verified_header: &Header) -> Result, T::Error> { - let (mut block, mut receipts) = (None, None); - - // First, check without providing auxiliary data. - match self.engine.signals_epoch_end(verified_header, None, None) { - EpochChange::No => return Ok(None), - EpochChange::Yes(proof) => return Ok(Some(proof)), - EpochChange::Unsure(unsure) => { - let (b, r) = match unsure { - Unsure::NeedsBody => - (Some(self.fetcher.block_body(verified_header)), None), - Unsure::NeedsReceipts => - (None, Some(self.fetcher.block_receipts(verified_header))), - Unsure::NeedsBoth => ( - Some(self.fetcher.block_body(verified_header)), - Some(self.fetcher.block_receipts(verified_header)), - ), - }; - - if let Some(b) = b { - block = Some(b.into_future().wait()?.into_inner()); - } - - if let Some(r) = r { - receipts = Some(r.into_future().wait()?); - } - } - } - - let block = block.as_ref().map(|x| &x[..]); - let receipts = receipts.as_ref().map(|x| &x[..]); - - // Check again now that required data has been fetched. - match self.engine.signals_epoch_end(verified_header, block, receipts) { - EpochChange::No => return Ok(None), - EpochChange::Yes(proof) => return Ok(Some(proof)), - EpochChange::Unsure(_) => - panic!("Detected faulty engine implementation: requests additional \ - data to check epoch end signal when everything necessary provided"), - } - } - - // attempts to fetch the epoch proof from the network until successful. - fn write_pending_proof(&self, header: &Header, proof: Proof) -> Result<(), T::Error> { - let proof = match proof { - Proof::Known(known) => known, - Proof::WithState(state_dependent) => { - self.fetcher.epoch_transition( - header.hash(), - self.engine.clone(), - state_dependent - ).into_future().wait()? - } - }; - - let mut batch = self.db.transaction(); - self.chain.insert_pending_transition(&mut batch, header.hash(), epoch::PendingTransition { - proof: proof, - }); - self.db.write_buffered(batch); - Ok(()) - } } -impl LightChainClient for Client { - fn add_listener(&self, listener: Weak) { - Client::add_listener(self, listener) - } - +impl LightChainClient for Client { fn chain_info(&self) -> BlockChainInfo { Client::chain_info(self) } fn queue_header(&self, header: Header) -> Result { @@ -593,7 +482,7 @@ impl LightChainClient for Client { Client::cht_root(self, i) } - fn eip86_transition(&self) -> BlockNumber { + fn eip86_transition(&self) -> u64 { self.engine().params().eip86_transition } @@ -601,38 +490,3 @@ impl LightChainClient for Client { Client::report(self) } } - -impl ::ethcore::client::EngineClient for Client { - fn update_sealing(&self) { } - fn submit_seal(&self, _block_hash: H256, _seal: Vec>) { } - fn broadcast_consensus_message(&self, _message: Vec) { } - - fn epoch_transition_for(&self, parent_hash: H256) -> Option { - self.chain.epoch_transition_for(parent_hash).map(|(hdr, proof)| EpochTransition { - block_hash: hdr.hash(), - block_number: hdr.number(), - proof: proof, - }) - } - - fn chain_info(&self) -> BlockChainInfo { - Client::chain_info(self) - } - - fn call_contract(&self, _id: BlockId, _address: Address, _data: Vec) -> Result, String> { - Err("Contract calling not supported by light client".into()) - } - - fn transact_contract(&self, _address: Address, _data: Vec) - -> Result - { - // TODO: these are only really used for misbehavior reporting. - // no relevant clients will be running light clients, but maybe - // they could be at some point? - Err(TransactionError::LimitReached.into()) - } - - fn block_number(&self, id: BlockId) -> Option { - self.block_header(id).map(|hdr| hdr.number()) - } -} diff --git a/ethcore/light/src/client/service.rs b/ethcore/light/src/client/service.rs index 20aea69ce..99dccc999 100644 --- a/ethcore/light/src/client/service.rs +++ b/ethcore/light/src/client/service.rs @@ -30,7 +30,7 @@ use util::kvdb::{Database, DatabaseConfig}; use cache::Cache; use parking_lot::Mutex; -use super::{ChainDataFetcher, Client, Config as ClientConfig}; +use super::{Client, Config as ClientConfig}; /// Errors on service initialization. #[derive(Debug)] @@ -51,14 +51,14 @@ impl fmt::Display for Error { } /// Light client service. -pub struct Service { - client: Arc>, +pub struct Service { + client: Arc, io_service: IoService, } -impl Service { +impl Service { /// Start the service: initialize I/O workers and client itself. - pub fn start(config: ClientConfig, spec: &Spec, fetcher: T, path: &Path, cache: Arc>) -> Result { + pub fn start(config: ClientConfig, spec: &Spec, path: &Path, cache: Arc>) -> Result { // initialize database. let mut db_config = DatabaseConfig::with_columns(db::NUM_COLUMNS); @@ -81,14 +81,10 @@ impl Service { db, db::COL_LIGHT_CHAIN, spec, - fetcher, io_service.channel(), cache, ).map_err(Error::Database)?); - io_service.register_handler(Arc::new(ImportBlocks(client.clone()))).map_err(Error::Io)?; - spec.engine.register_client(Arc::downgrade(&client) as _); - Ok(Service { client: client, io_service: io_service, @@ -101,14 +97,14 @@ impl Service { } /// Get a handle to the client. - pub fn client(&self) -> &Arc> { + pub fn client(&self) -> &Arc { &self.client } } -struct ImportBlocks(Arc>); +struct ImportBlocks(Arc); -impl IoHandler for ImportBlocks { +impl IoHandler for ImportBlocks { fn message(&self, _io: &IoContext, message: &ClientIoMessage) { if let ClientIoMessage::BlockVerified = *message { self.0.import_verified(); @@ -124,7 +120,6 @@ mod tests { use std::sync::Arc; use cache::Cache; - use client::fetch; use time::Duration; use parking_lot::Mutex; @@ -134,6 +129,6 @@ mod tests { let temp_path = RandomTempPath::new(); let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6)))); - Service::start(Default::default(), &spec, fetch::unavailable(), temp_path.as_path(), cache).unwrap(); + Service::start(Default::default(), &spec, temp_path.as_path(), cache).unwrap(); } } diff --git a/ethcore/light/src/net/load_timer.rs b/ethcore/light/src/net/load_timer.rs index 8df8fdf17..7b78fc693 100644 --- a/ethcore/light/src/net/load_timer.rs +++ b/ethcore/light/src/net/load_timer.rs @@ -62,7 +62,6 @@ fn hardcoded_serve_time(kind: Kind) -> u64 { Kind::Storage => 2_000_000, Kind::Code => 1_500_000, Kind::Execution => 250, // per gas. - Kind::Signal => 500_000, } } diff --git a/ethcore/light/src/net/mod.rs b/ethcore/light/src/net/mod.rs index 968b98281..cccb32458 100644 --- a/ethcore/light/src/net/mod.rs +++ b/ethcore/light/src/net/mod.rs @@ -104,8 +104,9 @@ mod packet { // relay transactions to peers. pub const SEND_TRANSACTIONS: u8 = 0x06; - // two packets were previously meant to be reserved for epoch proofs. - // these have since been moved to requests. + // request and respond with epoch transition proof + pub const REQUEST_EPOCH_PROOF: u8 = 0x07; + pub const EPOCH_PROOF: u8 = 0x08; } // timeouts for different kinds of requests. all values are in milliseconds. @@ -123,7 +124,6 @@ mod timeout { pub const CONTRACT_CODE: i64 = 100; pub const HEADER_PROOF: i64 = 100; pub const TRANSACTION_PROOF: i64 = 1000; // per gas? - pub const EPOCH_SIGNAL: i64 = 200; } /// A request id. @@ -584,6 +584,12 @@ impl LightProtocol { packet::SEND_TRANSACTIONS => self.relay_transactions(peer, io, rlp), + packet::REQUEST_EPOCH_PROOF | packet::EPOCH_PROOF => { + // ignore these for now, but leave them specified. + debug!(target: "pip", "Ignoring request/response for epoch proof"); + Ok(()) + } + other => { Err(Error::UnrecognizedPacket(other)) } @@ -946,7 +952,6 @@ impl LightProtocol { 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), - CompleteRequest::Signal(req) => self.provider.epoch_signal(req).map(Response::Signal), } }); diff --git a/ethcore/light/src/net/request_credits.rs b/ethcore/light/src/net/request_credits.rs index 8c2e89eec..39eb33106 100644 --- a/ethcore/light/src/net/request_credits.rs +++ b/ethcore/light/src/net/request_credits.rs @@ -91,7 +91,6 @@ pub struct CostTable { code: U256, header_proof: U256, transaction_proof: U256, // cost per gas. - epoch_signal: U256, } impl Default for CostTable { @@ -108,7 +107,6 @@ impl Default for CostTable { code: 20000.into(), header_proof: 15000.into(), transaction_proof: 2.into(), - epoch_signal: 10000.into(), } } } @@ -123,7 +121,7 @@ impl Encodable for CostTable { s.append(cost); } - s.begin_list(11).append(&self.base); + s.begin_list(10).append(&self.base); append_cost(s, &self.headers, request::Kind::Headers); append_cost(s, &self.transaction_index, request::Kind::TransactionIndex); append_cost(s, &self.body, request::Kind::Body); @@ -133,7 +131,6 @@ impl Encodable for CostTable { 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); - append_cost(s, &self.epoch_signal, request::Kind::Signal); } } @@ -150,7 +147,6 @@ impl Decodable for CostTable { let mut code = None; let mut header_proof = None; let mut transaction_proof = None; - let mut epoch_signal = None; for cost_list in rlp.iter().skip(1) { let cost = cost_list.val_at(1)?; @@ -164,7 +160,6 @@ impl Decodable for CostTable { request::Kind::Code => code = Some(cost), request::Kind::HeaderProof => header_proof = Some(cost), request::Kind::Execution => transaction_proof = Some(cost), - request::Kind::Signal => epoch_signal = Some(cost), } } @@ -181,7 +176,6 @@ impl Decodable for CostTable { code: unwrap_cost(code)?, header_proof: unwrap_cost(header_proof)?, transaction_proof: unwrap_cost(transaction_proof)?, - epoch_signal: unwrap_cost(epoch_signal)?, }) } } @@ -244,7 +238,6 @@ impl FlowParams { code: cost_for_kind(Kind::Code), header_proof: cost_for_kind(Kind::HeaderProof), transaction_proof: cost_for_kind(Kind::Execution), - epoch_signal: cost_for_kind(Kind::Signal), }; FlowParams { @@ -270,8 +263,7 @@ impl FlowParams { storage: free_cost.clone(), code: free_cost.clone(), header_proof: free_cost.clone(), - transaction_proof: free_cost.clone(), - epoch_signal: free_cost, + transaction_proof: free_cost, } } } @@ -301,7 +293,6 @@ impl FlowParams { Request::Storage(_) => self.costs.storage, Request::Code(_) => self.costs.code, Request::Execution(ref req) => self.costs.transaction_proof * req.gas, - Request::Signal(_) => self.costs.epoch_signal, } } diff --git a/ethcore/light/src/net/request_set.rs b/ethcore/light/src/net/request_set.rs index 7ec668884..e83c33bff 100644 --- a/ethcore/light/src/net/request_set.rs +++ b/ethcore/light/src/net/request_set.rs @@ -139,7 +139,6 @@ fn compute_timeout(reqs: &Requests) -> Duration { Request::Storage(_) => timeout::PROOF, Request::Code(_) => timeout::CONTRACT_CODE, Request::Execution(_) => timeout::TRANSACTION_PROOF, - Request::Signal(_) => timeout::EPOCH_SIGNAL, } })) } diff --git a/ethcore/light/src/net/tests/mod.rs b/ethcore/light/src/net/tests/mod.rs index 539a60ffb..8e928dd22 100644 --- a/ethcore/light/src/net/tests/mod.rs +++ b/ethcore/light/src/net/tests/mod.rs @@ -158,12 +158,6 @@ impl Provider for TestProvider { None } - fn epoch_signal(&self, _req: request::CompleteSignalRequest) -> Option { - Some(request::SignalResponse { - signal: vec![1, 2, 3, 4], - }) - } - fn ready_transactions(&self) -> Vec { self.0.client.ready_transactions() } @@ -529,50 +523,6 @@ fn get_contract_code() { proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); } -#[test] -fn epoch_signal() { - let capabilities = capabilities(); - - let (provider, proto) = setup(capabilities.clone()); - let flow_params = proto.flow_params.read().clone(); - - let cur_status = status(provider.client.chain_info()); - - { - let packet_body = write_handshake(&cur_status, &capabilities, &proto); - proto.on_connect(&1, &Expect::Send(1, packet::STATUS, packet_body.clone())); - proto.handle_packet(&Expect::Nothing, &1, packet::STATUS, &packet_body); - } - - let req_id = 112; - let request = Request::Signal(request::IncompleteSignalRequest { - block_hash: H256([1; 32]).into(), - }); - - let requests = encode_single(request.clone()); - let request_body = make_packet(req_id, &requests); - - let response = { - let response = vec![Response::Signal(SignalResponse { - signal: vec![1, 2, 3, 4], - })]; - - let limit = *flow_params.limit(); - let cost = flow_params.compute_cost_multi(requests.requests()); - - println!("limit = {}, cost = {}", limit, cost); - let new_creds = limit - cost; - - let mut response_stream = RlpStream::new_list(3); - response_stream.append(&req_id).append(&new_creds).append_list(&response); - - response_stream.out() - }; - - let expected = Expect::Respond(packet::RESPONSE, response); - proto.handle_packet(&expected, &1, packet::REQUEST, &request_body); -} - #[test] fn proof_of_execution() { let capabilities = capabilities(); diff --git a/ethcore/light/src/on_demand/mod.rs b/ethcore/light/src/on_demand/mod.rs index 6a9ecb4d1..d67b7dc4e 100644 --- a/ethcore/light/src/on_demand/mod.rs +++ b/ethcore/light/src/on_demand/mod.rs @@ -195,8 +195,6 @@ fn guess_capabilities(requests: &[CheckedRequest]) -> Capabilities { caps.serve_headers = true, CheckedRequest::HeaderByHash(_, _) => caps.serve_headers = true, - CheckedRequest::Signal(_, _) => - caps.serve_headers = true, CheckedRequest::Body(ref req, _) => if let Ok(ref hdr) = req.0.as_ref() { update_since(&mut caps.serve_chain_since, hdr.number()); }, diff --git a/ethcore/light/src/on_demand/request.rs b/ethcore/light/src/on_demand/request.rs index d9afd5582..9f03955da 100644 --- a/ethcore/light/src/on_demand/request.rs +++ b/ethcore/light/src/on_demand/request.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use ethcore::basic_account::BasicAccount; use ethcore::encoded; -use ethcore::engines::{Engine, StateDependentProof}; +use ethcore::engines::Engine; use ethcore::receipt::Receipt; use ethcore::state::{self, ProvedExecution}; use ethcore::transaction::SignedTransaction; @@ -56,8 +56,6 @@ pub enum Request { Code(Code), /// A request for proof of execution. Execution(TransactionProof), - /// A request for epoch change signal. - Signal(Signal), } /// A request argument. @@ -138,7 +136,6 @@ impl_single!(Body, Body, encoded::Block); impl_single!(Account, Account, Option); impl_single!(Code, Code, Bytes); impl_single!(Execution, TransactionProof, super::ExecutionResult); -impl_single!(Signal, Signal, Vec); macro_rules! impl_args { () => { @@ -247,7 +244,6 @@ pub enum CheckedRequest { Account(Account, net_request::IncompleteAccountRequest), Code(Code, net_request::IncompleteCodeRequest), Execution(TransactionProof, net_request::IncompleteExecutionRequest), - Signal(Signal, net_request::IncompleteSignalRequest) } impl From for CheckedRequest { @@ -306,12 +302,6 @@ impl From for CheckedRequest { }; CheckedRequest::Execution(req, net_req) } - Request::Signal(req) => { - let net_req = net_request::IncompleteSignalRequest { - block_hash: req.hash.into(), - }; - CheckedRequest::Signal(req, net_req) - } } } } @@ -329,7 +319,6 @@ impl CheckedRequest { CheckedRequest::Account(_, req) => NetRequest::Account(req), CheckedRequest::Code(_, req) => NetRequest::Code(req), CheckedRequest::Execution(_, req) => NetRequest::Execution(req), - CheckedRequest::Signal(_, req) => NetRequest::Signal(req), } } @@ -457,7 +446,6 @@ macro_rules! match_me { CheckedRequest::Account($check, $req) => $e, CheckedRequest::Code($check, $req) => $e, CheckedRequest::Execution($check, $req) => $e, - CheckedRequest::Signal($check, $req) => $e, } } } @@ -485,7 +473,6 @@ impl IncompleteRequest for CheckedRequest { CheckedRequest::Account(_, ref req) => req.check_outputs(f), CheckedRequest::Code(_, ref req) => req.check_outputs(f), CheckedRequest::Execution(_, ref req) => req.check_outputs(f), - CheckedRequest::Signal(_, ref req) => req.check_outputs(f), } } @@ -506,7 +493,6 @@ impl IncompleteRequest for CheckedRequest { CheckedRequest::Account(_, req) => req.complete().map(CompleteRequest::Account), CheckedRequest::Code(_, req) => req.complete().map(CompleteRequest::Code), CheckedRequest::Execution(_, req) => req.complete().map(CompleteRequest::Execution), - CheckedRequest::Signal(_, req) => req.complete().map(CompleteRequest::Signal), } } @@ -558,9 +544,6 @@ impl net_request::CheckedRequest for CheckedRequest { CheckedRequest::Execution(ref prover, _) => expect!((&NetResponse::Execution(ref res), _) => prover.check_response(cache, &res.items).map(Response::Execution)), - CheckedRequest::Signal(ref prover, _) => - expect!((&NetResponse::Signal(ref res), _) => - prover.check_response(cache, &res.signal).map(Response::Signal)), } } } @@ -584,8 +567,6 @@ pub enum Response { Code(Vec), /// Response to a request for proved execution. Execution(super::ExecutionResult), - /// Response to a request for epoch change signal. - Signal(Vec), } impl net_request::ResponseLike for Response { @@ -869,27 +850,6 @@ impl TransactionProof { } } -/// Request for epoch signal. -/// Provide engine and state-dependent proof checker. -#[derive(Clone)] -pub struct Signal { - /// Block hash and number to fetch proof for. - pub hash: H256, - /// Consensus engine, used to check the proof. - pub engine: Arc, - /// Special checker for the proof. - pub proof_check: Arc, -} - -impl Signal { - /// Check the signal, returning the signal or indicate that it's bad. - pub fn check_response(&self, _: &Mutex<::cache::Cache>, signal: &[u8]) -> Result, Error> { - self.proof_check.check_proof(&*self.engine, signal) - .map(|_| signal.to_owned()) - .map_err(|_| Error::BadProof) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/ethcore/light/src/provider.rs b/ethcore/light/src/provider.rs index d71a5fff0..6db81dcdd 100644 --- a/ethcore/light/src/provider.rs +++ b/ethcore/light/src/provider.rs @@ -127,9 +127,6 @@ pub trait Provider: Send + Sync { /// 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::CompleteExecutionRequest) -> Option; - - /// Provide epoch signal data at given block hash. This should be just the - fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option; } // Implementation of a light client data provider for a client. @@ -268,12 +265,6 @@ impl Provider for T { fn ready_transactions(&self) -> Vec { BlockChainClient::ready_transactions(self) } - - fn epoch_signal(&self, req: request::CompleteSignalRequest) -> Option { - self.epoch_signal(req.block_hash).map(|signal| request::SignalResponse { - signal: signal, - }) - } } /// The light client "provider" implementation. This wraps a `LightClient` and @@ -339,10 +330,6 @@ impl Provider for LightProvider { None } - fn epoch_signal(&self, _req: request::CompleteSignalRequest) -> Option { - None - } - fn ready_transactions(&self) -> Vec { let chain_info = self.chain_info(); self.txqueue.read().ready_transactions(chain_info.best_block_number, chain_info.best_block_timestamp) diff --git a/ethcore/light/src/types/request/mod.rs b/ethcore/light/src/types/request/mod.rs index c623ca656..38e736673 100644 --- a/ethcore/light/src/types/request/mod.rs +++ b/ethcore/light/src/types/request/mod.rs @@ -67,11 +67,6 @@ pub use self::execution::{ Incomplete as IncompleteExecutionRequest, Response as ExecutionResponse, }; -pub use self::epoch_signal::{ - Complete as CompleteSignalRequest, - Incomplete as IncompleteSignalRequest, - Response as SignalResponse, -}; pub use self::builder::{RequestBuilder, Requests}; @@ -266,8 +261,6 @@ pub enum Request { Code(IncompleteCodeRequest), /// A request for proof of execution, Execution(IncompleteExecutionRequest), - /// A request for an epoch signal. - Signal(IncompleteSignalRequest), } /// All request types, in an answerable state. @@ -291,8 +284,6 @@ pub enum CompleteRequest { Code(CompleteCodeRequest), /// A request for proof of execution, Execution(CompleteExecutionRequest), - /// A request for an epoch signal. - Signal(CompleteSignalRequest), } impl CompleteRequest { @@ -308,7 +299,6 @@ impl CompleteRequest { CompleteRequest::Storage(_) => Kind::Storage, CompleteRequest::Code(_) => Kind::Code, CompleteRequest::Execution(_) => Kind::Execution, - CompleteRequest::Signal(_) => Kind::Signal, } } } @@ -326,7 +316,6 @@ impl Request { Request::Storage(_) => Kind::Storage, Request::Code(_) => Kind::Code, Request::Execution(_) => Kind::Execution, - Request::Signal(_) => Kind::Signal, } } } @@ -343,7 +332,6 @@ impl Decodable for Request { 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)?)), - Kind::Signal => Ok(Request::Signal(rlp.val_at(1)?)), } } } @@ -365,7 +353,6 @@ impl Encodable for Request { Request::Storage(ref req) => s.append(req), Request::Code(ref req) => s.append(req), Request::Execution(ref req) => s.append(req), - Request::Signal(ref req) => s.append(req), }; } } @@ -387,7 +374,6 @@ impl IncompleteRequest for Request { Request::Storage(ref req) => req.check_outputs(f), Request::Code(ref req) => req.check_outputs(f), Request::Execution(ref req) => req.check_outputs(f), - Request::Signal(ref req) => req.check_outputs(f), } } @@ -402,7 +388,6 @@ impl IncompleteRequest for Request { Request::Storage(ref req) => req.note_outputs(f), Request::Code(ref req) => req.note_outputs(f), Request::Execution(ref req) => req.note_outputs(f), - Request::Signal(ref req) => req.note_outputs(f), } } @@ -417,7 +402,6 @@ impl IncompleteRequest for Request { Request::Storage(ref mut req) => req.fill(oracle), Request::Code(ref mut req) => req.fill(oracle), Request::Execution(ref mut req) => req.fill(oracle), - Request::Signal(ref mut req) => req.fill(oracle), } } @@ -432,7 +416,6 @@ impl IncompleteRequest for Request { Request::Storage(req) => req.complete().map(CompleteRequest::Storage), Request::Code(req) => req.complete().map(CompleteRequest::Code), Request::Execution(req) => req.complete().map(CompleteRequest::Execution), - Request::Signal(req) => req.complete().map(CompleteRequest::Signal), } } @@ -447,7 +430,6 @@ impl IncompleteRequest for Request { Request::Storage(ref mut req) => req.adjust_refs(mapping), Request::Code(ref mut req) => req.adjust_refs(mapping), Request::Execution(ref mut req) => req.adjust_refs(mapping), - Request::Signal(ref mut req) => req.adjust_refs(mapping), } } } @@ -489,8 +471,6 @@ pub enum Kind { Code = 7, /// A request for transaction execution + state proof. Execution = 8, - /// A request for epoch transition signal. - Signal = 9, } impl Decodable for Kind { @@ -505,7 +485,6 @@ impl Decodable for Kind { 6 => Ok(Kind::Storage), 7 => Ok(Kind::Code), 8 => Ok(Kind::Execution), - 9 => Ok(Kind::Signal), _ => Err(DecoderError::Custom("Unknown PIP request ID.")), } } @@ -538,8 +517,6 @@ pub enum Response { Code(CodeResponse), /// A response for proof of execution, Execution(ExecutionResponse), - /// A response for epoch change signal. - Signal(SignalResponse), } impl ResponseLike for Response { @@ -555,7 +532,6 @@ impl ResponseLike for Response { Response::Storage(ref res) => res.fill_outputs(f), Response::Code(ref res) => res.fill_outputs(f), Response::Execution(ref res) => res.fill_outputs(f), - Response::Signal(ref res) => res.fill_outputs(f), } } } @@ -573,7 +549,6 @@ impl Response { Response::Storage(_) => Kind::Storage, Response::Code(_) => Kind::Code, Response::Execution(_) => Kind::Execution, - Response::Signal(_) => Kind::Signal, } } } @@ -590,7 +565,6 @@ impl Decodable for Response { 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)?)), - Kind::Signal => Ok(Response::Signal(rlp.val_at(1)?)), } } } @@ -612,7 +586,6 @@ impl Encodable for Response { Response::Storage(ref res) => s.append(res), Response::Code(ref res) => s.append(res), Response::Execution(ref res) => s.append(res), - Response::Signal(ref res) => s.append(res), }; } } @@ -787,8 +760,8 @@ pub mod header { pub mod header_proof { use super::{Field, NoSuchOutput, OutputKind, Output}; use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; - use bigint::prelude::U256; use bigint::hash::H256; + use bigint::prelude::U256; use util::Bytes; /// Potentially incomplete header proof request. @@ -1118,8 +1091,8 @@ pub mod block_body { /// A request for an account proof. pub mod account { use super::{Field, NoSuchOutput, OutputKind, Output}; - use bigint::prelude::U256; use bigint::hash::H256; + use bigint::prelude::U256; use util::Bytes; /// Potentially incomplete request for an account proof. @@ -1415,8 +1388,8 @@ pub mod execution { use super::{Field, NoSuchOutput, OutputKind, Output}; use ethcore::transaction::Action; use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; - use bigint::prelude::U256; use bigint::hash::H256; + use bigint::prelude::U256; use util::{Bytes, Address, DBValue}; /// Potentially incomplete execution proof request. @@ -1536,105 +1509,6 @@ pub mod execution { } } -/// A request for epoch signal data. -pub mod epoch_signal { - use super::{Field, NoSuchOutput, OutputKind, Output}; - use rlp::{Encodable, Decodable, DecoderError, RlpStream, UntrustedRlp}; - use bigint::hash::H256; - use util::Bytes; - - /// Potentially incomplete epoch signal request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Incomplete { - /// The block hash to request the signal for. - pub block_hash: Field, - } - - impl Decodable for Incomplete { - fn decode(rlp: &UntrustedRlp) -> Result { - Ok(Incomplete { - block_hash: rlp.val_at(0)?, - }) - } - } - - impl Encodable for Incomplete { - fn rlp_append(&self, s: &mut RlpStream) { - s.begin_list(1).append(&self.block_hash); - } - } - - impl super::IncompleteRequest for Incomplete { - type Complete = Complete; - type Response = Response; - - 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()?, - }) - } - - fn adjust_refs(&mut self, mut mapping: F) where F: FnMut(usize) -> usize { - self.block_hash.adjust_req(&mut mapping); - } - } - - /// A complete request. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Complete { - /// The block hash to request the epoch signal for. - pub block_hash: H256, - } - - /// The output of a request for an epoch signal. - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct Response { - /// The requested epoch signal. - pub signal: Bytes, - } - - impl super::ResponseLike for Response { - /// Fill reusable outputs by providing them to the function. - fn fill_outputs(&self, _: F) where F: FnMut(usize, Output) {} - } - - impl Decodable for Response { - fn decode(rlp: &UntrustedRlp) -> Result { - - Ok(Response { - signal: rlp.as_val()?, - }) - } - } - - impl Encodable for Response { - fn rlp_append(&self, s: &mut RlpStream) { - s.append(&self.signal); - } - } -} - #[cfg(test)] mod tests { use super::*; @@ -1923,22 +1797,4 @@ mod tests { let raw = ::rlp::encode_list(&reqs); assert_eq!(::rlp::decode_list::(&raw), reqs); } - - #[test] - fn epoch_signal_roundtrip() { - let req = IncompleteSignalRequest { - block_hash: Field::Scalar(Default::default()), - }; - - let full_req = Request::Signal(req.clone()); - let res = SignalResponse { - signal: vec![1, 2, 3, 4, 5, 6, 7, 6, 5, 4], - }; - let full_res = Response::Signal(res.clone()); - - check_roundtrip(req); - check_roundtrip(full_req); - check_roundtrip(res); - check_roundtrip(full_res); - } } diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 26ef11ffb..55e1cf903 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -42,7 +42,7 @@ use client::ancient_import::AncientVerifier; use client::Error as ClientError; use client::{ BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, - MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode, + MiningBlockChainClient, EngineClient, TraceFilter, CallAnalytics, BlockImportError, Mode, ChainNotify, PruningInfo, ProvingBlockChainClient, }; use encoded; @@ -770,7 +770,7 @@ impl Client { res.map(|(output, proof)| (output, proof.into_iter().map(|x| x.into_vec()).collect())) }; - match with_state.generate_proof(&call) { + match (with_state)(&call) { Ok(proof) => proof, Err(e) => { warn!(target: "client", "Failed to generate transition proof for block {}: {}", hash, e); @@ -1131,7 +1131,9 @@ impl Client { T: trace::Tracer, V: trace::VMTracer, { - let options = options.dont_check_nonce(); + let options = options + .dont_check_nonce() + .save_output_from_contract(); let original_state = if state_diff { Some(state.clone()) } else { None }; let mut ret = Executive::new(state, env_info, engine).transact_virtual(transaction, options)?; @@ -1934,7 +1936,7 @@ impl MiningBlockChainClient for Client { } } -impl super::traits::EngineClient for Client { +impl EngineClient for Client { fn update_sealing(&self) { self.miner.update_sealing(self) } @@ -1952,22 +1954,6 @@ impl super::traits::EngineClient for Client { fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition> { self.chain.read().epoch_transition_for(parent_hash) } - - fn chain_info(&self) -> BlockChainInfo { - BlockChainClient::chain_info(self) - } - - fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result { - BlockChainClient::call_contract(self, id, address, data) - } - - fn transact_contract(&self, address: Address, data: Bytes) -> Result { - BlockChainClient::transact_contract(self, address, data) - } - - fn block_number(&self, id: BlockId) -> Option { - BlockChainClient::block_number(self, id) - } } impl ProvingBlockChainClient for Client { @@ -1982,29 +1968,27 @@ impl ProvingBlockChainClient for Client { } fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec)> { - let (header, mut env_info) = match (self.block_header(id), self.env_info(id)) { + let (state, mut env_info) = match (self.state_at(id), self.env_info(id)) { (Some(s), Some(e)) => (s, e), _ => return None, }; env_info.gas_limit = transaction.gas.clone(); let mut jdb = self.state_db.lock().journal_db().boxed_clone(); + let backend = state::backend::Proving::new(jdb.as_hashdb_mut()); - state::prove_transaction( - jdb.as_hashdb_mut(), - header.state_root().clone(), - &transaction, - &*self.engine, - &env_info, - self.factories.clone(), - false, - ) - } + let mut state = state.replace_backend(backend); + let options = TransactOptions::with_no_tracing().dont_check_nonce(); + let res = Executive::new(&mut state, &env_info, &*self.engine).transact(&transaction, options); - fn epoch_signal(&self, hash: H256) -> Option> { - // pending transitions are never deleted, and do not contain - // finality proofs by definition. - self.chain.read().get_pending_transition(hash).map(|pending| pending.proof) + match res { + Err(ExecutionError::Internal(_)) => None, + Err(e) => { + trace!(target: "client", "Proved call failed: {}", e); + Some((Vec::new(), state.drop().1.extract_proof())) + } + Ok(res) => Some((res.output, state.drop().1.extract_proof())), + } } } diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 0cd9fedc8..aabd744f9 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -33,7 +33,7 @@ use devtools::*; use transaction::{Transaction, LocalizedTransaction, PendingTransaction, SignedTransaction, Action}; use blockchain::TreeRoute; use client::{ - BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId, + BlockChainClient, MiningBlockChainClient, EngineClient, BlockChainInfo, BlockStatus, BlockId, TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError, ProvingBlockChainClient, }; @@ -801,13 +801,9 @@ impl ProvingBlockChainClient for TestBlockChainClient { fn prove_transaction(&self, _: SignedTransaction, _: BlockId) -> Option<(Bytes, Vec)> { None } - - fn epoch_signal(&self, _: H256) -> Option> { - None - } } -impl super::traits::EngineClient for TestBlockChainClient { +impl EngineClient for TestBlockChainClient { fn update_sealing(&self) { self.miner.update_sealing(self) } @@ -823,20 +819,4 @@ impl super::traits::EngineClient for TestBlockChainClient { fn epoch_transition_for(&self, _block_hash: H256) -> Option<::engines::EpochTransition> { None } - - fn chain_info(&self) -> BlockChainInfo { - BlockChainClient::chain_info(self) - } - - fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result { - BlockChainClient::call_contract(self, id, address, data) - } - - fn transact_contract(&self, address: Address, data: Bytes) -> Result { - BlockChainClient::transact_contract(self, address, data) - } - - fn block_number(&self, id: BlockId) -> Option { - BlockChainClient::block_number(self, id) - } } diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index 45736e2c5..4a1f8a0c5 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -317,7 +317,7 @@ pub trait MiningBlockChainClient: BlockChainClient { } /// Client facilities used by internally sealing Engines. -pub trait EngineClient: Sync + Send { +pub trait EngineClient: MiningBlockChainClient { /// Make a new block and seal it. fn update_sealing(&self); @@ -333,17 +333,6 @@ pub trait EngineClient: Sync + Send { /// /// The block corresponding the the parent hash must be stored already. fn epoch_transition_for(&self, parent_hash: H256) -> Option<::engines::EpochTransition>; - - /// Get block chain info. - fn chain_info(&self) -> BlockChainInfo; - - /// Like `call`, but with various defaults. Designed to be used for calling contracts. - fn call_contract(&self, id: BlockId, address: Address, data: Bytes) -> Result; - - /// Import a transaction: used for misbehaviour reporting. - fn transact_contract(&self, address: Address, data: Bytes) -> Result; - - fn block_number(&self, id: BlockId) -> Option; } /// Extended client interface for providing proofs of the state. @@ -363,7 +352,4 @@ pub trait ProvingBlockChainClient: BlockChainClient { /// Returns the output of the call and a vector of database items necessary /// to reproduce it. fn prove_transaction(&self, transaction: SignedTransaction, id: BlockId) -> Option<(Bytes, Vec)>; - - /// Get an epoch change signal by block hash. - fn epoch_signal(&self, hash: H256) -> Option>; } diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 4eb268000..47df0a8e5 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -25,7 +25,7 @@ use std::cmp; use account_provider::AccountProvider; use block::*; use builtin::Builtin; -use client::EngineClient; +use client::{Client, EngineClient}; use engines::{Call, Engine, Seal, EngineError, ConstructedVerifier}; use error::{Error, TransactionError, BlockError}; use ethjson; @@ -646,8 +646,6 @@ impl Engine for AuthorityRound { (&active_set as &_, epoch_manager.epoch_transition_number) }; - // always report with "self.validators" so that the report actually gets - // to the contract. let report = |report| match report { Report::Benign(address, block_number) => self.validators.report_benign(&address, set_number, block_number), @@ -740,18 +738,13 @@ impl Engine for AuthorityRound { { if let Ok(finalized) = epoch_manager.finality_checker.push_hash(chain_head.hash(), *chain_head.author()) { let mut finalized = finalized.into_iter(); - while let Some(finalized_hash) = finalized.next() { - if let Some(pending) = transition_store(finalized_hash) { - let finality_proof = ::std::iter::once(finalized_hash) + while let Some(hash) = finalized.next() { + if let Some(pending) = transition_store(hash) { + let finality_proof = ::std::iter::once(hash) .chain(finalized) .chain(epoch_manager.finality_checker.unfinalized_hashes()) - .map(|h| if h == chain_head.hash() { - // chain closure only stores ancestry, but the chain head is also - // unfinalized. - chain_head.clone() - } else { - chain(h).expect("these headers fetched before when constructing finality checker; qed") - }) + .map(|hash| chain(hash) + .expect("these headers fetched before when constructing finality checker; qed")) .collect::>(); // this gives us the block number for `hash`, assuming it's ancestry. @@ -815,9 +808,9 @@ impl Engine for AuthorityRound { Ok(()) } - fn register_client(&self, client: Weak) { + fn register_client(&self, client: Weak) { *self.client.write() = Some(client.clone()); - self.validators.register_client(client); + self.validators.register_contract(client); } fn set_signer(&self, ap: Arc, address: Address, password: String) { diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 15cf15fd6..8b816cf64 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -33,7 +33,7 @@ use error::{BlockError, Error}; use evm::Schedule; use ethjson; use header::{Header, BlockNumber}; -use client::EngineClient; +use client::Client; use semantic_version::SemanticVersion; use super::signer::EngineSigner; use super::validator_set::{ValidatorSet, SimpleList, new_validator_set}; @@ -236,8 +236,8 @@ impl Engine for BasicAuthority { } } - fn register_client(&self, client: Weak) { - self.validators.register_client(client); + fn register_client(&self, client: Weak) { + self.validators.register_contract(client); } fn set_signer(&self, ap: Arc, address: Address, password: String) { diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 19ec62b15..5b7e9f606 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -44,7 +44,7 @@ use self::epoch::PendingTransition; use account_provider::AccountProvider; use block::ExecutedBlock; use builtin::Builtin; -use client::EngineClient; +use client::Client; use vm::{EnvInfo, LastHashes, Schedule, CreateContractAddress}; use error::Error; use header::{Header, BlockNumber}; @@ -123,22 +123,12 @@ pub type Headers<'a> = Fn(H256) -> Option
+ 'a; /// Type alias for a function we can query pending transitions by block hash through. pub type PendingTransitionStore<'a> = Fn(H256) -> Option + 'a; -/// Proof dependent on state. -pub trait StateDependentProof: Send + Sync { - /// Generate a proof, given the state. - fn generate_proof(&self, caller: &Call) -> Result, String>; - /// Check a proof generated elsewhere (potentially by a peer). - // `engine` needed to check state proofs, while really this should - // just be state machine params. - fn check_proof(&self, engine: &Engine, proof: &[u8]) -> Result<(), String>; -} - /// Proof generated on epoch change. pub enum Proof { - /// Known proof (extracted from signal) + /// Known proof (exctracted from signal) Known(Vec), - /// State dependent proof. - WithState(Arc), + /// Extract proof from caller. + WithState(Box Result, String>>), } /// Generated epoch verifier. @@ -370,7 +360,7 @@ pub trait Engine : Sync + Send { fn sign(&self, _hash: H256) -> Result { unimplemented!() } /// Add Client which can be used for sealing, querying the state and sending messages. - fn register_client(&self, _client: Weak) {} + fn register_client(&self, _client: Weak) {} /// Trigger next step of the consensus engine. fn step(&self) {} diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 5e902f895..be48aacf6 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -34,7 +34,7 @@ use bigint::prelude::{U128, U256}; use bigint::hash::{H256, H520}; use parking_lot::RwLock; use util::*; -use client::EngineClient; +use client::{Client, EngineClient}; use error::{Error, BlockError}; use header::{Header, BlockNumber}; use builtin::Builtin; @@ -570,35 +570,18 @@ impl Engine for Tendermint { Ok(()) } - /// Verify gas limit. + /// Verify validators and gas limit. fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { if header.number() == 0 { return Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }).into()); } - let gas_limit_divisor = self.params().gas_limit_bound_divisor; - let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor; - let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor; - if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { - self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default()); - return Err(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }).into()); - } - - Ok(()) - } - - fn verify_block_external(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> { if let Ok(proposal) = ConsensusMessage::new_proposal(header) { let proposer = proposal.verify()?; if !self.is_authority(&proposer) { return Err(EngineError::NotAuthorized(proposer).into()); } - self.check_view_proposer( - header.parent_hash(), - proposal.vote_step.height, - proposal.vote_step.view, - &proposer - ).map_err(Into::into) + self.check_view_proposer(header.parent_hash(), proposal.vote_step.height, proposal.vote_step.view, &proposer)?; } else { let vote_step = VoteStep::new(header.number() as usize, consensus_view(header)?, Step::Precommit); let precommit_hash = message_hash(vote_step.clone(), header.bare_hash()); @@ -624,8 +607,18 @@ impl Engine for Tendermint { } } - self.check_above_threshold(origins.len()).map_err(Into::into) + self.check_above_threshold(origins.len())? } + + let gas_limit_divisor = self.params().gas_limit_bound_divisor; + let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor; + let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor; + if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { + self.validators.report_malicious(header.author(), header.number(), header.number(), Default::default()); + return Err(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }).into()); + } + + Ok(()) } fn signals_epoch_end(&self, header: &Header, block: Option<&[u8]>, receipts: Option<&[::receipt::Receipt]>) @@ -760,12 +753,13 @@ impl Engine for Tendermint { self.to_step(next_step); } - fn register_client(&self, client: Weak) { + fn register_client(&self, client: Weak) { + use client::BlockChainClient; if let Some(c) = client.upgrade() { self.height.store(c.chain_info().best_block_number as usize + 1, AtomicOrdering::SeqCst); } *self.client.write() = Some(client.clone()); - self.validators.register_client(client); + self.validators.register_contract(client); } } @@ -893,14 +887,14 @@ mod tests { let seal = proposal_seal(&tap, &header, 0); header.set_seal(seal); // Good proposer. - assert!(engine.verify_block_external(&header, None).is_ok()); + assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); let validator = insert_and_unlock(&tap, "0"); header.set_author(validator); let seal = proposal_seal(&tap, &header, 0); header.set_seal(seal); // Bad proposer. - match engine.verify_block_external(&header, None) { + match engine.verify_block_family(&header, &parent_header, None) { Err(Error::Engine(EngineError::NotProposer(_))) => {}, _ => panic!(), } @@ -910,7 +904,7 @@ mod tests { let seal = proposal_seal(&tap, &header, 0); header.set_seal(seal); // Not authority. - match engine.verify_block_external(&header, None) { + match engine.verify_block_family(&header, &parent_header, None) { Err(Error::Engine(EngineError::NotAuthorized(_))) => {}, _ => panic!(), }; @@ -940,7 +934,7 @@ mod tests { header.set_seal(seal.clone()); // One good signature is not enough. - match engine.verify_block_external(&header, None) { + match engine.verify_block_family(&header, &parent_header, None) { Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {}, _ => panic!(), } @@ -951,7 +945,7 @@ mod tests { seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).into_vec(); header.set_seal(seal.clone()); - assert!(engine.verify_block_external(&header, None).is_ok()); + assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); let bad_voter = insert_and_unlock(&tap, "101"); let bad_signature = tap.sign(bad_voter, None, keccak(vote_info)).unwrap(); @@ -960,7 +954,7 @@ mod tests { header.set_seal(seal); // One good and one bad signature. - match engine.verify_block_external(&header, None) { + match engine.verify_block_family(&header, &parent_header, None) { Err(Error::Engine(EngineError::NotAuthorized(_))) => {}, _ => panic!(), }; @@ -1006,7 +1000,7 @@ mod tests { let client = generate_dummy_client(0); let notify = Arc::new(TestNotify::default()); client.add_notify(notify.clone()); - engine.register_client(Arc::downgrade(&client) as _); + engine.register_client(Arc::downgrade(&client)); let prevote_current = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal); @@ -1024,6 +1018,7 @@ mod tests { fn seal_submission() { use ethkey::{Generator, Random}; use transaction::{Transaction, Action}; + use client::BlockChainClient; let tap = Arc::new(AccountProvider::transient_provider()); // Accounts for signing votes. @@ -1036,7 +1031,7 @@ mod tests { let notify = Arc::new(TestNotify::default()); client.add_notify(notify.clone()); - engine.register_client(Arc::downgrade(&client) as _); + engine.register_client(Arc::downgrade(&client)); let keypair = Random.generate().unwrap(); let transaction = Transaction { diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index 9a3705b63..c84c6e448 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -25,7 +25,7 @@ use util::*; use futures::Future; use native_contracts::ValidatorReport as Provider; -use client::EngineClient; +use client::{Client, BlockChainClient}; use engines::{Call, Engine}; use header::{Header, BlockNumber}; @@ -36,7 +36,7 @@ use super::safe_contract::ValidatorSafeContract; pub struct ValidatorContract { validators: ValidatorSafeContract, provider: Provider, - client: RwLock>>, // TODO [keorn]: remove + client: RwLock>>, // TODO [keorn]: remove } impl ValidatorContract { @@ -120,8 +120,8 @@ impl ValidatorSet for ValidatorContract { } } - fn register_client(&self, client: Weak) { - self.validators.register_client(client.clone()); + fn register_contract(&self, client: Weak) { + self.validators.register_contract(client.clone()); *self.client.write() = Some(client); } } @@ -148,7 +148,7 @@ mod tests { fn fetches_validators() { let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_contract, None); let vc = Arc::new(ValidatorContract::new("0000000000000000000000000000000000000005".parse::
().unwrap())); - vc.register_client(Arc::downgrade(&client) as _); + vc.register_contract(Arc::downgrade(&client)); let last_hash = client.best_block_header().hash(); assert!(vc.contains(&last_hash, &"7d577a597b2742b498cb5cf0c26cdcd726d39e6e".parse::
().unwrap())); assert!(vc.contains(&last_hash, &"82a978b3f5962a5b0957d9ee9eef472ee55b42f1".parse::
().unwrap())); @@ -159,7 +159,7 @@ mod tests { let tap = Arc::new(AccountProvider::transient_provider()); let v1 = tap.insert_account(keccak("1").into(), "").unwrap(); let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_contract, Some(tap.clone())); - client.engine().register_client(Arc::downgrade(&client) as _); + client.engine().register_client(Arc::downgrade(&client)); let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); // Make sure reporting can be done. diff --git a/ethcore/src/engines/validator_set/mod.rs b/ethcore/src/engines/validator_set/mod.rs index 451abe6f2..d60518c45 100644 --- a/ethcore/src/engines/validator_set/mod.rs +++ b/ethcore/src/engines/validator_set/mod.rs @@ -28,7 +28,7 @@ use ids::BlockId; use bigint::hash::H256; use util::{Bytes, Address}; use ethjson::spec::ValidatorSet as ValidatorSpec; -use client::EngineClient; +use client::Client; use header::{Header, BlockNumber}; #[cfg(test)] @@ -142,5 +142,5 @@ pub trait ValidatorSet: Send + Sync { /// Notifies about benign misbehaviour. fn report_benign(&self, _validator: &Address, _set_block: BlockNumber, _block: BlockNumber) {} /// Allows blockchain state access. - fn register_client(&self, _client: Weak) {} + fn register_contract(&self, _client: Weak) {} } diff --git a/ethcore/src/engines/validator_set/multi.rs b/ethcore/src/engines/validator_set/multi.rs index 043f8aab6..c115d1596 100644 --- a/ethcore/src/engines/validator_set/multi.rs +++ b/ethcore/src/engines/validator_set/multi.rs @@ -24,7 +24,7 @@ use parking_lot::RwLock; use util::{Bytes, Address}; use ids::BlockId; use header::{BlockNumber, Header}; -use client::EngineClient; +use client::{Client, BlockChainClient}; use super::{SystemCall, ValidatorSet}; type BlockNumberLookup = Box Result + Send + Sync + 'static>; @@ -131,9 +131,9 @@ impl ValidatorSet for Multi { self.correct_set_by_number(set_block).1.report_benign(validator, set_block, block); } - fn register_client(&self, client: Weak) { + fn register_contract(&self, client: Weak) { for set in self.sets.values() { - set.register_client(client.clone()); + set.register_contract(client.clone()); } *self.block_number.write() = Box::new(move |id| client .upgrade() @@ -148,7 +148,7 @@ mod tests { use std::collections::BTreeMap; use hash::keccak; use account_provider::AccountProvider; - use client::BlockChainClient; + use client::{BlockChainClient, EngineClient}; use engines::EpochChange; use engines::validator_set::ValidatorSet; use ethkey::Secret; @@ -170,7 +170,7 @@ mod tests { let v0 = tap.insert_account(s0.clone(), "").unwrap(); let v1 = tap.insert_account(keccak("1").into(), "").unwrap(); let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_multi, Some(tap)); - client.engine().register_client(Arc::downgrade(&client) as _); + client.engine().register_client(Arc::downgrade(&client)); // Make sure txs go through. client.miner().set_gas_floor_target(1_000_000.into()); @@ -178,27 +178,27 @@ mod tests { // Wrong signer for the first block. client.miner().set_engine_signer(v1, "".into()).unwrap(); client.transact_contract(Default::default(), Default::default()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + 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::EngineClient::update_sealing(&*client); + 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::EngineClient::update_sealing(&*client); + client.update_sealing(); assert_eq!(client.chain_info().best_block_number, 1); client.miner().set_engine_signer(v1, "".into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + 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::EngineClient::update_sealing(&*client); + 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) as _); + 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(); } diff --git a/ethcore/src/engines/validator_set/safe_contract.rs b/ethcore/src/engines/validator_set/safe_contract.rs index 2411f4770..42666eb64 100644 --- a/ethcore/src/engines/validator_set/safe_contract.rs +++ b/ethcore/src/engines/validator_set/safe_contract.rs @@ -23,14 +23,13 @@ use hash::keccak; use bigint::prelude::U256; use bigint::hash::{H160, H256}; -use parking_lot::{Mutex, RwLock}; - +use parking_lot::RwLock; use util::*; use util::cache::MemoryLruCache; use rlp::{UntrustedRlp, RlpStream}; use basic_types::LogBloom; -use client::EngineClient; +use client::{Client, BlockChainClient}; use engines::{Call, Engine}; use header::Header; use ids::BlockId; @@ -49,35 +48,12 @@ lazy_static! { static ref EVENT_NAME_HASH: H256 = keccak(EVENT_NAME); } -// state-dependent proofs for the safe contract: -// only "first" proofs are such. -struct StateProof { - header: Mutex
, - provider: Provider, -} - -impl ::engines::StateDependentProof for StateProof { - fn generate_proof(&self, caller: &Call) -> Result, String> { - prove_initial(&self.provider, &*self.header.lock(), caller) - } - - fn check_proof(&self, engine: &Engine, proof: &[u8]) -> Result<(), String> { - let (header, state_items) = decode_first_proof(&UntrustedRlp::new(proof)) - .map_err(|e| format!("proof incorrectly encoded: {}", e))?; - if &header != &*self.header.lock(){ - return Err("wrong header in proof".into()); - } - - check_first_proof(engine, &self.provider, header, &state_items).map(|_| ()) - } -} - /// The validator contract should have the following interface: pub struct ValidatorSafeContract { pub address: Address, validators: RwLock>, provider: Provider, - client: RwLock>>, // TODO [keorn]: remove + client: RwLock>>, // TODO [keorn]: remove } // first proof is just a state proof call of `getValidators` at header's state. @@ -91,59 +67,6 @@ fn encode_first_proof(header: &Header, state_items: &[Vec]) -> Bytes { stream.out() } -// check a first proof: fetch the validator set at the given block. -fn check_first_proof(engine: &Engine, provider: &Provider, old_header: Header, state_items: &[DBValue]) - -> Result, String> -{ - use transaction::{Action, Transaction}; - - // TODO: match client contract_call_tx more cleanly without duplication. - const PROVIDED_GAS: u64 = 50_000_000; - - let env_info = ::vm::EnvInfo { - number: old_header.number(), - author: *old_header.author(), - difficulty: *old_header.difficulty(), - gas_limit: PROVIDED_GAS.into(), - timestamp: old_header.timestamp(), - last_hashes: { - // this will break if we don't inclue all 256 last hashes. - let mut last_hashes: Vec<_> = (0..256).map(|_| H256::default()).collect(); - last_hashes[255] = *old_header.parent_hash(); - Arc::new(last_hashes) - }, - gas_used: 0.into(), - }; - - // check state proof using given engine. - let number = old_header.number(); - provider.get_validators(move |a, d| { - let from = Address::default(); - let tx = Transaction { - nonce: engine.account_start_nonce(number), - action: Action::Call(a), - gas: PROVIDED_GAS.into(), - gas_price: U256::default(), - value: U256::default(), - data: d, - }.fake_sign(from); - - let res = ::state::check_proof( - state_items, - *old_header.state_root(), - &tx, - engine, - &env_info, - ); - - match res { - ::state::ProvedExecution::BadProof => Err("Bad proof".into()), - ::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)), - ::state::ProvedExecution::Complete(e) => Ok(e.output), - } - }).wait() -} - fn decode_first_proof(rlp: &UntrustedRlp) -> Result<(Header, Vec), ::error::Error> { let header = rlp.val_at(0)?; let state_items = rlp.at(1)?.iter().map(|x| { @@ -181,7 +104,8 @@ fn prove_initial(provider: &Provider, header: &Header, caller: &Call) -> Result< Ok(result) }; - provider.get_validators(caller).wait() + provider.get_validators(caller) + .wait() }; res.map(|validators| { @@ -335,11 +259,9 @@ impl ValidatorSet for ValidatorSafeContract { // transition to the first block of a contract requires finality but has no log event. if first { debug!(target: "engine", "signalling transition to fresh contract."); - let state_proof = Arc::new(StateProof { - header: Mutex::new(header.clone()), - provider: self.provider.clone(), - }); - return ::engines::EpochChange::Yes(::engines::Proof::WithState(state_proof as Arc<_>)); + let (provider, header) = (self.provider.clone(), header.clone()); + let with_caller: Box _> = Box::new(move |caller| prove_initial(&provider, &header, caller)); + return ::engines::EpochChange::Yes(::engines::Proof::WithState(with_caller)) } // otherwise, we're checking for logs. @@ -368,16 +290,61 @@ impl ValidatorSet for ValidatorSafeContract { fn epoch_set(&self, first: bool, engine: &Engine, _number: ::header::BlockNumber, proof: &[u8]) -> Result<(SimpleList, Option), ::error::Error> { + use transaction::{Action, Transaction}; + let rlp = UntrustedRlp::new(proof); if first { trace!(target: "engine", "Recovering initial epoch set"); + // TODO: match client contract_call_tx more cleanly without duplication. + const PROVIDED_GAS: u64 = 50_000_000; + let (old_header, state_items) = decode_first_proof(&rlp)?; - let number = old_header.number(); let old_hash = old_header.hash(); - let addresses = check_first_proof(engine, &self.provider, old_header, &state_items) - .map_err(::engines::EngineError::InsufficientProof)?; + + let env_info = ::vm::EnvInfo { + number: old_header.number(), + author: *old_header.author(), + difficulty: *old_header.difficulty(), + gas_limit: PROVIDED_GAS.into(), + timestamp: old_header.timestamp(), + last_hashes: { + // this will break if we don't inclue all 256 last hashes. + let mut last_hashes: Vec<_> = (0..256).map(|_| H256::default()).collect(); + last_hashes[255] = *old_header.parent_hash(); + Arc::new(last_hashes) + }, + gas_used: 0.into(), + }; + + // check state proof using given engine. + let number = old_header.number(); + let addresses = self.provider.get_validators(move |a, d| { + let from = Address::default(); + let tx = Transaction { + nonce: engine.account_start_nonce(number), + action: Action::Call(a), + gas: PROVIDED_GAS.into(), + gas_price: U256::default(), + value: U256::default(), + data: d, + }.fake_sign(from); + + let res = ::state::check_proof( + &state_items, + *old_header.state_root(), + &tx, + engine, + &env_info, + ); + + match res { + ::state::ProvedExecution::BadProof => Err("Bad proof".into()), + ::state::ProvedExecution::Failed(e) => Err(format!("Failed call: {}", e)), + ::state::ProvedExecution::Complete(e) => Ok(e.output), + } + }).wait().map_err(::engines::EngineError::InsufficientProof)?; trace!(target: "engine", "extracted epoch set at #{}: {} addresses", number, addresses.len()); @@ -451,7 +418,7 @@ impl ValidatorSet for ValidatorSafeContract { })) } - fn register_client(&self, client: Weak) { + fn register_contract(&self, client: Weak) { trace!(target: "engine", "Setting up contract caller."); *self.client.write() = Some(client); } @@ -467,7 +434,7 @@ mod tests { use spec::Spec; use account_provider::AccountProvider; use transaction::{Transaction, Action}; - use client::BlockChainClient; + 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}; @@ -478,7 +445,7 @@ mod tests { fn fetches_validators() { let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, None); let vc = Arc::new(ValidatorSafeContract::new("0000000000000000000000000000000000000005".parse::
().unwrap())); - vc.register_client(Arc::downgrade(&client) as _); + vc.register_contract(Arc::downgrade(&client)); let last_hash = client.best_block_header().hash(); assert!(vc.contains(&last_hash, &"7d577a597b2742b498cb5cf0c26cdcd726d39e6e".parse::
().unwrap())); assert!(vc.contains(&last_hash, &"82a978b3f5962a5b0957d9ee9eef472ee55b42f1".parse::
().unwrap())); @@ -492,7 +459,7 @@ mod tests { let v1 = tap.insert_account(keccak("0").into(), "").unwrap(); let chain_id = Spec::new_validator_safe_contract().chain_id(); let client = generate_dummy_client_with_spec_and_accounts(Spec::new_validator_safe_contract, Some(tap)); - client.engine().register_client(Arc::downgrade(&client) as _); + client.engine().register_client(Arc::downgrade(&client)); let validator_contract = "0000000000000000000000000000000000000005".parse::
().unwrap(); client.miner().set_engine_signer(v1, "".into()).unwrap(); @@ -506,7 +473,7 @@ mod tests { data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), }.sign(&s0, Some(chain_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + client.update_sealing(); assert_eq!(client.chain_info().best_block_number, 1); // Add "1" validator back in. let tx = Transaction { @@ -518,13 +485,13 @@ mod tests { data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(), }.sign(&s0, Some(chain_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + client.update_sealing(); // The transaction is not yet included so still unable to seal. assert_eq!(client.chain_info().best_block_number, 1); // Switch to the validator that is still there. client.miner().set_engine_signer(v0, "".into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + client.update_sealing(); assert_eq!(client.chain_info().best_block_number, 2); // Switch back to the added validator, since the state is updated. client.miner().set_engine_signer(v1, "".into()).unwrap(); @@ -537,13 +504,13 @@ mod tests { data: Vec::new(), }.sign(&s0, Some(chain_id)); client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap(); - ::client::EngineClient::update_sealing(&*client); + client.update_sealing(); // Able to seal again. assert_eq!(client.chain_info().best_block_number, 3); // Check syncing. let sync_client = generate_dummy_client_with_spec_and_data(Spec::new_validator_safe_contract, 0, 0, &[]); - sync_client.engine().register_client(Arc::downgrade(&sync_client) as _); + 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(); } diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 4da8568c2..c00046e0e 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -77,6 +77,8 @@ pub struct TransactOptions { pub vm_tracer: V, /// Check transaction nonce before execution. pub check_nonce: bool, + /// Records the output from init contract calls. + pub output_from_init_contract: bool, } impl TransactOptions { @@ -86,6 +88,7 @@ impl TransactOptions { tracer, vm_tracer, check_nonce: true, + output_from_init_contract: false, } } @@ -94,6 +97,12 @@ impl TransactOptions { self.check_nonce = false; self } + + /// Saves the output from contract creation. + pub fn save_output_from_contract(mut self) -> Self { + self.output_from_init_contract = true; + self + } } impl TransactOptions { @@ -103,6 +112,7 @@ impl TransactOptions { tracer: trace::ExecutiveTracer::default(), vm_tracer: trace::ExecutiveVMTracer::toplevel(), check_nonce: true, + output_from_init_contract: false, } } } @@ -114,6 +124,7 @@ impl TransactOptions { tracer: trace::ExecutiveTracer::default(), vm_tracer: trace::NoopVMTracer, check_nonce: true, + output_from_init_contract: false, } } } @@ -125,6 +136,7 @@ impl TransactOptions { tracer: trace::NoopTracer, vm_tracer: trace::ExecutiveVMTracer::toplevel(), check_nonce: true, + output_from_init_contract: false, } } } @@ -136,6 +148,7 @@ impl TransactOptions { tracer: trace::NoopTracer, vm_tracer: trace::NoopVMTracer, check_nonce: true, + output_from_init_contract: false, } } } @@ -204,7 +217,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { pub fn transact(&'a mut self, t: &SignedTransaction, options: TransactOptions) -> Result where T: Tracer, V: VMTracer, { - self.transact_with_tracer(t, options.check_nonce, options.tracer, options.vm_tracer) + self.transact_with_tracer(t, options.check_nonce, options.output_from_init_contract, options.tracer, options.vm_tracer) } /// Execute a transaction in a "virtual" context. @@ -229,6 +242,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { &'a mut self, t: &SignedTransaction, check_nonce: bool, + output_from_create: bool, mut tracer: T, mut vm_tracer: V ) -> Result where T: Tracer, V: VMTracer { @@ -297,7 +311,8 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { data: None, call_type: CallType::None, }; - (self.create(params, &mut substate, &mut tracer, &mut vm_tracer), vec![]) + let mut out = if output_from_create { Some(vec![]) } else { None }; + (self.create(params, &mut substate, &mut out, &mut tracer, &mut vm_tracer), out.unwrap_or_else(Vec::new)) }, Action::Call(ref address) => { let params = ActionParams { @@ -490,6 +505,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { &mut self, params: ActionParams, substate: &mut Substate, + output: &mut Option, tracer: &mut T, vm_tracer: &mut V, ) -> vm::Result<(U256, ReturnData)> where T: Tracer, V: VMTracer { @@ -531,7 +547,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { let mut subvmtracer = vm_tracer.prepare_subtrace(params.code.as_ref().expect("two ways into create (Externalities::create and Executive::transact_with_tracer); both place `Some(...)` `code` in `params`; qed")); let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer) + self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(output.as_mut().or(trace_output.as_mut())), &mut subtracer, &mut subvmtracer) }; vm_tracer.done_subtrace(subvmtracer); @@ -540,7 +556,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> { Ok(ref res) => tracer.trace_create( trace_info, gas - res.gas_left, - trace_output, + trace_output.map(|data| output.as_ref().map(|out| out.to_vec()).unwrap_or(data)), created, subtracer.drain() ), @@ -701,7 +717,7 @@ mod tests { let (gas_left, _) = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() + ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(79_975)); @@ -759,7 +775,7 @@ mod tests { let (gas_left, _) = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() + ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(62_976)); @@ -926,7 +942,7 @@ mod tests { let (gas_left, _) = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.create(params.clone(), &mut substate, &mut tracer, &mut vm_tracer).unwrap() + ex.create(params.clone(), &mut substate, &mut None, &mut tracer, &mut vm_tracer).unwrap() }; assert_eq!(gas_left, U256::from(96_776)); @@ -1011,7 +1027,7 @@ mod tests { let (gas_left, _) = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap() + ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap() }; assert_eq!(gas_left, U256::from(62_976)); @@ -1062,7 +1078,7 @@ mod tests { { let mut ex = Executive::new(&mut state, &info, &engine); - ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer).unwrap(); + ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer).unwrap(); } assert_eq!(substate.contracts_created.len(), 1); @@ -1335,7 +1351,7 @@ mod tests { let result = { let mut ex = Executive::new(&mut state, &info, &engine); - ex.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) + ex.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer) }; match result { diff --git a/ethcore/src/externalities.rs b/ethcore/src/externalities.rs index 5054d4c08..5fc613844 100644 --- a/ethcore/src/externalities.rs +++ b/ethcore/src/externalities.rs @@ -224,7 +224,7 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E> let mut ex = Executive::from_parent(self.state, self.env_info, self.engine, self.depth, self.static_flag); // TODO: handle internal error separately - match ex.create(params, self.substate, self.tracer, self.vm_tracer) { + match ex.create(params, self.substate, &mut None, self.tracer, self.vm_tracer) { Ok((gas_left, _)) => { self.substate.contracts_created.push(address.clone()); ContractCreateResult::Created(address, gas_left) diff --git a/ethcore/src/header.rs b/ethcore/src/header.rs index 8dca493a7..aa94db036 100644 --- a/ethcore/src/header.rs +++ b/ethcore/src/header.rs @@ -261,13 +261,8 @@ impl Header { s.out() } - /// Get the SHA3 (Keccak) of this header, optionally `with_seal`. + /// Get the KECCAK (Keccak) of this header, optionally `with_seal`. pub fn rlp_keccak(&self, with_seal: Seal) -> H256 { keccak(self.rlp(with_seal)) } - - /// Encode the header, getting a type-safe wrapper around the RLP. - pub fn encoded(&self) -> ::encoded::Header { - ::encoded::Header::new(self.rlp(Seal::With)) - } } impl Decodable for Header { diff --git a/ethcore/src/service.rs b/ethcore/src/service.rs index 4e7e04341..b8f43f11c 100644 --- a/ethcore/src/service.rs +++ b/ethcore/src/service.rs @@ -116,7 +116,7 @@ impl ClientService { }); io_service.register_handler(client_io)?; - spec.engine.register_client(Arc::downgrade(&client) as _); + spec.engine.register_client(Arc::downgrade(&client)); let stop_guard = ::devtools::StopGuard::new(); run_ipc(ipc_path, client.clone(), snapshot.clone(), stop_guard.share()); diff --git a/ethcore/src/snapshot/tests/proof_of_authority.rs b/ethcore/src/snapshot/tests/proof_of_authority.rs index 7c00c8197..509ccb8fd 100644 --- a/ethcore/src/snapshot/tests/proof_of_authority.rs +++ b/ethcore/src/snapshot/tests/proof_of_authority.rs @@ -93,7 +93,7 @@ fn make_chain(accounts: Arc, blocks_beyond: usize, transitions: let mut cur_signers = vec![*RICH_ADDR]; { let engine = client.engine(); - engine.register_client(Arc::downgrade(&client) as _); + engine.register_client(Arc::downgrade(&client)); } { diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs index 727b08584..202daba13 100644 --- a/ethcore/src/spec/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -36,6 +36,7 @@ use factory::Factories; use header::{BlockNumber, Header}; use pod_state::*; use rlp::{Rlp, RlpStream}; +use state_db::StateDB; use state::{Backend, State, Substate}; use state::backend::Basic as BasicBackend; use trace::{NoopTracer, NoopVMTracer}; @@ -355,7 +356,7 @@ impl Spec { { let mut exec = Executive::new(&mut state, &env_info, self.engine.as_ref()); - if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) { + if let Err(e) = exec.create(params, &mut substate, &mut None, &mut NoopTracer, &mut NoopVMTracer) { warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e); } } @@ -464,7 +465,7 @@ impl Spec { } /// Ensure that the given state DB has the trie nodes in for the genesis state. - pub fn ensure_db_good(&self, db: T, factories: &Factories) -> Result { + pub fn ensure_db_good(&self, db: StateDB, factories: &Factories) -> Result { if db.as_hashdb().contains(&self.state_root()) { return Ok(db) } @@ -486,63 +487,6 @@ impl Spec { .and_then(|x| load_from(cache_dir, x).map_err(fmt)) } - /// initialize genesis epoch data, using in-memory database for - /// constructor. - pub fn genesis_epoch_data(&self) -> Result, String> { - use transaction::{Action, Transaction}; - use util::{journaldb, kvdb}; - - let genesis = self.genesis_header(); - - let factories = Default::default(); - let mut db = journaldb::new( - Arc::new(kvdb::in_memory(0)), - journaldb::Algorithm::Archive, - None, - ); - - self.ensure_db_good(BasicBackend(db.as_hashdb_mut()), &factories) - .map_err(|e| format!("Unable to initialize genesis state: {}", e))?; - - let call = |a, d| { - let mut db = db.boxed_clone(); - let env_info = ::evm::EnvInfo { - number: 0, - author: *genesis.author(), - timestamp: genesis.timestamp(), - difficulty: *genesis.difficulty(), - gas_limit: *genesis.gas_limit(), - last_hashes: Arc::new(Vec::new()), - gas_used: 0.into() - }; - - let from = Address::default(); - let tx = Transaction { - nonce: self.engine.account_start_nonce(0), - action: Action::Call(a), - gas: U256::from(50_000_000), // TODO: share with client. - gas_price: U256::default(), - value: U256::default(), - data: d, - }.fake_sign(from); - - let res = ::state::prove_transaction( - db.as_hashdb_mut(), - *genesis.state_root(), - &tx, - &*self.engine, - &env_info, - factories.clone(), - true, - ); - - res.map(|(out, proof)| (out, proof.into_iter().map(|x| x.into_vec()).collect())) - .ok_or_else(|| "Failed to prove call: insufficient state".into()) - }; - - self.engine.genesis_epoch_data(&genesis, &call) - } - /// Create a new Spec which conforms to the Frontier-era Morden chain except that it's a NullEngine consensus. pub fn new_test() -> Spec { load_bundled!("null_morden") } diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index 4539708ec..eb6deac23 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -212,7 +212,8 @@ pub fn check_proof( Err(_) => return ProvedExecution::BadProof, }; - match state.execute(env_info, engine, transaction, TransactOptions::with_no_tracing(), true) { + let options = TransactOptions::with_no_tracing().save_output_from_contract(); + match state.execute(env_info, engine, transaction, options, true) { Ok(executed) => ProvedExecution::Complete(executed), Err(ExecutionError::Internal(_)) => ProvedExecution::BadProof, Err(e) => ProvedExecution::Failed(e), @@ -246,7 +247,7 @@ pub fn prove_transaction( Err(_) => return None, }; - let options = TransactOptions::with_no_tracing().dont_check_nonce(); + let options = TransactOptions::with_no_tracing().dont_check_nonce().save_output_from_contract(); match state.execute(env_info, engine, transaction, options, virt) { Err(ExecutionError::Internal(_)) => None, Err(e) => { diff --git a/ethcore/src/tx_filter.rs b/ethcore/src/tx_filter.rs index f39b92451..0ba986608 100644 --- a/ethcore/src/tx_filter.rs +++ b/ethcore/src/tx_filter.rs @@ -21,7 +21,8 @@ use std::collections::HashMap; use std::collections::hash_map::Entry; use native_contracts::TransactAcl as Contract; use client::{BlockChainClient, BlockId, ChainNotify}; -use util::{Address, H256, Bytes}; +use util::{Address, Bytes}; +use bigint::hash::H256; use parking_lot::{Mutex, RwLock}; use futures::{self, Future}; use spec::CommonParams; diff --git a/js/package-lock.json b/js/package-lock.json index 0bc45f0c5..dba324568 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.8.18", + "version": "1.8.19", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/js/package.json b/js/package.json index 52415f95a..f48ea6699 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "1.8.18", + "version": "1.8.19", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", diff --git a/js/scripts/lint-i18n.js b/js/scripts/lint-i18n.js index d392353d0..b19851957 100644 --- a/js/scripts/lint-i18n.js +++ b/js/scripts/lint-i18n.js @@ -20,34 +20,57 @@ 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))); +const defaultValues = flatten(Object.assign({}, defaults, LANGUAGES)); +const defaultKeys = Object.keys(defaultValues); +const results = {}; 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; + const langResults = { found: [], missing: [], extras: [] }; - console.log(`*** Checking translations for ${lang}`); + console.warn(`*** Checking translations for ${lang}`); defaultKeys.forEach((key) => { if (messageKeys.includes(key)) { - found++; + langResults.found.push(key); } else { - missing++; - console.log(` Missing ${key}`); + langResults.missing.push(key); } }); messageKeys.forEach((key) => { if (!defaultKeys.includes(key)) { - extra++; - console.log(` Extra ${key}`); + langResults.extras.push(key); } }); - console.log(`Found ${found} keys, missing ${missing} keys, ${extra} extraneous keys\n`); + // Sort keys + langResults.extras.sort((kA, kB) => kA.localeCompare(kB)); + langResults.found.sort((kA, kB) => kA.localeCompare(kB)); + langResults.missing.sort((kA, kB) => kA.localeCompare(kB)); + + // Print to stderr the missing and extra keys + langResults.missing.forEach((key) => console.warn(` Missing ${key}`)); + langResults.extras.forEach((key) => console.warn(` Extra ${key}`)); + + results[lang] = langResults; + + console.warn(`Found ${langResults.found.length} keys, missing ${langResults.missing.length} keys, ${langResults.extras.length} extraneous keys\n`); }); + +const formattedResults = Object.keys(results) + .reduce((res, lang) => { + const { missing } = results[lang]; + + res[lang] = missing.map((key) => ({ + key, + default: defaultValues[key] + })); + + return res; + }, {}); + +process.stdout.write(JSON.stringify(formattedResults, null, 2) + '\n'); diff --git a/js/src/i18n/_default/account.js b/js/src/i18n/_default/account.js index 7c9d25520..3783ff368 100644 --- a/js/src/i18n/_default/account.js +++ b/js/src/i18n/_default/account.js @@ -18,12 +18,29 @@ export default { button: { delete: `delete`, edit: `edit`, + export: `export`, faucet: `Kovan ETH`, + forget: `forget`, password: `password`, shapeshift: `shapeshift`, transfer: `transfer`, verify: `verify` }, + export: { + info: `Export your account as a JSON file. Please enter the password linked with this account.`, + password: { + hint: `The password specified when creating this account`, + label: `Account password` + }, + setPassword: { + hint: `Enter password Here`, + label: `Password` + }, + title: `Export Account` + }, + external: { + confirmDelete: `Are you sure you want to remove the following external address from your account list?` + }, hardware: { confirmDelete: `Are you sure you want to remove the following hardware address from your account list?` }, diff --git a/js/src/i18n/_default/accounts.js b/js/src/i18n/_default/accounts.js index 2db3d5fd9..e90f0984f 100644 --- a/js/src/i18n/_default/accounts.js +++ b/js/src/i18n/_default/accounts.js @@ -16,10 +16,19 @@ export default { button: { + export: `export`, newAccount: `account`, newWallet: `wallet`, + restoreAccount: `restore`, vaults: `vaults` }, + export: { + button: { + cancel: `Cancel`, + export: `Export` + }, + title: `Export an Account` + }, summary: { minedBlock: `Mined at block #{blockNumber}` }, diff --git a/js/src/i18n/_default/application.js b/js/src/i18n/_default/application.js index f2e6be1ee..8f0de0dc1 100644 --- a/js/src/i18n/_default/application.js +++ b/js/src/i18n/_default/application.js @@ -22,8 +22,7 @@ export default { consensus: { capable: `Upgrade not required.`, capableUntil: `Upgrade required before #{blockNumber}`, - incapableSince: `Upgrade required since #{blockNumber}`, - unknown: `Upgrade status is unknown.` + incapableSince: `Upgrade required since #{blockNumber}` }, upgrade: `Upgrade` } diff --git a/js/src/i18n/_default/connection.js b/js/src/i18n/_default/connection.js index e51943178..4f38162e4 100644 --- a/js/src/i18n/_default/connection.js +++ b/js/src/i18n/_default/connection.js @@ -19,6 +19,7 @@ export default { connectingNode: `Connecting to the Parity Node. If this informational message persists, please ensure that your Parity node is running and reachable on the network.`, invalidToken: `invalid signer token`, noConnection: `Unable to make a connection to the Parity Secure API. To update your secure token or to generate a new one, run {newToken} and paste the generated token into the space below.`, + timestamp: `Ensure that both the Parity node and this machine connecting have computer clocks in-sync with each other and with a timestamp server, ensuring both successful token validation and block operations.`, token: { hint: `a generated token from Parity`, label: `secure token` diff --git a/js/src/i18n/_default/createAccount.js b/js/src/i18n/_default/createAccount.js index 8930fc7b4..791ee796b 100644 --- a/js/src/i18n/_default/createAccount.js +++ b/js/src/i18n/_default/createAccount.js @@ -21,8 +21,11 @@ export default { label: `address` }, phrase: { + backedUp: `Type "I have written down the phrase" below to confirm it is backed up.`, + backup: `Please back up the recovery phrase now. Make sure to keep it private and secure, it allows full and unlimited access to the account.`, + backupConfirm: `Type your recovery phrase now.`, hint: `the account recovery phrase`, - label: `owner recovery phrase (keep private and secure, it allows full and unlimited access to the account)` + label: `owner recovery phrase` } }, accountDetailsGeth: { @@ -50,14 +53,14 @@ export default { description: `Selecting your identity icon and specifying the password`, label: `New Account` }, - fromPhrase: { - description: `Recover using a previously stored recovery phrase and new password`, - label: `Recovery phrase` - }, fromPresale: { description: `Import an Ethereum presale wallet file with the original password`, label: `Presale wallet` }, + fromQr: { + description: `Attach an externally managed account via QR code`, + label: `External Account` + }, fromRaw: { description: `Enter a previously created raw private key with a new password`, label: `Private key` @@ -104,6 +107,21 @@ export default { label: `password` } }, + newQr: { + address: { + hint: `the network address for the account`, + label: `address` + }, + description: { + hint: `a description for the account`, + label: `account description` + }, + name: { + hint: `a descriptive name for the account`, + label: `account name` + }, + summary: `Use the built-in machine camera to scan to QR code of the account you wish to attach as an external account. External accounts are signed on the external device.` + }, rawKey: { hint: { hint: `(optional) a hint to help with remembering the password`, @@ -135,6 +153,9 @@ export default { hint: `a descriptive name for the account`, label: `account name` }, + passPhrase: { + error: `enter a recovery phrase` + }, password: { hint: `a strong, unique password`, label: `password` @@ -147,14 +168,27 @@ export default { hint: `the account recovery phrase`, label: `account recovery phrase` }, + warning: { + emptyPhrase: `The recovery phrase is empty. + This account can be recovered by anyone.`, + shortPhrase: `The recovery phrase is less than 11 words. + This account has not been generated by Parity and might be insecure. + Proceed with caution.`, + testnetEmptyPhrase: `The recovery phrase is empty. + This account can be recovered by anyone. + Proceed with caution.` + }, windowsKey: { label: `Key was created with Parity <1.4.5 on Windows` } }, title: { accountInfo: `account information`, + backupPhrase: `confirm recovery phrase`, createAccount: `create account`, createType: `creation type`, - importWallet: `import wallet` + importAccount: `import account`, + qr: `external account`, + restoreAccount: `restore account` } }; diff --git a/js/src/i18n/_default/createWallet.js b/js/src/i18n/_default/createWallet.js index eeb9e9a98..f32e95248 100644 --- a/js/src/i18n/_default/createWallet.js +++ b/js/src/i18n/_default/createWallet.js @@ -18,14 +18,9 @@ export default { button: { add: `Add`, cancel: `Cancel`, - close: `Close`, create: `Create`, done: `Done`, - next: `Next`, - sending: `Sending...` - }, - deployment: { - message: `The deployment is currently in progress` + next: `Next` }, details: { address: { @@ -73,21 +68,7 @@ export default { numOwners: `{numOwners} owners are required to confirm a transaction.`, owners: `The following are wallet owners` }, - rejected: { - message: `The deployment has been rejected`, - state: `The wallet will not be created. You can safely close this window.`, - title: `rejected` - }, - 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`, - waitingReceipt: `Waiting for the contract deployment transaction receipt` - }, steps: { - deployment: `wallet deployment`, details: `wallet details`, info: `wallet informaton`, type: `wallet type` diff --git a/js/src/i18n/_default/dapps.js b/js/src/i18n/_default/dapps.js index 9ed3415d1..514ef59c0 100644 --- a/js/src/i18n/_default/dapps.js +++ b/js/src/i18n/_default/dapps.js @@ -31,6 +31,9 @@ export default { } }, button: { + dapp: { + refresh: `refresh` + }, edit: `edit`, permissions: `permissions` }, diff --git a/js/src/i18n/_default/deployContract.js b/js/src/i18n/_default/deployContract.js index 0b5a05503..2436d540f 100644 --- a/js/src/i18n/_default/deployContract.js +++ b/js/src/i18n/_default/deployContract.js @@ -15,19 +15,12 @@ // along with Parity. If not, see . export default { - busy: { - title: `The deployment is currently in progress` - }, button: { cancel: `Cancel`, close: `Close`, create: `Create`, - done: `Done`, next: `Next` }, - completed: { - description: `Your contract has been deployed at` - }, details: { abi: { hint: `the abi of the contract to deploy or solc combined-output`, @@ -66,25 +59,9 @@ export default { parameters: { choose: `Choose the contract parameters` }, - rejected: { - description: `You can safely close this window, the contract deployment will not occur.`, - title: `The deployment has been rejected` - }, - 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`, - waitSigner: `Waiting for confirmation of the transaction in the Parity Secure Signer` - }, title: { - completed: `completed`, - deployment: `deployment`, details: `contract details`, extras: `extra information`, - failed: `deployment failed`, - parameters: `contract parameters`, - rejected: `rejected` + parameters: `contract parameters` } }; diff --git a/js/src/i18n/_default/errors.js b/js/src/i18n/_default/errors.js index 76fed24cd..1a9c3516a 100644 --- a/js/src/i18n/_default/errors.js +++ b/js/src/i18n/_default/errors.js @@ -19,6 +19,8 @@ export default { invalidKey: `the raw key needs to be hex, 64 characters in length and contain the prefix "0x"`, noFile: `select a valid wallet file to import`, noKey: `you need to provide the raw private key`, + noMatchBackupPhrase: `the supplied recovery phrase does not match`, noMatchPassword: `the supplied passwords does not match`, + noMatchPhraseBackedUp: `type "I have written down the phrase"`, noName: `you need to specify a valid name` }; diff --git a/js/src/i18n/_default/executeContract.js b/js/src/i18n/_default/executeContract.js index 011264d3f..205a1d4dc 100644 --- a/js/src/i18n/_default/executeContract.js +++ b/js/src/i18n/_default/executeContract.js @@ -15,14 +15,8 @@ // along with Parity. If not, see . export default { - busy: { - posted: `Your transaction has been posted to the network`, - title: `The function execution is in progress`, - waitAuth: `Waiting for authorization in the Parity Signer` - }, button: { cancel: `cancel`, - done: `done`, next: `next`, post: `post transaction`, prev: `prev` @@ -44,15 +38,8 @@ export default { label: `function to execute` } }, - rejected: { - state: `You can safely close this window, the function execution will not occur.`, - title: `The execution has been rejected` - }, steps: { advanced: `advanced options`, - complete: `complete`, - rejected: `rejected`, - sending: `sending`, transfer: `function details` } }; diff --git a/js/src/i18n/_default/firstRun.js b/js/src/i18n/_default/firstRun.js index 5f41fa9c4..c6e6a640e 100644 --- a/js/src/i18n/_default/firstRun.js +++ b/js/src/i18n/_default/firstRun.js @@ -20,6 +20,7 @@ export default { create: `Create`, next: `Next`, print: `Print Phrase`, + restart: `Start Over`, skip: `Skip` }, completed: { @@ -28,6 +29,7 @@ export default { }, title: { completed: `completed`, + confirmation: `confirmation`, newAccount: `new account`, recovery: `recovery`, terms: `terms`, diff --git a/js/src/i18n/_default/index.js b/js/src/i18n/_default/index.js index 687e558dc..341663c92 100644 --- a/js/src/i18n/_default/index.js +++ b/js/src/i18n/_default/index.js @@ -41,11 +41,14 @@ export home from './home'; export loadContract from './loadContract'; export parityBar from './parityBar'; export passwordChange from './passwordChange'; +export peers from './peers'; +export requests from './requests'; export saveContract from './saveContract'; export settings from './settings'; export shapeshift from './shapeshift'; export signer from './signer'; export status from './status'; +export syncWarning from './syncWarning'; export tabBar from './tabBar'; export transfer from './transfer'; export txEditor from './txEditor'; diff --git a/js/src/i18n/_default/peers.js b/js/src/i18n/_default/peers.js new file mode 100644 index 000000000..2e66aaff0 --- /dev/null +++ b/js/src/i18n/_default/peers.js @@ -0,0 +1,46 @@ +// 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 { + acceptNonReserved: { + label: `Accept non-reserved` + }, + acceptNonReservedPeers: { + success: `Accepting non-reserved peers` + }, + addReserved: { + label: `Add reserved` + }, + dropNonReserved: { + label: `Drop non-reserved` + }, + dropNonReservedPeers: { + success: `Dropping non-reserved peers` + }, + form: { + action: { + label: `{add, select, true {Add} false {}}{remove, select, true {Remove} false {}}`, + success: `Successfully {add, select, true {added} false {}}{remove, select, true {removed} false {}} a reserved peer` + }, + cancel: { + label: `Cancel` + }, + label: `Peer enode URL` + }, + removeReserved: { + label: `Remove reserved` + } +}; diff --git a/js/src/i18n/_default/requests.js b/js/src/i18n/_default/requests.js new file mode 100644 index 000000000..328d52156 --- /dev/null +++ b/js/src/i18n/_default/requests.js @@ -0,0 +1,24 @@ +// 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 { + status: { + error: `An error occured:`, + transactionMined: `Transaction mined at block #{blockNumber} ({blockHeight} blocks ago)`, + transactionSent: `Transaction sent to network with hash`, + waitingForSigner: `Waiting for authorization in the Parity Signer` + } +}; diff --git a/js/src/i18n/_default/settings.js b/js/src/i18n/_default/settings.js index ea447e4a9..aef412b48 100644 --- a/js/src/i18n/_default/settings.js +++ b/js/src/i18n/_default/settings.js @@ -36,7 +36,7 @@ export default { }, languages: { hint: `the language this interface is displayed with`, - label: `UI language` + label: `language` }, loglevels: `Choose the different logs level.`, modes: { @@ -51,7 +51,7 @@ export default { label: `parity` }, proxy: { - details_0: `Instead of accessing Parity via the IP address and port, you will be able to access it via the .parity subdomain, by visiting {homeProxy}. To setup subdomain-based routing, you need to add the relevant proxy entries to your browser,`, + details_0: `Instead of accessing Parity via the IP address and port, you will be able to access it via the .web3.site subdomain, by visiting {homeProxy}. To setup subdomain-based routing, you need to add the relevant proxy entries to your browser,`, details_1: `To learn how to configure the proxy, instructions are provided for {windowsLink}, {macOSLink} or {ubuntuLink}.`, details_macos: `macOS`, details_ubuntu: `Ubuntu`, @@ -88,13 +88,12 @@ export default { description: `The secure transaction management area of the application where you can approve any outgoing transactions made from the application as well as those placed into the queue by decentralized applications.`, label: `Signer` }, - status: { - description: `See how the Parity node is performing in terms of connections to the network, logs from the actual running instance and details of mining (if enabled and configured).`, - label: `Status` - }, label: `views`, home: { label: `Home` + }, + status: { + label: `Status` } }, label: `settings` diff --git a/js/src/i18n/_default/signer.js b/js/src/i18n/_default/signer.js index 3f8615c52..6ba8fde46 100644 --- a/js/src/i18n/_default/signer.js +++ b/js/src/i18n/_default/signer.js @@ -15,6 +15,13 @@ // along with Parity. If not, see . export default { + decryptRequest: { + request: `A request to decrypt data using your account:`, + state: { + confirmed: `Confirmed`, + rejected: `Rejected` + } + }, embedded: { noPending: `There are currently no pending requests awaiting your confirmation` }, @@ -29,7 +36,7 @@ export default { requestOrigin: { dapp: `by a dapp at {url}`, ipc: `via IPC session`, - rpc: `via RPC {rpc}`, + rpc: `via RPC {url}`, signerCurrent: `via current tab`, signerUI: `via UI session`, unknownInterface: `via unknown interface`, @@ -38,10 +45,14 @@ export default { }, requestsPage: { noPending: `There are no requests requiring your confirmation.`, - pendingTitle: `Pending Requests`, + pendingTitle: `Pending Signature Authorization`, queueTitle: `Local Transactions` }, sending: { + external: { + scanSigned: `Scan the QR code of the signed transaction from your external device`, + scanTx: `Please scan the transaction QR on your external device` + }, hardware: { confirm: `Please confirm the transaction on your attached hardware device`, connect: `Please attach your hardware device before confirming the transaction` @@ -53,6 +64,10 @@ export default { confirmed: `Confirmed`, rejected: `Rejected` }, + tooltip: { + data: `Data: {data}`, + hash: `Hash to be signed: {hashToSign}` + }, unknownBinary: `(Unknown binary data)`, warning: `WARNING: This consequences of doing this may be grave. Confirm the request only if you are sure.` }, @@ -65,7 +80,8 @@ export default { txPendingConfirm: { buttons: { confirmBusy: `Confirming...`, - confirmRequest: `Confirm Request` + confirmRequest: `Confirm Request`, + scanSigned: `Scan Signed QR` }, errors: { invalidWallet: `Given wallet file is invalid.` diff --git a/js/src/i18n/_default/status.js b/js/src/i18n/_default/status.js index 536c0ff90..1874a4044 100644 --- a/js/src/i18n/_default/status.js +++ b/js/src/i18n/_default/status.js @@ -20,6 +20,14 @@ export default { stopped: `Refresh and display of logs from Parity is currently stopped via the UI, start it to see the latest updates.`, title: `Node Logs` }, + health: { + no: `no`, + peers: `Connected Peers`, + sync: `Chain Synchronized`, + time: `Time Synchronized`, + title: `Node Health`, + yes: `yes` + }, miningSettings: { input: { author: { @@ -41,13 +49,25 @@ export default { }, title: `mining settings` }, + peers: { + table: { + header: { + caps: `Capabilities`, + ethDiff: `Difficulty (ETH)`, + ethHeader: `Header (ETH)`, + id: `ID`, + name: `Name`, + remoteAddress: `Remote Address` + } + }, + title: `network peers` + }, status: { hashrate: `{hashrate} H/s`, input: { chain: `chain`, enode: `enode`, no: `no`, - peers: `peers`, port: `network port`, rpcEnabled: `rpc enabled`, rpcInterface: `rpc interface`, diff --git a/js/src/i18n/_default/details_windows.js b/js/src/i18n/_default/syncWarning.js similarity index 83% rename from js/src/i18n/_default/details_windows.js rename to js/src/i18n/_default/syncWarning.js index fcc570066..109a7d6ca 100644 --- a/js/src/i18n/_default/details_windows.js +++ b/js/src/i18n/_default/syncWarning.js @@ -14,4 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -export default `Windows`; +export default { + dontShowAgain: { + label: `Do not show this warning again` + }, + understandBtn: { + label: `I understand` + } +}; diff --git a/js/src/i18n/_default/transfer.js b/js/src/i18n/_default/transfer.js index 3207f5b06..16c7c27a2 100644 --- a/js/src/i18n/_default/transfer.js +++ b/js/src/i18n/_default/transfer.js @@ -24,7 +24,6 @@ export default { buttons: { back: `Back`, cancel: `Cancel`, - close: `Close`, next: `Next`, send: `Send` }, @@ -51,10 +50,6 @@ export default { 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 d84e7bd13..5d04208ad 100644 --- a/js/src/i18n/_default/ui.js +++ b/js/src/i18n/_default/ui.js @@ -66,6 +66,18 @@ export default { errors: { close: `close` }, + features: { + defaults: { + i18n: { + desc: `Allows changing the default interface language`, + name: `Language Selection` + }, + logging: { + desc: `Allows changing of the log levels for various components`, + name: `Logging Level Selection` + } + } + }, fileSelect: { defaultLabel: `Drop a file here, or click to select a file to upload` }, @@ -80,8 +92,8 @@ export default { }, methodDecoding: { condition: { - block: `, {historic, select, true {Submitted} false {Submission}} at block {blockNumber}`, - time: `, {historic, select, true {Submitted} false {Submission}} at {timestamp}` + block: `{historic, select, true {Will be submitted} false {To be submitted}} at block {blockNumber}`, + time: `{historic, select, true {Will be submitted} false {To be submitted}} {timestamp}` }, deploy: { address: `Deployed a contract at address`, @@ -101,7 +113,7 @@ export default { 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}}}}` + info: `{historic, select, true {Executed} false {Will execute}} the {method} function on the contract {address} {showEth, select, true {transferring {ethValue}} false {}} {showInputs, select, false {} true {passing the following {inputLength, plural, one {parameter} other {parameters}}}}` }, token: { transfer: `{historic, select, true {Transferred} false {Will transfer}} {value} to {address}` @@ -131,6 +143,27 @@ export default { posted: `The transaction has been posted to the network with a hash of {hashLink}`, waiting: `waiting for confirmations` }, + txList: { + txRow: { + cancel: `Cancel`, + cancelWarning: `Warning: Editing or Canceling the transaction may not succeed!`, + canceled: `Canceled`, + edit: `Edit`, + editing: `Editing`, + pendingStatus: { + blocksLeft: `{blockNumber} blocks left`, + time: `{time} left` + }, + scheduled: `Scheduled`, + submitting: `Pending`, + verify: { + cancelEditCancel: `Cancel`, + cancelEditEdit: `Edit`, + confirm: `Are you sure?`, + nevermind: `Nevermind` + } + } + }, vaultSelect: { hint: `the vault this account is attached to`, label: `associated vault` diff --git a/js/src/i18n/_default/walletSettings.js b/js/src/i18n/_default/walletSettings.js index ddeae3798..e3cae3e3f 100644 --- a/js/src/i18n/_default/walletSettings.js +++ b/js/src/i18n/_default/walletSettings.js @@ -22,8 +22,12 @@ export default { cancel: `Cancel`, close: `Close`, next: `Next`, - send: `Send`, - sending: `Sending...` + send: `Send` + }, + changeOwner: { + labelFrom: `From`, + labelTo: `To`, + title: `Change Owner` }, changes: { modificationString: `For your modifications to be taken into account, @@ -62,7 +66,6 @@ export default { 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 index fc1100b77..62f83dd4d 100644 --- a/js/src/i18n/_default/writeContract.js +++ b/js/src/i18n/_default/writeContract.js @@ -37,7 +37,7 @@ export default { params: `An error occurred with the following description` }, input: { - abi: `ABI Interface`, + abi: `ABI Definition`, code: `Bytecode`, metadata: `Metadata`, swarm: `Swarm Metadata Hash` diff --git a/js/src/i18n/store.js b/js/src/i18n/store.js index f2506950e..79bc48fe6 100644 --- a/js/src/i18n/store.js +++ b/js/src/i18n/store.js @@ -33,8 +33,8 @@ import zhHantTWMessages from './zh-Hant-TW'; let instance = null; -const LANGUAGES = flatten({ languages }); -const MESSAGES = { +export const LANGUAGES = flatten({ languages }); +export const MESSAGES = { de: Object.assign(flatten(deMessages), LANGUAGES), en: Object.assign(flatten(enMessages), LANGUAGES), nl: Object.assign(flatten(nlMessages), LANGUAGES), @@ -75,8 +75,3 @@ export default class Store { return instance; } } - -export { - LANGUAGES, - MESSAGES -}; diff --git a/js/src/modals/ExportAccount/exportAccount.js b/js/src/modals/ExportAccount/exportAccount.js index 48faaaec3..eddd23cd6 100644 --- a/js/src/modals/ExportAccount/exportAccount.js +++ b/js/src/modals/ExportAccount/exportAccount.js @@ -58,7 +58,7 @@ class ExportAccount extends Component { key='cancel' label={ } @@ -70,7 +70,7 @@ class ExportAccount extends Component { key='execute' label={ } @@ -81,7 +81,7 @@ class ExportAccount extends Component { open title={ } diff --git a/js/src/modals/ExportAccount/exportInput/exportInput.js b/js/src/modals/ExportAccount/exportInput/exportInput.js index 07673b29b..619166add 100644 --- a/js/src/modals/ExportAccount/exportInput/exportInput.js +++ b/js/src/modals/ExportAccount/exportInput/exportInput.js @@ -34,13 +34,13 @@ export default class ExportInput extends Component { type='password' label={ } hint={ } diff --git a/js/src/ui/Features/defaults.js b/js/src/ui/Features/defaults.js index 1e85c398e..62ce1fd99 100644 --- a/js/src/ui/Features/defaults.js +++ b/js/src/ui/Features/defaults.js @@ -30,17 +30,17 @@ const FEATURES = { const DEFAULTS = { [FEATURES.LANGUAGE]: { - mode: MODES.TESTING, + mode: MODES.PRODUCTION, name: ( ), description: ( ) }, @@ -49,13 +49,13 @@ const DEFAULTS = { name: ( ), description: ( ) } diff --git a/js/src/ui/LanguageSelector/languageSelector.js b/js/src/ui/LanguageSelector/languageSelector.js index 369ca6f28..e8f24e7f6 100644 --- a/js/src/ui/LanguageSelector/languageSelector.js +++ b/js/src/ui/LanguageSelector/languageSelector.js @@ -45,7 +45,7 @@ export default class LanguageSelector extends Component { label={ } value={ this.store.locale } diff --git a/js/src/ui/TxList/TxRow/txRow.js b/js/src/ui/TxList/TxRow/txRow.js index a28736ddb..fc40fc596 100644 --- a/js/src/ui/TxList/TxRow/txRow.js +++ b/js/src/ui/TxList/TxRow/txRow.js @@ -291,7 +291,7 @@ class TxRow extends Component {
diff --git a/js/src/views/Account/account.js b/js/src/views/Account/account.js index fdccac793..a475e3a6b 100644 --- a/js/src/views/Account/account.js +++ b/js/src/views/Account/account.js @@ -370,14 +370,14 @@ class Account extends Component { onDeny={ this.exportClose } title={ } >
@@ -388,13 +388,13 @@ class Account extends Component { type='password' hint={ } label={ } diff --git a/js/src/views/Settings/Parity/parity.js b/js/src/views/Settings/Parity/parity.js index 7de9b8061..9cf2c4f86 100644 --- a/js/src/views/Settings/Parity/parity.js +++ b/js/src/views/Settings/Parity/parity.js @@ -46,6 +46,8 @@ export default class Parity extends Component { } > + +
@@ -58,10 +60,10 @@ export default class Parity extends Component {
{ this.renderChains() } { this.renderModes() } -
+ { this.renderLogsConfig() } ); diff --git a/parity/blockchain.rs b/parity/blockchain.rs index f21364214..eee785102 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -208,9 +208,7 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024; config.queue.verifier_settings = cmd.verifier_settings; - // TODO: could epoch signals be avilable at the end of the file? - let fetch = ::light::client::fetch::unavailable(); - let service = LightClientService::start(config, &spec, fetch, &client_path, cache) + let service = LightClientService::start(config, &spec, &client_path, cache) .map_err(|e| format!("Failed to start client: {}", e))?; // free up the spec in memory. diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index b5b949cc7..e181bf88d 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -17,387 +17,949 @@ #[macro_use] mod usage; mod presets; -use dir; usage! { { - // Commands - cmd_daemon: bool, - cmd_wallet: bool, - cmd_account: bool, - cmd_new: bool, - cmd_list: bool, - cmd_export: bool, - cmd_blocks: bool, - cmd_state: bool, - cmd_import: bool, - cmd_signer: bool, - cmd_new_token: bool, - cmd_sign: bool, - cmd_reject: bool, - cmd_snapshot: bool, - cmd_restore: bool, - cmd_ui: bool, - cmd_dapp: bool, - cmd_tools: bool, - cmd_hash: bool, - cmd_kill: bool, - cmd_db: bool, + // CLI subcommands + // Subcommands must start with cmd_ and have '_' in place of '-' + // Sub-subcommands must start with the name of the subcommand + // Arguments must start with arg_ - // Arguments - arg_pid_file: String, - arg_file: Option, - arg_path: Vec, - arg_id: Option, + CMD cmd_ui { + "Manage ui", + } - // Flags - // -- Legacy Options - flag_geth: bool, - flag_testnet: bool, - flag_import_geth_keys: bool, - flag_datadir: Option, - flag_networkid: Option, - flag_peers: Option, - flag_nodekey: Option, - flag_nodiscover: bool, - flag_jsonrpc: bool, - flag_jsonrpc_off: bool, - flag_webapp: bool, - flag_dapps_off: bool, - flag_rpc: bool, - flag_rpcaddr: Option, - flag_rpcport: Option, - flag_rpcapi: Option, - flag_rpccorsdomain: Option, - flag_ipcdisable: bool, - flag_ipc_off: bool, - flag_ipcapi: Option, - flag_ipcpath: Option, - flag_gasprice: Option, - flag_etherbase: Option, - flag_extradata: Option, - flag_cache: Option, + CMD cmd_dapp + { + "Manage dapps", - // -- Miscellaneous Options - flag_version: bool, - flag_no_config: bool, + ARG arg_dapp_path: (Option) = None, + "", + "Path to the dapps", + } + + CMD cmd_daemon + { + "Use Parity as a daemon", + + ARG arg_daemon_pid_file: (Option) = None, + "", + "Path to the pid file", + } + + CMD cmd_account + { + "Manage accounts", + + CMD cmd_account_new { + "Create a new acount", + + ARG arg_account_new_password: (Option) = None, + "--password=[FILE]", + "Path to the password file", + } + + CMD cmd_account_list { + "List existing accounts", + } + + CMD cmd_account_import + { + "Import account", + + ARG arg_account_import_path : (Option>) = None, + "...", + "Path to the accounts", + } + } + + CMD cmd_wallet + { + "Manage wallet", + + CMD cmd_wallet_import + { + "Import wallet", + + ARG arg_wallet_import_password: (Option) = None, + "--password=[FILE]", + "Path to the password file", + + ARG arg_wallet_import_path: (Option) = None, + "", + "Path to the wallet", + } + } + + CMD cmd_import + { + "Import blockchain", + + ARG arg_import_format: (Option) = None, + "--format=[FORMAT]", + "Import in a given format. FORMAT must be either 'hex' or 'binary'. (default: auto)", + + ARG arg_import_file: (Option) = None, + "[FILE]", + "Path to the file to import from", + } + + CMD cmd_export + { + "Export blockchain", + + CMD cmd_export_blocks + { + "Export blocks", + + ARG arg_export_blocks_format: (Option) = None, + "--format=[FORMAT]", + "Export in a given format. FORMAT must be either 'hex' or 'binary'. (default: binary)", + + ARG arg_export_blocks_from: (String) = "1", + "--from=[BLOCK]", + "Export from block BLOCK, which may be an index or hash.", + + ARG arg_export_blocks_to: (String) = "latest", + "--to=[BLOCK]", + "Export to (including) block BLOCK, which may be an index, hash or latest.", + + ARG arg_export_blocks_file: (Option) = None, + "[FILE]", + "Path to the exported file", + } + + CMD cmd_export_state + { + "Export state", + + FLAG flag_export_state_no_storage: (bool) = false, + "--no-storage", + "Don't export account storage.", + + FLAG flag_export_state_no_code: (bool) = false, + "--no-code", + "Don't export account code.", + + ARG arg_export_state_min_balance: (Option) = None, + "--min-balance=[WEI]", + "Don't export accounts with balance less than specified.", + + ARG arg_export_state_max_balance: (Option) = None, + "--max-balance=[WEI]", + "Don't export accounts with balance greater than specified.", + + ARG arg_export_state_at: (String) = "latest", + "--at=[BLOCK]", + "Take a snapshot at the given block, which may be an index, hash, or latest. Note that taking snapshots at non-recent blocks will only work with --pruning archive", + + ARG arg_export_state_format: (Option) = None, + "--format=[FORMAT]", + "Export in a given format. FORMAT must be either 'hex' or 'binary'. (default: binary)", + + ARG arg_export_state_file: (Option) = None, + "[FILE]", + "Path to the exported file", + } + } + + CMD cmd_signer + { + "Manage signer", + + CMD cmd_signer_new_token { + "Generate new token", + } + + CMD cmd_signer_list { + "List", + } + + CMD cmd_signer_sign + { + "Sign", + + ARG arg_signer_sign_password: (Option) = None, + "--password=[FILE]", + "Path to the password file", + + ARG arg_signer_sign_id: (Option) = None, + "[ID]", + "ID", + } + + CMD cmd_signer_reject + { + "Reject", + + ARG arg_signer_reject_id: (Option) = None, + "", + "ID", + } + } + + CMD cmd_snapshot + { + "Make a snapshot of the database", + + ARG arg_snapshot_at: (String) = "latest", + "--at=[BLOCK]", + "Take a snapshot at the given block, which may be an index, hash, or latest. Note that taking snapshots at non-recent blocks will only work with --pruning archive", + + ARG arg_snapshot_file: (Option) = None, + "", + "Path to the file to export to", + } + + CMD cmd_restore + { + "Restore database from snapshot", + + ARG arg_restore_file: (Option) = None, + "[FILE]", + "Path to the file to restore from", + } + + CMD cmd_tools + { + "Tools", + + CMD cmd_tools_hash + { + "Hash a file", + + ARG arg_tools_hash_file: (Option) = None, + "", + "File", + } + } + + CMD cmd_db + { + "Manage the database representing the state of the blockchain on this system", + + CMD cmd_db_kill { + "Clean the database", + } + } } { - // -- Operating Options - flag_mode: String = "last", or |c: &Config| otry!(c.parity).mode.clone(), - flag_mode_timeout: u64 = 300u64, or |c: &Config| otry!(c.parity).mode_timeout.clone(), - flag_mode_alarm: u64 = 3600u64, or |c: &Config| otry!(c.parity).mode_alarm.clone(), - flag_auto_update: String = "critical", or |c: &Config| otry!(c.parity).auto_update.clone(), - flag_release_track: String = "current", or |c: &Config| otry!(c.parity).release_track.clone(), - flag_public_node: bool = false, or |c: &Config| otry!(c.parity).public_node.clone(), - flag_no_download: bool = false, or |c: &Config| otry!(c.parity).no_download.clone(), - flag_no_consensus: bool = false, or |c: &Config| otry!(c.parity).no_consensus.clone(), - flag_chain: String = "foundation", or |c: &Config| otry!(c.parity).chain.clone(), - flag_keys_path: String = "$BASE/keys", or |c: &Config| otry!(c.parity).keys_path.clone(), - flag_identity: String = "", or |c: &Config| otry!(c.parity).identity.clone(), - flag_light: bool = false, or |c: &Config| otry!(c.parity).light, - flag_no_persistent_txqueue: bool = false, - or |c: &Config| otry!(c.parity).no_persistent_txqueue, + // Flags and arguments + ["Operating Options"] + FLAG flag_public_node: (bool) = false, or |c: &Config| otry!(c.parity).public_node.clone(), + "--public-node", + "Start Parity as a public web server. Account storage and transaction signing will be delegated to the UI.", - // -- Convenience Options - flag_config: String = "$BASE/config.toml", or |_| None, - flag_ports_shift: u16 = 0u16, - or |c: &Config| otry!(c.misc).ports_shift, - flag_unsafe_expose: bool = false, - or |c: &Config| otry!(c.misc).unsafe_expose, + FLAG flag_no_download: (bool) = false, or |c: &Config| otry!(c.parity).no_download.clone(), + "--no-download", + "Normally new releases will be downloaded ready for updating. This disables it. Not recommended.", - // -- Account Options - flag_unlock: Option = None, - or |c: &Config| otry!(c.account).unlock.as_ref().map(|vec| Some(vec.join(","))), - flag_password: Vec = Vec::new(), - or |c: &Config| otry!(c.account).password.clone(), - flag_keys_iterations: u32 = 10240u32, - or |c: &Config| otry!(c.account).keys_iterations.clone(), - flag_no_hardware_wallets: bool = false, - or |c: &Config| otry!(c.account).disable_hardware.clone(), - flag_fast_unlock: bool = false, - or |c: &Config| otry!(c.account).fast_unlock.clone(), + FLAG flag_no_consensus: (bool) = false, or |c: &Config| otry!(c.parity).no_consensus.clone(), + "--no-consensus", + "Force the binary to run even if there are known issues regarding consensus. Not recommended.", + FLAG flag_light: (bool) = false, or |c: &Config| otry!(c.parity).light, + "--light", + "Experimental: run in light client mode. Light clients synchronize a bare minimum of data and fetch necessary data on-demand from the network. Much lower in storage, potentially higher in bandwidth. Has no effect with subcommands.", - flag_force_ui: bool = false, - or |c: &Config| otry!(c.ui).force.clone(), - flag_no_ui: bool = false, - or |c: &Config| otry!(c.ui).disable.clone(), - flag_ui_port: u16 = 8180u16, - or |c: &Config| otry!(c.ui).port.clone(), - flag_ui_interface: String = "local", - or |c: &Config| otry!(c.ui).interface.clone(), - flag_ui_hosts: String = "none", - or |c: &Config| otry!(c.ui).hosts.as_ref().map(|vec| vec.join(",")), - flag_ui_path: String = "$BASE/signer", - or |c: &Config| otry!(c.ui).path.clone(), - // NOTE [todr] For security reasons don't put this to config files - flag_ui_no_validation: bool = false, or |_| None, + FLAG flag_force_direct: (bool) = false, or |_| None, + "--force-direct", + "Run the originally installed version of Parity, ignoring any updates that have since been installed.", - // -- Networking Options - flag_no_warp: bool = false, - or |c: &Config| otry!(c.network).warp.clone().map(|w| !w), - flag_port: u16 = 30303u16, - or |c: &Config| otry!(c.network).port.clone(), - flag_min_peers: u16 = 25u16, - or |c: &Config| otry!(c.network).min_peers.clone(), - flag_max_peers: u16 = 50u16, - or |c: &Config| otry!(c.network).max_peers.clone(), - flag_max_pending_peers: u16 = 64u16, - or |c: &Config| otry!(c.network).max_pending_peers.clone(), - flag_snapshot_peers: u16 = 0u16, - or |c: &Config| otry!(c.network).snapshot_peers.clone(), - flag_nat: String = "any", - or |c: &Config| otry!(c.network).nat.clone(), - flag_allow_ips: String = "all", - or |c: &Config| otry!(c.network).allow_ips.clone(), - flag_network_id: Option = None, - or |c: &Config| otry!(c.network).id.clone().map(Some), - flag_bootnodes: Option = None, - or |c: &Config| otry!(c.network).bootnodes.as_ref().map(|vec| Some(vec.join(","))), - flag_no_discovery: bool = false, - or |c: &Config| otry!(c.network).discovery.map(|d| !d).clone(), - flag_node_key: Option = None, - or |c: &Config| otry!(c.network).node_key.clone().map(Some), - flag_reserved_peers: Option = None, - or |c: &Config| otry!(c.network).reserved_peers.clone().map(Some), - flag_reserved_only: bool = false, - or |c: &Config| otry!(c.network).reserved_only.clone(), - flag_no_ancient_blocks: bool = false, or |_| None, - flag_no_serve_light: bool = false, - or |c: &Config| otry!(c.network).no_serve_light.clone(), + ARG arg_mode: (String) = "last", or |c: &Config| otry!(c.parity).mode.clone(), + "--mode=[MODE]", + "Set the operating mode. MODE can be one of: + last - Uses the last-used mode, active if none. + active - Parity continuously syncs the chain. + passive - Parity syncs initially, then sleeps and wakes regularly to resync. + dark - Parity syncs only when the RPC is active. + offline - Parity doesn't sync.", - // -- API and Console Options - // RPC - flag_no_jsonrpc: bool = false, - or |c: &Config| otry!(c.rpc).disable.clone(), - flag_jsonrpc_port: u16 = 8545u16, - or |c: &Config| otry!(c.rpc).port.clone(), - flag_jsonrpc_interface: String = "local", - or |c: &Config| otry!(c.rpc).interface.clone(), - flag_jsonrpc_cors: Option = None, - or |c: &Config| otry!(c.rpc).cors.clone().map(Some), - flag_jsonrpc_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore,shh,shh_pubsub", - or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")), - flag_jsonrpc_hosts: String = "none", - or |c: &Config| otry!(c.rpc).hosts.as_ref().map(|vec| vec.join(",")), - flag_jsonrpc_server_threads: Option = None, - or |c: &Config| otry!(c.rpc).server_threads.map(Some), - flag_jsonrpc_threads: usize = 0usize, - or |c: &Config| otry!(c.rpc).processing_threads, + ARG arg_mode_timeout: (u64) = 300u64, or |c: &Config| otry!(c.parity).mode_timeout.clone(), + "--mode-timeout=[SECS]", + "Specify the number of seconds before inactivity timeout occurs when mode is dark or passive", - // WS - flag_no_ws: bool = false, - or |c: &Config| otry!(c.websockets).disable.clone(), - flag_ws_port: u16 = 8546u16, - or |c: &Config| otry!(c.websockets).port.clone(), - flag_ws_interface: String = "local", - or |c: &Config| otry!(c.websockets).interface.clone(), - flag_ws_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore,shh,shh_pubsub", - or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")), - flag_ws_origins: String = "chrome-extension://*,moz-extension://*", - or |c: &Config| otry!(c.websockets).origins.as_ref().map(|vec| vec.join(",")), - flag_ws_hosts: String = "none", - or |c: &Config| otry!(c.websockets).hosts.as_ref().map(|vec| vec.join(",")), + ARG arg_mode_alarm: (u64) = 3600u64, or |c: &Config| otry!(c.parity).mode_alarm.clone(), + "--mode-alarm=[SECS]", + "Specify the number of seconds before auto sleep reawake timeout occurs when mode is passive", - // IPC - flag_no_ipc: bool = false, - or |c: &Config| otry!(c.ipc).disable.clone(), - flag_ipc_path: String = if cfg!(windows) { r"\\.\pipe\jsonrpc.ipc" } else { "$BASE/jsonrpc.ipc" }, - or |c: &Config| otry!(c.ipc).path.clone(), - flag_ipc_apis: String = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,traces,rpc,secretstore,shh,shh_pubsub", - or |c: &Config| otry!(c.ipc).apis.as_ref().map(|vec| vec.join(",")), + ARG arg_auto_update: (String) = "critical", or |c: &Config| otry!(c.parity).auto_update.clone(), + "--auto-update=[SET]", + "Set a releases set to automatically update and install. + all - All updates in the our release track. + critical - Only consensus/security updates. + none - No updates will be auto-installed.", - // DAPPS - flag_no_dapps: bool = false, - or |c: &Config| otry!(c.dapps).disable.clone(), - flag_dapps_path: String = "$BASE/dapps", - or |c: &Config| otry!(c.dapps).path.clone(), + ARG arg_release_track: (String) = "current", or |c: &Config| otry!(c.parity).release_track.clone(), + "--release-track=[TRACK]", + "Set which release track we should use for updates. + stable - Stable releases. + beta - Beta releases. + nightly - Nightly releases (unstable). + testing - Testing releases (do not use). + current - Whatever track this executable was released on", - // Secret Store - flag_no_secretstore: bool = false, - or |c: &Config| otry!(c.secretstore).disable.clone(), - flag_no_secretstore_http: bool = false, - or |c: &Config| otry!(c.secretstore).disable_http.clone(), - flag_no_secretstore_acl_check: bool = false, - or |c: &Config| otry!(c.secretstore).disable_acl_check.clone(), - flag_secretstore_secret: Option = None, - or |c: &Config| otry!(c.secretstore).self_secret.clone().map(Some), - flag_secretstore_nodes: String = "", - or |c: &Config| otry!(c.secretstore).nodes.as_ref().map(|vec| vec.join(",")), - flag_secretstore_interface: String = "local", - or |c: &Config| otry!(c.secretstore).interface.clone(), - flag_secretstore_port: u16 = 8083u16, - or |c: &Config| otry!(c.secretstore).port.clone(), - flag_secretstore_http_interface: String = "local", - or |c: &Config| otry!(c.secretstore).http_interface.clone(), - flag_secretstore_http_port: u16 = 8082u16, - or |c: &Config| otry!(c.secretstore).http_port.clone(), - flag_secretstore_path: String = "$BASE/secretstore", - or |c: &Config| otry!(c.secretstore).path.clone(), + ARG arg_chain: (String) = "foundation", or |c: &Config| otry!(c.parity).chain.clone(), + "--chain=[CHAIN]", + "Specify the blockchain type. CHAIN may be either a JSON chain specification file or olympic, frontier, homestead, mainnet, morden, ropsten, classic, expanse, testnet, kovan or dev.", - // IPFS - flag_ipfs_api: bool = false, - or |c: &Config| otry!(c.ipfs).enable.clone(), - flag_ipfs_api_port: u16 = 5001u16, - or |c: &Config| otry!(c.ipfs).port.clone(), - flag_ipfs_api_interface: String = "local", - or |c: &Config| otry!(c.ipfs).interface.clone(), - flag_ipfs_api_cors: Option = None, - or |c: &Config| otry!(c.ipfs).cors.clone().map(Some), - flag_ipfs_api_hosts: String = "none", - or |c: &Config| otry!(c.ipfs).hosts.as_ref().map(|vec| vec.join(",")), + ARG arg_keys_path: (String) = "$BASE/keys", or |c: &Config| otry!(c.parity).keys_path.clone(), + "--keys-path=[PATH]", + "Specify the path for JSON key files to be found", - // -- Sealing/Mining Options - flag_author: Option = None, - or |c: &Config| otry!(c.mining).author.clone().map(Some), - flag_engine_signer: Option = None, - or |c: &Config| otry!(c.mining).engine_signer.clone().map(Some), - flag_force_sealing: bool = false, - or |c: &Config| otry!(c.mining).force_sealing.clone(), - flag_reseal_on_txs: String = "own", - or |c: &Config| otry!(c.mining).reseal_on_txs.clone(), - flag_reseal_on_uncle: bool = false, - or |c: &Config| otry!(c.mining).reseal_on_uncle.clone(), - flag_reseal_min_period: u64 = 2000u64, - or |c: &Config| otry!(c.mining).reseal_min_period.clone(), - flag_reseal_max_period: u64 = 120000u64, - or |c: &Config| otry!(c.mining).reseal_max_period.clone(), - flag_work_queue_size: usize = 20usize, - or |c: &Config| otry!(c.mining).work_queue_size.clone(), - flag_tx_gas_limit: Option = None, - or |c: &Config| otry!(c.mining).tx_gas_limit.clone().map(Some), - flag_tx_time_limit: Option = None, - or |c: &Config| otry!(c.mining).tx_time_limit.clone().map(Some), - flag_relay_set: String = "cheap", - or |c: &Config| otry!(c.mining).relay_set.clone(), - flag_min_gas_price: Option = None, - or |c: &Config| otry!(c.mining).min_gas_price.clone().map(Some), - flag_usd_per_tx: String = "0.0025", - or |c: &Config| otry!(c.mining).usd_per_tx.clone(), - flag_usd_per_eth: String = "auto", - or |c: &Config| otry!(c.mining).usd_per_eth.clone(), - flag_price_update_period: String = "hourly", - or |c: &Config| otry!(c.mining).price_update_period.clone(), - flag_gas_floor_target: String = "4700000", - or |c: &Config| otry!(c.mining).gas_floor_target.clone(), - flag_gas_cap: String = "6283184", - or |c: &Config| otry!(c.mining).gas_cap.clone(), - flag_extra_data: Option = None, - or |c: &Config| otry!(c.mining).extra_data.clone().map(Some), - flag_tx_queue_size: usize = 8192usize, - or |c: &Config| otry!(c.mining).tx_queue_size.clone(), - flag_tx_queue_mem_limit: u32 = 2u32, - or |c: &Config| otry!(c.mining).tx_queue_mem_limit.clone(), - flag_tx_queue_gas: String = "off", - or |c: &Config| otry!(c.mining).tx_queue_gas.clone(), - flag_tx_queue_strategy: String = "gas_price", - or |c: &Config| otry!(c.mining).tx_queue_strategy.clone(), - flag_tx_queue_ban_count: u16 = 1u16, - or |c: &Config| otry!(c.mining).tx_queue_ban_count.clone(), - flag_tx_queue_ban_time: u16 = 180u16, - or |c: &Config| otry!(c.mining).tx_queue_ban_time.clone(), - flag_remove_solved: bool = false, - or |c: &Config| otry!(c.mining).remove_solved.clone(), - flag_notify_work: Option = None, - or |c: &Config| otry!(c.mining).notify_work.as_ref().map(|vec| Some(vec.join(","))), - flag_refuse_service_transactions: bool = false, - or |c: &Config| otry!(c.mining).refuse_service_transactions.clone(), + ARG arg_identity: (String) = "", or |c: &Config| otry!(c.parity).identity.clone(), + "--identity=[NAME]", + "Specify your node's name.", - flag_stratum: bool = false, - or |c: &Config| Some(c.stratum.is_some()), - flag_stratum_interface: String = "local", - or |c: &Config| otry!(c.stratum).interface.clone(), - flag_stratum_port: u16 = 8008u16, - or |c: &Config| otry!(c.stratum).port.clone(), - flag_stratum_secret: Option = None, - or |c: &Config| otry!(c.stratum).secret.clone().map(Some), + ARG arg_base_path: (Option) = None, or |c: &Config| otry!(c.parity).base_path.clone(), + "-d, --base-path=[PATH]", + "Specify the base data storage path.", - // -- Footprint Options - flag_tracing: String = "auto", - or |c: &Config| otry!(c.footprint).tracing.clone(), - flag_pruning: String = "auto", - or |c: &Config| otry!(c.footprint).pruning.clone(), - flag_pruning_history: u64 = 64u64, - or |c: &Config| otry!(c.footprint).pruning_history.clone(), - flag_pruning_memory: usize = 32usize, - or |c: &Config| otry!(c.footprint).pruning_memory.clone(), - flag_cache_size_db: u32 = 32u32, - or |c: &Config| otry!(c.footprint).cache_size_db.clone(), - flag_cache_size_blocks: u32 = 8u32, - or |c: &Config| otry!(c.footprint).cache_size_blocks.clone(), - flag_cache_size_queue: u32 = 40u32, - or |c: &Config| otry!(c.footprint).cache_size_queue.clone(), - flag_cache_size_state: u32 = 25u32, - or |c: &Config| otry!(c.footprint).cache_size_state.clone(), - flag_cache_size: Option = None, - or |c: &Config| otry!(c.footprint).cache_size.clone().map(Some), - flag_fast_and_loose: bool = false, - or |c: &Config| otry!(c.footprint).fast_and_loose.clone(), - flag_db_compaction: String = "auto", - or |c: &Config| otry!(c.footprint).db_compaction.clone(), - flag_fat_db: String = "auto", - or |c: &Config| otry!(c.footprint).fat_db.clone(), - flag_scale_verifiers: bool = false, - or |c: &Config| otry!(c.footprint).scale_verifiers.clone(), - flag_num_verifiers: Option = None, - or |c: &Config| otry!(c.footprint).num_verifiers.clone().map(Some), + ARG arg_db_path: (Option) = None, or |c: &Config| otry!(c.parity).db_path.clone(), + "--db-path=[PATH]", + "Specify the database directory path", - // -- Import/Export Options - flag_from: String = "1", or |_| None, - flag_to: String = "latest", or |_| None, - flag_format: Option = None, or |_| None, - flag_no_seal_check: bool = false, or |_| None, - flag_no_storage: bool = false, or |_| None, - flag_no_code: bool = false, or |_| None, - flag_min_balance: Option = None, or |_| None, - flag_max_balance: Option = None, or |_| None, + ["Convenience options"] + FLAG flag_unsafe_expose: (bool) = false, or |c: &Config| otry!(c.misc).unsafe_expose, + "--unsafe-expose", + "All servers will listen on external interfaces and will be remotely accessible. It's equivalent with setting the following: --{{ws,jsonrpc,ui,ipfs,secret_store,stratum}}-interface=all --*-hosts=all + This option is UNSAFE and should be used with great care!", - // -- Snapshot Optons - flag_at: String = "latest", or |_| None, - flag_no_periodic_snapshot: bool = false, - or |c: &Config| otry!(c.snapshots).disable_periodic.clone(), + ARG arg_config: (String) = "$BASE/config.toml", or |_| None, + "-c, --config=[CONFIG]", + "Specify a configuration. CONFIG may be either a configuration file or a preset: dev, insecure, dev-insecure, mining, or non-standard-ports.", - // -- Virtual Machine Options - flag_jitvm: bool = false, - or |c: &Config| otry!(c.vm).jit.clone(), + ARG arg_ports_shift: (u16) = 0u16, or |c: &Config| otry!(c.misc).ports_shift, + "--ports-shift=[SHIFT]", + "Add SHIFT to all port numbers Parity is listening on. Includes network port and all servers (RPC, WebSockets, UI, IPFS, SecretStore).", - // -- Miscellaneous Options - flag_ntp_servers: String = "0.parity.pool.ntp.org:123,1.parity.pool.ntp.org:123,2.parity.pool.ntp.org:123,3.parity.pool.ntp.org:123", - or |c: &Config| otry!(c.misc).ntp_servers.clone().map(|vec| vec.join(",")), - flag_logging: Option = None, - or |c: &Config| otry!(c.misc).logging.clone().map(Some), - flag_log_file: Option = None, - or |c: &Config| otry!(c.misc).log_file.clone().map(Some), - flag_no_color: bool = false, - or |c: &Config| otry!(c.misc).color.map(|c| !c).clone(), + ["Account options"] + FLAG flag_no_hardware_wallets: (bool) = false, or |c: &Config| otry!(c.account).disable_hardware.clone(), + "--no-hardware-wallets", + "Disables hardware wallet support.", - // -- Whisper options - flag_whisper: bool = false, - or |c: &Config| otry!(c.whisper).enabled, - flag_whisper_pool_size: usize = 10usize, - or |c: &Config| otry!(c.whisper).pool_size.clone(), + FLAG flag_fast_unlock: (bool) = false, or |c: &Config| otry!(c.account).fast_unlock.clone(), + "--fast-unlock", + "Use drasticly faster unlocking mode. This setting causes raw secrets to be stored unprotected in memory, so use with care.", + + ARG arg_keys_iterations: (u32) = 10240u32, or |c: &Config| otry!(c.account).keys_iterations.clone(), + "--keys-iterations=[NUM]", + "Specify the number of iterations to use when deriving key from the password (bigger is more secure)", + + ARG arg_unlock: (Option) = None, or |c: &Config| otry!(c.account).unlock.as_ref().map(|vec| vec.join(",")), + "--unlock=[ACCOUNTS]", + "Unlock ACCOUNTS for the duration of the execution. ACCOUNTS is a comma-delimited list of addresses. Implies --no-ui.", + + ARG arg_password: (Vec) = Vec::new(), or |c: &Config| otry!(c.account).password.clone(), + "--password=[FILE]...", + "Provide a file containing a password for unlocking an account. Leading and trailing whitespace is trimmed.", + + ["UI options"] + FLAG flag_force_ui: (bool) = false, or |c: &Config| otry!(c.ui).force.clone(), + "--force-ui", + "Enable Trusted UI WebSocket endpoint, even when --unlock is in use.", + + FLAG flag_no_ui: (bool) = false, or |c: &Config| otry!(c.ui).disable.clone(), + "--no-ui", + "Disable Trusted UI WebSocket endpoint.", + + // NOTE [todr] For security reasons don't put this to config files + FLAG flag_ui_no_validation: (bool) = false, or |_| None, + "--ui-no-validation", + "Disable Origin and Host headers validation for Trusted UI. WARNING: INSECURE. Used only for development.", + + ARG arg_ui_interface: (String) = "local", or |c: &Config| otry!(c.ui).interface.clone(), + "--ui-interface=[IP]", + "Specify the hostname portion of the Trusted UI server, IP should be an interface's IP address, or local.", + + ARG arg_ui_hosts: (String) = "none", or |c: &Config| otry!(c.ui).hosts.as_ref().map(|vec| vec.join(",")), + "--ui-hosts=[HOSTS]", + "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\",.", + + ARG arg_ui_path: (String) = "$BASE/signer", or |c: &Config| otry!(c.ui).path.clone(), + "--ui-path=[PATH]", + "Specify directory where Trusted UIs tokens should be stored.", + + ARG arg_ui_port: (u16) = 8180u16, or |c: &Config| otry!(c.ui).port.clone(), + "--ui-port=[PORT]", + "Specify the port of Trusted UI server.", + + ["Networking options"] + FLAG flag_no_warp: (bool) = false, or |c: &Config| otry!(c.network).warp.clone().map(|w| !w), + "--no-warp", + "Disable syncing from the snapshot over the network.", + + FLAG flag_no_discovery: (bool) = false, or |c: &Config| otry!(c.network).discovery.map(|d| !d).clone(), + "--no-discovery", + "Disable new peer discovery.", + + FLAG flag_reserved_only: (bool) = false, or |c: &Config| otry!(c.network).reserved_only.clone(), + "--reserved-only", + "Connect only to reserved nodes.", + + FLAG flag_no_ancient_blocks: (bool) = false, or |_| None, + "--no-ancient-blocks", + "Disable downloading old blocks after snapshot restoration or warp sync.", + + FLAG flag_no_serve_light: (bool) = false, or |c: &Config| otry!(c.network).no_serve_light.clone(), + "--no-serve-light", + "Disable serving of light peers.", + + ARG arg_port: (u16) = 30303u16, or |c: &Config| otry!(c.network).port.clone(), + "--port=[PORT]", + "Override the port on which the node should listen.", + + ARG arg_min_peers: (u16) = 25u16, or |c: &Config| otry!(c.network).min_peers.clone(), + "--min-peers=[NUM]", + "Try to maintain at least NUM peers.", + + ARG arg_max_peers: (u16) = 50u16, or |c: &Config| otry!(c.network).max_peers.clone(), + "--max-peers=[NUM]", + "Allow up to NUM peers.", + + ARG arg_snapshot_peers: (u16) = 0u16, or |c: &Config| otry!(c.network).snapshot_peers.clone(), + "--snapshot-peers=[NUM]", + "Allow additional NUM peers for a snapshot sync.", + + ARG arg_nat: (String) = "any", or |c: &Config| otry!(c.network).nat.clone(), + "--nat=[METHOD]", + "Specify method to use for determining public address. Must be one of: any, none, upnp, extip:.", + + ARG arg_allow_ips: (String) = "all", or |c: &Config| otry!(c.network).allow_ips.clone(), + "--allow-ips=[FILTER]", + "Filter outbound connections. Must be one of: private - connect to private network IP addresses only; public - connect to public network IP addresses only; all - connect to any IP address.", + + ARG arg_max_pending_peers: (u16) = 64u16, or |c: &Config| otry!(c.network).max_pending_peers.clone(), + "--max-pending-peers=[NUM]", + "Allow up to NUM pending connections.", + + ARG arg_network_id: (Option) = None, or |c: &Config| otry!(c.network).id.clone(), + "--network-id=[INDEX]", + "Override the network identifier from the chain we are on.", + + ARG arg_bootnodes: (Option) = None, or |c: &Config| otry!(c.network).bootnodes.as_ref().map(|vec| vec.join(",")), + "--bootnodes=[NODES]", + "Override the bootnodes from our chain. NODES should be comma-delimited enodes.", + + ARG arg_node_key: (Option) = None, or |c: &Config| otry!(c.network).node_key.clone(), + "--node-key=[KEY]", + "Specify node secret key, either as 64-character hex string or input to SHA3 operation.", + + ARG arg_reserved_peers: (Option) = None, or |c: &Config| otry!(c.network).reserved_peers.clone(), + "--reserved-peers=[FILE]", + "Provide a file containing enodes, one per line. These nodes will always have a reserved slot on top of the normal maximum peers.", + + ["API and console options – RPC"] + FLAG flag_no_jsonrpc: (bool) = false, or |c: &Config| otry!(c.rpc).disable.clone(), + "--no-jsonrpc", + "Disable the JSON-RPC API server.", + + ARG arg_jsonrpc_port: (u16) = 8545u16, or |c: &Config| otry!(c.rpc).port.clone(), + "--jsonrpc-port=[PORT]", + "Specify the port portion of the JSONRPC API server.", + + ARG arg_jsonrpc_interface: (String) = "local", or |c: &Config| otry!(c.rpc).interface.clone(), + "--jsonrpc-interface=[IP]", + "Specify the hostname portion of the JSONRPC API server, IP should be an interface's IP address, or all (all interfaces) or local.", + + ARG arg_jsonrpc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore,shh,shh_pubsub", or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")), + "--jsonrpc-apis=[APIS]", + "Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited list of API name. Possible name are all, safe, web3, eth, net, personal, parity, parity_set, traces, rpc, parity_accounts. You can also disable a specific API by putting '-' in the front: all,-personal.", + + ARG arg_jsonrpc_hosts: (String) = "none", or |c: &Config| otry!(c.rpc).hosts.as_ref().map(|vec| vec.join(",")), + "--jsonrpc-hosts=[HOSTS]", + "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\",.", + + ARG arg_jsonrpc_threads: (usize) = 0usize, or |c: &Config| otry!(c.rpc).processing_threads, + "--jsonrpc-threads=[THREADS]", + "Turn on additional processing threads in all RPC servers. Setting this to non-zero value allows parallel cpu-heavy queries execution.", + + ARG arg_jsonrpc_cors: (Option) = None, or |c: &Config| otry!(c.rpc).cors.clone(), + "--jsonrpc-cors=[URL]", + "Specify CORS header for JSON-RPC API responses.", + + ARG arg_jsonrpc_server_threads: (Option) = None, or |c: &Config| otry!(c.rpc).server_threads, + "--jsonrpc-server-threads=[NUM]", + "Enables experimental faster implementation of JSON-RPC server. Requires Dapps server to be disabled using --no-dapps.", + + ["API and console options – WebSockets"] + FLAG flag_no_ws: (bool) = false, or |c: &Config| otry!(c.websockets).disable.clone(), + "--no-ws", + "Disable the WebSockets server.", + + ARG arg_ws_port: (u16) = 8546u16, or |c: &Config| otry!(c.websockets).port.clone(), + "--ws-port=[PORT]", + "Specify the port portion of the WebSockets server.", + + ARG arg_ws_interface: (String) = "local", or |c: &Config| otry!(c.websockets).interface.clone(), + "--ws-interface=[IP]", + "Specify the hostname portion of the WebSockets server, IP should be an interface's IP address, or all (all interfaces) or local.", + + ARG arg_ws_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore,shh,shh_pubsub", or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")), + "--ws-apis=[APIS]", + "Specify the APIs available through the WebSockets interface. APIS is a comma-delimited list of API name. Possible name are web3, eth, pubsub, net, personal, parity, parity_set, traces, rpc, parity_accounts..", + + ARG arg_ws_origins: (String) = "chrome-extension://*,moz-extension://*", or |c: &Config| otry!(c.websockets).origins.as_ref().map(|vec| vec.join(",")), + "--ws-origins=[URL]", + "Specify Origin header values allowed to connect. Special options: \"all\", \"none\".", + + ARG arg_ws_hosts: (String) = "none", or |c: &Config| otry!(c.websockets).hosts.as_ref().map(|vec| vec.join(",")), + "--ws-hosts=[HOSTS]", + "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\",.", + + ["API and console options – IPC"] + FLAG flag_no_ipc: (bool) = false, or |c: &Config| otry!(c.ipc).disable.clone(), + "--no-ipc", + "Disable JSON-RPC over IPC service.", + + ARG arg_ipc_path: (String) = if cfg!(windows) { r"\\.\pipe\jsonrpc.ipc" } else { "$BASE/jsonrpc.ipc" }, or |c: &Config| otry!(c.ipc).path.clone(), + "--ipc-path=[PATH]", + "Specify custom path for JSON-RPC over IPC service.", + + ARG arg_ipc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,traces,rpc,secretstore,shh,shh_pubsub", or |c: &Config| otry!(c.ipc).apis.as_ref().map(|vec| vec.join(",")), + "--ipc-apis=[APIS]", + "Specify custom API set available via JSON-RPC over IPC.", + + ["API and console options – Dapps"] + FLAG flag_no_dapps: (bool) = false, or |c: &Config| otry!(c.dapps).disable.clone(), + "--no-dapps", + "Disable the Dapps server (e.g. status page).", + + ARG arg_dapps_path: (String) = "$BASE/dapps", or |c: &Config| otry!(c.dapps).path.clone(), + "--dapps-path=[PATH]", + "Specify directory where dapps should be installed.", + + ["API and console options – IPFS"] + FLAG flag_ipfs_api: (bool) = false, or |c: &Config| otry!(c.ipfs).enable.clone(), + "--ipfs-api", + "Enable IPFS-compatible HTTP API.", + + ARG arg_ipfs_api_port: (u16) = 5001u16, or |c: &Config| otry!(c.ipfs).port.clone(), + "--ipfs-api-port=[PORT]", + "Configure on which port the IPFS HTTP API should listen.", + + ARG arg_ipfs_api_interface: (String) = "local", or |c: &Config| otry!(c.ipfs).interface.clone(), + "--ipfs-api-interface=[IP]", + "Specify the hostname portion of the IPFS API server, IP should be an interface's IP address or local.", + + ARG arg_ipfs_api_hosts: (String) = "none", or |c: &Config| otry!(c.ipfs).hosts.as_ref().map(|vec| vec.join(",")), + "--ipfs-api-hosts=[HOSTS]", + "List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: \"all\", \"none\".", + + ARG arg_ipfs_api_cors: (Option) = None, or |c: &Config| otry!(c.ipfs).cors.clone(), + "--ipfs-api-cors=[URL]", + "Specify CORS header for IPFS API responses.", + + ["Secret store options"] + FLAG flag_no_secretstore: (bool) = false, or |c: &Config| otry!(c.secretstore).disable.clone(), + "--no-secretstore", + "Disable Secret Store functionality.", + + FLAG flag_no_secretstore_http: (bool) = false, or |c: &Config| otry!(c.secretstore).disable_http.clone(), + "--no-secretstore-http", + "Disable Secret Store HTTP API.", + + FLAG flag_no_secretstore_acl_check: (bool) = false, or |c: &Config| otry!(c.secretstore).disable_acl_check.clone(), + "--no-acl-check", + "Disable ACL check (useful for test environments).", + + ARG arg_secretstore_nodes: (String) = "", or |c: &Config| otry!(c.secretstore).nodes.as_ref().map(|vec| vec.join(",")), + "--secretstore-nodes=[NODES]", + "Comma-separated list of other secret store cluster nodes in form NODE_PUBLIC_KEY_IN_HEX@NODE_IP_ADDR:NODE_PORT.", + + ARG arg_secretstore_interface: (String) = "local", or |c: &Config| otry!(c.secretstore).interface.clone(), + "--secretstore-interface=[IP]", + "Specify the hostname portion for listening to Secret Store Key Server internal requests, IP should be an interface's IP address, or local.", + + ARG arg_secretstore_port: (u16) = 8083u16, or |c: &Config| otry!(c.secretstore).port.clone(), + "--secretstore-port=[PORT]", + "Specify the port portion for listening to Secret Store Key Server internal requests.", + + ARG arg_secretstore_http_interface: (String) = "local", or |c: &Config| otry!(c.secretstore).http_interface.clone(), + "--secretstore-http-interface=[IP]", + "Specify the hostname portion for listening to Secret Store Key Server HTTP requests, IP should be an interface's IP address, or local.", + + ARG arg_secretstore_http_port: (u16) = 8082u16, or |c: &Config| otry!(c.secretstore).http_port.clone(), + "--secretstore-http-port=[PORT]", + "Specify the port portion for listening to Secret Store Key Server HTTP requests.", + + ARG arg_secretstore_path: (String) = "$BASE/secretstore", or |c: &Config| otry!(c.secretstore).path.clone(), + "--secretstore-path=[PATH]", + "Specify directory where Secret Store should save its data..", + + ARG arg_secretstore_secret: (Option) = None, or |c: &Config| otry!(c.secretstore).self_secret.clone(), + "--secretstore-secret=[SECRET]", + "Hex-encoded secret key of this node.", + + ["Sealing/Mining options"] + FLAG flag_force_sealing: (bool) = false, or |c: &Config| otry!(c.mining).force_sealing.clone(), + "--force-sealing", + "Force the node to author new blocks as if it were always sealing/mining.", + + FLAG flag_reseal_on_uncle: (bool) = false, or |c: &Config| otry!(c.mining).reseal_on_uncle.clone(), + "--reseal-on-uncle", + "Force the node to author new blocks when a new uncle block is imported.", + + FLAG flag_remove_solved: (bool) = false, or |c: &Config| otry!(c.mining).remove_solved.clone(), + "--remove-solved", + "Move solved blocks from the work package queue instead of cloning them. This gives a slightly faster import speed, but means that extra solutions submitted for the same work package will go unused.", + + FLAG flag_refuse_service_transactions: (bool) = false, or |c: &Config| otry!(c.mining).refuse_service_transactions.clone(), + "--refuse-service-transactions", + "Always refuse service transactions..", + + FLAG flag_no_persistent_txqueue: (bool) = false, or |c: &Config| otry!(c.parity).no_persistent_txqueue, + "--no-persistent-txqueue", + "Don't save pending local transactions to disk to be restored whenever the node restarts.", + + FLAG flag_stratum: (bool) = false, or |c: &Config| Some(c.stratum.is_some()), + "--stratum", + "Run Stratum server for miner push notification.", + + ARG arg_reseal_on_txs: (String) = "own", or |c: &Config| otry!(c.mining).reseal_on_txs.clone(), + "--reseal-on-txs=[SET]", + "Specify which transactions should force the node to reseal a block. SET is one of: none - never reseal on new transactions; own - reseal only on a new local transaction; ext - reseal only on a new external transaction; all - reseal on all new transactions.", + + ARG arg_reseal_min_period: (u64) = 2000u64, or |c: &Config| otry!(c.mining).reseal_min_period.clone(), + "--reseal-min-period=[MS]", + "Specify the minimum time between reseals from incoming transactions. MS is time measured in milliseconds.", + + ARG arg_reseal_max_period: (u64) = 120000u64, or |c: &Config| otry!(c.mining).reseal_max_period.clone(), + "--reseal-max-period=[MS]", + "Specify the maximum time since last block to enable force-sealing. MS is time measured in milliseconds.", + + ARG arg_work_queue_size: (usize) = 20usize, or |c: &Config| otry!(c.mining).work_queue_size.clone(), + "--work-queue-size=[ITEMS]", + "Specify the number of historical work packages which are kept cached lest a solution is found for them later. High values take more memory but result in fewer unusable solutions.", + + ARG arg_relay_set: (String) = "cheap", or |c: &Config| otry!(c.mining).relay_set.clone(), + "--relay-set=[SET]", + "Set of transactions to relay. SET may be: cheap - Relay any transaction in the queue (this may include invalid transactions); strict - Relay only executed transactions (this guarantees we don't relay invalid transactions, but means we relay nothing if not mining); lenient - Same as strict when mining, and cheap when not.", + + ARG arg_usd_per_tx: (String) = "0.0025", or |c: &Config| otry!(c.mining).usd_per_tx.clone(), + "--usd-per-tx=[USD]", + "Amount of USD to be paid for a basic transaction. The minimum gas price is set accordingly.", + + ARG arg_usd_per_eth: (String) = "auto", or |c: &Config| otry!(c.mining).usd_per_eth.clone(), + "--usd-per-eth=[SOURCE]", + "USD value of a single ETH. SOURCE may be either an amount in USD, a web service or 'auto' to use each web service in turn and fallback on the last known good value.", + + ARG arg_price_update_period: (String) = "hourly", or |c: &Config| otry!(c.mining).price_update_period.clone(), + "--price-update-period=[T]", + "T will be allowed to pass between each gas price update. T may be daily, hourly, a number of seconds, or a time string of the form \"2 days\", \"30 minutes\" etc..", + + ARG arg_gas_floor_target: (String) = "4700000", or |c: &Config| otry!(c.mining).gas_floor_target.clone(), + "--gas-floor-target=[GAS]", + "Amount of gas per block to target when sealing a new block.", + + ARG arg_gas_cap: (String) = "6283184", or |c: &Config| otry!(c.mining).gas_cap.clone(), + "--gas-cap=[GAS]", + "A cap on how large we will raise the gas limit per block due to transaction volume.", + + ARG arg_tx_queue_mem_limit: (u32) = 2u32, or |c: &Config| otry!(c.mining).tx_queue_mem_limit.clone(), + "--tx-queue-mem-limit=[MB]", + "Maximum amount of memory that can be used by the transaction queue. Setting this parameter to 0 disables limiting.", + + ARG arg_tx_queue_size: (usize) = 8192usize, or |c: &Config| otry!(c.mining).tx_queue_size.clone(), + "--tx-queue-size=[LIMIT]", + "Maximum amount of transactions in the queue (waiting to be included in next block).", + + ARG arg_tx_queue_gas: (String) = "off", or |c: &Config| otry!(c.mining).tx_queue_gas.clone(), + "--tx-queue-gas=[LIMIT]", + "Maximum amount of total gas for external transactions in the queue. LIMIT can be either an amount of gas or 'auto' or 'off'. 'auto' sets the limit to be 20x the current block gas limit..", + + ARG arg_tx_queue_strategy: (String) = "gas_price", or |c: &Config| otry!(c.mining).tx_queue_strategy.clone(), + "--tx-queue-strategy=[S]", + "Prioritization strategy used to order transactions in the queue. S may be: gas - Prioritize txs with low gas limit; gas_price - Prioritize txs with high gas price; gas_factor - Prioritize txs using gas price and gas limit ratio.", + + ARG arg_tx_queue_ban_count: (u16) = 1u16, or |c: &Config| otry!(c.mining).tx_queue_ban_count.clone(), + "--tx-queue-ban-count=[C]", + "Number of times maximal time for execution (--tx-time-limit) can be exceeded before banning sender/recipient/code.", + + ARG arg_tx_queue_ban_time: (u16) = 180u16, or |c: &Config| otry!(c.mining).tx_queue_ban_time.clone(), + "--tx-queue-ban-time=[SEC]", + "Banning time (in seconds) for offenders of specified execution time limit. Also number of offending actions have to reach the threshold within that time.", + + ARG arg_stratum_interface: (String) = "local", or |c: &Config| otry!(c.stratum).interface.clone(), + "--stratum-interface=[IP]", + "Interface address for Stratum server.", + + ARG arg_stratum_port: (u16) = 8008u16, or |c: &Config| otry!(c.stratum).port.clone(), + "--stratum-port=[PORT]", + "Port for Stratum server to listen on.", + + ARG arg_min_gas_price: (Option) = None, or |c: &Config| otry!(c.mining).min_gas_price.clone(), + "--min-gas-price=[STRING]", + "Minimum amount of Wei per GAS to be paid for a transaction to be accepted for mining. Overrides --basic-tx-usd.", + + ARG arg_author: (Option) = None, or |c: &Config| otry!(c.mining).author.clone(), + "--author=[ADDRESS]", + "Specify the block author (aka \"coinbase\") address for sending block rewards from sealed blocks. NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION.", // Sealing/Mining Option + + ARG arg_engine_signer: (Option) = None, or |c: &Config| otry!(c.mining).engine_signer.clone(), + "--engine-signer=[ADDRESS]", + "Specify the address which should be used to sign consensus messages and issue blocks. Relevant only to non-PoW chains.", + + ARG arg_tx_gas_limit: (Option) = None, or |c: &Config| otry!(c.mining).tx_gas_limit.clone(), + "--tx-gas-limit=[GAS]", + "Apply a limit of GAS as the maximum amount of gas a single transaction may have for it to be mined.", + + ARG arg_tx_time_limit: (Option) = None, or |c: &Config| otry!(c.mining).tx_time_limit.clone(), + "--tx-time-limit=[MS]", + "Maximal time for processing single transaction. If enabled senders/recipients/code of transactions offending the limit will be banned from being included in transaction queue for 180 seconds.", + + ARG arg_extra_data: (Option) = None, or |c: &Config| otry!(c.mining).extra_data.clone(), + "--extra-data=[STRING]", + "Specify a custom extra-data for authored blocks, no more than 32 characters.", + + ARG arg_notify_work: (Option) = None, or |c: &Config| otry!(c.mining).notify_work.as_ref().map(|vec| vec.join(",")), + "--notify-work=[URLS]", + "URLs to which work package notifications are pushed. URLS should be a comma-delimited list of HTTP URLs.", + + ARG arg_stratum_secret: (Option) = None, or |c: &Config| otry!(c.stratum).secret.clone(), + "--stratum-secret=[STRING]", + "Secret for authorizing Stratum server for peers.", + + ["Internal Options"] + FLAG flag_can_restart: (bool) = false, or |_| None, + "--can-restart", + "Executable will auto-restart if exiting with 69", + + ["Miscellaneous options"] + FLAG flag_no_color: (bool) = false, or |c: &Config| otry!(c.misc).color.map(|c| !c).clone(), + "--no-color", + "Don't use terminal color codes in output.", + + FLAG flag_version: (bool) = false, or |_| None, + "-v, --version", + "Show information about version.", + + FLAG flag_no_config: (bool) = false, or |_| None, + "--no-config", + "Don't load a configuration file.", + + ARG arg_ntp_servers: (String) = "0.parity.pool.ntp.org:123,1.parity.pool.ntp.org:123,2.parity.pool.ntp.org:123,3.parity.pool.ntp.org:123", or |c: &Config| otry!(c.misc).ntp_servers.clone().map(|vec| vec.join(",")), + "--ntp-servers=[HOSTS]", + "Comma separated list of NTP servers to provide current time (host:port). Used to verify node health. Parity uses pool.ntp.org NTP servers; consider joining the pool: http://www.pool.ntp.org/join.html", + + ARG arg_logging: (Option) = None, or |c: &Config| otry!(c.misc).logging.clone(), + "-l, --logging=[LOGGING]", + "Specify the logging level. Must conform to the same format as RUST_LOG.", + + ARG arg_log_file: (Option) = None, or |c: &Config| otry!(c.misc).log_file.clone(), + "--log-file=[FILENAME]", + "Specify a filename into which logging should be appended.", + + ["Footprint options"] + FLAG flag_fast_and_loose: (bool) = false, or |c: &Config| otry!(c.footprint).fast_and_loose.clone(), + "--fast-and-loose", + "Disables DB WAL, which gives a significant speed up but means an unclean exit is unrecoverable.", + + FLAG flag_scale_verifiers: (bool) = false, or |c: &Config| otry!(c.footprint).scale_verifiers.clone(), + "--scale-verifiers", + "Automatically scale amount of verifier threads based on workload. Not guaranteed to be faster.", + + ARG arg_tracing: (String) = "auto", or |c: &Config| otry!(c.footprint).tracing.clone(), + "--tracing=[BOOL]", + "Indicates if full transaction tracing should be enabled. Works only if client had been fully synced with tracing enabled. BOOL may be one of auto, on, off. auto uses last used value of this option (off if it does not exist).", // footprint option + + ARG arg_pruning: (String) = "auto", or |c: &Config| otry!(c.footprint).pruning.clone(), + "--pruning=[METHOD]", + "Configure pruning of the state/storage trie. METHOD may be one of auto, archive, fast: archive - keep all state trie data. No pruning. fast - maintain journal overlay. Fast but 50MB used. auto - use the method most recently synced or default to fast if none synced.", + + ARG arg_pruning_history: (u64) = 64u64, or |c: &Config| otry!(c.footprint).pruning_history.clone(), + "--pruning-history=[NUM]", + "Set a minimum number of recent states to keep when pruning is active..", + + ARG arg_pruning_memory: (usize) = 32usize, or |c: &Config| otry!(c.footprint).pruning_memory.clone(), + "--pruning-memory=[MB]", + "The ideal amount of memory in megabytes to use to store recent states. As many states as possible will be kept within this limit, and at least --pruning-history states will always be kept.", + + ARG arg_cache_size_db: (u32) = 32u32, or |c: &Config| otry!(c.footprint).cache_size_db.clone(), + "--cache-size-db=[MB]", + "Override database cache size.", + + ARG arg_cache_size_blocks: (u32) = 8u32, or |c: &Config| otry!(c.footprint).cache_size_blocks.clone(), + "--cache-size-blocks=[MB]", + "Specify the prefered size of the blockchain cache in megabytes.", + + ARG arg_cache_size_queue: (u32) = 40u32, or |c: &Config| otry!(c.footprint).cache_size_queue.clone(), + "--cache-size-queue=[MB]", + "Specify the maximum size of memory to use for block queue.", + + ARG arg_cache_size_state: (u32) = 25u32, or |c: &Config| otry!(c.footprint).cache_size_state.clone(), + "--cache-size-state=[MB]", + "Specify the maximum size of memory to use for the state cache.", + + ARG arg_db_compaction: (String) = "auto", or |c: &Config| otry!(c.footprint).db_compaction.clone(), + "--db-compaction=[TYPE]", + "Database compaction type. TYPE may be one of: ssd - suitable for SSDs and fast HDDs; hdd - suitable for slow HDDs; auto - determine automatically.", + + ARG arg_fat_db: (String) = "auto", or |c: &Config| otry!(c.footprint).fat_db.clone(), + "--fat-db=[BOOL]", + "Build appropriate information to allow enumeration of all accounts and storage keys. Doubles the size of the state database. BOOL may be one of on, off or auto.", + + ARG arg_cache_size: (Option) = None, or |c: &Config| otry!(c.footprint).cache_size.clone(), + "--cache-size=[MB]", + "Set total amount of discretionary memory to use for the entire system, overrides other cache and queue options.", + + ARG arg_num_verifiers: (Option) = None, or |c: &Config| otry!(c.footprint).num_verifiers.clone(), + "--num-verifiers=[INT]", + "Amount of verifier threads to use or to begin with, if verifier auto-scaling is enabled.", + + ["Import/export options"] + FLAG flag_no_seal_check: (bool) = false, or |_| None, + "--no-seal-check", + "Skip block seal check.", + + ["Snapshot options"] + FLAG flag_no_periodic_snapshot: (bool) = false, or |c: &Config| otry!(c.snapshots).disable_periodic.clone(), + "--no-periodic-snapshot", + "Disable automated snapshots which usually occur once every 10000 blocks.", + + ["Virtual Machine options"] + FLAG flag_jitvm: (bool) = false, or |c: &Config| otry!(c.vm).jit.clone(), + "--jitvm", + "Enable the JIT VM.", + + ["Whisper options"] + FLAG flag_whisper: (bool) = false, or |c: &Config| otry!(c.whisper).enabled, + "--whisper", + "Enable the Whisper network.", + + ARG arg_whisper_pool_size: (usize) = 10usize, or |c: &Config| otry!(c.whisper).pool_size.clone(), + "--whisper-pool-size=[MB]", + "Target size of the whisper message pool in megabytes.", + + ["Legacy options"] + FLAG flag_dapps_apis_all: (bool) = false, or |_| None, + "--dapps-apis-all", + "Dapps server is merged with RPC server. Use --jsonrpc-apis.", + + FLAG flag_geth: (bool) = false, or |_| None, + "--geth", + "Run in Geth-compatibility mode. Sets the IPC path to be the same as Geth's. Overrides the --ipc-path and --ipcpath options. Alters RPCs to reflect Geth bugs. Includes the personal_ RPC by default.", + + FLAG flag_testnet: (bool) = false, or |_| None, + "--testnet", + "Testnet mode. Equivalent to --chain testnet. Overrides the --keys-path option.", + + FLAG flag_import_geth_keys: (bool) = false, or |_| None, + "--import-geth-keys", + "Attempt to import keys from Geth client.", + + FLAG flag_ipcdisable: (bool) = false, or |_| None, + "--ipcdisable", + "Equivalent to --no-ipc.", + + FLAG flag_ipc_off: (bool) = false, or |_| None, + "--ipc-off", + "Equivalent to --no-ipc.", + + FLAG flag_nodiscover: (bool) = false, or |_| None, + "--nodiscover", + "Equivalent to --no-discovery.", + + FLAG flag_jsonrpc: (bool) = false, or |_| None, + "-j, --jsonrpc", + "Does nothing; JSON-RPC is on by default now.", + + FLAG flag_jsonrpc_off: (bool) = false, or |_| None, + "--jsonrpc-off", + "Equivalent to --no-jsonrpc.", + + FLAG flag_webapp: (bool) = false, or |_| None, + "-w, --webapp", + "Does nothing; dapps server is on by default now.", + + FLAG flag_dapps_off: (bool) = false, or |_| None, + "--dapps-off", + "Equivalent to --no-dapps.", + + FLAG flag_rpc: (bool) = false, or |_| None, + "--rpc", + "Does nothing; JSON-RPC is on by default now.", + + ARG arg_dapps_port: (Option) = None, or |c: &Config| otry!(c.dapps).port.clone(), + "--dapps-port=[PORT]", + "Dapps server is merged with RPC server. Use --jsonrpc-port.", + + ARG arg_dapps_interface: (Option) = None, or |c: &Config| otry!(c.dapps).interface.clone(), + "--dapps-interface=[IP]", + "Dapps server is merged with RPC server. Use --jsonrpc-interface.", + + ARG arg_dapps_hosts: (Option) = None, or |c: &Config| otry!(c.dapps).hosts.as_ref().map(|vec| vec.join(",")), + "--dapps-hosts=[HOSTS]", + "Dapps server is merged with RPC server. Use --jsonrpc-hosts.", + + ARG arg_dapps_cors: (Option) = None, or |c: &Config| otry!(c.dapps).cors.clone(), + "--dapps-cors=[URL]", + "Dapps server is merged with RPC server. Use --jsonrpc-cors.", + + ARG arg_dapps_user: (Option) = None, or |c: &Config| otry!(c.dapps).user.clone(), + "--dapps-user=[USERNAME]", + "Dapps server authentication has been removed.", + + ARG arg_dapps_pass: (Option) = None, or |c: &Config| otry!(c.dapps).pass.clone(), + "--dapps-pass=[PASSWORD]", + "Dapps server authentication has been removed.", + + ARG arg_datadir: (Option) = None, or |_| None, + "--datadir=[PATH]", + "Equivalent to --base-path PATH.", + + ARG arg_networkid: (Option) = None, or |_| None, + "--networkid=[INDEX]", + "Equivalent to --network-id INDEX.", + + ARG arg_peers: (Option) = None, or |_| None, + "--peers=[NUM]", + "Equivalent to --min-peers NUM.", + + ARG arg_nodekey: (Option) = None, or |_| None, + "--nodekey=[KEY]", + "Equivalent to --node-key KEY.", + + ARG arg_rpcaddr: (Option) = None, or |_| None, + "--rpcaddr=[IP]", + "Equivalent to --jsonrpc-interface IP.", + + ARG arg_rpcport: (Option) = None, or |_| None, + "--rpcport=[PORT]", + "Equivalent to --jsonrpc-port PORT.", + + ARG arg_rpcapi: (Option) = None, or |_| None, + "--rpcapi=[APIS]", + "Equivalent to --jsonrpc-apis APIS.", + + ARG arg_rpccorsdomain: (Option) = None, or |_| None, + "--rpccorsdomain=[URL]", + "Equivalent to --jsonrpc-cors URL.", + + ARG arg_ipcapi: (Option) = None, or |_| None, + "--ipcapi=[APIS]", + "Equivalent to --ipc-apis APIS.", + + ARG arg_ipcpath: (Option) = None, or |_| None, + "--ipcpath=[PATH]", + "Equivalent to --ipc-path PATH.", + + ARG arg_gasprice: (Option) = None, or |_| None, + "--gasprice=[WEI]", + "Equivalent to --min-gas-price WEI.", + + ARG arg_etherbase: (Option) = None, or |_| None, + "--etherbase=[ADDRESS]", + "Equivalent to --author ADDRESS.", + + ARG arg_extradata: (Option) = None, or |_| None, + "--extradata=[STRING]", + "Equivalent to --extra-data STRING.", + + ARG arg_cache: (Option) = None, or |_| None, + "--cache=[MB]", + "Equivalent to --cache-size MB.", - // -- Legacy Options supported in configs - flag_dapps_port: Option = None, - or |c: &Config| otry!(c.dapps).port.clone().map(Some), - flag_dapps_interface: Option = None, - or |c: &Config| otry!(c.dapps).interface.clone().map(Some), - flag_dapps_hosts: Option = None, - or |c: &Config| otry!(c.dapps).hosts.as_ref().map(|vec| Some(vec.join(","))), - flag_dapps_cors: Option = None, - or |c: &Config| otry!(c.dapps).cors.clone().map(Some), - flag_dapps_user: Option = None, - or |c: &Config| otry!(c.dapps).user.clone().map(Some), - flag_dapps_pass: Option = None, - or |c: &Config| otry!(c.dapps).pass.clone().map(Some), - flag_dapps_apis_all: Option = None, or |_| None, - } - { - // Values with optional default value. - flag_base_path: Option, display dir::default_data_path(), or |c: &Config| otry!(c.parity).base_path.clone().map(Some), - flag_db_path: Option, display dir::CHAINS_PATH, or |c: &Config| otry!(c.parity).db_path.clone().map(Some), - flag_warp: Option, display true, or |c: &Config| Some(otry!(c.network).warp.clone()), } } @@ -635,6 +1197,65 @@ mod tests { }; use toml; + #[test] + fn should_parse_args_and_flags() { + let args = Args::parse(&["parity", "--no-warp"]).unwrap(); + assert_eq!(args.flag_no_warp, true); + + let args = Args::parse(&["parity", "--pruning", "archive"]).unwrap(); + assert_eq!(args.arg_pruning, "archive"); + + let args = Args::parse(&["parity", "export", "state", "--no-storage"]).unwrap(); + assert_eq!(args.flag_export_state_no_storage, true); + + let args = Args::parse(&["parity", "export", "state", "--min-balance","123"]).unwrap(); + assert_eq!(args.arg_export_state_min_balance, Some("123".to_string())); + } + + #[test] + fn should_use_subcommand_arg_default() { + let args = Args::parse(&["parity", "export", "state", "--at", "123"]).unwrap(); + assert_eq!(args.arg_export_state_at, "123"); + assert_eq!(args.arg_snapshot_at, "latest"); + + let args = Args::parse(&["parity", "snapshot", "--at", "123", "file.dump"]).unwrap(); + assert_eq!(args.arg_snapshot_at, "123"); + assert_eq!(args.arg_export_state_at, "latest"); + + let args = Args::parse(&["parity", "export", "state"]).unwrap(); + assert_eq!(args.arg_snapshot_at, "latest"); + assert_eq!(args.arg_export_state_at, "latest"); + + let args = Args::parse(&["parity", "snapshot", "file.dump"]).unwrap(); + assert_eq!(args.arg_snapshot_at, "latest"); + assert_eq!(args.arg_export_state_at, "latest"); + } + + #[test] + fn should_parse_multiple_values() { + let args = Args::parse(&["parity", "account", "import", "~/1", "~/2"]).unwrap(); + assert_eq!(args.arg_account_import_path, Some(vec!["~/1".to_owned(), "~/2".to_owned()])); + + let args = Args::parse(&["parity", "account", "import", "~/1,ext"]).unwrap(); + assert_eq!(args.arg_account_import_path, Some(vec!["~/1,ext".to_owned()])); + + let args = Args::parse(&["parity", "--secretstore-nodes", "abc@127.0.0.1:3333,cde@10.10.10.10:4444"]).unwrap(); + assert_eq!(args.arg_secretstore_nodes, "abc@127.0.0.1:3333,cde@10.10.10.10:4444"); + + // Arguments with a single value shouldn't accept multiple values + let args = Args::parse(&["parity", "--auto-update", "critical", "all"]); + assert!(args.is_err()); + + let args = Args::parse(&["parity", "--password", "~/.safe/1", "~/.safe/2"]).unwrap(); + assert_eq!(args.arg_password, vec!["~/.safe/1".to_owned(), "~/.safe/2".to_owned()]); + } + + #[test] + fn should_parse_global_args_with_subcommand() { + let args = Args::parse(&["parity", "--chain", "dev", "account", "list"]).unwrap(); + assert_eq!(args.arg_chain, "dev".to_owned()); + } + #[test] fn should_parse_args_and_include_config() { // given @@ -647,7 +1268,7 @@ mod tests { let args = Args::parse_with_config(&["parity"], config).unwrap(); // then - assert_eq!(args.flag_chain, "morden".to_owned()); + assert_eq!(args.arg_chain, "morden".to_owned()); } #[test] @@ -662,7 +1283,7 @@ mod tests { let args = Args::parse_with_config(&["parity", "--chain", "xyz"], config).unwrap(); // then - assert_eq!(args.flag_chain, "xyz".to_owned()); + assert_eq!(args.arg_chain, "xyz".to_owned()); } #[test] @@ -676,13 +1297,13 @@ mod tests { let args = Args::parse_with_config(&["parity"], config).unwrap(); // then - assert_eq!(args.flag_pruning_history, 128); + assert_eq!(args.arg_pruning_history, 128); } #[test] fn should_parse_full_config() { // given - let config = toml::from_str(include_str!("./config.full.toml")).unwrap(); + let config = toml::from_str(include_str!("./tests/config.full.toml")).unwrap(); // when let args = Args::parse_with_config(&["parity", "--chain", "xyz"], config).unwrap(); @@ -690,85 +1311,104 @@ mod tests { // then assert_eq!(args, Args { // Commands - cmd_daemon: false, - cmd_wallet: false, - cmd_account: false, - cmd_new: false, - cmd_list: false, - cmd_export: false, - cmd_state: false, - cmd_blocks: false, - cmd_import: false, - cmd_signer: false, - cmd_sign: false, - cmd_reject: false, - cmd_new_token: false, - cmd_snapshot: false, - cmd_restore: false, cmd_ui: false, cmd_dapp: false, + cmd_daemon: false, + cmd_account: false, + cmd_account_new: false, + cmd_account_list: false, + cmd_account_import: false, + cmd_wallet: false, + cmd_wallet_import: false, + cmd_import: false, + cmd_export: false, + cmd_export_blocks: false, + cmd_export_state: false, + cmd_signer: false, + cmd_signer_list: false, + cmd_signer_sign: false, + cmd_signer_reject: false, + cmd_signer_new_token: false, + cmd_snapshot: false, + cmd_restore: false, cmd_tools: false, - cmd_hash: false, + cmd_tools_hash: false, cmd_db: false, - cmd_kill: false, + cmd_db_kill: false, // Arguments - arg_pid_file: "".into(), - arg_file: None, - arg_id: None, - arg_path: vec![], + arg_daemon_pid_file: None, + arg_import_file: None, + arg_import_format: None, + arg_export_blocks_file: None, + arg_export_blocks_format: None, + arg_export_state_file: None, + arg_export_state_format: None, + arg_snapshot_file: None, + arg_restore_file: None, + arg_tools_hash_file: None, + + arg_account_new_password: None, + arg_signer_sign_password: None, + arg_wallet_import_password: None, + arg_signer_sign_id: None, + arg_signer_reject_id: None, + arg_dapp_path: None, + arg_account_import_path: None, + arg_wallet_import_path: None, // -- Operating Options - flag_mode: "last".into(), - flag_mode_timeout: 300u64, - flag_mode_alarm: 3600u64, - flag_auto_update: "none".into(), - flag_release_track: "current".into(), + arg_mode: "last".into(), + arg_mode_timeout: 300u64, + arg_mode_alarm: 3600u64, + arg_auto_update: "none".into(), + arg_release_track: "current".into(), flag_public_node: false, flag_no_download: false, flag_no_consensus: false, - flag_chain: "xyz".into(), - flag_base_path: Some("$HOME/.parity".into()), - flag_db_path: Some("$HOME/.parity/chains".into()), - flag_keys_path: "$HOME/.parity/keys".into(), - flag_identity: "".into(), + arg_chain: "xyz".into(), + arg_base_path: Some("$HOME/.parity".into()), + arg_db_path: Some("$HOME/.parity/chains".into()), + arg_keys_path: "$HOME/.parity/keys".into(), + arg_identity: "".into(), flag_light: false, flag_no_persistent_txqueue: false, + flag_force_direct: false, // -- Convenience Options - flag_config: "$BASE/config.toml".into(), - flag_ports_shift: 0, + arg_config: "$BASE/config.toml".into(), + arg_ports_shift: 0, flag_unsafe_expose: false, // -- Account Options - flag_unlock: Some("0xdeadbeefcafe0000000000000000000000000000".into()), - flag_password: vec!["~/.safe/password.file".into()], - flag_keys_iterations: 10240u32, + arg_unlock: Some("0xdeadbeefcafe0000000000000000000000000000".into()), + arg_password: vec!["~/.safe/password.file".into()], + arg_keys_iterations: 10240u32, flag_no_hardware_wallets: false, flag_fast_unlock: false, flag_force_ui: false, flag_no_ui: false, - flag_ui_port: 8180u16, - flag_ui_interface: "127.0.0.1".into(), - flag_ui_hosts: "none".into(), - flag_ui_path: "$HOME/.parity/signer".into(), + arg_ui_port: 8180u16, + arg_ui_interface: "127.0.0.1".into(), + arg_ui_hosts: "none".into(), + arg_ui_path: "$HOME/.parity/signer".into(), flag_ui_no_validation: false, // -- Networking Options flag_no_warp: false, - flag_port: 30303u16, - flag_min_peers: 25u16, - flag_max_peers: 50u16, - flag_max_pending_peers: 64u16, - flag_snapshot_peers: 0u16, - flag_allow_ips: "all".into(), - flag_nat: "any".into(), - flag_network_id: Some(1), - flag_bootnodes: Some("".into()), + arg_port: 30303u16, + arg_min_peers: 25u16, + arg_max_peers: 50u16, + arg_max_pending_peers: 64u16, + arg_snapshot_peers: 0u16, + arg_allow_ips: "all".into(), + arg_nat: "any".into(), + arg_network_id: Some(1), + arg_bootnodes: Some("".into()), flag_no_discovery: false, - flag_node_key: None, - flag_reserved_peers: Some("./path_to_file".into()), + arg_node_key: None, + arg_reserved_peers: Some("./path_to_file".into()), flag_reserved_only: false, flag_no_ancient_blocks: false, flag_no_serve_light: false, @@ -776,111 +1416,111 @@ mod tests { // -- API and Console Options // RPC flag_no_jsonrpc: false, - flag_jsonrpc_port: 8545u16, - flag_jsonrpc_interface: "local".into(), - flag_jsonrpc_cors: Some("null".into()), - flag_jsonrpc_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(), - flag_jsonrpc_hosts: "none".into(), - flag_jsonrpc_server_threads: None, - flag_jsonrpc_threads: 0, + arg_jsonrpc_port: 8545u16, + arg_jsonrpc_interface: "local".into(), + arg_jsonrpc_cors: Some("null".into()), + arg_jsonrpc_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(), + arg_jsonrpc_hosts: "none".into(), + arg_jsonrpc_server_threads: None, + arg_jsonrpc_threads: 0, // WS flag_no_ws: false, - flag_ws_port: 8546u16, - flag_ws_interface: "local".into(), - flag_ws_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(), - flag_ws_origins: "none".into(), - flag_ws_hosts: "none".into(), + arg_ws_port: 8546u16, + arg_ws_interface: "local".into(), + arg_ws_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(), + arg_ws_origins: "none".into(), + arg_ws_hosts: "none".into(), // IPC flag_no_ipc: false, - flag_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), - flag_ipc_apis: "web3,eth,net,parity,parity_accounts,personal,traces,rpc,secretstore".into(), + arg_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), + arg_ipc_apis: "web3,eth,net,parity,parity_accounts,personal,traces,rpc,secretstore".into(), // DAPPS - flag_dapps_path: "$HOME/.parity/dapps".into(), + arg_dapps_path: "$HOME/.parity/dapps".into(), flag_no_dapps: false, flag_no_secretstore: false, flag_no_secretstore_http: false, flag_no_secretstore_acl_check: false, - flag_secretstore_secret: None, - flag_secretstore_nodes: "".into(), - flag_secretstore_interface: "local".into(), - flag_secretstore_port: 8083u16, - flag_secretstore_http_interface: "local".into(), - flag_secretstore_http_port: 8082u16, - flag_secretstore_path: "$HOME/.parity/secretstore".into(), + arg_secretstore_secret: None, + arg_secretstore_nodes: "".into(), + arg_secretstore_interface: "local".into(), + arg_secretstore_port: 8083u16, + arg_secretstore_http_interface: "local".into(), + arg_secretstore_http_port: 8082u16, + arg_secretstore_path: "$HOME/.parity/secretstore".into(), // IPFS flag_ipfs_api: false, - flag_ipfs_api_port: 5001u16, - flag_ipfs_api_interface: "local".into(), - flag_ipfs_api_cors: Some("null".into()), - flag_ipfs_api_hosts: "none".into(), + arg_ipfs_api_port: 5001u16, + arg_ipfs_api_interface: "local".into(), + arg_ipfs_api_cors: Some("null".into()), + arg_ipfs_api_hosts: "none".into(), // -- Sealing/Mining Options - flag_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), - flag_engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + arg_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + arg_engine_signer: Some("0xdeadbeefcafe0000000000000000000000000001".into()), flag_force_sealing: true, - flag_reseal_on_txs: "all".into(), - flag_reseal_min_period: 4000u64, - flag_reseal_max_period: 60000u64, + arg_reseal_on_txs: "all".into(), + arg_reseal_min_period: 4000u64, + arg_reseal_max_period: 60000u64, flag_reseal_on_uncle: false, - flag_work_queue_size: 20usize, - flag_tx_gas_limit: Some("6283184".into()), - flag_tx_time_limit: Some(100u64), - flag_relay_set: "cheap".into(), - flag_min_gas_price: Some(0u64), - flag_usd_per_tx: "0.0025".into(), - flag_usd_per_eth: "auto".into(), - flag_price_update_period: "hourly".into(), - flag_gas_floor_target: "4700000".into(), - flag_gas_cap: "6283184".into(), - flag_extra_data: Some("Parity".into()), - flag_tx_queue_size: 8192usize, - flag_tx_queue_mem_limit: 2u32, - flag_tx_queue_gas: "off".into(), - flag_tx_queue_strategy: "gas_factor".into(), - flag_tx_queue_ban_count: 1u16, - flag_tx_queue_ban_time: 180u16, + arg_work_queue_size: 20usize, + arg_tx_gas_limit: Some("6283184".into()), + arg_tx_time_limit: Some(100u64), + arg_relay_set: "cheap".into(), + arg_min_gas_price: Some(0u64), + arg_usd_per_tx: "0.0025".into(), + arg_usd_per_eth: "auto".into(), + arg_price_update_period: "hourly".into(), + arg_gas_floor_target: "4700000".into(), + arg_gas_cap: "6283184".into(), + arg_extra_data: Some("Parity".into()), + arg_tx_queue_size: 8192usize, + arg_tx_queue_mem_limit: 2u32, + arg_tx_queue_gas: "off".into(), + arg_tx_queue_strategy: "gas_factor".into(), + arg_tx_queue_ban_count: 1u16, + arg_tx_queue_ban_time: 180u16, flag_remove_solved: false, - flag_notify_work: Some("http://localhost:3001".into()), + arg_notify_work: Some("http://localhost:3001".into()), flag_refuse_service_transactions: false, flag_stratum: false, - flag_stratum_interface: "local".to_owned(), - flag_stratum_port: 8008u16, - flag_stratum_secret: None, + arg_stratum_interface: "local".to_owned(), + arg_stratum_port: 8008u16, + arg_stratum_secret: None, // -- Footprint Options - flag_tracing: "auto".into(), - flag_pruning: "auto".into(), - flag_pruning_history: 64u64, - flag_pruning_memory: 500usize, - flag_cache_size_db: 64u32, - flag_cache_size_blocks: 8u32, - flag_cache_size_queue: 50u32, - flag_cache_size_state: 25u32, - flag_cache_size: Some(128), + arg_tracing: "auto".into(), + arg_pruning: "auto".into(), + arg_pruning_history: 64u64, + arg_pruning_memory: 500usize, + arg_cache_size_db: 64u32, + arg_cache_size_blocks: 8u32, + arg_cache_size_queue: 50u32, + arg_cache_size_state: 25u32, + arg_cache_size: Some(128), flag_fast_and_loose: false, - flag_db_compaction: "ssd".into(), - flag_fat_db: "auto".into(), + arg_db_compaction: "ssd".into(), + arg_fat_db: "auto".into(), flag_scale_verifiers: true, - flag_num_verifiers: Some(6), + arg_num_verifiers: Some(6), // -- Import/Export Options - flag_from: "1".into(), - flag_to: "latest".into(), - flag_format: None, + arg_export_blocks_from: "1".into(), + arg_export_blocks_to: "latest".into(), flag_no_seal_check: false, - flag_no_code: false, - flag_no_storage: false, - flag_min_balance: None, - flag_max_balance: None, + flag_export_state_no_code: false, + flag_export_state_no_storage: false, + arg_export_state_min_balance: None, + arg_export_state_max_balance: None, // -- Snapshot Optons - flag_at: "latest".into(), + arg_export_state_at: "latest".into(), + arg_snapshot_at: "latest".into(), flag_no_periodic_snapshot: false, // -- Virtual Machine Options @@ -888,49 +1528,51 @@ mod tests { // -- Whisper options. flag_whisper: false, - flag_whisper_pool_size: 20, + arg_whisper_pool_size: 20, // -- Legacy Options flag_geth: false, flag_testnet: false, flag_import_geth_keys: false, - flag_datadir: None, - flag_networkid: None, - flag_peers: None, - flag_nodekey: None, + arg_datadir: None, + arg_networkid: None, + arg_peers: None, + arg_nodekey: None, flag_nodiscover: false, flag_jsonrpc: false, flag_jsonrpc_off: false, flag_webapp: false, flag_dapps_off: false, flag_rpc: false, - flag_rpcaddr: None, - flag_rpcport: None, - flag_rpcapi: None, - flag_rpccorsdomain: None, + arg_rpcaddr: None, + arg_rpcport: None, + arg_rpcapi: None, + arg_rpccorsdomain: None, flag_ipcdisable: false, flag_ipc_off: false, - flag_ipcapi: None, - flag_ipcpath: None, - flag_gasprice: None, - flag_etherbase: None, - flag_extradata: None, - flag_cache: None, - flag_warp: Some(true), + arg_ipcapi: None, + arg_ipcpath: None, + arg_gasprice: None, + arg_etherbase: None, + arg_extradata: None, + arg_cache: None, // Legacy-Dapps - flag_dapps_port: Some(8080), - flag_dapps_interface: Some("local".into()), - flag_dapps_hosts: Some("none".into()), - flag_dapps_cors: None, - flag_dapps_user: Some("test_user".into()), - flag_dapps_pass: Some("test_pass".into()), - flag_dapps_apis_all: None, + arg_dapps_port: Some(8080), + arg_dapps_interface: Some("local".into()), + arg_dapps_hosts: Some("none".into()), + arg_dapps_cors: None, + arg_dapps_user: Some("test_user".into()), + arg_dapps_pass: Some("test_pass".into()), + flag_dapps_apis_all: false, + + // -- Internal Options + flag_can_restart: false, // -- Miscellaneous Options - flag_ntp_servers: "0.parity.pool.ntp.org:123,1.parity.pool.ntp.org:123,2.parity.pool.ntp.org:123,3.parity.pool.ntp.org:123".into(), + arg_ntp_servers: "0.parity.pool.ntp.org:123,1.parity.pool.ntp.org:123,2.parity.pool.ntp.org:123,3.parity.pool.ntp.org:123".into(), flag_version: false, - flag_logging: Some("own_tx=trace".into()), - flag_log_file: Some("/var/log/parity.log".into()), + arg_logging: Some("own_tx=trace".into()), + arg_log_file: Some("/var/log/parity.log".into()), flag_no_color: false, flag_no_config: false, }); @@ -938,9 +1580,9 @@ mod tests { #[test] fn should_parse_config_and_return_errors() { - let config1 = Args::parse_config(include_str!("./config.invalid1.toml")); - let config2 = Args::parse_config(include_str!("./config.invalid2.toml")); - let config3 = Args::parse_config(include_str!("./config.invalid3.toml")); + let config1 = Args::parse_config(include_str!("./tests/config.invalid1.toml")); + let config2 = Args::parse_config(include_str!("./tests/config.invalid2.toml")); + let config3 = Args::parse_config(include_str!("./tests/config.invalid3.toml")); match (config1, config2, config3) { (Err(ArgsError::Decode(_)), Err(ArgsError::Decode(_)), Err(ArgsError::Decode(_))) => {}, @@ -952,7 +1594,7 @@ mod tests { #[test] fn should_deserialize_toml_file() { - let config: Config = toml::from_str(include_str!("./config.toml")).unwrap(); + let config: Config = toml::from_str(include_str!("./tests/config.toml")).unwrap(); assert_eq!(config, Config { parity: Some(Operating { diff --git a/parity/cli/config.full.toml b/parity/cli/tests/config.full.toml similarity index 100% rename from parity/cli/config.full.toml rename to parity/cli/tests/config.full.toml diff --git a/parity/cli/config.invalid1.toml b/parity/cli/tests/config.invalid1.toml similarity index 100% rename from parity/cli/config.invalid1.toml rename to parity/cli/tests/config.invalid1.toml diff --git a/parity/cli/config.invalid2.toml b/parity/cli/tests/config.invalid2.toml similarity index 100% rename from parity/cli/config.invalid2.toml rename to parity/cli/tests/config.invalid2.toml diff --git a/parity/cli/config.invalid3.toml b/parity/cli/tests/config.invalid3.toml similarity index 100% rename from parity/cli/config.invalid3.toml rename to parity/cli/tests/config.invalid3.toml diff --git a/parity/cli/config.toml b/parity/cli/tests/config.toml similarity index 100% rename from parity/cli/config.toml rename to parity/cli/tests/config.toml diff --git a/parity/cli/usage.rs b/parity/cli/usage.rs index 182efca92..b6a4b723e 100644 --- a/parity/cli/usage.rs +++ b/parity/cli/usage.rs @@ -22,7 +22,7 @@ macro_rules! println_stderr( ); macro_rules! otry { - ($e: expr) => ( + ($e:expr) => ( match $e { Some(ref v) => v, None => { @@ -31,21 +31,107 @@ macro_rules! otry { } ) } + +macro_rules! if_option { + (Option<$type:ty>, THEN {$($then:tt)*} ELSE {$($otherwise:tt)*}) => ( + $($then)* + ); + ($type:ty, THEN {$($then:tt)*} ELSE {$($otherwise:tt)*}) => ( + $($otherwise)* + ); +} + +macro_rules! if_vec { + (Vec<$type:ty>, THEN {$($then:tt)*} ELSE {$($otherwise:tt)*}) => ( + $($then)* + ); + ($type:ty, THEN {$($then:tt)*} ELSE {$($otherwise:tt)*}) => ( + $($otherwise)* + ); +} + +macro_rules! if_option_vec { + (Option>, THEN {$then:expr} ELSE {$otherwise:expr}) => ( + $then + ); + (Option<$type:ty>, THEN {$then:expr} ELSE {$otherwise:expr}) => ( + $otherwise + ); +} + +macro_rules! inner_option_type { + (Option<$type:ty>) => ( + $type + ) +} + +macro_rules! inner_vec_type { + (Vec<$type:ty>) => ( + $type + ) +} + +macro_rules! inner_option_vec_type { + (Option>) => ( + String + ) +} + +macro_rules! usage_with_ident { + ($name:expr, $usage:expr, $help:expr) => ( + if $usage.contains("<") { + format!("<{}> {} '{}'",$name, $usage, $help) + } else { + format!("[{}] {} '{}'",$name, $usage, $help) + } + ); +} + +macro_rules! underscore_to_hyphen { + ($e:expr) => ( + str::replace($e, "_", "-") + ) +} + macro_rules! usage { ( { $( - $field_a:ident : $typ_a:ty, + CMD $subc:ident + { + $subc_help:expr, + + $( + CMD $subc_subc:ident + { + $subc_subc_help:expr, + $( + FLAG $subc_subc_flag:ident : (bool) = false, $subc_subc_flag_usage:expr, $subc_subc_flag_help:expr, + )* + $( + ARG $subc_subc_arg:ident : ($($subc_subc_arg_type_tt:tt)+) = $subc_subc_arg_default:expr, $subc_subc_arg_usage:expr, $subc_subc_arg_help:expr, + )* + } + )* + + $( + FLAG $subc_flag:ident : (bool) = false, $subc_flag_usage:expr, $subc_flag_help:expr, + )* + $( + ARG $subc_arg:ident : ($($subc_arg_type_tt:tt)+) = $subc_arg_default:expr, $subc_arg_usage:expr, $subc_arg_help:expr, + )* + } )* } { $( - $field:ident : $typ:ty = $default:expr, or $from_config:expr, - )* - } - { - $( - $field_s:ident : $typ_s:ty, display $default_s:expr, or $from_config_s:expr, + [$group_name:expr] + $( + FLAG $flag:ident : (bool) = false, or $flag_from_config:expr, $flag_usage:expr, $flag_help:expr, + )* + $( + ARG $arg:ident : ($($arg_type_tt:tt)+) = $arg_default:expr, or $arg_from_config:expr, $arg_usage:expr, $arg_help:expr, + )* )* } ) => { @@ -53,12 +139,17 @@ macro_rules! usage { use std::{fs, io, process}; use std::io::{Read, Write}; use util::version; - use docopt::{Docopt, Error as DocoptError}; + use clap::{Arg, App, SubCommand, AppSettings, Error as ClapError}; use helpers::replace_home; + use std::ffi::OsStr; + use std::collections::HashMap; + + #[cfg(test)] + use regex::Regex; #[derive(Debug)] pub enum ArgsError { - Docopt(DocoptError), + Clap(ClapError), Decode(toml::de::Error), Config(String, io::Error), } @@ -66,7 +157,7 @@ macro_rules! usage { impl ArgsError { pub fn exit(self) -> ! { match self { - ArgsError::Docopt(e) => e.exit(), + ArgsError::Clap(e) => e.exit(), ArgsError::Decode(e) => { println_stderr!("You might have supplied invalid parameters in config file."); println_stderr!("{}", e); @@ -81,9 +172,9 @@ macro_rules! usage { } } - impl From for ArgsError { - fn from(e: DocoptError) -> Self { - ArgsError::Docopt(e) + impl From for ArgsError { + fn from(e: ClapError) -> Self { + ArgsError::Clap(e) } } @@ -96,15 +187,33 @@ macro_rules! usage { #[derive(Debug, PartialEq)] pub struct Args { $( - pub $field_a: $typ_a, + pub $subc: bool, + + $( + pub $subc_subc: bool, + $( + pub $subc_subc_flag: bool, + )* + $( + pub $subc_subc_arg: $($subc_subc_arg_type_tt)+, + )* + )* + + $( + pub $subc_flag: bool, + )* + $( + pub $subc_arg: $($subc_arg_type_tt)+, + )* )* $( - pub $field: $typ, - )* - - $( - pub $field_s: $typ_s, + $( + pub $flag: bool, + )* + $( + pub $arg: $($arg_type_tt)+, + )* )* } @@ -112,15 +221,32 @@ macro_rules! usage { fn default() -> Self { Args { $( - $field_a: Default::default(), + $subc: Default::default(), + $( + $subc_subc: Default::default(), + $( + $subc_subc_flag: Default::default(), + )* + $( + $subc_subc_arg: Default::default(), + )* + )* + + $( + $subc_flag: Default::default(), + )* + $( + $subc_arg: Default::default(), + )* )* $( - $field: $default.into(), - )* - - $( - $field_s: Default::default(), + $( + $flag: Default::default(), + )* + $( + $arg: Default::default(), + )* )* } } @@ -129,13 +255,46 @@ macro_rules! usage { #[derive(Default, Debug, PartialEq, Clone, Deserialize)] struct RawArgs { $( - $field_a: $typ_a, + $subc: bool, + + $( + $subc_subc: bool, + $( + $subc_subc_flag: bool, + )* + $( + $subc_subc_arg: if_option!( + $($subc_subc_arg_type_tt)+, + THEN { $($subc_subc_arg_type_tt)+ } + ELSE { Option<$($subc_subc_arg_type_tt)+> } + ), + )* + )* + + $( + $subc_flag: bool, + )* + $( + $subc_arg: if_option!( + $($subc_arg_type_tt)+, + THEN { $($subc_arg_type_tt)+ } + ELSE { Option<$($subc_arg_type_tt)+> } + ), + )* + )* $( - $field: Option<$typ>, - )* - $( - $field_s: Option<$typ_s>, + $( + $flag: bool, + )* + + $( + $arg: if_option!( + $($arg_type_tt)+, + THEN { $($arg_type_tt)+ } + ELSE { Option<$($arg_type_tt)+> } + ), + )* )* } @@ -149,9 +308,9 @@ macro_rules! usage { return Ok(raw_args.into_args(Config::default())); } - let config_file = raw_args.flag_config.clone().unwrap_or_else(|| raw_args.clone().into_args(Config::default()).flag_config); + let config_file = raw_args.arg_config.clone().unwrap_or_else(|| raw_args.clone().into_args(Config::default()).arg_config); let config_file = replace_home(&::dir::default_data_path(), &config_file); - match (fs::File::open(&config_file), raw_args.flag_config.clone()) { + match (fs::File::open(&config_file), raw_args.arg_config.clone()) { // Load config file (Ok(mut file), _) => { println_stderr!("Loading config file from {}", &config_file); @@ -178,7 +337,7 @@ macro_rules! usage { #[cfg(test)] fn parse_with_config>(command: &[S], config: Config) -> Result { - RawArgs::parse(command).map(|raw| raw.into_args(config)).map_err(ArgsError::Docopt) + RawArgs::parse(command).map(|raw| raw.into_args(config)).map_err(ArgsError::Clap) } fn parse_config(config: &str) -> Result { @@ -188,41 +347,346 @@ macro_rules! usage { pub fn print_version() -> String { format!(include_str!("./version.txt"), version()) } + + #[allow(unused_mut)] // subc_subc_exist may be assigned true by the macro + #[allow(unused_assignments)] // Rust issue #22630 + pub fn print_help() -> String { + let mut help : String = include_str!("./usage_header.txt").to_owned(); + + help.push_str("\n\n"); + + // Subcommands + help.push_str("parity [options]\n"); + $( + { + let mut subc_subc_exist = false; + + $( + subc_subc_exist = true; + let subc_subc_usages : Vec<&str> = vec![ + $( + concat!("[",$subc_subc_flag_usage,"]"), + )* + $( + $subc_subc_arg_usage, + )* + ]; + + if subc_subc_usages.is_empty() { + help.push_str(&format!("parity [options] {} {}\n", underscore_to_hyphen!(&stringify!($subc)[4..]), underscore_to_hyphen!(&stringify!($subc_subc)[stringify!($subc).len()+1..]))); + } else { + help.push_str(&format!("parity [options] {} {} {}\n", underscore_to_hyphen!(&stringify!($subc)[4..]), underscore_to_hyphen!(&stringify!($subc_subc)[stringify!($subc).len()+1..]), subc_subc_usages.join(" "))); + } + )* + + // Print the subcommand on its own only if it has no subsubcommands + if !subc_subc_exist { + let subc_usages : Vec<&str> = vec![ + $( + concat!("[",$subc_flag_usage,"]"), + )* + $( + $subc_arg_usage, + )* + ]; + + if subc_usages.is_empty() { + help.push_str(&format!("parity [options] {}\n", underscore_to_hyphen!(&stringify!($subc)[4..]))); + } else { + help.push_str(&format!("parity [options] {} {}\n", underscore_to_hyphen!(&stringify!($subc)[4..]), subc_usages.join(" "))); + } + } + } + )* + + // Arguments and flags + $( + help.push_str("\n"); + help.push_str($group_name); help.push_str(":\n"); + + $( + help.push_str(&format!("\t{}\n\t\t{}\n", $flag_usage, $flag_help)); + )* + + $( + if_option!( + $($arg_type_tt)+, + THEN { + if_option_vec!( + $($arg_type_tt)+, + THEN { + help.push_str(&format!("\t{}\n\t\t{} (default: {:?})\n", $arg_usage, $arg_help, {let x : inner_option_type!($($arg_type_tt)+)> = $arg_default; x})) + } + ELSE { + help.push_str(&format!("\t{}\n\t\t{}{}\n", $arg_usage, $arg_help, $arg_default.map(|x: inner_option_type!($($arg_type_tt)+)| format!(" (default: {})",x)).unwrap_or("".to_owned()))) + } + ) + } + ELSE { + if_vec!( + $($arg_type_tt)+, + THEN { + help.push_str(&format!("\t{}\n\t\t{} (default: {:?})\n", $arg_usage, $arg_help, {let x : $($arg_type_tt)+ = $arg_default; x})) + } + ELSE { + help.push_str(&format!("\t{}\n\t\t{} (default: {})\n", $arg_usage, $arg_help, $arg_default)) + } + ) + } + ); + )* + + )* + + help + } } impl RawArgs { fn into_args(self, config: Config) -> Args { let mut args = Args::default(); $( - args.$field_a = self.$field_a; + args.$subc = self.$subc; + + $( + args.$subc_subc = self.$subc_subc; + $( + args.$subc_subc_flag = self.$subc_subc_flag; + )* + $( + args.$subc_subc_arg = if_option!( + $($subc_subc_arg_type_tt)+, + THEN { self.$subc_subc_arg.or($subc_subc_arg_default) } + ELSE { self.$subc_subc_arg.unwrap_or($subc_subc_arg_default.into()) } + ); + )* + )* + + $( + args.$subc_flag = self.$subc_flag; + )* + $( + args.$subc_arg = if_option!( + $($subc_arg_type_tt)+, + THEN { self.$subc_arg.or($subc_arg_default) } + ELSE { self.$subc_arg.unwrap_or($subc_arg_default.into()) } + ); + )* )* + $( - args.$field = self.$field.or_else(|| $from_config(&config)).unwrap_or_else(|| $default.into()); - )* - $( - args.$field_s = self.$field_s.or_else(|| $from_config_s(&config)).unwrap_or(None); + $( + args.$flag = self.$flag || $flag_from_config(&config).unwrap_or(false); + )* + $( + args.$arg = if_option!( + $($arg_type_tt)+, + THEN { self.$arg.or_else(|| $arg_from_config(&config)).or_else(|| $arg_default.into()) } + ELSE { self.$arg.or_else(|| $arg_from_config(&config)).unwrap_or_else(|| $arg_default.into()) } + ); + )* )* args } - pub fn parse>(command: &[S]) -> Result { - Docopt::new(Self::usage()).and_then(|d| d.argv(command).deserialize()) + #[allow(unused_variables)] // the submatches of arg-less subcommands aren't used + pub fn parse>(command: &[S]) -> Result { + + let usages = vec![ + $( + $( + usage_with_ident!(stringify!($arg), $arg_usage, $arg_help), + )* + $( + usage_with_ident!(stringify!($flag), $flag_usage, $flag_help), + )* + )* + ]; + + // Hash of subc|subc_subc => Vec + let mut subc_usages = HashMap::new(); + $( + { + let this_subc_usages = vec![ + $( + usage_with_ident!(stringify!($subc_flag), $subc_flag_usage, $subc_flag_help), + )* + $( + usage_with_ident!(stringify!($subc_arg), $subc_arg_usage, $subc_arg_help), + )* + ]; + + subc_usages.insert(stringify!($subc),this_subc_usages); + + $( + { + let this_subc_subc_usages = vec![ + $( + usage_with_ident!(stringify!($subc_subc_flag), $subc_subc_flag_usage, $subc_subc_flag_help), + )* + $( + usage_with_ident!(stringify!($subc_subc_arg), $subc_subc_arg_usage, $subc_subc_arg_help), + )* + ]; + + subc_usages.insert(stringify!($subc_subc), this_subc_subc_usages); + } + )* + } + )* + + let matches = App::new("Parity") + .global_setting(AppSettings::VersionlessSubcommands) + .global_setting(AppSettings::AllowLeadingHyphen) // allow for example --allow-ips -10.0.0.0/8 + .global_setting(AppSettings::DisableHelpSubcommand) + .help(Args::print_help().as_ref()) + .args(&usages.iter().map(|u| Arg::from_usage(u).use_delimiter(false)).collect::>()) + $( + .subcommand( + SubCommand::with_name(&underscore_to_hyphen!(&stringify!($subc)[4..])) + .about($subc_help) + .args(&subc_usages.get(stringify!($subc)).unwrap().iter().map(|u| Arg::from_usage(u).use_delimiter(false)).collect::>()) + $( + .setting(AppSettings::SubcommandRequired) // prevent from running `parity account` + .subcommand( + SubCommand::with_name(&underscore_to_hyphen!(&stringify!($subc_subc)[stringify!($subc).len()+1..])) + .about($subc_subc_help) + .args(&subc_usages.get(stringify!($subc_subc)).unwrap().iter().map(|u| Arg::from_usage(u).use_delimiter(false)).collect::>()) + ) + )* + ) + )* + .get_matches_from_safe(command.iter().map(|x| OsStr::new(x.as_ref())))?; + + let mut raw_args : RawArgs = Default::default(); + $( + $( + raw_args.$flag = matches.is_present(stringify!($flag)); + )* + $( + raw_args.$arg = if_option!( + $($arg_type_tt)+, + THEN { + if_option_vec!( + $($arg_type_tt)+, + THEN { values_t!(matches, stringify!($arg), inner_option_vec_type!($($arg_type_tt)+)).ok() } + ELSE { value_t!(matches, stringify!($arg), inner_option_type!($($arg_type_tt)+)).ok() } + ) + } + ELSE { + if_vec!( + $($arg_type_tt)+, + THEN { values_t!(matches, stringify!($arg), inner_vec_type!($($arg_type_tt)+)).ok() } + ELSE { value_t!(matches, stringify!($arg), $($arg_type_tt)+).ok() } + ) + } + ); + )* + )* + + // Subcommands + $( + if let Some(submatches) = matches.subcommand_matches(&underscore_to_hyphen!(&stringify!($subc)[4..])) { + raw_args.$subc = true; + + // Subcommand flags + $( + raw_args.$subc_flag = submatches.is_present(&stringify!($subc_flag)); + )* + // Subcommand arguments + $( + raw_args.$subc_arg = if_option!( + $($subc_arg_type_tt)+, + THEN { + if_option_vec!( + $($subc_arg_type_tt)+, + THEN { values_t!(submatches, stringify!($subc_arg), inner_option_vec_type!($($subc_arg_type_tt)+)).ok() } + ELSE { value_t!(submatches, stringify!($subc_arg), inner_option_type!($($subc_arg_type_tt)+)).ok() } + ) + } + ELSE { + if_vec!( + $($subc_arg_type_tt)+, + THEN { values_t!(submatches, stringify!($subc_arg), inner_vec_type!($($subc_arg_type_tt)+)).ok() } + ELSE { value_t!(submatches, stringify!($subc_arg), $($subc_arg_type_tt)+).ok() } + ) + } + ); + )* + + // Sub-subcommands + $( + if let Some(subsubmatches) = submatches.subcommand_matches(&underscore_to_hyphen!(&stringify!($subc_subc)[stringify!($subc).len()+1..])) { + raw_args.$subc_subc = true; + + // Sub-subcommand flags + $( + raw_args.$subc_subc_flag = subsubmatches.is_present(&stringify!($subc_subc_flag)); + )* + // Sub-subcommand arguments + $( + raw_args.$subc_subc_arg = if_option!( + $($subc_subc_arg_type_tt)+, + THEN { + if_option_vec!( + $($subc_subc_arg_type_tt)+, + THEN { values_t!(subsubmatches, stringify!($subc_subc_arg), inner_option_vec_type!($($subc_subc_arg_type_tt)+)).ok() } + ELSE { value_t!(subsubmatches, stringify!($subc_subc_arg), inner_option_type!($($subc_subc_arg_type_tt)+)).ok() } + ) + } + ELSE { + if_vec!( + $($subc_subc_arg_type_tt)+, + THEN { values_t!(subsubmatches, stringify!($subc_subc_arg), inner_vec_type!($($subc_subc_arg_type_tt)+)).ok() } + ELSE { value_t!(subsubmatches, stringify!($subc_subc_arg), $($subc_subc_arg_type_tt)+).ok() } + ) + } + ); + )* + } + else { + raw_args.$subc_subc = false; + } + )* + } + else { + raw_args.$subc = false; + } + )* + + Ok(raw_args) } - fn usage() -> String { - format!( - include_str!("./usage.txt"), + } + + #[test] + fn usages_valid() { + let re = Regex::new(r"^(?:(-[a-zA-Z-]+, )?--[a-z-]+(=\[[a-zA-Z]+\](\.\.\.)?|=<[a-zA-Z]+>(\.\.\.)?)?)|(?:\[[a-zA-Z-]+\])(\.\.\.)?|(?:<[a-zA-Z-]+>)(\.\.\.)?$").unwrap(); + + let usages = vec![ + $( $( - $field={ let v: $typ = $default.into(); v }, - // Uncomment this to debug - // "named argument never used" error - // $field = $default, + $( + $subc_subc_arg_usage, + )* )* $( - $field_s = $default_s, + $subc_arg_usage, )* - ) + )* + $( + $( + $flag_usage, + )* + $( + $arg_usage, + )* + )* + ]; + + for usage in &usages { + assert!(re.is_match(usage)); } } - }; + } } diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt deleted file mode 100644 index dc1205ecf..000000000 --- a/parity/cli/usage.txt +++ /dev/null @@ -1,499 +0,0 @@ -Parity. Ethereum Client. - By Wood/Paronyan/Kotewicz/Drwięga/Volf et al. - Copyright 2015, 2016, 2017 Parity Technologies (UK) Ltd - -Usage: - parity [options] - parity ui [options] - parity dapp [options] - parity daemon [options] - parity account (new | list ) [options] - parity account import ... [options] - parity wallet import --password FILE [options] - parity import [ ] [options] - parity export (blocks | state) [ ] [options] - parity signer new-token [options] - parity signer list [options] - parity signer sign [ ] [ --password FILE ] [options] - parity signer reject [options] - parity snapshot [options] - parity restore [ ] [options] - parity tools hash - parity db kill [options] - -Operating Options: - --mode MODE Set the operating mode. MODE can be one of: - last - Uses the last-used mode, active if none. - active - Parity continuously syncs the chain. - passive - Parity syncs initially, then sleeps and - wakes regularly to resync. - dark - Parity syncs only when the RPC is active. - offline - Parity doesn't sync. (default: {flag_mode}). - --mode-timeout SECS Specify the number of seconds before inactivity - timeout occurs when mode is dark or passive - (default: {flag_mode_timeout}). - --mode-alarm SECS Specify the number of seconds before auto sleep - reawake timeout occurs when mode is passive - (default: {flag_mode_alarm}). - --auto-update SET Set a releases set to automatically update and - install. - all - All updates in the our release track. - critical - Only consensus/security updates. - none - No updates will be auto-installed. - (default: {flag_auto_update}). - --release-track TRACK Set which release track we should use for updates. - stable - Stable releases. - beta - Beta releases. - nightly - Nightly releases (unstable). - testing - Testing releases (do not use). - current - Whatever track this executable was - released on (default: {flag_release_track}). - --public-node Start Parity as a public web server. Account storage - and transaction signing will be delegated to the UI. - (default: {flag_public_node}). - --no-download Normally new releases will be downloaded ready for - updating. This disables it. Not recommended. - (default: {flag_no_download}). - --no-consensus Force the binary to run even if there are known - issues regarding consensus. Not recommended. - (default: {flag_no_consensus}). - --force-direct Run the originally installed version of Parity, - ignoring any updates that have since been installed. - --chain CHAIN Specify the blockchain type. CHAIN may be either a - JSON chain specification file or olympic, frontier, - homestead, mainnet, morden, ropsten, classic, expanse, - testnet, kovan or dev (default: {flag_chain}). - -d --base-path PATH Specify the base data storage path. - (default: {flag_base_path}). - --db-path PATH Specify the database directory path - (default: {flag_db_path}). - --keys-path PATH Specify the path for JSON key files to be found - (default: {flag_keys_path}). - --identity NAME Specify your node's name. (default: {flag_identity}) - --light Experimental: run in light client mode. Light clients - synchronize a bare minimum of data and fetch necessary - data on-demand from the network. Much lower in storage, - potentially higher in bandwidth. Has no effect with - subcommands (default: {flag_light}). - -Convenience Options: --c --config CONFIG Specify a configuration. CONFIG may be either a - configuration file or a preset: dev, insecure, dev-insecure, - mining, or non-standard-ports. - (default: {flag_config}). - --ports-shift SHIFT Add SHIFT to all port numbers Parity is listening on. - Includes network port and all servers (RPC, WebSockets, UI, IPFS, SecretStore). - (default: {flag_ports_shift}) - --unsafe-expose All servers will listen on external interfaces and will - be remotely accessible. It's equivalent with setting - the following: --{{ws,jsonrpc,ui,ipfs,secret_store,stratum}}-interface=all --*-hosts=all - This option is UNSAFE and should be used with great care! - (default: {flag_unsafe_expose}) - -Account Options: - --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution. - ACCOUNTS is a comma-delimited list of addresses. - Implies --no-ui. (default: {flag_unlock:?}) - --password FILE Provide a file containing a password for unlocking - an account. Leading and trailing whitespace is trimmed. - (default: {flag_password:?}) - --keys-iterations NUM Specify the number of iterations to use when - deriving key from the password (bigger is more - secure) (default: {flag_keys_iterations}). - --no-hardware-wallets Disables hardware wallet support. (default: {flag_no_hardware_wallets}) - --fast-unlock Use drasticly faster unlocking mode. This setting causes - raw secrets to be stored unprotected in memory, - so use with care. (default: {flag_fast_unlock}) - -UI Options: - --force-ui Enable Trusted UI WebSocket endpoint, - even when --unlock is in use. (default: {flag_force_ui}) - --no-ui Disable Trusted UI WebSocket endpoint. - (default: {flag_no_ui}) - --ui-port PORT Specify the port of Trusted UI server - (default: {flag_ui_port}). - --ui-interface IP Specify the hostname portion of the Trusted UI - server, IP should be an interface's IP address, - or local (default: {flag_ui_interface}). - --ui-hosts HOSTS List of allowed Host header values. This option will - validate the Host header sent by the browser, it - is additional security against some attack - vectors. Special options: "all", "none", - (default: {flag_ui_hosts}). - --ui-path PATH Specify directory where Trusted UIs tokens should - be stored. (default: {flag_ui_path}) - --ui-no-validation Disable Origin and Host headers validation for - Trusted UI. WARNING: INSECURE. Used only for - development. (default: {flag_ui_no_validation}) - -Networking Options: - --no-warp Disable syncing from the snapshot over the network. (default: {flag_no_warp}) - --port PORT Override the port on which the node should listen - (default: {flag_port}). - --min-peers NUM Try to maintain at least NUM peers (default: {flag_min_peers}). - --max-peers NUM Allow up to NUM peers (default: {flag_max_peers}). - --snapshot-peers NUM Allow additional NUM peers for a snapshot sync - (default: {flag_snapshot_peers}). - --nat METHOD Specify method to use for determining public - address. Must be one of: any, none, upnp, - extip: (default: {flag_nat}). - --network-id INDEX Override the network identifier from the chain we - are on. (default: {flag_network_id:?}) - --bootnodes NODES Override the bootnodes from our chain. NODES should - be comma-delimited enodes. (default: {flag_bootnodes:?}) - --no-discovery Disable new peer discovery. (default: {flag_no_discovery}) - --node-key KEY Specify node secret key, either as 64-character hex - string or input to SHA3 operation. (default: {flag_node_key:?}) - --reserved-peers FILE Provide a file containing enodes, one per line. - These nodes will always have a reserved slot on top - of the normal maximum peers. (default: {flag_reserved_peers:?}) - --reserved-only Connect only to reserved nodes. (default: {flag_reserved_only}) - --allow-ips FILTER Filter outbound connections. FILTER can be one of: - private - connect to private network IP addresses only; - public - connect to public network IP addresses only; - all - connect to any IP address; - none - block all (for use with a custom filter as below); - a custom filter list in the format: "private ip_range1 -ip_range2 ...". - Where ip_range1 would be allowed and ip_range2 blocked; - Custom blocks ("-ip_range") override custom allows ("ip_range"); - (default: {flag_allow_ips}). - --max-pending-peers NUM Allow up to NUM pending connections. (default: {flag_max_pending_peers}) - --no-ancient-blocks Disable downloading old blocks after snapshot restoration - or warp sync. (default: {flag_no_ancient_blocks}) - --no-serve-light Disable serving of light peers. (default: {flag_no_serve_light}) - -API and Console Options: - --no-jsonrpc Disable the JSON-RPC API server. (default: {flag_no_jsonrpc}) - --jsonrpc-port PORT Specify the port portion of the JSONRPC API server - (default: {flag_jsonrpc_port}). - --jsonrpc-interface IP Specify the hostname portion of the JSONRPC API - server, IP should be an interface's IP address, or - all (all interfaces) or local (default: {flag_jsonrpc_interface}). - --jsonrpc-cors URL Specify CORS header for JSON-RPC API responses. - (default: {flag_jsonrpc_cors:?}) - --jsonrpc-apis APIS Specify the APIs available through the JSONRPC - interface. APIS is a comma-delimited list of API - name. Possible name are all, safe, web3, eth, net, personal, - parity, parity_set, traces, rpc, parity_accounts. - You can also disable a specific API by putting '-' in the front: all,-personal - (default: {flag_jsonrpc_apis}). - --jsonrpc-hosts HOSTS List of allowed Host header values. This option will - validate the Host header sent by the browser, it - is additional security against some attack - vectors. Special options: "all", "none", - (default: {flag_jsonrpc_hosts}). - --jsonrpc-server-threads NUM Enables experimental faster implementation of JSON-RPC server. - Requires Dapps server to be disabled - using --no-dapps. (default: {flag_jsonrpc_server_threads:?}) - --jsonrpc-threads THREADS Turn on additional processing threads in all RPC servers. - Setting this to non-zero value allows parallel cpu-heavy queries - execution. (default: {flag_jsonrpc_threads}) - - --no-ws Disable the WebSockets server. (default: {flag_no_ws}) - --ws-port PORT Specify the port portion of the WebSockets server - (default: {flag_ws_port}). - --ws-interface IP Specify the hostname portion of the WebSockets - server, IP should be an interface's IP address, or - all (all interfaces) or local (default: {flag_ws_interface}). - --ws-apis APIS Specify the APIs available through the WebSockets - interface. APIS is a comma-delimited list of API - name. Possible name are web3, eth, pubsub, net, personal, - parity, parity_set, traces, rpc, parity_accounts. - (default: {flag_ws_apis}). - --ws-origins URL Specify Origin header values allowed to connect. - Special options: "all", "none". - (default: {flag_ws_origins}) - --ws-hosts HOSTS List of allowed Host header values. This option will - validate the Host header sent by the browser, it - is additional security against some attack - vectors. Special options: "all", "none", - (default: {flag_ws_hosts}). - - --no-ipc Disable JSON-RPC over IPC service. (default: {flag_no_ipc}) - --ipc-path PATH Specify custom path for JSON-RPC over IPC service - (default: {flag_ipc_path}). - --ipc-apis APIS Specify custom API set available via JSON-RPC over - IPC (default: {flag_ipc_apis}). - - --no-dapps Disable the Dapps server (e.g. status page). (default: {flag_no_dapps}) - --dapps-path PATH Specify directory where dapps should be installed. - (default: {flag_dapps_path}) - --ipfs-api Enable IPFS-compatible HTTP API. (default: {flag_ipfs_api}) - --ipfs-api-port PORT Configure on which port the IPFS HTTP API should listen. - (default: {flag_ipfs_api_port}) - --ipfs-api-interface IP Specify the hostname portion of the IPFS API server, - IP should be an interface's IP address or local. - (default: {flag_ipfs_api_interface}) - --ipfs-api-cors URL Specify CORS header for IPFS API responses. - (default: {flag_ipfs_api_cors:?}) - --ipfs-api-hosts HOSTS List of allowed Host header values. This option will - validate the Host header sent by the browser, it - is additional security against some attack - vectors. Special options: "all", "none" - (default: {flag_ipfs_api_hosts}). - -Secret Store Options: - --no-secretstore Disable Secret Store functionality. (default: {flag_no_secretstore}) - --no-secretstore-http Disable Secret Store HTTP API. (default: {flag_no_secretstore_http}) - --no-acl-check Disable ACL check (useful for test environments). (default: {flag_no_secretstore_acl_check}) - --secretstore-secret SECRET Hex-encoded secret key of this node. - (required, default: {flag_secretstore_secret:?}). - --secretstore-nodes NODES Comma-separated list of other secret store cluster nodes in form - NODE_PUBLIC_KEY_IN_HEX@NODE_IP_ADDR:NODE_PORT. - (required, default: {flag_secretstore_nodes}). - --secretstore-interface IP Specify the hostname portion for listening to Secret Store Key Server - internal requests, IP should be an interface's IP address, or local - (default: {flag_secretstore_interface}). - --secretstore-port PORT Specify the port portion for listening to Secret Store Key Server - internal requests (default: {flag_secretstore_port}). - --secretstore-http-interface IP Specify the hostname portion for listening to Secret Store Key Server - HTTP requests, IP should be an interface's IP address, or local - (default: {flag_secretstore_http_interface}). - --secretstore-http-port PORT Specify the port portion for listening to Secret Store Key Server - HTTP requests (default: {flag_secretstore_http_port}). - --secretstore-path PATH Specify directory where Secret Store should save its data. - (default: {flag_secretstore_path}). - -Sealing/Mining Options: - --author ADDRESS Specify the block author (aka "coinbase") address - for sending block rewards from sealed blocks. - NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION. - (default: {flag_author:?}) - --engine-signer ADDRESS Specify the address which should be used to - sign consensus messages and issue blocks. - Relevant only to non-PoW chains. - (default: {flag_engine_signer:?}) - --force-sealing Force the node to author new blocks as if it were - always sealing/mining. - (default: {flag_force_sealing}) - --reseal-on-txs SET Specify which transactions should force the node - to reseal a block. SET is one of: - none - never reseal on new transactions; - own - reseal only on a new local transaction; - ext - reseal only on a new external transaction; - all - reseal on all new transactions - (default: {flag_reseal_on_txs}). - --reseal-on-uncle Force the node to author new blocks when a new uncle - block is imported. - (default: {flag_reseal_on_uncle}) - --reseal-min-period MS Specify the minimum time between reseals from - incoming transactions. MS is time measured in - milliseconds (default: {flag_reseal_min_period}). - --reseal-max-period MS Specify the maximum time since last block to enable - force-sealing. MS is time measured in - milliseconds (default: {flag_reseal_max_period}). - --work-queue-size ITEMS Specify the number of historical work packages - which are kept cached lest a solution is found for - them later. High values take more memory but result - in fewer unusable solutions (default: {flag_work_queue_size}). - --tx-gas-limit GAS Apply a limit of GAS as the maximum amount of gas - a single transaction may have for it to be mined. - (default: {flag_tx_gas_limit:?}) - --tx-time-limit MS Maximal time for processing single transaction. - If enabled senders/recipients/code of transactions - offending the limit will be banned from being included - in transaction queue for 180 seconds. - (default: {flag_tx_time_limit:?}) - --relay-set SET Set of transactions to relay. SET may be: - cheap - Relay any transaction in the queue (this - may include invalid transactions); - strict - Relay only executed transactions (this - guarantees we don't relay invalid transactions, but - means we relay nothing if not mining); - lenient - Same as strict when mining, and cheap - when not (default: {flag_relay_set}). - --min-gas-price WEI Minimum amount of Wei per GAS to be paid for a - transaction to be accepted for mining. Overrides - --basic-tx-usd. - (default: {flag_min_gas_price:?}) - --usd-per-tx USD Amount of USD to be paid for a basic transaction - (default: {flag_usd_per_tx}). The minimum gas price is set - accordingly. - --usd-per-eth SOURCE USD value of a single ETH. SOURCE may be either an - amount in USD, a web service or 'auto' to use each - web service in turn and fallback on the last known - good value (default: {flag_usd_per_eth}). - --price-update-period T T will be allowed to pass between each gas price - update. T may be daily, hourly, a number of seconds, - or a time string of the form "2 days", "30 minutes" - etc. (default: {flag_price_update_period}). - --gas-floor-target GAS Amount of gas per block to target when sealing a new - block (default: {flag_gas_floor_target}). - --gas-cap GAS A cap on how large we will raise the gas limit per - block due to transaction volume (default: {flag_gas_cap}). - --extra-data STRING Specify a custom extra-data for authored blocks, no - more than 32 characters. (default: {flag_extra_data:?}) - --tx-queue-mem-limit MB Maximum amount of memory that can be used by the - transaction queue. Setting this parameter to 0 - disables limiting (default: {flag_tx_queue_mem_limit}). - --tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting - to be included in next block) (default: {flag_tx_queue_size}). - --tx-queue-gas LIMIT Maximum amount of total gas for external transactions in - the queue. LIMIT can be either an amount of gas or - 'auto' or 'off'. 'auto' sets the limit to be 20x - the current block gas limit. (default: {flag_tx_queue_gas}). - --tx-queue-strategy S Prioritization strategy used to order transactions - in the queue. S may be: - gas - Prioritize txs with low gas limit; - gas_price - Prioritize txs with high gas price; - gas_factor - Prioritize txs using gas price - and gas limit ratio (default: {flag_tx_queue_strategy}). - --tx-queue-ban-count C Number of times maximal time for execution (--tx-time-limit) - can be exceeded before banning sender/recipient/code. - (default: {flag_tx_queue_ban_count}) - --tx-queue-ban-time SEC Banning time (in seconds) for offenders of specified - execution time limit. Also number of offending actions - have to reach the threshold within that time. - (default: {flag_tx_queue_ban_time} seconds) - --no-persistent-txqueue Don't save pending local transactions to disk to be - restored whenever the node restarts. - (default: {flag_no_persistent_txqueue}). - --remove-solved Move solved blocks from the work package queue - instead of cloning them. This gives a slightly - faster import speed, but means that extra solutions - submitted for the same work package will go unused. - (default: {flag_remove_solved}) - --notify-work URLS URLs to which work package notifications are pushed. - URLS should be a comma-delimited list of HTTP URLs. - (default: {flag_notify_work:?}) - --refuse-service-transactions Always refuse service transactions. - (default: {flag_refuse_service_transactions}). - --stratum Run Stratum server for miner push notification. (default: {flag_stratum}) - --stratum-interface IP Interface address for Stratum server. (default: {flag_stratum_interface}) - --stratum-port PORT Port for Stratum server to listen on. (default: {flag_stratum_port}) - --stratum-secret STRING Secret for authorizing Stratum server for peers. - (default: {flag_stratum_secret:?}) - -Footprint Options: - --tracing BOOL Indicates if full transaction tracing should be - enabled. Works only if client had been fully synced - with tracing enabled. BOOL may be one of auto, on, - off. auto uses last used value of this option (off - if it does not exist) (default: {flag_tracing}). - --pruning METHOD Configure pruning of the state/storage trie. METHOD - may be one of auto, archive, fast: - archive - keep all state trie data. No pruning. - fast - maintain journal overlay. Fast but 50MB used. - auto - use the method most recently synced or - default to fast if none synced (default: {flag_pruning}). - --pruning-history NUM Set a minimum number of recent states to keep when pruning - is active. (default: {flag_pruning_history}). - --pruning-memory MB The ideal amount of memory in megabytes to use to store - recent states. As many states as possible will be kept - within this limit, and at least --pruning-history states - will always be kept. (default: {flag_pruning_memory}) - --cache-size-db MB Override database cache size (default: {flag_cache_size_db}). - --cache-size-blocks MB Specify the prefered size of the blockchain cache in - megabytes (default: {flag_cache_size_blocks}). - --cache-size-queue MB Specify the maximum size of memory to use for block - queue (default: {flag_cache_size_queue}). - --cache-size-state MB Specify the maximum size of memory to use for - the state cache (default: {flag_cache_size_state}). - --cache-size MB Set total amount of discretionary memory to use for - the entire system, overrides other cache and queue - options. (default: {flag_cache_size:?}) - --fast-and-loose Disables DB WAL, which gives a significant speed up - but means an unclean exit is unrecoverable. (default: {flag_fast_and_loose}) - --db-compaction TYPE Database compaction type. TYPE may be one of: - ssd - suitable for SSDs and fast HDDs; - hdd - suitable for slow HDDs; - auto - determine automatically (default: {flag_db_compaction}). - --fat-db BOOL Build appropriate information to allow enumeration - of all accounts and storage keys. Doubles the size - of the state database. BOOL may be one of on, off - or auto. (default: {flag_fat_db}) - --scale-verifiers Automatically scale amount of verifier threads based on - workload. Not guaranteed to be faster. - (default: {flag_scale_verifiers}) - --num-verifiers INT Amount of verifier threads to use or to begin with, if verifier - auto-scaling is enabled. (default: {flag_num_verifiers:?}) - -Import/Export Options: - --from BLOCK Export from block BLOCK, which may be an index or - hash (default: {flag_from}). - --to BLOCK Export to (including) block BLOCK, which may be an - index, hash or 'latest' (default: {flag_to}). - --format FORMAT For import/export in given format. FORMAT must be - one of 'hex' and 'binary'. - (default: {flag_format:?} = Import: auto, Export: binary) - --no-seal-check Skip block seal check. (default: {flag_no_seal_check}) - --at BLOCK Export state at the given block, which may be an - index, hash, or 'latest'. (default: {flag_at}) - --no-storage Don't export account storage. (default: {flag_no_storage}) - --no-code Don't export account code. (default: {flag_no_code}) - --min-balance WEI Don't export accounts with balance less than specified. - (default: {flag_min_balance:?}) - --max-balance WEI Don't export accounts with balance greater than specified. - (default: {flag_max_balance:?}) - -Snapshot Options: - --at BLOCK Take a snapshot at the given block, which may be an - index, hash, or 'latest'. Note that taking snapshots at - non-recent blocks will only work with --pruning archive - (default: {flag_at}) - --no-periodic-snapshot Disable automated snapshots which usually occur once - every 10000 blocks. (default: {flag_no_periodic_snapshot}) - -Virtual Machine Options: - --jitvm Enable the JIT VM. (default: {flag_jitvm}) - -Whisper Options: - --whisper Enable the Whisper network. (default: {flag_whisper}) - --whisper-pool-size MB Target size of the whisper message pool in megabytes. - (default: {flag_whisper_pool_size}) - -Legacy Options: - --geth Run in Geth-compatibility mode. Sets the IPC path - to be the same as Geth's. Overrides the --ipc-path - and --ipcpath options. Alters RPCs to reflect Geth - bugs. Includes the personal_ RPC by default. - --testnet Testnet mode. Equivalent to --chain testnet. - Overrides the --keys-path option. - --import-geth-keys Attempt to import keys from Geth client. - --datadir PATH Equivalent to --base-path PATH. - --networkid INDEX Equivalent to --network-id INDEX. - --peers NUM Equivalent to --min-peers NUM. - --nodekey KEY Equivalent to --node-key KEY. - --nodiscover Equivalent to --no-discovery. - -j --jsonrpc Does nothing; JSON-RPC is on by default now. - --jsonrpc-off Equivalent to --no-jsonrpc. - -w --webapp Does nothing; dapps server is on by default now. - --dapps-off Equivalent to --no-dapps. - --dapps-user USERNAME Dapps server authentication has been removed. (default: {flag_dapps_user:?}) - --dapps-pass PASSWORD Dapps server authentication has been removed. (default: {flag_dapps_pass:?}) - --dapps-apis-all Dapps server is merged with RPC server. Use --jsonrpc-apis. (default: {flag_dapps_apis_all:?}) - --dapps-cors URL Dapps server is merged with RPC server. Use --jsonrpc-cors. (default: {flag_dapps_cors:?}) - --dapps-hosts HOSTS Dapps server is merged with RPC server. Use --jsonrpc-hosts. (default: {flag_dapps_hosts:?}) - --dapps-interface IP Dapps server is merged with RPC server. Use --jsonrpc-interface. (default: {flag_dapps_interface:?}) - --dapps-port PORT Dapps server is merged with RPC server. Use --jsonrpc-port. (default: {flag_dapps_port:?}) - --rpc Does nothing; JSON-RPC is on by default now. - --warp Does nothing; Warp sync is on by default. (default: {flag_warp}) - --rpcaddr IP Equivalent to --jsonrpc-interface IP. - --rpcport PORT Equivalent to --jsonrpc-port PORT. - --rpcapi APIS Equivalent to --jsonrpc-apis APIS. - --rpccorsdomain URL Equivalent to --jsonrpc-cors URL. - --ipcdisable Equivalent to --no-ipc. - --ipc-off Equivalent to --no-ipc. - --ipcapi APIS Equivalent to --ipc-apis APIS. - --ipcpath PATH Equivalent to --ipc-path PATH. - --gasprice WEI Equivalent to --min-gas-price WEI. - --etherbase ADDRESS Equivalent to --author ADDRESS. - --extradata STRING Equivalent to --extra-data STRING. - --cache MB Equivalent to --cache-size MB. - -Internal Options: - --can-restart Executable will auto-restart if exiting with 69. - -Miscellaneous Options: - --ntp-servers HOSTS Comma separated list of NTP servers to provide current time (host:port). - Used to verify node health. Parity uses pool.ntp.org NTP servers, - consider joining the pool: http://www.pool.ntp.org/join.html - (default: {flag_ntp_servers}) - -l --logging LOGGING Specify the logging level. Must conform to the same - format as RUST_LOG. (default: {flag_logging:?}) - --log-file FILENAME Specify a filename into which logging should be - appended. (default: {flag_log_file:?}) - --no-config Don't load a configuration file. - --no-color Don't use terminal color codes in output. (default: {flag_no_color}) - -v --version Show information about version. - -h --help Show this screen. diff --git a/parity/cli/usage_header.txt b/parity/cli/usage_header.txt new file mode 100644 index 000000000..bcc0f93cb --- /dev/null +++ b/parity/cli/usage_header.txt @@ -0,0 +1,3 @@ +Parity. Ethereum Client. + By Wood/Paronyan/Kotewicz/Drwięga/Volf et al. + Copyright 2015, 2016, 2017 Parity Technologies (UK) Ltd \ No newline at end of file diff --git a/parity/cli/version.txt b/parity/cli/version.txt index a4febdcbd..855dbb5cf 100644 --- a/parity/cli/version.txt +++ b/parity/cli/version.txt @@ -7,4 +7,3 @@ There is NO WARRANTY, to the extent permitted by law. By Wood/Paronyan/Kotewicz/Drwięga/Volf Habermeier/Czaban/Greeff/Gotchac/Redmann - diff --git a/parity/configuration.rs b/parity/configuration.rs index 91305583d..88e5e8d02 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -106,13 +106,13 @@ impl Configuration { pub fn into_command(self) -> Result { let dirs = self.directories(); - let pruning = self.args.flag_pruning.parse()?; - let pruning_history = self.args.flag_pruning_history; + let pruning = self.args.arg_pruning.parse()?; + let pruning_history = self.args.arg_pruning_history; let vm_type = self.vm_type()?; let spec = self.chain().parse()?; - let mode = match self.args.flag_mode.as_ref() { + let mode = match self.args.arg_mode.as_ref() { "last" => None, - mode => Some(to_mode(&mode, self.args.flag_mode_timeout, self.args.flag_mode_alarm)?), + mode => Some(to_mode(&mode, self.args.arg_mode_timeout, self.args.arg_mode_alarm)?), }; let update_policy = self.update_policy()?; let logger_config = self.logger_config(); @@ -123,18 +123,21 @@ impl Configuration { let ui_conf = self.ui_config(); let network_id = self.network_id(); let cache_config = self.cache_config(); - let tracing = self.args.flag_tracing.parse()?; - let fat_db = self.args.flag_fat_db.parse()?; - let compaction = self.args.flag_db_compaction.parse()?; + let tracing = self.args.arg_tracing.parse()?; + let fat_db = self.args.arg_fat_db.parse()?; + let compaction = self.args.arg_db_compaction.parse()?; let wal = !self.args.flag_fast_and_loose; - match self.args.flag_warp { - // Logging is not initialized yet, so we print directly to stderr - Some(true) if fat_db == Switch::On => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because Fat DB is turned on").expect("Error writing to stderr"), - Some(true) if tracing == Switch::On => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because tracing is turned on").expect("Error writing to stderr"), - Some(true) if pruning == Pruning::Specific(Algorithm::Archive) => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because pruning mode is set to archive").expect("Error writing to stderr"), - _ => {}, - }; let public_node = self.args.flag_public_node; + if !self.args.flag_no_warp { + // Logging is not initialized yet, so we print directly to stderr + if fat_db == Switch::On { + writeln!(&mut stderr(), "Warning: Warp Sync is disabled because Fat DB is turned on").expect("Error writing to stderr"); + } else if tracing == Switch::On { + writeln!(&mut stderr(), "Warning: Warp Sync is disabled because tracing is turned on").expect("Error writing to stderr"); + } else if pruning == Pruning::Specific(Algorithm::Archive) { + writeln!(&mut stderr(), "Warning: Warp Sync is disabled because pruning mode is set to archive").expect("Error writing to stderr"); + } + } let warp_sync = !self.args.flag_no_warp && fat_db != Switch::On && tracing != Switch::On && pruning != Pruning::Specific(Algorithm::Archive); let geth_compatibility = self.args.flag_geth; let mut dapps_conf = self.dapps_config(); @@ -142,9 +145,9 @@ impl Configuration { let secretstore_conf = self.secretstore_config()?; let format = self.format()?; - if self.args.flag_jsonrpc_server_threads.is_some() && dapps_conf.enabled { + if self.args.arg_jsonrpc_server_threads.is_some() && dapps_conf.enabled { dapps_conf.enabled = false; - writeln!(&mut stderr(), "Warning: Disabling Dapps server because fast RPC server was enabled.").expect("Error writing to stderr.") + writeln!(&mut stderr(), "Warning: Disabling Dapps server because fast RPC server was enabled.").expect("Error writing to stderr."); } let cmd = if self.args.flag_version { @@ -152,25 +155,25 @@ impl Configuration { } else if self.args.cmd_signer { let authfile = ::signer::codes_path(&ws_conf.signer_path); - if self.args.cmd_new_token { + if self.args.cmd_signer_new_token { Cmd::SignerToken(ws_conf, ui_conf, logger_config.clone()) - } else if self.args.cmd_sign { - let pwfile = self.args.flag_password.get(0).map(|pwfile| { + } else if self.args.cmd_signer_sign { + let pwfile = self.args.arg_signer_sign_password.map(|pwfile| { PathBuf::from(pwfile) }); Cmd::SignerSign { - id: self.args.arg_id, + id: self.args.arg_signer_sign_id, pwfile: pwfile, port: ws_conf.port, authfile: authfile, } - } else if self.args.cmd_reject { + } else if self.args.cmd_signer_reject { Cmd::SignerReject { - id: self.args.arg_id, + id: self.args.arg_signer_reject_id, port: ws_conf.port, authfile: authfile, } - } else if self.args.cmd_list { + } else if self.args.cmd_signer_list { Cmd::SignerList { port: ws_conf.port, authfile: authfile, @@ -178,32 +181,32 @@ impl Configuration { } else { unreachable!(); } - } else if self.args.cmd_tools && self.args.cmd_hash { - Cmd::Hash(self.args.arg_file) - } else if self.args.cmd_db && self.args.cmd_kill { + } else if self.args.cmd_tools && self.args.cmd_tools_hash { + Cmd::Hash(self.args.arg_tools_hash_file) + } else if self.args.cmd_db && self.args.cmd_db_kill { Cmd::Blockchain(BlockchainCmd::Kill(KillBlockchain { spec: spec, dirs: dirs, pruning: pruning, })) } else if self.args.cmd_account { - let account_cmd = if self.args.cmd_new { + let account_cmd = if self.args.cmd_account_new { let new_acc = NewAccount { - iterations: self.args.flag_keys_iterations, + iterations: self.args.arg_keys_iterations, path: dirs.keys, spec: spec, - password_file: self.args.flag_password.first().cloned(), + password_file: self.args.arg_account_new_password.clone(), }; AccountCmd::New(new_acc) - } else if self.args.cmd_list { + } else if self.args.cmd_account_list { let list_acc = ListAccounts { path: dirs.keys, spec: spec, }; AccountCmd::List(list_acc) - } else if self.args.cmd_import { + } else if self.args.cmd_account_import { let import_acc = ImportAccounts { - from: self.args.arg_path.clone(), + from: self.args.arg_account_import_path.expect("CLI argument is required; qed").clone(), to: dirs.keys, spec: spec, }; @@ -223,11 +226,11 @@ impl Configuration { Cmd::Account(account_cmd) } else if self.args.cmd_wallet { let presale_cmd = ImportWallet { - iterations: self.args.flag_keys_iterations, + iterations: self.args.arg_keys_iterations, path: dirs.keys, spec: spec, - wallet_path: self.args.arg_path.first().unwrap().clone(), - password_file: self.args.flag_password.first().cloned(), + wallet_path: self.args.arg_wallet_import_path.unwrap().clone(), + password_file: self.args.arg_wallet_import_password, }; Cmd::ImportPresaleWallet(presale_cmd) } else if self.args.cmd_import { @@ -235,11 +238,11 @@ impl Configuration { spec: spec, cache_config: cache_config, dirs: dirs, - file_path: self.args.arg_file.clone(), + file_path: self.args.arg_import_file.clone(), format: format, pruning: pruning, pruning_history: pruning_history, - pruning_memory: self.args.flag_pruning_memory, + pruning_memory: self.args.arg_pruning_memory, compaction: compaction, wal: wal, tracing: tracing, @@ -252,44 +255,44 @@ impl Configuration { }; Cmd::Blockchain(BlockchainCmd::Import(import_cmd)) } else if self.args.cmd_export { - if self.args.cmd_blocks { + if self.args.cmd_export_blocks { let export_cmd = ExportBlockchain { spec: spec, cache_config: cache_config, dirs: dirs, - file_path: self.args.arg_file.clone(), + file_path: self.args.arg_export_blocks_file.clone(), format: format, pruning: pruning, pruning_history: pruning_history, - pruning_memory: self.args.flag_pruning_memory, + pruning_memory: self.args.arg_pruning_memory, compaction: compaction, wal: wal, tracing: tracing, fat_db: fat_db, - from_block: to_block_id(&self.args.flag_from)?, - to_block: to_block_id(&self.args.flag_to)?, + from_block: to_block_id(&self.args.arg_export_blocks_from)?, + to_block: to_block_id(&self.args.arg_export_blocks_to)?, check_seal: !self.args.flag_no_seal_check, }; Cmd::Blockchain(BlockchainCmd::Export(export_cmd)) - } else if self.args.cmd_state { + } else if self.args.cmd_export_state { let export_cmd = ExportState { spec: spec, cache_config: cache_config, dirs: dirs, - file_path: self.args.arg_file.clone(), + file_path: self.args.arg_export_state_file.clone(), format: format, pruning: pruning, pruning_history: pruning_history, - pruning_memory: self.args.flag_pruning_memory, + pruning_memory: self.args.arg_pruning_memory, compaction: compaction, wal: wal, tracing: tracing, fat_db: fat_db, - at: to_block_id(&self.args.flag_at)?, - storage: !self.args.flag_no_storage, - code: !self.args.flag_no_code, - min_balance: self.args.flag_min_balance.and_then(|s| to_u256(&s).ok()), - max_balance: self.args.flag_max_balance.and_then(|s| to_u256(&s).ok()), + at: to_block_id(&self.args.arg_export_state_at)?, + storage: !self.args.flag_export_state_no_storage, + code: !self.args.flag_export_state_no_code, + min_balance: self.args.arg_export_state_min_balance.and_then(|s| to_u256(&s).ok()), + max_balance: self.args.arg_export_state_max_balance.and_then(|s| to_u256(&s).ok()), }; Cmd::Blockchain(BlockchainCmd::ExportState(export_cmd)) } else { @@ -302,14 +305,14 @@ impl Configuration { spec: spec, pruning: pruning, pruning_history: pruning_history, - pruning_memory: self.args.flag_pruning_memory, + pruning_memory: self.args.arg_pruning_memory, tracing: tracing, fat_db: fat_db, compaction: compaction, - file_path: self.args.arg_file.clone(), + file_path: self.args.arg_snapshot_file.clone(), wal: wal, kind: snapshot::Kind::Take, - block_at: to_block_id(&self.args.flag_at)?, + block_at: to_block_id(&self.args.arg_snapshot_at)?, }; Cmd::Snapshot(snapshot_cmd) } else if self.args.cmd_restore { @@ -319,11 +322,11 @@ impl Configuration { spec: spec, pruning: pruning, pruning_history: pruning_history, - pruning_memory: self.args.flag_pruning_memory, + pruning_memory: self.args.arg_pruning_memory, tracing: tracing, fat_db: fat_db, compaction: compaction, - file_path: self.args.arg_file.clone(), + file_path: self.args.arg_restore_file.clone(), wal: wal, kind: snapshot::Kind::Restore, block_at: to_block_id("latest")?, // unimportant. @@ -331,7 +334,7 @@ impl Configuration { Cmd::Snapshot(restore_cmd) } else { let daemon = if self.args.cmd_daemon { - Some(self.args.arg_pid_file.clone()) + Some(self.args.arg_daemon_pid_file.clone().expect("CLI argument is required; qed")) } else { None }; @@ -345,10 +348,10 @@ impl Configuration { spec: spec, pruning: pruning, pruning_history: pruning_history, - pruning_memory: self.args.flag_pruning_memory, + pruning_memory: self.args.arg_pruning_memory, daemon: daemon, logger_config: logger_config.clone(), - miner_options: self.miner_options(self.args.flag_reseal_min_period)?, + miner_options: self.miner_options(self.args.arg_reseal_min_period)?, ntp_servers: self.ntp_servers(), ws_conf: ws_conf, http_conf: http_conf, @@ -376,8 +379,8 @@ impl Configuration { secretstore_conf: secretstore_conf, dapp: self.dapp_to_open()?, ui: self.args.cmd_ui, - name: self.args.flag_identity, - custom_bootnodes: self.args.flag_bootnodes.is_some(), + name: self.args.arg_identity, + custom_bootnodes: self.args.arg_bootnodes.is_some(), no_periodic_snapshot: self.args.flag_no_periodic_snapshot, check_seal: !self.args.flag_no_seal_check, download_old_blocks: !self.args.flag_no_ancient_blocks, @@ -408,8 +411,8 @@ impl Configuration { let extras = MinerExtras { author: self.author()?, extra_data: self.extra_data()?, - gas_floor_target: to_u256(&self.args.flag_gas_floor_target)?, - gas_ceil_target: to_u256(&self.args.flag_gas_cap)?, + gas_floor_target: to_u256(&self.args.arg_gas_floor_target)?, + gas_ceil_target: to_u256(&self.args.arg_gas_cap)?, engine_signer: self.engine_signer()?, }; @@ -417,37 +420,39 @@ impl Configuration { } fn author(&self) -> Result { - to_address(self.args.flag_etherbase.clone().or(self.args.flag_author.clone())) + to_address(self.args.arg_etherbase.clone().or(self.args.arg_author.clone())) } fn engine_signer(&self) -> Result { - to_address(self.args.flag_engine_signer.clone()) + to_address(self.args.arg_engine_signer.clone()) } fn format(&self) -> Result, String> { - match self.args.flag_format { + match self.args.arg_import_format.clone() + .or(self.args.arg_export_blocks_format.clone()) + .or(self.args.arg_export_state_format.clone()) { Some(ref f) => Ok(Some(f.parse()?)), None => Ok(None), } } fn cache_config(&self) -> CacheConfig { - match self.args.flag_cache_size.or(self.args.flag_cache) { + match self.args.arg_cache_size.or(self.args.arg_cache) { Some(size) => CacheConfig::new_with_total_cache_size(size), None => CacheConfig::new( - self.args.flag_cache_size_db, - self.args.flag_cache_size_blocks, - self.args.flag_cache_size_queue, - self.args.flag_cache_size_state, + self.args.arg_cache_size_db, + self.args.arg_cache_size_blocks, + self.args.arg_cache_size_queue, + self.args.arg_cache_size_state, ), } } fn logger_config(&self) -> LogConfig { LogConfig { - mode: self.args.flag_logging.clone(), + mode: self.args.arg_logging.clone(), color: !self.args.flag_no_color && !cfg!(windows), - file: self.args.flag_log_file.clone(), + file: self.args.arg_log_file.clone(), } } @@ -458,44 +463,44 @@ impl Configuration { else if self.args.flag_testnet { "testnet".to_owned() } else { - self.args.flag_chain.clone() + self.args.arg_chain.clone() } } fn max_peers(&self) -> u32 { - let peers = self.args.flag_max_peers as u32; + let peers = self.args.arg_max_peers as u32; max(self.min_peers(), peers) } fn ip_filter(&self) -> Result { - match IpFilter::parse(self.args.flag_allow_ips.as_str()) { + match IpFilter::parse(self.args.arg_allow_ips.as_str()) { Ok(allow_ip) => Ok(allow_ip), Err(_) => Err("Invalid IP filter value".to_owned()), } } fn min_peers(&self) -> u32 { - self.args.flag_peers.unwrap_or(self.args.flag_min_peers) as u32 + self.args.arg_peers.unwrap_or(self.args.arg_min_peers) as u32 } fn max_pending_peers(&self) -> u32 { - self.args.flag_max_pending_peers as u32 + self.args.arg_max_pending_peers as u32 } fn snapshot_peers(&self) -> u32 { - self.args.flag_snapshot_peers as u32 + self.args.arg_snapshot_peers as u32 } fn work_notify(&self) -> Vec { - self.args.flag_notify_work.as_ref().map_or_else(Vec::new, |s| s.split(',').map(|s| s.to_owned()).collect()) + self.args.arg_notify_work.as_ref().map_or_else(Vec::new, |s| s.split(',').map(|s| s.to_owned()).collect()) } fn accounts_config(&self) -> Result { let cfg = AccountsConfig { - iterations: self.args.flag_keys_iterations, + iterations: self.args.arg_keys_iterations, testnet: self.args.flag_testnet, - password_files: self.args.flag_password.clone(), - unlocked_accounts: to_addresses(&self.args.flag_unlock)?, + password_files: self.args.arg_password.clone(), + unlocked_accounts: to_addresses(&self.args.arg_unlock)?, enable_hardware_wallets: !self.args.flag_no_hardware_wallets, enable_fast_unlock: self.args.flag_fast_unlock, }; @@ -508,8 +513,8 @@ impl Configuration { Ok(Some(StratumOptions { io_path: self.directories().db, listen_addr: self.stratum_interface(), - port: self.args.flag_ports_shift + self.args.flag_stratum_port, - secret: self.args.flag_stratum_secret.as_ref().map(|s| s.parse::().unwrap_or_else(|_| keccak(s))), + port: self.args.arg_ports_shift + self.args.arg_stratum_port, + secret: self.args.arg_stratum_secret.as_ref().map(|s| s.parse::().unwrap_or_else(|_| keccak(s))), })) } else { Ok(None) } } @@ -519,7 +524,7 @@ impl Configuration { return Err("Force sealing can't be used with reseal_min_period = 0".into()); } - let reseal = self.args.flag_reseal_on_txs.parse::()?; + let reseal = self.args.arg_reseal_on_txs.parse::()?; let options = MinerOptions { new_work_notify: self.work_notify(), @@ -527,26 +532,26 @@ impl Configuration { reseal_on_external_tx: reseal.external, reseal_on_own_tx: reseal.own, reseal_on_uncle: self.args.flag_reseal_on_uncle, - tx_gas_limit: match self.args.flag_tx_gas_limit { + tx_gas_limit: match self.args.arg_tx_gas_limit { Some(ref d) => to_u256(d)?, None => U256::max_value(), }, - tx_queue_size: self.args.flag_tx_queue_size, - tx_queue_memory_limit: if self.args.flag_tx_queue_mem_limit > 0 { - Some(self.args.flag_tx_queue_mem_limit as usize * 1024 * 1024) + tx_queue_size: self.args.arg_tx_queue_size, + tx_queue_memory_limit: if self.args.arg_tx_queue_mem_limit > 0 { + Some(self.args.arg_tx_queue_mem_limit as usize * 1024 * 1024) } else { None }, - tx_queue_gas_limit: to_gas_limit(&self.args.flag_tx_queue_gas)?, - tx_queue_strategy: to_queue_strategy(&self.args.flag_tx_queue_strategy)?, - pending_set: to_pending_set(&self.args.flag_relay_set)?, + tx_queue_gas_limit: to_gas_limit(&self.args.arg_tx_queue_gas)?, + tx_queue_strategy: to_queue_strategy(&self.args.arg_tx_queue_strategy)?, + pending_set: to_pending_set(&self.args.arg_relay_set)?, reseal_min_period: Duration::from_millis(reseal_min_period), - reseal_max_period: Duration::from_millis(self.args.flag_reseal_max_period), - work_queue_size: self.args.flag_work_queue_size, + reseal_max_period: Duration::from_millis(self.args.arg_reseal_max_period), + work_queue_size: self.args.arg_work_queue_size, enable_resubmission: !self.args.flag_remove_solved, - tx_queue_banning: match self.args.flag_tx_time_limit { + tx_queue_banning: match self.args.arg_tx_time_limit { Some(limit) => Banning::Enabled { - min_offends: self.args.flag_tx_queue_ban_count, + min_offends: self.args.arg_tx_queue_ban_count, offend_threshold: Duration::from_millis(limit), - ban_duration: Duration::from_secs(self.args.flag_tx_queue_ban_time as u64), + ban_duration: Duration::from_secs(self.args.arg_tx_queue_ban_time as u64), }, None => Banning::Disabled, }, @@ -557,11 +562,11 @@ impl Configuration { } fn ui_port(&self) -> u16 { - self.args.flag_ports_shift + self.args.flag_ui_port + self.args.arg_ports_shift + self.args.arg_ui_port } fn ntp_servers(&self) -> Vec { - self.args.flag_ntp_servers.split(",").map(str::to_owned).collect() + self.args.arg_ntp_servers.split(",").map(str::to_owned).collect() } fn ui_config(&self) -> UiConfiguration { @@ -581,7 +586,7 @@ impl Configuration { enabled: self.dapps_enabled(), dapps_path: PathBuf::from(self.directories().dapps), extra_dapps: if self.args.cmd_dapp { - self.args.arg_path.iter().map(|path| PathBuf::from(path)).collect() + self.args.arg_dapp_path.iter().map(|path| PathBuf::from(path)).collect() } else { vec![] }, @@ -616,9 +621,9 @@ impl Configuration { self_secret: self.secretstore_self_secret()?, nodes: self.secretstore_nodes()?, interface: self.secretstore_interface(), - port: self.args.flag_ports_shift + self.args.flag_secretstore_port, + port: self.args.arg_ports_shift + self.args.arg_secretstore_port, http_interface: self.secretstore_http_interface(), - http_port: self.args.flag_ports_shift + self.args.flag_secretstore_http_port, + http_port: self.args.arg_ports_shift + self.args.arg_secretstore_http_port, data_path: self.directories().secretstore, }) } @@ -626,7 +631,7 @@ impl Configuration { fn ipfs_config(&self) -> IpfsConfiguration { IpfsConfiguration { enabled: self.args.flag_ipfs_api, - port: self.args.flag_ports_shift + self.args.flag_ipfs_api_port, + port: self.args.arg_ports_shift + self.args.arg_ipfs_api_port, interface: self.ipfs_interface(), cors: self.ipfs_cors(), hosts: self.ipfs_hosts(), @@ -637,7 +642,7 @@ impl Configuration { if !self.args.cmd_dapp { return Ok(None); } - let path = self.args.arg_path.get(0).map(String::as_str).unwrap_or("."); + let path = self.args.arg_dapp_path.as_ref().map(String::as_str).unwrap_or("."); let path = Path::new(path).canonicalize() .map_err(|e| format!("Invalid path: {}. Error: {:?}", path, e))?; let name = path.file_name() @@ -654,14 +659,14 @@ impl Configuration { U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap() } - if let Some(dec) = self.args.flag_gasprice.as_ref() { + if let Some(dec) = self.args.arg_gasprice.as_ref() { return Ok(GasPricerConfig::Fixed(to_u256(dec)?)); - } else if let Some(dec) = self.args.flag_min_gas_price { + } else if let Some(dec) = self.args.arg_min_gas_price { return Ok(GasPricerConfig::Fixed(U256::from(dec))); } - let usd_per_tx = to_price(&self.args.flag_usd_per_tx)?; - if "auto" == self.args.flag_usd_per_eth.as_str() { + let usd_per_tx = to_price(&self.args.arg_usd_per_tx)?; + if "auto" == self.args.arg_usd_per_eth.as_str() { // Just a very rough estimate to avoid accepting // ZGP transactions before the price is fetched // if user does not want it. @@ -669,11 +674,11 @@ impl Configuration { return Ok(GasPricerConfig::Calibrated { initial_minimum: wei_per_gas(usd_per_tx, last_known_usd_per_eth), usd_per_tx: usd_per_tx, - recalibration_period: to_duration(self.args.flag_price_update_period.as_str())?, + recalibration_period: to_duration(self.args.arg_price_update_period.as_str())?, }); } - let usd_per_eth = to_price(&self.args.flag_usd_per_eth)?; + let usd_per_eth = to_price(&self.args.arg_usd_per_eth)?; let wei_per_gas = wei_per_gas(usd_per_tx, usd_per_eth); info!( @@ -686,7 +691,7 @@ impl Configuration { } fn extra_data(&self) -> Result { - match self.args.flag_extradata.as_ref().or(self.args.flag_extra_data.as_ref()) { + match self.args.arg_extradata.as_ref().or(self.args.arg_extra_data.as_ref()) { Some(x) if x.len() <= 32 => Ok(x.as_bytes().to_owned()), None => Ok(version_data()), Some(_) => Err("Extra data must be at most 32 characters".into()), @@ -696,7 +701,7 @@ impl Configuration { fn init_reserved_nodes(&self) -> Result, String> { use std::fs::File; - match self.args.flag_reserved_peers { + match self.args.arg_reserved_peers { Some(ref path) => { let mut buffer = String::new(); let mut node_file = File::open(path).map_err(|e| format!("Error opening reserved nodes file: {}", e))?; @@ -712,10 +717,10 @@ impl Configuration { } fn net_addresses(&self) -> Result<(SocketAddr, Option), String> { - let port = self.args.flag_ports_shift + self.args.flag_port; + let port = self.args.arg_ports_shift + self.args.arg_port; let listen_address = SocketAddr::new("0.0.0.0".parse().unwrap(), port); - let public_address = if self.args.flag_nat.starts_with("extip:") { - let host = &self.args.flag_nat[6..]; + let public_address = if self.args.arg_nat.starts_with("extip:") { + let host = &self.args.arg_nat[6..]; let host = host.parse().map_err(|_| format!("Invalid host given with `--nat extip:{}`", host))?; Some(SocketAddr::new(host, port)) } else { @@ -726,12 +731,12 @@ impl Configuration { fn net_config(&self) -> Result { let mut ret = NetworkConfiguration::new(); - ret.nat_enabled = self.args.flag_nat == "any" || self.args.flag_nat == "upnp"; - ret.boot_nodes = to_bootnodes(&self.args.flag_bootnodes)?; + ret.nat_enabled = self.args.arg_nat == "any" || self.args.arg_nat == "upnp"; + ret.boot_nodes = to_bootnodes(&self.args.arg_bootnodes)?; let (listen, public) = self.net_addresses()?; ret.listen_address = Some(format!("{}", listen)); ret.public_address = public.map(|p| format!("{}", p)); - ret.use_secret = match self.args.flag_node_key.as_ref() + ret.use_secret = match self.args.arg_node_key.as_ref() .map(|s| s.parse::().or_else(|_| Secret::from_unsafe_slice(&keccak(s))).map_err(|e| format!("Invalid key: {:?}", e)) ) { None => None, @@ -753,13 +758,13 @@ impl Configuration { } fn network_id(&self) -> Option { - self.args.flag_network_id.or(self.args.flag_networkid) + self.args.arg_network_id.or(self.args.arg_networkid) } fn rpc_apis(&self) -> String { - let mut apis: Vec<&str> = self.args.flag_rpcapi + let mut apis: Vec<&str> = self.args.arg_rpcapi .as_ref() - .unwrap_or(&self.args.flag_jsonrpc_apis) + .unwrap_or(&self.args.arg_jsonrpc_apis) .split(",") .collect(); @@ -775,12 +780,12 @@ impl Configuration { } fn rpc_cors(&self) -> Option> { - let cors = self.args.flag_jsonrpc_cors.as_ref().or(self.args.flag_rpccorsdomain.as_ref()); + let cors = self.args.arg_jsonrpc_cors.as_ref().or(self.args.arg_rpccorsdomain.as_ref()); Self::cors(cors) } fn ipfs_cors(&self) -> Option> { - Self::cors(self.args.flag_ipfs_api_cors.as_ref()) + Self::cors(self.args.arg_ipfs_api_cors.as_ref()) } fn hosts(&self, hosts: &str, interface: &str) -> Option> { @@ -806,15 +811,15 @@ impl Configuration { } fn ui_hosts(&self) -> Option> { - self.hosts(&self.args.flag_ui_hosts, &self.ui_interface()) + self.hosts(&self.args.arg_ui_hosts, &self.ui_interface()) } fn rpc_hosts(&self) -> Option> { - self.hosts(&self.args.flag_jsonrpc_hosts, &self.rpc_interface()) + self.hosts(&self.args.arg_jsonrpc_hosts, &self.rpc_interface()) } fn ws_hosts(&self) -> Option> { - self.hosts(&self.args.flag_ws_hosts, &self.ws_interface()) + self.hosts(&self.args.arg_ws_hosts, &self.ws_interface()) } fn ws_origins(&self) -> Option> { @@ -822,11 +827,11 @@ impl Configuration { return None; } - Self::parse_hosts(&self.args.flag_ws_origins) + Self::parse_hosts(&self.args.arg_ws_origins) } fn ipfs_hosts(&self) -> Option> { - self.hosts(&self.args.flag_ipfs_api_hosts, &self.ipfs_interface()) + self.hosts(&self.args.arg_ipfs_api_hosts, &self.ipfs_interface()) } fn ipc_config(&self) -> Result { @@ -834,7 +839,7 @@ impl Configuration { enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc), socket_addr: self.ipc_path(), apis: { - let mut apis = self.args.flag_ipcapi.clone().unwrap_or(self.args.flag_ipc_apis.clone()); + let mut apis = self.args.arg_ipcapi.clone().unwrap_or(self.args.arg_ipc_apis.clone()); if self.args.flag_geth { if !apis.is_empty() { apis.push_str(","); @@ -852,19 +857,19 @@ impl Configuration { let conf = HttpConfiguration { enabled: self.rpc_enabled(), interface: self.rpc_interface(), - port: self.args.flag_ports_shift + self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port), + port: self.args.arg_ports_shift + self.args.arg_rpcport.unwrap_or(self.args.arg_jsonrpc_port), apis: match self.args.flag_public_node { false => self.rpc_apis().parse()?, true => self.rpc_apis().parse::()?.retain(ApiSet::PublicContext), }, hosts: self.rpc_hosts(), cors: self.rpc_cors(), - server_threads: match self.args.flag_jsonrpc_server_threads { + server_threads: match self.args.arg_jsonrpc_server_threads { Some(threads) if threads > 0 => Some(threads), None => None, _ => return Err("--jsonrpc-server-threads number needs to be positive.".into()), }, - processing_threads: self.args.flag_jsonrpc_threads, + processing_threads: self.args.arg_jsonrpc_threads, }; Ok(conf) @@ -876,8 +881,8 @@ impl Configuration { let conf = WsConfiguration { enabled: self.ws_enabled(), interface: self.ws_interface(), - port: self.args.flag_ports_shift + self.args.flag_ws_port, - apis: self.args.flag_ws_apis.parse()?, + port: self.args.arg_ports_shift + self.args.arg_ws_port, + apis: self.args.arg_ws_apis.parse()?, hosts: self.ws_hosts(), origins: self.ws_origins(), signer_path: self.directories().signer.into(), @@ -892,7 +897,7 @@ impl Configuration { let http_conf = self.http_config()?; let net_addresses = self.net_addresses()?; Ok(NetworkSettings { - name: self.args.flag_identity.clone(), + name: self.args.arg_identity.clone(), chain: self.chain(), network_port: net_addresses.0.port(), rpc_enabled: http_conf.enabled, @@ -905,13 +910,13 @@ impl Configuration { Ok(UpdatePolicy { enable_downloading: !self.args.flag_no_download, require_consensus: !self.args.flag_no_consensus, - filter: match self.args.flag_auto_update.as_ref() { + filter: match self.args.arg_auto_update.as_ref() { "none" => UpdateFilter::None, "critical" => UpdateFilter::Critical, "all" => UpdateFilter::All, _ => return Err("Invalid value for `--auto-update`. See `--help` for more information.".into()), }, - track: match self.args.flag_release_track.as_ref() { + track: match self.args.arg_release_track.as_ref() { "stable" => ReleaseTrack::Stable, "beta" => ReleaseTrack::Beta, "nightly" => ReleaseTrack::Nightly, @@ -927,23 +932,23 @@ impl Configuration { use path; let local_path = default_local_path(); - let base_path = self.args.flag_base_path.as_ref().or_else(|| self.args.flag_datadir.as_ref()).map_or_else(|| default_data_path(), |s| s.clone()); + let base_path = self.args.arg_base_path.as_ref().or_else(|| self.args.arg_datadir.as_ref()).map_or_else(|| default_data_path(), |s| s.clone()); let data_path = replace_home("", &base_path); - let is_using_base_path = self.args.flag_base_path.is_some(); + let is_using_base_path = self.args.arg_base_path.is_some(); // If base_path is set and db_path is not we default to base path subdir instead of LOCAL. - let base_db_path = if is_using_base_path && self.args.flag_db_path.is_none() { + let base_db_path = if is_using_base_path && self.args.arg_db_path.is_none() { "$BASE/chains" } else { - self.args.flag_db_path.as_ref().map_or(dir::CHAINS_PATH, |s| &s) + self.args.arg_db_path.as_ref().map_or(dir::CHAINS_PATH, |s| &s) }; let cache_path = if is_using_base_path { "$BASE/cache" } else { dir::CACHE_PATH }; let db_path = replace_home_and_local(&data_path, &local_path, &base_db_path); let cache_path = replace_home_and_local(&data_path, &local_path, cache_path); - let keys_path = replace_home(&data_path, &self.args.flag_keys_path); - let dapps_path = replace_home(&data_path, &self.args.flag_dapps_path); - let secretstore_path = replace_home(&data_path, &self.args.flag_secretstore_path); - let ui_path = replace_home(&data_path, &self.args.flag_ui_path); + let keys_path = replace_home(&data_path, &self.args.arg_keys_path); + let dapps_path = replace_home(&data_path, &self.args.arg_dapps_path); + let secretstore_path = replace_home(&data_path, &self.args.arg_secretstore_path); + let ui_path = replace_home(&data_path, &self.args.arg_ui_path); if self.args.flag_geth && !cfg!(windows) { let geth_root = if self.chain() == "testnet".to_owned() { path::ethereum::test() } else { path::ethereum::default() }; @@ -977,8 +982,8 @@ impl Configuration { } else { parity_ipc_path( &self.directories().base, - &self.args.flag_ipcpath.clone().unwrap_or(self.args.flag_ipc_path.clone()), - self.args.flag_ports_shift, + &self.args.arg_ipcpath.clone().unwrap_or(self.args.arg_ipc_path.clone()), + self.args.arg_ports_shift, ) } } @@ -996,32 +1001,32 @@ impl Configuration { } fn ui_interface(&self) -> String { - self.interface(&self.args.flag_ui_interface) + self.interface(&self.args.arg_ui_interface) } fn rpc_interface(&self) -> String { - let rpc_interface = self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone()); + let rpc_interface = self.args.arg_rpcaddr.clone().unwrap_or(self.args.arg_jsonrpc_interface.clone()); self.interface(&rpc_interface) } fn ws_interface(&self) -> String { - self.interface(&self.args.flag_ws_interface) + self.interface(&self.args.arg_ws_interface) } fn ipfs_interface(&self) -> String { - self.interface(&self.args.flag_ipfs_api_interface) + self.interface(&self.args.arg_ipfs_api_interface) } fn secretstore_interface(&self) -> String { - self.interface(&self.args.flag_secretstore_interface) + self.interface(&self.args.arg_secretstore_interface) } fn secretstore_http_interface(&self) -> String { - self.interface(&self.args.flag_secretstore_http_interface) + self.interface(&self.args.arg_secretstore_http_interface) } fn secretstore_self_secret(&self) -> Result, String> { - match self.args.flag_secretstore_secret { + match self.args.arg_secretstore_secret { Some(ref s) if s.len() == 64 => Ok(Some(NodeSecretKey::Plain(s.parse() .map_err(|e| format!("Invalid secret store secret: {}. Error: {:?}", s, e))?))), Some(ref s) if s.len() == 40 => Ok(Some(NodeSecretKey::KeyStore(s.parse() @@ -1033,7 +1038,7 @@ impl Configuration { fn secretstore_nodes(&self) -> Result, String> { let mut nodes = BTreeMap::new(); - for node in self.args.flag_secretstore_nodes.split(',').filter(|n| n != &"") { + for node in self.args.arg_secretstore_nodes.split(',').filter(|n| n != &"") { let public_and_addr: Vec<_> = node.split('@').collect(); if public_and_addr.len() != 2 { return Err(format!("Invalid secret store node: {}", node)); @@ -1056,7 +1061,7 @@ impl Configuration { } fn stratum_interface(&self) -> String { - self.interface(&self.args.flag_stratum_interface) + self.interface(&self.args.arg_stratum_interface) } fn rpc_enabled(&self) -> bool { @@ -1088,7 +1093,7 @@ impl Configuration { return true; } - let ui_disabled = self.args.flag_unlock.is_some() || + let ui_disabled = self.args.arg_unlock.is_some() || self.args.flag_geth || self.args.flag_no_ui; @@ -1098,7 +1103,7 @@ impl Configuration { fn verifier_settings(&self) -> VerifierSettings { let mut settings = VerifierSettings::default(); settings.scale_verifiers = self.args.flag_scale_verifiers; - if let Some(num_verifiers) = self.args.flag_num_verifiers { + if let Some(num_verifiers) = self.args.arg_num_verifiers { settings.num_verifiers = num_verifiers; } @@ -1108,7 +1113,7 @@ impl Configuration { fn whisper_config(&self) -> ::whisper::Config { ::whisper::Config { enabled: self.args.flag_whisper, - target_message_pool_size: self.args.flag_whisper_pool_size * 1024 * 1024, + target_message_pool_size: self.args.arg_whisper_pool_size * 1024 * 1024, } } } @@ -1404,7 +1409,7 @@ mod tests { let conf3 = parse(&["parity", "--tx-queue-strategy", "gas"]); // then - let min_period = conf0.args.flag_reseal_min_period; + let min_period = conf0.args.arg_reseal_min_period; assert_eq!(conf0.miner_options(min_period).unwrap(), mining_options); mining_options.tx_queue_strategy = PrioritizationStrategy::GasFactorAndGasPrice; assert_eq!(conf1.miner_options(min_period).unwrap(), mining_options); @@ -1563,10 +1568,10 @@ mod tests { // given // when - let conf0 = parse(&["parity", "--ui-path", "signer"]); - let conf1 = parse(&["parity", "--ui-path", "signer", "--ui-no-validation"]); - let conf2 = parse(&["parity", "--ui-path", "signer", "--ui-port", "3123"]); - let conf3 = parse(&["parity", "--ui-path", "signer", "--ui-interface", "test"]); + let conf0 = parse(&["parity", "--ui-path=signer"]); + let conf1 = parse(&["parity", "--ui-path=signer", "--ui-no-validation"]); + let conf2 = parse(&["parity", "--ui-path=signer", "--ui-port", "3123"]); + let conf3 = parse(&["parity", "--ui-path=signer", "--ui-interface", "test"]); // then assert_eq!(conf0.directories().signer, "signer".to_owned()); diff --git a/parity/dapps.rs b/parity/dapps.rs index 98eca3459..4177644d3 100644 --- a/parity/dapps.rs +++ b/parity/dapps.rs @@ -25,7 +25,7 @@ use futures::{future, IntoFuture, Future, BoxFuture}; use hash_fetch::fetch::Client as FetchClient; use hash_fetch::urlhint::ContractClient; use helpers::replace_home; -use light::client::LightChainClient; +use light::client::Client as LightClient; use light::on_demand::{self, OnDemand}; use node_health::{SyncStatus, NodeHealth}; use rpc; @@ -87,16 +87,16 @@ impl ContractClient for FullRegistrar { } /// Registrar implementation for the light client. -pub struct LightRegistrar { +pub struct LightRegistrar { /// The light client. - pub client: Arc, + pub client: Arc, /// Handle to the on-demand service. pub on_demand: Arc, /// Handle to the light network service. pub sync: Arc, } -impl ContractClient for LightRegistrar { +impl ContractClient for LightRegistrar { fn registrar(&self) -> Result { self.client.engine().additional_params().get("registrar") .ok_or_else(|| "Registrar not defined.".into()) @@ -106,14 +106,7 @@ impl ContractClient for LightRegistrar { } fn call(&self, address: Address, data: Bytes) -> BoxFuture { - let header = self.client.best_block_header(); - let env_info = self.client.env_info(BlockId::Hash(header.hash())) - .ok_or_else(|| format!("Cannot fetch env info for header {}", header.hash())); - - let env_info = match env_info { - Ok(x) => x, - Err(e) => return future::err(e).boxed(), - }; + let (header, env_info) = (self.client.best_block_header(), self.client.latest_env_info()); let maybe_future = self.sync.with_context(move |ctx| { self.on_demand diff --git a/parity/deprecated.rs b/parity/deprecated.rs index 820181efa..d80ea3357 100644 --- a/parity/deprecated.rs +++ b/parity/deprecated.rs @@ -65,40 +65,40 @@ pub fn find_deprecated(args: &Args) -> Vec { result.push(Deprecated::Replaced("--ipc-off", "--no-ipc")); } - if args.flag_etherbase.is_some() { + if args.arg_etherbase.is_some() { result.push(Deprecated::Replaced("--etherbase", "--author")); } - if args.flag_extradata.is_some() { + if args.arg_extradata.is_some() { result.push(Deprecated::Replaced("--extradata", "--extra-data")); } // Removed in 1.7 - if args.flag_dapps_port.is_some() { + if args.arg_dapps_port.is_some() { result.push(Deprecated::Replaced("--dapps-port", "--jsonrpc-port")); } - if args.flag_dapps_interface.is_some() { + if args.arg_dapps_interface.is_some() { result.push(Deprecated::Replaced("--dapps-interface", "--jsonrpc-interface")); } - if args.flag_dapps_hosts.is_some() { + if args.arg_dapps_hosts.is_some() { result.push(Deprecated::Replaced("--dapps-hosts", "--jsonrpc-hosts")); } - if args.flag_dapps_cors.is_some() { + if args.arg_dapps_cors.is_some() { result.push(Deprecated::Replaced("--dapps-cors", "--jsonrpc-cors")); } - if args.flag_dapps_user.is_some() { + if args.arg_dapps_user.is_some() { result.push(Deprecated::Removed("--dapps-user")); } - if args.flag_dapps_pass.is_some() { + if args.arg_dapps_pass.is_some() { result.push(Deprecated::Removed("--dapps-pass")); } - if args.flag_dapps_apis_all.is_some() { + if args.flag_dapps_apis_all { result.push(Deprecated::Replaced("--dapps-apis-all", "--jsonrpc-apis")); } @@ -124,15 +124,15 @@ mod tests { args.flag_dapps_off = true; args.flag_ipcdisable = true; args.flag_ipc_off = true; - args.flag_etherbase = Some(Default::default()); - args.flag_extradata = Some(Default::default()); - args.flag_dapps_port = Some(Default::default()); - args.flag_dapps_interface = Some(Default::default()); - args.flag_dapps_hosts = Some(Default::default()); - args.flag_dapps_cors = Some(Default::default()); - args.flag_dapps_user = Some(Default::default()); - args.flag_dapps_pass = Some(Default::default()); - args.flag_dapps_apis_all = Some(Default::default()); + args.arg_etherbase = Some(Default::default()); + args.arg_extradata = Some(Default::default()); + args.arg_dapps_port = Some(Default::default()); + args.arg_dapps_interface = Some(Default::default()); + args.arg_dapps_hosts = Some(Default::default()); + args.arg_dapps_cors = Some(Default::default()); + args.arg_dapps_user = Some(Default::default()); + args.arg_dapps_pass = Some(Default::default()); + args.flag_dapps_apis_all = true; args }), vec![ Deprecated::DoesNothing("--jsonrpc"), diff --git a/parity/informant.rs b/parity/informant.rs index deb2190d1..2c356a039 100644 --- a/parity/informant.rs +++ b/parity/informant.rs @@ -22,7 +22,7 @@ use std::sync::{Arc}; use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering}; use std::time::{Instant, Duration}; -use ethcore::client::{BlockId, BlockChainClient, BlockChainInfo, BlockQueueInfo, ChainNotify, ClientReport, Client}; +use ethcore::client::*; use ethcore::header::BlockNumber; use ethcore::service::ClientIoMessage; use ethcore::snapshot::{RestorationStatus, SnapshotService as SS}; diff --git a/parity/light_helpers/epoch_fetch.rs b/parity/light_helpers/epoch_fetch.rs deleted file mode 100644 index 8fccf049c..000000000 --- a/parity/light_helpers/epoch_fetch.rs +++ /dev/null @@ -1,90 +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::sync::{Arc, Weak}; - -use ethcore::encoded; -use ethcore::engines::{Engine, StateDependentProof}; -use ethcore::header::Header; -use ethcore::receipt::Receipt; -use ethsync::LightSync; - -use futures::{future, Future, BoxFuture}; - -use light::client::fetch::ChainDataFetcher; -use light::on_demand::{request, OnDemand}; - -use parking_lot::RwLock; -use bigint::hash::H256; - -const ALL_VALID_BACKREFS: &str = "no back-references, therefore all back-references valid; qed"; - -/// Allows on-demand fetch of data useful for the light client. -pub struct EpochFetch { - /// A handle to the sync service. - pub sync: Arc>>, - /// The on-demand request service. - pub on_demand: Arc, -} - -impl EpochFetch { - fn request(&self, req: T) -> BoxFuture - where T: Send + request::RequestAdapter + 'static, T::Out: Send + 'static - { - match self.sync.read().upgrade() { - Some(sync) => { - let on_demand = &self.on_demand; - let maybe_future = sync.with_context(move |ctx| { - on_demand.request(ctx, req).expect(ALL_VALID_BACKREFS) - }); - - match maybe_future { - Some(x) => x.map_err(|_| "Request canceled").boxed(), - None => future::err("Unable to access network.").boxed(), - } - } - None => future::err("Unable to access network").boxed(), - } - } -} - -impl ChainDataFetcher for EpochFetch { - type Error = &'static str; - - type Body = BoxFuture; - type Receipts = BoxFuture, &'static str>; - type Transition = BoxFuture, &'static str>; - - fn block_body(&self, header: &Header) -> Self::Body { - self.request(request::Body(header.encoded().into())) - } - - /// Fetch block receipts. - fn block_receipts(&self, header: &Header) -> Self::Receipts { - self.request(request::BlockReceipts(header.encoded().into())) - } - - /// Fetch epoch transition proof at given header. - fn epoch_transition(&self, hash: H256, engine: Arc, checker: Arc) - -> Self::Transition - { - self.request(request::Signal { - hash: hash, - engine: engine, - proof_check: checker, - }) - } -} diff --git a/parity/light_helpers/mod.rs b/parity/light_helpers/mod.rs index 5fc9c516b..488f970c2 100644 --- a/parity/light_helpers/mod.rs +++ b/parity/light_helpers/mod.rs @@ -16,8 +16,6 @@ //! Utilities and helpers for the light client. -mod epoch_fetch; mod queue_cull; -pub use self::epoch_fetch::EpochFetch; pub use self::queue_cull::QueueCull; diff --git a/parity/light_helpers/queue_cull.rs b/parity/light_helpers/queue_cull.rs index e024e70a5..eaf0ca9c5 100644 --- a/parity/light_helpers/queue_cull.rs +++ b/parity/light_helpers/queue_cull.rs @@ -23,7 +23,7 @@ use ethcore::service::ClientIoMessage; use ethsync::LightSync; use io::{IoContext, IoHandler, TimerToken}; -use light::client::LightChainClient; +use light::client::Client; use light::on_demand::{request, OnDemand}; use light::TransactionQueue; @@ -41,9 +41,9 @@ const TIMEOUT_MS: u64 = 1000 * 60 * 10; const PURGE_TIMEOUT_MS: u64 = 1000 * 60 * 9; /// Periodically culls the transaction queue of mined transactions. -pub struct QueueCull { +pub struct QueueCull { /// A handle to the client, for getting the latest block header. - pub client: Arc, + pub client: Arc, /// A handle to the sync service. pub sync: Arc, /// The on-demand request service. @@ -54,7 +54,7 @@ pub struct QueueCull { pub remote: Remote, } -impl IoHandler for QueueCull { +impl IoHandler for QueueCull { fn initialize(&self, io: &IoContext) { io.register_timer(TOKEN, TIMEOUT_MS).expect("Error registering timer"); } diff --git a/parity/main.rs b/parity/main.rs index a57989be5..1f065d838 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -26,6 +26,8 @@ extern crate ansi_term; extern crate app_dirs; extern crate ctrlc; extern crate docopt; +#[macro_use] +extern crate clap; extern crate env_logger; extern crate fdlimit; extern crate futures; diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 1e32c8bf1..d3171f381 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -32,7 +32,6 @@ use ethsync::{ManageNetwork, SyncProvider, LightSync}; use hash_fetch::fetch::Client as FetchClient; use jsonrpc_core::{self as core, MetaIoHandler}; use light::{TransactionQueue as LightTransactionQueue, Cache as LightDataCache}; -use light::client::LightChainClient; use node_health::NodeHealth; use parity_reactor; use parity_rpc::dispatch::{FullDispatcher, LightDispatcher}; @@ -399,9 +398,9 @@ impl ActivityNotifier for LightClientNotifier { } /// RPC dependencies for a light client. -pub struct LightDependencies { +pub struct LightDependencies { pub signer_service: Arc, - pub client: Arc, + pub client: Arc<::light::client::Client>, pub sync: Arc, pub net: Arc, pub secret_store: Arc, @@ -420,7 +419,7 @@ pub struct LightDependencies { pub whisper_rpc: Option<::whisper::RpcFactory>, } -impl LightDependencies { +impl LightDependencies { fn extend_api>( &self, handler: &mut MetaIoHandler, @@ -569,7 +568,7 @@ impl LightDependencies { } } -impl Dependencies for LightDependencies { +impl Dependencies for LightDependencies { type Notifier = LightClientNotifier; fn activity_notifier(&self) -> Self::Notifier { LightClientNotifier } diff --git a/parity/run.rs b/parity/run.rs index ee8ce5638..c9c1283ca 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -223,16 +223,7 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc) -> config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024; config.queue.verifier_settings = cmd.verifier_settings; - // start on_demand service. - let on_demand = Arc::new(::light::on_demand::OnDemand::new(cache.clone())); - - let sync_handle = Arc::new(RwLock::new(Weak::new())); - let fetch = ::light_helpers::EpochFetch { - on_demand: on_demand.clone(), - sync: sync_handle.clone(), - }; - - let service = light_client::Service::start(config, &spec, fetch, &db_dirs.client_path(algorithm), cache.clone()) + let service = light_client::Service::start(config, &spec, &db_dirs.client_path(algorithm), cache.clone()) .map_err(|e| format!("Error starting light client: {}", e))?; let txq = Arc::new(RwLock::new(::light::transaction_queue::TransactionQueue::default())); let provider = ::light::provider::LightProvider::new(service.client().clone(), txq.clone()); @@ -244,6 +235,9 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc) -> net_conf.boot_nodes = spec.nodes.clone(); } + // start on_demand service. + let on_demand = Arc::new(::light::on_demand::OnDemand::new(cache.clone())); + let mut attached_protos = Vec::new(); let whisper_factory = if cmd.whisper.enabled { let (whisper_net, whisper_factory) = ::whisper::setup(cmd.whisper.target_message_pool_size) @@ -267,7 +261,6 @@ fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc) -> }; let light_sync = LightSync::new(sync_params).map_err(|e| format!("Error starting network: {}", e))?; let light_sync = Arc::new(light_sync); - *sync_handle.write() = Arc::downgrade(&light_sync); // spin up event loop let event_loop = EventLoop::spawn(); diff --git a/rpc/src/v1/impls/light/eth.rs b/rpc/src/v1/impls/light/eth.rs index cb4550427..45c55346b 100644 --- a/rpc/src/v1/impls/light/eth.rs +++ b/rpc/src/v1/impls/light/eth.rs @@ -25,7 +25,7 @@ use jsonrpc_core::Error; use jsonrpc_macros::Trailing; use light::cache::Cache as LightDataCache; -use light::client::LightChainClient; +use light::client::{Client as LightClient, LightChainClient}; use light::{cht, TransactionQueue}; use light::on_demand::{request, OnDemand}; @@ -63,9 +63,9 @@ use util::Address; const NO_INVALID_BACK_REFS: &'static str = "Fails only on invalid back-references; back-references here known to be valid; qed"; /// Light client `ETH` (and filter) RPC. -pub struct EthClient { +pub struct EthClient { sync: Arc, - client: Arc, + client: Arc, on_demand: Arc, transaction_queue: Arc>, accounts: Arc, @@ -73,7 +73,7 @@ pub struct EthClient { polls: Mutex>, } -impl Clone for EthClient { +impl Clone for EthClient { fn clone(&self) -> Self { // each instance should have its own poll manager. EthClient { @@ -89,12 +89,12 @@ impl Clone for EthClient { } -impl EthClient { +impl EthClient { /// Create a new `EthClient` with a handle to the light sync instance, client, /// and on-demand request service, which is assumed to be attached as a handler. pub fn new( sync: Arc, - client: Arc, + client: Arc, on_demand: Arc, transaction_queue: Arc>, accounts: Arc, @@ -209,7 +209,7 @@ impl EthClient { } } -impl Eth for EthClient { +impl Eth for EthClient { type Metadata = Metadata; fn protocol_version(&self) -> Result { @@ -466,7 +466,7 @@ impl Eth for EthClient { } // This trait implementation triggers a blanked impl of `EthFilter`. -impl Filterable for EthClient { +impl Filterable for EthClient { fn best_block_number(&self) -> u64 { self.client.chain_info().best_block_number } fn block_hash(&self, id: BlockId) -> Option { diff --git a/sync/src/chain.rs b/sync/src/chain.rs index f00baf5a6..b3875fbcc 100644 --- a/sync/src/chain.rs +++ b/sync/src/chain.rs @@ -2244,7 +2244,7 @@ mod tests { use super::{PeerInfo, PeerAsking}; use ethkey; use ethcore::header::*; - use ethcore::client::{BlockChainClient, EachBlockWith, TestBlockChainClient}; + use ethcore::client::*; use ethcore::transaction::UnverifiedTransaction; use ethcore::miner::MinerService; diff --git a/sync/src/light_sync/tests/test_net.rs b/sync/src/light_sync/tests/test_net.rs index 535650ce1..65ddf92da 100644 --- a/sync/src/light_sync/tests/test_net.rs +++ b/sync/src/light_sync/tests/test_net.rs @@ -25,7 +25,7 @@ use tests::helpers::{TestNet, Peer as PeerLike, TestPacket}; use ethcore::client::TestBlockChainClient; use ethcore::spec::Spec; use io::IoChannel; -use light::client::fetch::{self, Unavailable}; +use light::client::Client as LightClient; use light::net::{LightProtocol, IoContext, Capabilities, Params as LightParams}; use light::provider::LightProvider; use network::{NodeId, PeerId}; @@ -36,8 +36,6 @@ use light::cache::Cache; const NETWORK_ID: u64 = 0xcafebabe; -pub type LightClient = ::light::client::Client; - struct TestIoContext<'a> { queue: &'a RwLock>, sender: Option, @@ -218,14 +216,7 @@ impl TestNet { // skip full verification because the blocks are bad. config.verify_full = false; let cache = Arc::new(Mutex::new(Cache::new(Default::default(), Duration::hours(6)))); - let client = LightClient::in_memory( - config, - &Spec::new_test(), - fetch::unavailable(), // TODO: allow fetch from full nodes. - IoChannel::disconnected(), - cache - ); - + let client = LightClient::in_memory(config, &Spec::new_test(), IoChannel::disconnected(), cache); peers.push(Arc::new(Peer::new_light(Arc::new(client)))) } diff --git a/sync/src/tests/consensus.rs b/sync/src/tests/consensus.rs index f45e614d7..a9c26712d 100644 --- a/sync/src/tests/consensus.rs +++ b/sync/src/tests/consensus.rs @@ -71,8 +71,8 @@ fn authority_round() { // Push transaction to both clients. Only one of them gets lucky to produce a block. net.peer(0).chain.miner().set_engine_signer(s0.address(), "".to_owned()).unwrap(); net.peer(1).chain.miner().set_engine_signer(s1.address(), "".to_owned()).unwrap(); - net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _); - net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _); + net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain)); + net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain)); net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1))); net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0))); // exchange statuses @@ -160,8 +160,8 @@ fn tendermint() { trace!(target: "poa", "Peer 0 is {}.", s0.address()); net.peer(1).chain.miner().set_engine_signer(s1.address(), "".to_owned()).unwrap(); trace!(target: "poa", "Peer 1 is {}.", s1.address()); - net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain) as _); - net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain) as _); + net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain)); + net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain)); net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0))); net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1))); // Exhange statuses