From 62de1c3891595b933e8b31ef5214dc8bca90b5c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sat, 10 Sep 2016 11:37:14 +0200 Subject: [PATCH 1/4] Config files WiP --- Cargo.lock | 10 + Cargo.toml | 1 + parity/blockchain.rs | 2 +- parity/cli/config.full.toml | 98 +++++ parity/cli/config.toml | 63 +++ parity/cli/mod.rs | 687 +++++++++++++++++++++++++++++++ parity/cli/usage.rs | 110 +++++ parity/{cli.rs => cli/usage.txt} | 314 ++++---------- parity/cli/version.txt | 9 + parity/configuration.rs | 36 +- parity/main.rs | 8 +- 11 files changed, 1082 insertions(+), 256 deletions(-) create mode 100644 parity/cli/config.full.toml create mode 100644 parity/cli/config.toml create mode 100644 parity/cli/mod.rs create mode 100644 parity/cli/usage.rs rename parity/{cli.rs => cli/usage.txt} (59%) create mode 100644 parity/cli/version.txt diff --git a/Cargo.lock b/Cargo.lock index 2ebf9b854..18a77833d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,7 @@ dependencies = [ "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1673,6 +1674,14 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "toml" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "traitobject" version = "0.0.1" @@ -1980,6 +1989,7 @@ dependencies = [ "checksum time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7ec6d62a20df54e07ab3b78b9a3932972f4b7981de295563686849eb3989af" "checksum tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f7aef43048292ca0bae4ab32180e85f6202cf2816c2a210c396a84b99dab9270" "checksum toml 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "fcd27a04ca509aff336ba5eb2abc58d456f52c4ff64d9724d88acb85ead560b6" +"checksum toml 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a442dfc13508e603c3f763274361db7f79d7469a0e95c411cde53662ab30fc72" "checksum traitobject 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "07eaeb7689bb7fca7ce15628319635758eda769fed481ecfe6686ddef2600616" "checksum transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15f7cc7116182edca1ed08f6f8c4da92104555ca77addbabea4eaa59b20373d0" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" diff --git a/Cargo.toml b/Cargo.toml index d8e42bf3a..6349c354d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ ansi_term = "0.7" lazy_static = "0.2" regex = "0.1" isatty = "0.1" +toml = "0.2" ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" } fdlimit = { path = "util/fdlimit" } ethcore = { path = "ethcore" } diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 0dd11b976..10e475fc4 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -221,7 +221,7 @@ fn execute_export(cmd: ExportBlockchain) -> Result { // Setup panic handler let panic_handler = PanicHandler::new_in_arc(); - let format = cmd.format.unwrap_or_else(Default::default); + let format = cmd.format.unwrap_or_default(); // load spec file let spec = try!(cmd.spec.spec()); diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml new file mode 100644 index 000000000..4d3fb3776 --- /dev/null +++ b/parity/cli/config.full.toml @@ -0,0 +1,98 @@ +[parity] +mode = "active" +mode_timeout = 300 +mode_alarm = 3600 +chain = "homestead" +db_path = "$HOME/.parity" +keys_path = "$HOME/.parity/keys" +identity = "" + +[account] +unlock = ["0xdeadbeefcafe0000000000000000000000000000"] +password = ["~/.safe/password.file"] +keys_iterations = 10240 + +[signer] +force = false +disable = false +port = 8180 +interface = "127.0.0.1" +path = "$HOME/.parity/signer" + +[network] +disable = false +port = 30303 +min_peers = 25 +max_peers = 50 +nat = "any" +id = "0x1" +bootnodes = [] +discovery = true + +reserved_only = false +reserved_peers = "./path_to_file" + +[rpc] +disable = false +port = 8545 +interface = "local" +cors = "null" +apis = ["web3", "eth", "net", "personal", "ethcore", "traces", "rpc"] +hosts = ["none"] + +[ipc] +disable = false +path = "$HOME/.parity/jsonrpc.ipc" +apis = ["web3", "eth", "net", "personal", "ethcore", "traces", "rpc"] + +[dapps] +disable = false +port = 8080 +interface = "local" +hosts = ["none"] +path = "$HOME/.parity/dapps" +# authorization: +user = "test_user" +pass = "test_pass" + +[mining] +author = "0xdeadbeefcafe0000000000000000000000000001" +force_sealing = true +reseal_on_txs = "all" +reseal_min_period = 4000 +work_queue_size = 20 +relay_set = "cheap" +usd_per_tx = "0" +usd_per_eth = "auto" +price_update_period = "hourly" +gas_floor_target = "4700000" +gas_cap = "6283184" +tx_queue_size = 1024 +tx_gas_limit = "6283184" +extra_data = "Parity" +remove_solved = false +notify_work = ["http://localhost:3001"] + +[footprint] +tracing = "auto" +pruning = "auto" +cache_size_db = 64 +cache_size_blocks = 8 +cache_size_queue = 50 +cache_size = 128 # Overrides above caches with total size +fast_and_loose = false +db_compaction = "ssd" +fat_db = false + +[snapshots] +disable_periodic = false + +[vm] +jit = false + +[misc] +logging = "own_tx=trace" +log_file = "/var/log/parity.log" +no_color = false + + diff --git a/parity/cli/config.toml b/parity/cli/config.toml new file mode 100644 index 000000000..5b8c7d149 --- /dev/null +++ b/parity/cli/config.toml @@ -0,0 +1,63 @@ +[parity] +mode = "dark" +mode_timeout = 15 +mode_alarm = 10 +chain = "./chain.json" + +[account] +unlock = ["0x1", "0x2", "0x3"] +password = ["passwdfile path"] + +[signer] +disable = true + +[network] +disable = false +discovery = true +nat = "any" +min_peers = 10 +max_peers = 20 + +reserved_only = true +reserved_peers = "./path/to/reserved_peers" + + +[rpc] +disable = true +port = 8180 + +[ipc] +apis = ["rpc", "eth"] + +[dapps] +port = 8080 +user = "username" +pass = "password" + +[mining] +author = "0xdeadbeefcafe0000000000000000000000000001" +force_sealing = true +reseal_on_txs = "all" +reseal_min_period = 4000 +price_update_period = "hourly" +tx_queue_size = 2048 + +[footprint] +tracing = "on" +pruning = "fast" +cache_size_db = 128 +cache_size_blocks = 16 +cache_size_queue = 100 +db_compaction = "ssd" +fat_db = true + +[snapshots] +disable_periodic = true + +[vm] +jit = false + +[misc] +logging = "own_tx=trace" +log_file = "/var/log/parity.log" +no_color = false diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs new file mode 100644 index 000000000..c58310944 --- /dev/null +++ b/parity/cli/mod.rs @@ -0,0 +1,687 @@ +// 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 . + +#[macro_use] +mod usage; + +usage! { + { + // Commands + cmd_daemon: bool, + cmd_wallet: bool, + cmd_account: bool, + cmd_new: bool, + cmd_list: bool, + cmd_export: bool, + cmd_import: bool, + cmd_signer: bool, + cmd_new_token: bool, + cmd_snapshot: bool, + cmd_restore: bool, + cmd_ui: bool, + + // Arguments + arg_pid_file: String, + arg_file: Option, + arg_path: Vec, + + // Flags + // -- Legacy Options + flag_geth: bool, + flag_testnet: bool, + flag_import_geth_keys: bool, + flag_datadir: Option, + flag_networkid: Option, + flag_peers: Option, + flag_nodekey: Option, + flag_nodiscover: bool, + flag_jsonrpc: bool, + flag_jsonrpc_off: bool, + flag_webapp: bool, + flag_dapps_off: bool, + flag_rpc: bool, + flag_rpcaddr: Option, + flag_rpcport: Option, + flag_rpcapi: Option, + flag_rpccorsdomain: Option, + flag_ipcdisable: bool, + flag_ipc_off: bool, + flag_ipcapi: Option, + flag_ipcpath: Option, + flag_gasprice: Option, + flag_etherbase: Option, + flag_extradata: Option, + flag_cache: Option, + + // -- Miscellaneous Options + flag_version: bool, + } + { + // -- Operating Options + flag_mode: String = "active", or |c: &Config| c.parity.mode.clone(), + flag_mode_timeout: u64 = 300u64, or |c: &Config| c.parity.mode_timeout.clone(), + flag_mode_alarm: u64 = 3600u64, or |c: &Config| c.parity.mode_alarm.clone(), + flag_chain: String = "homestead", or |c: &Config| c.parity.chain.clone(), + flag_db_path: String = "$HOME/.parity", or |c: &Config| c.parity.db_path.clone(), + flag_keys_path: String = "$HOME/.parity/keys", or |c: &Config| c.parity.keys_path.clone(), + flag_identity: String = "", or |c: &Config| c.parity.identity.clone(), + + // -- Account Options + flag_unlock: Option = None, + or |c: &Config| c.account.unlock.clone().map(|vec| Some(vec.join(","))), + flag_password: Vec = Vec::new(), + or |c: &Config| c.account.password.clone(), + flag_keys_iterations: u32 = 10240u32, + or |c: &Config| c.account.keys_iterations.clone(), + + flag_force_signer: bool = false, + or |c: &Config| c.signer.force.clone(), + flag_no_signer: bool = false, + or |c: &Config| c.signer.disable.clone(), + flag_signer_port: u16 = 8180u16, + or |c: &Config| c.signer.port.clone(), + flag_signer_interface: String = "local", + or |c: &Config| c.signer.interface.clone(), + flag_signer_path: String = "$HOME/.parity/signer", + or |c: &Config| c.signer.path.clone(), + // NOTE [todr] For security reasons don't put this to config files + flag_signer_no_validation: bool = false, or |_| None, + + // -- Networking Options + flag_no_network: bool = false, + or |c: &Config| c.network.disable.clone(), + flag_port: u16 = 30303u16, + or |c: &Config| c.network.port.clone(), + flag_min_peers: u16 = 25u16, + or |c: &Config| c.network.min_peers.clone(), + flag_max_peers: u16 = 50u16, + or |c: &Config| c.network.max_peers.clone(), + flag_nat: String = "any", + or |c: &Config| c.network.nat.clone(), + flag_network_id: Option = None, + or |c: &Config| c.network.id.clone().map(Some), + flag_bootnodes: Option = None, + or |c: &Config| c.network.bootnodes.clone().map(|vec| Some(vec.join(","))), + flag_no_discovery: bool = false, + or |c: &Config| c.network.discovery.clone(), + flag_node_key: Option = None, + or |c: &Config| c.network.node_key.clone().map(Some), + flag_reserved_peers: Option = None, + or |c: &Config| c.network.reserved_peers.clone().map(Some), + flag_reserved_only: bool = false, + or |c: &Config| c.network.reserved_only.clone(), + + // -- API and Console Options + // RPC + flag_no_jsonrpc: bool = false, + or |c: &Config| c.rpc.disable.clone(), + flag_jsonrpc_port: u16 = 8545u16, + or |c: &Config| c.rpc.port.clone(), + flag_jsonrpc_interface: String = "local", + or |c: &Config| c.rpc.interface.clone(), + flag_jsonrpc_cors: Option = None, + or |c: &Config| c.rpc.cors.clone().map(Some), + flag_jsonrpc_apis: String = "web3,eth,net,ethcore,personal,traces,rpc", + or |c: &Config| c.rpc.apis.clone().map(|vec| vec.join(",")), + flag_jsonrpc_hosts: String = "none", + or |c: &Config| c.rpc.hosts.clone().map(|vec| vec.join(",")), + + // IPC + flag_no_ipc: bool = false, + or |c: &Config| c.ipc.disable.clone(), + flag_ipc_path: String = "$HOME/.parity/jsonrpc.ipc", + or |c: &Config| c.ipc.path.clone(), + flag_ipc_apis: String = "web3,eth,net,ethcore,personal,traces,rpc", + or |c: &Config| c.ipc.apis.clone().map(|vec| vec.join(",")), + + // DAPPS + flag_no_dapps: bool = false, + or |c: &Config| c.dapps.disable.clone(), + flag_dapps_port: u16 = 8080u16, + or |c: &Config| c.dapps.port.clone(), + flag_dapps_interface: String = "local", + or |c: &Config| c.dapps.interface.clone(), + flag_dapps_hosts: String = "none", + or |c: &Config| c.dapps.hosts.clone().map(|vec| vec.join(",")), + flag_dapps_path: String = "$HOME/.parity/dapps", + or |c: &Config| c.dapps.path.clone(), + flag_dapps_user: Option = None, + or |c: &Config| c.dapps.user.clone().map(Some), + flag_dapps_pass: Option = None, + or |c: &Config| c.dapps.pass.clone().map(Some), + + // -- Sealing/Mining Options + flag_author: Option = None, + or |c: &Config| c.mining.author.clone().map(Some), + flag_force_sealing: bool = false, + or |c: &Config| c.mining.force_sealing.clone(), + flag_reseal_on_txs: String = "own", + or |c: &Config| c.mining.reseal_on_txs.clone(), + flag_reseal_min_period: u64 = 2000u64, + or |c: &Config| c.mining.reseal_min_period.clone(), + flag_work_queue_size: usize = 20usize, + or |c: &Config| c.mining.work_queue_size.clone(), + flag_tx_gas_limit: Option = None, + or |c: &Config| c.mining.tx_gas_limit.clone().map(Some), + flag_relay_set: String = "cheap", + or |c: &Config| c.mining.relay_set.clone(), + flag_usd_per_tx: String = "0", + or |c: &Config| c.mining.usd_per_tx.clone(), + flag_usd_per_eth: String = "auto", + or |c: &Config| c.mining.usd_per_eth.clone(), + flag_price_update_period: String = "hourly", + or |c: &Config| c.mining.price_update_period.clone(), + flag_gas_floor_target: String = "4700000", + or |c: &Config| c.mining.gas_floor_target.clone(), + flag_gas_cap: String = "6283184", + or |c: &Config| c.mining.gas_cap.clone(), + flag_extra_data: Option = None, + or |c: &Config| c.mining.extra_data.clone().map(Some), + flag_tx_queue_size: usize = 1024usize, + or |c: &Config| c.mining.tx_queue_size.clone(), + flag_remove_solved: bool = false, + or |c: &Config| c.mining.remove_solved.clone(), + flag_notify_work: Option = None, + or |c: &Config| c.mining.notify_work.clone().map(|vec| Some(vec.join(","))), + + // -- Footprint Options + flag_tracing: String = "auto", + or |c: &Config| c.footprint.tracing.clone(), + flag_pruning: String = "auto", + or |c: &Config| c.footprint.pruning.clone(), + flag_cache_size_db: u32 = 64u32, + or |c: &Config| c.footprint.cache_size_db.clone(), + flag_cache_size_blocks: u32 = 8u32, + or |c: &Config| c.footprint.cache_size_blocks.clone(), + flag_cache_size_queue: u32 = 50u32, + or |c: &Config| c.footprint.cache_size_queue.clone(), + flag_cache_size: Option = None, + or |c: &Config| c.footprint.cache_size.clone().map(Some), + flag_fast_and_loose: bool = false, + or |c: &Config| c.footprint.fast_and_loose.clone(), + flag_db_compaction: String = "ssd", + or |c: &Config| c.footprint.db_compaction.clone(), + flag_fat_db: bool = false, + or |c: &Config| c.footprint.fat_db.clone(), + + // -- Import/Export Options + flag_from: String = "1", or |_| None, + flag_to: String = "latest", or |_| None, + flag_format: Option = None, or |_| None, + + // -- Snapshot Optons + flag_at: String = "latest", or |_| None, + flag_no_periodic_snapshot: bool = false, + or |c: &Config| c.snapshots.disable_periodic.clone(), + + // -- Virtual Machine Options + flag_jitvm: bool = false, + or |c: &Config| c.vm.jit.clone(), + + // -- Miscellaneous Options + flag_config: String = "$HOME/.parity/config.toml", or |_| None, + flag_logging: Option = None, + or |c: &Config| c.misc.logging.clone().map(Some), + flag_log_file: Option = None, + or |c: &Config| c.misc.log_file.clone().map(Some), + flag_no_color: bool = false, + or |c: &Config| c.misc.no_color.clone(), + } +} + + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Config { + parity: Operating, + account: Account, + signer: Signer, + network: Network, + rpc: Rpc, + ipc: Ipc, + dapps: Dapps, + mining: Mining, + footprint: Footprint, + snapshots: Snapshots, + vm: VM, + misc: Misc, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Operating { + mode: Option, + mode_timeout: Option, + mode_alarm: Option, + chain: Option, + db_path: Option, + keys_path: Option, + identity: Option, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Account { + unlock: Option>, + password: Option>, + keys_iterations: Option, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Signer { + force: Option, + disable: Option, + port: Option, + interface: Option, + path: Option, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Network { + disable: Option, + port: Option, + min_peers: Option, + max_peers: Option, + nat: Option, + id: Option, + bootnodes: Option>, + discovery: Option, + node_key: Option, + reserved_peers: Option, + reserved_only: Option, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Rpc { + disable: Option, + port: Option, + interface: Option, + cors: Option, + apis: Option>, + hosts: Option>, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Ipc { + disable: Option, + path: Option, + apis: Option>, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Dapps { + disable: Option, + port: Option, + interface: Option, + hosts: Option>, + path: Option, + user: Option, + pass: Option, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Mining { + author: Option, + force_sealing: Option, + reseal_on_txs: Option, + reseal_min_period: Option, + work_queue_size: Option, + tx_gas_limit: Option, + relay_set: Option, + usd_per_tx: Option, + usd_per_eth: Option, + price_update_period: Option, + gas_floor_target: Option, + gas_cap: Option, + extra_data: Option, + tx_queue_size: Option, + remove_solved: Option, + notify_work: Option>, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Footprint { + tracing: Option, + pruning: Option, + fast_and_loose: Option, + cache_size: Option, + cache_size_db: Option, + cache_size_blocks: Option, + cache_size_queue: Option, + db_compaction: Option, + fat_db: Option, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Snapshots { + disable_periodic: Option, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct VM { + jit: Option, +} + +#[derive(Default, Debug, PartialEq, RustcDecodable)] +struct Misc { + logging: Option, + log_file: Option, + no_color: Option, +} + + + +#[cfg(test)] +mod tests { + use super::{ + Args, Config, Operating, Account, Signer, Network, Rpc, Ipc, Dapps, Mining, Footprint, Snapshots, VM, Misc + }; + use toml; + + #[test] + fn should_parse_args_and_include_config() { + // given + let mut config = Config::default(); + let mut operating = Operating::default(); + operating.chain = Some("morden".into()); + config.parity = operating; + + // when + let args = Args::parse_with_config(&["parity"], config).unwrap(); + + // then + assert_eq!(args.flag_chain, "morden".to_owned()); + } + + #[test] + fn should_not_use_config_if_cli_is_provided() { + // given + let mut config = Config::default(); + let mut operating = Operating::default(); + operating.chain = Some("morden".into()); + config.parity = operating; + + // when + let args = Args::parse_with_config(&["parity", "--chain", "xyz"], config).unwrap(); + + // then + assert_eq!(args.flag_chain, "xyz".to_owned()); + } + + #[test] + fn should_parse_full_config() { + // given + let config = toml::decode_str(include_str!("./config.full.toml")).unwrap(); + + // when + let args = Args::parse_with_config(&["parity", "--chain", "xyz"], config).unwrap(); + + // then + assert_eq!(args, Args { + // Commands + cmd_daemon: false, + cmd_wallet: false, + cmd_account: false, + cmd_new: false, + cmd_list: false, + cmd_export: false, + cmd_import: false, + cmd_signer: false, + cmd_new_token: false, + cmd_snapshot: false, + cmd_restore: false, + cmd_ui: false, + + // Arguments + arg_pid_file: "".into(), + arg_file: None, + arg_path: vec![], + + // -- Operating Options + flag_mode: "active".into(), + flag_mode_timeout: 300u64, + flag_mode_alarm: 3600u64, + flag_chain: "homestead".into(), + flag_db_path: "$HOME/.parity".into(), + flag_keys_path: "$HOME/.parity/keys".into(), + flag_identity: "".into(), + + // -- Account Options + flag_unlock: None, + flag_password: vec![], + flag_keys_iterations: 10240u32, + + flag_force_signer: false, + flag_no_signer: false, + flag_signer_port: 8180u16, + flag_signer_interface: "local".into(), + flag_signer_path: "$HOME/.parity/signer".into(), + flag_signer_no_validation: false, + + // -- Networking Options + flag_no_network: false, + flag_port: 30303u16, + flag_min_peers: 25u16, + flag_max_peers: 50u16, + flag_nat: "any".into(), + flag_network_id: None, + flag_bootnodes: None, + flag_no_discovery: false, + flag_node_key: None, + flag_reserved_peers: None, + flag_reserved_only: false, + + // -- API and Console Options + // RPC + flag_no_jsonrpc: false, + flag_jsonrpc_port: 8545u16, + flag_jsonrpc_interface: "local".into(), + flag_jsonrpc_cors: None, + flag_jsonrpc_apis: "web3,eth,net,ethcore,personal,traces,rpc".into(), + flag_jsonrpc_hosts: "none".into(), + + // IPC + flag_no_ipc: false, + flag_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), + flag_ipc_apis: "web3,eth,net,ethcore,personal,traces,rpc".into(), + + // DAPPS + flag_no_dapps: false, + flag_dapps_port: 8080u16, + flag_dapps_interface: "local".into(), + flag_dapps_hosts: "none".into(), + flag_dapps_path: "$HOME/.parity/dapps".into(), + flag_dapps_user: None, + flag_dapps_pass: None, + + // -- Sealing/Mining Options + flag_author: None, + flag_force_sealing: false, + flag_reseal_on_txs: "own".into(), + flag_reseal_min_period: 2000u64, + flag_work_queue_size: 20usize, + flag_tx_gas_limit: None, + flag_relay_set: "cheap".into(), + flag_usd_per_tx: "0".into(), + flag_usd_per_eth: "auto".into(), + flag_price_update_period: "hourly".into(), + flag_gas_floor_target: "4700000".into(), + flag_gas_cap: "6283184".into(), + flag_extra_data: None, + flag_tx_queue_size: 1024usize, + flag_remove_solved: false, + flag_notify_work: None, + + // -- Footprint Options + flag_tracing: "auto".into(), + flag_pruning: "auto".into(), + flag_cache_size_db: 64u32, + flag_cache_size_blocks: 8u32, + flag_cache_size_queue: 50u32, + flag_cache_size: None, + flag_fast_and_loose: false, + flag_db_compaction: "ssd".into(), + flag_fat_db: false, + + // -- Import/Export Options + flag_from: "1".into(), + flag_to: "latest".into(), + flag_format: None, + + // -- Snapshot Optons + flag_at: "latest".into(), + flag_no_periodic_snapshot: false, + + // -- Virtual Machine Options + flag_jitvm: false, + + // -- Legacy Options + flag_geth: false, + flag_testnet: false, + flag_import_geth_keys: false, + flag_datadir: None, + flag_networkid: None, + flag_peers: None, + flag_nodekey: None, + flag_nodiscover: false, + flag_jsonrpc: false, + flag_jsonrpc_off: false, + flag_webapp: false, + flag_dapps_off: false, + flag_rpc: false, + flag_rpcaddr: None, + flag_rpcport: None, + flag_rpcapi: None, + flag_rpccorsdomain: None, + flag_ipcdisable: false, + flag_ipc_off: false, + flag_ipcapi: None, + flag_ipcpath: None, + flag_gasprice: None, + flag_etherbase: None, + flag_extradata: None, + flag_cache: None, + + // -- Miscellaneous Options + flag_version: false, + flag_config: "$HOME/.parity/config.toml".into(), + flag_logging: None, + flag_log_file: None, + flag_no_color: false, + }); + } + + #[test] + fn should_deserialize_toml_file() { + let config: Config = toml::decode_str(include_str!("./config.toml")).unwrap(); + + assert_eq!(config, Config { + parity: Operating { + mode: Some("dark".into()), + mode_timeout: Some(15u64), + mode_alarm: Some(10u64), + chain: Some("./chain.json".into()), + db_path: None, + keys_path: None, + identity: None, + }, + account: Account { + unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]), + password: Some(vec!["passwdfile path".into()]), + keys_iterations: None, + }, + signer: Signer { + force: None, + disable: Some(true), + port: None, + interface: None, + path: None, + }, + network: Network { + disable: Some(false), + port: None, + min_peers: Some(10), + max_peers: Some(20), + nat: Some("any".into()), + id: None, + bootnodes: None, + discovery: Some(true), + node_key: None, + reserved_peers: Some("./path/to/reserved_peers".into()), + reserved_only: Some(true), + }, + rpc: Rpc { + disable: Some(true), + port: Some(8180), + interface: None, + cors: None, + apis: None, + hosts: None, + }, + ipc: Ipc { + disable: None, + path: None, + apis: Some(vec!["rpc".into(), "eth".into()]), + }, + dapps: Dapps { + disable: None, + port: Some(8080), + path: None, + interface: None, + hosts: None, + user: Some("username".into()), + pass: Some("password".into()) + }, + mining: Mining { + author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + force_sealing: Some(true), + reseal_on_txs: Some("all".into()), + reseal_min_period: Some(4000), + work_queue_size: None, + relay_set: None, + usd_per_tx: None, + usd_per_eth: None, + price_update_period: Some("hourly".into()), + gas_floor_target: None, + gas_cap: None, + tx_queue_size: Some(2048), + tx_gas_limit: None, + extra_data: None, + remove_solved: None, + notify_work: None, + }, + footprint: Footprint { + tracing: Some("on".into()), + pruning: Some("fast".into()), + fast_and_loose: None, + cache_size: None, + cache_size_db: Some(128), + cache_size_blocks: Some(16), + cache_size_queue: Some(100), + db_compaction: Some("ssd".into()), + fat_db: Some(true), + }, + snapshots: Snapshots { + disable_periodic: Some(true), + }, + vm: VM { + jit: Some(false), + }, + misc: Misc { + logging: Some("own_tx=trace".into()), + log_file: Some("/var/log/parity.log".into()), + no_color: Some(false), + } + }); + } +} diff --git a/parity/cli/usage.rs b/parity/cli/usage.rs new file mode 100644 index 000000000..7ca32aa2f --- /dev/null +++ b/parity/cli/usage.rs @@ -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 . + +macro_rules! usage { + ( + { + $( + $field_a:ident : $typ_a:ty, + )* + } + { + $( + $field:ident : $typ:ty = $default:expr, or $from_config:expr, + )* + } + ) => { + use util::version; + use docopt::{Docopt, Error as DocoptError}; + + #[derive(Debug, PartialEq)] + pub struct Args { + $( + pub $field: $typ, + )* + $( + pub $field_a: $typ_a, + )* + } + + impl Default for Args { + fn default() -> Self { + Args { + $( + $field: $default.into(), + )* + $( + $field_a: Default::default(), + )* + } + } + } + + #[derive(Default, Debug, PartialEq, RustcDecodable)] + struct RawArgs { + $( + $field_a: $typ_a, + )* + $( + $field: Option<$typ>, + )* + } + + impl Args { + + pub fn parse>(command: &[S]) -> Result { + Ok(try!(RawArgs::parse(command)).into_args(Default::default())) + } + + fn parse_with_config>(command: &[S], config: Config) -> Result { + Ok(try!(RawArgs::parse(command)).into_args(config)) + } + + pub fn print_version() -> String { + format!(include_str!("./version.txt"), version()) + } + } + + impl RawArgs { + fn into_args(self, config: Config) -> Args { + let mut args = Args::default(); + $( + args.$field_a = self.$field_a; + )* + $( + args.$field = self.$field.or_else(|| $from_config(&config)).unwrap_or_else(|| $default.into()); + )* + args + } + + pub fn parse>(command: &[S]) -> Result { + Docopt::new(Self::usage()).and_then(|d| d.argv(command).decode()) + } + + fn usage() -> String { + format!( + include_str!("./usage.txt"), + $( + $field={ let v: $typ = $default.into(); v }, + // Uncomment this to debug + // "named argument never used" error + // $field = $default, + )* + ) + } + } + }; +} diff --git a/parity/cli.rs b/parity/cli/usage.txt similarity index 59% rename from parity/cli.rs rename to parity/cli/usage.txt index bb46bda13..7d778d4f8 100644 --- a/parity/cli.rs +++ b/parity/cli/usage.txt @@ -1,23 +1,3 @@ -// 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 . - -use util::version; -use docopt::Docopt; - -pub const USAGE: &'static str = r#" Parity. Ethereum Client. By Wood/Paronyan/Kotewicz/Drwięga/Volf et al. Copyright 2015, 2016 Ethcore (UK) Limited @@ -41,134 +21,140 @@ Operating Options: passive - Parity syncs initially, then sleeps and wakes regularly to resync. dark - Parity syncs only when an external interface - is active. [default: active]. + is active. (default: {flag_mode}). --mode-timeout SECS Specify the number of seconds before inactivity timeout occurs when mode is dark or passive - [default: 300]. + (default: {flag_mode_timeout}). --mode-alarm SECS Specify the number of seconds before auto sleep reawake timeout occurs when mode is passive - [default: 3600]. + (default: {flag_mode_alarm}). --chain CHAIN Specify the blockchain type. CHAIN may be either a JSON chain specification file or olympic, frontier, homestead, mainnet, morden, classic or testnet - [default: homestead]. + (default: {flag_chain}). -d --db-path PATH Specify the database & configuration directory path - [default: $HOME/.parity]. + (default: {flag_db_path}). --keys-path PATH Specify the path for JSON key files to be found - [default: $HOME/.parity/keys]. - --identity NAME Specify your node's name. + (default: {flag_keys_path}). + --identity NAME Specify your node's name. (default: {flag_identity}) Account Options: --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution. ACCOUNTS is a comma-delimited list of addresses. - Implies --no-signer. + Implies --no-signer. (default: {flag_unlock:?}) --password FILE Provide a file containing a password for unlocking - an account. + an account. (default: {flag_password:?}) --keys-iterations NUM Specify the number of iterations to use when deriving key from the password (bigger is more - secure) [default: 10240]. + secure) (default: {flag_keys_iterations}). --force-signer Enable Trusted Signer WebSocket endpoint used by Signer UIs, even when --unlock is in use. + (default: ${flag_force_signer}) --no-signer Disable Trusted Signer WebSocket endpoint used by - Signer UIs. + Signer UIs. (default: ${flag_no_signer}) --signer-port PORT Specify the port of Trusted Signer server - [default: 8180]. + (default: {flag_signer_port}). --signer-interface IP Specify the hostname portion of the Trusted Signer server, IP should be an interface's IP address, - or local [default: local]. + or local (default: {flag_signer_interface}). --signer-path PATH Specify directory where Signer UIs tokens should - be stored. [default: $HOME/.parity/signer] + be stored. (default: {flag_signer_path}) --signer-no-validation Disable Origin and Host headers validation for Trusted Signer. WARNING: INSECURE. Used only for - development. + development. (default: {flag_signer_no_validation}) Networking Options: - --no-network Disable p2p networking. + --no-network Disable p2p networking. (default: {flag_no_network}) --port PORT Override the port on which the node should listen - [default: 30303]. - --min-peers NUM Try to maintain at least NUM peers [default: 25]. - --max-peers NUM Allow up to that many peers [default: 50]. + (default: {flag_port}). + --min-peers NUM Try to maintain at least NUM peers (default: {flag_min_peers}). + --max-peers NUM Allow up to that many peers (default: {flag_max_peers}). --nat METHOD Specify method to use for determining public address. Must be one of: any, none, upnp, - extip: [default: any]. + extip: (default: {flag_nat}). --network-id INDEX Override the network identifier from the chain we - are on. + are on. (default: {flag_network_id:?}) --bootnodes NODES Override the bootnodes from our chain. NODES should - be comma-delimited enodes. - --no-discovery Disable new peer discovery. + be comma-delimited enodes. (default: {flag_bootnodes:?}) + --no-discovery Disable new peer discovery. (default: {flag_no_discovery}) --node-key KEY Specify node secret key, either as 64-character hex - string or input to SHA3 operation. + string or input to SHA3 operation. (default: {flag_node_key:?}) --reserved-peers FILE Provide a file containing enodes, one per line. These nodes will always have a reserved slot on top - of the normal maximum peers. - --reserved-only Connect only to reserved nodes. + of the normal maximum peers. (default: {flag_reserved_peers:?}) + --reserved-only Connect only to reserved nodes. (default: {flag_reserved_only}) API and Console Options: - --no-jsonrpc Disable the JSON-RPC API server. + --no-jsonrpc Disable the JSON-RPC API server. (default: {flag_no_jsonrpc}) --jsonrpc-port PORT Specify the port portion of the JSONRPC API server - [default: 8545]. + (default: {flag_jsonrpc_port}). --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]. + all (all interfaces) or local (default: {flag_jsonrpc_interface}). --jsonrpc-cors URL Specify CORS header for JSON-RPC API responses. + (default: {flag_jsonrpc_cors:?}) --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, net, personal, ethcore, ethcore_set, traces, rpc. - [default: web3,eth,net,ethcore,personal,traces,rpc]. + (default: {flag_jsonrpc_apis}). --jsonrpc-hosts HOSTS List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: "all", "none", - [default: none]. + (default: {flag_jsonrpc_hosts}). - --no-ipc Disable JSON-RPC over IPC service. + --no-ipc Disable JSON-RPC over IPC service. (default: {flag_no_ipc}) --ipc-path PATH Specify custom path for JSON-RPC over IPC service - [default: $HOME/.parity/jsonrpc.ipc]. + (default: {flag_ipc_path}). --ipc-apis APIS Specify custom API set available via JSON-RPC over - IPC [default: web3,eth,net,ethcore,personal,traces,rpc]. + IPC (default: {flag_ipc_apis}). - --no-dapps Disable the Dapps server (e.g. status page). + --no-dapps Disable the Dapps server (e.g. status page). (default: {flag_no_dapps}) --dapps-port PORT Specify the port portion of the Dapps server - [default: 8080]. + (default: {flag_dapps_port}). --dapps-interface IP Specify the hostname portion of the Dapps server, IP should be an interface's IP address, - or local [default: local]. + or local (default: {flag_dapps_interface}). --dapps-hosts HOSTS List of allowed Host header values. This option will validate the Host header sent by the browser, it is additional security against some attack vectors. Special options: "all", "none", - [default: none]. + (default: {flag_dapps_hosts}). --dapps-user USERNAME Specify username for Dapps server. It will be used in HTTP Basic Authentication Scheme. If --dapps-pass is not specified you will be - asked for password on startup. + asked for password on startup. (default: {flag_dapps_user:?}) --dapps-pass PASSWORD Specify password for Dapps server. Use only in - conjunction with --dapps-user. + conjunction with --dapps-user. (default: {flag_dapps_pass:?}) --dapps-path PATH Specify directory where dapps should be installed. - [default: $HOME/.parity/dapps] + (default: {flag_dapps_path}) Sealing/Mining Options: --author ADDRESS Specify the block author (aka "coinbase") address for sending block rewards from sealed blocks. NOTE: MINING WILL NOT WORK WITHOUT THIS OPTION. + (default: {flag_author:?}) --force-sealing Force the node to author new blocks as if it were always sealing/mining. + (default: {flag_force_sealing}) --reseal-on-txs SET Specify which transactions should force the node to reseal a block. SET is one of: none - never reseal on new transactions; own - reseal only on a new local transaction; ext - reseal only on a new external transaction; - all - reseal on all new transactions [default: own]. + all - reseal on all new transactions + (default: {flag_reseal_on_txs}). --reseal-min-period MS Specify the minimum time between reseals from incoming transactions. MS is time measured in - milliseconds [default: 2000]. + milliseconds (default: {flag_reseal_min_period}). --work-queue-size ITEMS Specify the number of historical work packages which are kept cached lest a solution is found for them later. High values take more memory but result - in fewer unusable solutions [default: 20]. + in fewer unusable solutions (default: {flag_work_queue_size}). --tx-gas-limit GAS Apply a limit of GAS as the maximum amount of gas a single transaction may have for it to be mined. + (default: {flag_tx_gas_limit:?}) --relay-set SET Set of transactions to relay. SET may be: cheap - Relay any transaction in the queue (this may include invalid transactions); @@ -176,78 +162,81 @@ Sealing/Mining Options: guarantees we don't relay invalid transactions, but means we relay nothing if not mining); lenient - Same as strict when mining, and cheap - when not [default: cheap]. + when not (default: {flag_relay_set}). --usd-per-tx USD Amount of USD to be paid for a basic transaction - [default: 0]. The minimum gas price is set + (default: {flag_usd_per_tx}). 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, a web service or 'auto' to use each web service in turn and fallback on the last known - good value [default: auto]. + good value (default: {flag_usd_per_eth}). --price-update-period T T will be allowed to pass between each gas price update. T may be daily, hourly, a number of seconds, or a time string of the form "2 days", "30 minutes" - etc. [default: hourly]. + etc. (default: {flag_price_update_period}). --gas-floor-target GAS Amount of gas per block to target when sealing a new - block [default: 4700000]. + block (default: {flag_gas_floor_target}). --gas-cap GAS A cap on how large we will raise the gas limit per - block due to transaction volume [default: 6283184]. + block due to transaction volume (default: {flag_gas_cap}). --extra-data STRING Specify a custom extra-data for authored blocks, no - more than 32 characters. + more than 32 characters. (default: {flag_extra_data:?}) --tx-queue-size LIMIT Maximum amount of transactions in the queue (waiting - to be included in next block) [default: 1024]. + to be included in next block) (default: {flag_tx_queue_size}). --remove-solved Move solved blocks from the work package queue instead of cloning them. This gives a slightly faster import speed, but means that extra solutions submitted for the same work package will go unused. + (default: {flag_remove_solved}) --notify-work URLS URLs to which work package notifications are pushed. URLS should be a comma-delimited list of HTTP URLs. + (default: {flag_notify_work:?}) Footprint Options: --tracing BOOL Indicates if full transaction tracing should be enabled. Works only if client had been fully synced with tracing enabled. BOOL may be one of auto, on, off. auto uses last used value of this option (off - if it does not exist) [default: auto]. + if it does not exist) (default: {flag_tracing}). --pruning METHOD Configure pruning of the state/storage trie. METHOD may be one of auto, archive, fast: archive - keep all state trie data. No pruning. fast - maintain journal overlay. Fast but 50MB used. auto - use the method most recently synced or - default to fast if none synced [default: auto]. - --cache-size-db MB Override database cache size [default: 64]. + default to fast if none synced (default: {flag_pruning}). + --cache-size-db MB Override database cache size (default: {flag_cache_size_db}). --cache-size-blocks MB Specify the prefered size of the blockchain cache in - megabytes [default: 8]. + megabytes (default: {flag_cache_size_blocks}). --cache-size-queue MB Specify the maximum size of memory to use for block - queue [default: 50]. + queue (default: {flag_cache_size_queue}). --cache-size MB Set total amount of discretionary memory to use for the entire system, overrides other cache and queue - options. + options.a (default: {flag_cache_size:?}) --fast-and-loose Disables DB WAL, which gives a significant speed up - but means an unclean exit is unrecoverable. + but means an unclean exit is unrecoverable. (default: {flag_fast_and_loose}) --db-compaction TYPE Database compaction type. TYPE may be one of: ssd - suitable for SSDs and fast HDDs; - hdd - suitable for slow HDDs [default: ssd]. - --fat-db Fat database. + hdd - suitable for slow HDDs (default: {flag_db_compaction}). + --fat-db Fat database. (default: {flag_fat_db}) Import/Export Options: --from BLOCK Export from block BLOCK, which may be an index or - hash [default: 1]. + hash (default: {flag_from}). --to BLOCK Export to (including) block BLOCK, which may be an - index, hash or 'latest' [default: latest]. + index, hash or 'latest' (default: {flag_to}). --format FORMAT For import/export in given format. FORMAT must be one of 'hex' and 'binary'. + (default: {flag_format:?} = Import: auto, Export: binary) Snapshot Options: --at BLOCK Take a snapshot at the given block, which may be an index, hash, or 'latest'. Note that taking snapshots at non-recent blocks will only work with --pruning archive - [default: latest] + (default: {flag_at}) --no-periodic-snapshot Disable automated snapshots which usually occur once - every 10000 blocks. + every 10000 blocks. (default: {flag_no_periodic_snapshot}) Virtual Machine Options: - --jitvm Enable the JIT VM. + --jitvm Enable the JIT VM. (default: {flag_jitvm}) Legacy Options: --geth Run in Geth-compatibility mode. Sets the IPC path @@ -284,156 +273,13 @@ Legacy Options: --cache MB Equivalent to --cache-size MB. 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. + format as RUST_LOG. (default: {flag_logging:?}) --log-file FILENAME Specify a filename into which logging should be - directed. - --no-color Don't use terminal color codes in output. + directed. (default: {flag_log_file:?}) + --no-color Don't use terminal color codes in output. (default: {flag_no_color}) -v --version Show information about version. -h --help Show this screen. -"#; - -#[derive(Debug, PartialEq, RustcDecodable)] -pub struct Args { - pub cmd_daemon: bool, - pub cmd_account: bool, - pub cmd_wallet: bool, - pub cmd_new: bool, - pub cmd_list: bool, - pub cmd_export: bool, - pub cmd_import: bool, - pub cmd_signer: bool, - pub cmd_new_token: bool, - pub cmd_snapshot: bool, - pub cmd_restore: bool, - pub cmd_ui: bool, - pub arg_pid_file: String, - pub arg_file: Option, - pub arg_path: Vec, - pub flag_mode: String, - pub flag_mode_timeout: u64, - pub flag_mode_alarm: u64, - pub flag_chain: String, - pub flag_db_path: String, - pub flag_identity: String, - pub flag_unlock: Option, - pub flag_password: Vec, - pub flag_keys_path: String, - pub flag_keys_iterations: u32, - pub flag_import_geth_keys: bool, - pub flag_bootnodes: Option, - pub flag_network_id: Option, - pub flag_pruning: String, - pub flag_tracing: String, - pub flag_port: u16, - pub flag_min_peers: u16, - pub flag_max_peers: u16, - pub flag_no_discovery: bool, - pub flag_nat: String, - pub flag_node_key: Option, - pub flag_reserved_peers: Option, - pub flag_reserved_only: bool, - - pub flag_cache_size_db: u32, - pub flag_cache_size_blocks: u32, - pub flag_cache_size_queue: u32, - pub flag_cache_size: Option, - pub flag_cache: Option, - pub flag_fast_and_loose: bool, - - pub flag_no_jsonrpc: bool, - pub flag_jsonrpc_interface: String, - pub flag_jsonrpc_port: u16, - pub flag_jsonrpc_cors: Option, - pub flag_jsonrpc_hosts: String, - pub flag_jsonrpc_apis: String, - pub flag_no_ipc: bool, - pub flag_ipc_path: String, - pub flag_ipc_apis: String, - pub flag_no_dapps: bool, - pub flag_dapps_port: u16, - pub flag_dapps_interface: String, - pub flag_dapps_hosts: String, - pub flag_dapps_user: Option, - pub flag_dapps_pass: Option, - pub flag_dapps_path: String, - pub flag_force_signer: bool, - pub flag_no_signer: bool, - pub flag_signer_port: u16, - pub flag_signer_interface: String, - pub flag_signer_path: String, - pub flag_signer_no_validation: bool, - pub flag_force_sealing: bool, - pub flag_reseal_on_txs: String, - pub flag_reseal_min_period: u64, - pub flag_work_queue_size: usize, - pub flag_remove_solved: bool, - pub flag_tx_gas_limit: Option, - pub flag_relay_set: String, - pub flag_author: Option, - pub flag_usd_per_tx: String, - pub flag_usd_per_eth: String, - pub flag_price_update_period: String, - pub flag_gas_floor_target: String, - pub flag_gas_cap: String, - pub flag_extra_data: Option, - pub flag_tx_queue_size: usize, - pub flag_notify_work: Option, - pub flag_logging: Option, - pub flag_version: bool, - pub flag_from: String, - pub flag_to: String, - pub flag_at: String, - pub flag_no_periodic_snapshot: bool, - pub flag_format: Option, - pub flag_jitvm: bool, - pub flag_log_file: Option, - pub flag_no_color: bool, - pub flag_no_network: bool, - // legacy... - pub flag_geth: bool, - pub flag_nodekey: Option, - pub flag_nodiscover: bool, - pub flag_peers: Option, - pub flag_datadir: Option, - pub flag_extradata: Option, - pub flag_etherbase: Option, - pub flag_gasprice: Option, - pub flag_jsonrpc: bool, - pub flag_webapp: bool, - pub flag_rpc: bool, - pub flag_rpcaddr: Option, - pub flag_rpcport: Option, - pub flag_rpccorsdomain: Option, - pub flag_rpcapi: Option, - pub flag_testnet: bool, - pub flag_networkid: Option, - pub flag_ipcdisable: bool, - pub flag_ipc_off: bool, - pub flag_jsonrpc_off: bool, - pub flag_dapps_off: bool, - pub flag_ipcpath: Option, - pub flag_ipcapi: Option, - pub flag_db_compaction: String, - pub flag_fat_db: bool, -} - -impl Default for Args { - fn default() -> Self { - Docopt::new(USAGE).unwrap().argv(&[] as &[&str]).decode().unwrap() - } -} - -pub fn print_version() -> String { - format!("\ -Parity - version {} -Copyright 2015, 2016 Ethcore (UK) Limited -License GPLv3+: GNU GPL version 3 or later . -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()) -} diff --git a/parity/cli/version.txt b/parity/cli/version.txt new file mode 100644 index 000000000..acb7cd9e6 --- /dev/null +++ b/parity/cli/version.txt @@ -0,0 +1,9 @@ +Parity + version {} +Copyright 2015, 2016 Ethcore (UK) Limited +License GPLv3+: GNU GPL version 3 or later . +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. + diff --git a/parity/configuration.rs b/parity/configuration.rs index 51d637580..c5c6d7de7 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -19,8 +19,8 @@ use std::io::Read; use std::net::SocketAddr; use std::path::PathBuf; use std::cmp::max; -use cli::{USAGE, Args}; -use docopt::{Docopt, Error as DocoptError}; +use cli::Args; +use docopt::Error as DocoptError; use util::{Hashable, U256, Uint, Bytes, version_data, Secret, Address}; use util::log::Colour; use ethsync::{NetworkConfiguration, is_valid_node_url}; @@ -60,8 +60,8 @@ pub struct Configuration { } impl Configuration { - pub fn parse(command: I) -> Result where I: IntoIterator, S: AsRef { - let args = try!(Docopt::new(USAGE).and_then(|d| d.argv(command).decode())); + pub fn parse>(command: &[S]) -> Result { + let args = try!(Args::parse(command)); let config = Configuration { args: args, @@ -613,6 +613,7 @@ impl Configuration { } fn signer_enabled(&self) -> bool { + println!("Force: {:?} {:?}", self.args.flag_force_signer, self.args.flag_unlock); if self.args.flag_force_signer { return true; } @@ -628,8 +629,7 @@ impl Configuration { #[cfg(test)] mod tests { use super::*; - use cli::USAGE; - use docopt::Docopt; + use cli::Args; use ethcore_rpc::NetworkSettings; use ethcore::client::{VMType, BlockID}; use helpers::{replace_home, default_network_config}; @@ -647,21 +647,21 @@ mod tests { fn parse(args: &[&str]) -> Configuration { Configuration { - args: Docopt::new(USAGE).unwrap().argv(args).decode().unwrap(), + args: Args::parse(args).unwrap(), } } #[test] fn test_command_version() { let args = vec!["parity", "--version"]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); assert_eq!(conf.into_command().unwrap(), Cmd::Version); } #[test] fn test_command_account_new() { let args = vec!["parity", "account", "new"]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); assert_eq!(conf.into_command().unwrap(), Cmd::Account(AccountCmd::New(NewAccount { iterations: 10240, path: replace_home("$HOME/.parity/keys"), @@ -672,7 +672,7 @@ mod tests { #[test] fn test_command_account_list() { let args = vec!["parity", "account", "list"]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); assert_eq!(conf.into_command().unwrap(), Cmd::Account( AccountCmd::List(replace_home("$HOME/.parity/keys"))) ); @@ -681,7 +681,7 @@ mod tests { #[test] fn test_command_account_import() { let args = vec!["parity", "account", "import", "my_dir", "another_dir"]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); assert_eq!(conf.into_command().unwrap(), Cmd::Account(AccountCmd::Import(ImportAccounts { from: vec!["my_dir".into(), "another_dir".into()], to: replace_home("$HOME/.parity/keys"), @@ -691,7 +691,7 @@ mod tests { #[test] fn test_command_wallet_import() { let args = vec!["parity", "wallet", "import", "my_wallet.json", "--password", "pwd"]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); assert_eq!(conf.into_command().unwrap(), Cmd::ImportPresaleWallet(ImportWallet { iterations: 10240, path: replace_home("$HOME/.parity/keys"), @@ -703,7 +703,7 @@ mod tests { #[test] fn test_command_blockchain_import() { let args = vec!["parity", "import", "blockchain.json"]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); assert_eq!(conf.into_command().unwrap(), Cmd::Blockchain(BlockchainCmd::Import(ImportBlockchain { spec: Default::default(), logger_config: Default::default(), @@ -723,7 +723,7 @@ mod tests { #[test] fn test_command_blockchain_export() { let args = vec!["parity", "export", "blockchain.json"]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); assert_eq!(conf.into_command().unwrap(), Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { spec: Default::default(), logger_config: Default::default(), @@ -744,7 +744,7 @@ mod tests { #[test] fn test_command_blockchain_export_with_custom_format() { let args = vec!["parity", "export", "--format", "hex", "blockchain.json"]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); assert_eq!(conf.into_command().unwrap(), Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { spec: Default::default(), logger_config: Default::default(), @@ -765,7 +765,7 @@ mod tests { #[test] fn test_command_signer_new_token() { let args = vec!["parity", "signer", "new-token"]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); let expected = replace_home("$HOME/.parity/signer"); assert_eq!(conf.into_command().unwrap(), Cmd::SignerToken(expected)); } @@ -773,7 +773,7 @@ mod tests { #[test] fn test_run_cmd() { let args = vec!["parity"]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); assert_eq!(conf.into_command().unwrap(), Cmd::Run(RunCmd { cache_config: Default::default(), dirs: Default::default(), @@ -962,7 +962,7 @@ mod tests { let filename = temp.as_str().to_owned() + "/peers"; File::create(filename.clone()).unwrap().write_all(b" \n\t\n").unwrap(); let args = vec!["parity", "--reserved-peers", &filename]; - let conf = Configuration::parse(args).unwrap(); + let conf = Configuration::parse(&args).unwrap(); assert!(conf.init_reserved_nodes().is_ok()); } } diff --git a/parity/main.rs b/parity/main.rs index 9c2ae7942..8cb348958 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -51,6 +51,7 @@ extern crate ansi_term; extern crate regex; extern crate isatty; +extern crate toml; #[macro_use] extern crate ethcore_util as util; @@ -110,7 +111,7 @@ mod boot; mod stratum; use std::{process, env}; -use cli::print_version; +use cli::Args; use configuration::{Cmd, Configuration}; use deprecated::find_deprecated; @@ -120,7 +121,7 @@ fn execute(command: Cmd) -> Result { try!(run::execute(run_cmd)); Ok("".into()) }, - Cmd::Version => Ok(print_version()), + Cmd::Version => Ok(Args::print_version()), Cmd::Account(account_cmd) => account::execute(account_cmd), Cmd::ImportPresaleWallet(presale_cmd) => presale::execute(presale_cmd), Cmd::Blockchain(blockchain_cmd) => blockchain::execute(blockchain_cmd), @@ -130,7 +131,8 @@ fn execute(command: Cmd) -> Result { } fn start() -> Result { - let conf = Configuration::parse(env::args()).unwrap_or_else(|e| e.exit()); + let args: Vec = env::args().collect(); + let conf = Configuration::parse(&args).unwrap_or_else(|e| e.exit()); let deprecated = find_deprecated(&conf.args); for d in deprecated { From b7bf10d62d47e29aebc22f631496f24e3a36b2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sun, 11 Sep 2016 11:52:12 +0200 Subject: [PATCH 2/4] Adding nice error messages for invalid configs. --- parity/cli/config.full.toml | 2 +- parity/cli/config.invalid1.toml | 2 + parity/cli/config.invalid2.toml | 4 + parity/cli/config.toml | 2 +- parity/cli/mod.rs | 286 +++++++++++++++++--------------- parity/cli/usage.rs | 121 ++++++++++++-- parity/configuration.rs | 8 +- 7 files changed, 271 insertions(+), 154 deletions(-) create mode 100644 parity/cli/config.invalid1.toml create mode 100644 parity/cli/config.invalid2.toml diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index 4d3fb3776..ec5dfbe35 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -93,6 +93,6 @@ jit = false [misc] logging = "own_tx=trace" log_file = "/var/log/parity.log" -no_color = false +color = true diff --git a/parity/cli/config.invalid1.toml b/parity/cli/config.invalid1.toml new file mode 100644 index 000000000..b340da102 --- /dev/null +++ b/parity/cli/config.invalid1.toml @@ -0,0 +1,2 @@ +[account +unlock = "0x1" diff --git a/parity/cli/config.invalid2.toml b/parity/cli/config.invalid2.toml new file mode 100644 index 000000000..766dcfb59 --- /dev/null +++ b/parity/cli/config.invalid2.toml @@ -0,0 +1,4 @@ +[account] +unlock = "0x1" +passwd = [] + diff --git a/parity/cli/config.toml b/parity/cli/config.toml index 5b8c7d149..11ec333aa 100644 --- a/parity/cli/config.toml +++ b/parity/cli/config.toml @@ -60,4 +60,4 @@ jit = false [misc] logging = "own_tx=trace" log_file = "/var/log/parity.log" -no_color = false +color = true diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index c58310944..b9f8a7d42 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -71,151 +71,151 @@ usage! { } { // -- Operating Options - flag_mode: String = "active", or |c: &Config| c.parity.mode.clone(), - flag_mode_timeout: u64 = 300u64, or |c: &Config| c.parity.mode_timeout.clone(), - flag_mode_alarm: u64 = 3600u64, or |c: &Config| c.parity.mode_alarm.clone(), - flag_chain: String = "homestead", or |c: &Config| c.parity.chain.clone(), - flag_db_path: String = "$HOME/.parity", or |c: &Config| c.parity.db_path.clone(), - flag_keys_path: String = "$HOME/.parity/keys", or |c: &Config| c.parity.keys_path.clone(), - flag_identity: String = "", or |c: &Config| c.parity.identity.clone(), + flag_mode: String = "active", or |c: &Config| otry!(c.parity).mode.clone(), + flag_mode_timeout: u64 = 300u64, or |c: &Config| otry!(c.parity).mode_timeout.clone(), + flag_mode_alarm: u64 = 3600u64, or |c: &Config| otry!(c.parity).mode_alarm.clone(), + flag_chain: String = "homestead", or |c: &Config| otry!(c.parity).chain.clone(), + flag_db_path: String = "$HOME/.parity", or |c: &Config| otry!(c.parity).db_path.clone(), + flag_keys_path: String = "$HOME/.parity/keys", or |c: &Config| otry!(c.parity).keys_path.clone(), + flag_identity: String = "", or |c: &Config| otry!(c.parity).identity.clone(), // -- Account Options flag_unlock: Option = None, - or |c: &Config| c.account.unlock.clone().map(|vec| Some(vec.join(","))), + or |c: &Config| otry!(c.account).unlock.clone().map(|vec| Some(vec.join(","))), flag_password: Vec = Vec::new(), - or |c: &Config| c.account.password.clone(), + or |c: &Config| otry!(c.account).password.clone(), flag_keys_iterations: u32 = 10240u32, - or |c: &Config| c.account.keys_iterations.clone(), + or |c: &Config| otry!(c.account).keys_iterations.clone(), flag_force_signer: bool = false, - or |c: &Config| c.signer.force.clone(), + or |c: &Config| otry!(c.signer).force.clone(), flag_no_signer: bool = false, - or |c: &Config| c.signer.disable.clone(), + or |c: &Config| otry!(c.signer).disable.clone(), flag_signer_port: u16 = 8180u16, - or |c: &Config| c.signer.port.clone(), + or |c: &Config| otry!(c.signer).port.clone(), flag_signer_interface: String = "local", - or |c: &Config| c.signer.interface.clone(), + or |c: &Config| otry!(c.signer).interface.clone(), flag_signer_path: String = "$HOME/.parity/signer", - or |c: &Config| c.signer.path.clone(), + or |c: &Config| otry!(c.signer).path.clone(), // NOTE [todr] For security reasons don't put this to config files flag_signer_no_validation: bool = false, or |_| None, // -- Networking Options flag_no_network: bool = false, - or |c: &Config| c.network.disable.clone(), + or |c: &Config| otry!(c.network).disable.clone(), flag_port: u16 = 30303u16, - or |c: &Config| c.network.port.clone(), + or |c: &Config| otry!(c.network).port.clone(), flag_min_peers: u16 = 25u16, - or |c: &Config| c.network.min_peers.clone(), + or |c: &Config| otry!(c.network).min_peers.clone(), flag_max_peers: u16 = 50u16, - or |c: &Config| c.network.max_peers.clone(), + or |c: &Config| otry!(c.network).max_peers.clone(), flag_nat: String = "any", - or |c: &Config| c.network.nat.clone(), + or |c: &Config| otry!(c.network).nat.clone(), flag_network_id: Option = None, - or |c: &Config| c.network.id.clone().map(Some), + or |c: &Config| otry!(c.network).id.clone().map(Some), flag_bootnodes: Option = None, - or |c: &Config| c.network.bootnodes.clone().map(|vec| Some(vec.join(","))), + or |c: &Config| otry!(c.network).bootnodes.clone().map(|vec| Some(vec.join(","))), flag_no_discovery: bool = false, - or |c: &Config| c.network.discovery.clone(), + or |c: &Config| otry!(c.network).discovery.map(|d| !d).clone(), flag_node_key: Option = None, - or |c: &Config| c.network.node_key.clone().map(Some), + or |c: &Config| otry!(c.network).node_key.clone().map(Some), flag_reserved_peers: Option = None, - or |c: &Config| c.network.reserved_peers.clone().map(Some), + or |c: &Config| otry!(c.network).reserved_peers.clone().map(Some), flag_reserved_only: bool = false, - or |c: &Config| c.network.reserved_only.clone(), + or |c: &Config| otry!(c.network).reserved_only.clone(), // -- API and Console Options // RPC flag_no_jsonrpc: bool = false, - or |c: &Config| c.rpc.disable.clone(), + or |c: &Config| otry!(c.rpc).disable.clone(), flag_jsonrpc_port: u16 = 8545u16, - or |c: &Config| c.rpc.port.clone(), + or |c: &Config| otry!(c.rpc).port.clone(), flag_jsonrpc_interface: String = "local", - or |c: &Config| c.rpc.interface.clone(), + or |c: &Config| otry!(c.rpc).interface.clone(), flag_jsonrpc_cors: Option = None, - or |c: &Config| c.rpc.cors.clone().map(Some), + or |c: &Config| otry!(c.rpc).cors.clone().map(Some), flag_jsonrpc_apis: String = "web3,eth,net,ethcore,personal,traces,rpc", - or |c: &Config| c.rpc.apis.clone().map(|vec| vec.join(",")), + or |c: &Config| otry!(c.rpc).apis.clone().map(|vec| vec.join(",")), flag_jsonrpc_hosts: String = "none", - or |c: &Config| c.rpc.hosts.clone().map(|vec| vec.join(",")), + or |c: &Config| otry!(c.rpc).hosts.clone().map(|vec| vec.join(",")), // IPC flag_no_ipc: bool = false, - or |c: &Config| c.ipc.disable.clone(), + or |c: &Config| otry!(c.ipc).disable.clone(), flag_ipc_path: String = "$HOME/.parity/jsonrpc.ipc", - or |c: &Config| c.ipc.path.clone(), + or |c: &Config| otry!(c.ipc).path.clone(), flag_ipc_apis: String = "web3,eth,net,ethcore,personal,traces,rpc", - or |c: &Config| c.ipc.apis.clone().map(|vec| vec.join(",")), + or |c: &Config| otry!(c.ipc).apis.clone().map(|vec| vec.join(",")), // DAPPS flag_no_dapps: bool = false, - or |c: &Config| c.dapps.disable.clone(), + or |c: &Config| otry!(c.dapps).disable.clone(), flag_dapps_port: u16 = 8080u16, - or |c: &Config| c.dapps.port.clone(), + or |c: &Config| otry!(c.dapps).port.clone(), flag_dapps_interface: String = "local", - or |c: &Config| c.dapps.interface.clone(), + or |c: &Config| otry!(c.dapps).interface.clone(), flag_dapps_hosts: String = "none", - or |c: &Config| c.dapps.hosts.clone().map(|vec| vec.join(",")), + or |c: &Config| otry!(c.dapps).hosts.clone().map(|vec| vec.join(",")), flag_dapps_path: String = "$HOME/.parity/dapps", - or |c: &Config| c.dapps.path.clone(), + or |c: &Config| otry!(c.dapps).path.clone(), flag_dapps_user: Option = None, - or |c: &Config| c.dapps.user.clone().map(Some), + or |c: &Config| otry!(c.dapps).user.clone().map(Some), flag_dapps_pass: Option = None, - or |c: &Config| c.dapps.pass.clone().map(Some), + or |c: &Config| otry!(c.dapps).pass.clone().map(Some), // -- Sealing/Mining Options flag_author: Option = None, - or |c: &Config| c.mining.author.clone().map(Some), + or |c: &Config| otry!(c.mining).author.clone().map(Some), flag_force_sealing: bool = false, - or |c: &Config| c.mining.force_sealing.clone(), + or |c: &Config| otry!(c.mining).force_sealing.clone(), flag_reseal_on_txs: String = "own", - or |c: &Config| c.mining.reseal_on_txs.clone(), + or |c: &Config| otry!(c.mining).reseal_on_txs.clone(), flag_reseal_min_period: u64 = 2000u64, - or |c: &Config| c.mining.reseal_min_period.clone(), + or |c: &Config| otry!(c.mining).reseal_min_period.clone(), flag_work_queue_size: usize = 20usize, - or |c: &Config| c.mining.work_queue_size.clone(), + or |c: &Config| otry!(c.mining).work_queue_size.clone(), flag_tx_gas_limit: Option = None, - or |c: &Config| c.mining.tx_gas_limit.clone().map(Some), + or |c: &Config| otry!(c.mining).tx_gas_limit.clone().map(Some), flag_relay_set: String = "cheap", - or |c: &Config| c.mining.relay_set.clone(), + or |c: &Config| otry!(c.mining).relay_set.clone(), flag_usd_per_tx: String = "0", - or |c: &Config| c.mining.usd_per_tx.clone(), + or |c: &Config| otry!(c.mining).usd_per_tx.clone(), flag_usd_per_eth: String = "auto", - or |c: &Config| c.mining.usd_per_eth.clone(), + or |c: &Config| otry!(c.mining).usd_per_eth.clone(), flag_price_update_period: String = "hourly", - or |c: &Config| c.mining.price_update_period.clone(), + or |c: &Config| otry!(c.mining).price_update_period.clone(), flag_gas_floor_target: String = "4700000", - or |c: &Config| c.mining.gas_floor_target.clone(), + or |c: &Config| otry!(c.mining).gas_floor_target.clone(), flag_gas_cap: String = "6283184", - or |c: &Config| c.mining.gas_cap.clone(), + or |c: &Config| otry!(c.mining).gas_cap.clone(), flag_extra_data: Option = None, - or |c: &Config| c.mining.extra_data.clone().map(Some), + or |c: &Config| otry!(c.mining).extra_data.clone().map(Some), flag_tx_queue_size: usize = 1024usize, - or |c: &Config| c.mining.tx_queue_size.clone(), + or |c: &Config| otry!(c.mining).tx_queue_size.clone(), flag_remove_solved: bool = false, - or |c: &Config| c.mining.remove_solved.clone(), + or |c: &Config| otry!(c.mining).remove_solved.clone(), flag_notify_work: Option = None, - or |c: &Config| c.mining.notify_work.clone().map(|vec| Some(vec.join(","))), + or |c: &Config| otry!(c.mining).notify_work.clone().map(|vec| Some(vec.join(","))), // -- Footprint Options flag_tracing: String = "auto", - or |c: &Config| c.footprint.tracing.clone(), + or |c: &Config| otry!(c.footprint).tracing.clone(), flag_pruning: String = "auto", - or |c: &Config| c.footprint.pruning.clone(), + or |c: &Config| otry!(c.footprint).pruning.clone(), flag_cache_size_db: u32 = 64u32, - or |c: &Config| c.footprint.cache_size_db.clone(), + or |c: &Config| otry!(c.footprint).cache_size_db.clone(), flag_cache_size_blocks: u32 = 8u32, - or |c: &Config| c.footprint.cache_size_blocks.clone(), + or |c: &Config| otry!(c.footprint).cache_size_blocks.clone(), flag_cache_size_queue: u32 = 50u32, - or |c: &Config| c.footprint.cache_size_queue.clone(), + or |c: &Config| otry!(c.footprint).cache_size_queue.clone(), flag_cache_size: Option = None, - or |c: &Config| c.footprint.cache_size.clone().map(Some), + or |c: &Config| otry!(c.footprint).cache_size.clone().map(Some), flag_fast_and_loose: bool = false, - or |c: &Config| c.footprint.fast_and_loose.clone(), + or |c: &Config| otry!(c.footprint).fast_and_loose.clone(), flag_db_compaction: String = "ssd", - or |c: &Config| c.footprint.db_compaction.clone(), + or |c: &Config| otry!(c.footprint).db_compaction.clone(), flag_fat_db: bool = false, - or |c: &Config| c.footprint.fat_db.clone(), + or |c: &Config| otry!(c.footprint).fat_db.clone(), // -- Import/Export Options flag_from: String = "1", or |_| None, @@ -225,38 +225,38 @@ usage! { // -- Snapshot Optons flag_at: String = "latest", or |_| None, flag_no_periodic_snapshot: bool = false, - or |c: &Config| c.snapshots.disable_periodic.clone(), + or |c: &Config| otry!(c.snapshots).disable_periodic.clone(), // -- Virtual Machine Options flag_jitvm: bool = false, - or |c: &Config| c.vm.jit.clone(), + or |c: &Config| otry!(c.vm).jit.clone(), // -- Miscellaneous Options flag_config: String = "$HOME/.parity/config.toml", or |_| None, flag_logging: Option = None, - or |c: &Config| c.misc.logging.clone().map(Some), + or |c: &Config| otry!(c.misc).logging.clone().map(Some), flag_log_file: Option = None, - or |c: &Config| c.misc.log_file.clone().map(Some), + or |c: &Config| otry!(c.misc).log_file.clone().map(Some), flag_no_color: bool = false, - or |c: &Config| c.misc.no_color.clone(), + or |c: &Config| otry!(c.misc).color.map(|c| !c).clone(), } } #[derive(Default, Debug, PartialEq, RustcDecodable)] struct Config { - parity: Operating, - account: Account, - signer: Signer, - network: Network, - rpc: Rpc, - ipc: Ipc, - dapps: Dapps, - mining: Mining, - footprint: Footprint, - snapshots: Snapshots, - vm: VM, - misc: Misc, + parity: Option, + account: Option, + signer: Option, + network: Option, + rpc: Option, + ipc: Option, + dapps: Option, + mining: Option, + footprint: Option, + snapshots: Option, + vm: Option, + misc: Option, } #[derive(Default, Debug, PartialEq, RustcDecodable)] @@ -376,15 +376,14 @@ struct VM { struct Misc { logging: Option, log_file: Option, - no_color: Option, + color: Option, } - - #[cfg(test)] mod tests { use super::{ - Args, Config, Operating, Account, Signer, Network, Rpc, Ipc, Dapps, Mining, Footprint, Snapshots, VM, Misc + Args, ArgsError, + Config, Operating, Account, Signer, Network, Rpc, Ipc, Dapps, Mining, Footprint, Snapshots, VM, Misc }; use toml; @@ -394,7 +393,7 @@ mod tests { let mut config = Config::default(); let mut operating = Operating::default(); operating.chain = Some("morden".into()); - config.parity = operating; + config.parity = Some(operating); // when let args = Args::parse_with_config(&["parity"], config).unwrap(); @@ -409,7 +408,7 @@ mod tests { let mut config = Config::default(); let mut operating = Operating::default(); operating.chain = Some("morden".into()); - config.parity = operating; + config.parity = Some(operating); // when let args = Args::parse_with_config(&["parity", "--chain", "xyz"], config).unwrap(); @@ -451,20 +450,20 @@ mod tests { flag_mode: "active".into(), flag_mode_timeout: 300u64, flag_mode_alarm: 3600u64, - flag_chain: "homestead".into(), + flag_chain: "xyz".into(), flag_db_path: "$HOME/.parity".into(), flag_keys_path: "$HOME/.parity/keys".into(), flag_identity: "".into(), // -- Account Options - flag_unlock: None, - flag_password: vec![], + flag_unlock: Some("0xdeadbeefcafe0000000000000000000000000000".into()), + flag_password: vec!["~/.safe/password.file".into()], flag_keys_iterations: 10240u32, flag_force_signer: false, flag_no_signer: false, flag_signer_port: 8180u16, - flag_signer_interface: "local".into(), + flag_signer_interface: "127.0.0.1".into(), flag_signer_path: "$HOME/.parity/signer".into(), flag_signer_no_validation: false, @@ -474,11 +473,11 @@ mod tests { flag_min_peers: 25u16, flag_max_peers: 50u16, flag_nat: "any".into(), - flag_network_id: None, - flag_bootnodes: None, + flag_network_id: Some("0x1".into()), + flag_bootnodes: Some("".into()), flag_no_discovery: false, flag_node_key: None, - flag_reserved_peers: None, + flag_reserved_peers: Some("./path_to_file".into()), flag_reserved_only: false, // -- API and Console Options @@ -486,14 +485,14 @@ mod tests { flag_no_jsonrpc: false, flag_jsonrpc_port: 8545u16, flag_jsonrpc_interface: "local".into(), - flag_jsonrpc_cors: None, - flag_jsonrpc_apis: "web3,eth,net,ethcore,personal,traces,rpc".into(), + flag_jsonrpc_cors: Some("null".into()), + flag_jsonrpc_apis: "web3,eth,net,personal,ethcore,traces,rpc".into(), flag_jsonrpc_hosts: "none".into(), // IPC flag_no_ipc: false, flag_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(), - flag_ipc_apis: "web3,eth,net,ethcore,personal,traces,rpc".into(), + flag_ipc_apis: "web3,eth,net,personal,ethcore,traces,rpc".into(), // DAPPS flag_no_dapps: false, @@ -501,26 +500,26 @@ mod tests { flag_dapps_interface: "local".into(), flag_dapps_hosts: "none".into(), flag_dapps_path: "$HOME/.parity/dapps".into(), - flag_dapps_user: None, - flag_dapps_pass: None, + flag_dapps_user: Some("test_user".into()), + flag_dapps_pass: Some("test_pass".into()), // -- Sealing/Mining Options - flag_author: None, - flag_force_sealing: false, - flag_reseal_on_txs: "own".into(), - flag_reseal_min_period: 2000u64, + flag_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), + flag_force_sealing: true, + flag_reseal_on_txs: "all".into(), + flag_reseal_min_period: 4000u64, flag_work_queue_size: 20usize, - flag_tx_gas_limit: None, + flag_tx_gas_limit: Some("6283184".into()), flag_relay_set: "cheap".into(), flag_usd_per_tx: "0".into(), flag_usd_per_eth: "auto".into(), flag_price_update_period: "hourly".into(), flag_gas_floor_target: "4700000".into(), flag_gas_cap: "6283184".into(), - flag_extra_data: None, + flag_extra_data: Some("Parity".into()), flag_tx_queue_size: 1024usize, flag_remove_solved: false, - flag_notify_work: None, + flag_notify_work: Some("http://localhost:3001".into()), // -- Footprint Options flag_tracing: "auto".into(), @@ -528,7 +527,7 @@ mod tests { flag_cache_size_db: 64u32, flag_cache_size_blocks: 8u32, flag_cache_size_queue: 50u32, - flag_cache_size: None, + flag_cache_size: Some(128), flag_fast_and_loose: false, flag_db_compaction: "ssd".into(), flag_fat_db: false, @@ -575,18 +574,31 @@ mod tests { // -- Miscellaneous Options flag_version: false, flag_config: "$HOME/.parity/config.toml".into(), - flag_logging: None, - flag_log_file: None, + flag_logging: Some("own_tx=trace".into()), + flag_log_file: Some("/var/log/parity.log".into()), flag_no_color: false, }); } + #[test] + fn should_parse_config_and_return_errors() { + let config1 = Args::parse_config(include_str!("./config.invalid1.toml")); + let config2 = Args::parse_config(include_str!("./config.invalid2.toml")); + + match (config1, config2) { + (Err(ArgsError::Parsing(_)), Err(ArgsError::Decode(_))) => {}, + (a, b) => { + assert!(false, "Got invalid error types: {:?}, {:?}", a, b); + } + } + } + #[test] fn should_deserialize_toml_file() { let config: Config = toml::decode_str(include_str!("./config.toml")).unwrap(); assert_eq!(config, Config { - parity: Operating { + parity: Some(Operating { mode: Some("dark".into()), mode_timeout: Some(15u64), mode_alarm: Some(10u64), @@ -594,20 +606,20 @@ mod tests { db_path: None, keys_path: None, identity: None, - }, - account: Account { + }), + account: Some(Account { unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]), password: Some(vec!["passwdfile path".into()]), keys_iterations: None, - }, - signer: Signer { + }), + signer: Some(Signer { force: None, disable: Some(true), port: None, interface: None, path: None, - }, - network: Network { + }), + network: Some(Network { disable: Some(false), port: None, min_peers: Some(10), @@ -619,21 +631,21 @@ mod tests { node_key: None, reserved_peers: Some("./path/to/reserved_peers".into()), reserved_only: Some(true), - }, - rpc: Rpc { + }), + rpc: Some(Rpc { disable: Some(true), port: Some(8180), interface: None, cors: None, apis: None, hosts: None, - }, - ipc: Ipc { + }), + ipc: Some(Ipc { disable: None, path: None, apis: Some(vec!["rpc".into(), "eth".into()]), - }, - dapps: Dapps { + }), + dapps: Some(Dapps { disable: None, port: Some(8080), path: None, @@ -641,8 +653,8 @@ mod tests { hosts: None, user: Some("username".into()), pass: Some("password".into()) - }, - mining: Mining { + }), + mining: Some(Mining { author: Some("0xdeadbeefcafe0000000000000000000000000001".into()), force_sealing: Some(true), reseal_on_txs: Some("all".into()), @@ -659,8 +671,8 @@ mod tests { extra_data: None, remove_solved: None, notify_work: None, - }, - footprint: Footprint { + }), + footprint: Some(Footprint { tracing: Some("on".into()), pruning: Some("fast".into()), fast_and_loose: None, @@ -670,18 +682,18 @@ mod tests { cache_size_queue: Some(100), db_compaction: Some("ssd".into()), fat_db: Some(true), - }, - snapshots: Snapshots { + }), + snapshots: Some(Snapshots { disable_periodic: Some(true), - }, - vm: VM { + }), + vm: Some(VM { jit: Some(false), - }, - misc: Misc { + }), + misc: Some(Misc { logging: Some("own_tx=trace".into()), log_file: Some("/var/log/parity.log".into()), - no_color: Some(false), - } + color: Some(true), + }) }); } } diff --git a/parity/cli/usage.rs b/parity/cli/usage.rs index 7ca32aa2f..79f728b41 100644 --- a/parity/cli/usage.rs +++ b/parity/cli/usage.rs @@ -14,6 +14,16 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +macro_rules! otry { + ($e: expr) => ( + match $e { + Some(ref v) => v, + None => { + return None; + } + } + ) +} macro_rules! usage { ( { @@ -27,33 +37,81 @@ macro_rules! usage { )* } ) => { + use toml; + use std::{fs, io, process}; + use std::io::Read; use util::version; use docopt::{Docopt, Error as DocoptError}; + use helpers::replace_home; + use rustc_serialize; + + #[derive(Debug)] + pub enum ArgsError { + Docopt(DocoptError), + Parsing(Vec), + Decode(toml::DecodeError), + Config(String, io::Error), + } + + impl ArgsError { + pub fn exit(self) -> ! { + match self { + ArgsError::Docopt(e) => e.exit(), + ArgsError::Parsing(errors) => { + println!("There is an error in config file."); + for e in &errors { + println!("{}", e); + } + process::exit(2) + }, + ArgsError::Decode(e) => { + println!("You might have supplied invalid parameters in config file."); + println!("{}", e); + process::exit(2) + }, + ArgsError::Config(path, e) => { + println!("There was an error reading your config file at: {}", path); + println!("{}", e); + process::exit(2) + } + } + } + } + + impl From for ArgsError { + fn from(e: DocoptError) -> Self { ArgsError::Docopt(e) } + } + + impl From for ArgsError { + fn from(e: toml::DecodeError) -> Self { ArgsError::Decode(e) } + } #[derive(Debug, PartialEq)] pub struct Args { - $( - pub $field: $typ, - )* $( pub $field_a: $typ_a, )* + + $( + pub $field: $typ, + )* } impl Default for Args { fn default() -> Self { Args { - $( - $field: $default.into(), - )* $( $field_a: Default::default(), )* + + $( + $field: $default.into(), + )* } } } - #[derive(Default, Debug, PartialEq, RustcDecodable)] + #[derive(Default, Debug, PartialEq, Clone, RustcDecodable)] struct RawArgs { $( $field_a: $typ_a, @@ -65,14 +123,57 @@ macro_rules! usage { impl Args { - pub fn parse>(command: &[S]) -> Result { - Ok(try!(RawArgs::parse(command)).into_args(Default::default())) + pub fn parse>(command: &[S]) -> Result { + let raw_args = try!(RawArgs::parse(command)); + let config_file = raw_args.flag_config.clone().unwrap_or_else(|| raw_args.clone().into_args(Config::default()).flag_config); + let config_file = replace_home(&config_file); + let config = match (fs::File::open(&config_file), raw_args.flag_config.is_some()) { + // Load config file + (Ok(mut file), _) => { + println!("Loading config file from {}", &config_file); + let mut config = String::new(); + try!(file.read_to_string(&mut config).map_err(|e| ArgsError::Config(config_file, e))); + try!(Self::parse_config(&config)) + }, + // Don't display error in case default config cannot be loaded. + (Err(_), false) => Config::default(), + // Config set from CLI (fail with error) + (Err(e), true) => { + return Err(ArgsError::Config(config_file, e)); + }, + }; + + Ok(raw_args.into_args(config)) } - fn parse_with_config>(command: &[S], config: Config) -> Result { + #[cfg(test)] + pub fn parse_without_config>(command: &[S]) -> Result { + Self::parse_with_config(command, Config::default()) + } + + #[cfg(test)] + fn parse_with_config>(command: &[S], config: Config) -> Result { Ok(try!(RawArgs::parse(command)).into_args(config)) } + fn parse_config(config: &str) -> Result { + let mut value_parser = toml::Parser::new(&config); + match value_parser.parse() { + Some(value) => { + let result = rustc_serialize::Decodable::decode(&mut toml::Decoder::new(toml::Value::Table(value))); + match result { + Ok(config) => Ok(config), + Err(e) => { + return Err(e.into()); + } + } + }, + None => { + return Err(ArgsError::Parsing(value_parser.errors)); + }, + } + } + pub fn print_version() -> String { format!(include_str!("./version.txt"), version()) } diff --git a/parity/configuration.rs b/parity/configuration.rs index c5c6d7de7..ec8e18bc0 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -19,8 +19,7 @@ use std::io::Read; use std::net::SocketAddr; use std::path::PathBuf; use std::cmp::max; -use cli::Args; -use docopt::Error as DocoptError; +use cli::{Args, ArgsError}; use util::{Hashable, U256, Uint, Bytes, version_data, Secret, Address}; use util::log::Colour; use ethsync::{NetworkConfiguration, is_valid_node_url}; @@ -60,7 +59,7 @@ pub struct Configuration { } impl Configuration { - pub fn parse>(command: &[S]) -> Result { + pub fn parse>(command: &[S]) -> Result { let args = try!(Args::parse(command)); let config = Configuration { @@ -613,7 +612,6 @@ impl Configuration { } fn signer_enabled(&self) -> bool { - println!("Force: {:?} {:?}", self.args.flag_force_signer, self.args.flag_unlock); if self.args.flag_force_signer { return true; } @@ -647,7 +645,7 @@ mod tests { fn parse(args: &[&str]) -> Configuration { Configuration { - args: Args::parse(args).unwrap(), + args: Args::parse_without_config(args).unwrap(), } } From d046a7d5d42f3f2075178e49ffe92f43bcf82444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 12 Sep 2016 08:42:22 +0200 Subject: [PATCH 3/4] no-config flag --- parity/cli/mod.rs | 1 + parity/cli/usage.rs | 6 ++++++ parity/cli/usage.txt | 1 + 3 files changed, 8 insertions(+) diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index b9f8a7d42..0c042256a 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -68,6 +68,7 @@ usage! { // -- Miscellaneous Options flag_version: bool, + flag_no_config: bool, } { // -- Operating Options diff --git a/parity/cli/usage.rs b/parity/cli/usage.rs index 79f728b41..6ff6c254e 100644 --- a/parity/cli/usage.rs +++ b/parity/cli/usage.rs @@ -125,6 +125,12 @@ macro_rules! usage { pub fn parse>(command: &[S]) -> Result { let raw_args = try!(RawArgs::parse(command)); + + // Skip loading config file if no_config flag is specified + if raw_args.flag_no_config { + return Ok(raw_args.into_args(Config::default())); + } + let config_file = raw_args.flag_config.clone().unwrap_or_else(|| raw_args.clone().into_args(Config::default()).flag_config); let config_file = replace_home(&config_file); let config = match (fs::File::open(&config_file), raw_args.flag_config.is_some()) { diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index 14673eb2b..1749cd4b7 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -279,6 +279,7 @@ Miscellaneous Options: format as RUST_LOG. (default: {flag_logging:?}) --log-file FILENAME Specify a filename into which logging should be directed. (default: {flag_log_file:?}) + --no-config Don't load a configuration file. --no-color Don't use terminal color codes in output. (default: {flag_no_color}) -v --version Show information about version. -h --help Show this screen. From 27d30fca229d0065a748b80cfe33d29fd4c4b0d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 12 Sep 2016 14:11:30 +0200 Subject: [PATCH 4/4] Fixing tests --- parity/cli/mod.rs | 1 + parity/configuration.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 0c042256a..aa7b460db 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -578,6 +578,7 @@ mod tests { flag_logging: Some("own_tx=trace".into()), flag_log_file: Some("/var/log/parity.log".into()), flag_no_color: false, + flag_no_config: false, }); } diff --git a/parity/configuration.rs b/parity/configuration.rs index ec8e18bc0..6ee89eb35 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -652,14 +652,14 @@ mod tests { #[test] fn test_command_version() { let args = vec!["parity", "--version"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = parse(&args); assert_eq!(conf.into_command().unwrap(), Cmd::Version); } #[test] fn test_command_account_new() { let args = vec!["parity", "account", "new"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = parse(&args); assert_eq!(conf.into_command().unwrap(), Cmd::Account(AccountCmd::New(NewAccount { iterations: 10240, path: replace_home("$HOME/.parity/keys"), @@ -670,7 +670,7 @@ mod tests { #[test] fn test_command_account_list() { let args = vec!["parity", "account", "list"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = parse(&args); assert_eq!(conf.into_command().unwrap(), Cmd::Account( AccountCmd::List(replace_home("$HOME/.parity/keys"))) ); @@ -679,7 +679,7 @@ mod tests { #[test] fn test_command_account_import() { let args = vec!["parity", "account", "import", "my_dir", "another_dir"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = parse(&args); assert_eq!(conf.into_command().unwrap(), Cmd::Account(AccountCmd::Import(ImportAccounts { from: vec!["my_dir".into(), "another_dir".into()], to: replace_home("$HOME/.parity/keys"), @@ -689,7 +689,7 @@ mod tests { #[test] fn test_command_wallet_import() { let args = vec!["parity", "wallet", "import", "my_wallet.json", "--password", "pwd"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = parse(&args); assert_eq!(conf.into_command().unwrap(), Cmd::ImportPresaleWallet(ImportWallet { iterations: 10240, path: replace_home("$HOME/.parity/keys"), @@ -701,7 +701,7 @@ mod tests { #[test] fn test_command_blockchain_import() { let args = vec!["parity", "import", "blockchain.json"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = parse(&args); assert_eq!(conf.into_command().unwrap(), Cmd::Blockchain(BlockchainCmd::Import(ImportBlockchain { spec: Default::default(), logger_config: Default::default(), @@ -721,7 +721,7 @@ mod tests { #[test] fn test_command_blockchain_export() { let args = vec!["parity", "export", "blockchain.json"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = parse(&args); assert_eq!(conf.into_command().unwrap(), Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { spec: Default::default(), logger_config: Default::default(), @@ -742,7 +742,7 @@ mod tests { #[test] fn test_command_blockchain_export_with_custom_format() { let args = vec!["parity", "export", "--format", "hex", "blockchain.json"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = parse(&args); assert_eq!(conf.into_command().unwrap(), Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { spec: Default::default(), logger_config: Default::default(), @@ -763,7 +763,7 @@ mod tests { #[test] fn test_command_signer_new_token() { let args = vec!["parity", "signer", "new-token"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = parse(&args); let expected = replace_home("$HOME/.parity/signer"); assert_eq!(conf.into_command().unwrap(), Cmd::SignerToken(expected)); } @@ -771,7 +771,7 @@ mod tests { #[test] fn test_run_cmd() { let args = vec!["parity"]; - let conf = Configuration::parse(&args).unwrap(); + let conf = parse(&args); assert_eq!(conf.into_command().unwrap(), Cmd::Run(RunCmd { cache_config: Default::default(), dirs: Default::default(),