diff --git a/ethcore/src/snapshot/tests/proof_of_authority.rs b/ethcore/src/snapshot/tests/proof_of_authority.rs index d82b6f3ae..5958a5f64 100644 --- a/ethcore/src/snapshot/tests/proof_of_authority.rs +++ b/ethcore/src/snapshot/tests/proof_of_authority.rs @@ -40,7 +40,7 @@ const TRANSITION_BLOCK_1: usize = 2; // block at which the contract becomes acti const TRANSITION_BLOCK_2: usize = 6; // block at which the second contract activates. macro_rules! secret { - ($e: expr) => { Secret::from_slice(&$e.sha3()).expect(format!("sha3({}) not valid secret.", $e).as_str()) } + ($e: expr) => { Secret::from_slice(&$e.sha3()) } } lazy_static! { diff --git a/parity/cli/config.toml b/parity/cli/config.toml index b6695f3f5..c3617077e 100644 --- a/parity/cli/config.toml +++ b/parity/cli/config.toml @@ -81,3 +81,5 @@ jit = false logging = "own_tx=trace" log_file = "/var/log/parity.log" color = true +ports_shift = 0 +unsafe_expose = false diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index f36622357..efd618ffb 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -98,6 +98,13 @@ usage! { flag_no_persistent_txqueue: bool = false, or |c: &Config| otry!(c.parity).no_persistent_txqueue, + // -- Convenience Options + flag_config: String = "$BASE/config.toml", or |_| None, + flag_ports_shift: u16 = 0u16, + or |c: &Config| otry!(c.misc).ports_shift, + flag_unsafe_expose: bool = false, + or |c: &Config| otry!(c.misc).unsafe_expose, + // -- Account Options flag_unlock: Option = None, or |c: &Config| otry!(c.account).unlock.as_ref().map(|vec| Some(vec.join(","))), @@ -189,7 +196,7 @@ usage! { // IPC flag_no_ipc: bool = false, or |c: &Config| otry!(c.ipc).disable.clone(), - flag_ipc_path: String = "$BASE/jsonrpc.ipc", + flag_ipc_path: String = if cfg!(windows) { r"\\.\pipe\jsonrpc.ipc" } else { "$BASE/jsonrpc.ipc" }, or |c: &Config| otry!(c.ipc).path.clone(), flag_ipc_apis: String = "web3,eth,net,parity,parity_accounts,traces,rpc,secretstore", or |c: &Config| otry!(c.ipc).apis.as_ref().map(|vec| vec.join(",")), @@ -339,7 +346,6 @@ usage! { or |c: &Config| otry!(c.vm).jit.clone(), // -- Miscellaneous Options - flag_config: String = "$BASE/config.toml", or |_| None, flag_logging: Option = None, or |c: &Config| otry!(c.misc).logging.clone().map(Some), flag_log_file: Option = None, @@ -575,6 +581,8 @@ struct Misc { logging: Option, log_file: Option, color: Option, + ports_shift: Option, + unsafe_expose: Option, } #[cfg(test)] @@ -686,6 +694,11 @@ mod tests { flag_light: false, flag_no_persistent_txqueue: false, + // -- Convenience Options + flag_config: "$BASE/config.toml".into(), + flag_ports_shift: 0, + flag_unsafe_expose: false, + // -- Account Options flag_unlock: Some("0xdeadbeefcafe0000000000000000000000000000".into()), flag_password: vec!["~/.safe/password.file".into()], @@ -862,7 +875,6 @@ mod tests { // -- Miscellaneous Options flag_version: false, - flag_config: "$BASE/config.toml".into(), flag_logging: Some("own_tx=trace".into()), flag_log_file: Some("/var/log/parity.log".into()), flag_no_color: false, @@ -1037,6 +1049,8 @@ mod tests { logging: Some("own_tx=trace".into()), log_file: Some("/var/log/parity.log".into()), color: Some(true), + ports_shift: Some(0), + unsafe_expose: Some(false), }), stratum: None, }); diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index 2eee70c7c..bd5ed1f84 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -76,6 +76,18 @@ Operating Options: potentially higher in bandwidth. Has no effect with subcommands (default: {flag_light}). +Convenience Options: + -c --config CONFIG Specify a filename containing a configuration file. + (default: {flag_config}) + --ports-shift SHIFT Add SHIFT to all port numbers Parity is listening on. + Includes network port and all servers (RPC, WebSockets, UI, IPFS, SecretStore). + (default: {flag_ports_shift}) + --unsafe-expose All servers will listen on external interfaces and will + be remotely accessible. It's equivalent with setting + the following: --{{ws,jsonrpc,ui,ipfs,secret_store,stratum}}-interface=all --*-hosts=all + This option is UNSAFE and should be used with great care! + (default: {flag_unsafe_expose}) + Account Options: --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution. ACCOUNTS is a comma-delimited list of addresses. @@ -441,8 +453,6 @@ Internal Options: --can-restart Executable will auto-restart if exiting with 69. Miscellaneous Options: - -c --config CONFIG Specify a filename containing a configuration file. - (default: {flag_config}) -l --logging LOGGING Specify the logging level. Must conform to the same format as RUST_LOG. (default: {flag_logging:?}) --log-file FILENAME Specify a filename into which logging should be diff --git a/parity/configuration.rs b/parity/configuration.rs index bfed67aa1..7985c9cd3 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -373,7 +373,7 @@ impl Configuration { public_node: public_node, geth_compatibility: geth_compatibility, ui_address: ui_address, - net_settings: self.network_settings(), + net_settings: self.network_settings()?, dapps_conf: dapps_conf, ipfs_conf: ipfs_conf, signer_conf: signer_conf, @@ -513,7 +513,7 @@ impl Configuration { Ok(Some(StratumOptions { io_path: self.directories().db, listen_addr: self.stratum_interface(), - port: self.args.flag_stratum_port, + port: self.args.flag_ports_shift + self.args.flag_stratum_port, secret: self.args.flag_stratum_secret.as_ref().map(|s| s.parse::().unwrap_or_else(|_| s.sha3())), })) } else { Ok(None) } @@ -556,10 +556,10 @@ impl Configuration { fn signer_config(&self) -> SignerConfiguration { SignerConfiguration { enabled: self.ui_enabled(), - port: self.args.flag_ui_port, + port: self.args.flag_ports_shift + self.args.flag_ui_port, interface: self.ui_interface(), signer_path: self.directories().signer, - skip_origin_validation: self.args.flag_ui_no_validation, + skip_origin_validation: self.args.flag_unsafe_expose || self.args.flag_ui_no_validation, } } @@ -581,9 +581,9 @@ impl Configuration { self_secret: self.secretstore_self_secret()?, nodes: self.secretstore_nodes()?, interface: self.secretstore_interface(), - port: self.args.flag_secretstore_port, + port: self.args.flag_ports_shift + self.args.flag_secretstore_port, http_interface: self.secretstore_http_interface(), - http_port: self.args.flag_secretstore_http_port, + http_port: self.args.flag_ports_shift + self.args.flag_secretstore_http_port, data_path: self.directories().secretstore, }) } @@ -591,7 +591,7 @@ impl Configuration { fn ipfs_config(&self) -> IpfsConfiguration { IpfsConfiguration { enabled: self.args.flag_ipfs_api, - port: self.args.flag_ipfs_api_port, + port: self.args.flag_ports_shift + self.args.flag_ipfs_api_port, interface: self.ipfs_interface(), cors: self.ipfs_cors(), hosts: self.ipfs_hosts(), @@ -674,9 +674,9 @@ impl Configuration { } } - fn net_addresses(&self) -> Result<(Option, Option), String> { - let port = self.args.flag_port; - let listen_address = Some(SocketAddr::new("0.0.0.0".parse().unwrap(), port)); + fn net_addresses(&self) -> Result<(SocketAddr, Option), String> { + let port = self.args.flag_ports_shift + self.args.flag_port; + let listen_address = SocketAddr::new("0.0.0.0".parse().unwrap(), port); let public_address = if self.args.flag_nat.starts_with("extip:") { let host = &self.args.flag_nat[6..]; let host = host.parse().map_err(|_| format!("Invalid host given with `--nat extip:{}`", host))?; @@ -692,7 +692,7 @@ impl Configuration { ret.nat_enabled = self.args.flag_nat == "any" || self.args.flag_nat == "upnp"; ret.boot_nodes = to_bootnodes(&self.args.flag_bootnodes)?; let (listen, public) = self.net_addresses()?; - ret.listen_address = listen.map(|l| format!("{}", l)); + ret.listen_address = Some(format!("{}", listen)); ret.public_address = public.map(|p| format!("{}", p)); ret.use_secret = match self.args.flag_node_key.as_ref() .map(|s| s.parse::().or_else(|_| Secret::from_unsafe_slice(&s.sha3())).map_err(|e| format!("Invalid key: {:?}", e)) @@ -746,7 +746,19 @@ impl Configuration { Self::cors(self.args.flag_ipfs_api_cors.as_ref()) } - fn hosts(hosts: &str) -> Option> { + fn hosts(&self, hosts: &str, interface: &str) -> Option> { + if self.args.flag_unsafe_expose { + return None; + } + + if interface == "0.0.0.0" && hosts == "none" { + return None; + } + + Self::parse_hosts(hosts) + } + + fn parse_hosts(hosts: &str) -> Option> { match hosts { "none" => return Some(Vec::new()), "*" | "all" | "any" => return None, @@ -757,19 +769,19 @@ impl Configuration { } fn rpc_hosts(&self) -> Option> { - Self::hosts(&self.args.flag_jsonrpc_hosts) + self.hosts(&self.args.flag_jsonrpc_hosts, &self.rpc_interface()) } fn ws_hosts(&self) -> Option> { - Self::hosts(&self.args.flag_ws_hosts) + self.hosts(&self.args.flag_ws_hosts, &self.ws_interface()) } fn ws_origins(&self) -> Option> { - Self::hosts(&self.args.flag_ws_origins) + Self::parse_hosts(&self.args.flag_ws_origins) } fn ipfs_hosts(&self) -> Option> { - Self::hosts(&self.args.flag_ipfs_api_hosts) + self.hosts(&self.args.flag_ipfs_api_hosts, &self.ipfs_interface()) } fn ipc_config(&self) -> Result { @@ -795,7 +807,7 @@ impl Configuration { let conf = HttpConfiguration { enabled: self.rpc_enabled(), interface: self.rpc_interface(), - port: self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port), + port: self.args.flag_ports_shift + self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port), apis: match self.args.flag_public_node { false => self.rpc_apis().parse()?, true => self.rpc_apis().parse::()?.retain(ApiSet::PublicContext), @@ -816,7 +828,7 @@ impl Configuration { let conf = WsConfiguration { enabled: self.ws_enabled(), interface: self.ws_interface(), - port: self.args.flag_ws_port, + port: self.args.flag_ports_shift + self.args.flag_ws_port, apis: self.args.flag_ws_apis.parse()?, hosts: self.ws_hosts(), origins: self.ws_origins() @@ -825,15 +837,17 @@ impl Configuration { Ok(conf) } - fn network_settings(&self) -> NetworkSettings { - NetworkSettings { + fn network_settings(&self) -> Result { + let http_conf = self.http_config()?; + let net_addresses = self.net_addresses()?; + Ok(NetworkSettings { name: self.args.flag_identity.clone(), chain: self.chain(), - network_port: self.args.flag_port, - rpc_enabled: self.rpc_enabled(), - rpc_interface: self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone()), - rpc_port: self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port), - } + network_port: net_addresses.0.port(), + rpc_enabled: http_conf.enabled, + rpc_interface: http_conf.interface, + rpc_port: http_conf.port, + }) } fn update_policy(&self) -> Result { @@ -906,7 +920,11 @@ impl Configuration { if self.args.flag_geth { geth_ipc_path(self.args.flag_testnet) } else { - parity_ipc_path(&self.directories().base, &self.args.flag_ipcpath.clone().unwrap_or(self.args.flag_ipc_path.clone())) + parity_ipc_path( + &self.directories().base, + &self.args.flag_ipcpath.clone().unwrap_or(self.args.flag_ipc_path.clone()), + self.args.flag_ports_shift, + ) } } @@ -919,13 +937,14 @@ impl Configuration { } fn ui_interface(&self) -> String { - match self.args.flag_ui_interface.as_str() { - "local" => "127.0.0.1", - x => x, - }.into() + self.interface(&self.args.flag_ui_interface) } - fn interface(interface: &str) -> String { + fn interface(&self, interface: &str) -> String { + if self.args.flag_unsafe_expose { + return "0.0.0.0".into(); + } + match interface { "all" => "0.0.0.0", "local" => "127.0.0.1", @@ -934,23 +953,24 @@ impl Configuration { } fn rpc_interface(&self) -> String { - Self::interface(&self.network_settings().rpc_interface) + let rpc_interface = self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone()); + self.interface(&rpc_interface) } fn ws_interface(&self) -> String { - Self::interface(&self.args.flag_ws_interface) + self.interface(&self.args.flag_ws_interface) } fn ipfs_interface(&self) -> String { - Self::interface(&self.args.flag_ipfs_api_interface) + self.interface(&self.args.flag_ipfs_api_interface) } fn secretstore_interface(&self) -> String { - Self::interface(&self.args.flag_secretstore_interface) + self.interface(&self.args.flag_secretstore_interface) } fn secretstore_http_interface(&self) -> String { - Self::interface(&self.args.flag_secretstore_http_interface) + self.interface(&self.args.flag_secretstore_http_interface) } fn secretstore_self_secret(&self) -> Result, String> { @@ -986,7 +1006,7 @@ impl Configuration { } fn stratum_interface(&self) -> String { - Self::interface(&self.args.flag_stratum_interface) + self.interface(&self.args.flag_stratum_interface) } fn rpc_enabled(&self) -> bool { @@ -1322,23 +1342,23 @@ mod tests { let conf = parse(&["parity", "--testnet", "--identity", "testname"]); // then - assert_eq!(conf.network_settings(), NetworkSettings { + assert_eq!(conf.network_settings(), Ok(NetworkSettings { name: "testname".to_owned(), chain: "testnet".to_owned(), network_port: 30303, rpc_enabled: true, - rpc_interface: "local".to_owned(), + rpc_interface: "127.0.0.1".to_owned(), rpc_port: 8545, - }); + })); } #[test] fn should_parse_rpc_settings_with_geth_compatiblity() { // given fn assert(conf: Configuration) { - let net = conf.network_settings(); + let net = conf.network_settings().unwrap(); assert_eq!(net.rpc_enabled, true); - assert_eq!(net.rpc_interface, "all".to_owned()); + assert_eq!(net.rpc_interface, "0.0.0.0".to_owned()); assert_eq!(net.rpc_port, 8000); assert_eq!(conf.rpc_cors(), Some(vec!["*".to_owned()])); assert_eq!(conf.rpc_apis(), "web3,eth".to_owned()); @@ -1516,4 +1536,57 @@ mod tests { _ => panic!("Should be Cmd::Run"), } } + + #[test] + fn should_apply_ports_shift() { + // given + + // when + let conf0 = parse(&["parity", "--ports-shift", "1", "--stratum"]); + let conf1 = parse(&["parity", "--ports-shift", "1", "--jsonrpc-port", "8544"]); + + // then + assert_eq!(conf0.net_addresses().unwrap().0.port(), 30304); + assert_eq!(conf0.network_settings().unwrap().network_port, 30304); + assert_eq!(conf0.network_settings().unwrap().rpc_port, 8546); + assert_eq!(conf0.http_config().unwrap().port, 8546); + assert_eq!(conf0.ws_config().unwrap().port, 8547); + assert_eq!(conf0.signer_config().port, 8181); + assert_eq!(conf0.secretstore_config().unwrap().port, 8084); + assert_eq!(conf0.secretstore_config().unwrap().http_port, 8083); + assert_eq!(conf0.ipfs_config().port, 5002); + assert_eq!(conf0.stratum_options().unwrap().unwrap().port, 8009); + + + assert_eq!(conf1.net_addresses().unwrap().0.port(), 30304); + assert_eq!(conf1.network_settings().unwrap().network_port, 30304); + assert_eq!(conf1.network_settings().unwrap().rpc_port, 8545); + assert_eq!(conf1.http_config().unwrap().port, 8545); + assert_eq!(conf1.ws_config().unwrap().port, 8547); + assert_eq!(conf1.signer_config().port, 8181); + assert_eq!(conf1.secretstore_config().unwrap().port, 8084); + assert_eq!(conf1.secretstore_config().unwrap().http_port, 8083); + assert_eq!(conf1.ipfs_config().port, 5002); + } + + #[test] + fn should_expose_all_servers() { + // given + + // when + let conf0 = parse(&["parity", "--unsafe-expose"]); + + // then + assert_eq!(&conf0.network_settings().unwrap().rpc_interface, "0.0.0.0"); + assert_eq!(&conf0.http_config().unwrap().interface, "0.0.0.0"); + assert_eq!(conf0.http_config().unwrap().hosts, None); + assert_eq!(&conf0.ws_config().unwrap().interface, "0.0.0.0"); + assert_eq!(conf0.ws_config().unwrap().hosts, None); + assert_eq!(&conf0.signer_config().interface, "0.0.0.0"); + assert_eq!(conf0.signer_config().skip_origin_validation, true); + assert_eq!(&conf0.secretstore_config().unwrap().interface, "0.0.0.0"); + assert_eq!(&conf0.secretstore_config().unwrap().http_interface, "0.0.0.0"); + assert_eq!(&conf0.ipfs_config().interface, "0.0.0.0"); + assert_eq!(conf0.ipfs_config().hosts, None); + } } diff --git a/parity/helpers.rs b/parity/helpers.rs index e98c31ab7..449d8f569 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -166,13 +166,12 @@ pub fn geth_ipc_path(testnet: bool) -> String { } /// Formats and returns parity ipc path. -pub fn parity_ipc_path(base: &str, s: &str) -> String { - // Windows path should not be hardcoded here. - if cfg!(windows) { - return r"\\.\pipe\parity.jsonrpc".to_owned(); +pub fn parity_ipc_path(base: &str, path: &str, shift: u16) -> String { + let mut path = path.to_owned(); + if shift != 0 { + path = path.replace("jsonrpc.ipc", &format!("jsonrpc-{}.ipc", shift)); } - - replace_home(base, s) + replace_home(base, &path) } /// Validates and formats bootnodes option. diff --git a/parity/rpc.rs b/parity/rpc.rs index 2ac93baf7..eb8f5c279 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -18,7 +18,6 @@ use std::io; use std::sync::Arc; use dapps; -use dir::default_data_path; use parity_rpc::informant::{RpcStats, Middleware}; use parity_rpc::{self as rpc, HttpServerError, Metadata, Origin, DomainsValidation}; use helpers::parity_ipc_path; @@ -63,10 +62,14 @@ pub struct IpcConfiguration { impl Default for IpcConfiguration { fn default() -> Self { - let data_dir = default_data_path(); IpcConfiguration { enabled: true, - socket_addr: parity_ipc_path(&data_dir, "$BASE/jsonrpc.ipc"), + socket_addr: if cfg!(windows) { + r"\\.\pipe\jsonrpc.ipc".into() + } else { + let data_dir = ::dir::default_data_path(); + parity_ipc_path(&data_dir, "$BASE/jsonrpc.ipc", 0) + }, apis: ApiSet::IpcContext, } } diff --git a/rpc/src/v1/helpers/network_settings.rs b/rpc/src/v1/helpers/network_settings.rs index cda32c658..a79828624 100644 --- a/rpc/src/v1/helpers/network_settings.rs +++ b/rpc/src/v1/helpers/network_settings.rs @@ -39,7 +39,7 @@ impl Default for NetworkSettings { chain: "foundation".into(), network_port: 30303, rpc_enabled: true, - rpc_interface: "local".into(), + rpc_interface: "127.0.0.1".into(), rpc_port: 8545 } }