diff --git a/parity/cli.rs b/parity/cli.rs
new file mode 100644
index 000000000..45c621665
--- /dev/null
+++ b/parity/cli.rs
@@ -0,0 +1,223 @@
+// 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 util::version;
+
+pub const USAGE: &'static str = r#"
+Parity. Ethereum Client.
+ By Wood/Paronyan/Kotewicz/Drwięga/Volf.
+ Copyright 2015, 2016 Ethcore (UK) Limited
+
+Usage:
+ parity daemon [options]
+ parity account (new | list) [options]
+ parity [options]
+
+Protocol Options:
+ --chain CHAIN Specify the blockchain type. CHAIN may be either a
+ JSON chain specification file or olympic, frontier,
+ homestead, mainnet, morden, or testnet
+ [default: homestead].
+ -d --db-path PATH Specify the database & configuration directory path
+ [default: $HOME/.parity].
+ --keys-path PATH Specify the path for JSON key files to be found
+ [default: $HOME/.parity/keys].
+ --identity NAME Specify your node's name.
+
+Account Options:
+ --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution.
+ ACCOUNTS is a comma-delimited list of addresses.
+ --password FILE Provide a file containing a password for unlocking
+ an account.
+
+Networking Options:
+ --port PORT Override the port on which the node should listen
+ [default: 30303].
+ --peers NUM Try to maintain that many peers [default: 25].
+ --nat METHOD Specify method to use for determining public
+ address. Must be one of: any, none, upnp,
+ extip: [default: any].
+ --network-id INDEX Override the network identifier from the chain we
+ are on.
+ --bootnodes NODES Override the bootnodes from our chain. NODES should
+ be comma-delimited enodes.
+ --no-discovery Disable new peer discovery.
+ --node-key KEY Specify node secret key, either as 64-character hex
+ string or input to SHA3 operation.
+
+API and Console Options:
+ -j --jsonrpc Enable the JSON-RPC API server.
+ --jsonrpc-port PORT Specify the port portion of the JSONRPC API server
+ [default: 8545].
+ --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: local].
+ --jsonrpc-cors URL Specify CORS header for JSON-RPC API responses.
+ --jsonrpc-apis APIS Specify the APIs available through the JSONRPC
+ interface. APIS is a comma-delimited list of API
+ name. Possible name are web3, eth and net.
+ [default: web3,eth,net,personal,ethcore].
+ -w --webapp Enable the web applications server (e.g.
+ status page).
+ --webapp-port PORT Specify the port portion of the WebApps server
+ [default: 8080].
+ --webapp-interface IP Specify the hostname portion of the WebApps
+ server, IP should be an interface's IP address, or
+ all (all interfaces) or local [default: local].
+ --webapp-user USERNAME Specify username for WebApps server. It will be
+ used in HTTP Basic Authentication Scheme.
+ If --webapp-pass is not specified you will be
+ asked for password on startup.
+ --webapp-pass PASSWORD Specify password for WebApps server. Use only in
+ conjunction with --webapp-user.
+
+Sealing/Mining Options:
+ --force-sealing Force the node to author new blocks as if it were
+ always sealing/mining.
+ --usd-per-tx USD Amount of USD to be paid for a basic transaction
+ [default: 0.005]. 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 or a web service [default: etherscan].
+ --gas-floor-target GAS Amount of gas per block to target when sealing a new
+ block [default: 4712388].
+ --author ADDRESS Specify the block author (aka "coinbase") address
+ for sending block rewards from sealed blocks
+ [default: 0037a6b811ffeb6e072da21179d11b1406371c63].
+ --extra-data STRING Specify a custom extra-data for authored blocks, no
+ more than 32 characters.
+ --tx-limit LIMIT Limit of transactions kept in the queue (waiting to
+ be included in next block) [default: 1024].
+
+Footprint Options:
+ --pruning METHOD Configure pruning of the state/storage trie. METHOD
+ may be one of auto, archive, basic, fast, light:
+ archive - keep all state trie data. No pruning.
+ basic - reference count in disk DB. Slow but light.
+ fast - maintain journal overlay. Fast but 50MB used.
+ light - early merges with partial tracking. Fast
+ and light. Experimental!
+ auto - use the method most recently synced or
+ default to archive if none synced [default: auto].
+ --cache-pref-size BYTES Specify the prefered size of the blockchain cache in
+ bytes [default: 16384].
+ --cache-max-size BYTES Specify the maximum size of the blockchain cache in
+ bytes [default: 262144].
+ --queue-max-size BYTES Specify the maximum size of memory to use for block
+ queue [default: 52428800].
+ --cache MEGABYTES Set total amount of discretionary memory to use for
+ the entire system, overrides other cache and queue
+ options.
+
+Geth-compatibility Options:
+ --datadir PATH Equivalent to --db-path PATH.
+ --testnet Equivalent to --chain testnet.
+ --networkid INDEX Equivalent to --network-id INDEX.
+ --maxpeers COUNT Equivalent to --peers COUNT.
+ --nodekey KEY Equivalent to --node-key KEY.
+ --nodiscover Equivalent to --no-discovery.
+ --rpc Equivalent to --jsonrpc.
+ --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.
+ --gasprice WEI Minimum amount of Wei per GAS to be paid for a
+ transaction to be accepted for mining. Overrides
+ --basic-tx-usd.
+ --etherbase ADDRESS Equivalent to --author ADDRESS.
+ --extradata STRING Equivalent to --extra-data STRING.
+
+Miscellaneous Options:
+ -l --logging LOGGING Specify the logging level. Must conform to the same
+ format as RUST_LOG.
+ -v --version Show information about version.
+ -h --help Show this screen.
+"#;
+
+#[derive(Debug, RustcDecodable)]
+pub struct Args {
+ pub cmd_daemon: bool,
+ pub cmd_account: bool,
+ pub cmd_new: bool,
+ pub cmd_list: bool,
+ pub arg_pid_file: String,
+ pub flag_chain: String,
+ pub flag_db_path: String,
+ pub flag_identity: String,
+ pub flag_unlock: Option,
+ pub flag_password: Vec,
+ pub flag_cache: Option,
+ pub flag_keys_path: String,
+ pub flag_bootnodes: Option,
+ pub flag_network_id: Option,
+ pub flag_pruning: String,
+ pub flag_port: u16,
+ pub flag_peers: usize,
+ pub flag_no_discovery: bool,
+ pub flag_nat: String,
+ pub flag_node_key: Option,
+ pub flag_cache_pref_size: usize,
+ pub flag_cache_max_size: usize,
+ pub flag_queue_max_size: usize,
+ pub flag_jsonrpc: bool,
+ pub flag_jsonrpc_interface: String,
+ pub flag_jsonrpc_port: u16,
+ pub flag_jsonrpc_cors: Option,
+ pub flag_jsonrpc_apis: String,
+ pub flag_webapp: bool,
+ pub flag_webapp_port: u16,
+ pub flag_webapp_interface: String,
+ pub flag_webapp_user: Option,
+ pub flag_webapp_pass: Option,
+ pub flag_force_sealing: bool,
+ pub flag_author: String,
+ pub flag_usd_per_tx: String,
+ pub flag_usd_per_eth: String,
+ pub flag_gas_floor_target: String,
+ pub flag_extra_data: Option,
+ pub flag_tx_limit: usize,
+ pub flag_logging: Option,
+ pub flag_version: bool,
+ // geth-compatibility...
+ pub flag_nodekey: Option,
+ pub flag_nodiscover: bool,
+ pub flag_maxpeers: Option,
+ pub flag_datadir: Option,
+ pub flag_extradata: Option,
+ pub flag_etherbase: Option,
+ pub flag_gasprice: Option,
+ pub flag_rpc: bool,
+ pub flag_rpcaddr: Option,
+ pub flag_rpcport: Option,
+ pub flag_rpccorsdomain: Option,
+ pub flag_rpcapi: Option,
+ pub flag_testnet: bool,
+ pub flag_networkid: Option,
+}
+
+pub fn print_version() {
+ println!("\
+Parity
+ version {}
+Copyright 2015, 2016 Ethcore (UK) Limited
+License GPLv3+: GNU GPL version 3 or later .
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+
+By Wood/Paronyan/Kotewicz/Drwięga/Volf.\
+", version());
+}
+
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/die.rs b/parity/die.rs
new file mode 100644
index 000000000..602e2cdee
--- /dev/null
+++ b/parity/die.rs
@@ -0,0 +1,54 @@
+// 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;
+use ethcore;
+use util::UtilError;
+use std::process::exit;
+
+#[macro_export]
+macro_rules! die {
+ ($($arg:tt)*) => (die_with_message(&format!("{}", format_args!($($arg)*))));
+}
+
+pub fn die_with_error(e: ethcore::error::Error) -> ! {
+ use ethcore::error::Error;
+
+ match e {
+ Error::Util(UtilError::StdIo(e)) => die_with_io_error(e),
+ _ => die!("{:?}", e),
+ }
+}
+
+pub fn die_with_io_error(e: std::io::Error) -> ! {
+ match e.kind() {
+ std::io::ErrorKind::PermissionDenied => {
+ die!("No permissions to bind to specified port.")
+ },
+ std::io::ErrorKind::AddrInUse => {
+ die!("Specified address is already in use. Please make sure that nothing is listening on the same port or try using a different one.")
+ },
+ std::io::ErrorKind::AddrNotAvailable => {
+ die!("Could not use specified interface or given address is invalid.")
+ },
+ _ => die!("{:?}", e),
+ }
+}
+
+pub fn die_with_message(msg: &str) -> ! {
+ println!("ERROR: {}", msg);
+ exit(1);
+}
diff --git a/parity/informant.rs b/parity/informant.rs
new file mode 100644
index 000000000..866e92e40
--- /dev/null
+++ b/parity/informant.rs
@@ -0,0 +1,89 @@
+// 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::sync::RwLock;
+use std::ops::{Deref, DerefMut};
+use ethsync::{EthSync, SyncProvider};
+use ethcore::client::*;
+use number_prefix::{binary_prefix, Standalone, Prefixed};
+
+pub struct Informant {
+ chain_info: RwLock