diff --git a/Cargo.lock b/Cargo.lock index ceb14ff27..64e468e67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1954,7 +1954,6 @@ dependencies = [ "ethcore-private-tx 1.0.0", "ethcore-secretstore 1.0.0", "ethcore-service 0.1.0", - "ethcore-stratum 1.12.0", "ethcore-sync 1.12.0", "ethcore-transaction 0.1.0", "ethereum-types 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2008,6 +2007,13 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parity-clib" +version = "1.12.0" +dependencies = [ + "parity 1.12.0", +] + [[package]] name = "parity-dapps" version = "1.12.0" diff --git a/Cargo.toml b/Cargo.toml index a611458e6..de1a78bf4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,6 @@ ethcore-miner = { path = "miner" } ethcore-network = { path = "util/network" } ethcore-private-tx = { path = "ethcore/private-tx" } ethcore-service = { path = "ethcore/service" } -ethcore-stratum = { path = "ethcore/stratum" } ethcore-sync = { path = "ethcore/sync" } ethcore-transaction = { path = "ethcore/transaction" } ethereum-types = "0.3" @@ -108,6 +107,9 @@ slow-blocks = ["ethcore/slow-blocks"] secretstore = ["ethcore-secretstore"] final = ["parity-version/final"] +[lib] +path = "parity/lib.rs" + [[bin]] path = "parity/main.rs" name = "parity" @@ -130,6 +132,7 @@ members = [ "ethstore/cli", "evmbin", "miner", + "parity-clib", "transaction-pool", "whisper", "whisper/cli", diff --git a/parity-clib-example/CMakeLists.txt b/parity-clib-example/CMakeLists.txt new file mode 100644 index 000000000..143d014e3 --- /dev/null +++ b/parity-clib-example/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.5) +include(ExternalProject) + +include_directories("${CMAKE_SOURCE_DIR}/../parity-clib") + +add_executable(parity-example main.cpp) + +ExternalProject_Add( + libparity + DOWNLOAD_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + COMMAND cargo build -p parity-clib # Note: use --release in a real project + BINARY_DIR "${CMAKE_SOURCE_DIR}/../target" + INSTALL_COMMAND "" + LOG_BUILD ON) + +add_dependencies(parity-example libparity) +target_link_libraries(parity-example "${CMAKE_SOURCE_DIR}/../target/debug/libparity.so") diff --git a/parity-clib-example/main.cpp b/parity-clib-example/main.cpp new file mode 100644 index 000000000..1fadf1b5b --- /dev/null +++ b/parity-clib-example/main.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +void on_restart(void*, const char*, size_t) {} + +int main() { + ParityParams cfg = { 0 }; + cfg.on_client_restart_cb = on_restart; + + const char* args[] = {"--light"}; + size_t str_lens[] = {7}; + if (parity_config_from_cli(args, str_lens, 1, &cfg.configuration) != 0) { + return 1; + } + + void* parity; + if (parity_start(&cfg, &parity) != 0) { + return 1; + } + + sleep(5); + if (parity != NULL) { + parity_destroy(parity); + } + + return 0; +} diff --git a/parity-clib/Cargo.toml b/parity-clib/Cargo.toml new file mode 100644 index 000000000..001f954c2 --- /dev/null +++ b/parity-clib/Cargo.toml @@ -0,0 +1,17 @@ +[package] +description = "C bindings for the Parity Ethereum client" +name = "parity-clib" +version = "1.12.0" +license = "GPL-3.0" +authors = ["Parity Technologies "] + +[lib] +name = "parity" +crate-type = ["cdylib", "staticlib"] + +[dependencies] +parity = { path = "../", default-features = false } + +[features] +default = [] +final = ["parity/final"] diff --git a/parity-clib/parity.h b/parity-clib/parity.h new file mode 100644 index 000000000..b61da8e45 --- /dev/null +++ b/parity-clib/parity.h @@ -0,0 +1,93 @@ +// Copyright 2018 Parity Technologies (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 . + +#ifndef _PARITY_H_INCLUDED_ +#define _PARITY_H_INCLUDED_ + +#include + +/// Parameters to pass to `parity_start`. +struct ParityParams { + /// Configuration object, as handled by the `parity_config_*` functions. + /// Note that calling `parity_start` will destroy the configuration object (even on failure). + void *configuration; + + /// Callback function to call when the client receives an RPC request to change its chain spec. + /// + /// Will only be called if you enable the `--can-restart` flag. + /// + /// The first parameter of the callback is the value of `on_client_restart_cb_custom`. + /// The second and third parameters of the callback are the string pointer and length. + void (*on_client_restart_cb)(void* custom, const char* new_chain, size_t new_chain_len); + + /// Custom parameter passed to the `on_client_restart_cb` callback as first parameter. + void *on_client_restart_cb_custom; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/// Builds a new configuration object by parsing a list of CLI arguments. +/// +/// The first two parameters are string pointers and string lengths. They must have a length equal +/// to `len`. The strings don't need to be zero-terminated. +/// +/// On success, the produced object will be written to the `void*` pointed by `out`. +/// +/// Returns 0 on success, and non-zero on error. +/// +/// # Example +/// +/// ```no_run +/// void* cfg; +/// const char *args[] = {"--light", "--can-restart"}; +/// size_t str_lens[] = {7, 13}; +/// if (parity_config_from_cli(args, str_lens, 2, &cfg) != 0) { +/// return 1; +/// } +/// ``` +/// +int parity_config_from_cli(char const* const* args, size_t const* arg_lens, size_t len, void** out); + +/// Destroys a configuration object created earlier. +/// +/// **Important**: You probably don't need to call this function. Calling `parity_start` destroys +/// the configuration object as well (even on failure). +void parity_config_destroy(void* cfg); + +/// Starts the parity client in background threads. Returns a pointer to a struct that represents +/// the running client. Can also return NULL if the execution completes instantly. +/// +/// **Important**: The configuration object passed inside `cfg` is destroyed when you +/// call `parity_start` (even on failure). +/// +/// On success, the produced object will be written to the `void*` pointed by `out`. +/// +/// Returns 0 on success, and non-zero on error. +int parity_start(const ParityParams* params, void** out); + +/// Destroys the parity client created with `parity_start`. +/// +/// **Warning**: `parity_start` can return NULL if execution finished instantly, in which case you +/// must not call this function. +void parity_destroy(void* parity); + +#ifdef __cplusplus +} +#endif + +#endif // include guard diff --git a/parity-clib/src/lib.rs b/parity-clib/src/lib.rs new file mode 100644 index 000000000..b08d6487d --- /dev/null +++ b/parity-clib/src/lib.rs @@ -0,0 +1,133 @@ +// Copyright 2018 Parity Technologies (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 . + +//! Note that all the structs and functions here are documented in `parity.h`, to avoid +//! duplicating documentation. + +extern crate parity; + +use std::os::raw::{c_char, c_void, c_int}; +use std::panic; +use std::ptr; +use std::slice; + +#[repr(C)] +pub struct ParityParams { + pub configuration: *mut c_void, + pub on_client_restart_cb: Option, + pub on_client_restart_cb_custom: *mut c_void, +} + +#[no_mangle] +pub extern fn parity_config_from_cli(args: *const *const c_char, args_lens: *const usize, len: usize, output: *mut *mut c_void) -> c_int { + unsafe { + panic::catch_unwind(|| { + *output = ptr::null_mut(); + + let args = { + let arg_ptrs = slice::from_raw_parts(args, len); + let arg_lens = slice::from_raw_parts(args_lens, len); + + let mut args = Vec::with_capacity(len + 1); + args.push("parity".to_owned()); + + for (&arg, &len) in arg_ptrs.iter().zip(arg_lens.iter()) { + let string = slice::from_raw_parts(arg as *const u8, len); + match String::from_utf8(string.to_owned()) { + Ok(a) => args.push(a), + Err(_) => return 1, + }; + } + + args + }; + + match parity::Configuration::parse_cli(&args) { + Ok(mut cfg) => { + // Always disable the auto-updater when used as a library. + cfg.args.arg_auto_update = "none".to_owned(); + + let cfg = Box::into_raw(Box::new(cfg)); + *output = cfg as *mut _; + 0 + }, + Err(_) => { + 1 + }, + } + }).unwrap_or(1) + } +} + +#[no_mangle] +pub extern fn parity_config_destroy(cfg: *mut c_void) { + unsafe { + let _ = panic::catch_unwind(|| { + let _cfg = Box::from_raw(cfg as *mut parity::Configuration); + }); + } +} + +#[no_mangle] +pub extern fn parity_start(cfg: *const ParityParams, output: *mut *mut c_void) -> c_int { + unsafe { + panic::catch_unwind(|| { + *output = ptr::null_mut(); + let cfg: &ParityParams = &*cfg; + + let config = Box::from_raw(cfg.configuration as *mut parity::Configuration); + + let on_client_restart_cb = { + struct Cb(Option, *mut c_void); + unsafe impl Send for Cb {} + unsafe impl Sync for Cb {} + impl Cb { + fn call(&self, new_chain: String) { + if let Some(ref cb) = self.0 { + cb(self.1, new_chain.as_bytes().as_ptr() as *const _, new_chain.len()) + } + } + } + let cb = Cb(cfg.on_client_restart_cb, cfg.on_client_restart_cb_custom); + move |new_chain: String| { cb.call(new_chain); } + }; + + let action = match parity::start(*config, on_client_restart_cb, || {}) { + Ok(action) => action, + Err(_) => return 1, + }; + + match action { + parity::ExecutionAction::Instant(Some(s)) => { println!("{}", s); 0 }, + parity::ExecutionAction::Instant(None) => 0, + parity::ExecutionAction::Running(client) => { + *output = Box::into_raw(Box::::new(client)) as *mut c_void; + 0 + } + } + }).unwrap_or(1) + } +} + +#[no_mangle] +pub extern fn parity_destroy(client: *mut c_void) { + unsafe { + let _ = panic::catch_unwind(|| { + let client = Box::from_raw(client as *mut parity::RunningClient); + client.shutdown(); + }); + } +} diff --git a/parity/blockchain.rs b/parity/blockchain.rs index f9c2f8ba3..027814f24 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -37,7 +37,6 @@ use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_ use helpers::{to_client_config, execute_upgrades}; use dir::Directories; use user_defaults::UserDefaults; -use fdlimit; use ethcore_private_tx; use db; @@ -178,8 +177,6 @@ fn execute_import_light(cmd: ImportBlockchain) -> Result<(), String> { // load user defaults let user_defaults = UserDefaults::load(&user_defaults_path)?; - fdlimit::raise_fd_limit(); - // select pruning algorithm let algorithm = cmd.pruning.to_algorithm(&user_defaults); @@ -327,8 +324,6 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { // load user defaults let mut user_defaults = UserDefaults::load(&user_defaults_path)?; - fdlimit::raise_fd_limit(); - // select pruning algorithm let algorithm = cmd.pruning.to_algorithm(&user_defaults); @@ -518,8 +513,6 @@ fn start_client( // load user defaults let user_defaults = UserDefaults::load(&user_defaults_path)?; - fdlimit::raise_fd_limit(); - // select pruning algorithm let algorithm = pruning.to_algorithm(&user_defaults); diff --git a/parity/cli/usage.rs b/parity/cli/usage.rs index 2bdeaaed1..ce138fdff 100644 --- a/parity/cli/usage.rs +++ b/parity/cli/usage.rs @@ -198,6 +198,7 @@ macro_rules! usage { } } + /// Parsed command line arguments. #[derive(Debug, PartialEq)] pub struct Args { $( diff --git a/parity/configuration.rs b/parity/configuration.rs index 93cc9a4dd..315162180 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -92,23 +92,30 @@ pub struct Execute { pub cmd: Cmd, } +/// Configuration for the Parity client. #[derive(Debug, PartialEq)] pub struct Configuration { + /// Arguments to be interpreted. pub args: Args, } impl Configuration { - pub fn parse>(command: &[S]) -> Result { - let args = Args::parse(command)?; - + /// Parses a configuration from a list of command line arguments. + /// + /// # Example + /// + /// ``` + /// let _cfg = parity::Configuration::parse_cli(&["--light", "--chain", "koven"]).unwrap(); + /// ``` + pub fn parse_cli>(command: &[S]) -> Result { let config = Configuration { - args: args, + args: Args::parse(command)?, }; Ok(config) } - pub fn into_command(self) -> Result { + pub(crate) fn into_command(self) -> Result { let dirs = self.directories(); let pruning = self.args.arg_pruning.parse()?; let pruning_history = self.args.arg_pruning_history; @@ -1843,7 +1850,7 @@ mod tests { let filename = tempdir.path().join("peers"); File::create(&filename).unwrap().write_all(b" \n\t\n").unwrap(); let args = vec!["parity", "--reserved-peers", filename.to_str().unwrap()]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); assert!(conf.init_reserved_nodes().is_ok()); } @@ -1853,7 +1860,7 @@ mod tests { let filename = tempdir.path().join("peers_comments"); File::create(&filename).unwrap().write_all(b"# Sample comment\nenode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@172.0.0.1:30303\n").unwrap(); let args = vec!["parity", "--reserved-peers", filename.to_str().unwrap()]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); let reserved_nodes = conf.init_reserved_nodes(); assert!(reserved_nodes.is_ok()); assert_eq!(reserved_nodes.unwrap().len(), 1); @@ -1862,7 +1869,7 @@ mod tests { #[test] fn test_dev_preset() { let args = vec!["parity", "--config", "dev"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_settings.chain, "dev"); @@ -1876,7 +1883,7 @@ mod tests { #[test] fn test_mining_preset() { let args = vec!["parity", "--config", "mining"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_conf.min_peers, 50); @@ -1898,7 +1905,7 @@ mod tests { #[test] fn test_non_standard_ports_preset() { let args = vec!["parity", "--config", "non-standard-ports"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_settings.network_port, 30305); @@ -1911,7 +1918,7 @@ mod tests { #[test] fn test_insecure_preset() { let args = vec!["parity", "--config", "insecure"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.update_policy.require_consensus, false); @@ -1931,7 +1938,7 @@ mod tests { #[test] fn test_dev_insecure_preset() { let args = vec!["parity", "--config", "dev-insecure"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_settings.chain, "dev"); @@ -1954,7 +1961,7 @@ mod tests { #[test] fn test_override_preset() { let args = vec!["parity", "--config", "mining", "--min-peers=99"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_conf.min_peers, 99); @@ -2077,7 +2084,7 @@ mod tests { #[test] fn should_respect_only_max_peers_and_default() { let args = vec!["parity", "--max-peers=50"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_conf.min_peers, 25); @@ -2090,7 +2097,7 @@ mod tests { #[test] fn should_respect_only_max_peers_less_than_default() { let args = vec!["parity", "--max-peers=5"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_conf.min_peers, 5); @@ -2103,7 +2110,7 @@ mod tests { #[test] fn should_respect_only_min_peers_and_default() { let args = vec!["parity", "--min-peers=5"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_conf.min_peers, 5); @@ -2116,7 +2123,7 @@ mod tests { #[test] fn should_respect_only_min_peers_and_greater_than_default() { let args = vec!["parity", "--min-peers=500"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse_cli(&args).unwrap(); match conf.into_command().unwrap().cmd { Cmd::Run(c) => { assert_eq!(c.net_conf.min_peers, 500); diff --git a/parity/lib.rs b/parity/lib.rs new file mode 100644 index 000000000..4d9d1a2c9 --- /dev/null +++ b/parity/lib.rs @@ -0,0 +1,249 @@ +// Copyright 2015-2018 Parity Technologies (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 . + +//! Ethcore client application. + +#![warn(missing_docs)] + +extern crate ansi_term; +extern crate docopt; +#[macro_use] +extern crate clap; +extern crate dir; +extern crate env_logger; +extern crate futures; +extern crate futures_cpupool; +extern crate atty; +extern crate jsonrpc_core; +extern crate num_cpus; +extern crate number_prefix; +extern crate parking_lot; +extern crate regex; +extern crate rlp; +extern crate rpassword; +extern crate rustc_hex; +extern crate semver; +extern crate serde; +extern crate serde_json; +#[macro_use] +extern crate serde_derive; +extern crate toml; + +extern crate ethcore; +extern crate ethcore_bytes as bytes; +extern crate ethcore_io as io; +extern crate ethcore_light as light; +extern crate ethcore_logger; +extern crate ethcore_miner as miner; +extern crate ethcore_network as network; +extern crate ethcore_private_tx; +extern crate ethcore_service; +extern crate ethcore_sync as sync; +extern crate ethcore_transaction as transaction; +extern crate ethereum_types; +extern crate ethkey; +extern crate kvdb; +extern crate node_health; +extern crate panic_hook; +extern crate parity_hash_fetch as hash_fetch; +extern crate parity_ipfs_api; +extern crate parity_local_store as local_store; +extern crate parity_reactor; +extern crate parity_rpc; +extern crate parity_updater as updater; +extern crate parity_version; +extern crate parity_whisper; +extern crate path; +extern crate rpc_cli; +extern crate node_filter; +extern crate keccak_hash as hash; +extern crate journaldb; +extern crate registrar; + +#[macro_use] +extern crate log as rlog; + +#[cfg(feature="secretstore")] +extern crate ethcore_secretstore; + +#[cfg(feature = "dapps")] +extern crate parity_dapps; + +#[cfg(test)] +#[macro_use] +extern crate pretty_assertions; + +#[cfg(windows)] extern crate winapi; + +#[cfg(test)] +extern crate tempdir; + +mod account; +mod blockchain; +mod cache; +mod cli; +mod configuration; +mod dapps; +mod export_hardcoded_sync; +mod ipfs; +mod deprecated; +mod helpers; +mod informant; +mod light_helpers; +mod modules; +mod params; +mod presale; +mod rpc; +mod rpc_apis; +mod run; +mod secretstore; +mod signer; +mod snapshot; +mod upgrade; +mod url; +mod user_defaults; +mod whisper; +mod db; + +use std::net::{TcpListener}; +use std::io::BufReader; +use std::fs::File; +use ansi_term::Style; +use hash::keccak_buffer; +use cli::Args; +use configuration::{Cmd, Execute}; +use deprecated::find_deprecated; +use ethcore_logger::{Config as LogConfig, setup_log}; + +pub use self::configuration::Configuration; +pub use self::run::RunningClient; + +fn print_hash_of(maybe_file: Option) -> Result { + if let Some(file) = maybe_file { + let mut f = BufReader::new(File::open(&file).map_err(|_| "Unable to open file".to_owned())?); + let hash = keccak_buffer(&mut f).map_err(|_| "Unable to read from file".to_owned())?; + Ok(format!("{:x}", hash)) + } else { + Err("Streaming from standard input not yet supported. Specify a file.".to_owned()) + } +} + +/// Action that Parity performed when running `start`. +pub enum ExecutionAction { + /// The execution didn't require starting a node, and thus has finished. + /// Contains the string to print on stdout, if any. + Instant(Option), + + /// The client has started running and must be shut down manually by calling `shutdown`. + /// + /// If you don't call `shutdown()`, execution will continue in the background. + Running(RunningClient), +} + +fn execute(command: Execute, on_client_rq: Cr, on_updater_rq: Rr) -> Result + where Cr: Fn(String) + 'static + Send, + Rr: Fn() + 'static + Send +{ + // TODO: move this to `main()` and expose in the C API so that users can setup logging the way + // they want + let logger = setup_log(&command.logger).expect("Logger is initialized only once; qed"); + + match command.cmd { + Cmd::Run(run_cmd) => { + if run_cmd.ui_conf.enabled && !run_cmd.ui_conf.info_page_only { + warn!("{}", Style::new().bold().paint("Parity browser interface is deprecated. It's going to be removed in the next version, use standalone Parity UI instead.")); + warn!("{}", Style::new().bold().paint("Standalone Parity UI: https://github.com/Parity-JS/shell/releases")); + } + + if run_cmd.ui && run_cmd.dapps_conf.enabled { + // Check if Parity is already running + let addr = format!("{}:{}", run_cmd.ui_conf.interface, run_cmd.ui_conf.port); + if !TcpListener::bind(&addr as &str).is_ok() { + return open_ui(&run_cmd.ws_conf, &run_cmd.ui_conf, &run_cmd.logger_config).map(|_| ExecutionAction::Instant(None)); + } + } + + // start ui + if run_cmd.ui { + open_ui(&run_cmd.ws_conf, &run_cmd.ui_conf, &run_cmd.logger_config)?; + } + + if let Some(ref dapp) = run_cmd.dapp { + open_dapp(&run_cmd.dapps_conf, &run_cmd.http_conf, dapp)?; + } + + let outcome = run::execute(run_cmd, logger, on_client_rq, on_updater_rq)?; + Ok(ExecutionAction::Running(outcome)) + }, + Cmd::Version => Ok(ExecutionAction::Instant(Some(Args::print_version()))), + Cmd::Hash(maybe_file) => print_hash_of(maybe_file).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::Account(account_cmd) => account::execute(account_cmd).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::ImportPresaleWallet(presale_cmd) => presale::execute(presale_cmd).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::Blockchain(blockchain_cmd) => blockchain::execute(blockchain_cmd).map(|_| ExecutionAction::Instant(None)), + Cmd::SignerToken(ws_conf, ui_conf, logger_config) => signer::execute(ws_conf, ui_conf, logger_config).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::SignerSign { id, pwfile, port, authfile } => rpc_cli::signer_sign(id, pwfile, port, authfile).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::SignerList { port, authfile } => rpc_cli::signer_list(port, authfile).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::SignerReject { id, port, authfile } => rpc_cli::signer_reject(id, port, authfile).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::Snapshot(snapshot_cmd) => snapshot::execute(snapshot_cmd).map(|s| ExecutionAction::Instant(Some(s))), + Cmd::ExportHardcodedSync(export_hs_cmd) => export_hardcoded_sync::execute(export_hs_cmd).map(|s| ExecutionAction::Instant(Some(s))), + } +} + +/// Starts the parity client. +/// +/// `on_client_rq` is the action to perform when the client receives an RPC request to be restarted +/// with a different chain. +/// +/// `on_updater_rq` is the action to perform when the updater has a new binary to execute. +/// +/// The first parameter is the command line arguments that you would pass when running the parity +/// binary. +/// +/// On error, returns what to print on stderr. +pub fn start(conf: Configuration, on_client_rq: Cr, on_updater_rq: Rr) -> Result + where Cr: Fn(String) + 'static + Send, + Rr: Fn() + 'static + Send +{ + let deprecated = find_deprecated(&conf.args); + for d in deprecated { + println!("{}", d); + } + + execute(conf.into_command()?, on_client_rq, on_updater_rq) +} + +fn open_ui(ws_conf: &rpc::WsConfiguration, ui_conf: &rpc::UiConfiguration, logger_config: &LogConfig) -> Result<(), String> { + if !ui_conf.enabled { + return Err("Cannot use UI command with UI turned off.".into()) + } + + let token = signer::generate_token_and_url(ws_conf, ui_conf, logger_config)?; + // Open a browser + url::open(&token.url).map_err(|e| format!("{}", e))?; + // Print a message + println!("{}", token.message); + Ok(()) +} + +fn open_dapp(dapps_conf: &dapps::Configuration, rpc_conf: &rpc::HttpConfiguration, dapp: &str) -> Result<(), String> { + if !dapps_conf.enabled { + return Err("Cannot use DAPP command with Dapps turned off.".into()) + } + + let url = format!("http://{}:{}/{}/", rpc_conf.interface, rpc_conf.port, dapp); + url::open(&url).map_err(|e| format!("{}", e))?; + Ok(()) +} diff --git a/parity/main.rs b/parity/main.rs index 6774a8386..e489ad865 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -1,4 +1,4 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify @@ -18,187 +18,28 @@ #![warn(missing_docs)] -extern crate ansi_term; +extern crate parity; + extern crate ctrlc; -extern crate docopt; -#[macro_use] -extern crate clap; extern crate dir; -extern crate env_logger; extern crate fdlimit; -extern crate futures; -extern crate futures_cpupool; -extern crate atty; -extern crate jsonrpc_core; -extern crate num_cpus; -extern crate number_prefix; -extern crate parking_lot; -extern crate regex; -extern crate rlp; -extern crate rpassword; -extern crate rustc_hex; -extern crate semver; -extern crate serde; -extern crate serde_json; #[macro_use] -extern crate serde_derive; -extern crate toml; - -extern crate ethcore; -extern crate ethcore_bytes as bytes; -extern crate ethcore_io as io; -extern crate ethcore_light as light; -extern crate ethcore_logger; -extern crate ethcore_miner as miner; -extern crate ethcore_network as network; -extern crate ethcore_private_tx; -extern crate ethcore_service; -extern crate ethcore_sync as sync; -extern crate ethcore_transaction as transaction; -extern crate ethereum_types; -extern crate ethkey; -extern crate kvdb; -extern crate node_health; +extern crate log; extern crate panic_hook; -extern crate parity_hash_fetch as hash_fetch; -extern crate parity_ipfs_api; -extern crate parity_local_store as local_store; -extern crate parity_reactor; -extern crate parity_rpc; -extern crate parity_updater as updater; -extern crate parity_version; -extern crate parity_whisper; -extern crate path; -extern crate rpc_cli; -extern crate node_filter; -extern crate keccak_hash as hash; -extern crate journaldb; -extern crate registrar; - -#[macro_use] -extern crate log as rlog; - -#[cfg(feature="stratum")] -extern crate ethcore_stratum; - -#[cfg(feature="secretstore")] -extern crate ethcore_secretstore; - -#[cfg(feature = "dapps")] -extern crate parity_dapps; - -#[cfg(test)] -#[macro_use] -extern crate pretty_assertions; +extern crate parking_lot; #[cfg(windows)] extern crate winapi; -#[cfg(test)] -extern crate tempdir; - -mod account; -mod blockchain; -mod cache; -mod cli; -mod configuration; -mod dapps; -mod export_hardcoded_sync; -mod ipfs; -mod deprecated; -mod helpers; -mod informant; -mod light_helpers; -mod modules; -mod params; -mod presale; -mod rpc; -mod rpc_apis; -mod run; -mod secretstore; -mod signer; -mod snapshot; -mod upgrade; -mod url; -mod user_defaults; -mod whisper; -mod db; - -#[cfg(feature="stratum")] -mod stratum; - use std::{process, env}; -use std::collections::HashMap; -use std::io::{self as stdio, BufReader, Read, Write}; +use std::io::{self as stdio, Read, Write}; use std::fs::{remove_file, metadata, File, create_dir_all}; use std::path::PathBuf; -use hash::keccak_buffer; -use cli::Args; -use configuration::{Cmd, Execute, Configuration}; -use deprecated::find_deprecated; -use ethcore_logger::setup_log; +use std::sync::Arc; +use ctrlc::CtrlC; use dir::default_hypervisor_path; - -fn print_hash_of(maybe_file: Option) -> Result { - if let Some(file) = maybe_file { - let mut f = BufReader::new(File::open(&file).map_err(|_| "Unable to open file".to_owned())?); - let hash = keccak_buffer(&mut f).map_err(|_| "Unable to read from file".to_owned())?; - Ok(format!("{:x}", hash)) - } else { - Err("Streaming from standard input not yet supported. Specify a file.".to_owned()) - } -} - -enum PostExecutionAction { - Print(String), - Restart(Option), - Quit, -} - -fn execute(command: Execute, can_restart: bool) -> Result { - let logger = setup_log(&command.logger).expect("Logger is initialized only once; qed"); - - match command.cmd { - Cmd::Run(run_cmd) => { - let (restart, spec_name) = run::execute(run_cmd, can_restart, logger)?; - Ok(if restart { PostExecutionAction::Restart(spec_name) } else { PostExecutionAction::Quit }) - }, - Cmd::Version => Ok(PostExecutionAction::Print(Args::print_version())), - Cmd::Hash(maybe_file) => print_hash_of(maybe_file).map(|s| PostExecutionAction::Print(s)), - Cmd::Account(account_cmd) => account::execute(account_cmd).map(|s| PostExecutionAction::Print(s)), - Cmd::ImportPresaleWallet(presale_cmd) => presale::execute(presale_cmd).map(|s| PostExecutionAction::Print(s)), - Cmd::Blockchain(blockchain_cmd) => blockchain::execute(blockchain_cmd).map(|_| PostExecutionAction::Quit), - Cmd::SignerToken(ws_conf, ui_conf, logger_config) => signer::execute(ws_conf, ui_conf, logger_config).map(|s| PostExecutionAction::Print(s)), - Cmd::SignerSign { id, pwfile, port, authfile } => rpc_cli::signer_sign(id, pwfile, port, authfile).map(|s| PostExecutionAction::Print(s)), - Cmd::SignerList { port, authfile } => rpc_cli::signer_list(port, authfile).map(|s| PostExecutionAction::Print(s)), - Cmd::SignerReject { id, port, authfile } => rpc_cli::signer_reject(id, port, authfile).map(|s| PostExecutionAction::Print(s)), - Cmd::Snapshot(snapshot_cmd) => snapshot::execute(snapshot_cmd).map(|s| PostExecutionAction::Print(s)), - Cmd::ExportHardcodedSync(export_hs_cmd) => export_hardcoded_sync::execute(export_hs_cmd).map(|s| PostExecutionAction::Print(s)), - } -} - -fn start(mut args: Vec) -> Result { - args.insert(0, "parity".to_owned()); - let conf = Configuration::parse(&args).unwrap_or_else(|e| e.exit()); - let can_restart = conf.args.flag_can_restart; - - let deprecated = find_deprecated(&conf.args); - for d in deprecated { - println!("{}", d); - } - - let cmd = conf.into_command()?; - execute(cmd, can_restart) -} - -#[cfg(not(feature="stratum"))] -fn stratum_main(_: &mut HashMap) {} - -#[cfg(feature="stratum")] -fn stratum_main(alt_mains: &mut HashMap) { - alt_mains.insert("stratum".to_owned(), stratum::main); -} - -fn sync_main(_: &mut HashMap) {} +use fdlimit::raise_fd_limit; +use parity::{start, ExecutionAction}; +use parking_lot::{Condvar, Mutex}; fn updates_path(name: &str) -> PathBuf { let mut dest = PathBuf::from(default_hypervisor_path()); @@ -275,48 +116,68 @@ const PLEASE_RESTART_EXIT_CODE: i32 = 69; // Returns the exit error code. fn main_direct(force_can_restart: bool) -> i32 { global_init(); - let mut alt_mains = HashMap::new(); - sync_main(&mut alt_mains); - stratum_main(&mut alt_mains); - let res = if let Some(f) = std::env::args().nth(1).and_then(|arg| alt_mains.get(&arg.to_string())) { - f(); - 0 - } else { - let mut args = std::env::args().skip(1).collect::>(); - if force_can_restart && !args.iter().any(|arg| arg == "--can-restart") { - args.push("--can-restart".to_owned()); - } - if let Some(spec_override) = take_spec_name_override() { - args.retain(|f| f != "--testnet"); - args.retain(|f| !f.starts_with("--chain=")); - while let Some(pos) = args.iter().position(|a| a == "--chain") { - if args.len() > pos + 1 { - args.remove(pos + 1); - } - args.remove(pos); - } - args.push("--chain".to_owned()); - args.push(spec_override); - } - - match start(args) { - Ok(result) => match result { - PostExecutionAction::Print(s) => { println!("{}", s); 0 }, - PostExecutionAction::Restart(spec_name_override) => { - if let Some(spec_name) = spec_name_override { - set_spec_name_override(spec_name); - } - PLEASE_RESTART_EXIT_CODE - }, - PostExecutionAction::Quit => 0, - }, - Err(err) => { - writeln!(&mut stdio::stderr(), "{}", err).expect("StdErr available; qed"); - 1 - }, - } + let mut conf = { + let args = std::env::args().collect::>(); + parity::Configuration::parse_cli(&args).unwrap_or_else(|e| e.exit()) }; + + if let Some(spec_override) = take_spec_name_override() { + conf.args.flag_testnet = false; + conf.args.arg_chain = spec_override; + } + + let can_restart = force_can_restart || conf.args.flag_can_restart; + + // increase max number of open files + raise_fd_limit(); + + let exit = Arc::new((Mutex::new((false, None)), Condvar::new())); + + let exec = if can_restart { + let e1 = exit.clone(); + let e2 = exit.clone(); + start(conf, + move |new_chain: String| { *e1.0.lock() = (true, Some(new_chain)); e1.1.notify_all(); }, + move || { *e2.0.lock() = (true, None); e2.1.notify_all(); }) + } else { + trace!(target: "mode", "Not hypervised: not setting exit handlers."); + start(conf, move |_| {}, move || {}) + }; + + let res = match exec { + Ok(result) => match result { + ExecutionAction::Instant(Some(s)) => { println!("{}", s); 0 }, + ExecutionAction::Instant(None) => 0, + ExecutionAction::Running(client) => { + CtrlC::set_handler({ + let e = exit.clone(); + move || { e.1.notify_all(); } + }); + + // Wait for signal + let mut lock = exit.0.lock(); + let _ = exit.1.wait(&mut lock); + + client.shutdown(); + + match &*lock { + &(true, ref spec_name_override) => { + if let &Some(ref spec_name) = spec_name_override { + set_spec_name_override(spec_name.clone()); + } + PLEASE_RESTART_EXIT_CODE + }, + _ => 0, + } + }, + }, + Err(err) => { + writeln!(&mut stdio::stderr(), "{}", err).expect("StdErr available; qed"); + 1 + }, + }; + global_cleanup(); res } diff --git a/parity/run.rs b/parity/run.rs index fdb32293b..73113055b 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -19,10 +19,8 @@ use std::fmt; use std::sync::{Arc, Weak}; use std::time::{Duration, Instant}; use std::thread; -use std::net::{TcpListener}; -use ansi_term::{Colour, Style}; -use ctrlc::CtrlC; +use ansi_term::Colour; use ethcore::account_provider::{AccountProvider, AccountProviderSettings}; use ethcore::client::{Client, Mode, DatabaseCompactionProfile, VMType, BlockChainClient, BlockInfo}; use ethcore::ethstore::ethkey; @@ -34,7 +32,6 @@ use ethcore_logger::{Config as LogConfig, RotatingLogger}; use ethcore_service::ClientService; use sync::{self, SyncConfig}; use miner::work_notify::WorkPoster; -use fdlimit::raise_fd_limit; use futures_cpupool::CpuPool; use hash_fetch::{self, fetch}; use informant::{Informant, LightNodeInformantData, FullNodeInformantData}; @@ -45,7 +42,6 @@ use node_filter::NodeFilter; use node_health; use parity_reactor::EventLoop; use parity_rpc::{NetworkSettings, informant, is_major_importing}; -use parking_lot::{Condvar, Mutex}; use updater::{UpdatePolicy, Updater}; use parity_version::version; use ethcore_private_tx::{ProviderConfig, EncryptorConfig, SecretStoreEncryptor}; @@ -65,7 +61,6 @@ use rpc; use rpc_apis; use secretstore; use signer; -use url; use db; // how often to take periodic snapshots. @@ -138,28 +133,6 @@ pub struct RunCmd { pub no_hardcoded_sync: bool, } -pub fn open_ui(ws_conf: &rpc::WsConfiguration, ui_conf: &rpc::UiConfiguration, logger_config: &LogConfig) -> Result<(), String> { - if !ui_conf.enabled { - return Err("Cannot use UI command with UI turned off.".into()) - } - - let token = signer::generate_token_and_url(ws_conf, ui_conf, logger_config)?; - // Open a browser - url::open(&token.url).map_err(|e| format!("{}", e))?; - // Print a message - println!("{}", token.message); - Ok(()) -} - -pub fn open_dapp(dapps_conf: &dapps::Configuration, rpc_conf: &rpc::HttpConfiguration, dapp: &str) -> Result<(), String> { - if !dapps_conf.enabled { - return Err("Cannot use DAPP command with Dapps turned off.".into()) - } - - let url = format!("http://{}:{}/{}/", rpc_conf.interface, rpc_conf.port, dapp); - url::open(&url).map_err(|e| format!("{}", e))?; - Ok(()) -} // node info fetcher for the local store. struct FullNodeInfo { miner: Option>, // TODO: only TXQ needed, just use that after decoupling. @@ -415,10 +388,12 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc) -> Result(cmd: RunCmd, logger: Arc, on_client_rq: }, }; - // start ui - if cmd.ui { - open_ui(&cmd.ws_conf, &cmd.ui_conf, &cmd.logger_config)?; - } - - if let Some(dapp) = cmd.dapp { - open_dapp(&cmd.dapps_conf, &cmd.http_conf, &dapp)?; - } - client.set_exit_handler(on_client_rq); updater.set_exit_handler(on_updater_rq); - Ok(RunningClient::Full { - informant, - client, - keep_alive: Box::new((watcher, service, updater, ws_server, http_server, ipc_server, ui_server, secretstore_key_server, ipfs_server, event_loop)), + Ok(RunningClient { + inner: RunningClientInner::Full { + informant, + client, + keep_alive: Box::new((watcher, service, updater, ws_server, http_server, ipc_server, ui_server, secretstore_key_server, ipfs_server, event_loop)), + } }) } -enum RunningClient { +/// Parity client currently executing in background threads. +/// +/// Should be destroyed by calling `shutdown()`, otherwise execution will continue in the +/// background. +pub struct RunningClient { + inner: RunningClientInner +} + +enum RunningClientInner { Light { informant: Arc>, client: Arc, @@ -931,9 +907,10 @@ enum RunningClient { } impl RunningClient { - fn shutdown(self) { - match self { - RunningClient::Light { informant, client, keep_alive } => { + /// Shuts down the client. + pub fn shutdown(self) { + match self.inner { + RunningClientInner::Light { informant, client, keep_alive } => { // Create a weak reference to the client so that we can wait on shutdown // until it is dropped let weak_client = Arc::downgrade(&client); @@ -943,7 +920,7 @@ impl RunningClient { drop(client); wait_for_drop(weak_client); }, - RunningClient::Full { informant, client, keep_alive } => { + RunningClientInner::Full { informant, client, keep_alive } => { info!("Finishing work, please wait..."); // Create a weak reference to the client so that we can wait on shutdown // until it is dropped @@ -961,51 +938,24 @@ impl RunningClient { } } -pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> Result<(bool, Option), String> { - if cmd.ui_conf.enabled && !cmd.ui_conf.info_page_only { - warn!("{}", Style::new().bold().paint("Parity browser interface is deprecated. It's going to be removed in the next version, use standalone Parity UI instead.")); - warn!("{}", Style::new().bold().paint("Standalone Parity UI: https://github.com/Parity-JS/shell/releases")); - } - - if cmd.ui && cmd.dapps_conf.enabled { - // Check if Parity is already running - let addr = format!("{}:{}", cmd.ui_conf.interface, cmd.ui_conf.port); - if !TcpListener::bind(&addr as &str).is_ok() { - return open_ui(&cmd.ws_conf, &cmd.ui_conf, &cmd.logger_config).map(|_| (false, None)); - } - } - - // increase max number of open files - raise_fd_limit(); - - let exit = Arc::new((Mutex::new((false, None)), Condvar::new())); - - let running_client = if cmd.light { - execute_light_impl(cmd, logger)? - } else if can_restart { - let e1 = exit.clone(); - let e2 = exit.clone(); - execute_impl(cmd, logger, - move |new_chain: String| { *e1.0.lock() = (true, Some(new_chain)); e1.1.notify_all(); }, - move || { *e2.0.lock() = (true, None); e2.1.notify_all(); })? +/// Executes the given run command. +/// +/// `on_client_rq` is the action to perform when the client receives an RPC request to be restarted +/// with a different chain. +/// +/// `on_updater_rq` is the action to perform when the updater has a new binary to execute. +/// +/// On error, returns what to print on stderr. +pub fn execute(cmd: RunCmd, logger: Arc, + on_client_rq: Cr, on_updater_rq: Rr) -> Result + where Cr: Fn(String) + 'static + Send, + Rr: Fn() + 'static + Send +{ + if cmd.light { + execute_light_impl(cmd, logger) } else { - trace!(target: "mode", "Not hypervised: not setting exit handlers."); - execute_impl(cmd, logger, move |_| {}, move || {})? - }; - - // Handle possible exits - CtrlC::set_handler({ - let e = exit.clone(); - move || { e.1.notify_all(); } - }); - - // Wait for signal - let mut l = exit.0.lock(); - let _ = exit.1.wait(&mut l); - - running_client.shutdown(); - - Ok(l.clone()) + execute_impl(cmd, logger, on_client_rq, on_updater_rq) + } } #[cfg(not(windows))] diff --git a/parity/snapshot.rs b/parity/snapshot.rs index ad93801c0..423864679 100644 --- a/parity/snapshot.rs +++ b/parity/snapshot.rs @@ -35,7 +35,6 @@ use params::{SpecType, Pruning, Switch, tracing_switch_to_bool, fatdb_switch_to_ use helpers::{to_client_config, execute_upgrades}; use dir::Directories; use user_defaults::UserDefaults; -use fdlimit; use ethcore_private_tx; use db; @@ -149,8 +148,6 @@ impl SnapshotCommand { // load user defaults let user_defaults = UserDefaults::load(&user_defaults_path)?; - fdlimit::raise_fd_limit(); - // select pruning algorithm let algorithm = self.pruning.to_algorithm(&user_defaults); diff --git a/parity/url.rs b/parity/url.rs index 41c4e5458..4f547c28f 100644 --- a/parity/url.rs +++ b/parity/url.rs @@ -80,8 +80,9 @@ pub fn open(url: &str) -> Result<(), Error> { } #[cfg(target_os="android")] -pub fn open(_url: &str) { +pub fn open(_url: &str) -> Result<(), Error> { // TODO: While it is generally always bad to leave a function implemented, there is not much // more we can do here. This function will eventually be removed when we compile Parity // as a library and not as a full binary. + Ok(()) } diff --git a/test.sh b/test.sh index 84940a6ac..6dcd258ea 100755 --- a/test.sh +++ b/test.sh @@ -40,8 +40,18 @@ echo "________Validate chainspecs________" fi -# Running test's +# Running the C example +echo "________Running the C example________" +cd parity-clib-example && \ + mkdir -p build && \ + cd build && \ + cmake .. && \ + make && \ + ./parity-example && \ + cd .. && \ + rm -rf build && \ + cd .. + +# Running tests echo "________Running Parity Full Test Suite________" - cargo test -j 8 $OPTIONS --features "$FEATURES" --all $1 -