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/>.
2016-01-27 17:18:38 +01:00
//! Ethcore client application.
#![ warn(missing_docs) ]
2016-02-19 00:50:23 +01:00
#![ cfg_attr(feature= " dev " , feature(plugin)) ]
2016-02-12 22:01:23 +01:00
#![ cfg_attr(feature= " dev " , plugin(clippy)) ]
2016-01-23 23:53:20 +01:00
extern crate docopt ;
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 ;
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 ;
2016-01-22 00:11:19 +01:00
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 ;
2015-12-22 22:19:50 +01:00
2016-01-20 04:19:38 +01:00
#[ cfg(feature = " rpc " ) ]
2016-01-26 13:14:22 +01:00
extern crate ethcore_rpc as rpc ;
2016-01-20 04:19:38 +01:00
2016-02-08 15:04:12 +01:00
use std ::net ::{ SocketAddr } ;
2016-01-14 22:38:49 +01:00
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 ;
2016-01-14 22:38:49 +01:00
use env_logger ::LogBuilder ;
2016-01-22 00:11:19 +01:00
use ctrlc ::CtrlC ;
2016-01-16 13:30:27 +01:00
use util ::* ;
2016-02-10 12:50:27 +01:00
use util ::panics ::MayPanic ;
2016-02-09 15:51:48 +01:00
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 } ;
2016-01-09 18:50:45 +01:00
use ethcore ::ethereum ;
2016-01-18 23:23:32 +01:00
use ethcore ::blockchain ::CacheSize ;
2016-01-29 15:56:06 +01:00
use ethsync ::EthSync ;
2016-02-18 14:16:55 +01:00
use docopt ::Docopt ;
use daemonize ::Daemonize ;
2016-01-23 23:53:20 +01:00
2016-02-18 14:16:55 +01:00
const USAGE : & 'static str = "
2016-01-23 23:53:20 +01:00
Parity . Ethereum Client .
2016-02-09 15:51:48 +01:00
By Wood / Paronyan / Kotewicz / Drwięga / Volf .
Copyright 2015 , 2016 Ethcore ( UK ) Limited
2016-01-23 23:53:20 +01:00
Usage :
2016-02-18 14:28:24 +01:00
parity daemon < pid - file > [ options ] [ - - no - bootstrap | < enode > .. . ]
2016-02-11 10:46:55 +01:00
parity [ options ] [ - - no - bootstrap | < enode > .. . ]
2016-01-23 23:53:20 +01:00
Options :
2016-02-09 15:51:48 +01:00
- - chain CHAIN Specify the blockchain type . CHAIN may be either a JSON chain specification file
2016-02-18 13:56:15 +01:00
or frontier , mainnet , morden , or testnet [ default : frontier ] .
2016-02-10 21:17:47 +01:00
- d - - db - path PATH Specify the database & configuration directory path [ default : $HOME / . parity ]
2016-02-18 12:42:01 +01:00
- - keys - path PATH Specify the path for JSON key files to be found [ default : $HOME / . web3 / keys ]
2016-02-02 01:59:14 +01:00
2016-02-11 10:46:55 +01:00
- - no - bootstrap Don ' t bother trying to connect to any nodes initially .
2016-02-08 15:04:12 +01:00
- - listen - address URL Specify the IP / port on which to listen for peers [ default : 0. 0. 0.0 :30304 ] .
2016-02-16 02:05:36 +01:00
- - public - address URL Specify the IP / port on which peers may connect .
2016-02-08 15:04:12 +01:00
- - address URL Equivalent to - - listen - address URL - - public - address URL .
2016-02-16 19:51:51 +01:00
- - peers NUM Try to manintain that many peers [ default : 25 ] .
- - no - discovery Disable new peer discovery .
2016-02-10 16:45:54 +01:00
- - upnp Use UPnP to try to figure out the correct network settings .
2016-02-22 18:11:53 +01:00
- - 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
2016-02-04 00:48:36 +01:00
- - 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 ] .
2016-02-02 01:59:14 +01:00
2016-02-09 15:51:48 +01:00
- j - - jsonrpc Enable the JSON - RPC API sever .
- - jsonrpc - url URL Specify URL for JSON - RPC API server [ default : 127. 0. 0.1 :8545 ] .
- l - - logging LOGGING Specify the logging level .
- v - - version Show information about version .
2016-02-02 01:59:14 +01:00
- h - - help Show this screen .
2016-02-18 14:16:55 +01:00
" ;
#[ derive(Debug, RustcDecodable) ]
struct Args {
cmd_daemon : bool ,
arg_pid_file : String ,
arg_enode : Vec < String > ,
flag_chain : String ,
flag_db_path : String ,
flag_keys_path : String ,
flag_no_bootstrap : bool ,
flag_listen_address : String ,
2016-02-18 21:49:11 +01:00
flag_public_address : Option < String > ,
2016-02-18 14:16:55 +01:00
flag_address : Option < String > ,
2016-02-18 21:49:11 +01:00
flag_peers : u32 ,
flag_no_discovery : bool ,
2016-02-18 14:16:55 +01:00
flag_upnp : bool ,
flag_node_key : Option < String > ,
flag_cache_pref_size : usize ,
flag_cache_max_size : usize ,
flag_jsonrpc : bool ,
flag_jsonrpc_url : String ,
flag_logging : Option < String > ,
flag_version : bool ,
}
2015-12-22 22:19:50 +01:00
2016-02-18 14:16:55 +01:00
fn setup_log ( init : & Option < String > ) {
2016-02-23 20:14:37 +01:00
use rlog ::* ;
2016-01-14 22:38:49 +01:00
let mut builder = LogBuilder ::new ( ) ;
2016-01-15 01:44:57 +01:00
builder . filter ( None , LogLevelFilter ::Info ) ;
2016-01-14 22:38:49 +01:00
if env ::var ( " RUST_LOG " ) . is_ok ( ) {
builder . parse ( & env ::var ( " RUST_LOG " ) . unwrap ( ) ) ;
}
2016-02-18 14:16:55 +01:00
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 ) ;
2016-01-14 22:38:49 +01:00
builder . init ( ) . unwrap ( ) ;
}
2016-01-20 04:19:38 +01:00
#[ cfg(feature = " rpc " ) ]
2016-02-02 01:59:14 +01:00
fn setup_rpc_server ( client : Arc < Client > , sync : Arc < EthSync > , url : & str ) {
2016-01-27 17:14:41 +01:00
use rpc ::v1 ::* ;
2016-02-10 12:50:27 +01:00
2016-01-27 17:18:38 +01:00
let mut server = rpc ::HttpServer ::new ( 1 ) ;
2016-01-21 00:54:19 +01:00
server . add_delegate ( Web3Client ::new ( ) . to_delegate ( ) ) ;
2016-02-10 16:28:59 +01:00
server . add_delegate ( EthClient ::new ( client . clone ( ) , sync . clone ( ) ) . to_delegate ( ) ) ;
2016-01-21 11:25:39 +01:00
server . add_delegate ( EthFilterClient ::new ( client ) . to_delegate ( ) ) ;
2016-01-28 10:54:49 +01:00
server . add_delegate ( NetClient ::new ( sync ) . to_delegate ( ) ) ;
2016-02-02 01:59:14 +01:00
server . start_async ( url ) ;
2016-01-20 04:19:38 +01:00
}
#[ cfg(not(feature = " rpc " )) ]
2016-02-02 01:59:14 +01:00
fn setup_rpc_server ( _client : Arc < Client > , _sync : Arc < EthSync > , _url : & str ) {
2016-01-20 04:19:38 +01:00
}
2016-02-10 18:11:10 +01:00
fn print_version ( ) {
println! ( " \
2016-02-21 20:00:45 +01:00
Parity
version { }
2016-02-09 15:51:48 +01:00
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
}
2016-02-19 12:54:51 +01:00
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 ) * ) ) ) ) ;
}
2016-02-10 18:11:10 +01:00
struct Configuration {
args : Args
}
impl Configuration {
fn parse ( ) -> Self {
Configuration {
2016-02-18 14:16:55 +01:00
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 13:13:04 +01:00
}
2016-02-10 21:17:47 +01:00
fn path ( & self ) -> String {
self . args . flag_db_path . replace ( " $HOME " , env ::home_dir ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) )
}
2016-02-18 21:49:11 +01:00
fn _keys_path ( & self ) -> String {
2016-02-23 11:40:23 +01:00
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 {
2016-02-10 14:16:42 +01:00
match self . args . flag_chain . as_ref ( ) {
2016-02-10 13:13:04 +01:00
" frontier " | " mainnet " = > ethereum ::new_frontier ( ) ,
" morden " | " testnet " = > ethereum ::new_morden ( ) ,
" olympic " = > ethereum ::new_olympic ( ) ,
2016-02-19 12:54:51 +01:00
f = > Spec ::from_json_utf8 ( contents ( f ) . unwrap_or_else ( | _ | die! ( " {}: Couldn't read chain specification file. Sure it exists? " , f ) ) . as_ref ( ) ) ,
}
}
fn normalize_enode ( e : & str ) -> Option < String > {
2016-02-23 11:40:23 +01:00
if is_valid_node_url ( e ) {
Some ( e . to_owned ( ) )
} else {
None
2016-02-19 20:02:23 +01:00
}
2016-02-10 13:13:04 +01:00
}
2016-02-10 21:17:47 +01:00
fn init_nodes ( & self , spec : & Spec ) -> Vec < String > {
2016-02-11 10:46:55 +01:00
if self . args . flag_no_bootstrap { Vec ::new ( ) } else {
match self . args . arg_enode . len ( ) {
0 = > spec . nodes ( ) . clone ( ) ,
2016-02-19 12:54:51 +01:00
_ = > self . args . arg_enode . iter ( ) . map ( | s | Self ::normalize_enode ( s ) . unwrap_or_else ( | | die! ( " {}: Invalid node address format given for a boot node. " , s ) ) ) . collect ( ) ,
2016-02-11 10:46:55 +01:00
}
2016-02-10 12:50:27 +01:00
}
}
2016-02-23 11:40:23 +01:00
#[ cfg_attr(feature= " dev " , allow(useless_format)) ]
2016-02-16 02:05:36 +01:00
fn net_addresses ( & self ) -> ( Option < SocketAddr > , Option < SocketAddr > ) {
let mut listen_address = None ;
let mut public_address = None ;
2016-02-10 12:50:27 +01:00
2016-02-16 02:05:36 +01:00
if let Some ( ref a ) = self . args . flag_address {
2016-02-22 13:58:41 +01:00
public_address = Some ( SocketAddr ::from_str ( a . as_ref ( ) ) . unwrap_or_else ( | _ | die! ( " {}: Invalid listen/public address given with --address " , a ) ) ) ;
2016-02-16 02:05:36 +01:00
listen_address = public_address ;
}
2016-02-16 19:51:51 +01:00
if listen_address . is_none ( ) {
2016-02-22 13:58:41 +01:00
listen_address = Some ( SocketAddr ::from_str ( self . args . flag_listen_address . as_ref ( ) ) . unwrap_or_else ( | _ | die! ( " {}: Invalid listen/public address given with --listen-address " , self . args . flag_listen_address ) ) ) ;
2016-02-16 02:05:36 +01:00
}
if let Some ( ref a ) = self . args . flag_public_address {
if public_address . is_some ( ) {
2016-02-22 13:58:41 +01:00
die! ( " Conflicting flags provided: --address and --public-address " ) ;
2016-02-10 12:50:27 +01:00
}
2016-02-22 13:58:41 +01:00
public_address = Some ( SocketAddr ::from_str ( a . as_ref ( ) ) . unwrap_or_else ( | _ | die! ( " {}: Invalid listen/public address given with --public-address " , a ) ) ) ;
2016-02-16 02:05:36 +01:00
}
2016-02-10 12:50:27 +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 ( ) ;
ret . nat_enabled = self . args . flag_upnp ;
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-02-19 19:55:26 +01:00
ret . discovery_enabled = ! self . args . flag_no_discovery ;
ret . ideal_peers = self . args . flag_peers ;
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
}
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-02-18 12:42:01 +01:00
self . execute_client ( ) ;
}
fn execute_client ( & self ) {
// 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 ) ;
2016-02-18 12:42:01 +01:00
// Build client
let mut service = ClientService ::start ( spec , net_settings , & Path ::new ( & self . path ( ) ) ) . unwrap ( ) ;
let client = service . client ( ) . clone ( ) ;
client . configure_cache ( self . args . flag_cache_pref_size , self . args . flag_cache_max_size ) ;
// Sync
let sync = EthSync ::register ( service . network ( ) , client ) ;
// Setup rpc
if self . args . flag_jsonrpc {
2016-02-22 13:58:41 +01:00
SocketAddr ::from_str ( & self . args . flag_jsonrpc_url ) . unwrap_or_else ( | _ | die! ( " {}: Invalid JSONRPC listen address given with --jsonrpc-url. Should be of the form 'IP:port'. " , self . args . flag_jsonrpc_url ) ) ;
2016-02-18 12:42:01 +01:00
setup_rpc_server ( service . client ( ) , sync . clone ( ) , & self . args . flag_jsonrpc_url ) ;
}
// Register IO handler
let io_handler = Arc ::new ( ClientIoHandler {
client : service . client ( ) ,
info : Default ::default ( ) ,
sync : sync
} ) ;
service . io ( ) . register_handler ( io_handler ) . expect ( " Error registering IO handler " ) ;
// Handle exit
wait_for_exit ( & service ) ;
}
2016-02-10 12:50:27 +01:00
}
2016-02-10 16:35:52 +01:00
fn wait_for_exit ( client_service : & ClientService ) {
2016-02-10 12:50:27 +01:00
let exit = Arc ::new ( Condvar ::new ( ) ) ;
2016-02-19 12:54:51 +01:00
2016-02-10 12:50:27 +01:00
// Handle possible exits
let e = exit . clone ( ) ;
CtrlC ::set_handler ( move | | { e . notify_all ( ) ; } ) ;
let e = exit . clone ( ) ;
2016-02-10 16:35:52 +01:00
client_service . on_panic ( move | _reason | { e . notify_all ( ) ; } ) ;
2016-02-19 12:54:51 +01:00
2016-02-10 12:50:27 +01:00
// Wait for signal
let mutex = Mutex ::new ( ( ) ) ;
let _ = exit . wait ( mutex . lock ( ) . unwrap ( ) ) . unwrap ( ) ;
}
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 {
2016-01-21 17:21:51 +01:00
chain_info : RwLock < Option < BlockChainInfo > > ,
cache_info : RwLock < Option < CacheSize > > ,
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-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 = 5 usize ;
let chain_info = client . chain_info ( ) ;
2016-01-22 04:54:38 +01:00
let queue_info = client . queue_info ( ) ;
2016-01-18 23:23:32 +01:00
let cache_info = client . cache_info ( ) ;
let report = client . report ( ) ;
2016-01-22 04:54:38 +01:00
let sync_info = sync . status ( ) ;
2016-01-18 23:23:32 +01:00
2016-01-21 17:21:51 +01:00
if let ( _ , & Some ( ref last_cache_info ) , & Some ( ref last_report ) ) = ( self . chain_info . read ( ) . unwrap ( ) . deref ( ) , self . cache_info . read ( ) . unwrap ( ) . deref ( ) , self . report . read ( ) . unwrap ( ) . deref ( ) ) {
2016-02-11 22:14:06 +01:00
println! ( " [ # {} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {} / {} peers, # {} , {} + {} queued ···// {} ( {} ) bl {} ( {} ) ex ] " ,
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 ) ,
2016-01-22 15:58:52 +01:00
queue_info . unverified_queue_size ,
queue_info . verified_queue_size ,
2016-01-22 04:54:38 +01:00
2016-01-18 23:23:32 +01:00
cache_info . blocks ,
cache_info . blocks as isize - last_cache_info . blocks as isize ,
cache_info . block_details ,
cache_info . block_details as isize - last_cache_info . block_details as isize
) ;
}
2016-01-21 17:21:51 +01:00
* self . chain_info . write ( ) . unwrap ( ) . deref_mut ( ) = Some ( chain_info ) ;
* self . cache_info . write ( ) . unwrap ( ) . deref_mut ( ) = Some ( cache_info ) ;
* self . report . write ( ) . unwrap ( ) . deref_mut ( ) = Some ( report ) ;
2016-01-18 23:23:32 +01:00
}
}
2016-01-16 13:30:27 +01:00
2016-01-21 17:21:51 +01:00
const INFO_TIMER : TimerToken = 0 ;
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-01-18 23:23:32 +01:00
info : Informant ,
2016-01-16 13:30:27 +01:00
}
impl IoHandler < NetSyncMessage > for ClientIoHandler {
2016-02-10 12:50:27 +01:00
fn initialize ( & self , io : & IoContext < NetSyncMessage > ) {
2016-01-21 17:21:51 +01:00
io . register_timer ( INFO_TIMER , 5000 ) . expect ( " Error registering timer " ) ;
2016-01-16 13:30:27 +01:00
}
2016-01-21 17:21:51 +01:00
fn timeout ( & self , _io : & IoContext < NetSyncMessage > , timer : TimerToken ) {
if INFO_TIMER = = timer {
2016-01-22 04:54:38 +01:00
self . info . tick ( & self . client , & self . sync ) ;
2016-01-16 13:30:27 +01:00
}
}
}
2016-02-02 02:04:03 +01:00
/// Parity needs at least 1 test to generate coverage reports correctly.
#[ test ]
fn if_works ( ) {
}