openethereum/parity/main.rs

834 lines
27 KiB
Rust
Raw Normal View History

2016-02-05 13:40:41 +01:00
// 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 <http://www.gnu.org/licenses/>.
//! Ethcore client application.
#![warn(missing_docs)]
2016-03-11 11:16:49 +01:00
#![cfg_attr(feature="dev", feature(plugin))]
#![cfg_attr(feature="dev", plugin(clippy))]
2016-04-06 10:07:24 +02:00
#![cfg_attr(feature="dev", allow(useless_format))]
2016-01-23 23:53:20 +01:00
extern crate docopt;
extern crate num_cpus;
2016-01-23 23:53:20 +01:00
extern crate rustc_serialize;
2015-12-22 22:19:50 +01:00
extern crate ethcore_util as util;
2016-01-07 16:08:12 +01:00
extern crate ethcore;
2016-01-29 15:56:06 +01:00
extern crate ethsync;
extern crate ethminer;
2016-02-18 12:42:01 +01:00
#[macro_use]
2016-01-31 11:08:04 +01:00
extern crate log as rlog;
2016-01-09 23:21:57 +01:00
extern crate env_logger;
extern crate ctrlc;
2016-02-05 13:49:36 +01:00
extern crate fdlimit;
2016-02-18 13:10:04 +01:00
extern crate daemonize;
2016-02-23 20:14:37 +01:00
extern crate time;
2016-02-25 14:09:39 +01:00
extern crate number_prefix;
2016-03-09 14:11:15 +01:00
extern crate rpassword;
2015-12-22 22:19:50 +01:00
2016-03-28 00:49:35 +02:00
// for price_info.rs
#[macro_use] extern crate hyper;
#[cfg(feature = "rpc")]
2016-01-26 13:14:22 +01:00
extern crate ethcore_rpc as rpc;
2016-04-07 10:49:00 +02:00
#[cfg(feature = "webapp")]
extern crate ethcore_webapp as webapp;
use std::any::Any;
use std::io::{BufRead, BufReader};
2016-03-27 01:35:42 +01:00
use std::fs::File;
2016-03-10 21:36:45 +01:00
use std::net::{SocketAddr, IpAddr};
use std::env;
2016-02-19 12:54:51 +01:00
use std::process::exit;
2016-02-15 18:36:34 +01:00
use std::path::PathBuf;
use env_logger::LogBuilder;
use ctrlc::CtrlC;
2016-01-16 13:30:27 +01:00
use util::*;
use util::panics::{MayPanic, ForwardPanic, PanicHandler};
2016-03-19 23:51:24 +01:00
use util::keys::store::*;
use ethcore::spec::*;
2016-01-16 13:30:27 +01:00
use ethcore::client::*;
2016-01-21 23:33:52 +01:00
use ethcore::service::{ClientService, NetSyncMessage};
use ethcore::ethereum;
use ethsync::{EthSync, SyncConfig, SyncProvider};
use ethminer::{Miner, MinerService};
use docopt::Docopt;
use daemonize::Daemonize;
2016-02-25 14:09:39 +01:00
use number_prefix::{binary_prefix, Standalone, Prefixed};
2016-01-23 23:53:20 +01:00
2016-03-28 00:49:35 +02:00
mod price_info;
fn die_with_message(msg: &str) -> ! {
println!("ERROR: {}", msg);
exit(1);
}
#[macro_export]
macro_rules! die {
($($arg:tt)*) => (die_with_message(&format!("{}", format_args!($($arg)*))));
}
const USAGE: &'static str = r#"
2016-01-23 23:53:20 +01:00
Parity. Ethereum Client.
By Wood/Paronyan/Kotewicz/Drwięga/Volf.
Copyright 2015, 2016 Ethcore (UK) Limited
2016-01-23 23:53:20 +01:00
Usage:
2016-03-10 19:50:04 +01:00
parity daemon <pid-file> [options]
2016-04-06 13:03:53 +02:00
parity account (new | list) [options]
2016-03-10 19:50:04 +01:00
parity [options]
2016-01-23 23:53:20 +01:00
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
2016-04-06 13:03:53 +02:00
[default: $HOME/.parity/keys].
--identity NAME Specify your node's name.
2016-03-27 01:35:42 +01:00
Account Options:
--unlock ACCOUNT Unlock ACCOUNT for the duration of the execution.
--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].
2016-02-26 22:40:32 +01:00
--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:<IP> [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.
2016-02-26 22:40:32 +01:00
--no-discovery Disable new peer discovery.
--node-key KEY Specify node secret key, either as 64-character hex
string or input to SHA3 operation.
2016-02-08 15:04:12 +01:00
API and Console Options:
2016-04-07 11:06:49 +02:00
-j --jsonrpc Enable the JSON-RPC API server.
--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-port PORT Specify the port portion of the JSONRPC API server
[default: 8545].
--jsonrpc-cors URL Specify CORS header for JSON-RPC API responses
[default: null].
--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].
2016-04-07 12:10:26 +02:00
-w --webapp Enable the web applications server (e.g. status page).
2016-04-07 11:06:49 +02:00
--webapp-port PORT Specify the port portion of the WebApps server
[default: 8080].
Sealing/Mining Options:
2016-03-28 00:49:35 +02:00
--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.
Footprint Options:
--pruning METHOD Configure pruning of the state/storage trie. METHOD
may be one of: archive, basic (experimental), fast
(experimental) [default: archive].
--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:
2016-03-10 21:36:45 +01:00
--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.
2016-03-10 19:50:04 +01:00
--rpc Equivalent to --jsonrpc.
--rpcaddr IP Equivalent to --jsonrpc-interface IP.
2016-03-10 19:50:04 +01:00
--rpcport PORT Equivalent to --jsonrpc-port PORT.
--rpcapi APIS Equivalent to --jsonrpc-apis APIS.
--rpccorsdomain URL Equivalent to --jsonrpc-cors URL.
2016-03-28 00:49:35 +02:00
--gasprice WEI Minimum amount of Wei per GAS to be paid for a
transaction to be accepted for mining. Overrides
--basic-tx-usd.
2016-03-10 19:50:04 +01:00
--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)]
struct Args {
cmd_daemon: bool,
2016-03-09 14:11:15 +01:00
cmd_account: bool,
2016-03-09 20:39:36 +01:00
cmd_new: bool,
cmd_list: bool,
arg_pid_file: String,
flag_chain: String,
2016-03-10 21:36:45 +01:00
flag_db_path: String,
flag_identity: String,
2016-03-27 01:35:42 +01:00
flag_unlock: Vec<String>,
flag_password: Vec<String>,
flag_cache: Option<usize>,
flag_keys_path: String,
2016-03-10 21:36:45 +01:00
flag_bootnodes: Option<String>,
flag_network_id: Option<String>,
flag_pruning: String,
2016-03-10 21:36:45 +01:00
flag_port: u16,
flag_peers: usize,
flag_no_discovery: bool,
2016-03-10 21:36:45 +01:00
flag_nat: String,
flag_node_key: Option<String>,
flag_cache_pref_size: usize,
flag_cache_max_size: usize,
2016-02-25 14:09:39 +01:00
flag_queue_max_size: usize,
flag_jsonrpc: bool,
flag_jsonrpc_interface: String,
flag_jsonrpc_port: u16,
flag_jsonrpc_cors: String,
flag_jsonrpc_apis: String,
2016-04-07 11:06:49 +02:00
flag_webapp: bool,
flag_webapp_port: u16,
2016-03-13 15:29:55 +01:00
flag_author: String,
2016-03-28 00:49:35 +02:00
flag_usd_per_tx: String,
flag_usd_per_eth: String,
flag_gas_floor_target: String,
2016-03-13 15:29:55 +01:00
flag_extra_data: Option<String>,
2016-03-10 21:36:45 +01:00
flag_logging: Option<String>,
flag_version: bool,
// geth-compatibility...
flag_nodekey: Option<String>,
flag_nodiscover: bool,
flag_maxpeers: Option<usize>,
flag_datadir: Option<String>,
flag_extradata: Option<String>,
flag_etherbase: Option<String>,
2016-03-13 15:29:55 +01:00
flag_gasprice: Option<String>,
flag_rpc: bool,
flag_rpcaddr: Option<String>,
flag_rpcport: Option<u16>,
flag_rpccorsdomain: Option<String>,
flag_rpcapi: Option<String>,
2016-03-10 21:36:45 +01:00
flag_testnet: bool,
flag_networkid: Option<String>,
}
2015-12-22 22:19:50 +01:00
fn setup_log(init: &Option<String>) {
2016-02-23 20:14:37 +01:00
use rlog::*;
let mut builder = LogBuilder::new();
2016-01-15 01:44:57 +01:00
builder.filter(None, LogLevelFilter::Info);
if env::var("RUST_LOG").is_ok() {
builder.parse(&env::var("RUST_LOG").unwrap());
}
if let Some(ref s) = *init {
builder.parse(s);
}
2016-01-23 23:53:20 +01:00
2016-02-23 20:14:37 +01:00
let format = |record: &LogRecord| {
let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap();
if max_log_level() <= LogLevelFilter::Info {
format!("{}{}", timestamp, record.args())
} else {
format!("{}{}:{}: {}", timestamp, record.level(), record.target(), record.args())
}
};
builder.format(format);
builder.init().unwrap();
}
#[cfg(feature = "rpc")]
fn setup_rpc_server(
2016-03-12 19:19:16 +01:00
client: Arc<Client>,
sync: Arc<EthSync>,
secret_store: Arc<AccountService>,
miner: Arc<Miner>,
url: &str,
cors_domain: &str,
apis: Vec<&str>
) -> Box<Any> {
use rpc::v1::*;
let server = rpc::RpcServer::new();
for api in apis.into_iter() {
match api {
"web3" => server.add_delegate(Web3Client::new().to_delegate()),
"net" => server.add_delegate(NetClient::new(&sync).to_delegate()),
"eth" => {
server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate());
2016-03-10 16:00:55 +01:00
server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate());
}
2016-03-11 12:49:49 +01:00
"personal" => server.add_delegate(PersonalClient::new(&secret_store).to_delegate()),
_ => {
die!("{}: Invalid API name to be enabled.", api);
}
}
}
let start_result = server.start_http(url, cors_domain, ::num_cpus::get());
match start_result {
Err(rpc::RpcServerError::IoError(err)) => die_with_io_error(err),
Err(e) => die!("{:?}", e),
Ok(handle) => Box::new(handle),
}
}
2016-04-07 11:06:49 +02:00
#[cfg(feature = "webapp")]
fn setup_webapp_server(
2016-04-07 12:12:02 +02:00
client: Arc<Client>,
sync: Arc<EthSync>,
secret_store: Arc<AccountService>,
miner: Arc<Miner>,
2016-04-07 11:06:49 +02:00
url: &str
) -> Box<Any> {
2016-04-07 12:10:26 +02:00
use rpc::v1::*;
2016-04-07 11:06:49 +02:00
let server = webapp::WebappServer::new();
2016-04-07 12:10:26 +02:00
server.add_delegate(Web3Client::new().to_delegate());
2016-04-07 12:12:02 +02:00
server.add_delegate(NetClient::new(&sync).to_delegate());
server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate());
server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate());
server.add_delegate(PersonalClient::new(&secret_store).to_delegate());
let start_result = server.start_http(url, ::num_cpus::get());
match start_result {
Err(webapp::WebappServerError::IoError(err)) => die_with_io_error(err),
Err(e) => die!("{:?}", e),
Ok(handle) => Box::new(handle),
}
2016-04-07 11:06:49 +02:00
}
#[cfg(not(feature = "rpc"))]
fn setup_rpc_server(
2016-03-12 10:44:48 +01:00
_client: Arc<Client>,
_sync: Arc<EthSync>,
_secret_store: Arc<AccountService>,
_miner: Arc<Miner>,
_url: &str,
_cors_domain: &str,
_apis: Vec<&str>
) -> ! {
2016-04-07 12:13:06 +02:00
die!("Your Parity version has been compiled without JSON-RPC support.")
}
2016-04-07 11:06:49 +02:00
#[cfg(not(feature = "webapp"))]
fn setup_webapp_server(
2016-04-07 12:12:02 +02:00
_client: Arc<Client>,
_sync: Arc<EthSync>,
_secret_store: Arc<AccountService>,
_miner: Arc<Miner>,
2016-04-07 11:06:49 +02:00
_url: &str
) -> ! {
2016-04-07 12:13:06 +02:00
die!("Your Parity version has been compiled without WebApps support.")
2016-04-07 11:06:49 +02:00
}
2016-02-10 18:11:10 +01:00
fn print_version() {
println!("\
2016-02-21 20:00:45 +01:00
Parity
version {}
Copyright 2015, 2016 Ethcore (UK) Limited
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
2016-02-09 17:23:25 +01:00
By Wood/Paronyan/Kotewicz/Drwięga/Volf.\
2016-02-21 20:00:45 +01:00
", version());
2016-02-10 18:11:10 +01:00
}
struct Configuration {
args: Args
}
impl Configuration {
fn parse() -> Self {
Configuration {
args: Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()),
2016-02-10 18:11:10 +01:00
}
}
2016-02-10 21:17:47 +01:00
fn path(&self) -> String {
2016-03-10 21:36:45 +01:00
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())
2016-02-10 21:17:47 +01:00
}
fn author(&self) -> Address {
2016-03-10 21:36:45 +01:00
let d = self.args.flag_etherbase.as_ref().unwrap_or(&self.args.flag_author);
2016-03-19 23:51:24 +01:00
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)
})
}
2016-03-13 15:29:55 +01:00
fn gas_price(&self) -> U256 {
2016-03-28 00:49:35 +02:00
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() {
2016-04-06 10:07:24 +02:00
"etherscan" => price_info::PriceInfo::get().map_or_else(|| {
2016-03-28 00:49:35 +02:00
die!("Unable to retrieve USD value of ETH from etherscan. Rerun with a different value for --usd-per-eth.")
2016-04-06 10:07:24 +02:00
}, |x| x.ethusd),
2016-03-28 00:49:35 +02:00
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 {
2016-03-10 21:36:45 +01:00
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); }
}
}
2016-04-06 13:21:19 +02:00
fn keys_path(&self) -> String {
self.args.flag_keys_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap())
2016-02-18 12:42:01 +01:00
}
2016-02-10 21:17:47 +01:00
fn spec(&self) -> Spec {
if self.args.flag_testnet {
return ethereum::new_morden();
}
2016-02-10 14:16:42 +01:00
match self.args.flag_chain.as_ref() {
"frontier" | "homestead" | "mainnet" => ethereum::new_frontier(),
"morden" | "testnet" => ethereum::new_morden(),
"olympic" => ethereum::new_olympic(),
f => Spec::from_json_utf8(contents(f).unwrap_or_else(|_| {
die!("{}: Couldn't read chain specification file. Sure it exists?", f)
}).as_ref()),
2016-02-19 12:54:51 +01:00
}
}
fn normalize_enode(e: &str) -> Option<String> {
if is_valid_node_url(e) {
Some(e.to_owned())
} else {
None
2016-02-19 20:02:23 +01:00
}
}
2016-02-10 21:17:47 +01:00
fn init_nodes(&self, spec: &Spec) -> Vec<String> {
match self.args.flag_bootnodes {
2016-03-18 10:14:19 +01:00
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(),
}
}
2016-02-16 02:05:36 +01:00
fn net_addresses(&self) -> (Option<SocketAddr>, Option<SocketAddr>) {
2016-03-11 10:05:27 +01:00
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))
2016-03-10 21:36:45 +01:00
} else {
2016-03-12 10:07:55 +01:00
listen_address
2016-03-10 21:36:45 +01:00
};
(listen_address, public_address)
}
2016-02-18 12:42:01 +01:00
2016-02-19 12:54:51 +01:00
fn net_settings(&self, spec: &Spec) -> NetworkConfiguration {
let mut ret = NetworkConfiguration::new();
2016-03-10 21:36:45 +01:00
ret.nat_enabled = self.args.flag_nat == "any" || self.args.flag_nat == "upnp";
2016-02-19 12:54:51 +01:00
ret.boot_nodes = self.init_nodes(spec);
let (listen, public) = self.net_addresses();
ret.listen_address = listen;
ret.public_address = public;
2016-02-22 13:58:41 +01:00
ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3()));
2016-03-10 21:36:45 +01:00
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());
2016-02-19 20:02:23 +01:00
ret
2016-02-19 12:54:51 +01:00
}
fn client_config(&self) -> 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,
_ => { die!("Invalid pruning method given."); }
};
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))
2016-03-11 14:52:47 +01:00
});
sync_config
}
2016-02-18 12:42:01 +01:00
fn execute(&self) {
if self.args.flag_version {
print_version();
return;
}
2016-02-18 13:10:04 +01:00
if self.args.cmd_daemon {
2016-02-19 12:54:51 +01:00
Daemonize::new()
.pid_file(self.args.arg_pid_file.clone())
.chown_pid_file(true)
.start()
.unwrap_or_else(|e| die!("Couldn't daemonize; {}", e));
2016-02-18 13:10:04 +01:00
}
2016-03-09 14:11:15 +01:00
if self.args.cmd_account {
2016-03-09 20:39:36 +01:00
self.execute_account_cli();
2016-03-09 14:11:15 +01:00
return;
}
2016-02-18 12:42:01 +01:00
self.execute_client();
}
2016-03-09 20:39:36 +01:00
fn execute_account_cli(&self) {
2016-03-09 14:11:15 +01:00
use util::keys::store::SecretStore;
use rpassword::read_password;
2016-04-06 13:21:19 +02:00
let mut secret_store = SecretStore::new_in(Path::new(&self.keys_path()));
2016-03-09 20:39:36 +01:00
if self.args.cmd_new {
2016-03-09 14:11:15 +01:00
println!("Please note that password is NOT RECOVERABLE.");
println!("Type password: ");
let password = read_password().unwrap();
println!("Repeat password: ");
let password_repeat = read_password().unwrap();
if password != password_repeat {
println!("Passwords do not match!");
return;
}
println!("New account address:");
let new_address = secret_store.new_account(&password).unwrap();
println!("{:?}", new_address);
2016-03-09 16:27:44 +01:00
return;
2016-03-09 14:11:15 +01:00
}
2016-03-09 20:39:36 +01:00
if self.args.cmd_list {
2016-03-09 14:11:15 +01:00
println!("Known addresses:");
2016-03-12 10:07:55 +01:00
for &(addr, _) in &secret_store.accounts().unwrap() {
2016-03-09 14:11:15 +01:00
println!("{:?}", addr);
}
}
}
2016-03-27 01:35:42 +01:00
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::<Vec<_>>()
.into_iter()
}).collect::<Vec<_>>();
2016-03-27 01:45:43 +01:00
2016-04-06 13:21:19 +02:00
let account_service = AccountService::new_in(Path::new(&self.keys_path()));
2016-03-27 01:35:42 +01:00
for d in &self.args.flag_unlock {
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)
});
2016-03-27 01:45:43 +01:00
if passwords.iter().find(|p| account_service.unlock_account_no_expire(&a, p).is_ok()).is_none() {
2016-03-27 01:35:42 +01:00
die!("No password given to unlock account {}. Pass the password using `--password`.", a);
}
}
account_service
}
2016-02-18 12:42:01 +01:00
fn execute_client(&self) {
// Setup panic handler
let panic_handler = PanicHandler::new_in_arc();
2016-02-18 12:42:01 +01:00
// Setup logging
setup_log(&self.args.flag_logging);
// Raise fdlimit
unsafe { ::fdlimit::raise_fd_limit(); }
let spec = self.spec();
2016-02-19 12:54:51 +01:00
let net_settings = self.net_settings(&spec);
let sync_config = self.sync_config(&spec);
2016-02-18 12:42:01 +01:00
2016-03-27 01:35:42 +01:00
// Secret Store
let account_service = Arc::new(self.account_service());
2016-02-18 12:42:01 +01:00
// Build client
2016-04-06 19:07:27 +02:00
let mut service = ClientService::start(
self.client_config(), spec, net_settings, &Path::new(&self.path())
).unwrap_or_else(|e| die_with_error(e));
panic_handler.forward_from(&service);
let client = service.client();
// Miner
let miner = Miner::new();
miner.set_author(self.author());
miner.set_gas_floor_target(self.gas_floor_target());
miner.set_extra_data(self.extra_data());
2016-03-13 15:29:55 +01:00
miner.set_minimal_gas_price(self.gas_price());
2016-02-18 12:42:01 +01:00
// Sync
let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone());
2016-02-18 12:42:01 +01:00
// Setup rpc
2016-04-06 19:07:27 +02:00
let rpc_server = if self.args.flag_jsonrpc || self.args.flag_rpc {
let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis);
let url = format!("{}:{}",
match self.args.flag_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_interface).as_str() {
"all" => "0.0.0.0",
"local" => "127.0.0.1",
x => x,
},
self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port)
);
SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url));
2016-04-06 19:07:27 +02:00
let cors_domain = self.args.flag_rpccorsdomain.as_ref().unwrap_or(&self.args.flag_jsonrpc_cors);
Some(setup_rpc_server(
service.client(),
sync.clone(),
account_service.clone(),
miner.clone(),
2016-03-12 19:22:38 +01:00
&url,
&cors_domain,
2016-03-12 19:22:38 +01:00
apis.split(',').collect()
))
} else {
None
};
2016-02-18 12:42:01 +01:00
let webapp_server = if self.args.flag_webapp {
let url = format!("127.0.0.1:{}", self.args.flag_webapp_port);
Some(setup_webapp_server(
2016-04-07 12:12:02 +02:00
service.client(),
sync.clone(),
account_service.clone(),
miner.clone(),
2016-04-07 11:06:49 +02:00
&url,
))
} else {
None
};
2016-04-07 11:06:49 +02:00
2016-02-18 12:42:01 +01:00
// Register IO handler
let io_handler = Arc::new(ClientIoHandler {
client: service.client(),
info: Default::default(),
2016-03-09 11:38:53 +01:00
sync: sync.clone(),
2016-03-13 14:46:45 +01:00
accounts: account_service.clone(),
2016-02-18 12:42:01 +01:00
});
service.io().register_handler(io_handler).expect("Error registering IO handler");
// Handle exit
wait_for_exit(panic_handler, rpc_server, webapp_server);
2016-02-18 12:42:01 +01:00
}
}
fn wait_for_exit(panic_handler: Arc<PanicHandler>, _rpc_server: Option<Box<Any>>, _webapp_server: Option<Box<Any>>) {
let exit = Arc::new(Condvar::new());
2016-02-19 12:54:51 +01:00
// Handle possible exits
let e = exit.clone();
CtrlC::set_handler(move || { e.notify_all(); });
2016-02-19 12:54:51 +01:00
// Handle panics
let e = exit.clone();
panic_handler.on_panic(move |_reason| { e.notify_all(); });
2016-02-19 12:54:51 +01:00
// Wait for signal
let mutex = Mutex::new(());
let _ = exit.wait(mutex.lock().unwrap()).unwrap();
2016-04-06 23:58:23 +02:00
info!("Finishing work, please wait...");
2016-04-06 19:07:27 +02:00
}
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),
}
}
fn die_with_io_error(e: std::io::Error) -> ! {
match e.kind() {
std::io::ErrorKind::PermissionDenied => {
2016-04-06 19:22:10 +02:00
die!("No permissions to bind to specified port.")
2016-04-06 19:07:27 +02:00
},
std::io::ErrorKind::AddrInUse => {
2016-04-06 19:22:10 +02:00
die!("Specified address is already in use. Please make sure that nothing is listening on the same port or try using a different one.")
2016-04-06 19:07:27 +02:00
},
std::io::ErrorKind::AddrNotAvailable => {
2016-04-06 19:22:10 +02:00
die!("Could not use specified interface or given address is invalid.")
2016-04-06 19:07:27 +02:00
},
_ => die!("{:?}", e),
}
}
2015-12-22 22:19:50 +01:00
fn main() {
2016-02-18 12:42:01 +01:00
Configuration::parse().execute();
2015-12-22 22:19:50 +01:00
}
2016-01-18 23:23:32 +01:00
struct Informant {
chain_info: RwLock<Option<BlockChainInfo>>,
2016-02-25 14:09:39 +01:00
cache_info: RwLock<Option<BlockChainCacheSize>>,
report: RwLock<Option<ClientReport>>,
}
impl Default for Informant {
fn default() -> Self {
Informant {
chain_info: RwLock::new(None),
cache_info: RwLock::new(None),
report: RwLock::new(None),
}
}
2016-01-18 23:23:32 +01:00
}
impl Informant {
2016-02-25 14:09:39 +01:00
fn format_bytes(b: usize) -> String {
match binary_prefix(b as f64) {
Standalone(bytes) => format!("{} bytes", bytes),
Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix),
}
}
2016-01-22 04:54:38 +01:00
pub fn tick(&self, client: &Client, sync: &EthSync) {
2016-01-18 23:23:32 +01:00
// 5 seconds betwen calls. TODO: calculate this properly.
let dur = 5usize;
let chain_info = client.chain_info();
2016-01-22 04:54:38 +01:00
let queue_info = client.queue_info();
2016-02-25 14:09:39 +01:00
let cache_info = client.blockchain_cache_info();
2016-01-22 04:54:38 +01:00
let sync_info = sync.status();
2016-01-18 23:23:32 +01:00
2016-03-14 14:22:18 +01:00
let mut write_report = self.report.write().unwrap();
let report = client.report();
if let (_, _, &Some(ref last_report)) = (
self.chain_info.read().unwrap().deref(),
self.cache_info.read().unwrap().deref(),
write_report.deref()
) {
println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} db, {} chain, {} queue, {} sync ]",
2016-01-18 23:23:32 +01:00
chain_info.best_block_number,
chain_info.best_block_hash,
(report.blocks_imported - last_report.blocks_imported) / dur,
(report.transactions_applied - last_report.transactions_applied) / dur,
(report.gas_processed - last_report.gas_processed) / From::from(dur),
2016-01-22 04:54:38 +01:00
sync_info.num_active_peers,
sync_info.num_peers,
2016-02-11 22:14:06 +01:00
sync_info.last_imported_block_number.unwrap_or(chain_info.best_block_number),
queue_info.unverified_queue_size,
queue_info.verified_queue_size,
2016-01-22 04:54:38 +01:00
Informant::format_bytes(report.state_db_mem),
2016-02-25 14:09:39 +01:00
Informant::format_bytes(cache_info.total()),
Informant::format_bytes(queue_info.mem_used),
Informant::format_bytes(sync_info.mem_used),
2016-01-18 23:23:32 +01:00
);
}
*self.chain_info.write().unwrap().deref_mut() = Some(chain_info);
*self.cache_info.write().unwrap().deref_mut() = Some(cache_info);
*write_report.deref_mut() = Some(report);
2016-01-18 23:23:32 +01:00
}
}
2016-01-16 13:30:27 +01:00
const INFO_TIMER: TimerToken = 0;
2016-03-13 14:46:45 +01:00
const ACCOUNT_TICK_TIMER: TimerToken = 10;
const ACCOUNT_TICK_MS: u64 = 60000;
2016-01-16 13:30:27 +01:00
struct ClientIoHandler {
2016-01-21 23:33:52 +01:00
client: Arc<Client>,
2016-01-22 04:54:38 +01:00
sync: Arc<EthSync>,
2016-03-13 14:46:45 +01:00
accounts: Arc<AccountService>,
2016-01-18 23:23:32 +01:00
info: Informant,
2016-01-16 13:30:27 +01:00
}
impl IoHandler<NetSyncMessage> for ClientIoHandler {
fn initialize(&self, io: &IoContext<NetSyncMessage>) {
io.register_timer(INFO_TIMER, 5000).expect("Error registering timer");
2016-03-13 14:46:45 +01:00
io.register_timer(ACCOUNT_TICK_TIMER, ACCOUNT_TICK_MS).expect("Error registering account timer");
2016-01-16 13:30:27 +01:00
}
fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
2016-03-13 14:46:45 +01:00
match timer {
INFO_TIMER => { self.info.tick(&self.client, &self.sync); }
2016-03-13 15:11:16 +01:00
ACCOUNT_TICK_TIMER => { self.accounts.tick(); },
_ => {}
2016-01-16 13:30:27 +01:00
}
}
}
/// Parity needs at least 1 test to generate coverage reports correctly.
#[test]
fn if_works() {
}