Merge pull request #981 from ethcore/main-refactor
Refactoring of `parity/main.rs`
This commit is contained in:
commit
5ccf653841
223
parity/cli.rs
Normal file
223
parity/cli.rs
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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 <pid-file> [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:<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.
|
||||
--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<String>,
|
||||
pub flag_password: Vec<String>,
|
||||
pub flag_cache: Option<usize>,
|
||||
pub flag_keys_path: String,
|
||||
pub flag_bootnodes: Option<String>,
|
||||
pub flag_network_id: Option<String>,
|
||||
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<String>,
|
||||
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<String>,
|
||||
pub flag_jsonrpc_apis: String,
|
||||
pub flag_webapp: bool,
|
||||
pub flag_webapp_port: u16,
|
||||
pub flag_webapp_interface: String,
|
||||
pub flag_webapp_user: Option<String>,
|
||||
pub flag_webapp_pass: Option<String>,
|
||||
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<String>,
|
||||
pub flag_tx_limit: usize,
|
||||
pub flag_logging: Option<String>,
|
||||
pub flag_version: bool,
|
||||
// geth-compatibility...
|
||||
pub flag_nodekey: Option<String>,
|
||||
pub flag_nodiscover: bool,
|
||||
pub flag_maxpeers: Option<usize>,
|
||||
pub flag_datadir: Option<String>,
|
||||
pub flag_extradata: Option<String>,
|
||||
pub flag_etherbase: Option<String>,
|
||||
pub flag_gasprice: Option<String>,
|
||||
pub flag_rpc: bool,
|
||||
pub flag_rpcaddr: Option<String>,
|
||||
pub flag_rpcport: Option<u16>,
|
||||
pub flag_rpccorsdomain: Option<String>,
|
||||
pub flag_rpcapi: Option<String>,
|
||||
pub flag_testnet: bool,
|
||||
pub flag_networkid: Option<String>,
|
||||
}
|
||||
|
||||
pub fn print_version() {
|
||||
println!("\
|
||||
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.
|
||||
|
||||
By Wood/Paronyan/Kotewicz/Drwięga/Volf.\
|
||||
", version());
|
||||
}
|
||||
|
240
parity/configuration.rs
Normal file
240
parity/configuration.rs
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<String> {
|
||||
if is_valid_node_url(e) {
|
||||
Some(e.to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_nodes(&self, spec: &Spec) -> Vec<String> {
|
||||
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<SocketAddr>, Option<SocketAddr>) {
|
||||
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<journaldb::Algorithm> {
|
||||
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::<Vec<_>>()
|
||||
.into_iter()
|
||||
}).collect::<Vec<_>>();
|
||||
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
|
||||
}
|
||||
}
|
||||
|
54
parity/die.rs
Normal file
54
parity/die.rs
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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);
|
||||
}
|
89
parity/informant.rs
Normal file
89
parity/informant.rs
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<Option<BlockChainInfo>>,
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Informant {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(&self, client: &Client, sync: &EthSync) {
|
||||
// 5 seconds betwen calls. TODO: calculate this properly.
|
||||
let dur = 5usize;
|
||||
|
||||
let chain_info = client.chain_info();
|
||||
let queue_info = client.queue_info();
|
||||
let cache_info = client.blockchain_cache_info();
|
||||
let sync_info = sync.status();
|
||||
|
||||
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 ]",
|
||||
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),
|
||||
|
||||
sync_info.num_active_peers,
|
||||
sync_info.num_peers,
|
||||
sync_info.last_imported_block_number.unwrap_or(chain_info.best_block_number),
|
||||
queue_info.unverified_queue_size,
|
||||
queue_info.verified_queue_size,
|
||||
|
||||
Informant::format_bytes(report.state_db_mem),
|
||||
Informant::format_bytes(cache_info.total()),
|
||||
Informant::format_bytes(queue_info.mem_used),
|
||||
Informant::format_bytes(sync_info.mem_used),
|
||||
);
|
||||
}
|
||||
|
||||
*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);
|
||||
}
|
||||
}
|
||||
|
54
parity/io_handler.rs
Normal file
54
parity/io_handler.rs
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use ethcore::client::Client;
|
||||
use ethcore::service::NetSyncMessage;
|
||||
use ethsync::EthSync;
|
||||
use util::keys::store::AccountService;
|
||||
use util::{TimerToken, IoHandler, IoContext};
|
||||
|
||||
use informant::Informant;
|
||||
|
||||
const INFO_TIMER: TimerToken = 0;
|
||||
|
||||
const ACCOUNT_TICK_TIMER: TimerToken = 10;
|
||||
const ACCOUNT_TICK_MS: u64 = 60000;
|
||||
|
||||
pub struct ClientIoHandler {
|
||||
pub client: Arc<Client>,
|
||||
pub sync: Arc<EthSync>,
|
||||
pub accounts: Arc<AccountService>,
|
||||
pub info: Informant,
|
||||
}
|
||||
|
||||
impl IoHandler<NetSyncMessage> for ClientIoHandler {
|
||||
fn initialize(&self, io: &IoContext<NetSyncMessage>) {
|
||||
io.register_timer(INFO_TIMER, 5000).expect("Error registering timer");
|
||||
io.register_timer(ACCOUNT_TICK_TIMER, ACCOUNT_TICK_MS).expect("Error registering account timer");
|
||||
|
||||
}
|
||||
|
||||
fn timeout(&self, _io: &IoContext<NetSyncMessage>, timer: TimerToken) {
|
||||
match timer {
|
||||
INFO_TIMER => { self.info.tick(&self.client, &self.sync); }
|
||||
ACCOUNT_TICK_TIMER => { self.accounts.tick(); },
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
982
parity/main.rs
982
parity/main.rs
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,19 @@
|
||||
// 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/>.
|
||||
|
||||
use rustc_serialize::json::Json;
|
||||
use std::io::Read;
|
||||
use hyper::Client;
|
||||
|
110
parity/rpc.rs
Normal file
110
parity/rpc.rs
Normal file
@ -0,0 +1,110 @@
|
||||
// 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/>.
|
||||
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::net::SocketAddr;
|
||||
use ethcore::client::Client;
|
||||
use ethsync::EthSync;
|
||||
use ethminer::Miner;
|
||||
use util::RotatingLogger;
|
||||
use util::keys::store::{AccountService};
|
||||
use die::*;
|
||||
|
||||
#[cfg(feature = "rpc")]
|
||||
pub use ethcore_rpc::Server as RpcServer;
|
||||
#[cfg(feature = "rpc")]
|
||||
use ethcore_rpc::{RpcServerError, RpcServer as Server};
|
||||
#[cfg(not(feature = "rpc"))]
|
||||
pub struct RpcServer;
|
||||
|
||||
pub struct Configuration {
|
||||
pub enabled: bool,
|
||||
pub interface: String,
|
||||
pub port: u16,
|
||||
pub apis: String,
|
||||
pub cors: Option<String>,
|
||||
}
|
||||
|
||||
pub struct Dependencies {
|
||||
pub client: Arc<Client>,
|
||||
pub sync: Arc<EthSync>,
|
||||
pub secret_store: Arc<AccountService>,
|
||||
pub miner: Arc<Miner>,
|
||||
pub logger: Arc<RotatingLogger>,
|
||||
}
|
||||
|
||||
pub fn new(conf: Configuration, deps: Dependencies) -> Option<RpcServer> {
|
||||
if !conf.enabled {
|
||||
return None;
|
||||
}
|
||||
|
||||
let interface = match conf.interface.as_str() {
|
||||
"all" => "0.0.0.0",
|
||||
"local" => "127.0.0.1",
|
||||
x => x,
|
||||
};
|
||||
let apis = conf.apis.split(',').collect();
|
||||
let url = format!("{}:{}", interface, conf.port);
|
||||
let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url));
|
||||
|
||||
Some(setup_rpc_server(deps, &addr, conf.cors, apis))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rpc"))]
|
||||
pub fn setup_rpc_server(
|
||||
_deps: Dependencies,
|
||||
_url: &SocketAddr,
|
||||
_cors_domain: Option<String>,
|
||||
_apis: Vec<&str>,
|
||||
) -> ! {
|
||||
die!("Your Parity version has been compiled without JSON-RPC support.")
|
||||
}
|
||||
|
||||
#[cfg(feature = "rpc")]
|
||||
pub fn setup_rpc_server(
|
||||
deps: Dependencies,
|
||||
url: &SocketAddr,
|
||||
cors_domain: Option<String>,
|
||||
apis: Vec<&str>,
|
||||
) -> RpcServer {
|
||||
use ethcore_rpc::v1::*;
|
||||
|
||||
let server = Server::new();
|
||||
for api in apis.into_iter() {
|
||||
match api {
|
||||
"web3" => server.add_delegate(Web3Client::new().to_delegate()),
|
||||
"net" => server.add_delegate(NetClient::new(&deps.sync).to_delegate()),
|
||||
"eth" => {
|
||||
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner).to_delegate());
|
||||
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
|
||||
},
|
||||
"personal" => server.add_delegate(PersonalClient::new(&deps.secret_store).to_delegate()),
|
||||
"ethcore" => server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone()).to_delegate()),
|
||||
_ => {
|
||||
die!("{}: Invalid API name to be enabled.", api);
|
||||
},
|
||||
}
|
||||
}
|
||||
let start_result = server.start_http(url, cors_domain);
|
||||
match start_result {
|
||||
Err(RpcServerError::IoError(err)) => die_with_io_error(err),
|
||||
Err(e) => die!("{:?}", e),
|
||||
Ok(server) => server,
|
||||
}
|
||||
}
|
||||
|
60
parity/setup_log.rs
Normal file
60
parity/setup_log.rs
Normal file
@ -0,0 +1,60 @@
|
||||
// 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/>.
|
||||
|
||||
|
||||
use std::env;
|
||||
use std::sync::Arc;
|
||||
use time;
|
||||
use env_logger::LogBuilder;
|
||||
use util::{RotatingLogger};
|
||||
|
||||
/// Sets up the logger
|
||||
pub fn setup_log(init: &Option<String>) -> Arc<RotatingLogger> {
|
||||
use rlog::*;
|
||||
|
||||
let mut levels = String::new();
|
||||
let mut builder = LogBuilder::new();
|
||||
builder.filter(None, LogLevelFilter::Info);
|
||||
|
||||
if env::var("RUST_LOG").is_ok() {
|
||||
let lvl = &env::var("RUST_LOG").unwrap();
|
||||
levels.push_str(&lvl);
|
||||
levels.push_str(",");
|
||||
builder.parse(lvl);
|
||||
}
|
||||
|
||||
if let Some(ref s) = *init {
|
||||
levels.push_str(s);
|
||||
builder.parse(s);
|
||||
}
|
||||
|
||||
let logs = Arc::new(RotatingLogger::new(levels));
|
||||
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 {
|
||||
format!("{}{}", timestamp, record.args())
|
||||
} else {
|
||||
format!("{}{}:{}: {}", timestamp, record.level(), record.target(), record.args())
|
||||
};
|
||||
logger.append(format.clone());
|
||||
format
|
||||
};
|
||||
builder.format(format);
|
||||
builder.init().unwrap();
|
||||
logs
|
||||
}
|
||||
|
117
parity/webapp.rs
Normal file
117
parity/webapp.rs
Normal file
@ -0,0 +1,117 @@
|
||||
// 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/>.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::str::FromStr;
|
||||
use std::net::SocketAddr;
|
||||
use ethcore::client::Client;
|
||||
use ethsync::EthSync;
|
||||
use ethminer::Miner;
|
||||
use util::RotatingLogger;
|
||||
use util::keys::store::{AccountService};
|
||||
use die::*;
|
||||
|
||||
#[cfg(feature = "webapp")]
|
||||
pub use ethcore_webapp::Server as WebappServer;
|
||||
#[cfg(not(feature = "webapp"))]
|
||||
pub struct WebappServer;
|
||||
|
||||
pub struct Configuration {
|
||||
pub enabled: bool,
|
||||
pub interface: String,
|
||||
pub port: u16,
|
||||
pub user: Option<String>,
|
||||
pub pass: Option<String>,
|
||||
}
|
||||
|
||||
pub struct Dependencies {
|
||||
pub client: Arc<Client>,
|
||||
pub sync: Arc<EthSync>,
|
||||
pub secret_store: Arc<AccountService>,
|
||||
pub miner: Arc<Miner>,
|
||||
pub logger: Arc<RotatingLogger>,
|
||||
}
|
||||
|
||||
pub fn new(configuration: Configuration, deps: Dependencies) -> Option<WebappServer> {
|
||||
if !configuration.enabled {
|
||||
return None;
|
||||
}
|
||||
|
||||
let interface = match configuration.interface.as_str() {
|
||||
"all" => "0.0.0.0",
|
||||
"local" => "127.0.0.1",
|
||||
x => x,
|
||||
};
|
||||
let url = format!("{}:{}", interface, configuration.port);
|
||||
let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid Webapps listen host/port given.", url));
|
||||
|
||||
let auth = configuration.user.as_ref().map(|username| {
|
||||
let password = configuration.pass.as_ref().map_or_else(|| {
|
||||
use rpassword::read_password;
|
||||
println!("Type password for WebApps server (user: {}): ", username);
|
||||
let pass = read_password().unwrap();
|
||||
println!("OK, got it. Starting server...");
|
||||
pass
|
||||
}, |pass| pass.to_owned());
|
||||
(username.to_owned(), password)
|
||||
});
|
||||
|
||||
Some(setup_webapp_server(deps, &addr, auth))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "webapp"))]
|
||||
pub fn setup_webapp_server(
|
||||
_deps: Dependencies,
|
||||
_url: &SocketAddr,
|
||||
_auth: Option<(String, String)>,
|
||||
) -> ! {
|
||||
die!("Your Parity version has been compiled without WebApps support.")
|
||||
}
|
||||
|
||||
#[cfg(feature = "webapp")]
|
||||
pub fn setup_webapp_server(
|
||||
deps: Dependencies,
|
||||
url: &SocketAddr,
|
||||
auth: Option<(String, String)>
|
||||
) -> WebappServer {
|
||||
use ethcore_rpc::v1::*;
|
||||
use ethcore_webapp as webapp;
|
||||
|
||||
let server = webapp::ServerBuilder::new();
|
||||
server.add_delegate(Web3Client::new().to_delegate());
|
||||
server.add_delegate(NetClient::new(&deps.sync).to_delegate());
|
||||
server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner).to_delegate());
|
||||
server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate());
|
||||
server.add_delegate(PersonalClient::new(&deps.secret_store).to_delegate());
|
||||
server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger).to_delegate());
|
||||
|
||||
let start_result = match auth {
|
||||
None => {
|
||||
server.start_unsecure_http(url)
|
||||
},
|
||||
Some((username, password)) => {
|
||||
server.start_basic_auth_http(url, &username, &password)
|
||||
},
|
||||
};
|
||||
|
||||
match start_result {
|
||||
Err(webapp::ServerError::IoError(err)) => die_with_io_error(err),
|
||||
Err(e) => die!("{:?}", e),
|
||||
Ok(handle) => handle,
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user