diff --git a/parity/configuration.rs b/parity/configuration.rs
new file mode 100644
index 000000000..d6a50d398
--- /dev/null
+++ b/parity/configuration.rs
@@ -0,0 +1,240 @@
+// Copyright 2015, 2016 Ethcore (UK) Ltd.
+// This file is part of Parity.
+
+// Parity is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Parity is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Parity. If not, see .
+
+use std::env;
+use std::fs::File;
+use std::io::{BufRead, BufReader};
+use std::net::{SocketAddr, IpAddr};
+use std::path::PathBuf;
+use cli::{USAGE, Args};
+use docopt::Docopt;
+
+use die::*;
+use util::*;
+use util::keys::store::AccountService;
+use ethcore::client::{append_path, get_db_path, ClientConfig};
+use ethcore::ethereum;
+use ethcore::spec::Spec;
+use ethsync::SyncConfig;
+use price_info::PriceInfo;
+
+pub struct Configuration {
+ pub args: Args
+}
+
+impl Configuration {
+ pub fn parse() -> Self {
+ Configuration {
+ args: Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()),
+ }
+ }
+
+ pub fn path(&self) -> String {
+ let d = self.args.flag_datadir.as_ref().unwrap_or(&self.args.flag_db_path);
+ d.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
+ }
+
+ pub fn author(&self) -> Address {
+ let d = self.args.flag_etherbase.as_ref().unwrap_or(&self.args.flag_author);
+ Address::from_str(clean_0x(d)).unwrap_or_else(|_| {
+ die!("{}: Invalid address for --author. Must be 40 hex characters, with or without the 0x at the beginning.", d)
+ })
+ }
+
+ pub fn gas_floor_target(&self) -> U256 {
+ let d = &self.args.flag_gas_floor_target;
+ U256::from_dec_str(d).unwrap_or_else(|_| {
+ die!("{}: Invalid target gas floor given. Must be a decimal unsigned 256-bit number.", d)
+ })
+ }
+
+ pub fn gas_price(&self) -> U256 {
+ match self.args.flag_gasprice.as_ref() {
+ Some(d) => {
+ U256::from_dec_str(d).unwrap_or_else(|_| {
+ die!("{}: Invalid gas price given. Must be a decimal unsigned 256-bit number.", d)
+ })
+ }
+ _ => {
+ let usd_per_tx: f32 = FromStr::from_str(&self.args.flag_usd_per_tx).unwrap_or_else(|_| {
+ die!("{}: Invalid basic transaction price given in USD. Must be a decimal number.", self.args.flag_usd_per_tx)
+ });
+ let usd_per_eth = match self.args.flag_usd_per_eth.as_str() {
+ "etherscan" => PriceInfo::get().map_or_else(|| {
+ die!("Unable to retrieve USD value of ETH from etherscan. Rerun with a different value for --usd-per-eth.")
+ }, |x| x.ethusd),
+ x => FromStr::from_str(x).unwrap_or_else(|_| die!("{}: Invalid ether price given in USD. Must be a decimal number.", x))
+ };
+ let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
+ let gas_per_tx: f32 = 21000.0;
+ let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx;
+ info!("Using a conversion rate of Ξ1 = US${} ({} wei/gas)", usd_per_eth, wei_per_gas);
+ U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap()
+ }
+ }
+ }
+
+ pub fn extra_data(&self) -> Bytes {
+ match self.args.flag_extradata.as_ref().or(self.args.flag_extra_data.as_ref()) {
+ Some(ref x) if x.len() <= 32 => x.as_bytes().to_owned(),
+ None => version_data(),
+ Some(ref x) => { die!("{}: Extra data must be at most 32 characters.", x); }
+ }
+ }
+
+ pub fn keys_path(&self) -> String {
+ self.args.flag_keys_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
+ }
+
+ pub fn spec(&self) -> Spec {
+ if self.args.flag_testnet {
+ return ethereum::new_morden();
+ }
+ match self.args.flag_chain.as_ref() {
+ "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
+ "morden" | "testnet" => ethereum::new_morden(),
+ "olympic" => ethereum::new_olympic(),
+ f => Spec::load(contents(f).unwrap_or_else(|_| {
+ die!("{}: Couldn't read chain specification file. Sure it exists?", f)
+ }).as_ref()),
+ }
+ }
+
+ pub fn normalize_enode(e: &str) -> Option {
+ if is_valid_node_url(e) {
+ Some(e.to_owned())
+ } else {
+ None
+ }
+ }
+
+ pub fn init_nodes(&self, spec: &Spec) -> Vec {
+ match self.args.flag_bootnodes {
+ Some(ref x) if !x.is_empty() => x.split(',').map(|s| {
+ Self::normalize_enode(s).unwrap_or_else(|| {
+ die!("{}: Invalid node address format given for a boot node.", s)
+ })
+ }).collect(),
+ Some(_) => Vec::new(),
+ None => spec.nodes().clone(),
+ }
+ }
+
+ pub fn net_addresses(&self) -> (Option, Option) {
+ let listen_address = Some(SocketAddr::new(IpAddr::from_str("0.0.0.0").unwrap(), self.args.flag_port));
+ let public_address = if self.args.flag_nat.starts_with("extip:") {
+ let host = &self.args.flag_nat[6..];
+ let host = IpAddr::from_str(host).unwrap_or_else(|_| die!("Invalid host given with `--nat extip:{}`", host));
+ Some(SocketAddr::new(host, self.args.flag_port))
+ } else {
+ listen_address
+ };
+ (listen_address, public_address)
+ }
+
+ pub fn net_settings(&self, spec: &Spec) -> NetworkConfiguration {
+ let mut ret = NetworkConfiguration::new();
+ ret.nat_enabled = self.args.flag_nat == "any" || self.args.flag_nat == "upnp";
+ ret.boot_nodes = self.init_nodes(spec);
+ let (listen, public) = self.net_addresses();
+ ret.listen_address = listen;
+ ret.public_address = public;
+ ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3()));
+ ret.discovery_enabled = !self.args.flag_no_discovery && !self.args.flag_nodiscover;
+ ret.ideal_peers = self.args.flag_maxpeers.unwrap_or(self.args.flag_peers) as u32;
+ let mut net_path = PathBuf::from(&self.path());
+ net_path.push("network");
+ ret.config_path = Some(net_path.to_str().unwrap().to_owned());
+ ret
+ }
+
+ pub fn find_best_db(&self, spec: &Spec) -> Option {
+ let mut ret = None;
+ let mut latest_era = None;
+ let jdb_types = [journaldb::Algorithm::Archive, journaldb::Algorithm::EarlyMerge, journaldb::Algorithm::OverlayRecent, journaldb::Algorithm::RefCounted];
+ for i in jdb_types.into_iter() {
+ let db = journaldb::new(&append_path(&get_db_path(&Path::new(&self.path()), *i, spec.genesis_header().hash()), "state"), *i);
+ trace!(target: "parity", "Looking for best DB: {} at {:?}", i, db.latest_era());
+ match (latest_era, db.latest_era()) {
+ (Some(best), Some(this)) if best >= this => {}
+ (_, None) => {}
+ (_, Some(this)) => {
+ latest_era = Some(this);
+ ret = Some(*i);
+ }
+ }
+ }
+ ret
+ }
+
+ pub fn client_config(&self, spec: &Spec) -> ClientConfig {
+ let mut client_config = ClientConfig::default();
+ match self.args.flag_cache {
+ Some(mb) => {
+ client_config.blockchain.max_cache_size = mb * 1024 * 1024;
+ client_config.blockchain.pref_cache_size = client_config.blockchain.max_cache_size * 3 / 4;
+ }
+ None => {
+ client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
+ client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
+ }
+ }
+ client_config.pruning = match self.args.flag_pruning.as_str() {
+ "archive" => journaldb::Algorithm::Archive,
+ "light" => journaldb::Algorithm::EarlyMerge,
+ "fast" => journaldb::Algorithm::OverlayRecent,
+ "basic" => journaldb::Algorithm::RefCounted,
+ "auto" => self.find_best_db(spec).unwrap_or(journaldb::Algorithm::OverlayRecent),
+ _ => { die!("Invalid pruning method given."); }
+ };
+ trace!(target: "parity", "Using pruning strategy of {}", client_config.pruning);
+ client_config.name = self.args.flag_identity.clone();
+ client_config.queue.max_mem_use = self.args.flag_queue_max_size;
+ client_config
+ }
+
+ pub fn sync_config(&self, spec: &Spec) -> SyncConfig {
+ let mut sync_config = SyncConfig::default();
+ sync_config.network_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref()).map_or(spec.network_id(), |id| {
+ U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --network-id/--networkid", id))
+ });
+ sync_config
+ }
+
+ pub fn account_service(&self) -> AccountService {
+ // Secret Store
+ let passwords = self.args.flag_password.iter().flat_map(|filename| {
+ BufReader::new(&File::open(filename).unwrap_or_else(|_| die!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename)))
+ .lines()
+ .map(|l| l.unwrap())
+ .collect::>()
+ .into_iter()
+ }).collect::>();
+ let account_service = AccountService::new_in(Path::new(&self.keys_path()));
+ if let Some(ref unlocks) = self.args.flag_unlock {
+ for d in unlocks.split(',') {
+ let a = Address::from_str(clean_0x(&d)).unwrap_or_else(|_| {
+ die!("{}: Invalid address for --unlock. Must be 40 hex characters, without the 0x at the beginning.", d)
+ });
+ if passwords.iter().find(|p| account_service.unlock_account_no_expire(&a, p).is_ok()).is_none() {
+ die!("No password given to unlock account {}. Pass the password using `--password`.", a);
+ }
+ }
+ }
+ account_service
+ }
+}
+
diff --git a/parity/main.rs b/parity/main.rs
index e8aa24839..267ae3d54 100644
--- a/parity/main.rs
+++ b/parity/main.rs
@@ -42,8 +42,8 @@ extern crate ethcore_ipc as ipc;
extern crate ethcore_ipc_nano as nanoipc;
extern crate serde;
extern crate bincode;
-// for price_info.rs
-#[macro_use] extern crate hyper;
+#[macro_use]
+extern crate hyper; // for price_info.rs
#[cfg(feature = "rpc")]
extern crate ethcore_rpc;
@@ -51,24 +51,6 @@ extern crate ethcore_rpc;
#[cfg(feature = "webapp")]
extern crate ethcore_webapp;
-use std::io::{BufRead, BufReader};
-use std::fs::File;
-use std::net::{SocketAddr, IpAddr};
-use std::env;
-use std::path::PathBuf;
-use ctrlc::CtrlC;
-use util::*;
-use util::panics::{MayPanic, ForwardPanic, PanicHandler};
-use util::keys::store::AccountService;
-use ethcore::ethereum;
-use ethcore::client::{append_path, get_db_path, ClientConfig};
-use ethcore::spec::Spec;
-use ethcore::service::ClientService;
-use ethsync::{EthSync, SyncConfig};
-use ethminer::{Miner, MinerService};
-use docopt::Docopt;
-use daemonize::Daemonize;
-
#[macro_use]
mod die;
mod price_info;
@@ -80,16 +62,22 @@ mod webapp;
mod informant;
mod io_handler;
mod cli;
+mod configuration;
+
+use ctrlc::CtrlC;
+use util::*;
+use util::panics::{MayPanic, ForwardPanic, PanicHandler};
+use ethcore::service::ClientService;
+use ethsync::EthSync;
+use ethminer::{Miner, MinerService};
+use daemonize::Daemonize;
use die::*;
-use cli::{USAGE, print_version, Args};
+use cli::print_version;
use rpc::RpcServer;
use webapp::WebappServer;
use io_handler::ClientIoHandler;
-
-struct Configuration {
- args: Args
-}
+use configuration::Configuration;
fn main() {
let conf = Configuration::parse();
@@ -254,209 +242,6 @@ fn wait_for_exit(panic_handler: Arc, _rpc_server: Option Self {
- Configuration {
- args: Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()),
- }
- }
-
- fn path(&self) -> String {
- let d = self.args.flag_datadir.as_ref().unwrap_or(&self.args.flag_db_path);
- d.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
- }
-
- fn author(&self) -> Address {
- let d = self.args.flag_etherbase.as_ref().unwrap_or(&self.args.flag_author);
- Address::from_str(clean_0x(d)).unwrap_or_else(|_| {
- die!("{}: Invalid address for --author. Must be 40 hex characters, with or without the 0x at the beginning.", d)
- })
- }
-
- fn gas_floor_target(&self) -> U256 {
- let d = &self.args.flag_gas_floor_target;
- U256::from_dec_str(d).unwrap_or_else(|_| {
- die!("{}: Invalid target gas floor given. Must be a decimal unsigned 256-bit number.", d)
- })
- }
-
- fn gas_price(&self) -> U256 {
- match self.args.flag_gasprice.as_ref() {
- Some(d) => {
- U256::from_dec_str(d).unwrap_or_else(|_| {
- die!("{}: Invalid gas price given. Must be a decimal unsigned 256-bit number.", d)
- })
- }
- _ => {
- let usd_per_tx: f32 = FromStr::from_str(&self.args.flag_usd_per_tx).unwrap_or_else(|_| {
- die!("{}: Invalid basic transaction price given in USD. Must be a decimal number.", self.args.flag_usd_per_tx)
- });
- let usd_per_eth = match self.args.flag_usd_per_eth.as_str() {
- "etherscan" => price_info::PriceInfo::get().map_or_else(|| {
- die!("Unable to retrieve USD value of ETH from etherscan. Rerun with a different value for --usd-per-eth.")
- }, |x| x.ethusd),
- x => FromStr::from_str(x).unwrap_or_else(|_| die!("{}: Invalid ether price given in USD. Must be a decimal number.", x))
- };
- let wei_per_usd: f32 = 1.0e18 / usd_per_eth;
- let gas_per_tx: f32 = 21000.0;
- let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx;
- info!("Using a conversion rate of Ξ1 = US${} ({} wei/gas)", usd_per_eth, wei_per_gas);
- U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap()
- }
- }
- }
-
- fn extra_data(&self) -> Bytes {
- match self.args.flag_extradata.as_ref().or(self.args.flag_extra_data.as_ref()) {
- Some(ref x) if x.len() <= 32 => x.as_bytes().to_owned(),
- None => version_data(),
- Some(ref x) => { die!("{}: Extra data must be at most 32 characters.", x); }
- }
- }
-
- fn keys_path(&self) -> String {
- self.args.flag_keys_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
- }
-
- fn spec(&self) -> Spec {
- if self.args.flag_testnet {
- return ethereum::new_morden();
- }
- match self.args.flag_chain.as_ref() {
- "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
- "morden" | "testnet" => ethereum::new_morden(),
- "olympic" => ethereum::new_olympic(),
- f => Spec::load(contents(f).unwrap_or_else(|_| {
- die!("{}: Couldn't read chain specification file. Sure it exists?", f)
- }).as_ref()),
- }
- }
-
- fn normalize_enode(e: &str) -> Option {
- if is_valid_node_url(e) {
- Some(e.to_owned())
- } else {
- None
- }
- }
-
- fn init_nodes(&self, spec: &Spec) -> Vec {
- match self.args.flag_bootnodes {
- Some(ref x) if !x.is_empty() => x.split(',').map(|s| {
- Self::normalize_enode(s).unwrap_or_else(|| {
- die!("{}: Invalid node address format given for a boot node.", s)
- })
- }).collect(),
- Some(_) => Vec::new(),
- None => spec.nodes().clone(),
- }
- }
-
- fn net_addresses(&self) -> (Option, Option) {
- let listen_address = Some(SocketAddr::new(IpAddr::from_str("0.0.0.0").unwrap(), self.args.flag_port));
- let public_address = if self.args.flag_nat.starts_with("extip:") {
- let host = &self.args.flag_nat[6..];
- let host = IpAddr::from_str(host).unwrap_or_else(|_| die!("Invalid host given with `--nat extip:{}`", host));
- Some(SocketAddr::new(host, self.args.flag_port))
- } else {
- listen_address
- };
- (listen_address, public_address)
- }
-
- fn net_settings(&self, spec: &Spec) -> NetworkConfiguration {
- let mut ret = NetworkConfiguration::new();
- ret.nat_enabled = self.args.flag_nat == "any" || self.args.flag_nat == "upnp";
- ret.boot_nodes = self.init_nodes(spec);
- let (listen, public) = self.net_addresses();
- ret.listen_address = listen;
- ret.public_address = public;
- ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3()));
- ret.discovery_enabled = !self.args.flag_no_discovery && !self.args.flag_nodiscover;
- ret.ideal_peers = self.args.flag_maxpeers.unwrap_or(self.args.flag_peers) as u32;
- let mut net_path = PathBuf::from(&self.path());
- net_path.push("network");
- ret.config_path = Some(net_path.to_str().unwrap().to_owned());
- ret
- }
-
- fn find_best_db(&self, spec: &Spec) -> Option {
- let mut ret = None;
- let mut latest_era = None;
- let jdb_types = [journaldb::Algorithm::Archive, journaldb::Algorithm::EarlyMerge, journaldb::Algorithm::OverlayRecent, journaldb::Algorithm::RefCounted];
- for i in jdb_types.into_iter() {
- let db = journaldb::new(&append_path(&get_db_path(&Path::new(&self.path()), *i, spec.genesis_header().hash()), "state"), *i);
- trace!(target: "parity", "Looking for best DB: {} at {:?}", i, db.latest_era());
- match (latest_era, db.latest_era()) {
- (Some(best), Some(this)) if best >= this => {}
- (_, None) => {}
- (_, Some(this)) => {
- latest_era = Some(this);
- ret = Some(*i);
- }
- }
- }
- ret
- }
-
- fn client_config(&self, spec: &Spec) -> ClientConfig {
- let mut client_config = ClientConfig::default();
- match self.args.flag_cache {
- Some(mb) => {
- client_config.blockchain.max_cache_size = mb * 1024 * 1024;
- client_config.blockchain.pref_cache_size = client_config.blockchain.max_cache_size * 3 / 4;
- }
- None => {
- client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size;
- client_config.blockchain.max_cache_size = self.args.flag_cache_max_size;
- }
- }
- client_config.pruning = match self.args.flag_pruning.as_str() {
- "archive" => journaldb::Algorithm::Archive,
- "light" => journaldb::Algorithm::EarlyMerge,
- "fast" => journaldb::Algorithm::OverlayRecent,
- "basic" => journaldb::Algorithm::RefCounted,
- "auto" => self.find_best_db(spec).unwrap_or(journaldb::Algorithm::OverlayRecent),
- _ => { die!("Invalid pruning method given."); }
- };
- trace!(target: "parity", "Using pruning strategy of {}", client_config.pruning);
- client_config.name = self.args.flag_identity.clone();
- client_config.queue.max_mem_use = self.args.flag_queue_max_size;
- client_config
- }
-
- fn sync_config(&self, spec: &Spec) -> SyncConfig {
- let mut sync_config = SyncConfig::default();
- sync_config.network_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref()).map_or(spec.network_id(), |id| {
- U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --network-id/--networkid", id))
- });
- sync_config
- }
-
- fn account_service(&self) -> AccountService {
- // Secret Store
- let passwords = self.args.flag_password.iter().flat_map(|filename| {
- BufReader::new(&File::open(filename).unwrap_or_else(|_| die!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename)))
- .lines()
- .map(|l| l.unwrap())
- .collect::>()
- .into_iter()
- }).collect::>();
- let account_service = AccountService::new_in(Path::new(&self.keys_path()));
- if let Some(ref unlocks) = self.args.flag_unlock {
- for d in unlocks.split(',') {
- let a = Address::from_str(clean_0x(&d)).unwrap_or_else(|_| {
- die!("{}: Invalid address for --unlock. Must be 40 hex characters, without the 0x at the beginning.", d)
- });
- if passwords.iter().find(|p| account_service.unlock_account_no_expire(&a, p).is_ok()).is_none() {
- die!("No password given to unlock account {}. Pass the password using `--password`.", a);
- }
- }
- }
- account_service
- }
-}
-
/// Parity needs at least 1 test to generate coverage reports correctly.
#[test]
fn if_works() {
diff --git a/parity/setup_log.rs b/parity/setup_log.rs
index 1fdc625f5..75cd0f574 100644
--- a/parity/setup_log.rs
+++ b/parity/setup_log.rs
@@ -42,7 +42,7 @@ pub fn setup_log(init: &Option) -> Arc {
}
let logs = Arc::new(RotatingLogger::new(levels));
- let loggger = logs.clone();
+ let logger = logs.clone();
let format = move |record: &LogRecord| {
let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap();
let format = if max_log_level() <= LogLevelFilter::Info {